2#include "dbc_statement_handler.h"
5#include "signal_layout.h"
14 std::uint32_t ParseDecimal(
const std::string &text,
15 std::uint32_t maximum,
17 std::uint32_t value = 0U;
18 const std::from_chars_result parsed = std::from_chars(text.data(), text.data() + text.size(), value);
19 if (text.empty() || parsed.ec != std::errc() ||
20 parsed.ptr != text.data() + text.size() || value > maximum)
21 throw std::runtime_error(std::string(
"Invalid ") + field +
": " + text);
25 std::uint32_t ParseCanId(
const std::string &text) {
26 const std::uint32_t rawId = ParseDecimal(
27 text, std::numeric_limits<std::uint32_t>::max(),
"CAN ID");
29 if ((rawId & 0x60000000U) != 0U)
30 throw std::runtime_error(
"CAN ID exceeds the 29-bit identifier range.");
34 double ParsePhysicalValue(
const std::string &text,
const char *field) {
35 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);
41 const char *begin = text.data();
42 const char *end = begin + text.size();
46 const std::from_chars_result parsed =
47 std::from_chars(begin, end, value, std::chars_format::general);
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);
55 Multiplexing ParseMultiplexingMarker(
const std::string &marker) {
58 result.
role = MultiplexRole::Selector;
59 else if (!marker.empty()) {
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;
73 RawSignalValue ParseValueKey(
const std::string &number) {
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));
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);
93 std::string UnescapeDescription(
const std::string "ed) {
94 std::string description;
95 for (std::size_t i = 0; i < quoted.size(); ++i) {
96 if (quoted[i] ==
'\\')
98 description += quoted[i];
105 for (
FrameInfo &candidate : database.frames) {
106 if (candidate.canId == canId && candidate.isExtended == isExtended) {
108 throw std::runtime_error(
"Ambiguous VAL_ frame reference.");
113 throw std::runtime_error(
"Unresolved VAL_ frame reference.");
120 if (candidate.name == signalName) {
122 throw std::runtime_error(
"Ambiguous VAL_ signal reference: " + signalName);
127 throw std::runtime_error(
"Unresolved VAL_ signal reference: " + signalName);
131 std::uint64_t NextSignalBit(std::uint64_t bit,
bool isLittleEndian) {
135 return bit % 8U == 0U ? bit + 15U : bit - 1U;
143 std::vector<std::string> SplitReceivers(
const std::string &text) {
144 std::vector<std::string> receivers;
146 std::istringstream stream(text);
148 while (std::getline(stream, token,
',')) {
149 token = dbc_detail::TrimText(token);
151 receivers.push_back(token);
157 void NormalizeCanId(std::uint32_t rawCanId, std::uint32_t &normalizedCanId,
bool &isExtended) {
163 isExtended = (rawCanId & 0x80000000U) != 0U || rawCanId > 0x7FFU;
164 normalizedCanId = rawCanId & 0x1FFFFFFFU;
168FrameInfo DbcFrameHandler::ParseFrameLine(
const std::string &line) {
174 std::istringstream stream(line);
177 if (!(stream >> keyword >> idText) || keyword !=
"BO_")
178 throw std::runtime_error(
"Missing CAN ID in frame definition.");
180 const std::uint32_t rawCanId = ParseCanId(idText);
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);
188 if (!(nameStream >> frame.
name) || (nameStream >> extra))
189 throw std::runtime_error(
"Missing or invalid frame name.");
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"));
197 throw std::runtime_error(
"Missing transmitter in frame definition.");
199 throw std::runtime_error(
"Unexpected trailing field in frame definition: " + extra);
206SignalInfo DbcSignalHandler::ParseSignalLine(
const std::string &line) {
209 NameAndMarker = 1, StartBit, BitLength,
ByteOrder, Signedness,
210 Factor, Offset, Minimum, Maximum, Unit, Receivers
212 static const std::regex syntax(
214 R
"dbc(^SG_[ \t]+([^:]+):[ \t]*)dbc"
216 R
"dbc(([^| \t]+)[ \t]*\|[ \t]*([^@ \t]+)[ \t]*@[ \t]*([01])([+-])[ \t]*)dbc"
218 R
"dbc(\([ \t]*([^, \t]+)[ \t]*,[ \t]*([^ )\t]+)[ \t]*\)[ \t]*)dbc"
220 R
"dbc(\[[ \t]*([^| \t]+)[ \t]*\|[ \t]*([^\] \t]+)[ \t]*\][ \t]*)dbc"
222 R
"dbc("([^"\r\n]*)"[ \t]+([^ \t,]+(?:[ \t]*,[ \t]*[^ \t,]+)*)[ \t]*$)dbc");
224 if (!std::regex_match(line, fields, syntax))
225 throw std::runtime_error(
"Malformed or unsupported SG_ signal definition.");
228 std::istringstream nameFields(fields[NameAndMarker].str());
231 nameFields >> signal.
name >> marker;
232 if (signal.
name.empty() || (nameFields >> extra))
233 throw std::runtime_error(
"Invalid signal name or multiplexing marker.");
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");
240 throw std::runtime_error(
"Signal length must be within 1..64 bits.");
242 signal.
isSigned = fields[Signedness].str() ==
"-";
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");
249 throw std::runtime_error(
"Signal minimum exceeds maximum.");
250 signal.
unit = fields[Unit].str();
251 signal.
receivers = SplitReceivers(fields[Receivers].str());
260 for (
FrameInfo &frame : database.frames) {
270 if (signal.
name == signalName)
282 frame.
pgn = ExtractJ1939Pgn(frame.
canId);
285 throw std::runtime_error(
"Duplicate " + std::string(frame.
isExtended ?
"extended" :
"standard") +
286 " frame CAN ID: " + std::to_string(frame.
canId));
294 throw std::runtime_error(
"Signal found before any frame definition.");
297 throw std::runtime_error(
"Duplicate signal name '" + signal.
name +
301 throw std::runtime_error(
"Signal exceeds the frame payload length.");
302 context.
currentFrame->signals.push_back(std::move(signal));
307 std::istringstream stream(line);
310 stream >> keyword >> target;
311 if (keyword !=
"CM_" || (target !=
"BO_" && target !=
"SG_"))
318 if (!(stream >> idText))
319 throw std::runtime_error(
"Missing CAN ID in CM_ statement.");
322 pending.
signalName.find_first_of(
"\";") != std::string::npos))
323 throw std::runtime_error(
"Missing or invalid signal name in CM_ statement.");
326 if (stream.get() !=
'"')
327 throw std::runtime_error(
"Missing opening quote in CM_ statement.");
330 while (stream.get(character)) {
331 if (character ==
'"') {
335 if (character ==
'\\') {
336 if (!stream.get(character) || (character !=
'"' && character !=
'\\'))
337 throw std::runtime_error(
"Invalid escape in CM_ statement.");
342 throw std::runtime_error(
"Unterminated CM_ comment.");
344 if (stream.get() !=
';')
345 throw std::runtime_error(
"Missing semicolon in CM_ statement.");
348 throw std::runtime_error(
"Unexpected trailing text in CM_ statement.");
357 pending.lineNumber,
"Unresolved CM_ frame reference.");
358 if (pending.isSignal) {
359 SignalInfo *signal = FindSignalByName(*frame, pending.signalName);
362 pending.lineNumber,
"Unresolved CM_ signal reference: " + pending.signalName);
363 signal->
comment = pending.comment;
365 frame->
comment = pending.comment;
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_");
392 std::istringstream stream(line);
395 const HandlerMap::const_iterator handler = m_handlers.find(keyword);
396 if (handler == m_handlers.end())
398 handler->second->Handle(line, context);
403 static const std::regex header(R
"(^VAL_[ \t]+([^ \t]+)[ \t]+([^ \t;]+)[ \t]+(.*)$)");
404 static const std::regex pair(R
"dbc(^([^ \t]+)[ \t]+"((?:[^"\\]|\\["\\])*)")dbc");
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.");
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);
431 throw std::runtime_error(
"VAL_ requires at least one value/description pair.");
439 SignalInfo *signal = FindUniqueSignal(*frame, pending.signalName);
440 for (
const ValueDescriptions::value_type &entry: pending.descriptions) {
442 throw std::runtime_error(
"Duplicate VAL_ value key for signal: " + pending.signalName);
444 }
catch (
const std::exception &error) {
446 filePath, pending.lineNumber, error.what());
452 throw std::runtime_error(
"Extended and nested multiplexing are unsupported.");
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) {
475 if (selector ==
nullptr)
476 return "Multiplexed branch has no selector.";
478 return "Branch value exceeds selector raw range.";
483 const std::vector<std::size_t> &lines,
484 const std::string &filePath) {
486 for (std::size_t index = 0U; index < frame.signals.size(); ++index) {
487 const SignalInfo &signal = frame.signals[index];
490 if (selector !=
nullptr || signal.
isSigned)
492 filePath, lines[index],
493 "Multiplexing requires one unsigned selector per frame.");
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)
513 for (std::size_t frameIndex = 0U; frameIndex <
database.
frames.size(); ++frameIndex) {
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.";
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.
Deferred raw value descriptions and their target.
ValueDescriptions descriptions
Describes one signal inside a DBC frame.
std::vector< std::string > receivers
ValueDescriptions valueDescriptions
Multiplexing multiplexing