Closes #3 Validate DBC frame definitions and report error locations

This commit is contained in:
deeaitch committed 2026-10-01 13:23:01 -04:00
1 parent 69074e3321
commit 3bd9339baf
6 files changed
+213 -36

No files matched your search

+60 -35
View File
@@ -15,8 +15,21 @@
#include <sstream>
#include <stdexcept>
#include <cctype>
#include <charconv>
#include <limits>
namespace {
std::uint32_t ParseDecimal (const std::string &text,
std::uint32_t maximum,
const char *field) {
std::uint32_t value = 0U;
const auto parsed = std::from_chars (text.data(), text.data() + text.size(), value);
if (text.empty() || parsed.ec != std::errc() ||
parsed.ptr != text.data() + text.size() || value > maximum)
throw std::runtime_error (std::string ("Invalid ") + field + ": " + text);
return value;
}
/**
* @brief Remove leading and trailing spaces.
* @param text Input text.
@@ -45,31 +58,39 @@ DbcDatabase DbcParser::ParseFile (const std::string &filePath) const {
DbcDatabase database;
FrameInfo *currentFrame = nullptr;
std::size_t lineNumber = 0U;
std::string line;
while (std::getline (input, line)) {
++lineNumber;
line = Trim (line);
if (line.empty())
continue;
if (IsFrameLine (line)) {
FrameInfo frame = ParseFrameLine (line);
database.frames.push_back (frame);
currentFrame = &database.frames.back();
} else if (IsSignalLine (line)) {
if (currentFrame == nullptr)
throw std::runtime_error ("Signal found before any frame definition.");
try {
if (IsFrameLine (line)) {
FrameInfo frame = ParseFrameLine (line);
database.frames.push_back (frame);
currentFrame = &database.frames.back();
} else if (IsSignalLine (line)) {
if (currentFrame == nullptr)
throw std::runtime_error ("Signal found before any frame definition.");
SignalInfo signal = ParseSignalLine (line);
currentFrame->signals.push_back (signal);
} else if (IsCommentLine (line))
ParseCommentLine (line, database);
SignalInfo signal = ParseSignalLine (line);
currentFrame->signals.push_back (signal);
} else if (IsCommentLine (line))
ParseCommentLine (line, database);
} catch (const std::exception &error) {
throw std::runtime_error (filePath + ":" + std::to_string (lineNumber) +
": " + error.what());
}
}
return database;
}
bool DbcParser::IsFrameLine (const std::string &line) {
return (line.size() >= 4U) && (line.compare (0U, 4U, "BO_ ") == 0);
return (line.compare (0U, 3U, "BO_") == 0) &&
((line.size() == 3U) || std::isspace (static_cast<unsigned char> (line[3U])));
}
bool DbcParser::IsSignalLine (const std::string &line) {
@@ -146,33 +167,37 @@ FrameInfo DbcParser::ParseFrameLine (const std::string &line) {
*/
std::istringstream stream (line);
std::string token;
std::string keyword;
std::string idText;
if (!(stream >> keyword >> idText) || keyword != "BO_")
throw std::runtime_error ("Missing CAN ID in frame definition.");
const std::uint32_t rawCanId = ParseDecimal (
idText, std::numeric_limits<std::uint32_t>::max(), "CAN ID");
// Only bit 31 may accompany the actual 29-bit identifier.
if ((rawCanId & 0x60000000U) != 0U)
throw std::runtime_error ("CAN ID exceeds the 29-bit identifier range.");
std::string nameText;
if (!std::getline (stream, nameText, ':') || stream.eof())
throw std::runtime_error ("Missing ':' in frame definition.");
std::istringstream nameStream (nameText);
FrameInfo frame;
std::uint32_t rawCanId = 0U;
std::string extra;
if (!(nameStream >> frame.name) || (nameStream >> extra))
throw std::runtime_error ("Missing or invalid frame name.");
stream >> token;
if (token != "BO_")
throw std::runtime_error ("Invalid frame line: " + line);
std::string lengthText;
if (!(stream >> lengthText))
throw std::runtime_error ("Missing payload length in frame definition.");
frame.dlc = static_cast<std::uint8_t> (ParseDecimal (
lengthText, std::numeric_limits<std::uint8_t>::max(), "payload length"));
if (!(stream >> frame.transmitter))
throw std::runtime_error ("Missing transmitter in frame definition.");
if (stream >> extra)
throw std::runtime_error ("Unexpected trailing field in frame definition: " + extra);
stream >> rawCanId;
NormalizeCanId (rawCanId, frame.canId, frame.isExtended);
stream >> token;
if (token.empty())
throw std::runtime_error ("Missing frame name: " + line);
if (token[token.size() - 1U] == ':')
token.erase (token.size() - 1U, 1U);
frame.name = token;
{
unsigned int dlcValue = 0U;
stream >> dlcValue;
frame.dlc = static_cast<std::uint8_t> (dlcValue);
}
stream >> frame.transmitter;
frame.pgn = TryExtractPgn (frame.canId, frame.isExtended, frame.hasPgn);
return frame;