Closes #3 Validate DBC frame definitions and report error locations
This commit is contained in:
1 parent
69074e3321
commit
3bd9339baf
6 files changed
+213
-36
No files matched your search
+60
-35
@@ -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;
|
||||
|
||||
Reference in new issue
Block a user