2#include "dbc_statement_handler.h"
4#include "signal_layout.h"
13 std::uint32_t ParseDecimal(
const std::string &text,
14 std::uint32_t maximum,
16 std::uint32_t value = 0U;
17 const std::from_chars_result parsed = std::from_chars(text.data(), text.data() + text.size(), value);
18 if (text.empty() || parsed.ec != std::errc() ||
19 parsed.ptr != text.data() + text.size() || value > maximum)
20 throw std::runtime_error(std::string(
"Invalid ") + field +
": " + text);
24 double ParsePhysicalValue(
const std::string &text,
const char *field) {
25 static const std::regex number(
26 R
"(^[+-]?(?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+)(?:[eE][+-]?[0-9]+)?$)");
27 if (!std::regex_match(text, number))
28 throw std::runtime_error(std::string(
"Invalid ") + field +
": " + text);
29 std::size_t consumed = 0U;
32 value = std::stod(text, &consumed);
33 }
catch (
const std::exception &) {
34 throw std::runtime_error(std::string(
"Invalid ") + field +
": " + text);
36 if (consumed != text.size() || !std::isfinite(value))
37 throw std::runtime_error(std::string(
"Invalid ") + field +
": " + text);
41 Multiplexing ParseMultiplexingMarker(
const std::string &marker) {
44 result.role = MultiplexRole::Selector;
45 else if (!marker.empty()) {
47 throw std::runtime_error(
"Unsupported multiplexing marker.");
48 const std::string value = marker.substr(1U);
49 const std::from_chars_result parsed =
50 std::from_chars(value.data(), value.data() + value.size(), result.value);
51 if (value.empty() || parsed.ec != std::errc() || parsed.ptr != value.data() + value.size())
52 throw std::runtime_error(
"Invalid or unsupported multiplexing marker.");
53 result.role = MultiplexRole::Branch;
59 RawSignalValue ParseValueKey(
const std::string &number) {
61 if (!number.empty() && number[0] ==
'-') {
62 std::int64_t value = 0;
63 const std::from_chars_result parsed =
64 std::from_chars(number.data(), number.data() + number.size(), value);
65 if (parsed.ec != std::errc() || parsed.ptr != number.data() + number.size())
66 throw std::runtime_error(
"Invalid VAL_ integer: " + number);
67 key = NormalizeValueKey(RawSignalValue(value));
69 std::uint64_t value = 0;
70 const std::from_chars_result parsed =
71 std::from_chars(number.data(), number.data() + number.size(), value);
72 if (number.empty() || parsed.ec != std::errc() || parsed.ptr != number.data() + number.size())
73 throw std::runtime_error(
"Invalid VAL_ integer: " + number);
79 std::string UnescapeDescription(
const std::string "ed) {
80 std::string description;
81 for (std::size_t i = 0; i < quoted.size(); ++i) {
82 if (quoted[i] ==
'\\')
84 description += quoted[i];
91 for (
FrameInfo &candidate : database.frames) {
92 if (candidate.canId == canId && candidate.isExtended == isExtended) {
94 throw std::runtime_error(
"Ambiguous VAL_ frame reference.");
99 throw std::runtime_error(
"Unresolved VAL_ frame reference.");
106 if (candidate.name == signalName) {
108 throw std::runtime_error(
"Ambiguous VAL_ signal reference: " + signalName);
113 throw std::runtime_error(
"Unresolved VAL_ signal reference: " + signalName);
117 std::uint64_t NextSignalBit(std::uint64_t bit,
bool isLittleEndian) {
121 return bit % 8U == 0U ? bit + 15U : bit - 1U;
129 std::vector<std::string> SplitReceivers(
const std::string &text) {
130 std::vector<std::string> receivers;
132 std::istringstream stream(text);
134 while (std::getline(stream, token,
',')) {
135 token = dbc_detail::TrimText(token);
137 receivers.push_back(token);
143 void NormalizeCanId(std::uint32_t rawCanId, std::uint32_t &normalizedCanId,
bool &isExtended) {
149 if ((rawCanId & 0x80000000U) != 0U) {
151 normalizedCanId = (rawCanId & 0x1FFFFFFFU);
153 isExtended = (rawCanId > 0x7FFU);
154 normalizedCanId = rawCanId;
158 std::uint32_t TryExtractPgn(std::uint32_t canId,
bool isExtended,
bool &hasPgn) {
164 if ((canId & 0x1FFFFFFFU) != canId)
167 const std::uint32_t pf = (canId >> 16U) & 0xFFU;
168 const std::uint32_t ps = (canId >> 8U) & 0xFFU;
169 const std::uint32_t dp = (canId >> 24U) & 0x01U;
171 std::uint32_t pgn = 0U;
174 pgn = (dp << 16U) | (pf << 8U);
176 pgn = (dp << 16U) | (pf << 8U) | ps;
184FrameInfo DbcFrameHandler::ParseFrameLine(
const std::string &line) {
190 std::istringstream stream(line);
193 if (!(stream >> keyword >> idText) || keyword !=
"BO_")
194 throw std::runtime_error(
"Missing CAN ID in frame definition.");
196 const std::uint32_t rawCanId = ParseDecimal(
197 idText, std::numeric_limits<std::uint32_t>::max(),
"CAN ID");
199 if ((rawCanId & 0x60000000U) != 0U)
200 throw std::runtime_error(
"CAN ID exceeds the 29-bit identifier range.");
202 std::string nameText;
203 if (!std::getline(stream, nameText,
':') || stream.eof())
204 throw std::runtime_error(
"Missing ':' in frame definition.");
205 std::istringstream nameStream(nameText);
208 if (!(nameStream >> frame.
name) || (nameStream >> extra))
209 throw std::runtime_error(
"Missing or invalid frame name.");
211 std::string lengthText;
212 if (!(stream >> lengthText))
213 throw std::runtime_error(
"Missing payload length in frame definition.");
214 frame.
dlc =
static_cast<std::uint8_t
>(ParseDecimal(
215 lengthText, std::numeric_limits<std::uint8_t>::max(),
"payload length"));
217 throw std::runtime_error(
"Missing transmitter in frame definition.");
219 throw std::runtime_error(
"Unexpected trailing field in frame definition: " + extra);
227SignalInfo DbcSignalHandler::ParseSignalLine(
const std::string &line) {
230 NameAndMarker = 1, StartBit, BitLength,
ByteOrder, Signedness,
231 Factor, Offset, Minimum, Maximum, Unit, Receivers
233 static const std::regex syntax(
235 R
"dbc(^SG_[ \t]+([^:]+):[ \t]*)dbc"
237 R
"dbc(([^| \t]+)[ \t]*\|[ \t]*([^@ \t]+)[ \t]*@[ \t]*([01])([+-])[ \t]*)dbc"
239 R
"dbc(\([ \t]*([^, \t]+)[ \t]*,[ \t]*([^ )\t]+)[ \t]*\)[ \t]*)dbc"
241 R
"dbc(\[[ \t]*([^| \t]+)[ \t]*\|[ \t]*([^\] \t]+)[ \t]*\][ \t]*)dbc"
243 R
"dbc("([^"\r\n]*)"[ \t]+([^ \t,]+(?:[ \t]*,[ \t]*[^ \t,]+)*)[ \t]*$)dbc");
245 if (!std::regex_match(line, fields, syntax))
246 throw std::runtime_error(
"Malformed or unsupported SG_ signal definition.");
249 std::istringstream nameFields(fields[NameAndMarker].str());
252 nameFields >> signal.
name >> marker;
253 if (signal.
name.empty() || (nameFields >> extra))
254 throw std::runtime_error(
"Invalid signal name or multiplexing marker.");
255 signal.multiplexing = ParseMultiplexingMarker(marker);
257 signal.
startBit = ParseDecimal(fields[StartBit].str(),
258 std::numeric_limits<std::uint32_t>::max(),
"signal start bit");
259 signal.
length = ParseDecimal(fields[BitLength].str(), 64U,
"signal length");
261 throw std::runtime_error(
"Signal length must be within 1..64 bits.");
263 signal.
isSigned = fields[Signedness].str() ==
"-";
265 signal.
factor = ParsePhysicalValue(fields[Factor].str(),
"signal factor");
266 signal.
offset = ParsePhysicalValue(fields[Offset].str(),
"signal offset");
267 signal.
minimum = ParsePhysicalValue(fields[Minimum].str(),
"signal minimum");
268 signal.
maximum = ParsePhysicalValue(fields[Maximum].str(),
"signal maximum");
270 throw std::runtime_error(
"Signal minimum exceeds maximum.");
271 signal.
unit = fields[Unit].str();
272 signal.
receivers = SplitReceivers(fields[Receivers].str());
276void DbcCommentHandler::ParseCommentLine(
const std::string &line,
DbcDatabase &database) {
283 std::istringstream stream(line);
291 if (token !=
"BO_" && token !=
"SG_")
294 std::uint32_t rawCanId = 0U;
295 std::uint32_t canId = 0U;
296 bool isExtended =
false;
297 std::string signalName;
300 stream >> signalName;
301 NormalizeCanId(rawCanId, canId, isExtended);
303 const std::string::size_type quoteBegin = line.find(
'"');
304 const std::string::size_type quoteEnd = line.rfind(
'"');
305 if (quoteBegin == std::string::npos || quoteEnd == std::string::npos || quoteEnd <= quoteBegin)
308 FrameInfo *frame = FindFrameById(database, canId, isExtended);
309 if (frame ==
nullptr)
311 const std::string comment = line.substr(quoteBegin + 1U, quoteEnd - quoteBegin - 1U);
312 if (token ==
"BO_") {
316 SignalInfo *signal = FindSignalByName(*frame, signalName);
317 if (signal !=
nullptr)
325 for (
FrameInfo &frame : database.frames) {
335 if (signal.
name == signalName)
343void DbcFrameHandler::Handle(
const std::string &line,
DbcParseContext &context)
const {
345 if (FindFrameById(context.database, frame.
canId, frame.
isExtended) !=
nullptr)
346 throw std::runtime_error(
"Duplicate " + std::string(frame.
isExtended ?
"extended" :
"standard") +
347 " frame CAN ID: " + std::to_string(frame.canId));
348 context.database.
frames.push_back(std::move(frame));
349 context.currentFrame = &context.database.
frames.back();
350 context.signalLines.emplace_back();
353void DbcSignalHandler::Handle(
const std::string &line,
DbcParseContext &context)
const {
354 if (context.currentFrame ==
nullptr)
355 throw std::runtime_error(
"Signal found before any frame definition.");
357 if (FindSignalByName(*context.currentFrame, signal.
name) !=
nullptr)
358 throw std::runtime_error(
"Duplicate signal name '" + signal.
name +
359 "' in frame '" + context.currentFrame->
name +
"'.");
361 context.currentFrame->
dlc))
362 throw std::runtime_error(
"Signal exceeds the frame payload length.");
363 context.currentFrame->signals.push_back(std::move(signal));
364 context.signalLines.back().push_back(context.lineNumber);
367void DbcCommentHandler::Handle(
const std::string &line,
DbcParseContext &context)
const {
368 ParseCommentLine(line, context.database);
371DbcStatementDispatcher::DbcStatementDispatcher() {
372 RegisterType<DbcHeaderHandler>(
"VERSION");
373 RegisterType<DbcHeaderHandler>(
"NS_");
374 RegisterType<DbcHeaderHandler>(
"NS_:");
375 RegisterType<DbcHeaderHandler>(
"BS_");
376 RegisterType<DbcHeaderHandler>(
"BS_:");
377 RegisterType<DbcHeaderHandler>(
"BU_");
378 RegisterType<DbcHeaderHandler>(
"BU_:");
379 RegisterType<DbcUnsupportedMultiplexingHandler>(
"SG_MUL_VAL_");
380 RegisterType<DbcFrameHandler>(
"BO_");
381 RegisterType<DbcSignalHandler>(
"SG_");
382 RegisterType<DbcCommentHandler>(
"CM_");
383 RegisterType<DbcValueDescriptionHandler>(
"VAL_");
386void DbcHeaderHandler::Handle(
const std::string &,
DbcParseContext &)
const {}
390 std::istringstream stream(line);
393 const HandlerMap::const_iterator handler = m_handlers.find(keyword);
394 if (handler == m_handlers.end())
396 handler->second->Handle(line, context);
400void DbcValueDescriptionHandler::Handle(
const std::string &line,
DbcParseContext &context)
const {
401 static const std::regex header(R
"(^VAL_[ \t]+([^ \t]+)[ \t]+([^ \t;]+)[ \t]+(.*)$)");
402 static const std::regex pair(R
"dbc(^([^ \t]+)[ \t]+"((?:[^"\\]|\\["\\])*)")dbc");
404 if (!std::regex_match(line, fields, header))
405 throw std::runtime_error(
"Malformed or unsupported VAL_ statement.");
406 const std::uint32_t rawId = ParseDecimal(fields[1].str(), std::numeric_limits<std::uint32_t>::max(),
"VAL_ CAN ID");
407 if ((rawId & 0x60000000U) != 0U)
408 throw std::runtime_error(
"VAL_ CAN ID exceeds the 29-bit identifier range.");
410 NormalizeCanId(rawId, pending.canId, pending.isExtended);
411 pending.signalName = fields[2].str();
412 pending.lineNumber = context.lineNumber;
413 std::string rest = fields[3].str();
414 while (rest !=
";") {
415 if (!std::regex_search(rest, fields, pair))
416 throw std::runtime_error(
"Malformed VAL_ value/description pair or missing semicolon.");
417 const std::string number = fields[1].str();
418 const RawSignalValue key = ParseValueKey(number);
419 const std::string description = UnescapeDescription(fields[2].str());
420 if (!pending.descriptions.emplace(key, description).second)
421 throw std::runtime_error(
"Duplicate VAL_ value key: " + number);
422 const std::size_t consumed =
static_cast<std::size_t
>(fields.length());
423 rest.erase(0, consumed);
424 if (!rest.empty() && rest[0] !=
';' && rest[0] !=
' ' && rest[0] !=
'\t')
425 throw std::runtime_error(
"Missing whitespace between VAL_ pairs.");
426 rest = dbc_detail::TrimText(rest);
428 if (pending.descriptions.empty())
429 throw std::runtime_error(
"VAL_ requires at least one value/description pair.");
430 context.pendingValues.push_back(std::move(pending));
433void DbcParseContext::ResolveValueDescriptions(
const std::string &filePath) {
436 FrameInfo *frame = FindUniqueFrame(database, pending.canId, pending.isExtended);
437 SignalInfo *signal = FindUniqueSignal(*frame, pending.signalName);
438 for (
const ValueDescriptions::value_type &entry: pending.descriptions) {
440 throw std::runtime_error(
"Duplicate VAL_ value key for signal: " + pending.signalName);
442 }
catch (
const std::exception &error) {
444 filePath, pending.lineNumber, error.what());
449void DbcUnsupportedMultiplexingHandler::Handle(
const std::string &,
DbcParseContext &)
const {
450 throw std::runtime_error(
"Extended and nested multiplexing are unsupported.");
454 std::vector<std::uint64_t> SignalBits(
const SignalInfo &signal) {
455 std::vector<std::uint64_t> bits;
456 std::uint64_t bit = signal.
startBit;
457 for (std::uint32_t index = 0U; index < signal.
length; ++index) {
465 return first.multiplexing.role == MultiplexRole::Branch &&
466 second.multiplexing.role == MultiplexRole::Branch &&
467 first.multiplexing.value != second.multiplexing.value;
471 if (signal.multiplexing.role != MultiplexRole::Branch)
473 if (selector ==
nullptr)
474 return "Multiplexed branch has no selector.";
475 if (selector->
length < 64U && signal.multiplexing.value >= (std::uint64_t(1U) << selector->
length))
476 return "Branch value exceeds selector raw range.";
481 const std::vector<std::size_t> &lines,
482 const std::string &filePath) {
484 for (std::size_t index = 0U; index < frame.signals.size(); ++index) {
485 const SignalInfo &signal = frame.signals[index];
486 if (signal.multiplexing.role != MultiplexRole::Selector)
488 if (selector !=
nullptr || signal.
isSigned)
490 filePath, lines[index],
491 "Multiplexing requires one unsigned selector per frame.");
498 const std::vector<std::uint64_t> firstBits = SignalBits(first);
499 const std::vector<std::uint64_t> secondBits = SignalBits(second);
500 for (
const std::uint64_t firstBit : firstBits) {
501 for (
const std::uint64_t secondBit : secondBits) {
502 if (firstBit == secondBit)
510void DbcParseContext::ValidateMultiplexing(
const std::string &filePath)
const {
511 for (std::size_t frameIndex = 0U; frameIndex < database.
frames.size(); ++frameIndex) {
513 const SignalInfo *selector = FindSelector(frame, signalLines[frameIndex], filePath);
514 for (std::size_t index = 0U; index < frame.signals.size(); ++index) {
515 const SignalInfo &signal = frame.signals[index];
516 std::string error = ValidateBranch(signal, selector);
517 for (std::size_t previous = 0U; previous < index && error.empty(); ++previous) {
518 const SignalInfo &other = frame.signals[previous];
519 if (!AreMutuallyExclusive(signal, other) && SignalsOverlap(signal, other))
520 error =
"Signals overlap without mutually exclusive multiplexing conditions.";
524 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