From 7022c84941815e4c27bb871c6a5607ae73399146 Mon Sep 17 00:00:00 2001 From: deeaitch Date: Tue, 13 Oct 2026 08:05:40 -0400 Subject: [PATCH] Closes #15 --- README.md | 43 +- changelog.md | 21 + core/dbc_decoder.cpp | 34 +- core/dbc_decoder.h | 18 +- docs/dbcframework.tag | 18 + docs/doxygen_warnings.log | 4 +- docs/html/classDbcDecoder.html | 12 +- docs/html/classDecodeDatabase.html | 2 +- .../html/dbc__coverage__test_8cpp_source.html | 1456 +++++++++-------- docs/html/dbc__decoder_8cpp.html | 1 + docs/html/dbc__decoder_8cpp_source.html | 420 ++--- docs/html/dbc__decoder_8h.html | 32 + docs/html/dbc__decoder_8h.js | 8 +- docs/html/dbc__decoder_8h_source.html | 182 ++- docs/html/dbc__decoder__test_8cpp_source.html | 570 +++++-- .../html/dbc__lifetime__test_8cpp_source.html | 27 +- docs/html/dbc__parser__test_8cpp_source.html | 43 +- docs/html/examples_2main_8cpp_source.html | 12 +- docs/html/functions.html | 1 + docs/html/functions_vars.html | 1 + docs/html/globals.html | 1 + docs/html/globals_enum.html | 1 + docs/html/index.qhp | 203 +-- docs/html/md_README.html | 182 ++- docs/html/md_changelog.html | 56 +- docs/html/md_tests_2README.html | 14 +- docs/html/navtreedata.js | 192 +-- docs/html/navtreeindex0.js | 200 +-- docs/html/navtreeindex1.js | 9 + docs/html/search/all_0.js | 14 +- docs/html/search/all_1.js | 21 +- docs/html/search/all_10.js | 14 +- docs/html/search/all_11.js | 6 +- docs/html/search/all_12.js | 10 +- docs/html/search/all_13.js | 20 +- docs/html/search/all_14.js | 8 +- docs/html/search/all_15.js | 28 +- docs/html/search/all_16.js | 39 +- docs/html/search/all_17.js | 56 +- docs/html/search/all_18.js | 14 +- docs/html/search/all_19.js | 14 +- docs/html/search/all_1a.js | 8 +- docs/html/search/all_1b.js | 4 +- docs/html/search/all_2.js | 19 +- docs/html/search/all_3.js | 2 +- docs/html/search/all_4.js | 2 +- docs/html/search/all_5.js | 38 +- docs/html/search/all_6.js | 18 +- docs/html/search/all_7.js | 24 +- docs/html/search/all_8.js | 58 +- docs/html/search/all_9.js | 10 +- docs/html/search/all_a.js | 29 +- docs/html/search/all_b.js | 8 +- docs/html/search/all_c.js | 4 +- docs/html/search/all_d.js | 22 +- docs/html/search/all_f.js | 18 +- docs/html/search/enums_1.js | 2 +- docs/html/search/enums_2.js | 2 +- docs/html/search/enums_3.js | 4 + docs/html/search/searchdata.js | 2 +- docs/html/search/variables_d.js | 3 +- .../html/structDecodedFrameValue-members.html | 3 +- docs/html/structDecodedFrameValue.html | 37 +- docs/html/structDecodedFrameValue.js | 3 +- docs/html/structDecodedSignalValue.html | 30 +- docs/html/structRawCanFrame.html | 6 +- tests/README.md | 7 +- tests/dbc_coverage_test.cpp | 3 +- tests/dbc_decoder_test.cpp | 235 ++- 69 files changed, 2696 insertions(+), 1912 deletions(-) create mode 100644 docs/html/search/enums_3.js diff --git a/README.md b/README.md index dfb1868..0b34b93 100644 --- a/README.md +++ b/README.md @@ -392,7 +392,8 @@ Fields: `std::variant`. For a valid signal, signed definitions produce the `int64_t` alternative and unsigned definitions produce the `uint64_t` alternative. Raw values remain exact across their full 64-bit ranges. Check -`valid` before using the value; an invalid result's default value is not decoded data. +`valid` before using the physical value. Raw data is also available when +`status == PhysicalConversionFailed`; other invalid results have no decoded numeric data. Use `std::get` when the numeric type is known, or `std::get_if` to handle both: @@ -428,13 +429,41 @@ Fields: - `key` - `signals` - `valid` +- `status` `valid` means a matching frame definition was found. Check each signal's `valid` separately: insufficient payload data can leave individual signals -invalid even when the frame is valid. An unknown frame returns `valid == false`. +invalid even when the frame is valid. An unknown frame returns `valid == false`, +a null definition, its requested key, and an empty signal list. + +`FrameDecodeStatus` summarizes the result: + +| Status | Meaning | +| --- | --- | +| `UnknownFrame` | No definition matches both CAN ID and standard/extended type | +| `InsufficientPayload` | Actual byte count is less than the declared `dlc` | +| `SignalErrors` | Payload is long enough, but at least one signal failed | +| `Valid` | Payload is long enough and every signal is valid or inactive | + +Short-payload status takes precedence over signal errors; inspect each signal's +status for the individual cause. `valid` retains its existing meaning of a matched +definition and does not imply `status == FrameDecodeStatus::Valid`. + +Payload policy uses `dlc` as a byte count, including for CAN FD; it is not the +encoded wire DLC. A shorter payload is never padded: all signals whose bytes are +available still decode, even if other signals fail. A longer payload is accepted; +bytes beyond `dlc` are ignored. Signal bit layouts must fit the declared `dlc`, so +extra bytes cannot rescue an invalid definition. Empty payloads and zero-byte +frame definitions follow the same rules. Results include every signal in definition +order, preserving successful values and definition handles on partial failures. The decoder applies factor and offset; it does not clamp physical values to the -declared minimum and maximum. +declared minimum and maximum. Non-finite conversion results (NaN or positive/ +negative infinity), including overflow with finite factor/offset, produce +`PhysicalConversionFailed`. The exact raw value and non-finite `physicalValue` +remain available, while signal `valid` is false. Non-finite factor/offset in manually +constructed definitions follow this same conversion-result check. Physical +conversion failure of a selector does not prevent raw branch selection. ### Definition ownership and reload @@ -734,10 +763,12 @@ Each result has `SignalDecodeStatus status`: | `Inactive` | Selector is available and chooses another branch | | `SelectorUnavailable` | Branch activation is unknown because selector bytes are missing | | `InsufficientPayload` | Selected or unconditional signal bytes are missing | -| `InvalidDefinition` | Invalid bit length or missing/invalid selector in manually built runtime metadata | +| `InvalidDefinition` | Invalid length, byte order, numeric type, multiplexing role, layout outside declared DLC, or missing/invalid selector in manually built runtime metadata | +| `PhysicalConversionFailed` | Raw value is available, but the converted physical value is NaN or infinity | -All statuses except `Valid` have `valid == false`; their numeric fields contain -no decoded data and `GetValueDescription()` returns nullptr. Check `status` to +All statuses except `Valid` have `valid == false` and `GetValueDescription()` +returns nullptr. `PhysicalConversionFailed` preserves extracted raw data and the +non-finite conversion result; other invalid statuses have no decoded numeric data. Check `status` to distinguish inactive branches from errors. Consumers constructing runtime metadata manually must obey the same non-overlap rules as the parser. Extended and nested multiplexing are outside this stage and are explicitly rejected, rather than diff --git a/changelog.md b/changelog.md index 8ae623b..aa8152d 100644 --- a/changelog.md +++ b/changelog.md @@ -6,6 +6,27 @@ existing Git tag; subsequent changes are listed as unreleased. ## Unreleased +### 2026-10-13 + +#### Added + +- `FrameDecodeStatus` distinguishes unknown frames, insufficient payloads, + signal errors, and successful decoding; frame `valid` still means matched. +- `SignalDecodeStatus::PhysicalConversionFailed` reports NaN/Inf results, + retaining exact raw values and the non-finite physical result. +- Regression tests for payload-length policy, partial results, invalid runtime + metadata, physical overflow/non-finite parameters, and raw branch selection + when selector physical conversion fails. + +#### Changed + +- Short payloads decode available signals without padding; longer payloads + ignore bytes beyond the declared DLC. Short frame status takes precedence + over signal errors, with per-signal causes retained in definition order. +- Runtime signal validation checks enum values and bit layouts against declared + DLC using overflow-safe coordinates. Extra bytes cannot rescue invalid layouts. +- Documented frame/signal status meanings, value availability, and payload policy. + ### 2026-10-11 #### Added diff --git a/core/dbc_decoder.cpp b/core/dbc_decoder.cpp index ef8da84..45ca0e5 100644 --- a/core/dbc_decoder.cpp +++ b/core/dbc_decoder.cpp @@ -10,6 +10,7 @@ */ #include "dbc_decoder.h" +#include #include std::shared_ptr DbcDecoder::FindFrame(const DecodeDatabase &database, @@ -29,11 +30,17 @@ DecodedFrameValue DbcDecoder::Decode(const DecodeDatabase &database, result.definition = definition; result.valid = true; + result.status = frame.data.size() < definition->dlc + ? FrameDecodeStatus::InsufficientPayload + : FrameDecodeStatus::Valid; const SelectorState selector = ReadSelector(*definition, frame); result.signals.reserve(definition->signals.size()); for (const DecodeSignal &signal: definition->signals) { - DecodedSignalValue decoded = DecodeOneSignal(signal, frame, selector); + DecodedSignalValue decoded = DecodeOneSignal(signal, definition->dlc, frame, selector); + if (!decoded.valid && decoded.status != SignalDecodeStatus::Inactive && + result.status == FrameDecodeStatus::Valid) + result.status = FrameDecodeStatus::SignalErrors; decoded.definition = std::shared_ptr(definition, &signal); decoded.key = SignalKey(result.key, signal.name); result.signals.push_back(std::move(decoded)); @@ -42,6 +49,22 @@ DecodedFrameValue DbcDecoder::Decode(const DecodeDatabase &database, return result; } +bool DbcDecoder::IsValidDefinition(const DecodeSignal &signal, std::uint8_t dlc) { + if (signal.length == 0U || signal.length > 64U || + (signal.byteOrder != ByteOrder::Intel && signal.byteOrder != ByteOrder::Motorola) || + (signal.valueType != ValueType::Unsigned && signal.valueType != ValueType::Signed) || + (signal.multiplexing.role != MultiplexRole::Unconditional && + signal.multiplexing.role != MultiplexRole::Selector && + signal.multiplexing.role != MultiplexRole::Branch)) + return false; + + // Use widened coordinates to avoid overflow on malformed start bits. + const std::uint64_t start = signal.byteOrder == ByteOrder::Intel + ? signal.startBit + : std::uint64_t(signal.startBit / 8U) * 8U + 7U - signal.startBit % 8U; + return start + signal.length <= std::uint64_t(dlc) * 8U; +} + DbcDecoder::SelectorState DbcDecoder::ReadSelector(const DecodeFrame &definition, const RawCanFrame &frame) { SelectorState selector; @@ -57,18 +80,19 @@ DbcDecoder::SelectorState DbcDecoder::ReadSelector(const DecodeFrame &definition const DecodeSignal &signal = *selector.definition; selector.valid = signal.valueType == ValueType::Unsigned && - signal.length > 0U && signal.length <= 64U; + IsValidDefinition(signal, definition.dlc); selector.available = selector.valid && ExtractUnsigned(frame.data, signal, selector.value); return selector; } DecodedSignalValue DbcDecoder::DecodeOneSignal(const DecodeSignal &signal, + std::uint8_t dlc, const RawCanFrame &frame, const SelectorState &selector) { DecodedSignalValue decoded; std::uint64_t unsignedValue = 0U; - if (signal.length == 0U || signal.length > 64U || + if (!IsValidDefinition(signal, dlc) || (signal.multiplexing.role != MultiplexRole::Unconditional && !selector.valid)) { return decoded; } @@ -101,6 +125,10 @@ DecodedSignalValue DbcDecoder::DecodeOneSignal(const DecodeSignal &signal, else numericValue = static_cast(std::get(decoded.rawValue)); decoded.physicalValue = numericValue * signal.factor + signal.offset; + if (!std::isfinite(decoded.physicalValue)) { + decoded.status = SignalDecodeStatus::PhysicalConversionFailed; + return decoded; + } decoded.valid = true; decoded.status = SignalDecodeStatus::Valid; diff --git a/core/dbc_decoder.h b/core/dbc_decoder.h index 3908871..6c0bdf3 100644 --- a/core/dbc_decoder.h +++ b/core/dbc_decoder.h @@ -34,6 +34,15 @@ enum class SignalDecodeStatus { InsufficientPayload, SelectorUnavailable, Inactive, + Valid, + PhysicalConversionFailed +}; + +/** Frame summary: short payload takes precedence over individual signal errors. */ +enum class FrameDecodeStatus { + UnknownFrame, + InsufficientPayload, + SignalErrors, Valid }; @@ -43,8 +52,8 @@ enum class SignalDecodeStatus { struct DecodedSignalValue { std::shared_ptr definition; /**< Owning immutable definition. */ SignalKey key; /**< Stable application identifier. */ - RawSignalValue rawValue = std::uint64_t(0U); /**< Exact signed or unsigned raw value. */ - double physicalValue = 0.0; /**< Converted physical value. */ + RawSignalValue rawValue = std::uint64_t(0U); /**< Exact raw value for Valid or PhysicalConversionFailed. */ + double physicalValue = 0.0; /**< Conversion result, possibly NaN/Inf on failure. */ bool valid = false; /**< true if decoding succeeded. */ SignalDecodeStatus status = SignalDecodeStatus::InvalidDefinition; @@ -64,6 +73,7 @@ struct DecodedFrameValue { FrameKey key; /**< Stable application identifier. */ std::vector signals; /**< Decoded signal values. */ bool valid = false; /**< true if frame was matched. */ + FrameDecodeStatus status = FrameDecodeStatus::UnknownFrame; /**< Inspect signals for individual failures. */ DecodedFrameValue() = default; }; @@ -88,12 +98,15 @@ public: * @brief Decode one raw CAN frame. * @param database Runtime decode database. * @param frame Raw CAN frame. + * Short payloads decode available signals; bytes beyond declared DLC are ignored. * @return Decoded frame value. */ DecodedFrameValue Decode(const DecodeDatabase &database, const RawCanFrame &frame) const; private: + static bool IsValidDefinition(const DecodeSignal &signal, std::uint8_t dlc); + struct SelectorState { const DecodeSignal *definition = nullptr; bool valid = false; @@ -103,6 +116,7 @@ private: static SelectorState ReadSelector(const DecodeFrame &definition, const RawCanFrame &frame); static DecodedSignalValue DecodeOneSignal(const DecodeSignal &signal, + std::uint8_t dlc, const RawCanFrame &frame, const SelectorState &selector); diff --git a/docs/dbcframework.tag b/docs/dbcframework.tag index 84239b9..c5696ec 100644 --- a/docs/dbcframework.tag +++ b/docs/dbcframework.tag @@ -36,6 +36,17 @@ DecodedSignalValue DecodedFrameValue DbcDecoder + + + FrameDecodeStatus + dbc__decoder_8h.html + a855b2bc36cb18bfbae5ff30a2394e961 + + UnknownFrame + InsufficientPayload + SignalErrors + Valid + dbc_parser.cpp @@ -325,6 +336,13 @@ a4f51c31110dba1f73a763c596372beb0 + + FrameDecodeStatus + status + structDecodedFrameValue.html + a645f433798d48a2787e7f00dfbb7dff9 + + DecodedSignalValue diff --git a/docs/doxygen_warnings.log b/docs/doxygen_warnings.log index 36166ce..cc72916 100644 --- a/docs/doxygen_warnings.log +++ b/docs/doxygen_warnings.log @@ -10,7 +10,7 @@ /home/deeaitch/work/3.projects/git.himro.ca/automotive/dbc/core/definition_key.h:56: warning: Compound SignalKeyHasher is not documented. /home/deeaitch/work/3.projects/git.himro.ca/automotive/dbc/core/dbc_decoder.h:32: warning: Member SignalDecodeStatus (enumeration) of file dbc_decoder.h is not documented. /home/deeaitch/work/3.projects/git.himro.ca/automotive/dbc/examples/main.cpp:100: warning: Member main(int argc, char *argv[]) (function) of file examples/main.cpp is not documented. -/home/deeaitch/work/3.projects/git.himro.ca/automotive/dbc/README.md:1576: warning: end of comment block while expecting command +/home/deeaitch/work/3.projects/git.himro.ca/automotive/dbc/README.md:1607: warning: end of comment block while expecting command /home/deeaitch/work/3.projects/git.himro.ca/automotive/dbc/core/dbc_statement_handler.h:68: warning: Member Handle(const std::string &line, DbcParseContext &context) const override (function) of class DbcCommentHandler is not documented. /home/deeaitch/work/3.projects/git.himro.ca/automotive/dbc/core/dbc_diagnostic.h:26: warning: Member severity (variable) of struct DbcDiagnostic is not documented. /home/deeaitch/work/3.projects/git.himro.ca/automotive/dbc/core/dbc_diagnostic.h:27: warning: Member code (variable) of struct DbcDiagnostic is not documented. @@ -48,7 +48,7 @@ /home/deeaitch/work/3.projects/git.himro.ca/automotive/dbc/core/dbc_statement_handler.h:81: warning: Member Handle(const std::string &line, DbcParseContext &context) const override (function) of class DbcUnsupportedMultiplexingHandler is not documented. /home/deeaitch/work/3.projects/git.himro.ca/automotive/dbc/core/dbc_statement_handler.h:76: warning: Member Handle(const std::string &line, DbcParseContext &context) const override (function) of class DbcValueDescriptionHandler is not documented. /home/deeaitch/work/3.projects/git.himro.ca/automotive/dbc/core/decode_database.h:101: warning: Member FindSignal(const SignalKey &key) const (function) of class DecodeDatabase is not documented. -/home/deeaitch/work/3.projects/git.himro.ca/automotive/dbc/core/dbc_decoder.h:49: warning: Member status (variable) of struct DecodedSignalValue is not documented. +/home/deeaitch/work/3.projects/git.himro.ca/automotive/dbc/core/dbc_decoder.h:58: warning: Member status (variable) of struct DecodedSignalValue is not documented. /home/deeaitch/work/3.projects/git.himro.ca/automotive/dbc/core/decode_database.h:78: warning: Member GetKey() const (function) of struct DecodeFrame is not documented. /home/deeaitch/work/3.projects/git.himro.ca/automotive/dbc/core/decode_database.h:44: warning: Member multiplexing (variable) of struct DecodeSignal is not documented. /home/deeaitch/work/3.projects/git.himro.ca/automotive/dbc/core/definition_key.h:21: warning: Member FrameKey(std::uint32_t id, bool extended) (function) of struct FrameKey is not documented. diff --git a/docs/html/classDbcDecoder.html b/docs/html/classDbcDecoder.html index 4f0483e..97cb237 100644 --- a/docs/html/classDbcDecoder.html +++ b/docs/html/classDbcDecoder.html @@ -115,7 +115,7 @@ Public Member Functions

Detailed Description

Runtime CAN decoder using prebuilt decode database.

-

Definition at line 74 of file dbc_decoder.h.

+

Definition at line 84 of file dbc_decoder.h.

Member Function Documentation

◆ FindFrame()

@@ -160,11 +160,11 @@ Public Member Functions
Returns
Owning immutable frame handle or nullptr. Safe across reload.
-

Definition at line 15 of file dbc_decoder.cpp.

+

Definition at line 16 of file dbc_decoder.cpp.

References DecodeDatabase::FindFrame().

-

Referenced by Decode().

+

Referenced by Decode().

@@ -198,15 +198,15 @@ Public Member Functions
Parameters
- +
databaseRuntime decode database.
frameRaw CAN frame.
frameRaw CAN frame. Short payloads decode available signals; bytes beyond declared DLC are ignored.
Returns
Decoded frame value.
-

Definition at line 21 of file dbc_decoder.cpp.

+

Definition at line 22 of file dbc_decoder.cpp.

-

References RawCanFrame::canId, DecodedSignalValue::definition, DecodedFrameValue::definition, FindFrame(), RawCanFrame::isExtended, DecodedSignalValue::key, DecodedFrameValue::key, and DecodedFrameValue::valid.

+

References RawCanFrame::canId, RawCanFrame::data, DecodedSignalValue::definition, DecodedFrameValue::definition, FindFrame(), RawCanFrame::isExtended, DecodedSignalValue::key, DecodedFrameValue::key, DecodedFrameValue::status, DecodedSignalValue::valid, and DecodedFrameValue::valid.

diff --git a/docs/html/classDecodeDatabase.html b/docs/html/classDecodeDatabase.html index fd1a9f2..8fdd0c9 100644 --- a/docs/html/classDecodeDatabase.html +++ b/docs/html/classDecodeDatabase.html @@ -193,7 +193,7 @@ Public Member Functions

Definition at line 33 of file decode_database.cpp.

-

Referenced by DbcDecoder::FindFrame().

+

Referenced by DbcDecoder::FindFrame().

diff --git a/docs/html/dbc__coverage__test_8cpp_source.html b/docs/html/dbc__coverage__test_8cpp_source.html index aca7cd9..c9bd1a8 100644 --- a/docs/html/dbc__coverage__test_8cpp_source.html +++ b/docs/html/dbc__coverage__test_8cpp_source.html @@ -123,733 +123,734 @@ $(document).ready(function(){initNavTree('dbc__coverage__test_8cpp_source.html',
23 DecodeDatabase SingleSignal(const DecodeSignal &signal) {
24 DecodeFrame frame;
25 frame.canId = 17U;
-
26 frame.signals.push_back(signal);
-
27 return DecodeDatabase({frame});
-
28 }
-
29
-
30 // Independent reference: collect the visited bits first, then weight them.
-
31 // Motorola uses a linear MSB-first coordinate instead of sawtooth traversal.
-
32 std::uint64_t ReferenceExtract(const std::vector<std::uint8_t> &data,
-
33 ByteOrder order, unsigned start, unsigned length) {
-
34 std::uint64_t expected = 0;
-
35 for (unsigned i = 0; i < length; ++i) {
-
36 const unsigned coordinate = order == ByteOrder::Intel
-
37 ? start + i
-
38 : (start / 8U) * 8U + (7U - start % 8U) + i;
-
39 const unsigned bit = order == ByteOrder::Intel ? coordinate % 8U : 7U - coordinate % 8U;
-
40 const unsigned weight = order == ByteOrder::Intel ? i : length - 1U - i;
-
41 expected |= std::uint64_t((data.at(coordinate / 8U) >> bit) & 1U) << weight;
-
42 }
-
43 return expected;
-
44 }
-
45
-
46 struct FactoryBase {
-
47 virtual ~FactoryBase() = default;
-
48 virtual int Value() const = 0;
-
49 };
-
50 struct FactoryFirst : FactoryBase {
-
51 explicit FactoryFirst(std::unique_ptr<int> value) : value(std::move(value)) {}
-
52 int Value() const override { return *value; }
-
53 std::unique_ptr<int> value;
-
54 };
-
55 struct FactorySecond : FactoryFirst {
-
56 using FactoryFirst::FactoryFirst;
-
57 int Value() const override { return *value + 1; }
-
58 };
-
59}// namespace
-
60
-
61class CoverageTest : public QObject {
-
62 Q_OBJECT
-
63private Q_SLOTS:
-
64 void frameLookup();
-
65 void signedBoundaries_data();
-
66 void signedBoundaries();
-
67 void extraction_data();
-
68 void extraction();
-
69 void invalidSignalStatus_data();
-
70 void invalidSignalStatus();
-
71 void malformedRuntimeMultiplexing_data();
-
72 void malformedRuntimeMultiplexing();
-
73 void pgn_data();
-
74 void pgn();
-
75 void comments_data();
-
76 void comments();
-
77 void ignoredCommentStatements_data();
-
78 void ignoredCommentStatements();
-
79 void ambiguousValueReferences_data();
-
80 void ambiguousValueReferences();
-
81 void valueReferenceScope();
-
82 void fileFormatting_data();
-
83 void fileFormatting();
-
84 void missingFile();
-
85 void builders();
-
86 void physicalConversion_data();
-
87 void physicalConversion();
-
88 void multiplexingScenarios();
-
89 void multiplexingErrorLocations_data();
-
90 void multiplexingErrorLocations();
-
91 void escapedValueDescriptions();
-
92 void factory();
-
93};
-
94
-
95void CoverageTest::frameLookup() {
-
96 DbcDecoder decoder;
-
97 RawCanFrame raw;
-
98 raw.canId = 17U;
-
99 DecodeDatabase empty;
-
100 QVERIFY(decoder.FindFrame(empty, 17U, false) == nullptr);
-
101 const DecodedFrameValue absent = decoder.Decode(empty, raw);
-
102 QVERIFY(!absent.valid);
-
103 QVERIFY(absent.definition == nullptr);
-
104 QVERIFY(absent.signals.empty());
-
105 DecodeSignal signal;
-
106 DecodeDatabase database = SingleSignal(signal);
-
107 QVERIFY(decoder.FindFrame(database, 18U, false) == nullptr);
-
108 QVERIFY(decoder.FindFrame(database, 17U, true) == nullptr);
-
109 raw.isExtended = true;
-
110 QVERIFY(!decoder.Decode(database, raw).valid);
-
111 raw.isExtended = false;
-
112 raw.canId = 18U;
-
113 QVERIFY(!decoder.Decode(database, raw).valid);
-
114 raw.canId = 17U;
-
115 DecodeFrame noSignals;
-
116 noSignals.canId = 17U;
-
117 database = DecodeDatabase({noSignals});
-
118 const DecodedFrameValue matched = decoder.Decode(database, raw);
-
119 QVERIFY(matched.valid);
-
120 QCOMPARE(matched.definition.get(), &database.GetFrames()[0]);
-
121 QVERIFY(matched.signals.empty());
-
122}
-
123
-
124void CoverageTest::signedBoundaries_data() {
-
125 QTest::addColumn<int>("order");
-
126 QTest::addColumn<unsigned>("length");
-
127 QTest::addColumn<qlonglong>("expected");
-
128 for (int order = 0; order < 2; ++order) {
-
129 for (unsigned length: {1U, 2U, 7U, 8U, 31U, 32U, 63U}) {
-
130 const std::int64_t half = std::int64_t(1) << (length - 1U);
-
131 const std::array<std::int64_t, 4> values{{0, -1, -half, half - 1}};
-
132 for (unsigned index = 0; index < values.size(); ++index) {
-
133 const QByteArray name = QByteArray::number(order) + "-" + QByteArray::number(length) + "-" + QByteArray::number(index);
-
134 QTest::newRow(name.constData()) << order << length << qlonglong(values[index]);
-
135 }
-
136 }
-
137 }
-
138}
-
139
-
140void CoverageTest::signedBoundaries() {
-
141 QFETCH(int, order);
-
142 QFETCH(unsigned, length);
-
143 QFETCH(qlonglong, expected);
-
144 DecodeSignal signal;
-
145 signal.byteOrder = order == 0 ? ByteOrder::Intel : ByteOrder::Motorola;
-
146 signal.startBit = order == 0 ? 0U : 7U;
-
147 signal.length = length;
-
148 signal.valueType = ValueType::Signed;
-
149 RawCanFrame raw;
-
150 raw.canId = 17U;
-
151 raw.data.assign(8U, 0U);
-
152 const std::uint64_t bits = static_cast<std::uint64_t>(expected);
-
153 for (unsigned i = 0; i < length; ++i) {
-
154 const unsigned weight = order == 0 ? i : length - 1U - i;
-
155 const unsigned position = order == 0 ? i % 8U : 7U - i % 8U;
-
156 raw.data[i / 8U] |= static_cast<std::uint8_t>(((bits >> weight) & 1U) << position);
-
157 }
-
158 const DecodeDatabase database = SingleSignal(signal);
-
159 const DecodedFrameValue result = DbcDecoder().Decode(database, raw);
-
160 QVERIFY(result.signals[0].valid);
-
161 QVERIFY(std::holds_alternative<std::int64_t>(result.signals[0].rawValue));
-
162 QCOMPARE(std::get<std::int64_t>(result.signals[0].rawValue), static_cast<std::int64_t>(expected));
-
163}
-
164
-
165void CoverageTest::extraction_data() {
-
166 QTest::addColumn<int>("order");
-
167 QTest::addColumn<unsigned>("start");
-
168 QTest::addColumn<unsigned>("length");
-
169 for (int order = 0; order < 2; ++order)
-
170 for (unsigned start = 0; start < 16U; ++start)
-
171 for (unsigned length = 1; length <= 64U; ++length) {
-
172 const QByteArray name = QByteArray::number(order) + "-" + QByteArray::number(start) + "-" + QByteArray::number(length);
-
173 QTest::newRow(name.constData()) << order << start << length;
-
174 }
-
175}
-
176
-
177void CoverageTest::extraction() {
-
178 QFETCH(int, order);
-
179 QFETCH(unsigned, start);
-
180 QFETCH(unsigned, length);
-
181 DecodeSignal signal;
-
182 signal.byteOrder = order == 0 ? ByteOrder::Intel : ByteOrder::Motorola;
-
183 signal.startBit = start;
-
184 signal.length = length;
-
185 const DecodeDatabase database = SingleSignal(signal);
-
186 RawCanFrame raw;
-
187 raw.canId = 17U;
-
188 const std::vector<std::vector<std::uint8_t>> patterns = {
-
189 std::vector<std::uint8_t>(10U, 0U),
-
190 std::vector<std::uint8_t>(10U, 255U),
-
191 {0xA5, 0x3C, 0x81, 0x7E, 0x12, 0xD4, 0x69, 0xB0, 0x5A, 0xC3}};
-
192 for (const std::vector<std::uint8_t> &data: patterns) {
-
193 raw.data = data;
-
194 const unsigned firstCoordinate = order == 0 ? start : (start / 8U) * 8U + 7U - start % 8U;
-
195 raw.data.resize((firstCoordinate + length + 7U) / 8U);
-
196 const DecodedFrameValue decoded = DbcDecoder().Decode(database, raw);
-
197 QVERIFY(decoded.signals[0].valid);
-
198 QCOMPARE(std::get<std::uint64_t>(decoded.signals[0].rawValue), ReferenceExtract(data, signal.byteOrder, start, length));
-
199 }
-
200}
-
201
-
202void CoverageTest::invalidSignalStatus_data() {
-
203 QTest::addColumn<int>("order");
-
204 QTest::addColumn<unsigned>("start");
-
205 QTest::addColumn<unsigned>("length");
-
206 QTest::addColumn<unsigned>("bytes");
-
207 QTest::addColumn<int>("status");
-
208 for (int order = 0; order < 2; ++order) {
-
209 const unsigned start = order == 0 ? 0U : 7U;
-
210 const QByteArray prefix = QByteArray::number(order);
-
211 QTest::newRow((prefix + "-zero").constData()) << order << start << 0U << 1U << int(SignalDecodeStatus::InvalidDefinition);
-
212 QTest::newRow((prefix + "-65").constData()) << order << start << 65U << 9U << int(SignalDecodeStatus::InvalidDefinition);
-
213 QTest::newRow((prefix + "-empty").constData()) << order << start << 8U << 0U << int(SignalDecodeStatus::InsufficientPayload);
-
214 QTest::newRow((prefix + "-short").constData()) << order << start << 16U << 1U << int(SignalDecodeStatus::InsufficientPayload);
-
215 QTest::newRow((prefix + "-large-start").constData()) << order << std::numeric_limits<unsigned>::max() << 8U << 1U << int(SignalDecodeStatus::InsufficientPayload);
-
216 }
-
217}
-
218
-
219void CoverageTest::invalidSignalStatus() {
-
220 QFETCH(int, order);
-
221 QFETCH(unsigned, start);
-
222 QFETCH(unsigned, length);
-
223 QFETCH(unsigned, bytes);
-
224 QFETCH(int, status);
-
225 DecodeSignal signal;
-
226 signal.startBit = start;
-
227 signal.length = length;
-
228 signal.byteOrder = order == 0 ? ByteOrder::Intel : ByteOrder::Motorola;
-
229 const DecodeDatabase database = SingleSignal(signal);
-
230 RawCanFrame raw;
-
231 raw.canId = 17U;
-
232 raw.data.assign(bytes, 255U);
-
233 const DecodedFrameValue decoded = DbcDecoder().Decode(database, raw);
-
234 QVERIFY(decoded.valid);
-
235 QVERIFY(!decoded.signals[0].valid);
-
236 QCOMPARE(int(decoded.signals[0].status), status);
-
237 QVERIFY(decoded.signals[0].GetValueDescription() == nullptr);
-
238}
-
239
-
240void CoverageTest::malformedRuntimeMultiplexing_data() {
-
241 QTest::addColumn<int>("kind");
-
242 QTest::newRow("two-selectors") << 0;
-
243 QTest::newRow("signed-selector") << 1;
-
244 QTest::newRow("65-bit-selector") << 2;
-
245 QTest::newRow("branch-out-of-range") << 3;
-
246}
-
247
-
248void CoverageTest::malformedRuntimeMultiplexing() {
-
249 QFETCH(int, kind);
-
250 DecodeSignal selector;
-
251 selector.length = 1U;
-
252 selector.multiplexing.role = MultiplexRole::Selector;
-
253 selector.name = "Selector";
-
254 std::vector<DecodeFrame> definitions = SingleSignal(selector).GetFrames();
-
255 DecodeSignal branch;
-
256 branch.name = "Branch";
-
257 branch.startBit = 8U;
-
258 branch.length = 8U;
-
259 branch.multiplexing.role = MultiplexRole::Branch;
-
260 branch.multiplexing.value = 1U;
-
261 definitions[0].signals.push_back(branch);
-
262 DecodeSignal ordinary;
-
263 ordinary.name = "Ordinary";
-
264 ordinary.startBit = 16U;
-
265 ordinary.length = 8U;
-
266 definitions[0].signals.push_back(ordinary);
-
267 if (kind == 0) {
-
268 selector.name = "SecondSelector";
-
269 definitions[0].signals.push_back(selector);
-
270 }
-
271 if (kind == 1) definitions[0].signals[0].valueType = ValueType::Signed;
-
272 if (kind == 2) definitions[0].signals[0].length = 65U;
-
273 if (kind == 3) definitions[0].signals[1].multiplexing.value = 2U;
-
274 const DecodeDatabase database(std::move(definitions));
-
275 RawCanFrame raw;
-
276 raw.canId = 17U;
-
277 raw.data = {1U, 42U, 99U};
-
278 const DecodedFrameValue result = DbcDecoder().Decode(database, raw);
-
279 QCOMPARE(int(result.signals[1].status), int(SignalDecodeStatus::InvalidDefinition));
-
280 QVERIFY(!result.signals[1].valid);
-
281 QVERIFY(result.signals[2].valid);
-
282 QCOMPARE(std::get<std::uint64_t>(result.signals[2].rawValue), std::uint64_t(99));
-
283 if (kind != 3) {
-
284 QCOMPARE(int(result.signals[0].status), int(SignalDecodeStatus::InvalidDefinition));
-
285 if (kind == 0) QCOMPARE(int(result.signals[3].status), int(SignalDecodeStatus::InvalidDefinition));
-
286 }
-
287}
-
288
-
289void CoverageTest::pgn_data() {
-
290 QTest::addColumn<unsigned>("id");
-
291 QTest::addColumn<unsigned>("expected");
-
292 QTest::addColumn<bool>("hasPgn");
-
293 QTest::newRow("standard") << 0x7FFU << 0U << false;
-
294 for (unsigned dp: {0U, 1U})
-
295 for (unsigned pf: {239U, 240U})
-
296 for (unsigned ps: {0U, 0xABU}) {
-
297 const unsigned id = 0x80000000U | (dp << 24U) | (pf << 16U) | (ps << 8U) | 0x42U;
-
298 const unsigned expected = (dp << 16U) | (pf == 239U ? 0xEF00U : 0xF000U | ps);
-
299 const QByteArray name = QByteArray::number(dp) + "-" + QByteArray::number(pf) + "-" + QByteArray::number(ps);
-
300 QTest::newRow(name.constData()) << id << expected << true;
-
301 }
-
302}
-
303
-
304void CoverageTest::pgn() {
-
305 QFETCH(unsigned, id);
-
306 QFETCH(unsigned, expected);
-
307 QFETCH(bool, hasPgn);
-
308 const DbcDatabase parsed = ParseText("BO_ " + std::to_string(id) + " Frame: 8 ECU\n");
-
309 QCOMPARE(parsed.frames[0].hasPgn, hasPgn);
-
310 QCOMPARE(parsed.frames[0].pgn, expected);
-
311}
-
312
-
313void CoverageTest::comments_data() {
-
314 QTest::addColumn<QString>("before");
-
315 QTest::addColumn<QString>("after");
-
316 QTest::addColumn<QString>("frameComment");
-
317 QTest::addColumn<QString>("signalComment");
-
318 QTest::newRow("tabs") << QString::fromUtf8("") << QString::fromUtf8("CM_\tBO_ 1 \"Frame\";\nCM_\tSG_ 1 State \"Signal\";\n") << QString::fromUtf8("Frame") << QString::fromUtf8("Signal");
-
319 QTest::newRow("empty-overwrite") << QString::fromUtf8("") << QString::fromUtf8("CM_ BO_ 1 \"Old\";\nCM_ BO_ 1 \"\";\nCM_ SG_ 1 State \"Old\";\nCM_ SG_ 1 State \"\";\n") << QString::fromUtf8("") << QString::fromUtf8("");
-
320 QTest::newRow("last-wins") << QString::fromUtf8("") << QString::fromUtf8("CM_ BO_ 1 \"First\";\nCM_ BO_ 1 \"Last\";\nCM_ SG_ 1 State \"First\";\nCM_ SG_ 1 State \"Last\";\n") << QString::fromUtf8("Last") << QString::fromUtf8("Last");
-
321 QTest::newRow("missing-targets") << QString::fromUtf8("") << QString::fromUtf8("CM_ BO_ 2 \"Unknown\";\nCM_ SG_ 2 State \"Unknown\";\nCM_ SG_ 1 Missing \"Unknown\";\n") << QString::fromUtf8("") << QString::fromUtf8("");
-
322 // Current documented limitation: comments resolve only already parsed targets.
-
323 QTest::newRow("before-definitions") << QString::fromUtf8("CM_ BO_ 1 \"Early\";\nCM_ SG_ 1 State \"Early\";\n") << QString::fromUtf8("") << QString::fromUtf8("") << QString::fromUtf8("");
-
324 QTest::newRow("missing-quotes") << QString::fromUtf8("") << QString::fromUtf8("CM_ BO_ 1 Missing;\nCM_ SG_ 1 State \"Unclosed;\n") << QString::fromUtf8("") << QString::fromUtf8("");
-
325}
-
326
-
327void CoverageTest::comments() {
-
328 QFETCH(QString, before);
-
329 QFETCH(QString, after);
-
330 QFETCH(QString, frameComment);
-
331 QFETCH(QString, signalComment);
-
332 const DbcDatabase parsed = ParseText(before.toStdString() + "BO_ 1 Frame: 1 ECU\nSG_ State : 0|8@1+ (1,0) [0|255] \"\" ECU\n" + after.toStdString());
-
333 QCOMPARE(parsed.frames[0].comment, frameComment.toStdString());
-
334 QCOMPARE(parsed.frames[0].signals[0].comment, signalComment.toStdString());
-
335}
-
336
-
337void CoverageTest::ignoredCommentStatements_data() {
-
338 QTest::addColumn<QString>("statement");
-
339 QTest::newRow("empty") << QString();
-
340 QTest::newRow("wrong-keyword") << QStringLiteral("OTHER_ BO_ 1 \"Ignored\";");
-
341 QTest::newRow("keyword-prefix") << QStringLiteral("CM_OTHER BO_ 1 \"Ignored\";");
-
342 QTest::newRow("frame-without-cm") << QStringLiteral("BO_ 1 \"Ignored\";");
-
343 QTest::newRow("missing-subtype") << QStringLiteral("CM_");
-
344 QTest::newRow("global-comment") << QStringLiteral("CM_ \"Ignored\";");
-
345 QTest::newRow("node-comment") << QStringLiteral("CM_ BU_ ECU \"Ignored\";");
-
346 QTest::newRow("environment-comment") << QStringLiteral("CM_ EV_ State \"Ignored\";");
-
347 QTest::newRow("unknown-subtype") << QStringLiteral("CM_ OTHER_ 1 \"Ignored\";");
-
348}
-
349
-
350void CoverageTest::ignoredCommentStatements() {
-
351 QFETCH(QString, statement);
-
352 DbcDatabase database;
-
353 FrameInfo frame;
-
354 frame.canId = 1U;
-
355 frame.name = "Frame";
-
356 frame.comment = "Existing frame comment";
-
357 SignalInfo signal;
-
358 signal.name = "State";
-
359 signal.comment = "Existing signal comment";
-
360 frame.signals.push_back(signal);
-
361 database.frames.push_back(frame);
-
362 DbcParseContext context{database, &database.frames[0]};
-
363
-
364 // The handler is public: callers may bypass keyword dispatch. Ignored input
-
365 // must preserve existing metadata, including comments already assigned.
-
366 DbcCommentHandler().Handle(statement.toStdString(), context);
-
367 QCOMPARE(database.frames.size(), std::size_t(1));
-
368 QCOMPARE(database.frames[0].canId, 1U);
-
369 QCOMPARE(database.frames[0].name, frame.name);
-
370 QCOMPARE(database.frames[0].comment, frame.comment);
-
371 QCOMPARE(database.frames[0].signals.size(), std::size_t(1));
-
372 QCOMPARE(database.frames[0].signals[0].name, signal.name);
-
373 QCOMPARE(database.frames[0].signals[0].comment, signal.comment);
-
374 QCOMPARE(context.currentFrame, &database.frames[0]);
-
375}
-
376
-
377void CoverageTest::ambiguousValueReferences_data() {
-
378 QTest::addColumn<bool>("duplicateFrames");
-
379 QTest::addColumn<bool>("extended");
-
380 QTest::newRow("duplicate-standard-frames") << true << false;
-
381 QTest::newRow("duplicate-extended-frames") << true << true;
-
382 QTest::newRow("duplicate-signals-in-standard-frame") << false << false;
-
383 QTest::newRow("duplicate-signals-in-extended-frame") << false << true;
-
384}
-
385
-
386void CoverageTest::ambiguousValueReferences() {
-
387 QFETCH(bool, duplicateFrames);
-
388 QFETCH(bool, extended);
-
389 DbcDatabase database;
-
390 FrameInfo frame;
-
391 frame.canId = 1U;
-
392 frame.name = "Frame";
-
393 frame.isExtended = extended;
-
394 SignalInfo signal;
-
395 signal.name = "State";
-
396 frame.signals.push_back(signal);
-
397 if (!duplicateFrames) frame.signals.push_back(signal);
-
398 database.frames.push_back(frame);
-
399 if (duplicateFrames) database.frames.push_back(frame);
-
400
-
401 // ParseFile rejects duplicate definitions earlier. Construct the database
-
402 // through the public metadata API to exercise the resolver's own checks.
-
403 DbcParseContext context{database, nullptr};
-
404 context.lineNumber = 37U;
-
405 const std::string rawId = extended ? "2147483649" : "1";
-
406 DbcValueDescriptionHandler().Handle("VAL_ " + rawId + " State 0 \"Off\";", context);
-
407 QCOMPARE(context.pendingValues.size(), std::size_t(1));
-
408 QCOMPARE(context.pendingValues[0].lineNumber, std::size_t(37));
-
409 const std::string path = "manual database.dbc";
-
410 try {
-
411 context.ResolveValueDescriptions(path);
-
412 QFAIL("The resolver must reject an ambiguous target");
-
413 } catch (const std::runtime_error &error) {
-
414 const std::string message = duplicateFrames ? "Ambiguous VAL_ frame reference."
-
415 : "Ambiguous VAL_ signal reference: State";
-
416 QCOMPARE(std::string(error.what()), path + ":37: " + message);
-
417 }
-
418 // Ambiguity must be detected before applying any descriptions.
-
419 for (const FrameInfo &candidate: database.frames)
-
420 for (const SignalInfo &candidateSignal: candidate.signals)
-
421 QVERIFY(candidateSignal.valueDescriptions.empty());
-
422}
-
423
-
424void CoverageTest::valueReferenceScope() {
-
425 DbcDatabase database;
-
426 FrameInfo frame;
-
427 frame.canId = 1U;
-
428 SignalInfo signal;
-
429 signal.name = "State";
-
430 frame.signals.push_back(signal);
-
431 database.frames.push_back(frame);
-
432 frame.isExtended = true;
-
433 database.frames.push_back(frame);
-
434 frame.canId = 2U;
-
435 frame.isExtended = false;
-
436 database.frames.push_back(frame);
-
437
-
438 DbcParseContext context{database, nullptr};
-
439 const DbcValueDescriptionHandler handler;
-
440 context.lineNumber = 3U;
-
441 handler.Handle("VAL_ 1 State 0 \"Standard\";", context);
-
442 context.lineNumber = 7U;
-
443 handler.Handle("VAL_ 2147483649 State 0 \"Extended\";", context);
-
444 context.ResolveValueDescriptions("manual database.dbc");
-
445
-
446 const std::string *standard = FindValueDescription(database.frames[0].signals[0].valueDescriptions, std::uint64_t(0));
-
447 const std::string *extended = FindValueDescription(database.frames[1].signals[0].valueDescriptions, std::uint64_t(0));
-
448 QVERIFY(standard != nullptr);
-
449 QVERIFY(extended != nullptr);
-
450 QCOMPARE(*standard, std::string("Standard"));
-
451 QCOMPARE(*extended, std::string("Extended"));
-
452 QVERIFY(database.frames[2].signals[0].valueDescriptions.empty());
-
453}
-
454
-
455void CoverageTest::fileFormatting_data() {
-
456 QTest::addColumn<QString>("text");
-
457 QTest::addColumn<unsigned>("frames");
-
458 QTest::newRow("empty") << QString::fromUtf8("") << 0U;
-
459 QTest::newRow("whitespace") << QString::fromUtf8(" \t\r\n\n\t") << 0U;
-
460 QTest::newRow("unknown-only") << QString::fromUtf8("VERSION \"test\"\nBU_: ECU\nUNKNOWN_ data\n") << 0U;
-
461 QTest::newRow("no-final-newline") << QString::fromUtf8("BO_ 1 Frame: 1 ECU\nSG_ State : 0|8@1+ (1,0) [0|255] \"\" ECU") << 1U;
-
462 QTest::newRow("crlf") << QString::fromUtf8("\r\nBO_ 1 Frame: 1 ECU\r\nSG_ State : 0|8@1+ (1,0) [0|255] \"\" ECU\r\nCM_ BO_ 1 \"Comment\";\r\nVAL_ 1 State 0 \"Off\";\r\n") << 1U;
-
463}
-
464
-
465void CoverageTest::fileFormatting() {
-
466 QFETCH(QString, text);
-
467 QFETCH(unsigned, frames);
-
468 const DbcDatabase parsed = ParseText(text.toStdString());
-
469 QCOMPARE(parsed.frames.size(), std::size_t(frames));
-
470 if (frames) {
-
471 QCOMPARE(parsed.frames[0].signals.size(), std::size_t(1));
-
472 if (text.contains("\r\nBO_")) {
-
473 QCOMPARE(parsed.frames[0].comment, std::string("Comment"));
-
474 const std::string *description = FindValueDescription(parsed.frames[0].signals[0].valueDescriptions, std::uint64_t(0));
-
475 QVERIFY(description != nullptr);
-
476 QCOMPARE(*description, std::string("Off"));
-
477 }
-
478 }
-
479}
-
480
-
481void CoverageTest::missingFile() {
-
482 QTemporaryFile file;
-
483 QVERIFY(file.open());
-
484 const QString path = file.fileName();
-
485 QVERIFY(file.remove());
-
486 try {
-
487 DbcParser().ParseFile(path.toStdString());
-
488 QFAIL("Missing file must throw");
-
489 } catch (const std::runtime_error &error) {
-
490 QCOMPARE(std::string(error.what()), "Failed to open DBC file: " + path.toStdString());
-
491 }
-
492}
-
493
-
494void CoverageTest::builders() {
-
495 const DecodeDatabase emptyRuntime = DbcDecodeBuilder().Build(DbcDatabase());
-
496 QVERIFY(emptyRuntime.GetFrames().empty());
-
497 QVERIFY(!emptyRuntime.FindFrame(FrameKey(0U, false)));
-
498 const std::unique_ptr<TreeNode> emptyTree = DbcTreeBuilder().Build(DbcDatabase());
-
499 QCOMPARE(emptyTree->GetType(), NodeType::Root);
-
500 QCOMPARE(emptyTree->GetName(), std::string("dbc"));
-
501 QCOMPARE(emptyTree->GetChildCount(), std::size_t(0));
-
502 QVERIFY(emptyTree->GetFrame() == nullptr && emptyTree->GetSignal() == nullptr);
-
503 const DbcDatabase expected = ParseText(
-
504 "BO_ 256 Standard: 3 ECU1\n"
-
505 "SG_ Mode M : 0|8@1+ (2,3) [0|513] \"mode\" ECU1,ECU2\n"
-
506 "SG_ Branch m1 : 15|8@0- (-2,5) [-249|261] \"rpm\" ECU2,ECU3\n"
-
507 "SG_ Always : 16|8@1+ (0.25,-1) [-1|62.75] \"V\" ECU3\n"
-
508 "CM_ BO_ 256 \"Frame comment\";\nCM_ SG_ 256 Branch \"Branch comment\";\n"
-
509 "VAL_ 256 Branch -1 \"Negative\" 1 \"Positive\";\n"
-
510 "BO_ 2163212866 Extended: 8 ECU4\n");
-
511 DecodeDatabase runtime;
-
512 std::unique_ptr<TreeNode> tree;
-
513 {
-
514 DbcDatabase source = expected;
-
515 runtime = DbcDecodeBuilder().Build(source);
-
516 tree = DbcTreeBuilder().Build(source);
-
517 source.frames[0].name = "Changed";
-
518 source.frames[0].signals[1].receivers.clear();
-
519 source.frames[0].signals[1].valueDescriptions.clear();
-
520 source.frames.clear();
-
521 }
-
522 QCOMPARE(runtime.GetFrames().size(), expected.frames.size());
-
523 QCOMPARE(tree->GetChildCount(), expected.frames.size());
-
524 for (std::size_t i = 0; i < expected.frames.size(); ++i) {
-
525 const FrameInfo &frame = expected.frames[i];
-
526 const DecodeFrame &decodedFrame = runtime.GetFrames()[i];
-
527 const TreeNode *node = tree->GetChild(i);
-
528 QVERIFY(node && node->GetFrame());
-
529 QCOMPARE(node->GetType(), NodeType::Frame);
-
530 QCOMPARE(node->GetName(), frame.name);
-
531 QVERIFY(node->GetSignal() == nullptr);
-
532 QCOMPARE(runtime.FindFrame(FrameKey(frame.canId, frame.isExtended)).get(), &decodedFrame);
-
533 const FrameInfo &copied = *node->GetFrame();
-
534 QCOMPARE(copied.name, frame.name);
-
535 QCOMPARE(decodedFrame.name, frame.name);
-
536 QCOMPARE(copied.canId, frame.canId);
-
537 QCOMPARE(decodedFrame.canId, frame.canId);
-
538 QCOMPARE(copied.isExtended, frame.isExtended);
-
539 QCOMPARE(decodedFrame.isExtended, frame.isExtended);
-
540 QCOMPARE(copied.dlc, frame.dlc);
-
541 QCOMPARE(decodedFrame.dlc, frame.dlc);
-
542 QCOMPARE(copied.pgn, frame.pgn);
-
543 QCOMPARE(decodedFrame.pgn, frame.pgn);
-
544 QCOMPARE(copied.hasPgn, frame.hasPgn);
-
545 QCOMPARE(decodedFrame.hasPgn, frame.hasPgn);
-
546 QCOMPARE(copied.transmitter, frame.transmitter);
-
547 QCOMPARE(decodedFrame.transmitter, frame.transmitter);
-
548 QCOMPARE(copied.comment, frame.comment);
-
549 QCOMPARE(decodedFrame.comment, frame.comment);
-
550 QCOMPARE(node->GetChildCount(), frame.signals.size());
-
551 QCOMPARE(copied.signals.size(), frame.signals.size());
-
552 QCOMPARE(decodedFrame.signals.size(), frame.signals.size());
-
553 for (std::size_t j = 0; j < frame.signals.size(); ++j) {
-
554 const SignalInfo &original = frame.signals[j];
-
555 const DecodeSignal &target = decodedFrame.signals[j];
-
556 const TreeNode *leaf = node->GetChild(j);
-
557 QVERIFY(leaf && leaf->GetSignal());
-
558 QCOMPARE(leaf->GetType(), NodeType::Signal);
-
559 QCOMPARE(leaf->GetName(), original.name);
-
560 QVERIFY(leaf->GetFrame() == nullptr);
-
561 // Check both the frame payload copy and each child payload copy.
-
562 for (const SignalInfo *copy: {&copied.signals[j], leaf->GetSignal()}) {
-
563 QCOMPARE(copy->name, original.name);
-
564 QCOMPARE(copy->startBit, original.startBit);
-
565 QCOMPARE(copy->length, original.length);
-
566 QCOMPARE(copy->isLittleEndian, original.isLittleEndian);
-
567 QCOMPARE(copy->isSigned, original.isSigned);
-
568 QCOMPARE(copy->factor, original.factor);
-
569 QCOMPARE(copy->offset, original.offset);
-
570 QCOMPARE(copy->minimum, original.minimum);
-
571 QCOMPARE(copy->maximum, original.maximum);
-
572 QCOMPARE(copy->unit, original.unit);
-
573 QCOMPARE(copy->comment, original.comment);
-
574 QVERIFY(copy->receivers == original.receivers);
-
575 QVERIFY(copy->valueDescriptions == original.valueDescriptions);
-
576 QCOMPARE(copy->multiplexing.role, original.multiplexing.role);
-
577 QCOMPARE(copy->multiplexing.value, original.multiplexing.value);
-
578 }
-
579 QCOMPARE(target.name, original.name);
-
580 QCOMPARE(target.startBit, original.startBit);
-
581 QCOMPARE(target.length, original.length);
-
582 QCOMPARE(target.byteOrder, original.isLittleEndian ? ByteOrder::Intel : ByteOrder::Motorola);
-
583 QCOMPARE(target.valueType, original.isSigned ? ValueType::Signed : ValueType::Unsigned);
-
584 QCOMPARE(target.factor, original.factor);
-
585 QCOMPARE(target.offset, original.offset);
-
586 QCOMPARE(target.minimum, original.minimum);
-
587 QCOMPARE(target.maximum, original.maximum);
-
588 QCOMPARE(target.unit, original.unit);
-
589 QCOMPARE(target.comment, original.comment);
-
590 QVERIFY(target.receivers == original.receivers);
-
591 QVERIFY(target.valueDescriptions == original.valueDescriptions);
-
592 QCOMPARE(target.multiplexing.role, original.multiplexing.role);
-
593 QCOMPARE(target.multiplexing.value, original.multiplexing.value);
-
594 }
-
595 }
-
596}
-
597
-
598void CoverageTest::physicalConversion_data() {
-
599 QTest::addColumn<double>("factor");
-
600 QTest::addColumn<double>("offset");
-
601 QTest::addColumn<double>("expected");
-
602 QTest::newRow("zero-factor") << 0.0 << 7.0 << 7.0;
-
603 QTest::newRow("negative-factor") << -2.0 << 5.0 << -15.0;
-
604 QTest::newRow("above-declared-range") << 2.0 << 3.0 << 23.0;
-
605}
-
606
-
607void CoverageTest::physicalConversion() {
-
608 QFETCH(double, factor);
-
609 QFETCH(double, offset);
-
610 QFETCH(double, expected);
-
611 DecodeSignal signal;
-
612 signal.length = 8U;
-
613 signal.factor = factor;
-
614 signal.offset = offset;
-
615 signal.minimum = 0.0;
-
616 signal.maximum = 1.0;
-
617 const DecodeDatabase database = SingleSignal(signal);
-
618 RawCanFrame raw;
-
619 raw.canId = 17U;
-
620 raw.data = {10U};
-
621 const DecodedFrameValue decoded = DbcDecoder().Decode(database, raw);
-
622 QVERIFY(decoded.signals[0].valid);
-
623 QCOMPARE(std::get<std::uint64_t>(decoded.signals[0].rawValue), std::uint64_t(10));
-
624 QCOMPARE(decoded.signals[0].physicalValue, expected);
-
625}
-
626
-
627void CoverageTest::multiplexingScenarios() {
-
628 const DecodeDatabase selectorOnly = DbcDecodeBuilder().Build(ParseText(
-
629 "BO_ 1 Frame: 1 ECU\nSG_ Mode M : 0|1@1+ (1,0) [0|1] \"\" ECU\n"));
-
630 RawCanFrame raw;
-
631 raw.canId = 1U;
-
632 raw.data = {1U};
-
633 const DecodedFrameValue only = DbcDecoder().Decode(selectorOnly, raw);
-
634 QCOMPARE(only.signals.size(), std::size_t(1));
-
635 QVERIFY(only.signals[0].valid);
-
636 const DecodeDatabase database = DbcDecodeBuilder().Build(ParseText(
-
637 "BO_ 1 First: 3 ECU\n"
-
638 "SG_ Mode M : 0|1@1+ (1,0) [0|1] \"\" ECU\n"
-
639 "SG_ A m0 : 8|8@1+ (1,0) [0|255] \"\" ECU\n"
-
640 "SG_ B m0 : 16|8@1+ (1,0) [0|255] \"\" ECU\n"
-
641 "BO_ 2 Second: 2 ECU\n"
-
642 "SG_ Mode M : 0|1@1+ (1,0) [0|1] \"\" ECU\n"
-
643 "SG_ A m1 : 8|8@1+ (1,0) [0|255] \"\" ECU\n"));
-
644 raw.data = {0U, 42U, 99U};
-
645 const DecodedFrameValue first = DbcDecoder().Decode(database, raw);
-
646 QVERIFY(first.signals[1].valid && first.signals[2].valid);
-
647 QCOMPARE(std::get<std::uint64_t>(first.signals[1].rawValue), std::uint64_t(42));
-
648 QCOMPARE(std::get<std::uint64_t>(first.signals[2].rawValue), std::uint64_t(99));
-
649 raw.canId = 2U;
-
650 const DecodedFrameValue secondInactive = DbcDecoder().Decode(database, raw);
-
651 QCOMPARE(secondInactive.signals[1].status, SignalDecodeStatus::Inactive);
-
652 raw.data[0] = 1U;
-
653 QVERIFY(DbcDecoder().Decode(database, raw).signals[1].valid);
-
654 raw.canId = 1U;
-
655 const DecodedFrameValue firstInactive = DbcDecoder().Decode(database, raw);
-
656 QCOMPARE(firstInactive.signals[1].status, SignalDecodeStatus::Inactive);
-
657 QCOMPARE(firstInactive.signals[2].status, SignalDecodeStatus::Inactive);
-
658}
-
659
-
660void CoverageTest::multiplexingErrorLocations_data() {
-
661 QTest::addColumn<QString>("text");
-
662 QTest::addColumn<unsigned>("line");
-
663 QTest::addColumn<QString>("message");
-
664 const QString header = "BO_ 1 Frame: 3 ECU\n\n";
-
665 const QString selector = "SG_ Mode M : 0|1@1+ (1,0) [0|1] \"\" ECU\n";
-
666 const QString branch = "SG_ A m1 : 8|8@1+ (1,0) [0|255] \"\" ECU\n";
-
667 QTest::newRow("missing-selector") << header + branch << 3U << QString::fromUtf8("Multiplexed branch has no selector.");
-
668 QTest::newRow("second-selector") << header + selector + "\nSG_ Other M : 16|1@1+ (1,0) [0|1] \"\" ECU\n"
-
669 << 5U << QString::fromUtf8("Multiplexing requires one unsigned selector per frame.");
-
670 QTest::newRow("signed-selector") << header + "SG_ Mode M : 0|1@1- (1,0) [-1|0] \"\" ECU\n"
-
671 << 3U << QString::fromUtf8("Multiplexing requires one unsigned selector per frame.");
-
672 QTest::newRow("branch-range") << header + "SG_ A m2 : 8|8@1+ (1,0) [0|255] \"\" ECU\n" + selector << 3U << QString::fromUtf8("Branch value exceeds selector raw range.");
-
673 QTest::newRow("same-branch-overlap") << header + selector + branch + "SG_ B m1 : 15|8@0+ (1,0) [0|255] \"\" ECU\n"
-
674 << 5U << QString::fromUtf8("Signals overlap without mutually exclusive multiplexing conditions.");
-
675 QTest::newRow("selector-overlap") << header + selector + "SG_ A m1 : 0|1@1+ (1,0) [0|1] \"\" ECU\n"
-
676 << 4U << QString::fromUtf8("Signals overlap without mutually exclusive multiplexing conditions.");
-
677 QTest::newRow("unconditional-overlap") << header + selector + branch + "SG_ Always : 8|8@1+ (1,0) [0|255] \"\" ECU\n"
-
678 << 5U << QString::fromUtf8("Signals overlap without mutually exclusive multiplexing conditions.");
-
679 QTest::newRow("ordinary-overlap") << header + "SG_ A : 8|8@1+ (1,0) [0|255] \"\" ECU\nSG_ B : 15|8@0+ (1,0) [0|255] \"\" ECU\n"
-
680 << 4U << QString::fromUtf8("Signals overlap without mutually exclusive multiplexing conditions.");
-
681 QTest::newRow("extended-multiplexing") << header + "SG_MUL_VAL_ 1 A Mode 1-2;\n"
-
682 << 3U << QString::fromUtf8("Extended and nested multiplexing are unsupported.");
-
683 QTest::newRow("nested-marker") << header + "SG_ A m1M : 8|8@1+ (1,0) [0|255] \"\" ECU\n"
-
684 << 3U << QString::fromUtf8("Invalid or unsupported multiplexing marker.");
-
685}
-
686
-
687void CoverageTest::multiplexingErrorLocations() {
-
688 QFETCH(QString, text);
-
689 QFETCH(unsigned, line);
-
690 QFETCH(QString, message);
-
691 QTemporaryFile file;
-
692 QVERIFY(file.open());
-
693 const QByteArray content = text.toUtf8();
-
694 QCOMPARE(file.write(content), qint64(content.size()));
-
695 QVERIFY(file.flush());
-
696 try {
-
697 DbcParser().ParseFile(file.fileName().toStdString());
-
698 QFAIL("Expected multiplexing rejection");
-
699 } catch (const std::runtime_error &error) {
-
700 QCOMPARE(QString::fromStdString(error.what()), file.fileName() + ":" + QString::number(line) + ": " + message);
-
701 }
-
702}
-
703
-
704void CoverageTest::escapedValueDescriptions() {
-
705 const DbcDatabase parsed = ParseText(
-
706 "BO_ 1 Frame: 1 ECU\nSG_ State : 0|8@1+ (1,0) [0|255] \"\" ECU\n"
-
707 R"dbc(VAL_ 1 State -0 "Ноль" 1 "C:\\trace" 2 "\\\"quoted\"\\";)dbc"
-
708 "\n");
-
709 const ValueDescriptions &values = parsed.frames[0].signals[0].valueDescriptions;
-
710 QCOMPARE(values.size(), std::size_t(3));
-
711 QVERIFY(values.find(RawSignalValue(std::int64_t(0))) == values.end());
-
712 const std::string *zero = FindValueDescription(values, std::int64_t(0));
-
713 const std::string *path = FindValueDescription(values, std::uint64_t(1));
-
714 const std::string *quoted = FindValueDescription(values, std::uint64_t(2));
-
715 QVERIFY(zero && path && quoted);
-
716 QCOMPARE(*zero, std::string("Ноль"));
-
717 QCOMPARE(*path, std::string("C:\\trace"));
-
718 QCOMPARE(*quoted, std::string("\\\"quoted\"\\"));
-
719 QVERIFY(NormalizeValueKey(RawSignalValue(std::int64_t(0))) == RawSignalValue(std::uint64_t(0)));
-
720 QVERIFY(NormalizeValueKey(RawSignalValue(std::int64_t(-1))) == RawSignalValue(std::int64_t(-1)));
-
721}
-
722
-
723void CoverageTest::factory() {
- -
725 QVERIFY(!factory.contains("value"));
-
726 QVERIFY_THROWS_EXCEPTION(std::out_of_range, static_cast<void>(factory.creator("missing")));
-
727 QVERIFY_THROWS_EXCEPTION(std::out_of_range, static_cast<void>(factory.create("missing", std::make_unique<int>(1))));
-
728 factory.registerType<FactoryFirst>("value");
-
729 QVERIFY(factory.contains("value"));
-
730 std::unique_ptr<int> argument = std::make_unique<int>(42);
-
731 const std::shared_ptr<FactoryBase> first = factory.create("value", std::move(argument));
-
732 QVERIFY(argument == nullptr);
-
733 QCOMPARE(first->Value(), 42);
-
734 const TypeFactory<std::string, FactoryBase, std::unique_ptr<int>>::CreatorFn creator = factory.creator("value");
-
735 QCOMPARE(creator(std::make_unique<int>(7))->Value(), 7);
-
736 factory.registerType<FactorySecond>("value");
-
737 QCOMPARE(factory.create("value", std::make_unique<int>(42))->Value(), 43);
-
738 QCOMPARE(first->Value(), 42);
-
739 QCOMPARE(creator(std::make_unique<int>(7))->Value(), 7);
-
740}
-
741
-
742int RunCoverageTests(int argc, char **argv) {
-
743 CoverageTest tests;
-
744 return QTest::qExec(&tests, argc, argv);
-
745}
-
746#include "dbc_coverage_test.moc"
+
26 frame.dlc = 16U;
+
27 frame.signals.push_back(signal);
+
28 return DecodeDatabase({frame});
+
29 }
+
30
+
31 // Independent reference: collect the visited bits first, then weight them.
+
32 // Motorola uses a linear MSB-first coordinate instead of sawtooth traversal.
+
33 std::uint64_t ReferenceExtract(const std::vector<std::uint8_t> &data,
+
34 ByteOrder order, unsigned start, unsigned length) {
+
35 std::uint64_t expected = 0;
+
36 for (unsigned i = 0; i < length; ++i) {
+
37 const unsigned coordinate = order == ByteOrder::Intel
+
38 ? start + i
+
39 : (start / 8U) * 8U + (7U - start % 8U) + i;
+
40 const unsigned bit = order == ByteOrder::Intel ? coordinate % 8U : 7U - coordinate % 8U;
+
41 const unsigned weight = order == ByteOrder::Intel ? i : length - 1U - i;
+
42 expected |= std::uint64_t((data.at(coordinate / 8U) >> bit) & 1U) << weight;
+
43 }
+
44 return expected;
+
45 }
+
46
+
47 struct FactoryBase {
+
48 virtual ~FactoryBase() = default;
+
49 virtual int Value() const = 0;
+
50 };
+
51 struct FactoryFirst : FactoryBase {
+
52 explicit FactoryFirst(std::unique_ptr<int> value) : value(std::move(value)) {}
+
53 int Value() const override { return *value; }
+
54 std::unique_ptr<int> value;
+
55 };
+
56 struct FactorySecond : FactoryFirst {
+
57 using FactoryFirst::FactoryFirst;
+
58 int Value() const override { return *value + 1; }
+
59 };
+
60}// namespace
+
61
+
62class CoverageTest : public QObject {
+
63 Q_OBJECT
+
64private Q_SLOTS:
+
65 void frameLookup();
+
66 void signedBoundaries_data();
+
67 void signedBoundaries();
+
68 void extraction_data();
+
69 void extraction();
+
70 void invalidSignalStatus_data();
+
71 void invalidSignalStatus();
+
72 void malformedRuntimeMultiplexing_data();
+
73 void malformedRuntimeMultiplexing();
+
74 void pgn_data();
+
75 void pgn();
+
76 void comments_data();
+
77 void comments();
+
78 void ignoredCommentStatements_data();
+
79 void ignoredCommentStatements();
+
80 void ambiguousValueReferences_data();
+
81 void ambiguousValueReferences();
+
82 void valueReferenceScope();
+
83 void fileFormatting_data();
+
84 void fileFormatting();
+
85 void missingFile();
+
86 void builders();
+
87 void physicalConversion_data();
+
88 void physicalConversion();
+
89 void multiplexingScenarios();
+
90 void multiplexingErrorLocations_data();
+
91 void multiplexingErrorLocations();
+
92 void escapedValueDescriptions();
+
93 void factory();
+
94};
+
95
+
96void CoverageTest::frameLookup() {
+
97 DbcDecoder decoder;
+
98 RawCanFrame raw;
+
99 raw.canId = 17U;
+
100 DecodeDatabase empty;
+
101 QVERIFY(decoder.FindFrame(empty, 17U, false) == nullptr);
+
102 const DecodedFrameValue absent = decoder.Decode(empty, raw);
+
103 QVERIFY(!absent.valid);
+
104 QVERIFY(absent.definition == nullptr);
+
105 QVERIFY(absent.signals.empty());
+
106 DecodeSignal signal;
+
107 DecodeDatabase database = SingleSignal(signal);
+
108 QVERIFY(decoder.FindFrame(database, 18U, false) == nullptr);
+
109 QVERIFY(decoder.FindFrame(database, 17U, true) == nullptr);
+
110 raw.isExtended = true;
+
111 QVERIFY(!decoder.Decode(database, raw).valid);
+
112 raw.isExtended = false;
+
113 raw.canId = 18U;
+
114 QVERIFY(!decoder.Decode(database, raw).valid);
+
115 raw.canId = 17U;
+
116 DecodeFrame noSignals;
+
117 noSignals.canId = 17U;
+
118 database = DecodeDatabase({noSignals});
+
119 const DecodedFrameValue matched = decoder.Decode(database, raw);
+
120 QVERIFY(matched.valid);
+
121 QCOMPARE(matched.definition.get(), &database.GetFrames()[0]);
+
122 QVERIFY(matched.signals.empty());
+
123}
+
124
+
125void CoverageTest::signedBoundaries_data() {
+
126 QTest::addColumn<int>("order");
+
127 QTest::addColumn<unsigned>("length");
+
128 QTest::addColumn<qlonglong>("expected");
+
129 for (int order = 0; order < 2; ++order) {
+
130 for (unsigned length: {1U, 2U, 7U, 8U, 31U, 32U, 63U}) {
+
131 const std::int64_t half = std::int64_t(1) << (length - 1U);
+
132 const std::array<std::int64_t, 4> values{{0, -1, -half, half - 1}};
+
133 for (unsigned index = 0; index < values.size(); ++index) {
+
134 const QByteArray name = QByteArray::number(order) + "-" + QByteArray::number(length) + "-" + QByteArray::number(index);
+
135 QTest::newRow(name.constData()) << order << length << qlonglong(values[index]);
+
136 }
+
137 }
+
138 }
+
139}
+
140
+
141void CoverageTest::signedBoundaries() {
+
142 QFETCH(int, order);
+
143 QFETCH(unsigned, length);
+
144 QFETCH(qlonglong, expected);
+
145 DecodeSignal signal;
+
146 signal.byteOrder = order == 0 ? ByteOrder::Intel : ByteOrder::Motorola;
+
147 signal.startBit = order == 0 ? 0U : 7U;
+
148 signal.length = length;
+
149 signal.valueType = ValueType::Signed;
+
150 RawCanFrame raw;
+
151 raw.canId = 17U;
+
152 raw.data.assign(8U, 0U);
+
153 const std::uint64_t bits = static_cast<std::uint64_t>(expected);
+
154 for (unsigned i = 0; i < length; ++i) {
+
155 const unsigned weight = order == 0 ? i : length - 1U - i;
+
156 const unsigned position = order == 0 ? i % 8U : 7U - i % 8U;
+
157 raw.data[i / 8U] |= static_cast<std::uint8_t>(((bits >> weight) & 1U) << position);
+
158 }
+
159 const DecodeDatabase database = SingleSignal(signal);
+
160 const DecodedFrameValue result = DbcDecoder().Decode(database, raw);
+
161 QVERIFY(result.signals[0].valid);
+
162 QVERIFY(std::holds_alternative<std::int64_t>(result.signals[0].rawValue));
+
163 QCOMPARE(std::get<std::int64_t>(result.signals[0].rawValue), static_cast<std::int64_t>(expected));
+
164}
+
165
+
166void CoverageTest::extraction_data() {
+
167 QTest::addColumn<int>("order");
+
168 QTest::addColumn<unsigned>("start");
+
169 QTest::addColumn<unsigned>("length");
+
170 for (int order = 0; order < 2; ++order)
+
171 for (unsigned start = 0; start < 16U; ++start)
+
172 for (unsigned length = 1; length <= 64U; ++length) {
+
173 const QByteArray name = QByteArray::number(order) + "-" + QByteArray::number(start) + "-" + QByteArray::number(length);
+
174 QTest::newRow(name.constData()) << order << start << length;
+
175 }
+
176}
+
177
+
178void CoverageTest::extraction() {
+
179 QFETCH(int, order);
+
180 QFETCH(unsigned, start);
+
181 QFETCH(unsigned, length);
+
182 DecodeSignal signal;
+
183 signal.byteOrder = order == 0 ? ByteOrder::Intel : ByteOrder::Motorola;
+
184 signal.startBit = start;
+
185 signal.length = length;
+
186 const DecodeDatabase database = SingleSignal(signal);
+
187 RawCanFrame raw;
+
188 raw.canId = 17U;
+
189 const std::vector<std::vector<std::uint8_t>> patterns = {
+
190 std::vector<std::uint8_t>(10U, 0U),
+
191 std::vector<std::uint8_t>(10U, 255U),
+
192 {0xA5, 0x3C, 0x81, 0x7E, 0x12, 0xD4, 0x69, 0xB0, 0x5A, 0xC3}};
+
193 for (const std::vector<std::uint8_t> &data: patterns) {
+
194 raw.data = data;
+
195 const unsigned firstCoordinate = order == 0 ? start : (start / 8U) * 8U + 7U - start % 8U;
+
196 raw.data.resize((firstCoordinate + length + 7U) / 8U);
+
197 const DecodedFrameValue decoded = DbcDecoder().Decode(database, raw);
+
198 QVERIFY(decoded.signals[0].valid);
+
199 QCOMPARE(std::get<std::uint64_t>(decoded.signals[0].rawValue), ReferenceExtract(data, signal.byteOrder, start, length));
+
200 }
+
201}
+
202
+
203void CoverageTest::invalidSignalStatus_data() {
+
204 QTest::addColumn<int>("order");
+
205 QTest::addColumn<unsigned>("start");
+
206 QTest::addColumn<unsigned>("length");
+
207 QTest::addColumn<unsigned>("bytes");
+
208 QTest::addColumn<int>("status");
+
209 for (int order = 0; order < 2; ++order) {
+
210 const unsigned start = order == 0 ? 0U : 7U;
+
211 const QByteArray prefix = QByteArray::number(order);
+
212 QTest::newRow((prefix + "-zero").constData()) << order << start << 0U << 1U << int(SignalDecodeStatus::InvalidDefinition);
+
213 QTest::newRow((prefix + "-65").constData()) << order << start << 65U << 9U << int(SignalDecodeStatus::InvalidDefinition);
+
214 QTest::newRow((prefix + "-empty").constData()) << order << start << 8U << 0U << int(SignalDecodeStatus::InsufficientPayload);
+
215 QTest::newRow((prefix + "-short").constData()) << order << start << 16U << 1U << int(SignalDecodeStatus::InsufficientPayload);
+
216 QTest::newRow((prefix + "-large-start").constData()) << order << std::numeric_limits<unsigned>::max() << 8U << 1U << int(SignalDecodeStatus::InvalidDefinition);
+
217 }
+
218}
+
219
+
220void CoverageTest::invalidSignalStatus() {
+
221 QFETCH(int, order);
+
222 QFETCH(unsigned, start);
+
223 QFETCH(unsigned, length);
+
224 QFETCH(unsigned, bytes);
+
225 QFETCH(int, status);
+
226 DecodeSignal signal;
+
227 signal.startBit = start;
+
228 signal.length = length;
+
229 signal.byteOrder = order == 0 ? ByteOrder::Intel : ByteOrder::Motorola;
+
230 const DecodeDatabase database = SingleSignal(signal);
+
231 RawCanFrame raw;
+
232 raw.canId = 17U;
+
233 raw.data.assign(bytes, 255U);
+
234 const DecodedFrameValue decoded = DbcDecoder().Decode(database, raw);
+
235 QVERIFY(decoded.valid);
+
236 QVERIFY(!decoded.signals[0].valid);
+
237 QCOMPARE(int(decoded.signals[0].status), status);
+
238 QVERIFY(decoded.signals[0].GetValueDescription() == nullptr);
+
239}
+
240
+
241void CoverageTest::malformedRuntimeMultiplexing_data() {
+
242 QTest::addColumn<int>("kind");
+
243 QTest::newRow("two-selectors") << 0;
+
244 QTest::newRow("signed-selector") << 1;
+
245 QTest::newRow("65-bit-selector") << 2;
+
246 QTest::newRow("branch-out-of-range") << 3;
+
247}
+
248
+
249void CoverageTest::malformedRuntimeMultiplexing() {
+
250 QFETCH(int, kind);
+
251 DecodeSignal selector;
+
252 selector.length = 1U;
+
253 selector.multiplexing.role = MultiplexRole::Selector;
+
254 selector.name = "Selector";
+
255 std::vector<DecodeFrame> definitions = SingleSignal(selector).GetFrames();
+
256 DecodeSignal branch;
+
257 branch.name = "Branch";
+
258 branch.startBit = 8U;
+
259 branch.length = 8U;
+
260 branch.multiplexing.role = MultiplexRole::Branch;
+
261 branch.multiplexing.value = 1U;
+
262 definitions[0].signals.push_back(branch);
+
263 DecodeSignal ordinary;
+
264 ordinary.name = "Ordinary";
+
265 ordinary.startBit = 16U;
+
266 ordinary.length = 8U;
+
267 definitions[0].signals.push_back(ordinary);
+
268 if (kind == 0) {
+
269 selector.name = "SecondSelector";
+
270 definitions[0].signals.push_back(selector);
+
271 }
+
272 if (kind == 1) definitions[0].signals[0].valueType = ValueType::Signed;
+
273 if (kind == 2) definitions[0].signals[0].length = 65U;
+
274 if (kind == 3) definitions[0].signals[1].multiplexing.value = 2U;
+
275 const DecodeDatabase database(std::move(definitions));
+
276 RawCanFrame raw;
+
277 raw.canId = 17U;
+
278 raw.data = {1U, 42U, 99U};
+
279 const DecodedFrameValue result = DbcDecoder().Decode(database, raw);
+
280 QCOMPARE(int(result.signals[1].status), int(SignalDecodeStatus::InvalidDefinition));
+
281 QVERIFY(!result.signals[1].valid);
+
282 QVERIFY(result.signals[2].valid);
+
283 QCOMPARE(std::get<std::uint64_t>(result.signals[2].rawValue), std::uint64_t(99));
+
284 if (kind != 3) {
+
285 QCOMPARE(int(result.signals[0].status), int(SignalDecodeStatus::InvalidDefinition));
+
286 if (kind == 0) QCOMPARE(int(result.signals[3].status), int(SignalDecodeStatus::InvalidDefinition));
+
287 }
+
288}
+
289
+
290void CoverageTest::pgn_data() {
+
291 QTest::addColumn<unsigned>("id");
+
292 QTest::addColumn<unsigned>("expected");
+
293 QTest::addColumn<bool>("hasPgn");
+
294 QTest::newRow("standard") << 0x7FFU << 0U << false;
+
295 for (unsigned dp: {0U, 1U})
+
296 for (unsigned pf: {239U, 240U})
+
297 for (unsigned ps: {0U, 0xABU}) {
+
298 const unsigned id = 0x80000000U | (dp << 24U) | (pf << 16U) | (ps << 8U) | 0x42U;
+
299 const unsigned expected = (dp << 16U) | (pf == 239U ? 0xEF00U : 0xF000U | ps);
+
300 const QByteArray name = QByteArray::number(dp) + "-" + QByteArray::number(pf) + "-" + QByteArray::number(ps);
+
301 QTest::newRow(name.constData()) << id << expected << true;
+
302 }
+
303}
+
304
+
305void CoverageTest::pgn() {
+
306 QFETCH(unsigned, id);
+
307 QFETCH(unsigned, expected);
+
308 QFETCH(bool, hasPgn);
+
309 const DbcDatabase parsed = ParseText("BO_ " + std::to_string(id) + " Frame: 8 ECU\n");
+
310 QCOMPARE(parsed.frames[0].hasPgn, hasPgn);
+
311 QCOMPARE(parsed.frames[0].pgn, expected);
+
312}
+
313
+
314void CoverageTest::comments_data() {
+
315 QTest::addColumn<QString>("before");
+
316 QTest::addColumn<QString>("after");
+
317 QTest::addColumn<QString>("frameComment");
+
318 QTest::addColumn<QString>("signalComment");
+
319 QTest::newRow("tabs") << QString::fromUtf8("") << QString::fromUtf8("CM_\tBO_ 1 \"Frame\";\nCM_\tSG_ 1 State \"Signal\";\n") << QString::fromUtf8("Frame") << QString::fromUtf8("Signal");
+
320 QTest::newRow("empty-overwrite") << QString::fromUtf8("") << QString::fromUtf8("CM_ BO_ 1 \"Old\";\nCM_ BO_ 1 \"\";\nCM_ SG_ 1 State \"Old\";\nCM_ SG_ 1 State \"\";\n") << QString::fromUtf8("") << QString::fromUtf8("");
+
321 QTest::newRow("last-wins") << QString::fromUtf8("") << QString::fromUtf8("CM_ BO_ 1 \"First\";\nCM_ BO_ 1 \"Last\";\nCM_ SG_ 1 State \"First\";\nCM_ SG_ 1 State \"Last\";\n") << QString::fromUtf8("Last") << QString::fromUtf8("Last");
+
322 QTest::newRow("missing-targets") << QString::fromUtf8("") << QString::fromUtf8("CM_ BO_ 2 \"Unknown\";\nCM_ SG_ 2 State \"Unknown\";\nCM_ SG_ 1 Missing \"Unknown\";\n") << QString::fromUtf8("") << QString::fromUtf8("");
+
323 // Current documented limitation: comments resolve only already parsed targets.
+
324 QTest::newRow("before-definitions") << QString::fromUtf8("CM_ BO_ 1 \"Early\";\nCM_ SG_ 1 State \"Early\";\n") << QString::fromUtf8("") << QString::fromUtf8("") << QString::fromUtf8("");
+
325 QTest::newRow("missing-quotes") << QString::fromUtf8("") << QString::fromUtf8("CM_ BO_ 1 Missing;\nCM_ SG_ 1 State \"Unclosed;\n") << QString::fromUtf8("") << QString::fromUtf8("");
+
326}
+
327
+
328void CoverageTest::comments() {
+
329 QFETCH(QString, before);
+
330 QFETCH(QString, after);
+
331 QFETCH(QString, frameComment);
+
332 QFETCH(QString, signalComment);
+
333 const DbcDatabase parsed = ParseText(before.toStdString() + "BO_ 1 Frame: 1 ECU\nSG_ State : 0|8@1+ (1,0) [0|255] \"\" ECU\n" + after.toStdString());
+
334 QCOMPARE(parsed.frames[0].comment, frameComment.toStdString());
+
335 QCOMPARE(parsed.frames[0].signals[0].comment, signalComment.toStdString());
+
336}
+
337
+
338void CoverageTest::ignoredCommentStatements_data() {
+
339 QTest::addColumn<QString>("statement");
+
340 QTest::newRow("empty") << QString();
+
341 QTest::newRow("wrong-keyword") << QStringLiteral("OTHER_ BO_ 1 \"Ignored\";");
+
342 QTest::newRow("keyword-prefix") << QStringLiteral("CM_OTHER BO_ 1 \"Ignored\";");
+
343 QTest::newRow("frame-without-cm") << QStringLiteral("BO_ 1 \"Ignored\";");
+
344 QTest::newRow("missing-subtype") << QStringLiteral("CM_");
+
345 QTest::newRow("global-comment") << QStringLiteral("CM_ \"Ignored\";");
+
346 QTest::newRow("node-comment") << QStringLiteral("CM_ BU_ ECU \"Ignored\";");
+
347 QTest::newRow("environment-comment") << QStringLiteral("CM_ EV_ State \"Ignored\";");
+
348 QTest::newRow("unknown-subtype") << QStringLiteral("CM_ OTHER_ 1 \"Ignored\";");
+
349}
+
350
+
351void CoverageTest::ignoredCommentStatements() {
+
352 QFETCH(QString, statement);
+
353 DbcDatabase database;
+
354 FrameInfo frame;
+
355 frame.canId = 1U;
+
356 frame.name = "Frame";
+
357 frame.comment = "Existing frame comment";
+
358 SignalInfo signal;
+
359 signal.name = "State";
+
360 signal.comment = "Existing signal comment";
+
361 frame.signals.push_back(signal);
+
362 database.frames.push_back(frame);
+
363 DbcParseContext context{database, &database.frames[0]};
+
364
+
365 // The handler is public: callers may bypass keyword dispatch. Ignored input
+
366 // must preserve existing metadata, including comments already assigned.
+
367 DbcCommentHandler().Handle(statement.toStdString(), context);
+
368 QCOMPARE(database.frames.size(), std::size_t(1));
+
369 QCOMPARE(database.frames[0].canId, 1U);
+
370 QCOMPARE(database.frames[0].name, frame.name);
+
371 QCOMPARE(database.frames[0].comment, frame.comment);
+
372 QCOMPARE(database.frames[0].signals.size(), std::size_t(1));
+
373 QCOMPARE(database.frames[0].signals[0].name, signal.name);
+
374 QCOMPARE(database.frames[0].signals[0].comment, signal.comment);
+
375 QCOMPARE(context.currentFrame, &database.frames[0]);
+
376}
+
377
+
378void CoverageTest::ambiguousValueReferences_data() {
+
379 QTest::addColumn<bool>("duplicateFrames");
+
380 QTest::addColumn<bool>("extended");
+
381 QTest::newRow("duplicate-standard-frames") << true << false;
+
382 QTest::newRow("duplicate-extended-frames") << true << true;
+
383 QTest::newRow("duplicate-signals-in-standard-frame") << false << false;
+
384 QTest::newRow("duplicate-signals-in-extended-frame") << false << true;
+
385}
+
386
+
387void CoverageTest::ambiguousValueReferences() {
+
388 QFETCH(bool, duplicateFrames);
+
389 QFETCH(bool, extended);
+
390 DbcDatabase database;
+
391 FrameInfo frame;
+
392 frame.canId = 1U;
+
393 frame.name = "Frame";
+
394 frame.isExtended = extended;
+
395 SignalInfo signal;
+
396 signal.name = "State";
+
397 frame.signals.push_back(signal);
+
398 if (!duplicateFrames) frame.signals.push_back(signal);
+
399 database.frames.push_back(frame);
+
400 if (duplicateFrames) database.frames.push_back(frame);
+
401
+
402 // ParseFile rejects duplicate definitions earlier. Construct the database
+
403 // through the public metadata API to exercise the resolver's own checks.
+
404 DbcParseContext context{database, nullptr};
+
405 context.lineNumber = 37U;
+
406 const std::string rawId = extended ? "2147483649" : "1";
+
407 DbcValueDescriptionHandler().Handle("VAL_ " + rawId + " State 0 \"Off\";", context);
+
408 QCOMPARE(context.pendingValues.size(), std::size_t(1));
+
409 QCOMPARE(context.pendingValues[0].lineNumber, std::size_t(37));
+
410 const std::string path = "manual database.dbc";
+
411 try {
+
412 context.ResolveValueDescriptions(path);
+
413 QFAIL("The resolver must reject an ambiguous target");
+
414 } catch (const std::runtime_error &error) {
+
415 const std::string message = duplicateFrames ? "Ambiguous VAL_ frame reference."
+
416 : "Ambiguous VAL_ signal reference: State";
+
417 QCOMPARE(std::string(error.what()), path + ":37: " + message);
+
418 }
+
419 // Ambiguity must be detected before applying any descriptions.
+
420 for (const FrameInfo &candidate: database.frames)
+
421 for (const SignalInfo &candidateSignal: candidate.signals)
+
422 QVERIFY(candidateSignal.valueDescriptions.empty());
+
423}
+
424
+
425void CoverageTest::valueReferenceScope() {
+
426 DbcDatabase database;
+
427 FrameInfo frame;
+
428 frame.canId = 1U;
+
429 SignalInfo signal;
+
430 signal.name = "State";
+
431 frame.signals.push_back(signal);
+
432 database.frames.push_back(frame);
+
433 frame.isExtended = true;
+
434 database.frames.push_back(frame);
+
435 frame.canId = 2U;
+
436 frame.isExtended = false;
+
437 database.frames.push_back(frame);
+
438
+
439 DbcParseContext context{database, nullptr};
+
440 const DbcValueDescriptionHandler handler;
+
441 context.lineNumber = 3U;
+
442 handler.Handle("VAL_ 1 State 0 \"Standard\";", context);
+
443 context.lineNumber = 7U;
+
444 handler.Handle("VAL_ 2147483649 State 0 \"Extended\";", context);
+
445 context.ResolveValueDescriptions("manual database.dbc");
+
446
+
447 const std::string *standard = FindValueDescription(database.frames[0].signals[0].valueDescriptions, std::uint64_t(0));
+
448 const std::string *extended = FindValueDescription(database.frames[1].signals[0].valueDescriptions, std::uint64_t(0));
+
449 QVERIFY(standard != nullptr);
+
450 QVERIFY(extended != nullptr);
+
451 QCOMPARE(*standard, std::string("Standard"));
+
452 QCOMPARE(*extended, std::string("Extended"));
+
453 QVERIFY(database.frames[2].signals[0].valueDescriptions.empty());
+
454}
+
455
+
456void CoverageTest::fileFormatting_data() {
+
457 QTest::addColumn<QString>("text");
+
458 QTest::addColumn<unsigned>("frames");
+
459 QTest::newRow("empty") << QString::fromUtf8("") << 0U;
+
460 QTest::newRow("whitespace") << QString::fromUtf8(" \t\r\n\n\t") << 0U;
+
461 QTest::newRow("unknown-only") << QString::fromUtf8("VERSION \"test\"\nBU_: ECU\nUNKNOWN_ data\n") << 0U;
+
462 QTest::newRow("no-final-newline") << QString::fromUtf8("BO_ 1 Frame: 1 ECU\nSG_ State : 0|8@1+ (1,0) [0|255] \"\" ECU") << 1U;
+
463 QTest::newRow("crlf") << QString::fromUtf8("\r\nBO_ 1 Frame: 1 ECU\r\nSG_ State : 0|8@1+ (1,0) [0|255] \"\" ECU\r\nCM_ BO_ 1 \"Comment\";\r\nVAL_ 1 State 0 \"Off\";\r\n") << 1U;
+
464}
+
465
+
466void CoverageTest::fileFormatting() {
+
467 QFETCH(QString, text);
+
468 QFETCH(unsigned, frames);
+
469 const DbcDatabase parsed = ParseText(text.toStdString());
+
470 QCOMPARE(parsed.frames.size(), std::size_t(frames));
+
471 if (frames) {
+
472 QCOMPARE(parsed.frames[0].signals.size(), std::size_t(1));
+
473 if (text.contains("\r\nBO_")) {
+
474 QCOMPARE(parsed.frames[0].comment, std::string("Comment"));
+
475 const std::string *description = FindValueDescription(parsed.frames[0].signals[0].valueDescriptions, std::uint64_t(0));
+
476 QVERIFY(description != nullptr);
+
477 QCOMPARE(*description, std::string("Off"));
+
478 }
+
479 }
+
480}
+
481
+
482void CoverageTest::missingFile() {
+
483 QTemporaryFile file;
+
484 QVERIFY(file.open());
+
485 const QString path = file.fileName();
+
486 QVERIFY(file.remove());
+
487 try {
+
488 DbcParser().ParseFile(path.toStdString());
+
489 QFAIL("Missing file must throw");
+
490 } catch (const std::runtime_error &error) {
+
491 QCOMPARE(std::string(error.what()), "Failed to open DBC file: " + path.toStdString());
+
492 }
+
493}
+
494
+
495void CoverageTest::builders() {
+
496 const DecodeDatabase emptyRuntime = DbcDecodeBuilder().Build(DbcDatabase());
+
497 QVERIFY(emptyRuntime.GetFrames().empty());
+
498 QVERIFY(!emptyRuntime.FindFrame(FrameKey(0U, false)));
+
499 const std::unique_ptr<TreeNode> emptyTree = DbcTreeBuilder().Build(DbcDatabase());
+
500 QCOMPARE(emptyTree->GetType(), NodeType::Root);
+
501 QCOMPARE(emptyTree->GetName(), std::string("dbc"));
+
502 QCOMPARE(emptyTree->GetChildCount(), std::size_t(0));
+
503 QVERIFY(emptyTree->GetFrame() == nullptr && emptyTree->GetSignal() == nullptr);
+
504 const DbcDatabase expected = ParseText(
+
505 "BO_ 256 Standard: 3 ECU1\n"
+
506 "SG_ Mode M : 0|8@1+ (2,3) [0|513] \"mode\" ECU1,ECU2\n"
+
507 "SG_ Branch m1 : 15|8@0- (-2,5) [-249|261] \"rpm\" ECU2,ECU3\n"
+
508 "SG_ Always : 16|8@1+ (0.25,-1) [-1|62.75] \"V\" ECU3\n"
+
509 "CM_ BO_ 256 \"Frame comment\";\nCM_ SG_ 256 Branch \"Branch comment\";\n"
+
510 "VAL_ 256 Branch -1 \"Negative\" 1 \"Positive\";\n"
+
511 "BO_ 2163212866 Extended: 8 ECU4\n");
+
512 DecodeDatabase runtime;
+
513 std::unique_ptr<TreeNode> tree;
+
514 {
+
515 DbcDatabase source = expected;
+
516 runtime = DbcDecodeBuilder().Build(source);
+
517 tree = DbcTreeBuilder().Build(source);
+
518 source.frames[0].name = "Changed";
+
519 source.frames[0].signals[1].receivers.clear();
+
520 source.frames[0].signals[1].valueDescriptions.clear();
+
521 source.frames.clear();
+
522 }
+
523 QCOMPARE(runtime.GetFrames().size(), expected.frames.size());
+
524 QCOMPARE(tree->GetChildCount(), expected.frames.size());
+
525 for (std::size_t i = 0; i < expected.frames.size(); ++i) {
+
526 const FrameInfo &frame = expected.frames[i];
+
527 const DecodeFrame &decodedFrame = runtime.GetFrames()[i];
+
528 const TreeNode *node = tree->GetChild(i);
+
529 QVERIFY(node && node->GetFrame());
+
530 QCOMPARE(node->GetType(), NodeType::Frame);
+
531 QCOMPARE(node->GetName(), frame.name);
+
532 QVERIFY(node->GetSignal() == nullptr);
+
533 QCOMPARE(runtime.FindFrame(FrameKey(frame.canId, frame.isExtended)).get(), &decodedFrame);
+
534 const FrameInfo &copied = *node->GetFrame();
+
535 QCOMPARE(copied.name, frame.name);
+
536 QCOMPARE(decodedFrame.name, frame.name);
+
537 QCOMPARE(copied.canId, frame.canId);
+
538 QCOMPARE(decodedFrame.canId, frame.canId);
+
539 QCOMPARE(copied.isExtended, frame.isExtended);
+
540 QCOMPARE(decodedFrame.isExtended, frame.isExtended);
+
541 QCOMPARE(copied.dlc, frame.dlc);
+
542 QCOMPARE(decodedFrame.dlc, frame.dlc);
+
543 QCOMPARE(copied.pgn, frame.pgn);
+
544 QCOMPARE(decodedFrame.pgn, frame.pgn);
+
545 QCOMPARE(copied.hasPgn, frame.hasPgn);
+
546 QCOMPARE(decodedFrame.hasPgn, frame.hasPgn);
+
547 QCOMPARE(copied.transmitter, frame.transmitter);
+
548 QCOMPARE(decodedFrame.transmitter, frame.transmitter);
+
549 QCOMPARE(copied.comment, frame.comment);
+
550 QCOMPARE(decodedFrame.comment, frame.comment);
+
551 QCOMPARE(node->GetChildCount(), frame.signals.size());
+
552 QCOMPARE(copied.signals.size(), frame.signals.size());
+
553 QCOMPARE(decodedFrame.signals.size(), frame.signals.size());
+
554 for (std::size_t j = 0; j < frame.signals.size(); ++j) {
+
555 const SignalInfo &original = frame.signals[j];
+
556 const DecodeSignal &target = decodedFrame.signals[j];
+
557 const TreeNode *leaf = node->GetChild(j);
+
558 QVERIFY(leaf && leaf->GetSignal());
+
559 QCOMPARE(leaf->GetType(), NodeType::Signal);
+
560 QCOMPARE(leaf->GetName(), original.name);
+
561 QVERIFY(leaf->GetFrame() == nullptr);
+
562 // Check both the frame payload copy and each child payload copy.
+
563 for (const SignalInfo *copy: {&copied.signals[j], leaf->GetSignal()}) {
+
564 QCOMPARE(copy->name, original.name);
+
565 QCOMPARE(copy->startBit, original.startBit);
+
566 QCOMPARE(copy->length, original.length);
+
567 QCOMPARE(copy->isLittleEndian, original.isLittleEndian);
+
568 QCOMPARE(copy->isSigned, original.isSigned);
+
569 QCOMPARE(copy->factor, original.factor);
+
570 QCOMPARE(copy->offset, original.offset);
+
571 QCOMPARE(copy->minimum, original.minimum);
+
572 QCOMPARE(copy->maximum, original.maximum);
+
573 QCOMPARE(copy->unit, original.unit);
+
574 QCOMPARE(copy->comment, original.comment);
+
575 QVERIFY(copy->receivers == original.receivers);
+
576 QVERIFY(copy->valueDescriptions == original.valueDescriptions);
+
577 QCOMPARE(copy->multiplexing.role, original.multiplexing.role);
+
578 QCOMPARE(copy->multiplexing.value, original.multiplexing.value);
+
579 }
+
580 QCOMPARE(target.name, original.name);
+
581 QCOMPARE(target.startBit, original.startBit);
+
582 QCOMPARE(target.length, original.length);
+
583 QCOMPARE(target.byteOrder, original.isLittleEndian ? ByteOrder::Intel : ByteOrder::Motorola);
+
584 QCOMPARE(target.valueType, original.isSigned ? ValueType::Signed : ValueType::Unsigned);
+
585 QCOMPARE(target.factor, original.factor);
+
586 QCOMPARE(target.offset, original.offset);
+
587 QCOMPARE(target.minimum, original.minimum);
+
588 QCOMPARE(target.maximum, original.maximum);
+
589 QCOMPARE(target.unit, original.unit);
+
590 QCOMPARE(target.comment, original.comment);
+
591 QVERIFY(target.receivers == original.receivers);
+
592 QVERIFY(target.valueDescriptions == original.valueDescriptions);
+
593 QCOMPARE(target.multiplexing.role, original.multiplexing.role);
+
594 QCOMPARE(target.multiplexing.value, original.multiplexing.value);
+
595 }
+
596 }
+
597}
+
598
+
599void CoverageTest::physicalConversion_data() {
+
600 QTest::addColumn<double>("factor");
+
601 QTest::addColumn<double>("offset");
+
602 QTest::addColumn<double>("expected");
+
603 QTest::newRow("zero-factor") << 0.0 << 7.0 << 7.0;
+
604 QTest::newRow("negative-factor") << -2.0 << 5.0 << -15.0;
+
605 QTest::newRow("above-declared-range") << 2.0 << 3.0 << 23.0;
+
606}
+
607
+
608void CoverageTest::physicalConversion() {
+
609 QFETCH(double, factor);
+
610 QFETCH(double, offset);
+
611 QFETCH(double, expected);
+
612 DecodeSignal signal;
+
613 signal.length = 8U;
+
614 signal.factor = factor;
+
615 signal.offset = offset;
+
616 signal.minimum = 0.0;
+
617 signal.maximum = 1.0;
+
618 const DecodeDatabase database = SingleSignal(signal);
+
619 RawCanFrame raw;
+
620 raw.canId = 17U;
+
621 raw.data = {10U};
+
622 const DecodedFrameValue decoded = DbcDecoder().Decode(database, raw);
+
623 QVERIFY(decoded.signals[0].valid);
+
624 QCOMPARE(std::get<std::uint64_t>(decoded.signals[0].rawValue), std::uint64_t(10));
+
625 QCOMPARE(decoded.signals[0].physicalValue, expected);
+
626}
+
627
+
628void CoverageTest::multiplexingScenarios() {
+
629 const DecodeDatabase selectorOnly = DbcDecodeBuilder().Build(ParseText(
+
630 "BO_ 1 Frame: 1 ECU\nSG_ Mode M : 0|1@1+ (1,0) [0|1] \"\" ECU\n"));
+
631 RawCanFrame raw;
+
632 raw.canId = 1U;
+
633 raw.data = {1U};
+
634 const DecodedFrameValue only = DbcDecoder().Decode(selectorOnly, raw);
+
635 QCOMPARE(only.signals.size(), std::size_t(1));
+
636 QVERIFY(only.signals[0].valid);
+
637 const DecodeDatabase database = DbcDecodeBuilder().Build(ParseText(
+
638 "BO_ 1 First: 3 ECU\n"
+
639 "SG_ Mode M : 0|1@1+ (1,0) [0|1] \"\" ECU\n"
+
640 "SG_ A m0 : 8|8@1+ (1,0) [0|255] \"\" ECU\n"
+
641 "SG_ B m0 : 16|8@1+ (1,0) [0|255] \"\" ECU\n"
+
642 "BO_ 2 Second: 2 ECU\n"
+
643 "SG_ Mode M : 0|1@1+ (1,0) [0|1] \"\" ECU\n"
+
644 "SG_ A m1 : 8|8@1+ (1,0) [0|255] \"\" ECU\n"));
+
645 raw.data = {0U, 42U, 99U};
+
646 const DecodedFrameValue first = DbcDecoder().Decode(database, raw);
+
647 QVERIFY(first.signals[1].valid && first.signals[2].valid);
+
648 QCOMPARE(std::get<std::uint64_t>(first.signals[1].rawValue), std::uint64_t(42));
+
649 QCOMPARE(std::get<std::uint64_t>(first.signals[2].rawValue), std::uint64_t(99));
+
650 raw.canId = 2U;
+
651 const DecodedFrameValue secondInactive = DbcDecoder().Decode(database, raw);
+
652 QCOMPARE(secondInactive.signals[1].status, SignalDecodeStatus::Inactive);
+
653 raw.data[0] = 1U;
+
654 QVERIFY(DbcDecoder().Decode(database, raw).signals[1].valid);
+
655 raw.canId = 1U;
+
656 const DecodedFrameValue firstInactive = DbcDecoder().Decode(database, raw);
+
657 QCOMPARE(firstInactive.signals[1].status, SignalDecodeStatus::Inactive);
+
658 QCOMPARE(firstInactive.signals[2].status, SignalDecodeStatus::Inactive);
+
659}
+
660
+
661void CoverageTest::multiplexingErrorLocations_data() {
+
662 QTest::addColumn<QString>("text");
+
663 QTest::addColumn<unsigned>("line");
+
664 QTest::addColumn<QString>("message");
+
665 const QString header = "BO_ 1 Frame: 3 ECU\n\n";
+
666 const QString selector = "SG_ Mode M : 0|1@1+ (1,0) [0|1] \"\" ECU\n";
+
667 const QString branch = "SG_ A m1 : 8|8@1+ (1,0) [0|255] \"\" ECU\n";
+
668 QTest::newRow("missing-selector") << header + branch << 3U << QString::fromUtf8("Multiplexed branch has no selector.");
+
669 QTest::newRow("second-selector") << header + selector + "\nSG_ Other M : 16|1@1+ (1,0) [0|1] \"\" ECU\n"
+
670 << 5U << QString::fromUtf8("Multiplexing requires one unsigned selector per frame.");
+
671 QTest::newRow("signed-selector") << header + "SG_ Mode M : 0|1@1- (1,0) [-1|0] \"\" ECU\n"
+
672 << 3U << QString::fromUtf8("Multiplexing requires one unsigned selector per frame.");
+
673 QTest::newRow("branch-range") << header + "SG_ A m2 : 8|8@1+ (1,0) [0|255] \"\" ECU\n" + selector << 3U << QString::fromUtf8("Branch value exceeds selector raw range.");
+
674 QTest::newRow("same-branch-overlap") << header + selector + branch + "SG_ B m1 : 15|8@0+ (1,0) [0|255] \"\" ECU\n"
+
675 << 5U << QString::fromUtf8("Signals overlap without mutually exclusive multiplexing conditions.");
+
676 QTest::newRow("selector-overlap") << header + selector + "SG_ A m1 : 0|1@1+ (1,0) [0|1] \"\" ECU\n"
+
677 << 4U << QString::fromUtf8("Signals overlap without mutually exclusive multiplexing conditions.");
+
678 QTest::newRow("unconditional-overlap") << header + selector + branch + "SG_ Always : 8|8@1+ (1,0) [0|255] \"\" ECU\n"
+
679 << 5U << QString::fromUtf8("Signals overlap without mutually exclusive multiplexing conditions.");
+
680 QTest::newRow("ordinary-overlap") << header + "SG_ A : 8|8@1+ (1,0) [0|255] \"\" ECU\nSG_ B : 15|8@0+ (1,0) [0|255] \"\" ECU\n"
+
681 << 4U << QString::fromUtf8("Signals overlap without mutually exclusive multiplexing conditions.");
+
682 QTest::newRow("extended-multiplexing") << header + "SG_MUL_VAL_ 1 A Mode 1-2;\n"
+
683 << 3U << QString::fromUtf8("Extended and nested multiplexing are unsupported.");
+
684 QTest::newRow("nested-marker") << header + "SG_ A m1M : 8|8@1+ (1,0) [0|255] \"\" ECU\n"
+
685 << 3U << QString::fromUtf8("Invalid or unsupported multiplexing marker.");
+
686}
+
687
+
688void CoverageTest::multiplexingErrorLocations() {
+
689 QFETCH(QString, text);
+
690 QFETCH(unsigned, line);
+
691 QFETCH(QString, message);
+
692 QTemporaryFile file;
+
693 QVERIFY(file.open());
+
694 const QByteArray content = text.toUtf8();
+
695 QCOMPARE(file.write(content), qint64(content.size()));
+
696 QVERIFY(file.flush());
+
697 try {
+
698 DbcParser().ParseFile(file.fileName().toStdString());
+
699 QFAIL("Expected multiplexing rejection");
+
700 } catch (const std::runtime_error &error) {
+
701 QCOMPARE(QString::fromStdString(error.what()), file.fileName() + ":" + QString::number(line) + ": " + message);
+
702 }
+
703}
+
704
+
705void CoverageTest::escapedValueDescriptions() {
+
706 const DbcDatabase parsed = ParseText(
+
707 "BO_ 1 Frame: 1 ECU\nSG_ State : 0|8@1+ (1,0) [0|255] \"\" ECU\n"
+
708 R"dbc(VAL_ 1 State -0 "Ноль" 1 "C:\\trace" 2 "\\\"quoted\"\\";)dbc"
+
709 "\n");
+
710 const ValueDescriptions &values = parsed.frames[0].signals[0].valueDescriptions;
+
711 QCOMPARE(values.size(), std::size_t(3));
+
712 QVERIFY(values.find(RawSignalValue(std::int64_t(0))) == values.end());
+
713 const std::string *zero = FindValueDescription(values, std::int64_t(0));
+
714 const std::string *path = FindValueDescription(values, std::uint64_t(1));
+
715 const std::string *quoted = FindValueDescription(values, std::uint64_t(2));
+
716 QVERIFY(zero && path && quoted);
+
717 QCOMPARE(*zero, std::string("Ноль"));
+
718 QCOMPARE(*path, std::string("C:\\trace"));
+
719 QCOMPARE(*quoted, std::string("\\\"quoted\"\\"));
+
720 QVERIFY(NormalizeValueKey(RawSignalValue(std::int64_t(0))) == RawSignalValue(std::uint64_t(0)));
+
721 QVERIFY(NormalizeValueKey(RawSignalValue(std::int64_t(-1))) == RawSignalValue(std::int64_t(-1)));
+
722}
+
723
+
724void CoverageTest::factory() {
+ +
726 QVERIFY(!factory.contains("value"));
+
727 QVERIFY_THROWS_EXCEPTION(std::out_of_range, static_cast<void>(factory.creator("missing")));
+
728 QVERIFY_THROWS_EXCEPTION(std::out_of_range, static_cast<void>(factory.create("missing", std::make_unique<int>(1))));
+
729 factory.registerType<FactoryFirst>("value");
+
730 QVERIFY(factory.contains("value"));
+
731 std::unique_ptr<int> argument = std::make_unique<int>(42);
+
732 const std::shared_ptr<FactoryBase> first = factory.create("value", std::move(argument));
+
733 QVERIFY(argument == nullptr);
+
734 QCOMPARE(first->Value(), 42);
+
735 const TypeFactory<std::string, FactoryBase, std::unique_ptr<int>>::CreatorFn creator = factory.creator("value");
+
736 QCOMPARE(creator(std::make_unique<int>(7))->Value(), 7);
+
737 factory.registerType<FactorySecond>("value");
+
738 QCOMPARE(factory.create("value", std::make_unique<int>(42))->Value(), 43);
+
739 QCOMPARE(first->Value(), 42);
+
740 QCOMPARE(creator(std::make_unique<int>(7))->Value(), 7);
+
741}
+
742
+
743int RunCoverageTests(int argc, char **argv) {
+
744 CoverageTest tests;
+
745 return QTest::qExec(&tests, argc, argv);
+
746}
+
747#include "dbc_coverage_test.moc"
Converts parsed DBC data into runtime decode database.
DecodeDatabase Build(const DbcDatabase &source) const
Build runtime decode database.
-
Runtime CAN decoder using prebuilt decode database.
Definition dbc_decoder.h:74
-
DecodedFrameValue Decode(const DecodeDatabase &database, const RawCanFrame &frame) const
Decode one raw CAN frame.
-
std::shared_ptr< const DecodeFrame > FindFrame(const DecodeDatabase &database, std::uint32_t canId, bool isExtended) const
Find frame definition by CAN ID.
+
Runtime CAN decoder using prebuilt decode database.
Definition dbc_decoder.h:84
+
DecodedFrameValue Decode(const DecodeDatabase &database, const RawCanFrame &frame) const
Decode one raw CAN frame.
+
std::shared_ptr< const DecodeFrame > FindFrame(const DecodeDatabase &database, std::uint32_t canId, bool isExtended) const
Find frame definition by CAN ID.
Minimal DBC parser.
Definition dbc_parser.h:33
DbcDatabase ParseFile(const std::string &filePath) const
Parse DBC file.
Builds a simple tree from parsed DBC database.
@@ -902,9 +903,10 @@ $(document).ready(function(){initNavTree('dbc__coverage__test_8cpp_source.html',
ByteOrder byteOrder
std::uint32_t length
std::string comment
-
Fully decoded frame.
Definition dbc_decoder.h:62
-
std::shared_ptr< const DecodeFrame > definition
Definition dbc_decoder.h:63
- +
Fully decoded frame.
Definition dbc_decoder.h:71
+
std::shared_ptr< const DecodeFrame > definition
Definition dbc_decoder.h:72
+ +
FrameDecodeStatus status
Definition dbc_decoder.h:76
Describes one CAN frame from a DBC file.
Definition frame_info.h:24
bool isExtended
Definition frame_info.h:27
bool hasPgn
Definition frame_info.h:29
diff --git a/docs/html/dbc__decoder_8cpp.html b/docs/html/dbc__decoder_8cpp.html index 17fbfd5..8598b59 100644 --- a/docs/html/dbc__decoder_8cpp.html +++ b/docs/html/dbc__decoder_8cpp.html @@ -97,6 +97,7 @@ $(document).ready(function(){initNavTree('dbc__decoder_8cpp.html',''); initResiz

Created: 2026-03-13 Author: Deeaitch (Dim. Himro) More...

#include "dbc_decoder.h"
+#include <cmath>
#include <utility>

Go to the source code of this file.

diff --git a/docs/html/dbc__decoder_8cpp_source.html b/docs/html/dbc__decoder_8cpp_source.html index 06a125b..09aebee 100644 --- a/docs/html/dbc__decoder_8cpp_source.html +++ b/docs/html/dbc__decoder_8cpp_source.html @@ -100,198 +100,227 @@ $(document).ready(function(){initNavTree('dbc__decoder_8cpp_source.html',''); in
Go to the documentation of this file.
1
12#include "dbc_decoder.h"
-
13#include <utility>
-
14
-
-
15std::shared_ptr<const DecodeFrame> DbcDecoder::FindFrame(const DecodeDatabase &database,
-
16 std::uint32_t canId,
-
17 bool isExtended) const {
-
18 return database.FindFrame(FrameKey(canId, isExtended));
-
19}
+
13#include <cmath>
+
14#include <utility>
+
15
+
+
16std::shared_ptr<const DecodeFrame> DbcDecoder::FindFrame(const DecodeDatabase &database,
+
17 std::uint32_t canId,
+
18 bool isExtended) const {
+
19 return database.FindFrame(FrameKey(canId, isExtended));
+
20}
-
20
-
- -
22 const RawCanFrame &frame) const {
-
23 DecodedFrameValue result;
-
24 result.key = FrameKey(frame.canId, frame.isExtended);
-
25 const std::shared_ptr<const DecodeFrame> definition = FindFrame(database, frame.canId, frame.isExtended);
-
26
-
27 if (definition == nullptr)
-
28 return result;
-
29
-
30 result.definition = definition;
-
31 result.valid = true;
-
32
-
33 const SelectorState selector = ReadSelector(*definition, frame);
-
34 result.signals.reserve(definition->signals.size());
-
35 for (const DecodeSignal &signal: definition->signals) {
-
36 DecodedSignalValue decoded = DecodeOneSignal(signal, frame, selector);
-
37 decoded.definition = std::shared_ptr<const DecodeSignal>(definition, &signal);
-
38 decoded.key = SignalKey(result.key, signal.name);
-
39 result.signals.push_back(std::move(decoded));
-
40 }
-
41
-
42 return result;
-
43}
+
21
+
+ +
23 const RawCanFrame &frame) const {
+
24 DecodedFrameValue result;
+
25 result.key = FrameKey(frame.canId, frame.isExtended);
+
26 const std::shared_ptr<const DecodeFrame> definition = FindFrame(database, frame.canId, frame.isExtended);
+
27
+
28 if (definition == nullptr)
+
29 return result;
+
30
+
31 result.definition = definition;
+
32 result.valid = true;
+
33 result.status = frame.data.size() < definition->dlc
+
34 ? FrameDecodeStatus::InsufficientPayload
+
35 : FrameDecodeStatus::Valid;
+
36
+
37 const SelectorState selector = ReadSelector(*definition, frame);
+
38 result.signals.reserve(definition->signals.size());
+
39 for (const DecodeSignal &signal: definition->signals) {
+
40 DecodedSignalValue decoded = DecodeOneSignal(signal, definition->dlc, frame, selector);
+
41 if (!decoded.valid && decoded.status != SignalDecodeStatus::Inactive &&
+
42 result.status == FrameDecodeStatus::Valid)
+
43 result.status = FrameDecodeStatus::SignalErrors;
+
44 decoded.definition = std::shared_ptr<const DecodeSignal>(definition, &signal);
+
45 decoded.key = SignalKey(result.key, signal.name);
+
46 result.signals.push_back(std::move(decoded));
+
47 }
+
48
+
49 return result;
+
50}
-
44
-
45DbcDecoder::SelectorState DbcDecoder::ReadSelector(const DecodeFrame &definition,
-
46 const RawCanFrame &frame) {
-
47 SelectorState selector;
-
48 std::size_t count = 0U;
-
49 for (const DecodeSignal &signal: definition.signals) {
-
50 if (signal.multiplexing.role == MultiplexRole::Selector) {
-
51 selector.definition = &signal;
-
52 ++count;
-
53 }
-
54 }
-
55 if (count != 1U)
-
56 return selector;
-
57
-
58 const DecodeSignal &signal = *selector.definition;
-
59 selector.valid = signal.valueType == ValueType::Unsigned &&
-
60 signal.length > 0U && signal.length <= 64U;
-
61 selector.available = selector.valid && ExtractUnsigned(frame.data, signal, selector.value);
-
62 return selector;
-
63}
-
64
-
65DecodedSignalValue DbcDecoder::DecodeOneSignal(const DecodeSignal &signal,
-
66 const RawCanFrame &frame,
-
67 const SelectorState &selector) {
-
68 DecodedSignalValue decoded;
-
69 std::uint64_t unsignedValue = 0U;
-
70
-
71 if (signal.length == 0U || signal.length > 64U ||
-
72 (signal.multiplexing.role != MultiplexRole::Unconditional && !selector.valid)) {
-
73 return decoded;
-
74 }
-
75 if (signal.multiplexing.role == MultiplexRole::Branch) {
-
76 if (selector.definition->length < 64U && signal.multiplexing.value >= (std::uint64_t(1U) << selector.definition->length)) {
-
77 return decoded;
-
78 }
-
79 if (!selector.available) {
-
80 decoded.status = SignalDecodeStatus::SelectorUnavailable;
-
81 return decoded;
-
82 }
-
83 if (signal.multiplexing.value != selector.value) {
-
84 decoded.status = SignalDecodeStatus::Inactive;
-
85 return decoded;
-
86 }
-
87 }
-
88 if (!ExtractUnsigned(frame.data, signal, unsignedValue)) {
-
89 decoded.status = SignalDecodeStatus::InsufficientPayload;
-
90 return decoded;
-
91 }
-
92
-
93 if (signal.valueType == ValueType::Signed)
-
94 decoded.rawValue = SignExtend(unsignedValue, signal.length);
-
95 else
-
96 decoded.rawValue = unsignedValue;
-
97
-
98 double numericValue;
-
99 if (const std::int64_t *signedValue = std::get_if<std::int64_t>(&decoded.rawValue))
-
100 numericValue = static_cast<double>(*signedValue);
-
101 else
-
102 numericValue = static_cast<double>(std::get<std::uint64_t>(decoded.rawValue));
-
103 decoded.physicalValue = numericValue * signal.factor + signal.offset;
-
104 decoded.valid = true;
-
105 decoded.status = SignalDecodeStatus::Valid;
-
106
-
107 return decoded;
-
108}
-
109
-
110bool DbcDecoder::ExtractUnsigned(const std::vector<std::uint8_t> &data,
-
111 const DecodeSignal &signal,
-
112 std::uint64_t &value) {
-
113 if ((signal.length == 0U) || (signal.length > 64U))
-
114 return false;
-
115
-
116 if (signal.byteOrder == ByteOrder::Intel)
-
117 return ExtractIntel(data, signal.startBit, signal.length, value);
-
118
-
119 return ExtractMotorola(data, signal.startBit, signal.length, value);
-
120}
+
51
+
52bool DbcDecoder::IsValidDefinition(const DecodeSignal &signal, std::uint8_t dlc) {
+
53 if (signal.length == 0U || signal.length > 64U ||
+
54 (signal.byteOrder != ByteOrder::Intel && signal.byteOrder != ByteOrder::Motorola) ||
+
55 (signal.valueType != ValueType::Unsigned && signal.valueType != ValueType::Signed) ||
+
56 (signal.multiplexing.role != MultiplexRole::Unconditional &&
+
57 signal.multiplexing.role != MultiplexRole::Selector &&
+
58 signal.multiplexing.role != MultiplexRole::Branch))
+
59 return false;
+
60
+
61 // Use widened coordinates to avoid overflow on malformed start bits.
+
62 const std::uint64_t start = signal.byteOrder == ByteOrder::Intel
+
63 ? signal.startBit
+
64 : std::uint64_t(signal.startBit / 8U) * 8U + 7U - signal.startBit % 8U;
+
65 return start + signal.length <= std::uint64_t(dlc) * 8U;
+
66}
+
67
+
68DbcDecoder::SelectorState DbcDecoder::ReadSelector(const DecodeFrame &definition,
+
69 const RawCanFrame &frame) {
+
70 SelectorState selector;
+
71 std::size_t count = 0U;
+
72 for (const DecodeSignal &signal: definition.signals) {
+
73 if (signal.multiplexing.role == MultiplexRole::Selector) {
+
74 selector.definition = &signal;
+
75 ++count;
+
76 }
+
77 }
+
78 if (count != 1U)
+
79 return selector;
+
80
+
81 const DecodeSignal &signal = *selector.definition;
+
82 selector.valid = signal.valueType == ValueType::Unsigned &&
+
83 IsValidDefinition(signal, definition.dlc);
+
84 selector.available = selector.valid && ExtractUnsigned(frame.data, signal, selector.value);
+
85 return selector;
+
86}
+
87
+
88DecodedSignalValue DbcDecoder::DecodeOneSignal(const DecodeSignal &signal,
+
89 std::uint8_t dlc,
+
90 const RawCanFrame &frame,
+
91 const SelectorState &selector) {
+
92 DecodedSignalValue decoded;
+
93 std::uint64_t unsignedValue = 0U;
+
94
+
95 if (!IsValidDefinition(signal, dlc) ||
+
96 (signal.multiplexing.role != MultiplexRole::Unconditional && !selector.valid)) {
+
97 return decoded;
+
98 }
+
99 if (signal.multiplexing.role == MultiplexRole::Branch) {
+
100 if (selector.definition->length < 64U && signal.multiplexing.value >= (std::uint64_t(1U) << selector.definition->length)) {
+
101 return decoded;
+
102 }
+
103 if (!selector.available) {
+
104 decoded.status = SignalDecodeStatus::SelectorUnavailable;
+
105 return decoded;
+
106 }
+
107 if (signal.multiplexing.value != selector.value) {
+
108 decoded.status = SignalDecodeStatus::Inactive;
+
109 return decoded;
+
110 }
+
111 }
+
112 if (!ExtractUnsigned(frame.data, signal, unsignedValue)) {
+
113 decoded.status = SignalDecodeStatus::InsufficientPayload;
+
114 return decoded;
+
115 }
+
116
+
117 if (signal.valueType == ValueType::Signed)
+
118 decoded.rawValue = SignExtend(unsignedValue, signal.length);
+
119 else
+
120 decoded.rawValue = unsignedValue;
121
-
122bool DbcDecoder::ExtractIntel(const std::vector<std::uint8_t> &data,
-
123 std::uint32_t startBit,
-
124 std::uint32_t length,
-
125 std::uint64_t &value) {
-
126 value = 0U;
-
127
-
128 for (std::uint32_t bitIndex = 0U; bitIndex < length; ++bitIndex) {
-
129 const std::uint32_t absoluteBit = startBit + bitIndex;
-
130 const std::uint32_t byteIndex = absoluteBit / 8U;
-
131 const std::uint32_t bitInByte = absoluteBit % 8U;
-
132
-
133 if (byteIndex >= data.size())
-
134 return false;
-
135
-
136 const std::uint64_t bitValue =
-
137 (static_cast<std::uint64_t>((data[byteIndex] >> bitInByte) & 0x01U) << bitIndex);
-
138
-
139 value |= bitValue;
-
140 }
-
141
-
142 return true;
-
143}
-
144
-
145bool DbcDecoder::ExtractMotorola(const std::vector<std::uint8_t> &data,
-
146 std::uint32_t startBit,
-
147 std::uint32_t length,
-
148 std::uint64_t &value) {
-
149 /*
-
150 * DBC Motorola bit numbering:
-
151 * - startBit points to the most significant bit of the signal
-
152 * - inside a byte, bit numbering goes 7..0
-
153 * - crossing byte boundary moves to the next byte, bit 7
-
154 * Example: a signal starting at 7 follows 7..0, 15..8, 23..16.
-
155 */
-
156
-
157 value = 0U;
-
158
-
159 std::size_t byteIndex = startBit / 8U;
-
160 std::uint32_t bitInByte = startBit % 8U;
-
161
-
162 for (std::uint32_t bitIndex = 0U; bitIndex < length; ++bitIndex) {
-
163 if (byteIndex >= data.size())
-
164 return false;
-
165
-
166 value <<= 1U;
-
167 value |= static_cast<std::uint64_t>((data[byteIndex] >> bitInByte) & 0x01U);
-
168
-
169 if (bitInByte == 0U) {
-
170 ++byteIndex;
-
171 bitInByte = 7U;
-
172 } else
-
173 --bitInByte;
-
174 }
-
175
-
176 return true;
-
177}
-
178
-
179std::int64_t DbcDecoder::SignExtend(std::uint64_t value, std::uint32_t bitLength) {
-
180 if ((bitLength == 0U) || (bitLength > 64U))
-
181 return 0;
-
182
-
183 const std::uint64_t signMask = std::uint64_t(1U) << (bitLength - 1U);
-
184 const std::uint64_t valueMask = (bitLength == 64U)
-
185 ? ~std::uint64_t(0U)
-
186 : (std::uint64_t(1U) << bitLength) - 1U;
-
187 value &= valueMask;
-
188
-
189 if ((value & signMask) == 0U)
-
190 return static_cast<std::int64_t>(value);
-
191
-
192 // The complemented magnitude fits int64_t, including for INT64_MIN.
-
193 return -1 - static_cast<std::int64_t>((~value) & valueMask);
-
194}
-
DecodedFrameValue Decode(const DecodeDatabase &database, const RawCanFrame &frame) const
Decode one raw CAN frame.
-
std::shared_ptr< const DecodeFrame > FindFrame(const DecodeDatabase &database, std::uint32_t canId, bool isExtended) const
Find frame definition by CAN ID.
+
122 double numericValue;
+
123 if (const std::int64_t *signedValue = std::get_if<std::int64_t>(&decoded.rawValue))
+
124 numericValue = static_cast<double>(*signedValue);
+
125 else
+
126 numericValue = static_cast<double>(std::get<std::uint64_t>(decoded.rawValue));
+
127 decoded.physicalValue = numericValue * signal.factor + signal.offset;
+
128 if (!std::isfinite(decoded.physicalValue)) {
+
129 decoded.status = SignalDecodeStatus::PhysicalConversionFailed;
+
130 return decoded;
+
131 }
+
132 decoded.valid = true;
+
133 decoded.status = SignalDecodeStatus::Valid;
+
134
+
135 return decoded;
+
136}
+
137
+
138bool DbcDecoder::ExtractUnsigned(const std::vector<std::uint8_t> &data,
+
139 const DecodeSignal &signal,
+
140 std::uint64_t &value) {
+
141 if ((signal.length == 0U) || (signal.length > 64U))
+
142 return false;
+
143
+
144 if (signal.byteOrder == ByteOrder::Intel)
+
145 return ExtractIntel(data, signal.startBit, signal.length, value);
+
146
+
147 return ExtractMotorola(data, signal.startBit, signal.length, value);
+
148}
+
149
+
150bool DbcDecoder::ExtractIntel(const std::vector<std::uint8_t> &data,
+
151 std::uint32_t startBit,
+
152 std::uint32_t length,
+
153 std::uint64_t &value) {
+
154 value = 0U;
+
155
+
156 for (std::uint32_t bitIndex = 0U; bitIndex < length; ++bitIndex) {
+
157 const std::uint32_t absoluteBit = startBit + bitIndex;
+
158 const std::uint32_t byteIndex = absoluteBit / 8U;
+
159 const std::uint32_t bitInByte = absoluteBit % 8U;
+
160
+
161 if (byteIndex >= data.size())
+
162 return false;
+
163
+
164 const std::uint64_t bitValue =
+
165 (static_cast<std::uint64_t>((data[byteIndex] >> bitInByte) & 0x01U) << bitIndex);
+
166
+
167 value |= bitValue;
+
168 }
+
169
+
170 return true;
+
171}
+
172
+
173bool DbcDecoder::ExtractMotorola(const std::vector<std::uint8_t> &data,
+
174 std::uint32_t startBit,
+
175 std::uint32_t length,
+
176 std::uint64_t &value) {
+
177 /*
+
178 * DBC Motorola bit numbering:
+
179 * - startBit points to the most significant bit of the signal
+
180 * - inside a byte, bit numbering goes 7..0
+
181 * - crossing byte boundary moves to the next byte, bit 7
+
182 * Example: a signal starting at 7 follows 7..0, 15..8, 23..16.
+
183 */
+
184
+
185 value = 0U;
+
186
+
187 std::size_t byteIndex = startBit / 8U;
+
188 std::uint32_t bitInByte = startBit % 8U;
+
189
+
190 for (std::uint32_t bitIndex = 0U; bitIndex < length; ++bitIndex) {
+
191 if (byteIndex >= data.size())
+
192 return false;
+
193
+
194 value <<= 1U;
+
195 value |= static_cast<std::uint64_t>((data[byteIndex] >> bitInByte) & 0x01U);
+
196
+
197 if (bitInByte == 0U) {
+
198 ++byteIndex;
+
199 bitInByte = 7U;
+
200 } else
+
201 --bitInByte;
+
202 }
+
203
+
204 return true;
+
205}
+
206
+
207std::int64_t DbcDecoder::SignExtend(std::uint64_t value, std::uint32_t bitLength) {
+
208 if ((bitLength == 0U) || (bitLength > 64U))
+
209 return 0;
+
210
+
211 const std::uint64_t signMask = std::uint64_t(1U) << (bitLength - 1U);
+
212 const std::uint64_t valueMask = (bitLength == 64U)
+
213 ? ~std::uint64_t(0U)
+
214 : (std::uint64_t(1U) << bitLength) - 1U;
+
215 value &= valueMask;
+
216
+
217 if ((value & signMask) == 0U)
+
218 return static_cast<std::int64_t>(value);
+
219
+
220 // The complemented magnitude fits int64_t, including for INT64_MIN.
+
221 return -1 - static_cast<std::int64_t>((~value) & valueMask);
+
222}
+
DecodedFrameValue Decode(const DecodeDatabase &database, const RawCanFrame &frame) const
Decode one raw CAN frame.
+
std::shared_ptr< const DecodeFrame > FindFrame(const DecodeDatabase &database, std::uint32_t canId, bool isExtended) const
Find frame definition by CAN ID.
Immutable runtime snapshot. Copies share storage; reload replaces a snapshot. Owning lookup handles a...
std::shared_ptr< const DecodeFrame > FindFrame(const FrameKey &key) const
Created: 2026-03-13 Author: Deeaitch (Dim. Himro)
Runtime-ready frame definition.
+
std::uint8_t dlc
Runtime-ready signal definition.
@@ -299,16 +328,17 @@ $(document).ready(function(){initNavTree('dbc__decoder_8cpp_source.html',''); in
std::uint32_t startBit
ByteOrder byteOrder
std::uint32_t length
-
Fully decoded frame.
Definition dbc_decoder.h:62
- -
std::shared_ptr< const DecodeFrame > definition
Definition dbc_decoder.h:63
- -
One decoded signal value.
Definition dbc_decoder.h:43
- -
RawSignalValue rawValue
Definition dbc_decoder.h:46
- -
std::shared_ptr< const DecodeSignal > definition
Definition dbc_decoder.h:44
- +
Fully decoded frame.
Definition dbc_decoder.h:71
+ +
std::shared_ptr< const DecodeFrame > definition
Definition dbc_decoder.h:72
+ +
FrameDecodeStatus status
Definition dbc_decoder.h:76
+
One decoded signal value.
Definition dbc_decoder.h:52
+ +
RawSignalValue rawValue
Definition dbc_decoder.h:55
+ +
std::shared_ptr< const DecodeSignal > definition
Definition dbc_decoder.h:53
+
Stable frame identifier: normalized CAN ID and frame type.
Raw CAN frame used for runtime or trace decoding.
Definition dbc_decoder.h:24
std::vector< std::uint8_t > data
Definition dbc_decoder.h:27
diff --git a/docs/html/dbc__decoder_8h.html b/docs/html/dbc__decoder_8h.html index 5cdac13..dd09886 100644 --- a/docs/html/dbc__decoder_8h.html +++ b/docs/html/dbc__decoder_8h.html @@ -130,9 +130,16 @@ Enumerations , Inactive ,
  Valid +, PhysicalConversionFailed
}   +enum class  FrameDecodeStatus { UnknownFrame +, InsufficientPayload +, SignalErrors +, Valid + } + 

Detailed Description

Created: 2026-03-13 Author: Deeaitch (Dim. Himro)

@@ -162,6 +169,31 @@ Enumerations

Definition at line 32 of file dbc_decoder.h.

+
+
+ +

◆ FrameDecodeStatus

+ +
+
+ + + + + +
+ + + + +
enum class FrameDecodeStatus
+
+strong
+
+

Frame summary: short payload takes precedence over individual signal errors.

+ +

Definition at line 42 of file dbc_decoder.h.

+
diff --git a/docs/html/dbc__decoder_8h.js b/docs/html/dbc__decoder_8h.js index b9711cc..8c0b117 100644 --- a/docs/html/dbc__decoder_8h.js +++ b/docs/html/dbc__decoder_8h.js @@ -3,5 +3,11 @@ var dbc__decoder_8h = [ "RawCanFrame", "structRawCanFrame.html", "structRawCanFrame" ], [ "DecodedSignalValue", "structDecodedSignalValue.html", "structDecodedSignalValue" ], [ "DecodedFrameValue", "structDecodedFrameValue.html", "structDecodedFrameValue" ], - [ "DbcDecoder", "classDbcDecoder.html", "classDbcDecoder" ] + [ "DbcDecoder", "classDbcDecoder.html", "classDbcDecoder" ], + [ "FrameDecodeStatus", "dbc__decoder_8h.html#a855b2bc36cb18bfbae5ff30a2394e961", [ + [ "UnknownFrame", "dbc__decoder_8h.html#a855b2bc36cb18bfbae5ff30a2394e961a85f5d387f9a01dce258470e1a5a23332", null ], + [ "InsufficientPayload", "dbc__decoder_8h.html#a855b2bc36cb18bfbae5ff30a2394e961a7c9fa088a1e7db1fc83cb62aefd3ea7f", null ], + [ "SignalErrors", "dbc__decoder_8h.html#a855b2bc36cb18bfbae5ff30a2394e961aedeb8da96660592dfe0853f212b77469", null ], + [ "Valid", "dbc__decoder_8h.html#a855b2bc36cb18bfbae5ff30a2394e961a3ac705f2acd51a4613f9188c05c91d0d", null ] + ] ] ]; \ No newline at end of file diff --git a/docs/html/dbc__decoder_8h_source.html b/docs/html/dbc__decoder_8h_source.html index ab35196..c003037 100644 --- a/docs/html/dbc__decoder_8h_source.html +++ b/docs/html/dbc__decoder_8h_source.html @@ -122,98 +122,114 @@ $(document).ready(function(){initNavTree('dbc__decoder_8h_source.html',''); init
34 InsufficientPayload,
35 SelectorUnavailable,
36 Inactive,
-
37 Valid
-
38};
-
39
-
- -
44 std::shared_ptr<const DecodeSignal> definition;
- -
46 RawSignalValue rawValue = std::uint64_t(0U);
-
47 double physicalValue = 0.0;
-
48 bool valid = false;
-
49 SignalDecodeStatus status = SignalDecodeStatus::InvalidDefinition;
-
50
-
-
52 const std::string *GetValueDescription() const {
-
53 return valid && definition ? FindValueDescription(definition->valueDescriptions, rawValue) : nullptr;
-
54 }
+
37 Valid,
+
38 PhysicalConversionFailed
+
39};
+
40
+
+ +
43 UnknownFrame,
+
44 InsufficientPayload,
+
45 SignalErrors,
+
46 Valid
+
47};
-
55
-
56 DecodedSignalValue() = default;
-
57};
+
48
+
+ +
53 std::shared_ptr<const DecodeSignal> definition;
+ +
55 RawSignalValue rawValue = std::uint64_t(0U);
+
56 double physicalValue = 0.0;
+
57 bool valid = false;
+
58 SignalDecodeStatus status = SignalDecodeStatus::InvalidDefinition;
+
59
+
+
61 const std::string *GetValueDescription() const {
+
62 return valid && definition ? FindValueDescription(definition->valueDescriptions, rawValue) : nullptr;
+
63 }
-
58
-
- -
63 std::shared_ptr<const DecodeFrame> definition;
- -
65 std::vector<DecodedSignalValue> signals;
-
66 bool valid = false;
-
68 DecodedFrameValue() = default;
-
69};
+
64
+
65 DecodedSignalValue() = default;
+
66};
-
70
-
- -
75public:
-
83 std::shared_ptr<const DecodeFrame> FindFrame(const DecodeDatabase &database,
-
84 std::uint32_t canId,
-
85 bool isExtended) const;
-
86
- -
94 const RawCanFrame &frame) const;
-
95
-
96private:
-
97 struct SelectorState {
-
98 const DecodeSignal *definition = nullptr;
-
99 bool valid = false;
-
100 bool available = false;
-
101 std::uint64_t value = 0U;
-
102 };
-
103
-
104 static SelectorState ReadSelector(const DecodeFrame &definition, const RawCanFrame &frame);
-
105 static DecodedSignalValue DecodeOneSignal(const DecodeSignal &signal,
-
106 const RawCanFrame &frame,
-
107 const SelectorState &selector);
-
108
-
109 static bool ExtractUnsigned(const std::vector<std::uint8_t> &data,
-
110 const DecodeSignal &signal,
-
111 std::uint64_t &value);
-
112
-
113 static bool ExtractIntel(const std::vector<std::uint8_t> &data,
-
114 std::uint32_t startBit,
-
115 std::uint32_t length,
-
116 std::uint64_t &value);
-
117
-
118 static bool ExtractMotorola(const std::vector<std::uint8_t> &data,
-
119 std::uint32_t startBit,
-
120 std::uint32_t length,
-
121 std::uint64_t &value);
+
67
+
+ +
72 std::shared_ptr<const DecodeFrame> definition;
+ +
74 std::vector<DecodedSignalValue> signals;
+
75 bool valid = false;
+
76 FrameDecodeStatus status = FrameDecodeStatus::UnknownFrame;
+
78 DecodedFrameValue() = default;
+
79};
+
+
80
+
+ +
85public:
+
93 std::shared_ptr<const DecodeFrame> FindFrame(const DecodeDatabase &database,
+
94 std::uint32_t canId,
+
95 bool isExtended) const;
+
96
+ +
105 const RawCanFrame &frame) const;
+
106
+
107private:
+
108 static bool IsValidDefinition(const DecodeSignal &signal, std::uint8_t dlc);
+
109
+
110 struct SelectorState {
+
111 const DecodeSignal *definition = nullptr;
+
112 bool valid = false;
+
113 bool available = false;
+
114 std::uint64_t value = 0U;
+
115 };
+
116
+
117 static SelectorState ReadSelector(const DecodeFrame &definition, const RawCanFrame &frame);
+
118 static DecodedSignalValue DecodeOneSignal(const DecodeSignal &signal,
+
119 std::uint8_t dlc,
+
120 const RawCanFrame &frame,
+
121 const SelectorState &selector);
122
-
123 static std::int64_t SignExtend(std::uint64_t value, std::uint32_t bitLength);
-
124};
+
123 static bool ExtractUnsigned(const std::vector<std::uint8_t> &data,
+
124 const DecodeSignal &signal,
+
125 std::uint64_t &value);
+
126
+
127 static bool ExtractIntel(const std::vector<std::uint8_t> &data,
+
128 std::uint32_t startBit,
+
129 std::uint32_t length,
+
130 std::uint64_t &value);
+
131
+
132 static bool ExtractMotorola(const std::vector<std::uint8_t> &data,
+
133 std::uint32_t startBit,
+
134 std::uint32_t length,
+
135 std::uint64_t &value);
+
136
+
137 static std::int64_t SignExtend(std::uint64_t value, std::uint32_t bitLength);
+
138};
-
125
-
126#endif /* DBC_DECODER_H */
-
Runtime CAN decoder using prebuilt decode database.
Definition dbc_decoder.h:74
-
DecodedFrameValue Decode(const DecodeDatabase &database, const RawCanFrame &frame) const
Decode one raw CAN frame.
-
std::shared_ptr< const DecodeFrame > FindFrame(const DecodeDatabase &database, std::uint32_t canId, bool isExtended) const
Find frame definition by CAN ID.
+
139
+
140#endif /* DBC_DECODER_H */
+
Runtime CAN decoder using prebuilt decode database.
Definition dbc_decoder.h:84
+
DecodedFrameValue Decode(const DecodeDatabase &database, const RawCanFrame &frame) const
Decode one raw CAN frame.
+
std::shared_ptr< const DecodeFrame > FindFrame(const DecodeDatabase &database, std::uint32_t canId, bool isExtended) const
Find frame definition by CAN ID.
Immutable runtime snapshot. Copies share storage; reload replaces a snapshot. Owning lookup handles a...
+
FrameDecodeStatus
Definition dbc_decoder.h:42
Created: 2026-03-13 Author: Deeaitch (Dim. Himro)
Runtime-ready frame definition.
Runtime-ready signal definition.
-
Fully decoded frame.
Definition dbc_decoder.h:62
- -
std::shared_ptr< const DecodeFrame > definition
Definition dbc_decoder.h:63
- -
One decoded signal value.
Definition dbc_decoder.h:43
- -
RawSignalValue rawValue
Definition dbc_decoder.h:46
- -
std::shared_ptr< const DecodeSignal > definition
Definition dbc_decoder.h:44
- -
const std::string * GetValueDescription() const
Definition dbc_decoder.h:52
+
Fully decoded frame.
Definition dbc_decoder.h:71
+ +
std::shared_ptr< const DecodeFrame > definition
Definition dbc_decoder.h:72
+ +
FrameDecodeStatus status
Definition dbc_decoder.h:76
+
One decoded signal value.
Definition dbc_decoder.h:52
+ +
RawSignalValue rawValue
Definition dbc_decoder.h:55
+ +
std::shared_ptr< const DecodeSignal > definition
Definition dbc_decoder.h:53
+ +
const std::string * GetValueDescription() const
Definition dbc_decoder.h:61
Stable frame identifier: normalized CAN ID and frame type.
Raw CAN frame used for runtime or trace decoding.
Definition dbc_decoder.h:24
std::vector< std::uint8_t > data
Definition dbc_decoder.h:27
diff --git a/docs/html/dbc__decoder__test_8cpp_source.html b/docs/html/dbc__decoder__test_8cpp_source.html index 8162103..67ceeb8 100644 --- a/docs/html/dbc__decoder__test_8cpp_source.html +++ b/docs/html/dbc__decoder__test_8cpp_source.html @@ -101,186 +101,426 @@ $(document).ready(function(){initNavTree('dbc__decoder__test_8cpp_source.html','
1#include "dbc_decoder.h"
2
3#include <QtTest>
-
4#include <limits>
-
5#include <sstream>
-
6
-
7struct DecodeCase {
-
8 const char *name;
-
9 ByteOrder byteOrder;
-
10 std::uint32_t startBit;
-
11 std::uint32_t length;
-
12 ValueType valueType;
-
13 std::vector<std::uint8_t> data;
-
14 bool valid;
-
15 std::int64_t rawValue;
-
16 double factor;
-
17 double offset;
-
18 double physicalValue;
-
19};
-
20
-
21Q_DECLARE_METATYPE(DecodeCase)
-
22
-
23class DbcDecoderTest : public QObject {
-
24 Q_OBJECT
-
25private Q_SLOTS:
-
26 void decode_data();
-
27 void decode();
-
28 void boundaries();
-
29};
-
30
-
31void DbcDecoderTest::decode_data() {
-
32 QTest::addColumn<DecodeCase>("test");
-
33 const DecodeCase cases[] = {
-
34 {"Motorola reproduction", ByteOrder::Motorola, 7U, 16U, ValueType::Unsigned, {0x12, 0x34}, true, 0x1234, 1.0, 0.0, 4660.0},
-
35 {"Motorola within byte", ByteOrder::Motorola, 5U, 4U, ValueType::Unsigned, {0xAC}, true, 11, 1.0, 0.0, 11.0},
-
36 {"Motorola unaligned crossing", ByteOrder::Motorola, 3U, 12U, ValueType::Unsigned, {0xAB, 0xCD}, true, 0xBCD, 1.0, 0.0, 3021.0},
-
37 {"Motorola starts at bit zero", ByteOrder::Motorola, 0U, 2U, ValueType::Unsigned, {0x01, 0x80}, true, 3, 1.0, 0.0, 3.0},
-
38 {"Motorola second byte", ByteOrder::Motorola, 15U, 16U, ValueType::Unsigned, {0xFF, 0x12, 0x34}, true, 0x1234, 1.0, 0.0, 4660.0},
-
39 {"Motorola signed scaled", ByteOrder::Motorola, 7U, 16U, ValueType::Signed, {0xFF, 0xFE}, true, -2, 0.5, 10.0, 9.0},
-
40 {"Motorola 64 bits", ByteOrder::Motorola, 7U, 64U, ValueType::Unsigned, {0, 0, 0, 0, 0, 0, 0x12, 0x34}, true, 0x1234, 1.0, 0.0, 4660.0},
-
41 {"Motorola 64 bits high bit", ByteOrder::Motorola, 7U, 64U, ValueType::Unsigned, {0x40, 0, 0, 0, 0, 0, 0, 0}, true, 0x4000000000000000LL, 1.0, 0.0, 4611686018427387904.0},
-
42 {"Motorola short payload", ByteOrder::Motorola, 7U, 16U, ValueType::Unsigned, {0x12}, false, 0, 1.0, 0.0, 0.0},
-
43 {"Motorola empty payload", ByteOrder::Motorola, 7U, 8U, ValueType::Unsigned, {}, false, 0, 1.0, 0.0, 0.0},
-
44 {"Motorola large start", ByteOrder::Motorola, std::numeric_limits<std::uint32_t>::max(), 8U, ValueType::Unsigned, {0xFF}, false, 0, 1.0, 0.0, 0.0},
-
45 {"Motorola zero length", ByteOrder::Motorola, 7U, 0U, ValueType::Unsigned, {0xFF}, false, 0, 1.0, 0.0, 0.0},
-
46 {"Motorola excessive length", ByteOrder::Motorola, 7U, 65U, ValueType::Unsigned,
-
47 std::vector<std::uint8_t>(9U, 0xFF), false, 0, 1.0, 0.0, 0.0},
-
48 {"Intel regression", ByteOrder::Intel, 0U, 16U, ValueType::Unsigned, {0x12, 0x34}, true, 0x3412, 1.0, 0.0, 13330.0},
-
49 {"Intel signed regression", ByteOrder::Intel, 0U, 16U, ValueType::Signed, {0xFE, 0xFF}, true, -2, 0.5, 10.0, 9.0},
-
50 {"Intel short payload", ByteOrder::Intel, 0U, 16U, ValueType::Unsigned, {0x12}, false, 0, 1.0, 0.0, 0.0}};
-
51
-
52 for (const DecodeCase &test: cases)
-
53 QTest::newRow(test.name) << test;
-
54}
-
55
-
56void DbcDecoderTest::decode() {
-
57 QFETCH(DecodeCase, test);
-
58 DecodeSignal signal;
-
59 signal.name = test.name;
-
60 signal.byteOrder = test.byteOrder;
-
61 signal.startBit = test.startBit;
-
62 signal.length = test.length;
-
63 signal.valueType = test.valueType;
-
64 signal.factor = test.factor;
-
65 signal.offset = test.offset;
-
66
-
67 DecodeFrame definition;
-
68 definition.canId = 0x123U;
-
69 definition.dlc = static_cast<std::uint8_t>(test.data.size());
-
70 definition.signals.push_back(signal);
-
71 const DecodeDatabase database({definition});
-
72
-
73 RawCanFrame frame;
-
74 frame.canId = definition.canId;
-
75 frame.data = test.data;
-
76 const DecodedFrameValue result = DbcDecoder().Decode(database, frame);
-
77 QVERIFY2(result.valid, test.name);
-
78 QCOMPARE(result.definition.get(), &database.GetFrames()[0]);
-
79 QCOMPARE(result.signals.size(), std::size_t(1U));
-
80 const DecodedSignalValue &decoded = result.signals[0];
-
81 QCOMPARE(decoded.definition.get(), &database.GetFrames()[0].signals[0]);
-
82 QCOMPARE(decoded.valid, test.valid);
-
83 if (!test.valid)
-
84 return;
-
85
-
86 if (test.valueType == ValueType::Signed) {
-
87 const std::int64_t *raw = std::get_if<std::int64_t>(&decoded.rawValue);
-
88 QVERIFY(raw != nullptr);
-
89 QCOMPARE(*raw, test.rawValue);
-
90 } else {
-
91 const std::uint64_t *raw = std::get_if<std::uint64_t>(&decoded.rawValue);
-
92 QVERIFY(raw != nullptr);
-
93 QCOMPARE(*raw, static_cast<std::uint64_t>(test.rawValue));
-
94 }
-
95 QCOMPARE(decoded.physicalValue, test.physicalValue);
-
96}
-
97
-
98void DbcDecoderTest::boundaries() {
-
99 for (ByteOrder order: {ByteOrder::Intel, ByteOrder::Motorola}) {
-
100 for (ValueType type: {ValueType::Unsigned, ValueType::Signed}) {
-
101 for (std::uint64_t bits: {std::uint64_t(0U), std::uint64_t(0x7FFFFFFFFFFFFFFFULL),
-
102 std::uint64_t(0x8000000000000000ULL),
-
103 std::uint64_t(0x8000000000000001ULL),
-
104 std::uint64_t(0xFFFFFFFFFFFFFFFFULL)}) {
-
105 DecodeFrame definition;
-
106 DecodeSignal signal;
-
107 signal.startBit = order == ByteOrder::Intel ? 0U : 7U;
-
108 signal.length = 64U;
-
109 signal.byteOrder = order;
-
110 signal.valueType = type;
-
111 signal.factor = 0.5;
-
112 signal.offset = 10.0;
-
113 definition.signals.push_back(signal);
-
114 const DecodeDatabase database({definition});
-
115 RawCanFrame frame;
-
116 for (unsigned byte = 0U; byte < 8U; ++byte) {
-
117 const unsigned shift = (order == ByteOrder::Intel ? byte : 7U - byte) * 8U;
-
118 frame.data.push_back(static_cast<std::uint8_t>(bits >> shift));
-
119 }
-
120 const DecodedFrameValue result = DbcDecoder().Decode(database, frame);
-
121 const std::int64_t signedExpected = bits <= 0x7FFFFFFFFFFFFFFFULL
-
122 ? static_cast<std::int64_t>(bits)
-
123 : std::numeric_limits<std::int64_t>::min() +
-
124 static_cast<std::int64_t>(bits - 0x8000000000000000ULL);
-
125 const DecodedSignalValue &decoded = result.signals.at(0U);
-
126 const RawSignalValue expected = type == ValueType::Unsigned
-
127 ? std::variant<std::int64_t, std::uint64_t>(bits)
-
128 : std::variant<std::int64_t, std::uint64_t>(signedExpected);
-
129 const double physical = (type == ValueType::Unsigned
-
130 ? static_cast<double>(bits)
-
131 : static_cast<double>(signedExpected)) *
-
132 0.5 +
-
133 10.0;
-
134 std::ostringstream actualText;
-
135 std::ostringstream expectedText;
-
136 if (const std::int64_t *signedValue = std::get_if<std::int64_t>(&decoded.rawValue))
-
137 actualText << *signedValue;
-
138 else
-
139 actualText << std::get<std::uint64_t>(decoded.rawValue);
-
140 if (type == ValueType::Unsigned)
-
141 expectedText << bits;
-
142 else
-
143 expectedText << signedExpected;
-
144 QVERIFY(decoded.valid);
-
145 QVERIFY(decoded.rawValue == expected);
-
146 QCOMPARE(decoded.physicalValue, physical);
-
147 QCOMPARE(actualText.str(), expectedText.str());
-
148 }
-
149 }
-
150 }
-
151}
-
152
-
153int RunDecoderTests(int argc, char **argv) {
-
154 DbcDecoderTest tests;
-
155 return QTest::qExec(&tests, argc, argv);
-
156}
-
157
-
158#include "dbc_decoder_test.moc"
-
Runtime CAN decoder using prebuilt decode database.
Definition dbc_decoder.h:74
-
DecodedFrameValue Decode(const DecodeDatabase &database, const RawCanFrame &frame) const
Decode one raw CAN frame.
+
4#include <cmath>
+
5#include <limits>
+
6#include <sstream>
+
7
+
8struct DecodeCase {
+
9 const char *name;
+
10 ByteOrder byteOrder;
+
11 std::uint32_t startBit;
+
12 std::uint32_t length;
+
13 ValueType valueType;
+
14 std::vector<std::uint8_t> data;
+
15 bool valid;
+
16 std::int64_t rawValue;
+
17 double factor;
+
18 double offset;
+
19 double physicalValue;
+
20};
+
21
+
22Q_DECLARE_METATYPE(DecodeCase)
+
23
+
24class DbcDecoderTest : public QObject {
+
25 Q_OBJECT
+
26private Q_SLOTS:
+
27 void decode_data();
+
28 void decode();
+
29 void boundaries();
+
30 void payloadPolicy_data();
+
31 void payloadPolicy();
+
32 void invalidDefinitions_data();
+
33 void invalidDefinitions();
+
34 void physicalConversion_data();
+
35 void physicalConversion();
+
36 void selectorConversionFailure();
+
37 void zeroDlc();
+
38};
+
39
+
40void DbcDecoderTest::decode_data() {
+
41 QTest::addColumn<DecodeCase>("test");
+
42 const DecodeCase cases[] = {
+
43 {"Motorola reproduction", ByteOrder::Motorola, 7U, 16U, ValueType::Unsigned, {0x12, 0x34}, true, 0x1234, 1.0, 0.0, 4660.0},
+
44 {"Motorola within byte", ByteOrder::Motorola, 5U, 4U, ValueType::Unsigned, {0xAC}, true, 11, 1.0, 0.0, 11.0},
+
45 {"Motorola unaligned crossing", ByteOrder::Motorola, 3U, 12U, ValueType::Unsigned, {0xAB, 0xCD}, true, 0xBCD, 1.0, 0.0, 3021.0},
+
46 {"Motorola starts at bit zero", ByteOrder::Motorola, 0U, 2U, ValueType::Unsigned, {0x01, 0x80}, true, 3, 1.0, 0.0, 3.0},
+
47 {"Motorola second byte", ByteOrder::Motorola, 15U, 16U, ValueType::Unsigned, {0xFF, 0x12, 0x34}, true, 0x1234, 1.0, 0.0, 4660.0},
+
48 {"Motorola signed scaled", ByteOrder::Motorola, 7U, 16U, ValueType::Signed, {0xFF, 0xFE}, true, -2, 0.5, 10.0, 9.0},
+
49 {"Motorola 64 bits", ByteOrder::Motorola, 7U, 64U, ValueType::Unsigned, {0, 0, 0, 0, 0, 0, 0x12, 0x34}, true, 0x1234, 1.0, 0.0, 4660.0},
+
50 {"Motorola 64 bits high bit", ByteOrder::Motorola, 7U, 64U, ValueType::Unsigned, {0x40, 0, 0, 0, 0, 0, 0, 0}, true, 0x4000000000000000LL, 1.0, 0.0, 4611686018427387904.0},
+
51 {"Motorola short payload", ByteOrder::Motorola, 7U, 16U, ValueType::Unsigned, {0x12}, false, 0, 1.0, 0.0, 0.0},
+
52 {"Motorola empty payload", ByteOrder::Motorola, 7U, 8U, ValueType::Unsigned, {}, false, 0, 1.0, 0.0, 0.0},
+
53 {"Motorola large start", ByteOrder::Motorola, std::numeric_limits<std::uint32_t>::max(), 8U, ValueType::Unsigned, {0xFF}, false, 0, 1.0, 0.0, 0.0},
+
54 {"Motorola zero length", ByteOrder::Motorola, 7U, 0U, ValueType::Unsigned, {0xFF}, false, 0, 1.0, 0.0, 0.0},
+
55 {"Motorola excessive length", ByteOrder::Motorola, 7U, 65U, ValueType::Unsigned,
+
56 std::vector<std::uint8_t>(9U, 0xFF), false, 0, 1.0, 0.0, 0.0},
+
57 {"Intel regression", ByteOrder::Intel, 0U, 16U, ValueType::Unsigned, {0x12, 0x34}, true, 0x3412, 1.0, 0.0, 13330.0},
+
58 {"Intel signed regression", ByteOrder::Intel, 0U, 16U, ValueType::Signed, {0xFE, 0xFF}, true, -2, 0.5, 10.0, 9.0},
+
59 {"Intel short payload", ByteOrder::Intel, 0U, 16U, ValueType::Unsigned, {0x12}, false, 0, 1.0, 0.0, 0.0}};
+
60
+
61 for (const DecodeCase &test: cases)
+
62 QTest::newRow(test.name) << test;
+
63}
+
64
+
65void DbcDecoderTest::decode() {
+
66 QFETCH(DecodeCase, test);
+
67 DecodeSignal signal;
+
68 signal.name = test.name;
+
69 signal.byteOrder = test.byteOrder;
+
70 signal.startBit = test.startBit;
+
71 signal.length = test.length;
+
72 signal.valueType = test.valueType;
+
73 signal.factor = test.factor;
+
74 signal.offset = test.offset;
+
75
+
76 DecodeFrame definition;
+
77 definition.canId = 0x123U;
+
78 definition.dlc = 8U;
+
79 definition.signals.push_back(signal);
+
80 const DecodeDatabase database({definition});
+
81
+
82 RawCanFrame frame;
+
83 frame.canId = definition.canId;
+
84 frame.data = test.data;
+
85 const DecodedFrameValue result = DbcDecoder().Decode(database, frame);
+
86 QVERIFY2(result.valid, test.name);
+
87 QCOMPARE(result.definition.get(), &database.GetFrames()[0]);
+
88 QCOMPARE(result.signals.size(), std::size_t(1U));
+
89 const DecodedSignalValue &decoded = result.signals[0];
+
90 QCOMPARE(decoded.definition.get(), &database.GetFrames()[0].signals[0]);
+
91 QCOMPARE(decoded.valid, test.valid);
+
92 if (!test.valid)
+
93 return;
+
94
+
95 if (test.valueType == ValueType::Signed) {
+
96 const std::int64_t *raw = std::get_if<std::int64_t>(&decoded.rawValue);
+
97 QVERIFY(raw != nullptr);
+
98 QCOMPARE(*raw, test.rawValue);
+
99 } else {
+
100 const std::uint64_t *raw = std::get_if<std::uint64_t>(&decoded.rawValue);
+
101 QVERIFY(raw != nullptr);
+
102 QCOMPARE(*raw, static_cast<std::uint64_t>(test.rawValue));
+
103 }
+
104 QCOMPARE(decoded.physicalValue, test.physicalValue);
+
105}
+
106
+
107void DbcDecoderTest::boundaries() {
+
108 for (ByteOrder order: {ByteOrder::Intel, ByteOrder::Motorola}) {
+
109 for (ValueType type: {ValueType::Unsigned, ValueType::Signed}) {
+
110 for (std::uint64_t bits: {std::uint64_t(0U), std::uint64_t(0x7FFFFFFFFFFFFFFFULL),
+
111 std::uint64_t(0x8000000000000000ULL),
+
112 std::uint64_t(0x8000000000000001ULL),
+
113 std::uint64_t(0xFFFFFFFFFFFFFFFFULL)}) {
+
114 DecodeFrame definition;
+
115 definition.dlc = 8U;
+
116 DecodeSignal signal;
+
117 signal.startBit = order == ByteOrder::Intel ? 0U : 7U;
+
118 signal.length = 64U;
+
119 signal.byteOrder = order;
+
120 signal.valueType = type;
+
121 signal.factor = 0.5;
+
122 signal.offset = 10.0;
+
123 definition.signals.push_back(signal);
+
124 const DecodeDatabase database({definition});
+
125 RawCanFrame frame;
+
126 for (unsigned byte = 0U; byte < 8U; ++byte) {
+
127 const unsigned shift = (order == ByteOrder::Intel ? byte : 7U - byte) * 8U;
+
128 frame.data.push_back(static_cast<std::uint8_t>(bits >> shift));
+
129 }
+
130 const DecodedFrameValue result = DbcDecoder().Decode(database, frame);
+
131 const std::int64_t signedExpected = bits <= 0x7FFFFFFFFFFFFFFFULL
+
132 ? static_cast<std::int64_t>(bits)
+
133 : std::numeric_limits<std::int64_t>::min() +
+
134 static_cast<std::int64_t>(bits - 0x8000000000000000ULL);
+
135 const DecodedSignalValue &decoded = result.signals.at(0U);
+
136 const RawSignalValue expected = type == ValueType::Unsigned
+
137 ? std::variant<std::int64_t, std::uint64_t>(bits)
+
138 : std::variant<std::int64_t, std::uint64_t>(signedExpected);
+
139 const double physical = (type == ValueType::Unsigned
+
140 ? static_cast<double>(bits)
+
141 : static_cast<double>(signedExpected)) *
+
142 0.5 +
+
143 10.0;
+
144 std::ostringstream actualText;
+
145 std::ostringstream expectedText;
+
146 if (const std::int64_t *signedValue = std::get_if<std::int64_t>(&decoded.rawValue))
+
147 actualText << *signedValue;
+
148 else
+
149 actualText << std::get<std::uint64_t>(decoded.rawValue);
+
150 if (type == ValueType::Unsigned)
+
151 expectedText << bits;
+
152 else
+
153 expectedText << signedExpected;
+
154 QVERIFY(decoded.valid);
+
155 QVERIFY(decoded.rawValue == expected);
+
156 QCOMPARE(decoded.physicalValue, physical);
+
157 QCOMPARE(actualText.str(), expectedText.str());
+
158 }
+
159 }
+
160 }
+
161}
+
162
+
163void DbcDecoderTest::payloadPolicy_data() {
+
164 QTest::addColumn<unsigned>("bytes");
+
165 QTest::addColumn<FrameDecodeStatus>("status");
+
166 QTest::newRow("empty") << 0U << FrameDecodeStatus::InsufficientPayload;
+
167 QTest::newRow("partial") << 1U << FrameDecodeStatus::InsufficientPayload;
+
168 QTest::newRow("short-with-all-signals") << 2U << FrameDecodeStatus::InsufficientPayload;
+
169 QTest::newRow("exact") << 3U << FrameDecodeStatus::Valid;
+
170 QTest::newRow("long") << 4U << FrameDecodeStatus::Valid;
+
171}
+
172
+
173void DbcDecoderTest::payloadPolicy() {
+
174 QFETCH(unsigned, bytes);
+
175 QFETCH(FrameDecodeStatus, status);
+
176 for (ByteOrder order: {ByteOrder::Intel, ByteOrder::Motorola}) {
+
177 DecodeFrame definition;
+
178 definition.canId = 17U;
+
179 definition.dlc = 3U;
+
180 DecodeSignal signal;
+
181 signal.name = "First";
+
182 signal.length = 8U;
+
183 signal.byteOrder = order;
+
184 signal.startBit = order == ByteOrder::Intel ? 0U : 7U;
+
185 definition.signals.push_back(signal);
+
186 signal.name = "Second";
+
187 signal.startBit += 8U;
+
188 definition.signals.push_back(signal);
+
189 const DecodeDatabase database({definition});
+
190 RawCanFrame raw;
+
191 raw.canId = 17U;
+
192 raw.data.assign(bytes, 42U);
+
193 const DecodedFrameValue result = DbcDecoder().Decode(database, raw);
+
194 QVERIFY(result.valid);
+
195 QCOMPARE(result.status, status);
+
196 QCOMPARE(result.signals.size(), std::size_t(2U));
+
197 for (std::size_t index = 0U; index < result.signals.size(); ++index) {
+
198 const DecodedSignalValue &value = result.signals[index];
+
199 QCOMPARE(value.status, bytes > index ? SignalDecodeStatus::Valid : SignalDecodeStatus::InsufficientPayload);
+
200 QCOMPARE(value.valid, bytes > index);
+
201 if (value.valid) {
+
202 QCOMPARE(std::get<std::uint64_t>(value.rawValue), std::uint64_t(42U));
+
203 QCOMPARE(value.physicalValue, 42.0);
+
204 }
+
205 }
+
206 raw.isExtended = true;
+
207 const DecodedFrameValue unknown = DbcDecoder().Decode(database, raw);
+
208 QVERIFY(!unknown.valid && !unknown.definition && unknown.signals.empty());
+
209 QCOMPARE(unknown.status, FrameDecodeStatus::UnknownFrame);
+
210 QVERIFY(unknown.key == FrameKey(17U, true));
+
211 QCOMPARE(DbcDecoder().Decode(DecodeDatabase(), raw).status, FrameDecodeStatus::UnknownFrame);
+
212
+
213 definition.signals.clear();
+
214 raw.isExtended = false;
+
215 const DecodedFrameValue empty = DbcDecoder().Decode(DecodeDatabase({definition}), raw);
+
216 QVERIFY(empty.valid && empty.signals.empty());
+
217 QCOMPARE(empty.status, status);
+
218 }
+
219}
+
220
+
221void DbcDecoderTest::invalidDefinitions_data() {
+
222 QTest::addColumn<int>("kind");
+
223 QTest::newRow("zero-length") << 0;
+
224 QTest::newRow("65-bits") << 1;
+
225 QTest::newRow("outside-dlc") << 2;
+
226 QTest::newRow("start-overflow") << 3;
+
227 QTest::newRow("invalid-byte-order") << 4;
+
228 QTest::newRow("invalid-value-type") << 5;
+
229 QTest::newRow("invalid-multiplex-role") << 6;
+
230 QTest::newRow("missing-selector") << 7;
+
231}
+
232
+
233void DbcDecoderTest::invalidDefinitions() {
+
234 QFETCH(int, kind);
+
235 for (ByteOrder order: {ByteOrder::Intel, ByteOrder::Motorola}) {
+
236 DecodeFrame definition;
+
237 definition.dlc = 1U;
+
238 DecodeSignal good;
+
239 good.name = "Good";
+
240 good.length = 4U;
+
241 good.byteOrder = order;
+
242 good.startBit = order == ByteOrder::Intel ? 0U : 7U;
+
243 DecodeSignal bad = good;
+
244 bad.name = "Bad";
+
245 switch (kind) {
+
246 case 0: bad.length = 0U; break;
+
247 case 1: bad.length = 65U; break;
+
248 case 2: bad.startBit += 8U; break;
+
249 case 3: bad.startBit = std::numeric_limits<std::uint32_t>::max(); break;
+
250 case 4: bad.byteOrder = static_cast<ByteOrder>(99); break;
+
251 case 5: bad.valueType = static_cast<ValueType>(99); break;
+
252 case 6: bad.multiplexing.role = static_cast<MultiplexRole>(99); break;
+
253 case 7: bad.multiplexing.role = MultiplexRole::Branch; break;
+
254 }
+
255 definition.signals = {bad, good};
+
256 const DecodeDatabase database({definition});
+
257 RawCanFrame raw;
+
258 raw.data = {0xFFU, 0xFFU}; // Extra bytes must not rescue an invalid definition.
+
259 const DecodedFrameValue result = DbcDecoder().Decode(database, raw);
+
260 QVERIFY(result.valid);
+
261 QCOMPARE(result.status, FrameDecodeStatus::SignalErrors);
+
262 QCOMPARE(result.signals[0].status, SignalDecodeStatus::InvalidDefinition);
+
263 QVERIFY(!result.signals[0].valid && !result.signals[0].GetValueDescription());
+
264 QCOMPARE(result.signals[1].status, SignalDecodeStatus::Valid);
+
265 QCOMPARE(std::get<std::uint64_t>(result.signals[1].rawValue), std::uint64_t(15U));
+
266 QCOMPARE(result.signals[1].physicalValue, 15.0);
+
267 raw.data.clear();
+
268 const DecodedFrameValue shortResult = DbcDecoder().Decode(database, raw);
+
269 QCOMPARE(shortResult.status, FrameDecodeStatus::InsufficientPayload);
+
270 QCOMPARE(shortResult.signals[0].status, SignalDecodeStatus::InvalidDefinition);
+
271 QCOMPARE(shortResult.signals[1].status, SignalDecodeStatus::InsufficientPayload);
+
272 }
+
273}
+
274
+
275void DbcDecoderTest::physicalConversion_data() {
+
276 QTest::addColumn<double>("factor");
+
277 QTest::addColumn<double>("offset");
+
278 QTest::addColumn<bool>("finite");
+
279 const double infinity = std::numeric_limits<double>::infinity();
+
280 const double nan = std::numeric_limits<double>::quiet_NaN();
+
281 const double maximum = std::numeric_limits<double>::max();
+
282 QTest::newRow("nan-factor") << nan << 0.0 << false;
+
283 QTest::newRow("nan-offset") << 1.0 << nan << false;
+
284 QTest::newRow("infinite-factor") << infinity << 0.0 << false;
+
285 QTest::newRow("negative-infinite-factor") << -infinity << 0.0 << false;
+
286 QTest::newRow("infinite-offset") << 1.0 << infinity << false;
+
287 QTest::newRow("negative-infinite-offset") << 1.0 << -infinity << false;
+
288 QTest::newRow("multiply-overflow") << maximum << 0.0 << false;
+
289 QTest::newRow("add-overflow") << maximum / 2.0 << maximum << false;
+
290 QTest::newRow("finite-limit") << maximum / 2.0 << 0.0 << true;
+
291 QTest::newRow("zero-factor") << 0.0 << 42.0 << true;
+
292}
+
293
+
294void DbcDecoderTest::physicalConversion() {
+
295 QFETCH(double, factor);
+
296 QFETCH(double, offset);
+
297 QFETCH(bool, finite);
+
298 for (ValueType type: {ValueType::Signed, ValueType::Unsigned}) {
+
299 DecodeFrame definition;
+
300 definition.dlc = 1U;
+
301 DecodeSignal signal;
+
302 signal.name = "Scaled";
+
303 signal.length = 4U;
+
304 signal.valueType = type;
+
305 signal.factor = factor;
+
306 signal.offset = offset;
+
307 signal.valueDescriptions.emplace(std::uint64_t(2U), "Two");
+
308 definition.signals.push_back(signal);
+
309 signal.name = "Good";
+
310 signal.startBit = 4U;
+
311 signal.factor = 1.0;
+
312 signal.offset = 0.0;
+
313 definition.signals.push_back(signal);
+
314 RawCanFrame raw;
+
315 raw.data = {0x32U};
+
316 const DecodedFrameValue result = DbcDecoder().Decode(DecodeDatabase({definition}), raw);
+
317 QVERIFY(result.valid);
+
318 QCOMPARE(result.status, finite ? FrameDecodeStatus::Valid : FrameDecodeStatus::SignalErrors);
+
319 const DecodedSignalValue &scaled = result.signals[0];
+
320 QCOMPARE(scaled.valid, finite);
+
321 QCOMPARE(scaled.status, finite ? SignalDecodeStatus::Valid : SignalDecodeStatus::PhysicalConversionFailed);
+
322 QCOMPARE(std::isfinite(scaled.physicalValue), finite);
+
323 if (type == ValueType::Signed)
+
324 QCOMPARE(std::get<std::int64_t>(scaled.rawValue), std::int64_t(2));
+
325 else
+
326 QCOMPARE(std::get<std::uint64_t>(scaled.rawValue), std::uint64_t(2));
+
327 QVERIFY(result.signals[1].valid);
+
328 QCOMPARE(result.signals[1].physicalValue, 3.0);
+
329 if (finite) {
+
330 QVERIFY(scaled.GetValueDescription());
+
331 QCOMPARE(*scaled.GetValueDescription(), std::string("Two"));
+
332 } else {
+
333 QVERIFY(!scaled.GetValueDescription());
+
334 }
+
335 }
+
336}
+
337
+
338void DbcDecoderTest::selectorConversionFailure() {
+
339 DecodeFrame definition;
+
340 definition.dlc = 1U;
+
341 DecodeSignal selector;
+
342 selector.name = "Selector";
+
343 selector.length = 1U;
+
344 selector.multiplexing.role = MultiplexRole::Selector;
+
345 selector.factor = std::numeric_limits<double>::infinity();
+
346 DecodeSignal branch;
+
347 branch.name = "Branch";
+
348 branch.startBit = 1U;
+
349 branch.length = 7U;
+
350 branch.multiplexing.role = MultiplexRole::Branch;
+
351 branch.multiplexing.value = 1U;
+
352 definition.signals = {branch, selector};
+
353 RawCanFrame raw;
+
354 raw.data = {85U};
+
355 const DecodedFrameValue result = DbcDecoder().Decode(DecodeDatabase({definition}), raw);
+
356 QCOMPARE(result.status, FrameDecodeStatus::SignalErrors);
+
357 QVERIFY(result.signals[0].valid);
+
358 QCOMPARE(result.signals[0].physicalValue, 42.0);
+
359 QCOMPARE(result.signals[1].status, SignalDecodeStatus::PhysicalConversionFailed);
+
360 QCOMPARE(std::get<std::uint64_t>(result.signals[1].rawValue), std::uint64_t(1U));
+
361 raw.data = {84U};
+
362 selector.factor = 1.0;
+
363 definition.signals = {branch, selector};
+
364 const DecodedFrameValue inactive = DbcDecoder().Decode(DecodeDatabase({definition}), raw);
+
365 QCOMPARE(inactive.status, FrameDecodeStatus::Valid);
+
366 QCOMPARE(inactive.signals[0].status, SignalDecodeStatus::Inactive);
+
367}
+
368
+
369void DbcDecoderTest::zeroDlc() {
+
370 DecodeFrame definition;
+
371 RawCanFrame raw;
+
372 for (const std::vector<std::uint8_t> &data: {std::vector<std::uint8_t>(), std::vector<std::uint8_t>{42U}}) {
+
373 raw.data = data;
+
374 const DecodedFrameValue result = DbcDecoder().Decode(DecodeDatabase({definition}), raw);
+
375 QVERIFY(result.valid && result.signals.empty());
+
376 QCOMPARE(result.status, FrameDecodeStatus::Valid);
+
377 }
+
378 DecodeSignal signal;
+
379 signal.length = 8U;
+
380 definition.signals.push_back(signal);
+
381 const DecodedFrameValue invalid = DbcDecoder().Decode(DecodeDatabase({definition}), raw);
+
382 QCOMPARE(invalid.status, FrameDecodeStatus::SignalErrors);
+
383 QCOMPARE(invalid.signals[0].status, SignalDecodeStatus::InvalidDefinition);
+
384}
+
385
+
386int RunDecoderTests(int argc, char **argv) {
+
387 DbcDecoderTest tests;
+
388 return QTest::qExec(&tests, argc, argv);
+
389}
+
390
+
391#include "dbc_decoder_test.moc"
+
Runtime CAN decoder using prebuilt decode database.
Definition dbc_decoder.h:84
+
DecodedFrameValue Decode(const DecodeDatabase &database, const RawCanFrame &frame) const
Decode one raw CAN frame.
Immutable runtime snapshot. Copies share storage; reload replaces a snapshot. Owning lookup handles a...
Created: 2026-03-13 Author: Deeaitch (Dim. Himro)
+
FrameDecodeStatus
Definition dbc_decoder.h:42
ByteOrder
Signal byte order used for runtime decoding.
ValueType
Signal numeric type.
Runtime-ready frame definition.
+
std::uint8_t dlc
std::uint32_t canId
Runtime-ready signal definition.
std::string name
+
ValueDescriptions valueDescriptions
ValueType valueType
std::uint32_t startBit
ByteOrder byteOrder
std::uint32_t length
-
Fully decoded frame.
Definition dbc_decoder.h:62
-
std::shared_ptr< const DecodeFrame > definition
Definition dbc_decoder.h:63
- -
One decoded signal value.
Definition dbc_decoder.h:43
-
RawSignalValue rawValue
Definition dbc_decoder.h:46
- -
std::shared_ptr< const DecodeSignal > definition
Definition dbc_decoder.h:44
- +
Fully decoded frame.
Definition dbc_decoder.h:71
+ +
std::shared_ptr< const DecodeFrame > definition
Definition dbc_decoder.h:72
+ +
FrameDecodeStatus status
Definition dbc_decoder.h:76
+
One decoded signal value.
Definition dbc_decoder.h:52
+
RawSignalValue rawValue
Definition dbc_decoder.h:55
+ +
std::shared_ptr< const DecodeSignal > definition
Definition dbc_decoder.h:53
+ +
const std::string * GetValueDescription() const
Definition dbc_decoder.h:61
+
Stable frame identifier: normalized CAN ID and frame type.
Raw CAN frame used for runtime or trace decoding.
Definition dbc_decoder.h:24
std::vector< std::uint8_t > data
Definition dbc_decoder.h:27
std::uint32_t canId
Definition dbc_decoder.h:25
diff --git a/docs/html/dbc__lifetime__test_8cpp_source.html b/docs/html/dbc__lifetime__test_8cpp_source.html index 7a96641..0e64f3b 100644 --- a/docs/html/dbc__lifetime__test_8cpp_source.html +++ b/docs/html/dbc__lifetime__test_8cpp_source.html @@ -301,7 +301,7 @@ $(document).ready(function(){initNavTree('dbc__lifetime__test_8cpp_source.html',
201 DecodedFrameValue result = DbcDecoder().Decode(BuildText(firstDbc), raw);
202 QVERIFY(result.valid);
203 QVERIFY(!result.signals[0].valid);
-
204 QCOMPARE(result.signals[0].status, SignalDecodeStatus::InsufficientPayload);
+
204 QCOMPARE(result.signals[0].status, SignalDecodeStatus::InsufficientPayload);
205 QVERIFY(!result.signals[0].GetValueDescription());
206 QCOMPARE(result.signals[0].definition->name, std::string("State"));
207 result = DbcDecoder().Decode(DecodeDatabase(), raw);
@@ -342,9 +342,9 @@ $(document).ready(function(){initNavTree('dbc__lifetime__test_8cpp_source.html',
242#include "dbc_lifetime_test.moc"
Converts parsed DBC data into runtime decode database.
DecodeDatabase Build(const DbcDatabase &source) const
Build runtime decode database.
-
Runtime CAN decoder using prebuilt decode database.
Definition dbc_decoder.h:74
-
DecodedFrameValue Decode(const DecodeDatabase &database, const RawCanFrame &frame) const
Decode one raw CAN frame.
-
std::shared_ptr< const DecodeFrame > FindFrame(const DecodeDatabase &database, std::uint32_t canId, bool isExtended) const
Find frame definition by CAN ID.
+
Runtime CAN decoder using prebuilt decode database.
Definition dbc_decoder.h:84
+
DecodedFrameValue Decode(const DecodeDatabase &database, const RawCanFrame &frame) const
Decode one raw CAN frame.
+
std::shared_ptr< const DecodeFrame > FindFrame(const DecodeDatabase &database, std::uint32_t canId, bool isExtended) const
Find frame definition by CAN ID.
Minimal DBC parser.
Definition dbc_parser.h:33
Immutable runtime snapshot. Copies share storage; reload replaces a snapshot. Owning lookup handles a...
std::shared_ptr< const DecodeFrame > FindFrame(const FrameKey &key) const
@@ -358,15 +358,16 @@ $(document).ready(function(){initNavTree('dbc__lifetime__test_8cpp_source.html',
std::uint32_t canId
Runtime-ready signal definition.
std::string name
-
Fully decoded frame.
Definition dbc_decoder.h:62
- -
std::shared_ptr< const DecodeFrame > definition
Definition dbc_decoder.h:63
- -
One decoded signal value.
Definition dbc_decoder.h:43
- -
std::shared_ptr< const DecodeSignal > definition
Definition dbc_decoder.h:44
- -
const std::string * GetValueDescription() const
Definition dbc_decoder.h:52
+
Fully decoded frame.
Definition dbc_decoder.h:71
+ +
std::shared_ptr< const DecodeFrame > definition
Definition dbc_decoder.h:72
+ +
FrameDecodeStatus status
Definition dbc_decoder.h:76
+
One decoded signal value.
Definition dbc_decoder.h:52
+ +
std::shared_ptr< const DecodeSignal > definition
Definition dbc_decoder.h:53
+ +
const std::string * GetValueDescription() const
Definition dbc_decoder.h:61
Describes one CAN frame from a DBC file.
Definition frame_info.h:24
std::uint32_t canId
Definition frame_info.h:26
Stable frame identifier: normalized CAN ID and frame type.
diff --git a/docs/html/dbc__parser__test_8cpp_source.html b/docs/html/dbc__parser__test_8cpp_source.html index d139fd5..6895b51 100644 --- a/docs/html/dbc__parser__test_8cpp_source.html +++ b/docs/html/dbc__parser__test_8cpp_source.html @@ -633,28 +633,28 @@ $(document).ready(function(){initNavTree('dbc__parser__test_8cpp_source.html',''
533 CHECK(result.signals[0].valid && std::get<std::int64_t>(result.signals[0].rawValue) == -1 &&
534 result.signals[0].physicalValue == -2 && *result.signals[0].GetValueDescription() == "Negative",
535 "Active signed branch and VAL_ use exact raw selector");
-
536 CHECK(!result.signals[1].valid && result.signals[1].status == SignalDecodeStatus::Inactive &&
+
536 CHECK(!result.signals[1].valid && result.signals[1].status == SignalDecodeStatus::Inactive &&
537 result.signals[1].GetValueDescription() == nullptr && result.signals[2].valid,
538 "Inactive branch and unconditional signal");
539 raw.data[0] = 2U;
540 result = DbcDecoder().Decode(runtime, raw);
-
541 CHECK(result.signals[0].status == SignalDecodeStatus::Inactive && result.signals[1].valid &&
+
541 CHECK(result.signals[0].status == SignalDecodeStatus::Inactive && result.signals[1].valid &&
542 std::get<std::uint64_t>(result.signals[1].rawValue) == 255U,
543 "Motorola branch becomes active");
544 raw.data = {3U};
545 result = DbcDecoder().Decode(runtime, raw);
-
546 CHECK(result.signals[0].status == SignalDecodeStatus::Inactive &&
-
547 result.signals[1].status == SignalDecodeStatus::Inactive &&
-
548 result.signals[2].status == SignalDecodeStatus::InsufficientPayload,
+
546 CHECK(result.signals[0].status == SignalDecodeStatus::Inactive &&
+
547 result.signals[1].status == SignalDecodeStatus::Inactive &&
+
548 result.signals[2].status == SignalDecodeStatus::InsufficientPayload,
549 "Unmapped selector leaves branches inactive even with missing bytes");
550 raw.data = {1U};
551 result = DbcDecoder().Decode(runtime, raw);
-
552 CHECK(result.signals[0].status == SignalDecodeStatus::InsufficientPayload,
+
552 CHECK(result.signals[0].status == SignalDecodeStatus::InsufficientPayload,
553 "Active branch requires payload");
554 raw.data.clear();
555 result = DbcDecoder().Decode(runtime, raw);
-
556 CHECK(result.signals[0].status == SignalDecodeStatus::SelectorUnavailable &&
-
557 result.signals[3].status == SignalDecodeStatus::InsufficientPayload,
+
556 CHECK(result.signals[0].status == SignalDecodeStatus::SelectorUnavailable &&
+
557 result.signals[3].status == SignalDecodeStatus::InsufficientPayload,
558 "Unavailable selector does not imply inactive branch");
559
560 const std::vector<std::string> invalid = {
@@ -699,19 +699,19 @@ $(document).ready(function(){initNavTree('dbc__parser__test_8cpp_source.html',''
599 "Motorola uint64 selector compares raw value before zero scaling");
600 raw.data[7] = 254U;
601 result = DbcDecoder().Decode(runtime, raw);
-
602 CHECK(result.signals[0].status == SignalDecodeStatus::Inactive,
+
602 CHECK(result.signals[0].status == SignalDecodeStatus::Inactive,
603 "Adjacent large integers remain distinct");
604 std::vector<DecodeFrame> definitions = runtime.GetFrames();
605 definitions[0].signals[1].length = 0U;
606 runtime = DecodeDatabase(definitions);
607 result = DbcDecoder().Decode(runtime, raw);
-
608 CHECK(result.signals[0].status == SignalDecodeStatus::InvalidDefinition &&
-
609 result.signals[1].status == SignalDecodeStatus::InvalidDefinition,
+
608 CHECK(result.signals[0].status == SignalDecodeStatus::InvalidDefinition &&
+
609 result.signals[1].status == SignalDecodeStatus::InvalidDefinition,
610 "Malformed runtime selector distinguished from payload errors");
611 definitions[0].signals.pop_back();
612 runtime = DecodeDatabase(definitions);
613 result = DbcDecoder().Decode(runtime, raw);
-
614 CHECK(result.signals[0].status == SignalDecodeStatus::InvalidDefinition,
+
614 CHECK(result.signals[0].status == SignalDecodeStatus::InvalidDefinition,
615 "Missing runtime selector");
616 fixture.Write("BO_ 1 Frame: 2 ECU\n\n"
617 "SG_ Branch m2 : 8|8@1+ (1,0) [0|255] \"\" ECU\n"
@@ -998,8 +998,8 @@ $(document).ready(function(){initNavTree('dbc__parser__test_8cpp_source.html',''
898#include "dbc_parser_test.moc"
Converts parsed DBC data into runtime decode database.
DecodeDatabase Build(const DbcDatabase &source) const
Build runtime decode database.
-
Runtime CAN decoder using prebuilt decode database.
Definition dbc_decoder.h:74
-
DecodedFrameValue Decode(const DecodeDatabase &database, const RawCanFrame &frame) const
Decode one raw CAN frame.
+
Runtime CAN decoder using prebuilt decode database.
Definition dbc_decoder.h:84
+
DecodedFrameValue Decode(const DecodeDatabase &database, const RawCanFrame &frame) const
Decode one raw CAN frame.
Minimal DBC parser.
Definition dbc_parser.h:33
DbcParseResult ParseFileWithDiagnostics(const std::string &filePath) const
Parse with warnings and a structured first error instead of throwing.
DbcDatabase ParseFile(const std::string &filePath) const
Parse DBC file.
@@ -1019,13 +1019,14 @@ $(document).ready(function(){initNavTree('dbc__parser__test_8cpp_source.html','' -
Fully decoded frame.
Definition dbc_decoder.h:62
-
std::shared_ptr< const DecodeFrame > definition
Definition dbc_decoder.h:63
- -
One decoded signal value.
Definition dbc_decoder.h:43
- - -
const std::string * GetValueDescription() const
Definition dbc_decoder.h:52
+
Fully decoded frame.
Definition dbc_decoder.h:71
+
std::shared_ptr< const DecodeFrame > definition
Definition dbc_decoder.h:72
+ +
FrameDecodeStatus status
Definition dbc_decoder.h:76
+
One decoded signal value.
Definition dbc_decoder.h:52
+ + +
const std::string * GetValueDescription() const
Definition dbc_decoder.h:61
Describes one CAN frame from a DBC file.
Definition frame_info.h:24
bool isExtended
Definition frame_info.h:27
std::string transmitter
Definition frame_info.h:31
diff --git a/docs/html/examples_2main_8cpp_source.html b/docs/html/examples_2main_8cpp_source.html index fbc9d49..46628c8 100644 --- a/docs/html/examples_2main_8cpp_source.html +++ b/docs/html/examples_2main_8cpp_source.html @@ -229,8 +229,8 @@ $(document).ready(function(){initNavTree('examples_2main_8cpp_source.html','');
139}
Converts parsed DBC data into runtime decode database.
DecodeDatabase Build(const DbcDatabase &source) const
Build runtime decode database.
-
Runtime CAN decoder using prebuilt decode database.
Definition dbc_decoder.h:74
-
DecodedFrameValue Decode(const DecodeDatabase &database, const RawCanFrame &frame) const
Decode one raw CAN frame.
+
Runtime CAN decoder using prebuilt decode database.
Definition dbc_decoder.h:84
+
DecodedFrameValue Decode(const DecodeDatabase &database, const RawCanFrame &frame) const
Decode one raw CAN frame.
Minimal DBC parser.
Definition dbc_parser.h:33
DbcDatabase ParseFile(const std::string &filePath) const
Parse DBC file.
Builds a simple tree from parsed DBC database.
@@ -249,10 +249,10 @@ $(document).ready(function(){initNavTree('examples_2main_8cpp_source.html','');
Created: 2026-03-13 Author: Deeaitch (Dim. Himro)
Created: 2026-03-13 Author: Deeaitch (Dim. Himro)
Parsed DBC content stored in a simple internal form.
-
Fully decoded frame.
Definition dbc_decoder.h:62
-
std::shared_ptr< const DecodeFrame > definition
Definition dbc_decoder.h:63
- -
One decoded signal value.
Definition dbc_decoder.h:43
+
Fully decoded frame.
Definition dbc_decoder.h:71
+
std::shared_ptr< const DecodeFrame > definition
Definition dbc_decoder.h:72
+ +
One decoded signal value.
Definition dbc_decoder.h:52
Describes one CAN frame from a DBC file.
Definition frame_info.h:24
bool isExtended
Definition frame_info.h:27
bool hasPgn
Definition frame_info.h:29
diff --git a/docs/html/functions.html b/docs/html/functions.html index c891107..676394d 100644 --- a/docs/html/functions.html +++ b/docs/html/functions.html @@ -199,6 +199,7 @@ $(document).ready(function(){initNavTree('functions.html',''); initResizable();

- s -

diff --git a/docs/html/functions_vars.html b/docs/html/functions_vars.html index 1c8e905..5e15ff6 100644 --- a/docs/html/functions_vars.html +++ b/docs/html/functions_vars.html @@ -169,6 +169,7 @@ $(document).ready(function(){initNavTree('functions_vars.html',''); initResizabl

- s -

diff --git a/docs/html/globals.html b/docs/html/globals.html index cc2431b..7ec6799 100644 --- a/docs/html/globals.html +++ b/docs/html/globals.html @@ -92,6 +92,7 @@ $(document).ready(function(){initNavTree('globals.html',''); initResizable(); })
Here is a list of all documented file members with links to the documentation:
diff --git a/docs/html/globals_enum.html b/docs/html/globals_enum.html index fa9a3ec..5886d5d 100644 --- a/docs/html/globals_enum.html +++ b/docs/html/globals_enum.html @@ -92,6 +92,7 @@ $(document).ready(function(){initNavTree('globals_enum.html',''); initResizable(
Here is a list of all documented enums with links to the documentation:
diff --git a/docs/html/index.qhp b/docs/html/index.qhp index 470f7b3..b02409b 100644 --- a/docs/html/index.qhp +++ b/docs/html/index.qhp @@ -14,125 +14,129 @@
-
+
-
+
-
-
-
+
+
+
+
-
-
-
-
+
+
-
-
-
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
@@ -190,6 +194,7 @@
+
@@ -375,11 +380,18 @@
+
+
+
+
+
+
+
@@ -639,6 +651,7 @@ + @@ -654,6 +667,11 @@ + + + + + @@ -848,6 +866,7 @@ search/enums_0.js search/enums_1.js search/enums_2.js +search/enums_3.js search/files_0.js search/files_1.js search/files_2.js diff --git a/docs/html/md_README.html b/docs/html/md_README.html index 9337e05..f686d23 100644 --- a/docs/html/md_README.html +++ b/docs/html/md_README.html @@ -93,8 +93,8 @@ $(document).ready(function(){initNavTree('md_README.html',''); initResizable();
DBC Parser and Runtime Decode Module Documentation
-

-

+

+

Overview

This module provides a minimal but extensible DBC parser and runtime decode foundation for the FrameTap project.

Its purpose is to:

@@ -116,10 +116,10 @@ Overview

The core library uses C++17 and has no Qt dependency. Qt integration targets the installed Qt 6.8.2, which is the only supported Qt version. Qt Core and Qt Test are used by the regression tests; model tests also link QtGui for graphical metatypes. The optional adapter requires only Qt Core. Live CAN acquisition, trace-file readers, are left to the consuming application. An optional Qt Core model adapter is provided in qt/.

See changelog.md for the release and development history, tests/README.md for test instructions, and generated API documentation for the API reference.


-

+

High-Level Architecture

The module is divided into two main paths.

-

+

1. Parse and UI path

Used for:

    @@ -139,7 +139,7 @@ High-Level Architecture
    TreeNode hierarchy
    ↓
    DbcTreeModel → Qt views
    -

+

2. Runtime decode path

Used for:

    @@ -164,7 +164,7 @@ High-Level Architecture

This separation is intentional.

The tree is useful for UI, but it is not the main data structure for runtime decoding.


-

+

Why the Runtime Decode Layer Exists

A tree structure is convenient for browsing, but a runtime decoder needs something different:

    @@ -181,7 +181,7 @@ Why the Runtime Decode Layer Exists

This avoids forcing UI-oriented structures into a decode role they were not meant for.


-

+

Module Layout

core/ C++17 parser, statement handlers, metadata, tree, and decoder
examples/ Command-line demo (built separately)
@@ -209,9 +209,9 @@ Module Layout Build integration core/core.pro, core/core_files.pri, core/core.pri
-

+

Parsed Data Structures

-

+

<tt>SignalInfo</tt>

Represents one signal as parsed from the DBC file.

Fields:

@@ -238,7 +238,7 @@ Parsed Data Structures

Physical value rule:

physical = raw * factor + offset

-

+

<tt>FrameInfo</tt>

Represents one frame as parsed from the DBC file.

Fields:

@@ -260,7 +260,7 @@ Parsed Data Structures
  • pgn is derived using simplified J1939 logic when applicable

  • -

    +

    <tt>DbcDatabase</tt>

    Top-level parsed DBC container.

    Conceptually:

    @@ -268,9 +268,9 @@ Parsed Data Structures
    └── vector<FrameInfo>

    This is the central structure produced by DbcParser.


    -

    +

    UI Tree Layer

    -

    +

    <tt>TreeNode</tt>

    The UI tree contains three node types:

      @@ -295,14 +295,14 @@ UI Tree Layer

      The tree supports browsing through the optional DbcTreeModel adapter.

      It is not the primary runtime decode structure.


      -

      +

      Runtime Decode Layer

      -

      +

      Purpose

      The decode layer exists so that decoding can be fast and independent from UI concerns.

      Instead of searching a tree, the decoder uses a prepared database with direct lookup.


      -

      +

      <tt>ByteOrder</tt>

      Runtime byte order enum:

        @@ -311,7 +311,7 @@ Purpose

      This is better for decode code than passing around raw DBC characters.


      -

      +

      <tt>ValueType</tt>

      Numeric type enum:

        @@ -320,7 +320,7 @@ Purpose

      This is clearer than combining multiple boolean flags during runtime logic.


      -

      +

      <tt>DecodeSignal</tt>

      Represents one runtime-ready signal definition.

      Fields:

      @@ -340,7 +340,7 @@ Purpose

    This structure contains all information required for extracting and converting a signal value from raw frame data.


    -

    +

    <tt>DecodeFrame</tt>

    Represents one runtime-ready frame definition.

    Fields:

    @@ -357,7 +357,7 @@ Purpose

    This structure is used directly by the decoder.


    -

    +

    <tt>FrameKey</tt>

    Stable identifier for runtime frame matching and application state.

    Fields:

    @@ -367,7 +367,7 @@ Purpose

    This matters because the same numeric identifier must not be confused between standard and extended frames.


    -

    +

    <tt>DecodeDatabase</tt>

    Immutable runtime snapshot with a private frame index. Build it with DbcDecodeBuilder::Build(parsed) or construct it from std::vector<DecodeFrame>. Construction rejects duplicate frame keys and duplicate signal names within a frame with std::invalid_argument, including manually supplied metadata.

      @@ -379,9 +379,9 @@ Purpose

    Frame lookup uses a hash index; signal lookup scans the matched frame's signals. Decoding shares the snapshot without copying definitions or allocating ownership objects per signal.


    -

    +

    Decoder Layer

    -

    +

    <tt>RawCanFrame</tt>

    Represents a raw CAN frame to decode.

    Fields:

    @@ -397,7 +397,7 @@ Decoder Layer
  • unit tests

  • -

    +

    <tt>DecodedSignalValue</tt>

    Represents one decoded signal result.

    Fields:

    @@ -410,9 +410,9 @@ Decoder Layer
  • status

  • -

    +

    Raw value representation and precision

    -

    DecodedSignalValue::rawValue is a C++17 std::variant<std::int64_t, std::uint64_t>. For a valid signal, signed definitions produce the int64_t alternative and unsigned definitions produce the uint64_t alternative. Raw values remain exact across their full 64-bit ranges. Check valid before using the value; an invalid result's default value is not decoded data.

    +

    DecodedSignalValue::rawValue is a C++17 std::variant<std::int64_t, std::uint64_t>. For a valid signal, signed definitions produce the int64_t alternative and unsigned definitions produce the uint64_t alternative. Raw values remain exact across their full 64-bit ranges. Check valid before using the physical value. Raw data is also available when status == PhysicalConversionFailed; other invalid results have no decoded numeric data.

    Use std::get when the numeric type is known, or std::get_if to handle both:

    if (const std::int64_t *value = std::get_if<std::int64_t>(&decodedSignal.rawValue)) {
    std::cout << *value;
    @@ -422,7 +422,7 @@ Raw value representation and precision

    This replaces the previous int64_t field: consumers that read or print rawValue directly must handle the variant.

    Physical conversion uses the active numeric type: physicalValue = static_cast<double>(raw) * factor + offset. physicalValue remains a double and is approximate for large integers. On usual IEEE 754 binary64 implementations, integers are exactly representable through 2^53; above that, some integers round to the same floating-point value. For example, unsigned UINT64_MAX remains exact in rawValue, but converts to approximately 18446744073709551616 in binary64 before scaling. Use rawValue whenever exact integer values are required.


    -

    +

    <tt>DecodedFrameValue</tt>

    Represents one decoded frame result.

    Fields:

    @@ -431,16 +431,32 @@ Raw value representation and precision
  • key
  • signals
  • valid
  • +
  • status
  • -

    valid means a matching frame definition was found. Check each signal's valid separately: insufficient payload data can leave individual signals invalid even when the frame is valid. An unknown frame returns valid == false.

    -

    The decoder applies factor and offset; it does not clamp physical values to the declared minimum and maximum.

    -

    +

    valid means a matching frame definition was found. Check each signal's valid separately: insufficient payload data can leave individual signals invalid even when the frame is valid. An unknown frame returns valid == false, a null definition, its requested key, and an empty signal list.

    +

    FrameDecodeStatus summarizes the result:

    + + + + + + + + + + + +
    Status Meaning
    UnknownFrame No definition matches both CAN ID and standard/extended type
    InsufficientPayload Actual byte count is less than the declared dlc
    SignalErrors Payload is long enough, but at least one signal failed
    Valid Payload is long enough and every signal is valid or inactive
    +

    Short-payload status takes precedence over signal errors; inspect each signal's status for the individual cause. valid retains its existing meaning of a matched definition and does not imply status == FrameDecodeStatus::Valid.

    +

    Payload policy uses dlc as a byte count, including for CAN FD; it is not the encoded wire DLC. A shorter payload is never padded: all signals whose bytes are available still decode, even if other signals fail. A longer payload is accepted; bytes beyond dlc are ignored. Signal bit layouts must fit the declared dlc, so extra bytes cannot rescue an invalid definition. Empty payloads and zero-byte frame definitions follow the same rules. Results include every signal in definition order, preserving successful values and definition handles on partial failures.

    +

    The decoder applies factor and offset; it does not clamp physical values to the declared minimum and maximum. Non-finite conversion results (NaN or positive/ negative infinity), including overflow with finite factor/offset, produce PhysicalConversionFailed. The exact raw value and non-finite physicalValue remain available, while signal valid is false. Non-finite factor/offset in manually constructed definitions follow this same conversion-result check. Physical conversion failure of a selector does not prevent raw branch selection.

    +

    Definition ownership and reload

    DecodedFrameValue::definition and DecodedSignalValue::definition are owning std::shared_ptr<const ...> handles. Each handle retains the immutable runtime snapshot. Definitions remain valid until the last database copy, lookup handle, or decoded result owning that snapshot is released. An independently copied signal result retains its definition even after the parent frame result is removed. Copying or moving results preserves ownership.

    Reload by assigning a newly built database. Stored results continue to describe the old DBC, including its units, scaling, comments, and VAL_ descriptions; subsequent decoding uses the new DBC. Removed definitions cannot be found in the new database, but old results safely retain their old definitions. Keeping even one signal handle retains the whole old snapshot; release stored results when that metadata is no longer needed.

    GetFrames() is a borrowed view. References, iterators, and raw pointers obtained from it or from a handle's .get() do not retain ownership. Keep an owning handle or an unchanged database copy while using them. Do not retain mutable aliases into a vector moved into the database constructor. The raw string pointer from GetValueDescription() follows the same rule: keep the signal definition handle alive while using the string.

    Parsed DbcDatabase/FrameInfo/SignalInfo remain ordinary mutable value containers: borrowed pointers follow standard vector invalidation rules. Builders copy their metadata, so parsed input may be changed or destroyed after building. Tree nodes own metadata copies; the Qt model owns its tree and invalidates model indexes on reset. Runtime handles do not depend on the parsed database or tree. Synchronize replacement of a shared database variable with decoding; immutable snapshots do not make concurrent assignment to the same object safe.

    -

    +

    Stable frame and signal identifiers

    DecodedFrameValue::key is a FrameKey (normalized canId, isExtended), also available through DecodeFrame::GetKey(). DecodedSignalValue::key is a SignalKey (frame key, exact case-sensitive signal name). Both are value types with equality and hashers (FrameKeyHasher, SignalKeyHasher) for application maps and saved selections. Construct the same keys from parsed metadata when needed; do not use addresses or vector positions as identifiers.

    Keys stay equal across reloads and reordering when their components stay equal. Frame renaming and changes to units, bit layout, scaling, or descriptions do not change a key. Changing a CAN ID/frame type or renaming a signal produces a new key. The same name in different frames and standard/extended frames with the same numeric CAN ID remain distinct. Keys identify definitions within one logical DBC database; applications combining databases must add their own database ID. A key is not a version identifier: matching keys in different snapshots may have different metadata. Use valid/the definition handle to distinguish a matched result; default keys alone do not indicate successful decoding.

    @@ -455,17 +471,17 @@ Stable frame and signal identifiers
    }
    Converts parsed DBC data into runtime decode database.
    DecodeDatabase Build(const DbcDatabase &source) const
    Build runtime decode database.
    -
    Runtime CAN decoder using prebuilt decode database.
    Definition dbc_decoder.h:74
    -
    DecodedFrameValue Decode(const DecodeDatabase &database, const RawCanFrame &frame) const
    Decode one raw CAN frame.
    +
    Runtime CAN decoder using prebuilt decode database.
    Definition dbc_decoder.h:84
    +
    DecodedFrameValue Decode(const DecodeDatabase &database, const RawCanFrame &frame) const
    Decode one raw CAN frame.
    Immutable runtime snapshot. Copies share storage; reload replaces a snapshot. Owning lookup handles a...
    -
    Fully decoded frame.
    Definition dbc_decoder.h:62
    - -
    One decoded signal value.
    Definition dbc_decoder.h:43
    - +
    Fully decoded frame.
    Definition dbc_decoder.h:71
    + +
    One decoded signal value.
    Definition dbc_decoder.h:52
    +

    API migration: replace reads of runtime.frames with runtime.GetFrames(). To edit runtime definitions, prepare a vector and construct a new database; frameIndexByKey is now private and built automatically. DbcDecoder::FindFrame and result definition fields now use owning handles instead of raw pointers; definition->name, boolean/null checks, and existing decode calls remain usable. Use .get() only for temporary raw-pointer interoperation with an owner alive.


    -

    +

    <tt>DbcDecoder</tt>

    Main runtime decoder class.

    Responsibilities:

    @@ -482,7 +498,7 @@ Stable frame and signal identifiers
  • Decode(...)

  • -

    +

    Parser Support

    The current parser supports the following DBC elements:

      @@ -493,7 +509,7 @@ Parser Support
    • VAL_ signal value descriptions
    • basic M / mN multiplexing
    -

    +

    Structured diagnostics

    ParseFileWithDiagnostics(path) returns DbcParseResult containing database and diagnostics. Each DbcDiagnostic has severity, code, filePath, lineNumber, and message. Paths preserve the caller's supplied spelling. Lines are one-based physical lines, including blank lines; file-level errors use zero.

    const DbcParser parser;
    @@ -533,9 +549,9 @@ Structured diagnostics

    Common VERSION, NS_, BS_, and BU_ headers produce no diagnostics. Single-token symbols inside the NS_ declaration list are quiet regardless of indentation, even when they name unsupported syntax. Frame, signal, bus, and node definitions or a line with arguments end the list. Actual unsupported statements outside that list are reported. Node and bus metadata are not stored. Other comment forms (CM_, CM_ BU_, CM_ EV_) are ignored by the comment handler; comments only attach to already parsed frames/signals. Supported statements currently require one physical line.

    ParseFile(path) remains compatible: it returns the database, discards warnings, and throws std::runtime_error on the first error with the existing path:line: format. Use ParseFileWithDiagnostics to see skipped syntax. Resource failures such as allocation errors may still throw.


    -

    +

    Supported DBC Syntax

    -

    +

    Frame definition

    Example:

    BO_ 256 EngineData: 8 EEC1
    @@ -547,7 +563,7 @@ Frame definition
  • transmitter ECU

  • -

    +

    Frame validation and parse errors

    BO_ definitions require a decimal CAN ID, a nonempty single-token frame name, a colon, a decimal payload length, and a transmitter token. Spaces or tabs may separate fields, and whitespace before the colon is accepted. Additional trailing fields are rejected.

    Numeric fields must contain decimal digits only; signs, hexadecimal notation, fractional values, suffixes, and overflow are rejected. CAN IDs may contain the 29-bit identifier plus the optional DBC extended flag (bit 31). Bits 29 and 30 are rejected rather than silently discarded. For compatibility, unflagged IDs above 0x7FF are still treated as extended.

    @@ -556,7 +572,7 @@ Frame validation and parse errors
    example.dbc:3: Invalid CAN ID: invalid

    Frame and signal definitions are validated as described here; comprehensive DBC grammar coverage is still planned. File-open exceptions contain the path but no line number. The structured diagnostic API uses line zero for file-level errors.


    -

    +

    Signal definition

    Example:

    SG_ EngineSpeed : 0|16@1+ (0.125,0) [0|8000] "rpm" ECU1,ECU2
    @@ -575,7 +591,7 @@ Signal definition
  • receivers

  • -

    +

    Signal validation and parse errors

    SG_ statements are matched completely. Signal names must be nonempty and contain no whitespace. Spaces and tabs are accepted between syntax elements; byte order and signedness remain adjacent (@0+, @0-, @1+, or @1-). A quoted unit (which may be empty) and a nonempty comma-separated receiver list are required. Unexpected trailing fields are rejected.

    Start bits must be unsigned decimal integers fitting uint32_t; lengths must be within 1..64 bits. Signs, fractional values, numeric suffixes, and overflow are rejected for these fields. Every signal bit must fit within the frame's declared payload length, using Intel increasing bit positions or Motorola sawtooth traversal. This uses the stored payload length without introducing an additional CAN or CAN FD payload limit.

    @@ -583,7 +599,7 @@ Signal validation and parse errors

    Basic multiplexing markers M and mN are supported as described below. Overlapping signals are rejected unless their branch values differ. Duplicate frame keys and signal names within a frame are rejected.

    Invalid signal definitions stop parsing with path:line: description, including when the signal exceeds the declared payload length.


    -

    +

    Basic multiplexing

    BO_ 256 Status: 3 ECU
    SG_ Mode M : 0|8@1+ (1,0) [0|255] "" ECU
    @@ -606,11 +622,13 @@ Basic multiplexing InsufficientPayload Selected or unconditional signal bytes are missing -InvalidDefinition Invalid bit length or missing/invalid selector in manually built runtime metadata +InvalidDefinition Invalid length, byte order, numeric type, multiplexing role, layout outside declared DLC, or missing/invalid selector in manually built runtime metadata + +PhysicalConversionFailed Raw value is available, but the converted physical value is NaN or infinity -

    All statuses except Valid have valid == false; their numeric fields contain no decoded data and GetValueDescription() returns nullptr. Check status to distinguish inactive branches from errors. Consumers constructing runtime metadata manually must obey the same non-overlap rules as the parser. Extended and nested multiplexing are outside this stage and are explicitly rejected, rather than silently decoded as unconditional signals.

    +

    All statuses except Valid have valid == false and GetValueDescription() returns nullptr. PhysicalConversionFailed preserves extracted raw data and the non-finite conversion result; other invalid statuses have no decoded numeric data. Check status to distinguish inactive branches from errors. Consumers constructing runtime metadata manually must obey the same non-overlap rules as the parser. Extended and nested multiplexing are outside this stage and are explicitly rejected, rather than silently decoded as unconditional signals.


    -

    +

    Comments

    Frame comment example:

    CM_ BO_ 256 "Engine data frame";
    @@ -622,7 +640,7 @@ Comments
  • SignalInfo::comment

  • -

    +

    CAN ID Normalization

    The parser normalizes frame identifiers.

    Common DBC behavior:

    @@ -637,7 +655,7 @@ CAN ID Normalization

    This is important both for correct lookup and for future interoperability with live CAN APIs.


    -

    +

    PGN Extraction

    PGN is derived only when the frame is treated as extended.

    The current logic is simplified J1939 extraction:

    @@ -648,7 +666,7 @@ PGN Extraction

    This is enough for a practical start but should not be treated as full J1939 validation.


    -

    +

    Decode Flow

    Typical runtime decode flow:

    RawCanFrame
    @@ -663,7 +681,7 @@ Decode Flow
    ↓
    DecodedFrameValue

    -

    +

    Intel and Motorola Extraction

    The decoder currently has separate extraction paths:

      @@ -674,7 +692,7 @@ Intel and Motorola Extraction

      Intel and Motorola require different bit extraction logic.

      This is one of the main reasons why the runtime decode layer should be explicit and prepared in advance.


      -

      +

      Example Usage

      Include the public headers from the core/ include directory supplied by core/core.pri:

      #include "dbc_parser.h"
      @@ -685,23 +703,23 @@ Example Usage
      Created: 2026-03-13 Author: Deeaitch (Dim. Himro)
      Created: 2026-03-13 Author: Deeaitch (Dim. Himro)
      Created: 2026-03-13 Author: Deeaitch (Dim. Himro)
      -

      +

    Parse DBC

    DbcParser parser;
    DbcDatabase database = parser.ParseFile("example.dbc");
    DbcDatabase ParseFile(const std::string &filePath) const
    Parse DBC file.
    Parsed DBC content stored in a simple internal form.
    -

    +

    Build UI tree

    DbcTreeBuilder treeBuilder;
    std::unique_ptr<TreeNode> root = treeBuilder.Build(database);
    Builds a simple tree from parsed DBC database.
    std::unique_ptr< TreeNode > Build(const DbcDatabase &database) const
    Build tree representation of parsed DBC data.
    -

    +

    Build runtime decode database

    DbcDecodeBuilder decodeBuilder;
    DecodeDatabase decodeDatabase = decodeBuilder.Build(database);
    -

    +

    Decode a raw frame

    RawCanFrame rawFrame;
    rawFrame.canId = 256; // EngineData from the DBC example above
    @@ -715,7 +733,7 @@ Decode a raw frame
    std::uint32_t canId
    Definition dbc_decoder.h:25
    bool isExtended
    Definition dbc_decoder.h:26

    -

    +

    Unified Decode Strategy

    A key design goal is that the same decoder should work for both:

      @@ -723,7 +741,7 @@ Unified Decode Strategy
    • trace replay frames

    That means this architecture supports:

    -

    +

    live path

    live CAN input
    ↓
    @@ -732,7 +750,7 @@ live path
    DbcDecoder
    ↓
    decoded signal values
    -

    +

    trace path

    trace reader
    ↓
    @@ -743,7 +761,7 @@ trace path
    decoded signal values

    This avoids duplicating decode logic in two separate parts of the application.


    -

    +

    Intended Use in FrameTap

    This module is meant to support at least the following FrameTap workflows:

      @@ -769,7 +787,7 @@ Intended Use in FrameTap
      - live frames
      - trace replay

    -

    +

    Why the Tree Is Not Enough

    The tree exists for browsing.

    However, runtime decode should not rely on tree traversal because that would introduce unnecessary coupling and inefficiency.

    @@ -781,7 +799,7 @@ Why the Tree Is Not Enough

    That is why DecodeDatabase is a separate layer.


    -

    +

    Statement Handler Factory

    DbcParser owns file reading, trimming, line numbers, and error reporting. Each ParseFile() call creates a fresh DbcParseContext and a DbcStatementDispatcher. The context contains the parsed database and current frame; handlers receive it by reference and retain no parsing state.

    The dispatcher uses TypeFactory<std::string, DbcStatementHandler> to create registered handlers once per parsing session, then reuses them for all matching lines:

    @@ -802,7 +820,7 @@ Statement Handler Factory

    Registration creates one handler; replacing a registration replaces that instance. Register during initialization before dispatching. A line is routed using its full first whitespace-delimited keyword, so BO_TX_BU_ cannot match BO_. Unregistered keywords produce UnsupportedStatement warnings through ParseFileWithDiagnostics. SG_ and CM_ accept spaces and tabs after their keywords.

    This factory handles statements inside DBC files. A separate format-level factory can be introduced when another file format is implemented. The decoder and database builders remain independent of statement dispatch.


    -

    +

    Signal value descriptions (<tt>VAL_</tt>)

    Signal descriptions attach text to exact raw integer values:

    BO_ 256 Status: 8 ECU
    @@ -852,7 +870,7 @@ are outside this implementation; the optional Qt adapter exposes signal mappings <hr> -@section autotoc_md114 Current Limitations +@section autotoc_md117 Current Limitations This is still a minimal implementation. @@ -886,11 +904,11 @@ byte boundaries, and 64-bit values. <hr> -@section autotoc_md116 Recommended Next Steps +@section autotoc_md119 Recommended Next Steps A practical development order would be: -@subsection autotoc_md117 Stage 1 - already implemented +@subsection autotoc_md120 Stage 1 - already implemented - <tt>BO_</tt> - <tt>SG_</tt> @@ -905,13 +923,13 @@ A practical development order would be: - runtime decode database - runtime decoder -@subsection autotoc_md118 Stage 2 +@subsection autotoc_md121 Stage 2 Recommended additions: - selective decoding of only chosen signals -@subsection autotoc_md119 Stage 3 +@subsection autotoc_md122 Stage 3 Recommended additions: @@ -921,7 +939,7 @@ Recommended additions: - CSV export of decoded traces - optimized filtering and signal selection -@subsection autotoc_md120 Stage 4 +@subsection autotoc_md123 Stage 4 Advanced functionality: @@ -932,7 +950,7 @@ Advanced functionality: <hr> -@section autotoc_md122 Build Integration +@section autotoc_md125 Build Integration The repository supplies qmake projects. A C++17 compiler is required; the root build also requires Qt Core and Qt Test development packages. Other build @@ -963,7 +981,7 @@ source lists live in <tt>tests/parser_tests.pri</tt> and <tt>t When building a consumer separately, pass <tt>DBC_CORE_BUILD_DIR=/absolute/path/to/build/core</tt> to qmake. -@subsection autotoc_md123 Build core without Qt libraries +@subsection autotoc_md126 Build core without Qt libraries From the repository root: @@ -976,7 +994,7 @@ make -j qmake is still the build tool; this target does not link Qt libraries. -@subsection autotoc_md124 Optional Qt model adapter +@subsection autotoc_md127 Optional Qt model adapter The adapter and its <tt>QAbstractItemModelTester</tt> suite use Qt 6.8.2. The adapter itself links only Qt Core. @@ -1097,7 +1115,7 @@ Valid items are enabled and selectable, with no editing support.

    Each value-description entry contains value (qlonglong for negative keys, qulonglong for nonnegative keys) and description (QString). This preserves the full signed/unsigned 64-bit range without converting through double. An unmapped signal returns an empty list. Data comes from SignalInfo::valueDescriptions. Unsupported roles return invalid QVariant.

    -

    +

    Standalone tree tests without Qt

    From the repository root, use only a C++17 compiler:

    @@ -1106,7 +1124,7 @@ Standalone tree tests without Qt
    /tmp/dbc_tree_node_test

    -

    +

    Build and run the demo

    After building core, run these commands from the repository root:

    @@ -1118,7 +1136,7 @@ Build and run the demo

    The demo prints the parsed tree and decodes eight zero bytes using the first frame definition. Replace that sample payload in examples/main.cpp to feed live CAN data or trace records.

    -

    +

    Generate API documentation

    With Doxygen installed, run from the repository root:

    @@ -1128,17 +1146,17 @@ Generate API documentation


    -

    +

    Regression tests

    See tests/README.md for QtTest build and execution commands. Tests cover frame validation and error locations, Intel and Motorola extraction, signal syntax and payload bounds, VAL_ syntax and reference validation, raw description lookup and preservation through both builders, statement dispatch, signed and unsigned 64-bit boundaries, physical conversion, numeric output, and invalid signal payloads.

    -

    +

    Measuring code coverage

    Coverage instrumentation is separate from the QtTest suite named CoverageTest: that suite runs extra regression checks, but coverage counters require a specially instrumented build. Use a separate build directory for each coverage tool.

    -

    +

    GCC, gcov, and LCOV on Debian/Ubuntu

    Install the build and report tools:

    @@ -1192,7 +1210,7 @@ GCC, gcov, and LCOV on Debian/Ubuntu

    After adding direct comment-handler and ambiguous VAL_ reference tests, the same configuration measured 98.4% line coverage (728/740) and 71.4% branch coverage (841/1178).

    See GCC instrumentation options and the LCOV project documentation.

    -

    +

    Configure the GCC coverage build in Qt Creator

      @@ -1208,7 +1226,7 @@ Configure the GCC coverage build in Qt Creator

    -

    +

    Configure all build variants in Qt Creator

    The local dbc.pro.user stores machine-specific kit IDs and build/run settings. It is ignored by Git (*.user) and should not be committed or copied between machines. Recreate the configurations below through the UI when setting up a new checkout. The Qt4ProjectManager identifiers inside .user are historical Qt Creator setting names; the project still requires Qt 6.8.2.

    diff --git a/docs/html/md_changelog.html b/docs/html/md_changelog.html index 2dabd74..ad2e5ea 100644 --- a/docs/html/md_changelog.html +++ b/docs/html/md_changelog.html @@ -97,10 +97,26 @@ $(document).ready(function(){initNavTree('md_changelog.html',''); initResizable(

    Unreleased

    -2026-10-11

    +2026-10-13

    Added

      +
    • FrameDecodeStatus distinguishes unknown frames, insufficient payloads, signal errors, and successful decoding; frame valid still means matched.
    • +
    • SignalDecodeStatus::PhysicalConversionFailed reports NaN/Inf results, retaining exact raw values and the non-finite physical result.
    • +
    • Regression tests for payload-length policy, partial results, invalid runtime metadata, physical overflow/non-finite parameters, and raw branch selection when selector physical conversion fails.
    • +
    +

    +Changed

    +
      +
    • Short payloads decode available signals without padding; longer payloads ignore bytes beyond the declared DLC. Short frame status takes precedence over signal errors, with per-signal causes retained in definition order.
    • +
    • Runtime signal validation checks enum values and bit layouts against declared DLC using overflow-safe coordinates. Extra bytes cannot rescue invalid layouts.
    • +
    • Documented frame/signal status meanings, value availability, and payload policy.
    • +
    +

    +2026-10-11

    +

    +Added

    +
    • Stable application identifiers: FrameKey (normalized CAN ID and frame type) and SignalKey (frame key and signal name), with equality and hashers.
    • Runtime FindSignal, frame GetKey, and keys on decoded frame/signal results.
    • Reload/lifetime tests for retained frames and independent signals, lookup handles, copies/moves, snapshot release, stable keys, removed/renamed definitions, failed/empty reloads, duplicate keys, and insufficient payloads.
    • @@ -109,7 +125,7 @@ Added
    • Quiet common DBC header handlers registered through TypeFactory; NS_ symbol declarations do not produce unsupported-statement warnings.
    • Regression tests for warning locations, header handling, immediate/deferred errors, file errors, empty results on failure, and legacy exception compatibility.
    -

    +

    Changed

    • API change: DecodeDatabase is an immutable shared snapshot. Use GetFrames() for reads or construct a new snapshot from a vector; its frame index is private and built automatically, with duplicate-key validation.
    • @@ -120,9 +136,9 @@ Changed
    • Documented supported syntax, warning behavior, diagnostic codes, and limitations.
    • Audited diagnostic coverage and added unsupported-statement variants, namespace termination, formatting/path handling, first-error stopping, deferred diagnostic order, duplicate/overlap errors, and supported decoding preservation tests. Linux file-read errors and legacy file-error messages are checked explicitly.
    -

    +

    2026-10-07

    -

    +

    Added

    • Added regression coverage for signed bit-length boundaries, deterministic Intel/Motorola extraction, lookup failures, runtime validation, PGN, comments, file formatting, metadata copying/lifetime, and TypeFactory behavior.
    • @@ -134,7 +150,7 @@ Added
    • Selector-first decoding using exact raw values, preserving definition order and unconditional signals. Explicit statuses distinguish inactive branches, missing selector data, insufficient payloads, and malformed runtime metadata.
    • Registered SG_MUL_VAL_ rejection through TypeFactory; extended and nested multiplexing remain unsupported. Added parser/decoder regression coverage and documented supported syntax and consumer behavior.
    -

    +

    Changed

    • Qt-dependent targets now require the installed Qt 6.8.2 through a shared qmake version check; removed Qt 5 compatibility instructions. Core remains Qt-free.
    • @@ -142,7 +158,7 @@ Changed
    • Coverage documentation now describes the selected open GCC/LCOV toolchain.
    • Overlapping unconditional signals are now rejected during parsing.
    -

    +

    Fixed

    • Linked QtGui only in the optional model tests and used QGuiApplication with an offscreen default, resolving the QAbstractItemModelTester invalid QPixmap metatype warning without adding QtGui to the adapter library.
    • @@ -150,9 +166,9 @@ Fixed
    • Removed test compiler warnings by using QVERIFY_THROWS_EXCEPTION and explicitly discarding nodiscard return values in exception checks.
    • Reject duplicate frames by normalized CAN ID and frame type, including equivalent extended ID encodings, and duplicate signal names within a frame. Errors report the file path and line of the repeated definition. Standard and extended frames may share a numeric ID; separate frames may reuse signal names. Added regression coverage and updated parser documentation.
    -

    +

    2026-10-06

    -

    +

    Added

    • Added non-owning TreeNode parent navigation and stable sibling positions; disabled node moves to preserve links while retaining unique_ptr ownership.
    • @@ -162,61 +178,61 @@ Added
    • Preserved descriptions in parsed metadata, runtime decode definitions, and UI tree metadata. Added DecodedSignalValue::GetValueDescription() and FindValueDescription() for raw integer lookup before physical scaling. Unknown values retain normal numeric decoding.
    • Added regression coverage for syntax, quoting, whitespace, integer boundaries, declaration order, invalid references, duplicates, scaling, and both builders; documented syntax, lookup behavior, and limitations in README.
    -

    +

    2026-10-05

    -

    +

    Changed

    • Split the qmake project into a reusable static library in core/, a demo in examples/, and a combined regression test executable in tests/. Added shared .pri files and updated build instructions. (f17ddb6, #11)
    • Added .clang-format and applied consistent formatting to the source code and tests. (b91cf4a, #7)
    • Replaced lambdas with explicit functions and simpler code in statement handlers, the decoder, the demo, and regression tests. (03dc8f2)
    -

    +

    Fixed

    • Added complete validation of SG_ signal statements, including syntax, receivers, numeric fields, lengths of 1–64 bits, physical ranges, and signal bounds within the declared frame payload for Intel and Motorola bit order. Invalid definitions report the file path and line number. Multiplexing markers are explicitly rejected. Added regression tests and documentation. (8d82b13, #6)
    -

    +

    Documentation and maintenance

    • Regenerated Doxygen documentation after code changes and the project restructuring. (6cd9976, 0067cb4, f17ddb6)
    • Removed the temporary signal-validation issue document. (1163277)
    -

    +

    2026-10-01

    -

    +

    Changed

    • Moved DBC statement parsing into TypeFactory-registered handlers for BO_, SG_, and CM_, with a shared parse context and keyword dispatcher. Added tests for dispatch and handler replacement, and documented how to register additional handlers. (88b5883, #4)
    • API change: DecodedSignalValue::rawValue now uses std::variant<std::int64_t, std::uint64_t> instead of int64_t. Consumers must handle the appropriate variant alternative. Physical values remain double and may lose precision for large integers. (69074e3, #2)
    -

    +

    Fixed

    • Corrected Motorola signal extraction within bytes and across byte boundaries using DBC sawtooth bit numbering. Added decoder regression tests and build instructions; introduced the TypeFactory utility. (2c057b6)
    • Preserved the full unsigned 64-bit raw-value range, including values above INT64_MAX, and updated physical conversion and demo output. Added tests for signed and unsigned boundaries and numeric output. (69074e3, #2)
    • Validated BO_ frame syntax, CAN identifiers, payload lengths, numeric overflow, and unexpected trailing fields. Parsing errors now include the file path and physical line number. Added parser regression tests. (3bd9339, #3)
    -

    +

    v1.0 — 2026-06-14

    Tag v1.0 points to 38ce043 and includes the initial implementation below.

    -

    +

    Documentation

    • Added source-file headers with author information and MIT license references. (e2ffa97)
    • Added and subsequently simplified the Doxygen configuration. (e2c07dd, 4c8af9f)
    • Generated API documentation and refreshed the generated documentation. (dfec39d, 38ce043)
    -

    +

    Initial implementation — 2026-03-13

    -

    +

    Added

    • Initialized the repository with a README and MIT license. (4270459)
    • Added the DBC parser, frame and signal metadata, a tree representation and builder, a demonstration program, and a qmake project. (2a90b2d)
    • Added runtime decoding through DbcDecodeBuilder, DecodeDatabase, and DbcDecoder, including CAN-frame lookup, Intel and Motorola extraction, signed and unsigned signals, and factor/offset conversion. Updated parser metadata and the demo to support decoding. (a53a5df)
    -

    +

    Documentation and maintenance

    • Added .gitignore and expanded the README with architecture, data structures, parsing behavior, decoding examples, and usage documentation. (efc57d6, f2aa10c, cb20f5f, f0e7065)
    • diff --git a/docs/html/md_tests_2README.html b/docs/html/md_tests_2README.html index f09c1a0..2b52317 100644 --- a/docs/html/md_tests_2README.html +++ b/docs/html/md_tests_2README.html @@ -93,7 +93,7 @@ $(document).ready(function(){initNavTree('md_tests_2README.html',''); initResiza
      QtTest regression tests
    -

    For instrumented builds, LCOV HTML reports, and Qt Creator coverage configuration, see Measuring code coverage. For the complete IDE build/run configuration set, see Configure all build variants in Qt Creator. The Coverage configuration uses coverage_report.sh after its instrumented build; the script takes the source and build directories as its two arguments.

    +

    For instrumented builds, LCOV HTML reports, and Qt Creator coverage configuration, see Measuring code coverage. For the complete IDE build/run configuration set, see Configure all build variants in Qt Creator. The Coverage configuration uses coverage_report.sh after its instrumented build; the script takes the source and build directories as its two arguments.

    Open the repository's root dbc.pro in Qt Creator. It contains core (a static C++17 library) and tests (the dbc_tests QtTest executable). Core is built first; tests link its archive rather than compiling its implementation again. Qt Core and Qt Test development packages are required for tests only. The optional model tests also link QtGui, which is provided by Qt base development packages. The adapter library itself still links only QtCore.

    Build from the repository root with the installed Qt 6.8.2:

    mkdir build
    @@ -110,24 +110,24 @@ $(document).ready(function(){initNavTree('md_tests_2README.html',''); initResiza
    make
    make check

    parser_tests.pri and decoder_tests.pri include the original test sources; tests.pro also includes dbc_coverage_test.cpp and dbc_lifetime_test.cpp. ../core/core.pri supplies the library include path and linkage.

    -

    +

    Decoder coverage

    -

    Coverage includes Motorola bit numbering within and across bytes, unaligned signals, signed values, scaling, 64-bit extraction, signed/unsigned boundary values (including INT64_MIN, INT64_MAX, and UINT64_MAX), numeric output, invalid lengths, insufficient payloads, and Intel regression cases. Frame validity means the definition was found; insufficient payloads are checked through signal validity.

    -

    +

    Coverage includes Motorola bit numbering within and across bytes, unaligned signals, signed values, scaling, 64-bit extraction, signed/unsigned boundary values (including INT64_MIN, INT64_MAX, and UINT64_MAX), numeric output, invalid lengths, insufficient payloads, and Intel regression cases. Frame validity means the definition was found; frame status reports unknown frames, short payloads, and signal errors. Tests exercise empty/short/exact/long payloads, ignored trailing bytes, signals outside declared DLC, invalid enum metadata and overflowing start bits, and preservation of successful signals. Physical conversion checks cover NaN/Inf parameters, multiplication/addition overflow, finite limits, signed/unsigned raw retention, and raw selector activation after physical conversion failure.

    +

    Parser coverage

    Structured diagnostic tests cover severity, code, supplied path, and physical line numbers for skipped syntax and immediate/deferred errors; warnings preceding an error are retained, and failed parses return an empty database. Common headers and namespace symbol lists stay quiet. Tests also check file-open errors, fresh parse sessions, and compatibility with the legacy throwing API. The diagnostic audit also covers 15 unsupported-statement variants, all namespace termination forms, CRLF and missing final newlines, relative paths with spaces, first-error stopping, deferred diagnostic ordering, duplicate definitions/values, selector ranges, overlaps, and preservation of supported decoding and metadata. On Linux, reading a directory exercises the distinct FileReadError path.

    Parser tests cover standard and extended CAN IDs, whitespace, payload storage boundaries, missing fields, malformed and overflowing numbers, reserved ID bits, error paths and line numbers, and integration with signals and comments. Duplicate definitions are rejected at their source lines, including equivalent extended ID encodings and repeated signal names within a frame. Standard and extended frames may share a numeric ID, and separate frames may reuse signal names. Signal tests cover complete syntax and numeric validation, scientific notation, finite physical parameters and ordered ranges, spaces and tabs, receiver lists, unsupported multiplexing, and Intel/Motorola declared-payload boundaries. Value-description tests cover full VAL_ syntax, whitespace, escaped quotes, empty text, exact integer boundaries, duplicate keys within and across statements, deferred and normalized frame references, missing targets, error locations, raw lookup with scaling, unknown/invalid values, and both database builders. Temporary DBC fixtures are created in the system temporary directory and removed automatically.

    Dispatch tests also verify exact keyword matching, handler construction and reuse, unknown statements, and independent parsing sessions, including recovery after errors.

    -

    +

    Tree and optional model tests

    tree_node_test.cpp is a standalone C++17 executable without Qt; see the root README for its compiler command. It checks parents, sibling positions, null children, bounds, and unique_ptr transfer.

    Enable CONFIG+=dbc_qt in the root qmake invocation to build dbc_qt and dbc_model_tests alongside existing tests. Qt 6.8.2 is required. make check runs all suites. Model tests parse a temporary DBC fixture and use QAbstractItemModelTester, verify navigation across columns, invalid/foreign indexes, metadata and exact VAL_ integers, and persistent-index invalidation when reloading or clearing the tree. Their QGuiApplication registers graphical metatypes used by QAbstractItemModelTester, avoiding the invalid QPixmap type warning in Qt 6.8.2. The test executable defaults to QT_QPA_PLATFORM=offscreen when the variable is unset or empty, so no display server is needed. An explicit platform setting is preserved.

    Basic multiplexing coverage includes selector declarations after branches, Intel/Motorola branch overlap, signed active values and VAL_ lookup, unconditional signals, inactive branches, missing selector/branch payloads, exact UINT64_MAX selector matching, malformed runtime selectors, source locations, and rejection of invalid markers, selector references/ranges, overlaps, and SG_MUL_VAL_.

    -

    +

    Additional regression coverage

    dbc_coverage_test.cpp runs as part of dbc_tests. It covers signed boundaries below 64 bits, deterministic extraction comparisons for both byte orders across lengths 1–64, frame lookup failures, exact decode statuses, malformed runtime multiplexing, PGN boundaries, comment handling, file formatting, metadata copying and source lifetime, physical conversion, and TypeFactory registration/creation. Direct resolver tests construct ambiguous databases through the public metadata API, verify source locations, and check that ambiguous targets receive no values.

    All QtTest targets use Qt 6.8.2 and QVERIFY_THROWS_EXCEPTION. Exception tests explicitly discard nodiscard results when only the exception is being checked. The standalone tree test requires only a C++17 compiler.

    -

    +

    Definition lifetime and reload coverage

    dbc_lifetime_test.cpp checks retained definitions and value-description strings across reload, clear, and destruction; independently saved signal results; copied/moved results and database copies; owning lookup handles; and release of the old snapshot after its final owner is removed. Compile-time checks enforce const access to runtime metadata. Reload fixtures change ordering, frame names, scaling, units, and descriptions, remove definitions, and rename signals.

    Key tests check equality and hashing, standard/extended separation, the same signal name in different frames, and missing lookups. Constructor tests reject duplicate runtime keys, including manually supplied parsed metadata. Failed reload leaves the active database intact; empty reload clears it. Invalid/unknown results and insufficient payloads retain their documented ownership and status behavior. Inactive branches and unavailable selectors also retain definitions across reload.

    diff --git a/docs/html/navtreedata.js b/docs/html/navtreedata.js index ff8c69b..7a3d11a 100644 --- a/docs/html/navtreedata.js +++ b/docs/html/navtreedata.js @@ -27,125 +27,129 @@ var NAVTREE = [ "DBC Framework", "index.html", [ [ "Changelog", "md_changelog.html", [ [ "Unreleased", "md_changelog.html#autotoc_md1", [ - [ "2026-10-11", "md_changelog.html#autotoc_md2", [ + [ "2026-10-13", "md_changelog.html#autotoc_md2", [ [ "Added", "md_changelog.html#autotoc_md3", null ], [ "Changed", "md_changelog.html#autotoc_md4", null ] ] ], - [ "2026-10-07", "md_changelog.html#autotoc_md5", [ + [ "2026-10-11", "md_changelog.html#autotoc_md5", [ [ "Added", "md_changelog.html#autotoc_md6", null ], - [ "Changed", "md_changelog.html#autotoc_md7", null ], - [ "Fixed", "md_changelog.html#autotoc_md8", null ] + [ "Changed", "md_changelog.html#autotoc_md7", null ] ] ], - [ "2026-10-06", "md_changelog.html#autotoc_md9", [ - [ "Added", "md_changelog.html#autotoc_md10", null ] + [ "2026-10-07", "md_changelog.html#autotoc_md8", [ + [ "Added", "md_changelog.html#autotoc_md9", null ], + [ "Changed", "md_changelog.html#autotoc_md10", null ], + [ "Fixed", "md_changelog.html#autotoc_md11", null ] ] ], - [ "2026-10-05", "md_changelog.html#autotoc_md11", [ - [ "Changed", "md_changelog.html#autotoc_md12", null ], - [ "Fixed", "md_changelog.html#autotoc_md13", null ], - [ "Documentation and maintenance", "md_changelog.html#autotoc_md14", null ] + [ "2026-10-06", "md_changelog.html#autotoc_md12", [ + [ "Added", "md_changelog.html#autotoc_md13", null ] ] ], - [ "2026-10-01", "md_changelog.html#autotoc_md15", [ - [ "Changed", "md_changelog.html#autotoc_md16", null ], - [ "Fixed", "md_changelog.html#autotoc_md17", null ] + [ "2026-10-05", "md_changelog.html#autotoc_md14", [ + [ "Changed", "md_changelog.html#autotoc_md15", null ], + [ "Fixed", "md_changelog.html#autotoc_md16", null ], + [ "Documentation and maintenance", "md_changelog.html#autotoc_md17", null ] + ] ], + [ "2026-10-01", "md_changelog.html#autotoc_md18", [ + [ "Changed", "md_changelog.html#autotoc_md19", null ], + [ "Fixed", "md_changelog.html#autotoc_md20", null ] ] ] ] ], - [ "v1.0 — 2026-06-14", "md_changelog.html#autotoc_md18", [ - [ "Documentation", "md_changelog.html#autotoc_md19", null ], - [ "Initial implementation — 2026-03-13", "md_changelog.html#autotoc_md20", [ - [ "Added", "md_changelog.html#autotoc_md21", null ], - [ "Documentation and maintenance", "md_changelog.html#autotoc_md22", null ] + [ "v1.0 — 2026-06-14", "md_changelog.html#autotoc_md21", [ + [ "Documentation", "md_changelog.html#autotoc_md22", null ], + [ "Initial implementation — 2026-03-13", "md_changelog.html#autotoc_md23", [ + [ "Added", "md_changelog.html#autotoc_md24", null ], + [ "Documentation and maintenance", "md_changelog.html#autotoc_md25", null ] ] ] ] ] ] ], [ "DBC Parser and Runtime Decode Module Documentation", "md_README.html", [ - [ "Overview", "md_README.html#autotoc_md24", null ], - [ "High-Level Architecture", "md_README.html#autotoc_md26", [ - [ "1. Parse and UI path", "md_README.html#autotoc_md27", null ], - [ "2. Runtime decode path", "md_README.html#autotoc_md28", null ] + [ "Overview", "md_README.html#autotoc_md27", null ], + [ "High-Level Architecture", "md_README.html#autotoc_md29", [ + [ "1. Parse and UI path", "md_README.html#autotoc_md30", null ], + [ "2. Runtime decode path", "md_README.html#autotoc_md31", null ] ] ], - [ "Why the Runtime Decode Layer Exists", "md_README.html#autotoc_md30", null ], - [ "Module Layout", "md_README.html#autotoc_md32", null ], - [ "Parsed Data Structures", "md_README.html#autotoc_md34", null ], - [ "SignalInfo", "md_README.html#autotoc_md35", null ], - [ "FrameInfo", "md_README.html#autotoc_md37", null ], - [ "DbcDatabase", "md_README.html#autotoc_md39", null ], - [ "UI Tree Layer", "md_README.html#autotoc_md41", null ], - [ "TreeNode", "md_README.html#autotoc_md42", null ], - [ "Runtime Decode Layer", "md_README.html#autotoc_md44", null ], - [ "Purpose", "md_README.html#autotoc_md45", null ], - [ "ByteOrder", "md_README.html#autotoc_md47", null ], - [ "ValueType", "md_README.html#autotoc_md49", null ], - [ "DecodeSignal", "md_README.html#autotoc_md51", null ], - [ "DecodeFrame", "md_README.html#autotoc_md53", null ], - [ "FrameKey", "md_README.html#autotoc_md55", null ], - [ "DecodeDatabase", "md_README.html#autotoc_md57", null ], - [ "Decoder Layer", "md_README.html#autotoc_md59", null ], - [ "RawCanFrame", "md_README.html#autotoc_md60", null ], - [ "DecodedSignalValue", "md_README.html#autotoc_md62", [ - [ "Raw value representation and precision", "md_README.html#autotoc_md64", null ] + [ "Why the Runtime Decode Layer Exists", "md_README.html#autotoc_md33", null ], + [ "Module Layout", "md_README.html#autotoc_md35", null ], + [ "Parsed Data Structures", "md_README.html#autotoc_md37", null ], + [ "SignalInfo", "md_README.html#autotoc_md38", null ], + [ "FrameInfo", "md_README.html#autotoc_md40", null ], + [ "DbcDatabase", "md_README.html#autotoc_md42", null ], + [ "UI Tree Layer", "md_README.html#autotoc_md44", null ], + [ "TreeNode", "md_README.html#autotoc_md45", null ], + [ "Runtime Decode Layer", "md_README.html#autotoc_md47", null ], + [ "Purpose", "md_README.html#autotoc_md48", null ], + [ "ByteOrder", "md_README.html#autotoc_md50", null ], + [ "ValueType", "md_README.html#autotoc_md52", null ], + [ "DecodeSignal", "md_README.html#autotoc_md54", null ], + [ "DecodeFrame", "md_README.html#autotoc_md56", null ], + [ "FrameKey", "md_README.html#autotoc_md58", null ], + [ "DecodeDatabase", "md_README.html#autotoc_md60", null ], + [ "Decoder Layer", "md_README.html#autotoc_md62", null ], + [ "RawCanFrame", "md_README.html#autotoc_md63", null ], + [ "DecodedSignalValue", "md_README.html#autotoc_md65", [ + [ "Raw value representation and precision", "md_README.html#autotoc_md67", null ] ] ], - [ "DecodedFrameValue", "md_README.html#autotoc_md66", [ - [ "Definition ownership and reload", "md_README.html#autotoc_md67", null ], - [ "Stable frame and signal identifiers", "md_README.html#autotoc_md68", null ] + [ "DecodedFrameValue", "md_README.html#autotoc_md69", [ + [ "Definition ownership and reload", "md_README.html#autotoc_md70", null ], + [ "Stable frame and signal identifiers", "md_README.html#autotoc_md71", null ] ] ], - [ "DbcDecoder", "md_README.html#autotoc_md70", null ], - [ "Parser Support", "md_README.html#autotoc_md72", [ - [ "Structured diagnostics", "md_README.html#autotoc_md73", null ] + [ "DbcDecoder", "md_README.html#autotoc_md73", null ], + [ "Parser Support", "md_README.html#autotoc_md75", [ + [ "Structured diagnostics", "md_README.html#autotoc_md76", null ] ] ], - [ "Supported DBC Syntax", "md_README.html#autotoc_md75", null ], - [ "Frame definition", "md_README.html#autotoc_md76", [ - [ "Frame validation and parse errors", "md_README.html#autotoc_md78", null ] + [ "Supported DBC Syntax", "md_README.html#autotoc_md78", null ], + [ "Frame definition", "md_README.html#autotoc_md79", [ + [ "Frame validation and parse errors", "md_README.html#autotoc_md81", null ] ] ], - [ "Signal definition", "md_README.html#autotoc_md80", [ - [ "Signal validation and parse errors", "md_README.html#autotoc_md82", null ] + [ "Signal definition", "md_README.html#autotoc_md83", [ + [ "Signal validation and parse errors", "md_README.html#autotoc_md85", null ] ] ], - [ "Basic multiplexing", "md_README.html#autotoc_md84", null ], - [ "Comments", "md_README.html#autotoc_md86", null ], - [ "CAN ID Normalization", "md_README.html#autotoc_md88", null ], - [ "PGN Extraction", "md_README.html#autotoc_md90", null ], - [ "Decode Flow", "md_README.html#autotoc_md92", null ], - [ "Intel and Motorola Extraction", "md_README.html#autotoc_md94", null ], - [ "Example Usage", "md_README.html#autotoc_md96", [ - [ "Parse DBC", "md_README.html#autotoc_md97", null ], - [ "Build UI tree", "md_README.html#autotoc_md98", null ], - [ "Build runtime decode database", "md_README.html#autotoc_md99", null ], - [ "Decode a raw frame", "md_README.html#autotoc_md100", null ] + [ "Basic multiplexing", "md_README.html#autotoc_md87", null ], + [ "Comments", "md_README.html#autotoc_md89", null ], + [ "CAN ID Normalization", "md_README.html#autotoc_md91", null ], + [ "PGN Extraction", "md_README.html#autotoc_md93", null ], + [ "Decode Flow", "md_README.html#autotoc_md95", null ], + [ "Intel and Motorola Extraction", "md_README.html#autotoc_md97", null ], + [ "Example Usage", "md_README.html#autotoc_md99", [ + [ "Parse DBC", "md_README.html#autotoc_md100", null ], + [ "Build UI tree", "md_README.html#autotoc_md101", null ], + [ "Build runtime decode database", "md_README.html#autotoc_md102", null ], + [ "Decode a raw frame", "md_README.html#autotoc_md103", null ] ] ], - [ "Unified Decode Strategy", "md_README.html#autotoc_md102", [ - [ "live path", "md_README.html#autotoc_md103", null ], - [ "trace path", "md_README.html#autotoc_md104", null ] + [ "Unified Decode Strategy", "md_README.html#autotoc_md105", [ + [ "live path", "md_README.html#autotoc_md106", null ], + [ "trace path", "md_README.html#autotoc_md107", null ] ] ], - [ "Intended Use in FrameTap", "md_README.html#autotoc_md106", null ], - [ "Why the Tree Is Not Enough", "md_README.html#autotoc_md108", null ], - [ "Statement Handler Factory", "md_README.html#autotoc_md110", null ], - [ "Signal value descriptions (VAL_)", "md_README.html#autotoc_md112", null ], - [ "Current Limitations", "md_README.html#autotoc_md114", null ], - [ "Recommended Next Steps", "md_README.html#autotoc_md116", [ - [ "Stage 1 - already implemented", "md_README.html#autotoc_md117", null ], - [ "Stage 2", "md_README.html#autotoc_md118", null ], - [ "Stage 3", "md_README.html#autotoc_md119", null ], - [ "Stage 4", "md_README.html#autotoc_md120", null ] + [ "Intended Use in FrameTap", "md_README.html#autotoc_md109", null ], + [ "Why the Tree Is Not Enough", "md_README.html#autotoc_md111", null ], + [ "Statement Handler Factory", "md_README.html#autotoc_md113", null ], + [ "Signal value descriptions (VAL_)", "md_README.html#autotoc_md115", null ], + [ "Current Limitations", "md_README.html#autotoc_md117", null ], + [ "Recommended Next Steps", "md_README.html#autotoc_md119", [ + [ "Stage 1 - already implemented", "md_README.html#autotoc_md120", null ], + [ "Stage 2", "md_README.html#autotoc_md121", null ], + [ "Stage 3", "md_README.html#autotoc_md122", null ], + [ "Stage 4", "md_README.html#autotoc_md123", null ] ] ], - [ "Build Integration", "md_README.html#autotoc_md122", [ - [ "Build core without Qt libraries", "md_README.html#autotoc_md123", null ], - [ "Optional Qt model adapter", "md_README.html#autotoc_md124", null ], - [ "Standalone tree tests without Qt", "md_README.html#autotoc_md125", null ], - [ "Build and run the demo", "md_README.html#autotoc_md126", null ], - [ "Generate API documentation", "md_README.html#autotoc_md127", null ] + [ "Build Integration", "md_README.html#autotoc_md125", [ + [ "Build core without Qt libraries", "md_README.html#autotoc_md126", null ], + [ "Optional Qt model adapter", "md_README.html#autotoc_md127", null ], + [ "Standalone tree tests without Qt", "md_README.html#autotoc_md128", null ], + [ "Build and run the demo", "md_README.html#autotoc_md129", null ], + [ "Generate API documentation", "md_README.html#autotoc_md130", null ] ] ], - [ "Regression tests", "md_README.html#autotoc_md129", null ], - [ "Measuring code coverage", "md_README.html#autotoc_md130", [ - [ "GCC, gcov, and LCOV on Debian/Ubuntu", "md_README.html#autotoc_md131", null ], - [ "Configure the GCC coverage build in Qt Creator", "md_README.html#autotoc_md132", null ], - [ "Configure all build variants in Qt Creator", "md_README.html#autotoc_md133", null ] + [ "Regression tests", "md_README.html#autotoc_md132", null ], + [ "Measuring code coverage", "md_README.html#autotoc_md133", [ + [ "GCC, gcov, and LCOV on Debian/Ubuntu", "md_README.html#autotoc_md134", null ], + [ "Configure the GCC coverage build in Qt Creator", "md_README.html#autotoc_md135", null ], + [ "Configure all build variants in Qt Creator", "md_README.html#autotoc_md136", null ] ] ] ] ], [ "QtTest regression tests", "md_tests_2README.html", [ - [ "Decoder coverage", "md_tests_2README.html#autotoc_md135", null ], - [ "Parser coverage", "md_tests_2README.html#autotoc_md136", null ], - [ "Tree and optional model tests", "md_tests_2README.html#autotoc_md137", null ], - [ "Additional regression coverage", "md_tests_2README.html#autotoc_md138", null ], - [ "Definition lifetime and reload coverage", "md_tests_2README.html#autotoc_md139", null ] + [ "Decoder coverage", "md_tests_2README.html#autotoc_md138", null ], + [ "Parser coverage", "md_tests_2README.html#autotoc_md139", null ], + [ "Tree and optional model tests", "md_tests_2README.html#autotoc_md140", null ], + [ "Additional regression coverage", "md_tests_2README.html#autotoc_md141", null ], + [ "Definition lifetime and reload coverage", "md_tests_2README.html#autotoc_md142", null ] ] ], [ "Classes", "annotated.html", [ [ "Class List", "annotated.html", "annotated_dup" ], @@ -170,7 +174,7 @@ var NAVTREE = var NAVTREEINDEX = [ "annotated.html", -"structFrameInfo.html#aa2b198ea0f55e560c123d35e7b3ecab9" +"structDecodedSignalValue.html#a6847d05b3cb00d5e0f6c611968f57978" ]; var SYNCONMSG = 'click to disable panel synchronisation'; diff --git a/docs/html/navtreeindex0.js b/docs/html/navtreeindex0.js index 8b18d8a..7cbf539 100644 --- a/docs/html/navtreeindex0.js +++ b/docs/html/navtreeindex0.js @@ -56,6 +56,11 @@ var NAVTREEINDEX0 = "dbc__decoder_8cpp.html":[4,0,0,3], "dbc__decoder_8cpp_source.html":[4,0,0,3], "dbc__decoder_8h.html":[4,0,0,4], +"dbc__decoder_8h.html#a855b2bc36cb18bfbae5ff30a2394e961":[4,0,0,4,4], +"dbc__decoder_8h.html#a855b2bc36cb18bfbae5ff30a2394e961a3ac705f2acd51a4613f9188c05c91d0d":[4,0,0,4,4,3], +"dbc__decoder_8h.html#a855b2bc36cb18bfbae5ff30a2394e961a7c9fa088a1e7db1fc83cb62aefd3ea7f":[4,0,0,4,4,1], +"dbc__decoder_8h.html#a855b2bc36cb18bfbae5ff30a2394e961a85f5d387f9a01dce258470e1a5a23332":[4,0,0,4,4,0], +"dbc__decoder_8h.html#a855b2bc36cb18bfbae5ff30a2394e961aedeb8da96660592dfe0853f212b77469":[4,0,0,4,4,2], "dbc__decoder_8h_source.html":[4,0,0,4], "dbc__decoder__test_8cpp_source.html":[4,0,3,1], "dbc__diagnostic_8h_source.html":[4,0,0,5], @@ -101,105 +106,108 @@ var NAVTREEINDEX0 = "hierarchy.html":[3,2], "index.html":[], "md_README.html":[1], -"md_README.html#autotoc_md100":[1,33,3], -"md_README.html#autotoc_md102":[1,34], -"md_README.html#autotoc_md103":[1,34,0], -"md_README.html#autotoc_md104":[1,34,1], -"md_README.html#autotoc_md106":[1,35], -"md_README.html#autotoc_md108":[1,36], -"md_README.html#autotoc_md110":[1,37], -"md_README.html#autotoc_md112":[1,38], -"md_README.html#autotoc_md114":[1,39], -"md_README.html#autotoc_md116":[1,40], -"md_README.html#autotoc_md117":[1,40,0], -"md_README.html#autotoc_md118":[1,40,1], -"md_README.html#autotoc_md119":[1,40,2], -"md_README.html#autotoc_md120":[1,40,3], -"md_README.html#autotoc_md122":[1,41], -"md_README.html#autotoc_md123":[1,41,0], -"md_README.html#autotoc_md124":[1,41,1], -"md_README.html#autotoc_md125":[1,41,2], -"md_README.html#autotoc_md126":[1,41,3], -"md_README.html#autotoc_md127":[1,41,4], -"md_README.html#autotoc_md129":[1,42], -"md_README.html#autotoc_md130":[1,43], -"md_README.html#autotoc_md131":[1,43,0], -"md_README.html#autotoc_md132":[1,43,1], -"md_README.html#autotoc_md133":[1,43,2], -"md_README.html#autotoc_md24":[1,0], -"md_README.html#autotoc_md26":[1,1], -"md_README.html#autotoc_md27":[1,1,0], -"md_README.html#autotoc_md28":[1,1,1], -"md_README.html#autotoc_md30":[1,2], -"md_README.html#autotoc_md32":[1,3], -"md_README.html#autotoc_md34":[1,4], -"md_README.html#autotoc_md35":[1,5], -"md_README.html#autotoc_md37":[1,6], -"md_README.html#autotoc_md39":[1,7], -"md_README.html#autotoc_md41":[1,8], -"md_README.html#autotoc_md42":[1,9], -"md_README.html#autotoc_md44":[1,10], -"md_README.html#autotoc_md45":[1,11], -"md_README.html#autotoc_md47":[1,12], -"md_README.html#autotoc_md49":[1,13], -"md_README.html#autotoc_md51":[1,14], -"md_README.html#autotoc_md53":[1,15], -"md_README.html#autotoc_md55":[1,16], -"md_README.html#autotoc_md57":[1,17], -"md_README.html#autotoc_md59":[1,18], -"md_README.html#autotoc_md60":[1,19], -"md_README.html#autotoc_md62":[1,20], -"md_README.html#autotoc_md64":[1,20,0], -"md_README.html#autotoc_md66":[1,21], -"md_README.html#autotoc_md67":[1,21,0], -"md_README.html#autotoc_md68":[1,21,1], -"md_README.html#autotoc_md70":[1,22], -"md_README.html#autotoc_md72":[1,23], -"md_README.html#autotoc_md73":[1,23,0], -"md_README.html#autotoc_md75":[1,24], -"md_README.html#autotoc_md76":[1,25], -"md_README.html#autotoc_md78":[1,25,0], -"md_README.html#autotoc_md80":[1,26], -"md_README.html#autotoc_md82":[1,26,0], -"md_README.html#autotoc_md84":[1,27], -"md_README.html#autotoc_md86":[1,28], -"md_README.html#autotoc_md88":[1,29], -"md_README.html#autotoc_md90":[1,30], -"md_README.html#autotoc_md92":[1,31], -"md_README.html#autotoc_md94":[1,32], -"md_README.html#autotoc_md96":[1,33], -"md_README.html#autotoc_md97":[1,33,0], -"md_README.html#autotoc_md98":[1,33,1], -"md_README.html#autotoc_md99":[1,33,2], +"md_README.html#autotoc_md100":[1,33,0], +"md_README.html#autotoc_md101":[1,33,1], +"md_README.html#autotoc_md102":[1,33,2], +"md_README.html#autotoc_md103":[1,33,3], +"md_README.html#autotoc_md105":[1,34], +"md_README.html#autotoc_md106":[1,34,0], +"md_README.html#autotoc_md107":[1,34,1], +"md_README.html#autotoc_md109":[1,35], +"md_README.html#autotoc_md111":[1,36], +"md_README.html#autotoc_md113":[1,37], +"md_README.html#autotoc_md115":[1,38], +"md_README.html#autotoc_md117":[1,39], +"md_README.html#autotoc_md119":[1,40], +"md_README.html#autotoc_md120":[1,40,0], +"md_README.html#autotoc_md121":[1,40,1], +"md_README.html#autotoc_md122":[1,40,2], +"md_README.html#autotoc_md123":[1,40,3], +"md_README.html#autotoc_md125":[1,41], +"md_README.html#autotoc_md126":[1,41,0], +"md_README.html#autotoc_md127":[1,41,1], +"md_README.html#autotoc_md128":[1,41,2], +"md_README.html#autotoc_md129":[1,41,3], +"md_README.html#autotoc_md130":[1,41,4], +"md_README.html#autotoc_md132":[1,42], +"md_README.html#autotoc_md133":[1,43], +"md_README.html#autotoc_md134":[1,43,0], +"md_README.html#autotoc_md135":[1,43,1], +"md_README.html#autotoc_md136":[1,43,2], +"md_README.html#autotoc_md27":[1,0], +"md_README.html#autotoc_md29":[1,1], +"md_README.html#autotoc_md30":[1,1,0], +"md_README.html#autotoc_md31":[1,1,1], +"md_README.html#autotoc_md33":[1,2], +"md_README.html#autotoc_md35":[1,3], +"md_README.html#autotoc_md37":[1,4], +"md_README.html#autotoc_md38":[1,5], +"md_README.html#autotoc_md40":[1,6], +"md_README.html#autotoc_md42":[1,7], +"md_README.html#autotoc_md44":[1,8], +"md_README.html#autotoc_md45":[1,9], +"md_README.html#autotoc_md47":[1,10], +"md_README.html#autotoc_md48":[1,11], +"md_README.html#autotoc_md50":[1,12], +"md_README.html#autotoc_md52":[1,13], +"md_README.html#autotoc_md54":[1,14], +"md_README.html#autotoc_md56":[1,15], +"md_README.html#autotoc_md58":[1,16], +"md_README.html#autotoc_md60":[1,17], +"md_README.html#autotoc_md62":[1,18], +"md_README.html#autotoc_md63":[1,19], +"md_README.html#autotoc_md65":[1,20], +"md_README.html#autotoc_md67":[1,20,0], +"md_README.html#autotoc_md69":[1,21], +"md_README.html#autotoc_md70":[1,21,0], +"md_README.html#autotoc_md71":[1,21,1], +"md_README.html#autotoc_md73":[1,22], +"md_README.html#autotoc_md75":[1,23], +"md_README.html#autotoc_md76":[1,23,0], +"md_README.html#autotoc_md78":[1,24], +"md_README.html#autotoc_md79":[1,25], +"md_README.html#autotoc_md81":[1,25,0], +"md_README.html#autotoc_md83":[1,26], +"md_README.html#autotoc_md85":[1,26,0], +"md_README.html#autotoc_md87":[1,27], +"md_README.html#autotoc_md89":[1,28], +"md_README.html#autotoc_md91":[1,29], +"md_README.html#autotoc_md93":[1,30], +"md_README.html#autotoc_md95":[1,31], +"md_README.html#autotoc_md97":[1,32], +"md_README.html#autotoc_md99":[1,33], "md_changelog.html":[0], "md_changelog.html#autotoc_md1":[0,0], -"md_changelog.html#autotoc_md10":[0,0,2,0], -"md_changelog.html#autotoc_md11":[0,0,3], -"md_changelog.html#autotoc_md12":[0,0,3,0], -"md_changelog.html#autotoc_md13":[0,0,3,1], -"md_changelog.html#autotoc_md14":[0,0,3,2], -"md_changelog.html#autotoc_md15":[0,0,4], -"md_changelog.html#autotoc_md16":[0,0,4,0], -"md_changelog.html#autotoc_md17":[0,0,4,1], -"md_changelog.html#autotoc_md18":[0,1], -"md_changelog.html#autotoc_md19":[0,1,0], +"md_changelog.html#autotoc_md10":[0,0,2,1], +"md_changelog.html#autotoc_md11":[0,0,2,2], +"md_changelog.html#autotoc_md12":[0,0,3], +"md_changelog.html#autotoc_md13":[0,0,3,0], +"md_changelog.html#autotoc_md14":[0,0,4], +"md_changelog.html#autotoc_md15":[0,0,4,0], +"md_changelog.html#autotoc_md16":[0,0,4,1], +"md_changelog.html#autotoc_md17":[0,0,4,2], +"md_changelog.html#autotoc_md18":[0,0,5], +"md_changelog.html#autotoc_md19":[0,0,5,0], "md_changelog.html#autotoc_md2":[0,0,0], -"md_changelog.html#autotoc_md20":[0,1,1], -"md_changelog.html#autotoc_md21":[0,1,1,0], -"md_changelog.html#autotoc_md22":[0,1,1,1], +"md_changelog.html#autotoc_md20":[0,0,5,1], +"md_changelog.html#autotoc_md21":[0,1], +"md_changelog.html#autotoc_md22":[0,1,0], +"md_changelog.html#autotoc_md23":[0,1,1], +"md_changelog.html#autotoc_md24":[0,1,1,0], +"md_changelog.html#autotoc_md25":[0,1,1,1], "md_changelog.html#autotoc_md3":[0,0,0,0], "md_changelog.html#autotoc_md4":[0,0,0,1], "md_changelog.html#autotoc_md5":[0,0,1], "md_changelog.html#autotoc_md6":[0,0,1,0], "md_changelog.html#autotoc_md7":[0,0,1,1], -"md_changelog.html#autotoc_md8":[0,0,1,2], -"md_changelog.html#autotoc_md9":[0,0,2], +"md_changelog.html#autotoc_md8":[0,0,2], +"md_changelog.html#autotoc_md9":[0,0,2,0], "md_tests_2README.html":[2], -"md_tests_2README.html#autotoc_md135":[2,0], -"md_tests_2README.html#autotoc_md136":[2,1], -"md_tests_2README.html#autotoc_md137":[2,2], -"md_tests_2README.html#autotoc_md138":[2,3], -"md_tests_2README.html#autotoc_md139":[2,4], +"md_tests_2README.html#autotoc_md138":[2,0], +"md_tests_2README.html#autotoc_md139":[2,1], +"md_tests_2README.html#autotoc_md140":[2,2], +"md_tests_2README.html#autotoc_md141":[2,3], +"md_tests_2README.html#autotoc_md142":[2,4], "multiplexing_8h_source.html":[4,0,0,16], "pages.html":[], "signal__info_8h.html":[4,0,0,17], @@ -237,17 +245,9 @@ var NAVTREEINDEX0 = "structDecodedFrameValue.html#a32143efe2023a1ecd977a9ae93e50b73":[3,0,19,1], "structDecodedFrameValue.html#a33e971ba10030506007aa201c89f351d":[3,0,19,0], "structDecodedFrameValue.html#a4f51c31110dba1f73a763c596372beb0":[3,0,19,3], +"structDecodedFrameValue.html#a645f433798d48a2787e7f00dfbb7dff9":[3,0,19,4], "structDecodedFrameValue.html#adae123ac7fbb4a5c314b64b01109f4a8":[3,0,19,2], "structDecodedSignalValue.html":[3,0,20], "structDecodedSignalValue.html#a33df0fcd92b23b3e08539906fc6b4c4a":[3,0,20,2], -"structDecodedSignalValue.html#a503c90b4b1a610b8252c45e466367c28":[3,0,20,3], -"structDecodedSignalValue.html#a6847d05b3cb00d5e0f6c611968f57978":[3,0,20,4], -"structDecodedSignalValue.html#a828b5ef7faefc14fbf5dbd46249bcad2":[3,0,20,1], -"structDecodedSignalValue.html#aac0ed361af545cde48d0e6f41a859686":[3,0,20,5], -"structDecodedSignalValue.html#affc38af1bf31bcb6f2c33044bce92bd2":[3,0,20,0], -"structFrameInfo.html":[3,0,23], -"structFrameInfo.html#a34b726afaad2ac5c697fdb2ac070cdaf":[3,0,23,2], -"structFrameInfo.html#a3f7e25d67ddae22f2ed50823a5a26084":[3,0,23,8], -"structFrameInfo.html#a4737a5420213ad4304724e39f3a4e55d":[3,0,23,4], -"structFrameInfo.html#a92749ec3798d4211a505860fb389a9e8":[3,0,23,6] +"structDecodedSignalValue.html#a503c90b4b1a610b8252c45e466367c28":[3,0,20,3] }; diff --git a/docs/html/navtreeindex1.js b/docs/html/navtreeindex1.js index 3a8ac1e..e58ba8f 100644 --- a/docs/html/navtreeindex1.js +++ b/docs/html/navtreeindex1.js @@ -1,5 +1,14 @@ var NAVTREEINDEX1 = { +"structDecodedSignalValue.html#a6847d05b3cb00d5e0f6c611968f57978":[3,0,20,4], +"structDecodedSignalValue.html#a828b5ef7faefc14fbf5dbd46249bcad2":[3,0,20,1], +"structDecodedSignalValue.html#aac0ed361af545cde48d0e6f41a859686":[3,0,20,5], +"structDecodedSignalValue.html#affc38af1bf31bcb6f2c33044bce92bd2":[3,0,20,0], +"structFrameInfo.html":[3,0,23], +"structFrameInfo.html#a34b726afaad2ac5c697fdb2ac070cdaf":[3,0,23,2], +"structFrameInfo.html#a3f7e25d67ddae22f2ed50823a5a26084":[3,0,23,8], +"structFrameInfo.html#a4737a5420213ad4304724e39f3a4e55d":[3,0,23,4], +"structFrameInfo.html#a92749ec3798d4211a505860fb389a9e8":[3,0,23,6], "structFrameInfo.html#aa2b198ea0f55e560c123d35e7b3ecab9":[3,0,23,5], "structFrameInfo.html#abb53a7f93ed7abc0ea1ae4a8ccafcdcb":[3,0,23,0], "structFrameInfo.html#ace75121294f9d89b762080ab5643293c":[3,0,23,7], diff --git a/docs/html/search/all_0.js b/docs/html/search/all_0.js index 90954db..c279849 100644 --- a/docs/html/search/all_0.js +++ b/docs/html/search/all_0.js @@ -1,10 +1,10 @@ var searchData= [ - ['0_20—_202026_2006_2014_0',['v1.0 — 2026-06-14',['../md_changelog.html#autotoc_md18',1,'']]], - ['01_1',['2026-10-01',['../md_changelog.html#autotoc_md15',1,'']]], - ['03_2013_2',['Initial implementation — 2026-03-13',['../md_changelog.html#autotoc_md20',1,'']]], - ['05_3',['2026-10-05',['../md_changelog.html#autotoc_md11',1,'']]], - ['06_4',['2026-10-06',['../md_changelog.html#autotoc_md9',1,'']]], - ['06_2014_5',['v1.0 — 2026-06-14',['../md_changelog.html#autotoc_md18',1,'']]], - ['07_6',['2026-10-07',['../md_changelog.html#autotoc_md5',1,'']]] + ['0_20—_202026_2006_2014_0',['v1.0 — 2026-06-14',['../md_changelog.html#autotoc_md21',1,'']]], + ['01_1',['2026-10-01',['../md_changelog.html#autotoc_md18',1,'']]], + ['03_2013_2',['Initial implementation — 2026-03-13',['../md_changelog.html#autotoc_md23',1,'']]], + ['05_3',['2026-10-05',['../md_changelog.html#autotoc_md14',1,'']]], + ['06_4',['2026-10-06',['../md_changelog.html#autotoc_md12',1,'']]], + ['06_2014_5',['v1.0 — 2026-06-14',['../md_changelog.html#autotoc_md21',1,'']]], + ['07_6',['2026-10-07',['../md_changelog.html#autotoc_md8',1,'']]] ]; diff --git a/docs/html/search/all_1.js b/docs/html/search/all_1.js index 4a5c663..b4d0879 100644 --- a/docs/html/search/all_1.js +++ b/docs/html/search/all_1.js @@ -1,13 +1,14 @@ var searchData= [ - ['1_20already_20implemented_0',['Stage 1 - already implemented',['../md_README.html#autotoc_md117',1,'']]], - ['1_20parse_20and_20ui_20path_1',['1. Parse and UI path',['../md_README.html#autotoc_md27',1,'']]], - ['10_2001_2',['2026-10-01',['../md_changelog.html#autotoc_md15',1,'']]], - ['10_2005_3',['2026-10-05',['../md_changelog.html#autotoc_md11',1,'']]], - ['10_2006_4',['2026-10-06',['../md_changelog.html#autotoc_md9',1,'']]], - ['10_2007_5',['2026-10-07',['../md_changelog.html#autotoc_md5',1,'']]], - ['10_2011_6',['2026-10-11',['../md_changelog.html#autotoc_md2',1,'']]], - ['11_7',['2026-10-11',['../md_changelog.html#autotoc_md2',1,'']]], - ['13_8',['Initial implementation — 2026-03-13',['../md_changelog.html#autotoc_md20',1,'']]], - ['14_9',['v1.0 — 2026-06-14',['../md_changelog.html#autotoc_md18',1,'']]] + ['1_20already_20implemented_0',['Stage 1 - already implemented',['../md_README.html#autotoc_md120',1,'']]], + ['1_20parse_20and_20ui_20path_1',['1. Parse and UI path',['../md_README.html#autotoc_md30',1,'']]], + ['10_2001_2',['2026-10-01',['../md_changelog.html#autotoc_md18',1,'']]], + ['10_2005_3',['2026-10-05',['../md_changelog.html#autotoc_md14',1,'']]], + ['10_2006_4',['2026-10-06',['../md_changelog.html#autotoc_md12',1,'']]], + ['10_2007_5',['2026-10-07',['../md_changelog.html#autotoc_md8',1,'']]], + ['10_2011_6',['2026-10-11',['../md_changelog.html#autotoc_md5',1,'']]], + ['10_2013_7',['2026-10-13',['../md_changelog.html#autotoc_md2',1,'']]], + ['11_8',['2026-10-11',['../md_changelog.html#autotoc_md5',1,'']]], + ['13_9',['13',['../md_changelog.html#autotoc_md2',1,'2026-10-13'],['../md_changelog.html#autotoc_md23',1,'Initial implementation — 2026-03-13']]], + ['14_10',['v1.0 — 2026-06-14',['../md_changelog.html#autotoc_md21',1,'']]] ]; diff --git a/docs/html/search/all_10.js b/docs/html/search/all_10.js index 9b467b5..069ea5d 100644 --- a/docs/html/search/all_10.js +++ b/docs/html/search/all_10.js @@ -1,13 +1,13 @@ var searchData= [ - ['maintenance_0',['maintenance',['../md_changelog.html#autotoc_md14',1,'Documentation and maintenance'],['../md_changelog.html#autotoc_md22',1,'Documentation and maintenance']]], + ['maintenance_0',['maintenance',['../md_changelog.html#autotoc_md17',1,'Documentation and maintenance'],['../md_changelog.html#autotoc_md25',1,'Documentation and maintenance']]], ['maximum_1',['maximum',['../structDecodeSignal.html#a4a6ba87142a9badbf0ca830e9e709cb7',1,'DecodeSignal::maximum'],['../structSignalInfo.html#adf1c4331b9eabced0d7fdda40ff0eb52',1,'SignalInfo::maximum']]], - ['measuring_20code_20coverage_2',['Measuring code coverage',['../md_README.html#autotoc_md130',1,'']]], + ['measuring_20code_20coverage_2',['Measuring code coverage',['../md_README.html#autotoc_md133',1,'']]], ['minimum_3',['minimum',['../structDecodeSignal.html#a0fb84e19a0ab3bd50b6f65c1075ebc25',1,'DecodeSignal::minimum'],['../structSignalInfo.html#ada7bf94fbeaa26bd08c73982b5af255b',1,'SignalInfo::minimum']]], - ['model_20adapter_4',['Optional Qt model adapter',['../md_README.html#autotoc_md124',1,'']]], - ['model_20tests_5',['Tree and optional model tests',['../md_tests_2README.html#autotoc_md137',1,'']]], + ['model_20adapter_4',['Optional Qt model adapter',['../md_README.html#autotoc_md127',1,'']]], + ['model_20tests_5',['Tree and optional model tests',['../md_tests_2README.html#autotoc_md140',1,'']]], ['module_20documentation_6',['DBC Parser and Runtime Decode Module Documentation',['../md_README.html',1,'']]], - ['module_20layout_7',['Module Layout',['../md_README.html#autotoc_md32',1,'']]], - ['motorola_20extraction_8',['Intel and Motorola Extraction',['../md_README.html#autotoc_md94',1,'']]], - ['multiplexing_9',['multiplexing',['../md_README.html#autotoc_md84',1,'Basic multiplexing'],['../structMultiplexing.html',1,'Multiplexing']]] + ['module_20layout_7',['Module Layout',['../md_README.html#autotoc_md35',1,'']]], + ['motorola_20extraction_8',['Intel and Motorola Extraction',['../md_README.html#autotoc_md97',1,'']]], + ['multiplexing_9',['multiplexing',['../md_README.html#autotoc_md87',1,'Basic multiplexing'],['../structMultiplexing.html',1,'Multiplexing']]] ]; diff --git a/docs/html/search/all_11.js b/docs/html/search/all_11.js index ac067a1..f5cd7b1 100644 --- a/docs/html/search/all_11.js +++ b/docs/html/search/all_11.js @@ -1,8 +1,8 @@ var searchData= [ ['name_0',['name',['../structDecodeSignal.html#a58cf5844f2272a23dcb9687d912e4780',1,'DecodeSignal::name'],['../structDecodeFrame.html#aa0a7f8fd5bfde2148dfd0d838328f579',1,'DecodeFrame::name'],['../structFrameInfo.html#abb53a7f93ed7abc0ea1ae4a8ccafcdcb',1,'FrameInfo::name'],['../structSignalInfo.html#a379e422a00cad56f896cacf60455b088',1,'SignalInfo::name']]], - ['next_20steps_1',['Recommended Next Steps',['../md_README.html#autotoc_md116',1,'']]], + ['next_20steps_1',['Recommended Next Steps',['../md_README.html#autotoc_md119',1,'']]], ['nodetype_2',['NodeType',['../tree__node_8h.html#acac9cbaeea226ed297804c012dc12b16',1,'tree_node.h']]], - ['normalization_3',['CAN ID Normalization',['../md_README.html#autotoc_md88',1,'']]], - ['not_20enough_4',['Why the Tree Is Not Enough',['../md_README.html#autotoc_md108',1,'']]] + ['normalization_3',['CAN ID Normalization',['../md_README.html#autotoc_md91',1,'']]], + ['not_20enough_4',['Why the Tree Is Not Enough',['../md_README.html#autotoc_md111',1,'']]] ]; diff --git a/docs/html/search/all_12.js b/docs/html/search/all_12.js index 164a502..8af7674 100644 --- a/docs/html/search/all_12.js +++ b/docs/html/search/all_12.js @@ -1,9 +1,9 @@ var searchData= [ ['offset_0',['offset',['../structDecodeSignal.html#a92db637af4e224a0ae70173050dc3f74',1,'DecodeSignal::offset'],['../structSignalInfo.html#ab7d0769f47aa540656b22e4e49db517e',1,'SignalInfo::offset']]], - ['on_20debian_20ubuntu_1',['GCC, gcov, and LCOV on Debian/Ubuntu',['../md_README.html#autotoc_md131',1,'']]], - ['optional_20model_20tests_2',['Tree and optional model tests',['../md_tests_2README.html#autotoc_md137',1,'']]], - ['optional_20qt_20model_20adapter_3',['Optional Qt model adapter',['../md_README.html#autotoc_md124',1,'']]], - ['overview_4',['Overview',['../md_README.html#autotoc_md24',1,'']]], - ['ownership_20and_20reload_5',['Definition ownership and reload',['../md_README.html#autotoc_md67',1,'']]] + ['on_20debian_20ubuntu_1',['GCC, gcov, and LCOV on Debian/Ubuntu',['../md_README.html#autotoc_md134',1,'']]], + ['optional_20model_20tests_2',['Tree and optional model tests',['../md_tests_2README.html#autotoc_md140',1,'']]], + ['optional_20qt_20model_20adapter_3',['Optional Qt model adapter',['../md_README.html#autotoc_md127',1,'']]], + ['overview_4',['Overview',['../md_README.html#autotoc_md27',1,'']]], + ['ownership_20and_20reload_5',['Definition ownership and reload',['../md_README.html#autotoc_md70',1,'']]] ]; diff --git a/docs/html/search/all_13.js b/docs/html/search/all_13.js index 191bae6..e8497e2 100644 --- a/docs/html/search/all_13.js +++ b/docs/html/search/all_13.js @@ -1,20 +1,20 @@ var searchData= [ - ['parse_20and_20ui_20path_0',['1. Parse and UI path',['../md_README.html#autotoc_md27',1,'']]], - ['parse_20dbc_1',['Parse DBC',['../md_README.html#autotoc_md97',1,'']]], - ['parse_20errors_2',['parse errors',['../md_README.html#autotoc_md78',1,'Frame validation and parse errors'],['../md_README.html#autotoc_md82',1,'Signal validation and parse errors']]], - ['parsed_20data_20structures_3',['Parsed Data Structures',['../md_README.html#autotoc_md34',1,'']]], + ['parse_20and_20ui_20path_0',['1. Parse and UI path',['../md_README.html#autotoc_md30',1,'']]], + ['parse_20dbc_1',['Parse DBC',['../md_README.html#autotoc_md100',1,'']]], + ['parse_20errors_2',['parse errors',['../md_README.html#autotoc_md81',1,'Frame validation and parse errors'],['../md_README.html#autotoc_md85',1,'Signal validation and parse errors']]], + ['parsed_20data_20structures_3',['Parsed Data Structures',['../md_README.html#autotoc_md37',1,'']]], ['parsefile_4',['ParseFile',['../classDbcParser.html#aef72826942c9095d653fafa435855e56',1,'DbcParser']]], ['parsefilewithdiagnostics_5',['ParseFileWithDiagnostics',['../classDbcParser.html#a0e915ce79c2e3d4c95a5aaf2bf0409bc',1,'DbcParser']]], ['parser_20and_20runtime_20decode_20module_20documentation_6',['DBC Parser and Runtime Decode Module Documentation',['../md_README.html',1,'']]], - ['parser_20coverage_7',['Parser coverage',['../md_tests_2README.html#autotoc_md136',1,'']]], - ['parser_20support_8',['Parser Support',['../md_README.html#autotoc_md72',1,'']]], - ['path_9',['path',['../md_README.html#autotoc_md27',1,'1. Parse and UI path'],['../md_README.html#autotoc_md28',1,'2. Runtime decode path'],['../md_README.html#autotoc_md103',1,'live path'],['../md_README.html#autotoc_md104',1,'trace path']]], + ['parser_20coverage_7',['Parser coverage',['../md_tests_2README.html#autotoc_md139',1,'']]], + ['parser_20support_8',['Parser Support',['../md_README.html#autotoc_md75',1,'']]], + ['path_9',['path',['../md_README.html#autotoc_md30',1,'1. Parse and UI path'],['../md_README.html#autotoc_md31',1,'2. Runtime decode path'],['../md_README.html#autotoc_md106',1,'live path'],['../md_README.html#autotoc_md107',1,'trace path']]], ['pendingvaluedescriptions_10',['PendingValueDescriptions',['../structPendingValueDescriptions.html',1,'']]], ['pgn_11',['pgn',['../structFrameInfo.html#aee518b33f1000bf0b7f6e4a87474c943',1,'FrameInfo::pgn'],['../structDecodeFrame.html#a1d3c54055c0cbceb73adf551f7067f23',1,'DecodeFrame::pgn']]], - ['pgn_20extraction_12',['PGN Extraction',['../md_README.html#autotoc_md90',1,'']]], + ['pgn_20extraction_12',['PGN Extraction',['../md_README.html#autotoc_md93',1,'']]], ['physicalvalue_13',['physicalValue',['../structDecodedSignalValue.html#a6847d05b3cb00d5e0f6c611968f57978',1,'DecodedSignalValue']]], - ['precision_14',['Raw value representation and precision',['../md_README.html#autotoc_md64',1,'']]], + ['precision_14',['Raw value representation and precision',['../md_README.html#autotoc_md67',1,'']]], ['public_15',['public',['../structFrameInfo.html#a3f7e25d67ddae22f2ed50823a5a26084',1,'FrameInfo::public'],['../structDecodeFrame.html#a7f4e71bbdec3f424b625e724e3dfbf14',1,'DecodeFrame::public'],['../structDecodedFrameValue.html#adae123ac7fbb4a5c314b64b01109f4a8',1,'DecodedFrameValue::public']]], - ['purpose_16',['Purpose',['../md_README.html#autotoc_md45',1,'']]] + ['purpose_16',['Purpose',['../md_README.html#autotoc_md48',1,'']]] ]; diff --git a/docs/html/search/all_14.js b/docs/html/search/all_14.js index 6c9ae5c..a35e146 100644 --- a/docs/html/search/all_14.js +++ b/docs/html/search/all_14.js @@ -1,8 +1,8 @@ var searchData= [ - ['qt_0',['Standalone tree tests without Qt',['../md_README.html#autotoc_md125',1,'']]], - ['qt_20creator_1',['qt creator',['../md_README.html#autotoc_md133',1,'Configure all build variants in Qt Creator'],['../md_README.html#autotoc_md132',1,'Configure the GCC coverage build in Qt Creator']]], - ['qt_20libraries_2',['Build core without Qt libraries',['../md_README.html#autotoc_md123',1,'']]], - ['qt_20model_20adapter_3',['Optional Qt model adapter',['../md_README.html#autotoc_md124',1,'']]], + ['qt_0',['Standalone tree tests without Qt',['../md_README.html#autotoc_md128',1,'']]], + ['qt_20creator_1',['qt creator',['../md_README.html#autotoc_md136',1,'Configure all build variants in Qt Creator'],['../md_README.html#autotoc_md135',1,'Configure the GCC coverage build in Qt Creator']]], + ['qt_20libraries_2',['Build core without Qt libraries',['../md_README.html#autotoc_md126',1,'']]], + ['qt_20model_20adapter_3',['Optional Qt model adapter',['../md_README.html#autotoc_md127',1,'']]], ['qttest_20regression_20tests_4',['QtTest regression tests',['../md_tests_2README.html',1,'']]] ]; diff --git a/docs/html/search/all_15.js b/docs/html/search/all_15.js index f411f4c..9d997aa 100644 --- a/docs/html/search/all_15.js +++ b/docs/html/search/all_15.js @@ -1,22 +1,22 @@ var searchData= [ - ['raw_20frame_0',['Decode a raw frame',['../md_README.html#autotoc_md100',1,'']]], - ['raw_20value_20representation_20and_20precision_1',['Raw value representation and precision',['../md_README.html#autotoc_md64',1,'']]], + ['raw_20frame_0',['Decode a raw frame',['../md_README.html#autotoc_md103',1,'']]], + ['raw_20value_20representation_20and_20precision_1',['Raw value representation and precision',['../md_README.html#autotoc_md67',1,'']]], ['rawcanframe_2',['RawCanFrame',['../structRawCanFrame.html',1,'']]], - ['rawcanframe_20tt_3',['<tt>RawCanFrame</tt>',['../md_README.html#autotoc_md60',1,'']]], + ['rawcanframe_20tt_3',['<tt>RawCanFrame</tt>',['../md_README.html#autotoc_md63',1,'']]], ['rawvalue_4',['rawValue',['../structDecodedSignalValue.html#a503c90b4b1a610b8252c45e466367c28',1,'DecodedSignalValue']]], ['receivers_5',['receivers',['../structSignalInfo.html#a2c95562fd0b29327a7fd7b1e6feee7f3',1,'SignalInfo::receivers'],['../structDecodeSignal.html#abdbae62a64d2df716bf60c6b67b30eaa',1,'DecodeSignal::receivers']]], - ['recommended_20next_20steps_6',['Recommended Next Steps',['../md_README.html#autotoc_md116',1,'']]], + ['recommended_20next_20steps_6',['Recommended Next Steps',['../md_README.html#autotoc_md119',1,'']]], ['registertype_7',['registerType',['../classTypeFactory.html#ad6d85795285ee21193ac03e1b24762d3',1,'TypeFactory']]], - ['regression_20coverage_8',['Additional regression coverage',['../md_tests_2README.html#autotoc_md138',1,'']]], - ['regression_20tests_9',['regression tests',['../md_tests_2README.html',1,'QtTest regression tests'],['../md_README.html#autotoc_md129',1,'Regression tests']]], - ['reload_10',['Definition ownership and reload',['../md_README.html#autotoc_md67',1,'']]], - ['reload_20coverage_11',['Definition lifetime and reload coverage',['../md_tests_2README.html#autotoc_md139',1,'']]], - ['representation_20and_20precision_12',['Raw value representation and precision',['../md_README.html#autotoc_md64',1,'']]], - ['run_20the_20demo_13',['Build and run the demo',['../md_README.html#autotoc_md126',1,'']]], - ['runtime_20decode_20database_14',['Build runtime decode database',['../md_README.html#autotoc_md99',1,'']]], - ['runtime_20decode_20layer_15',['Runtime Decode Layer',['../md_README.html#autotoc_md44',1,'']]], - ['runtime_20decode_20layer_20exists_16',['Why the Runtime Decode Layer Exists',['../md_README.html#autotoc_md30',1,'']]], + ['regression_20coverage_8',['Additional regression coverage',['../md_tests_2README.html#autotoc_md141',1,'']]], + ['regression_20tests_9',['regression tests',['../md_tests_2README.html',1,'QtTest regression tests'],['../md_README.html#autotoc_md132',1,'Regression tests']]], + ['reload_10',['Definition ownership and reload',['../md_README.html#autotoc_md70',1,'']]], + ['reload_20coverage_11',['Definition lifetime and reload coverage',['../md_tests_2README.html#autotoc_md142',1,'']]], + ['representation_20and_20precision_12',['Raw value representation and precision',['../md_README.html#autotoc_md67',1,'']]], + ['run_20the_20demo_13',['Build and run the demo',['../md_README.html#autotoc_md129',1,'']]], + ['runtime_20decode_20database_14',['Build runtime decode database',['../md_README.html#autotoc_md102',1,'']]], + ['runtime_20decode_20layer_15',['Runtime Decode Layer',['../md_README.html#autotoc_md47',1,'']]], + ['runtime_20decode_20layer_20exists_16',['Why the Runtime Decode Layer Exists',['../md_README.html#autotoc_md33',1,'']]], ['runtime_20decode_20module_20documentation_17',['DBC Parser and Runtime Decode Module Documentation',['../md_README.html',1,'']]], - ['runtime_20decode_20path_18',['2. Runtime decode path',['../md_README.html#autotoc_md28',1,'']]] + ['runtime_20decode_20path_18',['2. Runtime decode path',['../md_README.html#autotoc_md31',1,'']]] ]; diff --git a/docs/html/search/all_16.js b/docs/html/search/all_16.js index 4618291..2f2fa1a 100644 --- a/docs/html/search/all_16.js +++ b/docs/html/search/all_16.js @@ -1,27 +1,28 @@ var searchData= [ - ['signal_20definition_0',['Signal definition',['../md_README.html#autotoc_md80',1,'']]], - ['signal_20identifiers_1',['Stable frame and signal identifiers',['../md_README.html#autotoc_md68',1,'']]], - ['signal_20validation_20and_20parse_20errors_2',['Signal validation and parse errors',['../md_README.html#autotoc_md82',1,'']]], - ['signal_20value_20descriptions_20tt_20val_5f_20tt_3',['Signal value descriptions (<tt>VAL_</tt>)',['../md_README.html#autotoc_md112',1,'']]], + ['signal_20definition_0',['Signal definition',['../md_README.html#autotoc_md83',1,'']]], + ['signal_20identifiers_1',['Stable frame and signal identifiers',['../md_README.html#autotoc_md71',1,'']]], + ['signal_20validation_20and_20parse_20errors_2',['Signal validation and parse errors',['../md_README.html#autotoc_md85',1,'']]], + ['signal_20value_20descriptions_20tt_20val_5f_20tt_3',['Signal value descriptions (<tt>VAL_</tt>)',['../md_README.html#autotoc_md115',1,'']]], ['signal_5finfo_2eh_4',['signal_info.h',['../signal__info_8h.html',1,'']]], ['signalinfo_5',['SignalInfo',['../structSignalInfo.html',1,'']]], - ['signalinfo_20tt_6',['<tt>SignalInfo</tt>',['../md_README.html#autotoc_md35',1,'']]], + ['signalinfo_20tt_6',['<tt>SignalInfo</tt>',['../md_README.html#autotoc_md38',1,'']]], ['signalkey_7',['SignalKey',['../structSignalKey.html',1,'']]], ['signalkeyhasher_8',['SignalKeyHasher',['../structSignalKeyHasher.html',1,'']]], - ['stable_20frame_20and_20signal_20identifiers_9',['Stable frame and signal identifiers',['../md_README.html#autotoc_md68',1,'']]], - ['stage_201_20already_20implemented_10',['Stage 1 - already implemented',['../md_README.html#autotoc_md117',1,'']]], - ['stage_202_11',['Stage 2',['../md_README.html#autotoc_md118',1,'']]], - ['stage_203_12',['Stage 3',['../md_README.html#autotoc_md119',1,'']]], - ['stage_204_13',['Stage 4',['../md_README.html#autotoc_md120',1,'']]], - ['standalone_20tree_20tests_20without_20qt_14',['Standalone tree tests without Qt',['../md_README.html#autotoc_md125',1,'']]], + ['stable_20frame_20and_20signal_20identifiers_9',['Stable frame and signal identifiers',['../md_README.html#autotoc_md71',1,'']]], + ['stage_201_20already_20implemented_10',['Stage 1 - already implemented',['../md_README.html#autotoc_md120',1,'']]], + ['stage_202_11',['Stage 2',['../md_README.html#autotoc_md121',1,'']]], + ['stage_203_12',['Stage 3',['../md_README.html#autotoc_md122',1,'']]], + ['stage_204_13',['Stage 4',['../md_README.html#autotoc_md123',1,'']]], + ['standalone_20tree_20tests_20without_20qt_14',['Standalone tree tests without Qt',['../md_README.html#autotoc_md128',1,'']]], ['startbit_15',['startbit',['../structSignalInfo.html#ace290724f9801521609aedaa946d61f1',1,'SignalInfo::startBit'],['../structDecodeSignal.html#aaf5d7527ddf65b6355c2b4f3626df22d',1,'DecodeSignal::startBit']]], - ['statement_20handler_20factory_16',['Statement Handler Factory',['../md_README.html#autotoc_md110',1,'']]], - ['steps_17',['Recommended Next Steps',['../md_README.html#autotoc_md116',1,'']]], - ['strategy_18',['Unified Decode Strategy',['../md_README.html#autotoc_md102',1,'']]], - ['structured_20diagnostics_19',['Structured diagnostics',['../md_README.html#autotoc_md73',1,'']]], - ['structures_20',['Parsed Data Structures',['../md_README.html#autotoc_md34',1,'']]], - ['support_21',['Parser Support',['../md_README.html#autotoc_md72',1,'']]], - ['supported_20dbc_20syntax_22',['Supported DBC Syntax',['../md_README.html#autotoc_md75',1,'']]], - ['syntax_23',['Supported DBC Syntax',['../md_README.html#autotoc_md75',1,'']]] + ['statement_20handler_20factory_16',['Statement Handler Factory',['../md_README.html#autotoc_md113',1,'']]], + ['status_17',['status',['../structDecodedFrameValue.html#a645f433798d48a2787e7f00dfbb7dff9',1,'DecodedFrameValue']]], + ['steps_18',['Recommended Next Steps',['../md_README.html#autotoc_md119',1,'']]], + ['strategy_19',['Unified Decode Strategy',['../md_README.html#autotoc_md105',1,'']]], + ['structured_20diagnostics_20',['Structured diagnostics',['../md_README.html#autotoc_md76',1,'']]], + ['structures_21',['Parsed Data Structures',['../md_README.html#autotoc_md37',1,'']]], + ['support_22',['Parser Support',['../md_README.html#autotoc_md75',1,'']]], + ['supported_20dbc_20syntax_23',['Supported DBC Syntax',['../md_README.html#autotoc_md78',1,'']]], + ['syntax_24',['Supported DBC Syntax',['../md_README.html#autotoc_md78',1,'']]] ]; diff --git a/docs/html/search/all_17.js b/docs/html/search/all_17.js index ec968d2..950b235 100644 --- a/docs/html/search/all_17.js +++ b/docs/html/search/all_17.js @@ -1,37 +1,37 @@ var searchData= [ - ['tests_0',['tests',['../md_tests_2README.html',1,'QtTest regression tests'],['../md_README.html#autotoc_md129',1,'Regression tests'],['../md_tests_2README.html#autotoc_md137',1,'Tree and optional model tests']]], - ['tests_20without_20qt_1',['Standalone tree tests without Qt',['../md_README.html#autotoc_md125',1,'']]], - ['the_20demo_2',['Build and run the demo',['../md_README.html#autotoc_md126',1,'']]], - ['the_20gcc_20coverage_20build_20in_20qt_20creator_3',['Configure the GCC coverage build in Qt Creator',['../md_README.html#autotoc_md132',1,'']]], - ['the_20runtime_20decode_20layer_20exists_4',['Why the Runtime Decode Layer Exists',['../md_README.html#autotoc_md30',1,'']]], - ['the_20tree_20is_20not_20enough_5',['Why the Tree Is Not Enough',['../md_README.html#autotoc_md108',1,'']]], - ['trace_20path_6',['trace path',['../md_README.html#autotoc_md104',1,'']]], + ['tests_0',['tests',['../md_tests_2README.html',1,'QtTest regression tests'],['../md_README.html#autotoc_md132',1,'Regression tests'],['../md_tests_2README.html#autotoc_md140',1,'Tree and optional model tests']]], + ['tests_20without_20qt_1',['Standalone tree tests without Qt',['../md_README.html#autotoc_md128',1,'']]], + ['the_20demo_2',['Build and run the demo',['../md_README.html#autotoc_md129',1,'']]], + ['the_20gcc_20coverage_20build_20in_20qt_20creator_3',['Configure the GCC coverage build in Qt Creator',['../md_README.html#autotoc_md135',1,'']]], + ['the_20runtime_20decode_20layer_20exists_4',['Why the Runtime Decode Layer Exists',['../md_README.html#autotoc_md33',1,'']]], + ['the_20tree_20is_20not_20enough_5',['Why the Tree Is Not Enough',['../md_README.html#autotoc_md111',1,'']]], + ['trace_20path_6',['trace path',['../md_README.html#autotoc_md107',1,'']]], ['transmitter_7',['transmitter',['../structFrameInfo.html#a92749ec3798d4211a505860fb389a9e8',1,'FrameInfo::transmitter'],['../structDecodeFrame.html#ad78241dc40c793aabb8495b6989b81a9',1,'DecodeFrame::transmitter']]], - ['tree_8',['Build UI tree',['../md_README.html#autotoc_md98',1,'']]], - ['tree_20and_20optional_20model_20tests_9',['Tree and optional model tests',['../md_tests_2README.html#autotoc_md137',1,'']]], - ['tree_20is_20not_20enough_10',['Why the Tree Is Not Enough',['../md_README.html#autotoc_md108',1,'']]], - ['tree_20layer_11',['UI Tree Layer',['../md_README.html#autotoc_md41',1,'']]], - ['tree_20tests_20without_20qt_12',['Standalone tree tests without Qt',['../md_README.html#autotoc_md125',1,'']]], + ['tree_8',['Build UI tree',['../md_README.html#autotoc_md101',1,'']]], + ['tree_20and_20optional_20model_20tests_9',['Tree and optional model tests',['../md_tests_2README.html#autotoc_md140',1,'']]], + ['tree_20is_20not_20enough_10',['Why the Tree Is Not Enough',['../md_README.html#autotoc_md111',1,'']]], + ['tree_20layer_11',['UI Tree Layer',['../md_README.html#autotoc_md44',1,'']]], + ['tree_20tests_20without_20qt_12',['Standalone tree tests without Qt',['../md_README.html#autotoc_md128',1,'']]], ['tree_5fnode_2ecpp_13',['tree_node.cpp',['../tree__node_8cpp.html',1,'']]], ['tree_5fnode_2eh_14',['tree_node.h',['../tree__node_8h.html',1,'']]], ['treenode_15',['treenode',['../classTreeNode.html#ab9f00f5253d56461bb2d420fd639c5ae',1,'TreeNode::TreeNode()'],['../classTreeNode.html',1,'TreeNode'],['../classTreeNode.html#a984a98d5ccf7ef1f5a18094c6821f35d',1,'TreeNode::TreeNode()'],['../classTreeNode.html#a8e38dd591d8801d7ee3a7dbad90c67fc',1,'TreeNode::TreeNode(const FrameInfo &frame)']]], - ['treenode_20tt_16',['<tt>TreeNode</tt>',['../md_README.html#autotoc_md42',1,'']]], - ['tt_20byteorder_20tt_17',['<tt>ByteOrder</tt>',['../md_README.html#autotoc_md47',1,'']]], - ['tt_20dbcdatabase_20tt_18',['<tt>DbcDatabase</tt>',['../md_README.html#autotoc_md39',1,'']]], - ['tt_20dbcdecoder_20tt_19',['<tt>DbcDecoder</tt>',['../md_README.html#autotoc_md70',1,'']]], - ['tt_20decodedatabase_20tt_20',['<tt>DecodeDatabase</tt>',['../md_README.html#autotoc_md57',1,'']]], - ['tt_20decodedframevalue_20tt_21',['<tt>DecodedFrameValue</tt>',['../md_README.html#autotoc_md66',1,'']]], - ['tt_20decodedsignalvalue_20tt_22',['<tt>DecodedSignalValue</tt>',['../md_README.html#autotoc_md62',1,'']]], - ['tt_20decodeframe_20tt_23',['<tt>DecodeFrame</tt>',['../md_README.html#autotoc_md53',1,'']]], - ['tt_20decodesignal_20tt_24',['<tt>DecodeSignal</tt>',['../md_README.html#autotoc_md51',1,'']]], - ['tt_20frameinfo_20tt_25',['<tt>FrameInfo</tt>',['../md_README.html#autotoc_md37',1,'']]], - ['tt_20framekey_20tt_26',['<tt>FrameKey</tt>',['../md_README.html#autotoc_md55',1,'']]], - ['tt_20rawcanframe_20tt_27',['<tt>RawCanFrame</tt>',['../md_README.html#autotoc_md60',1,'']]], - ['tt_20signalinfo_20tt_28',['<tt>SignalInfo</tt>',['../md_README.html#autotoc_md35',1,'']]], - ['tt_20treenode_20tt_29',['<tt>TreeNode</tt>',['../md_README.html#autotoc_md42',1,'']]], - ['tt_20val_5f_20tt_30',['Signal value descriptions (<tt>VAL_</tt>)',['../md_README.html#autotoc_md112',1,'']]], - ['tt_20valuetype_20tt_31',['<tt>ValueType</tt>',['../md_README.html#autotoc_md49',1,'']]], + ['treenode_20tt_16',['<tt>TreeNode</tt>',['../md_README.html#autotoc_md45',1,'']]], + ['tt_20byteorder_20tt_17',['<tt>ByteOrder</tt>',['../md_README.html#autotoc_md50',1,'']]], + ['tt_20dbcdatabase_20tt_18',['<tt>DbcDatabase</tt>',['../md_README.html#autotoc_md42',1,'']]], + ['tt_20dbcdecoder_20tt_19',['<tt>DbcDecoder</tt>',['../md_README.html#autotoc_md73',1,'']]], + ['tt_20decodedatabase_20tt_20',['<tt>DecodeDatabase</tt>',['../md_README.html#autotoc_md60',1,'']]], + ['tt_20decodedframevalue_20tt_21',['<tt>DecodedFrameValue</tt>',['../md_README.html#autotoc_md69',1,'']]], + ['tt_20decodedsignalvalue_20tt_22',['<tt>DecodedSignalValue</tt>',['../md_README.html#autotoc_md65',1,'']]], + ['tt_20decodeframe_20tt_23',['<tt>DecodeFrame</tt>',['../md_README.html#autotoc_md56',1,'']]], + ['tt_20decodesignal_20tt_24',['<tt>DecodeSignal</tt>',['../md_README.html#autotoc_md54',1,'']]], + ['tt_20frameinfo_20tt_25',['<tt>FrameInfo</tt>',['../md_README.html#autotoc_md40',1,'']]], + ['tt_20framekey_20tt_26',['<tt>FrameKey</tt>',['../md_README.html#autotoc_md58',1,'']]], + ['tt_20rawcanframe_20tt_27',['<tt>RawCanFrame</tt>',['../md_README.html#autotoc_md63',1,'']]], + ['tt_20signalinfo_20tt_28',['<tt>SignalInfo</tt>',['../md_README.html#autotoc_md38',1,'']]], + ['tt_20treenode_20tt_29',['<tt>TreeNode</tt>',['../md_README.html#autotoc_md45',1,'']]], + ['tt_20val_5f_20tt_30',['Signal value descriptions (<tt>VAL_</tt>)',['../md_README.html#autotoc_md115',1,'']]], + ['tt_20valuetype_20tt_31',['<tt>ValueType</tt>',['../md_README.html#autotoc_md52',1,'']]], ['typefactory_32',['TypeFactory',['../classTypeFactory.html',1,'']]], ['typefactory_3c_20std_3a_3astring_2c_20dbcstatementhandler_20_3e_33',['TypeFactory< std::string, DbcStatementHandler >',['../classTypeFactory.html',1,'']]] ]; diff --git a/docs/html/search/all_18.js b/docs/html/search/all_18.js index b909aa5..dc3b4a8 100644 --- a/docs/html/search/all_18.js +++ b/docs/html/search/all_18.js @@ -1,12 +1,12 @@ var searchData= [ - ['ubuntu_0',['GCC, gcov, and LCOV on Debian/Ubuntu',['../md_README.html#autotoc_md131',1,'']]], - ['ui_20path_1',['1. Parse and UI path',['../md_README.html#autotoc_md27',1,'']]], - ['ui_20tree_2',['Build UI tree',['../md_README.html#autotoc_md98',1,'']]], - ['ui_20tree_20layer_3',['UI Tree Layer',['../md_README.html#autotoc_md41',1,'']]], - ['unified_20decode_20strategy_4',['Unified Decode Strategy',['../md_README.html#autotoc_md102',1,'']]], + ['ubuntu_0',['GCC, gcov, and LCOV on Debian/Ubuntu',['../md_README.html#autotoc_md134',1,'']]], + ['ui_20path_1',['1. Parse and UI path',['../md_README.html#autotoc_md30',1,'']]], + ['ui_20tree_2',['Build UI tree',['../md_README.html#autotoc_md101',1,'']]], + ['ui_20tree_20layer_3',['UI Tree Layer',['../md_README.html#autotoc_md44',1,'']]], + ['unified_20decode_20strategy_4',['Unified Decode Strategy',['../md_README.html#autotoc_md105',1,'']]], ['unit_5',['unit',['../structDecodeSignal.html#a38eed9ca7581423fd8bfbedcffc12092',1,'DecodeSignal::unit'],['../structSignalInfo.html#a1f214f03e94ef5794de2d3367bfcbd01',1,'SignalInfo::unit']]], ['unreleased_6',['Unreleased',['../md_changelog.html#autotoc_md1',1,'']]], - ['usage_7',['Example Usage',['../md_README.html#autotoc_md96',1,'']]], - ['use_20in_20frametap_8',['Intended Use in FrameTap',['../md_README.html#autotoc_md106',1,'']]] + ['usage_7',['Example Usage',['../md_README.html#autotoc_md99',1,'']]], + ['use_20in_20frametap_8',['Intended Use in FrameTap',['../md_README.html#autotoc_md109',1,'']]] ]; diff --git a/docs/html/search/all_19.js b/docs/html/search/all_19.js index 91bc6da..85c0826 100644 --- a/docs/html/search/all_19.js +++ b/docs/html/search/all_19.js @@ -1,13 +1,13 @@ var searchData= [ - ['v1_200_20—_202026_2006_2014_0',['v1.0 — 2026-06-14',['../md_changelog.html#autotoc_md18',1,'']]], - ['val_5f_20tt_1',['Signal value descriptions (<tt>VAL_</tt>)',['../md_README.html#autotoc_md112',1,'']]], + ['v1_200_20—_202026_2006_2014_0',['v1.0 — 2026-06-14',['../md_changelog.html#autotoc_md21',1,'']]], + ['val_5f_20tt_1',['Signal value descriptions (<tt>VAL_</tt>)',['../md_README.html#autotoc_md115',1,'']]], ['valid_2',['valid',['../structDecodedSignalValue.html#aac0ed361af545cde48d0e6f41a859686',1,'DecodedSignalValue::valid'],['../structDecodedFrameValue.html#a4f51c31110dba1f73a763c596372beb0',1,'DecodedFrameValue::valid']]], - ['validation_20and_20parse_20errors_3',['validation and parse errors',['../md_README.html#autotoc_md78',1,'Frame validation and parse errors'],['../md_README.html#autotoc_md82',1,'Signal validation and parse errors']]], - ['value_20descriptions_20tt_20val_5f_20tt_4',['Signal value descriptions (<tt>VAL_</tt>)',['../md_README.html#autotoc_md112',1,'']]], - ['value_20representation_20and_20precision_5',['Raw value representation and precision',['../md_README.html#autotoc_md64',1,'']]], + ['validation_20and_20parse_20errors_3',['validation and parse errors',['../md_README.html#autotoc_md81',1,'Frame validation and parse errors'],['../md_README.html#autotoc_md85',1,'Signal validation and parse errors']]], + ['value_20descriptions_20tt_20val_5f_20tt_4',['Signal value descriptions (<tt>VAL_</tt>)',['../md_README.html#autotoc_md115',1,'']]], + ['value_20representation_20and_20precision_5',['Raw value representation and precision',['../md_README.html#autotoc_md67',1,'']]], ['valuedescriptions_6',['valuedescriptions',['../structDecodeSignal.html#a822155d9bf4ed5f3300c9e9befe606ff',1,'DecodeSignal::valueDescriptions'],['../structSignalInfo.html#a43f47d0433d0cc39076bd7278b46af9c',1,'SignalInfo::valueDescriptions']]], ['valuetype_7',['valuetype',['../structDecodeSignal.html#a9d37638129263b8902ba5be59baf39a8',1,'DecodeSignal::valueType'],['../decode__database_8h.html#ad9971b6ef33e02ba2c75d19c1d2518a1',1,'ValueType: decode_database.h']]], - ['valuetype_20tt_8',['<tt>ValueType</tt>',['../md_README.html#autotoc_md49',1,'']]], - ['variants_20in_20qt_20creator_9',['Configure all build variants in Qt Creator',['../md_README.html#autotoc_md133',1,'']]] + ['valuetype_20tt_8',['<tt>ValueType</tt>',['../md_README.html#autotoc_md52',1,'']]], + ['variants_20in_20qt_20creator_9',['Configure all build variants in Qt Creator',['../md_README.html#autotoc_md136',1,'']]] ]; diff --git a/docs/html/search/all_1a.js b/docs/html/search/all_1a.js index d902d6a..eb8f4c2 100644 --- a/docs/html/search/all_1a.js +++ b/docs/html/search/all_1a.js @@ -1,7 +1,7 @@ var searchData= [ - ['why_20the_20runtime_20decode_20layer_20exists_0',['Why the Runtime Decode Layer Exists',['../md_README.html#autotoc_md30',1,'']]], - ['why_20the_20tree_20is_20not_20enough_1',['Why the Tree Is Not Enough',['../md_README.html#autotoc_md108',1,'']]], - ['without_20qt_2',['Standalone tree tests without Qt',['../md_README.html#autotoc_md125',1,'']]], - ['without_20qt_20libraries_3',['Build core without Qt libraries',['../md_README.html#autotoc_md123',1,'']]] + ['why_20the_20runtime_20decode_20layer_20exists_0',['Why the Runtime Decode Layer Exists',['../md_README.html#autotoc_md33',1,'']]], + ['why_20the_20tree_20is_20not_20enough_1',['Why the Tree Is Not Enough',['../md_README.html#autotoc_md111',1,'']]], + ['without_20qt_2',['Standalone tree tests without Qt',['../md_README.html#autotoc_md128',1,'']]], + ['without_20qt_20libraries_3',['Build core without Qt libraries',['../md_README.html#autotoc_md126',1,'']]] ]; diff --git a/docs/html/search/all_1b.js b/docs/html/search/all_1b.js index f1c1625..b63d920 100644 --- a/docs/html/search/all_1b.js +++ b/docs/html/search/all_1b.js @@ -1,5 +1,5 @@ var searchData= [ - ['—_202026_2003_2013_0',['Initial implementation — 2026-03-13',['../md_changelog.html#autotoc_md20',1,'']]], - ['—_202026_2006_2014_1',['v1.0 — 2026-06-14',['../md_changelog.html#autotoc_md18',1,'']]] + ['—_202026_2003_2013_0',['Initial implementation — 2026-03-13',['../md_changelog.html#autotoc_md23',1,'']]], + ['—_202026_2006_2014_1',['v1.0 — 2026-06-14',['../md_changelog.html#autotoc_md21',1,'']]] ]; diff --git a/docs/html/search/all_2.js b/docs/html/search/all_2.js index 43efda9..54d8527 100644 --- a/docs/html/search/all_2.js +++ b/docs/html/search/all_2.js @@ -1,12 +1,13 @@ var searchData= [ - ['2_0',['Stage 2',['../md_README.html#autotoc_md118',1,'']]], - ['2_20runtime_20decode_20path_1',['2. Runtime decode path',['../md_README.html#autotoc_md28',1,'']]], - ['2026_2003_2013_2',['Initial implementation — 2026-03-13',['../md_changelog.html#autotoc_md20',1,'']]], - ['2026_2006_2014_3',['v1.0 — 2026-06-14',['../md_changelog.html#autotoc_md18',1,'']]], - ['2026_2010_2001_4',['2026-10-01',['../md_changelog.html#autotoc_md15',1,'']]], - ['2026_2010_2005_5',['2026-10-05',['../md_changelog.html#autotoc_md11',1,'']]], - ['2026_2010_2006_6',['2026-10-06',['../md_changelog.html#autotoc_md9',1,'']]], - ['2026_2010_2007_7',['2026-10-07',['../md_changelog.html#autotoc_md5',1,'']]], - ['2026_2010_2011_8',['2026-10-11',['../md_changelog.html#autotoc_md2',1,'']]] + ['2_0',['Stage 2',['../md_README.html#autotoc_md121',1,'']]], + ['2_20runtime_20decode_20path_1',['2. Runtime decode path',['../md_README.html#autotoc_md31',1,'']]], + ['2026_2003_2013_2',['Initial implementation — 2026-03-13',['../md_changelog.html#autotoc_md23',1,'']]], + ['2026_2006_2014_3',['v1.0 — 2026-06-14',['../md_changelog.html#autotoc_md21',1,'']]], + ['2026_2010_2001_4',['2026-10-01',['../md_changelog.html#autotoc_md18',1,'']]], + ['2026_2010_2005_5',['2026-10-05',['../md_changelog.html#autotoc_md14',1,'']]], + ['2026_2010_2006_6',['2026-10-06',['../md_changelog.html#autotoc_md12',1,'']]], + ['2026_2010_2007_7',['2026-10-07',['../md_changelog.html#autotoc_md8',1,'']]], + ['2026_2010_2011_8',['2026-10-11',['../md_changelog.html#autotoc_md5',1,'']]], + ['2026_2010_2013_9',['2026-10-13',['../md_changelog.html#autotoc_md2',1,'']]] ]; diff --git a/docs/html/search/all_3.js b/docs/html/search/all_3.js index 84f0fd4..19455c4 100644 --- a/docs/html/search/all_3.js +++ b/docs/html/search/all_3.js @@ -1,4 +1,4 @@ var searchData= [ - ['3_0',['Stage 3',['../md_README.html#autotoc_md119',1,'']]] + ['3_0',['Stage 3',['../md_README.html#autotoc_md122',1,'']]] ]; diff --git a/docs/html/search/all_4.js b/docs/html/search/all_4.js index 4e80a63..4a84df0 100644 --- a/docs/html/search/all_4.js +++ b/docs/html/search/all_4.js @@ -1,4 +1,4 @@ var searchData= [ - ['4_0',['Stage 4',['../md_README.html#autotoc_md120',1,'']]] + ['4_0',['Stage 4',['../md_README.html#autotoc_md123',1,'']]] ]; diff --git a/docs/html/search/all_5.js b/docs/html/search/all_5.js index ec57e00..2d9658f 100644 --- a/docs/html/search/all_5.js +++ b/docs/html/search/all_5.js @@ -1,24 +1,24 @@ var searchData= [ - ['a_20raw_20frame_0',['Decode a raw frame',['../md_README.html#autotoc_md100',1,'']]], - ['adapter_1',['Optional Qt model adapter',['../md_README.html#autotoc_md124',1,'']]], + ['a_20raw_20frame_0',['Decode a raw frame',['../md_README.html#autotoc_md103',1,'']]], + ['adapter_1',['Optional Qt model adapter',['../md_README.html#autotoc_md127',1,'']]], ['addchild_2',['AddChild',['../classTreeNode.html#a6161f2ebb92184eab4215d121870e788',1,'TreeNode']]], - ['added_3',['added',['../md_changelog.html#autotoc_md3',1,'Added'],['../md_changelog.html#autotoc_md6',1,'Added'],['../md_changelog.html#autotoc_md10',1,'Added'],['../md_changelog.html#autotoc_md21',1,'Added']]], - ['additional_20regression_20coverage_4',['Additional regression coverage',['../md_tests_2README.html#autotoc_md138',1,'']]], - ['all_20build_20variants_20in_20qt_20creator_5',['Configure all build variants in Qt Creator',['../md_README.html#autotoc_md133',1,'']]], - ['already_20implemented_6',['Stage 1 - already implemented',['../md_README.html#autotoc_md117',1,'']]], - ['and_20lcov_20on_20debian_20ubuntu_7',['GCC, gcov, and LCOV on Debian/Ubuntu',['../md_README.html#autotoc_md131',1,'']]], - ['and_20maintenance_8',['and maintenance',['../md_changelog.html#autotoc_md22',1,'Documentation and maintenance'],['../md_changelog.html#autotoc_md14',1,'Documentation and maintenance']]], - ['and_20motorola_20extraction_9',['Intel and Motorola Extraction',['../md_README.html#autotoc_md94',1,'']]], - ['and_20optional_20model_20tests_10',['Tree and optional model tests',['../md_tests_2README.html#autotoc_md137',1,'']]], - ['and_20parse_20errors_11',['and parse errors',['../md_README.html#autotoc_md78',1,'Frame validation and parse errors'],['../md_README.html#autotoc_md82',1,'Signal validation and parse errors']]], - ['and_20precision_12',['Raw value representation and precision',['../md_README.html#autotoc_md64',1,'']]], - ['and_20reload_13',['Definition ownership and reload',['../md_README.html#autotoc_md67',1,'']]], - ['and_20reload_20coverage_14',['Definition lifetime and reload coverage',['../md_tests_2README.html#autotoc_md139',1,'']]], - ['and_20run_20the_20demo_15',['Build and run the demo',['../md_README.html#autotoc_md126',1,'']]], + ['added_3',['added',['../md_changelog.html#autotoc_md3',1,'Added'],['../md_changelog.html#autotoc_md6',1,'Added'],['../md_changelog.html#autotoc_md9',1,'Added'],['../md_changelog.html#autotoc_md13',1,'Added'],['../md_changelog.html#autotoc_md24',1,'Added']]], + ['additional_20regression_20coverage_4',['Additional regression coverage',['../md_tests_2README.html#autotoc_md141',1,'']]], + ['all_20build_20variants_20in_20qt_20creator_5',['Configure all build variants in Qt Creator',['../md_README.html#autotoc_md136',1,'']]], + ['already_20implemented_6',['Stage 1 - already implemented',['../md_README.html#autotoc_md120',1,'']]], + ['and_20lcov_20on_20debian_20ubuntu_7',['GCC, gcov, and LCOV on Debian/Ubuntu',['../md_README.html#autotoc_md134',1,'']]], + ['and_20maintenance_8',['and maintenance',['../md_changelog.html#autotoc_md25',1,'Documentation and maintenance'],['../md_changelog.html#autotoc_md17',1,'Documentation and maintenance']]], + ['and_20motorola_20extraction_9',['Intel and Motorola Extraction',['../md_README.html#autotoc_md97',1,'']]], + ['and_20optional_20model_20tests_10',['Tree and optional model tests',['../md_tests_2README.html#autotoc_md140',1,'']]], + ['and_20parse_20errors_11',['and parse errors',['../md_README.html#autotoc_md81',1,'Frame validation and parse errors'],['../md_README.html#autotoc_md85',1,'Signal validation and parse errors']]], + ['and_20precision_12',['Raw value representation and precision',['../md_README.html#autotoc_md67',1,'']]], + ['and_20reload_13',['Definition ownership and reload',['../md_README.html#autotoc_md70',1,'']]], + ['and_20reload_20coverage_14',['Definition lifetime and reload coverage',['../md_tests_2README.html#autotoc_md142',1,'']]], + ['and_20run_20the_20demo_15',['Build and run the demo',['../md_README.html#autotoc_md129',1,'']]], ['and_20runtime_20decode_20module_20documentation_16',['DBC Parser and Runtime Decode Module Documentation',['../md_README.html',1,'']]], - ['and_20signal_20identifiers_17',['Stable frame and signal identifiers',['../md_README.html#autotoc_md68',1,'']]], - ['and_20ui_20path_18',['1. Parse and UI path',['../md_README.html#autotoc_md27',1,'']]], - ['api_20documentation_19',['Generate API documentation',['../md_README.html#autotoc_md127',1,'']]], - ['architecture_20',['High-Level Architecture',['../md_README.html#autotoc_md26',1,'']]] + ['and_20signal_20identifiers_17',['Stable frame and signal identifiers',['../md_README.html#autotoc_md71',1,'']]], + ['and_20ui_20path_18',['1. Parse and UI path',['../md_README.html#autotoc_md30',1,'']]], + ['api_20documentation_19',['Generate API documentation',['../md_README.html#autotoc_md130',1,'']]], + ['architecture_20',['High-Level Architecture',['../md_README.html#autotoc_md29',1,'']]] ]; diff --git a/docs/html/search/all_6.js b/docs/html/search/all_6.js index 994993f..bfddede 100644 --- a/docs/html/search/all_6.js +++ b/docs/html/search/all_6.js @@ -1,14 +1,14 @@ var searchData= [ - ['basic_20multiplexing_0',['Basic multiplexing',['../md_README.html#autotoc_md84',1,'']]], + ['basic_20multiplexing_0',['Basic multiplexing',['../md_README.html#autotoc_md87',1,'']]], ['build_1',['build',['../classDbcDecodeBuilder.html#a2bc6386dfb5e58976c42e22c19ec471b',1,'DbcDecodeBuilder::Build()'],['../classDbcTreeBuilder.html#a7e57d067d831b14b383947fd125edd4b',1,'DbcTreeBuilder::Build()']]], - ['build_20and_20run_20the_20demo_2',['Build and run the demo',['../md_README.html#autotoc_md126',1,'']]], - ['build_20core_20without_20qt_20libraries_3',['Build core without Qt libraries',['../md_README.html#autotoc_md123',1,'']]], - ['build_20in_20qt_20creator_4',['Configure the GCC coverage build in Qt Creator',['../md_README.html#autotoc_md132',1,'']]], - ['build_20integration_5',['Build Integration',['../md_README.html#autotoc_md122',1,'']]], - ['build_20runtime_20decode_20database_6',['Build runtime decode database',['../md_README.html#autotoc_md99',1,'']]], - ['build_20ui_20tree_7',['Build UI tree',['../md_README.html#autotoc_md98',1,'']]], - ['build_20variants_20in_20qt_20creator_8',['Configure all build variants in Qt Creator',['../md_README.html#autotoc_md133',1,'']]], + ['build_20and_20run_20the_20demo_2',['Build and run the demo',['../md_README.html#autotoc_md129',1,'']]], + ['build_20core_20without_20qt_20libraries_3',['Build core without Qt libraries',['../md_README.html#autotoc_md126',1,'']]], + ['build_20in_20qt_20creator_4',['Configure the GCC coverage build in Qt Creator',['../md_README.html#autotoc_md135',1,'']]], + ['build_20integration_5',['Build Integration',['../md_README.html#autotoc_md125',1,'']]], + ['build_20runtime_20decode_20database_6',['Build runtime decode database',['../md_README.html#autotoc_md102',1,'']]], + ['build_20ui_20tree_7',['Build UI tree',['../md_README.html#autotoc_md101',1,'']]], + ['build_20variants_20in_20qt_20creator_8',['Configure all build variants in Qt Creator',['../md_README.html#autotoc_md136',1,'']]], ['byteorder_9',['byteorder',['../structDecodeSignal.html#abdc4c4bb4a67a6d61d6ca81e8a52833d',1,'DecodeSignal::byteOrder'],['../decode__database_8h.html#aaeb92d42f5a6e27b8ba19f18d69d142b',1,'ByteOrder: decode_database.h']]], - ['byteorder_20tt_10',['<tt>ByteOrder</tt>',['../md_README.html#autotoc_md47',1,'']]] + ['byteorder_20tt_10',['<tt>ByteOrder</tt>',['../md_README.html#autotoc_md50',1,'']]] ]; diff --git a/docs/html/search/all_7.js b/docs/html/search/all_7.js index 7677122..1685e60 100644 --- a/docs/html/search/all_7.js +++ b/docs/html/search/all_7.js @@ -1,19 +1,19 @@ var searchData= [ - ['can_20id_20normalization_0',['CAN ID Normalization',['../md_README.html#autotoc_md88',1,'']]], + ['can_20id_20normalization_0',['CAN ID Normalization',['../md_README.html#autotoc_md91',1,'']]], ['canid_1',['canid',['../structDecodeFrame.html#a52e10215388db7df8b541b9d9909edcf',1,'DecodeFrame::canId'],['../structFrameInfo.html#af470b0742c05bfbffc31c51a68f5c05e',1,'FrameInfo::canId'],['../structRawCanFrame.html#a2d0e7fa0e3d5c20bbed55ed8f7888ff4',1,'RawCanFrame::canId']]], - ['changed_2',['changed',['../md_changelog.html#autotoc_md4',1,'Changed'],['../md_changelog.html#autotoc_md7',1,'Changed'],['../md_changelog.html#autotoc_md12',1,'Changed'],['../md_changelog.html#autotoc_md16',1,'Changed']]], + ['changed_2',['changed',['../md_changelog.html#autotoc_md19',1,'Changed'],['../md_changelog.html#autotoc_md15',1,'Changed'],['../md_changelog.html#autotoc_md10',1,'Changed'],['../md_changelog.html#autotoc_md7',1,'Changed'],['../md_changelog.html#autotoc_md4',1,'Changed']]], ['changelog_3',['Changelog',['../md_changelog.html',1,'']]], - ['code_20coverage_4',['Measuring code coverage',['../md_README.html#autotoc_md130',1,'']]], - ['comment_5',['comment',['../structDecodeSignal.html#ae28c905d4a2bc86419c2a8361bfea473',1,'DecodeSignal::comment'],['../structDecodeFrame.html#a25819ef907a32391af5c9f7cae9cdb22',1,'DecodeFrame::comment'],['../structFrameInfo.html#ace75121294f9d89b762080ab5643293c',1,'FrameInfo::comment'],['../structSignalInfo.html#ac99ba7563a5dad01e39c4257216953f5',1,'SignalInfo::comment']]], - ['comments_6',['Comments',['../md_README.html#autotoc_md86',1,'']]], - ['configure_20all_20build_20variants_20in_20qt_20creator_7',['Configure all build variants in Qt Creator',['../md_README.html#autotoc_md133',1,'']]], - ['configure_20the_20gcc_20coverage_20build_20in_20qt_20creator_8',['Configure the GCC coverage build in Qt Creator',['../md_README.html#autotoc_md132',1,'']]], + ['code_20coverage_4',['Measuring code coverage',['../md_README.html#autotoc_md133',1,'']]], + ['comment_5',['comment',['../structSignalInfo.html#ac99ba7563a5dad01e39c4257216953f5',1,'SignalInfo::comment'],['../structFrameInfo.html#ace75121294f9d89b762080ab5643293c',1,'FrameInfo::comment'],['../structDecodeFrame.html#a25819ef907a32391af5c9f7cae9cdb22',1,'DecodeFrame::comment'],['../structDecodeSignal.html#ae28c905d4a2bc86419c2a8361bfea473',1,'DecodeSignal::comment']]], + ['comments_6',['Comments',['../md_README.html#autotoc_md89',1,'']]], + ['configure_20all_20build_20variants_20in_20qt_20creator_7',['Configure all build variants in Qt Creator',['../md_README.html#autotoc_md136',1,'']]], + ['configure_20the_20gcc_20coverage_20build_20in_20qt_20creator_8',['Configure the GCC coverage build in Qt Creator',['../md_README.html#autotoc_md135',1,'']]], ['contains_9',['contains',['../classTypeFactory.html#a1a119ad2956abe146213c6ca124d8dd4',1,'TypeFactory']]], - ['core_20without_20qt_20libraries_10',['Build core without Qt libraries',['../md_README.html#autotoc_md123',1,'']]], - ['coverage_11',['coverage',['../md_tests_2README.html#autotoc_md138',1,'Additional regression coverage'],['../md_tests_2README.html#autotoc_md135',1,'Decoder coverage'],['../md_tests_2README.html#autotoc_md139',1,'Definition lifetime and reload coverage'],['../md_README.html#autotoc_md130',1,'Measuring code coverage'],['../md_tests_2README.html#autotoc_md136',1,'Parser coverage']]], - ['coverage_20build_20in_20qt_20creator_12',['Configure the GCC coverage build in Qt Creator',['../md_README.html#autotoc_md132',1,'']]], + ['core_20without_20qt_20libraries_10',['Build core without Qt libraries',['../md_README.html#autotoc_md126',1,'']]], + ['coverage_11',['coverage',['../md_tests_2README.html#autotoc_md141',1,'Additional regression coverage'],['../md_tests_2README.html#autotoc_md138',1,'Decoder coverage'],['../md_tests_2README.html#autotoc_md142',1,'Definition lifetime and reload coverage'],['../md_README.html#autotoc_md133',1,'Measuring code coverage'],['../md_tests_2README.html#autotoc_md139',1,'Parser coverage']]], + ['coverage_20build_20in_20qt_20creator_12',['Configure the GCC coverage build in Qt Creator',['../md_README.html#autotoc_md135',1,'']]], ['create_13',['create',['../classTypeFactory.html#a3f43b03d6e3a0fd503fdec01ecf62e9f',1,'TypeFactory']]], - ['creator_14',['creator',['../md_README.html#autotoc_md133',1,'Configure all build variants in Qt Creator'],['../md_README.html#autotoc_md132',1,'Configure the GCC coverage build in Qt Creator'],['../classTypeFactory.html#ad82f537569ece3d3a4a376523fa94473',1,'TypeFactory::creator()']]], - ['current_20limitations_15',['Current Limitations',['../md_README.html#autotoc_md114',1,'']]] + ['creator_14',['creator',['../md_README.html#autotoc_md136',1,'Configure all build variants in Qt Creator'],['../md_README.html#autotoc_md135',1,'Configure the GCC coverage build in Qt Creator'],['../classTypeFactory.html#ad82f537569ece3d3a4a376523fa94473',1,'TypeFactory::creator()']]], + ['current_20limitations_15',['Current Limitations',['../md_README.html#autotoc_md117',1,'']]] ]; diff --git a/docs/html/search/all_8.js b/docs/html/search/all_8.js index 9ee8360..785186e 100644 --- a/docs/html/search/all_8.js +++ b/docs/html/search/all_8.js @@ -1,11 +1,11 @@ var searchData= [ ['data_0',['data',['../structRawCanFrame.html#a0acb487612104c99c9907344c035df83',1,'RawCanFrame']]], - ['data_20structures_1',['Parsed Data Structures',['../md_README.html#autotoc_md34',1,'']]], - ['database_2',['Build runtime decode database',['../md_README.html#autotoc_md99',1,'']]], - ['dbc_3',['Parse DBC',['../md_README.html#autotoc_md97',1,'']]], + ['data_20structures_1',['Parsed Data Structures',['../md_README.html#autotoc_md37',1,'']]], + ['database_2',['Build runtime decode database',['../md_README.html#autotoc_md102',1,'']]], + ['dbc_3',['Parse DBC',['../md_README.html#autotoc_md100',1,'']]], ['dbc_20parser_20and_20runtime_20decode_20module_20documentation_4',['DBC Parser and Runtime Decode Module Documentation',['../md_README.html',1,'']]], - ['dbc_20syntax_5',['Supported DBC Syntax',['../md_README.html#autotoc_md75',1,'']]], + ['dbc_20syntax_5',['Supported DBC Syntax',['../md_README.html#autotoc_md78',1,'']]], ['dbc_5fdatabase_2eh_6',['dbc_database.h',['../dbc__database_8h.html',1,'']]], ['dbc_5fdecode_5fbuilder_2ecpp_7',['dbc_decode_builder.cpp',['../dbc__decode__builder_8cpp.html',1,'']]], ['dbc_5fdecode_5fbuilder_2eh_8',['dbc_decode_builder.h',['../dbc__decode__builder_8h.html',1,'']]], @@ -17,10 +17,10 @@ var searchData= ['dbc_5ftree_5fbuilder_2eh_14',['dbc_tree_builder.h',['../dbc__tree__builder_8h.html',1,'']]], ['dbccommenthandler_15',['DbcCommentHandler',['../classDbcCommentHandler.html',1,'']]], ['dbcdatabase_16',['DbcDatabase',['../structDbcDatabase.html',1,'']]], - ['dbcdatabase_20tt_17',['<tt>DbcDatabase</tt>',['../md_README.html#autotoc_md39',1,'']]], + ['dbcdatabase_20tt_17',['<tt>DbcDatabase</tt>',['../md_README.html#autotoc_md42',1,'']]], ['dbcdecodebuilder_18',['DbcDecodeBuilder',['../classDbcDecodeBuilder.html',1,'']]], ['dbcdecoder_19',['DbcDecoder',['../classDbcDecoder.html',1,'']]], - ['dbcdecoder_20tt_20',['<tt>DbcDecoder</tt>',['../md_README.html#autotoc_md70',1,'']]], + ['dbcdecoder_20tt_20',['<tt>DbcDecoder</tt>',['../md_README.html#autotoc_md73',1,'']]], ['dbcdiagnostic_21',['DbcDiagnostic',['../structDbcDiagnostic.html',1,'']]], ['dbcdiagnosticerror_22',['DbcDiagnosticError',['../classDbcDiagnosticError.html',1,'']]], ['dbcframehandler_23',['DbcFrameHandler',['../classDbcFrameHandler.html',1,'']]], @@ -35,37 +35,37 @@ var searchData= ['dbctreemodel_32',['DbcTreeModel',['../classDbcTreeModel.html',1,'']]], ['dbcunsupportedmultiplexinghandler_33',['DbcUnsupportedMultiplexingHandler',['../classDbcUnsupportedMultiplexingHandler.html',1,'']]], ['dbcvaluedescriptionhandler_34',['DbcValueDescriptionHandler',['../classDbcValueDescriptionHandler.html',1,'']]], - ['debian_20ubuntu_35',['GCC, gcov, and LCOV on Debian/Ubuntu',['../md_README.html#autotoc_md131',1,'']]], + ['debian_20ubuntu_35',['GCC, gcov, and LCOV on Debian/Ubuntu',['../md_README.html#autotoc_md134',1,'']]], ['decode_36',['Decode',['../classDbcDecoder.html#a345951ffe5d943a70d3a1ea1f39e74f7',1,'DbcDecoder']]], - ['decode_20a_20raw_20frame_37',['Decode a raw frame',['../md_README.html#autotoc_md100',1,'']]], - ['decode_20database_38',['Build runtime decode database',['../md_README.html#autotoc_md99',1,'']]], - ['decode_20flow_39',['Decode Flow',['../md_README.html#autotoc_md92',1,'']]], - ['decode_20layer_40',['Runtime Decode Layer',['../md_README.html#autotoc_md44',1,'']]], - ['decode_20layer_20exists_41',['Why the Runtime Decode Layer Exists',['../md_README.html#autotoc_md30',1,'']]], + ['decode_20a_20raw_20frame_37',['Decode a raw frame',['../md_README.html#autotoc_md103',1,'']]], + ['decode_20database_38',['Build runtime decode database',['../md_README.html#autotoc_md102',1,'']]], + ['decode_20flow_39',['Decode Flow',['../md_README.html#autotoc_md95',1,'']]], + ['decode_20layer_40',['Runtime Decode Layer',['../md_README.html#autotoc_md47',1,'']]], + ['decode_20layer_20exists_41',['Why the Runtime Decode Layer Exists',['../md_README.html#autotoc_md33',1,'']]], ['decode_20module_20documentation_42',['DBC Parser and Runtime Decode Module Documentation',['../md_README.html',1,'']]], - ['decode_20path_43',['2. Runtime decode path',['../md_README.html#autotoc_md28',1,'']]], - ['decode_20strategy_44',['Unified Decode Strategy',['../md_README.html#autotoc_md102',1,'']]], + ['decode_20path_43',['2. Runtime decode path',['../md_README.html#autotoc_md31',1,'']]], + ['decode_20strategy_44',['Unified Decode Strategy',['../md_README.html#autotoc_md105',1,'']]], ['decode_5fdatabase_2eh_45',['decode_database.h',['../decode__database_8h.html',1,'']]], ['decodedatabase_46',['decodedatabase',['../classDecodeDatabase.html',1,'DecodeDatabase'],['../classDecodeDatabase.html#aa935a3a0d334f932d6a8c89a3818286a',1,'DecodeDatabase::DecodeDatabase()']]], - ['decodedatabase_20tt_47',['<tt>DecodeDatabase</tt>',['../md_README.html#autotoc_md57',1,'']]], + ['decodedatabase_20tt_47',['<tt>DecodeDatabase</tt>',['../md_README.html#autotoc_md60',1,'']]], ['decodedframevalue_48',['DecodedFrameValue',['../structDecodedFrameValue.html',1,'']]], - ['decodedframevalue_20tt_49',['<tt>DecodedFrameValue</tt>',['../md_README.html#autotoc_md66',1,'']]], + ['decodedframevalue_20tt_49',['<tt>DecodedFrameValue</tt>',['../md_README.html#autotoc_md69',1,'']]], ['decodedsignalvalue_50',['DecodedSignalValue',['../structDecodedSignalValue.html',1,'']]], - ['decodedsignalvalue_20tt_51',['<tt>DecodedSignalValue</tt>',['../md_README.html#autotoc_md62',1,'']]], + ['decodedsignalvalue_20tt_51',['<tt>DecodedSignalValue</tt>',['../md_README.html#autotoc_md65',1,'']]], ['decodeframe_52',['DecodeFrame',['../structDecodeFrame.html',1,'']]], - ['decodeframe_20tt_53',['<tt>DecodeFrame</tt>',['../md_README.html#autotoc_md53',1,'']]], - ['decoder_20coverage_54',['Decoder coverage',['../md_tests_2README.html#autotoc_md135',1,'']]], - ['decoder_20layer_55',['Decoder Layer',['../md_README.html#autotoc_md59',1,'']]], + ['decodeframe_20tt_53',['<tt>DecodeFrame</tt>',['../md_README.html#autotoc_md56',1,'']]], + ['decoder_20coverage_54',['Decoder coverage',['../md_tests_2README.html#autotoc_md138',1,'']]], + ['decoder_20layer_55',['Decoder Layer',['../md_README.html#autotoc_md62',1,'']]], ['decodesignal_56',['DecodeSignal',['../structDecodeSignal.html',1,'']]], - ['decodesignal_20tt_57',['<tt>DecodeSignal</tt>',['../md_README.html#autotoc_md51',1,'']]], - ['definition_58',['definition',['../structDecodedFrameValue.html#a33e971ba10030506007aa201c89f351d',1,'DecodedFrameValue::definition'],['../structDecodedSignalValue.html#a828b5ef7faefc14fbf5dbd46249bcad2',1,'DecodedSignalValue::definition'],['../md_README.html#autotoc_md76',1,'Frame definition'],['../md_README.html#autotoc_md80',1,'Signal definition']]], - ['definition_20lifetime_20and_20reload_20coverage_59',['Definition lifetime and reload coverage',['../md_tests_2README.html#autotoc_md139',1,'']]], - ['definition_20ownership_20and_20reload_60',['Definition ownership and reload',['../md_README.html#autotoc_md67',1,'']]], - ['demo_61',['Build and run the demo',['../md_README.html#autotoc_md126',1,'']]], - ['descriptions_20tt_20val_5f_20tt_62',['Signal value descriptions (<tt>VAL_</tt>)',['../md_README.html#autotoc_md112',1,'']]], - ['diagnostics_63',['Structured diagnostics',['../md_README.html#autotoc_md73',1,'']]], + ['decodesignal_20tt_57',['<tt>DecodeSignal</tt>',['../md_README.html#autotoc_md54',1,'']]], + ['definition_58',['definition',['../structDecodedFrameValue.html#a33e971ba10030506007aa201c89f351d',1,'DecodedFrameValue::definition'],['../structDecodedSignalValue.html#a828b5ef7faefc14fbf5dbd46249bcad2',1,'DecodedSignalValue::definition'],['../md_README.html#autotoc_md79',1,'Frame definition'],['../md_README.html#autotoc_md83',1,'Signal definition']]], + ['definition_20lifetime_20and_20reload_20coverage_59',['Definition lifetime and reload coverage',['../md_tests_2README.html#autotoc_md142',1,'']]], + ['definition_20ownership_20and_20reload_60',['Definition ownership and reload',['../md_README.html#autotoc_md70',1,'']]], + ['demo_61',['Build and run the demo',['../md_README.html#autotoc_md129',1,'']]], + ['descriptions_20tt_20val_5f_20tt_62',['Signal value descriptions (<tt>VAL_</tt>)',['../md_README.html#autotoc_md115',1,'']]], + ['diagnostics_63',['Structured diagnostics',['../md_README.html#autotoc_md76',1,'']]], ['dispatch_64',['Dispatch',['../classDbcStatementDispatcher.html#a495c2d5c7ebb8103b1987cdd87c142ed',1,'DbcStatementDispatcher']]], ['dlc_65',['dlc',['../structFrameInfo.html#aa2b198ea0f55e560c123d35e7b3ecab9',1,'FrameInfo::dlc'],['../structDecodeFrame.html#a51111ac13988be372d5673d12cf3e060',1,'DecodeFrame::dlc']]], - ['documentation_66',['documentation',['../md_README.html',1,'DBC Parser and Runtime Decode Module Documentation'],['../md_changelog.html#autotoc_md19',1,'Documentation'],['../md_README.html#autotoc_md127',1,'Generate API documentation']]], - ['documentation_20and_20maintenance_67',['documentation and maintenance',['../md_changelog.html#autotoc_md22',1,'Documentation and maintenance'],['../md_changelog.html#autotoc_md14',1,'Documentation and maintenance']]] + ['documentation_66',['documentation',['../md_README.html',1,'DBC Parser and Runtime Decode Module Documentation'],['../md_changelog.html#autotoc_md22',1,'Documentation'],['../md_README.html#autotoc_md130',1,'Generate API documentation']]], + ['documentation_20and_20maintenance_67',['documentation and maintenance',['../md_changelog.html#autotoc_md25',1,'Documentation and maintenance'],['../md_changelog.html#autotoc_md17',1,'Documentation and maintenance']]] ]; diff --git a/docs/html/search/all_9.js b/docs/html/search/all_9.js index 3d22a59..81bcfa1 100644 --- a/docs/html/search/all_9.js +++ b/docs/html/search/all_9.js @@ -1,9 +1,9 @@ var searchData= [ - ['enough_0',['Why the Tree Is Not Enough',['../md_README.html#autotoc_md108',1,'']]], - ['errors_1',['errors',['../md_README.html#autotoc_md78',1,'Frame validation and parse errors'],['../md_README.html#autotoc_md82',1,'Signal validation and parse errors']]], - ['example_20usage_2',['Example Usage',['../md_README.html#autotoc_md96',1,'']]], + ['enough_0',['Why the Tree Is Not Enough',['../md_README.html#autotoc_md111',1,'']]], + ['errors_1',['errors',['../md_README.html#autotoc_md81',1,'Frame validation and parse errors'],['../md_README.html#autotoc_md85',1,'Signal validation and parse errors']]], + ['example_20usage_2',['Example Usage',['../md_README.html#autotoc_md99',1,'']]], ['examples_2fmain_2ecpp_3',['main.cpp',['../examples_2main_8cpp.html',1,'']]], - ['exists_4',['Why the Runtime Decode Layer Exists',['../md_README.html#autotoc_md30',1,'']]], - ['extraction_5',['extraction',['../md_README.html#autotoc_md94',1,'Intel and Motorola Extraction'],['../md_README.html#autotoc_md90',1,'PGN Extraction']]] + ['exists_4',['Why the Runtime Decode Layer Exists',['../md_README.html#autotoc_md33',1,'']]], + ['extraction_5',['extraction',['../md_README.html#autotoc_md97',1,'Intel and Motorola Extraction'],['../md_README.html#autotoc_md93',1,'PGN Extraction']]] ]; diff --git a/docs/html/search/all_a.js b/docs/html/search/all_a.js index b66e5f7..d54b9a2 100644 --- a/docs/html/search/all_a.js +++ b/docs/html/search/all_a.js @@ -1,20 +1,21 @@ var searchData= [ ['factor_0',['factor',['../structDecodeSignal.html#a428c39913a366a8476dd291061354391',1,'DecodeSignal::factor'],['../structSignalInfo.html#ae4f78d99a2750aa564d7eba860171499',1,'SignalInfo::factor']]], - ['factory_1',['Statement Handler Factory',['../md_README.html#autotoc_md110',1,'']]], + ['factory_1',['Statement Handler Factory',['../md_README.html#autotoc_md113',1,'']]], ['findframe_2',['findframe',['../classDbcDecoder.html#a4005c4311f57b7faf21c0cb31463039c',1,'DbcDecoder::FindFrame()'],['../classDecodeDatabase.html#a780d6e747a48b10a5eb0ab85448d2970',1,'DecodeDatabase::FindFrame()']]], - ['fixed_3',['fixed',['../md_changelog.html#autotoc_md13',1,'Fixed'],['../md_changelog.html#autotoc_md17',1,'Fixed'],['../md_changelog.html#autotoc_md8',1,'Fixed']]], - ['flow_4',['Decode Flow',['../md_README.html#autotoc_md92',1,'']]], - ['frame_5',['Decode a raw frame',['../md_README.html#autotoc_md100',1,'']]], - ['frame_20and_20signal_20identifiers_6',['Stable frame and signal identifiers',['../md_README.html#autotoc_md68',1,'']]], - ['frame_20definition_7',['Frame definition',['../md_README.html#autotoc_md76',1,'']]], - ['frame_20validation_20and_20parse_20errors_8',['Frame validation and parse errors',['../md_README.html#autotoc_md78',1,'']]], + ['fixed_3',['fixed',['../md_changelog.html#autotoc_md16',1,'Fixed'],['../md_changelog.html#autotoc_md20',1,'Fixed'],['../md_changelog.html#autotoc_md11',1,'Fixed']]], + ['flow_4',['Decode Flow',['../md_README.html#autotoc_md95',1,'']]], + ['frame_5',['Decode a raw frame',['../md_README.html#autotoc_md103',1,'']]], + ['frame_20and_20signal_20identifiers_6',['Stable frame and signal identifiers',['../md_README.html#autotoc_md71',1,'']]], + ['frame_20definition_7',['Frame definition',['../md_README.html#autotoc_md79',1,'']]], + ['frame_20validation_20and_20parse_20errors_8',['Frame validation and parse errors',['../md_README.html#autotoc_md81',1,'']]], ['frame_5finfo_2eh_9',['frame_info.h',['../frame__info_8h.html',1,'']]], - ['frameinfo_10',['FrameInfo',['../structFrameInfo.html',1,'']]], - ['frameinfo_20tt_11',['<tt>FrameInfo</tt>',['../md_README.html#autotoc_md37',1,'']]], - ['framekey_12',['FrameKey',['../structFrameKey.html',1,'']]], - ['framekey_20tt_13',['<tt>FrameKey</tt>',['../md_README.html#autotoc_md55',1,'']]], - ['framekeyhasher_14',['FrameKeyHasher',['../structFrameKeyHasher.html',1,'']]], - ['frames_15',['frames',['../structDbcDatabase.html#a2cade5d0234ee816132127542f8c612b',1,'DbcDatabase']]], - ['frametap_16',['Intended Use in FrameTap',['../md_README.html#autotoc_md106',1,'']]] + ['framedecodestatus_10',['FrameDecodeStatus',['../dbc__decoder_8h.html#a855b2bc36cb18bfbae5ff30a2394e961',1,'dbc_decoder.h']]], + ['frameinfo_11',['FrameInfo',['../structFrameInfo.html',1,'']]], + ['frameinfo_20tt_12',['<tt>FrameInfo</tt>',['../md_README.html#autotoc_md40',1,'']]], + ['framekey_13',['FrameKey',['../structFrameKey.html',1,'']]], + ['framekey_20tt_14',['<tt>FrameKey</tt>',['../md_README.html#autotoc_md58',1,'']]], + ['framekeyhasher_15',['FrameKeyHasher',['../structFrameKeyHasher.html',1,'']]], + ['frames_16',['frames',['../structDbcDatabase.html#a2cade5d0234ee816132127542f8c612b',1,'DbcDatabase']]], + ['frametap_17',['Intended Use in FrameTap',['../md_README.html#autotoc_md109',1,'']]] ]; diff --git a/docs/html/search/all_b.js b/docs/html/search/all_b.js index 6d7364b..ee72eff 100644 --- a/docs/html/search/all_b.js +++ b/docs/html/search/all_b.js @@ -1,9 +1,9 @@ var searchData= [ - ['gcc_20coverage_20build_20in_20qt_20creator_0',['Configure the GCC coverage build in Qt Creator',['../md_README.html#autotoc_md132',1,'']]], - ['gcc_20gcov_20and_20lcov_20on_20debian_20ubuntu_1',['GCC, gcov, and LCOV on Debian/Ubuntu',['../md_README.html#autotoc_md131',1,'']]], - ['gcov_20and_20lcov_20on_20debian_20ubuntu_2',['GCC, gcov, and LCOV on Debian/Ubuntu',['../md_README.html#autotoc_md131',1,'']]], - ['generate_20api_20documentation_3',['Generate API documentation',['../md_README.html#autotoc_md127',1,'']]], + ['gcc_20coverage_20build_20in_20qt_20creator_0',['Configure the GCC coverage build in Qt Creator',['../md_README.html#autotoc_md135',1,'']]], + ['gcc_20gcov_20and_20lcov_20on_20debian_20ubuntu_1',['GCC, gcov, and LCOV on Debian/Ubuntu',['../md_README.html#autotoc_md134',1,'']]], + ['gcov_20and_20lcov_20on_20debian_20ubuntu_2',['GCC, gcov, and LCOV on Debian/Ubuntu',['../md_README.html#autotoc_md134',1,'']]], + ['generate_20api_20documentation_3',['Generate API documentation',['../md_README.html#autotoc_md130',1,'']]], ['getchild_4',['getchild',['../classTreeNode.html#a4727ff801a2d1b9323c5ae50a35457a6',1,'TreeNode::GetChild(std::size_t index) const'],['../classTreeNode.html#a81047bd7d2a4cc96b9ef5e20d0d63b5f',1,'TreeNode::GetChild(std::size_t index)']]], ['getchildcount_5',['GetChildCount',['../classTreeNode.html#abdd82460187dca8d3e5d66f5d5c529ea',1,'TreeNode']]], ['getframe_6',['GetFrame',['../classTreeNode.html#afc503b40004764163605d6dbdb4ef811',1,'TreeNode']]], diff --git a/docs/html/search/all_c.js b/docs/html/search/all_c.js index b87fdf7..acd3563 100644 --- a/docs/html/search/all_c.js +++ b/docs/html/search/all_c.js @@ -1,6 +1,6 @@ var searchData= [ - ['handler_20factory_0',['Statement Handler Factory',['../md_README.html#autotoc_md110',1,'']]], + ['handler_20factory_0',['Statement Handler Factory',['../md_README.html#autotoc_md113',1,'']]], ['haspgn_1',['haspgn',['../structDecodeFrame.html#a2551102670c4a540fced4c71da0b369d',1,'DecodeFrame::hasPgn'],['../structFrameInfo.html#a4737a5420213ad4304724e39f3a4e55d',1,'FrameInfo::hasPgn']]], - ['high_20level_20architecture_2',['High-Level Architecture',['../md_README.html#autotoc_md26',1,'']]] + ['high_20level_20architecture_2',['High-Level Architecture',['../md_README.html#autotoc_md29',1,'']]] ]; diff --git a/docs/html/search/all_d.js b/docs/html/search/all_d.js index ab6e442..ea248c5 100644 --- a/docs/html/search/all_d.js +++ b/docs/html/search/all_d.js @@ -1,16 +1,16 @@ var searchData= [ - ['id_20normalization_0',['CAN ID Normalization',['../md_README.html#autotoc_md88',1,'']]], - ['identifiers_1',['Stable frame and signal identifiers',['../md_README.html#autotoc_md68',1,'']]], - ['implementation_20—_202026_2003_2013_2',['Initial implementation — 2026-03-13',['../md_changelog.html#autotoc_md20',1,'']]], - ['implemented_3',['Stage 1 - already implemented',['../md_README.html#autotoc_md117',1,'']]], - ['in_20frametap_4',['Intended Use in FrameTap',['../md_README.html#autotoc_md106',1,'']]], - ['in_20qt_20creator_5',['in qt creator',['../md_README.html#autotoc_md133',1,'Configure all build variants in Qt Creator'],['../md_README.html#autotoc_md132',1,'Configure the GCC coverage build in Qt Creator']]], - ['initial_20implementation_20—_202026_2003_2013_6',['Initial implementation — 2026-03-13',['../md_changelog.html#autotoc_md20',1,'']]], - ['integration_7',['Build Integration',['../md_README.html#autotoc_md122',1,'']]], - ['intel_20and_20motorola_20extraction_8',['Intel and Motorola Extraction',['../md_README.html#autotoc_md94',1,'']]], - ['intended_20use_20in_20frametap_9',['Intended Use in FrameTap',['../md_README.html#autotoc_md106',1,'']]], - ['is_20not_20enough_10',['Why the Tree Is Not Enough',['../md_README.html#autotoc_md108',1,'']]], + ['id_20normalization_0',['CAN ID Normalization',['../md_README.html#autotoc_md91',1,'']]], + ['identifiers_1',['Stable frame and signal identifiers',['../md_README.html#autotoc_md71',1,'']]], + ['implementation_20—_202026_2003_2013_2',['Initial implementation — 2026-03-13',['../md_changelog.html#autotoc_md23',1,'']]], + ['implemented_3',['Stage 1 - already implemented',['../md_README.html#autotoc_md120',1,'']]], + ['in_20frametap_4',['Intended Use in FrameTap',['../md_README.html#autotoc_md109',1,'']]], + ['in_20qt_20creator_5',['in qt creator',['../md_README.html#autotoc_md136',1,'Configure all build variants in Qt Creator'],['../md_README.html#autotoc_md135',1,'Configure the GCC coverage build in Qt Creator']]], + ['initial_20implementation_20—_202026_2003_2013_6',['Initial implementation — 2026-03-13',['../md_changelog.html#autotoc_md23',1,'']]], + ['integration_7',['Build Integration',['../md_README.html#autotoc_md125',1,'']]], + ['intel_20and_20motorola_20extraction_8',['Intel and Motorola Extraction',['../md_README.html#autotoc_md97',1,'']]], + ['intended_20use_20in_20frametap_9',['Intended Use in FrameTap',['../md_README.html#autotoc_md109',1,'']]], + ['is_20not_20enough_10',['Why the Tree Is Not Enough',['../md_README.html#autotoc_md111',1,'']]], ['isextended_11',['isextended',['../structFrameInfo.html#a34b726afaad2ac5c697fdb2ac070cdaf',1,'FrameInfo::isExtended'],['../structDecodeFrame.html#a3ee2348d781f8f15aaf87e8dcf57ec41',1,'DecodeFrame::isExtended'],['../structRawCanFrame.html#a462193d275adc1dba8bd18ffa0e36bb7',1,'RawCanFrame::isExtended']]], ['islittleendian_12',['isLittleEndian',['../structSignalInfo.html#af363b1563fb4c7cc8973f9a80c06846c',1,'SignalInfo']]], ['issigned_13',['isSigned',['../structSignalInfo.html#a52a52b320eef92fbcb4626307f9a498f',1,'SignalInfo']]] diff --git a/docs/html/search/all_f.js b/docs/html/search/all_f.js index 4030811..b182bdf 100644 --- a/docs/html/search/all_f.js +++ b/docs/html/search/all_f.js @@ -1,13 +1,13 @@ var searchData= [ - ['layer_0',['layer',['../md_README.html#autotoc_md59',1,'Decoder Layer'],['../md_README.html#autotoc_md44',1,'Runtime Decode Layer'],['../md_README.html#autotoc_md41',1,'UI Tree Layer']]], - ['layer_20exists_1',['Why the Runtime Decode Layer Exists',['../md_README.html#autotoc_md30',1,'']]], - ['layout_2',['Module Layout',['../md_README.html#autotoc_md32',1,'']]], - ['lcov_20on_20debian_20ubuntu_3',['GCC, gcov, and LCOV on Debian/Ubuntu',['../md_README.html#autotoc_md131',1,'']]], + ['layer_0',['layer',['../md_README.html#autotoc_md62',1,'Decoder Layer'],['../md_README.html#autotoc_md47',1,'Runtime Decode Layer'],['../md_README.html#autotoc_md44',1,'UI Tree Layer']]], + ['layer_20exists_1',['Why the Runtime Decode Layer Exists',['../md_README.html#autotoc_md33',1,'']]], + ['layout_2',['Module Layout',['../md_README.html#autotoc_md35',1,'']]], + ['lcov_20on_20debian_20ubuntu_3',['GCC, gcov, and LCOV on Debian/Ubuntu',['../md_README.html#autotoc_md134',1,'']]], ['length_4',['length',['../structDecodeSignal.html#acd4ef5ee30421d117b5756854302debb',1,'DecodeSignal::length'],['../structSignalInfo.html#a1008b5201f71848177217bd52fb84baa',1,'SignalInfo::length']]], - ['level_20architecture_5',['High-Level Architecture',['../md_README.html#autotoc_md26',1,'']]], - ['libraries_6',['Build core without Qt libraries',['../md_README.html#autotoc_md123',1,'']]], - ['lifetime_20and_20reload_20coverage_7',['Definition lifetime and reload coverage',['../md_tests_2README.html#autotoc_md139',1,'']]], - ['limitations_8',['Current Limitations',['../md_README.html#autotoc_md114',1,'']]], - ['live_20path_9',['live path',['../md_README.html#autotoc_md103',1,'']]] + ['level_20architecture_5',['High-Level Architecture',['../md_README.html#autotoc_md29',1,'']]], + ['libraries_6',['Build core without Qt libraries',['../md_README.html#autotoc_md126',1,'']]], + ['lifetime_20and_20reload_20coverage_7',['Definition lifetime and reload coverage',['../md_tests_2README.html#autotoc_md142',1,'']]], + ['limitations_8',['Current Limitations',['../md_README.html#autotoc_md117',1,'']]], + ['live_20path_9',['live path',['../md_README.html#autotoc_md106',1,'']]] ]; diff --git a/docs/html/search/enums_1.js b/docs/html/search/enums_1.js index 07a6d2a..ed50e83 100644 --- a/docs/html/search/enums_1.js +++ b/docs/html/search/enums_1.js @@ -1,4 +1,4 @@ var searchData= [ - ['nodetype_0',['NodeType',['../tree__node_8h.html#acac9cbaeea226ed297804c012dc12b16',1,'tree_node.h']]] + ['framedecodestatus_0',['FrameDecodeStatus',['../dbc__decoder_8h.html#a855b2bc36cb18bfbae5ff30a2394e961',1,'dbc_decoder.h']]] ]; diff --git a/docs/html/search/enums_2.js b/docs/html/search/enums_2.js index 9189d7f..07a6d2a 100644 --- a/docs/html/search/enums_2.js +++ b/docs/html/search/enums_2.js @@ -1,4 +1,4 @@ var searchData= [ - ['valuetype_0',['ValueType',['../decode__database_8h.html#ad9971b6ef33e02ba2c75d19c1d2518a1',1,'decode_database.h']]] + ['nodetype_0',['NodeType',['../tree__node_8h.html#acac9cbaeea226ed297804c012dc12b16',1,'tree_node.h']]] ]; diff --git a/docs/html/search/enums_3.js b/docs/html/search/enums_3.js new file mode 100644 index 0000000..9189d7f --- /dev/null +++ b/docs/html/search/enums_3.js @@ -0,0 +1,4 @@ +var searchData= +[ + ['valuetype_0',['ValueType',['../decode__database_8h.html#ad9971b6ef33e02ba2c75d19c1d2518a1',1,'decode_database.h']]] +]; diff --git a/docs/html/search/searchdata.js b/docs/html/search/searchdata.js index 6008f18..72d1a64 100644 --- a/docs/html/search/searchdata.js +++ b/docs/html/search/searchdata.js @@ -5,7 +5,7 @@ var indexSectionsWithContent = 2: "defst", 3: "abcdfgprt", 4: "bcdfhiklmnoprstuv", - 5: "bnv", + 5: "bfnv", 6: "acdmpqrt" }; diff --git a/docs/html/search/variables_d.js b/docs/html/search/variables_d.js index eb3e73b..2e397eb 100644 --- a/docs/html/search/variables_d.js +++ b/docs/html/search/variables_d.js @@ -1,4 +1,5 @@ var searchData= [ - ['startbit_0',['startbit',['../structDecodeSignal.html#aaf5d7527ddf65b6355c2b4f3626df22d',1,'DecodeSignal::startBit'],['../structSignalInfo.html#ace290724f9801521609aedaa946d61f1',1,'SignalInfo::startBit']]] + ['startbit_0',['startbit',['../structDecodeSignal.html#aaf5d7527ddf65b6355c2b4f3626df22d',1,'DecodeSignal::startBit'],['../structSignalInfo.html#ace290724f9801521609aedaa946d61f1',1,'SignalInfo::startBit']]], + ['status_1',['status',['../structDecodedFrameValue.html#a645f433798d48a2787e7f00dfbb7dff9',1,'DecodedFrameValue']]] ]; diff --git a/docs/html/structDecodedFrameValue-members.html b/docs/html/structDecodedFrameValue-members.html index 3d24f0e..d1ca8f0 100644 --- a/docs/html/structDecodedFrameValue-members.html +++ b/docs/html/structDecodedFrameValue-members.html @@ -100,7 +100,8 @@ $(document).ready(function(){initNavTree('structDecodedFrameValue.html',''); ini definitionDecodedFrameValue keyDecodedFrameValue publicDecodedFrameValue - validDecodedFrameValue + statusDecodedFrameValue + validDecodedFrameValue
    diff --git a/docs/html/structDecodedFrameValue.html b/docs/html/structDecodedFrameValue.html index f0b9160..8b72d47 100644 --- a/docs/html/structDecodedFrameValue.html +++ b/docs/html/structDecodedFrameValue.html @@ -112,11 +112,13 @@ Public Attributes   bool valid = false   +FrameDecodeStatus status = FrameDecodeStatus::UnknownFrame + 

    Detailed Description

    Fully decoded frame.

    -

    Definition at line 62 of file dbc_decoder.h.

    +

    Definition at line 71 of file dbc_decoder.h.

    Member Data Documentation

    ◆ definition

    @@ -131,9 +133,9 @@ Public Attributes

    Owning immutable definition.

    -

    Definition at line 63 of file dbc_decoder.h.

    +

    Definition at line 72 of file dbc_decoder.h.

    -

    Referenced by DbcDecoder::Decode().

    +

    Referenced by DbcDecoder::Decode().

    @@ -150,9 +152,9 @@ Public Attributes

    Stable application identifier.

    -

    Definition at line 64 of file dbc_decoder.h.

    +

    Definition at line 73 of file dbc_decoder.h.

    -

    Referenced by DbcDecoder::Decode().

    +

    Referenced by DbcDecoder::Decode().

    @@ -169,7 +171,7 @@ Public Attributes

    Decoded signal values.

    -

    Definition at line 65 of file dbc_decoder.h.

    +

    Definition at line 74 of file dbc_decoder.h.

    @@ -186,9 +188,28 @@ Public Attributes

    true if frame was matched.

    -

    Definition at line 66 of file dbc_decoder.h.

    +

    Definition at line 75 of file dbc_decoder.h.

    -

    Referenced by DbcDecoder::Decode().

    +

    Referenced by DbcDecoder::Decode().

    + +
    + + +

    ◆ status

    + +
    +
    + + + + +
    FrameDecodeStatus DecodedFrameValue::status = FrameDecodeStatus::UnknownFrame
    +
    +

    Inspect signals for individual failures.

    + +

    Definition at line 76 of file dbc_decoder.h.

    + +

    Referenced by DbcDecoder::Decode().

    diff --git a/docs/html/structDecodedFrameValue.js b/docs/html/structDecodedFrameValue.js index 53f4684..8cb683f 100644 --- a/docs/html/structDecodedFrameValue.js +++ b/docs/html/structDecodedFrameValue.js @@ -3,5 +3,6 @@ var structDecodedFrameValue = [ "definition", "structDecodedFrameValue.html#a33e971ba10030506007aa201c89f351d", null ], [ "key", "structDecodedFrameValue.html#a32143efe2023a1ecd977a9ae93e50b73", null ], [ "public", "structDecodedFrameValue.html#adae123ac7fbb4a5c314b64b01109f4a8", null ], - [ "valid", "structDecodedFrameValue.html#a4f51c31110dba1f73a763c596372beb0", null ] + [ "valid", "structDecodedFrameValue.html#a4f51c31110dba1f73a763c596372beb0", null ], + [ "status", "structDecodedFrameValue.html#a645f433798d48a2787e7f00dfbb7dff9", null ] ]; \ No newline at end of file diff --git a/docs/html/structDecodedSignalValue.html b/docs/html/structDecodedSignalValue.html index 6f58434..3f05095 100644 --- a/docs/html/structDecodedSignalValue.html +++ b/docs/html/structDecodedSignalValue.html @@ -126,7 +126,7 @@ Public Attributes

    Detailed Description

    One decoded signal value.

    -

    Definition at line 43 of file dbc_decoder.h.

    +

    Definition at line 52 of file dbc_decoder.h.

    Member Function Documentation

    ◆ GetValueDescription()

    @@ -152,9 +152,9 @@ Public Attributes

    Lookup using the raw integer; nullptr for invalid or unmapped values.

    -

    Definition at line 52 of file dbc_decoder.h.

    +

    Definition at line 61 of file dbc_decoder.h.

    -

    References definition, rawValue, and valid.

    +

    References definition, rawValue, and valid.

    @@ -172,9 +172,9 @@ Public Attributes

    Owning immutable definition.

    -

    Definition at line 44 of file dbc_decoder.h.

    +

    Definition at line 53 of file dbc_decoder.h.

    -

    Referenced by DbcDecoder::Decode(), and GetValueDescription().

    +

    Referenced by DbcDecoder::Decode(), and GetValueDescription().

    @@ -191,9 +191,9 @@ Public Attributes

    Stable application identifier.

    -

    Definition at line 45 of file dbc_decoder.h.

    +

    Definition at line 54 of file dbc_decoder.h.

    -

    Referenced by DbcDecoder::Decode().

    +

    Referenced by DbcDecoder::Decode().

    @@ -208,11 +208,11 @@ Public Attributes
    -

    Exact signed or unsigned raw value.

    +

    Exact raw value for Valid or PhysicalConversionFailed.

    -

    Definition at line 46 of file dbc_decoder.h.

    +

    Definition at line 55 of file dbc_decoder.h.

    -

    Referenced by GetValueDescription().

    +

    Referenced by GetValueDescription().

    @@ -227,9 +227,9 @@ Public Attributes
    -

    Converted physical value.

    +

    Conversion result, possibly NaN/Inf on failure.

    -

    Definition at line 47 of file dbc_decoder.h.

    +

    Definition at line 56 of file dbc_decoder.h.

    @@ -246,9 +246,9 @@ Public Attributes

    true if decoding succeeded.

    -

    Definition at line 48 of file dbc_decoder.h.

    +

    Definition at line 57 of file dbc_decoder.h.

    -

    Referenced by GetValueDescription().

    +

    Referenced by DbcDecoder::Decode(), and GetValueDescription().

    @@ -264,7 +264,7 @@ Public Attributes
    -

    Definition at line 49 of file dbc_decoder.h.

    +

    Definition at line 58 of file dbc_decoder.h.

    diff --git a/docs/html/structRawCanFrame.html b/docs/html/structRawCanFrame.html index fe8ced3..30941b2 100644 --- a/docs/html/structRawCanFrame.html +++ b/docs/html/structRawCanFrame.html @@ -131,7 +131,7 @@ Public Attributes

    Definition at line 25 of file dbc_decoder.h.

    -

    Referenced by DbcDecoder::Decode().

    +

    Referenced by DbcDecoder::Decode().

    @@ -150,7 +150,7 @@ Public Attributes

    Definition at line 26 of file dbc_decoder.h.

    -

    Referenced by DbcDecoder::Decode().

    +

    Referenced by DbcDecoder::Decode().

    @@ -169,6 +169,8 @@ Public Attributes

    Definition at line 27 of file dbc_decoder.h.

    +

    Referenced by DbcDecoder::Decode().

    +
    The documentation for this struct was generated from the following file:
      diff --git a/tests/README.md b/tests/README.md index 77e4d6d..f856791 100644 --- a/tests/README.md +++ b/tests/README.md @@ -50,7 +50,12 @@ Coverage includes Motorola bit numbering within and across bytes, unaligned signals, signed values, scaling, 64-bit extraction, signed/unsigned boundary values (including `INT64_MIN`, `INT64_MAX`, and `UINT64_MAX`), numeric output, invalid lengths, insufficient payloads, and Intel regression cases. Frame validity means the definition was -found; insufficient payloads are checked through signal validity. +found; frame status reports unknown frames, short payloads, and signal errors. +Tests exercise empty/short/exact/long payloads, ignored trailing bytes, signals +outside declared DLC, invalid enum metadata and overflowing start bits, and +preservation of successful signals. Physical conversion checks cover NaN/Inf +parameters, multiplication/addition overflow, finite limits, signed/unsigned raw +retention, and raw selector activation after physical conversion failure. ## Parser coverage diff --git a/tests/dbc_coverage_test.cpp b/tests/dbc_coverage_test.cpp index 30fbf35..01dddac 100644 --- a/tests/dbc_coverage_test.cpp +++ b/tests/dbc_coverage_test.cpp @@ -23,6 +23,7 @@ namespace { DecodeDatabase SingleSignal(const DecodeSignal &signal) { DecodeFrame frame; frame.canId = 17U; + frame.dlc = 16U; frame.signals.push_back(signal); return DecodeDatabase({frame}); } @@ -212,7 +213,7 @@ void CoverageTest::invalidSignalStatus_data() { QTest::newRow((prefix + "-65").constData()) << order << start << 65U << 9U << int(SignalDecodeStatus::InvalidDefinition); QTest::newRow((prefix + "-empty").constData()) << order << start << 8U << 0U << int(SignalDecodeStatus::InsufficientPayload); QTest::newRow((prefix + "-short").constData()) << order << start << 16U << 1U << int(SignalDecodeStatus::InsufficientPayload); - QTest::newRow((prefix + "-large-start").constData()) << order << std::numeric_limits::max() << 8U << 1U << int(SignalDecodeStatus::InsufficientPayload); + QTest::newRow((prefix + "-large-start").constData()) << order << std::numeric_limits::max() << 8U << 1U << int(SignalDecodeStatus::InvalidDefinition); } } diff --git a/tests/dbc_decoder_test.cpp b/tests/dbc_decoder_test.cpp index dae43d4..c47018d 100644 --- a/tests/dbc_decoder_test.cpp +++ b/tests/dbc_decoder_test.cpp @@ -1,6 +1,7 @@ #include "dbc_decoder.h" #include +#include #include #include @@ -26,6 +27,14 @@ private Q_SLOTS: void decode_data(); void decode(); void boundaries(); + void payloadPolicy_data(); + void payloadPolicy(); + void invalidDefinitions_data(); + void invalidDefinitions(); + void physicalConversion_data(); + void physicalConversion(); + void selectorConversionFailure(); + void zeroDlc(); }; void DbcDecoderTest::decode_data() { @@ -66,7 +75,7 @@ void DbcDecoderTest::decode() { DecodeFrame definition; definition.canId = 0x123U; - definition.dlc = static_cast(test.data.size()); + definition.dlc = 8U; definition.signals.push_back(signal); const DecodeDatabase database({definition}); @@ -103,6 +112,7 @@ void DbcDecoderTest::boundaries() { std::uint64_t(0x8000000000000001ULL), std::uint64_t(0xFFFFFFFFFFFFFFFFULL)}) { DecodeFrame definition; + definition.dlc = 8U; DecodeSignal signal; signal.startBit = order == ByteOrder::Intel ? 0U : 7U; signal.length = 64U; @@ -150,6 +160,229 @@ void DbcDecoderTest::boundaries() { } } +void DbcDecoderTest::payloadPolicy_data() { + QTest::addColumn("bytes"); + QTest::addColumn("status"); + QTest::newRow("empty") << 0U << FrameDecodeStatus::InsufficientPayload; + QTest::newRow("partial") << 1U << FrameDecodeStatus::InsufficientPayload; + QTest::newRow("short-with-all-signals") << 2U << FrameDecodeStatus::InsufficientPayload; + QTest::newRow("exact") << 3U << FrameDecodeStatus::Valid; + QTest::newRow("long") << 4U << FrameDecodeStatus::Valid; +} + +void DbcDecoderTest::payloadPolicy() { + QFETCH(unsigned, bytes); + QFETCH(FrameDecodeStatus, status); + for (ByteOrder order: {ByteOrder::Intel, ByteOrder::Motorola}) { + DecodeFrame definition; + definition.canId = 17U; + definition.dlc = 3U; + DecodeSignal signal; + signal.name = "First"; + signal.length = 8U; + signal.byteOrder = order; + signal.startBit = order == ByteOrder::Intel ? 0U : 7U; + definition.signals.push_back(signal); + signal.name = "Second"; + signal.startBit += 8U; + definition.signals.push_back(signal); + const DecodeDatabase database({definition}); + RawCanFrame raw; + raw.canId = 17U; + raw.data.assign(bytes, 42U); + const DecodedFrameValue result = DbcDecoder().Decode(database, raw); + QVERIFY(result.valid); + QCOMPARE(result.status, status); + QCOMPARE(result.signals.size(), std::size_t(2U)); + for (std::size_t index = 0U; index < result.signals.size(); ++index) { + const DecodedSignalValue &value = result.signals[index]; + QCOMPARE(value.status, bytes > index ? SignalDecodeStatus::Valid : SignalDecodeStatus::InsufficientPayload); + QCOMPARE(value.valid, bytes > index); + if (value.valid) { + QCOMPARE(std::get(value.rawValue), std::uint64_t(42U)); + QCOMPARE(value.physicalValue, 42.0); + } + } + raw.isExtended = true; + const DecodedFrameValue unknown = DbcDecoder().Decode(database, raw); + QVERIFY(!unknown.valid && !unknown.definition && unknown.signals.empty()); + QCOMPARE(unknown.status, FrameDecodeStatus::UnknownFrame); + QVERIFY(unknown.key == FrameKey(17U, true)); + QCOMPARE(DbcDecoder().Decode(DecodeDatabase(), raw).status, FrameDecodeStatus::UnknownFrame); + + definition.signals.clear(); + raw.isExtended = false; + const DecodedFrameValue empty = DbcDecoder().Decode(DecodeDatabase({definition}), raw); + QVERIFY(empty.valid && empty.signals.empty()); + QCOMPARE(empty.status, status); + } +} + +void DbcDecoderTest::invalidDefinitions_data() { + QTest::addColumn("kind"); + QTest::newRow("zero-length") << 0; + QTest::newRow("65-bits") << 1; + QTest::newRow("outside-dlc") << 2; + QTest::newRow("start-overflow") << 3; + QTest::newRow("invalid-byte-order") << 4; + QTest::newRow("invalid-value-type") << 5; + QTest::newRow("invalid-multiplex-role") << 6; + QTest::newRow("missing-selector") << 7; +} + +void DbcDecoderTest::invalidDefinitions() { + QFETCH(int, kind); + for (ByteOrder order: {ByteOrder::Intel, ByteOrder::Motorola}) { + DecodeFrame definition; + definition.dlc = 1U; + DecodeSignal good; + good.name = "Good"; + good.length = 4U; + good.byteOrder = order; + good.startBit = order == ByteOrder::Intel ? 0U : 7U; + DecodeSignal bad = good; + bad.name = "Bad"; + switch (kind) { + case 0: bad.length = 0U; break; + case 1: bad.length = 65U; break; + case 2: bad.startBit += 8U; break; + case 3: bad.startBit = std::numeric_limits::max(); break; + case 4: bad.byteOrder = static_cast(99); break; + case 5: bad.valueType = static_cast(99); break; + case 6: bad.multiplexing.role = static_cast(99); break; + case 7: bad.multiplexing.role = MultiplexRole::Branch; break; + } + definition.signals = {bad, good}; + const DecodeDatabase database({definition}); + RawCanFrame raw; + raw.data = {0xFFU, 0xFFU}; // Extra bytes must not rescue an invalid definition. + const DecodedFrameValue result = DbcDecoder().Decode(database, raw); + QVERIFY(result.valid); + QCOMPARE(result.status, FrameDecodeStatus::SignalErrors); + QCOMPARE(result.signals[0].status, SignalDecodeStatus::InvalidDefinition); + QVERIFY(!result.signals[0].valid && !result.signals[0].GetValueDescription()); + QCOMPARE(result.signals[1].status, SignalDecodeStatus::Valid); + QCOMPARE(std::get(result.signals[1].rawValue), std::uint64_t(15U)); + QCOMPARE(result.signals[1].physicalValue, 15.0); + raw.data.clear(); + const DecodedFrameValue shortResult = DbcDecoder().Decode(database, raw); + QCOMPARE(shortResult.status, FrameDecodeStatus::InsufficientPayload); + QCOMPARE(shortResult.signals[0].status, SignalDecodeStatus::InvalidDefinition); + QCOMPARE(shortResult.signals[1].status, SignalDecodeStatus::InsufficientPayload); + } +} + +void DbcDecoderTest::physicalConversion_data() { + QTest::addColumn("factor"); + QTest::addColumn("offset"); + QTest::addColumn("finite"); + const double infinity = std::numeric_limits::infinity(); + const double nan = std::numeric_limits::quiet_NaN(); + const double maximum = std::numeric_limits::max(); + QTest::newRow("nan-factor") << nan << 0.0 << false; + QTest::newRow("nan-offset") << 1.0 << nan << false; + QTest::newRow("infinite-factor") << infinity << 0.0 << false; + QTest::newRow("negative-infinite-factor") << -infinity << 0.0 << false; + QTest::newRow("infinite-offset") << 1.0 << infinity << false; + QTest::newRow("negative-infinite-offset") << 1.0 << -infinity << false; + QTest::newRow("multiply-overflow") << maximum << 0.0 << false; + QTest::newRow("add-overflow") << maximum / 2.0 << maximum << false; + QTest::newRow("finite-limit") << maximum / 2.0 << 0.0 << true; + QTest::newRow("zero-factor") << 0.0 << 42.0 << true; +} + +void DbcDecoderTest::physicalConversion() { + QFETCH(double, factor); + QFETCH(double, offset); + QFETCH(bool, finite); + for (ValueType type: {ValueType::Signed, ValueType::Unsigned}) { + DecodeFrame definition; + definition.dlc = 1U; + DecodeSignal signal; + signal.name = "Scaled"; + signal.length = 4U; + signal.valueType = type; + signal.factor = factor; + signal.offset = offset; + signal.valueDescriptions.emplace(std::uint64_t(2U), "Two"); + definition.signals.push_back(signal); + signal.name = "Good"; + signal.startBit = 4U; + signal.factor = 1.0; + signal.offset = 0.0; + definition.signals.push_back(signal); + RawCanFrame raw; + raw.data = {0x32U}; + const DecodedFrameValue result = DbcDecoder().Decode(DecodeDatabase({definition}), raw); + QVERIFY(result.valid); + QCOMPARE(result.status, finite ? FrameDecodeStatus::Valid : FrameDecodeStatus::SignalErrors); + const DecodedSignalValue &scaled = result.signals[0]; + QCOMPARE(scaled.valid, finite); + QCOMPARE(scaled.status, finite ? SignalDecodeStatus::Valid : SignalDecodeStatus::PhysicalConversionFailed); + QCOMPARE(std::isfinite(scaled.physicalValue), finite); + if (type == ValueType::Signed) + QCOMPARE(std::get(scaled.rawValue), std::int64_t(2)); + else + QCOMPARE(std::get(scaled.rawValue), std::uint64_t(2)); + QVERIFY(result.signals[1].valid); + QCOMPARE(result.signals[1].physicalValue, 3.0); + if (finite) { + QVERIFY(scaled.GetValueDescription()); + QCOMPARE(*scaled.GetValueDescription(), std::string("Two")); + } else { + QVERIFY(!scaled.GetValueDescription()); + } + } +} + +void DbcDecoderTest::selectorConversionFailure() { + DecodeFrame definition; + definition.dlc = 1U; + DecodeSignal selector; + selector.name = "Selector"; + selector.length = 1U; + selector.multiplexing.role = MultiplexRole::Selector; + selector.factor = std::numeric_limits::infinity(); + DecodeSignal branch; + branch.name = "Branch"; + branch.startBit = 1U; + branch.length = 7U; + branch.multiplexing.role = MultiplexRole::Branch; + branch.multiplexing.value = 1U; + definition.signals = {branch, selector}; + RawCanFrame raw; + raw.data = {85U}; + const DecodedFrameValue result = DbcDecoder().Decode(DecodeDatabase({definition}), raw); + QCOMPARE(result.status, FrameDecodeStatus::SignalErrors); + QVERIFY(result.signals[0].valid); + QCOMPARE(result.signals[0].physicalValue, 42.0); + QCOMPARE(result.signals[1].status, SignalDecodeStatus::PhysicalConversionFailed); + QCOMPARE(std::get(result.signals[1].rawValue), std::uint64_t(1U)); + raw.data = {84U}; + selector.factor = 1.0; + definition.signals = {branch, selector}; + const DecodedFrameValue inactive = DbcDecoder().Decode(DecodeDatabase({definition}), raw); + QCOMPARE(inactive.status, FrameDecodeStatus::Valid); + QCOMPARE(inactive.signals[0].status, SignalDecodeStatus::Inactive); +} + +void DbcDecoderTest::zeroDlc() { + DecodeFrame definition; + RawCanFrame raw; + for (const std::vector &data: {std::vector(), std::vector{42U}}) { + raw.data = data; + const DecodedFrameValue result = DbcDecoder().Decode(DecodeDatabase({definition}), raw); + QVERIFY(result.valid && result.signals.empty()); + QCOMPARE(result.status, FrameDecodeStatus::Valid); + } + DecodeSignal signal; + signal.length = 8U; + definition.signals.push_back(signal); + const DecodedFrameValue invalid = DbcDecoder().Decode(DecodeDatabase({definition}), raw); + QCOMPARE(invalid.status, FrameDecodeStatus::SignalErrors); + QCOMPARE(invalid.signals[0].status, SignalDecodeStatus::InvalidDefinition); +} + int RunDecoderTests(int argc, char **argv) { DbcDecoderTest tests; return QTest::qExec(&tests, argc, argv);