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