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);
28 std::string TrimText(
const std::string &text) {
29 std::string::size_type begin = 0U;
30 while ((begin < text.size()) &&
31 std::isspace(
static_cast<unsigned char>(text[begin])))
34 std::string::size_type end = text.size();
35 while ((end > begin) &&
36 std::isspace(
static_cast<unsigned char>(text[end - 1U])))
39 return text.substr(begin, end - begin);
46 std::vector<std::string> SplitReceivers(
const std::string &text) {
47 std::vector<std::string> receivers;
49 std::istringstream stream(text);
51 while (std::getline(stream, token,
',')) {
52 token = TrimText(token);
54 receivers.push_back(token);
60 void NormalizeCanId(std::uint32_t rawCanId,
61 std::uint32_t &normalizedCanId,
68 if ((rawCanId & 0x80000000U) != 0U) {
70 normalizedCanId = (rawCanId & 0x1FFFFFFFU);
72 isExtended = (rawCanId > 0x7FFU);
73 normalizedCanId = rawCanId;
77 std::uint32_t TryExtractPgn(std::uint32_t canId,
bool isExtended,
bool &hasPgn) {
83 if ((canId & 0x1FFFFFFFU) != canId)
86 const std::uint32_t pf = (canId >> 16U) & 0xFFU;
87 const std::uint32_t ps = (canId >> 8U) & 0xFFU;
88 const std::uint32_t dp = (canId >> 24U) & 0x01U;
90 std::uint32_t pgn = 0U;
93 pgn = (dp << 16U) | (pf << 8U);
95 pgn = (dp << 16U) | (pf << 8U) | ps;
103FrameInfo DbcFrameHandler::ParseFrameLine(
const std::string &line) {
109 std::istringstream stream(line);
112 if (!(stream >> keyword >> idText) || keyword !=
"BO_")
113 throw std::runtime_error(
"Missing CAN ID in frame definition.");
115 const std::uint32_t rawCanId = ParseDecimal(
116 idText, std::numeric_limits<std::uint32_t>::max(),
"CAN ID");
118 if ((rawCanId & 0x60000000U) != 0U)
119 throw std::runtime_error(
"CAN ID exceeds the 29-bit identifier range.");
121 std::string nameText;
122 if (!std::getline(stream, nameText,
':') || stream.eof())
123 throw std::runtime_error(
"Missing ':' in frame definition.");
124 std::istringstream nameStream(nameText);
127 if (!(nameStream >> frame.
name) || (nameStream >> extra))
128 throw std::runtime_error(
"Missing or invalid frame name.");
130 std::string lengthText;
131 if (!(stream >> lengthText))
132 throw std::runtime_error(
"Missing payload length in frame definition.");
133 frame.
dlc =
static_cast<std::uint8_t
>(ParseDecimal(
134 lengthText, std::numeric_limits<std::uint8_t>::max(),
"payload length"));
136 throw std::runtime_error(
"Missing transmitter in frame definition.");
138 throw std::runtime_error(
"Unexpected trailing field in frame definition: " + extra);
146SignalInfo DbcSignalHandler::ParseSignalLine(
const std::string &line) {
148 static const std::regex syntax(
149 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");
151 if (!std::regex_match(line, fields, syntax))
152 throw std::runtime_error(
"Malformed or unsupported SG_ signal definition.");
155 signal.
name = TrimText(fields[1].str());
156 if (signal.
name.empty() || signal.
name.find_first_of(
" \t\r\n") != std::string::npos)
157 throw std::runtime_error(
"Invalid signal name or unsupported multiplexing syntax.");
159 signal.
startBit = ParseDecimal(fields[2].str(),
160 std::numeric_limits<std::uint32_t>::max(),
"signal start bit");
161 signal.
length = ParseDecimal(fields[3].str(), 64U,
"signal length");
163 throw std::runtime_error(
"Signal length must be within 1..64 bits.");
165 signal.
isSigned = fields[5].str() ==
"-";
167 const auto parsePhysical = [](
const std::string &text,
const char *field) {
168 static const std::regex number(
169 R
"(^[+-]?(?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+)(?:[eE][+-]?[0-9]+)?$)");
170 if (!std::regex_match(text, number))
171 throw std::runtime_error(std::string(
"Invalid ") + field +
": " + text);
172 std::size_t consumed = 0U;
175 value = std::stod(text, &consumed);
176 }
catch (
const std::exception &) {
177 throw std::runtime_error(std::string(
"Invalid ") + field +
": " + text);
179 if (consumed != text.size() || !std::isfinite(value))
180 throw std::runtime_error(std::string(
"Invalid ") + field +
": " + text);
183 signal.
factor = parsePhysical(fields[6].str(),
"signal factor");
184 signal.
offset = parsePhysical(fields[7].str(),
"signal offset");
185 signal.
minimum = parsePhysical(fields[8].str(),
"signal minimum");
186 signal.
maximum = parsePhysical(fields[9].str(),
"signal maximum");
188 throw std::runtime_error(
"Signal minimum exceeds maximum.");
189 signal.
unit = fields[10].str();
190 signal.
receivers = SplitReceivers(fields[11].str());
194void DbcCommentHandler::ParseCommentLine(
const std::string &line,
DbcDatabase &database) {
201 std::istringstream stream(line);
210 if (token ==
"BO_") {
211 std::uint32_t rawCanId = 0U;
212 std::uint32_t canId = 0U;
213 bool isExtended =
false;
216 NormalizeCanId(rawCanId, canId, isExtended);
218 const std::string::size_type quoteBegin = line.find(
'"');
219 const std::string::size_type quoteEnd = line.rfind(
'"');
221 if ((quoteBegin == std::string::npos) ||
222 (quoteEnd == std::string::npos) ||
223 (quoteEnd <= quoteBegin))
226 FrameInfo *frame = FindFrameById(database, canId, isExtended);
227 if (frame !=
nullptr)
228 frame->
comment = line.substr(quoteBegin + 1U, quoteEnd - quoteBegin - 1U);
229 }
else if (token ==
"SG_") {
230 std::uint32_t rawCanId = 0U;
231 std::uint32_t canId = 0U;
232 bool isExtended =
false;
233 std::string signalName;
236 stream >> signalName;
238 NormalizeCanId(rawCanId, canId, isExtended);
240 const std::string::size_type quoteBegin = line.find(
'"');
241 const std::string::size_type quoteEnd = line.rfind(
'"');
243 if ((quoteBegin == std::string::npos) ||
244 (quoteEnd == std::string::npos) ||
245 (quoteEnd <= quoteBegin))
248 FrameInfo *frame = FindFrameById(database, canId, isExtended);
249 if (frame !=
nullptr) {
250 SignalInfo *signal = FindSignalByName(*frame, signalName);
251 if (signal !=
nullptr)
252 signal->
comment = line.substr(quoteBegin + 1U, quoteEnd - quoteBegin - 1U);
261 for (std::size_t index = 0U; index < database.
frames.size(); ++index) {
262 if ((database.
frames[index].canId == canId) &&
263 (database.
frames[index].isExtended == isExtended))
264 return &database.
frames[index];
271 for (std::size_t index = 0U; index < frame.signals.size(); ++index) {
272 if (frame.signals[index].
name == signalName)
273 return &frame.signals[index];
280void DbcFrameHandler::Handle(
const std::string &line,
DbcParseContext &context)
const {
281 context.database.
frames.push_back(ParseFrameLine(line));
282 context.currentFrame = &context.database.
frames.back();
285void DbcSignalHandler::Handle(
const std::string &line,
DbcParseContext &context)
const {
286 if (context.currentFrame ==
nullptr)
287 throw std::runtime_error(
"Signal found before any frame definition.");
289 const std::uint64_t payloadBits =
static_cast<std::uint64_t
>(context.currentFrame->
dlc) * 8U;
290 std::uint64_t bit = signal.
startBit;
291 for (std::uint32_t index = 0U; index < signal.
length; ++index) {
292 if (bit >= payloadBits)
293 throw std::runtime_error(
"Signal exceeds the frame payload length.");
297 bit = (bit % 8U == 0U) ? bit + 15U : bit - 1U;
299 context.currentFrame->signals.push_back(std::move(signal));
302void DbcCommentHandler::Handle(
const std::string &line,
DbcParseContext &context)
const {
303 if (line.compare(0U, 4U,
"CM_ ") == 0)
304 ParseCommentLine(line, context.database);
307DbcStatementDispatcher::DbcStatementDispatcher() {
308 RegisterType<DbcFrameHandler>(
"BO_");
309 RegisterType<DbcSignalHandler>(
"SG_");
310 RegisterType<DbcCommentHandler>(
"CM_");
315 std::istringstream stream(line);
318 const auto handler = m_handlers.find(keyword);
319 if (handler == m_handlers.end())
321 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