diff options
author | Ivan Solovev <ivan.solovev@qt.io> | 2022-11-22 12:48:36 +0100 |
---|---|---|
committer | Ivan Solovev <ivan.solovev@qt.io> | 2022-12-02 11:26:44 +0100 |
commit | 2d9b0002f613795b57257146e17b98a08dff7dfc (patch) | |
tree | cedce227bc62975f77fb6966326b3a54f5588374 /src | |
parent | 186f34bec49bcdf69817dec016317dd1edcbfa22 (diff) |
QCanDbcFileParser: add value description parsing
This commit adds support for the DBC VAL_ command, which defines the
textual descriptions for raw values.
Task-number: QTBUG-107075
Change-Id: I6d62e8496540755e175f2f98d70a4c2c9b39b18b
Reviewed-by: Alex Blasche <alexander.blasche@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Diffstat (limited to 'src')
-rw-r--r-- | src/serialbus/qcandbcfileparser.cpp | 150 | ||||
-rw-r--r-- | src/serialbus/qcandbcfileparser.h | 9 | ||||
-rw-r--r-- | src/serialbus/qcandbcfileparser_p.h | 2 |
3 files changed, 160 insertions, 1 deletions
diff --git a/src/serialbus/qcandbcfileparser.cpp b/src/serialbus/qcandbcfileparser.cpp index 9b90397..f5c0492 100644 --- a/src/serialbus/qcandbcfileparser.cpp +++ b/src/serialbus/qcandbcfileparser.cpp @@ -47,7 +47,8 @@ QT_BEGIN_NAMESPACE If the parsing completes successfully, call \l messageDescriptions() to get a list of the message descriptions that were extracted during the last - \l parse() call. + \l parse() call. Call \l valueDescriptions() to get the textual descriptions + of signal raw values, if they are available. Use the static \l uniqueIdDescription() function to get a \l QCanUniqueIdDescription for the DBC format. @@ -78,6 +79,7 @@ QT_BEGIN_NAMESPACE \li \c {SIG_VALTYPE_} - signal type description. \li \c {SG_MUL_VAL_} - extended multiplexing description. \li \c {CM_} - comments (only for message and signal descriptions). + \li \c {VAL_} - textual descriptions for raw signal values. \endlist Lines starting from other keywords are simply ignored. @@ -86,6 +88,40 @@ QT_BEGIN_NAMESPACE */ /*! + \typealias QCanDbcFileParser::ValueDescriptions + + This is a type alias for \c {QHash<quint32, QString>}. + + The keys of the hash represent raw signal values, and the values of the + hash represent corresponding string descriptions. +*/ + +/*! + \typealias QCanDbcFileParser::SignalValueDescriptions + + This is a type alias for \c {QHash<QString, ValueDescriptions>}. + + The keys of the hash represent signal names, and the values of the + hash contain the corresponding \l QCanDbcFileParser::ValueDescriptions + entries. + + \sa QCanDbcFileParser::ValueDescriptions +*/ + +/*! + \typealias QCanDbcFileParser::MessageValueDescriptions + + This is a type alias for + \c {QHash<QtCanBus::UniqueId, SignalValueDescriptions>}. + + The keys of the hash represent message unique ids, and the values of the + hash contain the corresponding \l QCanDbcFileParser::SignalValueDescriptions + entries. + + \sa QCanDbcFileParser::SignalValueDescriptions +*/ + +/*! \enum QCanDbcFileParser::Error This enum represents the possible errors that can happen during the parsing @@ -169,6 +205,27 @@ QList<QCanMessageDescription> QCanDbcFileParser::messageDescriptions() const } /*! + Returns the textual descriptions for signal raw values. + + DBC supports the possibility to provide textual descriptions to signal raw + values. If such data exists in the parsed DBC file(s), it can be accessed + using this function. + + The textual descriptions are unique for a certain signal within a specific + message, so the returned structure contains the information about the + message unique id and the signal name, as well as the actual value + descriptions. + + \sa QCanDbcFileParser::MessageValueDescriptions, + QCanDbcFileParser::SignalValueDescriptions, + QCanDbcFileParser::ValueDescriptions +*/ +QCanDbcFileParser::MessageValueDescriptions QCanDbcFileParser::valueDescriptions() const +{ + return d->m_valueDescriptions; +} + +/*! Returns the last error which occurred during the parsing. \sa errorString(), parse() @@ -236,6 +293,7 @@ static constexpr auto kSignalDef = "SG_ "_L1; static constexpr auto kSigValTypeDef = "SIG_VALTYPE_ "_L1; static constexpr auto kCommentDef = "CM_ "_L1; static constexpr auto kExtendedMuxDef = "SG_MUL_VAL_ "_L1; +static constexpr auto kValDef = "VAL_ "_L1; static constexpr auto kUnsignedIntRegExp = "\\d+"_L1; static constexpr auto kDoubleRegExp = "[+-]?\\d+(.\\d+([eE][+-]?\\d+)?)?"_L1; @@ -260,6 +318,7 @@ void QCanDbcFileParserPrivate::reset() m_seenExtraData = false; m_currentMessage = {}; m_messageDescriptions.clear(); + m_valueDescriptions.clear(); } /*! @@ -340,6 +399,10 @@ bool QCanDbcFileParserPrivate::processLine(const QStringView line) m_seenExtraData = true; addCurrentMessage(); parseExtendedMux(data); + } else if (data.startsWith(kValDef)) { + m_seenExtraData = true; + addCurrentMessage(); + parseValueDescriptions(data); } return true; } @@ -781,6 +844,91 @@ void QCanDbcFileParserPrivate::parseExtendedMux(const QStringView data) m_messageDescriptions.insert(uid, messageDesc); } +void QCanDbcFileParserPrivate::parseValueDescriptions(const QStringView data) +{ + // The regexp should match the following pattern: + // VAL_ message_id signal_name { value_description }; + // Here the value_description is defined as follows + // value_description = unsigned_int char_string + + // %1 valDef + // %2 maybeSpace + // %3 unsignedInt + // %4 oneOrMoreSpace + // %5 DbcIdentifier + // %6 charStr + const QString regExStr = + "%1%2(?<messageId>%3)%4(?<signalName>%5)(%4%3%4\"(%6)\")+%2;"_L1. + arg(kValDef, kMaybeSpaceRegExp, kUnsignedIntRegExp, kOneOrMoreSpaceRegExp, + kDbcIdentRegExp, kCharStrRegExp); + + const QRegularExpression valueDescRegExp(regExStr); + + const auto match = valueDescRegExp.matchView(data); + if (!match.hasMatch()) { + m_lineOffset = data.size(); + addWarning(QObject::tr("Failed to parse value description from string %1").arg(data)); + return; + } + + m_lineOffset = match.capturedEnd(0); + + bool ok = false; + const auto uid = match.capturedView(u"messageId"_s).toUInt(&ok); + if (!ok) { + addWarning(QObject::tr("Failed to parse value description from string %1").arg(data)); + return; + } + + // Check if the message exists + const auto messageDesc = m_messageDescriptions.value(uid); + if (!messageDesc.isValid()) { + addWarning(QObject::tr("Value description for message id %1 is skipped because " + "the message description is not found").arg(uid)); + return; + } + + // Check if the signal exists within the message + const QString signalName = match.captured(u"signalName"_s); + if (!messageDesc.signalDescriptionForName(signalName).isValid()) { + addWarning(QObject::tr("Value description for signal %1 and message id %2 is skipped " + "because the signal description is not found"). + arg(signalName).arg(uid)); + return; + } + + // We can have an arbitrary amount of value descriptions, so we can't use + // capture groups to capture them. But we know that they follow a specific + // pattern (because the full string matched the regexp). So we need to parse + // the rest of the matched string manually + const auto totalEnd = match.capturedEnd(0); // including the ';' + const auto signalNameEnd = match.capturedEnd(u"signalName"_s); + const auto len = totalEnd - signalNameEnd - 1; + if (len > 0) { + auto signalDescriptionsView = data.sliced(signalNameEnd, len).trimmed(); + while (signalDescriptionsView.size()) { + const auto spacePos = signalDescriptionsView.indexOf(u' '); + if (spacePos == -1) + break; + bool ok = false; + const auto value = signalDescriptionsView.sliced(0, spacePos).toUInt(&ok); + if (!ok) + break; + const auto firstQuotePos = signalDescriptionsView.indexOf(u'"', spacePos + 1); + if (firstQuotePos == -1) + break; + const auto nextQuotePos = signalDescriptionsView.indexOf(u'"', firstQuotePos + 1); + if (nextQuotePos == -1) + break; + const auto description = signalDescriptionsView.sliced( + firstQuotePos + 1, nextQuotePos - firstQuotePos - 1); + + m_valueDescriptions[uid][signalName].insert(value, description.toString()); + signalDescriptionsView = signalDescriptionsView.sliced(nextQuotePos + 1).trimmed(); + } + } +} + void QCanDbcFileParserPrivate::postProcessSignalMultiplexing() { // For the case of simple multiplexing we need to do the following for diff --git a/src/serialbus/qcandbcfileparser.h b/src/serialbus/qcandbcfileparser.h index 2186c7d..7869fb4 100644 --- a/src/serialbus/qcandbcfileparser.h +++ b/src/serialbus/qcandbcfileparser.h @@ -6,6 +6,7 @@ #include <QtCore/QList> +#include <QtSerialBus/qcancommondefinitions.h> #include <QtSerialBus/qtserialbusglobal.h> #include <memory> @@ -25,6 +26,13 @@ public: ParseError }; + // The DBC protocol uses unsinged_integer to describe the supported values. + // Do we need to use QVariant instead of quint32? Or qint64 for better BC + // guarantees? + using ValueDescriptions = QHash<quint32, QString>; + using SignalValueDescriptions = QHash<QString, ValueDescriptions>; + using MessageValueDescriptions = QHash<QtCanBus::UniqueId, SignalValueDescriptions>; + QCanDbcFileParser(); ~QCanDbcFileParser(); @@ -32,6 +40,7 @@ public: bool parse(const QStringList &fileNames); QList<QCanMessageDescription> messageDescriptions() const; + MessageValueDescriptions valueDescriptions() const; Error error() const; QString errorString() const; diff --git a/src/serialbus/qcandbcfileparser_p.h b/src/serialbus/qcandbcfileparser_p.h index 1a698b2..6381b39 100644 --- a/src/serialbus/qcandbcfileparser_p.h +++ b/src/serialbus/qcandbcfileparser_p.h @@ -37,6 +37,7 @@ public: void parseSignalType(const QStringView data); void parseComment(const QStringView data); void parseExtendedMux(const QStringView data); + void parseValueDescriptions(const QStringView data); void postProcessSignalMultiplexing(); void addWarning(QString &&warning); @@ -53,6 +54,7 @@ public: bool m_seenExtraData = false; QCanMessageDescription m_currentMessage; QHash<QtCanBus::UniqueId, QCanMessageDescription> m_messageDescriptions; + QCanDbcFileParser::MessageValueDescriptions m_valueDescriptions; }; QT_END_NAMESPACE |