2#include "dbc_statement_handler.h"
4#include "multiplex_graph.h"
5#include "signal_layout.h"
14 std::uint32_t ParseDecimal(
const std::string &text,
15 std::uint32_t maximum,
17 std::uint32_t value = 0U;
18 const std::from_chars_result parsed = std::from_chars(text.data(), text.data() + text.size(), value);
19 if (text.empty() || parsed.ec != std::errc() ||
20 parsed.ptr != text.data() + text.size() || value > maximum)
21 throw std::runtime_error(std::string(
"Invalid ") + field +
": " + text);
25 std::uint32_t ParseCanId(
const std::string &text) {
26 const std::uint32_t rawId = ParseDecimal(
27 text, std::numeric_limits<std::uint32_t>::max(),
"CAN ID");
29 if ((rawId & 0x60000000U) != 0U)
30 throw std::runtime_error(
"CAN ID exceeds the 29-bit identifier range.");
34 double ParsePhysicalValue(
const std::string &text,
const char *field) {
35 if (text.empty() || (text[0] ==
'+' &&
36 (text.size() == 1U || text[1] ==
'+' || text[1] ==
'-')))
37 throw std::runtime_error(std::string(
"Invalid ") + field +
": " + text);
40 const char *begin = text.data();
41 const char *end = begin + text.size();
45 const std::from_chars_result parsed =
46 std::from_chars(begin, end, value, std::chars_format::general);
48 if (parsed.ec != std::errc() || parsed.ptr != end || !std::isfinite(value) ||
49 std::fpclassify(value) == FP_SUBNORMAL)
50 throw std::runtime_error(std::string(
"Invalid ") + field +
": " + text);
57 SignalTokens(
const std::string &text, std::size_t position) : text(text), position(position) {}
60 while (position < text.size() && (text[position] ==
' ' || text[position] ==
'\t'))
65 if (position == text.size())
66 throw std::runtime_error(
"Incomplete SG_ signal definition.");
67 return text[position++];
70 void Expect(
char expected) {
72 if (Character() != expected)
73 throw std::runtime_error(
"Malformed SG_ signal definition.");
76 std::string Until(
char delimiter) {
77 const std::size_t end = text.find(delimiter, position);
78 if (end == std::string::npos)
79 throw std::runtime_error(
"Missing delimiter in SG_ signal definition.");
80 const std::string value = text.substr(position, end - position);
85 std::vector<std::string> Receivers() {
86 if (position == text.size() || (text[position] !=
' ' && text[position] !=
'\t'))
87 throw std::runtime_error(
"Missing receiver separator in SG_ signal definition.");
88 std::vector<std::string> receivers;
90 const std::size_t end = text.find(
',', position);
91 const std::string receiver = dbc_detail::TrimText(text.substr(position, end - position));
92 if (receiver.empty() || receiver.find_first_of(
" \t\r\n") != std::string::npos)
93 throw std::runtime_error(
"Invalid SG_ receiver list.");
94 receivers.push_back(receiver);
95 if (end == std::string::npos)
102 const std::string &text;
103 std::size_t position = 0U;
106 Multiplexing ParseMultiplexingMarker(
const std::string &marker) {
109 result.role = MultiplexRole::Selector;
110 else if (!marker.empty()) {
111 if (marker[0] !=
'm')
112 throw std::runtime_error(
"Unsupported multiplexing marker.");
113 const bool nested = marker.back() ==
'M';
114 const std::string value = marker.substr(1U, marker.size() - (nested ? 2U : 1U));
115 const std::from_chars_result parsed =
116 std::from_chars(value.data(), value.data() + value.size(), result.
value);
117 if (value.empty() || parsed.ec != std::errc() || parsed.ptr != value.data() + value.size())
118 throw std::runtime_error(
"Invalid or unsupported multiplexing marker.");
119 result.role = nested ? MultiplexRole::BranchSelector : MultiplexRole::Branch;
125 RawSignalValue ParseValueKey(
const std::string &number) {
127 if (!number.empty() && number[0] ==
'-') {
128 std::int64_t value = 0;
129 const std::from_chars_result parsed =
130 std::from_chars(number.data(), number.data() + number.size(), value);
131 if (parsed.ec != std::errc() || parsed.ptr != number.data() + number.size())
132 throw std::runtime_error(
"Invalid VAL_ integer: " + number);
133 key = NormalizeValueKey(RawSignalValue(value));
135 std::uint64_t value = 0;
136 const std::from_chars_result parsed =
137 std::from_chars(number.data(), number.data() + number.size(), value);
138 if (number.empty() || parsed.ec != std::errc() || parsed.ptr != number.data() + number.size())
139 throw std::runtime_error(
"Invalid VAL_ integer: " + number);
146 class StatementTokens {
148 StatementTokens(
const std::string &text,
const std::string &keyword)
149 : text(text), keyword(keyword) {
150 if (Word() != keyword)
156 const std::size_t begin = position;
157 while (position < text.size() && (text[position] ==
' ' || text[position] ==
'\t'))
159 return position != begin;
162 void RequireSpace() {
167 bool Consume(
char character) {
168 if (position == text.size() || text[position] != character)
174 void Expect(
char character) {
176 if (!Consume(character))
181 const std::size_t begin = position;
182 while (position < text.size() && text[position] !=
' ' && text[position] !=
'\t' &&
183 text[position] !=
';' && text[position] !=
'"')
185 if (position == begin)
187 return text.substr(begin, position - begin);
190 std::uint64_t Unsigned() {
192 const std::size_t begin = position;
193 while (position < text.size() && text[position] >=
'0' && text[position] <=
'9')
195 std::uint64_t value = 0U;
196 const std::from_chars_result parsed =
197 std::from_chars(text.data() + begin, text.data() + position, value);
198 if (position == begin || parsed.ec != std::errc() || parsed.ptr != text.data() + position)
203 std::string Description() {
206 std::string description;
207 while (position < text.size()) {
208 char character = text[position++];
209 if (character ==
'"')
211 if (character ==
'\\') {
212 if (position == text.size())
214 character = text[position++];
215 if (character !=
'"' && character !=
'\\')
218 description += character;
225 if (position != text.size())
230 [[noreturn]]
void Fail()
const {
231 throw std::runtime_error(
"Malformed " + keyword +
" statement.");
234 const std::string &text;
235 const std::string keyword;
236 std::size_t position = 0U;
239 FrameInfo *FindUniqueFrame(
DbcDatabase &database, std::uint32_t canId,
bool isExtended,
const std::string &statement =
"VAL_") {
241 for (
FrameInfo &candidate : database.frames) {
242 if (candidate.canId == canId && candidate.isExtended == isExtended) {
244 throw std::runtime_error(
"Ambiguous " + statement +
" frame reference.");
249 throw std::runtime_error(
"Unresolved " + statement +
" frame reference.");
253 SignalInfo *FindUniqueSignal(
FrameInfo &frame,
const std::string &signalName,
const std::string &statement =
"VAL_") {
256 if (candidate.name == signalName) {
258 throw std::runtime_error(
"Ambiguous " + statement +
" signal reference: " + signalName);
263 throw std::runtime_error(
"Unresolved " + statement +
" signal reference: " + signalName);
267 std::uint64_t NextSignalBit(std::uint64_t bit,
bool isLittleEndian) {
271 return bit % 8U == 0U ? bit + 15U : bit - 1U;
279 void NormalizeCanId(std::uint32_t rawCanId, std::uint32_t &normalizedCanId,
bool &isExtended) {
285 isExtended = (rawCanId & 0x80000000U) != 0U || rawCanId > 0x7FFU;
286 normalizedCanId = rawCanId & 0x1FFFFFFFU;
290FrameInfo DbcFrameHandler::ParseFrameLine(
const std::string &line) {
296 std::istringstream stream(line);
299 if (!(stream >> keyword >> idText) || keyword !=
"BO_")
300 throw std::runtime_error(
"Missing CAN ID in frame definition.");
302 const std::uint32_t rawCanId = ParseCanId(idText);
304 std::string nameText;
305 if (!std::getline(stream, nameText,
':') || stream.eof())
306 throw std::runtime_error(
"Missing ':' in frame definition.");
307 std::istringstream nameStream(nameText);
310 if (!(nameStream >> frame.
name) || (nameStream >> extra))
311 throw std::runtime_error(
"Missing or invalid frame name.");
313 std::string lengthText;
314 if (!(stream >> lengthText))
315 throw std::runtime_error(
"Missing payload length in frame definition.");
316 frame.
dlc =
static_cast<std::uint8_t
>(ParseDecimal(
317 lengthText, std::numeric_limits<std::uint8_t>::max(),
"payload length"));
319 throw std::runtime_error(
"Missing transmitter in frame definition.");
321 throw std::runtime_error(
"Unexpected trailing field in frame definition: " + extra);
328SignalInfo DbcSignalHandler::ParseSignalLine(
const std::string &line) {
329 if (line.size() < 4U || line.compare(0U, 3U,
"SG_") != 0 ||
330 (line[3] !=
' ' && line[3] !=
'\t'))
331 throw std::runtime_error(
"Malformed SG_ signal definition.");
332 SignalTokens tokens(line, 3U);
334 std::istringstream nameFields(tokens.Until(
':'));
337 nameFields >> signal.
name >> marker;
338 if (signal.
name.empty() || (nameFields >> extra))
339 throw std::runtime_error(
"Invalid signal name or multiplexing marker.");
342 signal.
startBit = ParseDecimal(dbc_detail::TrimText(tokens.Until(
'|')),
343 std::numeric_limits<std::uint32_t>::max(),
"signal start bit");
344 signal.
length = ParseDecimal(dbc_detail::TrimText(tokens.Until(
'@')), 64U,
"signal length");
346 throw std::runtime_error(
"Signal length must be within 1..64 bits.");
348 const char byteOrder = tokens.Character();
349 const char signedness = tokens.Character();
350 if ((byteOrder !=
'0' && byteOrder !=
'1') || (signedness !=
'+' && signedness !=
'-'))
351 throw std::runtime_error(
"Invalid SG_ byte order or signedness.");
353 signal.
isSigned = signedness ==
'-';
356 signal.
factor = ParsePhysicalValue(dbc_detail::TrimText(tokens.Until(
',')),
"signal factor");
357 signal.
offset = ParsePhysicalValue(dbc_detail::TrimText(tokens.Until(
')')),
"signal offset");
359 signal.
minimum = ParsePhysicalValue(dbc_detail::TrimText(tokens.Until(
'|')),
"signal minimum");
360 signal.
maximum = ParsePhysicalValue(dbc_detail::TrimText(tokens.Until(
']')),
"signal maximum");
362 throw std::runtime_error(
"Signal minimum exceeds maximum.");
364 signal.
unit = tokens.Until(
'"');
365 if (signal.
unit.find_first_of(
"\r\n") != std::string::npos)
366 throw std::runtime_error(
"Invalid newline in SG_ unit.");
376 for (
FrameInfo &frame : database.frames) {
386 if (signal.
name == signalName)
398 frame.
pgn = ExtractJ1939Pgn(frame.
canId);
401 throw std::runtime_error(
"Duplicate " + std::string(frame.
isExtended ?
"extended" :
"standard") +
402 " frame CAN ID: " + std::to_string(frame.
canId));
410 throw std::runtime_error(
"Signal found before any frame definition.");
413 throw std::runtime_error(
"Duplicate signal name '" + signal.
name +
417 throw std::runtime_error(
"Signal exceeds the frame payload length.");
418 context.
currentFrame->signals.push_back(std::move(signal));
423 std::istringstream stream(line);
426 stream >> keyword >> target;
427 if (keyword !=
"CM_" || (target !=
"BO_" && target !=
"SG_"))
434 if (!(stream >> idText))
435 throw std::runtime_error(
"Missing CAN ID in CM_ statement.");
438 pending.
signalName.find_first_of(
"\";") != std::string::npos))
439 throw std::runtime_error(
"Missing or invalid signal name in CM_ statement.");
442 if (stream.get() !=
'"')
443 throw std::runtime_error(
"Missing opening quote in CM_ statement.");
446 while (stream.get(character)) {
447 if (character ==
'"') {
451 if (character ==
'\\') {
452 if (!stream.get(character) || (character !=
'"' && character !=
'\\'))
453 throw std::runtime_error(
"Invalid escape in CM_ statement.");
458 throw std::runtime_error(
"Unterminated CM_ comment.");
460 if (stream.get() !=
';')
461 throw std::runtime_error(
"Missing semicolon in CM_ statement.");
464 throw std::runtime_error(
"Unexpected trailing text in CM_ statement.");
473 pending.lineNumber,
"Unresolved CM_ frame reference.");
474 if (pending.isSignal) {
475 SignalInfo *signal = FindSignalByName(*frame, pending.signalName);
478 pending.lineNumber,
"Unresolved CM_ signal reference: " + pending.signalName);
479 signal->
comment = pending.comment;
481 frame->
comment = pending.comment;
487 RegisterType<DbcHeaderHandler>(
"VERSION");
488 RegisterType<DbcHeaderHandler>(
"NS_");
489 RegisterType<DbcHeaderHandler>(
"NS_:");
490 RegisterType<DbcHeaderHandler>(
"BS_");
491 RegisterType<DbcHeaderHandler>(
"BS_:");
492 RegisterType<DbcHeaderHandler>(
"BU_");
493 RegisterType<DbcHeaderHandler>(
"BU_:");
494 RegisterType<DbcMultiplexingHandler>(
"SG_MUL_VAL_");
495 RegisterType<DbcFrameHandler>(
"BO_");
496 RegisterType<DbcSignalHandler>(
"SG_");
497 RegisterType<DbcUnsupportedSignalTypeHandler>(
"SIG_VALTYPE_");
498 RegisterType<DbcCommentHandler>(
"CM_");
499 RegisterType<DbcValueDescriptionHandler>(
"VAL_");
500 RegisterType<DbcAttributeHandler>(
"BA_DEF_");
501 RegisterType<DbcAttributeHandler>(
"BA_DEF_DEF_");
502 RegisterType<DbcAttributeHandler>(
"BA_");
506 throw std::runtime_error(
"SIG_VALTYPE_ declarations are unsupported; refusing potentially incorrect integer decoding.");
513 std::istringstream stream(line);
516 const HandlerMap::const_iterator handler = m_handlers.find(keyword);
517 if (handler == m_handlers.end())
519 handler->second->Handle(line, context);
524 StatementTokens tokens(line,
"VAL_");
525 const std::uint32_t rawId = ParseCanId(tokens.Word());
526 tokens.RequireSpace();
530 tokens.RequireSpace();
532 while (!tokens.Consume(
';')) {
533 const std::string number = tokens.Word();
534 const RawSignalValue key = ParseValueKey(number);
535 tokens.RequireSpace();
536 const std::string description = tokens.Description();
537 if (!pending.
descriptions.emplace(key, description).second)
538 throw std::runtime_error(
"Duplicate VAL_ value key: " + number);
539 const bool separated = tokens.SkipSpaces();
540 if (tokens.Consume(
';'))
543 throw std::runtime_error(
"Missing whitespace between VAL_ pairs.");
547 throw std::runtime_error(
"VAL_ requires at least one value/description pair.");
555 SignalInfo *signal = FindUniqueSignal(*frame, pending.signalName);
556 for (
const ValueDescriptions::value_type &entry: pending.descriptions) {
558 throw std::runtime_error(
"Duplicate VAL_ value key for signal: " + pending.signalName);
560 }
catch (
const std::exception &error) {
562 filePath, pending.lineNumber, error.what());
568 StatementTokens tokens(line,
"SG_MUL_VAL_");
570 NormalizeCanId(ParseCanId(tokens.Word()), pending.canId, pending.isExtended);
571 tokens.RequireSpace();
572 pending.signalName = tokens.Word();
573 tokens.RequireSpace();
574 pending.selectorName = tokens.Word();
575 tokens.RequireSpace();
579 range.minimum = tokens.Unsigned();
581 range.maximum = tokens.Unsigned();
582 if (range.minimum > range.maximum)
583 throw std::runtime_error(
"Invalid multiplex range.");
584 pending.ranges.push_back(range);
586 if (tokens.Consume(
';'))
591 context.pendingMultiplexing.push_back(std::move(pending));
594void DbcParseContext::ResolveMultiplexing(
const std::string &filePath) {
597 FrameInfo *frame = FindUniqueFrame(
database, pending.canId, pending.isExtended,
"SG_MUL_VAL_");
598 SignalInfo *signal = FindUniqueSignal(*frame, pending.signalName,
"SG_MUL_VAL_");
599 SignalInfo *selector = FindUniqueSignal(*frame, pending.selectorName,
"SG_MUL_VAL_");
601 throw std::runtime_error(
"Referenced signal must be an unsigned multiplex selector.");
603 throw std::runtime_error(
"Multiple parent selectors for one signal are unsupported.");
605 if (selector->
length < 64U && range.maximum >= (std::uint64_t(1U) << selector->
length))
606 throw std::runtime_error(
"Branch value exceeds selector raw range.");
613 signal->
multiplexing.role = MultiplexRole::BranchSelector;
614 }
catch (
const std::exception &error) {
615 throw DbcDiagnosticError(DbcDiagnosticCode::InvalidMultiplexing, filePath, pending.lineNumber, error.what());
621 std::vector<std::uint64_t> SignalBits(
const SignalInfo &signal) {
622 std::vector<std::uint64_t> bits;
623 std::uint64_t bit = signal.
startBit;
624 for (std::uint32_t index = 0U; index < signal.
length; ++index) {
632 const std::vector<std::uint64_t> firstBits = SignalBits(first);
633 const std::vector<std::uint64_t> secondBits = SignalBits(second);
634 for (
const std::uint64_t firstBit : firstBits) {
635 for (
const std::uint64_t secondBit : secondBits) {
636 if (firstBit == secondBit)
645 for (std::size_t frameIndex = 0U; frameIndex <
database.
frames.size(); ++frameIndex) {
649 for (std::size_t index = 0U; index < frame.signals.size(); ++index) {
650 const SignalInfo &signal = frame.signals[index];
653 for (std::size_t previous = 0U; previous < index; ++previous) {
654 if (!graph.AreMutuallyExclusive(index, previous) && SignalsOverlap(signal, frame.signals[previous]))
660 signalLines[frameIndex][error.index], error.what());
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.
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.
std::vector< FrameInfo > frames
std::vector< PendingComment > pendingComments
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.
std::vector< PendingValueDescriptions > pendingValues
void ResolveComments(const std::string &filePath)
Resolve deferred comments after all definitions are known.
std::vector< std::vector< std::size_t > > signalLines
Describes one CAN frame from a DBC file.
Inclusive raw selector range, without expanding individual values.
Basic markers and optional explicit extended multiplexing condition.
std::vector< MultiplexRange > ranges
Deferred extended multiplexing condition and source location.
Deferred raw value descriptions and their target.
ValueDescriptions descriptions
Describes one signal inside a DBC frame.
std::vector< std::string > receivers
ValueDescriptions valueDescriptions
Multiplexing multiplexing