2#include "dbc_statement_handler.h"
12 std::uint32_t ParseDecimal(
const std::string &text,
13 std::uint32_t maximum,
15 std::uint32_t value = 0U;
16 const std::from_chars_result parsed = std::from_chars(text.data(), text.data() + text.size(), value);
17 if (text.empty() || parsed.ec != std::errc() ||
18 parsed.ptr != text.data() + text.size() || value > maximum)
19 throw std::runtime_error(std::string(
"Invalid ") + field +
": " + text);
23 double ParsePhysicalValue(
const std::string &text,
const char *field) {
24 static const std::regex number(
25 R
"(^[+-]?(?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+)(?:[eE][+-]?[0-9]+)?$)");
26 if (!std::regex_match(text, number))
27 throw std::runtime_error(std::string(
"Invalid ") + field +
": " + text);
28 std::size_t consumed = 0U;
31 value = std::stod(text, &consumed);
32 }
catch (
const std::exception &) {
33 throw std::runtime_error(std::string(
"Invalid ") + field +
": " + text);
35 if (consumed != text.size() || !std::isfinite(value))
36 throw std::runtime_error(std::string(
"Invalid ") + field +
": " + text);
40 Multiplexing ParseMultiplexingMarker(
const std::string &marker) {
43 result.role = MultiplexRole::Selector;
44 else if (!marker.empty()) {
46 throw std::runtime_error(
"Unsupported multiplexing marker.");
47 const std::string value = marker.substr(1U);
48 const std::from_chars_result parsed =
49 std::from_chars(value.data(), value.data() + value.size(), result.value);
50 if (value.empty() || parsed.ec != std::errc() || parsed.ptr != value.data() + value.size())
51 throw std::runtime_error(
"Invalid or unsupported multiplexing marker.");
52 result.role = MultiplexRole::Branch;
58 RawSignalValue ParseValueKey(
const std::string &number) {
60 if (!number.empty() && number[0] ==
'-') {
61 std::int64_t value = 0;
62 const std::from_chars_result parsed =
63 std::from_chars(number.data(), number.data() + number.size(), value);
64 if (parsed.ec != std::errc() || parsed.ptr != number.data() + number.size())
65 throw std::runtime_error(
"Invalid VAL_ integer: " + number);
66 key = NormalizeValueKey(RawSignalValue(value));
68 std::uint64_t value = 0;
69 const std::from_chars_result parsed =
70 std::from_chars(number.data(), number.data() + number.size(), value);
71 if (number.empty() || parsed.ec != std::errc() || parsed.ptr != number.data() + number.size())
72 throw std::runtime_error(
"Invalid VAL_ integer: " + number);
78 std::string UnescapeDescription(
const std::string "ed) {
79 std::string description;
80 for (std::size_t i = 0; i < quoted.size(); ++i) {
81 if (quoted[i] ==
'\\')
83 description += quoted[i];
90 for (
FrameInfo &candidate : database.frames) {
91 if (candidate.canId == canId && candidate.isExtended == isExtended) {
93 throw std::runtime_error(
"Ambiguous VAL_ frame reference.");
98 throw std::runtime_error(
"Unresolved VAL_ frame reference.");
105 if (candidate.name == signalName) {
107 throw std::runtime_error(
"Ambiguous VAL_ signal reference: " + signalName);
112 throw std::runtime_error(
"Unresolved VAL_ signal reference: " + signalName);
116 std::uint64_t NextSignalBit(std::uint64_t bit,
bool isLittleEndian) {
120 return bit % 8U == 0U ? bit + 15U : bit - 1U;
128 std::vector<std::string> SplitReceivers(
const std::string &text) {
129 std::vector<std::string> receivers;
131 std::istringstream stream(text);
133 while (std::getline(stream, token,
',')) {
134 token = dbc_detail::TrimText(token);
136 receivers.push_back(token);
142 void NormalizeCanId(std::uint32_t rawCanId, std::uint32_t &normalizedCanId,
bool &isExtended) {
148 if ((rawCanId & 0x80000000U) != 0U) {
150 normalizedCanId = (rawCanId & 0x1FFFFFFFU);
152 isExtended = (rawCanId > 0x7FFU);
153 normalizedCanId = rawCanId;
157 std::uint32_t TryExtractPgn(std::uint32_t canId,
bool isExtended,
bool &hasPgn) {
163 if ((canId & 0x1FFFFFFFU) != canId)
166 const std::uint32_t pf = (canId >> 16U) & 0xFFU;
167 const std::uint32_t ps = (canId >> 8U) & 0xFFU;
168 const std::uint32_t dp = (canId >> 24U) & 0x01U;
170 std::uint32_t pgn = 0U;
173 pgn = (dp << 16U) | (pf << 8U);
175 pgn = (dp << 16U) | (pf << 8U) | ps;
183FrameInfo DbcFrameHandler::ParseFrameLine(
const std::string &line) {
189 std::istringstream stream(line);
192 if (!(stream >> keyword >> idText) || keyword !=
"BO_")
193 throw std::runtime_error(
"Missing CAN ID in frame definition.");
195 const std::uint32_t rawCanId = ParseDecimal(
196 idText, std::numeric_limits<std::uint32_t>::max(),
"CAN ID");
198 if ((rawCanId & 0x60000000U) != 0U)
199 throw std::runtime_error(
"CAN ID exceeds the 29-bit identifier range.");
201 std::string nameText;
202 if (!std::getline(stream, nameText,
':') || stream.eof())
203 throw std::runtime_error(
"Missing ':' in frame definition.");
204 std::istringstream nameStream(nameText);
207 if (!(nameStream >> frame.
name) || (nameStream >> extra))
208 throw std::runtime_error(
"Missing or invalid frame name.");
210 std::string lengthText;
211 if (!(stream >> lengthText))
212 throw std::runtime_error(
"Missing payload length in frame definition.");
213 frame.
dlc =
static_cast<std::uint8_t
>(ParseDecimal(
214 lengthText, std::numeric_limits<std::uint8_t>::max(),
"payload length"));
216 throw std::runtime_error(
"Missing transmitter in frame definition.");
218 throw std::runtime_error(
"Unexpected trailing field in frame definition: " + extra);
226SignalInfo DbcSignalHandler::ParseSignalLine(
const std::string &line) {
229 NameAndMarker = 1, StartBit, BitLength,
ByteOrder, Signedness,
230 Factor, Offset, Minimum, Maximum, Unit, Receivers
232 static const std::regex syntax(
234 R
"dbc(^SG_[ \t]+([^:]+):[ \t]*)dbc"
236 R
"dbc(([^| \t]+)[ \t]*\|[ \t]*([^@ \t]+)[ \t]*@[ \t]*([01])([+-])[ \t]*)dbc"
238 R
"dbc(\([ \t]*([^, \t]+)[ \t]*,[ \t]*([^ )\t]+)[ \t]*\)[ \t]*)dbc"
240 R
"dbc(\[[ \t]*([^| \t]+)[ \t]*\|[ \t]*([^\] \t]+)[ \t]*\][ \t]*)dbc"
242 R
"dbc("([^"\r\n]*)"[ \t]+([^ \t,]+(?:[ \t]*,[ \t]*[^ \t,]+)*)[ \t]*$)dbc");
244 if (!std::regex_match(line, fields, syntax))
245 throw std::runtime_error(
"Malformed or unsupported SG_ signal definition.");
248 std::istringstream nameFields(fields[NameAndMarker].str());
251 nameFields >> signal.
name >> marker;
252 if (signal.
name.empty() || (nameFields >> extra))
253 throw std::runtime_error(
"Invalid signal name or multiplexing marker.");
254 signal.multiplexing = ParseMultiplexingMarker(marker);
256 signal.
startBit = ParseDecimal(fields[StartBit].str(),
257 std::numeric_limits<std::uint32_t>::max(),
"signal start bit");
258 signal.
length = ParseDecimal(fields[BitLength].str(), 64U,
"signal length");
260 throw std::runtime_error(
"Signal length must be within 1..64 bits.");
262 signal.
isSigned = fields[Signedness].str() ==
"-";
264 signal.
factor = ParsePhysicalValue(fields[Factor].str(),
"signal factor");
265 signal.
offset = ParsePhysicalValue(fields[Offset].str(),
"signal offset");
266 signal.
minimum = ParsePhysicalValue(fields[Minimum].str(),
"signal minimum");
267 signal.
maximum = ParsePhysicalValue(fields[Maximum].str(),
"signal maximum");
269 throw std::runtime_error(
"Signal minimum exceeds maximum.");
270 signal.
unit = fields[Unit].str();
271 signal.
receivers = SplitReceivers(fields[Receivers].str());
275void DbcCommentHandler::ParseCommentLine(
const std::string &line,
DbcDatabase &database) {
282 std::istringstream stream(line);
290 if (token !=
"BO_" && token !=
"SG_")
293 std::uint32_t rawCanId = 0U;
294 std::uint32_t canId = 0U;
295 bool isExtended =
false;
296 std::string signalName;
299 stream >> signalName;
300 NormalizeCanId(rawCanId, canId, isExtended);
302 const std::string::size_type quoteBegin = line.find(
'"');
303 const std::string::size_type quoteEnd = line.rfind(
'"');
304 if (quoteBegin == std::string::npos || quoteEnd == std::string::npos || quoteEnd <= quoteBegin)
307 FrameInfo *frame = FindFrameById(database, canId, isExtended);
308 if (frame ==
nullptr)
310 const std::string comment = line.substr(quoteBegin + 1U, quoteEnd - quoteBegin - 1U);
311 if (token ==
"BO_") {
315 SignalInfo *signal = FindSignalByName(*frame, signalName);
316 if (signal !=
nullptr)
324 for (
FrameInfo &frame : database.frames) {
334 if (signal.
name == signalName)
342void DbcFrameHandler::Handle(
const std::string &line,
DbcParseContext &context)
const {
344 if (FindFrameById(context.database, frame.
canId, frame.
isExtended) !=
nullptr)
345 throw std::runtime_error(
"Duplicate " + std::string(frame.
isExtended ?
"extended" :
"standard") +
346 " frame CAN ID: " + std::to_string(frame.canId));
347 context.database.
frames.push_back(std::move(frame));
348 context.currentFrame = &context.database.
frames.back();
349 context.signalLines.emplace_back();
352void DbcSignalHandler::Handle(
const std::string &line,
DbcParseContext &context)
const {
353 if (context.currentFrame ==
nullptr)
354 throw std::runtime_error(
"Signal found before any frame definition.");
356 if (FindSignalByName(*context.currentFrame, signal.
name) !=
nullptr)
357 throw std::runtime_error(
"Duplicate signal name '" + signal.
name +
358 "' in frame '" + context.currentFrame->
name +
"'.");
359 const std::uint64_t payloadBits =
static_cast<std::uint64_t
>(context.currentFrame->
dlc) * 8U;
360 std::uint64_t bit = signal.
startBit;
361 for (std::uint32_t index = 0U; index < signal.
length; ++index) {
362 if (bit >= payloadBits)
363 throw std::runtime_error(
"Signal exceeds the frame payload length.");
366 context.currentFrame->signals.push_back(std::move(signal));
367 context.signalLines.back().push_back(context.lineNumber);
370void DbcCommentHandler::Handle(
const std::string &line,
DbcParseContext &context)
const {
371 ParseCommentLine(line, context.database);
374DbcStatementDispatcher::DbcStatementDispatcher() {
375 RegisterType<DbcHeaderHandler>(
"VERSION");
376 RegisterType<DbcHeaderHandler>(
"NS_");
377 RegisterType<DbcHeaderHandler>(
"NS_:");
378 RegisterType<DbcHeaderHandler>(
"BS_");
379 RegisterType<DbcHeaderHandler>(
"BS_:");
380 RegisterType<DbcHeaderHandler>(
"BU_");
381 RegisterType<DbcHeaderHandler>(
"BU_:");
382 RegisterType<DbcUnsupportedMultiplexingHandler>(
"SG_MUL_VAL_");
383 RegisterType<DbcFrameHandler>(
"BO_");
384 RegisterType<DbcSignalHandler>(
"SG_");
385 RegisterType<DbcCommentHandler>(
"CM_");
386 RegisterType<DbcValueDescriptionHandler>(
"VAL_");
389void DbcHeaderHandler::Handle(
const std::string &,
DbcParseContext &)
const {}
393 std::istringstream stream(line);
396 const HandlerMap::const_iterator handler = m_handlers.find(keyword);
397 if (handler == m_handlers.end())
399 handler->second->Handle(line, context);
403void DbcValueDescriptionHandler::Handle(
const std::string &line,
DbcParseContext &context)
const {
404 static const std::regex header(R
"(^VAL_[ \t]+([^ \t]+)[ \t]+([^ \t;]+)[ \t]+(.*)$)");
405 static const std::regex pair(R
"dbc(^([^ \t]+)[ \t]+"((?:[^"\\]|\\["\\])*)")dbc");
407 if (!std::regex_match(line, fields, header))
408 throw std::runtime_error(
"Malformed or unsupported VAL_ statement.");
409 const std::uint32_t rawId = ParseDecimal(fields[1].str(), std::numeric_limits<std::uint32_t>::max(),
"VAL_ CAN ID");
410 if ((rawId & 0x60000000U) != 0U)
411 throw std::runtime_error(
"VAL_ CAN ID exceeds the 29-bit identifier range.");
413 NormalizeCanId(rawId, pending.canId, pending.isExtended);
414 pending.signalName = fields[2].str();
415 pending.lineNumber = context.lineNumber;
416 std::string rest = fields[3].str();
417 while (rest !=
";") {
418 if (!std::regex_search(rest, fields, pair))
419 throw std::runtime_error(
"Malformed VAL_ value/description pair or missing semicolon.");
420 const std::string number = fields[1].str();
421 const RawSignalValue key = ParseValueKey(number);
422 const std::string description = UnescapeDescription(fields[2].str());
423 if (!pending.descriptions.emplace(key, description).second)
424 throw std::runtime_error(
"Duplicate VAL_ value key: " + number);
425 const std::size_t consumed =
static_cast<std::size_t
>(fields.length());
426 rest.erase(0, consumed);
427 if (!rest.empty() && rest[0] !=
';' && rest[0] !=
' ' && rest[0] !=
'\t')
428 throw std::runtime_error(
"Missing whitespace between VAL_ pairs.");
429 rest = dbc_detail::TrimText(rest);
431 if (pending.descriptions.empty())
432 throw std::runtime_error(
"VAL_ requires at least one value/description pair.");
433 context.pendingValues.push_back(std::move(pending));
436void DbcParseContext::ResolveValueDescriptions(
const std::string &filePath) {
439 FrameInfo *frame = FindUniqueFrame(database, pending.canId, pending.isExtended);
440 SignalInfo *signal = FindUniqueSignal(*frame, pending.signalName);
441 for (
const ValueDescriptions::value_type &entry: pending.descriptions) {
443 throw std::runtime_error(
"Duplicate VAL_ value key for signal: " + pending.signalName);
445 }
catch (
const std::exception &error) {
447 filePath, pending.lineNumber, error.what());
452void DbcUnsupportedMultiplexingHandler::Handle(
const std::string &,
DbcParseContext &)
const {
453 throw std::runtime_error(
"Extended and nested multiplexing are unsupported.");
457 std::vector<std::uint64_t> SignalBits(
const SignalInfo &signal) {
458 std::vector<std::uint64_t> bits;
459 std::uint64_t bit = signal.
startBit;
460 for (std::uint32_t index = 0U; index < signal.
length; ++index) {
468 return first.multiplexing.role == MultiplexRole::Branch &&
469 second.multiplexing.role == MultiplexRole::Branch &&
470 first.multiplexing.value != second.multiplexing.value;
474 if (signal.multiplexing.role != MultiplexRole::Branch)
476 if (selector ==
nullptr)
477 return "Multiplexed branch has no selector.";
478 if (selector->
length < 64U && signal.multiplexing.value >= (std::uint64_t(1U) << selector->
length))
479 return "Branch value exceeds selector raw range.";
484 const std::vector<std::size_t> &lines,
485 const std::string &filePath) {
487 for (std::size_t index = 0U; index < frame.signals.size(); ++index) {
488 const SignalInfo &signal = frame.signals[index];
489 if (signal.multiplexing.role != MultiplexRole::Selector)
491 if (selector !=
nullptr || signal.
isSigned)
493 filePath, lines[index],
494 "Multiplexing requires one unsigned selector per frame.");
501 const std::vector<std::uint64_t> firstBits = SignalBits(first);
502 const std::vector<std::uint64_t> secondBits = SignalBits(second);
503 for (
const std::uint64_t firstBit : firstBits) {
504 for (
const std::uint64_t secondBit : secondBits) {
505 if (firstBit == secondBit)
513void DbcParseContext::ValidateMultiplexing(
const std::string &filePath)
const {
514 for (std::size_t frameIndex = 0U; frameIndex < database.
frames.size(); ++frameIndex) {
516 const SignalInfo *selector = FindSelector(frame, signalLines[frameIndex], filePath);
517 for (std::size_t index = 0U; index < frame.signals.size(); ++index) {
518 const SignalInfo &signal = frame.signals[index];
519 std::string error = ValidateBranch(signal, selector);
520 for (std::size_t previous = 0U; previous < index && error.empty(); ++previous) {
521 const SignalInfo &other = frame.signals[previous];
522 if (!AreMutuallyExclusive(signal, other) && SignalsOverlap(signal, other))
523 error =
"Signals overlap without mutually exclusive multiplexing conditions.";
527 filePath, signalLines[frameIndex][index], error);
bool Dispatch(const std::string &line, DbcParseContext &context) const
ByteOrder
Signal byte order used for runtime decoding.
Parsed DBC content stored in a simple internal form.
std::vector< FrameInfo > frames
Describes one CAN frame from a DBC file.
Describes one signal inside a DBC frame.
std::vector< std::string > receivers
ValueDescriptions valueDescriptions