DBC Framework
DBC parsing and CAN signal decoding framework
Loading...
Searching...
No Matches
dbc_statement_handler.cpp
1// SPDX-License-Identifier: MIT
2#include "dbc_statement_handler.h"
3#include "j1939.h"
4#include "multiplex_graph.h"
5#include "signal_layout.h"
6#include "text_utils.h"
7#include <charconv>
8#include <cmath>
9#include <limits>
10#include <sstream>
11#include <stdexcept>
12
13namespace {
14 std::uint32_t ParseDecimal(const std::string &text,
15 std::uint32_t maximum,
16 const char *field) {
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);
22 return value;
23 }
24
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");
28 // Only bit 31 may accompany the actual 29-bit identifier.
29 if ((rawId & 0x60000000U) != 0U)
30 throw std::runtime_error("CAN ID exceeds the 29-bit identifier range.");
31 return rawId;
32 }
33
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);
38 // Floating-point from_chars is locale-independent and does not accept
39 // a leading '+', which the DBC numeric grammar permits.
40 const char *begin = text.data();
41 const char *end = begin + text.size();
42 if (*begin == '+')
43 ++begin;
44 double value = 0.0;
45 const std::from_chars_result parsed =
46 std::from_chars(begin, end, value, std::chars_format::general);
47 // Keep the existing rejection of conversion underflow into subnormal values.
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);
51 return value;
52 }
53
54 // Iterative scanning keeps stack use independent of the statement length.
55 class SignalTokens {
56 public:
57 SignalTokens(const std::string &text, std::size_t position) : text(text), position(position) {}
58
59 void SkipSpaces() {
60 while (position < text.size() && (text[position] == ' ' || text[position] == '\t'))
61 ++position;
62 }
63
64 char Character() {
65 if (position == text.size())
66 throw std::runtime_error("Incomplete SG_ signal definition.");
67 return text[position++];
68 }
69
70 void Expect(char expected) {
71 SkipSpaces();
72 if (Character() != expected)
73 throw std::runtime_error("Malformed SG_ signal definition.");
74 }
75
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);
81 position = end + 1U;
82 return value;
83 }
84
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;
89 for (;;) {
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)
96 return receivers;
97 position = end + 1U;
98 }
99 }
100
101 private:
102 const std::string &text;
103 std::size_t position = 0U;
104 };
105
106 Multiplexing ParseMultiplexingMarker(const std::string &marker) {
107 Multiplexing result;
108 if (marker == "M")
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;
120 }
121
122 return result;
123 }
124
125 RawSignalValue ParseValueKey(const std::string &number) {
126 RawSignalValue key;
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));
134 } else {
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);
140 key = value;
141 }
142 return key;
143 }
144
145 // VAL_ and SG_MUL_VAL_ share a small, forward-only statement reader.
146 class StatementTokens {
147 public:
148 StatementTokens(const std::string &text, const std::string &keyword)
149 : text(text), keyword(keyword) {
150 if (Word() != keyword)
151 Fail();
152 RequireSpace();
153 }
154
155 bool SkipSpaces() {
156 const std::size_t begin = position;
157 while (position < text.size() && (text[position] == ' ' || text[position] == '\t'))
158 ++position;
159 return position != begin;
160 }
161
162 void RequireSpace() {
163 if (!SkipSpaces())
164 Fail();
165 }
166
167 bool Consume(char character) {
168 if (position == text.size() || text[position] != character)
169 return false;
170 ++position;
171 return true;
172 }
173
174 void Expect(char character) {
175 SkipSpaces();
176 if (!Consume(character))
177 Fail();
178 }
179
180 std::string Word() {
181 const std::size_t begin = position;
182 while (position < text.size() && text[position] != ' ' && text[position] != '\t' &&
183 text[position] != ';' && text[position] != '"')
184 ++position;
185 if (position == begin)
186 Fail();
187 return text.substr(begin, position - begin);
188 }
189
190 std::uint64_t Unsigned() {
191 SkipSpaces();
192 const std::size_t begin = position;
193 while (position < text.size() && text[position] >= '0' && text[position] <= '9')
194 ++position;
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)
199 Fail();
200 return value;
201 }
202
203 std::string Description() {
204 if (!Consume('"'))
205 Fail();
206 std::string description;
207 while (position < text.size()) {
208 char character = text[position++];
209 if (character == '"')
210 return description;
211 if (character == '\\') {
212 if (position == text.size())
213 Fail();
214 character = text[position++];
215 if (character != '"' && character != '\\')
216 Fail();
217 }
218 description += character;
219 }
220 Fail();
221 }
222
223 void End() {
224 SkipSpaces();
225 if (position != text.size())
226 Fail();
227 }
228
229 private:
230 [[noreturn]] void Fail() const {
231 throw std::runtime_error("Malformed " + keyword + " statement.");
232 }
233
234 const std::string &text;
235 const std::string keyword;
236 std::size_t position = 0U;
237 };
238
239 FrameInfo *FindUniqueFrame(DbcDatabase &database, std::uint32_t canId, bool isExtended, const std::string &statement = "VAL_") {
240 FrameInfo *frame = nullptr;
241 for (FrameInfo &candidate : database.frames) {
242 if (candidate.canId == canId && candidate.isExtended == isExtended) {
243 if (frame)
244 throw std::runtime_error("Ambiguous " + statement + " frame reference.");
245 frame = &candidate;
246 }
247 }
248 if (!frame)
249 throw std::runtime_error("Unresolved " + statement + " frame reference.");
250 return frame;
251 }
252
253 SignalInfo *FindUniqueSignal(FrameInfo &frame, const std::string &signalName, const std::string &statement = "VAL_") {
254 SignalInfo *signal = nullptr;
255 for (SignalInfo &candidate : frame.signals) {
256 if (candidate.name == signalName) {
257 if (signal)
258 throw std::runtime_error("Ambiguous " + statement + " signal reference: " + signalName);
259 signal = &candidate;
260 }
261 }
262 if (!signal)
263 throw std::runtime_error("Unresolved " + statement + " signal reference: " + signalName);
264 return signal;
265 }
266
267 std::uint64_t NextSignalBit(std::uint64_t bit, bool isLittleEndian) {
268 if (isLittleEndian)
269 return bit + 1U;
270 // From bit 0, Motorola continues at bit 7 of the next byte (0 -> 15).
271 return bit % 8U == 0U ? bit + 15U : bit - 1U;
272 }
273
274 FrameInfo *FindFrameById(DbcDatabase &, std::uint32_t, bool);
275 SignalInfo *FindSignalByName(FrameInfo &, const std::string &);
276} // namespace
277
278namespace {
279 void NormalizeCanId(std::uint32_t rawCanId, std::uint32_t &normalizedCanId, bool &isExtended) {
280 /*
281 * DBC commonly stores extended identifiers with bit 31 set.
282 * Example:
283 * raw id = 0x80000000 | actual_29_bit_id
284 */
285 isExtended = (rawCanId & 0x80000000U) != 0U || rawCanId > 0x7FFU;
286 normalizedCanId = rawCanId & 0x1FFFFFFFU;
287 }
288} // namespace
289
290FrameInfo DbcFrameHandler::ParseFrameLine(const std::string &line) {
291 /*
292 * Example:
293 * BO_ 256 EngineData: 8 EEC1
294 */
295
296 std::istringstream stream(line);
297 std::string keyword;
298 std::string idText;
299 if (!(stream >> keyword >> idText) || keyword != "BO_")
300 throw std::runtime_error("Missing CAN ID in frame definition.");
301
302 const std::uint32_t rawCanId = ParseCanId(idText);
303
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);
308 FrameInfo frame;
309 std::string extra;
310 if (!(nameStream >> frame.name) || (nameStream >> extra))
311 throw std::runtime_error("Missing or invalid frame name.");
312
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"));
318 if (!(stream >> frame.transmitter))
319 throw std::runtime_error("Missing transmitter in frame definition.");
320 if (stream >> extra)
321 throw std::runtime_error("Unexpected trailing field in frame definition: " + extra);
322
323 NormalizeCanId(rawCanId, frame.canId, frame.isExtended);
324
325 return frame;
326}
327
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);
333 SignalInfo signal;
334 std::istringstream nameFields(tokens.Until(':'));
335 std::string marker;
336 std::string extra;
337 nameFields >> signal.name >> marker;
338 if (signal.name.empty() || (nameFields >> extra))
339 throw std::runtime_error("Invalid signal name or multiplexing marker.");
340 signal.multiplexing = ParseMultiplexingMarker(marker);
341
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");
345 if (signal.length == 0U)
346 throw std::runtime_error("Signal length must be within 1..64 bits.");
347 tokens.SkipSpaces();
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.");
352 signal.isLittleEndian = byteOrder == '1';
353 signal.isSigned = signedness == '-';
354
355 tokens.Expect('(');
356 signal.factor = ParsePhysicalValue(dbc_detail::TrimText(tokens.Until(',')), "signal factor");
357 signal.offset = ParsePhysicalValue(dbc_detail::TrimText(tokens.Until(')')), "signal offset");
358 tokens.Expect('[');
359 signal.minimum = ParsePhysicalValue(dbc_detail::TrimText(tokens.Until('|')), "signal minimum");
360 signal.maximum = ParsePhysicalValue(dbc_detail::TrimText(tokens.Until(']')), "signal maximum");
361 if (signal.minimum > signal.maximum)
362 throw std::runtime_error("Signal minimum exceeds maximum.");
363 tokens.Expect('"');
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.");
367 signal.receivers = tokens.Receivers();
368 return signal;
369}
370
371
372namespace {
373 FrameInfo *FindFrameById(DbcDatabase &database,
374 std::uint32_t canId,
375 bool isExtended) {
376 for (FrameInfo &frame : database.frames) {
377 if (frame.canId == canId && frame.isExtended == isExtended)
378 return &frame;
379 }
380
381 return nullptr;
382 }
383
384 SignalInfo *FindSignalByName(FrameInfo &frame, const std::string &signalName) {
385 for (SignalInfo &signal : frame.signals) {
386 if (signal.name == signalName)
387 return &signal;
388 }
389
390 return nullptr;
391 }
392}// namespace
393
394void DbcFrameHandler::Handle(const std::string &line, DbcParseContext &context) const {
395 FrameInfo frame = ParseFrameLine(line);
396 if (context.enableJ1939 && frame.isExtended) {
397 frame.hasPgn = true;
398 frame.pgn = ExtractJ1939Pgn(frame.canId);
399 }
400 if (FindFrameById(context.database, frame.canId, frame.isExtended) != nullptr)
401 throw std::runtime_error("Duplicate " + std::string(frame.isExtended ? "extended" : "standard") +
402 " frame CAN ID: " + std::to_string(frame.canId));
403 context.database.frames.push_back(std::move(frame));
404 context.currentFrame = &context.database.frames.back();
405 context.signalLines.emplace_back();
406}
407
408void DbcSignalHandler::Handle(const std::string &line, DbcParseContext &context) const {
409 if (context.currentFrame == nullptr)
410 throw std::runtime_error("Signal found before any frame definition.");
411 SignalInfo signal = ParseSignalLine(line);
412 if (FindSignalByName(*context.currentFrame, signal.name) != nullptr)
413 throw std::runtime_error("Duplicate signal name '" + signal.name +
414 "' in frame '" + context.currentFrame->name + "'.");
415 if (!IsValidSignalLayout(signal.startBit, signal.length, signal.isLittleEndian,
416 context.currentFrame->dlc))
417 throw std::runtime_error("Signal exceeds the frame payload length.");
418 context.currentFrame->signals.push_back(std::move(signal));
419 context.signalLines.back().push_back(context.lineNumber);
420}
421
422void DbcCommentHandler::Handle(const std::string &line, DbcParseContext &context) const {
423 std::istringstream stream(line);
424 std::string keyword;
425 std::string target;
426 stream >> keyword >> target;
427 if (keyword != "CM_" || (target != "BO_" && target != "SG_"))
428 return;
429
430 PendingComment pending;
431 pending.isSignal = target == "SG_";
432 pending.lineNumber = context.lineNumber;
433 std::string idText;
434 if (!(stream >> idText))
435 throw std::runtime_error("Missing CAN ID in CM_ statement.");
436 NormalizeCanId(ParseCanId(idText), pending.canId, pending.isExtended);
437 if (pending.isSignal && (!(stream >> pending.signalName) ||
438 pending.signalName.find_first_of("\";") != std::string::npos))
439 throw std::runtime_error("Missing or invalid signal name in CM_ statement.");
440
441 stream >> std::ws;
442 if (stream.get() != '"')
443 throw std::runtime_error("Missing opening quote in CM_ statement.");
444 bool closed = false;
445 char character;
446 while (stream.get(character)) {
447 if (character == '"') {
448 closed = true;
449 break;
450 }
451 if (character == '\\') {
452 if (!stream.get(character) || (character != '"' && character != '\\'))
453 throw std::runtime_error("Invalid escape in CM_ statement.");
454 }
455 pending.comment += character;
456 }
457 if (!closed)
458 throw std::runtime_error("Unterminated CM_ comment.");
459 stream >> std::ws;
460 if (stream.get() != ';')
461 throw std::runtime_error("Missing semicolon in CM_ statement.");
462 stream >> std::ws;
463 if (!stream.eof())
464 throw std::runtime_error("Unexpected trailing text in CM_ statement.");
465 context.pendingComments.push_back(std::move(pending));
466}
467
468void DbcParseContext::ResolveComments(const std::string &filePath) {
469 for (const PendingComment &pending: pendingComments) {
470 FrameInfo *frame = FindFrameById(database, pending.canId, pending.isExtended);
471 if (!frame)
472 throw DbcDiagnosticError(DbcDiagnosticCode::InvalidStatement, filePath,
473 pending.lineNumber, "Unresolved CM_ frame reference.");
474 if (pending.isSignal) {
475 SignalInfo *signal = FindSignalByName(*frame, pending.signalName);
476 if (!signal)
477 throw DbcDiagnosticError(DbcDiagnosticCode::InvalidStatement, filePath,
478 pending.lineNumber, "Unresolved CM_ signal reference: " + pending.signalName);
479 signal->comment = pending.comment;
480 } else {
481 frame->comment = pending.comment;
482 }
483 }
484}
485
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_");
503}
504
506 throw std::runtime_error("SIG_VALTYPE_ declarations are unsupported; refusing potentially incorrect integer decoding.");
507}
508
509void DbcHeaderHandler::Handle(const std::string &, DbcParseContext &) const {}
510
511bool DbcStatementDispatcher::Dispatch(const std::string &line,
512 DbcParseContext &context) const {
513 std::istringstream stream(line);
514 std::string keyword;
515 stream >> keyword;
516 const HandlerMap::const_iterator handler = m_handlers.find(keyword);
517 if (handler == m_handlers.end())
518 return false;
519 handler->second->Handle(line, context);
520 return true;
521}
522
523void DbcValueDescriptionHandler::Handle(const std::string &line, DbcParseContext &context) const {
524 StatementTokens tokens(line, "VAL_");
525 const std::uint32_t rawId = ParseCanId(tokens.Word());
526 tokens.RequireSpace();
528 NormalizeCanId(rawId, pending.canId, pending.isExtended);
529 pending.signalName = tokens.Word();
530 tokens.RequireSpace();
531 pending.lineNumber = context.lineNumber;
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(';'))
541 break;
542 if (!separated)
543 throw std::runtime_error("Missing whitespace between VAL_ pairs.");
544 }
545 tokens.End();
546 if (pending.descriptions.empty())
547 throw std::runtime_error("VAL_ requires at least one value/description pair.");
548 context.pendingValues.push_back(std::move(pending));
549}
550
551void DbcParseContext::ResolveValueDescriptions(const std::string &filePath) {
552 for (const PendingValueDescriptions &pending: pendingValues) {
553 try {
554 FrameInfo *frame = FindUniqueFrame(database, pending.canId, pending.isExtended);
555 SignalInfo *signal = FindUniqueSignal(*frame, pending.signalName);
556 for (const ValueDescriptions::value_type &entry: pending.descriptions) {
557 if (!signal->valueDescriptions.insert(entry).second)
558 throw std::runtime_error("Duplicate VAL_ value key for signal: " + pending.signalName);
559 }
560 } catch (const std::exception &error) {
561 throw DbcDiagnosticError(DbcDiagnosticCode::InvalidValueDescription,
562 filePath, pending.lineNumber, error.what());
563 }
564 }
565}
566
567void DbcMultiplexingHandler::Handle(const std::string &line, DbcParseContext &context) const {
568 StatementTokens tokens(line, "SG_MUL_VAL_");
569 PendingMultiplexing pending;
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();
576 pending.lineNumber = context.lineNumber;
577 for (;;) {
578 MultiplexRange range;
579 range.minimum = tokens.Unsigned();
580 tokens.Expect('-');
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);
585 tokens.SkipSpaces();
586 if (tokens.Consume(';'))
587 break;
588 tokens.Expect(',');
589 }
590 tokens.End();
591 context.pendingMultiplexing.push_back(std::move(pending));
592}
593
594void DbcParseContext::ResolveMultiplexing(const std::string &filePath) {
595 for (const PendingMultiplexing &pending: pendingMultiplexing) {
596 try {
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_");
600 if (!IsMultiplexSelector(selector->multiplexing.role) || selector->isSigned)
601 throw std::runtime_error("Referenced signal must be an unsigned multiplex selector.");
602 if (!signal->multiplexing.selector.empty() && signal->multiplexing.selector != pending.selectorName)
603 throw std::runtime_error("Multiple parent selectors for one signal are unsupported.");
604 for (const MultiplexRange &range: pending.ranges) {
605 if (selector->length < 64U && range.maximum >= (std::uint64_t(1U) << selector->length))
606 throw std::runtime_error("Branch value exceeds selector raw range.");
607 }
608 signal->multiplexing.selector = pending.selectorName;
609 signal->multiplexing.ranges.insert(signal->multiplexing.ranges.end(), pending.ranges.begin(), pending.ranges.end());
610 if (!IsMultiplexSelector(signal->multiplexing.role))
611 signal->multiplexing.role = MultiplexRole::Branch;
612 else
613 signal->multiplexing.role = MultiplexRole::BranchSelector;
614 } catch (const std::exception &error) {
615 throw DbcDiagnosticError(DbcDiagnosticCode::InvalidMultiplexing, filePath, pending.lineNumber, error.what());
616 }
617 }
618}
619
620namespace {
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) {
625 bits.push_back(bit);
626 bit = NextSignalBit(bit, signal.isLittleEndian);
627 }
628 return bits;
629 }
630
631 bool SignalsOverlap(const SignalInfo &first, const SignalInfo &second) {
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)
637 return true;
638 }
639 }
640 return false;
641 }
642}
643
644void DbcParseContext::ValidateMultiplexing(const std::string &filePath) const {
645 for (std::size_t frameIndex = 0U; frameIndex < database.frames.size(); ++frameIndex) {
646 const FrameInfo &frame = database.frames[frameIndex];
647 try {
648 const dbc_detail::MultiplexGraph graph(frame.signals);
649 for (std::size_t index = 0U; index < frame.signals.size(); ++index) {
650 const SignalInfo &signal = frame.signals[index];
651 if (IsMultiplexSelector(signal.multiplexing.role) && signal.isSigned)
652 throw dbc_detail::MultiplexError(index, "Multiplex selectors must be unsigned.");
653 for (std::size_t previous = 0U; previous < index; ++previous) {
654 if (!graph.AreMutuallyExclusive(index, previous) && SignalsOverlap(signal, frame.signals[previous]))
655 throw dbc_detail::MultiplexError(index, "Signals overlap without mutually exclusive multiplexing conditions.");
656 }
657 }
658 } catch (const dbc_detail::MultiplexError &error) {
659 throw DbcDiagnosticError(DbcDiagnosticCode::InvalidMultiplexing, filePath,
660 signalLines[frameIndex][error.index], error.what());
661 }
662 }
663}
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.
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.
Definition frame_info.h:25
bool isExtended
Definition frame_info.h:28
bool hasPgn
Definition frame_info.h:30
std::string transmitter
Definition frame_info.h:32
std::uint8_t dlc
Definition frame_info.h:31
std::string name
Definition frame_info.h:26
std::string comment
Definition frame_info.h:33
std::uint32_t pgn
Definition frame_info.h:29
std::uint32_t canId
Definition frame_info.h:27
Inclusive raw selector range, without expanding individual values.
Basic markers and optional explicit extended multiplexing condition.
std::uint64_t value
std::vector< MultiplexRange > ranges
std::string selector
Deferred frame or signal comment and its source location.
Deferred extended multiplexing condition and source location.
Deferred raw value descriptions and their target.
Describes one signal inside a DBC frame.
Definition signal_info.h:25
std::uint32_t length
Definition signal_info.h:29
std::string unit
Definition signal_info.h:36
std::vector< std::string > receivers
Definition signal_info.h:37
std::string name
Definition signal_info.h:27
ValueDescriptions valueDescriptions
Definition signal_info.h:38
bool isSigned
Definition signal_info.h:31
Multiplexing multiplexing
Definition signal_info.h:26
double offset
Definition signal_info.h:33
std::string comment
Definition signal_info.h:39
std::uint32_t startBit
Definition signal_info.h:28
double minimum
Definition signal_info.h:34
double maximum
Definition signal_info.h:35
double factor
Definition signal_info.h:32
bool isLittleEndian
Definition signal_info.h:30