diff --git a/docs/doxygen_warnings.log b/docs/doxygen_warnings.log
index cf37f61..bdf9ccb 100644
--- a/docs/doxygen_warnings.log
+++ b/docs/doxygen_warnings.log
@@ -2,7 +2,7 @@
/home/deeaitch/work/3.projects/git.himro.ca/automotive/dbc/dbc_statement_handler.h:22: warning: Compound DbcFrameHandler is not documented.
/home/deeaitch/work/3.projects/git.himro.ca/automotive/dbc/dbc_statement_handler.h:30: warning: Compound DbcSignalHandler is not documented.
/home/deeaitch/work/3.projects/git.himro.ca/automotive/dbc/dbc_statement_handler.h:16: warning: Compound DbcStatementHandler is not documented.
-/home/deeaitch/work/3.projects/git.himro.ca/automotive/dbc/main.cpp:98: warning: Member main(int argc, char *argv[]) (function) of file main.cpp is not documented.
+/home/deeaitch/work/3.projects/git.himro.ca/automotive/dbc/main.cpp:101: warning: Member main(int argc, char *argv[]) (function) of file main.cpp is not documented.
/home/deeaitch/work/3.projects/git.himro.ca/automotive/dbc/dbc_statement_handler.h:40: warning: Member Handle(const std::string &line, DbcParseContext &context) const override (function) of class DbcCommentHandler is not documented.
/home/deeaitch/work/3.projects/git.himro.ca/automotive/dbc/dbc_statement_handler.h:24: warning: Member Handle(const std::string &line, DbcParseContext &context) const override (function) of class DbcFrameHandler is not documented.
/home/deeaitch/work/3.projects/git.himro.ca/automotive/dbc/dbc_statement_handler.h:12: warning: Member database (variable) of struct DbcParseContext is not documented.
diff --git a/docs/html/classDbcCommentHandler.html b/docs/html/classDbcCommentHandler.html
index 9dac6f5..98aaefc 100644
--- a/docs/html/classDbcCommentHandler.html
+++ b/docs/html/classDbcCommentHandler.html
@@ -149,7 +149,7 @@ Public Member Functions
Implements DbcStatementHandler.
-Definition at line 302 of file dbc_statement_handler.cpp.
+Definition at line 303 of file dbc_statement_handler.cpp.
diff --git a/docs/html/classDbcFrameHandler.html b/docs/html/classDbcFrameHandler.html
index d59b675..8dd89a5 100644
--- a/docs/html/classDbcFrameHandler.html
+++ b/docs/html/classDbcFrameHandler.html
@@ -149,7 +149,7 @@ Public Member Functions
Implements DbcStatementHandler.
-Definition at line 280 of file dbc_statement_handler.cpp.
+Definition at line 281 of file dbc_statement_handler.cpp.
diff --git a/docs/html/classDbcParser.html b/docs/html/classDbcParser.html
index ca824d9..84f346e 100644
--- a/docs/html/classDbcParser.html
+++ b/docs/html/classDbcParser.html
@@ -157,7 +157,7 @@ Public Member Functions
Definition at line 40 of file dbc_parser.cpp.
-References DbcStatementDispatcher::Dispatch().
+References DbcStatementDispatcher::Dispatch().
diff --git a/docs/html/classDbcSignalHandler.html b/docs/html/classDbcSignalHandler.html
index 27a9a4f..e8ac567 100644
--- a/docs/html/classDbcSignalHandler.html
+++ b/docs/html/classDbcSignalHandler.html
@@ -149,7 +149,7 @@ Public Member Functions
Implements DbcStatementHandler.
-Definition at line 285 of file dbc_statement_handler.cpp.
+Definition at line 286 of file dbc_statement_handler.cpp.
diff --git a/docs/html/classDbcStatementDispatcher.html b/docs/html/classDbcStatementDispatcher.html
index 2b4ab73..7de847b 100644
--- a/docs/html/classDbcStatementDispatcher.html
+++ b/docs/html/classDbcStatementDispatcher.html
@@ -127,7 +127,7 @@ Public Member Functions
@@ -189,7 +189,7 @@ template<class Handler >
Returns false for an unregistered keyword; input must be trimmed.
-
Definition at line 313 of file dbc_statement_handler.cpp.
+
Definition at line 314 of file dbc_statement_handler.cpp.
Referenced by DbcParser::ParseFile().
diff --git a/docs/html/dbc__decoder_8cpp_source.html b/docs/html/dbc__decoder_8cpp_source.html
index f7ac376..d52d007 100644
--- a/docs/html/dbc__decoder_8cpp_source.html
+++ b/docs/html/dbc__decoder_8cpp_source.html
@@ -150,105 +150,106 @@ $(document).ready(function(){initNavTree('dbc__decoder_8cpp_source.html',''); in
-
-
61 std::visit([&signal](
auto rawValue) {
-
62 return (
static_cast<double>(rawValue) * signal.
factor) + signal.
offset;
-
-
-
-
-
67 result.signals.push_back(decoded);
-
-
-
-
+
+
61 if (
const auto *signedValue = std::get_if<std::int64_t>(&decoded.
rawValue))
+
62 numericValue =
static_cast<double>(*signedValue);
+
+
64 numericValue =
static_cast<double>(std::get<std::uint64_t>(decoded.
rawValue));
+
+
+
+
68 result.signals.push_back(decoded);
+
+
+
+
-
- 73bool DbcDecoder::ExtractUnsigned(
const std::vector<std::uint8_t> &data,
-
- 75 std::uint64_t &value) {
-
-
-
-
-
-
- 82 return ExtractMotorola(data, signal.
startBit, signal.
length, value);
-
-
- 85bool DbcDecoder::ExtractIntel(
const std::vector<std::uint8_t> &data,
- 86 std::uint32_t startBit,
-
- 88 std::uint64_t &value) {
-
-
- 91 for (std::uint32_t bitIndex = 0U; bitIndex < length; ++bitIndex) {
- 92 const std::uint32_t absoluteBit = startBit + bitIndex;
- 93 const std::uint32_t byteIndex = absoluteBit / 8U;
- 94 const std::uint32_t bitInByte = absoluteBit % 8U;
-
- 96 if (byteIndex >= data.size())
-
-
- 99 const std::uint64_t bitValue =
- 100 (
static_cast<std::uint64_t
>((data[byteIndex] >> bitInByte) & 0x01U) << bitIndex);
-
-
-
-
-
-
-
- 108bool DbcDecoder::ExtractMotorola(
const std::vector<std::uint8_t> &data,
- 109 std::uint32_t startBit,
- 110 std::uint32_t length,
- 111 std::uint64_t &value) {
-
-
-
-
-
-
-
-
-
-
- 122 std::size_t byteIndex = startBit / 8U;
- 123 std::uint32_t bitInByte = startBit % 8U;
-
- 125 for (std::uint32_t bitIndex = 0U; bitIndex < length; ++bitIndex) {
- 126 if (byteIndex >= data.size())
-
-
-
- 130 value |=
static_cast<std::uint64_t
>((data[byteIndex] >> bitInByte) & 0x01U);
-
- 132 if (bitInByte == 0U) {
-
-
-
-
-
-
-
-
-
- 142std::int64_t DbcDecoder::SignExtend(std::uint64_t value, std::uint32_t bitLength) {
- 143 if ((bitLength == 0U) || (bitLength > 64U))
-
-
- 146 const std::uint64_t signMask = std::uint64_t(1U) << (bitLength - 1U);
- 147 const std::uint64_t valueMask = (bitLength == 64U)
-
- 149 : (std::uint64_t(1U) << bitLength) - 1U;
-
-
- 152 if ((value & signMask) == 0U)
- 153 return static_cast<std::int64_t
>(value);
-
-
- 156 return -1 -
static_cast<std::int64_t
>((~value) & valueMask);
-
+
+ 74bool DbcDecoder::ExtractUnsigned(
const std::vector<std::uint8_t> &data,
+
+ 76 std::uint64_t &value) {
+
+
+
+
+
+
+ 83 return ExtractMotorola(data, signal.
startBit, signal.
length, value);
+
+
+ 86bool DbcDecoder::ExtractIntel(
const std::vector<std::uint8_t> &data,
+ 87 std::uint32_t startBit,
+
+ 89 std::uint64_t &value) {
+
+
+ 92 for (std::uint32_t bitIndex = 0U; bitIndex < length; ++bitIndex) {
+ 93 const std::uint32_t absoluteBit = startBit + bitIndex;
+ 94 const std::uint32_t byteIndex = absoluteBit / 8U;
+ 95 const std::uint32_t bitInByte = absoluteBit % 8U;
+
+ 97 if (byteIndex >= data.size())
+
+
+ 100 const std::uint64_t bitValue =
+ 101 (
static_cast<std::uint64_t
>((data[byteIndex] >> bitInByte) & 0x01U) << bitIndex);
+
+
+
+
+
+
+
+ 109bool DbcDecoder::ExtractMotorola(
const std::vector<std::uint8_t> &data,
+ 110 std::uint32_t startBit,
+ 111 std::uint32_t length,
+ 112 std::uint64_t &value) {
+
+
+
+
+
+
+
+
+
+
+ 123 std::size_t byteIndex = startBit / 8U;
+ 124 std::uint32_t bitInByte = startBit % 8U;
+
+ 126 for (std::uint32_t bitIndex = 0U; bitIndex < length; ++bitIndex) {
+ 127 if (byteIndex >= data.size())
+
+
+
+ 131 value |=
static_cast<std::uint64_t
>((data[byteIndex] >> bitInByte) & 0x01U);
+
+ 133 if (bitInByte == 0U) {
+
+
+
+
+
+
+
+
+
+ 143std::int64_t DbcDecoder::SignExtend(std::uint64_t value, std::uint32_t bitLength) {
+ 144 if ((bitLength == 0U) || (bitLength > 64U))
+
+
+ 147 const std::uint64_t signMask = std::uint64_t(1U) << (bitLength - 1U);
+ 148 const std::uint64_t valueMask = (bitLength == 64U)
+
+ 150 : (std::uint64_t(1U) << bitLength) - 1U;
+
+
+ 153 if ((value & signMask) == 0U)
+ 154 return static_cast<std::int64_t
>(value);
+
+
+ 157 return -1 -
static_cast<std::int64_t
>((~value) & valueMask);
+
DecodedFrameValue Decode(const DecodeDatabase &database, const RawCanFrame &frame) const
Decode one raw CAN frame.
const DecodeFrame * FindFrame(const DecodeDatabase &database, std::uint32_t canId, bool isExtended) const
Find frame definition by CAN ID.
Created: 2026-03-13 Author: Deeaitch (Dim. Himro)
diff --git a/docs/html/dbc__decoder__test_8cpp_source.html b/docs/html/dbc__decoder__test_8cpp_source.html
index f14acf6..1279b8e 100644
--- a/docs/html/dbc__decoder__test_8cpp_source.html
+++ b/docs/html/dbc__decoder__test_8cpp_source.html
@@ -218,25 +218,28 @@ $(document).ready(function(){initNavTree('dbc__decoder__test_8cpp_source.html','
119 std::ostringstream actualText;
120 std::ostringstream expectedText;
- 121 std::visit([&actualText](
auto raw) { actualText << raw; }, decoded.rawValue);
- 122 if (type == ValueType::Unsigned)
- 123 expectedText << bits;
-
- 125 expectedText << signedExpected;
- 126 if (!decoded.valid || decoded.rawValue != expected ||
- 127 decoded.physicalValue != physical || actualText.str() != expectedText.str()) {
- 128 std::cerr <<
"FAIL: 64-bit boundary " << bits <<
'\n';
-
-
-
-
-
-
-
-
- 137 std::cout <<
"All " <<
sizeof(cases) /
sizeof(cases[0]) + boundaryCases <<
" decoder cases passed.\n";
-
-
+ 121 if (
const auto *signedValue = std::get_if<std::int64_t>(&decoded.rawValue))
+ 122 actualText << *signedValue;
+
+ 124 actualText << std::get<std::uint64_t>(decoded.rawValue);
+ 125 if (type == ValueType::Unsigned)
+ 126 expectedText << bits;
+
+ 128 expectedText << signedExpected;
+ 129 if (!decoded.valid || decoded.rawValue != expected ||
+ 130 decoded.physicalValue != physical || actualText.str() != expectedText.str()) {
+ 131 std::cerr <<
"FAIL: 64-bit boundary " << bits <<
'\n';
+
+
+
+
+
+
+
+
+ 140 std::cout <<
"All " <<
sizeof(cases) /
sizeof(cases[0]) + boundaryCases <<
" decoder cases passed.\n";
+
+
Runtime CAN decoder using prebuilt decode database.
DecodedFrameValue Decode(const DecodeDatabase &database, const RawCanFrame &frame) const
Decode one raw CAN frame.
Created: 2026-03-13 Author: Deeaitch (Dim. Himro)
diff --git a/docs/html/dbc__parser_8cpp_source.html b/docs/html/dbc__parser_8cpp_source.html
index 379414a..fa3fa3c 100644
--- a/docs/html/dbc__parser_8cpp_source.html
+++ b/docs/html/dbc__parser_8cpp_source.html
@@ -157,7 +157,7 @@ $(document).ready(function(){initNavTree('dbc__parser_8cpp_source.html',''); ini
DbcDatabase ParseFile(const std::string &filePath) const
Parse DBC file.
-bool Dispatch(const std::string &line, DbcParseContext &context) const
+bool Dispatch(const std::string &line, DbcParseContext &context) const
Created: 2026-03-13 Author: Deeaitch (Dim. Himro)
Parsed DBC content stored in a simple internal form.
diff --git a/docs/html/dbc__parser__test_8cpp_source.html b/docs/html/dbc__parser__test_8cpp_source.html
index 67f8873..5f11c75 100644
--- a/docs/html/dbc__parser__test_8cpp_source.html
+++ b/docs/html/dbc__parser__test_8cpp_source.html
@@ -133,228 +133,233 @@ $(document).ready(function(){initNavTree('dbc__parser__test_8cpp_source.html',''
-
-
-
- 39 std::size_t count = 0U;
- 40 const auto check = [&](
bool passed,
const std::string &name) {
-
-
-
- 44 std::cerr <<
"FAIL: " << name <<
'\n';
-
-
-
-
-
-
-
-
- 53 const ValidCase valid[] = {
- 54 {
"BO_ 0 Frame: 0 ECU", 0U,
false, 0U},
- 55 {
"BO_ 2047 Frame: 8 ECU", 2047U,
false, 8U},
- 56 {
"BO_ 2048 Frame: 64 ECU", 2048U,
true, 64U},
- 57 {
"BO_ 536870911 Frame: 255 ECU", 0x1FFFFFFFU,
true, 255U},
- 58 {
"BO_ 2147483649 Frame: 8 ECU", 1U,
true, 8U},
- 59 {
"BO_ 2684354559 Frame: 8 ECU", 0x1FFFFFFFU,
true, 8U},
- 60 {
" BO_\t256\tFrame :\t8\tECU \r", 256U,
false, 8U}};
- 61 for (
const auto &test: valid) {
- 62 fixture.Write(std::string(test.line) +
'\n');
-
-
- 65 check(database.frames.size() == 1U && database.frames[0].name ==
"Frame" &&
- 66 database.frames[0].canId == test.id &&
- 67 database.frames[0].isExtended == test.extended &&
- 68 database.frames[0].dlc == test.length &&
- 69 database.frames[0].transmitter ==
"ECU",
-
- 71 }
catch (
const std::exception &error) { check(
false, error.what()); }
-
- 73 const char *invalid[] = {
- 74 "BO_ invalid",
"BO_",
"BO_ 1",
"BO_ 1 : 8 ECU",
"BO_ 1 Frame 8 ECU",
- 75 "BO_ 1 Frame:",
"BO_ 1 Frame: 8",
"BO_ 1 Frame: 8 ECU extra",
- 76 "BO_ 1 Two Names: 8 ECU",
"BO_ -1 Frame: 8 ECU",
"BO_ +1 Frame: 8 ECU",
- 77 "BO_ 1x Frame: 8 ECU",
"BO_ 0x123 Frame: 8 ECU",
"BO_ 1.5 Frame: 8 ECU",
- 78 "BO_ 4294967296 Frame: 8 ECU",
"BO_ 999999999999999999999 Frame: 8 ECU",
- 79 "BO_ 536870912 Frame: 8 ECU",
"BO_ 1073741824 Frame: 8 ECU",
- 80 "BO_ 2684354560 Frame: 8 ECU",
"BO_ 4294967295 Frame: 8 ECU",
- 81 "BO_ 1 Frame: -1 ECU",
"BO_ 1 Frame: +8 ECU",
"BO_ 1 Frame: 8x ECU",
- 82 "BO_ 1 Frame: 8.5 ECU",
"BO_ 1 Frame: 256 ECU",
- 83 "BO_ 1 Frame: 4294967296 ECU",
"BO_\tinvalid"};
- 84 for (
const auto *line: invalid) {
- 85 fixture.Write(std::string(
"VERSION \"test\"\n\n") + line +
'\n');
-
-
-
- 89 }
catch (
const std::runtime_error &error) {
- 90 const std::string prefix = fixture.path.string() +
":3: ";
- 91 check(std::string(error.what()).find(prefix) == 0U &&
- 92 std::string(error.what()).size() > prefix.size(),
-
-
-
-
- 97 "BO_TX_BU_ 256 : ECU;\nBO_ 256 Frame: 8 ECU\n"
- 98 " SG_ Speed : 0|16@1+ (0.5,1) [0|100] \"rpm\" ECU\n"
- 99 "CM_ BO_ 256 \"Frame comment\";\nCM_ SG_ 256 Speed \"Signal comment\";\n"
- 100 "BO_ 257 Second: 8 ECU\n");
-
- 102 check(database.frames.size() == 2U && database.frames[0].signals.size() == 1U &&
- 103 database.frames[0].comment ==
"Frame comment" &&
- 104 database.frames[0].signals[0].comment ==
"Signal comment",
-
- 106 fixture.Write(
"\nSG_ Speed : invalid\n");
-
-
- 109 check(
false,
"Signal error location");
- 110 }
catch (
const std::runtime_error &error) {
- 111 check(std::string(error.what()).find(fixture.path.string() +
":2: ") == 0U,
- 112 "Signal error location");
-
-
- 115 const auto signalLine = [](
const std::string &start,
const std::string &length,
- 116 const std::string &order,
const std::string &factor,
- 117 const std::string &offset,
const std::string &minimum,
- 118 const std::string &maximum) {
- 119 return "SG_ Value : " + start +
"|" + length +
"@" + order +
- 120 " (" + factor +
"," + offset +
") [" + minimum +
"|" + maximum +
"] \"\" ECU";
-
- 122 const auto rejectSignal = [&](
const std::string &line,
unsigned payload = 8U) {
- 123 fixture.Write(
"BO_ 1 Frame: " + std::to_string(payload) +
" ECU\n\n" + line +
"\n");
-
- 125 signalParser.
ParseFile(fixture.path.string());
- 126 check(
false,
"Reject " + line);
- 127 }
catch (
const std::runtime_error &error) {
- 128 const std::string prefix = fixture.path.string() +
":3: ";
- 129 check(std::string(error.what()).find(prefix) == 0U &&
- 130 std::string(error.what()).size() > prefix.size(),
-
-
-
- 134 const std::string ordinary = signalLine(
"0",
"8",
"1+",
"1",
"0",
"0",
"255");
- 135 for (
const std::string value: {
"-1",
"+1",
"0junk",
"0x10",
"1.5",
- 136 "4294967296",
"999999999999999999999"}) {
- 137 rejectSignal(signalLine(value,
"8",
"1+",
"1",
"0",
"0",
"255"));
- 138 rejectSignal(signalLine(
"0", value,
"1+",
"1",
"0",
"0",
"255"));
-
- 140 for (
const std::string length: {
"0",
"65"})
- 141 rejectSignal(signalLine(
"0", length,
"1+",
"1",
"0",
"0",
"255"));
- 142 for (
const std::string order: {
"2+",
"9-",
"1",
"1x",
"1++",
"01+"})
- 143 rejectSignal(signalLine(
"0",
"8", order,
"1",
"0",
"0",
"255"));
- 144 for (
const std::string value: {
"1junk",
"nan",
"NaN",
"inf",
"-inf",
"1e999",
"1e-999",
"0x1p2",
"",
"1e",
"1.2.3"}) {
- 145 rejectSignal(signalLine(
"0",
"8",
"1+", value,
"0",
"0",
"255"));
- 146 rejectSignal(signalLine(
"0",
"8",
"1+",
"1", value,
"0",
"255"));
- 147 rejectSignal(signalLine(
"0",
"8",
"1+",
"1",
"0", value,
"255"));
- 148 rejectSignal(signalLine(
"0",
"8",
"1+",
"1",
"0",
"0", value));
-
- 150 rejectSignal(signalLine(
"0",
"8",
"1+",
"1",
"0",
"2",
"1"));
- 151 for (
const std::string name: {
"",
"Two Names",
"Value M",
"Value m1",
"Value m1M"}) {
- 152 std::string line = ordinary;
- 153 line.replace(4U, 5U, name);
-
-
- 156 for (
const std::string broken: {
- 157 "SG_",
"SG_ Value",
"SG_ Value : 0|8@1+ (1,0) [0|255] ECU",
- 158 "SG_ Value : 0|8@1+ (1,0) [0|255] \"\"",
- 159 "SG_ Value : 0|8@1+ (1,0) [0|255] \"\" ECU,",
- 160 "SG_ Value : 0|8@1+ (1,0) [0|255] \"\" ECU extra"})
- 161 rejectSignal(broken);
- 162 rejectSignal(signalLine(
"64",
"1",
"1+",
"1",
"0",
"0",
"1"));
- 163 rejectSignal(signalLine(
"63",
"2",
"1+",
"1",
"0",
"0",
"3"));
- 164 rejectSignal(signalLine(
"56",
"2",
"0+",
"1",
"0",
"0",
"3"));
- 165 rejectSignal(signalLine(
"4294967295",
"64",
"1+",
"1",
"0",
"0",
"1"));
- 166 rejectSignal(ordinary, 0U);
- 167 for (
const char delimiter: {
'|',
'@',
'(',
')',
'[',
']',
'"'}) {
- 168 std::string line = ordinary;
- 169 line.erase(line.find(delimiter), 1U);
-
-
- 172 fixture.Write(
"BO_ 1 Frame: 8 ECU\n" +
- 173 signalLine(
"0",
"8",
"1+",
"0",
"0",
"0",
"0") +
"\n" +
- 174 signalLine(
"8",
"8",
"1-",
"-1",
"0",
"-255",
"0") +
"\n");
- 175 const auto factors = signalParser.
ParseFile(fixture.path.string());
- 176 check(factors.frames[0].signals.size() == 2U &&
- 177 factors.frames[0].signals[0].factor == 0.0 &&
- 178 factors.frames[0].signals[1].factor == -1.0,
- 179 "Zero and negative factors and equal range boundaries");
-
-
-
-
-
-
-
- 187 const SignalCase validSignals[] = {
- 188 {
"0",
"1",
"1+", 1U},
- 189 {
"7",
"1",
"0-", 1U},
- 190 {
"0",
"64",
"1+", 8U},
- 191 {
"7",
"64",
"0-", 8U},
- 192 {
"63",
"1",
"1+", 8U},
- 193 {
"56",
"1",
"0+", 8U},
- 194 {
"3",
"12",
"0+", 2U},
- 195 {
"2039",
"1",
"1+", 255U}};
- 196 for (
const auto &test: validSignals) {
- 197 fixture.Write(
"BO_ 1 Frame: " + std::to_string(test.payload) +
" ECU\n" +
- 198 signalLine(test.start, test.length, test.order,
".5e+1",
"-2.5E-1",
"-1.",
"+2e2") +
"\n");
-
- 200 const auto parsed = signalParser.
ParseFile(fixture.path.string());
- 201 const auto &signal = parsed.
frames.at(0).signals.at(0);
- 202 check(signal.startBit == std::stoul(test.start) &&
- 203 signal.length == std::stoul(test.length) &&
- 204 signal.isLittleEndian == (test.order[0] ==
'1') &&
- 205 signal.isSigned == (test.order[1] ==
'-') &&
- 206 signal.factor == 5.0 && signal.offset == -0.25 &&
- 207 signal.minimum == -1.0 && signal.maximum == 200.0,
- 208 "Valid signal boundary and numeric metadata");
- 209 }
catch (
const std::exception &error) { check(
false, error.what()); }
-
- 211 fixture.Write(
"BO_ 1 Frame: 8 ECU\n"
- 212 "SG_\tValue\t:\t0 | 8 @1+ ( 1 , 0 ) [ 0 | 255 ] \"rpm\" ECU1,\tECU2\n");
-
- 214 const auto parsed = signalParser.
ParseFile(fixture.path.string());
- 215 const auto &signal = parsed.
frames.at(0).signals.at(0);
- 216 check(signal.name ==
"Value" && signal.unit ==
"rpm" &&
- 217 signal.receivers == std::vector<std::string>({
"ECU1",
"ECU2"}),
- 218 "Signal tabs, spaces, and receiver list");
- 219 }
catch (
const std::exception &error) { check(
false, error.what()); }
-
-
-
- 223 dispatcher.RegisterType<CountingHandler>(
"TEST_");
- 224 check(dispatcher.
Dispatch(
"TEST_ first", context) &&
- 225 dispatcher.
Dispatch(
"TEST_ second", context) &&
- 226 CountingHandler::instances == 1 && CountingHandler::calls == 2 &&
- 227 dispatchDatabase.
frames.size() == 2U,
- 228 "Factory dispatch and handler reuse");
- 229 check(!dispatcher.
Dispatch(
"TEST_OTHER data", context) &&
- 230 !dispatcher.
Dispatch(
"BO_TX_BU_ 1 : ECU;", context) &&
- 231 !dispatcher.
Dispatch(
"VAL_ 1 Speed 0 \"Stopped\";", context) &&
- 232 CountingHandler::calls == 2,
- 233 "Exact and unknown keyword dispatch");
-
- 235 fixture.Write(
"BO_ 1 First: 8 ECU\n");
- 236 check(parser.
ParseFile(fixture.path.string()).
frames.size() == 1U,
"First parse");
- 237 fixture.Write(
"SG_ Speed : invalid\n");
-
-
- 240 check(
false,
"Independent parse context");
- 241 }
catch (
const std::runtime_error &error) {
- 242 check(std::string(error.what()).find(
"Signal found before any frame") !=
-
- 244 "Independent parse context");
-
- 246 fixture.Write(
"BO_ 2 Second: 8 ECU\n");
- 247 const auto recovered = parser.
ParseFile(fixture.path.string());
- 248 check(recovered.frames.size() == 1U && recovered.frames[0].canId == 2U,
- 249 "Fresh parse after error");
- 250 if (failures != 0)
return 1;
- 251 std::cout <<
"All " << count <<
" parser cases passed.\n";
-
-
+ 36std::string SignalLine(
const std::string &start,
const std::string &length,
+ 37 const std::string &order,
const std::string &factor,
+ 38 const std::string &offset,
const std::string &minimum,
+ 39 const std::string &maximum) {
+ 40 return "SG_ Value : " + start +
"|" + length +
"@" + order +
+ 41 " (" + factor +
"," + offset +
") [" + minimum +
"|" + maximum +
"] \"\" ECU";
+
+
+
+
+ 46 std::size_t count = 0U;
+ 47 void Check(
bool passed,
const std::string &name) {
+
+
+
+ 51 std::cerr <<
"FAIL: " << name <<
'\n';
+
+
+ 54 void RejectSignal(
const Fixture &fixture,
const std::string &line,
unsigned payload = 8U) {
+ 55 fixture.Write(
"BO_ 1 Frame: " + std::to_string(payload) +
" ECU\n\n" + line +
"\n");
+
+
+ 58 Check(
false,
"Reject " + line);
+ 59 }
catch (
const std::runtime_error &error) {
+ 60 const std::string prefix = fixture.path.string() +
":3: ";
+ 61 Check(std::string(error.what()).find(prefix) == 0U &&
+ 62 std::string(error.what()).size() > prefix.size(),
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 77 const ValidCase valid[] = {
+ 78 {
"BO_ 0 Frame: 0 ECU", 0U,
false, 0U},
+ 79 {
"BO_ 2047 Frame: 8 ECU", 2047U,
false, 8U},
+ 80 {
"BO_ 2048 Frame: 64 ECU", 2048U,
true, 64U},
+ 81 {
"BO_ 536870911 Frame: 255 ECU", 0x1FFFFFFFU,
true, 255U},
+ 82 {
"BO_ 2147483649 Frame: 8 ECU", 1U,
true, 8U},
+ 83 {
"BO_ 2684354559 Frame: 8 ECU", 0x1FFFFFFFU,
true, 8U},
+ 84 {
" BO_\t256\tFrame :\t8\tECU \r", 256U,
false, 8U}};
+ 85 for (
const auto &test: valid) {
+ 86 fixture.Write(std::string(test.line) +
'\n');
+
+
+ 89 tests.Check(database.frames.size() == 1U && database.frames[0].name ==
"Frame" &&
+ 90 database.frames[0].canId == test.id &&
+ 91 database.frames[0].isExtended == test.extended &&
+ 92 database.frames[0].dlc == test.length &&
+ 93 database.frames[0].transmitter ==
"ECU",
+
+ 95 }
catch (
const std::exception &error) { tests.Check(
false, error.what()); }
+
+ 97 const char *invalid[] = {
+ 98 "BO_ invalid",
"BO_",
"BO_ 1",
"BO_ 1 : 8 ECU",
"BO_ 1 Frame 8 ECU",
+ 99 "BO_ 1 Frame:",
"BO_ 1 Frame: 8",
"BO_ 1 Frame: 8 ECU extra",
+ 100 "BO_ 1 Two Names: 8 ECU",
"BO_ -1 Frame: 8 ECU",
"BO_ +1 Frame: 8 ECU",
+ 101 "BO_ 1x Frame: 8 ECU",
"BO_ 0x123 Frame: 8 ECU",
"BO_ 1.5 Frame: 8 ECU",
+ 102 "BO_ 4294967296 Frame: 8 ECU",
"BO_ 999999999999999999999 Frame: 8 ECU",
+ 103 "BO_ 536870912 Frame: 8 ECU",
"BO_ 1073741824 Frame: 8 ECU",
+ 104 "BO_ 2684354560 Frame: 8 ECU",
"BO_ 4294967295 Frame: 8 ECU",
+ 105 "BO_ 1 Frame: -1 ECU",
"BO_ 1 Frame: +8 ECU",
"BO_ 1 Frame: 8x ECU",
+ 106 "BO_ 1 Frame: 8.5 ECU",
"BO_ 1 Frame: 256 ECU",
+ 107 "BO_ 1 Frame: 4294967296 ECU",
"BO_\tinvalid"};
+ 108 for (
const auto *line: invalid) {
+ 109 fixture.Write(std::string(
"VERSION \"test\"\n\n") + line +
'\n');
+
+
+ 112 tests.Check(
false, line);
+ 113 }
catch (
const std::runtime_error &error) {
+ 114 const std::string prefix = fixture.path.string() +
":3: ";
+ 115 tests.Check(std::string(error.what()).find(prefix) == 0U &&
+ 116 std::string(error.what()).size() > prefix.size(),
+
+
+
+
+ 121 "BO_TX_BU_ 256 : ECU;\nBO_ 256 Frame: 8 ECU\n"
+ 122 " SG_ Speed : 0|16@1+ (0.5,1) [0|100] \"rpm\" ECU\n"
+ 123 "CM_ BO_ 256 \"Frame comment\";\nCM_ SG_ 256 Speed \"Signal comment\";\n"
+ 124 "BO_ 257 Second: 8 ECU\n");
+
+ 126 tests.Check(database.frames.size() == 2U && database.frames[0].signals.size() == 1U &&
+ 127 database.frames[0].comment ==
"Frame comment" &&
+ 128 database.frames[0].signals[0].comment ==
"Signal comment",
+
+ 130 fixture.Write(
"\nSG_ Speed : invalid\n");
+
+
+ 133 tests.Check(
false,
"Signal error location");
+ 134 }
catch (
const std::runtime_error &error) {
+ 135 tests.Check(std::string(error.what()).find(fixture.path.string() +
":2: ") == 0U,
+ 136 "Signal error location");
+
+
+ 139 const std::string ordinary = SignalLine(
"0",
"8",
"1+",
"1",
"0",
"0",
"255");
+ 140 for (
const std::string value: {
"-1",
"+1",
"0junk",
"0x10",
"1.5",
+ 141 "4294967296",
"999999999999999999999"}) {
+ 142 tests.RejectSignal(fixture, SignalLine(value,
"8",
"1+",
"1",
"0",
"0",
"255"));
+ 143 tests.RejectSignal(fixture, SignalLine(
"0", value,
"1+",
"1",
"0",
"0",
"255"));
+
+ 145 for (
const std::string length: {
"0",
"65"})
+ 146 tests.RejectSignal(fixture, SignalLine(
"0", length,
"1+",
"1",
"0",
"0",
"255"));
+ 147 for (
const std::string order: {
"2+",
"9-",
"1",
"1x",
"1++",
"01+"})
+ 148 tests.RejectSignal(fixture, SignalLine(
"0",
"8", order,
"1",
"0",
"0",
"255"));
+ 149 for (
const std::string value: {
"1junk",
"nan",
"NaN",
"inf",
"-inf",
"1e999",
"1e-999",
"0x1p2",
"",
"1e",
"1.2.3"}) {
+ 150 tests.RejectSignal(fixture, SignalLine(
"0",
"8",
"1+", value,
"0",
"0",
"255"));
+ 151 tests.RejectSignal(fixture, SignalLine(
"0",
"8",
"1+",
"1", value,
"0",
"255"));
+ 152 tests.RejectSignal(fixture, SignalLine(
"0",
"8",
"1+",
"1",
"0", value,
"255"));
+ 153 tests.RejectSignal(fixture, SignalLine(
"0",
"8",
"1+",
"1",
"0",
"0", value));
+
+ 155 tests.RejectSignal(fixture, SignalLine(
"0",
"8",
"1+",
"1",
"0",
"2",
"1"));
+ 156 for (
const std::string name: {
"",
"Two Names",
"Value M",
"Value m1",
"Value m1M"}) {
+ 157 std::string line = ordinary;
+ 158 line.replace(4U, 5U, name);
+ 159 tests.RejectSignal(fixture, line);
+
+ 161 for (
const std::string broken: {
+ 162 "SG_",
"SG_ Value",
"SG_ Value : 0|8@1+ (1,0) [0|255] ECU",
+ 163 "SG_ Value : 0|8@1+ (1,0) [0|255] \"\"",
+ 164 "SG_ Value : 0|8@1+ (1,0) [0|255] \"\" ECU,",
+ 165 "SG_ Value : 0|8@1+ (1,0) [0|255] \"\" ECU extra"})
+ 166 tests.RejectSignal(fixture, broken);
+ 167 tests.RejectSignal(fixture, SignalLine(
"64",
"1",
"1+",
"1",
"0",
"0",
"1"));
+ 168 tests.RejectSignal(fixture, SignalLine(
"63",
"2",
"1+",
"1",
"0",
"0",
"3"));
+ 169 tests.RejectSignal(fixture, SignalLine(
"56",
"2",
"0+",
"1",
"0",
"0",
"3"));
+ 170 tests.RejectSignal(fixture, SignalLine(
"4294967295",
"64",
"1+",
"1",
"0",
"0",
"1"));
+ 171 tests.RejectSignal(fixture, ordinary, 0U);
+ 172 for (
const char delimiter: {
'|',
'@',
'(',
')',
'[',
']',
'"'}) {
+ 173 std::string line = ordinary;
+ 174 line.erase(line.find(delimiter), 1U);
+ 175 tests.RejectSignal(fixture, line);
+
+ 177 fixture.Write(
"BO_ 1 Frame: 8 ECU\n" +
+ 178 SignalLine(
"0",
"8",
"1+",
"0",
"0",
"0",
"0") +
"\n" +
+ 179 SignalLine(
"8",
"8",
"1-",
"-1",
"0",
"-255",
"0") +
"\n");
+ 180 const auto factors = signalParser.
ParseFile(fixture.path.string());
+ 181 tests.Check(factors.frames[0].signals.size() == 2U &&
+ 182 factors.frames[0].signals[0].factor == 0.0 &&
+ 183 factors.frames[0].signals[1].factor == -1.0,
+ 184 "Zero and negative factors and equal range boundaries");
+
+
+
+
+
+
+
+ 192 const SignalCase validSignals[] = {
+ 193 {
"0",
"1",
"1+", 1U},
+ 194 {
"7",
"1",
"0-", 1U},
+ 195 {
"0",
"64",
"1+", 8U},
+ 196 {
"7",
"64",
"0-", 8U},
+ 197 {
"63",
"1",
"1+", 8U},
+ 198 {
"56",
"1",
"0+", 8U},
+ 199 {
"3",
"12",
"0+", 2U},
+ 200 {
"2039",
"1",
"1+", 255U}};
+ 201 for (
const auto &test: validSignals) {
+ 202 fixture.Write(
"BO_ 1 Frame: " + std::to_string(test.payload) +
" ECU\n" +
+ 203 SignalLine(test.start, test.length, test.order,
".5e+1",
"-2.5E-1",
"-1.",
"+2e2") +
"\n");
+
+ 205 const auto parsed = signalParser.
ParseFile(fixture.path.string());
+ 206 const auto &signal = parsed.
frames.at(0).signals.at(0);
+ 207 tests.Check(signal.startBit == std::stoul(test.start) &&
+ 208 signal.length == std::stoul(test.length) &&
+ 209 signal.isLittleEndian == (test.order[0] ==
'1') &&
+ 210 signal.isSigned == (test.order[1] ==
'-') &&
+ 211 signal.factor == 5.0 && signal.offset == -0.25 &&
+ 212 signal.minimum == -1.0 && signal.maximum == 200.0,
+ 213 "Valid signal boundary and numeric metadata");
+ 214 }
catch (
const std::exception &error) { tests.Check(
false, error.what()); }
+
+ 216 fixture.Write(
"BO_ 1 Frame: 8 ECU\n"
+ 217 "SG_\tValue\t:\t0 | 8 @1+ ( 1 , 0 ) [ 0 | 255 ] \"rpm\" ECU1,\tECU2\n");
+
+ 219 const auto parsed = signalParser.
ParseFile(fixture.path.string());
+ 220 const auto &signal = parsed.
frames.at(0).signals.at(0);
+ 221 tests.Check(signal.name ==
"Value" && signal.unit ==
"rpm" &&
+ 222 signal.receivers == std::vector<std::string>({
"ECU1",
"ECU2"}),
+ 223 "Signal tabs, spaces, and receiver list");
+ 224 }
catch (
const std::exception &error) { tests.Check(
false, error.what()); }
+
+
+
+ 228 dispatcher.RegisterType<CountingHandler>(
"TEST_");
+ 229 tests.Check(dispatcher.
Dispatch(
"TEST_ first", context) &&
+ 230 dispatcher.
Dispatch(
"TEST_ second", context) &&
+ 231 CountingHandler::instances == 1 && CountingHandler::calls == 2 &&
+ 232 dispatchDatabase.
frames.size() == 2U,
+ 233 "Factory dispatch and handler reuse");
+ 234 tests.Check(!dispatcher.
Dispatch(
"TEST_OTHER data", context) &&
+ 235 !dispatcher.
Dispatch(
"BO_TX_BU_ 1 : ECU;", context) &&
+ 236 !dispatcher.
Dispatch(
"VAL_ 1 Speed 0 \"Stopped\";", context) &&
+ 237 CountingHandler::calls == 2,
+ 238 "Exact and unknown keyword dispatch");
+
+ 240 fixture.Write(
"BO_ 1 First: 8 ECU\n");
+ 241 tests.Check(parser.
ParseFile(fixture.path.string()).
frames.size() == 1U,
"First parse");
+ 242 fixture.Write(
"SG_ Speed : invalid\n");
+
+
+ 245 tests.Check(
false,
"Independent parse context");
+ 246 }
catch (
const std::runtime_error &error) {
+ 247 tests.Check(std::string(error.what()).find(
"Signal found before any frame") !=
+
+ 249 "Independent parse context");
+
+ 251 fixture.Write(
"BO_ 2 Second: 8 ECU\n");
+ 252 const auto recovered = parser.
ParseFile(fixture.path.string());
+ 253 tests.Check(recovered.frames.size() == 1U && recovered.frames[0].canId == 2U,
+ 254 "Fresh parse after error");
+ 255 if (tests.failures != 0)
return 1;
+ 256 std::cout <<
"All " << tests.count <<
" parser cases passed.\n";
+
+
DbcDatabase ParseFile(const std::string &filePath) const
Parse DBC file.
-bool Dispatch(const std::string &line, DbcParseContext &context) const
+bool Dispatch(const std::string &line, DbcParseContext &context) const
Created: 2026-03-13 Author: Deeaitch (Dim. Himro)
Parsed DBC content stored in a simple internal form.
diff --git a/docs/html/dbc__statement__handler_8cpp_source.html b/docs/html/dbc__statement__handler_8cpp_source.html
index 855638b..30ee562 100644
--- a/docs/html/dbc__statement__handler_8cpp_source.html
+++ b/docs/html/dbc__statement__handler_8cpp_source.html
@@ -120,305 +120,306 @@ $(document).ready(function(){initNavTree('dbc__statement__handler_8cpp_source.ht
- 28 std::string TrimText(
const std::string &text) {
- 29 std::string::size_type begin = 0U;
- 30 while ((begin < text.size()) &&
- 31 std::isspace(
static_cast<unsigned char>(text[begin])))
-
-
- 34 std::string::size_type end = text.size();
- 35 while ((end > begin) &&
- 36 std::isspace(
static_cast<unsigned char>(text[end - 1U])))
-
-
- 39 return text.substr(begin, end - begin);
-
-
-
-
-
-
- 46 std::vector<std::string> SplitReceivers(
const std::string &text) {
- 47 std::vector<std::string> receivers;
-
- 49 std::istringstream stream(text);
+ 23 double ParsePhysicalValue(
const std::string &text,
const char *field) {
+ 24 static const std::regex number(
+ 25 R
"(^[+-]?(?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+)(?:[eE][+-]?[0-9]+)?$)");
+ 26 if (!std::regex_match(text, number))
+ 27 throw std::runtime_error(std::string(
"Invalid ") + field +
": " + text);
+ 28 std::size_t consumed = 0U;
+
+
+ 31 value = std::stod(text, &consumed);
+ 32 }
catch (
const std::exception &) {
+ 33 throw std::runtime_error(std::string(
"Invalid ") + field +
": " + text);
+
+ 35 if (consumed != text.size() || !std::isfinite(value))
+ 36 throw std::runtime_error(std::string(
"Invalid ") + field +
": " + text);
+
+
+
+ 45 std::string TrimText(
const std::string &text) {
+ 46 std::string::size_type begin = 0U;
+ 47 while ((begin < text.size()) &&
+ 48 std::isspace(
static_cast<unsigned char>(text[begin])))
+
- 51 while (std::getline(stream, token,
',')) {
- 52 token = TrimText(token);
-
- 54 receivers.push_back(token);
-
-
-
-
-
- 60 void NormalizeCanId(std::uint32_t rawCanId,
- 61 std::uint32_t &normalizedCanId,
-
-
-
-
-
-
- 68 if ((rawCanId & 0x80000000U) != 0U) {
-
- 70 normalizedCanId = (rawCanId & 0x1FFFFFFFU);
-
- 72 isExtended = (rawCanId > 0x7FFU);
- 73 normalizedCanId = rawCanId;
-
+ 51 std::string::size_type end = text.size();
+ 52 while ((end > begin) &&
+ 53 std::isspace(
static_cast<unsigned char>(text[end - 1U])))
+
+
+ 56 return text.substr(begin, end - begin);
+
+
+
+
+
+
+ 63 std::vector<std::string> SplitReceivers(
const std::string &text) {
+ 64 std::vector<std::string> receivers;
+
+ 66 std::istringstream stream(text);
+
+ 68 while (std::getline(stream, token,
',')) {
+ 69 token = TrimText(token);
+
+ 71 receivers.push_back(token);
+
+
+
- 77 std::uint32_t TryExtractPgn(std::uint32_t canId,
bool isExtended,
bool &hasPgn) {
-
-
-
-
-
- 83 if ((canId & 0x1FFFFFFFU) != canId)
-
-
- 86 const std::uint32_t pf = (canId >> 16U) & 0xFFU;
- 87 const std::uint32_t ps = (canId >> 8U) & 0xFFU;
- 88 const std::uint32_t dp = (canId >> 24U) & 0x01U;
-
- 90 std::uint32_t pgn = 0U;
-
-
- 93 pgn = (dp << 16U) | (pf << 8U);
-
- 95 pgn = (dp << 16U) | (pf << 8U) | ps;
+ 77 void NormalizeCanId(std::uint32_t rawCanId,
+ 78 std::uint32_t &normalizedCanId,
+
+
+
+
+
+
+ 85 if ((rawCanId & 0x80000000U) != 0U) {
+
+ 87 normalizedCanId = (rawCanId & 0x1FFFFFFFU);
+
+ 89 isExtended = (rawCanId > 0x7FFU);
+ 90 normalizedCanId = rawCanId;
+
+
+
+ 94 std::uint32_t TryExtractPgn(std::uint32_t canId,
bool isExtended,
bool &hasPgn) {
+
-
-
-
-
-
+
+
+
+ 100 if ((canId & 0x1FFFFFFFU) != canId)
+
- 103FrameInfo DbcFrameHandler::ParseFrameLine(
const std::string &line) {
-
-
-
-
+ 103 const std::uint32_t pf = (canId >> 16U) & 0xFFU;
+ 104 const std::uint32_t ps = (canId >> 8U) & 0xFFU;
+ 105 const std::uint32_t dp = (canId >> 24U) & 0x01U;
+
+ 107 std::uint32_t pgn = 0U;
- 109 std::istringstream stream(line);
-
-
- 112 if (!(stream >> keyword >> idText) || keyword !=
"BO_")
- 113 throw std::runtime_error(
"Missing CAN ID in frame definition.");
-
- 115 const std::uint32_t rawCanId = ParseDecimal(
- 116 idText, std::numeric_limits<std::uint32_t>::max(),
"CAN ID");
-
- 118 if ((rawCanId & 0x60000000U) != 0U)
- 119 throw std::runtime_error(
"CAN ID exceeds the 29-bit identifier range.");
-
- 121 std::string nameText;
- 122 if (!std::getline(stream, nameText,
':') || stream.eof())
- 123 throw std::runtime_error(
"Missing ':' in frame definition.");
- 124 std::istringstream nameStream(nameText);
-
-
- 127 if (!(nameStream >> frame.
name) || (nameStream >> extra))
- 128 throw std::runtime_error(
"Missing or invalid frame name.");
-
- 130 std::string lengthText;
- 131 if (!(stream >> lengthText))
- 132 throw std::runtime_error(
"Missing payload length in frame definition.");
- 133 frame.
dlc =
static_cast<std::uint8_t
>(ParseDecimal(
- 134 lengthText, std::numeric_limits<std::uint8_t>::max(),
"payload length"));
-
- 136 throw std::runtime_error(
"Missing transmitter in frame definition.");
-
- 138 throw std::runtime_error(
"Unexpected trailing field in frame definition: " + extra);
-
-
-
-
-
-
-
- 146SignalInfo DbcSignalHandler::ParseSignalLine(
const std::string &line) {
-
- 148 static const std::regex syntax(
- 149 R
"dbc(^SG_[ \t]+([^:]+):[ \t]*([^| \t]+)[ \t]*\|[ \t]*([^@ \t]+)[ \t]*@[ \t]*([01])([+-])[ \t]*\([ \t]*([^, \t]+)[ \t]*,[ \t]*([^ )\t]+)[ \t]*\)[ \t]*\[[ \t]*([^| \t]+)[ \t]*\|[ \t]*([^\] \t]+)[ \t]*\][ \t]*"([^"\r\n]*)"[ \t]+([^ \t,]+(?:[ \t]*,[ \t]*[^ \t,]+)*)[ \t]*$)dbc");
-
- 151 if (!std::regex_match(line, fields, syntax))
- 152 throw std::runtime_error(
"Malformed or unsupported SG_ signal definition.");
-
-
- 155 signal.
name = TrimText(fields[1].str());
- 156 if (signal.
name.empty() || signal.
name.find_first_of(
" \t\r\n") != std::string::npos)
- 157 throw std::runtime_error(
"Invalid signal name or unsupported multiplexing syntax.");
-
- 159 signal.
startBit = ParseDecimal(fields[2].str(),
- 160 std::numeric_limits<std::uint32_t>::max(),
"signal start bit");
- 161 signal.
length = ParseDecimal(fields[3].str(), 64U,
"signal length");
-
- 163 throw std::runtime_error(
"Signal length must be within 1..64 bits.");
-
- 165 signal.
isSigned = fields[5].str() ==
"-";
-
- 167 const auto parsePhysical = [](
const std::string &text,
const char *field) {
- 168 static const std::regex number(
- 169 R
"(^[+-]?(?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+)(?:[eE][+-]?[0-9]+)?$)");
- 170 if (!std::regex_match(text, number))
- 171 throw std::runtime_error(std::string(
"Invalid ") + field +
": " + text);
- 172 std::size_t consumed = 0U;
-
-
- 175 value = std::stod(text, &consumed);
- 176 }
catch (
const std::exception &) {
- 177 throw std::runtime_error(std::string(
"Invalid ") + field +
": " + text);
-
- 179 if (consumed != text.size() || !std::isfinite(value))
- 180 throw std::runtime_error(std::string(
"Invalid ") + field +
": " + text);
-
-
- 183 signal.
factor = parsePhysical(fields[6].str(),
"signal factor");
- 184 signal.
offset = parsePhysical(fields[7].str(),
"signal offset");
- 185 signal.
minimum = parsePhysical(fields[8].str(),
"signal minimum");
- 186 signal.
maximum = parsePhysical(fields[9].str(),
"signal maximum");
-
- 188 throw std::runtime_error(
"Signal minimum exceeds maximum.");
- 189 signal.
unit = fields[10].str();
- 190 signal.
receivers = SplitReceivers(fields[11].str());
-
-
-
- 194void DbcCommentHandler::ParseCommentLine(
const std::string &line,
DbcDatabase &database) {
-
-
-
-
-
-
- 201 std::istringstream stream(line);
-
-
-
-
-
-
-
-
- 210 if (token ==
"BO_") {
- 211 std::uint32_t rawCanId = 0U;
- 212 std::uint32_t canId = 0U;
- 213 bool isExtended =
false;
-
-
- 216 NormalizeCanId(rawCanId, canId, isExtended);
-
- 218 const std::string::size_type quoteBegin = line.find(
'"');
- 219 const std::string::size_type quoteEnd = line.rfind(
'"');
-
- 221 if ((quoteBegin == std::string::npos) ||
- 222 (quoteEnd == std::string::npos) ||
- 223 (quoteEnd <= quoteBegin))
-
-
- 226 FrameInfo *frame = FindFrameById(database, canId, isExtended);
- 227 if (frame !=
nullptr)
- 228 frame->
comment = line.substr(quoteBegin + 1U, quoteEnd - quoteBegin - 1U);
- 229 }
else if (token ==
"SG_") {
- 230 std::uint32_t rawCanId = 0U;
- 231 std::uint32_t canId = 0U;
- 232 bool isExtended =
false;
- 233 std::string signalName;
-
-
- 236 stream >> signalName;
-
- 238 NormalizeCanId(rawCanId, canId, isExtended);
-
- 240 const std::string::size_type quoteBegin = line.find(
'"');
- 241 const std::string::size_type quoteEnd = line.rfind(
'"');
-
- 243 if ((quoteBegin == std::string::npos) ||
- 244 (quoteEnd == std::string::npos) ||
- 245 (quoteEnd <= quoteBegin))
-
-
- 248 FrameInfo *frame = FindFrameById(database, canId, isExtended);
- 249 if (frame !=
nullptr) {
- 250 SignalInfo *signal = FindSignalByName(*frame, signalName);
- 251 if (signal !=
nullptr)
- 252 signal->
comment = line.substr(quoteBegin + 1U, quoteEnd - quoteBegin - 1U);
-
-
-
-
-
-
-
-
- 261 for (std::size_t index = 0U; index < database.
frames.size(); ++index) {
- 262 if ((database.
frames[index].canId == canId) &&
- 263 (database.
frames[index].isExtended == isExtended))
- 264 return &database.
frames[index];
-
-
-
-
-
-
- 271 for (std::size_t index = 0U; index < frame.signals.size(); ++index) {
- 272 if (frame.signals[index].
name == signalName)
- 273 return &frame.signals[index];
-
-
-
-
-
-
- 280void DbcFrameHandler::Handle(
const std::string &line,
DbcParseContext &context)
const {
- 281 context.database.
frames.push_back(ParseFrameLine(line));
- 282 context.currentFrame = &context.database.
frames.back();
-
-
- 285void DbcSignalHandler::Handle(
const std::string &line,
DbcParseContext &context)
const {
- 286 if (context.currentFrame ==
nullptr)
- 287 throw std::runtime_error(
"Signal found before any frame definition.");
-
- 289 const std::uint64_t payloadBits =
static_cast<std::uint64_t
>(context.currentFrame->
dlc) * 8U;
- 290 std::uint64_t bit = signal.
startBit;
- 291 for (std::uint32_t index = 0U; index < signal.
length; ++index) {
- 292 if (bit >= payloadBits)
- 293 throw std::runtime_error(
"Signal exceeds the frame payload length.");
-
-
-
- 297 bit = (bit % 8U == 0U) ? bit + 15U : bit - 1U;
-
- 299 context.currentFrame->signals.push_back(std::move(signal));
-
-
- 302void DbcCommentHandler::Handle(
const std::string &line,
DbcParseContext &context)
const {
- 303 if (line.compare(0U, 4U,
"CM_ ") == 0)
- 304 ParseCommentLine(line, context.database);
-
-
- 307DbcStatementDispatcher::DbcStatementDispatcher() {
- 308 RegisterType<DbcFrameHandler>(
"BO_");
- 309 RegisterType<DbcSignalHandler>(
"SG_");
- 310 RegisterType<DbcCommentHandler>(
"CM_");
-
-
-
-
-
-
315 std::istringstream stream(line);
-
-
-
318 const auto handler = m_handlers.find(keyword);
-
319 if (handler == m_handlers.end())
-
-
321 handler->second->Handle(line, context);
-
-
+
+
110 pgn = (dp << 16U) | (pf << 8U);
+
+
112 pgn = (dp << 16U) | (pf << 8U) | ps;
+
+
+
+
+
+
+
+
120FrameInfo DbcFrameHandler::ParseFrameLine(
const std::string &line) {
+
+
+
+
+
+
126 std::istringstream stream(line);
+
+
+
129 if (!(stream >> keyword >> idText) || keyword !=
"BO_")
+
130 throw std::runtime_error(
"Missing CAN ID in frame definition.");
+
+
132 const std::uint32_t rawCanId = ParseDecimal(
+
133 idText, std::numeric_limits<std::uint32_t>::max(),
"CAN ID");
+
+
135 if ((rawCanId & 0x60000000U) != 0U)
+
136 throw std::runtime_error(
"CAN ID exceeds the 29-bit identifier range.");
+
+
138 std::string nameText;
+
139 if (!std::getline(stream, nameText,
':') || stream.eof())
+
140 throw std::runtime_error(
"Missing ':' in frame definition.");
+
141 std::istringstream nameStream(nameText);
+
+
+
144 if (!(nameStream >> frame.
name) || (nameStream >> extra))
+
145 throw std::runtime_error(
"Missing or invalid frame name.");
+
+
147 std::string lengthText;
+
148 if (!(stream >> lengthText))
+
149 throw std::runtime_error(
"Missing payload length in frame definition.");
+
150 frame.
dlc =
static_cast<std::uint8_t
>(ParseDecimal(
+
151 lengthText, std::numeric_limits<std::uint8_t>::max(),
"payload length"));
+
+
153 throw std::runtime_error(
"Missing transmitter in frame definition.");
+
+
155 throw std::runtime_error(
"Unexpected trailing field in frame definition: " + extra);
+
+
+
+
+
+
+
+
163SignalInfo DbcSignalHandler::ParseSignalLine(
const std::string &line) {
+
+
165 static const std::regex syntax(
+
166 R
"dbc(^SG_[ \t]+([^:]+):[ \t]*([^| \t]+)[ \t]*\|[ \t]*([^@ \t]+)[ \t]*@[ \t]*([01])([+-])[ \t]*\([ \t]*([^, \t]+)[ \t]*,[ \t]*([^ )\t]+)[ \t]*\)[ \t]*\[[ \t]*([^| \t]+)[ \t]*\|[ \t]*([^\] \t]+)[ \t]*\][ \t]*"([^"\r\n]*)"[ \t]+([^ \t,]+(?:[ \t]*,[ \t]*[^ \t,]+)*)[ \t]*$)dbc");
+
+
168 if (!std::regex_match(line, fields, syntax))
+
169 throw std::runtime_error(
"Malformed or unsupported SG_ signal definition.");
+
+
+
172 signal.
name = TrimText(fields[1].str());
+
173 if (signal.
name.empty() || signal.
name.find_first_of(
" \t\r\n") != std::string::npos)
+
174 throw std::runtime_error(
"Invalid signal name or unsupported multiplexing syntax.");
+
+
176 signal.
startBit = ParseDecimal(fields[2].str(),
+
177 std::numeric_limits<std::uint32_t>::max(),
"signal start bit");
+
178 signal.
length = ParseDecimal(fields[3].str(), 64U,
"signal length");
+
+
180 throw std::runtime_error(
"Signal length must be within 1..64 bits.");
+
+
182 signal.
isSigned = fields[5].str() ==
"-";
+
+
184 signal.
factor = ParsePhysicalValue(fields[6].str(),
"signal factor");
+
185 signal.
offset = ParsePhysicalValue(fields[7].str(),
"signal offset");
+
186 signal.
minimum = ParsePhysicalValue(fields[8].str(),
"signal minimum");
+
187 signal.
maximum = ParsePhysicalValue(fields[9].str(),
"signal maximum");
+
+
189 throw std::runtime_error(
"Signal minimum exceeds maximum.");
+
190 signal.
unit = fields[10].str();
+
191 signal.
receivers = SplitReceivers(fields[11].str());
+
+
+
+
195void DbcCommentHandler::ParseCommentLine(
const std::string &line,
DbcDatabase &database) {
+
+
+
+
+
+
+
202 std::istringstream stream(line);
+
+
+
+
+
+
+
+
+
211 if (token ==
"BO_") {
+
212 std::uint32_t rawCanId = 0U;
+
213 std::uint32_t canId = 0U;
+
214 bool isExtended =
false;
+
+
+
217 NormalizeCanId(rawCanId, canId, isExtended);
+
+
219 const std::string::size_type quoteBegin = line.find(
'"');
+
220 const std::string::size_type quoteEnd = line.rfind(
'"');
+
+
222 if ((quoteBegin == std::string::npos) ||
+
223 (quoteEnd == std::string::npos) ||
+
224 (quoteEnd <= quoteBegin))
+
+
+
227 FrameInfo *frame = FindFrameById(database, canId, isExtended);
+
228 if (frame !=
nullptr)
+
229 frame->
comment = line.substr(quoteBegin + 1U, quoteEnd - quoteBegin - 1U);
+
230 }
else if (token ==
"SG_") {
+
231 std::uint32_t rawCanId = 0U;
+
232 std::uint32_t canId = 0U;
+
233 bool isExtended =
false;
+
234 std::string signalName;
+
+
+
237 stream >> signalName;
+
+
239 NormalizeCanId(rawCanId, canId, isExtended);
+
+
241 const std::string::size_type quoteBegin = line.find(
'"');
+
242 const std::string::size_type quoteEnd = line.rfind(
'"');
+
+
244 if ((quoteBegin == std::string::npos) ||
+
245 (quoteEnd == std::string::npos) ||
+
246 (quoteEnd <= quoteBegin))
+
+
+
249 FrameInfo *frame = FindFrameById(database, canId, isExtended);
+
250 if (frame !=
nullptr) {
+
251 SignalInfo *signal = FindSignalByName(*frame, signalName);
+
252 if (signal !=
nullptr)
+
253 signal->
comment = line.substr(quoteBegin + 1U, quoteEnd - quoteBegin - 1U);
+
+
+
+
+
+
+
+
+
262 for (std::size_t index = 0U; index < database.
frames.size(); ++index) {
+
263 if ((database.
frames[index].canId == canId) &&
+
264 (database.
frames[index].isExtended == isExtended))
+
265 return &database.
frames[index];
+
+
+
+
+
+
+
272 for (std::size_t index = 0U; index < frame.signals.size(); ++index) {
+
273 if (frame.signals[index].
name == signalName)
+
274 return &frame.signals[index];
+
+
+
+
+
+
+
281void DbcFrameHandler::Handle(
const std::string &line,
DbcParseContext &context)
const {
+
282 context.database.
frames.push_back(ParseFrameLine(line));
+
283 context.currentFrame = &context.database.
frames.back();
+
+
+
286void DbcSignalHandler::Handle(
const std::string &line,
DbcParseContext &context)
const {
+
287 if (context.currentFrame ==
nullptr)
+
288 throw std::runtime_error(
"Signal found before any frame definition.");
+
+
290 const std::uint64_t payloadBits =
static_cast<std::uint64_t
>(context.currentFrame->
dlc) * 8U;
+
291 std::uint64_t bit = signal.
startBit;
+
292 for (std::uint32_t index = 0U; index < signal.
length; ++index) {
+
293 if (bit >= payloadBits)
+
294 throw std::runtime_error(
"Signal exceeds the frame payload length.");
+
+
+
+
298 bit = (bit % 8U == 0U) ? bit + 15U : bit - 1U;
+
+
300 context.currentFrame->signals.push_back(std::move(signal));
+
+
+
303void DbcCommentHandler::Handle(
const std::string &line,
DbcParseContext &context)
const {
+
304 if (line.compare(0U, 4U,
"CM_ ") == 0)
+
305 ParseCommentLine(line, context.database);
+
+
+
308DbcStatementDispatcher::DbcStatementDispatcher() {
+
309 RegisterType<DbcFrameHandler>(
"BO_");
+
310 RegisterType<DbcSignalHandler>(
"SG_");
+
311 RegisterType<DbcCommentHandler>(
"CM_");
+
+
+
+
+
+
316 std::istringstream stream(line);
+
+
+
319 const auto handler = m_handlers.find(keyword);
+
320 if (handler == m_handlers.end())
+
+
322 handler->second->Handle(line, context);
+
+
-
bool Dispatch(const std::string &line, DbcParseContext &context) const
+
bool Dispatch(const std::string &line, DbcParseContext &context) const
Parsed DBC content stored in a simple internal form.
std::vector< FrameInfo > frames
diff --git a/docs/html/dbc__statement__handler_8h_source.html b/docs/html/dbc__statement__handler_8h_source.html
index 5466c66..a441043 100644
--- a/docs/html/dbc__statement__handler_8h_source.html
+++ b/docs/html/dbc__statement__handler_8h_source.html
@@ -176,7 +176,7 @@ $(document).ready(function(){initNavTree('dbc__statement__handler_8h_source.html
-
bool Dispatch(const std::string &line, DbcParseContext &context) const
+
bool Dispatch(const std::string &line, DbcParseContext &context) const
Registry-based factory for creating objects by runtime identifier.
BasePtr create(const ClassId &id, Args... args) const
Creates an instance for the given id using provided arguments.
diff --git a/docs/html/main_8cpp.html b/docs/html/main_8cpp.html
index 1907ad1..dfe9b86 100644
--- a/docs/html/main_8cpp.html
+++ b/docs/html/main_8cpp.html
@@ -147,7 +147,7 @@ Functions
diff --git a/docs/html/main_8cpp_source.html b/docs/html/main_8cpp_source.html
index 5ca134b..1c3bec7 100644
--- a/docs/html/main_8cpp_source.html
+++ b/docs/html/main_8cpp_source.html
@@ -178,53 +178,56 @@ $(document).ready(function(){initNavTree('main_8cpp_source.html',''); initResiza
- 91 std::visit([](
auto rawValue) { std::cout << rawValue; }, signal.
rawValue);
-
-
-
-
-
-
- 98int main(
int argc,
char *argv[]) {
-
- 100 std::cerr <<
"Usage: dbc_demo <file.dbc>\n";
-
-
-
-
-
-
-
-
- 109 std::unique_ptr<TreeNode> root = treeBuilder.
Build(database);
+ 91 if (
const auto *signedValue = std::get_if<std::int64_t>(&signal.
rawValue))
+ 92 std::cout << *signedValue;
+
+ 94 std::cout << std::get<std::uint64_t>(signal.
rawValue);
+
+
+
+
+
+
+ 101int main(
int argc,
char *argv[]) {
+
+ 103 std::cerr <<
"Usage: dbc_demo <file.dbc>\n";
+
+
+
+
+
+
- 111 std::cout <<
"=== Parsed tree ===\n";
- 112 PrintTree(root.get(), 0);
+
+ 112 std::unique_ptr<TreeNode> root = treeBuilder.
Build(database);
-
-
+ 114 std::cout <<
"=== Parsed tree ===\n";
+ 115 PrintTree(root.get(), 0);
-
-
-
-
-
- 122 rawFrame.
canId = decodeDatabase.frames.empty() ? 0U : decodeDatabase.frames[0].canId;
- 123 rawFrame.
isExtended = decodeDatabase.frames.empty() ? false : decodeDatabase.frames[0].isExtended;
- 124 rawFrame.
data.resize(8U, 0U);
-
-
-
+
+
+
+
+
+
+
+
+ 125 rawFrame.
canId = decodeDatabase.frames.empty() ? 0U : decodeDatabase.frames[0].canId;
+ 126 rawFrame.
isExtended = decodeDatabase.frames.empty() ? false : decodeDatabase.frames[0].isExtended;
+ 127 rawFrame.
data.resize(8U, 0U);
- 129 std::cout <<
"\n=== Decoded frame ===\n";
- 130 PrintDecodedFrame(decoded);
- 131 }
catch (
const std::exception &ex) {
- 132 std::cerr <<
"Error: " << ex.what() <<
"\n";
-
-
-
-
-
+
+
+
+ 132 std::cout <<
"\n=== Decoded frame ===\n";
+ 133 PrintDecodedFrame(decoded);
+ 134 }
catch (
const std::exception &ex) {
+ 135 std::cerr <<
"Error: " << ex.what() <<
"\n";
+
+
+
+
+
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.
diff --git a/docs/html/md_README.html b/docs/html/md_README.html
index 162256a..ed88b20 100644
--- a/docs/html/md_README.html
+++ b/docs/html/md_README.html
@@ -441,8 +441,12 @@ Decoder Layer
Raw value representation and precision
DecodedSignalValue::rawValue is a C++17 std::variant<std::int64_t, std::uint64_t>. For a valid signal, signed definitions produce the int64_t alternative and unsigned definitions produce the uint64_t alternative. Raw values remain exact across their full 64-bit ranges. Check valid before using the value; an invalid result's default value is not decoded data.
-Use std::get when the numeric type is known, or std::visit to handle both:
-std::visit([] (auto raw) { std::cout << raw; }, decodedSignal.rawValue);
+
Use std::get when the numeric type is known, or std::get_if to handle both:
+
if (const auto *value = std::get_if<std::int64_t>(&decodedSignal.rawValue)) {
+
std::cout << *value;
+
} else {
+
std::cout << std::get<std::uint64_t>(decodedSignal.rawValue);
+
}
This replaces the previous int64_t field: consumers that read or print rawValue directly must handle the variant.
Physical conversion uses the active numeric type: physicalValue = static_cast<double>(raw) * factor + offset. physicalValue remains a double and is approximate for large integers. On usual IEEE 754 binary64 implementations, integers are exactly representable through 2^53; above that, some integers round to the same floating-point value. For example, unsigned UINT64_MAX remains exact in rawValue, but converts to approximately 18446744073709551616 in binary64 before scaling. Use rawValue whenever exact integer values are required.