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 auto 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);
45 std::string TrimText(
const std::string &text) {
46 std::string::size_type begin = 0U;
47 while ((begin < text.size()) &&
48 std::isspace(
static_cast<unsigned char>(text[begin])))
51 std::string::size_type end = text.size();
52 while ((end > begin) &&
53 std::isspace(
static_cast<unsigned char>(text[end - 1U])))
56 return text.substr(begin, end - begin);
63 std::vector<std::string> SplitReceivers(
const std::string &text) {
64 std::vector<std::string> receivers;
66 std::istringstream stream(text);
68 while (std::getline(stream, token,
',')) {
69 token = TrimText(token);
71 receivers.push_back(token);
77 void NormalizeCanId(std::uint32_t rawCanId,
78 std::uint32_t &normalizedCanId,
85 if ((rawCanId & 0x80000000U) != 0U) {
87 normalizedCanId = (rawCanId & 0x1FFFFFFFU);
89 isExtended = (rawCanId > 0x7FFU);
90 normalizedCanId = rawCanId;
94 std::uint32_t TryExtractPgn(std::uint32_t canId,
bool isExtended,
bool &hasPgn) {
100 if ((canId & 0x1FFFFFFFU) != canId)
103 const std::uint32_t pf = (canId >> 16U) & 0xFFU;
104 const std::uint32_t ps = (canId >> 8U) & 0xFFU;
105 const std::uint32_t dp = (canId >> 24U) & 0x01U;
107 std::uint32_t pgn = 0U;
110 pgn = (dp << 16U) | (pf << 8U);
112 pgn = (dp << 16U) | (pf << 8U) | ps;
120FrameInfo DbcFrameHandler::ParseFrameLine(
const std::string &line) {
126 std::istringstream stream(line);
129 if (!(stream >> keyword >> idText) || keyword !=
"BO_")
130 throw std::runtime_error(
"Missing CAN ID in frame definition.");
132 const std::uint32_t rawCanId = ParseDecimal(
133 idText, std::numeric_limits<std::uint32_t>::max(),
"CAN ID");
135 if ((rawCanId & 0x60000000U) != 0U)
136 throw std::runtime_error(
"CAN ID exceeds the 29-bit identifier range.");
138 std::string nameText;
139 if (!std::getline(stream, nameText,
':') || stream.eof())
140 throw std::runtime_error(
"Missing ':' in frame definition.");
141 std::istringstream nameStream(nameText);
144 if (!(nameStream >> frame.
name) || (nameStream >> extra))
145 throw std::runtime_error(
"Missing or invalid frame name.");
147 std::string lengthText;
148 if (!(stream >> lengthText))
149 throw std::runtime_error(
"Missing payload length in frame definition.");
150 frame.
dlc =
static_cast<std::uint8_t
>(ParseDecimal(
151 lengthText, std::numeric_limits<std::uint8_t>::max(),
"payload length"));
153 throw std::runtime_error(
"Missing transmitter in frame definition.");
155 throw std::runtime_error(
"Unexpected trailing field in frame definition: " + extra);
163SignalInfo DbcSignalHandler::ParseSignalLine(
const std::string &line) {
165 static const std::regex syntax(
166 R
"dbc(^SG_[ \t]+([^:]+):[ \t]*([^| \t]+)[ \t]*\|[ \t]*([^@ \t]+)[ \t]*@[ \t]*([01])([+-])[ \t]*\([ \t]*([^, \t]+)[ \t]*,[ \t]*([^ )\t]+)[ \t]*\)[ \t]*\[[ \t]*([^| \t]+)[ \t]*\|[ \t]*([^\] \t]+)[ \t]*\][ \t]*"([^"\r\n]*)"[ \t]+([^ \t,]+(?:[ \t]*,[ \t]*[^ \t,]+)*)[ \t]*$)dbc");
168 if (!std::regex_match(line, fields, syntax))
169 throw std::runtime_error(
"Malformed or unsupported SG_ signal definition.");
172 signal.
name = TrimText(fields[1].str());
173 if (signal.
name.empty() || signal.
name.find_first_of(
" \t\r\n") != std::string::npos)
174 throw std::runtime_error(
"Invalid signal name or unsupported multiplexing syntax.");
176 signal.
startBit = ParseDecimal(fields[2].str(),
177 std::numeric_limits<std::uint32_t>::max(),
"signal start bit");
178 signal.
length = ParseDecimal(fields[3].str(), 64U,
"signal length");
180 throw std::runtime_error(
"Signal length must be within 1..64 bits.");
182 signal.
isSigned = fields[5].str() ==
"-";
184 signal.
factor = ParsePhysicalValue(fields[6].str(),
"signal factor");
185 signal.
offset = ParsePhysicalValue(fields[7].str(),
"signal offset");
186 signal.
minimum = ParsePhysicalValue(fields[8].str(),
"signal minimum");
187 signal.
maximum = ParsePhysicalValue(fields[9].str(),
"signal maximum");
189 throw std::runtime_error(
"Signal minimum exceeds maximum.");
190 signal.
unit = fields[10].str();
191 signal.
receivers = SplitReceivers(fields[11].str());
195void DbcCommentHandler::ParseCommentLine(
const std::string &line,
DbcDatabase &database) {
202 std::istringstream stream(line);
211 if (token ==
"BO_") {
212 std::uint32_t rawCanId = 0U;
213 std::uint32_t canId = 0U;
214 bool isExtended =
false;
217 NormalizeCanId(rawCanId, canId, isExtended);
219 const std::string::size_type quoteBegin = line.find(
'"');
220 const std::string::size_type quoteEnd = line.rfind(
'"');
222 if ((quoteBegin == std::string::npos) ||
223 (quoteEnd == std::string::npos) ||
224 (quoteEnd <= quoteBegin))
227 FrameInfo *frame = FindFrameById(database, canId, isExtended);
228 if (frame !=
nullptr)
229 frame->
comment = line.substr(quoteBegin + 1U, quoteEnd - quoteBegin - 1U);
230 }
else if (token ==
"SG_") {
231 std::uint32_t rawCanId = 0U;
232 std::uint32_t canId = 0U;
233 bool isExtended =
false;
234 std::string signalName;
237 stream >> signalName;
239 NormalizeCanId(rawCanId, canId, isExtended);
241 const std::string::size_type quoteBegin = line.find(
'"');
242 const std::string::size_type quoteEnd = line.rfind(
'"');
244 if ((quoteBegin == std::string::npos) ||
245 (quoteEnd == std::string::npos) ||
246 (quoteEnd <= quoteBegin))
249 FrameInfo *frame = FindFrameById(database, canId, isExtended);
250 if (frame !=
nullptr) {
251 SignalInfo *signal = FindSignalByName(*frame, signalName);
252 if (signal !=
nullptr)
253 signal->
comment = line.substr(quoteBegin + 1U, quoteEnd - quoteBegin - 1U);
262 for (std::size_t index = 0U; index < database.
frames.size(); ++index) {
263 if ((database.
frames[index].canId == canId) &&
264 (database.
frames[index].isExtended == isExtended))
265 return &database.
frames[index];
272 for (std::size_t index = 0U; index < frame.signals.size(); ++index) {
273 if (frame.signals[index].
name == signalName)
274 return &frame.signals[index];
281void DbcFrameHandler::Handle(
const std::string &line,
DbcParseContext &context)
const {
282 context.database.
frames.push_back(ParseFrameLine(line));
283 context.currentFrame = &context.database.
frames.back();
286void DbcSignalHandler::Handle(
const std::string &line,
DbcParseContext &context)
const {
287 if (context.currentFrame ==
nullptr)
288 throw std::runtime_error(
"Signal found before any frame definition.");
290 const std::uint64_t payloadBits =
static_cast<std::uint64_t
>(context.currentFrame->
dlc) * 8U;
291 std::uint64_t bit = signal.
startBit;
292 for (std::uint32_t index = 0U; index < signal.
length; ++index) {
293 if (bit >= payloadBits)
294 throw std::runtime_error(
"Signal exceeds the frame payload length.");
298 bit = (bit % 8U == 0U) ? bit + 15U : bit - 1U;
300 context.currentFrame->signals.push_back(std::move(signal));
303void DbcCommentHandler::Handle(
const std::string &line,
DbcParseContext &context)
const {
304 if (line.compare(0U, 4U,
"CM_ ") == 0)
305 ParseCommentLine(line, context.database);
308DbcStatementDispatcher::DbcStatementDispatcher() {
309 RegisterType<DbcFrameHandler>(
"BO_");
310 RegisterType<DbcSignalHandler>(
"SG_");
311 RegisterType<DbcCommentHandler>(
"CM_");
316 std::istringstream stream(line);
319 const auto handler = m_handlers.find(keyword);
320 if (handler == m_handlers.end())
322 handler->second->Handle(line, context);
bool Dispatch(const std::string &line, DbcParseContext &context) const
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