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 <regex>
11#include <sstream>
12#include <stdexcept>
13
14namespace {
15 std::uint32_t ParseDecimal(const std::string &text,
16 std::uint32_t maximum,
17 const char *field) {
18 std::uint32_t value = 0U;
19 const std::from_chars_result parsed = std::from_chars(text.data(), text.data() + text.size(), value);
20 if (text.empty() || parsed.ec != std::errc() ||
21 parsed.ptr != text.data() + text.size() || value > maximum)
22 throw std::runtime_error(std::string("Invalid ") + field + ": " + text);
23 return value;
24 }
25
26 std::uint32_t ParseCanId(const std::string &text) {
27 const std::uint32_t rawId = ParseDecimal(
28 text, std::numeric_limits<std::uint32_t>::max(), "CAN ID");
29 // Only bit 31 may accompany the actual 29-bit identifier.
30 if ((rawId & 0x60000000U) != 0U)
31 throw std::runtime_error("CAN ID exceeds the 29-bit identifier range.");
32 return rawId;
33 }
34
35 double ParsePhysicalValue(const std::string &text, const char *field) {
36 if (text.empty() || (text[0] == '+' &&
37 (text.size() == 1U || text[1] == '+' || text[1] == '-')))
38 throw std::runtime_error(std::string("Invalid ") + field + ": " + text);
39 // Floating-point from_chars is locale-independent and does not accept
40 // a leading '+', which the DBC numeric grammar permits.
41 const char *begin = text.data();
42 const char *end = begin + text.size();
43 if (*begin == '+')
44 ++begin;
45 double value = 0.0;
46 const std::from_chars_result parsed =
47 std::from_chars(begin, end, value, std::chars_format::general);
48 // Keep the existing rejection of conversion underflow into subnormal values.
49 if (parsed.ec != std::errc() || parsed.ptr != end || !std::isfinite(value) ||
50 std::fpclassify(value) == FP_SUBNORMAL)
51 throw std::runtime_error(std::string("Invalid ") + field + ": " + text);
52 return value;
53 }
54
55 // Iterative scanning keeps stack use independent of the statement length.
56 class SignalTokens {
57 public:
58 SignalTokens(const std::string &text, std::size_t position) : text(text), position(position) {}
59
60 void SkipSpaces() {
61 while (position < text.size() && (text[position] == ' ' || text[position] == '\t'))
62 ++position;
63 }
64
65 char Character() {
66 if (position == text.size())
67 throw std::runtime_error("Incomplete SG_ signal definition.");
68 return text[position++];
69 }
70
71 void Expect(char expected) {
72 SkipSpaces();
73 if (Character() != expected)
74 throw std::runtime_error("Malformed SG_ signal definition.");
75 }
76
77 std::string Until(char delimiter) {
78 const std::size_t end = text.find(delimiter, position);
79 if (end == std::string::npos)
80 throw std::runtime_error("Missing delimiter in SG_ signal definition.");
81 const std::string value = text.substr(position, end - position);
82 position = end + 1U;
83 return value;
84 }
85
86 std::vector<std::string> Receivers() {
87 if (position == text.size() || (text[position] != ' ' && text[position] != '\t'))
88 throw std::runtime_error("Missing receiver separator in SG_ signal definition.");
89 std::vector<std::string> receivers;
90 for (;;) {
91 const std::size_t end = text.find(',', position);
92 const std::string receiver = dbc_detail::TrimText(text.substr(position, end - position));
93 if (receiver.empty() || receiver.find_first_of(" \t\r\n") != std::string::npos)
94 throw std::runtime_error("Invalid SG_ receiver list.");
95 receivers.push_back(receiver);
96 if (end == std::string::npos)
97 return receivers;
98 position = end + 1U;
99 }
100 }
101
102 private:
103 const std::string &text;
104 std::size_t position = 0U;
105 };
106
107 Multiplexing ParseMultiplexingMarker(const std::string &marker) {
108 Multiplexing result;
109 if (marker == "M")
110 result.role = MultiplexRole::Selector;
111 else if (!marker.empty()) {
112 if (marker[0] != 'm')
113 throw std::runtime_error("Unsupported multiplexing marker.");
114 const bool nested = marker.back() == 'M';
115 const std::string value = marker.substr(1U, marker.size() - (nested ? 2U : 1U));
116 const std::from_chars_result parsed =
117 std::from_chars(value.data(), value.data() + value.size(), result.value);
118 if (value.empty() || parsed.ec != std::errc() || parsed.ptr != value.data() + value.size())
119 throw std::runtime_error("Invalid or unsupported multiplexing marker.");
120 result.role = nested ? MultiplexRole::BranchSelector : MultiplexRole::Branch;
121 }
122
123 return result;
124 }
125
126 RawSignalValue ParseValueKey(const std::string &number) {
127 RawSignalValue key;
128 if (!number.empty() && number[0] == '-') {
129 std::int64_t value = 0;
130 const std::from_chars_result parsed =
131 std::from_chars(number.data(), number.data() + number.size(), value);
132 if (parsed.ec != std::errc() || parsed.ptr != number.data() + number.size())
133 throw std::runtime_error("Invalid VAL_ integer: " + number);
134 key = NormalizeValueKey(RawSignalValue(value));
135 } else {
136 std::uint64_t value = 0;
137 const std::from_chars_result parsed =
138 std::from_chars(number.data(), number.data() + number.size(), value);
139 if (number.empty() || parsed.ec != std::errc() || parsed.ptr != number.data() + number.size())
140 throw std::runtime_error("Invalid VAL_ integer: " + number);
141 key = value;
142 }
143 return key;
144 }
145
146 std::string UnescapeDescription(const std::string &quoted) {
147 std::string description;
148 for (std::size_t i = 0; i < quoted.size(); ++i) {
149 if (quoted[i] == '\\')
150 ++i;
151 description += quoted[i];
152 }
153 return description;
154 }
155
156 FrameInfo *FindUniqueFrame(DbcDatabase &database, std::uint32_t canId, bool isExtended, const std::string &statement = "VAL_") {
157 FrameInfo *frame = nullptr;
158 for (FrameInfo &candidate : database.frames) {
159 if (candidate.canId == canId && candidate.isExtended == isExtended) {
160 if (frame)
161 throw std::runtime_error("Ambiguous " + statement + " frame reference.");
162 frame = &candidate;
163 }
164 }
165 if (!frame)
166 throw std::runtime_error("Unresolved " + statement + " frame reference.");
167 return frame;
168 }
169
170 SignalInfo *FindUniqueSignal(FrameInfo &frame, const std::string &signalName, const std::string &statement = "VAL_") {
171 SignalInfo *signal = nullptr;
172 for (SignalInfo &candidate : frame.signals) {
173 if (candidate.name == signalName) {
174 if (signal)
175 throw std::runtime_error("Ambiguous " + statement + " signal reference: " + signalName);
176 signal = &candidate;
177 }
178 }
179 if (!signal)
180 throw std::runtime_error("Unresolved " + statement + " signal reference: " + signalName);
181 return signal;
182 }
183
184 std::uint64_t NextSignalBit(std::uint64_t bit, bool isLittleEndian) {
185 if (isLittleEndian)
186 return bit + 1U;
187 // From bit 0, Motorola continues at bit 7 of the next byte (0 -> 15).
188 return bit % 8U == 0U ? bit + 15U : bit - 1U;
189 }
190
191 FrameInfo *FindFrameById(DbcDatabase &, std::uint32_t, bool);
192 SignalInfo *FindSignalByName(FrameInfo &, const std::string &);
193} // namespace
194
195namespace {
196 void NormalizeCanId(std::uint32_t rawCanId, std::uint32_t &normalizedCanId, bool &isExtended) {
197 /*
198 * DBC commonly stores extended identifiers with bit 31 set.
199 * Example:
200 * raw id = 0x80000000 | actual_29_bit_id
201 */
202 isExtended = (rawCanId & 0x80000000U) != 0U || rawCanId > 0x7FFU;
203 normalizedCanId = rawCanId & 0x1FFFFFFFU;
204 }
205} // namespace
206
207FrameInfo DbcFrameHandler::ParseFrameLine(const std::string &line) {
208 /*
209 * Example:
210 * BO_ 256 EngineData: 8 EEC1
211 */
212
213 std::istringstream stream(line);
214 std::string keyword;
215 std::string idText;
216 if (!(stream >> keyword >> idText) || keyword != "BO_")
217 throw std::runtime_error("Missing CAN ID in frame definition.");
218
219 const std::uint32_t rawCanId = ParseCanId(idText);
220
221 std::string nameText;
222 if (!std::getline(stream, nameText, ':') || stream.eof())
223 throw std::runtime_error("Missing ':' in frame definition.");
224 std::istringstream nameStream(nameText);
225 FrameInfo frame;
226 std::string extra;
227 if (!(nameStream >> frame.name) || (nameStream >> extra))
228 throw std::runtime_error("Missing or invalid frame name.");
229
230 std::string lengthText;
231 if (!(stream >> lengthText))
232 throw std::runtime_error("Missing payload length in frame definition.");
233 frame.dlc = static_cast<std::uint8_t>(ParseDecimal(
234 lengthText, std::numeric_limits<std::uint8_t>::max(), "payload length"));
235 if (!(stream >> frame.transmitter))
236 throw std::runtime_error("Missing transmitter in frame definition.");
237 if (stream >> extra)
238 throw std::runtime_error("Unexpected trailing field in frame definition: " + extra);
239
240 NormalizeCanId(rawCanId, frame.canId, frame.isExtended);
241
242 return frame;
243}
244
245SignalInfo DbcSignalHandler::ParseSignalLine(const std::string &line) {
246 if (line.size() < 4U || line.compare(0U, 3U, "SG_") != 0 ||
247 (line[3] != ' ' && line[3] != '\t'))
248 throw std::runtime_error("Malformed SG_ signal definition.");
249 SignalTokens tokens(line, 3U);
250 SignalInfo signal;
251 std::istringstream nameFields(tokens.Until(':'));
252 std::string marker;
253 std::string extra;
254 nameFields >> signal.name >> marker;
255 if (signal.name.empty() || (nameFields >> extra))
256 throw std::runtime_error("Invalid signal name or multiplexing marker.");
257 signal.multiplexing = ParseMultiplexingMarker(marker);
258
259 signal.startBit = ParseDecimal(dbc_detail::TrimText(tokens.Until('|')),
260 std::numeric_limits<std::uint32_t>::max(), "signal start bit");
261 signal.length = ParseDecimal(dbc_detail::TrimText(tokens.Until('@')), 64U, "signal length");
262 if (signal.length == 0U)
263 throw std::runtime_error("Signal length must be within 1..64 bits.");
264 tokens.SkipSpaces();
265 const char byteOrder = tokens.Character();
266 const char signedness = tokens.Character();
267 if ((byteOrder != '0' && byteOrder != '1') || (signedness != '+' && signedness != '-'))
268 throw std::runtime_error("Invalid SG_ byte order or signedness.");
269 signal.isLittleEndian = byteOrder == '1';
270 signal.isSigned = signedness == '-';
271
272 tokens.Expect('(');
273 signal.factor = ParsePhysicalValue(dbc_detail::TrimText(tokens.Until(',')), "signal factor");
274 signal.offset = ParsePhysicalValue(dbc_detail::TrimText(tokens.Until(')')), "signal offset");
275 tokens.Expect('[');
276 signal.minimum = ParsePhysicalValue(dbc_detail::TrimText(tokens.Until('|')), "signal minimum");
277 signal.maximum = ParsePhysicalValue(dbc_detail::TrimText(tokens.Until(']')), "signal maximum");
278 if (signal.minimum > signal.maximum)
279 throw std::runtime_error("Signal minimum exceeds maximum.");
280 tokens.Expect('"');
281 signal.unit = tokens.Until('"');
282 if (signal.unit.find_first_of("\r\n") != std::string::npos)
283 throw std::runtime_error("Invalid newline in SG_ unit.");
284 signal.receivers = tokens.Receivers();
285 return signal;
286}
287
288
289namespace {
290 FrameInfo *FindFrameById(DbcDatabase &database,
291 std::uint32_t canId,
292 bool isExtended) {
293 for (FrameInfo &frame : database.frames) {
294 if (frame.canId == canId && frame.isExtended == isExtended)
295 return &frame;
296 }
297
298 return nullptr;
299 }
300
301 SignalInfo *FindSignalByName(FrameInfo &frame, const std::string &signalName) {
302 for (SignalInfo &signal : frame.signals) {
303 if (signal.name == signalName)
304 return &signal;
305 }
306
307 return nullptr;
308 }
309}// namespace
310
311void DbcFrameHandler::Handle(const std::string &line, DbcParseContext &context) const {
312 FrameInfo frame = ParseFrameLine(line);
313 if (context.enableJ1939 && frame.isExtended) {
314 frame.hasPgn = true;
315 frame.pgn = ExtractJ1939Pgn(frame.canId);
316 }
317 if (FindFrameById(context.database, frame.canId, frame.isExtended) != nullptr)
318 throw std::runtime_error("Duplicate " + std::string(frame.isExtended ? "extended" : "standard") +
319 " frame CAN ID: " + std::to_string(frame.canId));
320 context.database.frames.push_back(std::move(frame));
321 context.currentFrame = &context.database.frames.back();
322 context.signalLines.emplace_back();
323}
324
325void DbcSignalHandler::Handle(const std::string &line, DbcParseContext &context) const {
326 if (context.currentFrame == nullptr)
327 throw std::runtime_error("Signal found before any frame definition.");
328 SignalInfo signal = ParseSignalLine(line);
329 if (FindSignalByName(*context.currentFrame, signal.name) != nullptr)
330 throw std::runtime_error("Duplicate signal name '" + signal.name +
331 "' in frame '" + context.currentFrame->name + "'.");
332 if (!IsValidSignalLayout(signal.startBit, signal.length, signal.isLittleEndian,
333 context.currentFrame->dlc))
334 throw std::runtime_error("Signal exceeds the frame payload length.");
335 context.currentFrame->signals.push_back(std::move(signal));
336 context.signalLines.back().push_back(context.lineNumber);
337}
338
339void DbcCommentHandler::Handle(const std::string &line, DbcParseContext &context) const {
340 std::istringstream stream(line);
341 std::string keyword;
342 std::string target;
343 stream >> keyword >> target;
344 if (keyword != "CM_" || (target != "BO_" && target != "SG_"))
345 return;
346
347 PendingComment pending;
348 pending.isSignal = target == "SG_";
349 pending.lineNumber = context.lineNumber;
350 std::string idText;
351 if (!(stream >> idText))
352 throw std::runtime_error("Missing CAN ID in CM_ statement.");
353 NormalizeCanId(ParseCanId(idText), pending.canId, pending.isExtended);
354 if (pending.isSignal && (!(stream >> pending.signalName) ||
355 pending.signalName.find_first_of("\";") != std::string::npos))
356 throw std::runtime_error("Missing or invalid signal name in CM_ statement.");
357
358 stream >> std::ws;
359 if (stream.get() != '"')
360 throw std::runtime_error("Missing opening quote in CM_ statement.");
361 bool closed = false;
362 char character;
363 while (stream.get(character)) {
364 if (character == '"') {
365 closed = true;
366 break;
367 }
368 if (character == '\\') {
369 if (!stream.get(character) || (character != '"' && character != '\\'))
370 throw std::runtime_error("Invalid escape in CM_ statement.");
371 }
372 pending.comment += character;
373 }
374 if (!closed)
375 throw std::runtime_error("Unterminated CM_ comment.");
376 stream >> std::ws;
377 if (stream.get() != ';')
378 throw std::runtime_error("Missing semicolon in CM_ statement.");
379 stream >> std::ws;
380 if (!stream.eof())
381 throw std::runtime_error("Unexpected trailing text in CM_ statement.");
382 context.pendingComments.push_back(std::move(pending));
383}
384
385void DbcParseContext::ResolveComments(const std::string &filePath) {
386 for (const PendingComment &pending: pendingComments) {
387 FrameInfo *frame = FindFrameById(database, pending.canId, pending.isExtended);
388 if (!frame)
389 throw DbcDiagnosticError(DbcDiagnosticCode::InvalidStatement, filePath,
390 pending.lineNumber, "Unresolved CM_ frame reference.");
391 if (pending.isSignal) {
392 SignalInfo *signal = FindSignalByName(*frame, pending.signalName);
393 if (!signal)
394 throw DbcDiagnosticError(DbcDiagnosticCode::InvalidStatement, filePath,
395 pending.lineNumber, "Unresolved CM_ signal reference: " + pending.signalName);
396 signal->comment = pending.comment;
397 } else {
398 frame->comment = pending.comment;
399 }
400 }
401}
402
404 RegisterType<DbcHeaderHandler>("VERSION");
405 RegisterType<DbcHeaderHandler>("NS_");
406 RegisterType<DbcHeaderHandler>("NS_:");
407 RegisterType<DbcHeaderHandler>("BS_");
408 RegisterType<DbcHeaderHandler>("BS_:");
409 RegisterType<DbcHeaderHandler>("BU_");
410 RegisterType<DbcHeaderHandler>("BU_:");
411 RegisterType<DbcMultiplexingHandler>("SG_MUL_VAL_");
412 RegisterType<DbcFrameHandler>("BO_");
413 RegisterType<DbcSignalHandler>("SG_");
414 RegisterType<DbcUnsupportedSignalTypeHandler>("SIG_VALTYPE_");
415 RegisterType<DbcCommentHandler>("CM_");
416 RegisterType<DbcValueDescriptionHandler>("VAL_");
417 RegisterType<DbcAttributeHandler>("BA_DEF_");
418 RegisterType<DbcAttributeHandler>("BA_DEF_DEF_");
419 RegisterType<DbcAttributeHandler>("BA_");
420}
421
423 throw std::runtime_error("SIG_VALTYPE_ declarations are unsupported; refusing potentially incorrect integer decoding.");
424}
425
426void DbcHeaderHandler::Handle(const std::string &, DbcParseContext &) const {}
427
428bool DbcStatementDispatcher::Dispatch(const std::string &line,
429 DbcParseContext &context) const {
430 std::istringstream stream(line);
431 std::string keyword;
432 stream >> keyword;
433 const HandlerMap::const_iterator handler = m_handlers.find(keyword);
434 if (handler == m_handlers.end())
435 return false;
436 handler->second->Handle(line, context);
437 return true;
438}
439
440void DbcValueDescriptionHandler::Handle(const std::string &line, DbcParseContext &context) const {
441 static const std::regex header(R"(^VAL_[ \t]+([^ \t]+)[ \t]+([^ \t;]+)[ \t]+(.*)$)");
442 static const std::regex pair(R"dbc(^([^ \t]+)[ \t]+"((?:[^"\\]|\\["\\])*)")dbc");
443 std::smatch fields;
444 if (!std::regex_match(line, fields, header))
445 throw std::runtime_error("Malformed or unsupported VAL_ statement.");
446 const std::uint32_t rawId = ParseDecimal(fields[1].str(), std::numeric_limits<std::uint32_t>::max(), "VAL_ CAN ID");
447 if ((rawId & 0x60000000U) != 0U)
448 throw std::runtime_error("VAL_ CAN ID exceeds the 29-bit identifier range.");
450 NormalizeCanId(rawId, pending.canId, pending.isExtended);
451 pending.signalName = fields[2].str();
452 pending.lineNumber = context.lineNumber;
453 std::string rest = fields[3].str();
454 while (rest != ";") {
455 if (!std::regex_search(rest, fields, pair))
456 throw std::runtime_error("Malformed VAL_ value/description pair or missing semicolon.");
457 const std::string number = fields[1].str();
458 const RawSignalValue key = ParseValueKey(number);
459 const std::string description = UnescapeDescription(fields[2].str());
460 if (!pending.descriptions.emplace(key, description).second)
461 throw std::runtime_error("Duplicate VAL_ value key: " + number);
462 const std::size_t consumed = static_cast<std::size_t>(fields.length());
463 rest.erase(0, consumed);
464 if (!rest.empty() && rest[0] != ';' && rest[0] != ' ' && rest[0] != '\t')
465 throw std::runtime_error("Missing whitespace between VAL_ pairs.");
466 rest = dbc_detail::TrimText(rest);
467 }
468 if (pending.descriptions.empty())
469 throw std::runtime_error("VAL_ requires at least one value/description pair.");
470 context.pendingValues.push_back(std::move(pending));
471}
472
473void DbcParseContext::ResolveValueDescriptions(const std::string &filePath) {
474 for (const PendingValueDescriptions &pending: pendingValues) {
475 try {
476 FrameInfo *frame = FindUniqueFrame(database, pending.canId, pending.isExtended);
477 SignalInfo *signal = FindUniqueSignal(*frame, pending.signalName);
478 for (const ValueDescriptions::value_type &entry: pending.descriptions) {
479 if (!signal->valueDescriptions.insert(entry).second)
480 throw std::runtime_error("Duplicate VAL_ value key for signal: " + pending.signalName);
481 }
482 } catch (const std::exception &error) {
483 throw DbcDiagnosticError(DbcDiagnosticCode::InvalidValueDescription,
484 filePath, pending.lineNumber, error.what());
485 }
486 }
487}
488
489void DbcMultiplexingHandler::Handle(const std::string &line, DbcParseContext &context) const {
490 static const std::regex syntax(R"(^SG_MUL_VAL_[ \t]+([0-9]+)[ \t]+([^ \t;]+)[ \t]+([^ \t;]+)[ \t]+(.+);[ \t]*$)");
491 static const std::regex rangeSyntax(R"(^[ \t]*([0-9]+)[ \t]*-[ \t]*([0-9]+)[ \t]*$)");
492 std::smatch fields;
493 if (!std::regex_match(line, fields, syntax))
494 throw std::runtime_error("Malformed SG_MUL_VAL_ statement.");
495 PendingMultiplexing pending;
496 NormalizeCanId(ParseCanId(fields[1].str()), pending.canId, pending.isExtended);
497 pending.signalName = fields[2].str();
498 pending.selectorName = fields[3].str();
499 pending.lineNumber = context.lineNumber;
500 const std::string list = fields[4].str();
501 std::size_t begin = 0U;
502 for (;;) {
503 const std::size_t comma = list.find(',', begin);
504 const std::string item = list.substr(begin, comma == std::string::npos ? comma : comma - begin);
505 if (!std::regex_match(item, fields, rangeSyntax))
506 throw std::runtime_error("Malformed multiplex range.");
507 MultiplexRange range;
508 const std::string lower = fields[1].str();
509 const std::string upper = fields[2].str();
510 const std::from_chars_result low = std::from_chars(lower.data(), lower.data() + lower.size(), range.minimum);
511 const std::from_chars_result high = std::from_chars(upper.data(), upper.data() + upper.size(), range.maximum);
512 if (low.ec != std::errc() || high.ec != std::errc() || range.minimum > range.maximum)
513 throw std::runtime_error("Invalid multiplex range.");
514 pending.ranges.push_back(range);
515 if (comma == std::string::npos)
516 break;
517 begin = comma + 1U;
518 }
519 context.pendingMultiplexing.push_back(std::move(pending));
520}
521
522void DbcParseContext::ResolveMultiplexing(const std::string &filePath) {
523 for (const PendingMultiplexing &pending: pendingMultiplexing) {
524 try {
525 FrameInfo *frame = FindUniqueFrame(database, pending.canId, pending.isExtended, "SG_MUL_VAL_");
526 SignalInfo *signal = FindUniqueSignal(*frame, pending.signalName, "SG_MUL_VAL_");
527 SignalInfo *selector = FindUniqueSignal(*frame, pending.selectorName, "SG_MUL_VAL_");
528 if (!IsMultiplexSelector(selector->multiplexing.role) || selector->isSigned)
529 throw std::runtime_error("Referenced signal must be an unsigned multiplex selector.");
530 if (!signal->multiplexing.selector.empty() && signal->multiplexing.selector != pending.selectorName)
531 throw std::runtime_error("Multiple parent selectors for one signal are unsupported.");
532 for (const MultiplexRange &range: pending.ranges) {
533 if (selector->length < 64U && range.maximum >= (std::uint64_t(1U) << selector->length))
534 throw std::runtime_error("Branch value exceeds selector raw range.");
535 }
536 signal->multiplexing.selector = pending.selectorName;
537 signal->multiplexing.ranges.insert(signal->multiplexing.ranges.end(), pending.ranges.begin(), pending.ranges.end());
538 if (!IsMultiplexSelector(signal->multiplexing.role))
539 signal->multiplexing.role = MultiplexRole::Branch;
540 else
541 signal->multiplexing.role = MultiplexRole::BranchSelector;
542 } catch (const std::exception &error) {
543 throw DbcDiagnosticError(DbcDiagnosticCode::InvalidMultiplexing, filePath, pending.lineNumber, error.what());
544 }
545 }
546}
547
548namespace {
549 std::vector<std::uint64_t> SignalBits(const SignalInfo &signal) {
550 std::vector<std::uint64_t> bits;
551 std::uint64_t bit = signal.startBit;
552 for (std::uint32_t index = 0U; index < signal.length; ++index) {
553 bits.push_back(bit);
554 bit = NextSignalBit(bit, signal.isLittleEndian);
555 }
556 return bits;
557 }
558
559 bool SignalsOverlap(const SignalInfo &first, const SignalInfo &second) {
560 const std::vector<std::uint64_t> firstBits = SignalBits(first);
561 const std::vector<std::uint64_t> secondBits = SignalBits(second);
562 for (const std::uint64_t firstBit : firstBits) {
563 for (const std::uint64_t secondBit : secondBits) {
564 if (firstBit == secondBit)
565 return true;
566 }
567 }
568 return false;
569 }
570}
571
572void DbcParseContext::ValidateMultiplexing(const std::string &filePath) const {
573 for (std::size_t frameIndex = 0U; frameIndex < database.frames.size(); ++frameIndex) {
574 const FrameInfo &frame = database.frames[frameIndex];
575 try {
576 const dbc_detail::MultiplexGraph graph(frame.signals);
577 for (std::size_t index = 0U; index < frame.signals.size(); ++index) {
578 const SignalInfo &signal = frame.signals[index];
579 if (IsMultiplexSelector(signal.multiplexing.role) && signal.isSigned)
580 throw dbc_detail::MultiplexError(index, "Multiplex selectors must be unsigned.");
581 for (std::size_t previous = 0U; previous < index; ++previous) {
582 if (!graph.AreMutuallyExclusive(index, previous) && SignalsOverlap(signal, frame.signals[previous]))
583 throw dbc_detail::MultiplexError(index, "Signals overlap without mutually exclusive multiplexing conditions.");
584 }
585 }
586 } catch (const dbc_detail::MultiplexError &error) {
587 throw DbcDiagnosticError(DbcDiagnosticCode::InvalidMultiplexing, filePath,
588 signalLines[frameIndex][error.index], error.what());
589 }
590 }
591}
void Handle(const std::string &line, DbcParseContext &context) const override
Process a trimmed statement using state belonging to this parse.
void Handle(const std::string &line, DbcParseContext &context) const override
Process a trimmed statement using state belonging to this parse.
void Handle(const std::string &line, DbcParseContext &context) const override
Process a trimmed statement using state belonging to this parse.
void Handle(const std::string &line, DbcParseContext &context) const override
Process a trimmed statement using state belonging to this parse.
void Handle(const std::string &line, DbcParseContext &context) const override
Process a trimmed statement using state belonging to this parse.
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