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