diff --git a/README.md b/README.md index 242bbd3..92c9caa 100644 --- a/README.md +++ b/README.md @@ -639,6 +639,7 @@ if (!result.HasErrors()) { | `Error` / `InvalidValueDescription` | Invalid `VAL_` syntax, duplicate value, or unresolved reference | | `Error` / `InvalidMultiplexing` | Selector, branch range, or signal overlap validation failed | | `Warning` / `UnsupportedStatement` | Unregistered statement skipped; decoding or metadata may be incomplete | +| `Error` / `UnsupportedSignalType` | `SIG_VALTYPE_` declaration rejected to prevent incorrect integer decoding | | `Error` / `InvalidMultiplexing` | Invalid `SG_MUL_VAL_` syntax, references, ranges, or cycles | Parsing stops at the first error and returns an empty database, together with @@ -646,11 +647,20 @@ any preceding warnings and the error. Deferred comment, `VAL_`, and multiplexing retain their original source lines. Invalid signal markers use `InvalidStatement`. `HasErrors()` checks severity; warnings alone do not make a parse fail. -Unknown statements are reported conservatively, including `SIG_VALTYPE_`, -attribute relation forms (`BA_REL_`, `BA_DEF_REL_`), signal types/references, -environment variables, named `VAL_TABLE_` tables, and vendor extensions. For -example, ignoring `SIG_VALTYPE_` may decode a floating-point signal as an integer. -The parser does not implement the warned features; consumers should review warnings. +All `SIG_VALTYPE_` declarations are rejected with `Error / UnsupportedSignalType`. +This includes float32 (type 1), float64 (type 2), type 0, unknown types, and +malformed declarations: the operator itself is not implemented. `ParseFile()` +throws a source-located error; `ParseFileWithDiagnostics()` returns an empty +database with the error and any preceding warnings. There is no permissive mode. +This prevents IEEE 754 payload bits from being interpreted as a valid integer +signal. Integer signals defined with `SG_` without this operator are unchanged. +A bare `SIG_VALTYPE_` symbol inside an `NS_` symbol list remains quiet header +metadata, rather than a value-type declaration. + +Other unknown statements are reported conservatively, including attribute +relation forms (`BA_REL_`, `BA_DEF_REL_`), signal types/references, environment +variables, named `VAL_TABLE_` tables, and vendor extensions. The parser does not +implement the warned features; consumers should review warnings. ### Attributes and frame metadata @@ -1205,7 +1215,9 @@ Registration creates one handler; replacing a registration replaces that instanc 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. +`ParseFileWithDiagnostics`. A registered rejection handler for `SIG_VALTYPE_` +produces `UnsupportedSignalType` errors instead of skipping the declaration. +`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 @@ -1280,7 +1292,7 @@ Not supported yet: - multiple parent selectors for one signal - named `VAL_TABLE_` tables and environment-variable value descriptions - attribute relation forms (`BA_REL_`, `BA_DEF_REL_`) -- `SIG_VALTYPE_` floating-point signal types +- explicit `SIG_VALTYPE_` signal value types (declarations are rejected with an error) - signal types/references (`SGTYPE_`, `SIG_TYPE_REF_`) and environment variables - advanced comment handling - full DBC grammar coverage @@ -1523,6 +1535,10 @@ 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. +The demo prints all parser diagnostics, including severity and source location, +and exits with code 2 before building or decoding when parsing reports an error. +Warnings remain visible when parsing succeeds. + ### Generate API documentation With Doxygen installed, run from the repository root: diff --git a/changelog.md b/changelog.md index 540e5c3..0d0a92b 100644 --- a/changelog.md +++ b/changelog.md @@ -34,6 +34,19 @@ existing Git tag; subsequent changes are listed as unreleased. #### Fixed +- Reject all `SIG_VALTYPE_` declarations with a source-located + `UnsupportedSignalType` error instead of silently decoding their payload as + integers. Both parser APIs reject the unsupported operator, including type 0; + the diagnostic API publishes no partial database. Namespace symbol listings + remain quiet. No permissive mode is introduced. +- Display parser warnings and errors in the demo, and stop before building or + decoding when parsing fails. Add parser and pipeline regressions for float32, + float64, source locations, declaration order, and unchanged integer decoding. + Verify Release and ASan/UBSan builds with the optional Qt adapter: 2,420 core + checks, seven model checks, and the qmake tree test pass in each build. One + named-locale case skips; leak checking remains disabled due to ptrace. + Build the demo and verify error exits, visible warnings, and integer input. + - Replace recursive regular-expression matching for `SG_` statements with an iterative field scanner. Long unit strings no longer overflow the stack. - Parse signal physical values directly with locale-independent `from_chars`, diff --git a/core/dbc_diagnostic.h b/core/dbc_diagnostic.h index e186425..a791d21 100644 --- a/core/dbc_diagnostic.h +++ b/core/dbc_diagnostic.h @@ -22,7 +22,8 @@ enum class DbcDiagnosticCode { InvalidMultiplexing, UnsupportedStatement, InvalidAttribute, - UnsupportedAttribute + UnsupportedAttribute, + UnsupportedSignalType }; /** @brief Diagnostic message and physical source location. */ diff --git a/core/dbc_parser.cpp b/core/dbc_parser.cpp index d9b3d08..cefb840 100644 --- a/core/dbc_parser.cpp +++ b/core/dbc_parser.cpp @@ -83,6 +83,8 @@ namespace { return DbcDiagnosticCode::InvalidMultiplexing; if (keyword == "VAL_") return DbcDiagnosticCode::InvalidValueDescription; + if (keyword == "SIG_VALTYPE_") + return DbcDiagnosticCode::UnsupportedSignalType; return DbcDiagnosticCode::InvalidStatement; } } diff --git a/core/dbc_parser.h b/core/dbc_parser.h index 88c3771..b33667c 100644 --- a/core/dbc_parser.h +++ b/core/dbc_parser.h @@ -37,7 +37,9 @@ struct DbcParserOptions { * Attribute statements support multiline fields and quoted strings, ending at * a semicolon outside quoted text. Diagnostics use the first physical line. * - * Reports unsupported statements as warnings. Invalid multiplexing is + * Rejects all SIG_VALTYPE_ declarations, including type 0, with + * UnsupportedSignalType errors; explicit signal value types are not implemented. + * Other unsupported statements produce warnings. Invalid multiplexing is * an error. VERSION, NS_, BS_, BU_ and NS_ symbol declarations stay quiet. */ class DbcParser { @@ -49,7 +51,7 @@ public: * @brief Parse DBC file. * @param filePath Path to input file. * @return Parsed database. - * @throws std::runtime_error on file or parse errors. + * @throws std::runtime_error on file or parse errors, including SIG_VALTYPE_. * Line parse errors include the file path and one-based line number. * Duplicate normalized CAN IDs of the same frame type and duplicate signal * names within a frame are rejected at the repeated definition's line. diff --git a/core/dbc_statement_handler.cpp b/core/dbc_statement_handler.cpp index 0ceb355..2e87a93 100644 --- a/core/dbc_statement_handler.cpp +++ b/core/dbc_statement_handler.cpp @@ -411,6 +411,7 @@ DbcStatementDispatcher::DbcStatementDispatcher() { RegisterType("SG_MUL_VAL_"); RegisterType("BO_"); RegisterType("SG_"); + RegisterType("SIG_VALTYPE_"); RegisterType("CM_"); RegisterType("VAL_"); RegisterType("BA_DEF_"); @@ -418,6 +419,10 @@ DbcStatementDispatcher::DbcStatementDispatcher() { RegisterType("BA_"); } +void DbcUnsupportedSignalTypeHandler::Handle(const std::string &, DbcParseContext &) const { + throw std::runtime_error("SIG_VALTYPE_ declarations are unsupported; refusing potentially incorrect integer decoding."); +} + void DbcHeaderHandler::Handle(const std::string &, DbcParseContext &) const {} bool DbcStatementDispatcher::Dispatch(const std::string &line, diff --git a/core/dbc_statement_handler.h b/core/dbc_statement_handler.h index 2917307..b0c8550 100644 --- a/core/dbc_statement_handler.h +++ b/core/dbc_statement_handler.h @@ -133,6 +133,12 @@ public: void Handle(const std::string &line, DbcParseContext &context) const override; }; +/** Reject explicit signal value types instead of treating their bits as integers. */ +class DbcUnsupportedSignalTypeHandler : public DbcStatementHandler { +public: + void Handle(const std::string &line, DbcParseContext &context) const override; +}; + /** Common header metadata does not change the supported decode definitions. */ class DbcHeaderHandler : public DbcStatementHandler { public: diff --git a/docs/dbcframework.tag b/docs/dbcframework.tag index 26b0486..653fd93 100644 --- a/docs/dbcframework.tag +++ b/docs/dbcframework.tag @@ -749,6 +749,18 @@ (const QModelIndex &index) const override + + DbcUnsupportedSignalTypeHandler + classDbcUnsupportedSignalTypeHandler.html + DbcStatementHandler + + void + Handle + classDbcUnsupportedSignalTypeHandler.html + ae4132fcdbb134d51143133c1fb83a9df + (const std::string &line, DbcParseContext &context) const override + + DbcValueDescriptionHandler classDbcValueDescriptionHandler.html diff --git a/docs/html/annotated.html b/docs/html/annotated.html index 124636a..55d3c7d 100644 --- a/docs/html/annotated.html +++ b/docs/html/annotated.html @@ -119,28 +119,29 @@ $(document).ready(function(){initNavTree('annotated.html',''); initResizable();  CDbcStatementHandlerInterface for processing a registered DBC statement  CDbcTreeBuilderBuilds a simple tree from parsed DBC database  CDbcTreeModel - CDbcValueDescriptionHandlerDefer raw value descriptions for target resolution - CDecodeDatabaseImmutable runtime snapshot. Copies share storage; reload replaces a snapshot. Owning lookup handles and decoded results keep old definitions alive - CDecodedFrameValueFully decoded frame - CDecodedSignalValueOne decoded signal value - CDecodeFrameRuntime-ready frame definition - CDecodeSignalRuntime-ready signal definition - CFrameInfoDescribes one CAN frame from a DBC file - CFrameKeyStable frame identifier: normalized CAN ID and frame type - CFrameKeyHasherHasher for frame key - CMultiplexingBasic markers and optional explicit extended multiplexing condition - CMultiplexRangeInclusive raw selector range, without expanding individual values - CPendingAttributeAttribute statement retained for ordered resolution - CPendingCommentDeferred frame or signal comment and its source location - CPendingMultiplexingDeferred extended multiplexing condition and source location - CPendingValueDescriptionsDeferred raw value descriptions and their target - CPgnLookupResult - CRawCanFrameRaw CAN frame used for runtime or trace decoding - CSignalInfoDescribes one signal inside a DBC frame - CSignalKey - CSignalKeyHasherHasher for stable signal keys - CTreeNodeTree node for later use in model/view or other hierarchy consumers - CTypeFactoryRegistry-based factory for creating objects by runtime identifier + CDbcUnsupportedSignalTypeHandler + CDbcValueDescriptionHandlerDefer raw value descriptions for target resolution + CDecodeDatabaseImmutable runtime snapshot. Copies share storage; reload replaces a snapshot. Owning lookup handles and decoded results keep old definitions alive + CDecodedFrameValueFully decoded frame + CDecodedSignalValueOne decoded signal value + CDecodeFrameRuntime-ready frame definition + CDecodeSignalRuntime-ready signal definition + CFrameInfoDescribes one CAN frame from a DBC file + CFrameKeyStable frame identifier: normalized CAN ID and frame type + CFrameKeyHasherHasher for frame key + CMultiplexingBasic markers and optional explicit extended multiplexing condition + CMultiplexRangeInclusive raw selector range, without expanding individual values + CPendingAttributeAttribute statement retained for ordered resolution + CPendingCommentDeferred frame or signal comment and its source location + CPendingMultiplexingDeferred extended multiplexing condition and source location + CPendingValueDescriptionsDeferred raw value descriptions and their target + CPgnLookupResult + CRawCanFrameRaw CAN frame used for runtime or trace decoding + CSignalInfoDescribes one signal inside a DBC frame + CSignalKey + CSignalKeyHasherHasher for stable signal keys + CTreeNodeTree node for later use in model/view or other hierarchy consumers + CTypeFactoryRegistry-based factory for creating objects by runtime identifier diff --git a/docs/html/annotated_dup.js b/docs/html/annotated_dup.js index c48d144..c42c017 100644 --- a/docs/html/annotated_dup.js +++ b/docs/html/annotated_dup.js @@ -25,6 +25,7 @@ var annotated_dup = [ "DbcStatementHandler", "classDbcStatementHandler.html", "classDbcStatementHandler" ], [ "DbcTreeBuilder", "classDbcTreeBuilder.html", "classDbcTreeBuilder" ], [ "DbcTreeModel", "classDbcTreeModel.html", "classDbcTreeModel" ], + [ "DbcUnsupportedSignalTypeHandler", "classDbcUnsupportedSignalTypeHandler.html", "classDbcUnsupportedSignalTypeHandler" ], [ "DbcValueDescriptionHandler", "classDbcValueDescriptionHandler.html", "classDbcValueDescriptionHandler" ], [ "DecodeDatabase", "classDecodeDatabase.html", "classDecodeDatabase" ], [ "DecodedFrameValue", "structDecodedFrameValue.html", "structDecodedFrameValue" ], diff --git a/docs/html/classDbcDiagnosticError.html b/docs/html/classDbcDiagnosticError.html index 419eb40..03a4d8e 100644 --- a/docs/html/classDbcDiagnosticError.html +++ b/docs/html/classDbcDiagnosticError.html @@ -191,7 +191,7 @@ Public Attributes

Definition at line 40 of file dbc_statement_handler.h.

-

Referenced by DbcParser::ParseFileWithDiagnostics().

+

Referenced by DbcParser::ParseFileWithDiagnostics().

diff --git a/docs/html/classDbcHeaderHandler.html b/docs/html/classDbcHeaderHandler.html index c4c48f7..ba988cb 100644 --- a/docs/html/classDbcHeaderHandler.html +++ b/docs/html/classDbcHeaderHandler.html @@ -115,7 +115,7 @@ Public Member Functions

Detailed Description

Common header metadata does not change the supported decode definitions.

-

Definition at line 137 of file dbc_statement_handler.h.

+

Definition at line 143 of file dbc_statement_handler.h.

Member Function Documentation

◆ Handle()

@@ -155,7 +155,7 @@ Public Member Functions

Implements DbcStatementHandler.

-

Definition at line 421 of file dbc_statement_handler.cpp.

+

Definition at line 426 of file dbc_statement_handler.cpp.

diff --git a/docs/html/classDbcMultiplexingHandler.html b/docs/html/classDbcMultiplexingHandler.html index 1d9fb2e..91c3d6d 100644 --- a/docs/html/classDbcMultiplexingHandler.html +++ b/docs/html/classDbcMultiplexingHandler.html @@ -158,7 +158,7 @@ Public Member Functions

Implements DbcStatementHandler.

-

Definition at line 484 of file dbc_statement_handler.cpp.

+

Definition at line 489 of file dbc_statement_handler.cpp.

References DbcParseContext::lineNumber.

diff --git a/docs/html/classDbcParser.html b/docs/html/classDbcParser.html index 2fe4a02..844833b 100644 --- a/docs/html/classDbcParser.html +++ b/docs/html/classDbcParser.html @@ -126,9 +126,9 @@ Public Member Functions
  • BA_DEF_, BA_DEF_DEF_, BA_ typed attributes, frame format and cycle time
  • Frame/signal comments support multiline quoted text and deferred references. Attribute statements support multiline fields and quoted strings, ending at a semicolon outside quoted text. Diagnostics use the first physical line.

    -

    Reports unsupported statements as warnings. Invalid multiplexing is an error. VERSION, NS_, BS_, BU_ and NS_ symbol declarations stay quiet.

    +

    Rejects all SIG_VALTYPE_ declarations, including type 0, with UnsupportedSignalType errors; explicit signal value types are not implemented. Other unsupported statements produce warnings. Invalid multiplexing is an error. VERSION, NS_, BS_, BU_ and NS_ symbol declarations stay quiet.

    -

    Definition at line 43 of file dbc_parser.h.

    +

    Definition at line 45 of file dbc_parser.h.

    Constructor & Destructor Documentation

    ◆ DbcParser()

    @@ -156,7 +156,7 @@ Public Member Functions

    Create a parser with explicit extended-frame interpretation options.

    -

    Definition at line 46 of file dbc_parser.h.

    +

    Definition at line 48 of file dbc_parser.h.

    @@ -187,16 +187,14 @@ Public Member Functions
    Returns
    Parsed database.
    Exceptions
    - +
    std::runtime_erroron file or parse errors. Line parse errors include the file path and one-based line number. Duplicate normalized CAN IDs of the same frame type and duplicate signal names within a frame are rejected at the repeated definition's line.
    std::runtime_erroron file or parse errors, including SIG_VALTYPE_. Line parse errors include the file path and one-based line number. Duplicate normalized CAN IDs of the same frame type and duplicate signal names within a frame are rejected at the repeated definition's line.
    -

    Definition at line 90 of file dbc_parser.cpp.

    +

    Definition at line 92 of file dbc_parser.cpp.

    -

    References DbcParseResult::database, DbcParseResult::diagnostics, and ParseFileWithDiagnostics().

    - -

    Referenced by main().

    +

    References DbcParseResult::database, DbcParseResult::diagnostics, and ParseFileWithDiagnostics().

    @@ -219,11 +217,11 @@ Public Member Functions

    Parse with warnings and a structured first error instead of throwing.

    Returns
    Valid database with diagnostics; empty database on error. File errors use line zero; statement errors use physical one-based lines. Warnings indicate ignored syntax and possible incomplete decoding.
    -

    Definition at line 103 of file dbc_parser.cpp.

    +

    Definition at line 105 of file dbc_parser.cpp.

    -

    References DbcParseResult::database, DbcDiagnosticError::diagnostic, DbcParseResult::diagnostics, DbcStatementDispatcher::Dispatch(), and DbcParserOptions::enableJ1939.

    +

    References DbcParseResult::database, DbcDiagnosticError::diagnostic, DbcParseResult::diagnostics, DbcStatementDispatcher::Dispatch(), and DbcParserOptions::enableJ1939.

    -

    Referenced by ParseFile().

    +

    Referenced by main(), and ParseFile().

    diff --git a/docs/html/classDbcStatementDispatcher.html b/docs/html/classDbcStatementDispatcher.html index 0dfcab4..7191061 100644 --- a/docs/html/classDbcStatementDispatcher.html +++ b/docs/html/classDbcStatementDispatcher.html @@ -114,7 +114,7 @@ Public Member Functions

    Detailed Description

    Exact-key dispatch to handlers created once per registration via TypeFactory.

    -

    Definition at line 143 of file dbc_statement_handler.h.

    +

    Definition at line 149 of file dbc_statement_handler.h.

    Constructor & Destructor Documentation

    ◆ DbcStatementDispatcher()

    @@ -166,7 +166,7 @@ template<class Handler >

    Register or replace a handler and create its reusable instance.

    -

    Definition at line 150 of file dbc_statement_handler.h.

    +

    Definition at line 156 of file dbc_statement_handler.h.

    References TypeFactory< ClassId, BaseClass, Args >::create(), and TypeFactory< ClassId, BaseClass, Args >::registerType().

    @@ -199,9 +199,9 @@ template<class Handler >

    Returns false for an unregistered keyword; input must be trimmed.

    -

    Definition at line 423 of file dbc_statement_handler.cpp.

    +

    Definition at line 428 of file dbc_statement_handler.cpp.

    -

    Referenced by DbcParser::ParseFileWithDiagnostics().

    +

    Referenced by DbcParser::ParseFileWithDiagnostics().

    diff --git a/docs/html/classDbcStatementHandler.html b/docs/html/classDbcStatementHandler.html index 0d2d56f..2d313d1 100644 --- a/docs/html/classDbcStatementHandler.html +++ b/docs/html/classDbcStatementHandler.html @@ -156,7 +156,7 @@ Public Member Functions

    Process a trimmed statement using state belonging to this parse.

    -

    Implemented in DbcFrameHandler, DbcSignalHandler, DbcCommentHandler, DbcAttributeHandler, DbcValueDescriptionHandler, DbcMultiplexingHandler, and DbcHeaderHandler.

    +

    Implemented in DbcFrameHandler, DbcSignalHandler, DbcCommentHandler, DbcAttributeHandler, DbcValueDescriptionHandler, DbcMultiplexingHandler, DbcUnsupportedSignalTypeHandler, and DbcHeaderHandler.

    diff --git a/docs/html/classDbcStatementHandler__inherit__graph.map b/docs/html/classDbcStatementHandler__inherit__graph.map index 3afe99b..79936eb 100644 --- a/docs/html/classDbcStatementHandler__inherit__graph.map +++ b/docs/html/classDbcStatementHandler__inherit__graph.map @@ -1,19 +1,21 @@ - + - + - + - + - + - + - + - - - + + + + + diff --git a/docs/html/classDbcStatementHandler__inherit__graph.md5 b/docs/html/classDbcStatementHandler__inherit__graph.md5 index af5f909..20437cc 100644 --- a/docs/html/classDbcStatementHandler__inherit__graph.md5 +++ b/docs/html/classDbcStatementHandler__inherit__graph.md5 @@ -1 +1 @@ -2aacbc7d65e32463c49617e36866cbe8 \ No newline at end of file +dd7c5f048bae3904d94456d930b5f6a8 \ No newline at end of file diff --git a/docs/html/classDbcStatementHandler__inherit__graph.svg b/docs/html/classDbcStatementHandler__inherit__graph.svg index d0246d6..4df3354 100644 --- a/docs/html/classDbcStatementHandler__inherit__graph.svg +++ b/docs/html/classDbcStatementHandler__inherit__graph.svg @@ -46,7 +46,7 @@ @@ -58,16 +58,16 @@ var sectionId = 'dynsection-0'; Node1 - -DbcStatementHandler -  -+ -~DbcStatementHandler() -+ -Handle() - - - + +DbcStatementHandler +  ++ +~DbcStatementHandler() ++ +Handle() + + + @@ -96,8 +96,8 @@ var sectionId = 'dynsection-0'; Node1->Node2 - - + + @@ -120,8 +120,8 @@ var sectionId = 'dynsection-0'; Node1->Node3 - - + + @@ -144,8 +144,8 @@ var sectionId = 'dynsection-0'; Node1->Node4 - - + + @@ -168,8 +168,8 @@ var sectionId = 'dynsection-0'; Node1->Node5 - - + + @@ -192,8 +192,8 @@ var sectionId = 'dynsection-0'; Node1->Node6 - - + + @@ -216,8 +216,8 @@ var sectionId = 'dynsection-0'; Node1->Node7 - - + + @@ -240,23 +240,24 @@ var sectionId = 'dynsection-0'; Node1->Node8 - - + + Node9 - - -DbcValueDescriptionHandler -  -+ -Handle() - - - + + +DbcUnsupportedSignalType +Handler +  ++ +Handle() + + + @@ -264,8 +265,32 @@ var sectionId = 'dynsection-0'; Node1->Node9 - - + + + + + + + +Node10 + + +DbcValueDescriptionHandler +  ++ +Handle() + + + + + + + + +Node1->Node10 + + + diff --git a/docs/html/classDbcStatementHandler__inherit__graph_org.svg b/docs/html/classDbcStatementHandler__inherit__graph_org.svg index 084588c..a47f049 100644 --- a/docs/html/classDbcStatementHandler__inherit__graph_org.svg +++ b/docs/html/classDbcStatementHandler__inherit__graph_org.svg @@ -4,24 +4,24 @@ - + DbcStatementHandler Node1 - -DbcStatementHandler -  -+ -~DbcStatementHandler() -+ -Handle() - - - + +DbcStatementHandler +  ++ +~DbcStatementHandler() ++ +Handle() + + + @@ -50,8 +50,8 @@ Node1->Node2 - - + + @@ -74,8 +74,8 @@ Node1->Node3 - - + + @@ -98,8 +98,8 @@ Node1->Node4 - - + + @@ -122,8 +122,8 @@ Node1->Node5 - - + + @@ -146,8 +146,8 @@ Node1->Node6 - - + + @@ -170,8 +170,8 @@ Node1->Node7 - - + + @@ -194,23 +194,24 @@ Node1->Node8 - - + + Node9 - - -DbcValueDescriptionHandler -  -+ -Handle() - - - + + +DbcUnsupportedSignalType +Handler +  ++ +Handle() + + + @@ -218,8 +219,32 @@ Node1->Node9 - - + + + + + + + +Node10 + + +DbcValueDescriptionHandler +  ++ +Handle() + + + + + + + + +Node1->Node10 + + + diff --git a/docs/html/classDbcUnsupportedSignalTypeHandler-members.html b/docs/html/classDbcUnsupportedSignalTypeHandler-members.html new file mode 100644 index 0000000..3824ae4 --- /dev/null +++ b/docs/html/classDbcUnsupportedSignalTypeHandler-members.html @@ -0,0 +1,110 @@ + + + + + + + +DBC Framework: Member List + + + + + + + + + + + + + +
    +
    + + + + + + +
    +
    DBC Framework +
    +
    DBC parsing and CAN signal decoding framework
    +
    +
    + + + + + + + +
    +
    + +
    +
    +
    + +
    + +
    +
    + + +
    +
    +
    +
    +
    +
    Loading...
    +
    Searching...
    +
    No Matches
    +
    +
    +
    +
    + +
    +
    DbcUnsupportedSignalTypeHandler Member List
    +
    +
    + +

    This is the complete list of members for DbcUnsupportedSignalTypeHandler, including all inherited members.

    + + + +
    Handle(const std::string &line, DbcParseContext &context) const overrideDbcUnsupportedSignalTypeHandlervirtual
    ~DbcStatementHandler()=default (defined in DbcStatementHandler)DbcStatementHandlervirtual
    +
    + + + + diff --git a/docs/html/classDbcUnsupportedSignalTypeHandler.html b/docs/html/classDbcUnsupportedSignalTypeHandler.html new file mode 100644 index 0000000..c44ce69 --- /dev/null +++ b/docs/html/classDbcUnsupportedSignalTypeHandler.html @@ -0,0 +1,176 @@ + + + + + + + +DBC Framework: DbcUnsupportedSignalTypeHandler Class Reference + + + + + + + + + + + + + +
    +
    + + + + + + +
    +
    DBC Framework +
    +
    DBC parsing and CAN signal decoding framework
    +
    +
    + + + + + + + +
    +
    + +
    +
    +
    + +
    + +
    +
    + + +
    +
    +
    +
    +
    +
    Loading...
    +
    Searching...
    +
    No Matches
    +
    +
    +
    +
    + +
    + +
    DbcUnsupportedSignalTypeHandler Class Reference
    +
    +
    + +

    #include <dbc_statement_handler.h>

    +
    + + Inheritance diagram for DbcUnsupportedSignalTypeHandler:
    +
    +
    + + + + + + +

    +Public Member Functions

    void Handle (const std::string &line, DbcParseContext &context) const override
     Process a trimmed statement using state belonging to this parse.
     
    +

    Detailed Description

    +

    Reject explicit signal value types instead of treating their bits as integers.

    + +

    Definition at line 137 of file dbc_statement_handler.h.

    +

    Member Function Documentation

    + +

    ◆ Handle()

    + +
    +
    + + + + + +
    + + + + + + + + + + + + + + + + + + +
    void DbcUnsupportedSignalTypeHandler::Handle (const std::string & line,
    DbcParseContext & context 
    ) const
    +
    +overridevirtual
    +
    + +

    Process a trimmed statement using state belonging to this parse.

    + +

    Implements DbcStatementHandler.

    + +

    Definition at line 422 of file dbc_statement_handler.cpp.

    + +
    +
    +
    The documentation for this class was generated from the following files: +
    +
    + + + + diff --git a/docs/html/classDbcUnsupportedSignalTypeHandler.js b/docs/html/classDbcUnsupportedSignalTypeHandler.js new file mode 100644 index 0000000..d0dfe62 --- /dev/null +++ b/docs/html/classDbcUnsupportedSignalTypeHandler.js @@ -0,0 +1,4 @@ +var classDbcUnsupportedSignalTypeHandler = +[ + [ "Handle", "classDbcUnsupportedSignalTypeHandler.html#ae4132fcdbb134d51143133c1fb83a9df", null ] +]; \ No newline at end of file diff --git a/docs/html/classDbcUnsupportedSignalTypeHandler__inherit__graph.map b/docs/html/classDbcUnsupportedSignalTypeHandler__inherit__graph.map new file mode 100644 index 0000000..a50dce6 --- /dev/null +++ b/docs/html/classDbcUnsupportedSignalTypeHandler__inherit__graph.map @@ -0,0 +1,5 @@ + + + + + diff --git a/docs/html/classDbcUnsupportedSignalTypeHandler__inherit__graph.md5 b/docs/html/classDbcUnsupportedSignalTypeHandler__inherit__graph.md5 new file mode 100644 index 0000000..a058ae9 --- /dev/null +++ b/docs/html/classDbcUnsupportedSignalTypeHandler__inherit__graph.md5 @@ -0,0 +1 @@ +f59ba682c9bf52b3a1671051c349a473 \ No newline at end of file diff --git a/docs/html/classDbcUnsupportedSignalTypeHandler__inherit__graph.svg b/docs/html/classDbcUnsupportedSignalTypeHandler__inherit__graph.svg new file mode 100644 index 0000000..77ea056 --- /dev/null +++ b/docs/html/classDbcUnsupportedSignalTypeHandler__inherit__graph.svg @@ -0,0 +1,79 @@ + + + + + + + + + + + +DbcUnsupportedSignalTypeHandler + + +Node1 + + +DbcUnsupportedSignalType +Handler +  ++ +Handle() + + + + + + + + +Node2 + + +DbcStatementHandler +  ++ +~DbcStatementHandler() ++ +Handle() + + + + + + + + +Node2->Node1 + + + + + + + + + + + + + diff --git a/docs/html/classDbcUnsupportedSignalTypeHandler__inherit__graph_org.svg b/docs/html/classDbcUnsupportedSignalTypeHandler__inherit__graph_org.svg new file mode 100644 index 0000000..6a56153 --- /dev/null +++ b/docs/html/classDbcUnsupportedSignalTypeHandler__inherit__graph_org.svg @@ -0,0 +1,54 @@ + + + + + + +DbcUnsupportedSignalTypeHandler + + +Node1 + + +DbcUnsupportedSignalType +Handler +  ++ +Handle() + + + + + + + + +Node2 + + +DbcStatementHandler +  ++ +~DbcStatementHandler() ++ +Handle() + + + + + + + + +Node2->Node1 + + + + + + + + diff --git a/docs/html/classDbcValueDescriptionHandler.html b/docs/html/classDbcValueDescriptionHandler.html index 0c881b0..5101ad9 100644 --- a/docs/html/classDbcValueDescriptionHandler.html +++ b/docs/html/classDbcValueDescriptionHandler.html @@ -158,7 +158,7 @@ Public Member Functions

    Implements DbcStatementHandler.

    -

    Definition at line 435 of file dbc_statement_handler.cpp.

    +

    Definition at line 440 of file dbc_statement_handler.cpp.

    References PendingValueDescriptions::canId, PendingValueDescriptions::descriptions, PendingValueDescriptions::isExtended, PendingValueDescriptions::lineNumber, DbcParseContext::lineNumber, DbcParseContext::pendingValues, and PendingValueDescriptions::signalName.

    diff --git a/docs/html/classTypeFactory.html b/docs/html/classTypeFactory.html index 534a5f4..bf26689 100644 --- a/docs/html/classTypeFactory.html +++ b/docs/html/classTypeFactory.html @@ -274,7 +274,7 @@ template<class Derived >

    References TypeFactory< ClassId, BaseClass, Args >::classes.

    -

    Referenced by DbcStatementDispatcher::RegisterType().

    +

    Referenced by DbcStatementDispatcher::RegisterType().

    @@ -368,7 +368,7 @@ template<class ClassId , class BaseClass , class... Args>

    References TypeFactory< ClassId, BaseClass, Args >::creator().

    -

    Referenced by DbcStatementDispatcher::RegisterType().

    +

    Referenced by DbcStatementDispatcher::RegisterType().

    diff --git a/docs/html/classes.html b/docs/html/classes.html index 8d0ceb9..327df1c 100644 --- a/docs/html/classes.html +++ b/docs/html/classes.html @@ -100,7 +100,7 @@ $(document).ready(function(){initNavTree('classes.html',''); initResizable(); })
    AttributeAssignment
    AttributeDefinition
    D
    -
    DbcAttributeHandler
    DbcCommentHandler
    DbcDatabase
    DbcDecodeBuilder
    DbcDecoder
    DbcDiagnostic
    DbcDiagnosticError
    DbcFrameHandler
    DbcHeaderHandler
    DbcMultiplexingHandler
    DbcParseContext
    DbcParser
    DbcParseResult
    DbcParserOptions
    DbcSignalHandler
    DbcStatementDispatcher
    DbcStatementHandler
    DbcTreeBuilder
    DbcTreeModel
    DbcValueDescriptionHandler
    DecodeDatabase
    DecodedFrameValue
    DecodedSignalValue
    DecodeFrame
    DecodeSignal
    +
    DbcAttributeHandler
    DbcCommentHandler
    DbcDatabase
    DbcDecodeBuilder
    DbcDecoder
    DbcDiagnostic
    DbcDiagnosticError
    DbcFrameHandler
    DbcHeaderHandler
    DbcMultiplexingHandler
    DbcParseContext
    DbcParser
    DbcParseResult
    DbcParserOptions
    DbcSignalHandler
    DbcStatementDispatcher
    DbcStatementHandler
    DbcTreeBuilder
    DbcTreeModel
    DbcUnsupportedSignalTypeHandler
    DbcValueDescriptionHandler
    DecodeDatabase
    DecodedFrameValue
    DecodedSignalValue
    DecodeFrame
    DecodeSignal
    F
    FrameInfo
    FrameKey
    FrameKeyHasher
    diff --git a/docs/html/dbc__coverage__test_8cpp_source.html b/docs/html/dbc__coverage__test_8cpp_source.html index 5bb5f21..c6e458d 100644 --- a/docs/html/dbc__coverage__test_8cpp_source.html +++ b/docs/html/dbc__coverage__test_8cpp_source.html @@ -999,12 +999,12 @@ $(document).ready(function(){initNavTree('dbc__coverage__test_8cpp_source.html',
    Runtime CAN decoder using prebuilt decode database.
    Definition dbc_decoder.h:87
    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 exact CAN ID first, then unique PGN among explicitly J1939 definitions.
    -
    Minimal DBC parser.
    Definition dbc_parser.h:43
    -
    DbcDatabase ParseFile(const std::string &filePath) const
    Parse DBC file.
    +
    Minimal DBC parser.
    Definition dbc_parser.h:45
    +
    DbcDatabase ParseFile(const std::string &filePath) const
    Parse DBC file.
    Builds a simple tree from parsed DBC database.
    std::unique_ptr< TreeNode > Build(const DbcDatabase &database) const
    Build tree representation of parsed DBC data.
    Defer raw value descriptions for target resolution.
    -
    void Handle(const std::string &line, DbcParseContext &context) const override
    Process a trimmed statement using state belonging to this parse.
    +
    void Handle(const std::string &line, DbcParseContext &context) const override
    Process a trimmed statement using state belonging to this parse.
    Immutable runtime snapshot. Copies share storage; reload replaces a snapshot. Owning lookup handles a...
    std::shared_ptr< const DecodeFrame > FindFrame(const FrameKey &key) const
    const std::vector< DecodeFrame > & GetFrames() const
    diff --git a/docs/html/dbc__diagnostic_8h_source.html b/docs/html/dbc__diagnostic_8h_source.html index e10709f..b16fc60 100644 --- a/docs/html/dbc__diagnostic_8h_source.html +++ b/docs/html/dbc__diagnostic_8h_source.html @@ -120,48 +120,49 @@ $(document).ready(function(){initNavTree('dbc__diagnostic_8h_source.html',''); i
    22 InvalidMultiplexing,
    23 UnsupportedStatement,
    24 InvalidAttribute,
    -
    25 UnsupportedAttribute
    -
    26};
    -
    27
    -
    - -
    30 DbcDiagnosticSeverity severity;
    -
    31 DbcDiagnosticCode code;
    -
    32 std::string filePath;
    -
    33 std::size_t lineNumber;
    -
    34 std::string message;
    -
    35};
    +
    25 UnsupportedAttribute,
    +
    26 UnsupportedSignalType
    +
    27};
    +
    28
    +
    + +
    31 DbcDiagnosticSeverity severity;
    +
    32 DbcDiagnosticCode code;
    +
    33 std::string filePath;
    +
    34 std::size_t lineNumber;
    +
    35 std::string message;
    +
    36};
    -
    36
    -
    - - -
    40 std::vector<DbcDiagnostic> diagnostics;
    -
    -
    43 bool HasErrors() const {
    -
    44 for (const DbcDiagnostic &diagnostic: diagnostics) {
    -
    45 if (diagnostic.severity == DbcDiagnosticSeverity::Error)
    -
    46 return true;
    -
    47 }
    -
    48 return false;
    -
    49 }
    +
    37
    +
    + + +
    41 std::vector<DbcDiagnostic> diagnostics;
    +
    +
    44 bool HasErrors() const {
    +
    45 for (const DbcDiagnostic &diagnostic: diagnostics) {
    +
    46 if (diagnostic.severity == DbcDiagnosticSeverity::Error)
    +
    47 return true;
    +
    48 }
    +
    49 return false;
    +
    50 }
    -
    50};
    +
    51};
    -
    51
    -
    52#endif /* DBC_DIAGNOSTIC_H */
    +
    52
    +
    53#endif /* DBC_DIAGNOSTIC_H */
    Parsed DBC database and attribute metadata.
    Parsed DBC content stored in a simple internal form.
    -
    Diagnostic message and physical source location.
    -
    std::string message
    -
    DbcDiagnosticSeverity severity
    -
    DbcDiagnosticCode code
    -
    std::string filePath
    -
    std::size_t lineNumber
    -
    Parsed database and diagnostics; the database is empty on error.
    -
    std::vector< DbcDiagnostic > diagnostics
    -
    bool HasErrors() const
    Return whether any diagnostic has error severity.
    -
    DbcDatabase database
    +
    Diagnostic message and physical source location.
    +
    std::string message
    +
    DbcDiagnosticSeverity severity
    +
    DbcDiagnosticCode code
    +
    std::string filePath
    +
    std::size_t lineNumber
    +
    Parsed database and diagnostics; the database is empty on error.
    +
    std::vector< DbcDiagnostic > diagnostics
    +
    bool HasErrors() const
    Return whether any diagnostic has error severity.
    +
    DbcDatabase database
    diff --git a/docs/html/dbc__lifetime__test_8cpp_source.html b/docs/html/dbc__lifetime__test_8cpp_source.html index 6790a37..2e8c13a 100644 --- a/docs/html/dbc__lifetime__test_8cpp_source.html +++ b/docs/html/dbc__lifetime__test_8cpp_source.html @@ -347,7 +347,7 @@ $(document).ready(function(){initNavTree('dbc__lifetime__test_8cpp_source.html',
    Runtime CAN decoder using prebuilt decode database.
    Definition dbc_decoder.h:87
    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 exact CAN ID first, then unique PGN among explicitly J1939 definitions.
    -
    Minimal DBC parser.
    Definition dbc_parser.h:43
    +
    Minimal DBC parser.
    Definition dbc_parser.h:45
    Immutable runtime snapshot. Copies share storage; reload replaces a snapshot. Owning lookup handles a...
    std::shared_ptr< const DecodeSignal > FindSignal(const SignalKey &key) const
    Return an owning signal handle, or an empty handle for a missing key.
    std::shared_ptr< const DecodeFrame > FindFrame(const FrameKey &key) const
    diff --git a/docs/html/dbc__parser_8cpp_source.html b/docs/html/dbc__parser_8cpp_source.html index 97b765b..6d2bf39 100644 --- a/docs/html/dbc__parser_8cpp_source.html +++ b/docs/html/dbc__parser_8cpp_source.html @@ -173,123 +173,125 @@ $(document).ready(function(){initNavTree('dbc__parser_8cpp_source.html',''); ini
    83 return DbcDiagnosticCode::InvalidMultiplexing;
    84 if (keyword == "VAL_")
    85 return DbcDiagnosticCode::InvalidValueDescription;
    -
    86 return DbcDiagnosticCode::InvalidStatement;
    -
    87 }
    -
    88}
    -
    89
    -
    -
    90DbcDatabase DbcParser::ParseFile(const std::string &filePath) const {
    - -
    92 for (const DbcDiagnostic &diagnostic : result.diagnostics) {
    -
    93 if (diagnostic.severity == DbcDiagnosticSeverity::Error) {
    -
    94 if (diagnostic.lineNumber == 0U)
    -
    95 throw std::runtime_error(diagnostic.message);
    -
    96 throw std::runtime_error(diagnostic.filePath + ":" +
    -
    97 std::to_string(diagnostic.lineNumber) + ": " + diagnostic.message);
    -
    98 }
    -
    99 }
    -
    100 return std::move(result.database);
    -
    101}
    +
    86 if (keyword == "SIG_VALTYPE_")
    +
    87 return DbcDiagnosticCode::UnsupportedSignalType;
    +
    88 return DbcDiagnosticCode::InvalidStatement;
    +
    89 }
    +
    90}
    +
    91
    +
    +
    92DbcDatabase DbcParser::ParseFile(const std::string &filePath) const {
    + +
    94 for (const DbcDiagnostic &diagnostic : result.diagnostics) {
    +
    95 if (diagnostic.severity == DbcDiagnosticSeverity::Error) {
    +
    96 if (diagnostic.lineNumber == 0U)
    +
    97 throw std::runtime_error(diagnostic.message);
    +
    98 throw std::runtime_error(diagnostic.filePath + ":" +
    +
    99 std::to_string(diagnostic.lineNumber) + ": " + diagnostic.message);
    +
    100 }
    +
    101 }
    +
    102 return std::move(result.database);
    +
    103}
    -
    102
    -
    -
    103DbcParseResult DbcParser::ParseFileWithDiagnostics(const std::string &filePath) const {
    -
    104 DbcParseResult result;
    -
    105 std::ifstream input(filePath.c_str());
    -
    106 if (!input.is_open()) {
    -
    107 result.diagnostics.push_back({DbcDiagnosticSeverity::Error,
    -
    108 DbcDiagnosticCode::FileOpenError, filePath, 0U,
    -
    109 "Failed to open DBC file: " + filePath});
    -
    110 return result;
    -
    111 }
    -
    112
    -
    113 DbcDatabase database;
    -
    114 DbcParseContext context{database, nullptr};
    -
    115 context.enableJ1939 = options.enableJ1939;
    -
    116 const DbcStatementDispatcher dispatcher;
    -
    117 bool inNamespace = false;
    -
    118 std::size_t lineNumber = 0U;
    -
    119 std::string line;
    -
    120 while (std::getline(input, line)) {
    -
    121 ++lineNumber;
    -
    122 if (!line.empty() && line.back() == '\r')
    -
    123 line.pop_back();
    -
    124 const std::string rawLine = line;
    -
    125 const std::size_t statementLine = lineNumber;
    -
    126 line = dbc_detail::TrimText(line);
    -
    127 if (line.empty())
    -
    128 continue;
    -
    129
    -
    130 std::istringstream tokens(line);
    -
    131 std::string keyword;
    -
    132 std::string argument;
    -
    133 tokens >> keyword;
    -
    134 const bool hasArgument = static_cast<bool>(tokens >> argument);
    -
    135 if (inNamespace && !hasArgument && keyword != "BO_" && keyword != "SG_" &&
    -
    136 keyword != "BS_" && keyword != "BS_:" &&
    -
    137 keyword != "BU_" && keyword != "BU_:")
    -
    138 continue; // NS_ lists syntax symbols; BO_, SG_, BS_ and BU_ end the list.
    -
    139 inNamespace = keyword == "NS_" || keyword == "NS_:";
    -
    140
    -
    141 if (keyword == "CM_") {
    -
    142 line = rawLine;
    -
    143 ReadCommentLines(input, line, lineNumber);
    -
    144 } else if (keyword == "BA_DEF_" || keyword == "BA_DEF_DEF_" || keyword == "BA_") {
    -
    145 line = rawLine.substr(rawLine.find(keyword));
    -
    146 ReadAttributeLines(input, line, lineNumber);
    -
    147 }
    -
    148
    -
    149 try {
    -
    150 context.lineNumber = statementLine;
    -
    151 if (!dispatcher.Dispatch(line, context)) {
    -
    152 result.diagnostics.push_back({DbcDiagnosticSeverity::Warning,
    -
    153 DbcDiagnosticCode::UnsupportedStatement,
    -
    154 filePath, statementLine,
    -
    155 "Unsupported statement '" + keyword +
    -
    156 "' is ignored; decoding or metadata may be incomplete."});
    -
    157 }
    -
    158 } catch (const std::exception &error) {
    -
    159 result.diagnostics.push_back({DbcDiagnosticSeverity::Error,
    -
    160 StatementErrorCode(keyword), filePath,
    -
    161 statementLine, error.what()});
    -
    162 return result;
    -
    163 }
    -
    164 }
    -
    165
    -
    166 if (input.bad() || !input.eof()) {
    -
    167 result.diagnostics.push_back({DbcDiagnosticSeverity::Error,
    -
    168 DbcDiagnosticCode::FileReadError, filePath, 0U,
    -
    169 "Failed to read DBC file: " + filePath});
    -
    170 return result;
    -
    171 }
    -
    172
    -
    173 try {
    -
    174 context.ResolveComments(filePath);
    -
    175 context.ResolveValueDescriptions(filePath);
    -
    176 context.ResolveMultiplexing(filePath);
    -
    177 context.ValidateMultiplexing(filePath);
    -
    178 context.ResolveAttributes(filePath, result.diagnostics);
    -
    179 } catch (const DbcDiagnosticError &error) {
    -
    180 result.diagnostics.push_back(error.diagnostic);
    -
    181 return result;
    -
    182 }
    -
    183 result.database = std::move(database);
    -
    184 return result;
    -
    185}
    +
    104
    +
    +
    105DbcParseResult DbcParser::ParseFileWithDiagnostics(const std::string &filePath) const {
    +
    106 DbcParseResult result;
    +
    107 std::ifstream input(filePath.c_str());
    +
    108 if (!input.is_open()) {
    +
    109 result.diagnostics.push_back({DbcDiagnosticSeverity::Error,
    +
    110 DbcDiagnosticCode::FileOpenError, filePath, 0U,
    +
    111 "Failed to open DBC file: " + filePath});
    +
    112 return result;
    +
    113 }
    +
    114
    +
    115 DbcDatabase database;
    +
    116 DbcParseContext context{database, nullptr};
    +
    117 context.enableJ1939 = options.enableJ1939;
    +
    118 const DbcStatementDispatcher dispatcher;
    +
    119 bool inNamespace = false;
    +
    120 std::size_t lineNumber = 0U;
    +
    121 std::string line;
    +
    122 while (std::getline(input, line)) {
    +
    123 ++lineNumber;
    +
    124 if (!line.empty() && line.back() == '\r')
    +
    125 line.pop_back();
    +
    126 const std::string rawLine = line;
    +
    127 const std::size_t statementLine = lineNumber;
    +
    128 line = dbc_detail::TrimText(line);
    +
    129 if (line.empty())
    +
    130 continue;
    +
    131
    +
    132 std::istringstream tokens(line);
    +
    133 std::string keyword;
    +
    134 std::string argument;
    +
    135 tokens >> keyword;
    +
    136 const bool hasArgument = static_cast<bool>(tokens >> argument);
    +
    137 if (inNamespace && !hasArgument && keyword != "BO_" && keyword != "SG_" &&
    +
    138 keyword != "BS_" && keyword != "BS_:" &&
    +
    139 keyword != "BU_" && keyword != "BU_:")
    +
    140 continue; // NS_ lists syntax symbols; BO_, SG_, BS_ and BU_ end the list.
    +
    141 inNamespace = keyword == "NS_" || keyword == "NS_:";
    +
    142
    +
    143 if (keyword == "CM_") {
    +
    144 line = rawLine;
    +
    145 ReadCommentLines(input, line, lineNumber);
    +
    146 } else if (keyword == "BA_DEF_" || keyword == "BA_DEF_DEF_" || keyword == "BA_") {
    +
    147 line = rawLine.substr(rawLine.find(keyword));
    +
    148 ReadAttributeLines(input, line, lineNumber);
    +
    149 }
    +
    150
    +
    151 try {
    +
    152 context.lineNumber = statementLine;
    +
    153 if (!dispatcher.Dispatch(line, context)) {
    +
    154 result.diagnostics.push_back({DbcDiagnosticSeverity::Warning,
    +
    155 DbcDiagnosticCode::UnsupportedStatement,
    +
    156 filePath, statementLine,
    +
    157 "Unsupported statement '" + keyword +
    +
    158 "' is ignored; decoding or metadata may be incomplete."});
    +
    159 }
    +
    160 } catch (const std::exception &error) {
    +
    161 result.diagnostics.push_back({DbcDiagnosticSeverity::Error,
    +
    162 StatementErrorCode(keyword), filePath,
    +
    163 statementLine, error.what()});
    +
    164 return result;
    +
    165 }
    +
    166 }
    +
    167
    +
    168 if (input.bad() || !input.eof()) {
    +
    169 result.diagnostics.push_back({DbcDiagnosticSeverity::Error,
    +
    170 DbcDiagnosticCode::FileReadError, filePath, 0U,
    +
    171 "Failed to read DBC file: " + filePath});
    +
    172 return result;
    +
    173 }
    +
    174
    +
    175 try {
    +
    176 context.ResolveComments(filePath);
    +
    177 context.ResolveValueDescriptions(filePath);
    +
    178 context.ResolveMultiplexing(filePath);
    +
    179 context.ValidateMultiplexing(filePath);
    +
    180 context.ResolveAttributes(filePath, result.diagnostics);
    +
    181 } catch (const DbcDiagnosticError &error) {
    +
    182 result.diagnostics.push_back(error.diagnostic);
    +
    183 return result;
    +
    184 }
    +
    185 result.database = std::move(database);
    +
    186 return result;
    +
    187}
    -
    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.
    - -
    bool Dispatch(const std::string &line, DbcParseContext &context) const
    +
    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.
    + +
    bool Dispatch(const std::string &line, DbcParseContext &context) const
    DBC file parser and interpretation options.
    Parsed DBC content stored in a simple internal form.
    -
    Diagnostic message and physical source location.
    +
    Diagnostic message and physical source location.
    -
    Parsed database and diagnostics; the database is empty on error.
    -
    std::vector< DbcDiagnostic > diagnostics
    -
    DbcDatabase database
    +
    Parsed database and diagnostics; the database is empty on error.
    +
    std::vector< DbcDiagnostic > diagnostics
    +
    DbcDatabase database
    diff --git a/docs/html/dbc__parser_8h_source.html b/docs/html/dbc__parser_8h_source.html index 8587103..2588f27 100644 --- a/docs/html/dbc__parser_8h_source.html +++ b/docs/html/dbc__parser_8h_source.html @@ -112,27 +112,27 @@ $(document).ready(function(){initNavTree('dbc__parser_8h_source.html',''); initR
    22};
    23
    -
    -
    43class DbcParser {
    -
    44public:
    -
    46 explicit DbcParser(DbcParserOptions options = {}) : options(options) {}
    -
    47
    -
    57 DbcDatabase ParseFile(const std::string &filePath) const;
    -
    58
    -
    65 DbcParseResult ParseFileWithDiagnostics(const std::string &filePath) const;
    -
    66
    -
    67private:
    -
    68 DbcParserOptions options;
    -
    69};
    +
    +
    45class DbcParser {
    +
    46public:
    +
    48 explicit DbcParser(DbcParserOptions options = {}) : options(options) {}
    +
    49
    +
    59 DbcDatabase ParseFile(const std::string &filePath) const;
    +
    60
    +
    67 DbcParseResult ParseFileWithDiagnostics(const std::string &filePath) const;
    +
    68
    +
    69private:
    +
    70 DbcParserOptions options;
    +
    71};
    -
    70
    -
    71#endif /* DBC_PARSER_H */
    -
    Minimal DBC parser.
    Definition dbc_parser.h:43
    -
    DbcParseResult ParseFileWithDiagnostics(const std::string &filePath) const
    Parse with warnings and a structured first error instead of throwing.
    -
    DbcParser(DbcParserOptions options={})
    Create a parser with explicit extended-frame interpretation options.
    Definition dbc_parser.h:46
    -
    DbcDatabase ParseFile(const std::string &filePath) const
    Parse DBC file.
    +
    72
    +
    73#endif /* DBC_PARSER_H */
    +
    Minimal DBC parser.
    Definition dbc_parser.h:45
    +
    DbcParseResult ParseFileWithDiagnostics(const std::string &filePath) const
    Parse with warnings and a structured first error instead of throwing.
    +
    DbcParser(DbcParserOptions options={})
    Create a parser with explicit extended-frame interpretation options.
    Definition dbc_parser.h:48
    +
    DbcDatabase ParseFile(const std::string &filePath) const
    Parse DBC file.
    Parsed DBC content stored in a simple internal form.
    -
    Parsed database and diagnostics; the database is empty on error.
    +
    Parsed database and diagnostics; the database is empty on error.
    diff --git a/docs/html/dbc__parser__test_8cpp_source.html b/docs/html/dbc__parser__test_8cpp_source.html index 06ab445..c145eb7 100644 --- a/docs/html/dbc__parser__test_8cpp_source.html +++ b/docs/html/dbc__parser__test_8cpp_source.html @@ -199,1542 +199,1588 @@ $(document).ready(function(){initNavTree('dbc__parser__test_8cpp_source.html',''
    99 void diagnostics();
    100 void diagnosticErrors();
    101 void diagnosticUnsupportedStatements();
    -
    102 void diagnosticNamespaceBoundaries();
    -
    103 void diagnosticFormatting();
    -
    104 void multiplexing();
    -
    105 void multiplexingBoundaries();
    -
    106 void extendedMultiplexing();
    -
    107 void extendedMultiplexingErrors();
    -
    108 void validFrames();
    -
    109 void invalidFrames();
    -
    110 void duplicateFrames();
    -
    111 void duplicateSignals();
    -
    112 void distinctDefinitions();
    -
    113 void comments();
    -
    114 void invalidComments_data();
    -
    115 void invalidComments();
    -
    116 void integration();
    -
    117 void invalidSignals();
    -
    118 void validSignals();
    -
    119 void longSignalDefinitions_data();
    -
    120 void longSignalDefinitions();
    -
    121 void physicalNumbersCppLocale();
    -
    122 void physicalNumbersCLocale();
    -
    123 void dispatch();
    -
    124 void independentSessions();
    -
    125 void valueDescriptions();
    -
    126 void invalidValueDescriptions();
    -
    127};
    -
    128
    -
    129void DbcParserTest::attributes() {
    -
    130 Fixture fixture;
    -
    131 fixture.Write(
    -
    132 "BA_ \"GenMsgCycleTime\" BO_ 1 25;\n"
    -
    133 "BA_ \"VFrameFormat\" BO_ 2147483649 1;\n"
    -
    134 "BA_ \"Label\" SG_ 1 Value \"a\\\"b\\\\c\";\n"
    -
    135 "BA_ \"NodeLabel\" BU_ ECU \"node\";\n"
    -
    136 "BA_ \"Environment\" EV_ Env 2;\n"
    -
    137 "BA_DEF_DEF_ \"GenMsgCycleTime\" 100;\n"
    -
    138 "BA_DEF_DEF_ \"VFrameFormat\" \"StandardCAN\";\n"
    -
    139 "BA_DEF_DEF_ \"Label\" \"default\";\n"
    -
    140 "BA_DEF_DEF_ \"BusLabel\" \"CAN\";\n"
    -
    141 "BA_DEF_DEF_ \"Ratio\" 1.5e-1;\n"
    -
    142 "BA_DEF_ BO_ \"GenMsgCycleTime\" INT 0 10000;\n"
    -
    143 "BA_DEF_ BO_ \"VFrameFormat\" ENUM \"StandardCAN\",\"ExtendedCAN\",\"StandardCAN_FD\";\n"
    -
    144 "BA_DEF_ SG_ \"Label\" STRING;\n"
    -
    145 "BA_DEF_ \"BusLabel\" STRING;\n"
    -
    146 "BA_DEF_ BO_ \"Ratio\" FLOAT -1 1;\n"
    -
    147 "BA_DEF_ BU_ \"NodeLabel\" STRING;\n"
    -
    148 "BA_DEF_ EV_ \"Environment\" HEX 0 255;\n"
    -
    149 "BO_ 1 Standard: 1 ECU\n"
    -
    150 "SG_ Value : 0|8@1+ (1,0) [0|255] \"\" ECU\n"
    -
    151 "BO_ 2147483649 Extended: 1 ECU\n"
    -
    152 "SG_ Value : 0|8@1+ (1,0) [0|255] \"\" ECU\n");
    -
    153 const DbcParseResult result = DbcParser().ParseFileWithDiagnostics(fixture.path.string());
    -
    154 QVERIFY(!result.HasErrors());
    -
    155 QVERIFY(result.diagnostics.empty());
    -
    156 const DbcDatabase &database = result.database;
    -
    157 QCOMPARE(database.attributeDefinitions.size(), std::size_t(7));
    -
    158 QCOMPARE(database.attributeAssignments.size(), std::size_t(5));
    -
    159 QCOMPARE(std::get<std::string>(database.attributes.at("BusLabel")), std::string("CAN"));
    -
    160 QCOMPARE(database.frames[0].cycleTimeMs.value(), 25U);
    -
    161 QCOMPARE(database.frames[1].cycleTimeMs.value(), 100U);
    -
    162 QCOMPARE(database.frames[1].frameFormat.value(), std::string("ExtendedCAN"));
    -
    163 QVERIFY(!database.frames[1].hasPgn);
    -
    164 QCOMPARE(std::get<double>(database.frames[0].attributes.at("Ratio")), 0.15);
    -
    165 QCOMPARE(std::get<std::string>(database.frames[0].signals[0].attributes.at("Label")), std::string("a\"b\\c"));
    -
    166 QCOMPARE(std::get<std::string>(database.frames[1].signals[0].attributes.at("Label")), std::string("default"));
    -
    167 const DecodeDatabase runtime = DbcDecodeBuilder().Build(database);
    -
    168 QCOMPARE(runtime.GetFrames()[0].cycleTimeMs.value(), 25U);
    -
    169 QCOMPARE(runtime.GetFrames()[1].frameFormat.value(), std::string("ExtendedCAN"));
    -
    170 QCOMPARE(runtime.GetFrames()[0].signals[0].attributes, database.frames[0].signals[0].attributes);
    -
    171 const std::unique_ptr<TreeNode> tree = DbcTreeBuilder().Build(database);
    -
    172 QCOMPARE(tree->GetChild(0)->GetFrame()->cycleTimeMs.value(), 25U);
    -
    173 QCOMPARE(tree->GetChild(0)->GetChild(0)->GetSignal()->attributes, database.frames[0].signals[0].attributes);
    -
    174 fixture.Write("BA_DEF_ BO_ \"Period\" INT 0 10;\nBA_DEF_DEF_ \"Period\" 1;\n"
    -
    175 "BA_ \"Period\" BO_ 1 2;\nBA_ \"Period\" BO_ 1 3;\nBO_ 1 F: 0 ECU\n");
    -
    176 const DbcDatabase repeated = DbcParser().ParseFile(fixture.path.string());
    -
    177 QCOMPARE(std::get<std::int64_t>(repeated.frames[0].attributes.at("Period")), std::int64_t(3));
    -
    178
    -
    179 fixture.Write("BA_DEF_ BO_ \"GenMsgCycleTime\" INT 0 4294967295;\n"
    -
    180 "BA_DEF_DEF_ \"GenMsgCycleTime\" 0;\n"
    -
    181 "BA_ \"GenMsgCycleTime\" BO_ 1 4294967295;\n"
    -
    182 "BA_DEF_ \"Wide\" INT -9223372036854775808 9223372036854775807;\n"
    -
    183 "BA_DEF_DEF_ \"Wide\" -9223372036854775808;\n"
    -
    184 "BA_DEF_ SG_ \"State\" ENUM \"off\",\"on\";\n"
    -
    185 "BA_DEF_DEF_ \"State\" 1;\n"
    -
    186 "BO_ 1 F: 0 ECU\nBO_ 2 Empty: 0 ECU\n");
    -
    187 const DbcDatabase boundaries = DbcParser().ParseFile(fixture.path.string());
    -
    188 QCOMPARE(boundaries.frames[0].cycleTimeMs.value(), std::numeric_limits<std::uint32_t>::max());
    -
    189 QCOMPARE(boundaries.frames[1].cycleTimeMs.value(), 0U);
    -
    190 QVERIFY(!boundaries.frames[0].frameFormat);
    -
    191 QCOMPARE(std::get<std::int64_t>(boundaries.attributes.at("Wide")), std::numeric_limits<std::int64_t>::min());
    -
    192 QCOMPARE(std::get<std::int64_t>(*boundaries.attributeDefinitions.at("State").defaultValue), std::int64_t(1));
    -
    193}
    -
    194
    -
    195void DbcParserTest::multilineAttributes() {
    -
    196 Fixture fixture;
    -
    197 fixture.Write(R"DBC(BA_
    -
    198 "GenMsgCycleTime" BO_ 1
    -
    199 25;
    -
    200BA_DEF_ BO_ "VFrameFormat" ENUM
    -
    201 "StandardCAN",
    -
    202 "StandardCAN_FD";
    -
    203BA_DEF_DEF_
    -
    204 "VFrameFormat"
    -
    205 "StandardCAN_FD";
    -
    206BA_DEF_ BO_ "GenMsgCycleTime"
    -
    207 INT 0 100;
    -
    208BA_DEF_DEF_ "GenMsgCycleTime"
    -
    209 50;
    -
    210BA_DEF_ SG_ "Label" STRING;
    -
    211BA_ "Label" SG_ 1 Value
    -
    212 "first; \"quoted\"
    -
    213second\\path";
    -
    214BA_ "VFrameFormat" BO_ 1
    -
    215 0;
    -
    216BO_ 1 F: 1 ECU
    -
    217SG_ Value : 0|8@1+ (1,0) [0|255] "" ECU
    -
    218BO_ 2 Default: 0 ECU
    -
    219)DBC");
    -
    220 const DbcParseResult result = DbcParser().ParseFileWithDiagnostics(fixture.path.string());
    -
    221 QVERIFY(!result.HasErrors());
    -
    222 QCOMPARE(result.database.frames.size(), std::size_t(2));
    -
    223 QCOMPARE(result.database.frames[0].cycleTimeMs.value(), 25U);
    -
    224 QCOMPARE(result.database.frames[1].cycleTimeMs.value(), 50U);
    -
    225 QCOMPARE(result.database.frames[0].frameFormat.value(), std::string("StandardCAN"));
    -
    226 QCOMPARE(result.database.frames[1].frameFormat.value(), std::string("StandardCAN_FD"));
    -
    227 QCOMPARE(std::get<std::string>(result.database.frames[0].signals[0].attributes.at("Label")),
    -
    228 std::string("first; \"quoted\"\nsecond\\path"));
    -
    229 QCOMPARE(result.diagnostics.size(), std::size_t(1));
    -
    230 QCOMPARE(result.diagnostics[0].code, DbcDiagnosticCode::UnsupportedAttribute);
    -
    231 QCOMPARE(result.diagnostics[0].lineNumber, std::size_t(7));
    -
    232
    -
    233 fixture.Write("BA_DEF_ BO_ \"VFrameFormat\" ENUM\r\n \"StandardCAN\",\r\n \"ExtendedCAN_FD\";\r\n"
    -
    234 "BA_\r\n \"VFrameFormat\" BO_ 1\r\n 1;\r\nBO_ 1 F: 0 ECU\r\n");
    -
    235 const DbcParseResult explicitFormat = DbcParser().ParseFileWithDiagnostics(fixture.path.string());
    -
    236 QVERIFY(!explicitFormat.HasErrors());
    -
    237 QCOMPARE(explicitFormat.diagnostics.size(), std::size_t(1));
    -
    238 QCOMPARE(explicitFormat.diagnostics[0].code, DbcDiagnosticCode::UnsupportedAttribute);
    -
    239 QCOMPARE(explicitFormat.diagnostics[0].lineNumber, std::size_t(4));
    -
    240 QCOMPARE(explicitFormat.database.frames[0].frameFormat.value(), std::string("ExtendedCAN_FD"));
    -
    241
    -
    242 fixture.Write("BA_DEF_ \"Text\" STRING;\n BA_ \"Text\" \"first \nsecond\";\n"
    -
    243 "UnsupportedStatement\n");
    -
    244 const DbcParseResult whitespace = DbcParser().ParseFileWithDiagnostics(fixture.path.string());
    -
    245 QVERIFY(!whitespace.HasErrors());
    -
    246 QCOMPARE(std::get<std::string>(whitespace.database.attributes.at("Text")), std::string("first \nsecond"));
    -
    247 QCOMPARE(whitespace.diagnostics.size(), std::size_t(1));
    -
    248 QCOMPARE(whitespace.diagnostics[0].code, DbcDiagnosticCode::UnsupportedStatement);
    -
    249 QCOMPARE(whitespace.diagnostics[0].lineNumber, std::size_t(4));
    -
    250}
    -
    251
    -
    252void DbcParserTest::invalidMultilineAttributes() {
    -
    253 Fixture fixture;
    -
    254 const std::vector<std::string> invalid = {
    -
    255 "BA_DEF_\n BO_ \"A\" INT\n 10 0;\n",
    -
    256 "BA_DEF_ BO_ \"A\" ENUM\n \"one\",\n;\n",
    -
    257 "BA_DEF_ BO_ \"A\" STRING\n",
    -
    258 "BA_DEF_ BO_ \"A\" ENUM\n \"unterminated;\n",
    -
    259 "BA_DEF_DEF_\n \"Unknown\"\n 0;\n",
    -
    260 "BA_\n \"Unknown\" BO_ 1\n 0;\n",
    -
    261 "BA_DEF_ BO_ \"A\" STRING\n ; trailing\n"};
    -
    262 for (const std::string &statement: invalid) {
    -
    263 fixture.Write("BO_ 1 F: 0 ECU\n" + statement);
    -
    264 const DbcParseResult result = DbcParser().ParseFileWithDiagnostics(fixture.path.string());
    -
    265 QVERIFY2(result.HasErrors(), statement.c_str());
    -
    266 QVERIFY(result.database.frames.empty());
    -
    267 QCOMPARE(result.diagnostics.back().code, DbcDiagnosticCode::InvalidAttribute);
    -
    268 QCOMPARE(result.diagnostics.back().lineNumber, std::size_t(2));
    -
    269 QVERIFY_THROWS_EXCEPTION(std::runtime_error, DbcParser().ParseFile(fixture.path.string()));
    -
    270 }
    -
    271}
    -
    272
    -
    273void DbcParserTest::invalidAttributes() {
    -
    274 Fixture fixture;
    -
    275 const std::vector<std::string> invalid = {
    -
    276 "BA_DEF_ BO_ \"A\"STRING;",
    -
    277 "BA_DEF_ BO_\"A\" STRING;",
    -
    278 "BA_DEF_ BO_ \"A\" \"STRING\";",
    -
    279 "BA_DEF_ BO_ A STRING;",
    -
    280 "BA_DEF_ ZZ_ \"A\" STRING;",
    -
    281 "BA_DEF_ BO_ \"A\" BLOB;",
    -
    282 "BA_DEF_ BO_ \"A\" FLOAT 2 1;",
    -
    283 "BA_DEF_ BO_ \"A\" STRING;\nBA_ \"A\" BO_ 1 0;",
    -
    284 "BA_DEF_ BO_ \"A\" FLOAT 0 1;\nBA_ \"A\" BO_ 1 2;",
    -
    285 "BA_DEF_ BO_ \"A\" INT 0 1;\nBA_ \"A\" BO_ -1 0;",
    -
    286 "BA_DEF_ BO_ \"A\" INT 0 1;\nBA_ \"A\" BO_ 4294967296 0;",
    -
    287 "BA_DEF_ BO_ \"A\" INT 0 1;\nBA_ \"A\" BO_ 536870912 0;",
    -
    288 "BA_DEF_ BO_ \"A\" INT 2 1;",
    -
    289 "BA_DEF_ BO_ \"A\" FLOAT nan 1;",
    -
    290 "BA_DEF_ BO_ \"A\" FLOAT 0 inf;",
    -
    291 "BA_DEF_ BO_ \"A\" INT 0 9223372036854775808;",
    -
    292 "BA_DEF_ BO_ \"A\" STRING; trailing",
    -
    293 "BA_DEF_ BO_ \"A\" ENUM;",
    -
    294 "BA_DEF_ BO_ \"A\" ENUM \"one\",;",
    -
    295 "BA_DEF_ BO_ \"A\" ENUM \"one\",\"one\";",
    -
    296 "BA_DEF_ BO_ \"VFrameFormat\" STRING;",
    -
    297 "BA_DEF_ SG_ \"GenMsgCycleTime\" INT 0 10;",
    -
    298 "BA_ \"Unknown\" BO_ 1 0;",
    -
    299 "BA_DEF_DEF_ \"Unknown\" 0;",
    -
    300 "BA_DEF_ BO_ \"A\" INT 0 1;\nBA_ \"A\" BO_ 1 2;",
    -
    301 "BA_DEF_ BO_ \"A\" INT 0 1;\nBA_ \"A\" BO_ 1 \"0\";",
    -
    302 "BA_DEF_ BO_ \"A\" INT 0 1;\nBA_ \"A\" SG_ 1 S 0;",
    -
    303 "BA_DEF_ BO_ \"A\" INT 0 1;\nBA_ \"A\" BO_ 2 0;",
    -
    304 "BA_DEF_ SG_ \"A\" INT 0 1;\nBA_ \"A\" SG_ 1 Missing 0;",
    -
    305 "BA_DEF_ BO_ \"A\" INT 0 1;\nBA_DEF_ BO_ \"A\" INT 0 1;",
    -
    306 "BA_DEF_ BO_ \"A\" INT 0 1;\nBA_DEF_DEF_ \"A\" 0;\nBA_DEF_DEF_ \"A\" 1;",
    -
    307 "BA_DEF_ BO_ \"A\" STRING;\nBA_ \"A\" BO_ 1 \"bad\\q\";",
    -
    308 "BA_DEF_ BO_ \"A\" STRING;\nBA_ \"A\" BO_ 1 \"unterminated;",
    -
    309 "BA_DEF_ BO_ \"A\" ENUM \"one\";\nBA_DEF_DEF_ \"A\" \"missing\";",
    -
    310 "BA_DEF_ BO_ \"A\" ENUM \"one\";\nBA_ \"A\" BO_ 1 1;",
    -
    311 "BA_DEF_ BO_ \"GenMsgCycleTime\" INT -1 4294967296;\nBA_DEF_DEF_ \"GenMsgCycleTime\" -1;",
    -
    312 "BA_DEF_ BO_ \"GenMsgCycleTime\" INT 0 4294967296;\nBA_ \"GenMsgCycleTime\" BO_ 1 4294967296;"};
    -
    313 for (const std::string &statement: invalid) {
    -
    314 fixture.Write("BO_ 1 F: 0 ECU\n" + statement + '\n');
    -
    315 const DbcParseResult result = DbcParser().ParseFileWithDiagnostics(fixture.path.string());
    -
    316 QVERIFY2(result.HasErrors(), statement.c_str());
    -
    317 QVERIFY(result.database.frames.empty());
    -
    318 QCOMPARE(result.diagnostics.back().code, DbcDiagnosticCode::InvalidAttribute);
    -
    319 QCOMPARE(result.diagnostics.back().lineNumber, std::size_t(2 + std::count(statement.begin(), statement.end(), '\n')));
    -
    320 QVERIFY_THROWS_EXCEPTION(std::runtime_error, DbcParser().ParseFile(fixture.path.string()));
    -
    321 }
    -
    322}
    -
    323
    -
    324void DbcParserTest::attributeNumbers() {
    -
    325 ApplicationLocaleGuard guard;
    -
    326 std::locale::global(std::locale(std::locale::classic(), new CommaDecimalPunctuation));
    -
    327 Fixture fixture;
    -
    328 fixture.Write("BA_DEF_ \"I\" INT -9223372036854775808 +9223372036854775807;\n"
    -
    329 "BA_DEF_DEF_ \"I\" -9223372036854775808;\n"
    -
    330 "BA_ \"I\" +9223372036854775807;\n"
    -
    331 "BA_DEF_ \"H\" HEX -10 +10;\n"
    -
    332 "BA_DEF_DEF_ \"H\" +0;\n"
    -
    333 "BA_ \"H\" -10;\n"
    -
    334 "BA_DEF_ \"F\" FLOAT -1.25e+2 +2.5E2;\n"
    -
    335 "BA_DEF_DEF_ \"F\" +.5;\n"
    -
    336 "BA_ \"F\" -1.25e+2;\n"
    -
    337 "BA_DEF_ BU_ \"NodeDefault\" INT 0 10;\n"
    -
    338 "BA_DEF_DEF_ \"NodeDefault\" 5;\n"
    -
    339 "BA_DEF_ EV_ \"EnvironmentDefault\" STRING;\n"
    -
    340 "BA_DEF_DEF_ \"EnvironmentDefault\" \"default\";\n");
    -
    341 const DbcParseResult result = DbcParser().ParseFileWithDiagnostics(fixture.path.string());
    -
    342 QVERIFY(!result.HasErrors());
    -
    343 QVERIFY(result.diagnostics.empty());
    -
    344 QCOMPARE(std::get<std::int64_t>(*result.database.attributeDefinitions.at("I").defaultValue), std::numeric_limits<std::int64_t>::min());
    -
    345 QCOMPARE(std::get<std::int64_t>(result.database.attributes.at("I")), std::numeric_limits<std::int64_t>::max());
    -
    346 QCOMPARE(std::get<std::int64_t>(*result.database.attributeDefinitions.at("H").defaultValue), std::int64_t(0));
    -
    347 QCOMPARE(std::get<std::int64_t>(result.database.attributes.at("H")), std::int64_t(-10));
    -
    348 QCOMPARE(std::get<double>(*result.database.attributeDefinitions.at("F").defaultValue), 0.5);
    -
    349 QCOMPARE(std::get<double>(result.database.attributes.at("F")), -125.0);
    -
    350 QCOMPARE(result.database.attributes.size(), std::size_t(3));
    -
    351 QCOMPARE(std::get<std::int64_t>(*result.database.attributeDefinitions.at("NodeDefault").defaultValue), std::int64_t(5));
    -
    352 QCOMPARE(std::get<std::string>(*result.database.attributeDefinitions.at("EnvironmentDefault").defaultValue), std::string("default"));
    -
    353}
    -
    354
    -
    355void DbcParserTest::invalidAttributeNumbers_data() {
    -
    356 QTest::addColumn<QString>("type");
    -
    357 QTest::addColumn<QString>("number");
    -
    358 const char *types[] = {"INT", "HEX", "FLOAT"};
    -
    359 const char *invalid[] = {"+", "-", "++1", "+-1", "--1", "1x", "0x10",
    -
    360 "nan", "inf", "-inf", "1e9999", "1e-9999"};
    -
    361 for (const char *type: types) {
    -
    362 for (const char *number: invalid) {
    -
    363 const std::string name = std::string(type) + '-' + number;
    -
    364 QTest::newRow(name.c_str()) << QString(type) << QString(number);
    -
    365 }
    -
    366 }
    -
    367 QTest::newRow("integer-overflow") << QString("INT") << QString("9223372036854775808");
    -
    368 QTest::newRow("integer-underflow") << QString("INT") << QString("-9223372036854775809");
    -
    369 QTest::newRow("integer-fraction") << QString("INT") << QString("1.5");
    -
    370}
    -
    371
    -
    372void DbcParserTest::invalidAttributeNumbers() {
    -
    373 QFETCH(QString, type);
    -
    374 QFETCH(QString, number);
    -
    375 const std::string kind = type.toStdString();
    -
    376 const std::string text = number.toStdString();
    -
    377 const std::string definition = "BA_DEF_ \"A\" " + kind + " -10 10;\n";
    -
    378 const std::string statements[] = {
    -
    379 "BA_DEF_ \"A\" " + kind + ' ' + text + " 10;\n",
    -
    380 "BA_DEF_ \"A\" " + kind + " -10 " + text + ";\n",
    -
    381 definition + "BA_DEF_DEF_ \"A\" " + text + ";\n",
    -
    382 definition + "BA_ \"A\" " + text + ";\n"};
    -
    383 Fixture fixture;
    -
    384 for (const std::string &statement: statements) {
    -
    385 fixture.Write(statement);
    -
    386 const DbcParseResult result = DbcParser().ParseFileWithDiagnostics(fixture.path.string());
    -
    387 QVERIFY2(result.HasErrors(), statement.c_str());
    -
    388 QVERIFY(result.database.attributeDefinitions.empty());
    -
    389 QCOMPARE(result.diagnostics.back().code, DbcDiagnosticCode::InvalidAttribute);
    -
    390 QCOMPARE(result.diagnostics.back().lineNumber,
    -
    391 static_cast<std::size_t>(std::count(statement.begin(), statement.end(), '\n')));
    -
    392 QVERIFY_THROWS_EXCEPTION(std::runtime_error, DbcParser().ParseFile(fixture.path.string()));
    -
    393 }
    -
    394}
    -
    395
    -
    396void DbcParserTest::attributeWarnings() {
    -
    397 Fixture fixture;
    -
    398 fixture.Write("BA_DEF_ BO_ \"VFrameFormat\" ENUM \"StandardCAN\",\"ExtendedCAN\",\"J1939PG\",\"StandardCAN_FD\";\n"
    -
    399 "BA_DEF_DEF_ \"VFrameFormat\" \"StandardCAN_FD\";\n"
    -
    400 "BA_ \"VFrameFormat\" BO_ 1 2;\n"
    -
    401 "BA_ \"VFrameFormat\" BO_ 2147483649 0;\n"
    -
    402 "BO_ 1 F: 0 ECU\nBO_ 2147483649 E: 0 ECU\nBO_ 2 Default: 0 ECU\n");
    -
    403 const DbcParseResult result = DbcParser().ParseFileWithDiagnostics(fixture.path.string());
    -
    404 QVERIFY(!result.HasErrors());
    -
    405 QCOMPARE(result.diagnostics.size(), std::size_t(3));
    -
    406 const std::size_t lines[] = {3U, 4U, 2U};
    -
    407 for (std::size_t index = 0; index < 3U; ++index) {
    -
    408 QCOMPARE(result.diagnostics[index].code, DbcDiagnosticCode::UnsupportedAttribute);
    -
    409 QCOMPARE(result.diagnostics[index].severity, DbcDiagnosticSeverity::Warning);
    -
    410 QCOMPARE(result.diagnostics[index].lineNumber, lines[index]);
    -
    411 QCOMPARE(result.diagnostics[index].filePath, fixture.path.string());
    -
    412 }
    -
    413 QCOMPARE(result.database.frames[0].frameFormat.value(), std::string("J1939PG"));
    -
    414 QVERIFY(!result.database.frames[0].hasPgn);
    -
    415 QVERIFY(result.database.frames[1].isExtended);
    -
    416}
    -
    417
    -
    418void DbcParserTest::diagnostics() {
    -
    419 Fixture fixture;
    -
    420 fixture.Write("VERSION \"test\"\n"
    -
    421 "NS_ :\n"
    -
    422 " CM_\n"
    -
    423 " BA_\n"
    -
    424 " SIG_VALTYPE_\n"
    -
    425 " SG_MUL_VAL_\n\n"
    -
    426 "BS_:\n"
    -
    427 "BU_: ECU\n"
    -
    428 "BO_ 1 Frame: 1 ECU\n"
    -
    429 "SG_ Value : 0|8@1+ (1,0) [0|255] \"\" ECU\n"
    -
    430 "SIG_VALTYPE_ 1 Value : 1;\n"
    -
    431 "BA_REL_ \"SignalType\" SG_ 1 Value 1;\n"
    -
    432 "VENDOR_EXTENSION_ data;\n");
    -
    433 const DbcParseResult result = DbcParser().ParseFileWithDiagnostics(fixture.path.string());
    -
    434 CHECK(!result.HasErrors() && result.database.frames.size() == 1U,
    -
    435 "Warnings preserve the supported database");
    -
    436 QCOMPARE(result.diagnostics.size(), std::size_t(3U));
    -
    437 for (std::size_t index = 0U; index < result.diagnostics.size(); ++index) {
    -
    438 const DbcDiagnostic &diagnostic = result.diagnostics[index];
    -
    439 CHECK(diagnostic.severity == DbcDiagnosticSeverity::Warning &&
    -
    440 diagnostic.code == DbcDiagnosticCode::UnsupportedStatement,
    -
    441 "Unsupported statements have structured warning severity and code");
    -
    442 QCOMPARE(diagnostic.filePath, fixture.path.string());
    -
    443 QCOMPARE(diagnostic.lineNumber, 12U + index);
    -
    444 CHECK(diagnostic.message.find("ignored") != std::string::npos,
    -
    445 "Warning explains unsupported syntax");
    -
    446 }
    -
    447 CHECK(DbcParser().ParseFile(fixture.path.string()).frames.size() == 1U,
    -
    448 "Legacy API accepts files with warnings");
    -
    449 fixture.Write("VERSION \"\"\nNS_:\nSIG_VALTYPE_\n\tBA_\n BS_ :\n BU_ : ECU\n");
    -
    450 const DbcParseResult headers = DbcParser().ParseFileWithDiagnostics(fixture.path.string());
    -
    451 CHECK(headers.diagnostics.empty(), "Common header variants stay quiet");
    -
    452 fixture.Write("NS_ :\n BA_\n BO_\n");
    -
    453 CHECK(DbcParser().ParseFileWithDiagnostics(fixture.path.string()).HasErrors(),
    -
    454 "Namespace handling does not swallow a malformed frame");
    -
    455}
    -
    456
    -
    457void DbcParserTest::diagnosticErrors() {
    -
    458 Fixture fixture;
    -
    459 struct ErrorCase {
    -
    460 const char *text;
    -
    461 DbcDiagnosticCode code;
    -
    462 std::size_t line;
    -
    463 };
    -
    464 const ErrorCase cases[] = {
    -
    465 {"UNKNOWN_ data;\n\nBO_ invalid Frame: 1 ECU\n",
    -
    466 DbcDiagnosticCode::InvalidStatement, 3U},
    -
    467 {"BO_ 1 Frame: 1 ECU\nSG_MUL_VAL_ 1 Value Mode 1-2;\n",
    -
    468 DbcDiagnosticCode::InvalidMultiplexing, 2U},
    -
    469 {"BO_ 1 Frame: 1 ECU\nVAL_ 1 Missing 0 \"Off\";\n",
    -
    470 DbcDiagnosticCode::InvalidValueDescription, 2U},
    -
    471 {"BO_ 1 Frame: 1 ECU\nSG_ Value m1 : 0|8@1+ (1,0) [0|255] \"\" ECU\n",
    -
    472 DbcDiagnosticCode::InvalidMultiplexing, 2U},
    -
    473 {"BO_ 1 Frame: 1 ECU\nSG_ Mode M : 0|8@1- (1,0) [-128|127] \"\" ECU\n",
    +
    102 void unsupportedSignalValueTypes_data();
    +
    103 void unsupportedSignalValueTypes();
    +
    104 void diagnosticNamespaceBoundaries();
    +
    105 void diagnosticFormatting();
    +
    106 void multiplexing();
    +
    107 void multiplexingBoundaries();
    +
    108 void extendedMultiplexing();
    +
    109 void extendedMultiplexingErrors();
    +
    110 void validFrames();
    +
    111 void invalidFrames();
    +
    112 void duplicateFrames();
    +
    113 void duplicateSignals();
    +
    114 void distinctDefinitions();
    +
    115 void comments();
    +
    116 void invalidComments_data();
    +
    117 void invalidComments();
    +
    118 void integration();
    +
    119 void invalidSignals();
    +
    120 void validSignals();
    +
    121 void longSignalDefinitions_data();
    +
    122 void longSignalDefinitions();
    +
    123 void physicalNumbersCppLocale();
    +
    124 void physicalNumbersCLocale();
    +
    125 void dispatch();
    +
    126 void independentSessions();
    +
    127 void valueDescriptions();
    +
    128 void invalidValueDescriptions();
    +
    129};
    +
    130
    +
    131void DbcParserTest::attributes() {
    +
    132 Fixture fixture;
    +
    133 fixture.Write(
    +
    134 "BA_ \"GenMsgCycleTime\" BO_ 1 25;\n"
    +
    135 "BA_ \"VFrameFormat\" BO_ 2147483649 1;\n"
    +
    136 "BA_ \"Label\" SG_ 1 Value \"a\\\"b\\\\c\";\n"
    +
    137 "BA_ \"NodeLabel\" BU_ ECU \"node\";\n"
    +
    138 "BA_ \"Environment\" EV_ Env 2;\n"
    +
    139 "BA_DEF_DEF_ \"GenMsgCycleTime\" 100;\n"
    +
    140 "BA_DEF_DEF_ \"VFrameFormat\" \"StandardCAN\";\n"
    +
    141 "BA_DEF_DEF_ \"Label\" \"default\";\n"
    +
    142 "BA_DEF_DEF_ \"BusLabel\" \"CAN\";\n"
    +
    143 "BA_DEF_DEF_ \"Ratio\" 1.5e-1;\n"
    +
    144 "BA_DEF_ BO_ \"GenMsgCycleTime\" INT 0 10000;\n"
    +
    145 "BA_DEF_ BO_ \"VFrameFormat\" ENUM \"StandardCAN\",\"ExtendedCAN\",\"StandardCAN_FD\";\n"
    +
    146 "BA_DEF_ SG_ \"Label\" STRING;\n"
    +
    147 "BA_DEF_ \"BusLabel\" STRING;\n"
    +
    148 "BA_DEF_ BO_ \"Ratio\" FLOAT -1 1;\n"
    +
    149 "BA_DEF_ BU_ \"NodeLabel\" STRING;\n"
    +
    150 "BA_DEF_ EV_ \"Environment\" HEX 0 255;\n"
    +
    151 "BO_ 1 Standard: 1 ECU\n"
    +
    152 "SG_ Value : 0|8@1+ (1,0) [0|255] \"\" ECU\n"
    +
    153 "BO_ 2147483649 Extended: 1 ECU\n"
    +
    154 "SG_ Value : 0|8@1+ (1,0) [0|255] \"\" ECU\n");
    +
    155 const DbcParseResult result = DbcParser().ParseFileWithDiagnostics(fixture.path.string());
    +
    156 QVERIFY(!result.HasErrors());
    +
    157 QVERIFY(result.diagnostics.empty());
    +
    158 const DbcDatabase &database = result.database;
    +
    159 QCOMPARE(database.attributeDefinitions.size(), std::size_t(7));
    +
    160 QCOMPARE(database.attributeAssignments.size(), std::size_t(5));
    +
    161 QCOMPARE(std::get<std::string>(database.attributes.at("BusLabel")), std::string("CAN"));
    +
    162 QCOMPARE(database.frames[0].cycleTimeMs.value(), 25U);
    +
    163 QCOMPARE(database.frames[1].cycleTimeMs.value(), 100U);
    +
    164 QCOMPARE(database.frames[1].frameFormat.value(), std::string("ExtendedCAN"));
    +
    165 QVERIFY(!database.frames[1].hasPgn);
    +
    166 QCOMPARE(std::get<double>(database.frames[0].attributes.at("Ratio")), 0.15);
    +
    167 QCOMPARE(std::get<std::string>(database.frames[0].signals[0].attributes.at("Label")), std::string("a\"b\\c"));
    +
    168 QCOMPARE(std::get<std::string>(database.frames[1].signals[0].attributes.at("Label")), std::string("default"));
    +
    169 const DecodeDatabase runtime = DbcDecodeBuilder().Build(database);
    +
    170 QCOMPARE(runtime.GetFrames()[0].cycleTimeMs.value(), 25U);
    +
    171 QCOMPARE(runtime.GetFrames()[1].frameFormat.value(), std::string("ExtendedCAN"));
    +
    172 QCOMPARE(runtime.GetFrames()[0].signals[0].attributes, database.frames[0].signals[0].attributes);
    +
    173 const std::unique_ptr<TreeNode> tree = DbcTreeBuilder().Build(database);
    +
    174 QCOMPARE(tree->GetChild(0)->GetFrame()->cycleTimeMs.value(), 25U);
    +
    175 QCOMPARE(tree->GetChild(0)->GetChild(0)->GetSignal()->attributes, database.frames[0].signals[0].attributes);
    +
    176 fixture.Write("BA_DEF_ BO_ \"Period\" INT 0 10;\nBA_DEF_DEF_ \"Period\" 1;\n"
    +
    177 "BA_ \"Period\" BO_ 1 2;\nBA_ \"Period\" BO_ 1 3;\nBO_ 1 F: 0 ECU\n");
    +
    178 const DbcDatabase repeated = DbcParser().ParseFile(fixture.path.string());
    +
    179 QCOMPARE(std::get<std::int64_t>(repeated.frames[0].attributes.at("Period")), std::int64_t(3));
    +
    180
    +
    181 fixture.Write("BA_DEF_ BO_ \"GenMsgCycleTime\" INT 0 4294967295;\n"
    +
    182 "BA_DEF_DEF_ \"GenMsgCycleTime\" 0;\n"
    +
    183 "BA_ \"GenMsgCycleTime\" BO_ 1 4294967295;\n"
    +
    184 "BA_DEF_ \"Wide\" INT -9223372036854775808 9223372036854775807;\n"
    +
    185 "BA_DEF_DEF_ \"Wide\" -9223372036854775808;\n"
    +
    186 "BA_DEF_ SG_ \"State\" ENUM \"off\",\"on\";\n"
    +
    187 "BA_DEF_DEF_ \"State\" 1;\n"
    +
    188 "BO_ 1 F: 0 ECU\nBO_ 2 Empty: 0 ECU\n");
    +
    189 const DbcDatabase boundaries = DbcParser().ParseFile(fixture.path.string());
    +
    190 QCOMPARE(boundaries.frames[0].cycleTimeMs.value(), std::numeric_limits<std::uint32_t>::max());
    +
    191 QCOMPARE(boundaries.frames[1].cycleTimeMs.value(), 0U);
    +
    192 QVERIFY(!boundaries.frames[0].frameFormat);
    +
    193 QCOMPARE(std::get<std::int64_t>(boundaries.attributes.at("Wide")), std::numeric_limits<std::int64_t>::min());
    +
    194 QCOMPARE(std::get<std::int64_t>(*boundaries.attributeDefinitions.at("State").defaultValue), std::int64_t(1));
    +
    195}
    +
    196
    +
    197void DbcParserTest::multilineAttributes() {
    +
    198 Fixture fixture;
    +
    199 fixture.Write(R"DBC(BA_
    +
    200 "GenMsgCycleTime" BO_ 1
    +
    201 25;
    +
    202BA_DEF_ BO_ "VFrameFormat" ENUM
    +
    203 "StandardCAN",
    +
    204 "StandardCAN_FD";
    +
    205BA_DEF_DEF_
    +
    206 "VFrameFormat"
    +
    207 "StandardCAN_FD";
    +
    208BA_DEF_ BO_ "GenMsgCycleTime"
    +
    209 INT 0 100;
    +
    210BA_DEF_DEF_ "GenMsgCycleTime"
    +
    211 50;
    +
    212BA_DEF_ SG_ "Label" STRING;
    +
    213BA_ "Label" SG_ 1 Value
    +
    214 "first; \"quoted\"
    +
    215second\\path";
    +
    216BA_ "VFrameFormat" BO_ 1
    +
    217 0;
    +
    218BO_ 1 F: 1 ECU
    +
    219SG_ Value : 0|8@1+ (1,0) [0|255] "" ECU
    +
    220BO_ 2 Default: 0 ECU
    +
    221)DBC");
    +
    222 const DbcParseResult result = DbcParser().ParseFileWithDiagnostics(fixture.path.string());
    +
    223 QVERIFY(!result.HasErrors());
    +
    224 QCOMPARE(result.database.frames.size(), std::size_t(2));
    +
    225 QCOMPARE(result.database.frames[0].cycleTimeMs.value(), 25U);
    +
    226 QCOMPARE(result.database.frames[1].cycleTimeMs.value(), 50U);
    +
    227 QCOMPARE(result.database.frames[0].frameFormat.value(), std::string("StandardCAN"));
    +
    228 QCOMPARE(result.database.frames[1].frameFormat.value(), std::string("StandardCAN_FD"));
    +
    229 QCOMPARE(std::get<std::string>(result.database.frames[0].signals[0].attributes.at("Label")),
    +
    230 std::string("first; \"quoted\"\nsecond\\path"));
    +
    231 QCOMPARE(result.diagnostics.size(), std::size_t(1));
    +
    232 QCOMPARE(result.diagnostics[0].code, DbcDiagnosticCode::UnsupportedAttribute);
    +
    233 QCOMPARE(result.diagnostics[0].lineNumber, std::size_t(7));
    +
    234
    +
    235 fixture.Write("BA_DEF_ BO_ \"VFrameFormat\" ENUM\r\n \"StandardCAN\",\r\n \"ExtendedCAN_FD\";\r\n"
    +
    236 "BA_\r\n \"VFrameFormat\" BO_ 1\r\n 1;\r\nBO_ 1 F: 0 ECU\r\n");
    +
    237 const DbcParseResult explicitFormat = DbcParser().ParseFileWithDiagnostics(fixture.path.string());
    +
    238 QVERIFY(!explicitFormat.HasErrors());
    +
    239 QCOMPARE(explicitFormat.diagnostics.size(), std::size_t(1));
    +
    240 QCOMPARE(explicitFormat.diagnostics[0].code, DbcDiagnosticCode::UnsupportedAttribute);
    +
    241 QCOMPARE(explicitFormat.diagnostics[0].lineNumber, std::size_t(4));
    +
    242 QCOMPARE(explicitFormat.database.frames[0].frameFormat.value(), std::string("ExtendedCAN_FD"));
    +
    243
    +
    244 fixture.Write("BA_DEF_ \"Text\" STRING;\n BA_ \"Text\" \"first \nsecond\";\n"
    +
    245 "UnsupportedStatement\n");
    +
    246 const DbcParseResult whitespace = DbcParser().ParseFileWithDiagnostics(fixture.path.string());
    +
    247 QVERIFY(!whitespace.HasErrors());
    +
    248 QCOMPARE(std::get<std::string>(whitespace.database.attributes.at("Text")), std::string("first \nsecond"));
    +
    249 QCOMPARE(whitespace.diagnostics.size(), std::size_t(1));
    +
    250 QCOMPARE(whitespace.diagnostics[0].code, DbcDiagnosticCode::UnsupportedStatement);
    +
    251 QCOMPARE(whitespace.diagnostics[0].lineNumber, std::size_t(4));
    +
    252}
    +
    253
    +
    254void DbcParserTest::invalidMultilineAttributes() {
    +
    255 Fixture fixture;
    +
    256 const std::vector<std::string> invalid = {
    +
    257 "BA_DEF_\n BO_ \"A\" INT\n 10 0;\n",
    +
    258 "BA_DEF_ BO_ \"A\" ENUM\n \"one\",\n;\n",
    +
    259 "BA_DEF_ BO_ \"A\" STRING\n",
    +
    260 "BA_DEF_ BO_ \"A\" ENUM\n \"unterminated;\n",
    +
    261 "BA_DEF_DEF_\n \"Unknown\"\n 0;\n",
    +
    262 "BA_\n \"Unknown\" BO_ 1\n 0;\n",
    +
    263 "BA_DEF_ BO_ \"A\" STRING\n ; trailing\n"};
    +
    264 for (const std::string &statement: invalid) {
    +
    265 fixture.Write("BO_ 1 F: 0 ECU\n" + statement);
    +
    266 const DbcParseResult result = DbcParser().ParseFileWithDiagnostics(fixture.path.string());
    +
    267 QVERIFY2(result.HasErrors(), statement.c_str());
    +
    268 QVERIFY(result.database.frames.empty());
    +
    269 QCOMPARE(result.diagnostics.back().code, DbcDiagnosticCode::InvalidAttribute);
    +
    270 QCOMPARE(result.diagnostics.back().lineNumber, std::size_t(2));
    +
    271 QVERIFY_THROWS_EXCEPTION(std::runtime_error, DbcParser().ParseFile(fixture.path.string()));
    +
    272 }
    +
    273}
    +
    274
    +
    275void DbcParserTest::invalidAttributes() {
    +
    276 Fixture fixture;
    +
    277 const std::vector<std::string> invalid = {
    +
    278 "BA_DEF_ BO_ \"A\"STRING;",
    +
    279 "BA_DEF_ BO_\"A\" STRING;",
    +
    280 "BA_DEF_ BO_ \"A\" \"STRING\";",
    +
    281 "BA_DEF_ BO_ A STRING;",
    +
    282 "BA_DEF_ ZZ_ \"A\" STRING;",
    +
    283 "BA_DEF_ BO_ \"A\" BLOB;",
    +
    284 "BA_DEF_ BO_ \"A\" FLOAT 2 1;",
    +
    285 "BA_DEF_ BO_ \"A\" STRING;\nBA_ \"A\" BO_ 1 0;",
    +
    286 "BA_DEF_ BO_ \"A\" FLOAT 0 1;\nBA_ \"A\" BO_ 1 2;",
    +
    287 "BA_DEF_ BO_ \"A\" INT 0 1;\nBA_ \"A\" BO_ -1 0;",
    +
    288 "BA_DEF_ BO_ \"A\" INT 0 1;\nBA_ \"A\" BO_ 4294967296 0;",
    +
    289 "BA_DEF_ BO_ \"A\" INT 0 1;\nBA_ \"A\" BO_ 536870912 0;",
    +
    290 "BA_DEF_ BO_ \"A\" INT 2 1;",
    +
    291 "BA_DEF_ BO_ \"A\" FLOAT nan 1;",
    +
    292 "BA_DEF_ BO_ \"A\" FLOAT 0 inf;",
    +
    293 "BA_DEF_ BO_ \"A\" INT 0 9223372036854775808;",
    +
    294 "BA_DEF_ BO_ \"A\" STRING; trailing",
    +
    295 "BA_DEF_ BO_ \"A\" ENUM;",
    +
    296 "BA_DEF_ BO_ \"A\" ENUM \"one\",;",
    +
    297 "BA_DEF_ BO_ \"A\" ENUM \"one\",\"one\";",
    +
    298 "BA_DEF_ BO_ \"VFrameFormat\" STRING;",
    +
    299 "BA_DEF_ SG_ \"GenMsgCycleTime\" INT 0 10;",
    +
    300 "BA_ \"Unknown\" BO_ 1 0;",
    +
    301 "BA_DEF_DEF_ \"Unknown\" 0;",
    +
    302 "BA_DEF_ BO_ \"A\" INT 0 1;\nBA_ \"A\" BO_ 1 2;",
    +
    303 "BA_DEF_ BO_ \"A\" INT 0 1;\nBA_ \"A\" BO_ 1 \"0\";",
    +
    304 "BA_DEF_ BO_ \"A\" INT 0 1;\nBA_ \"A\" SG_ 1 S 0;",
    +
    305 "BA_DEF_ BO_ \"A\" INT 0 1;\nBA_ \"A\" BO_ 2 0;",
    +
    306 "BA_DEF_ SG_ \"A\" INT 0 1;\nBA_ \"A\" SG_ 1 Missing 0;",
    +
    307 "BA_DEF_ BO_ \"A\" INT 0 1;\nBA_DEF_ BO_ \"A\" INT 0 1;",
    +
    308 "BA_DEF_ BO_ \"A\" INT 0 1;\nBA_DEF_DEF_ \"A\" 0;\nBA_DEF_DEF_ \"A\" 1;",
    +
    309 "BA_DEF_ BO_ \"A\" STRING;\nBA_ \"A\" BO_ 1 \"bad\\q\";",
    +
    310 "BA_DEF_ BO_ \"A\" STRING;\nBA_ \"A\" BO_ 1 \"unterminated;",
    +
    311 "BA_DEF_ BO_ \"A\" ENUM \"one\";\nBA_DEF_DEF_ \"A\" \"missing\";",
    +
    312 "BA_DEF_ BO_ \"A\" ENUM \"one\";\nBA_ \"A\" BO_ 1 1;",
    +
    313 "BA_DEF_ BO_ \"GenMsgCycleTime\" INT -1 4294967296;\nBA_DEF_DEF_ \"GenMsgCycleTime\" -1;",
    +
    314 "BA_DEF_ BO_ \"GenMsgCycleTime\" INT 0 4294967296;\nBA_ \"GenMsgCycleTime\" BO_ 1 4294967296;"};
    +
    315 for (const std::string &statement: invalid) {
    +
    316 fixture.Write("BO_ 1 F: 0 ECU\n" + statement + '\n');
    +
    317 const DbcParseResult result = DbcParser().ParseFileWithDiagnostics(fixture.path.string());
    +
    318 QVERIFY2(result.HasErrors(), statement.c_str());
    +
    319 QVERIFY(result.database.frames.empty());
    +
    320 QCOMPARE(result.diagnostics.back().code, DbcDiagnosticCode::InvalidAttribute);
    +
    321 QCOMPARE(result.diagnostics.back().lineNumber, std::size_t(2 + std::count(statement.begin(), statement.end(), '\n')));
    +
    322 QVERIFY_THROWS_EXCEPTION(std::runtime_error, DbcParser().ParseFile(fixture.path.string()));
    +
    323 }
    +
    324}
    +
    325
    +
    326void DbcParserTest::attributeNumbers() {
    +
    327 ApplicationLocaleGuard guard;
    +
    328 std::locale::global(std::locale(std::locale::classic(), new CommaDecimalPunctuation));
    +
    329 Fixture fixture;
    +
    330 fixture.Write("BA_DEF_ \"I\" INT -9223372036854775808 +9223372036854775807;\n"
    +
    331 "BA_DEF_DEF_ \"I\" -9223372036854775808;\n"
    +
    332 "BA_ \"I\" +9223372036854775807;\n"
    +
    333 "BA_DEF_ \"H\" HEX -10 +10;\n"
    +
    334 "BA_DEF_DEF_ \"H\" +0;\n"
    +
    335 "BA_ \"H\" -10;\n"
    +
    336 "BA_DEF_ \"F\" FLOAT -1.25e+2 +2.5E2;\n"
    +
    337 "BA_DEF_DEF_ \"F\" +.5;\n"
    +
    338 "BA_ \"F\" -1.25e+2;\n"
    +
    339 "BA_DEF_ BU_ \"NodeDefault\" INT 0 10;\n"
    +
    340 "BA_DEF_DEF_ \"NodeDefault\" 5;\n"
    +
    341 "BA_DEF_ EV_ \"EnvironmentDefault\" STRING;\n"
    +
    342 "BA_DEF_DEF_ \"EnvironmentDefault\" \"default\";\n");
    +
    343 const DbcParseResult result = DbcParser().ParseFileWithDiagnostics(fixture.path.string());
    +
    344 QVERIFY(!result.HasErrors());
    +
    345 QVERIFY(result.diagnostics.empty());
    +
    346 QCOMPARE(std::get<std::int64_t>(*result.database.attributeDefinitions.at("I").defaultValue), std::numeric_limits<std::int64_t>::min());
    +
    347 QCOMPARE(std::get<std::int64_t>(result.database.attributes.at("I")), std::numeric_limits<std::int64_t>::max());
    +
    348 QCOMPARE(std::get<std::int64_t>(*result.database.attributeDefinitions.at("H").defaultValue), std::int64_t(0));
    +
    349 QCOMPARE(std::get<std::int64_t>(result.database.attributes.at("H")), std::int64_t(-10));
    +
    350 QCOMPARE(std::get<double>(*result.database.attributeDefinitions.at("F").defaultValue), 0.5);
    +
    351 QCOMPARE(std::get<double>(result.database.attributes.at("F")), -125.0);
    +
    352 QCOMPARE(result.database.attributes.size(), std::size_t(3));
    +
    353 QCOMPARE(std::get<std::int64_t>(*result.database.attributeDefinitions.at("NodeDefault").defaultValue), std::int64_t(5));
    +
    354 QCOMPARE(std::get<std::string>(*result.database.attributeDefinitions.at("EnvironmentDefault").defaultValue), std::string("default"));
    +
    355}
    +
    356
    +
    357void DbcParserTest::invalidAttributeNumbers_data() {
    +
    358 QTest::addColumn<QString>("type");
    +
    359 QTest::addColumn<QString>("number");
    +
    360 const char *types[] = {"INT", "HEX", "FLOAT"};
    +
    361 const char *invalid[] = {"+", "-", "++1", "+-1", "--1", "1x", "0x10",
    +
    362 "nan", "inf", "-inf", "1e9999", "1e-9999"};
    +
    363 for (const char *type: types) {
    +
    364 for (const char *number: invalid) {
    +
    365 const std::string name = std::string(type) + '-' + number;
    +
    366 QTest::newRow(name.c_str()) << QString(type) << QString(number);
    +
    367 }
    +
    368 }
    +
    369 QTest::newRow("integer-overflow") << QString("INT") << QString("9223372036854775808");
    +
    370 QTest::newRow("integer-underflow") << QString("INT") << QString("-9223372036854775809");
    +
    371 QTest::newRow("integer-fraction") << QString("INT") << QString("1.5");
    +
    372}
    +
    373
    +
    374void DbcParserTest::invalidAttributeNumbers() {
    +
    375 QFETCH(QString, type);
    +
    376 QFETCH(QString, number);
    +
    377 const std::string kind = type.toStdString();
    +
    378 const std::string text = number.toStdString();
    +
    379 const std::string definition = "BA_DEF_ \"A\" " + kind + " -10 10;\n";
    +
    380 const std::string statements[] = {
    +
    381 "BA_DEF_ \"A\" " + kind + ' ' + text + " 10;\n",
    +
    382 "BA_DEF_ \"A\" " + kind + " -10 " + text + ";\n",
    +
    383 definition + "BA_DEF_DEF_ \"A\" " + text + ";\n",
    +
    384 definition + "BA_ \"A\" " + text + ";\n"};
    +
    385 Fixture fixture;
    +
    386 for (const std::string &statement: statements) {
    +
    387 fixture.Write(statement);
    +
    388 const DbcParseResult result = DbcParser().ParseFileWithDiagnostics(fixture.path.string());
    +
    389 QVERIFY2(result.HasErrors(), statement.c_str());
    +
    390 QVERIFY(result.database.attributeDefinitions.empty());
    +
    391 QCOMPARE(result.diagnostics.back().code, DbcDiagnosticCode::InvalidAttribute);
    +
    392 QCOMPARE(result.diagnostics.back().lineNumber,
    +
    393 static_cast<std::size_t>(std::count(statement.begin(), statement.end(), '\n')));
    +
    394 QVERIFY_THROWS_EXCEPTION(std::runtime_error, DbcParser().ParseFile(fixture.path.string()));
    +
    395 }
    +
    396}
    +
    397
    +
    398void DbcParserTest::attributeWarnings() {
    +
    399 Fixture fixture;
    +
    400 fixture.Write("BA_DEF_ BO_ \"VFrameFormat\" ENUM \"StandardCAN\",\"ExtendedCAN\",\"J1939PG\",\"StandardCAN_FD\";\n"
    +
    401 "BA_DEF_DEF_ \"VFrameFormat\" \"StandardCAN_FD\";\n"
    +
    402 "BA_ \"VFrameFormat\" BO_ 1 2;\n"
    +
    403 "BA_ \"VFrameFormat\" BO_ 2147483649 0;\n"
    +
    404 "BO_ 1 F: 0 ECU\nBO_ 2147483649 E: 0 ECU\nBO_ 2 Default: 0 ECU\n");
    +
    405 const DbcParseResult result = DbcParser().ParseFileWithDiagnostics(fixture.path.string());
    +
    406 QVERIFY(!result.HasErrors());
    +
    407 QCOMPARE(result.diagnostics.size(), std::size_t(3));
    +
    408 const std::size_t lines[] = {3U, 4U, 2U};
    +
    409 for (std::size_t index = 0; index < 3U; ++index) {
    +
    410 QCOMPARE(result.diagnostics[index].code, DbcDiagnosticCode::UnsupportedAttribute);
    +
    411 QCOMPARE(result.diagnostics[index].severity, DbcDiagnosticSeverity::Warning);
    +
    412 QCOMPARE(result.diagnostics[index].lineNumber, lines[index]);
    +
    413 QCOMPARE(result.diagnostics[index].filePath, fixture.path.string());
    +
    414 }
    +
    415 QCOMPARE(result.database.frames[0].frameFormat.value(), std::string("J1939PG"));
    +
    416 QVERIFY(!result.database.frames[0].hasPgn);
    +
    417 QVERIFY(result.database.frames[1].isExtended);
    +
    418}
    +
    419
    +
    420void DbcParserTest::diagnostics() {
    +
    421 Fixture fixture;
    +
    422 fixture.Write("VERSION \"test\"\n"
    +
    423 "NS_ :\n"
    +
    424 " CM_\n"
    +
    425 " BA_\n"
    +
    426 " SIG_VALTYPE_\n"
    +
    427 " SG_MUL_VAL_\n\n"
    +
    428 "BS_:\n"
    +
    429 "BU_: ECU\n"
    +
    430 "BO_ 1 Frame: 1 ECU\n"
    +
    431 "SG_ Value : 0|8@1+ (1,0) [0|255] \"\" ECU\n"
    +
    432 "SIG_TYPE_REF_ 1 Value : Custom;\n"
    +
    433 "BA_REL_ \"SignalType\" SG_ 1 Value 1;\n"
    +
    434 "VENDOR_EXTENSION_ data;\n");
    +
    435 const DbcParseResult result = DbcParser().ParseFileWithDiagnostics(fixture.path.string());
    +
    436 CHECK(!result.HasErrors() && result.database.frames.size() == 1U,
    +
    437 "Warnings preserve the supported database");
    +
    438 QCOMPARE(result.diagnostics.size(), std::size_t(3U));
    +
    439 for (std::size_t index = 0U; index < result.diagnostics.size(); ++index) {
    +
    440 const DbcDiagnostic &diagnostic = result.diagnostics[index];
    +
    441 CHECK(diagnostic.severity == DbcDiagnosticSeverity::Warning &&
    +
    442 diagnostic.code == DbcDiagnosticCode::UnsupportedStatement,
    +
    443 "Unsupported statements have structured warning severity and code");
    +
    444 QCOMPARE(diagnostic.filePath, fixture.path.string());
    +
    445 QCOMPARE(diagnostic.lineNumber, 12U + index);
    +
    446 CHECK(diagnostic.message.find("ignored") != std::string::npos,
    +
    447 "Warning explains unsupported syntax");
    +
    448 }
    +
    449 CHECK(DbcParser().ParseFile(fixture.path.string()).frames.size() == 1U,
    +
    450 "Legacy API accepts files with warnings");
    +
    451 fixture.Write("VERSION \"\"\nNS_:\nSIG_VALTYPE_\n\tBA_\n BS_ :\n BU_ : ECU\n");
    +
    452 const DbcParseResult headers = DbcParser().ParseFileWithDiagnostics(fixture.path.string());
    +
    453 CHECK(headers.diagnostics.empty(), "Common header variants stay quiet");
    +
    454 fixture.Write("NS_ :\n BA_\n BO_\n");
    +
    455 CHECK(DbcParser().ParseFileWithDiagnostics(fixture.path.string()).HasErrors(),
    +
    456 "Namespace handling does not swallow a malformed frame");
    +
    457}
    +
    458
    +
    459void DbcParserTest::diagnosticErrors() {
    +
    460 Fixture fixture;
    +
    461 struct ErrorCase {
    +
    462 const char *text;
    +
    463 DbcDiagnosticCode code;
    +
    464 std::size_t line;
    +
    465 };
    +
    466 const ErrorCase cases[] = {
    +
    467 {"UNKNOWN_ data;\n\nBO_ invalid Frame: 1 ECU\n",
    +
    468 DbcDiagnosticCode::InvalidStatement, 3U},
    +
    469 {"BO_ 1 Frame: 1 ECU\nSG_MUL_VAL_ 1 Value Mode 1-2;\n",
    +
    470 DbcDiagnosticCode::InvalidMultiplexing, 2U},
    +
    471 {"BO_ 1 Frame: 1 ECU\nVAL_ 1 Missing 0 \"Off\";\n",
    +
    472 DbcDiagnosticCode::InvalidValueDescription, 2U},
    +
    473 {"BO_ 1 Frame: 1 ECU\nSG_ Value m1 : 0|8@1+ (1,0) [0|255] \"\" ECU\n",
    474 DbcDiagnosticCode::InvalidMultiplexing, 2U},
    -
    475 {"VAL_ broken\n", DbcDiagnosticCode::InvalidValueDescription, 1U},
    -
    476 {"SG_ Value : 0|8@1+ (1,0) [0|255] \"\" ECU\n",
    -
    477 DbcDiagnosticCode::InvalidStatement, 1U},
    -
    478 {"BO_ 1 Frame: 1 ECU\nBO_ 1 Duplicate: 1 ECU\n",
    -
    479 DbcDiagnosticCode::InvalidStatement, 2U},
    -
    480 {"BO_ 1 Frame: 1 ECU\nSG_ Value : 0|8@1+ (1,0) [0|255] \"\" ECU\n"
    -
    481 "SG_ Value : 0|8@1+ (1,0) [0|255] \"\" ECU\n",
    -
    482 DbcDiagnosticCode::InvalidStatement, 3U},
    -
    483 {"BO_ 1 Frame: 1 ECU\nSG_ Value : 8|8@1+ (1,0) [0|255] \"\" ECU\n",
    -
    484 DbcDiagnosticCode::InvalidStatement, 2U},
    -
    485 {"BO_ 1 Frame: 1 ECU\nVAL_ 2 Missing 0 \"Off\";\n",
    -
    486 DbcDiagnosticCode::InvalidValueDescription, 2U},
    -
    487 {"BO_ 1 Frame: 1 ECU\nSG_ Value : 0|8@1+ (1,0) [0|255] \"\" ECU\n"
    -
    488 "VAL_ 1 Value 0 \"Off\";\nVAL_ 1 Value 0 \"Duplicate\";\n",
    -
    489 DbcDiagnosticCode::InvalidValueDescription, 4U},
    -
    490 {"BO_ 1 Frame: 2 ECU\nSG_ Mode M : 0|1@1+ (1,0) [0|1] \"\" ECU\n"
    -
    491 "SG_ Value m2 : 8|8@1+ (1,0) [0|255] \"\" ECU\n",
    -
    492 DbcDiagnosticCode::InvalidMultiplexing, 3U},
    -
    493 {"BO_ 1 Frame: 2 ECU\nSG_ Mode M : 0|8@1+ (1,0) [0|255] \"\" ECU\n"
    -
    494 "SG_ Other M : 8|8@1- (1,0) [-128|127] \"\" ECU\n",
    -
    495 DbcDiagnosticCode::InvalidMultiplexing, 3U},
    -
    496 {"BO_ 1 Frame: 1 ECU\nSG_ First : 0|8@1+ (1,0) [0|255] \"\" ECU\n"
    -
    497 "SG_ Second : 7|8@0+ (1,0) [0|255] \"\" ECU\n",
    -
    498 DbcDiagnosticCode::InvalidMultiplexing, 3U}};
    -
    499 for (const ErrorCase &test : cases) {
    -
    500 fixture.Write(test.text);
    -
    501 const DbcParseResult result = DbcParser().ParseFileWithDiagnostics(fixture.path.string());
    -
    502 CHECK(result.HasErrors() && result.database.frames.empty(),
    -
    503 "Failed parse never returns a partial database");
    -
    504 const DbcDiagnostic &diagnostic = result.diagnostics.back();
    -
    505 CHECK(diagnostic.severity == DbcDiagnosticSeverity::Error && diagnostic.code == test.code,
    -
    506 "Error has expected severity and code");
    -
    507 QCOMPARE(diagnostic.filePath, fixture.path.string());
    -
    508 QCOMPARE(diagnostic.lineNumber, test.line);
    -
    509 CHECK(!diagnostic.message.empty(), "Error includes a message");
    -
    510 const bool hasWarning = std::string(test.text).find("UNKNOWN_") == 0U;
    -
    511 QCOMPARE(result.diagnostics.size(), hasWarning ? std::size_t(2U) : std::size_t(1U));
    -
    512 try {
    -
    513 DbcParser().ParseFile(fixture.path.string());
    -
    514 CHECK(false, "Legacy parser must throw");
    -
    515 } catch (const std::runtime_error &error) {
    -
    516 QCOMPARE(std::string(error.what()), fixture.path.string() + ":" +
    -
    517 std::to_string(test.line) + ": " + diagnostic.message);
    -
    518 }
    -
    519 }
    -
    520 const std::string missingPath = fixture.path.string() + ".missing";
    -
    521 const DbcParseResult missing = DbcParser().ParseFileWithDiagnostics(missingPath);
    -
    522 CHECK(missing.HasErrors() && missing.database.frames.empty(), "Missing file returns error");
    -
    523 QCOMPARE(missing.diagnostics.size(), std::size_t(1U));
    -
    524 CHECK(missing.diagnostics[0].code == DbcDiagnosticCode::FileOpenError,
    -
    525 "Missing file has a distinct code");
    -
    526 QCOMPARE(missing.diagnostics[0].lineNumber, std::size_t(0U));
    -
    527 QCOMPARE(missing.diagnostics[0].filePath, missingPath);
    -
    528 QCOMPARE(missing.diagnostics[0].message, "Failed to open DBC file: " + missingPath);
    -
    529 try {
    -
    530 DbcParser().ParseFile(missingPath);
    -
    531 CHECK(false, "Legacy API throws on missing file");
    -
    532 } catch (const std::runtime_error &error) {
    -
    533 QCOMPARE(std::string(error.what()), missing.diagnostics[0].message);
    -
    534 }
    -
    535 const std::string directoryPath = fixture.path.parent_path().string();
    -
    536 const DbcParseResult directory = DbcParser().ParseFileWithDiagnostics(directoryPath);
    -
    537 CHECK(directory.HasErrors() && directory.database.frames.empty(),
    -
    538 "Directory cannot be parsed as a file");
    -
    539 QCOMPARE(directory.diagnostics.size(), std::size_t(1U));
    -
    540 CHECK(directory.diagnostics[0].code == DbcDiagnosticCode::FileReadError ||
    -
    541 directory.diagnostics[0].code == DbcDiagnosticCode::FileOpenError,
    -
    542 "Directory produces a file-level error on supported platforms");
    -
    543 QCOMPARE(directory.diagnostics[0].lineNumber, std::size_t(0U));
    -
    544 QCOMPARE(directory.diagnostics[0].filePath, directoryPath);
    -
    545#ifdef __linux__
    -
    546 CHECK(directory.diagnostics[0].code == DbcDiagnosticCode::FileReadError,
    -
    547 "Linux directory input exercises the read-error path");
    -
    548 QCOMPARE(directory.diagnostics[0].message, "Failed to read DBC file: " + directoryPath);
    -
    549#endif
    -
    550 try {
    -
    551 DbcParser().ParseFile(directoryPath);
    -
    552 CHECK(false, "Legacy API throws on unreadable input");
    -
    553 } catch (const std::runtime_error &error) {
    -
    554 QCOMPARE(std::string(error.what()), directory.diagnostics[0].message);
    -
    555 }
    -
    556 fixture.Write("BO_ 1 Frame: 1 ECU\n");
    -
    557 CHECK(DbcParser().ParseFileWithDiagnostics(fixture.path.string()).diagnostics.empty(),
    -
    558 "Diagnostics belong to an independent parse session");
    -
    559}
    -
    560
    -
    561void DbcParserTest::diagnosticUnsupportedStatements() {
    -
    562 Fixture fixture;
    -
    563 const std::string definitions =
    -
    564 "BO_ 1 Frame: 1 ECU\nSG_ Value : 0|8@1+ (2,10) [10|520] \"V\" ECU\n"
    -
    565 "CM_ SG_ 1 Value \"Signal comment\";\nVAL_ 1 Value 1 \"On\";\n";
    -
    566 const std::vector<std::string> statements = {
    -
    567 "SIG_VALTYPE_ 1 Value : 1;", "SIG_VALTYPE_ 1 Value : 2;",
    -
    568 "BA_REL_ \"SignalType\" SG_ 1 Value 1;",
    -
    569 "BA_DEF_REL_ BU_SG_REL_ \"Relation\" INT 0 1;",
    -
    570 "BA_REL_ \"Relation\" BU_SG_REL_ ECU SG_ 1 Value 1;",
    -
    571 "SGTYPE_ Type : 8@1+ (1,0) [0|255] \"\" 0, Table;",
    -
    572 "SIG_TYPE_REF_ 1 Value : Type;",
    -
    573 "EV_ Environment : 0 [0|1] \"\" 0 1 DUMMY_NODE_VECTOR0 ECU;",
    -
    574 "ENVVAR_DATA_ Environment : 4;",
    -
    575 "VAL_TABLE_ Table 0 \"Off\" 1 \"On\";",
    -
    576 "BO_TX_BU_ 1 : ECU;", "VENDOR_EXTENSION_ data;", "UNKNOWN_"
    -
    577 };
    -
    578 for (const std::string &statement : statements) {
    -
    579 fixture.Write(definitions + statement + '\n');
    -
    580 const DbcParseResult result = DbcParser().ParseFileWithDiagnostics(fixture.path.string());
    -
    581 CHECK(!result.HasErrors(), statement);
    -
    582 QCOMPARE(result.diagnostics.size(), std::size_t(1U));
    -
    583 const DbcDiagnostic &warning = result.diagnostics[0];
    -
    584 CHECK(warning.severity == DbcDiagnosticSeverity::Warning &&
    -
    585 warning.code == DbcDiagnosticCode::UnsupportedStatement, statement);
    -
    586 QCOMPARE(warning.filePath, fixture.path.string());
    -
    587 QCOMPARE(warning.lineNumber, std::size_t(5U));
    -
    588 const std::string keyword = statement.substr(0U, statement.find(' '));
    -
    589 CHECK(warning.message.find("'" + keyword + "'") != std::string::npos, statement);
    -
    590 const DecodeDatabase runtime = DbcDecodeBuilder().Build(result.database);
    -
    591 RawCanFrame raw;
    -
    592 raw.canId = 1U;
    -
    593 raw.data = {1U};
    -
    594 const DecodedFrameValue decoded = DbcDecoder().Decode(runtime, raw);
    -
    595 CHECK(decoded.valid && decoded.signals.size() == 1U && decoded.signals[0].valid,
    -
    596 "Warnings preserve supported signal decoding");
    -
    597 QCOMPARE(decoded.signals[0].physicalValue, 12.0);
    -
    598 CHECK(decoded.signals[0].GetValueDescription() &&
    -
    599 *decoded.signals[0].GetValueDescription() == "On",
    -
    600 "Warnings preserve VAL_ descriptions");
    -
    601 QCOMPARE(runtime.GetFrames()[0].signals[0].comment, std::string("Signal comment"));
    -
    602 }
    -
    603}
    -
    604
    -
    605void DbcParserTest::diagnosticNamespaceBoundaries() {
    -
    606 Fixture fixture;
    -
    607 const std::vector<std::string> endings = {
    -
    608 "BS_\n", "BS_:\n", "BS_ :\n", "BU_\n", "BU_:\n", "BU_ : ECU\n",
    -
    609 "BO_ 1 Frame: 1 ECU\n", "VENDOR_EXTENSION_ data;\n"
    -
    610 };
    -
    611 for (const std::string &ending : endings) {
    -
    612 fixture.Write("NS_ :\n\tSG_MUL_VAL_\n\nBA_\n" + ending + "SIG_VALTYPE_\n");
    -
    613 const DbcParseResult result = DbcParser().ParseFileWithDiagnostics(fixture.path.string());
    -
    614 CHECK(!result.HasErrors(), ending);
    -
    615 const std::size_t expectedWarnings = ending.find("VENDOR_") == 0U ? 2U : 1U;
    -
    616 QCOMPARE(result.diagnostics.size(), expectedWarnings);
    -
    617 QCOMPARE(result.diagnostics.back().lineNumber, std::size_t(6U));
    -
    618 CHECK(result.diagnostics.back().message.find("SIG_VALTYPE_") != std::string::npos,
    -
    619 "Unsupported keyword outside NS_ must produce a warning");
    -
    620 }
    -
    621 for (const std::string &definition : {std::string("BO_"), std::string("SG_")}) {
    -
    622 fixture.Write("NS_:\n\tBA_\n\t" + definition + '\n');
    -
    623 const DbcParseResult result = DbcParser().ParseFileWithDiagnostics(fixture.path.string());
    -
    624 CHECK(result.HasErrors(), "Malformed definition ends NS_ instead of being hidden");
    -
    625 QCOMPARE(result.diagnostics.size(), std::size_t(1U));
    -
    626 QCOMPARE(result.diagnostics[0].lineNumber, std::size_t(3U));
    -
    627 }
    -
    628}
    -
    629
    -
    630void DbcParserTest::diagnosticFormatting() {
    -
    631 Fixture fixture;
    -
    632 fixture.path += " with spaces";
    -
    633 const DbcParser parser;
    -
    634 for (const std::string &text : {std::string(), std::string(" \t\r\n\n")}) {
    -
    635 fixture.Write(text);
    -
    636 const DbcParseResult empty = parser.ParseFileWithDiagnostics(fixture.path.string());
    -
    637 CHECK(!empty.HasErrors() && empty.diagnostics.empty() && empty.database.frames.empty(),
    -
    638 "Empty and whitespace-only inputs are valid");
    -
    639 }
    -
    640 fixture.Write("\r\n\tVENDOR_EXTENSION_ data;\r\n\r\nUNKNOWN_");
    -
    641 const std::string relativePath = std::filesystem::relative(fixture.path).string();
    -
    642 const DbcParseResult warnings = parser.ParseFileWithDiagnostics(relativePath);
    -
    643 CHECK(!warnings.HasErrors() && warnings.database.frames.empty(), "Warnings-only input");
    -
    644 QCOMPARE(warnings.diagnostics.size(), std::size_t(2U));
    -
    645 QCOMPARE(warnings.diagnostics[0].lineNumber, std::size_t(2U));
    -
    646 QCOMPARE(warnings.diagnostics[1].lineNumber, std::size_t(4U));
    -
    647 for (const DbcDiagnostic &warning : warnings.diagnostics)
    -
    648 QCOMPARE(warning.filePath, relativePath);
    -
    649
    -
    650 fixture.Write("UNKNOWN_ first;\nBO_ invalid Frame: 1 ECU\n"
    -
    651 "UNKNOWN_ after_error;\nSG_ broken\n");
    -
    652 const DbcParseResult stopped = parser.ParseFileWithDiagnostics(relativePath);
    -
    653 CHECK(stopped.HasErrors() && stopped.database.frames.empty(), "First error stops parsing");
    -
    654 QCOMPARE(stopped.diagnostics.size(), std::size_t(2U));
    -
    655 QCOMPARE(stopped.diagnostics[0].lineNumber, std::size_t(1U));
    -
    656 QCOMPARE(stopped.diagnostics[1].lineNumber, std::size_t(2U));
    -
    657 QCOMPARE(stopped.diagnostics[1].filePath, relativePath);
    -
    658
    -
    659 fixture.Write("VAL_ 1 Missing 0 \"Off\";\nBO_ 1 Frame: 1 ECU\nUNKNOWN_ data;\n");
    -
    660 const DbcParseResult deferred = parser.ParseFileWithDiagnostics(relativePath);
    -
    661 CHECK(deferred.HasErrors() && deferred.database.frames.empty(), "Deferred error rejects database");
    -
    662 QCOMPARE(deferred.diagnostics.size(), std::size_t(2U));
    -
    663 CHECK(deferred.diagnostics[0].severity == DbcDiagnosticSeverity::Warning &&
    -
    664 deferred.diagnostics[1].severity == DbcDiagnosticSeverity::Error,
    -
    665 "Deferred errors follow warnings in detection order");
    -
    666 QCOMPARE(deferred.diagnostics[0].lineNumber, std::size_t(3U));
    -
    667 QCOMPARE(deferred.diagnostics[1].lineNumber, std::size_t(1U));
    -
    668 QCOMPARE(deferred.diagnostics[1].filePath, relativePath);
    -
    669 fixture.Write("BO_ 1 Frame: 1 ECU");
    -
    670 const DbcParseResult recovered = parser.ParseFileWithDiagnostics(relativePath);
    -
    671 CHECK(!recovered.HasErrors() && recovered.diagnostics.empty() && recovered.database.frames.size() == 1U,
    -
    672 "Same parser has clean state after errors and accepts missing final newline");
    -
    673}
    -
    674
    -
    675void DbcParserTest::validFrames() {
    -
    676 Fixture fixture;
    -
    677 struct ValidCase {
    -
    678 const char *line;
    -
    679 std::uint32_t id;
    -
    680 bool extended;
    -
    681 unsigned length;
    -
    682 };
    -
    683 const ValidCase valid[] = {
    -
    684 {"BO_ 0 Frame: 0 ECU", 0U, false, 0U},
    -
    685 {"BO_ 2047 Frame: 8 ECU", 2047U, false, 8U},
    -
    686 {"BO_ 2048 Frame: 64 ECU", 2048U, true, 64U},
    -
    687 {"BO_ 536870911 Frame: 255 ECU", 0x1FFFFFFFU, true, 255U},
    -
    688 {"BO_ 2147483649 Frame: 8 ECU", 1U, true, 8U},
    -
    689 {"BO_ 2684354559 Frame: 8 ECU", 0x1FFFFFFFU, true, 8U},
    -
    690 {" BO_\t256\tFrame :\t8\tECU \r", 256U, false, 8U}};
    -
    691 for (const ValidCase &test: valid) {
    -
    692 fixture.Write(std::string(test.line) + '\n');
    -
    693 try {
    -
    694 const DbcDatabase database = DbcParser().ParseFile(fixture.path.string());
    -
    695 QCOMPARE(database.frames.size(), std::size_t(1U));
    -
    696 const FrameInfo &frame = database.frames[0];
    -
    697 QCOMPARE(frame.name, std::string("Frame"));
    -
    698 QCOMPARE(frame.canId, test.id);
    -
    699 QCOMPARE(frame.isExtended, test.extended);
    -
    700 QCOMPARE(static_cast<unsigned>(frame.dlc), test.length);
    -
    701 QCOMPARE(frame.transmitter, std::string("ECU"));
    -
    702 } catch (const std::exception &error) { CHECK(false, error.what()); }
    -
    703 }
    -
    704}
    -
    705
    -
    706void DbcParserTest::invalidFrames() {
    -
    707 Fixture fixture;
    -
    708 const char *invalid[] = {
    -
    709 "BO_ invalid", "BO_", "BO_ 1", "BO_ 1 : 8 ECU", "BO_ 1 Frame 8 ECU",
    -
    710 "BO_ 1 Frame:", "BO_ 1 Frame: 8", "BO_ 1 Frame: 8 ECU extra",
    -
    711 "BO_ 1 Two Names: 8 ECU", "BO_ -1 Frame: 8 ECU", "BO_ +1 Frame: 8 ECU",
    -
    712 "BO_ 1x Frame: 8 ECU", "BO_ 0x123 Frame: 8 ECU", "BO_ 1.5 Frame: 8 ECU",
    -
    713 "BO_ 4294967296 Frame: 8 ECU", "BO_ 999999999999999999999 Frame: 8 ECU",
    -
    714 "BO_ 536870912 Frame: 8 ECU", "BO_ 1073741824 Frame: 8 ECU",
    -
    715 "BO_ 2684354560 Frame: 8 ECU", "BO_ 4294967295 Frame: 8 ECU",
    -
    716 "BO_ 1 Frame: -1 ECU", "BO_ 1 Frame: +8 ECU", "BO_ 1 Frame: 8x ECU",
    -
    717 "BO_ 1 Frame: 8.5 ECU", "BO_ 1 Frame: 256 ECU",
    -
    718 "BO_ 1 Frame: 4294967296 ECU", "BO_\tinvalid"};
    -
    719 for (const char *line: invalid) {
    -
    720 fixture.Write(std::string("VERSION \"test\"\n\n") + line + '\n');
    -
    721 try {
    -
    722 DbcParser().ParseFile(fixture.path.string());
    -
    723 CHECK(false, line);
    -
    724 } catch (const std::runtime_error &error) {
    -
    725 const std::string prefix = fixture.path.string() + ":3: ";
    -
    726 CHECK(std::string(error.what()).find(prefix) == 0U &&
    -
    727 std::string(error.what()).size() > prefix.size(),
    -
    728 line);
    -
    729 }
    -
    730 }
    -
    731}
    -
    732
    -
    733void DbcParserTest::duplicateFrames() {
    -
    734 Fixture fixture;
    -
    735 struct DuplicateCase {
    -
    736 const char *firstId;
    -
    737 const char *duplicateId;
    -
    738 const char *message;
    -
    739 };
    -
    740 const DuplicateCase cases[] = {
    -
    741 {"1", "1", "Duplicate standard frame CAN ID: 1"},
    -
    742 {"2147483649", "2147483649", "Duplicate extended frame CAN ID: 1"},
    -
    743 {"2048", "2147485696", "Duplicate extended frame CAN ID: 2048"},
    -
    744 {"2147485696", "2048", "Duplicate extended frame CAN ID: 2048"},
    -
    745 {"536870911", "2684354559", "Duplicate extended frame CAN ID: 536870911"}
    -
    746 };
    -
    747 const DbcParser parser;
    -
    748 for (const DuplicateCase &test : cases) {
    -
    749 fixture.Write("BO_ " + std::string(test.firstId) + " First: 8 ECU\n"
    -
    750 "BO_ 2 Other: 8 ECU\n\n"
    -
    751 " BO_\t" + test.duplicateId + "\tSecond: 8 ECU\n");
    -
    752 try {
    -
    753 parser.ParseFile(fixture.path.string());
    -
    754 CHECK(false, "Duplicate frame must be rejected");
    -
    755 } catch (const std::runtime_error &error) {
    -
    756 QCOMPARE(std::string(error.what()), fixture.path.string() + ":4: " + test.message);
    -
    757 }
    -
    758 }
    -
    759 fixture.Write("BO_ 1 First: 8 ECU\n");
    -
    760 QCOMPARE(parser.ParseFile(fixture.path.string()).frames.size(), std::size_t(1U));
    -
    761}
    -
    762
    -
    763void DbcParserTest::duplicateSignals() {
    -
    764 Fixture fixture;
    -
    765 const std::string first = "SG_ State : 0|8@1+ (1,0) [0|255] \"\" ECU\n";
    -
    766 const std::string duplicates[] = {
    -
    767 first,
    -
    768 " SG_\tState : 16|8@1+ (2,1) [0|511] \"V\" ECU\n",
    -
    769 "SG_ State m1 : 16|8@1+ (1,0) [0|255] \"\" ECU\n"
    -
    770 };
    -
    771 for (const std::string &duplicate : duplicates) {
    -
    772 fixture.Write("BO_ 1 Frame: 8 ECU\n" + first +
    -
    773 "SG_ Other : 8|8@1+ (1,0) [0|255] \"\" ECU\n\n" + duplicate);
    -
    774 try {
    -
    775 DbcParser().ParseFile(fixture.path.string());
    -
    776 CHECK(false, "Duplicate signal must be rejected");
    -
    777 } catch (const std::runtime_error &error) {
    -
    778 QCOMPARE(std::string(error.what()), fixture.path.string() +
    -
    779 ":5: Duplicate signal name 'State' in frame 'Frame'.");
    -
    780 }
    -
    781 }
    -
    782}
    -
    783
    -
    784void DbcParserTest::distinctDefinitions() {
    -
    785 Fixture fixture;
    -
    786 fixture.Write("BO_ 1 Shared: 1 ECU\n"
    -
    787 "SG_ State : 0|8@1+ (1,0) [0|255] \"\" ECU\n"
    -
    788 "BO_ 2147483649 Shared: 1 ECU\n"
    -
    789 "SG_ State : 0|8@1+ (1,0) [0|255] \"\" ECU\n"
    -
    790 "BO_ 2 Shared: 2 ECU\n"
    -
    791 "SG_ State : 0|8@1+ (1,0) [0|255] \"\" ECU\n"
    -
    792 "SG_ state : 8|8@1+ (1,0) [0|255] \"\" ECU\n"
    -
    793 "CM_ BO_ 1 \"Standard\";\n"
    -
    794 "CM_ BO_ 2147483649 \"Extended\";\n"
    -
    795 "CM_ SG_ 1 State \"Standard state\";\n"
    -
    796 "CM_ SG_ 2147483649 State \"Extended state\";\n");
    -
    797 const DbcDatabase parsed = DbcParser().ParseFile(fixture.path.string());
    -
    798 QCOMPARE(parsed.frames.size(), std::size_t(3U));
    -
    799 QCOMPARE(parsed.frames[0].canId, parsed.frames[1].canId);
    -
    800 CHECK(!parsed.frames[0].isExtended && parsed.frames[1].isExtended,
    -
    801 "Standard and extended frames with the same numeric ID remain distinct");
    -
    802 QCOMPARE(parsed.frames[0].comment, std::string("Standard"));
    -
    803 QCOMPARE(parsed.frames[1].comment, std::string("Extended"));
    -
    804 QCOMPARE(parsed.frames[0].signals[0].comment, std::string("Standard state"));
    -
    805 QCOMPARE(parsed.frames[1].signals[0].comment, std::string("Extended state"));
    -
    806 QCOMPARE(parsed.frames[2].signals.size(), std::size_t(2U));
    -
    807 const DecodeDatabase runtime = DbcDecodeBuilder().Build(parsed);
    -
    808 RawCanFrame raw;
    -
    809 raw.canId = 1U;
    -
    810 raw.data = {7U};
    -
    811 const DecodedFrameValue standard = DbcDecoder().Decode(runtime, raw);
    -
    812 QVERIFY(standard.valid);
    -
    813 QCOMPARE(standard.definition->comment, std::string("Standard"));
    -
    814 raw.isExtended = true;
    -
    815 const DecodedFrameValue extended = DbcDecoder().Decode(runtime, raw);
    -
    816 QVERIFY(extended.valid);
    -
    817 QCOMPARE(extended.definition->comment, std::string("Extended"));
    -
    818}
    -
    819
    -
    820void DbcParserTest::comments() {
    -
    821 Fixture fixture;
    -
    822 fixture.Write(
    -
    823 "CM_\tBO_\t1\t\"First\";\r\n"
    -
    824 "CM_\tSG_\t1\tState\t\" Line one \r\n"
    -
    825 "\r\n"
    -
    826 "\tBO_ is text; \\\"quoted\\\" and \\\\slash\"\t;\r\n"
    -
    827 "CM_ BO_ 2147483649 \"Extended\";\r\n"
    -
    828 "CM_ SG_ 2147483649 State \"Extended signal\";\r\n"
    -
    829 "BO_ 1 Standard: 1 ECU\r\n"
    -
    830 "CM_ BO_ 1 \"Last\";\r\n"
    -
    831 "SG_ State : 0|8@1+ (1,0) [0|255] \"\" ECU\r\n"
    -
    832 "BO_ 2147483649 Extended: 1 ECU\r\n"
    -
    833 "SG_ State : 0|8@1+ (1,0) [0|255] \"\" ECU");
    -
    834 const DbcParseResult result = DbcParser().ParseFileWithDiagnostics(fixture.path.string());
    -
    835 QVERIFY(!result.HasErrors());
    -
    836 QVERIFY(result.diagnostics.empty());
    -
    837 QCOMPARE(result.database.frames.size(), std::size_t(2));
    -
    838 QCOMPARE(result.database.frames[0].comment, std::string("Last"));
    -
    839 QCOMPARE(result.database.frames[0].signals[0].comment,
    -
    840 std::string(" Line one \n\n\tBO_ is text; \"quoted\" and \\slash"));
    -
    841 QCOMPARE(result.database.frames[1].comment, std::string("Extended"));
    -
    842 QCOMPARE(result.database.frames[1].signals[0].comment, std::string("Extended signal"));
    -
    843
    -
    844 const std::string ids[][2] = {
    -
    845 {"0", "0"},
    -
    846 {"2047", "2047"},
    -
    847 {"2048", "2147485696"},
    -
    848 {"536870911", "2684354559"},
    -
    849 {"2684354559", "536870911"}};
    -
    850 for (const std::string(&id)[2]: ids) {
    -
    851 fixture.Write("CM_ BO_ " + id[0] + " \"Frame\";\nCM_ SG_ " + id[0] +
    -
    852 " State \"Signal\";\nBO_ " + id[1] + " Frame: 1 ECU\n"
    -
    853 "SG_ State : 0|8@1+ (1,0) [0|255] \"\" ECU\n");
    -
    854 const DbcDatabase database = DbcParser().ParseFile(fixture.path.string());
    -
    855 QCOMPARE(database.frames[0].comment, std::string("Frame"));
    -
    856 QCOMPARE(database.frames[0].signals[0].comment, std::string("Signal"));
    -
    857 }
    -
    858 fixture.Write("CM_ BO_ 1 \"Line one\nLine two\";\n"
    -
    859 "BO_ 1 Frame: 0 ECU\nUNKNOWN_ value\nBO_ bad Frame: 0 ECU\n");
    -
    860 const DbcParseResult following = DbcParser().ParseFileWithDiagnostics(fixture.path.string());
    -
    861 QVERIFY(following.HasErrors());
    -
    862 QCOMPARE(following.diagnostics.size(), std::size_t(2));
    -
    863 QCOMPARE(following.diagnostics[0].lineNumber, std::size_t(4));
    -
    864 QCOMPARE(following.diagnostics[1].lineNumber, std::size_t(5));
    -
    865}
    -
    866
    -
    867void DbcParserTest::invalidComments_data() {
    -
    868 QTest::addColumn<QString>("statement");
    -
    869 const char *ids[] = {"-1", "+1", "1x", "0x1", "4294967296", "536870912",
    -
    870 "1073741824", "2684354560", "4294967295"};
    -
    871 for (const char *id: ids) {
    -
    872 const std::string frameRow = std::string("frame-id-") + id;
    -
    873 const std::string signalRow = std::string("signal-id-") + id;
    -
    874 QTest::newRow(frameRow.c_str()) << QString::fromStdString("CM_ BO_ " + std::string(id) + " \"Text\";");
    -
    875 QTest::newRow(signalRow.c_str()) << QString::fromStdString("CM_ SG_ " + std::string(id) + " State \"Text\";");
    -
    876 }
    -
    877 QTest::newRow("missing-id") << QStringLiteral("CM_ BO_");
    -
    878 QTest::newRow("missing-signal") << QStringLiteral("CM_ SG_ 1 \"Text\";");
    -
    879 QTest::newRow("missing-quote") << QStringLiteral("CM_ BO_ 1 Text;");
    -
    880 QTest::newRow("unclosed") << QStringLiteral("CM_ SG_ 1 State \"Text\n More");
    -
    881 QTest::newRow("invalid-escape") << QStringLiteral("CM_ BO_ 1 \"bad\\q\";");
    -
    882 QTest::newRow("dangling-escape") << QStringLiteral("CM_ BO_ 1 \"bad\\");
    -
    883 QTest::newRow("missing-semicolon") << QStringLiteral("CM_ SG_ 1 State \"Text\"");
    -
    884 QTest::newRow("trailing-text") << QStringLiteral("CM_ BO_ 1 \"Text\"; garbage");
    -
    885 QTest::newRow("extra-header") << QStringLiteral("CM_ BO_ 1 extra \"Text\";");
    -
    886 QTest::newRow("extra-string") << QStringLiteral("CM_ BO_ 1 \"Text\" \"More\";");
    -
    887 QTest::newRow("missing-frame") << QStringLiteral("CM_ BO_ 2 \"Text\";");
    -
    888 QTest::newRow("missing-signal-frame") << QStringLiteral("CM_ SG_ 2 State \"Text\";");
    -
    889 QTest::newRow("missing-signal-reference") << QStringLiteral("CM_ SG_ 1 Missing \"Text\";");
    -
    890 QTest::newRow("different-frame-type") << QStringLiteral("CM_ BO_ 2147483649 \"Text\";");
    -
    891 QTest::newRow("missing-multiline-reference") << QStringLiteral("CM_ SG_ 1 Missing \"Text\nMore\";");
    -
    892}
    -
    893
    -
    894void DbcParserTest::invalidComments() {
    -
    895 QFETCH(QString, statement);
    -
    896 Fixture fixture;
    -
    897 fixture.Write("BO_ 1 Frame: 1 ECU\nSG_ State : 0|8@1+ (1,0) [0|255] \"\" ECU\n\n" +
    -
    898 statement.toStdString());
    -
    899 const DbcParseResult result = DbcParser().ParseFileWithDiagnostics(fixture.path.string());
    -
    900 QVERIFY(result.HasErrors());
    -
    901 QVERIFY(result.database.frames.empty());
    -
    902 QCOMPARE(result.diagnostics.size(), std::size_t(1));
    -
    903 const DbcDiagnostic &error = result.diagnostics[0];
    -
    904 QCOMPARE(error.severity, DbcDiagnosticSeverity::Error);
    -
    905 QCOMPARE(error.code, DbcDiagnosticCode::InvalidStatement);
    -
    906 QCOMPARE(error.filePath, fixture.path.string());
    -
    907 QCOMPARE(error.lineNumber, std::size_t(4));
    -
    908 QVERIFY(!error.message.empty());
    -
    909 try {
    -
    910 DbcParser().ParseFile(fixture.path.string());
    -
    911 QFAIL("Malformed or unresolved comment must throw.");
    -
    912 } catch (const std::runtime_error &exception) {
    -
    913 QVERIFY(std::string(exception.what()).find(fixture.path.string() + ":4: ") == 0U);
    -
    914 }
    -
    915}
    -
    916
    -
    917void DbcParserTest::integration() {
    -
    918 Fixture fixture;
    -
    919 fixture.Write(
    -
    920 "BO_TX_BU_ 256 : ECU;\nBO_ 256 Frame: 8 ECU\n"
    -
    921 " SG_ Speed : 0|16@1+ (0.5,1) [0|100] \"rpm\" ECU\n"
    -
    922 "CM_ BO_ 256 \"Frame comment\";\nCM_ SG_ 256 Speed \"Signal comment\";\n"
    -
    923 "BO_ 257 Second: 8 ECU\n");
    -
    924 const DbcDatabase database = DbcParser().ParseFile(fixture.path.string());
    -
    925 CHECK(database.frames.size() == 2U && database.frames[0].signals.size() == 1U &&
    -
    926 database.frames[0].comment == "Frame comment" &&
    -
    927 database.frames[0].signals[0].comment == "Signal comment",
    -
    928 "Integration");
    -
    929 fixture.Write("\nSG_ Speed : invalid\n");
    -
    930 try {
    -
    931 DbcParser().ParseFile(fixture.path.string());
    -
    932 CHECK(false, "Signal error location");
    -
    933 } catch (const std::runtime_error &error) {
    -
    934 CHECK(std::string(error.what()).find(fixture.path.string() + ":2: ") == 0U,
    -
    935 "Signal error location");
    -
    936 }
    -
    937}
    -
    938
    -
    939void DbcParserTest::multiplexing() {
    -
    940 Fixture fixture;
    -
    941 const std::string selector = "SG_ Mode M : 0|8@1+ (10,3) [0|255] \"\" ECU\n";
    -
    942 const std::string first = "SG_ First m1 : 8|8@1- (2,0) [-128|127] \"V\" ECU\n";
    -
    943 const std::string second = "SG_ Second m2 : 15|8@0+ (1,0) [0|255] \"\" ECU\n";
    -
    944 const std::string common = "SG_ Common : 16|8@1+ (1,0) [0|255] \"\" ECU\n";
    -
    945 fixture.Write("BO_ 1 Frame: 3 ECU\n" + first + second + common + selector +
    -
    946 "VAL_ 1 First -1 \"Negative\";\n");
    -
    947 const DbcDatabase parsed = DbcParser().ParseFile(fixture.path.string());
    -
    948 const DecodeDatabase runtime = DbcDecodeBuilder().Build(parsed);
    -
    949 CHECK(parsed.frames[0].signals[3].multiplexing.role == MultiplexRole::Selector,
    -
    950 "Selector declared after branches");
    -
    951 const std::unique_ptr<TreeNode> tree = DbcTreeBuilder().Build(parsed);
    -
    952 CHECK(tree->GetChild(0U)->GetChild(0U)->GetSignal()->multiplexing.value == 1U,
    -
    953 "Tree preserves multiplexing");
    -
    954 RawCanFrame raw;
    -
    955 raw.canId = 1U;
    -
    956 raw.data = {1U, 255U, 7U};
    -
    957 DecodedFrameValue result = DbcDecoder().Decode(runtime, raw);
    -
    958 CHECK(result.signals[0].valid && std::get<std::int64_t>(result.signals[0].rawValue) == -1 &&
    -
    959 result.signals[0].physicalValue == -2 && *result.signals[0].GetValueDescription() == "Negative",
    -
    960 "Active signed branch and VAL_ use exact raw selector");
    -
    961 CHECK(!result.signals[1].valid && result.signals[1].status == SignalDecodeStatus::Inactive &&
    -
    962 result.signals[1].GetValueDescription() == nullptr && result.signals[2].valid,
    -
    963 "Inactive branch and unconditional signal");
    -
    964 raw.data[0] = 2U;
    -
    965 result = DbcDecoder().Decode(runtime, raw);
    -
    966 CHECK(result.signals[0].status == SignalDecodeStatus::Inactive && result.signals[1].valid &&
    -
    967 std::get<std::uint64_t>(result.signals[1].rawValue) == 255U,
    -
    968 "Motorola branch becomes active");
    -
    969 raw.data = {3U};
    -
    970 result = DbcDecoder().Decode(runtime, raw);
    -
    971 CHECK(result.signals[0].status == SignalDecodeStatus::Inactive &&
    -
    972 result.signals[1].status == SignalDecodeStatus::Inactive &&
    -
    973 result.signals[2].status == SignalDecodeStatus::InsufficientPayload,
    -
    974 "Unmapped selector leaves branches inactive even with missing bytes");
    -
    975 raw.data = {1U};
    -
    976 result = DbcDecoder().Decode(runtime, raw);
    -
    977 CHECK(result.signals[0].status == SignalDecodeStatus::InsufficientPayload,
    -
    978 "Active branch requires payload");
    -
    979 raw.data.clear();
    -
    980 result = DbcDecoder().Decode(runtime, raw);
    -
    981 CHECK(result.signals[0].status == SignalDecodeStatus::SelectorUnavailable &&
    -
    982 result.signals[3].status == SignalDecodeStatus::InsufficientPayload,
    -
    983 "Unavailable selector does not imply inactive branch");
    +
    475 {"BO_ 1 Frame: 1 ECU\nSG_ Mode M : 0|8@1- (1,0) [-128|127] \"\" ECU\n",
    +
    476 DbcDiagnosticCode::InvalidMultiplexing, 2U},
    +
    477 {"VAL_ broken\n", DbcDiagnosticCode::InvalidValueDescription, 1U},
    +
    478 {"SG_ Value : 0|8@1+ (1,0) [0|255] \"\" ECU\n",
    +
    479 DbcDiagnosticCode::InvalidStatement, 1U},
    +
    480 {"BO_ 1 Frame: 1 ECU\nBO_ 1 Duplicate: 1 ECU\n",
    +
    481 DbcDiagnosticCode::InvalidStatement, 2U},
    +
    482 {"BO_ 1 Frame: 1 ECU\nSG_ Value : 0|8@1+ (1,0) [0|255] \"\" ECU\n"
    +
    483 "SG_ Value : 0|8@1+ (1,0) [0|255] \"\" ECU\n",
    +
    484 DbcDiagnosticCode::InvalidStatement, 3U},
    +
    485 {"BO_ 1 Frame: 1 ECU\nSG_ Value : 8|8@1+ (1,0) [0|255] \"\" ECU\n",
    +
    486 DbcDiagnosticCode::InvalidStatement, 2U},
    +
    487 {"BO_ 1 Frame: 1 ECU\nVAL_ 2 Missing 0 \"Off\";\n",
    +
    488 DbcDiagnosticCode::InvalidValueDescription, 2U},
    +
    489 {"BO_ 1 Frame: 1 ECU\nSG_ Value : 0|8@1+ (1,0) [0|255] \"\" ECU\n"
    +
    490 "VAL_ 1 Value 0 \"Off\";\nVAL_ 1 Value 0 \"Duplicate\";\n",
    +
    491 DbcDiagnosticCode::InvalidValueDescription, 4U},
    +
    492 {"BO_ 1 Frame: 2 ECU\nSG_ Mode M : 0|1@1+ (1,0) [0|1] \"\" ECU\n"
    +
    493 "SG_ Value m2 : 8|8@1+ (1,0) [0|255] \"\" ECU\n",
    +
    494 DbcDiagnosticCode::InvalidMultiplexing, 3U},
    +
    495 {"BO_ 1 Frame: 2 ECU\nSG_ Mode M : 0|8@1+ (1,0) [0|255] \"\" ECU\n"
    +
    496 "SG_ Other M : 8|8@1- (1,0) [-128|127] \"\" ECU\n",
    +
    497 DbcDiagnosticCode::InvalidMultiplexing, 3U},
    +
    498 {"BO_ 1 Frame: 1 ECU\nSG_ First : 0|8@1+ (1,0) [0|255] \"\" ECU\n"
    +
    499 "SG_ Second : 7|8@0+ (1,0) [0|255] \"\" ECU\n",
    +
    500 DbcDiagnosticCode::InvalidMultiplexing, 3U}};
    +
    501 for (const ErrorCase &test : cases) {
    +
    502 fixture.Write(test.text);
    +
    503 const DbcParseResult result = DbcParser().ParseFileWithDiagnostics(fixture.path.string());
    +
    504 CHECK(result.HasErrors() && result.database.frames.empty(),
    +
    505 "Failed parse never returns a partial database");
    +
    506 const DbcDiagnostic &diagnostic = result.diagnostics.back();
    +
    507 CHECK(diagnostic.severity == DbcDiagnosticSeverity::Error && diagnostic.code == test.code,
    +
    508 "Error has expected severity and code");
    +
    509 QCOMPARE(diagnostic.filePath, fixture.path.string());
    +
    510 QCOMPARE(diagnostic.lineNumber, test.line);
    +
    511 CHECK(!diagnostic.message.empty(), "Error includes a message");
    +
    512 const bool hasWarning = std::string(test.text).find("UNKNOWN_") == 0U;
    +
    513 QCOMPARE(result.diagnostics.size(), hasWarning ? std::size_t(2U) : std::size_t(1U));
    +
    514 try {
    +
    515 DbcParser().ParseFile(fixture.path.string());
    +
    516 CHECK(false, "Legacy parser must throw");
    +
    517 } catch (const std::runtime_error &error) {
    +
    518 QCOMPARE(std::string(error.what()), fixture.path.string() + ":" +
    +
    519 std::to_string(test.line) + ": " + diagnostic.message);
    +
    520 }
    +
    521 }
    +
    522 const std::string missingPath = fixture.path.string() + ".missing";
    +
    523 const DbcParseResult missing = DbcParser().ParseFileWithDiagnostics(missingPath);
    +
    524 CHECK(missing.HasErrors() && missing.database.frames.empty(), "Missing file returns error");
    +
    525 QCOMPARE(missing.diagnostics.size(), std::size_t(1U));
    +
    526 CHECK(missing.diagnostics[0].code == DbcDiagnosticCode::FileOpenError,
    +
    527 "Missing file has a distinct code");
    +
    528 QCOMPARE(missing.diagnostics[0].lineNumber, std::size_t(0U));
    +
    529 QCOMPARE(missing.diagnostics[0].filePath, missingPath);
    +
    530 QCOMPARE(missing.diagnostics[0].message, "Failed to open DBC file: " + missingPath);
    +
    531 try {
    +
    532 DbcParser().ParseFile(missingPath);
    +
    533 CHECK(false, "Legacy API throws on missing file");
    +
    534 } catch (const std::runtime_error &error) {
    +
    535 QCOMPARE(std::string(error.what()), missing.diagnostics[0].message);
    +
    536 }
    +
    537 const std::string directoryPath = fixture.path.parent_path().string();
    +
    538 const DbcParseResult directory = DbcParser().ParseFileWithDiagnostics(directoryPath);
    +
    539 CHECK(directory.HasErrors() && directory.database.frames.empty(),
    +
    540 "Directory cannot be parsed as a file");
    +
    541 QCOMPARE(directory.diagnostics.size(), std::size_t(1U));
    +
    542 CHECK(directory.diagnostics[0].code == DbcDiagnosticCode::FileReadError ||
    +
    543 directory.diagnostics[0].code == DbcDiagnosticCode::FileOpenError,
    +
    544 "Directory produces a file-level error on supported platforms");
    +
    545 QCOMPARE(directory.diagnostics[0].lineNumber, std::size_t(0U));
    +
    546 QCOMPARE(directory.diagnostics[0].filePath, directoryPath);
    +
    547#ifdef __linux__
    +
    548 CHECK(directory.diagnostics[0].code == DbcDiagnosticCode::FileReadError,
    +
    549 "Linux directory input exercises the read-error path");
    +
    550 QCOMPARE(directory.diagnostics[0].message, "Failed to read DBC file: " + directoryPath);
    +
    551#endif
    +
    552 try {
    +
    553 DbcParser().ParseFile(directoryPath);
    +
    554 CHECK(false, "Legacy API throws on unreadable input");
    +
    555 } catch (const std::runtime_error &error) {
    +
    556 QCOMPARE(std::string(error.what()), directory.diagnostics[0].message);
    +
    557 }
    +
    558 fixture.Write("BO_ 1 Frame: 1 ECU\n");
    +
    559 CHECK(DbcParser().ParseFileWithDiagnostics(fixture.path.string()).diagnostics.empty(),
    +
    560 "Diagnostics belong to an independent parse session");
    +
    561}
    +
    562
    +
    563void DbcParserTest::diagnosticUnsupportedStatements() {
    +
    564 Fixture fixture;
    +
    565 const std::string definitions =
    +
    566 "BO_ 1 Frame: 1 ECU\nSG_ Value : 0|8@1+ (2,10) [10|520] \"V\" ECU\n"
    +
    567 "CM_ SG_ 1 Value \"Signal comment\";\nVAL_ 1 Value 1 \"On\";\n";
    +
    568 const std::vector<std::string> statements = {
    +
    569 "BA_REL_ \"SignalType\" SG_ 1 Value 1;",
    +
    570 "BA_DEF_REL_ BU_SG_REL_ \"Relation\" INT 0 1;",
    +
    571 "BA_REL_ \"Relation\" BU_SG_REL_ ECU SG_ 1 Value 1;",
    +
    572 "SGTYPE_ Type : 8@1+ (1,0) [0|255] \"\" 0, Table;",
    +
    573 "SIG_TYPE_REF_ 1 Value : Type;",
    +
    574 "EV_ Environment : 0 [0|1] \"\" 0 1 DUMMY_NODE_VECTOR0 ECU;",
    +
    575 "ENVVAR_DATA_ Environment : 4;",
    +
    576 "VAL_TABLE_ Table 0 \"Off\" 1 \"On\";",
    +
    577 "BO_TX_BU_ 1 : ECU;", "VENDOR_EXTENSION_ data;", "UNKNOWN_"
    +
    578 };
    +
    579 for (const std::string &statement : statements) {
    +
    580 fixture.Write(definitions + statement + '\n');
    +
    581 const DbcParseResult result = DbcParser().ParseFileWithDiagnostics(fixture.path.string());
    +
    582 CHECK(!result.HasErrors(), statement);
    +
    583 QCOMPARE(result.diagnostics.size(), std::size_t(1U));
    +
    584 const DbcDiagnostic &warning = result.diagnostics[0];
    +
    585 CHECK(warning.severity == DbcDiagnosticSeverity::Warning &&
    +
    586 warning.code == DbcDiagnosticCode::UnsupportedStatement, statement);
    +
    587 QCOMPARE(warning.filePath, fixture.path.string());
    +
    588 QCOMPARE(warning.lineNumber, std::size_t(5U));
    +
    589 const std::string keyword = statement.substr(0U, statement.find(' '));
    +
    590 CHECK(warning.message.find("'" + keyword + "'") != std::string::npos, statement);
    +
    591 const DecodeDatabase runtime = DbcDecodeBuilder().Build(result.database);
    +
    592 RawCanFrame raw;
    +
    593 raw.canId = 1U;
    +
    594 raw.data = {1U};
    +
    595 const DecodedFrameValue decoded = DbcDecoder().Decode(runtime, raw);
    +
    596 CHECK(decoded.valid && decoded.signals.size() == 1U && decoded.signals[0].valid,
    +
    597 "Warnings preserve supported signal decoding");
    +
    598 QCOMPARE(decoded.signals[0].physicalValue, 12.0);
    +
    599 CHECK(decoded.signals[0].GetValueDescription() &&
    +
    600 *decoded.signals[0].GetValueDescription() == "On",
    +
    601 "Warnings preserve VAL_ descriptions");
    +
    602 QCOMPARE(runtime.GetFrames()[0].signals[0].comment, std::string("Signal comment"));
    +
    603 }
    +
    604}
    +
    605
    +
    606void DbcParserTest::unsupportedSignalValueTypes_data() {
    +
    607 QTest::addColumn<std::string>("statement");
    +
    608 QTest::addColumn<bool>("beforeDefinitions");
    +
    609 QTest::newRow("float32") << std::string("SIG_VALTYPE_ 1 Value : 1;") << false;
    +
    610 QTest::newRow("float64") << std::string("SIG_VALTYPE_ 1 Value : 2;") << false;
    +
    611 QTest::newRow("float32-before-definitions") << std::string("SIG_VALTYPE_ 1 Value : 1;") << true;
    +
    612 QTest::newRow("float64-before-definitions") << std::string("SIG_VALTYPE_ 1 Value : 2;") << true;
    +
    613 QTest::newRow("extended-frame") << std::string("SIG_VALTYPE_ 2147483649 Value : 1;") << false;
    +
    614 QTest::newRow("tabs-crlf") << std::string("\tSIG_VALTYPE_\t1\tValue : 2;\r") << false;
    +
    615 QTest::newRow("integer-declaration") << std::string("SIG_VALTYPE_ 1 Value : 0;") << false;
    +
    616 QTest::newRow("unknown-type") << std::string("SIG_VALTYPE_ 1 Value : 3;") << false;
    +
    617 QTest::newRow("malformed") << std::string("SIG_VALTYPE_") << false;
    +
    618}
    +
    619
    +
    620void DbcParserTest::unsupportedSignalValueTypes() {
    +
    621 QFETCH(std::string, statement);
    +
    622 QFETCH(bool, beforeDefinitions);
    +
    623 const std::string definitions = "BO_ 1 Frame: 8 ECU\n"
    +
    624 "SG_ Value : 0|64@1+ (1,0) [0|100] \"\" ECU\n";
    +
    625 Fixture fixture;
    +
    626 fixture.Write("VERSION \"\"\nNS_:\n\tSIG_VALTYPE_\nBS_:\nVENDOR_EXTENSION_ data;\n" +
    +
    627 (beforeDefinitions ? statement + "\n" + definitions : definitions + statement));
    +
    628 const std::size_t expectedLine = beforeDefinitions ? 6U : 8U;
    +
    629 const DbcParser parser;
    +
    630 const DbcParseResult result = parser.ParseFileWithDiagnostics(fixture.path.string());
    +
    631 QVERIFY(result.HasErrors());
    +
    632 QVERIFY(result.database.frames.empty());
    +
    633 QCOMPARE(result.diagnostics.size(), std::size_t(2));
    +
    634 QCOMPARE(result.diagnostics[0].severity, DbcDiagnosticSeverity::Warning);
    +
    635 QCOMPARE(result.diagnostics[0].code, DbcDiagnosticCode::UnsupportedStatement);
    +
    636 const DbcDiagnostic &error = result.diagnostics[1];
    +
    637 QCOMPARE(error.severity, DbcDiagnosticSeverity::Error);
    +
    638 QCOMPARE(error.code, DbcDiagnosticCode::UnsupportedSignalType);
    +
    639 QCOMPARE(error.lineNumber, expectedLine);
    +
    640 QCOMPARE(error.filePath, fixture.path.string());
    +
    641 QVERIFY(error.message.find("SIG_VALTYPE_") != std::string::npos);
    +
    642 try {
    +
    643 parser.ParseFile(fixture.path.string());
    +
    644 QFAIL("ParseFile must reject explicit signal value types");
    +
    645 } catch (const std::runtime_error &exception) {
    +
    646 QCOMPARE(std::string(exception.what()), fixture.path.string() + ":" +
    +
    647 std::to_string(expectedLine) + ": " + error.message);
    +
    648 }
    +
    649}
    +
    650
    +
    651void DbcParserTest::diagnosticNamespaceBoundaries() {
    +
    652 Fixture fixture;
    +
    653 const std::vector<std::string> endings = {
    +
    654 "BS_\n", "BS_:\n", "BS_ :\n", "BU_\n", "BU_:\n", "BU_ : ECU\n",
    +
    655 "BO_ 1 Frame: 1 ECU\n", "VENDOR_EXTENSION_ data;\n"
    +
    656 };
    +
    657 for (const std::string &ending : endings) {
    +
    658 fixture.Write("NS_ :\n\tSG_MUL_VAL_\n\nBA_\n" + ending + "SIG_TYPE_REF_\n");
    +
    659 const DbcParseResult result = DbcParser().ParseFileWithDiagnostics(fixture.path.string());
    +
    660 CHECK(!result.HasErrors(), ending);
    +
    661 const std::size_t expectedWarnings = ending.find("VENDOR_") == 0U ? 2U : 1U;
    +
    662 QCOMPARE(result.diagnostics.size(), expectedWarnings);
    +
    663 QCOMPARE(result.diagnostics.back().lineNumber, std::size_t(6U));
    +
    664 CHECK(result.diagnostics.back().message.find("SIG_TYPE_REF_") != std::string::npos,
    +
    665 "Unsupported keyword outside NS_ must produce a warning");
    +
    666 }
    +
    667 for (const std::string &definition : {std::string("BO_"), std::string("SG_")}) {
    +
    668 fixture.Write("NS_:\n\tBA_\n\t" + definition + '\n');
    +
    669 const DbcParseResult result = DbcParser().ParseFileWithDiagnostics(fixture.path.string());
    +
    670 CHECK(result.HasErrors(), "Malformed definition ends NS_ instead of being hidden");
    +
    671 QCOMPARE(result.diagnostics.size(), std::size_t(1U));
    +
    672 QCOMPARE(result.diagnostics[0].lineNumber, std::size_t(3U));
    +
    673 }
    +
    674}
    +
    675
    +
    676void DbcParserTest::diagnosticFormatting() {
    +
    677 Fixture fixture;
    +
    678 fixture.path += " with spaces";
    +
    679 const DbcParser parser;
    +
    680 for (const std::string &text : {std::string(), std::string(" \t\r\n\n")}) {
    +
    681 fixture.Write(text);
    +
    682 const DbcParseResult empty = parser.ParseFileWithDiagnostics(fixture.path.string());
    +
    683 CHECK(!empty.HasErrors() && empty.diagnostics.empty() && empty.database.frames.empty(),
    +
    684 "Empty and whitespace-only inputs are valid");
    +
    685 }
    +
    686 fixture.Write("\r\n\tVENDOR_EXTENSION_ data;\r\n\r\nUNKNOWN_");
    +
    687 const std::string relativePath = std::filesystem::relative(fixture.path).string();
    +
    688 const DbcParseResult warnings = parser.ParseFileWithDiagnostics(relativePath);
    +
    689 CHECK(!warnings.HasErrors() && warnings.database.frames.empty(), "Warnings-only input");
    +
    690 QCOMPARE(warnings.diagnostics.size(), std::size_t(2U));
    +
    691 QCOMPARE(warnings.diagnostics[0].lineNumber, std::size_t(2U));
    +
    692 QCOMPARE(warnings.diagnostics[1].lineNumber, std::size_t(4U));
    +
    693 for (const DbcDiagnostic &warning : warnings.diagnostics)
    +
    694 QCOMPARE(warning.filePath, relativePath);
    +
    695
    +
    696 fixture.Write("UNKNOWN_ first;\nBO_ invalid Frame: 1 ECU\n"
    +
    697 "UNKNOWN_ after_error;\nSG_ broken\n");
    +
    698 const DbcParseResult stopped = parser.ParseFileWithDiagnostics(relativePath);
    +
    699 CHECK(stopped.HasErrors() && stopped.database.frames.empty(), "First error stops parsing");
    +
    700 QCOMPARE(stopped.diagnostics.size(), std::size_t(2U));
    +
    701 QCOMPARE(stopped.diagnostics[0].lineNumber, std::size_t(1U));
    +
    702 QCOMPARE(stopped.diagnostics[1].lineNumber, std::size_t(2U));
    +
    703 QCOMPARE(stopped.diagnostics[1].filePath, relativePath);
    +
    704
    +
    705 fixture.Write("VAL_ 1 Missing 0 \"Off\";\nBO_ 1 Frame: 1 ECU\nUNKNOWN_ data;\n");
    +
    706 const DbcParseResult deferred = parser.ParseFileWithDiagnostics(relativePath);
    +
    707 CHECK(deferred.HasErrors() && deferred.database.frames.empty(), "Deferred error rejects database");
    +
    708 QCOMPARE(deferred.diagnostics.size(), std::size_t(2U));
    +
    709 CHECK(deferred.diagnostics[0].severity == DbcDiagnosticSeverity::Warning &&
    +
    710 deferred.diagnostics[1].severity == DbcDiagnosticSeverity::Error,
    +
    711 "Deferred errors follow warnings in detection order");
    +
    712 QCOMPARE(deferred.diagnostics[0].lineNumber, std::size_t(3U));
    +
    713 QCOMPARE(deferred.diagnostics[1].lineNumber, std::size_t(1U));
    +
    714 QCOMPARE(deferred.diagnostics[1].filePath, relativePath);
    +
    715 fixture.Write("BO_ 1 Frame: 1 ECU");
    +
    716 const DbcParseResult recovered = parser.ParseFileWithDiagnostics(relativePath);
    +
    717 CHECK(!recovered.HasErrors() && recovered.diagnostics.empty() && recovered.database.frames.size() == 1U,
    +
    718 "Same parser has clean state after errors and accepts missing final newline");
    +
    719}
    +
    720
    +
    721void DbcParserTest::validFrames() {
    +
    722 Fixture fixture;
    +
    723 struct ValidCase {
    +
    724 const char *line;
    +
    725 std::uint32_t id;
    +
    726 bool extended;
    +
    727 unsigned length;
    +
    728 };
    +
    729 const ValidCase valid[] = {
    +
    730 {"BO_ 0 Frame: 0 ECU", 0U, false, 0U},
    +
    731 {"BO_ 2047 Frame: 8 ECU", 2047U, false, 8U},
    +
    732 {"BO_ 2048 Frame: 64 ECU", 2048U, true, 64U},
    +
    733 {"BO_ 536870911 Frame: 255 ECU", 0x1FFFFFFFU, true, 255U},
    +
    734 {"BO_ 2147483649 Frame: 8 ECU", 1U, true, 8U},
    +
    735 {"BO_ 2684354559 Frame: 8 ECU", 0x1FFFFFFFU, true, 8U},
    +
    736 {" BO_\t256\tFrame :\t8\tECU \r", 256U, false, 8U}};
    +
    737 for (const ValidCase &test: valid) {
    +
    738 fixture.Write(std::string(test.line) + '\n');
    +
    739 try {
    +
    740 const DbcDatabase database = DbcParser().ParseFile(fixture.path.string());
    +
    741 QCOMPARE(database.frames.size(), std::size_t(1U));
    +
    742 const FrameInfo &frame = database.frames[0];
    +
    743 QCOMPARE(frame.name, std::string("Frame"));
    +
    744 QCOMPARE(frame.canId, test.id);
    +
    745 QCOMPARE(frame.isExtended, test.extended);
    +
    746 QCOMPARE(static_cast<unsigned>(frame.dlc), test.length);
    +
    747 QCOMPARE(frame.transmitter, std::string("ECU"));
    +
    748 } catch (const std::exception &error) { CHECK(false, error.what()); }
    +
    749 }
    +
    750}
    +
    751
    +
    752void DbcParserTest::invalidFrames() {
    +
    753 Fixture fixture;
    +
    754 const char *invalid[] = {
    +
    755 "BO_ invalid", "BO_", "BO_ 1", "BO_ 1 : 8 ECU", "BO_ 1 Frame 8 ECU",
    +
    756 "BO_ 1 Frame:", "BO_ 1 Frame: 8", "BO_ 1 Frame: 8 ECU extra",
    +
    757 "BO_ 1 Two Names: 8 ECU", "BO_ -1 Frame: 8 ECU", "BO_ +1 Frame: 8 ECU",
    +
    758 "BO_ 1x Frame: 8 ECU", "BO_ 0x123 Frame: 8 ECU", "BO_ 1.5 Frame: 8 ECU",
    +
    759 "BO_ 4294967296 Frame: 8 ECU", "BO_ 999999999999999999999 Frame: 8 ECU",
    +
    760 "BO_ 536870912 Frame: 8 ECU", "BO_ 1073741824 Frame: 8 ECU",
    +
    761 "BO_ 2684354560 Frame: 8 ECU", "BO_ 4294967295 Frame: 8 ECU",
    +
    762 "BO_ 1 Frame: -1 ECU", "BO_ 1 Frame: +8 ECU", "BO_ 1 Frame: 8x ECU",
    +
    763 "BO_ 1 Frame: 8.5 ECU", "BO_ 1 Frame: 256 ECU",
    +
    764 "BO_ 1 Frame: 4294967296 ECU", "BO_\tinvalid"};
    +
    765 for (const char *line: invalid) {
    +
    766 fixture.Write(std::string("VERSION \"test\"\n\n") + line + '\n');
    +
    767 try {
    +
    768 DbcParser().ParseFile(fixture.path.string());
    +
    769 CHECK(false, line);
    +
    770 } catch (const std::runtime_error &error) {
    +
    771 const std::string prefix = fixture.path.string() + ":3: ";
    +
    772 CHECK(std::string(error.what()).find(prefix) == 0U &&
    +
    773 std::string(error.what()).size() > prefix.size(),
    +
    774 line);
    +
    775 }
    +
    776 }
    +
    777}
    +
    778
    +
    779void DbcParserTest::duplicateFrames() {
    +
    780 Fixture fixture;
    +
    781 struct DuplicateCase {
    +
    782 const char *firstId;
    +
    783 const char *duplicateId;
    +
    784 const char *message;
    +
    785 };
    +
    786 const DuplicateCase cases[] = {
    +
    787 {"1", "1", "Duplicate standard frame CAN ID: 1"},
    +
    788 {"2147483649", "2147483649", "Duplicate extended frame CAN ID: 1"},
    +
    789 {"2048", "2147485696", "Duplicate extended frame CAN ID: 2048"},
    +
    790 {"2147485696", "2048", "Duplicate extended frame CAN ID: 2048"},
    +
    791 {"536870911", "2684354559", "Duplicate extended frame CAN ID: 536870911"}
    +
    792 };
    +
    793 const DbcParser parser;
    +
    794 for (const DuplicateCase &test : cases) {
    +
    795 fixture.Write("BO_ " + std::string(test.firstId) + " First: 8 ECU\n"
    +
    796 "BO_ 2 Other: 8 ECU\n\n"
    +
    797 " BO_\t" + test.duplicateId + "\tSecond: 8 ECU\n");
    +
    798 try {
    +
    799 parser.ParseFile(fixture.path.string());
    +
    800 CHECK(false, "Duplicate frame must be rejected");
    +
    801 } catch (const std::runtime_error &error) {
    +
    802 QCOMPARE(std::string(error.what()), fixture.path.string() + ":4: " + test.message);
    +
    803 }
    +
    804 }
    +
    805 fixture.Write("BO_ 1 First: 8 ECU\n");
    +
    806 QCOMPARE(parser.ParseFile(fixture.path.string()).frames.size(), std::size_t(1U));
    +
    807}
    +
    808
    +
    809void DbcParserTest::duplicateSignals() {
    +
    810 Fixture fixture;
    +
    811 const std::string first = "SG_ State : 0|8@1+ (1,0) [0|255] \"\" ECU\n";
    +
    812 const std::string duplicates[] = {
    +
    813 first,
    +
    814 " SG_\tState : 16|8@1+ (2,1) [0|511] \"V\" ECU\n",
    +
    815 "SG_ State m1 : 16|8@1+ (1,0) [0|255] \"\" ECU\n"
    +
    816 };
    +
    817 for (const std::string &duplicate : duplicates) {
    +
    818 fixture.Write("BO_ 1 Frame: 8 ECU\n" + first +
    +
    819 "SG_ Other : 8|8@1+ (1,0) [0|255] \"\" ECU\n\n" + duplicate);
    +
    820 try {
    +
    821 DbcParser().ParseFile(fixture.path.string());
    +
    822 CHECK(false, "Duplicate signal must be rejected");
    +
    823 } catch (const std::runtime_error &error) {
    +
    824 QCOMPARE(std::string(error.what()), fixture.path.string() +
    +
    825 ":5: Duplicate signal name 'State' in frame 'Frame'.");
    +
    826 }
    +
    827 }
    +
    828}
    +
    829
    +
    830void DbcParserTest::distinctDefinitions() {
    +
    831 Fixture fixture;
    +
    832 fixture.Write("BO_ 1 Shared: 1 ECU\n"
    +
    833 "SG_ State : 0|8@1+ (1,0) [0|255] \"\" ECU\n"
    +
    834 "BO_ 2147483649 Shared: 1 ECU\n"
    +
    835 "SG_ State : 0|8@1+ (1,0) [0|255] \"\" ECU\n"
    +
    836 "BO_ 2 Shared: 2 ECU\n"
    +
    837 "SG_ State : 0|8@1+ (1,0) [0|255] \"\" ECU\n"
    +
    838 "SG_ state : 8|8@1+ (1,0) [0|255] \"\" ECU\n"
    +
    839 "CM_ BO_ 1 \"Standard\";\n"
    +
    840 "CM_ BO_ 2147483649 \"Extended\";\n"
    +
    841 "CM_ SG_ 1 State \"Standard state\";\n"
    +
    842 "CM_ SG_ 2147483649 State \"Extended state\";\n");
    +
    843 const DbcDatabase parsed = DbcParser().ParseFile(fixture.path.string());
    +
    844 QCOMPARE(parsed.frames.size(), std::size_t(3U));
    +
    845 QCOMPARE(parsed.frames[0].canId, parsed.frames[1].canId);
    +
    846 CHECK(!parsed.frames[0].isExtended && parsed.frames[1].isExtended,
    +
    847 "Standard and extended frames with the same numeric ID remain distinct");
    +
    848 QCOMPARE(parsed.frames[0].comment, std::string("Standard"));
    +
    849 QCOMPARE(parsed.frames[1].comment, std::string("Extended"));
    +
    850 QCOMPARE(parsed.frames[0].signals[0].comment, std::string("Standard state"));
    +
    851 QCOMPARE(parsed.frames[1].signals[0].comment, std::string("Extended state"));
    +
    852 QCOMPARE(parsed.frames[2].signals.size(), std::size_t(2U));
    +
    853 const DecodeDatabase runtime = DbcDecodeBuilder().Build(parsed);
    +
    854 RawCanFrame raw;
    +
    855 raw.canId = 1U;
    +
    856 raw.data = {7U};
    +
    857 const DecodedFrameValue standard = DbcDecoder().Decode(runtime, raw);
    +
    858 QVERIFY(standard.valid);
    +
    859 QCOMPARE(standard.definition->comment, std::string("Standard"));
    +
    860 raw.isExtended = true;
    +
    861 const DecodedFrameValue extended = DbcDecoder().Decode(runtime, raw);
    +
    862 QVERIFY(extended.valid);
    +
    863 QCOMPARE(extended.definition->comment, std::string("Extended"));
    +
    864}
    +
    865
    +
    866void DbcParserTest::comments() {
    +
    867 Fixture fixture;
    +
    868 fixture.Write(
    +
    869 "CM_\tBO_\t1\t\"First\";\r\n"
    +
    870 "CM_\tSG_\t1\tState\t\" Line one \r\n"
    +
    871 "\r\n"
    +
    872 "\tBO_ is text; \\\"quoted\\\" and \\\\slash\"\t;\r\n"
    +
    873 "CM_ BO_ 2147483649 \"Extended\";\r\n"
    +
    874 "CM_ SG_ 2147483649 State \"Extended signal\";\r\n"
    +
    875 "BO_ 1 Standard: 1 ECU\r\n"
    +
    876 "CM_ BO_ 1 \"Last\";\r\n"
    +
    877 "SG_ State : 0|8@1+ (1,0) [0|255] \"\" ECU\r\n"
    +
    878 "BO_ 2147483649 Extended: 1 ECU\r\n"
    +
    879 "SG_ State : 0|8@1+ (1,0) [0|255] \"\" ECU");
    +
    880 const DbcParseResult result = DbcParser().ParseFileWithDiagnostics(fixture.path.string());
    +
    881 QVERIFY(!result.HasErrors());
    +
    882 QVERIFY(result.diagnostics.empty());
    +
    883 QCOMPARE(result.database.frames.size(), std::size_t(2));
    +
    884 QCOMPARE(result.database.frames[0].comment, std::string("Last"));
    +
    885 QCOMPARE(result.database.frames[0].signals[0].comment,
    +
    886 std::string(" Line one \n\n\tBO_ is text; \"quoted\" and \\slash"));
    +
    887 QCOMPARE(result.database.frames[1].comment, std::string("Extended"));
    +
    888 QCOMPARE(result.database.frames[1].signals[0].comment, std::string("Extended signal"));
    +
    889
    +
    890 const std::string ids[][2] = {
    +
    891 {"0", "0"},
    +
    892 {"2047", "2047"},
    +
    893 {"2048", "2147485696"},
    +
    894 {"536870911", "2684354559"},
    +
    895 {"2684354559", "536870911"}};
    +
    896 for (const std::string(&id)[2]: ids) {
    +
    897 fixture.Write("CM_ BO_ " + id[0] + " \"Frame\";\nCM_ SG_ " + id[0] +
    +
    898 " State \"Signal\";\nBO_ " + id[1] + " Frame: 1 ECU\n"
    +
    899 "SG_ State : 0|8@1+ (1,0) [0|255] \"\" ECU\n");
    +
    900 const DbcDatabase database = DbcParser().ParseFile(fixture.path.string());
    +
    901 QCOMPARE(database.frames[0].comment, std::string("Frame"));
    +
    902 QCOMPARE(database.frames[0].signals[0].comment, std::string("Signal"));
    +
    903 }
    +
    904 fixture.Write("CM_ BO_ 1 \"Line one\nLine two\";\n"
    +
    905 "BO_ 1 Frame: 0 ECU\nUNKNOWN_ value\nBO_ bad Frame: 0 ECU\n");
    +
    906 const DbcParseResult following = DbcParser().ParseFileWithDiagnostics(fixture.path.string());
    +
    907 QVERIFY(following.HasErrors());
    +
    908 QCOMPARE(following.diagnostics.size(), std::size_t(2));
    +
    909 QCOMPARE(following.diagnostics[0].lineNumber, std::size_t(4));
    +
    910 QCOMPARE(following.diagnostics[1].lineNumber, std::size_t(5));
    +
    911}
    +
    912
    +
    913void DbcParserTest::invalidComments_data() {
    +
    914 QTest::addColumn<QString>("statement");
    +
    915 const char *ids[] = {"-1", "+1", "1x", "0x1", "4294967296", "536870912",
    +
    916 "1073741824", "2684354560", "4294967295"};
    +
    917 for (const char *id: ids) {
    +
    918 const std::string frameRow = std::string("frame-id-") + id;
    +
    919 const std::string signalRow = std::string("signal-id-") + id;
    +
    920 QTest::newRow(frameRow.c_str()) << QString::fromStdString("CM_ BO_ " + std::string(id) + " \"Text\";");
    +
    921 QTest::newRow(signalRow.c_str()) << QString::fromStdString("CM_ SG_ " + std::string(id) + " State \"Text\";");
    +
    922 }
    +
    923 QTest::newRow("missing-id") << QStringLiteral("CM_ BO_");
    +
    924 QTest::newRow("missing-signal") << QStringLiteral("CM_ SG_ 1 \"Text\";");
    +
    925 QTest::newRow("missing-quote") << QStringLiteral("CM_ BO_ 1 Text;");
    +
    926 QTest::newRow("unclosed") << QStringLiteral("CM_ SG_ 1 State \"Text\n More");
    +
    927 QTest::newRow("invalid-escape") << QStringLiteral("CM_ BO_ 1 \"bad\\q\";");
    +
    928 QTest::newRow("dangling-escape") << QStringLiteral("CM_ BO_ 1 \"bad\\");
    +
    929 QTest::newRow("missing-semicolon") << QStringLiteral("CM_ SG_ 1 State \"Text\"");
    +
    930 QTest::newRow("trailing-text") << QStringLiteral("CM_ BO_ 1 \"Text\"; garbage");
    +
    931 QTest::newRow("extra-header") << QStringLiteral("CM_ BO_ 1 extra \"Text\";");
    +
    932 QTest::newRow("extra-string") << QStringLiteral("CM_ BO_ 1 \"Text\" \"More\";");
    +
    933 QTest::newRow("missing-frame") << QStringLiteral("CM_ BO_ 2 \"Text\";");
    +
    934 QTest::newRow("missing-signal-frame") << QStringLiteral("CM_ SG_ 2 State \"Text\";");
    +
    935 QTest::newRow("missing-signal-reference") << QStringLiteral("CM_ SG_ 1 Missing \"Text\";");
    +
    936 QTest::newRow("different-frame-type") << QStringLiteral("CM_ BO_ 2147483649 \"Text\";");
    +
    937 QTest::newRow("missing-multiline-reference") << QStringLiteral("CM_ SG_ 1 Missing \"Text\nMore\";");
    +
    938}
    +
    939
    +
    940void DbcParserTest::invalidComments() {
    +
    941 QFETCH(QString, statement);
    +
    942 Fixture fixture;
    +
    943 fixture.Write("BO_ 1 Frame: 1 ECU\nSG_ State : 0|8@1+ (1,0) [0|255] \"\" ECU\n\n" +
    +
    944 statement.toStdString());
    +
    945 const DbcParseResult result = DbcParser().ParseFileWithDiagnostics(fixture.path.string());
    +
    946 QVERIFY(result.HasErrors());
    +
    947 QVERIFY(result.database.frames.empty());
    +
    948 QCOMPARE(result.diagnostics.size(), std::size_t(1));
    +
    949 const DbcDiagnostic &error = result.diagnostics[0];
    +
    950 QCOMPARE(error.severity, DbcDiagnosticSeverity::Error);
    +
    951 QCOMPARE(error.code, DbcDiagnosticCode::InvalidStatement);
    +
    952 QCOMPARE(error.filePath, fixture.path.string());
    +
    953 QCOMPARE(error.lineNumber, std::size_t(4));
    +
    954 QVERIFY(!error.message.empty());
    +
    955 try {
    +
    956 DbcParser().ParseFile(fixture.path.string());
    +
    957 QFAIL("Malformed or unresolved comment must throw.");
    +
    958 } catch (const std::runtime_error &exception) {
    +
    959 QVERIFY(std::string(exception.what()).find(fixture.path.string() + ":4: ") == 0U);
    +
    960 }
    +
    961}
    +
    962
    +
    963void DbcParserTest::integration() {
    +
    964 Fixture fixture;
    +
    965 fixture.Write(
    +
    966 "BO_TX_BU_ 256 : ECU;\nBO_ 256 Frame: 8 ECU\n"
    +
    967 " SG_ Speed : 0|16@1+ (0.5,1) [0|100] \"rpm\" ECU\n"
    +
    968 "CM_ BO_ 256 \"Frame comment\";\nCM_ SG_ 256 Speed \"Signal comment\";\n"
    +
    969 "BO_ 257 Second: 8 ECU\n");
    +
    970 const DbcDatabase database = DbcParser().ParseFile(fixture.path.string());
    +
    971 CHECK(database.frames.size() == 2U && database.frames[0].signals.size() == 1U &&
    +
    972 database.frames[0].comment == "Frame comment" &&
    +
    973 database.frames[0].signals[0].comment == "Signal comment",
    +
    974 "Integration");
    +
    975 fixture.Write("\nSG_ Speed : invalid\n");
    +
    976 try {
    +
    977 DbcParser().ParseFile(fixture.path.string());
    +
    978 CHECK(false, "Signal error location");
    +
    979 } catch (const std::runtime_error &error) {
    +
    980 CHECK(std::string(error.what()).find(fixture.path.string() + ":2: ") == 0U,
    +
    981 "Signal error location");
    +
    982 }
    +
    983}
    984
    -
    985 const std::vector<std::string> invalid = {
    -
    986 first,
    -
    987 selector + "SG_ Other M : 16|8@1- (1,0) [-128|127] \"\" ECU\n",
    -
    988 "SG_ Mode M : 0|8@1- (1,0) [-128|127] \"\" ECU\n",
    -
    989 selector + "SG_ TooLarge m256 : 8|8@1+ (1,0) [0|255] \"\" ECU\n",
    -
    990 selector + first + "SG_ Same m1 : 15|8@0+ (1,0) [0|255] \"\" ECU\n",
    -
    991 selector + "SG_ Collision m1 : 0|8@1+ (1,0) [0|255] \"\" ECU\n",
    -
    992 selector + first + "SG_ Always : 8|8@1+ (1,0) [0|255] \"\" ECU\n",
    -
    993 "SG_MUL_VAL_ 1 First Mode 1-2;\n"};
    -
    994 for (const std::string &signals : invalid) {
    -
    995 fixture.Write("BO_ 1 Frame: 3 ECU\n" + signals);
    -
    996 bool rejected = false;
    -
    997 try {
    -
    998 DbcParser().ParseFile(fixture.path.string());
    -
    999 } catch (const std::runtime_error &error) {
    -
    1000 rejected = std::string(error.what()).find(fixture.path.string() + ":") == 0U;
    -
    1001 }
    -
    1002 CHECK(rejected, "Invalid multiplexing rejected with location");
    -
    1003 }
    -
    1004 for (const std::string marker : {"m", "m-1", "m+1", "m1M", "m18446744073709551616", "M m1"})
    -
    1005 RejectSignal(fixture, "SG_ Bad " + marker + " : 8|8@1+ (1,0) [0|255] \"\" ECU");
    -
    1006}
    -
    1007
    -
    1008void DbcParserTest::multiplexingBoundaries() {
    -
    1009 Fixture fixture;
    -
    1010 fixture.Write("BO_ 1 Frame: 9 ECU\n"
    -
    1011 "SG_ Branch m18446744073709551615 : 64|8@1+ (1,0) [0|255] \"\" ECU\n"
    -
    1012 "SG_ Mode M : 7|64@0+ (0,5) [0|1] \"\" ECU\n");
    -
    1013 const DbcDatabase parsed = DbcParser().ParseFile(fixture.path.string());
    -
    1014 DecodeDatabase runtime = DbcDecodeBuilder().Build(parsed);
    -
    1015 CHECK(runtime.GetFrames()[0].signals[0].multiplexing.value == std::numeric_limits<std::uint64_t>::max(),
    -
    1016 "Exact uint64 branch metadata");
    -
    1017 RawCanFrame raw;
    -
    1018 raw.canId = 1U;
    -
    1019 raw.data = std::vector<std::uint8_t>(9U, 255U);
    -
    1020 DecodedFrameValue result = DbcDecoder().Decode(runtime, raw);
    -
    1021 CHECK(result.signals[0].valid && result.signals[1].valid &&
    -
    1022 std::get<std::uint64_t>(result.signals[1].rawValue) == std::numeric_limits<std::uint64_t>::max(),
    -
    1023 "Motorola uint64 selector compares raw value before zero scaling");
    -
    1024 raw.data[7] = 254U;
    -
    1025 result = DbcDecoder().Decode(runtime, raw);
    -
    1026 CHECK(result.signals[0].status == SignalDecodeStatus::Inactive,
    -
    1027 "Adjacent large integers remain distinct");
    -
    1028 std::vector<DecodeFrame> definitions = runtime.GetFrames();
    -
    1029 definitions[0].signals[1].length = 0U;
    -
    1030 QVERIFY_THROWS_EXCEPTION(std::invalid_argument, runtime = DecodeDatabase(definitions));
    -
    1031 definitions[0].signals.pop_back();
    -
    1032 QVERIFY_THROWS_EXCEPTION(std::invalid_argument, runtime = DecodeDatabase(definitions));
    -
    1033 fixture.Write("BO_ 1 Frame: 2 ECU\n\n"
    -
    1034 "SG_ Branch m2 : 8|8@1+ (1,0) [0|255] \"\" ECU\n"
    -
    1035 "SG_ Mode M : 0|1@1+ (1,0) [0|1] \"\" ECU\n");
    -
    1036 bool rejected = false;
    -
    1037 try {
    -
    1038 DbcParser().ParseFile(fixture.path.string());
    -
    1039 } catch (const std::runtime_error &error) {
    -
    1040 rejected = std::string(error.what()).find(fixture.path.string() + ":3: ") == 0U;
    -
    1041 }
    -
    1042 CHECK(rejected, "Deferred validation reports branch source line");
    -
    1043 fixture.Write("BO_ 1 Frame: 2 ECU\n"
    -
    1044 "SG_ A : 8|8@1+ (1,0) [0|255] \"\" ECU\n"
    -
    1045 "SG_ B : 15|8@0+ (1,0) [0|255] \"\" ECU\n");
    -
    1046 rejected = false;
    -
    1047 try {
    -
    1048 DbcParser().ParseFile(fixture.path.string());
    -
    1049 } catch (const std::runtime_error &) {
    -
    1050 rejected = true;
    -
    1051 }
    -
    1052 CHECK(rejected, "Ordinary overlapping signals rejected");
    -
    1053}
    -
    1054
    -
    1055void DbcParserTest::invalidSignals() {
    -
    1056 Fixture fixture;
    -
    1057 const std::string ordinary = SignalLine("0", "8", "1+", "1", "0", "0", "255");
    -
    1058 for (const std::string value: {"-1", "+1", "0junk", "0x10", "1.5",
    -
    1059 "4294967296", "999999999999999999999"}) {
    -
    1060 RejectSignal(fixture, SignalLine(value, "8", "1+", "1", "0", "0", "255"));
    -
    1061 RejectSignal(fixture, SignalLine("0", value, "1+", "1", "0", "0", "255"));
    -
    1062 }
    -
    1063 for (const std::string length: {"0", "65"})
    -
    1064 RejectSignal(fixture, SignalLine("0", length, "1+", "1", "0", "0", "255"));
    -
    1065 for (const std::string order: {"2+", "9-", "1", "1x", "1++", "01+"})
    -
    1066 RejectSignal(fixture, SignalLine("0", "8", order, "1", "0", "0", "255"));
    -
    1067 for (const std::string value: {"1junk", "nan", "NaN", "inf", "-inf", "1e999", "1e-999", "0x1p2", "", "1e", "1.2.3"}) {
    -
    1068 RejectSignal(fixture, SignalLine("0", "8", "1+", value, "0", "0", "255"));
    -
    1069 RejectSignal(fixture, SignalLine("0", "8", "1+", "1", value, "0", "255"));
    -
    1070 RejectSignal(fixture, SignalLine("0", "8", "1+", "1", "0", value, "255"));
    -
    1071 RejectSignal(fixture, SignalLine("0", "8", "1+", "1", "0", "0", value));
    -
    1072 }
    -
    1073 RejectSignal(fixture, SignalLine("0", "8", "1+", "1", "0", "2", "1"));
    -
    1074 for (const std::string name: {"", "Two Names", "Value m1M"}) {
    -
    1075 std::string line = ordinary;
    -
    1076 line.replace(4U, 5U, name);
    -
    1077 RejectSignal(fixture, line);
    -
    1078 }
    -
    1079 for (const std::string broken: {
    -
    1080 "SG_", "SG_ Value", "SG_ Value : 0|8@1+ (1,0) [0|255] ECU",
    -
    1081 "SG_ Value : 0|8@1+ (1,0) [0|255] \"\"",
    -
    1082 "SG_ Value : 0|8@1+ (1,0) [0|255] \"\" ECU,",
    -
    1083 "SG_ Value : 0|8@1+ (1,0) [0|255] \"\" ECU extra"})
    -
    1084 RejectSignal(fixture, broken);
    -
    1085 RejectSignal(fixture, SignalLine("64", "1", "1+", "1", "0", "0", "1"));
    -
    1086 RejectSignal(fixture, SignalLine("63", "2", "1+", "1", "0", "0", "3"));
    -
    1087 RejectSignal(fixture, SignalLine("56", "2", "0+", "1", "0", "0", "3"));
    -
    1088 RejectSignal(fixture, SignalLine("4294967295", "64", "1+", "1", "0", "0", "1"));
    -
    1089 RejectSignal(fixture, ordinary, 0U);
    -
    1090 for (const char delimiter: {'|', '@', '(', ')', '[', ']', '"'}) {
    -
    1091 std::string line = ordinary;
    -
    1092 line.erase(line.find(delimiter), 1U);
    -
    1093 RejectSignal(fixture, line);
    -
    1094 }
    -
    1095}
    -
    1096
    -
    1097void DbcParserTest::validSignals() {
    -
    1098 Fixture fixture;
    -
    1099 const DbcParser signalParser;
    -
    1100 fixture.Write("BO_ 1 Frame: 8 ECU\n" +
    -
    1101 SignalLine("0", "8", "1+", "0", "0", "0", "0") + "\n" +
    -
    1102 SignalLine("8", "8", "1-", "-1", "0", "-255", "0", "Negative") + "\n");
    -
    1103 const DbcDatabase factors = signalParser.ParseFile(fixture.path.string());
    -
    1104 CHECK(factors.frames[0].signals.size() == 2U &&
    -
    1105 factors.frames[0].signals[0].factor == 0.0 &&
    -
    1106 factors.frames[0].signals[1].factor == -1.0,
    -
    1107 "Zero and negative factors and equal range boundaries");
    -
    1108
    -
    1109 struct SignalCase {
    -
    1110 const char *start;
    -
    1111 const char *length;
    -
    1112 const char *order;
    -
    1113 unsigned payload;
    -
    1114 };
    -
    1115 const SignalCase validSignals[] = {
    -
    1116 {"0", "1", "1+", 1U},
    -
    1117 {"7", "1", "0-", 1U},
    -
    1118 {"0", "64", "1+", 8U},
    -
    1119 {"7", "64", "0-", 8U},
    -
    1120 {"63", "1", "1+", 8U},
    -
    1121 {"56", "1", "0+", 8U},
    -
    1122 {"3", "12", "0+", 2U},
    -
    1123 {"2039", "1", "1+", 255U}};
    -
    1124 for (const SignalCase &test: validSignals) {
    -
    1125 fixture.Write("BO_ 1 Frame: " + std::to_string(test.payload) + " ECU\n" +
    -
    1126 SignalLine(test.start, test.length, test.order, ".5e+1", "-2.5E-1", "-1.", "+2e2") + "\n");
    -
    1127 try {
    -
    1128 const DbcDatabase parsed = signalParser.ParseFile(fixture.path.string());
    -
    1129 const SignalInfo &signal = parsed.frames.at(0).signals.at(0);
    -
    1130 QCOMPARE(signal.startBit, static_cast<std::uint32_t>(std::stoul(test.start)));
    -
    1131 QCOMPARE(signal.length, static_cast<std::uint32_t>(std::stoul(test.length)));
    -
    1132 QCOMPARE(signal.isLittleEndian, test.order[0] == '1');
    -
    1133 QCOMPARE(signal.isSigned, test.order[1] == '-');
    -
    1134 QCOMPARE(signal.factor, 5.0);
    -
    1135 QCOMPARE(signal.offset, -0.25);
    -
    1136 QCOMPARE(signal.minimum, -1.0);
    -
    1137 QCOMPARE(signal.maximum, 200.0);
    -
    1138 } catch (const std::exception &error) { CHECK(false, error.what()); }
    -
    1139 }
    -
    1140 fixture.Write("BO_ 1 Frame: 8 ECU\n"
    -
    1141 "SG_\tValue\t:\t0 | 8 @1+ ( 1 , 0 ) [ 0 | 255 ] \"rpm\" ECU1,\tECU2\n");
    -
    1142 try {
    -
    1143 const DbcDatabase parsed = signalParser.ParseFile(fixture.path.string());
    -
    1144 const SignalInfo &signal = parsed.frames.at(0).signals.at(0);
    -
    1145 CHECK(signal.name == "Value" && signal.unit == "rpm" &&
    -
    1146 signal.receivers == std::vector<std::string>({"ECU1", "ECU2"}),
    -
    1147 "Signal tabs, spaces, and receiver list");
    -
    1148 } catch (const std::exception &error) { CHECK(false, error.what()); }
    -
    1149}
    -
    1150
    -
    1151void DbcParserTest::longSignalDefinitions_data() {
    -
    1152 QTest::addColumn<std::string>("line");
    -
    1153 QTest::addColumn<bool>("valid");
    -
    1154 QTest::addColumn<std::string>("unit");
    -
    1155 const std::string longText(50000U, 'x');
    -
    1156 const std::string prefix = "SG_ Value : 0|8@1+ (1,0) [0|255] ";
    -
    1157 QTest::newRow("long-unit") << prefix + "\"" + longText + "\" ECU" << true << longText;
    -
    1158 QTest::newRow("unterminated-unit") << prefix + "\"" + longText << false << std::string();
    -
    1159 QTest::newRow("missing-receiver-separator") << prefix + "\"" + longText + "\"ECU" << false << std::string();
    -
    1160 QTest::newRow("long-name") << SignalLine("0", "8", "1+", "1", "0", "0", "255", longText) << true << std::string();
    -
    1161 QTest::newRow("long-receiver") << prefix + "\"\" " + longText << true << std::string();
    -
    1162 std::string receivers = "ECU";
    -
    1163 for (unsigned index = 0U; index < 10000U; ++index)
    -
    1164 receivers += ",ECU";
    -
    1165 QTest::newRow("many-receivers") << prefix + "\"\" " + receivers << true << std::string();
    -
    1166 QTest::newRow("trailing-receiver-comma") << prefix + "\"\" " + receivers + "," << false << std::string();
    -
    1167 QTest::newRow("long-factor") << SignalLine("0", "8", "1+", std::string(50000U, '0') + "1", "0", "0", "255") << true << std::string();
    -
    1168 QTest::newRow("invalid-long-factor") << SignalLine("0", "8", "1+", std::string(50000U, '0') + "1x", "0", "0", "255") << false << std::string();
    -
    1169 QTest::newRow("overflowing-start-bit") << SignalLine(std::string(50000U, '9'), "8", "1+", "1", "0", "0", "255") << false << std::string();
    -
    1170 QTest::newRow("missing-colon") << "SG_ " + longText << false << std::string();
    -
    1171}
    -
    1172
    -
    1173void DbcParserTest::longSignalDefinitions() {
    -
    1174 QFETCH(std::string, line);
    -
    1175 QFETCH(bool, valid);
    -
    1176 QFETCH(std::string, unit);
    -
    1177 Fixture fixture;
    -
    1178 fixture.Write("BO_ 1 Frame: 1 ECU\n" + line + "\n");
    -
    1179 const DbcParser parser;
    -
    1180 const DbcParseResult result = parser.ParseFileWithDiagnostics(fixture.path.string());
    -
    1181 QCOMPARE(result.HasErrors(), !valid);
    -
    1182 if (valid) {
    -
    1183 QVERIFY(result.diagnostics.empty());
    -
    1184 const DbcDatabase database = parser.ParseFile(fixture.path.string());
    -
    1185 QCOMPARE(database.frames.size(), std::size_t(1));
    -
    1186 QCOMPARE(database.frames[0].signals.size(), std::size_t(1));
    -
    1187 QCOMPARE(database.frames[0].signals[0].unit, unit);
    -
    1188 QCOMPARE(result.database.frames[0].signals[0].unit, unit);
    -
    1189 QCOMPARE(database.frames[0].signals[0].factor, 1.0);
    -
    1190 QVERIFY(!database.frames[0].signals[0].receivers.empty());
    -
    1191 } else {
    -
    1192 QVERIFY(result.database.frames.empty());
    -
    1193 QCOMPARE(result.diagnostics.size(), std::size_t(1));
    -
    1194 QCOMPARE(result.diagnostics[0].code, DbcDiagnosticCode::InvalidStatement);
    -
    1195 QCOMPARE(result.diagnostics[0].lineNumber, std::size_t(2));
    -
    1196 QCOMPARE(result.diagnostics[0].filePath, fixture.path.string());
    -
    1197 RejectSignal(fixture, line, 1U);
    -
    1198 }
    -
    1199}
    -
    1200
    -
    1201void DbcParserTest::CheckPhysicalNumbers() {
    -
    1202 Fixture fixture;
    -
    1203 struct PhysicalCase {
    -
    1204 const char *factor;
    -
    1205 const char *offset;
    -
    1206 const char *minimum;
    -
    1207 const char *maximum;
    -
    1208 double expectedFactor;
    -
    1209 double expectedOffset;
    -
    1210 double expectedMinimum;
    -
    1211 double expectedMaximum;
    -
    1212 };
    -
    1213 const PhysicalCase valid[] = {
    -
    1214 {"+1.25", "-0.5", "-2.75", "255.5", 1.25, -0.5, -2.75, 255.5},
    -
    1215 {"+.5e+1", "-2.5E-1", "-1.", "+2e2", 5.0, -0.25, -1.0, 200.0},
    -
    1216 {"0e-999", "+0.", "-.0", ".0e+999", 0.0, 0.0, 0.0, 0.0},
    -
    1217 {"1.7976931348623157e308", "2.2250738585072014e-308", "-1e308", "+1e308",
    -
    1218 std::numeric_limits<double>::max(), std::numeric_limits<double>::min(), -1e308, 1e308}};
    -
    1219 for (const PhysicalCase &test: valid) {
    -
    1220 fixture.Write("BO_ 1 Frame: 1 ECU\n" +
    -
    1221 SignalLine("0", "8", "1+", test.factor, test.offset, test.minimum, test.maximum) + "\n");
    -
    1222 const DbcParseResult result = DbcParser().ParseFileWithDiagnostics(fixture.path.string());
    -
    1223 QVERIFY(!result.HasErrors());
    -
    1224 QVERIFY(result.diagnostics.empty());
    -
    1225 QCOMPARE(result.database.frames.size(), std::size_t(1));
    -
    1226 const SignalInfo &signal = result.database.frames[0].signals[0];
    -
    1227 QCOMPARE(signal.factor, test.expectedFactor);
    -
    1228 QCOMPARE(signal.offset, test.expectedOffset);
    -
    1229 QCOMPARE(signal.minimum, test.expectedMinimum);
    -
    1230 QCOMPARE(signal.maximum, test.expectedMaximum);
    -
    1231 }
    -
    1232
    -
    1233 const char *invalid[] = {"1,5", "1.234,5", "1,234.5", "1junk", "nan", "inf", "-inf",
    -
    1234 "1e999", "1e-999", "1e-320", "-1e-320", "0x1p2", "1e", "1.2.3"};
    -
    1235 for (const char *value: invalid) {
    -
    1236 RejectSignal(fixture, SignalLine("0", "8", "1+", value, "0", "0", "255"));
    -
    1237 RejectSignal(fixture, SignalLine("0", "8", "1+", "1", value, "0", "255"));
    -
    1238 RejectSignal(fixture, SignalLine("0", "8", "1+", "1", "0", value, "255"));
    -
    1239 RejectSignal(fixture, SignalLine("0", "8", "1+", "1", "0", "0", value));
    -
    1240 }
    -
    1241 RejectSignal(fixture, SignalLine("0", "8", "1+", "1.25", "0", "2.5", "1.5"));
    -
    1242}
    -
    1243
    -
    1244void DbcParserTest::physicalNumbersCppLocale() {
    -
    1245 const ApplicationLocaleGuard guard;
    -
    1246 std::locale::global(std::locale(std::locale::classic(), new CommaDecimalPunctuation));
    -
    1247 QCOMPARE(std::use_facet<std::numpunct<char>>(std::locale()).decimal_point(), ',');
    -
    1248 CheckPhysicalNumbers();
    -
    1249 QCOMPARE(std::use_facet<std::numpunct<char>>(std::locale()).decimal_point(), ',');
    -
    1250}
    -
    1251
    -
    1252void DbcParserTest::physicalNumbersCLocale() {
    -
    1253 const ApplicationLocaleGuard guard;
    -
    1254 bool found = false;
    -
    1255 const char *names[] = {"de_DE.UTF-8", "de_DE.utf8", "fr_FR.UTF-8", "fr_FR.utf8",
    -
    1256 "German_Germany.1252", "French_France.1252"};
    -
    1257 for (const char *name: names) {
    -
    1258 if (std::setlocale(LC_NUMERIC, name) && std::string(std::localeconv()->decimal_point) == ",") {
    -
    1259 found = true;
    -
    1260 break;
    -
    1261 }
    -
    1262 }
    -
    1263 if (!found)
    -
    1264 QSKIP("No named comma-decimal C locale is installed; generate de_DE.UTF-8 and set LOCPATH.");
    -
    1265 QCOMPARE(std::string(std::localeconv()->decimal_point), std::string(","));
    -
    1266 CheckPhysicalNumbers();
    -
    1267 QCOMPARE(std::string(std::localeconv()->decimal_point), std::string(","));
    -
    1268}
    -
    1269
    -
    1270void DbcParserTest::dispatch() {
    -
    1271 CountingHandler::instances = 0;
    -
    1272 CountingHandler::calls = 0;
    -
    1273 DbcDatabase dispatchDatabase;
    -
    1274 DbcParseContext context{dispatchDatabase, nullptr};
    -
    1275 DbcStatementDispatcher dispatcher;
    -
    1276 dispatcher.RegisterType<CountingHandler>("TEST_");
    -
    1277 CHECK(dispatcher.Dispatch("TEST_ first", context) &&
    -
    1278 dispatcher.Dispatch("TEST_ second", context) &&
    -
    1279 CountingHandler::instances == 1 && CountingHandler::calls == 2 &&
    -
    1280 dispatchDatabase.frames.size() == 2U,
    -
    1281 "Factory dispatch and handler reuse");
    -
    1282 CHECK(!dispatcher.Dispatch("TEST_OTHER data", context) &&
    -
    1283 !dispatcher.Dispatch("BO_TX_BU_ 1 : ECU;", context) &&
    -
    1284 !dispatcher.Dispatch("VAL_TABLE_ States 0 \"Stopped\";", context) &&
    -
    1285 CountingHandler::calls == 2,
    -
    1286 "Exact and unknown keyword dispatch");
    -
    1287}
    -
    1288
    -
    1289void DbcParserTest::independentSessions() {
    -
    1290 Fixture fixture;
    -
    1291 const DbcParser parser;
    -
    1292 fixture.Write("BO_ 1 First: 8 ECU\n");
    -
    1293 CHECK(parser.ParseFile(fixture.path.string()).frames.size() == 1U, "First parse");
    -
    1294 fixture.Write("SG_ Speed : invalid\n");
    -
    1295 try {
    -
    1296 parser.ParseFile(fixture.path.string());
    -
    1297 CHECK(false, "Independent parse context");
    -
    1298 } catch (const std::runtime_error &error) {
    -
    1299 CHECK(std::string(error.what()).find("Signal found before any frame") !=
    -
    1300 std::string::npos,
    -
    1301 "Independent parse context");
    -
    1302 }
    -
    1303 fixture.Write("BO_ 2 Second: 8 ECU\n");
    -
    1304 const DbcDatabase recovered = parser.ParseFile(fixture.path.string());
    -
    1305 CHECK(recovered.frames.size() == 1U && recovered.frames[0].canId == 2U,
    -
    1306 "Fresh parse after error");
    -
    1307}
    -
    1308
    -
    1309
    -
    1310void DbcParserTest::valueDescriptions() {
    -
    1311 Fixture fixture;
    -
    1312 fixture.Write(
    -
    1313 "VAL_\t256\tState\t0\t\"Off\"\t1 \"On\" 2 \"Error with spaces; and \\\"quotes\\\"\";\n"
    -
    1314 "BO_ 256 Status: 8 ECU\n"
    -
    1315 "SG_ State : 0|8@1+ (2,10) [0|520] \"\" ECU\n"
    -
    1316 "VAL_ 256 State 3 \"\";\n"
    -
    1317 "VAL_ 2147483904 State -9223372036854775808 \"Minimum\" -1 \"Negative\" 9223372036854775807 \"Maximum\";\n"
    -
    1318 "BO_ 2147483904 Extended: 8 ECU\n"
    -
    1319 "SG_ State : 0|64@1- (1,0) [-1e20|1e20] \"\" ECU\n"
    -
    1320 "BO_ 2048 Unsigned: 8 ECU\n"
    -
    1321 "SG_ State : 0|64@1+ (1,0) [0|2e20] \"\" ECU\n"
    -
    1322 "VAL_ 2147485696 State 9223372036854775808 \"High\" 18446744073709551615 \"Full\";\n");
    -
    1323 const DbcDatabase parsed = DbcParser().ParseFile(fixture.path.string());
    -
    1324 const DecodeDatabase runtime = DbcDecodeBuilder().Build(parsed);
    -
    1325 const std::unique_ptr<TreeNode> tree = DbcTreeBuilder().Build(parsed);
    -
    1326 CHECK(tree->GetChild(0)->GetSignal() == nullptr, "Frame node");
    -
    1327 for (std::size_t index = 0; index < parsed.frames.size(); ++index) {
    -
    1328 CHECK(tree->GetChild(index)->GetChild(0)->GetSignal()->valueDescriptions ==
    -
    1329 parsed.frames[index].signals[0].valueDescriptions &&
    -
    1330 runtime.GetFrames()[index].signals[0].valueDescriptions ==
    -
    1331 parsed.frames[index].signals[0].valueDescriptions,
    -
    1332 "All mappings survive both builders");
    -
    1333 }
    -
    1334 CHECK(tree->GetChild(0)->GetChild(0)->GetSignal()->valueDescriptions ==
    -
    1335 runtime.GetFrames()[0].signals[0].valueDescriptions,
    -
    1336 "Both builders preserve descriptions");
    -
    1337 RawCanFrame frame;
    -
    1338 frame.canId = 256;
    -
    1339 frame.data.resize(8);
    -
    1340 const std::string expected[] = {"Off", "On", "Error with spaces; and \"quotes\"", ""};
    -
    1341 for (unsigned value = 0; value < 5; ++value) {
    -
    1342 frame.data[0] = value;
    -
    1343 const DecodedSignalValue decoded = DbcDecoder().Decode(runtime, frame).signals[0];
    -
    1344 CHECK(decoded.valid && decoded.physicalValue == value * 2 + 10, "Numeric scaling unchanged");
    -
    1345 const std::string *description = decoded.GetValueDescription();
    -
    1346 CHECK(value < 4 ? description && *description == expected[value] : !description,
    -
    1347 "Raw lookup and unknown value");
    +
    985void DbcParserTest::multiplexing() {
    +
    986 Fixture fixture;
    +
    987 const std::string selector = "SG_ Mode M : 0|8@1+ (10,3) [0|255] \"\" ECU\n";
    +
    988 const std::string first = "SG_ First m1 : 8|8@1- (2,0) [-128|127] \"V\" ECU\n";
    +
    989 const std::string second = "SG_ Second m2 : 15|8@0+ (1,0) [0|255] \"\" ECU\n";
    +
    990 const std::string common = "SG_ Common : 16|8@1+ (1,0) [0|255] \"\" ECU\n";
    +
    991 fixture.Write("BO_ 1 Frame: 3 ECU\n" + first + second + common + selector +
    +
    992 "VAL_ 1 First -1 \"Negative\";\n");
    +
    993 const DbcDatabase parsed = DbcParser().ParseFile(fixture.path.string());
    +
    994 const DecodeDatabase runtime = DbcDecodeBuilder().Build(parsed);
    +
    995 CHECK(parsed.frames[0].signals[3].multiplexing.role == MultiplexRole::Selector,
    +
    996 "Selector declared after branches");
    +
    997 const std::unique_ptr<TreeNode> tree = DbcTreeBuilder().Build(parsed);
    +
    998 CHECK(tree->GetChild(0U)->GetChild(0U)->GetSignal()->multiplexing.value == 1U,
    +
    999 "Tree preserves multiplexing");
    +
    1000 RawCanFrame raw;
    +
    1001 raw.canId = 1U;
    +
    1002 raw.data = {1U, 255U, 7U};
    +
    1003 DecodedFrameValue result = DbcDecoder().Decode(runtime, raw);
    +
    1004 CHECK(result.signals[0].valid && std::get<std::int64_t>(result.signals[0].rawValue) == -1 &&
    +
    1005 result.signals[0].physicalValue == -2 && *result.signals[0].GetValueDescription() == "Negative",
    +
    1006 "Active signed branch and VAL_ use exact raw selector");
    +
    1007 CHECK(!result.signals[1].valid && result.signals[1].status == SignalDecodeStatus::Inactive &&
    +
    1008 result.signals[1].GetValueDescription() == nullptr && result.signals[2].valid,
    +
    1009 "Inactive branch and unconditional signal");
    +
    1010 raw.data[0] = 2U;
    +
    1011 result = DbcDecoder().Decode(runtime, raw);
    +
    1012 CHECK(result.signals[0].status == SignalDecodeStatus::Inactive && result.signals[1].valid &&
    +
    1013 std::get<std::uint64_t>(result.signals[1].rawValue) == 255U,
    +
    1014 "Motorola branch becomes active");
    +
    1015 raw.data = {3U};
    +
    1016 result = DbcDecoder().Decode(runtime, raw);
    +
    1017 CHECK(result.signals[0].status == SignalDecodeStatus::Inactive &&
    +
    1018 result.signals[1].status == SignalDecodeStatus::Inactive &&
    +
    1019 result.signals[2].status == SignalDecodeStatus::InsufficientPayload,
    +
    1020 "Unmapped selector leaves branches inactive even with missing bytes");
    +
    1021 raw.data = {1U};
    +
    1022 result = DbcDecoder().Decode(runtime, raw);
    +
    1023 CHECK(result.signals[0].status == SignalDecodeStatus::InsufficientPayload,
    +
    1024 "Active branch requires payload");
    +
    1025 raw.data.clear();
    +
    1026 result = DbcDecoder().Decode(runtime, raw);
    +
    1027 CHECK(result.signals[0].status == SignalDecodeStatus::SelectorUnavailable &&
    +
    1028 result.signals[3].status == SignalDecodeStatus::InsufficientPayload,
    +
    1029 "Unavailable selector does not imply inactive branch");
    +
    1030
    +
    1031 const std::vector<std::string> invalid = {
    +
    1032 first,
    +
    1033 selector + "SG_ Other M : 16|8@1- (1,0) [-128|127] \"\" ECU\n",
    +
    1034 "SG_ Mode M : 0|8@1- (1,0) [-128|127] \"\" ECU\n",
    +
    1035 selector + "SG_ TooLarge m256 : 8|8@1+ (1,0) [0|255] \"\" ECU\n",
    +
    1036 selector + first + "SG_ Same m1 : 15|8@0+ (1,0) [0|255] \"\" ECU\n",
    +
    1037 selector + "SG_ Collision m1 : 0|8@1+ (1,0) [0|255] \"\" ECU\n",
    +
    1038 selector + first + "SG_ Always : 8|8@1+ (1,0) [0|255] \"\" ECU\n",
    +
    1039 "SG_MUL_VAL_ 1 First Mode 1-2;\n"};
    +
    1040 for (const std::string &signals : invalid) {
    +
    1041 fixture.Write("BO_ 1 Frame: 3 ECU\n" + signals);
    +
    1042 bool rejected = false;
    +
    1043 try {
    +
    1044 DbcParser().ParseFile(fixture.path.string());
    +
    1045 } catch (const std::runtime_error &error) {
    +
    1046 rejected = std::string(error.what()).find(fixture.path.string() + ":") == 0U;
    +
    1047 }
    +
    1048 CHECK(rejected, "Invalid multiplexing rejected with location");
    +
    1049 }
    +
    1050 for (const std::string marker : {"m", "m-1", "m+1", "m1M", "m18446744073709551616", "M m1"})
    +
    1051 RejectSignal(fixture, "SG_ Bad " + marker + " : 8|8@1+ (1,0) [0|255] \"\" ECU");
    +
    1052}
    +
    1053
    +
    1054void DbcParserTest::multiplexingBoundaries() {
    +
    1055 Fixture fixture;
    +
    1056 fixture.Write("BO_ 1 Frame: 9 ECU\n"
    +
    1057 "SG_ Branch m18446744073709551615 : 64|8@1+ (1,0) [0|255] \"\" ECU\n"
    +
    1058 "SG_ Mode M : 7|64@0+ (0,5) [0|1] \"\" ECU\n");
    +
    1059 const DbcDatabase parsed = DbcParser().ParseFile(fixture.path.string());
    +
    1060 DecodeDatabase runtime = DbcDecodeBuilder().Build(parsed);
    +
    1061 CHECK(runtime.GetFrames()[0].signals[0].multiplexing.value == std::numeric_limits<std::uint64_t>::max(),
    +
    1062 "Exact uint64 branch metadata");
    +
    1063 RawCanFrame raw;
    +
    1064 raw.canId = 1U;
    +
    1065 raw.data = std::vector<std::uint8_t>(9U, 255U);
    +
    1066 DecodedFrameValue result = DbcDecoder().Decode(runtime, raw);
    +
    1067 CHECK(result.signals[0].valid && result.signals[1].valid &&
    +
    1068 std::get<std::uint64_t>(result.signals[1].rawValue) == std::numeric_limits<std::uint64_t>::max(),
    +
    1069 "Motorola uint64 selector compares raw value before zero scaling");
    +
    1070 raw.data[7] = 254U;
    +
    1071 result = DbcDecoder().Decode(runtime, raw);
    +
    1072 CHECK(result.signals[0].status == SignalDecodeStatus::Inactive,
    +
    1073 "Adjacent large integers remain distinct");
    +
    1074 std::vector<DecodeFrame> definitions = runtime.GetFrames();
    +
    1075 definitions[0].signals[1].length = 0U;
    +
    1076 QVERIFY_THROWS_EXCEPTION(std::invalid_argument, runtime = DecodeDatabase(definitions));
    +
    1077 definitions[0].signals.pop_back();
    +
    1078 QVERIFY_THROWS_EXCEPTION(std::invalid_argument, runtime = DecodeDatabase(definitions));
    +
    1079 fixture.Write("BO_ 1 Frame: 2 ECU\n\n"
    +
    1080 "SG_ Branch m2 : 8|8@1+ (1,0) [0|255] \"\" ECU\n"
    +
    1081 "SG_ Mode M : 0|1@1+ (1,0) [0|1] \"\" ECU\n");
    +
    1082 bool rejected = false;
    +
    1083 try {
    +
    1084 DbcParser().ParseFile(fixture.path.string());
    +
    1085 } catch (const std::runtime_error &error) {
    +
    1086 rejected = std::string(error.what()).find(fixture.path.string() + ":3: ") == 0U;
    +
    1087 }
    +
    1088 CHECK(rejected, "Deferred validation reports branch source line");
    +
    1089 fixture.Write("BO_ 1 Frame: 2 ECU\n"
    +
    1090 "SG_ A : 8|8@1+ (1,0) [0|255] \"\" ECU\n"
    +
    1091 "SG_ B : 15|8@0+ (1,0) [0|255] \"\" ECU\n");
    +
    1092 rejected = false;
    +
    1093 try {
    +
    1094 DbcParser().ParseFile(fixture.path.string());
    +
    1095 } catch (const std::runtime_error &) {
    +
    1096 rejected = true;
    +
    1097 }
    +
    1098 CHECK(rejected, "Ordinary overlapping signals rejected");
    +
    1099}
    +
    1100
    +
    1101void DbcParserTest::invalidSignals() {
    +
    1102 Fixture fixture;
    +
    1103 const std::string ordinary = SignalLine("0", "8", "1+", "1", "0", "0", "255");
    +
    1104 for (const std::string value: {"-1", "+1", "0junk", "0x10", "1.5",
    +
    1105 "4294967296", "999999999999999999999"}) {
    +
    1106 RejectSignal(fixture, SignalLine(value, "8", "1+", "1", "0", "0", "255"));
    +
    1107 RejectSignal(fixture, SignalLine("0", value, "1+", "1", "0", "0", "255"));
    +
    1108 }
    +
    1109 for (const std::string length: {"0", "65"})
    +
    1110 RejectSignal(fixture, SignalLine("0", length, "1+", "1", "0", "0", "255"));
    +
    1111 for (const std::string order: {"2+", "9-", "1", "1x", "1++", "01+"})
    +
    1112 RejectSignal(fixture, SignalLine("0", "8", order, "1", "0", "0", "255"));
    +
    1113 for (const std::string value: {"1junk", "nan", "NaN", "inf", "-inf", "1e999", "1e-999", "0x1p2", "", "1e", "1.2.3"}) {
    +
    1114 RejectSignal(fixture, SignalLine("0", "8", "1+", value, "0", "0", "255"));
    +
    1115 RejectSignal(fixture, SignalLine("0", "8", "1+", "1", value, "0", "255"));
    +
    1116 RejectSignal(fixture, SignalLine("0", "8", "1+", "1", "0", value, "255"));
    +
    1117 RejectSignal(fixture, SignalLine("0", "8", "1+", "1", "0", "0", value));
    +
    1118 }
    +
    1119 RejectSignal(fixture, SignalLine("0", "8", "1+", "1", "0", "2", "1"));
    +
    1120 for (const std::string name: {"", "Two Names", "Value m1M"}) {
    +
    1121 std::string line = ordinary;
    +
    1122 line.replace(4U, 5U, name);
    +
    1123 RejectSignal(fixture, line);
    +
    1124 }
    +
    1125 for (const std::string broken: {
    +
    1126 "SG_", "SG_ Value", "SG_ Value : 0|8@1+ (1,0) [0|255] ECU",
    +
    1127 "SG_ Value : 0|8@1+ (1,0) [0|255] \"\"",
    +
    1128 "SG_ Value : 0|8@1+ (1,0) [0|255] \"\" ECU,",
    +
    1129 "SG_ Value : 0|8@1+ (1,0) [0|255] \"\" ECU extra"})
    +
    1130 RejectSignal(fixture, broken);
    +
    1131 RejectSignal(fixture, SignalLine("64", "1", "1+", "1", "0", "0", "1"));
    +
    1132 RejectSignal(fixture, SignalLine("63", "2", "1+", "1", "0", "0", "3"));
    +
    1133 RejectSignal(fixture, SignalLine("56", "2", "0+", "1", "0", "0", "3"));
    +
    1134 RejectSignal(fixture, SignalLine("4294967295", "64", "1+", "1", "0", "0", "1"));
    +
    1135 RejectSignal(fixture, ordinary, 0U);
    +
    1136 for (const char delimiter: {'|', '@', '(', ')', '[', ']', '"'}) {
    +
    1137 std::string line = ordinary;
    +
    1138 line.erase(line.find(delimiter), 1U);
    +
    1139 RejectSignal(fixture, line);
    +
    1140 }
    +
    1141}
    +
    1142
    +
    1143void DbcParserTest::validSignals() {
    +
    1144 Fixture fixture;
    +
    1145 const DbcParser signalParser;
    +
    1146 fixture.Write("BO_ 1 Frame: 8 ECU\n" +
    +
    1147 SignalLine("0", "8", "1+", "0", "0", "0", "0") + "\n" +
    +
    1148 SignalLine("8", "8", "1-", "-1", "0", "-255", "0", "Negative") + "\n");
    +
    1149 const DbcDatabase factors = signalParser.ParseFile(fixture.path.string());
    +
    1150 CHECK(factors.frames[0].signals.size() == 2U &&
    +
    1151 factors.frames[0].signals[0].factor == 0.0 &&
    +
    1152 factors.frames[0].signals[1].factor == -1.0,
    +
    1153 "Zero and negative factors and equal range boundaries");
    +
    1154
    +
    1155 struct SignalCase {
    +
    1156 const char *start;
    +
    1157 const char *length;
    +
    1158 const char *order;
    +
    1159 unsigned payload;
    +
    1160 };
    +
    1161 const SignalCase validSignals[] = {
    +
    1162 {"0", "1", "1+", 1U},
    +
    1163 {"7", "1", "0-", 1U},
    +
    1164 {"0", "64", "1+", 8U},
    +
    1165 {"7", "64", "0-", 8U},
    +
    1166 {"63", "1", "1+", 8U},
    +
    1167 {"56", "1", "0+", 8U},
    +
    1168 {"3", "12", "0+", 2U},
    +
    1169 {"2039", "1", "1+", 255U}};
    +
    1170 for (const SignalCase &test: validSignals) {
    +
    1171 fixture.Write("BO_ 1 Frame: " + std::to_string(test.payload) + " ECU\n" +
    +
    1172 SignalLine(test.start, test.length, test.order, ".5e+1", "-2.5E-1", "-1.", "+2e2") + "\n");
    +
    1173 try {
    +
    1174 const DbcDatabase parsed = signalParser.ParseFile(fixture.path.string());
    +
    1175 const SignalInfo &signal = parsed.frames.at(0).signals.at(0);
    +
    1176 QCOMPARE(signal.startBit, static_cast<std::uint32_t>(std::stoul(test.start)));
    +
    1177 QCOMPARE(signal.length, static_cast<std::uint32_t>(std::stoul(test.length)));
    +
    1178 QCOMPARE(signal.isLittleEndian, test.order[0] == '1');
    +
    1179 QCOMPARE(signal.isSigned, test.order[1] == '-');
    +
    1180 QCOMPARE(signal.factor, 5.0);
    +
    1181 QCOMPARE(signal.offset, -0.25);
    +
    1182 QCOMPARE(signal.minimum, -1.0);
    +
    1183 QCOMPARE(signal.maximum, 200.0);
    +
    1184 } catch (const std::exception &error) { CHECK(false, error.what()); }
    +
    1185 }
    +
    1186 fixture.Write("BO_ 1 Frame: 8 ECU\n"
    +
    1187 "SG_\tValue\t:\t0 | 8 @1+ ( 1 , 0 ) [ 0 | 255 ] \"rpm\" ECU1,\tECU2\n");
    +
    1188 try {
    +
    1189 const DbcDatabase parsed = signalParser.ParseFile(fixture.path.string());
    +
    1190 const SignalInfo &signal = parsed.frames.at(0).signals.at(0);
    +
    1191 CHECK(signal.name == "Value" && signal.unit == "rpm" &&
    +
    1192 signal.receivers == std::vector<std::string>({"ECU1", "ECU2"}),
    +
    1193 "Signal tabs, spaces, and receiver list");
    +
    1194 } catch (const std::exception &error) { CHECK(false, error.what()); }
    +
    1195}
    +
    1196
    +
    1197void DbcParserTest::longSignalDefinitions_data() {
    +
    1198 QTest::addColumn<std::string>("line");
    +
    1199 QTest::addColumn<bool>("valid");
    +
    1200 QTest::addColumn<std::string>("unit");
    +
    1201 const std::string longText(50000U, 'x');
    +
    1202 const std::string prefix = "SG_ Value : 0|8@1+ (1,0) [0|255] ";
    +
    1203 QTest::newRow("long-unit") << prefix + "\"" + longText + "\" ECU" << true << longText;
    +
    1204 QTest::newRow("unterminated-unit") << prefix + "\"" + longText << false << std::string();
    +
    1205 QTest::newRow("missing-receiver-separator") << prefix + "\"" + longText + "\"ECU" << false << std::string();
    +
    1206 QTest::newRow("long-name") << SignalLine("0", "8", "1+", "1", "0", "0", "255", longText) << true << std::string();
    +
    1207 QTest::newRow("long-receiver") << prefix + "\"\" " + longText << true << std::string();
    +
    1208 std::string receivers = "ECU";
    +
    1209 for (unsigned index = 0U; index < 10000U; ++index)
    +
    1210 receivers += ",ECU";
    +
    1211 QTest::newRow("many-receivers") << prefix + "\"\" " + receivers << true << std::string();
    +
    1212 QTest::newRow("trailing-receiver-comma") << prefix + "\"\" " + receivers + "," << false << std::string();
    +
    1213 QTest::newRow("long-factor") << SignalLine("0", "8", "1+", std::string(50000U, '0') + "1", "0", "0", "255") << true << std::string();
    +
    1214 QTest::newRow("invalid-long-factor") << SignalLine("0", "8", "1+", std::string(50000U, '0') + "1x", "0", "0", "255") << false << std::string();
    +
    1215 QTest::newRow("overflowing-start-bit") << SignalLine(std::string(50000U, '9'), "8", "1+", "1", "0", "0", "255") << false << std::string();
    +
    1216 QTest::newRow("missing-colon") << "SG_ " + longText << false << std::string();
    +
    1217}
    +
    1218
    +
    1219void DbcParserTest::longSignalDefinitions() {
    +
    1220 QFETCH(std::string, line);
    +
    1221 QFETCH(bool, valid);
    +
    1222 QFETCH(std::string, unit);
    +
    1223 Fixture fixture;
    +
    1224 fixture.Write("BO_ 1 Frame: 1 ECU\n" + line + "\n");
    +
    1225 const DbcParser parser;
    +
    1226 const DbcParseResult result = parser.ParseFileWithDiagnostics(fixture.path.string());
    +
    1227 QCOMPARE(result.HasErrors(), !valid);
    +
    1228 if (valid) {
    +
    1229 QVERIFY(result.diagnostics.empty());
    +
    1230 const DbcDatabase database = parser.ParseFile(fixture.path.string());
    +
    1231 QCOMPARE(database.frames.size(), std::size_t(1));
    +
    1232 QCOMPARE(database.frames[0].signals.size(), std::size_t(1));
    +
    1233 QCOMPARE(database.frames[0].signals[0].unit, unit);
    +
    1234 QCOMPARE(result.database.frames[0].signals[0].unit, unit);
    +
    1235 QCOMPARE(database.frames[0].signals[0].factor, 1.0);
    +
    1236 QVERIFY(!database.frames[0].signals[0].receivers.empty());
    +
    1237 } else {
    +
    1238 QVERIFY(result.database.frames.empty());
    +
    1239 QCOMPARE(result.diagnostics.size(), std::size_t(1));
    +
    1240 QCOMPARE(result.diagnostics[0].code, DbcDiagnosticCode::InvalidStatement);
    +
    1241 QCOMPARE(result.diagnostics[0].lineNumber, std::size_t(2));
    +
    1242 QCOMPARE(result.diagnostics[0].filePath, fixture.path.string());
    +
    1243 RejectSignal(fixture, line, 1U);
    +
    1244 }
    +
    1245}
    +
    1246
    +
    1247void DbcParserTest::CheckPhysicalNumbers() {
    +
    1248 Fixture fixture;
    +
    1249 struct PhysicalCase {
    +
    1250 const char *factor;
    +
    1251 const char *offset;
    +
    1252 const char *minimum;
    +
    1253 const char *maximum;
    +
    1254 double expectedFactor;
    +
    1255 double expectedOffset;
    +
    1256 double expectedMinimum;
    +
    1257 double expectedMaximum;
    +
    1258 };
    +
    1259 const PhysicalCase valid[] = {
    +
    1260 {"+1.25", "-0.5", "-2.75", "255.5", 1.25, -0.5, -2.75, 255.5},
    +
    1261 {"+.5e+1", "-2.5E-1", "-1.", "+2e2", 5.0, -0.25, -1.0, 200.0},
    +
    1262 {"0e-999", "+0.", "-.0", ".0e+999", 0.0, 0.0, 0.0, 0.0},
    +
    1263 {"1.7976931348623157e308", "2.2250738585072014e-308", "-1e308", "+1e308",
    +
    1264 std::numeric_limits<double>::max(), std::numeric_limits<double>::min(), -1e308, 1e308}};
    +
    1265 for (const PhysicalCase &test: valid) {
    +
    1266 fixture.Write("BO_ 1 Frame: 1 ECU\n" +
    +
    1267 SignalLine("0", "8", "1+", test.factor, test.offset, test.minimum, test.maximum) + "\n");
    +
    1268 const DbcParseResult result = DbcParser().ParseFileWithDiagnostics(fixture.path.string());
    +
    1269 QVERIFY(!result.HasErrors());
    +
    1270 QVERIFY(result.diagnostics.empty());
    +
    1271 QCOMPARE(result.database.frames.size(), std::size_t(1));
    +
    1272 const SignalInfo &signal = result.database.frames[0].signals[0];
    +
    1273 QCOMPARE(signal.factor, test.expectedFactor);
    +
    1274 QCOMPARE(signal.offset, test.expectedOffset);
    +
    1275 QCOMPARE(signal.minimum, test.expectedMinimum);
    +
    1276 QCOMPARE(signal.maximum, test.expectedMaximum);
    +
    1277 }
    +
    1278
    +
    1279 const char *invalid[] = {"1,5", "1.234,5", "1,234.5", "1junk", "nan", "inf", "-inf",
    +
    1280 "1e999", "1e-999", "1e-320", "-1e-320", "0x1p2", "1e", "1.2.3"};
    +
    1281 for (const char *value: invalid) {
    +
    1282 RejectSignal(fixture, SignalLine("0", "8", "1+", value, "0", "0", "255"));
    +
    1283 RejectSignal(fixture, SignalLine("0", "8", "1+", "1", value, "0", "255"));
    +
    1284 RejectSignal(fixture, SignalLine("0", "8", "1+", "1", "0", value, "255"));
    +
    1285 RejectSignal(fixture, SignalLine("0", "8", "1+", "1", "0", "0", value));
    +
    1286 }
    +
    1287 RejectSignal(fixture, SignalLine("0", "8", "1+", "1.25", "0", "2.5", "1.5"));
    +
    1288}
    +
    1289
    +
    1290void DbcParserTest::physicalNumbersCppLocale() {
    +
    1291 const ApplicationLocaleGuard guard;
    +
    1292 std::locale::global(std::locale(std::locale::classic(), new CommaDecimalPunctuation));
    +
    1293 QCOMPARE(std::use_facet<std::numpunct<char>>(std::locale()).decimal_point(), ',');
    +
    1294 CheckPhysicalNumbers();
    +
    1295 QCOMPARE(std::use_facet<std::numpunct<char>>(std::locale()).decimal_point(), ',');
    +
    1296}
    +
    1297
    +
    1298void DbcParserTest::physicalNumbersCLocale() {
    +
    1299 const ApplicationLocaleGuard guard;
    +
    1300 bool found = false;
    +
    1301 const char *names[] = {"de_DE.UTF-8", "de_DE.utf8", "fr_FR.UTF-8", "fr_FR.utf8",
    +
    1302 "German_Germany.1252", "French_France.1252"};
    +
    1303 for (const char *name: names) {
    +
    1304 if (std::setlocale(LC_NUMERIC, name) && std::string(std::localeconv()->decimal_point) == ",") {
    +
    1305 found = true;
    +
    1306 break;
    +
    1307 }
    +
    1308 }
    +
    1309 if (!found)
    +
    1310 QSKIP("No named comma-decimal C locale is installed; generate de_DE.UTF-8 and set LOCPATH.");
    +
    1311 QCOMPARE(std::string(std::localeconv()->decimal_point), std::string(","));
    +
    1312 CheckPhysicalNumbers();
    +
    1313 QCOMPARE(std::string(std::localeconv()->decimal_point), std::string(","));
    +
    1314}
    +
    1315
    +
    1316void DbcParserTest::dispatch() {
    +
    1317 CountingHandler::instances = 0;
    +
    1318 CountingHandler::calls = 0;
    +
    1319 DbcDatabase dispatchDatabase;
    +
    1320 DbcParseContext context{dispatchDatabase, nullptr};
    +
    1321 DbcStatementDispatcher dispatcher;
    +
    1322 dispatcher.RegisterType<CountingHandler>("TEST_");
    +
    1323 CHECK(dispatcher.Dispatch("TEST_ first", context) &&
    +
    1324 dispatcher.Dispatch("TEST_ second", context) &&
    +
    1325 CountingHandler::instances == 1 && CountingHandler::calls == 2 &&
    +
    1326 dispatchDatabase.frames.size() == 2U,
    +
    1327 "Factory dispatch and handler reuse");
    +
    1328 CHECK(!dispatcher.Dispatch("TEST_OTHER data", context) &&
    +
    1329 !dispatcher.Dispatch("BO_TX_BU_ 1 : ECU;", context) &&
    +
    1330 !dispatcher.Dispatch("VAL_TABLE_ States 0 \"Stopped\";", context) &&
    +
    1331 CountingHandler::calls == 2,
    +
    1332 "Exact and unknown keyword dispatch");
    +
    1333}
    +
    1334
    +
    1335void DbcParserTest::independentSessions() {
    +
    1336 Fixture fixture;
    +
    1337 const DbcParser parser;
    +
    1338 fixture.Write("BO_ 1 First: 8 ECU\n");
    +
    1339 CHECK(parser.ParseFile(fixture.path.string()).frames.size() == 1U, "First parse");
    +
    1340 fixture.Write("SG_ Speed : invalid\n");
    +
    1341 try {
    +
    1342 parser.ParseFile(fixture.path.string());
    +
    1343 CHECK(false, "Independent parse context");
    +
    1344 } catch (const std::runtime_error &error) {
    +
    1345 CHECK(std::string(error.what()).find("Signal found before any frame") !=
    +
    1346 std::string::npos,
    +
    1347 "Independent parse context");
    1348 }
    -
    1349 frame.isExtended = true;
    -
    1350 struct Boundary {
    -
    1351 std::uint64_t bits;
    -
    1352 const char *description;
    -
    1353 };
    -
    1354 for (const Boundary &test: {Boundary{0x8000000000000000ULL, "Minimum"},
    -
    1355 Boundary{0xFFFFFFFFFFFFFFFFULL, "Negative"},
    -
    1356 Boundary{0x7FFFFFFFFFFFFFFFULL, "Maximum"}}) {
    -
    1357 for (unsigned byte = 0; byte < 8; ++byte)
    -
    1358 frame.data[byte] = static_cast<std::uint8_t>(test.bits >> (byte * 8));
    -
    1359 const DecodedSignalValue decoded = DbcDecoder().Decode(runtime, frame).signals[0];
    -
    1360 CHECK(decoded.GetValueDescription() && *decoded.GetValueDescription() == test.description,
    -
    1361 "Signed boundaries and distinct extended frame");
    -
    1362 }
    -
    1363 frame.canId = 2048;
    -
    1364 for (const Boundary &test: {Boundary{0x8000000000000000ULL, "High"},
    -
    1365 Boundary{0xFFFFFFFFFFFFFFFFULL, "Full"}}) {
    -
    1366 for (unsigned byte = 0; byte < 8; ++byte)
    -
    1367 frame.data[byte] = static_cast<std::uint8_t>(test.bits >> (byte * 8));
    -
    1368 const DecodedSignalValue decoded = DbcDecoder().Decode(runtime, frame).signals[0];
    -
    1369 CHECK(decoded.GetValueDescription() && *decoded.GetValueDescription() == test.description,
    -
    1370 "Unsigned boundaries");
    -
    1371 }
    -
    1372 CHECK(FindValueDescription(runtime.GetFrames()[0].signals[0].valueDescriptions, std::int64_t(1)) != nullptr,
    -
    1373 "Nonnegative signed lookup matches unsigned key");
    -
    1374 frame.data.clear();
    -
    1375 CHECK(!DbcDecoder().Decode(runtime, frame).signals[0].GetValueDescription(), "Invalid decode has no description");
    -
    1376}
    -
    1377
    -
    1378void DbcParserTest::invalidValueDescriptions() {
    -
    1379 Fixture fixture;
    -
    1380 const std::string definitions = "BO_ 1 Frame: 8 ECU\nSG_ State : 0|64@1+ (1,0) [0|2e20] \"\" ECU\n";
    -
    1381 for (const std::string statement: {
    -
    1382 "VAL_", "VAL_ 1 State;", "VAL_ 1 State ;", "VAL_ 1 State 0 \"Off\"",
    -
    1383 "VAL_ 1 State 0 \"Off\"; junk", "VAL_ 1 State 0 Off;",
    -
    1384 "VAL_ 1 State 0 \"Unclosed;", "VAL_ 1 State 0 \"Off\"1 \"On\";",
    -
    1385 "VAL_ 1 State 0 \"Off\" 00 \"Duplicate\";",
    -
    1386 "VAL_ 1 State 0 \"Off\" -0 \"Duplicate\";",
    -
    1387 "VAL_ 1 State 0 \"Off\";\nVAL_ 1 State 0 \"Duplicate\";",
    -
    1388 "VAL_ 2 State 0 \"Missing frame\";", "VAL_ 1 Missing 0 \"Missing signal\";",
    -
    1389 "VAL_ -1 State 0 \"Bad ID\";", "VAL_ 536870912 State 0 \"Bad ID\";",
    -
    1390 "VAL_ 4294967296 State 0 \"Bad ID\";", "VAL_ Environment 0 \"Unsupported\";",
    -
    1391 "VAL_ 1 State 0 \"Bad\\q escape\";"}) {
    -
    1392 fixture.Write(definitions + statement + '\n');
    -
    1393 const int line = statement.find('\n') == std::string::npos ? 3 : 4;
    -
    1394 try {
    -
    1395 DbcParser().ParseFile(fixture.path.string());
    -
    1396 CHECK(false, statement);
    -
    1397 } catch (const std::runtime_error &error) {
    -
    1398 CHECK(std::string(error.what()).find(fixture.path.string() + ":" + std::to_string(line) + ": ") == 0,
    -
    1399 statement + " " + error.what());
    -
    1400 }
    -
    1401 }
    -
    1402 for (const std::string suffix: {
    -
    1403 "BO_ 1 Other: 8 ECU\n", // Duplicate frame definition.
    -
    1404 "SG_ State : 0|8@1+ (1,0) [0|255] \"\" ECU\n",// Duplicate signal definition.
    -
    1405 ""}) {
    -
    1406 fixture.Write("VAL_ 1 " + std::string(suffix.empty() ? "Missing" : "State") +
    -
    1407 " 0 \"Description\";\n" + definitions + suffix);
    -
    1408 try {
    -
    1409 DbcParser().ParseFile(fixture.path.string());
    -
    1410 CHECK(false, "Missing reference or duplicate definition");
    -
    1411 } catch (const std::runtime_error &error) {
    -
    1412 const std::string line = suffix.empty() ? "1" : "4";
    -
    1413 CHECK(std::string(error.what()).find(fixture.path.string() + ":" + line + ": ") == 0,
    -
    1414 "Error retains the reference or duplicate definition source location");
    -
    1415 }
    -
    1416 }
    -
    1417 for (const std::string number: {"+1", "1x", "0x10", "1.5", "1e2", "--1",
    -
    1418 "-9223372036854775809", "18446744073709551616"}) {
    -
    1419 fixture.Write("VAL_ 1 State " + number + " \"Bad\";\n" + definitions);
    -
    1420 try {
    -
    1421 DbcParser().ParseFile(fixture.path.string());
    -
    1422 CHECK(false, number);
    -
    1423 } catch (const std::runtime_error &error) {
    -
    1424 CHECK(std::string(error.what()).find(fixture.path.string() + ":1: ") == 0, number);
    -
    1425 }
    -
    1426 }
    -
    1427}
    -
    1428
    -
    1429
    -
    1430void DbcParserTest::extendedMultiplexing() {
    -
    1431 Fixture fixture;
    -
    1432 // Forward references, multiple ranges, repeated range statements, and nested siblings.
    -
    1433 fixture.Write(
    -
    1434 "SG_MUL_VAL_ 1 Child Root 1-2, 4-4;\n"
    -
    1435 "SG_MUL_VAL_ 1 Leaf Child 3-5;\n"
    -
    1436 "SG_MUL_VAL_ 1 Leaf Child 7-7;\n"
    -
    1437 "SG_MUL_VAL_ 1 Sibling Root 6-6;\n"
    -
    1438 "SG_MUL_VAL_ 1 Other Sibling 3-5;\n"
    -
    1439 "BO_ 1 Frame: 4 ECU\n"
    -
    1440 "SG_ Leaf m99 : 16|8@1- (2,1) [-128|127] \"\" ECU\n"
    -
    1441 "SG_ Other m0 : 23|8@0+ (1,0) [0|255] \"\" ECU\n"
    -
    1442 "SG_ Child m1M : 8|8@1+ (10,3) [0|255] \"\" ECU\n"
    -
    1443 "SG_ Sibling m6M : 15|8@0+ (1,0) [0|255] \"\" ECU\n"
    -
    1444 "SG_ Root M : 0|8@1+ (10,3) [0|255] \"\" ECU\n"
    -
    1445 "SG_ Common : 24|8@1+ (1,0) [0|255] \"\" ECU\n");
    -
    1446 const DbcDatabase parsed = DbcParser().ParseFile(fixture.path.string());
    -
    1447 const DecodeDatabase database = DbcDecodeBuilder().Build(parsed);
    -
    1448 const std::unique_ptr<TreeNode> tree = DbcTreeBuilder().Build(parsed);
    -
    1449 const Multiplexing &metadata = tree->GetChild(0U)->GetChild(0U)->GetSignal()->multiplexing;
    -
    1450 CHECK(metadata.selector == "Child" && metadata.ranges.size() == 2U &&
    -
    1451 metadata.ranges[1].minimum == 7U,
    -
    1452 "Tree preserves ranges and selector name");
    -
    1453 CHECK(database.GetFrames()[0].signals[2].multiplexing.role == MultiplexRole::BranchSelector,
    -
    1454 "Runtime preserves nested selector role");
    -
    1455 RawCanFrame raw;
    -
    1456 raw.canId = 1U;
    -
    1457 raw.data = {2U, 4U, 255U, 8U};
    -
    1458 DecodedFrameValue result = DbcDecoder().Decode(database, raw);
    -
    1459 CHECK(result.signals[0].valid && result.signals[0].physicalValue == -1.0 &&
    -
    1460 result.signals[1].status == SignalDecodeStatus::Inactive && result.signals[2].valid &&
    -
    1461 result.signals[3].status == SignalDecodeStatus::Inactive && result.signals[5].valid,
    -
    1462 "Nested raw conditions select one path and preserve definition order");
    -
    1463 const std::vector<SignalKey> selection = {SignalKey(FrameKey(1U, false), "Leaf")};
    -
    1464 result = DbcDecoder().Decode(database, raw, selection);
    -
    1465 CHECK(result.signals.size() == 1U && result.signals[0].valid,
    -
    1466 "Selection reads all ancestors internally");
    -
    1467 for (std::uint8_t root: {1U, 2U, 4U}) {
    -
    1468 for (std::uint8_t child: {3U, 5U, 7U}) {
    -
    1469 raw.data = {root, child, 9U, 8U};
    -
    1470 CHECK(DbcDecoder().Decode(database, raw, selection).signals[0].valid,
    -
    1471 "Inclusive endpoints and separated ranges");
    -
    1472 }
    -
    1473 }
    -
    1474 raw.data = {6U, 4U, 9U, 8U};
    -
    1475 result = DbcDecoder().Decode(database, raw);
    -
    1476 CHECK(result.signals[0].status == SignalDecodeStatus::Inactive && result.signals[1].valid &&
    -
    1477 result.signals[3].valid,
    -
    1478 "Overlapping sibling selector path becomes active");
    -
    1479 raw.data = {0U};
    -
    1480 CHECK(DbcDecoder().Decode(database, raw, selection).signals[0].status == SignalDecodeStatus::Inactive,
    -
    1481 "Inactive root does not require child selector bytes");
    -
    1482 raw.data = {1U};
    -
    1483 CHECK(DbcDecoder().Decode(database, raw, selection).signals[0].status == SignalDecodeStatus::SelectorUnavailable,
    -
    1484 "Active root with missing child selector");
    -
    1485 raw.data = {1U, 3U};
    -
    1486 CHECK(DbcDecoder().Decode(database, raw, selection).signals[0].status == SignalDecodeStatus::InsufficientPayload,
    -
    1487 "Active nested leaf needs its bytes");
    -
    1488 raw.data = {1U, 6U};
    -
    1489 CHECK(DbcDecoder().Decode(database, raw, selection).signals[0].status == SignalDecodeStatus::Inactive,
    -
    1490 "Gap between explicit ranges does not fall back to mN");
    -
    1491 raw.data.clear();
    -
    1492 CHECK(DbcDecoder().Decode(database, raw, selection).signals[0].status == SignalDecodeStatus::SelectorUnavailable,
    -
    1493 "Missing root selector");
    -
    1494
    -
    1495 fixture.Write("BO_ 1 Frame: 9 ECU\n"
    -
    1496 "SG_ Root M : 0|64@1+ (1,0) [0|1] \"\" ECU\n"
    -
    1497 "SG_ Leaf m0 : 64|8@1+ (1,0) [0|255] \"\" ECU\n"
    -
    1498 "SG_MUL_VAL_ 1 Leaf Root 18446744073709551614-18446744073709551615;\n");
    -
    1499 raw.data.assign(9U, 255U);
    -
    1500 CHECK(DbcDecoder().Decode(DbcDecodeBuilder().Build(DbcParser().ParseFile(fixture.path.string())), raw).signals[1].valid,
    -
    1501 "64-bit ranges retain exact UINT64_MAX without expansion");
    -
    1502
    -
    1503 fixture.Write("BO_ 1 Frame: 4 ECU\n"
    -
    1504 "SG_ Root M : 0|8@1+ (1,0) [0|255] \"\" ECU\n"
    -
    1505 "SG_ Second M : 8|8@1+ (1,0) [0|255] \"\" ECU\n"
    -
    1506 "SG_ A m0 : 16|8@1+ (1,0) [0|255] \"\" ECU\n"
    -
    1507 "SG_ B m0 : 24|8@1+ (1,0) [0|255] \"\" ECU\n"
    -
    1508 "SG_MUL_VAL_ 1 A Root 1-1;\nSG_MUL_VAL_ 1 B Second 2-2;\n");
    -
    1509 raw.data = {1U, 2U, 9U, 10U};
    -
    1510 result = DbcDecoder().Decode(DbcDecodeBuilder().Build(DbcParser().ParseFile(fixture.path.string())), raw);
    -
    1511 CHECK(result.signals[2].valid && result.signals[3].valid, "Independent explicit selectors");
    -
    1512
    -
    1513 fixture.Write("BO_ 1 Deep: 5 ECU\n"
    -
    1514 "SG_ Root M : 0|8@1+ (1,0) [0|255] \"\" ECU\n"
    -
    1515 "SG_ Child m1M : 8|8@1+ (1,0) [0|255] \"\" ECU\n"
    -
    1516 "SG_ Grandchild m2M : 16|8@1+ (1,0) [0|255] \"\" ECU\n"
    -
    1517 "SG_ Leaf : 24|8@1+ (1,0) [0|255] \"\" ECU\n"
    -
    1518 "SG_MUL_VAL_ 1 Grandchild Child 2-2;\n"
    -
    1519 "SG_MUL_VAL_ 1 Leaf Grandchild 3-3;\n");
    -
    1520 const DecodeDatabase deep = DbcDecodeBuilder().Build(DbcParser().ParseFile(fixture.path.string()));
    -
    1521 raw.data = {1U, 2U, 3U, 9U, 0U};
    -
    1522 CHECK(DbcDecoder().Decode(deep, raw).signals[3].valid, "Three selector levels and an unmarked extended branch");
    -
    1523 raw.data = {1U, 0U};
    -
    1524 CHECK(DbcDecoder().Decode(deep, raw).signals[3].status == SignalDecodeStatus::Inactive,
    -
    1525 "Inactive middle ancestor skips missing descendant bytes");
    -
    1526
    -
    1527 // Marker fallback also works on a nested selector without its own SG_MUL_VAL_.
    -
    1528 fixture.Write("BO_ 1 Frame: 3 ECU\n"
    -
    1529 "SG_ Root M : 0|8@1+ (1,0) [0|255] \"\" ECU\n"
    -
    1530 "SG_ Child m1M : 8|8@1+ (1,0) [0|255] \"\" ECU\n"
    -
    1531 "SG_ Leaf m2 : 16|8@1+ (1,0) [0|255] \"\" ECU\n"
    -
    1532 "SG_MUL_VAL_ 1 Leaf Child 2-2;\n");
    -
    1533 raw.data = {1U, 2U, 9U};
    -
    1534 CHECK(DbcDecoder().Decode(DbcDecodeBuilder().Build(DbcParser().ParseFile(fixture.path.string())), raw).signals[2].valid,
    -
    1535 "mNM retains implicit root fallback");
    -
    1536}
    -
    1537
    -
    1538void DbcParserTest::extendedMultiplexingErrors() {
    -
    1539 Fixture fixture;
    -
    1540 const std::string definitions =
    -
    1541 "BO_ 1 Frame: 3 ECU\n"
    -
    1542 "SG_ Root M : 0|8@1+ (1,0) [0|255] \"\" ECU\n"
    -
    1543 "SG_ Child m1M : 8|8@1+ (1,0) [0|255] \"\" ECU\n"
    -
    1544 "SG_ Leaf m2 : 16|8@1+ (1,0) [0|255] \"\" ECU\n";
    -
    1545 for (const std::string statement: {
    -
    1546 "SG_MUL_VAL_ 1 Leaf Root 3-2;", "SG_MUL_VAL_ 1 Leaf Root -1-2;",
    -
    1547 "SG_MUL_VAL_ 1 Leaf Root 1-256;", "SG_MUL_VAL_ 1 Leaf Root 1-2,;",
    -
    1548 "SG_MUL_VAL_ 1 Leaf Root 1-2 trailing;", "SG_MUL_VAL_ 1 Leaf Root 1-2",
    -
    1549 "SG_MUL_VAL_ 1 Leaf Root 0-18446744073709551616;",
    -
    1550 "SG_MUL_VAL_ 1 Leaf Missing 1-2;", "SG_MUL_VAL_ 1 Missing Root 1-2;",
    -
    1551 "SG_MUL_VAL_ 2 Leaf Root 1-2;", "SG_MUL_VAL_ 1 Leaf Leaf 1-2;"}) {
    -
    1552 fixture.Write(statement + "\n" + definitions);
    -
    1553 const DbcParseResult result = DbcParser().ParseFileWithDiagnostics(fixture.path.string());
    -
    1554 CHECK(result.HasErrors() && result.database.frames.empty(), statement);
    -
    1555 CHECK(result.diagnostics.back().code == DbcDiagnosticCode::InvalidMultiplexing &&
    -
    1556 result.diagnostics.back().lineNumber == 1U,
    -
    1557 "Extended error retains source statement location");
    -
    1558 }
    -
    1559 for (const std::string statements: {
    -
    1560 "SG_MUL_VAL_ 1 Root Child 1-1;\nSG_MUL_VAL_ 1 Child Root 1-1;\n",
    -
    1561 "SG_MUL_VAL_ 1 Child Child 1-1;\n",
    -
    1562 "SG_MUL_VAL_ 1 Leaf Root 1-1;\nSG_MUL_VAL_ 1 Leaf Child 1-1;\n"}) {
    -
    1563 fixture.Write(definitions + statements);
    -
    1564 CHECK(DbcParser().ParseFileWithDiagnostics(fixture.path.string()).HasErrors(), "Cyclic or conflicting parents rejected");
    -
    1565 }
    -
    1566 // Range intersection and inherited root conditions must both protect overlaps.
    -
    1567 const std::string overlapping =
    -
    1568 "BO_ 1 Frame: 4 ECU\n"
    -
    1569 "SG_ Root M : 0|8@1+ (1,0) [0|255] \"\" ECU\n"
    -
    1570 "SG_ Child m1M : 8|8@1+ (1,0) [0|255] \"\" ECU\n"
    -
    1571 "SG_ A m0 : 16|8@1+ (1,0) [0|255] \"\" ECU\n"
    -
    1572 "SG_ B m0 : 23|8@0+ (1,0) [0|255] \"\" ECU\n"
    -
    1573 "SG_MUL_VAL_ 1 A Child 2-4;\n"
    -
    1574 "SG_MUL_VAL_ 1 Child Root 1-1;\n";
    -
    1575 for (const std::string condition: {"SG_MUL_VAL_ 1 B Child 4-6;\n",
    -
    1576 "SG_MUL_VAL_ 1 B Root 1-1;\n"}) {
    -
    1577 fixture.Write(overlapping + condition);
    -
    1578 CHECK(DbcParser().ParseFileWithDiagnostics(fixture.path.string()).HasErrors(),
    -
    1579 "Overlapping ranges or compatible ancestors cannot share bits");
    -
    1580 }
    -
    1581 fixture.Write(overlapping + "SG_MUL_VAL_ 1 B Child 5-6;\n");
    -
    1582 CHECK(!DbcParser().ParseFileWithDiagnostics(fixture.path.string()).HasErrors(),
    -
    1583 "Disjoint adjacent ranges may share bits");
    -
    1584 fixture.Write(overlapping + "SG_ Second M : 24|8@1+ (1,0) [0|255] \"\" ECU\n"
    -
    1585 "SG_MUL_VAL_ 1 B Second 2-4;\n");
    -
    1586 const DbcParseResult independent = DbcParser().ParseFileWithDiagnostics(fixture.path.string());
    -
    1587 CHECK(independent.HasErrors() && independent.diagnostics.back().message ==
    -
    1588 "Signals overlap without mutually exclusive multiplexing conditions.",
    -
    1589 "Independent selector conditions do not imply exclusion");
    -
    1590
    -
    1591 // Validate manually constructed runtime graphs with the same rules.
    -
    1592 fixture.Write(definitions + "SG_MUL_VAL_ 1 Leaf Child 2-3;\n");
    -
    1593 const DecodeDatabase valid = DbcDecodeBuilder().Build(DbcParser().ParseFile(fixture.path.string()));
    -
    1594 for (unsigned kind = 0U; kind < 6U; ++kind) {
    -
    1595 std::vector<DecodeFrame> frames = valid.GetFrames();
    -
    1596 Multiplexing &leaf = frames[0].signals[2].multiplexing;
    -
    1597 switch (kind) {
    -
    1598 case 0:
    -
    1599 leaf.selector = "Missing";
    -
    1600 break;
    -
    1601 case 1:
    -
    1602 leaf.ranges[0] = {4U, 3U};
    -
    1603 break;
    -
    1604 case 2:
    -
    1605 leaf.ranges[0] = {0U, 256U};
    -
    1606 break;
    -
    1607 case 3:
    -
    1608 leaf.ranges.clear();
    -
    1609 break;
    -
    1610 case 4:
    -
    1611 frames[0].signals[0].multiplexing.selector = "Child";
    -
    1612 frames[0].signals[0].multiplexing.ranges = {{1U, 1U}};
    -
    1613 break;
    -
    1614 case 5:
    -
    1615 frames[0].signals[1].valueType = ValueType::Signed;
    -
    1616 break;
    -
    1617 }
    -
    1618 QVERIFY_THROWS_EXCEPTION(std::invalid_argument, (void) DecodeDatabase(frames));
    -
    1619 }
    -
    1620}
    -
    1621
    -
    1622int RunParserTests(int argc, char **argv) {
    -
    1623 DbcParserTest tests;
    -
    1624 return QTest::qExec(&tests, argc, argv);
    -
    1625}
    -
    1626
    -
    1627#include "dbc_parser_test.moc"
    +
    1349 fixture.Write("BO_ 2 Second: 8 ECU\n");
    +
    1350 const DbcDatabase recovered = parser.ParseFile(fixture.path.string());
    +
    1351 CHECK(recovered.frames.size() == 1U && recovered.frames[0].canId == 2U,
    +
    1352 "Fresh parse after error");
    +
    1353}
    +
    1354
    +
    1355
    +
    1356void DbcParserTest::valueDescriptions() {
    +
    1357 Fixture fixture;
    +
    1358 fixture.Write(
    +
    1359 "VAL_\t256\tState\t0\t\"Off\"\t1 \"On\" 2 \"Error with spaces; and \\\"quotes\\\"\";\n"
    +
    1360 "BO_ 256 Status: 8 ECU\n"
    +
    1361 "SG_ State : 0|8@1+ (2,10) [0|520] \"\" ECU\n"
    +
    1362 "VAL_ 256 State 3 \"\";\n"
    +
    1363 "VAL_ 2147483904 State -9223372036854775808 \"Minimum\" -1 \"Negative\" 9223372036854775807 \"Maximum\";\n"
    +
    1364 "BO_ 2147483904 Extended: 8 ECU\n"
    +
    1365 "SG_ State : 0|64@1- (1,0) [-1e20|1e20] \"\" ECU\n"
    +
    1366 "BO_ 2048 Unsigned: 8 ECU\n"
    +
    1367 "SG_ State : 0|64@1+ (1,0) [0|2e20] \"\" ECU\n"
    +
    1368 "VAL_ 2147485696 State 9223372036854775808 \"High\" 18446744073709551615 \"Full\";\n");
    +
    1369 const DbcDatabase parsed = DbcParser().ParseFile(fixture.path.string());
    +
    1370 const DecodeDatabase runtime = DbcDecodeBuilder().Build(parsed);
    +
    1371 const std::unique_ptr<TreeNode> tree = DbcTreeBuilder().Build(parsed);
    +
    1372 CHECK(tree->GetChild(0)->GetSignal() == nullptr, "Frame node");
    +
    1373 for (std::size_t index = 0; index < parsed.frames.size(); ++index) {
    +
    1374 CHECK(tree->GetChild(index)->GetChild(0)->GetSignal()->valueDescriptions ==
    +
    1375 parsed.frames[index].signals[0].valueDescriptions &&
    +
    1376 runtime.GetFrames()[index].signals[0].valueDescriptions ==
    +
    1377 parsed.frames[index].signals[0].valueDescriptions,
    +
    1378 "All mappings survive both builders");
    +
    1379 }
    +
    1380 CHECK(tree->GetChild(0)->GetChild(0)->GetSignal()->valueDescriptions ==
    +
    1381 runtime.GetFrames()[0].signals[0].valueDescriptions,
    +
    1382 "Both builders preserve descriptions");
    +
    1383 RawCanFrame frame;
    +
    1384 frame.canId = 256;
    +
    1385 frame.data.resize(8);
    +
    1386 const std::string expected[] = {"Off", "On", "Error with spaces; and \"quotes\"", ""};
    +
    1387 for (unsigned value = 0; value < 5; ++value) {
    +
    1388 frame.data[0] = value;
    +
    1389 const DecodedSignalValue decoded = DbcDecoder().Decode(runtime, frame).signals[0];
    +
    1390 CHECK(decoded.valid && decoded.physicalValue == value * 2 + 10, "Numeric scaling unchanged");
    +
    1391 const std::string *description = decoded.GetValueDescription();
    +
    1392 CHECK(value < 4 ? description && *description == expected[value] : !description,
    +
    1393 "Raw lookup and unknown value");
    +
    1394 }
    +
    1395 frame.isExtended = true;
    +
    1396 struct Boundary {
    +
    1397 std::uint64_t bits;
    +
    1398 const char *description;
    +
    1399 };
    +
    1400 for (const Boundary &test: {Boundary{0x8000000000000000ULL, "Minimum"},
    +
    1401 Boundary{0xFFFFFFFFFFFFFFFFULL, "Negative"},
    +
    1402 Boundary{0x7FFFFFFFFFFFFFFFULL, "Maximum"}}) {
    +
    1403 for (unsigned byte = 0; byte < 8; ++byte)
    +
    1404 frame.data[byte] = static_cast<std::uint8_t>(test.bits >> (byte * 8));
    +
    1405 const DecodedSignalValue decoded = DbcDecoder().Decode(runtime, frame).signals[0];
    +
    1406 CHECK(decoded.GetValueDescription() && *decoded.GetValueDescription() == test.description,
    +
    1407 "Signed boundaries and distinct extended frame");
    +
    1408 }
    +
    1409 frame.canId = 2048;
    +
    1410 for (const Boundary &test: {Boundary{0x8000000000000000ULL, "High"},
    +
    1411 Boundary{0xFFFFFFFFFFFFFFFFULL, "Full"}}) {
    +
    1412 for (unsigned byte = 0; byte < 8; ++byte)
    +
    1413 frame.data[byte] = static_cast<std::uint8_t>(test.bits >> (byte * 8));
    +
    1414 const DecodedSignalValue decoded = DbcDecoder().Decode(runtime, frame).signals[0];
    +
    1415 CHECK(decoded.GetValueDescription() && *decoded.GetValueDescription() == test.description,
    +
    1416 "Unsigned boundaries");
    +
    1417 }
    +
    1418 CHECK(FindValueDescription(runtime.GetFrames()[0].signals[0].valueDescriptions, std::int64_t(1)) != nullptr,
    +
    1419 "Nonnegative signed lookup matches unsigned key");
    +
    1420 frame.data.clear();
    +
    1421 CHECK(!DbcDecoder().Decode(runtime, frame).signals[0].GetValueDescription(), "Invalid decode has no description");
    +
    1422}
    +
    1423
    +
    1424void DbcParserTest::invalidValueDescriptions() {
    +
    1425 Fixture fixture;
    +
    1426 const std::string definitions = "BO_ 1 Frame: 8 ECU\nSG_ State : 0|64@1+ (1,0) [0|2e20] \"\" ECU\n";
    +
    1427 for (const std::string statement: {
    +
    1428 "VAL_", "VAL_ 1 State;", "VAL_ 1 State ;", "VAL_ 1 State 0 \"Off\"",
    +
    1429 "VAL_ 1 State 0 \"Off\"; junk", "VAL_ 1 State 0 Off;",
    +
    1430 "VAL_ 1 State 0 \"Unclosed;", "VAL_ 1 State 0 \"Off\"1 \"On\";",
    +
    1431 "VAL_ 1 State 0 \"Off\" 00 \"Duplicate\";",
    +
    1432 "VAL_ 1 State 0 \"Off\" -0 \"Duplicate\";",
    +
    1433 "VAL_ 1 State 0 \"Off\";\nVAL_ 1 State 0 \"Duplicate\";",
    +
    1434 "VAL_ 2 State 0 \"Missing frame\";", "VAL_ 1 Missing 0 \"Missing signal\";",
    +
    1435 "VAL_ -1 State 0 \"Bad ID\";", "VAL_ 536870912 State 0 \"Bad ID\";",
    +
    1436 "VAL_ 4294967296 State 0 \"Bad ID\";", "VAL_ Environment 0 \"Unsupported\";",
    +
    1437 "VAL_ 1 State 0 \"Bad\\q escape\";"}) {
    +
    1438 fixture.Write(definitions + statement + '\n');
    +
    1439 const int line = statement.find('\n') == std::string::npos ? 3 : 4;
    +
    1440 try {
    +
    1441 DbcParser().ParseFile(fixture.path.string());
    +
    1442 CHECK(false, statement);
    +
    1443 } catch (const std::runtime_error &error) {
    +
    1444 CHECK(std::string(error.what()).find(fixture.path.string() + ":" + std::to_string(line) + ": ") == 0,
    +
    1445 statement + " " + error.what());
    +
    1446 }
    +
    1447 }
    +
    1448 for (const std::string suffix: {
    +
    1449 "BO_ 1 Other: 8 ECU\n", // Duplicate frame definition.
    +
    1450 "SG_ State : 0|8@1+ (1,0) [0|255] \"\" ECU\n",// Duplicate signal definition.
    +
    1451 ""}) {
    +
    1452 fixture.Write("VAL_ 1 " + std::string(suffix.empty() ? "Missing" : "State") +
    +
    1453 " 0 \"Description\";\n" + definitions + suffix);
    +
    1454 try {
    +
    1455 DbcParser().ParseFile(fixture.path.string());
    +
    1456 CHECK(false, "Missing reference or duplicate definition");
    +
    1457 } catch (const std::runtime_error &error) {
    +
    1458 const std::string line = suffix.empty() ? "1" : "4";
    +
    1459 CHECK(std::string(error.what()).find(fixture.path.string() + ":" + line + ": ") == 0,
    +
    1460 "Error retains the reference or duplicate definition source location");
    +
    1461 }
    +
    1462 }
    +
    1463 for (const std::string number: {"+1", "1x", "0x10", "1.5", "1e2", "--1",
    +
    1464 "-9223372036854775809", "18446744073709551616"}) {
    +
    1465 fixture.Write("VAL_ 1 State " + number + " \"Bad\";\n" + definitions);
    +
    1466 try {
    +
    1467 DbcParser().ParseFile(fixture.path.string());
    +
    1468 CHECK(false, number);
    +
    1469 } catch (const std::runtime_error &error) {
    +
    1470 CHECK(std::string(error.what()).find(fixture.path.string() + ":1: ") == 0, number);
    +
    1471 }
    +
    1472 }
    +
    1473}
    +
    1474
    +
    1475
    +
    1476void DbcParserTest::extendedMultiplexing() {
    +
    1477 Fixture fixture;
    +
    1478 // Forward references, multiple ranges, repeated range statements, and nested siblings.
    +
    1479 fixture.Write(
    +
    1480 "SG_MUL_VAL_ 1 Child Root 1-2, 4-4;\n"
    +
    1481 "SG_MUL_VAL_ 1 Leaf Child 3-5;\n"
    +
    1482 "SG_MUL_VAL_ 1 Leaf Child 7-7;\n"
    +
    1483 "SG_MUL_VAL_ 1 Sibling Root 6-6;\n"
    +
    1484 "SG_MUL_VAL_ 1 Other Sibling 3-5;\n"
    +
    1485 "BO_ 1 Frame: 4 ECU\n"
    +
    1486 "SG_ Leaf m99 : 16|8@1- (2,1) [-128|127] \"\" ECU\n"
    +
    1487 "SG_ Other m0 : 23|8@0+ (1,0) [0|255] \"\" ECU\n"
    +
    1488 "SG_ Child m1M : 8|8@1+ (10,3) [0|255] \"\" ECU\n"
    +
    1489 "SG_ Sibling m6M : 15|8@0+ (1,0) [0|255] \"\" ECU\n"
    +
    1490 "SG_ Root M : 0|8@1+ (10,3) [0|255] \"\" ECU\n"
    +
    1491 "SG_ Common : 24|8@1+ (1,0) [0|255] \"\" ECU\n");
    +
    1492 const DbcDatabase parsed = DbcParser().ParseFile(fixture.path.string());
    +
    1493 const DecodeDatabase database = DbcDecodeBuilder().Build(parsed);
    +
    1494 const std::unique_ptr<TreeNode> tree = DbcTreeBuilder().Build(parsed);
    +
    1495 const Multiplexing &metadata = tree->GetChild(0U)->GetChild(0U)->GetSignal()->multiplexing;
    +
    1496 CHECK(metadata.selector == "Child" && metadata.ranges.size() == 2U &&
    +
    1497 metadata.ranges[1].minimum == 7U,
    +
    1498 "Tree preserves ranges and selector name");
    +
    1499 CHECK(database.GetFrames()[0].signals[2].multiplexing.role == MultiplexRole::BranchSelector,
    +
    1500 "Runtime preserves nested selector role");
    +
    1501 RawCanFrame raw;
    +
    1502 raw.canId = 1U;
    +
    1503 raw.data = {2U, 4U, 255U, 8U};
    +
    1504 DecodedFrameValue result = DbcDecoder().Decode(database, raw);
    +
    1505 CHECK(result.signals[0].valid && result.signals[0].physicalValue == -1.0 &&
    +
    1506 result.signals[1].status == SignalDecodeStatus::Inactive && result.signals[2].valid &&
    +
    1507 result.signals[3].status == SignalDecodeStatus::Inactive && result.signals[5].valid,
    +
    1508 "Nested raw conditions select one path and preserve definition order");
    +
    1509 const std::vector<SignalKey> selection = {SignalKey(FrameKey(1U, false), "Leaf")};
    +
    1510 result = DbcDecoder().Decode(database, raw, selection);
    +
    1511 CHECK(result.signals.size() == 1U && result.signals[0].valid,
    +
    1512 "Selection reads all ancestors internally");
    +
    1513 for (std::uint8_t root: {1U, 2U, 4U}) {
    +
    1514 for (std::uint8_t child: {3U, 5U, 7U}) {
    +
    1515 raw.data = {root, child, 9U, 8U};
    +
    1516 CHECK(DbcDecoder().Decode(database, raw, selection).signals[0].valid,
    +
    1517 "Inclusive endpoints and separated ranges");
    +
    1518 }
    +
    1519 }
    +
    1520 raw.data = {6U, 4U, 9U, 8U};
    +
    1521 result = DbcDecoder().Decode(database, raw);
    +
    1522 CHECK(result.signals[0].status == SignalDecodeStatus::Inactive && result.signals[1].valid &&
    +
    1523 result.signals[3].valid,
    +
    1524 "Overlapping sibling selector path becomes active");
    +
    1525 raw.data = {0U};
    +
    1526 CHECK(DbcDecoder().Decode(database, raw, selection).signals[0].status == SignalDecodeStatus::Inactive,
    +
    1527 "Inactive root does not require child selector bytes");
    +
    1528 raw.data = {1U};
    +
    1529 CHECK(DbcDecoder().Decode(database, raw, selection).signals[0].status == SignalDecodeStatus::SelectorUnavailable,
    +
    1530 "Active root with missing child selector");
    +
    1531 raw.data = {1U, 3U};
    +
    1532 CHECK(DbcDecoder().Decode(database, raw, selection).signals[0].status == SignalDecodeStatus::InsufficientPayload,
    +
    1533 "Active nested leaf needs its bytes");
    +
    1534 raw.data = {1U, 6U};
    +
    1535 CHECK(DbcDecoder().Decode(database, raw, selection).signals[0].status == SignalDecodeStatus::Inactive,
    +
    1536 "Gap between explicit ranges does not fall back to mN");
    +
    1537 raw.data.clear();
    +
    1538 CHECK(DbcDecoder().Decode(database, raw, selection).signals[0].status == SignalDecodeStatus::SelectorUnavailable,
    +
    1539 "Missing root selector");
    +
    1540
    +
    1541 fixture.Write("BO_ 1 Frame: 9 ECU\n"
    +
    1542 "SG_ Root M : 0|64@1+ (1,0) [0|1] \"\" ECU\n"
    +
    1543 "SG_ Leaf m0 : 64|8@1+ (1,0) [0|255] \"\" ECU\n"
    +
    1544 "SG_MUL_VAL_ 1 Leaf Root 18446744073709551614-18446744073709551615;\n");
    +
    1545 raw.data.assign(9U, 255U);
    +
    1546 CHECK(DbcDecoder().Decode(DbcDecodeBuilder().Build(DbcParser().ParseFile(fixture.path.string())), raw).signals[1].valid,
    +
    1547 "64-bit ranges retain exact UINT64_MAX without expansion");
    +
    1548
    +
    1549 fixture.Write("BO_ 1 Frame: 4 ECU\n"
    +
    1550 "SG_ Root M : 0|8@1+ (1,0) [0|255] \"\" ECU\n"
    +
    1551 "SG_ Second M : 8|8@1+ (1,0) [0|255] \"\" ECU\n"
    +
    1552 "SG_ A m0 : 16|8@1+ (1,0) [0|255] \"\" ECU\n"
    +
    1553 "SG_ B m0 : 24|8@1+ (1,0) [0|255] \"\" ECU\n"
    +
    1554 "SG_MUL_VAL_ 1 A Root 1-1;\nSG_MUL_VAL_ 1 B Second 2-2;\n");
    +
    1555 raw.data = {1U, 2U, 9U, 10U};
    +
    1556 result = DbcDecoder().Decode(DbcDecodeBuilder().Build(DbcParser().ParseFile(fixture.path.string())), raw);
    +
    1557 CHECK(result.signals[2].valid && result.signals[3].valid, "Independent explicit selectors");
    +
    1558
    +
    1559 fixture.Write("BO_ 1 Deep: 5 ECU\n"
    +
    1560 "SG_ Root M : 0|8@1+ (1,0) [0|255] \"\" ECU\n"
    +
    1561 "SG_ Child m1M : 8|8@1+ (1,0) [0|255] \"\" ECU\n"
    +
    1562 "SG_ Grandchild m2M : 16|8@1+ (1,0) [0|255] \"\" ECU\n"
    +
    1563 "SG_ Leaf : 24|8@1+ (1,0) [0|255] \"\" ECU\n"
    +
    1564 "SG_MUL_VAL_ 1 Grandchild Child 2-2;\n"
    +
    1565 "SG_MUL_VAL_ 1 Leaf Grandchild 3-3;\n");
    +
    1566 const DecodeDatabase deep = DbcDecodeBuilder().Build(DbcParser().ParseFile(fixture.path.string()));
    +
    1567 raw.data = {1U, 2U, 3U, 9U, 0U};
    +
    1568 CHECK(DbcDecoder().Decode(deep, raw).signals[3].valid, "Three selector levels and an unmarked extended branch");
    +
    1569 raw.data = {1U, 0U};
    +
    1570 CHECK(DbcDecoder().Decode(deep, raw).signals[3].status == SignalDecodeStatus::Inactive,
    +
    1571 "Inactive middle ancestor skips missing descendant bytes");
    +
    1572
    +
    1573 // Marker fallback also works on a nested selector without its own SG_MUL_VAL_.
    +
    1574 fixture.Write("BO_ 1 Frame: 3 ECU\n"
    +
    1575 "SG_ Root M : 0|8@1+ (1,0) [0|255] \"\" ECU\n"
    +
    1576 "SG_ Child m1M : 8|8@1+ (1,0) [0|255] \"\" ECU\n"
    +
    1577 "SG_ Leaf m2 : 16|8@1+ (1,0) [0|255] \"\" ECU\n"
    +
    1578 "SG_MUL_VAL_ 1 Leaf Child 2-2;\n");
    +
    1579 raw.data = {1U, 2U, 9U};
    +
    1580 CHECK(DbcDecoder().Decode(DbcDecodeBuilder().Build(DbcParser().ParseFile(fixture.path.string())), raw).signals[2].valid,
    +
    1581 "mNM retains implicit root fallback");
    +
    1582}
    +
    1583
    +
    1584void DbcParserTest::extendedMultiplexingErrors() {
    +
    1585 Fixture fixture;
    +
    1586 const std::string definitions =
    +
    1587 "BO_ 1 Frame: 3 ECU\n"
    +
    1588 "SG_ Root M : 0|8@1+ (1,0) [0|255] \"\" ECU\n"
    +
    1589 "SG_ Child m1M : 8|8@1+ (1,0) [0|255] \"\" ECU\n"
    +
    1590 "SG_ Leaf m2 : 16|8@1+ (1,0) [0|255] \"\" ECU\n";
    +
    1591 for (const std::string statement: {
    +
    1592 "SG_MUL_VAL_ 1 Leaf Root 3-2;", "SG_MUL_VAL_ 1 Leaf Root -1-2;",
    +
    1593 "SG_MUL_VAL_ 1 Leaf Root 1-256;", "SG_MUL_VAL_ 1 Leaf Root 1-2,;",
    +
    1594 "SG_MUL_VAL_ 1 Leaf Root 1-2 trailing;", "SG_MUL_VAL_ 1 Leaf Root 1-2",
    +
    1595 "SG_MUL_VAL_ 1 Leaf Root 0-18446744073709551616;",
    +
    1596 "SG_MUL_VAL_ 1 Leaf Missing 1-2;", "SG_MUL_VAL_ 1 Missing Root 1-2;",
    +
    1597 "SG_MUL_VAL_ 2 Leaf Root 1-2;", "SG_MUL_VAL_ 1 Leaf Leaf 1-2;"}) {
    +
    1598 fixture.Write(statement + "\n" + definitions);
    +
    1599 const DbcParseResult result = DbcParser().ParseFileWithDiagnostics(fixture.path.string());
    +
    1600 CHECK(result.HasErrors() && result.database.frames.empty(), statement);
    +
    1601 CHECK(result.diagnostics.back().code == DbcDiagnosticCode::InvalidMultiplexing &&
    +
    1602 result.diagnostics.back().lineNumber == 1U,
    +
    1603 "Extended error retains source statement location");
    +
    1604 }
    +
    1605 for (const std::string statements: {
    +
    1606 "SG_MUL_VAL_ 1 Root Child 1-1;\nSG_MUL_VAL_ 1 Child Root 1-1;\n",
    +
    1607 "SG_MUL_VAL_ 1 Child Child 1-1;\n",
    +
    1608 "SG_MUL_VAL_ 1 Leaf Root 1-1;\nSG_MUL_VAL_ 1 Leaf Child 1-1;\n"}) {
    +
    1609 fixture.Write(definitions + statements);
    +
    1610 CHECK(DbcParser().ParseFileWithDiagnostics(fixture.path.string()).HasErrors(), "Cyclic or conflicting parents rejected");
    +
    1611 }
    +
    1612 // Range intersection and inherited root conditions must both protect overlaps.
    +
    1613 const std::string overlapping =
    +
    1614 "BO_ 1 Frame: 4 ECU\n"
    +
    1615 "SG_ Root M : 0|8@1+ (1,0) [0|255] \"\" ECU\n"
    +
    1616 "SG_ Child m1M : 8|8@1+ (1,0) [0|255] \"\" ECU\n"
    +
    1617 "SG_ A m0 : 16|8@1+ (1,0) [0|255] \"\" ECU\n"
    +
    1618 "SG_ B m0 : 23|8@0+ (1,0) [0|255] \"\" ECU\n"
    +
    1619 "SG_MUL_VAL_ 1 A Child 2-4;\n"
    +
    1620 "SG_MUL_VAL_ 1 Child Root 1-1;\n";
    +
    1621 for (const std::string condition: {"SG_MUL_VAL_ 1 B Child 4-6;\n",
    +
    1622 "SG_MUL_VAL_ 1 B Root 1-1;\n"}) {
    +
    1623 fixture.Write(overlapping + condition);
    +
    1624 CHECK(DbcParser().ParseFileWithDiagnostics(fixture.path.string()).HasErrors(),
    +
    1625 "Overlapping ranges or compatible ancestors cannot share bits");
    +
    1626 }
    +
    1627 fixture.Write(overlapping + "SG_MUL_VAL_ 1 B Child 5-6;\n");
    +
    1628 CHECK(!DbcParser().ParseFileWithDiagnostics(fixture.path.string()).HasErrors(),
    +
    1629 "Disjoint adjacent ranges may share bits");
    +
    1630 fixture.Write(overlapping + "SG_ Second M : 24|8@1+ (1,0) [0|255] \"\" ECU\n"
    +
    1631 "SG_MUL_VAL_ 1 B Second 2-4;\n");
    +
    1632 const DbcParseResult independent = DbcParser().ParseFileWithDiagnostics(fixture.path.string());
    +
    1633 CHECK(independent.HasErrors() && independent.diagnostics.back().message ==
    +
    1634 "Signals overlap without mutually exclusive multiplexing conditions.",
    +
    1635 "Independent selector conditions do not imply exclusion");
    +
    1636
    +
    1637 // Validate manually constructed runtime graphs with the same rules.
    +
    1638 fixture.Write(definitions + "SG_MUL_VAL_ 1 Leaf Child 2-3;\n");
    +
    1639 const DecodeDatabase valid = DbcDecodeBuilder().Build(DbcParser().ParseFile(fixture.path.string()));
    +
    1640 for (unsigned kind = 0U; kind < 6U; ++kind) {
    +
    1641 std::vector<DecodeFrame> frames = valid.GetFrames();
    +
    1642 Multiplexing &leaf = frames[0].signals[2].multiplexing;
    +
    1643 switch (kind) {
    +
    1644 case 0:
    +
    1645 leaf.selector = "Missing";
    +
    1646 break;
    +
    1647 case 1:
    +
    1648 leaf.ranges[0] = {4U, 3U};
    +
    1649 break;
    +
    1650 case 2:
    +
    1651 leaf.ranges[0] = {0U, 256U};
    +
    1652 break;
    +
    1653 case 3:
    +
    1654 leaf.ranges.clear();
    +
    1655 break;
    +
    1656 case 4:
    +
    1657 frames[0].signals[0].multiplexing.selector = "Child";
    +
    1658 frames[0].signals[0].multiplexing.ranges = {{1U, 1U}};
    +
    1659 break;
    +
    1660 case 5:
    +
    1661 frames[0].signals[1].valueType = ValueType::Signed;
    +
    1662 break;
    +
    1663 }
    +
    1664 QVERIFY_THROWS_EXCEPTION(std::invalid_argument, (void) DecodeDatabase(frames));
    +
    1665 }
    +
    1666}
    +
    1667
    +
    1668int RunParserTests(int argc, char **argv) {
    +
    1669 DbcParserTest tests;
    +
    1670 return QTest::qExec(&tests, argc, argv);
    +
    1671}
    +
    1672
    +
    1673#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:87
    DecodedFrameValue Decode(const DecodeDatabase &database, const RawCanFrame &frame) const
    Decode one raw CAN frame.
    -
    Minimal DBC parser.
    Definition dbc_parser.h:43
    -
    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.
    - -
    void RegisterType(const std::string &keyword)
    Register or replace a handler and create its reusable instance.
    -
    bool Dispatch(const std::string &line, DbcParseContext &context) const
    +
    Minimal DBC parser.
    Definition dbc_parser.h:45
    +
    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.
    + +
    void RegisterType(const std::string &keyword)
    Register or replace a handler and create its reusable instance.
    +
    bool Dispatch(const std::string &line, DbcParseContext &context) const
    Interface for processing a registered DBC statement.
    virtual void Handle(const std::string &line, DbcParseContext &context) const =0
    Process a trimmed statement using state belonging to this parse.
    Builds a simple tree from parsed DBC database.
    @@ -1750,18 +1796,18 @@ $(document).ready(function(){initNavTree('dbc__parser__test_8cpp_source.html',''
    std::vector< FrameInfo > frames
    AttributeMap attributes
    std::vector< AttributeAssignment > attributeAssignments
    -
    Diagnostic message and physical source location.
    -
    std::string message
    -
    DbcDiagnosticSeverity severity
    -
    DbcDiagnosticCode code
    -
    std::string filePath
    -
    std::size_t lineNumber
    +
    Diagnostic message and physical source location.
    +
    std::string message
    +
    DbcDiagnosticSeverity severity
    +
    DbcDiagnosticCode code
    +
    std::string filePath
    +
    std::size_t lineNumber
    -
    Parsed database and diagnostics; the database is empty on error.
    -
    std::vector< DbcDiagnostic > diagnostics
    -
    bool HasErrors() const
    Return whether any diagnostic has error severity.
    -
    DbcDatabase database
    +
    Parsed database and diagnostics; the database is empty on error.
    +
    std::vector< DbcDiagnostic > diagnostics
    +
    bool HasErrors() const
    Return whether any diagnostic has error severity.
    +
    DbcDatabase database
    Fully decoded frame.
    Definition dbc_decoder.h:74
    std::shared_ptr< const DecodeFrame > definition
    Definition dbc_decoder.h:75
    diff --git a/docs/html/dbc__pipeline__test_8cpp_source.html b/docs/html/dbc__pipeline__test_8cpp_source.html index 859fd9b..f1c7dea 100644 --- a/docs/html/dbc__pipeline__test_8cpp_source.html +++ b/docs/html/dbc__pipeline__test_8cpp_source.html @@ -299,201 +299,251 @@ $(document).ready(function(){initNavTree('dbc__pipeline__test_8cpp_source.html',
    199 void emptyDatabase_data();
    200 void emptyDatabase();
    201 void databaseReplacement();
    -
    202};
    -
    203
    -
    204void PipelineTest::metadataLookupDecodeAndTree() {
    -
    205 const DbcParseResult parsed = ParseFixture(fixture);
    -
    206 QVERIFY(!parsed.HasErrors());
    -
    207 QVERIFY(parsed.diagnostics.empty());
    -
    208 const DbcDatabase expected = ExpectedMetadata();
    -
    209 QCOMPARE(parsed.database.frames.size(), expected.frames.size());
    -
    210 const DecodeDatabase runtime = DbcDecodeBuilder().Build(parsed.database);
    -
    211 QCOMPARE(runtime.GetFrames().size(), expected.frames.size());
    -
    212 const std::unique_ptr<TreeNode> tree = DbcTreeBuilder().Build(parsed.database);
    -
    213 QVERIFY(tree != nullptr);
    -
    214 CheckTree(*tree, expected);
    -
    215 for (std::size_t i = 0; i < expected.frames.size(); ++i) {
    -
    216 CheckFrame(parsed.database.frames[i], expected.frames[i]);
    -
    217 CheckFrame(runtime.GetFrames()[i], expected.frames[i]);
    -
    218 const FrameKey key(expected.frames[i].canId, expected.frames[i].isExtended);
    -
    219 const std::shared_ptr<const DecodeFrame> found = runtime.FindFrame(key);
    -
    220 QVERIFY(found != nullptr);
    -
    221 QCOMPARE(found.get(), &runtime.GetFrames()[i]);
    -
    222 QCOMPARE(DbcDecoder().FindFrame(runtime, key.canId, key.isExtended).get(), found.get());
    -
    223 for (std::size_t j = 0; j < found->signals.size(); ++j) {
    -
    224 const std::shared_ptr<const DecodeSignal> signal = runtime.FindSignal(SignalKey(key, found->signals[j].name));
    -
    225 QVERIFY(signal != nullptr);
    -
    226 QCOMPARE(signal.get(), &found->signals[j]);
    -
    227 }
    -
    228 }
    -
    229 QVERIFY(runtime.FindSignal(SignalKey(FrameKey(291U, true), "Mode")) == nullptr);
    -
    230 QVERIFY(runtime.FindFrame(FrameKey(292U, false)) == nullptr);
    -
    231
    -
    232 RawCanFrame raw;
    -
    233 raw.canId = 291U;
    -
    234 raw.data = {3U, 4U};
    -
    235 const DecodedFrameValue standard = DbcDecoder().Decode(runtime, raw);
    -
    236 QVERIFY(standard.valid);
    -
    237 QCOMPARE(standard.status, FrameDecodeStatus::Valid);
    -
    238 QVERIFY(standard.key == FrameKey(291U, false));
    -
    239 QCOMPARE(standard.definition.get(), runtime.FindFrame(standard.key).get());
    -
    240 QCOMPARE(standard.signals.size(), std::size_t(2));
    -
    241 for (std::size_t i = 0; i < standard.signals.size(); ++i) {
    -
    242 const DecodedSignalValue &signal = standard.signals[i];
    -
    243 QVERIFY(signal.valid);
    -
    244 QCOMPARE(signal.status, SignalDecodeStatus::Valid);
    -
    245 QVERIFY(signal.key == SignalKey(standard.key, i == 0 ? "Mode" : "State"));
    -
    246 QCOMPARE(signal.definition.get(), runtime.FindSignal(signal.key).get());
    -
    247 QCOMPARE(std::get<std::uint64_t>(signal.rawValue), i == 0 ? 3U : 4U);
    -
    248 QCOMPARE(signal.physicalValue, i == 0 ? 3.0 : 9.0);
    -
    249 QVERIFY(signal.GetValueDescription() != nullptr);
    -
    250 QCOMPARE(*signal.GetValueDescription(), std::string(i == 0 ? "Selected" : "Standard four"));
    -
    251 }
    -
    252 raw.isExtended = true;
    -
    253 raw.data = {0xFEU};
    -
    254 const DecodedFrameValue extended = DbcDecoder().Decode(runtime, raw);
    -
    255 QVERIFY(extended.valid);
    -
    256 QCOMPARE(extended.status, FrameDecodeStatus::Valid);
    -
    257 QVERIFY(extended.key == FrameKey(291U, true));
    -
    258 QCOMPARE(extended.definition.get(), runtime.FindFrame(extended.key).get());
    -
    259 QCOMPARE(extended.signals.size(), std::size_t(1));
    -
    260 const DecodedSignalValue &signal = extended.signals[0];
    -
    261 QVERIFY(signal.valid);
    -
    262 QCOMPARE(signal.status, SignalDecodeStatus::Valid);
    -
    263 QVERIFY(signal.key == SignalKey(extended.key, "State"));
    -
    264 QCOMPARE(signal.definition.get(), runtime.FindSignal(signal.key).get());
    -
    265 QCOMPARE(std::get<std::int64_t>(signal.rawValue), std::int64_t(-2));
    -
    266 QCOMPARE(signal.physicalValue, -4.0);
    -
    267 QVERIFY(signal.GetValueDescription() != nullptr);
    -
    268 QCOMPARE(*signal.GetValueDescription(), std::string("Extended minus two"));
    -
    269
    -
    270 raw.canId = 0x18F00442U;
    -
    271 raw.data.clear();
    -
    272 const DecodedFrameValue j1939 = DbcDecoder().Decode(runtime, raw);
    -
    273 QVERIFY(j1939.valid);
    -
    274 QCOMPARE(j1939.status, FrameDecodeStatus::Valid);
    -
    275 QVERIFY(j1939.key == FrameKey(0x18F00442U, true));
    -
    276 QCOMPARE(j1939.definition.get(), runtime.FindFrame(j1939.key).get());
    -
    277 QVERIFY(j1939.signals.empty());
    -
    278}
    -
    279
    -
    280void PipelineTest::emptyDatabase_data() {
    -
    281 QTest::addColumn<QString>("text");
    -
    282 QTest::newRow("empty-file") << QString();
    -
    283 QTest::newRow("headers-only") << QString::fromUtf8("VERSION \"empty\"\nNS_ :\nBS_:\nBU_: Sender\n");
    -
    284}
    -
    285
    -
    286void PipelineTest::emptyDatabase() {
    -
    287 QFETCH(QString, text);
    -
    288 const DbcParseResult parsed = ParseFixture(text.toStdString());
    -
    289 QVERIFY(!parsed.HasErrors());
    -
    290 QVERIFY(parsed.diagnostics.empty());
    -
    291 QVERIFY(parsed.database.frames.empty());
    -
    292 const DecodeDatabase runtime = DbcDecodeBuilder().Build(parsed.database);
    -
    293 QVERIFY(runtime.GetFrames().empty());
    -
    294 CheckUnknown(runtime, false);
    -
    295 CheckUnknown(runtime, true);
    -
    296 const std::unique_ptr<TreeNode> tree = DbcTreeBuilder().Build(parsed.database);
    -
    297 QVERIFY(tree != nullptr);
    -
    298 CheckTree(*tree, DbcDatabase());
    -
    299}
    -
    300
    -
    301void PipelineTest::databaseReplacement() {
    -
    302 DbcParseResult parsed = ParseFixture(fixture);
    -
    303 QVERIFY(!parsed.HasErrors());
    -
    304 DecodeDatabase runtime = DbcDecodeBuilder().Build(parsed.database);
    -
    305 std::unique_ptr<TreeNode> tree = DbcTreeBuilder().Build(parsed.database);
    -
    306 RawCanFrame raw;
    -
    307 raw.canId = 291U;
    -
    308 raw.data = {3U, 4U};
    -
    309 const DecodedFrameValue saved = DbcDecoder().Decode(runtime, raw);
    -
    310 QVERIFY(saved.valid);
    -
    311 QCOMPARE(saved.signals.size(), std::size_t(2));
    -
    312 QVERIFY(tree != nullptr);
    -
    313 std::unique_ptr<TreeNode> savedTree = std::move(tree);
    +
    202 void unsupportedSignalTypes_data();
    +
    203 void unsupportedSignalTypes();
    +
    204};
    +
    205
    +
    206void PipelineTest::metadataLookupDecodeAndTree() {
    +
    207 const DbcParseResult parsed = ParseFixture(fixture);
    +
    208 QVERIFY(!parsed.HasErrors());
    +
    209 QVERIFY(parsed.diagnostics.empty());
    +
    210 const DbcDatabase expected = ExpectedMetadata();
    +
    211 QCOMPARE(parsed.database.frames.size(), expected.frames.size());
    +
    212 const DecodeDatabase runtime = DbcDecodeBuilder().Build(parsed.database);
    +
    213 QCOMPARE(runtime.GetFrames().size(), expected.frames.size());
    +
    214 const std::unique_ptr<TreeNode> tree = DbcTreeBuilder().Build(parsed.database);
    +
    215 QVERIFY(tree != nullptr);
    +
    216 CheckTree(*tree, expected);
    +
    217 for (std::size_t i = 0; i < expected.frames.size(); ++i) {
    +
    218 CheckFrame(parsed.database.frames[i], expected.frames[i]);
    +
    219 CheckFrame(runtime.GetFrames()[i], expected.frames[i]);
    +
    220 const FrameKey key(expected.frames[i].canId, expected.frames[i].isExtended);
    +
    221 const std::shared_ptr<const DecodeFrame> found = runtime.FindFrame(key);
    +
    222 QVERIFY(found != nullptr);
    +
    223 QCOMPARE(found.get(), &runtime.GetFrames()[i]);
    +
    224 QCOMPARE(DbcDecoder().FindFrame(runtime, key.canId, key.isExtended).get(), found.get());
    +
    225 for (std::size_t j = 0; j < found->signals.size(); ++j) {
    +
    226 const std::shared_ptr<const DecodeSignal> signal = runtime.FindSignal(SignalKey(key, found->signals[j].name));
    +
    227 QVERIFY(signal != nullptr);
    +
    228 QCOMPARE(signal.get(), &found->signals[j]);
    +
    229 }
    +
    230 }
    +
    231 QVERIFY(runtime.FindSignal(SignalKey(FrameKey(291U, true), "Mode")) == nullptr);
    +
    232 QVERIFY(runtime.FindFrame(FrameKey(292U, false)) == nullptr);
    +
    233
    +
    234 RawCanFrame raw;
    +
    235 raw.canId = 291U;
    +
    236 raw.data = {3U, 4U};
    +
    237 const DecodedFrameValue standard = DbcDecoder().Decode(runtime, raw);
    +
    238 QVERIFY(standard.valid);
    +
    239 QCOMPARE(standard.status, FrameDecodeStatus::Valid);
    +
    240 QVERIFY(standard.key == FrameKey(291U, false));
    +
    241 QCOMPARE(standard.definition.get(), runtime.FindFrame(standard.key).get());
    +
    242 QCOMPARE(standard.signals.size(), std::size_t(2));
    +
    243 for (std::size_t i = 0; i < standard.signals.size(); ++i) {
    +
    244 const DecodedSignalValue &signal = standard.signals[i];
    +
    245 QVERIFY(signal.valid);
    +
    246 QCOMPARE(signal.status, SignalDecodeStatus::Valid);
    +
    247 QVERIFY(signal.key == SignalKey(standard.key, i == 0 ? "Mode" : "State"));
    +
    248 QCOMPARE(signal.definition.get(), runtime.FindSignal(signal.key).get());
    +
    249 QCOMPARE(std::get<std::uint64_t>(signal.rawValue), i == 0 ? 3U : 4U);
    +
    250 QCOMPARE(signal.physicalValue, i == 0 ? 3.0 : 9.0);
    +
    251 QVERIFY(signal.GetValueDescription() != nullptr);
    +
    252 QCOMPARE(*signal.GetValueDescription(), std::string(i == 0 ? "Selected" : "Standard four"));
    +
    253 }
    +
    254 raw.isExtended = true;
    +
    255 raw.data = {0xFEU};
    +
    256 const DecodedFrameValue extended = DbcDecoder().Decode(runtime, raw);
    +
    257 QVERIFY(extended.valid);
    +
    258 QCOMPARE(extended.status, FrameDecodeStatus::Valid);
    +
    259 QVERIFY(extended.key == FrameKey(291U, true));
    +
    260 QCOMPARE(extended.definition.get(), runtime.FindFrame(extended.key).get());
    +
    261 QCOMPARE(extended.signals.size(), std::size_t(1));
    +
    262 const DecodedSignalValue &signal = extended.signals[0];
    +
    263 QVERIFY(signal.valid);
    +
    264 QCOMPARE(signal.status, SignalDecodeStatus::Valid);
    +
    265 QVERIFY(signal.key == SignalKey(extended.key, "State"));
    +
    266 QCOMPARE(signal.definition.get(), runtime.FindSignal(signal.key).get());
    +
    267 QCOMPARE(std::get<std::int64_t>(signal.rawValue), std::int64_t(-2));
    +
    268 QCOMPARE(signal.physicalValue, -4.0);
    +
    269 QVERIFY(signal.GetValueDescription() != nullptr);
    +
    270 QCOMPARE(*signal.GetValueDescription(), std::string("Extended minus two"));
    +
    271
    +
    272 raw.canId = 0x18F00442U;
    +
    273 raw.data.clear();
    +
    274 const DecodedFrameValue j1939 = DbcDecoder().Decode(runtime, raw);
    +
    275 QVERIFY(j1939.valid);
    +
    276 QCOMPARE(j1939.status, FrameDecodeStatus::Valid);
    +
    277 QVERIFY(j1939.key == FrameKey(0x18F00442U, true));
    +
    278 QCOMPARE(j1939.definition.get(), runtime.FindFrame(j1939.key).get());
    +
    279 QVERIFY(j1939.signals.empty());
    +
    280}
    +
    281
    +
    282void PipelineTest::unsupportedSignalTypes_data() {
    +
    283 QTest::addColumn<unsigned>("bits");
    +
    284 QTest::addColumn<unsigned>("type");
    +
    285 QTest::addColumn<std::vector<std::uint8_t>>("payload");
    +
    286 QTest::addColumn<std::uint64_t>("integerValue");
    +
    287 QTest::newRow("float32-one") << 32U << 1U << std::vector<std::uint8_t>{0U, 0U, 128U, 63U}
    +
    288 << std::uint64_t(0x3F800000U);
    +
    289 QTest::newRow("float64-one") << 64U << 2U << std::vector<std::uint8_t>{0U, 0U, 0U, 0U, 0U, 0U, 240U, 63U}
    +
    290 << std::uint64_t(0x3FF0000000000000ULL);
    +
    291}
    +
    292
    +
    293void PipelineTest::unsupportedSignalTypes() {
    +
    294 QFETCH(unsigned, bits);
    +
    295 QFETCH(unsigned, type);
    +
    296 QFETCH(std::vector<std::uint8_t>, payload);
    +
    297 QFETCH(std::uint64_t, integerValue);
    +
    298 const std::string definitions = "BO_ 1 Frame: " + std::to_string(bits / 8U) + " ECU\n"
    +
    299 "SG_ Value : 0|" + std::to_string(bits) + "@1+ (1,0) [0|100] \"\" ECU\n";
    +
    300 RawCanFrame raw;
    +
    301 raw.canId = 1U;
    +
    302 raw.data = payload;
    +
    303 const DbcDecodeBuilder builder;
    +
    304 const DbcDecoder decoder;
    +
    305
    +
    306 // Without a type override, the same bits are a supported integer signal.
    +
    307 const DbcParseResult integers = ParseFixture(definitions);
    +
    308 QVERIFY(!integers.HasErrors());
    +
    309 const DecodedFrameValue integerFrame = decoder.Decode(builder.Build(integers.database), raw);
    +
    310 QCOMPARE(integerFrame.status, FrameDecodeStatus::Valid);
    +
    311 QCOMPARE(integerFrame.signals.size(), std::size_t(1));
    +
    312 QVERIFY(integerFrame.signals[0].valid);
    +
    313 QCOMPARE(std::get<std::uint64_t>(integerFrame.signals[0].rawValue), integerValue);
    314
    -
    315 parsed = ParseFixture(R"DBC(BO_ 291 Replacement: 1 NewSender
    -
    316 SG_ NewState : 0|8@1+ (3,-1) [-1|764] "V" NewReceiver
    -
    317CM_ BO_ 291 "Replacement frame";
    -
    318CM_ SG_ 291 NewState "Replacement signal";
    -
    319VAL_ 291 NewState 4 "New four";
    -
    320)DBC");
    -
    321 QVERIFY(!parsed.HasErrors());
    -
    322 QVERIFY(parsed.diagnostics.empty());
    -
    323 FrameInfo replacement;
    -
    324 replacement.name = "Replacement";
    -
    325 replacement.canId = 291U;
    -
    326 replacement.dlc = 1U;
    -
    327 replacement.transmitter = "NewSender";
    -
    328 replacement.comment = "Replacement frame";
    -
    329 SignalInfo state;
    -
    330 state.name = "NewState";
    -
    331 state.length = 8U;
    -
    332 state.factor = 3;
    -
    333 state.offset = -1;
    -
    334 state.minimum = -1;
    -
    335 state.maximum = 764;
    -
    336 state.unit = "V";
    -
    337 state.receivers = {"NewReceiver"};
    -
    338 state.comment = "Replacement signal";
    -
    339 state.valueDescriptions = {{std::uint64_t(4), "New four"}};
    -
    340 replacement.signals = {state};
    -
    341 const DbcDatabase expected{{replacement}};
    -
    342 QCOMPARE(parsed.database.frames.size(), std::size_t(1));
    -
    343 CheckFrame(parsed.database.frames[0], replacement);
    -
    344 runtime = DbcDecodeBuilder().Build(parsed.database);
    -
    345 tree = DbcTreeBuilder().Build(parsed.database);
    -
    346 QVERIFY(tree != nullptr);
    -
    347 QCOMPARE(runtime.GetFrames().size(), std::size_t(1));
    -
    348 CheckFrame(runtime.GetFrames()[0], replacement);
    -
    349 CheckTree(*tree, expected);
    -
    350 CheckUnknown(runtime, true);
    -
    351 QVERIFY(runtime.FindFrame(FrameKey(0x18F00442U, true)) == nullptr);
    -
    352 QVERIFY(runtime.FindSignal(SignalKey(FrameKey(291U, false), "State")) == nullptr);
    -
    353 QVERIFY(runtime.FindSignal(SignalKey(FrameKey(291U, false), "Mode")) == nullptr);
    -
    354 raw.data = {4U};
    -
    355 const DecodedFrameValue current = DbcDecoder().Decode(runtime, raw);
    -
    356 QVERIFY(current.valid);
    -
    357 QCOMPARE(current.status, FrameDecodeStatus::Valid);
    -
    358 QCOMPARE(current.signals.size(), std::size_t(1));
    -
    359 QVERIFY(current.signals[0].valid);
    -
    360 QVERIFY(current.signals[0].key == SignalKey(FrameKey(291U, false), "NewState"));
    -
    361 QCOMPARE(current.signals[0].definition.get(), runtime.FindSignal(current.signals[0].key).get());
    -
    362 QCOMPARE(std::get<std::uint64_t>(current.signals[0].rawValue), std::uint64_t(4));
    -
    363 QCOMPARE(current.signals[0].physicalValue, 11.0);
    -
    364 QVERIFY(current.signals[0].GetValueDescription() != nullptr);
    -
    365 QCOMPARE(*current.signals[0].GetValueDescription(), std::string("New four"));
    -
    366
    -
    367 parsed = ParseFixture("");
    -
    368 QVERIFY(!parsed.HasErrors());
    -
    369 QVERIFY(parsed.diagnostics.empty());
    -
    370 runtime = DbcDecodeBuilder().Build(parsed.database);
    -
    371 tree = DbcTreeBuilder().Build(parsed.database);
    -
    372 QVERIFY(tree != nullptr);
    -
    373 QVERIFY(runtime.GetFrames().empty());
    -
    374 CheckUnknown(runtime, false);
    -
    375 CheckUnknown(runtime, true);
    -
    376 CheckTree(*tree, DbcDatabase());
    -
    377 QVERIFY(runtime.FindSignal(current.signals[0].key) == nullptr);
    -
    378 CheckFrame(*current.definition, replacement);
    -
    379 CheckFrame(*saved.definition, ExpectedMetadata().frames[0]);
    -
    380 CheckTree(*savedTree, ExpectedMetadata());
    -
    381 QCOMPARE(saved.signals[1].physicalValue, 9.0);
    -
    382 QVERIFY(saved.signals[1].GetValueDescription() != nullptr);
    -
    383 QCOMPARE(*saved.signals[1].GetValueDescription(), std::string("Standard four"));
    -
    384}
    -
    385
    -
    386int RunPipelineTests(int argc, char **argv) {
    -
    387 PipelineTest test;
    -
    388 return QTest::qExec(&test, argc, argv);
    -
    389}
    -
    390
    -
    391#include "dbc_pipeline_test.moc"
    +
    315 const DbcParseResult floatingPoint = ParseFixture(definitions +
    +
    316 "SIG_VALTYPE_ 1 Value : " + std::to_string(type) + ";\n");
    +
    317 QVERIFY(floatingPoint.HasErrors());
    +
    318 QCOMPARE(floatingPoint.diagnostics.size(), std::size_t(1));
    +
    319 QCOMPARE(floatingPoint.diagnostics[0].code, DbcDiagnosticCode::UnsupportedSignalType);
    +
    320 QCOMPARE(floatingPoint.diagnostics[0].lineNumber, std::size_t(3));
    +
    321 QVERIFY(floatingPoint.database.frames.empty());
    +
    322 // Even if a caller overlooks the error, no integer definition is published.
    +
    323 const DecodedFrameValue rejected = decoder.Decode(builder.Build(floatingPoint.database), raw);
    +
    324 QCOMPARE(rejected.status, FrameDecodeStatus::UnknownFrame);
    +
    325 QVERIFY(!rejected.valid);
    +
    326 QVERIFY(!rejected.definition);
    +
    327 QVERIFY(rejected.signals.empty());
    +
    328}
    +
    329
    +
    330void PipelineTest::emptyDatabase_data() {
    +
    331 QTest::addColumn<QString>("text");
    +
    332 QTest::newRow("empty-file") << QString();
    +
    333 QTest::newRow("headers-only") << QString::fromUtf8("VERSION \"empty\"\nNS_ :\nBS_:\nBU_: Sender\n");
    +
    334}
    +
    335
    +
    336void PipelineTest::emptyDatabase() {
    +
    337 QFETCH(QString, text);
    +
    338 const DbcParseResult parsed = ParseFixture(text.toStdString());
    +
    339 QVERIFY(!parsed.HasErrors());
    +
    340 QVERIFY(parsed.diagnostics.empty());
    +
    341 QVERIFY(parsed.database.frames.empty());
    +
    342 const DecodeDatabase runtime = DbcDecodeBuilder().Build(parsed.database);
    +
    343 QVERIFY(runtime.GetFrames().empty());
    +
    344 CheckUnknown(runtime, false);
    +
    345 CheckUnknown(runtime, true);
    +
    346 const std::unique_ptr<TreeNode> tree = DbcTreeBuilder().Build(parsed.database);
    +
    347 QVERIFY(tree != nullptr);
    +
    348 CheckTree(*tree, DbcDatabase());
    +
    349}
    +
    350
    +
    351void PipelineTest::databaseReplacement() {
    +
    352 DbcParseResult parsed = ParseFixture(fixture);
    +
    353 QVERIFY(!parsed.HasErrors());
    +
    354 DecodeDatabase runtime = DbcDecodeBuilder().Build(parsed.database);
    +
    355 std::unique_ptr<TreeNode> tree = DbcTreeBuilder().Build(parsed.database);
    +
    356 RawCanFrame raw;
    +
    357 raw.canId = 291U;
    +
    358 raw.data = {3U, 4U};
    +
    359 const DecodedFrameValue saved = DbcDecoder().Decode(runtime, raw);
    +
    360 QVERIFY(saved.valid);
    +
    361 QCOMPARE(saved.signals.size(), std::size_t(2));
    +
    362 QVERIFY(tree != nullptr);
    +
    363 std::unique_ptr<TreeNode> savedTree = std::move(tree);
    +
    364
    +
    365 parsed = ParseFixture(R"DBC(BO_ 291 Replacement: 1 NewSender
    +
    366 SG_ NewState : 0|8@1+ (3,-1) [-1|764] "V" NewReceiver
    +
    367CM_ BO_ 291 "Replacement frame";
    +
    368CM_ SG_ 291 NewState "Replacement signal";
    +
    369VAL_ 291 NewState 4 "New four";
    +
    370)DBC");
    +
    371 QVERIFY(!parsed.HasErrors());
    +
    372 QVERIFY(parsed.diagnostics.empty());
    +
    373 FrameInfo replacement;
    +
    374 replacement.name = "Replacement";
    +
    375 replacement.canId = 291U;
    +
    376 replacement.dlc = 1U;
    +
    377 replacement.transmitter = "NewSender";
    +
    378 replacement.comment = "Replacement frame";
    +
    379 SignalInfo state;
    +
    380 state.name = "NewState";
    +
    381 state.length = 8U;
    +
    382 state.factor = 3;
    +
    383 state.offset = -1;
    +
    384 state.minimum = -1;
    +
    385 state.maximum = 764;
    +
    386 state.unit = "V";
    +
    387 state.receivers = {"NewReceiver"};
    +
    388 state.comment = "Replacement signal";
    +
    389 state.valueDescriptions = {{std::uint64_t(4), "New four"}};
    +
    390 replacement.signals = {state};
    +
    391 const DbcDatabase expected{{replacement}};
    +
    392 QCOMPARE(parsed.database.frames.size(), std::size_t(1));
    +
    393 CheckFrame(parsed.database.frames[0], replacement);
    +
    394 runtime = DbcDecodeBuilder().Build(parsed.database);
    +
    395 tree = DbcTreeBuilder().Build(parsed.database);
    +
    396 QVERIFY(tree != nullptr);
    +
    397 QCOMPARE(runtime.GetFrames().size(), std::size_t(1));
    +
    398 CheckFrame(runtime.GetFrames()[0], replacement);
    +
    399 CheckTree(*tree, expected);
    +
    400 CheckUnknown(runtime, true);
    +
    401 QVERIFY(runtime.FindFrame(FrameKey(0x18F00442U, true)) == nullptr);
    +
    402 QVERIFY(runtime.FindSignal(SignalKey(FrameKey(291U, false), "State")) == nullptr);
    +
    403 QVERIFY(runtime.FindSignal(SignalKey(FrameKey(291U, false), "Mode")) == nullptr);
    +
    404 raw.data = {4U};
    +
    405 const DecodedFrameValue current = DbcDecoder().Decode(runtime, raw);
    +
    406 QVERIFY(current.valid);
    +
    407 QCOMPARE(current.status, FrameDecodeStatus::Valid);
    +
    408 QCOMPARE(current.signals.size(), std::size_t(1));
    +
    409 QVERIFY(current.signals[0].valid);
    +
    410 QVERIFY(current.signals[0].key == SignalKey(FrameKey(291U, false), "NewState"));
    +
    411 QCOMPARE(current.signals[0].definition.get(), runtime.FindSignal(current.signals[0].key).get());
    +
    412 QCOMPARE(std::get<std::uint64_t>(current.signals[0].rawValue), std::uint64_t(4));
    +
    413 QCOMPARE(current.signals[0].physicalValue, 11.0);
    +
    414 QVERIFY(current.signals[0].GetValueDescription() != nullptr);
    +
    415 QCOMPARE(*current.signals[0].GetValueDescription(), std::string("New four"));
    +
    416
    +
    417 parsed = ParseFixture("");
    +
    418 QVERIFY(!parsed.HasErrors());
    +
    419 QVERIFY(parsed.diagnostics.empty());
    +
    420 runtime = DbcDecodeBuilder().Build(parsed.database);
    +
    421 tree = DbcTreeBuilder().Build(parsed.database);
    +
    422 QVERIFY(tree != nullptr);
    +
    423 QVERIFY(runtime.GetFrames().empty());
    +
    424 CheckUnknown(runtime, false);
    +
    425 CheckUnknown(runtime, true);
    +
    426 CheckTree(*tree, DbcDatabase());
    +
    427 QVERIFY(runtime.FindSignal(current.signals[0].key) == nullptr);
    +
    428 CheckFrame(*current.definition, replacement);
    +
    429 CheckFrame(*saved.definition, ExpectedMetadata().frames[0]);
    +
    430 CheckTree(*savedTree, ExpectedMetadata());
    +
    431 QCOMPARE(saved.signals[1].physicalValue, 9.0);
    +
    432 QVERIFY(saved.signals[1].GetValueDescription() != nullptr);
    +
    433 QCOMPARE(*saved.signals[1].GetValueDescription(), std::string("Standard four"));
    +
    434}
    +
    435
    +
    436int RunPipelineTests(int argc, char **argv) {
    +
    437 PipelineTest test;
    +
    438 return QTest::qExec(&test, argc, argv);
    +
    439}
    +
    440
    +
    441#include "dbc_pipeline_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:87
    DecodedFrameValue Decode(const DecodeDatabase &database, const RawCanFrame &frame) const
    Decode one raw CAN frame.
    -
    Minimal DBC parser.
    Definition dbc_parser.h:43
    +
    Minimal DBC parser.
    Definition dbc_parser.h:45
    Builds a simple tree from parsed DBC database.
    std::unique_ptr< TreeNode > Build(const DbcDatabase &database) const
    Build tree representation of parsed DBC data.
    Immutable runtime snapshot. Copies share storage; reload replaces a snapshot. Owning lookup handles a...
    @@ -517,10 +567,10 @@ $(document).ready(function(){initNavTree('dbc__pipeline__test_8cpp_source.html',
    ValueType
    Signal numeric type.
    Parsed DBC content stored in a simple internal form.
    std::vector< FrameInfo > frames
    -
    Parsed database and diagnostics; the database is empty on error.
    -
    std::vector< DbcDiagnostic > diagnostics
    -
    bool HasErrors() const
    Return whether any diagnostic has error severity.
    -
    DbcDatabase database
    +
    Parsed database and diagnostics; the database is empty on error.
    +
    std::vector< DbcDiagnostic > diagnostics
    +
    bool HasErrors() const
    Return whether any diagnostic has error severity.
    +
    DbcDatabase database
    Runtime-ready signal definition.
    ValueType valueType
    diff --git a/docs/html/dbc__statement__handler_8cpp_source.html b/docs/html/dbc__statement__handler_8cpp_source.html index 4cf2b4b..333d96b 100644 --- a/docs/html/dbc__statement__handler_8cpp_source.html +++ b/docs/html/dbc__statement__handler_8cpp_source.html @@ -520,199 +520,207 @@ $(document).ready(function(){initNavTree('dbc__statement__handler_8cpp_source.ht
    411 RegisterType<DbcMultiplexingHandler>("SG_MUL_VAL_");
    412 RegisterType<DbcFrameHandler>("BO_");
    413 RegisterType<DbcSignalHandler>("SG_");
    -
    414 RegisterType<DbcCommentHandler>("CM_");
    -
    415 RegisterType<DbcValueDescriptionHandler>("VAL_");
    -
    416 RegisterType<DbcAttributeHandler>("BA_DEF_");
    -
    417 RegisterType<DbcAttributeHandler>("BA_DEF_DEF_");
    -
    418 RegisterType<DbcAttributeHandler>("BA_");
    -
    419}
    +
    414 RegisterType<DbcUnsupportedSignalTypeHandler>("SIG_VALTYPE_");
    +
    415 RegisterType<DbcCommentHandler>("CM_");
    +
    416 RegisterType<DbcValueDescriptionHandler>("VAL_");
    +
    417 RegisterType<DbcAttributeHandler>("BA_DEF_");
    +
    418 RegisterType<DbcAttributeHandler>("BA_DEF_DEF_");
    +
    419 RegisterType<DbcAttributeHandler>("BA_");
    +
    420}
    -
    420
    -
    421void DbcHeaderHandler::Handle(const std::string &, DbcParseContext &) const {}
    -
    422
    -
    -
    423bool DbcStatementDispatcher::Dispatch(const std::string &line,
    -
    424 DbcParseContext &context) const {
    -
    425 std::istringstream stream(line);
    -
    426 std::string keyword;
    -
    427 stream >> keyword;
    -
    428 const HandlerMap::const_iterator handler = m_handlers.find(keyword);
    -
    429 if (handler == m_handlers.end())
    -
    430 return false;
    -
    431 handler->second->Handle(line, context);
    -
    432 return true;
    -
    433}
    +
    421
    +
    + +
    423 throw std::runtime_error("SIG_VALTYPE_ declarations are unsupported; refusing potentially incorrect integer decoding.");
    +
    424}
    -
    434
    -
    -
    435void DbcValueDescriptionHandler::Handle(const std::string &line, DbcParseContext &context) const {
    -
    436 static const std::regex header(R"(^VAL_[ \t]+([^ \t]+)[ \t]+([^ \t;]+)[ \t]+(.*)$)");
    -
    437 static const std::regex pair(R"dbc(^([^ \t]+)[ \t]+"((?:[^"\\]|\\["\\])*)")dbc");
    -
    438 std::smatch fields;
    -
    439 if (!std::regex_match(line, fields, header))
    -
    440 throw std::runtime_error("Malformed or unsupported VAL_ statement.");
    -
    441 const std::uint32_t rawId = ParseDecimal(fields[1].str(), std::numeric_limits<std::uint32_t>::max(), "VAL_ CAN ID");
    -
    442 if ((rawId & 0x60000000U) != 0U)
    -
    443 throw std::runtime_error("VAL_ CAN ID exceeds the 29-bit identifier range.");
    - -
    445 NormalizeCanId(rawId, pending.canId, pending.isExtended);
    -
    446 pending.signalName = fields[2].str();
    -
    447 pending.lineNumber = context.lineNumber;
    -
    448 std::string rest = fields[3].str();
    -
    449 while (rest != ";") {
    -
    450 if (!std::regex_search(rest, fields, pair))
    -
    451 throw std::runtime_error("Malformed VAL_ value/description pair or missing semicolon.");
    -
    452 const std::string number = fields[1].str();
    -
    453 const RawSignalValue key = ParseValueKey(number);
    -
    454 const std::string description = UnescapeDescription(fields[2].str());
    -
    455 if (!pending.descriptions.emplace(key, description).second)
    -
    456 throw std::runtime_error("Duplicate VAL_ value key: " + number);
    -
    457 const std::size_t consumed = static_cast<std::size_t>(fields.length());
    -
    458 rest.erase(0, consumed);
    -
    459 if (!rest.empty() && rest[0] != ';' && rest[0] != ' ' && rest[0] != '\t')
    -
    460 throw std::runtime_error("Missing whitespace between VAL_ pairs.");
    -
    461 rest = dbc_detail::TrimText(rest);
    -
    462 }
    -
    463 if (pending.descriptions.empty())
    -
    464 throw std::runtime_error("VAL_ requires at least one value/description pair.");
    -
    465 context.pendingValues.push_back(std::move(pending));
    -
    466}
    +
    425
    +
    426void DbcHeaderHandler::Handle(const std::string &, DbcParseContext &) const {}
    +
    427
    +
    +
    428bool DbcStatementDispatcher::Dispatch(const std::string &line,
    +
    429 DbcParseContext &context) const {
    +
    430 std::istringstream stream(line);
    +
    431 std::string keyword;
    +
    432 stream >> keyword;
    +
    433 const HandlerMap::const_iterator handler = m_handlers.find(keyword);
    +
    434 if (handler == m_handlers.end())
    +
    435 return false;
    +
    436 handler->second->Handle(line, context);
    +
    437 return true;
    +
    438}
    -
    467
    -
    -
    468void DbcParseContext::ResolveValueDescriptions(const std::string &filePath) {
    -
    469 for (const PendingValueDescriptions &pending: pendingValues) {
    -
    470 try {
    -
    471 FrameInfo *frame = FindUniqueFrame(database, pending.canId, pending.isExtended);
    -
    472 SignalInfo *signal = FindUniqueSignal(*frame, pending.signalName);
    -
    473 for (const ValueDescriptions::value_type &entry: pending.descriptions) {
    -
    474 if (!signal->valueDescriptions.insert(entry).second)
    -
    475 throw std::runtime_error("Duplicate VAL_ value key for signal: " + pending.signalName);
    -
    476 }
    -
    477 } catch (const std::exception &error) {
    -
    478 throw DbcDiagnosticError(DbcDiagnosticCode::InvalidValueDescription,
    -
    479 filePath, pending.lineNumber, error.what());
    -
    480 }
    -
    481 }
    -
    482}
    +
    439
    +
    +
    440void DbcValueDescriptionHandler::Handle(const std::string &line, DbcParseContext &context) const {
    +
    441 static const std::regex header(R"(^VAL_[ \t]+([^ \t]+)[ \t]+([^ \t;]+)[ \t]+(.*)$)");
    +
    442 static const std::regex pair(R"dbc(^([^ \t]+)[ \t]+"((?:[^"\\]|\\["\\])*)")dbc");
    +
    443 std::smatch fields;
    +
    444 if (!std::regex_match(line, fields, header))
    +
    445 throw std::runtime_error("Malformed or unsupported VAL_ statement.");
    +
    446 const std::uint32_t rawId = ParseDecimal(fields[1].str(), std::numeric_limits<std::uint32_t>::max(), "VAL_ CAN ID");
    +
    447 if ((rawId & 0x60000000U) != 0U)
    +
    448 throw std::runtime_error("VAL_ CAN ID exceeds the 29-bit identifier range.");
    + +
    450 NormalizeCanId(rawId, pending.canId, pending.isExtended);
    +
    451 pending.signalName = fields[2].str();
    +
    452 pending.lineNumber = context.lineNumber;
    +
    453 std::string rest = fields[3].str();
    +
    454 while (rest != ";") {
    +
    455 if (!std::regex_search(rest, fields, pair))
    +
    456 throw std::runtime_error("Malformed VAL_ value/description pair or missing semicolon.");
    +
    457 const std::string number = fields[1].str();
    +
    458 const RawSignalValue key = ParseValueKey(number);
    +
    459 const std::string description = UnescapeDescription(fields[2].str());
    +
    460 if (!pending.descriptions.emplace(key, description).second)
    +
    461 throw std::runtime_error("Duplicate VAL_ value key: " + number);
    +
    462 const std::size_t consumed = static_cast<std::size_t>(fields.length());
    +
    463 rest.erase(0, consumed);
    +
    464 if (!rest.empty() && rest[0] != ';' && rest[0] != ' ' && rest[0] != '\t')
    +
    465 throw std::runtime_error("Missing whitespace between VAL_ pairs.");
    +
    466 rest = dbc_detail::TrimText(rest);
    +
    467 }
    +
    468 if (pending.descriptions.empty())
    +
    469 throw std::runtime_error("VAL_ requires at least one value/description pair.");
    +
    470 context.pendingValues.push_back(std::move(pending));
    +
    471}
    -
    483
    -
    -
    484void DbcMultiplexingHandler::Handle(const std::string &line, DbcParseContext &context) const {
    -
    485 static const std::regex syntax(R"(^SG_MUL_VAL_[ \t]+([0-9]+)[ \t]+([^ \t;]+)[ \t]+([^ \t;]+)[ \t]+(.+);[ \t]*$)");
    -
    486 static const std::regex rangeSyntax(R"(^[ \t]*([0-9]+)[ \t]*-[ \t]*([0-9]+)[ \t]*$)");
    -
    487 std::smatch fields;
    -
    488 if (!std::regex_match(line, fields, syntax))
    -
    489 throw std::runtime_error("Malformed SG_MUL_VAL_ statement.");
    -
    490 PendingMultiplexing pending;
    -
    491 NormalizeCanId(ParseCanId(fields[1].str()), pending.canId, pending.isExtended);
    -
    492 pending.signalName = fields[2].str();
    -
    493 pending.selectorName = fields[3].str();
    -
    494 pending.lineNumber = context.lineNumber;
    -
    495 const std::string list = fields[4].str();
    -
    496 std::size_t begin = 0U;
    -
    497 for (;;) {
    -
    498 const std::size_t comma = list.find(',', begin);
    -
    499 const std::string item = list.substr(begin, comma == std::string::npos ? comma : comma - begin);
    -
    500 if (!std::regex_match(item, fields, rangeSyntax))
    -
    501 throw std::runtime_error("Malformed multiplex range.");
    -
    502 MultiplexRange range;
    -
    503 const std::string lower = fields[1].str();
    -
    504 const std::string upper = fields[2].str();
    -
    505 const std::from_chars_result low = std::from_chars(lower.data(), lower.data() + lower.size(), range.minimum);
    -
    506 const std::from_chars_result high = std::from_chars(upper.data(), upper.data() + upper.size(), range.maximum);
    -
    507 if (low.ec != std::errc() || high.ec != std::errc() || range.minimum > range.maximum)
    -
    508 throw std::runtime_error("Invalid multiplex range.");
    -
    509 pending.ranges.push_back(range);
    -
    510 if (comma == std::string::npos)
    -
    511 break;
    -
    512 begin = comma + 1U;
    -
    513 }
    -
    514 context.pendingMultiplexing.push_back(std::move(pending));
    -
    515}
    +
    472
    +
    +
    473void DbcParseContext::ResolveValueDescriptions(const std::string &filePath) {
    +
    474 for (const PendingValueDescriptions &pending: pendingValues) {
    +
    475 try {
    +
    476 FrameInfo *frame = FindUniqueFrame(database, pending.canId, pending.isExtended);
    +
    477 SignalInfo *signal = FindUniqueSignal(*frame, pending.signalName);
    +
    478 for (const ValueDescriptions::value_type &entry: pending.descriptions) {
    +
    479 if (!signal->valueDescriptions.insert(entry).second)
    +
    480 throw std::runtime_error("Duplicate VAL_ value key for signal: " + pending.signalName);
    +
    481 }
    +
    482 } catch (const std::exception &error) {
    +
    483 throw DbcDiagnosticError(DbcDiagnosticCode::InvalidValueDescription,
    +
    484 filePath, pending.lineNumber, error.what());
    +
    485 }
    +
    486 }
    +
    487}
    -
    516
    -
    517void DbcParseContext::ResolveMultiplexing(const std::string &filePath) {
    -
    518 for (const PendingMultiplexing &pending: pendingMultiplexing) {
    -
    519 try {
    -
    520 FrameInfo *frame = FindUniqueFrame(database, pending.canId, pending.isExtended, "SG_MUL_VAL_");
    -
    521 SignalInfo *signal = FindUniqueSignal(*frame, pending.signalName, "SG_MUL_VAL_");
    -
    522 SignalInfo *selector = FindUniqueSignal(*frame, pending.selectorName, "SG_MUL_VAL_");
    -
    523 if (!IsMultiplexSelector(selector->multiplexing.role) || selector->isSigned)
    -
    524 throw std::runtime_error("Referenced signal must be an unsigned multiplex selector.");
    -
    525 if (!signal->multiplexing.selector.empty() && signal->multiplexing.selector != pending.selectorName)
    -
    526 throw std::runtime_error("Multiple parent selectors for one signal are unsupported.");
    -
    527 for (const MultiplexRange &range: pending.ranges) {
    -
    528 if (selector->length < 64U && range.maximum >= (std::uint64_t(1U) << selector->length))
    -
    529 throw std::runtime_error("Branch value exceeds selector raw range.");
    -
    530 }
    -
    531 signal->multiplexing.selector = pending.selectorName;
    -
    532 signal->multiplexing.ranges.insert(signal->multiplexing.ranges.end(), pending.ranges.begin(), pending.ranges.end());
    -
    533 if (!IsMultiplexSelector(signal->multiplexing.role))
    -
    534 signal->multiplexing.role = MultiplexRole::Branch;
    -
    535 else
    -
    536 signal->multiplexing.role = MultiplexRole::BranchSelector;
    -
    537 } catch (const std::exception &error) {
    -
    538 throw DbcDiagnosticError(DbcDiagnosticCode::InvalidMultiplexing, filePath, pending.lineNumber, error.what());
    -
    539 }
    -
    540 }
    -
    541}
    -
    542
    -
    543namespace {
    -
    544 std::vector<std::uint64_t> SignalBits(const SignalInfo &signal) {
    -
    545 std::vector<std::uint64_t> bits;
    -
    546 std::uint64_t bit = signal.startBit;
    -
    547 for (std::uint32_t index = 0U; index < signal.length; ++index) {
    -
    548 bits.push_back(bit);
    -
    549 bit = NextSignalBit(bit, signal.isLittleEndian);
    -
    550 }
    -
    551 return bits;
    -
    552 }
    -
    553
    -
    554 bool SignalsOverlap(const SignalInfo &first, const SignalInfo &second) {
    -
    555 const std::vector<std::uint64_t> firstBits = SignalBits(first);
    -
    556 const std::vector<std::uint64_t> secondBits = SignalBits(second);
    -
    557 for (const std::uint64_t firstBit : firstBits) {
    -
    558 for (const std::uint64_t secondBit : secondBits) {
    -
    559 if (firstBit == secondBit)
    -
    560 return true;
    -
    561 }
    -
    562 }
    -
    563 return false;
    -
    564 }
    -
    565}
    -
    566
    -
    -
    567void DbcParseContext::ValidateMultiplexing(const std::string &filePath) const {
    -
    568 for (std::size_t frameIndex = 0U; frameIndex < database.frames.size(); ++frameIndex) {
    -
    569 const FrameInfo &frame = database.frames[frameIndex];
    -
    570 try {
    -
    571 const dbc_detail::MultiplexGraph graph(frame.signals);
    -
    572 for (std::size_t index = 0U; index < frame.signals.size(); ++index) {
    -
    573 const SignalInfo &signal = frame.signals[index];
    -
    574 if (IsMultiplexSelector(signal.multiplexing.role) && signal.isSigned)
    -
    575 throw dbc_detail::MultiplexError(index, "Multiplex selectors must be unsigned.");
    -
    576 for (std::size_t previous = 0U; previous < index; ++previous) {
    -
    577 if (!graph.AreMutuallyExclusive(index, previous) && SignalsOverlap(signal, frame.signals[previous]))
    -
    578 throw dbc_detail::MultiplexError(index, "Signals overlap without mutually exclusive multiplexing conditions.");
    -
    579 }
    -
    580 }
    -
    581 } catch (const dbc_detail::MultiplexError &error) {
    -
    582 throw DbcDiagnosticError(DbcDiagnosticCode::InvalidMultiplexing, filePath,
    -
    583 signalLines[frameIndex][error.index], error.what());
    -
    584 }
    -
    585 }
    -
    586}
    +
    488
    +
    +
    489void DbcMultiplexingHandler::Handle(const std::string &line, DbcParseContext &context) const {
    +
    490 static const std::regex syntax(R"(^SG_MUL_VAL_[ \t]+([0-9]+)[ \t]+([^ \t;]+)[ \t]+([^ \t;]+)[ \t]+(.+);[ \t]*$)");
    +
    491 static const std::regex rangeSyntax(R"(^[ \t]*([0-9]+)[ \t]*-[ \t]*([0-9]+)[ \t]*$)");
    +
    492 std::smatch fields;
    +
    493 if (!std::regex_match(line, fields, syntax))
    +
    494 throw std::runtime_error("Malformed SG_MUL_VAL_ statement.");
    +
    495 PendingMultiplexing pending;
    +
    496 NormalizeCanId(ParseCanId(fields[1].str()), pending.canId, pending.isExtended);
    +
    497 pending.signalName = fields[2].str();
    +
    498 pending.selectorName = fields[3].str();
    +
    499 pending.lineNumber = context.lineNumber;
    +
    500 const std::string list = fields[4].str();
    +
    501 std::size_t begin = 0U;
    +
    502 for (;;) {
    +
    503 const std::size_t comma = list.find(',', begin);
    +
    504 const std::string item = list.substr(begin, comma == std::string::npos ? comma : comma - begin);
    +
    505 if (!std::regex_match(item, fields, rangeSyntax))
    +
    506 throw std::runtime_error("Malformed multiplex range.");
    +
    507 MultiplexRange range;
    +
    508 const std::string lower = fields[1].str();
    +
    509 const std::string upper = fields[2].str();
    +
    510 const std::from_chars_result low = std::from_chars(lower.data(), lower.data() + lower.size(), range.minimum);
    +
    511 const std::from_chars_result high = std::from_chars(upper.data(), upper.data() + upper.size(), range.maximum);
    +
    512 if (low.ec != std::errc() || high.ec != std::errc() || range.minimum > range.maximum)
    +
    513 throw std::runtime_error("Invalid multiplex range.");
    +
    514 pending.ranges.push_back(range);
    +
    515 if (comma == std::string::npos)
    +
    516 break;
    +
    517 begin = comma + 1U;
    +
    518 }
    +
    519 context.pendingMultiplexing.push_back(std::move(pending));
    +
    520}
    +
    +
    521
    +
    522void DbcParseContext::ResolveMultiplexing(const std::string &filePath) {
    +
    523 for (const PendingMultiplexing &pending: pendingMultiplexing) {
    +
    524 try {
    +
    525 FrameInfo *frame = FindUniqueFrame(database, pending.canId, pending.isExtended, "SG_MUL_VAL_");
    +
    526 SignalInfo *signal = FindUniqueSignal(*frame, pending.signalName, "SG_MUL_VAL_");
    +
    527 SignalInfo *selector = FindUniqueSignal(*frame, pending.selectorName, "SG_MUL_VAL_");
    +
    528 if (!IsMultiplexSelector(selector->multiplexing.role) || selector->isSigned)
    +
    529 throw std::runtime_error("Referenced signal must be an unsigned multiplex selector.");
    +
    530 if (!signal->multiplexing.selector.empty() && signal->multiplexing.selector != pending.selectorName)
    +
    531 throw std::runtime_error("Multiple parent selectors for one signal are unsupported.");
    +
    532 for (const MultiplexRange &range: pending.ranges) {
    +
    533 if (selector->length < 64U && range.maximum >= (std::uint64_t(1U) << selector->length))
    +
    534 throw std::runtime_error("Branch value exceeds selector raw range.");
    +
    535 }
    +
    536 signal->multiplexing.selector = pending.selectorName;
    +
    537 signal->multiplexing.ranges.insert(signal->multiplexing.ranges.end(), pending.ranges.begin(), pending.ranges.end());
    +
    538 if (!IsMultiplexSelector(signal->multiplexing.role))
    +
    539 signal->multiplexing.role = MultiplexRole::Branch;
    +
    540 else
    +
    541 signal->multiplexing.role = MultiplexRole::BranchSelector;
    +
    542 } catch (const std::exception &error) {
    +
    543 throw DbcDiagnosticError(DbcDiagnosticCode::InvalidMultiplexing, filePath, pending.lineNumber, error.what());
    +
    544 }
    +
    545 }
    +
    546}
    +
    547
    +
    548namespace {
    +
    549 std::vector<std::uint64_t> SignalBits(const SignalInfo &signal) {
    +
    550 std::vector<std::uint64_t> bits;
    +
    551 std::uint64_t bit = signal.startBit;
    +
    552 for (std::uint32_t index = 0U; index < signal.length; ++index) {
    +
    553 bits.push_back(bit);
    +
    554 bit = NextSignalBit(bit, signal.isLittleEndian);
    +
    555 }
    +
    556 return bits;
    +
    557 }
    +
    558
    +
    559 bool SignalsOverlap(const SignalInfo &first, const SignalInfo &second) {
    +
    560 const std::vector<std::uint64_t> firstBits = SignalBits(first);
    +
    561 const std::vector<std::uint64_t> secondBits = SignalBits(second);
    +
    562 for (const std::uint64_t firstBit : firstBits) {
    +
    563 for (const std::uint64_t secondBit : secondBits) {
    +
    564 if (firstBit == secondBit)
    +
    565 return true;
    +
    566 }
    +
    567 }
    +
    568 return false;
    +
    569 }
    +
    570}
    +
    571
    +
    +
    572void DbcParseContext::ValidateMultiplexing(const std::string &filePath) const {
    +
    573 for (std::size_t frameIndex = 0U; frameIndex < database.frames.size(); ++frameIndex) {
    +
    574 const FrameInfo &frame = database.frames[frameIndex];
    +
    575 try {
    +
    576 const dbc_detail::MultiplexGraph graph(frame.signals);
    +
    577 for (std::size_t index = 0U; index < frame.signals.size(); ++index) {
    +
    578 const SignalInfo &signal = frame.signals[index];
    +
    579 if (IsMultiplexSelector(signal.multiplexing.role) && signal.isSigned)
    +
    580 throw dbc_detail::MultiplexError(index, "Multiplex selectors must be unsigned.");
    +
    581 for (std::size_t previous = 0U; previous < index; ++previous) {
    +
    582 if (!graph.AreMutuallyExclusive(index, previous) && SignalsOverlap(signal, frame.signals[previous]))
    +
    583 throw dbc_detail::MultiplexError(index, "Signals overlap without mutually exclusive multiplexing conditions.");
    +
    584 }
    +
    585 }
    +
    586 } catch (const dbc_detail::MultiplexError &error) {
    +
    587 throw DbcDiagnosticError(DbcDiagnosticCode::InvalidMultiplexing, filePath,
    +
    588 signalLines[frameIndex][error.index], error.what());
    +
    589 }
    +
    590 }
    +
    591}
    void Handle(const std::string &line, DbcParseContext &context) const override
    Process a trimmed statement using state belonging to this parse.
    void Handle(const std::string &line, DbcParseContext &context) const override
    Process a trimmed statement using state belonging to this parse.
    -
    void Handle(const std::string &line, DbcParseContext &context) const override
    Process a trimmed statement using state belonging to this parse.
    -
    void Handle(const std::string &line, DbcParseContext &context) const override
    Process a trimmed statement using state belonging to this parse.
    +
    void Handle(const std::string &line, DbcParseContext &context) const override
    Process a trimmed statement using state belonging to this parse.
    +
    void Handle(const std::string &line, DbcParseContext &context) const override
    Process a trimmed statement using state belonging to this parse.
    void Handle(const std::string &line, DbcParseContext &context) const override
    Process a trimmed statement using state belonging to this parse.
    DbcStatementDispatcher()
    Register supported statement handlers through TypeFactory.
    -
    bool Dispatch(const std::string &line, DbcParseContext &context) const
    -
    void Handle(const std::string &line, DbcParseContext &context) const override
    Process a trimmed statement using state belonging to this parse.
    +
    bool Dispatch(const std::string &line, DbcParseContext &context) const
    +
    void Handle(const std::string &line, DbcParseContext &context) const override
    Process a trimmed statement using state belonging to this parse.
    +
    void Handle(const std::string &line, DbcParseContext &context) const override
    Process a trimmed statement using state belonging to this parse.
    Parsed DBC content stored in a simple internal form.
    @@ -721,9 +729,9 @@ $(document).ready(function(){initNavTree('dbc__statement__handler_8cpp_source.ht
    std::vector< PendingComment > pendingComments
    -
    void ValidateMultiplexing(const std::string &filePath) const
    Validate selectors, branch ranges, and signal overlap.
    +
    void ValidateMultiplexing(const std::string &filePath) const
    Validate selectors, branch ranges, and signal overlap.
    -
    void ResolveValueDescriptions(const std::string &filePath)
    Resolve deferred raw descriptions against unique targets.
    +
    void ResolveValueDescriptions(const std::string &filePath)
    Resolve deferred raw descriptions against unique targets.
    std::vector< PendingValueDescriptions > pendingValues
    void ResolveComments(const std::string &filePath)
    Resolve deferred comments after all definitions are known.
    diff --git a/docs/html/dbc__statement__handler_8h_source.html b/docs/html/dbc__statement__handler_8h_source.html index f6d1fb1..29c127d 100644 --- a/docs/html/dbc__statement__handler_8h_source.html +++ b/docs/html/dbc__statement__handler_8h_source.html @@ -242,35 +242,42 @@ $(document).ready(function(){initNavTree('dbc__statement__handler_8h_source.html
    135
    - +
    138public:
    -
    139 void Handle(const std::string &line, DbcParseContext &context) const override;
    +
    139 void Handle(const std::string &line, DbcParseContext &context) const override;
    140};
    141
    - +
    144public:
    - +
    145 void Handle(const std::string &line, DbcParseContext &context) const override;
    +
    146};
    +
    147
    -
    149 template<class Handler>
    -
    -
    150 void RegisterType(const std::string &keyword) {
    -
    151 m_factory.registerType<Handler>(keyword);
    -
    152 m_handlers.insert_or_assign(keyword, m_factory.create(keyword));
    -
    153 }
    -
    -
    154
    -
    156 bool Dispatch(const std::string &line, DbcParseContext &context) const;
    -
    157
    -
    158private:
    -
    159 using HandlerMap = std::unordered_map<std::string, std::shared_ptr<DbcStatementHandler>>;
    - -
    161 HandlerMap m_handlers;
    -
    162};
    +
    + +
    150public:
    + +
    153
    +
    155 template<class Handler>
    +
    +
    156 void RegisterType(const std::string &keyword) {
    +
    157 m_factory.registerType<Handler>(keyword);
    +
    158 m_handlers.insert_or_assign(keyword, m_factory.create(keyword));
    +
    159 }
    +
    160
    +
    162 bool Dispatch(const std::string &line, DbcParseContext &context) const;
    163
    -
    164#endif /* DBC_STATEMENT_HANDLER_H */
    +
    164private:
    +
    165 using HandlerMap = std::unordered_map<std::string, std::shared_ptr<DbcStatementHandler>>;
    + +
    167 HandlerMap m_handlers;
    +
    168};
    +
    +
    169
    +
    170#endif /* DBC_STATEMENT_HANDLER_H */
    Defer typed attribute statements for resolution.
    void Handle(const std::string &line, DbcParseContext &context) const override
    Process a trimmed statement using state belonging to this parse.
    Defer frame and signal comments for target resolution.
    @@ -280,34 +287,36 @@ $(document).ready(function(){initNavTree('dbc__statement__handler_8h_source.html
    Parse and append frame definitions.
    void Handle(const std::string &line, DbcParseContext &context) const override
    Process a trimmed statement using state belonging to this parse.
    - -
    void Handle(const std::string &line, DbcParseContext &context) const override
    Process a trimmed statement using state belonging to this parse.
    + +
    void Handle(const std::string &line, DbcParseContext &context) const override
    Process a trimmed statement using state belonging to this parse.
    Parse extended multiplexing conditions for deferred resolution.
    -
    void Handle(const std::string &line, DbcParseContext &context) const override
    Process a trimmed statement using state belonging to this parse.
    +
    void Handle(const std::string &line, DbcParseContext &context) const override
    Process a trimmed statement using state belonging to this parse.
    Parse and append signals to the current frame.
    void Handle(const std::string &line, DbcParseContext &context) const override
    Process a trimmed statement using state belonging to this parse.
    - -
    void RegisterType(const std::string &keyword)
    Register or replace a handler and create its reusable instance.
    + +
    void RegisterType(const std::string &keyword)
    Register or replace a handler and create its reusable instance.
    DbcStatementDispatcher()
    Register supported statement handlers through TypeFactory.
    -
    bool Dispatch(const std::string &line, DbcParseContext &context) const
    +
    bool Dispatch(const std::string &line, DbcParseContext &context) const
    Interface for processing a registered DBC statement.
    virtual void Handle(const std::string &line, DbcParseContext &context) const =0
    Process a trimmed statement using state belonging to this parse.
    + +
    void Handle(const std::string &line, DbcParseContext &context) const override
    Process a trimmed statement using state belonging to this parse.
    Defer raw value descriptions for target resolution.
    -
    void Handle(const std::string &line, DbcParseContext &context) const override
    Process a trimmed statement using state belonging to this parse.
    +
    void Handle(const std::string &line, DbcParseContext &context) const override
    Process a trimmed statement using state belonging to this parse.
    Registry-based factory for creating objects by runtime identifier.
    Definition typefactory.h:59
    BasePtr create(const ClassId &id, Args... args) const
    Creates an instance for the given id using provided arguments.
    Definition typefactory.h:99
    void registerType(const ClassId &id)
    Registers a Derived type under the given id.
    Definition typefactory.h:78
    Parsed DBC database and attribute metadata.
    Parsed DBC content stored in a simple internal form.
    -
    Diagnostic message and physical source location.
    +
    Diagnostic message and physical source location.
    void ResolveAttributes(const std::string &filePath, std::vector< DbcDiagnostic > &diagnostics)
    Resolve definitions, defaults, then assignments; append format warnings.
    std::vector< PendingComment > pendingComments
    -
    void ValidateMultiplexing(const std::string &filePath) const
    Validate selectors, branch ranges, and signal overlap.
    +
    void ValidateMultiplexing(const std::string &filePath) const
    Validate selectors, branch ranges, and signal overlap.
    -
    void ResolveValueDescriptions(const std::string &filePath)
    Resolve deferred raw descriptions against unique targets.
    +
    void ResolveValueDescriptions(const std::string &filePath)
    Resolve deferred raw descriptions against unique targets.
    std::vector< PendingValueDescriptions > pendingValues
    void ResolveComments(const std::string &filePath)
    Resolve deferred comments after all definitions are known.
    diff --git a/docs/html/dbc__tree__model__test_8cpp_source.html b/docs/html/dbc__tree__model__test_8cpp_source.html index 458bcfc..d92e8e5 100644 --- a/docs/html/dbc__tree__model__test_8cpp_source.html +++ b/docs/html/dbc__tree__model__test_8cpp_source.html @@ -313,7 +313,7 @@ $(document).ready(function(){initNavTree('dbc__tree__model__test_8cpp_source.htm
    213 return QTest::qExec(&tests, argc, argv);
    214}
    215#include "dbc_tree_model_test.moc"
    -
    Minimal DBC parser.
    Definition dbc_parser.h:43
    +
    Minimal DBC parser.
    Definition dbc_parser.h:45
    Builds a simple tree from parsed DBC database.
    std::unique_ptr< TreeNode > Build(const DbcDatabase &database) const
    Build tree representation of parsed DBC data.
    diff --git a/docs/html/examples_2main_8cpp.html b/docs/html/examples_2main_8cpp.html index b212ae5..afb11a4 100644 --- a/docs/html/examples_2main_8cpp.html +++ b/docs/html/examples_2main_8cpp.html @@ -152,7 +152,7 @@ Functions

    Definition at line 101 of file examples/main.cpp.

    -

    References DbcTreeBuilder::Build(), DbcDecodeBuilder::Build(), RawCanFrame::canId, RawCanFrame::data, DbcDecoder::Decode(), DecodeDatabase::GetFrames(), RawCanFrame::isExtended, main(), and DbcParser::ParseFile().

    +

    References DbcTreeBuilder::Build(), DbcDecodeBuilder::Build(), RawCanFrame::canId, RawCanFrame::data, DbcParseResult::database, DbcDecoder::Decode(), DbcParseResult::diagnostics, DecodeDatabase::GetFrames(), DbcParseResult::HasErrors(), RawCanFrame::isExtended, main(), and DbcParser::ParseFileWithDiagnostics().

    Referenced by main().

    diff --git a/docs/html/examples_2main_8cpp_source.html b/docs/html/examples_2main_8cpp_source.html index ae64dbc..472f6be 100644 --- a/docs/html/examples_2main_8cpp_source.html +++ b/docs/html/examples_2main_8cpp_source.html @@ -195,46 +195,54 @@ $(document).ready(function(){initNavTree('examples_2main_8cpp_source.html','');
    105 }
    106
    107 try {
    -
    108 DbcParser parser;
    -
    109 DbcDatabase database = parser.ParseFile(argv[1]);
    -
    110
    -
    111 DbcTreeBuilder treeBuilder;
    -
    112 std::unique_ptr<TreeNode> root = treeBuilder.Build(database);
    -
    113
    -
    114 std::cout << "=== Parsed tree ===\n";
    -
    115 PrintTree(root.get(), 0);
    -
    116
    -
    117 DbcDecodeBuilder decodeBuilder;
    -
    118 DecodeDatabase decodeDatabase = decodeBuilder.Build(database);
    -
    119
    -
    120 /*
    -
    121 * Example raw frame.
    -
    122 * Replace with live CAN frame or trace record later.
    -
    123 */
    -
    124 RawCanFrame rawFrame;
    -
    125 rawFrame.canId = decodeDatabase.GetFrames().empty() ? 0U : decodeDatabase.GetFrames()[0].canId;
    -
    126 rawFrame.isExtended = decodeDatabase.GetFrames().empty() ? false : decodeDatabase.GetFrames()[0].isExtended;
    -
    127 rawFrame.data.resize(8U, 0U);
    -
    128
    -
    129 DbcDecoder decoder;
    -
    130 DecodedFrameValue decoded = decoder.Decode(decodeDatabase, rawFrame);
    -
    131
    -
    132 std::cout << "\n=== Decoded frame ===\n";
    -
    133 PrintDecodedFrame(decoded);
    -
    134 } catch (const std::exception &ex) {
    -
    135 std::cerr << "Error: " << ex.what() << "\n";
    -
    136 return 2;
    -
    137 }
    -
    138
    -
    139 return 0;
    -
    140}
    +
    108 const DbcParser parser;
    +
    109 const DbcParseResult parsed = parser.ParseFileWithDiagnostics(argv[1]);
    +
    110 for (const DbcDiagnostic &diagnostic: parsed.diagnostics) {
    +
    111 std::cerr << diagnostic.filePath << ":" << diagnostic.lineNumber << ": "
    +
    112 << (diagnostic.severity == DbcDiagnosticSeverity::Error ? "error: " : "warning: ")
    +
    113 << diagnostic.message << "\n";
    +
    114 }
    +
    115 if (parsed.HasErrors())
    +
    116 return 2;
    +
    117 const DbcDatabase &database = parsed.database;
    +
    118
    +
    119 DbcTreeBuilder treeBuilder;
    +
    120 std::unique_ptr<TreeNode> root = treeBuilder.Build(database);
    +
    121
    +
    122 std::cout << "=== Parsed tree ===\n";
    +
    123 PrintTree(root.get(), 0);
    +
    124
    +
    125 DbcDecodeBuilder decodeBuilder;
    +
    126 DecodeDatabase decodeDatabase = decodeBuilder.Build(database);
    +
    127
    +
    128 /*
    +
    129 * Example raw frame.
    +
    130 * Replace with live CAN frame or trace record later.
    +
    131 */
    +
    132 RawCanFrame rawFrame;
    +
    133 rawFrame.canId = decodeDatabase.GetFrames().empty() ? 0U : decodeDatabase.GetFrames()[0].canId;
    +
    134 rawFrame.isExtended = decodeDatabase.GetFrames().empty() ? false : decodeDatabase.GetFrames()[0].isExtended;
    +
    135 rawFrame.data.resize(8U, 0U);
    +
    136
    +
    137 DbcDecoder decoder;
    +
    138 DecodedFrameValue decoded = decoder.Decode(decodeDatabase, rawFrame);
    +
    139
    +
    140 std::cout << "\n=== Decoded frame ===\n";
    +
    141 PrintDecodedFrame(decoded);
    +
    142 } catch (const std::exception &ex) {
    +
    143 std::cerr << "Error: " << ex.what() << "\n";
    +
    144 return 2;
    +
    145 }
    +
    146
    +
    147 return 0;
    +
    148}
    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:87
    DecodedFrameValue Decode(const DecodeDatabase &database, const RawCanFrame &frame) const
    Decode one raw CAN frame.
    -
    Minimal DBC parser.
    Definition dbc_parser.h:43
    -
    DbcDatabase ParseFile(const std::string &filePath) const
    Parse DBC file.
    +
    Minimal DBC parser.
    Definition dbc_parser.h:45
    +
    DbcParseResult ParseFileWithDiagnostics(const std::string &filePath) const
    Parse with warnings and a structured first error instead of throwing.
    Builds a simple tree from parsed DBC database.
    std::unique_ptr< TreeNode > Build(const DbcDatabase &database) const
    Build tree representation of parsed DBC data.
    Immutable runtime snapshot. Copies share storage; reload replaces a snapshot. Owning lookup handles a...
    @@ -252,6 +260,11 @@ $(document).ready(function(){initNavTree('examples_2main_8cpp_source.html','');
    Metadata tree builder.
    int main(int argc, char *argv[])
    Parse a DBC file and demonstrate metadata lookup and CAN decoding.
    Parsed DBC content stored in a simple internal form.
    +
    Diagnostic message and physical source location.
    +
    Parsed database and diagnostics; the database is empty on error.
    +
    std::vector< DbcDiagnostic > diagnostics
    +
    bool HasErrors() const
    Return whether any diagnostic has error severity.
    +
    DbcDatabase database
    Fully decoded frame.
    Definition dbc_decoder.h:74
    std::shared_ptr< const DecodeFrame > definition
    Definition dbc_decoder.h:75
    diff --git a/docs/html/functions.html b/docs/html/functions.html index 83f5e8f..4d6222e 100644 --- a/docs/html/functions.html +++ b/docs/html/functions.html @@ -181,7 +181,7 @@ $(document).ready(function(){initNavTree('functions.html',''); initResizable();

    - h -

    diff --git a/docs/html/hierarchy.js b/docs/html/hierarchy.js index 9575fbe..5ddc811 100644 --- a/docs/html/hierarchy.js +++ b/docs/html/hierarchy.js @@ -18,6 +18,7 @@ var hierarchy = [ "DbcHeaderHandler", "classDbcHeaderHandler.html", null ], [ "DbcMultiplexingHandler", "classDbcMultiplexingHandler.html", null ], [ "DbcSignalHandler", "classDbcSignalHandler.html", null ], + [ "DbcUnsupportedSignalTypeHandler", "classDbcUnsupportedSignalTypeHandler.html", null ], [ "DbcValueDescriptionHandler", "classDbcValueDescriptionHandler.html", null ] ] ], [ "DbcTreeBuilder", "classDbcTreeBuilder.html", null ], diff --git a/docs/html/index.qhp b/docs/html/index.qhp index 953c55f..f2c5a58 100644 --- a/docs/html/index.qhp +++ b/docs/html/index.qhp @@ -168,8 +168,9 @@
    -
    -
    +
    +
    +
    @@ -314,6 +315,9 @@
    +
    +
    +
    @@ -505,6 +509,7 @@
    +
    @@ -810,6 +815,7 @@ + @@ -949,6 +955,7 @@ + @@ -1013,9 +1020,9 @@ - + @@ -1130,6 +1137,9 @@ classDbcTreeModel-members.html classDbcTreeModel.html classDbcTreeModel__inherit__graph.svg +classDbcUnsupportedSignalTypeHandler-members.html +classDbcUnsupportedSignalTypeHandler.html +classDbcUnsupportedSignalTypeHandler__inherit__graph.svg classDbcValueDescriptionHandler-members.html classDbcValueDescriptionHandler.html classDbcValueDescriptionHandler__inherit__graph.svg diff --git a/docs/html/inherit_graph_12.map b/docs/html/inherit_graph_12.map index 75cdcd6..6052e0c 100644 --- a/docs/html/inherit_graph_12.map +++ b/docs/html/inherit_graph_12.map @@ -1,17 +1,19 @@ - + - + - + - + - + - + - - - + + + + + diff --git a/docs/html/inherit_graph_12.md5 b/docs/html/inherit_graph_12.md5 index f2b92f4..e66eb95 100644 --- a/docs/html/inherit_graph_12.md5 +++ b/docs/html/inherit_graph_12.md5 @@ -1 +1 @@ -81f795b8af5d3417bb7193b7647f084f \ No newline at end of file +0c44e3ab7229f7187d00bf195c52950d \ No newline at end of file diff --git a/docs/html/inherit_graph_12.svg b/docs/html/inherit_graph_12.svg index cfb5678..5fb1347 100644 --- a/docs/html/inherit_graph_12.svg +++ b/docs/html/inherit_graph_12.svg @@ -4,16 +4,16 @@ - - + + Graphical Class Hierarchy Node0 - -DbcStatementHandler + +DbcStatementHandler @@ -21,8 +21,8 @@ Node1 - -DbcAttributeHandler + +DbcAttributeHandler @@ -30,8 +30,8 @@ Node0->Node1 - - + + @@ -39,8 +39,8 @@ Node2 - -DbcCommentHandler + +DbcCommentHandler @@ -48,8 +48,8 @@ Node0->Node2 - - + + @@ -57,8 +57,8 @@ Node3 - -DbcFrameHandler + +DbcFrameHandler @@ -66,8 +66,8 @@ Node0->Node3 - - + + @@ -75,8 +75,8 @@ Node4 - -DbcHeaderHandler + +DbcHeaderHandler @@ -84,8 +84,8 @@ Node0->Node4 - - + + @@ -93,8 +93,8 @@ Node5 - -DbcMultiplexingHandler + +DbcMultiplexingHandler @@ -102,8 +102,8 @@ Node0->Node5 - - + + @@ -111,8 +111,8 @@ Node6 - -DbcSignalHandler + +DbcSignalHandler @@ -120,17 +120,18 @@ Node0->Node6 - - + + Node7 - - -DbcValueDescriptionHandler + + +DbcUnsupportedSignalType +Handler @@ -138,8 +139,26 @@ Node0->Node7 - - + + + + + + + +Node8 + + +DbcValueDescriptionHandler + + + + + +Node0->Node8 + + + diff --git a/docs/html/inherit_graph_29.map b/docs/html/inherit_graph_29.map index b666546..8d8728a 100644 --- a/docs/html/inherit_graph_29.map +++ b/docs/html/inherit_graph_29.map @@ -1,5 +1,5 @@ - + diff --git a/docs/html/inherit_graph_29.md5 b/docs/html/inherit_graph_29.md5 index 95afcce..9bb5b65 100644 --- a/docs/html/inherit_graph_29.md5 +++ b/docs/html/inherit_graph_29.md5 @@ -1 +1 @@ -135d8c5ec586c0414ce0a7492f17be7a \ No newline at end of file +681c8df12e299700deb6c7b081831f8f \ No newline at end of file diff --git a/docs/html/inherit_graph_29.svg b/docs/html/inherit_graph_29.svg index 8aaf7b6..6620242 100644 --- a/docs/html/inherit_graph_29.svg +++ b/docs/html/inherit_graph_29.svg @@ -27,9 +27,9 @@ - + Node0->Node1 - + diff --git a/docs/html/inherit_graph_34.map b/docs/html/inherit_graph_34.map index b4aa682..a581cb5 100644 --- a/docs/html/inherit_graph_34.map +++ b/docs/html/inherit_graph_34.map @@ -1,13 +1,13 @@ - + - + - + - + - + diff --git a/docs/html/inherit_graph_34.md5 b/docs/html/inherit_graph_34.md5 index e3ca542..ba261f3 100644 --- a/docs/html/inherit_graph_34.md5 +++ b/docs/html/inherit_graph_34.md5 @@ -1 +1 @@ -20f0e2a99ef43e59e8d2d49d66e78b81 \ No newline at end of file +bdf9947a09767a7f8594fc5f061f603e \ No newline at end of file diff --git a/docs/html/inherit_graph_34.svg b/docs/html/inherit_graph_34.svg index 15a4891..9e3dbb2 100644 --- a/docs/html/inherit_graph_34.svg +++ b/docs/html/inherit_graph_34.svg @@ -27,9 +27,9 @@ - + Node0->Node1 - + @@ -45,9 +45,9 @@ - + Node0->Node4 - + @@ -63,9 +63,9 @@ - + Node1->Node2 - + @@ -81,9 +81,9 @@ - + Node2->Node3 - + @@ -99,9 +99,9 @@ - + Node4->Node5 - + diff --git a/docs/html/inherits.html b/docs/html/inherits.html index 0e63707..c0be115 100644 --- a/docs/html/inherits.html +++ b/docs/html/inherits.html @@ -108,7 +108,7 @@ $(document).ready(function(){initNavTree('hierarchy.html',''); initResizable();

    This browser is not able to show SVG: try Firefox, Chrome, Safari, or Opera instead.

    - + diff --git a/docs/html/md_README.html b/docs/html/md_README.html index b8d8994..994ef31 100644 --- a/docs/html/md_README.html +++ b/docs/html/md_README.html @@ -537,12 +537,12 @@ Structured diagnostics
    const DecodeDatabase runtime = DbcDecodeBuilder().Build(result.database);
    // Inspect warnings before relying on decoding results.
    }
    -
    Minimal DBC parser.
    Definition dbc_parser.h:43
    -
    DbcParseResult ParseFileWithDiagnostics(const std::string &filePath) const
    Parse with warnings and a structured first error instead of throwing.
    -
    Diagnostic message and physical source location.
    -
    Parsed database and diagnostics; the database is empty on error.
    -
    bool HasErrors() const
    Return whether any diagnostic has error severity.
    -
    DbcDatabase database
    +
    Minimal DBC parser.
    Definition dbc_parser.h:45
    +
    DbcParseResult ParseFileWithDiagnostics(const std::string &filePath) const
    Parse with warnings and a structured first error instead of throwing.
    +
    Diagnostic message and physical source location.
    +
    Parsed database and diagnostics; the database is empty on error.
    +
    bool HasErrors() const
    Return whether any diagnostic has error severity.
    +
    DbcDatabase database
    @@ -559,10 +559,13 @@ Structured diagnostics + +
    Severity / code Meaning
    Warning / UnsupportedStatement Unregistered statement skipped; decoding or metadata may be incomplete
    Error / UnsupportedSignalType SIG_VALTYPE_ declaration rejected to prevent incorrect integer decoding
    Error / InvalidMultiplexing Invalid SG_MUL_VAL_ syntax, references, ranges, or cycles

    Parsing stops at the first error and returns an empty database, together with any preceding warnings and the error. Deferred comment, VAL_, and multiplexing errors retain their original source lines. Invalid signal markers use InvalidStatement. HasErrors() checks severity; warnings alone do not make a parse fail.

    -

    Unknown statements are reported conservatively, including SIG_VALTYPE_, attribute relation forms (BA_REL_, BA_DEF_REL_), signal types/references, environment variables, named VAL_TABLE_ tables, and vendor extensions. For example, ignoring SIG_VALTYPE_ may decode a floating-point signal as an integer. The parser does not implement the warned features; consumers should review warnings.

    +

    All SIG_VALTYPE_ declarations are rejected with Error / UnsupportedSignalType. This includes float32 (type 1), float64 (type 2), type 0, unknown types, and malformed declarations: the operator itself is not implemented. ParseFile() throws a source-located error; ParseFileWithDiagnostics() returns an empty database with the error and any preceding warnings. There is no permissive mode. This prevents IEEE 754 payload bits from being interpreted as a valid integer signal. Integer signals defined with SG_ without this operator are unchanged. A bare SIG_VALTYPE_ symbol inside an NS_ symbol list remains quiet header metadata, rather than a value-type declaration.

    +

    Other unknown statements are reported conservatively, including attribute relation forms (BA_REL_, BA_DEF_REL_), signal types/references, environment variables, named VAL_TABLE_ tables, and vendor extensions. The parser does not implement the warned features; consumers should review warnings.

    Attributes and frame metadata

    BA_DEF_, BA_DEF_DEF_, and BA_ support database, BO_, SG_, BU_, and EV_ scopes with INT, HEX, FLOAT, STRING, and ENUM definitions. Integer/HEX values use decimal signed 64-bit numbers; floating values are finite and locale independent. ENUM assignments use zero-based indices; defaults accept an index or a quoted label. Strings support escaped quotes and backslashes. Statements end with ; outside quoted text and may span physical lines, including ENUM lists, defaults, and assignments. Newlines inside quoted strings are preserved; semicolons inside them do not terminate the statement. Attribute errors and format warnings refer to the statement's first physical line.

    @@ -700,7 +703,7 @@ J1939 Opt-in and PGN Lookup
    options.enableJ1939 = true;
    const DbcDatabase parsed = DbcParser(options).ParseFile("vehicle.dbc");
    const DecodeDatabase runtime = DbcDecodeBuilder().Build(parsed);
    -
    DbcDatabase ParseFile(const std::string &filePath) const
    Parse DBC file.
    +
    DbcDatabase ParseFile(const std::string &filePath) const
    Parse DBC file.
    Parsed DBC content stored in a simple internal form.
    @@ -864,7 +867,7 @@ Statement Handler Factory

    Register it in the dispatcher constructor:

    RegisterType<MyCustomHandler>("CUSTOM_"); // after implementing MyCustomHandler
    -

    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.

    +

    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. A registered rejection handler for SIG_VALTYPE_ produces UnsupportedSignalType errors instead of skipping the declaration. 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.


    @@ -893,7 +896,7 @@ Current Limitations

  • multiple parent selectors for one signal
  • named VAL_TABLE_ tables and environment-variable value descriptions
  • attribute relation forms (BA_REL_, BA_DEF_REL_)
  • -
  • SIG_VALTYPE_ floating-point signal types
  • +
  • explicit SIG_VALTYPE_ signal value types (declarations are rejected with an error)
  • signal types/references (SGTYPE_, SIG_TYPE_REF_) and environment variables
  • advanced comment handling
  • full DBC grammar coverage
  • @@ -1065,6 +1068,7 @@ Build and run the demo
    make -j
    ./dbc_demo /absolute/path/to/example.dbc

    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.

    +

    The demo prints all parser diagnostics, including severity and source location, and exits with code 2 before building or decoding when parsing reports an error. Warnings remain visible when parsing succeeds.

    Generate API documentation

    With Doxygen installed, run from the repository root:

    diff --git a/docs/html/md_changelog.html b/docs/html/md_changelog.html index 46a887f..d449a6f 100644 --- a/docs/html/md_changelog.html +++ b/docs/html/md_changelog.html @@ -115,6 +115,8 @@ Testing

    Fixed

      +
    • Reject all SIG_VALTYPE_ declarations with a source-located UnsupportedSignalType error instead of silently decoding their payload as integers. Both parser APIs reject the unsupported operator, including type 0; the diagnostic API publishes no partial database. Namespace symbol listings remain quiet. No permissive mode is introduced.
    • +
    • Display parser warnings and errors in the demo, and stop before building or decoding when parsing fails. Add parser and pipeline regressions for float32, float64, source locations, declaration order, and unchanged integer decoding. Verify Release and ASan/UBSan builds with the optional Qt adapter: 2,420 core checks, seven model checks, and the qmake tree test pass in each build. One named-locale case skips; leak checking remains disabled due to ptrace. Build the demo and verify error exits, visible warnings, and integer input.
    • Replace recursive regular-expression matching for SG_ statements with an iterative field scanner. Long unit strings no longer overflow the stack.
    • Parse signal physical values directly with locale-independent from_chars, retaining complete-token, finite-value, overflow, and underflow validation without recursive numeric regex matching.
    • Preserve long valid fields without a new length limit, and reject malformed statements through the existing source-located diagnostic and throwing APIs.
    • diff --git a/docs/html/md_tests_2README.html b/docs/html/md_tests_2README.html index 251b7d3..8ed1d8b 100644 --- a/docs/html/md_tests_2README.html +++ b/docs/html/md_tests_2README.html @@ -123,7 +123,7 @@ 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; 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 rejected at construction, and preservation of successful signals on partial payloads. Physical conversion checks cover NaN/Inf parameters rejected at construction, 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 13 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.

      +

      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 11 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. Comment regressions cover tabs, escaped quotes/backslashes, multiline text with blank lines and indentation, CRLF, missing final newlines, comments preceding frame/signal definitions, last-wins and empty overwrites, normalized ID boundaries, standard/extended separation, malformed syntax/IDs, and missing targets. Errors verify both structured diagnostics and the throwing API at the starting line. 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, invalid multiplexing, and Intel/Motorola declared-payload boundaries. Physical-number tests apply a custom comma-decimal C++ locale and an available named comma-decimal C numeric locale. They check all four physical fields, leading signs, fractions, scientific notation, finite boundaries, zero, malformed numbers, comma/grouped numbers, overflow/underflow, and reversed ranges. Locale changes are restored after each test. The named C locale case skips if none of the supported locale names are installed; the custom C++ locale case always runs. On Linux with locale sources and localedef, run both cases without installing a system locale:

      mkdir -p /tmp/dbc-test-locales
      localedef --no-archive -i de_DE -f UTF-8 /tmp/dbc-test-locales/de_DE.UTF-8
      @@ -160,10 +160,15 @@ Attribute coverage

      Parser slots attributes, invalidAttributes, and attributeWarnings cover all five scopes and types, deferred definitions/defaults/targets, explicit values overriding defaults, repeated assignments, escaped strings, typed range/scope errors, duplicate definitions/defaults, unknown attributes and missing targets. They check source diagnostics and the throwing API, distinct standard/extended targets, frame cycle times and enum labels, unsupported formats and ID conflicts, unchanged J1939 opt-in, and runtime/tree propagation. Existing unsupported-statement checks now use relation forms, which remain unsupported, instead of supported attribute forms.

      Attribute changes were verified with qmake6 ../dbc.pro, make -j4, and make check in build/: all six QtTest suites and the qmake tree target passed. The standalone C++17 tree test also passed. The existing named comma-decimal C locale test skipped because the locale is not installed.

      +Unsupported signal value types

      +

      unsupportedSignalValueTypes verifies that both parser APIs reject SIG_VALTYPE_ declarations with UnsupportedSignalType, the original file path and physical line, and an empty database. Cases include float32/float64, declarations before signal definitions, extended IDs, tabs/CRLF, no final newline, type 0, unknown types, and malformed declarations. Prior warnings are retained; SIG_VALTYPE_ symbols inside NS_ lists remain quiet.

      +

      The pipeline suite checks the IEEE 754 bytes for float32 and float64 1.0. Adding a type declaration must prevent publication of an integer definition, so building from the error result cannot produce a valid decoded signal. The same payloads still decode to exact integers when defined only by supported SG_ statements. New cases are included in the existing qmake targets.

      +

      Verified with Qt 6.8.2 in separate Release and ASan/UBSan builds with CONFIG+=dbc_qt: each passed 2,420 core QtTest checks, seven model checks, and the qmake tree test. One named-locale case skipped because its locale is unavailable. ASan/UBSan used fatal errors and detect_leaks=0 because of the previously documented ptrace restriction. The separately built demo was checked with float32 and float64 declarations (exit 2, source-located error, no decode output), a supported integer definition (exit 0), and an unknown extension (exit 0 with a visible warning).

      +

      Long signal definitions

      longSignalDefinitions reproduces the former stack overflow with a 50,000-character unit string. Both parser APIs are exercised with long units, signal names, receivers, decimal numbers, and a list of 10,001 receivers. Valid input must parse without truncating units; malformed input (including missing quotes, delimiters, receiver separators, numeric suffixes, overflow, and trailing receiver commas) must return an empty database and an InvalidStatement diagnostic at the signal's source line, or throw a source-located error through ParseFile(). These cases run as part of the existing parser target and sanitizer builds.

      Verified with Qt 6.8.2 in separate Release and ASan/UBSan builds, both with CONFIG+=dbc_qt: make check passed 2,409 core QtTest checks, seven model checks, and the qmake tree test in each build. The named comma-decimal C locale test skipped because the required locale is not installed. The sanitizer run used ASAN_OPTIONS=detect_leaks=0:halt_on_error=1 and UBSAN_OPTIONS=halt_on_error=1; address and undefined-behavior checks passed, while leak checking was disabled because of the environment's previously documented ptrace restriction.

      -

      +

      Extended validation

      attributeNumbers checks signed 64-bit limits, leading plus signs, decimal HEX values, FLOAT fractions/exponents, and default overrides under a comma-decimal C++ locale. Node/environment defaults remain on definitions without becoming database attributes. invalidAttributeNumbers supplies 39 data rows, each checked in both definition bounds, a default, and an explicit assignment. The throwing and diagnostic APIs must reject malformed signs, trailing text, hexadecimal notation, non-finite values, overflow, and underflow with the correct error line. Additional malformed-attribute cases cover token separators, scope/type names, reversed FLOAT bounds, value types/ranges, and invalid CAN references.

      Run AddressSanitizer and UndefinedBehaviorSanitizer in a separate directory:

      diff --git a/docs/html/navtreedata.js b/docs/html/navtreedata.js index 8726d34..a9eb8eb 100644 --- a/docs/html/navtreedata.js +++ b/docs/html/navtreedata.js @@ -181,8 +181,9 @@ var NAVTREE = [ "Database validation coverage", "md_tests_2README.html#autotoc_md167", null ], [ "End-to-end pipeline coverage", "md_tests_2README.html#autotoc_md168", null ], [ "Attribute coverage", "md_tests_2README.html#autotoc_md169", null ], - [ "Long signal definitions", "md_tests_2README.html#autotoc_md170", null ], - [ "Extended validation", "md_tests_2README.html#autotoc_md171", null ] + [ "Unsupported signal value types", "md_tests_2README.html#autotoc_md170", null ], + [ "Long signal definitions", "md_tests_2README.html#autotoc_md171", null ], + [ "Extended validation", "md_tests_2README.html#autotoc_md172", null ] ] ], [ "Classes", "annotated.html", [ [ "Class List", "annotated.html", "annotated_dup" ], @@ -210,7 +211,7 @@ var NAVTREE = var NAVTREEINDEX = [ "annotated.html", -"md_README.html#autotoc_md89" +"md_README.html#autotoc_md85" ]; var SYNCONMSG = 'click to disable panel synchronisation'; diff --git a/docs/html/navtreeindex0.js b/docs/html/navtreeindex0.js index aca3818..fe46832 100644 --- a/docs/html/navtreeindex0.js +++ b/docs/html/navtreeindex0.js @@ -68,38 +68,40 @@ var NAVTREEINDEX0 = "classDbcTreeModel.html#aad5834e8edb00808cbfa498ea8c63fe9":[4,0,21,9], "classDbcTreeModel.html#aca81d05c81cc48c1185fc36659e96e5b":[4,0,21,8], "classDbcTreeModel.html#ae40bb8b8a26d205f178c6dd1134ff4b3":[4,0,21,5], -"classDbcValueDescriptionHandler.html":[4,0,22], -"classDbcValueDescriptionHandler.html#a653e13364f396b54244edc2c0fdd831a":[4,0,22,0], -"classDecodeDatabase.html":[4,0,23], -"classDecodeDatabase.html#a0af5b15b9953a4f6551c27ee9ae8b514":[4,0,23,4], -"classDecodeDatabase.html#a51a44cf7c3f23c98ec8781afe0097c0c":[4,0,23,0], -"classDecodeDatabase.html#a780d6e747a48b10a5eb0ab85448d2970":[4,0,23,2], -"classDecodeDatabase.html#aa034c1510984a85f60ad6c0bd744d6e8":[4,0,23,1], -"classDecodeDatabase.html#aa96b62b4f3fff68ab651674760f60d6d":[4,0,23,3], -"classTreeNode.html":[4,0,42], -"classTreeNode.html#a1eb94f7b6e7962c9d867a120772dfe6e":[4,0,42,7], -"classTreeNode.html#a226518612d0d00b5988a2c44fc21d3f1":[4,0,42,14], -"classTreeNode.html#a4727ff801a2d1b9323c5ae50a35457a6":[4,0,42,9], -"classTreeNode.html#a81047bd7d2a4cc96b9ef5e20d0d63b5f":[4,0,42,10], -"classTreeNode.html#a8e38dd591d8801d7ee3a7dbad90c67fc":[4,0,42,1], -"classTreeNode.html#a984a98d5ccf7ef1f5a18094c6821f35d":[4,0,42,0], -"classTreeNode.html#aa48fbc17903eb8cfa40353e29f853c6f":[4,0,42,4], -"classTreeNode.html#aaac5b8d9dc9e5ae1f367d1adbcf20e18":[4,0,42,12], -"classTreeNode.html#ab9f00f5253d56461bb2d420fd639c5ae":[4,0,42,2], -"classTreeNode.html#abdd82460187dca8d3e5d66f5d5c529ea":[4,0,42,8], -"classTreeNode.html#ad501f97eeec7f7d3353bfae838fbb6a8":[4,0,42,5], -"classTreeNode.html#af4e6e916c48c024a3dfeadac8174b527":[4,0,42,6], -"classTreeNode.html#af87331684d4b40240aec75e3286921d3":[4,0,42,3], -"classTreeNode.html#afa2f4b0aafa12d3d41ed00df8e0250c9":[4,0,42,11], -"classTreeNode.html#afc503b40004764163605d6dbdb4ef811":[4,0,42,13], -"classTypeFactory.html":[4,0,43], -"classTypeFactory.html#a1a119ad2956abe146213c6ca124d8dd4":[4,0,43,5], -"classTypeFactory.html#a33b404da3e19ea7e1245b8a4975721bf":[4,0,43,0], -"classTypeFactory.html#a3f43b03d6e3a0fd503fdec01ecf62e9f":[4,0,43,4], -"classTypeFactory.html#a5f119fe61c1cb9f4725ce746e4a047b3":[4,0,43,6], -"classTypeFactory.html#a650022aac1e2f30af5bc554a5663939f":[4,0,43,1], -"classTypeFactory.html#ad6d85795285ee21193ac03e1b24762d3":[4,0,43,2], -"classTypeFactory.html#ad82f537569ece3d3a4a376523fa94473":[4,0,43,3], +"classDbcUnsupportedSignalTypeHandler.html":[4,0,22], +"classDbcUnsupportedSignalTypeHandler.html#ae4132fcdbb134d51143133c1fb83a9df":[4,0,22,0], +"classDbcValueDescriptionHandler.html":[4,0,23], +"classDbcValueDescriptionHandler.html#a653e13364f396b54244edc2c0fdd831a":[4,0,23,0], +"classDecodeDatabase.html":[4,0,24], +"classDecodeDatabase.html#a0af5b15b9953a4f6551c27ee9ae8b514":[4,0,24,4], +"classDecodeDatabase.html#a51a44cf7c3f23c98ec8781afe0097c0c":[4,0,24,0], +"classDecodeDatabase.html#a780d6e747a48b10a5eb0ab85448d2970":[4,0,24,2], +"classDecodeDatabase.html#aa034c1510984a85f60ad6c0bd744d6e8":[4,0,24,1], +"classDecodeDatabase.html#aa96b62b4f3fff68ab651674760f60d6d":[4,0,24,3], +"classTreeNode.html":[4,0,43], +"classTreeNode.html#a1eb94f7b6e7962c9d867a120772dfe6e":[4,0,43,7], +"classTreeNode.html#a226518612d0d00b5988a2c44fc21d3f1":[4,0,43,14], +"classTreeNode.html#a4727ff801a2d1b9323c5ae50a35457a6":[4,0,43,9], +"classTreeNode.html#a81047bd7d2a4cc96b9ef5e20d0d63b5f":[4,0,43,10], +"classTreeNode.html#a8e38dd591d8801d7ee3a7dbad90c67fc":[4,0,43,1], +"classTreeNode.html#a984a98d5ccf7ef1f5a18094c6821f35d":[4,0,43,0], +"classTreeNode.html#aa48fbc17903eb8cfa40353e29f853c6f":[4,0,43,4], +"classTreeNode.html#aaac5b8d9dc9e5ae1f367d1adbcf20e18":[4,0,43,12], +"classTreeNode.html#ab9f00f5253d56461bb2d420fd639c5ae":[4,0,43,2], +"classTreeNode.html#abdd82460187dca8d3e5d66f5d5c529ea":[4,0,43,8], +"classTreeNode.html#ad501f97eeec7f7d3353bfae838fbb6a8":[4,0,43,5], +"classTreeNode.html#af4e6e916c48c024a3dfeadac8174b527":[4,0,43,6], +"classTreeNode.html#af87331684d4b40240aec75e3286921d3":[4,0,43,3], +"classTreeNode.html#afa2f4b0aafa12d3d41ed00df8e0250c9":[4,0,43,11], +"classTreeNode.html#afc503b40004764163605d6dbdb4ef811":[4,0,43,13], +"classTypeFactory.html":[4,0,44], +"classTypeFactory.html#a1a119ad2956abe146213c6ca124d8dd4":[4,0,44,5], +"classTypeFactory.html#a33b404da3e19ea7e1245b8a4975721bf":[4,0,44,0], +"classTypeFactory.html#a3f43b03d6e3a0fd503fdec01ecf62e9f":[4,0,44,4], +"classTypeFactory.html#a5f119fe61c1cb9f4725ce746e4a047b3":[4,0,44,6], +"classTypeFactory.html#a650022aac1e2f30af5bc554a5663939f":[4,0,44,1], +"classTypeFactory.html#ad6d85795285ee21193ac03e1b24762d3":[4,0,44,2], +"classTypeFactory.html#ad82f537569ece3d3a4a376523fa94473":[4,0,44,3], "classdbc__detail_1_1MultiplexError.html":[4,0,0,0], "classdbc__detail_1_1MultiplexGraph.html":[4,0,0,1], "classes.html":[4,1], @@ -247,7 +249,5 @@ var NAVTREEINDEX0 = "md_README.html#autotoc_md78":[2,16], "md_README.html#autotoc_md80":[2,17], "md_README.html#autotoc_md82":[2,18], -"md_README.html#autotoc_md83":[2,19], -"md_README.html#autotoc_md85":[2,20], -"md_README.html#autotoc_md87":[2,20,0] +"md_README.html#autotoc_md83":[2,19] }; diff --git a/docs/html/navtreeindex1.js b/docs/html/navtreeindex1.js index a8723a7..ef20a31 100644 --- a/docs/html/navtreeindex1.js +++ b/docs/html/navtreeindex1.js @@ -1,5 +1,7 @@ var NAVTREEINDEX1 = { +"md_README.html#autotoc_md85":[2,20], +"md_README.html#autotoc_md87":[2,20,0], "md_README.html#autotoc_md89":[2,21], "md_README.html#autotoc_md90":[2,21,0], "md_README.html#autotoc_md91":[2,21,1], @@ -60,6 +62,7 @@ var NAVTREEINDEX1 = "md_tests_2README.html#autotoc_md169":[3,8], "md_tests_2README.html#autotoc_md170":[3,9], "md_tests_2README.html#autotoc_md171":[3,10], +"md_tests_2README.html#autotoc_md172":[3,11], "multiplex__graph_8h_source.html":[5,0,0,19], "multiplexing_8h_source.html":[5,0,0,20], "pages.html":[], @@ -112,122 +115,122 @@ var NAVTREEINDEX1 = "structDbcParseResult.html#abfa765d29a4397e8ddaf771bae21993e":[4,0,15,1], "structDbcParserOptions.html":[4,0,16], "structDbcParserOptions.html#ab31534f5c0853461b46cbb38149c33f9":[4,0,16,0], -"structDecodeFrame.html":[4,0,26], -"structDecodeFrame.html#a05e3d1c6143367553c2c3beba82a5c34":[4,0,26,0], -"structDecodeFrame.html#a1d3c54055c0cbceb73adf551f7067f23":[4,0,26,5], -"structDecodeFrame.html#a2551102670c4a540fced4c71da0b369d":[4,0,26,6], -"structDecodeFrame.html#a25819ef907a32391af5c9f7cae9cdb22":[4,0,26,8], -"structDecodeFrame.html#a3ee2348d781f8f15aaf87e8dcf57ec41":[4,0,26,3], -"structDecodeFrame.html#a475e669f390491a4d99383d3fee51300":[4,0,26,11], -"structDecodeFrame.html#a51111ac13988be372d5673d12cf3e060":[4,0,26,4], -"structDecodeFrame.html#a52e10215388db7df8b541b9d9909edcf":[4,0,26,2], -"structDecodeFrame.html#a732c2f6853d55ce8253db61207fc21d3":[4,0,26,12], -"structDecodeFrame.html#a7f4e71bbdec3f424b625e724e3dfbf14":[4,0,26,9], -"structDecodeFrame.html#aa0a7f8fd5bfde2148dfd0d838328f579":[4,0,26,1], -"structDecodeFrame.html#ad506b03108a40df7e8005638c9c59653":[4,0,26,10], -"structDecodeFrame.html#ad78241dc40c793aabb8495b6989b81a9":[4,0,26,7], -"structDecodeSignal.html":[4,0,27], -"structDecodeSignal.html#a0fb84e19a0ab3bd50b6f65c1075ebc25":[4,0,27,8], -"structDecodeSignal.html#a204f65c7ea45a2c148f19c12d922f99b":[4,0,27,14], -"structDecodeSignal.html#a38eed9ca7581423fd8bfbedcffc12092":[4,0,27,10], -"structDecodeSignal.html#a428c39913a366a8476dd291061354391":[4,0,27,6], -"structDecodeSignal.html#a4a6ba87142a9badbf0ca830e9e709cb7":[4,0,27,9], -"structDecodeSignal.html#a58cf5844f2272a23dcb9687d912e4780":[4,0,27,1], -"structDecodeSignal.html#a822155d9bf4ed5f3300c9e9befe606ff":[4,0,27,12], -"structDecodeSignal.html#a92db637af4e224a0ae70173050dc3f74":[4,0,27,7], -"structDecodeSignal.html#a9d37638129263b8902ba5be59baf39a8":[4,0,27,5], -"structDecodeSignal.html#aaf5d7527ddf65b6355c2b4f3626df22d":[4,0,27,2], -"structDecodeSignal.html#abdbae62a64d2df716bf60c6b67b30eaa":[4,0,27,11], -"structDecodeSignal.html#abdc4c4bb4a67a6d61d6ca81e8a52833d":[4,0,27,4], -"structDecodeSignal.html#acd4ef5ee30421d117b5756854302debb":[4,0,27,3], -"structDecodeSignal.html#ae28c905d4a2bc86419c2a8361bfea473":[4,0,27,13], -"structDecodeSignal.html#aea41c055cb255fe7bd178ed5727f28c9":[4,0,27,0], -"structDecodedFrameValue.html":[4,0,24], -"structDecodedFrameValue.html#a32143efe2023a1ecd977a9ae93e50b73":[4,0,24,1], -"structDecodedFrameValue.html#a33e971ba10030506007aa201c89f351d":[4,0,24,0], -"structDecodedFrameValue.html#a4f51c31110dba1f73a763c596372beb0":[4,0,24,3], -"structDecodedFrameValue.html#a645f433798d48a2787e7f00dfbb7dff9":[4,0,24,4], -"structDecodedFrameValue.html#adae123ac7fbb4a5c314b64b01109f4a8":[4,0,24,2], -"structDecodedSignalValue.html":[4,0,25], -"structDecodedSignalValue.html#a33df0fcd92b23b3e08539906fc6b4c4a":[4,0,25,2], -"structDecodedSignalValue.html#a503c90b4b1a610b8252c45e466367c28":[4,0,25,3], -"structDecodedSignalValue.html#a6847d05b3cb00d5e0f6c611968f57978":[4,0,25,4], -"structDecodedSignalValue.html#a828b5ef7faefc14fbf5dbd46249bcad2":[4,0,25,1], -"structDecodedSignalValue.html#a94161a80412b3961da8fd2fec43c25f6":[4,0,25,6], -"structDecodedSignalValue.html#aac0ed361af545cde48d0e6f41a859686":[4,0,25,5], -"structDecodedSignalValue.html#affc38af1bf31bcb6f2c33044bce92bd2":[4,0,25,0], -"structFrameInfo.html":[4,0,28], -"structFrameInfo.html#a099f02a77adfb9ac23e286ce75f4eef4":[4,0,28,10], -"structFrameInfo.html#a34b726afaad2ac5c697fdb2ac070cdaf":[4,0,28,2], -"structFrameInfo.html#a35bf839519ef3f48b5f52dd9bcd7e9ac":[4,0,28,9], -"structFrameInfo.html#a3f7e25d67ddae22f2ed50823a5a26084":[4,0,28,8], -"structFrameInfo.html#a4737a5420213ad4304724e39f3a4e55d":[4,0,28,4], -"structFrameInfo.html#a92749ec3798d4211a505860fb389a9e8":[4,0,28,6], -"structFrameInfo.html#a9ef2736053b593cf34ba2608cf8275c0":[4,0,28,11], -"structFrameInfo.html#aa2b198ea0f55e560c123d35e7b3ecab9":[4,0,28,5], -"structFrameInfo.html#abb53a7f93ed7abc0ea1ae4a8ccafcdcb":[4,0,28,0], -"structFrameInfo.html#ace75121294f9d89b762080ab5643293c":[4,0,28,7], -"structFrameInfo.html#aee518b33f1000bf0b7f6e4a87474c943":[4,0,28,3], -"structFrameInfo.html#af470b0742c05bfbffc31c51a68f5c05e":[4,0,28,1], -"structFrameKey.html":[4,0,29], -"structFrameKey.html#a022917652b4d3c2f2dfcc5a13ab7caa4":[4,0,29,2], -"structFrameKey.html#a33bde405c7ea53000d54b3dd6e058670":[4,0,29,0], -"structFrameKey.html#aa3179b709d02ef70cf0e736f040a2145":[4,0,29,3], -"structFrameKey.html#aaaec9f914e127787247fcda5e183b01f":[4,0,29,1], -"structFrameKeyHasher.html":[4,0,30], -"structFrameKeyHasher.html#a1612ce56b513790e3ed4c06ec7d28b9f":[4,0,30,0], -"structMultiplexRange.html":[4,0,32], -"structMultiplexing.html":[4,0,31], -"structMultiplexing.html#a79158d4fdbfd68a3d51a8ce79d34fd67":[4,0,31,0], -"structMultiplexing.html#aa174ac4a3af9b361d2de7517dfd4b69e":[4,0,31,2], -"structMultiplexing.html#aa91e54403c78a663686b2fd22282afaf":[4,0,31,1], -"structPendingAttribute.html":[4,0,33], -"structPendingAttribute.html#a0ec62fc577e3a2d5f63d926f829e2ab8":[4,0,33,0], -"structPendingAttribute.html#a9a68a0a54c313c3e43dfdcd6e7a55da9":[4,0,33,1], -"structPendingComment.html":[4,0,34], -"structPendingComment.html#a29a3733e0a4c8ae341695db3d21d9249":[4,0,34,5], -"structPendingComment.html#a6d1c3b1e149bb602a95224446709c771":[4,0,34,0], -"structPendingComment.html#a9abd7e15ed59eb4534c66ff74f276e07":[4,0,34,1], -"structPendingComment.html#aa344393f67770e12449a4f14f6fcf10b":[4,0,34,4], -"structPendingComment.html#ac03903ffb042d27ad66012eb18ea1437":[4,0,34,2], -"structPendingComment.html#ae7b5aea9c252a4ab87e02915d24fee27":[4,0,34,3], -"structPendingMultiplexing.html":[4,0,35], -"structPendingValueDescriptions.html":[4,0,36], -"structPendingValueDescriptions.html#a10ecbaeaed2cc3ac82fada73e7e041dd":[4,0,36,3], -"structPendingValueDescriptions.html#a5c19cff09b0e83bd3cd6085213c17da8":[4,0,36,4], -"structPendingValueDescriptions.html#a5f0082ceeaee77e2f823661547aad844":[4,0,36,0], -"structPendingValueDescriptions.html#a60f185e8d91010927c4aa3b4a750c9f4":[4,0,36,1], -"structPendingValueDescriptions.html#ac35446db10ebda5a12349759613f8c90":[4,0,36,2], -"structPgnLookupResult.html":[4,0,37], -"structPgnLookupResult.html#a6158833782348b3cc26037c2091fcc36":[4,0,37,1], -"structPgnLookupResult.html#aa5b79b61d2aef48fd6ae070cf5f901c1":[4,0,37,0], -"structRawCanFrame.html":[4,0,38], -"structRawCanFrame.html#a0acb487612104c99c9907344c035df83":[4,0,38,2], -"structRawCanFrame.html#a2d0e7fa0e3d5c20bbed55ed8f7888ff4":[4,0,38,0], -"structRawCanFrame.html#a462193d275adc1dba8bd18ffa0e36bb7":[4,0,38,1], -"structSignalInfo.html":[4,0,39], -"structSignalInfo.html#a1008b5201f71848177217bd52fb84baa":[4,0,39,3], -"structSignalInfo.html#a1f214f03e94ef5794de2d3367bfcbd01":[4,0,39,10], -"structSignalInfo.html#a2c95562fd0b29327a7fd7b1e6feee7f3":[4,0,39,11], -"structSignalInfo.html#a379e422a00cad56f896cacf60455b088":[4,0,39,1], -"structSignalInfo.html#a43f47d0433d0cc39076bd7278b46af9c":[4,0,39,12], -"structSignalInfo.html#a52a52b320eef92fbcb4626307f9a498f":[4,0,39,5], -"structSignalInfo.html#a781766eddbf6641071623c8e5b9acb4a":[4,0,39,14], -"structSignalInfo.html#a8a2e29f307c75926a1bd824266d23ca8":[4,0,39,0], -"structSignalInfo.html#ab7d0769f47aa540656b22e4e49db517e":[4,0,39,7], -"structSignalInfo.html#ac99ba7563a5dad01e39c4257216953f5":[4,0,39,13], -"structSignalInfo.html#ace290724f9801521609aedaa946d61f1":[4,0,39,2], -"structSignalInfo.html#ada7bf94fbeaa26bd08c73982b5af255b":[4,0,39,8], -"structSignalInfo.html#adf1c4331b9eabced0d7fdda40ff0eb52":[4,0,39,9], -"structSignalInfo.html#ae4f78d99a2750aa564d7eba860171499":[4,0,39,6], -"structSignalInfo.html#af363b1563fb4c7cc8973f9a80c06846c":[4,0,39,4], -"structSignalKey.html":[4,0,40], -"structSignalKey.html#a17525dc0116be7f4f58f3c3fae1022e2":[4,0,40,1], -"structSignalKey.html#a25f4ece1bf685791b576f68e23d8ccc7":[4,0,40,2], -"structSignalKey.html#aa4221b34e75469bf5aa939976c334f46":[4,0,40,0], -"structSignalKey.html#abab371cce740f05111fd3b4013ca6944":[4,0,40,3], -"structSignalKeyHasher.html":[4,0,41], -"structSignalKeyHasher.html#af8869bcb714857af7ad005fbd13bea80":[4,0,41,0], +"structDecodeFrame.html":[4,0,27], +"structDecodeFrame.html#a05e3d1c6143367553c2c3beba82a5c34":[4,0,27,0], +"structDecodeFrame.html#a1d3c54055c0cbceb73adf551f7067f23":[4,0,27,5], +"structDecodeFrame.html#a2551102670c4a540fced4c71da0b369d":[4,0,27,6], +"structDecodeFrame.html#a25819ef907a32391af5c9f7cae9cdb22":[4,0,27,8], +"structDecodeFrame.html#a3ee2348d781f8f15aaf87e8dcf57ec41":[4,0,27,3], +"structDecodeFrame.html#a475e669f390491a4d99383d3fee51300":[4,0,27,11], +"structDecodeFrame.html#a51111ac13988be372d5673d12cf3e060":[4,0,27,4], +"structDecodeFrame.html#a52e10215388db7df8b541b9d9909edcf":[4,0,27,2], +"structDecodeFrame.html#a732c2f6853d55ce8253db61207fc21d3":[4,0,27,12], +"structDecodeFrame.html#a7f4e71bbdec3f424b625e724e3dfbf14":[4,0,27,9], +"structDecodeFrame.html#aa0a7f8fd5bfde2148dfd0d838328f579":[4,0,27,1], +"structDecodeFrame.html#ad506b03108a40df7e8005638c9c59653":[4,0,27,10], +"structDecodeFrame.html#ad78241dc40c793aabb8495b6989b81a9":[4,0,27,7], +"structDecodeSignal.html":[4,0,28], +"structDecodeSignal.html#a0fb84e19a0ab3bd50b6f65c1075ebc25":[4,0,28,8], +"structDecodeSignal.html#a204f65c7ea45a2c148f19c12d922f99b":[4,0,28,14], +"structDecodeSignal.html#a38eed9ca7581423fd8bfbedcffc12092":[4,0,28,10], +"structDecodeSignal.html#a428c39913a366a8476dd291061354391":[4,0,28,6], +"structDecodeSignal.html#a4a6ba87142a9badbf0ca830e9e709cb7":[4,0,28,9], +"structDecodeSignal.html#a58cf5844f2272a23dcb9687d912e4780":[4,0,28,1], +"structDecodeSignal.html#a822155d9bf4ed5f3300c9e9befe606ff":[4,0,28,12], +"structDecodeSignal.html#a92db637af4e224a0ae70173050dc3f74":[4,0,28,7], +"structDecodeSignal.html#a9d37638129263b8902ba5be59baf39a8":[4,0,28,5], +"structDecodeSignal.html#aaf5d7527ddf65b6355c2b4f3626df22d":[4,0,28,2], +"structDecodeSignal.html#abdbae62a64d2df716bf60c6b67b30eaa":[4,0,28,11], +"structDecodeSignal.html#abdc4c4bb4a67a6d61d6ca81e8a52833d":[4,0,28,4], +"structDecodeSignal.html#acd4ef5ee30421d117b5756854302debb":[4,0,28,3], +"structDecodeSignal.html#ae28c905d4a2bc86419c2a8361bfea473":[4,0,28,13], +"structDecodeSignal.html#aea41c055cb255fe7bd178ed5727f28c9":[4,0,28,0], +"structDecodedFrameValue.html":[4,0,25], +"structDecodedFrameValue.html#a32143efe2023a1ecd977a9ae93e50b73":[4,0,25,1], +"structDecodedFrameValue.html#a33e971ba10030506007aa201c89f351d":[4,0,25,0], +"structDecodedFrameValue.html#a4f51c31110dba1f73a763c596372beb0":[4,0,25,3], +"structDecodedFrameValue.html#a645f433798d48a2787e7f00dfbb7dff9":[4,0,25,4], +"structDecodedFrameValue.html#adae123ac7fbb4a5c314b64b01109f4a8":[4,0,25,2], +"structDecodedSignalValue.html":[4,0,26], +"structDecodedSignalValue.html#a33df0fcd92b23b3e08539906fc6b4c4a":[4,0,26,2], +"structDecodedSignalValue.html#a503c90b4b1a610b8252c45e466367c28":[4,0,26,3], +"structDecodedSignalValue.html#a6847d05b3cb00d5e0f6c611968f57978":[4,0,26,4], +"structDecodedSignalValue.html#a828b5ef7faefc14fbf5dbd46249bcad2":[4,0,26,1], +"structDecodedSignalValue.html#a94161a80412b3961da8fd2fec43c25f6":[4,0,26,6], +"structDecodedSignalValue.html#aac0ed361af545cde48d0e6f41a859686":[4,0,26,5], +"structDecodedSignalValue.html#affc38af1bf31bcb6f2c33044bce92bd2":[4,0,26,0], +"structFrameInfo.html":[4,0,29], +"structFrameInfo.html#a099f02a77adfb9ac23e286ce75f4eef4":[4,0,29,10], +"structFrameInfo.html#a34b726afaad2ac5c697fdb2ac070cdaf":[4,0,29,2], +"structFrameInfo.html#a35bf839519ef3f48b5f52dd9bcd7e9ac":[4,0,29,9], +"structFrameInfo.html#a3f7e25d67ddae22f2ed50823a5a26084":[4,0,29,8], +"structFrameInfo.html#a4737a5420213ad4304724e39f3a4e55d":[4,0,29,4], +"structFrameInfo.html#a92749ec3798d4211a505860fb389a9e8":[4,0,29,6], +"structFrameInfo.html#a9ef2736053b593cf34ba2608cf8275c0":[4,0,29,11], +"structFrameInfo.html#aa2b198ea0f55e560c123d35e7b3ecab9":[4,0,29,5], +"structFrameInfo.html#abb53a7f93ed7abc0ea1ae4a8ccafcdcb":[4,0,29,0], +"structFrameInfo.html#ace75121294f9d89b762080ab5643293c":[4,0,29,7], +"structFrameInfo.html#aee518b33f1000bf0b7f6e4a87474c943":[4,0,29,3], +"structFrameInfo.html#af470b0742c05bfbffc31c51a68f5c05e":[4,0,29,1], +"structFrameKey.html":[4,0,30], +"structFrameKey.html#a022917652b4d3c2f2dfcc5a13ab7caa4":[4,0,30,2], +"structFrameKey.html#a33bde405c7ea53000d54b3dd6e058670":[4,0,30,0], +"structFrameKey.html#aa3179b709d02ef70cf0e736f040a2145":[4,0,30,3], +"structFrameKey.html#aaaec9f914e127787247fcda5e183b01f":[4,0,30,1], +"structFrameKeyHasher.html":[4,0,31], +"structFrameKeyHasher.html#a1612ce56b513790e3ed4c06ec7d28b9f":[4,0,31,0], +"structMultiplexRange.html":[4,0,33], +"structMultiplexing.html":[4,0,32], +"structMultiplexing.html#a79158d4fdbfd68a3d51a8ce79d34fd67":[4,0,32,0], +"structMultiplexing.html#aa174ac4a3af9b361d2de7517dfd4b69e":[4,0,32,2], +"structMultiplexing.html#aa91e54403c78a663686b2fd22282afaf":[4,0,32,1], +"structPendingAttribute.html":[4,0,34], +"structPendingAttribute.html#a0ec62fc577e3a2d5f63d926f829e2ab8":[4,0,34,0], +"structPendingAttribute.html#a9a68a0a54c313c3e43dfdcd6e7a55da9":[4,0,34,1], +"structPendingComment.html":[4,0,35], +"structPendingComment.html#a29a3733e0a4c8ae341695db3d21d9249":[4,0,35,5], +"structPendingComment.html#a6d1c3b1e149bb602a95224446709c771":[4,0,35,0], +"structPendingComment.html#a9abd7e15ed59eb4534c66ff74f276e07":[4,0,35,1], +"structPendingComment.html#aa344393f67770e12449a4f14f6fcf10b":[4,0,35,4], +"structPendingComment.html#ac03903ffb042d27ad66012eb18ea1437":[4,0,35,2], +"structPendingComment.html#ae7b5aea9c252a4ab87e02915d24fee27":[4,0,35,3], +"structPendingMultiplexing.html":[4,0,36], +"structPendingValueDescriptions.html":[4,0,37], +"structPendingValueDescriptions.html#a10ecbaeaed2cc3ac82fada73e7e041dd":[4,0,37,3], +"structPendingValueDescriptions.html#a5c19cff09b0e83bd3cd6085213c17da8":[4,0,37,4], +"structPendingValueDescriptions.html#a5f0082ceeaee77e2f823661547aad844":[4,0,37,0], +"structPendingValueDescriptions.html#a60f185e8d91010927c4aa3b4a750c9f4":[4,0,37,1], +"structPendingValueDescriptions.html#ac35446db10ebda5a12349759613f8c90":[4,0,37,2], +"structPgnLookupResult.html":[4,0,38], +"structPgnLookupResult.html#a6158833782348b3cc26037c2091fcc36":[4,0,38,1], +"structPgnLookupResult.html#aa5b79b61d2aef48fd6ae070cf5f901c1":[4,0,38,0], +"structRawCanFrame.html":[4,0,39], +"structRawCanFrame.html#a0acb487612104c99c9907344c035df83":[4,0,39,2], +"structRawCanFrame.html#a2d0e7fa0e3d5c20bbed55ed8f7888ff4":[4,0,39,0], +"structRawCanFrame.html#a462193d275adc1dba8bd18ffa0e36bb7":[4,0,39,1], +"structSignalInfo.html":[4,0,40], +"structSignalInfo.html#a1008b5201f71848177217bd52fb84baa":[4,0,40,3], +"structSignalInfo.html#a1f214f03e94ef5794de2d3367bfcbd01":[4,0,40,10], +"structSignalInfo.html#a2c95562fd0b29327a7fd7b1e6feee7f3":[4,0,40,11], +"structSignalInfo.html#a379e422a00cad56f896cacf60455b088":[4,0,40,1], +"structSignalInfo.html#a43f47d0433d0cc39076bd7278b46af9c":[4,0,40,12], +"structSignalInfo.html#a52a52b320eef92fbcb4626307f9a498f":[4,0,40,5], +"structSignalInfo.html#a781766eddbf6641071623c8e5b9acb4a":[4,0,40,14], +"structSignalInfo.html#a8a2e29f307c75926a1bd824266d23ca8":[4,0,40,0], +"structSignalInfo.html#ab7d0769f47aa540656b22e4e49db517e":[4,0,40,7], +"structSignalInfo.html#ac99ba7563a5dad01e39c4257216953f5":[4,0,40,13], +"structSignalInfo.html#ace290724f9801521609aedaa946d61f1":[4,0,40,2], +"structSignalInfo.html#ada7bf94fbeaa26bd08c73982b5af255b":[4,0,40,8], +"structSignalInfo.html#adf1c4331b9eabced0d7fdda40ff0eb52":[4,0,40,9], +"structSignalInfo.html#ae4f78d99a2750aa564d7eba860171499":[4,0,40,6], +"structSignalInfo.html#af363b1563fb4c7cc8973f9a80c06846c":[4,0,40,4], +"structSignalKey.html":[4,0,41], +"structSignalKey.html#a17525dc0116be7f4f58f3c3fae1022e2":[4,0,41,1], +"structSignalKey.html#a25f4ece1bf685791b576f68e23d8ccc7":[4,0,41,2], +"structSignalKey.html#aa4221b34e75469bf5aa939976c334f46":[4,0,41,0], +"structSignalKey.html#abab371cce740f05111fd3b4013ca6944":[4,0,41,3], +"structSignalKeyHasher.html":[4,0,42], +"structSignalKeyHasher.html#af8869bcb714857af7ad005fbd13bea80":[4,0,42,0], "tests_2main_8cpp_source.html":[5,0,3,7], "text__utils_8h_source.html":[5,0,0,23], "tree__node_8cpp.html":[5,0,0,24], diff --git a/docs/html/search/all_10.js b/docs/html/search/all_10.js index 175845f..bb1e80a 100644 --- a/docs/html/search/all_10.js +++ b/docs/html/search/all_10.js @@ -13,6 +13,6 @@ var searchData= ['live_20path_10',['live path',['../md_README.html#autotoc_md127',1,'']]], ['local_20core_20regression_20checks_11',['Local core regression checks',['../md_README.html#autotoc_md147',1,'']]], ['local_20regression_20checks_12',['Local regression checks',['../md_tests_2README.html#autotoc_md161',1,'']]], - ['long_20signal_20definitions_13',['Long signal definitions',['../md_tests_2README.html#autotoc_md170',1,'']]], + ['long_20signal_20definitions_13',['Long signal definitions',['../md_tests_2README.html#autotoc_md171',1,'']]], ['lookup_14',['J1939 Opt-in and PGN Lookup',['../md_README.html#autotoc_md114',1,'']]] ]; diff --git a/docs/html/search/all_16.js b/docs/html/search/all_16.js index fd17ede..e773b32 100644 --- a/docs/html/search/all_16.js +++ b/docs/html/search/all_16.js @@ -8,7 +8,7 @@ var searchData= ['rawvalue_5',['rawValue',['../structDecodedSignalValue.html#a503c90b4b1a610b8252c45e466367c28',1,'DecodedSignalValue']]], ['receivers_6',['receivers',['../structSignalInfo.html#a2c95562fd0b29327a7fd7b1e6feee7f3',1,'SignalInfo::receivers'],['../structDecodeSignal.html#abdbae62a64d2df716bf60c6b67b30eaa',1,'DecodeSignal::receivers']]], ['recommended_20next_20steps_7',['Recommended Next Steps',['../md_README.html#autotoc_md140',1,'']]], - ['registertype_8',['registertype',['../classDbcStatementDispatcher.html#a2c6e9a614ed747275d43bcccc50d0b2d',1,'DbcStatementDispatcher::RegisterType()'],['../classTypeFactory.html#ad6d85795285ee21193ac03e1b24762d3',1,'TypeFactory::registerType()']]], + ['registertype_8',['registertype',['../classTypeFactory.html#ad6d85795285ee21193ac03e1b24762d3',1,'TypeFactory::registerType()'],['../classDbcStatementDispatcher.html#a2c6e9a614ed747275d43bcccc50d0b2d',1,'DbcStatementDispatcher::RegisterType()']]], ['regression_20checks_9',['regression checks',['../md_README.html#autotoc_md147',1,'Local core regression checks'],['../md_tests_2README.html#autotoc_md161',1,'Local regression checks']]], ['regression_20coverage_10',['Additional regression coverage',['../md_tests_2README.html#autotoc_md165',1,'']]], ['regression_20tests_11',['regression tests',['../md_tests_2README.html',1,'QtTest regression tests'],['../md_README.html#autotoc_md154',1,'Regression tests']]], diff --git a/docs/html/search/all_17.js b/docs/html/search/all_17.js index d4c68f6..bb7eabb 100644 --- a/docs/html/search/all_17.js +++ b/docs/html/search/all_17.js @@ -5,33 +5,34 @@ var searchData= ['settree_2',['SetTree',['../classDbcTreeModel.html#a8cb5a81d456cdead42b75fd3462ee00a',1,'DbcTreeModel']]], ['severity_3',['severity',['../structDbcDiagnostic.html#a75040a94f0313a606478020b0491f81d',1,'DbcDiagnostic']]], ['signal_20definition_4',['Signal definition',['../md_README.html#autotoc_md104',1,'']]], - ['signal_20definitions_5',['Long signal definitions',['../md_tests_2README.html#autotoc_md170',1,'']]], + ['signal_20definitions_5',['Long signal definitions',['../md_tests_2README.html#autotoc_md171',1,'']]], ['signal_20identifiers_6',['Stable frame and signal identifiers',['../md_README.html#autotoc_md91',1,'']]], ['signal_20validation_20and_20parse_20errors_7',['Signal validation and parse errors',['../md_README.html#autotoc_md106',1,'']]], ['signal_20value_20descriptions_20tt_20val_5f_20tt_8',['Signal value descriptions (<tt>VAL_</tt>)',['../md_README.html#autotoc_md136',1,'']]], - ['signal_5finfo_2eh_9',['signal_info.h',['../signal__info_8h.html',1,'']]], - ['signaldecodestatus_10',['SignalDecodeStatus',['../dbc__decoder_8h.html#a5f4a41ee2810487020135b45e8d7559a',1,'dbc_decoder.h']]], - ['signalinfo_11',['SignalInfo',['../structSignalInfo.html',1,'']]], - ['signalinfo_20tt_12',['<tt>SignalInfo</tt>',['../md_README.html#autotoc_md58',1,'']]], - ['signalkey_13',['signalkey',['../structSignalKey.html',1,'SignalKey'],['../structSignalKey.html#aa4221b34e75469bf5aa939976c334f46',1,'SignalKey::SignalKey()']]], - ['signalkeyhasher_14',['SignalKeyHasher',['../structSignalKeyHasher.html',1,'']]], - ['signallines_15',['signalLines',['../structDbcParseContext.html#af8b411c938a2ea9100c4aaae452e6546',1,'DbcParseContext']]], - ['signalname_16',['signalname',['../structPendingValueDescriptions.html#ac35446db10ebda5a12349759613f8c90',1,'PendingValueDescriptions::signalName'],['../structPendingComment.html#ae7b5aea9c252a4ab87e02915d24fee27',1,'PendingComment::signalName']]], - ['stable_20frame_20and_20signal_20identifiers_17',['Stable frame and signal identifiers',['../md_README.html#autotoc_md91',1,'']]], - ['stage_201_20already_20implemented_18',['Stage 1 - already implemented',['../md_README.html#autotoc_md141',1,'']]], - ['stage_202_19',['Stage 2',['../md_README.html#autotoc_md142',1,'']]], - ['stage_203_20',['Stage 3',['../md_README.html#autotoc_md143',1,'']]], - ['stage_204_21',['Stage 4',['../md_README.html#autotoc_md144',1,'']]], - ['standalone_20tree_20tests_20without_20qt_22',['Standalone tree tests without Qt',['../md_README.html#autotoc_md150',1,'']]], - ['startbit_23',['startbit',['../structDecodeSignal.html#aaf5d7527ddf65b6355c2b4f3626df22d',1,'DecodeSignal::startBit'],['../structSignalInfo.html#ace290724f9801521609aedaa946d61f1',1,'SignalInfo::startBit']]], - ['statement_24',['statement',['../structPendingAttribute.html#a0ec62fc577e3a2d5f63d926f829e2ab8',1,'PendingAttribute']]], - ['statement_20handler_20factory_25',['Statement Handler Factory',['../md_README.html#autotoc_md134',1,'']]], - ['status_26',['status',['../structDecodedSignalValue.html#a94161a80412b3961da8fd2fec43c25f6',1,'DecodedSignalValue::status'],['../structDecodedFrameValue.html#a645f433798d48a2787e7f00dfbb7dff9',1,'DecodedFrameValue::status']]], - ['steps_27',['Recommended Next Steps',['../md_README.html#autotoc_md140',1,'']]], - ['strategy_28',['Unified Decode Strategy',['../md_README.html#autotoc_md126',1,'']]], - ['structured_20diagnostics_29',['Structured diagnostics',['../md_README.html#autotoc_md96',1,'']]], - ['structures_30',['Parsed Data Structures',['../md_README.html#autotoc_md57',1,'']]], - ['support_31',['Parser Support',['../md_README.html#autotoc_md95',1,'']]], - ['supported_20dbc_20syntax_32',['Supported DBC Syntax',['../md_README.html#autotoc_md99',1,'']]], - ['syntax_33',['Supported DBC Syntax',['../md_README.html#autotoc_md99',1,'']]] + ['signal_20value_20types_9',['Unsupported signal value types',['../md_tests_2README.html#autotoc_md170',1,'']]], + ['signal_5finfo_2eh_10',['signal_info.h',['../signal__info_8h.html',1,'']]], + ['signaldecodestatus_11',['SignalDecodeStatus',['../dbc__decoder_8h.html#a5f4a41ee2810487020135b45e8d7559a',1,'dbc_decoder.h']]], + ['signalinfo_12',['SignalInfo',['../structSignalInfo.html',1,'']]], + ['signalinfo_20tt_13',['<tt>SignalInfo</tt>',['../md_README.html#autotoc_md58',1,'']]], + ['signalkey_14',['signalkey',['../structSignalKey.html',1,'SignalKey'],['../structSignalKey.html#aa4221b34e75469bf5aa939976c334f46',1,'SignalKey::SignalKey()']]], + ['signalkeyhasher_15',['SignalKeyHasher',['../structSignalKeyHasher.html',1,'']]], + ['signallines_16',['signalLines',['../structDbcParseContext.html#af8b411c938a2ea9100c4aaae452e6546',1,'DbcParseContext']]], + ['signalname_17',['signalname',['../structPendingValueDescriptions.html#ac35446db10ebda5a12349759613f8c90',1,'PendingValueDescriptions::signalName'],['../structPendingComment.html#ae7b5aea9c252a4ab87e02915d24fee27',1,'PendingComment::signalName']]], + ['stable_20frame_20and_20signal_20identifiers_18',['Stable frame and signal identifiers',['../md_README.html#autotoc_md91',1,'']]], + ['stage_201_20already_20implemented_19',['Stage 1 - already implemented',['../md_README.html#autotoc_md141',1,'']]], + ['stage_202_20',['Stage 2',['../md_README.html#autotoc_md142',1,'']]], + ['stage_203_21',['Stage 3',['../md_README.html#autotoc_md143',1,'']]], + ['stage_204_22',['Stage 4',['../md_README.html#autotoc_md144',1,'']]], + ['standalone_20tree_20tests_20without_20qt_23',['Standalone tree tests without Qt',['../md_README.html#autotoc_md150',1,'']]], + ['startbit_24',['startbit',['../structDecodeSignal.html#aaf5d7527ddf65b6355c2b4f3626df22d',1,'DecodeSignal::startBit'],['../structSignalInfo.html#ace290724f9801521609aedaa946d61f1',1,'SignalInfo::startBit']]], + ['statement_25',['statement',['../structPendingAttribute.html#a0ec62fc577e3a2d5f63d926f829e2ab8',1,'PendingAttribute']]], + ['statement_20handler_20factory_26',['Statement Handler Factory',['../md_README.html#autotoc_md134',1,'']]], + ['status_27',['status',['../structDecodedFrameValue.html#a645f433798d48a2787e7f00dfbb7dff9',1,'DecodedFrameValue::status'],['../structDecodedSignalValue.html#a94161a80412b3961da8fd2fec43c25f6',1,'DecodedSignalValue::status']]], + ['steps_28',['Recommended Next Steps',['../md_README.html#autotoc_md140',1,'']]], + ['strategy_29',['Unified Decode Strategy',['../md_README.html#autotoc_md126',1,'']]], + ['structured_20diagnostics_30',['Structured diagnostics',['../md_README.html#autotoc_md96',1,'']]], + ['structures_31',['Parsed Data Structures',['../md_README.html#autotoc_md57',1,'']]], + ['support_32',['Parser Support',['../md_README.html#autotoc_md95',1,'']]], + ['supported_20dbc_20syntax_33',['Supported DBC Syntax',['../md_README.html#autotoc_md99',1,'']]], + ['syntax_34',['Supported DBC Syntax',['../md_README.html#autotoc_md99',1,'']]] ]; diff --git a/docs/html/search/all_18.js b/docs/html/search/all_18.js index 71c8324..3d2746e 100644 --- a/docs/html/search/all_18.js +++ b/docs/html/search/all_18.js @@ -10,7 +10,7 @@ var searchData= ['the_20tree_20is_20not_20enough_7',['Why the Tree Is Not Enough',['../md_README.html#autotoc_md132',1,'']]], ['to_20end_20pipeline_20coverage_8',['End-to-end pipeline coverage',['../md_tests_2README.html#autotoc_md168',1,'']]], ['trace_20path_9',['trace path',['../md_README.html#autotoc_md128',1,'']]], - ['transmitter_10',['transmitter',['../structDecodeFrame.html#ad78241dc40c793aabb8495b6989b81a9',1,'DecodeFrame::transmitter'],['../structFrameInfo.html#a92749ec3798d4211a505860fb389a9e8',1,'FrameInfo::transmitter']]], + ['transmitter_10',['transmitter',['../structFrameInfo.html#a92749ec3798d4211a505860fb389a9e8',1,'FrameInfo::transmitter'],['../structDecodeFrame.html#ad78241dc40c793aabb8495b6989b81a9',1,'DecodeFrame::transmitter']]], ['tree_11',['Build UI tree',['../md_README.html#autotoc_md122',1,'']]], ['tree_20and_20optional_20model_20tests_12',['Tree and optional model tests',['../md_tests_2README.html#autotoc_md164',1,'']]], ['tree_20is_20not_20enough_13',['Why the Tree Is Not Enough',['../md_README.html#autotoc_md132',1,'']]], @@ -18,7 +18,7 @@ var searchData= ['tree_20tests_20without_20qt_15',['Standalone tree tests without Qt',['../md_README.html#autotoc_md150',1,'']]], ['tree_5fnode_2ecpp_16',['tree_node.cpp',['../tree__node_8cpp.html',1,'']]], ['tree_5fnode_2eh_17',['tree_node.h',['../tree__node_8h.html',1,'']]], - ['treenode_18',['treenode',['../classTreeNode.html#ab9f00f5253d56461bb2d420fd639c5ae',1,'TreeNode::TreeNode(const SignalInfo &signal)'],['../classTreeNode.html#a8e38dd591d8801d7ee3a7dbad90c67fc',1,'TreeNode::TreeNode(const FrameInfo &frame)'],['../classTreeNode.html#a984a98d5ccf7ef1f5a18094c6821f35d',1,'TreeNode::TreeNode()'],['../classTreeNode.html',1,'TreeNode']]], + ['treenode_18',['treenode',['../classTreeNode.html',1,'TreeNode'],['../classTreeNode.html#a984a98d5ccf7ef1f5a18094c6821f35d',1,'TreeNode::TreeNode()'],['../classTreeNode.html#a8e38dd591d8801d7ee3a7dbad90c67fc',1,'TreeNode::TreeNode(const FrameInfo &frame)'],['../classTreeNode.html#ab9f00f5253d56461bb2d420fd639c5ae',1,'TreeNode::TreeNode(const SignalInfo &signal)']]], ['treenode_20tt_19',['<tt>TreeNode</tt>',['../md_README.html#autotoc_md65',1,'']]], ['tt_20byteorder_20tt_20',['<tt>ByteOrder</tt>',['../md_README.html#autotoc_md70',1,'']]], ['tt_20dbcdatabase_20tt_21',['<tt>DbcDatabase</tt>',['../md_README.html#autotoc_md62',1,'']]], @@ -37,5 +37,6 @@ var searchData= ['tt_20valuetype_20tt_34',['<tt>ValueType</tt>',['../md_README.html#autotoc_md72',1,'']]], ['type_35',['type',['../structAttributeDefinition.html#a5de5484174a8c5f6b309041089d20afc',1,'AttributeDefinition']]], ['typefactory_36',['TypeFactory',['../classTypeFactory.html',1,'']]], - ['typefactory_3c_20std_3a_3astring_2c_20dbcstatementhandler_20_3e_37',['TypeFactory< std::string, DbcStatementHandler >',['../classTypeFactory.html',1,'']]] + ['typefactory_3c_20std_3a_3astring_2c_20dbcstatementhandler_20_3e_37',['TypeFactory< std::string, DbcStatementHandler >',['../classTypeFactory.html',1,'']]], + ['types_38',['Unsupported signal value types',['../md_tests_2README.html#autotoc_md170',1,'']]] ]; diff --git a/docs/html/search/all_19.js b/docs/html/search/all_19.js index c39e66f..27d47b3 100644 --- a/docs/html/search/all_19.js +++ b/docs/html/search/all_19.js @@ -7,6 +7,7 @@ var searchData= ['unified_20decode_20strategy_4',['Unified Decode Strategy',['../md_README.html#autotoc_md126',1,'']]], ['unit_5',['unit',['../structDecodeSignal.html#a38eed9ca7581423fd8bfbedcffc12092',1,'DecodeSignal::unit'],['../structSignalInfo.html#a1f214f03e94ef5794de2d3367bfcbd01',1,'SignalInfo::unit']]], ['unreleased_6',['Unreleased',['../md_changelog.html#autotoc_md7',1,'']]], - ['usage_7',['Example Usage',['../md_README.html#autotoc_md120',1,'']]], - ['use_20in_20frametap_8',['Intended Use in FrameTap',['../md_README.html#autotoc_md130',1,'']]] + ['unsupported_20signal_20value_20types_7',['Unsupported signal value types',['../md_tests_2README.html#autotoc_md170',1,'']]], + ['usage_8',['Example Usage',['../md_README.html#autotoc_md120',1,'']]], + ['use_20in_20frametap_9',['Intended Use in FrameTap',['../md_README.html#autotoc_md130',1,'']]] ]; diff --git a/docs/html/search/all_1a.js b/docs/html/search/all_1a.js index 89a474f..8dbe1b5 100644 --- a/docs/html/search/all_1a.js +++ b/docs/html/search/all_1a.js @@ -4,14 +4,15 @@ var searchData= ['val_5f_20tt_1',['Signal value descriptions (<tt>VAL_</tt>)',['../md_README.html#autotoc_md136',1,'']]], ['valid_2',['valid',['../structDecodedFrameValue.html#a4f51c31110dba1f73a763c596372beb0',1,'DecodedFrameValue::valid'],['../structDecodedSignalValue.html#aac0ed361af545cde48d0e6f41a859686',1,'DecodedSignalValue::valid']]], ['validatemultiplexing_3',['ValidateMultiplexing',['../structDbcParseContext.html#a908056de0a97316a4ebecb3f99cc400b',1,'DbcParseContext']]], - ['validation_4',['Extended validation',['../md_tests_2README.html#autotoc_md171',1,'']]], + ['validation_4',['Extended validation',['../md_tests_2README.html#autotoc_md172',1,'']]], ['validation_20and_20parse_20errors_5',['validation and parse errors',['../md_README.html#autotoc_md102',1,'Frame validation and parse errors'],['../md_README.html#autotoc_md106',1,'Signal validation and parse errors']]], ['validation_20coverage_6',['Database validation coverage',['../md_tests_2README.html#autotoc_md167',1,'']]], - ['value_7',['value',['../structAttributeAssignment.html#a93de8c51c5154219bf18beadcec5708f',1,'AttributeAssignment::value'],['../structMultiplexing.html#a79158d4fdbfd68a3d51a8ce79d34fd67',1,'Multiplexing::value']]], + ['value_7',['value',['../structMultiplexing.html#a79158d4fdbfd68a3d51a8ce79d34fd67',1,'Multiplexing::value'],['../structAttributeAssignment.html#a93de8c51c5154219bf18beadcec5708f',1,'AttributeAssignment::value']]], ['value_20descriptions_20tt_20val_5f_20tt_8',['Signal value descriptions (<tt>VAL_</tt>)',['../md_README.html#autotoc_md136',1,'']]], ['value_20representation_20and_20precision_9',['Raw value representation and precision',['../md_README.html#autotoc_md87',1,'']]], - ['valuedescriptions_10',['valuedescriptions',['../structDecodeSignal.html#a822155d9bf4ed5f3300c9e9befe606ff',1,'DecodeSignal::valueDescriptions'],['../structSignalInfo.html#a43f47d0433d0cc39076bd7278b46af9c',1,'SignalInfo::valueDescriptions']]], - ['valuetype_11',['valuetype',['../structDecodeSignal.html#a9d37638129263b8902ba5be59baf39a8',1,'DecodeSignal::valueType'],['../decode__database_8h.html#ad9971b6ef33e02ba2c75d19c1d2518a1',1,'ValueType: decode_database.h']]], - ['valuetype_20tt_12',['<tt>ValueType</tt>',['../md_README.html#autotoc_md72',1,'']]], - ['variants_20in_20qt_20creator_13',['Configure all build variants in Qt Creator',['../md_README.html#autotoc_md159',1,'']]] + ['value_20types_10',['Unsupported signal value types',['../md_tests_2README.html#autotoc_md170',1,'']]], + ['valuedescriptions_11',['valuedescriptions',['../structSignalInfo.html#a43f47d0433d0cc39076bd7278b46af9c',1,'SignalInfo::valueDescriptions'],['../structDecodeSignal.html#a822155d9bf4ed5f3300c9e9befe606ff',1,'DecodeSignal::valueDescriptions']]], + ['valuetype_12',['valuetype',['../structDecodeSignal.html#a9d37638129263b8902ba5be59baf39a8',1,'DecodeSignal::valueType'],['../decode__database_8h.html#ad9971b6ef33e02ba2c75d19c1d2518a1',1,'ValueType: decode_database.h']]], + ['valuetype_20tt_13',['<tt>ValueType</tt>',['../md_README.html#autotoc_md72',1,'']]], + ['variants_20in_20qt_20creator_14',['Configure all build variants in Qt Creator',['../md_README.html#autotoc_md159',1,'']]] ]; diff --git a/docs/html/search/all_8.js b/docs/html/search/all_8.js index 60c8199..47f9a17 100644 --- a/docs/html/search/all_8.js +++ b/docs/html/search/all_8.js @@ -36,43 +36,44 @@ var searchData= ['dbcstatementdispatcher_33',['dbcstatementdispatcher',['../classDbcStatementDispatcher.html',1,'DbcStatementDispatcher'],['../classDbcStatementDispatcher.html#a2f064359b59d8644b8a8196653d47482',1,'DbcStatementDispatcher::DbcStatementDispatcher()']]], ['dbcstatementhandler_34',['DbcStatementHandler',['../classDbcStatementHandler.html',1,'']]], ['dbctreebuilder_35',['DbcTreeBuilder',['../classDbcTreeBuilder.html',1,'']]], - ['dbctreemodel_36',['dbctreemodel',['../classDbcTreeModel.html#a4f7214b727d812885bf077352ec9ece1',1,'DbcTreeModel::DbcTreeModel()'],['../classDbcTreeModel.html',1,'DbcTreeModel']]], - ['dbcvaluedescriptionhandler_37',['DbcValueDescriptionHandler',['../classDbcValueDescriptionHandler.html',1,'']]], - ['debian_20ubuntu_38',['GCC, gcov, and LCOV on Debian/Ubuntu',['../md_README.html#autotoc_md157',1,'']]], - ['decode_39',['decode',['../classDbcDecoder.html#a345951ffe5d943a70d3a1ea1f39e74f7',1,'DbcDecoder::Decode(const DecodeDatabase &database, const RawCanFrame &frame) const'],['../classDbcDecoder.html#ade48a94b6fcda6563bd1b2c6ff49439e',1,'DbcDecoder::Decode(const DecodeDatabase &database, const RawCanFrame &frame, const std::vector< SignalKey > &selection) const']]], - ['decode_20a_20raw_20frame_40',['Decode a raw frame',['../md_README.html#autotoc_md124',1,'']]], - ['decode_20database_41',['Build runtime decode database',['../md_README.html#autotoc_md123',1,'']]], - ['decode_20flow_42',['Decode Flow',['../md_README.html#autotoc_md116',1,'']]], - ['decode_20layer_43',['Runtime Decode Layer',['../md_README.html#autotoc_md67',1,'']]], - ['decode_20layer_20exists_44',['Why the Runtime Decode Layer Exists',['../md_README.html#autotoc_md53',1,'']]], - ['decode_20module_20documentation_45',['DBC Parser and Runtime Decode Module Documentation',['../md_README.html',1,'']]], - ['decode_20path_46',['2. Runtime decode path',['../md_README.html#autotoc_md51',1,'']]], - ['decode_20strategy_47',['Unified Decode Strategy',['../md_README.html#autotoc_md126',1,'']]], - ['decode_5fdatabase_2eh_48',['decode_database.h',['../decode__database_8h.html',1,'']]], - ['decodedatabase_49',['decodedatabase',['../classDecodeDatabase.html#a51a44cf7c3f23c98ec8781afe0097c0c',1,'DecodeDatabase::DecodeDatabase()'],['../classDecodeDatabase.html',1,'DecodeDatabase']]], - ['decodedatabase_20tt_50',['<tt>DecodeDatabase</tt>',['../md_README.html#autotoc_md80',1,'']]], - ['decodedframevalue_51',['DecodedFrameValue',['../structDecodedFrameValue.html',1,'']]], - ['decodedframevalue_20tt_52',['<tt>DecodedFrameValue</tt>',['../md_README.html#autotoc_md89',1,'']]], - ['decodedsignalvalue_53',['DecodedSignalValue',['../structDecodedSignalValue.html',1,'']]], - ['decodedsignalvalue_20tt_54',['<tt>DecodedSignalValue</tt>',['../md_README.html#autotoc_md85',1,'']]], - ['decodeframe_55',['DecodeFrame',['../structDecodeFrame.html',1,'']]], - ['decodeframe_20tt_56',['<tt>DecodeFrame</tt>',['../md_README.html#autotoc_md76',1,'']]], - ['decoder_20coverage_57',['Decoder coverage',['../md_tests_2README.html#autotoc_md162',1,'']]], - ['decoder_20layer_58',['Decoder Layer',['../md_README.html#autotoc_md82',1,'']]], - ['decodesignal_59',['DecodeSignal',['../structDecodeSignal.html',1,'']]], - ['decodesignal_20tt_60',['<tt>DecodeSignal</tt>',['../md_README.html#autotoc_md74',1,'']]], - ['defaultvalue_61',['defaultValue',['../structAttributeDefinition.html#ae0949edf67f9d4e60e4cb376a95da4f2',1,'AttributeDefinition']]], - ['definition_62',['definition',['../structDecodedSignalValue.html#a828b5ef7faefc14fbf5dbd46249bcad2',1,'DecodedSignalValue::definition'],['../structDecodedFrameValue.html#a33e971ba10030506007aa201c89f351d',1,'DecodedFrameValue::definition'],['../structPgnLookupResult.html#aa5b79b61d2aef48fd6ae070cf5f901c1',1,'PgnLookupResult::definition'],['../md_README.html#autotoc_md100',1,'Frame definition'],['../md_README.html#autotoc_md104',1,'Signal definition']]], - ['definition_20lifetime_20and_20reload_20coverage_63',['Definition lifetime and reload coverage',['../md_tests_2README.html#autotoc_md166',1,'']]], - ['definition_20ownership_20and_20reload_64',['Definition ownership and reload',['../md_README.html#autotoc_md90',1,'']]], - ['definitions_65',['Long signal definitions',['../md_tests_2README.html#autotoc_md170',1,'']]], - ['demo_66',['Build and run the demo',['../md_README.html#autotoc_md151',1,'']]], - ['descriptions_67',['descriptions',['../structPendingValueDescriptions.html#a10ecbaeaed2cc3ac82fada73e7e041dd',1,'PendingValueDescriptions']]], - ['descriptions_20tt_20val_5f_20tt_68',['Signal value descriptions (<tt>VAL_</tt>)',['../md_README.html#autotoc_md136',1,'']]], - ['diagnostic_69',['diagnostic',['../classDbcDiagnosticError.html#ab3965082f01d66b6d5c20525af44235d',1,'DbcDiagnosticError']]], - ['diagnostics_70',['diagnostics',['../structDbcParseResult.html#a3a1987b4381bb1e0a34dc09e31483679',1,'DbcParseResult::diagnostics'],['../md_README.html#autotoc_md96',1,'Structured diagnostics']]], - ['dispatch_71',['Dispatch',['../classDbcStatementDispatcher.html#a495c2d5c7ebb8103b1987cdd87c142ed',1,'DbcStatementDispatcher']]], - ['dlc_72',['dlc',['../structDecodeFrame.html#a51111ac13988be372d5673d12cf3e060',1,'DecodeFrame::dlc'],['../structFrameInfo.html#aa2b198ea0f55e560c123d35e7b3ecab9',1,'FrameInfo::dlc']]], - ['documentation_73',['documentation',['../md_README.html',1,'DBC Parser and Runtime Decode Module Documentation'],['../md_changelog.html#autotoc_md42',1,'Documentation'],['../md_README.html#autotoc_md152',1,'Generate API documentation']]], - ['documentation_20and_20maintenance_74',['documentation and maintenance',['../md_changelog.html#autotoc_md45',1,'Documentation and maintenance'],['../md_changelog.html#autotoc_md37',1,'Documentation and maintenance']]] + ['dbctreemodel_36',['dbctreemodel',['../classDbcTreeModel.html',1,'DbcTreeModel'],['../classDbcTreeModel.html#a4f7214b727d812885bf077352ec9ece1',1,'DbcTreeModel::DbcTreeModel()']]], + ['dbcunsupportedsignaltypehandler_37',['DbcUnsupportedSignalTypeHandler',['../classDbcUnsupportedSignalTypeHandler.html',1,'']]], + ['dbcvaluedescriptionhandler_38',['DbcValueDescriptionHandler',['../classDbcValueDescriptionHandler.html',1,'']]], + ['debian_20ubuntu_39',['GCC, gcov, and LCOV on Debian/Ubuntu',['../md_README.html#autotoc_md157',1,'']]], + ['decode_40',['decode',['../classDbcDecoder.html#ade48a94b6fcda6563bd1b2c6ff49439e',1,'DbcDecoder::Decode(const DecodeDatabase &database, const RawCanFrame &frame, const std::vector< SignalKey > &selection) const'],['../classDbcDecoder.html#a345951ffe5d943a70d3a1ea1f39e74f7',1,'DbcDecoder::Decode(const DecodeDatabase &database, const RawCanFrame &frame) const']]], + ['decode_20a_20raw_20frame_41',['Decode a raw frame',['../md_README.html#autotoc_md124',1,'']]], + ['decode_20database_42',['Build runtime decode database',['../md_README.html#autotoc_md123',1,'']]], + ['decode_20flow_43',['Decode Flow',['../md_README.html#autotoc_md116',1,'']]], + ['decode_20layer_44',['Runtime Decode Layer',['../md_README.html#autotoc_md67',1,'']]], + ['decode_20layer_20exists_45',['Why the Runtime Decode Layer Exists',['../md_README.html#autotoc_md53',1,'']]], + ['decode_20module_20documentation_46',['DBC Parser and Runtime Decode Module Documentation',['../md_README.html',1,'']]], + ['decode_20path_47',['2. Runtime decode path',['../md_README.html#autotoc_md51',1,'']]], + ['decode_20strategy_48',['Unified Decode Strategy',['../md_README.html#autotoc_md126',1,'']]], + ['decode_5fdatabase_2eh_49',['decode_database.h',['../decode__database_8h.html',1,'']]], + ['decodedatabase_50',['decodedatabase',['../classDecodeDatabase.html#a51a44cf7c3f23c98ec8781afe0097c0c',1,'DecodeDatabase::DecodeDatabase()'],['../classDecodeDatabase.html',1,'DecodeDatabase']]], + ['decodedatabase_20tt_51',['<tt>DecodeDatabase</tt>',['../md_README.html#autotoc_md80',1,'']]], + ['decodedframevalue_52',['DecodedFrameValue',['../structDecodedFrameValue.html',1,'']]], + ['decodedframevalue_20tt_53',['<tt>DecodedFrameValue</tt>',['../md_README.html#autotoc_md89',1,'']]], + ['decodedsignalvalue_54',['DecodedSignalValue',['../structDecodedSignalValue.html',1,'']]], + ['decodedsignalvalue_20tt_55',['<tt>DecodedSignalValue</tt>',['../md_README.html#autotoc_md85',1,'']]], + ['decodeframe_56',['DecodeFrame',['../structDecodeFrame.html',1,'']]], + ['decodeframe_20tt_57',['<tt>DecodeFrame</tt>',['../md_README.html#autotoc_md76',1,'']]], + ['decoder_20coverage_58',['Decoder coverage',['../md_tests_2README.html#autotoc_md162',1,'']]], + ['decoder_20layer_59',['Decoder Layer',['../md_README.html#autotoc_md82',1,'']]], + ['decodesignal_60',['DecodeSignal',['../structDecodeSignal.html',1,'']]], + ['decodesignal_20tt_61',['<tt>DecodeSignal</tt>',['../md_README.html#autotoc_md74',1,'']]], + ['defaultvalue_62',['defaultValue',['../structAttributeDefinition.html#ae0949edf67f9d4e60e4cb376a95da4f2',1,'AttributeDefinition']]], + ['definition_63',['definition',['../structDecodedSignalValue.html#a828b5ef7faefc14fbf5dbd46249bcad2',1,'DecodedSignalValue::definition'],['../structDecodedFrameValue.html#a33e971ba10030506007aa201c89f351d',1,'DecodedFrameValue::definition'],['../structPgnLookupResult.html#aa5b79b61d2aef48fd6ae070cf5f901c1',1,'PgnLookupResult::definition'],['../md_README.html#autotoc_md100',1,'Frame definition'],['../md_README.html#autotoc_md104',1,'Signal definition']]], + ['definition_20lifetime_20and_20reload_20coverage_64',['Definition lifetime and reload coverage',['../md_tests_2README.html#autotoc_md166',1,'']]], + ['definition_20ownership_20and_20reload_65',['Definition ownership and reload',['../md_README.html#autotoc_md90',1,'']]], + ['definitions_66',['Long signal definitions',['../md_tests_2README.html#autotoc_md171',1,'']]], + ['demo_67',['Build and run the demo',['../md_README.html#autotoc_md151',1,'']]], + ['descriptions_68',['descriptions',['../structPendingValueDescriptions.html#a10ecbaeaed2cc3ac82fada73e7e041dd',1,'PendingValueDescriptions']]], + ['descriptions_20tt_20val_5f_20tt_69',['Signal value descriptions (<tt>VAL_</tt>)',['../md_README.html#autotoc_md136',1,'']]], + ['diagnostic_70',['diagnostic',['../classDbcDiagnosticError.html#ab3965082f01d66b6d5c20525af44235d',1,'DbcDiagnosticError']]], + ['diagnostics_71',['diagnostics',['../structDbcParseResult.html#a3a1987b4381bb1e0a34dc09e31483679',1,'DbcParseResult::diagnostics'],['../md_README.html#autotoc_md96',1,'Structured diagnostics']]], + ['dispatch_72',['Dispatch',['../classDbcStatementDispatcher.html#a495c2d5c7ebb8103b1987cdd87c142ed',1,'DbcStatementDispatcher']]], + ['dlc_73',['dlc',['../structDecodeFrame.html#a51111ac13988be372d5673d12cf3e060',1,'DecodeFrame::dlc'],['../structFrameInfo.html#aa2b198ea0f55e560c123d35e7b3ecab9',1,'FrameInfo::dlc']]], + ['documentation_74',['documentation',['../md_README.html',1,'DBC Parser and Runtime Decode Module Documentation'],['../md_changelog.html#autotoc_md42',1,'Documentation'],['../md_README.html#autotoc_md152',1,'Generate API documentation']]], + ['documentation_20and_20maintenance_75',['documentation and maintenance',['../md_changelog.html#autotoc_md45',1,'Documentation and maintenance'],['../md_changelog.html#autotoc_md37',1,'Documentation and maintenance']]] ]; diff --git a/docs/html/search/all_9.js b/docs/html/search/all_9.js index 0a6b693..2a3a87f 100644 --- a/docs/html/search/all_9.js +++ b/docs/html/search/all_9.js @@ -9,6 +9,6 @@ var searchData= ['example_20usage_6',['Example Usage',['../md_README.html#autotoc_md120',1,'']]], ['examples_2fmain_2ecpp_7',['main.cpp',['../examples_2main_8cpp.html',1,'']]], ['exists_8',['Why the Runtime Decode Layer Exists',['../md_README.html#autotoc_md53',1,'']]], - ['extended_20validation_9',['Extended validation',['../md_tests_2README.html#autotoc_md171',1,'']]], + ['extended_20validation_9',['Extended validation',['../md_tests_2README.html#autotoc_md172',1,'']]], ['extraction_10',['Intel and Motorola Extraction',['../md_README.html#autotoc_md118',1,'']]] ]; diff --git a/docs/html/search/all_c.js b/docs/html/search/all_c.js index 2401cb4..d83db1d 100644 --- a/docs/html/search/all_c.js +++ b/docs/html/search/all_c.js @@ -1,6 +1,6 @@ var searchData= [ - ['handle_0',['handle',['../classDbcStatementHandler.html#a3de86e1e27e9bcb0fe6f1460a8ba5739',1,'DbcStatementHandler::Handle()'],['../classDbcFrameHandler.html#a0d3f43bbcc5e191460bc31827e74ce89',1,'DbcFrameHandler::Handle()'],['../classDbcSignalHandler.html#ad7d891ec1414fb645f85ccd410f2737b',1,'DbcSignalHandler::Handle()'],['../classDbcCommentHandler.html#aa4fce77b585a441d251d8535ef7a51a6',1,'DbcCommentHandler::Handle()'],['../classDbcAttributeHandler.html#a397f23259b7ff79abb4b5141489caa83',1,'DbcAttributeHandler::Handle()'],['../classDbcValueDescriptionHandler.html#a653e13364f396b54244edc2c0fdd831a',1,'DbcValueDescriptionHandler::Handle()'],['../classDbcMultiplexingHandler.html#a16f9e9f898640200477d2dcc8bab442e',1,'DbcMultiplexingHandler::Handle()'],['../classDbcHeaderHandler.html#a1e43a9dcf157f4d4418032d9e9711693',1,'DbcHeaderHandler::Handle()']]], + ['handle_0',['handle',['../classDbcStatementHandler.html#a3de86e1e27e9bcb0fe6f1460a8ba5739',1,'DbcStatementHandler::Handle()'],['../classDbcFrameHandler.html#a0d3f43bbcc5e191460bc31827e74ce89',1,'DbcFrameHandler::Handle()'],['../classDbcSignalHandler.html#ad7d891ec1414fb645f85ccd410f2737b',1,'DbcSignalHandler::Handle()'],['../classDbcCommentHandler.html#aa4fce77b585a441d251d8535ef7a51a6',1,'DbcCommentHandler::Handle()'],['../classDbcAttributeHandler.html#a397f23259b7ff79abb4b5141489caa83',1,'DbcAttributeHandler::Handle()'],['../classDbcValueDescriptionHandler.html#a653e13364f396b54244edc2c0fdd831a',1,'DbcValueDescriptionHandler::Handle()'],['../classDbcMultiplexingHandler.html#a16f9e9f898640200477d2dcc8bab442e',1,'DbcMultiplexingHandler::Handle()'],['../classDbcUnsupportedSignalTypeHandler.html#ae4132fcdbb134d51143133c1fb83a9df',1,'DbcUnsupportedSignalTypeHandler::Handle()'],['../classDbcHeaderHandler.html#a1e43a9dcf157f4d4418032d9e9711693',1,'DbcHeaderHandler::Handle()']]], ['handler_20factory_1',['Statement Handler Factory',['../md_README.html#autotoc_md134',1,'']]], ['haserrors_2',['HasErrors',['../structDbcParseResult.html#a4da8641de3f6b55c7447c4421e05d20d',1,'DbcParseResult']]], ['haspgn_3',['haspgn',['../structDecodeFrame.html#a2551102670c4a540fced4c71da0b369d',1,'DecodeFrame::hasPgn'],['../structFrameInfo.html#a4737a5420213ad4304724e39f3a4e55d',1,'FrameInfo::hasPgn']]], diff --git a/docs/html/search/classes_1.js b/docs/html/search/classes_1.js index 78daa12..dd58d58 100644 --- a/docs/html/search/classes_1.js +++ b/docs/html/search/classes_1.js @@ -19,10 +19,11 @@ var searchData= ['dbcstatementhandler_16',['DbcStatementHandler',['../classDbcStatementHandler.html',1,'']]], ['dbctreebuilder_17',['DbcTreeBuilder',['../classDbcTreeBuilder.html',1,'']]], ['dbctreemodel_18',['DbcTreeModel',['../classDbcTreeModel.html',1,'']]], - ['dbcvaluedescriptionhandler_19',['DbcValueDescriptionHandler',['../classDbcValueDescriptionHandler.html',1,'']]], - ['decodedatabase_20',['DecodeDatabase',['../classDecodeDatabase.html',1,'']]], - ['decodedframevalue_21',['DecodedFrameValue',['../structDecodedFrameValue.html',1,'']]], - ['decodedsignalvalue_22',['DecodedSignalValue',['../structDecodedSignalValue.html',1,'']]], - ['decodeframe_23',['DecodeFrame',['../structDecodeFrame.html',1,'']]], - ['decodesignal_24',['DecodeSignal',['../structDecodeSignal.html',1,'']]] + ['dbcunsupportedsignaltypehandler_19',['DbcUnsupportedSignalTypeHandler',['../classDbcUnsupportedSignalTypeHandler.html',1,'']]], + ['dbcvaluedescriptionhandler_20',['DbcValueDescriptionHandler',['../classDbcValueDescriptionHandler.html',1,'']]], + ['decodedatabase_21',['DecodeDatabase',['../classDecodeDatabase.html',1,'']]], + ['decodedframevalue_22',['DecodedFrameValue',['../structDecodedFrameValue.html',1,'']]], + ['decodedsignalvalue_23',['DecodedSignalValue',['../structDecodedSignalValue.html',1,'']]], + ['decodeframe_24',['DecodeFrame',['../structDecodeFrame.html',1,'']]], + ['decodesignal_25',['DecodeSignal',['../structDecodeSignal.html',1,'']]] ]; diff --git a/docs/html/search/functions_6.js b/docs/html/search/functions_6.js index 62b5854..e3208a1 100644 --- a/docs/html/search/functions_6.js +++ b/docs/html/search/functions_6.js @@ -1,6 +1,6 @@ var searchData= [ - ['handle_0',['handle',['../classDbcStatementHandler.html#a3de86e1e27e9bcb0fe6f1460a8ba5739',1,'DbcStatementHandler::Handle()'],['../classDbcFrameHandler.html#a0d3f43bbcc5e191460bc31827e74ce89',1,'DbcFrameHandler::Handle()'],['../classDbcSignalHandler.html#ad7d891ec1414fb645f85ccd410f2737b',1,'DbcSignalHandler::Handle()'],['../classDbcCommentHandler.html#aa4fce77b585a441d251d8535ef7a51a6',1,'DbcCommentHandler::Handle()'],['../classDbcAttributeHandler.html#a397f23259b7ff79abb4b5141489caa83',1,'DbcAttributeHandler::Handle()'],['../classDbcValueDescriptionHandler.html#a653e13364f396b54244edc2c0fdd831a',1,'DbcValueDescriptionHandler::Handle()'],['../classDbcMultiplexingHandler.html#a16f9e9f898640200477d2dcc8bab442e',1,'DbcMultiplexingHandler::Handle()'],['../classDbcHeaderHandler.html#a1e43a9dcf157f4d4418032d9e9711693',1,'DbcHeaderHandler::Handle()']]], + ['handle_0',['handle',['../classDbcStatementHandler.html#a3de86e1e27e9bcb0fe6f1460a8ba5739',1,'DbcStatementHandler::Handle()'],['../classDbcFrameHandler.html#a0d3f43bbcc5e191460bc31827e74ce89',1,'DbcFrameHandler::Handle()'],['../classDbcSignalHandler.html#ad7d891ec1414fb645f85ccd410f2737b',1,'DbcSignalHandler::Handle()'],['../classDbcCommentHandler.html#aa4fce77b585a441d251d8535ef7a51a6',1,'DbcCommentHandler::Handle()'],['../classDbcAttributeHandler.html#a397f23259b7ff79abb4b5141489caa83',1,'DbcAttributeHandler::Handle()'],['../classDbcValueDescriptionHandler.html#a653e13364f396b54244edc2c0fdd831a',1,'DbcValueDescriptionHandler::Handle()'],['../classDbcMultiplexingHandler.html#a16f9e9f898640200477d2dcc8bab442e',1,'DbcMultiplexingHandler::Handle()'],['../classDbcUnsupportedSignalTypeHandler.html#ae4132fcdbb134d51143133c1fb83a9df',1,'DbcUnsupportedSignalTypeHandler::Handle()'],['../classDbcHeaderHandler.html#a1e43a9dcf157f4d4418032d9e9711693',1,'DbcHeaderHandler::Handle()']]], ['haserrors_1',['HasErrors',['../structDbcParseResult.html#a4da8641de3f6b55c7447c4421e05d20d',1,'DbcParseResult']]], ['headerdata_2',['headerData',['../classDbcTreeModel.html#aad5834e8edb00808cbfa498ea8c63fe9',1,'DbcTreeModel']]] ]; diff --git a/docs/html/search/functions_b.js b/docs/html/search/functions_b.js index 539f29d..be5983e 100644 --- a/docs/html/search/functions_b.js +++ b/docs/html/search/functions_b.js @@ -1,6 +1,6 @@ var searchData= [ - ['registertype_0',['registertype',['../classDbcStatementDispatcher.html#a2c6e9a614ed747275d43bcccc50d0b2d',1,'DbcStatementDispatcher::RegisterType()'],['../classTypeFactory.html#ad6d85795285ee21193ac03e1b24762d3',1,'TypeFactory::registerType()']]], + ['registertype_0',['registertype',['../classTypeFactory.html#ad6d85795285ee21193ac03e1b24762d3',1,'TypeFactory::registerType()'],['../classDbcStatementDispatcher.html#a2c6e9a614ed747275d43bcccc50d0b2d',1,'DbcStatementDispatcher::RegisterType()']]], ['resolveattributes_1',['ResolveAttributes',['../structDbcParseContext.html#a1ac7f4a54550aaddef08749806f87001',1,'DbcParseContext']]], ['resolvecomments_2',['ResolveComments',['../structDbcParseContext.html#aeae343c57a244634489a0e0b960155d7',1,'DbcParseContext']]], ['resolvevaluedescriptions_3',['ResolveValueDescriptions',['../structDbcParseContext.html#ab5552dad944fde7fa6add6464ddb5369',1,'DbcParseContext']]], diff --git a/docs/html/structDbcDatabase.html b/docs/html/structDbcDatabase.html index 952d29f..230c8cd 100644 --- a/docs/html/structDbcDatabase.html +++ b/docs/html/structDbcDatabase.html @@ -133,7 +133,7 @@ Public Attributes

      Definition at line 23 of file dbc_database.h.

      -

      Referenced by DbcTreeBuilder::Build(), DbcDecodeBuilder::Build(), DbcFrameHandler::Handle(), DbcParseContext::ResolveAttributes(), and DbcParseContext::ValidateMultiplexing().

      +

      Referenced by DbcTreeBuilder::Build(), DbcDecodeBuilder::Build(), DbcFrameHandler::Handle(), DbcParseContext::ResolveAttributes(), and DbcParseContext::ValidateMultiplexing().

      diff --git a/docs/html/structDbcDiagnostic.html b/docs/html/structDbcDiagnostic.html index 2219948..fee725f 100644 --- a/docs/html/structDbcDiagnostic.html +++ b/docs/html/structDbcDiagnostic.html @@ -118,7 +118,7 @@ Public Attributes

      Detailed Description

      Diagnostic message and physical source location.

      -

      Definition at line 29 of file dbc_diagnostic.h.

      +

      Definition at line 30 of file dbc_diagnostic.h.

      Member Data Documentation

      ◆ severity

      @@ -133,7 +133,7 @@ Public Attributes

      Message severity.

      -

      Definition at line 30 of file dbc_diagnostic.h.

      +

      Definition at line 31 of file dbc_diagnostic.h.

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

      Stable diagnostic category.

      -

      Definition at line 31 of file dbc_diagnostic.h.

      +

      Definition at line 32 of file dbc_diagnostic.h.

      @@ -167,7 +167,7 @@ Public Attributes

      Input file path.

      -

      Definition at line 32 of file dbc_diagnostic.h.

      +

      Definition at line 33 of file dbc_diagnostic.h.

      @@ -184,7 +184,7 @@ Public Attributes

      One-based physical line; zero for file-level errors.

      -

      Definition at line 33 of file dbc_diagnostic.h.

      +

      Definition at line 34 of file dbc_diagnostic.h.

      @@ -201,7 +201,7 @@ Public Attributes

      Human-readable diagnostic details.

      -

      Definition at line 34 of file dbc_diagnostic.h.

      +

      Definition at line 35 of file dbc_diagnostic.h.

      diff --git a/docs/html/structDbcParseContext.html b/docs/html/structDbcParseContext.html index 254e54a..3b8c062 100644 --- a/docs/html/structDbcParseContext.html +++ b/docs/html/structDbcParseContext.html @@ -159,7 +159,7 @@ Public Attributes
      -

      Definition at line 517 of file dbc_statement_handler.cpp.

      +

      Definition at line 522 of file dbc_statement_handler.cpp.

      @@ -181,7 +181,7 @@ Public Attributes

      Validate selectors, branch ranges, and signal overlap.

      -

      Definition at line 567 of file dbc_statement_handler.cpp.

      +

      Definition at line 572 of file dbc_statement_handler.cpp.

      References database, DbcDatabase::frames, SignalInfo::isSigned, SignalInfo::multiplexing, and signalLines.

      @@ -263,7 +263,7 @@ Public Attributes

      Resolve deferred raw descriptions against unique targets.

      -

      Definition at line 468 of file dbc_statement_handler.cpp.

      +

      Definition at line 473 of file dbc_statement_handler.cpp.

      References database, pendingValues, and SignalInfo::valueDescriptions.

      @@ -285,7 +285,7 @@ Public Attributes

      Definition at line 61 of file dbc_statement_handler.h.

      -

      Referenced by DbcFrameHandler::Handle(), ResolveAttributes(), ResolveComments(), ResolveValueDescriptions(), and ValidateMultiplexing().

      +

      Referenced by DbcFrameHandler::Handle(), ResolveAttributes(), ResolveComments(), ResolveValueDescriptions(), and ValidateMultiplexing().

      @@ -342,7 +342,7 @@ Public Attributes

      Definition at line 64 of file dbc_statement_handler.h.

      -

      Referenced by DbcSignalHandler::Handle(), DbcCommentHandler::Handle(), DbcAttributeHandler::Handle(), DbcValueDescriptionHandler::Handle(), and DbcMultiplexingHandler::Handle().

      +

      Referenced by DbcSignalHandler::Handle(), DbcCommentHandler::Handle(), DbcAttributeHandler::Handle(), DbcValueDescriptionHandler::Handle(), and DbcMultiplexingHandler::Handle().

      @@ -361,7 +361,7 @@ Public Attributes

      Definition at line 65 of file dbc_statement_handler.h.

      -

      Referenced by DbcValueDescriptionHandler::Handle(), and ResolveValueDescriptions().

      +

      Referenced by DbcValueDescriptionHandler::Handle(), and ResolveValueDescriptions().

      @@ -396,7 +396,7 @@ Public Attributes

      Definition at line 70 of file dbc_statement_handler.h.

      -

      Referenced by DbcFrameHandler::Handle(), DbcSignalHandler::Handle(), and ValidateMultiplexing().

      +

      Referenced by DbcFrameHandler::Handle(), DbcSignalHandler::Handle(), and ValidateMultiplexing().

      diff --git a/docs/html/structDbcParseResult.html b/docs/html/structDbcParseResult.html index 884644c..f675e04 100644 --- a/docs/html/structDbcParseResult.html +++ b/docs/html/structDbcParseResult.html @@ -119,7 +119,7 @@ Public Attributes

      Detailed Description

      Parsed database and diagnostics; the database is empty on error.

      -

      Definition at line 38 of file dbc_diagnostic.h.

      +

      Definition at line 39 of file dbc_diagnostic.h.

      Member Function Documentation

      ◆ HasErrors()

      @@ -146,9 +146,11 @@ Public Attributes

      Return whether any diagnostic has error severity.

      -

      Definition at line 43 of file dbc_diagnostic.h.

      +

      Definition at line 44 of file dbc_diagnostic.h.

      -

      References diagnostics.

      +

      References diagnostics.

      + +

      Referenced by main().

      @@ -166,9 +168,9 @@ Public Attributes

      Parsed content, published only on success.

      -

      Definition at line 39 of file dbc_diagnostic.h.

      +

      Definition at line 40 of file dbc_diagnostic.h.

      -

      Referenced by DbcParser::ParseFile(), and DbcParser::ParseFileWithDiagnostics().

      +

      Referenced by main(), DbcParser::ParseFile(), and DbcParser::ParseFileWithDiagnostics().

      @@ -185,9 +187,9 @@ Public Attributes

      Warnings and the first error, if any.

      -

      Definition at line 40 of file dbc_diagnostic.h.

      +

      Definition at line 41 of file dbc_diagnostic.h.

      -

      Referenced by HasErrors(), DbcParser::ParseFile(), and DbcParser::ParseFileWithDiagnostics().

      +

      Referenced by HasErrors(), main(), DbcParser::ParseFile(), and DbcParser::ParseFileWithDiagnostics().

      diff --git a/docs/html/structDbcParserOptions.html b/docs/html/structDbcParserOptions.html index 067b0a4..bf8c719 100644 --- a/docs/html/structDbcParserOptions.html +++ b/docs/html/structDbcParserOptions.html @@ -124,7 +124,7 @@ Public Attributes

      Definition at line 21 of file dbc_parser.h.

      -

      Referenced by DbcParser::ParseFileWithDiagnostics().

      +

      Referenced by DbcParser::ParseFileWithDiagnostics().

      diff --git a/docs/html/structPendingValueDescriptions.html b/docs/html/structPendingValueDescriptions.html index 1a0e24e..919ad39 100644 --- a/docs/html/structPendingValueDescriptions.html +++ b/docs/html/structPendingValueDescriptions.html @@ -135,7 +135,7 @@ Public Attributes

      Definition at line 14 of file dbc_statement_handler.h.

      -

      Referenced by DbcValueDescriptionHandler::Handle().

      +

      Referenced by DbcValueDescriptionHandler::Handle().

      @@ -154,7 +154,7 @@ Public Attributes

      Definition at line 15 of file dbc_statement_handler.h.

      -

      Referenced by DbcValueDescriptionHandler::Handle().

      +

      Referenced by DbcValueDescriptionHandler::Handle().

      @@ -173,7 +173,7 @@ Public Attributes

      Definition at line 16 of file dbc_statement_handler.h.

      -

      Referenced by DbcValueDescriptionHandler::Handle().

      +

      Referenced by DbcValueDescriptionHandler::Handle().

      @@ -192,7 +192,7 @@ Public Attributes

      Definition at line 17 of file dbc_statement_handler.h.

      -

      Referenced by DbcValueDescriptionHandler::Handle().

      +

      Referenced by DbcValueDescriptionHandler::Handle().

      @@ -211,7 +211,7 @@ Public Attributes

      Definition at line 18 of file dbc_statement_handler.h.

      -

      Referenced by DbcValueDescriptionHandler::Handle().

      +

      Referenced by DbcValueDescriptionHandler::Handle().

      diff --git a/docs/html/structSignalInfo.html b/docs/html/structSignalInfo.html index c2aab15..e701400 100644 --- a/docs/html/structSignalInfo.html +++ b/docs/html/structSignalInfo.html @@ -155,7 +155,7 @@ Public Attributes

      Definition at line 26 of file signal_info.h.

      -

      Referenced by DbcParseContext::ValidateMultiplexing().

      +

      Referenced by DbcParseContext::ValidateMultiplexing().

      @@ -250,7 +250,7 @@ Public Attributes

      Definition at line 31 of file signal_info.h.

      -

      Referenced by DbcTreeModel::data(), and DbcParseContext::ValidateMultiplexing().

      +

      Referenced by DbcTreeModel::data(), and DbcParseContext::ValidateMultiplexing().

      @@ -373,7 +373,7 @@ Public Attributes

      Definition at line 38 of file signal_info.h.

      -

      Referenced by DbcTreeModel::data(), and DbcParseContext::ResolveValueDescriptions().

      +

      Referenced by DbcTreeModel::data(), and DbcParseContext::ResolveValueDescriptions().

      diff --git a/examples/main.cpp b/examples/main.cpp index b04607f..11fcccb 100644 --- a/examples/main.cpp +++ b/examples/main.cpp @@ -105,8 +105,16 @@ int main(int argc, char *argv[]) { } try { - DbcParser parser; - DbcDatabase database = parser.ParseFile(argv[1]); + const DbcParser parser; + const DbcParseResult parsed = parser.ParseFileWithDiagnostics(argv[1]); + for (const DbcDiagnostic &diagnostic: parsed.diagnostics) { + std::cerr << diagnostic.filePath << ":" << diagnostic.lineNumber << ": " + << (diagnostic.severity == DbcDiagnosticSeverity::Error ? "error: " : "warning: ") + << diagnostic.message << "\n"; + } + if (parsed.HasErrors()) + return 2; + const DbcDatabase &database = parsed.database; DbcTreeBuilder treeBuilder; std::unique_ptr root = treeBuilder.Build(database); diff --git a/tests/README.md b/tests/README.md index 18b8671..0639370 100644 --- a/tests/README.md +++ b/tests/README.md @@ -99,7 +99,7 @@ line numbers for skipped syntax and immediate/deferred errors; warnings precedin 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 13 unsupported-statement variants, all namespace +The diagnostic audit also covers 11 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. @@ -295,6 +295,30 @@ Attribute changes were verified with `qmake6 ../dbc.pro`, `make -j4`, and passed. The standalone C++17 tree test also passed. The existing named comma-decimal C locale test skipped because the locale is not installed. +## Unsupported signal value types + +`unsupportedSignalValueTypes` verifies that both parser APIs reject `SIG_VALTYPE_` +declarations with `UnsupportedSignalType`, the original file path and physical +line, and an empty database. Cases include float32/float64, declarations before +signal definitions, extended IDs, tabs/CRLF, no final newline, type 0, unknown +types, and malformed declarations. Prior warnings are retained; `SIG_VALTYPE_` +symbols inside `NS_` lists remain quiet. + +The pipeline suite checks the IEEE 754 bytes for float32 and float64 `1.0`. +Adding a type declaration must prevent publication of an integer definition, +so building from the error result cannot produce a valid decoded signal. The +same payloads still decode to exact integers when defined only by supported +`SG_` statements. New cases are included in the existing qmake targets. + +Verified with Qt 6.8.2 in separate Release and ASan/UBSan builds with +`CONFIG+=dbc_qt`: each passed 2,420 core QtTest checks, seven model checks, and +the qmake tree test. One named-locale case skipped because its locale is +unavailable. ASan/UBSan used fatal errors and `detect_leaks=0` because of the +previously documented ptrace restriction. The separately built demo was checked +with float32 and float64 declarations (exit 2, source-located error, no decode +output), a supported integer definition (exit 0), and an unknown extension +(exit 0 with a visible warning). + ## Long signal definitions `longSignalDefinitions` reproduces the former stack overflow with a 50,000-character diff --git a/tests/dbc_parser_test.cpp b/tests/dbc_parser_test.cpp index 55bddc3..085f82a 100644 --- a/tests/dbc_parser_test.cpp +++ b/tests/dbc_parser_test.cpp @@ -99,6 +99,8 @@ private Q_SLOTS: void diagnostics(); void diagnosticErrors(); void diagnosticUnsupportedStatements(); + void unsupportedSignalValueTypes_data(); + void unsupportedSignalValueTypes(); void diagnosticNamespaceBoundaries(); void diagnosticFormatting(); void multiplexing(); @@ -427,7 +429,7 @@ void DbcParserTest::diagnostics() { "BU_: ECU\n" "BO_ 1 Frame: 1 ECU\n" "SG_ Value : 0|8@1+ (1,0) [0|255] \"\" ECU\n" - "SIG_VALTYPE_ 1 Value : 1;\n" + "SIG_TYPE_REF_ 1 Value : Custom;\n" "BA_REL_ \"SignalType\" SG_ 1 Value 1;\n" "VENDOR_EXTENSION_ data;\n"); const DbcParseResult result = DbcParser().ParseFileWithDiagnostics(fixture.path.string()); @@ -564,7 +566,6 @@ void DbcParserTest::diagnosticUnsupportedStatements() { "BO_ 1 Frame: 1 ECU\nSG_ Value : 0|8@1+ (2,10) [10|520] \"V\" ECU\n" "CM_ SG_ 1 Value \"Signal comment\";\nVAL_ 1 Value 1 \"On\";\n"; const std::vector statements = { - "SIG_VALTYPE_ 1 Value : 1;", "SIG_VALTYPE_ 1 Value : 2;", "BA_REL_ \"SignalType\" SG_ 1 Value 1;", "BA_DEF_REL_ BU_SG_REL_ \"Relation\" INT 0 1;", "BA_REL_ \"Relation\" BU_SG_REL_ ECU SG_ 1 Value 1;", @@ -602,6 +603,51 @@ void DbcParserTest::diagnosticUnsupportedStatements() { } } +void DbcParserTest::unsupportedSignalValueTypes_data() { + QTest::addColumn("statement"); + QTest::addColumn("beforeDefinitions"); + QTest::newRow("float32") << std::string("SIG_VALTYPE_ 1 Value : 1;") << false; + QTest::newRow("float64") << std::string("SIG_VALTYPE_ 1 Value : 2;") << false; + QTest::newRow("float32-before-definitions") << std::string("SIG_VALTYPE_ 1 Value : 1;") << true; + QTest::newRow("float64-before-definitions") << std::string("SIG_VALTYPE_ 1 Value : 2;") << true; + QTest::newRow("extended-frame") << std::string("SIG_VALTYPE_ 2147483649 Value : 1;") << false; + QTest::newRow("tabs-crlf") << std::string("\tSIG_VALTYPE_\t1\tValue : 2;\r") << false; + QTest::newRow("integer-declaration") << std::string("SIG_VALTYPE_ 1 Value : 0;") << false; + QTest::newRow("unknown-type") << std::string("SIG_VALTYPE_ 1 Value : 3;") << false; + QTest::newRow("malformed") << std::string("SIG_VALTYPE_") << false; +} + +void DbcParserTest::unsupportedSignalValueTypes() { + QFETCH(std::string, statement); + QFETCH(bool, beforeDefinitions); + const std::string definitions = "BO_ 1 Frame: 8 ECU\n" + "SG_ Value : 0|64@1+ (1,0) [0|100] \"\" ECU\n"; + Fixture fixture; + fixture.Write("VERSION \"\"\nNS_:\n\tSIG_VALTYPE_\nBS_:\nVENDOR_EXTENSION_ data;\n" + + (beforeDefinitions ? statement + "\n" + definitions : definitions + statement)); + const std::size_t expectedLine = beforeDefinitions ? 6U : 8U; + const DbcParser parser; + const DbcParseResult result = parser.ParseFileWithDiagnostics(fixture.path.string()); + QVERIFY(result.HasErrors()); + QVERIFY(result.database.frames.empty()); + QCOMPARE(result.diagnostics.size(), std::size_t(2)); + QCOMPARE(result.diagnostics[0].severity, DbcDiagnosticSeverity::Warning); + QCOMPARE(result.diagnostics[0].code, DbcDiagnosticCode::UnsupportedStatement); + const DbcDiagnostic &error = result.diagnostics[1]; + QCOMPARE(error.severity, DbcDiagnosticSeverity::Error); + QCOMPARE(error.code, DbcDiagnosticCode::UnsupportedSignalType); + QCOMPARE(error.lineNumber, expectedLine); + QCOMPARE(error.filePath, fixture.path.string()); + QVERIFY(error.message.find("SIG_VALTYPE_") != std::string::npos); + try { + parser.ParseFile(fixture.path.string()); + QFAIL("ParseFile must reject explicit signal value types"); + } catch (const std::runtime_error &exception) { + QCOMPARE(std::string(exception.what()), fixture.path.string() + ":" + + std::to_string(expectedLine) + ": " + error.message); + } +} + void DbcParserTest::diagnosticNamespaceBoundaries() { Fixture fixture; const std::vector endings = { @@ -609,13 +655,13 @@ void DbcParserTest::diagnosticNamespaceBoundaries() { "BO_ 1 Frame: 1 ECU\n", "VENDOR_EXTENSION_ data;\n" }; for (const std::string &ending : endings) { - fixture.Write("NS_ :\n\tSG_MUL_VAL_\n\nBA_\n" + ending + "SIG_VALTYPE_\n"); + fixture.Write("NS_ :\n\tSG_MUL_VAL_\n\nBA_\n" + ending + "SIG_TYPE_REF_\n"); const DbcParseResult result = DbcParser().ParseFileWithDiagnostics(fixture.path.string()); CHECK(!result.HasErrors(), ending); const std::size_t expectedWarnings = ending.find("VENDOR_") == 0U ? 2U : 1U; QCOMPARE(result.diagnostics.size(), expectedWarnings); QCOMPARE(result.diagnostics.back().lineNumber, std::size_t(6U)); - CHECK(result.diagnostics.back().message.find("SIG_VALTYPE_") != std::string::npos, + CHECK(result.diagnostics.back().message.find("SIG_TYPE_REF_") != std::string::npos, "Unsupported keyword outside NS_ must produce a warning"); } for (const std::string &definition : {std::string("BO_"), std::string("SG_")}) { diff --git a/tests/dbc_pipeline_test.cpp b/tests/dbc_pipeline_test.cpp index e70563f..7ea79e7 100644 --- a/tests/dbc_pipeline_test.cpp +++ b/tests/dbc_pipeline_test.cpp @@ -199,6 +199,8 @@ private Q_SLOTS: void emptyDatabase_data(); void emptyDatabase(); void databaseReplacement(); + void unsupportedSignalTypes_data(); + void unsupportedSignalTypes(); }; void PipelineTest::metadataLookupDecodeAndTree() { @@ -277,6 +279,54 @@ void PipelineTest::metadataLookupDecodeAndTree() { QVERIFY(j1939.signals.empty()); } +void PipelineTest::unsupportedSignalTypes_data() { + QTest::addColumn("bits"); + QTest::addColumn("type"); + QTest::addColumn>("payload"); + QTest::addColumn("integerValue"); + QTest::newRow("float32-one") << 32U << 1U << std::vector{0U, 0U, 128U, 63U} + << std::uint64_t(0x3F800000U); + QTest::newRow("float64-one") << 64U << 2U << std::vector{0U, 0U, 0U, 0U, 0U, 0U, 240U, 63U} + << std::uint64_t(0x3FF0000000000000ULL); +} + +void PipelineTest::unsupportedSignalTypes() { + QFETCH(unsigned, bits); + QFETCH(unsigned, type); + QFETCH(std::vector, payload); + QFETCH(std::uint64_t, integerValue); + const std::string definitions = "BO_ 1 Frame: " + std::to_string(bits / 8U) + " ECU\n" + "SG_ Value : 0|" + std::to_string(bits) + "@1+ (1,0) [0|100] \"\" ECU\n"; + RawCanFrame raw; + raw.canId = 1U; + raw.data = payload; + const DbcDecodeBuilder builder; + const DbcDecoder decoder; + + // Without a type override, the same bits are a supported integer signal. + const DbcParseResult integers = ParseFixture(definitions); + QVERIFY(!integers.HasErrors()); + const DecodedFrameValue integerFrame = decoder.Decode(builder.Build(integers.database), raw); + QCOMPARE(integerFrame.status, FrameDecodeStatus::Valid); + QCOMPARE(integerFrame.signals.size(), std::size_t(1)); + QVERIFY(integerFrame.signals[0].valid); + QCOMPARE(std::get(integerFrame.signals[0].rawValue), integerValue); + + const DbcParseResult floatingPoint = ParseFixture(definitions + + "SIG_VALTYPE_ 1 Value : " + std::to_string(type) + ";\n"); + QVERIFY(floatingPoint.HasErrors()); + QCOMPARE(floatingPoint.diagnostics.size(), std::size_t(1)); + QCOMPARE(floatingPoint.diagnostics[0].code, DbcDiagnosticCode::UnsupportedSignalType); + QCOMPARE(floatingPoint.diagnostics[0].lineNumber, std::size_t(3)); + QVERIFY(floatingPoint.database.frames.empty()); + // Even if a caller overlooks the error, no integer definition is published. + const DecodedFrameValue rejected = decoder.Decode(builder.Build(floatingPoint.database), raw); + QCOMPARE(rejected.status, FrameDecodeStatus::UnknownFrame); + QVERIFY(!rejected.valid); + QVERIFY(!rejected.definition); + QVERIFY(rejected.signals.empty()); +} + void PipelineTest::emptyDatabase_data() { QTest::addColumn("text"); QTest::newRow("empty-file") << QString();