Closes #6
This commit is contained in:
1 parent
88b5883dda
commit
8d82b13dad
5 files changed
+227
-94
No files matched your search
+54
-89
@@ -5,6 +5,8 @@
|
||||
#include <cctype>
|
||||
#include <charconv>
|
||||
#include <limits>
|
||||
#include <regex>
|
||||
#include <cmath>
|
||||
|
||||
namespace {
|
||||
std::uint32_t ParseDecimal (const std::string &text,
|
||||
@@ -142,95 +144,50 @@ FrameInfo DbcFrameHandler::ParseFrameLine (const std::string &line) {
|
||||
}
|
||||
|
||||
SignalInfo DbcSignalHandler::ParseSignalLine (const std::string &line) {
|
||||
/*
|
||||
* Example:
|
||||
* SG_ EngineSpeed : 0|16@1+ (0.125,0) [0|8000] "rpm" ECU1,ECU2
|
||||
*/
|
||||
// Match the complete statement before converting any numeric fields.
|
||||
static const std::regex syntax (
|
||||
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");
|
||||
std::smatch fields;
|
||||
if (!std::regex_match (line, fields, syntax))
|
||||
throw std::runtime_error ("Malformed or unsupported SG_ signal definition.");
|
||||
|
||||
SignalInfo signal;
|
||||
signal.name = TrimText (fields[1].str());
|
||||
if (signal.name.empty() || signal.name.find_first_of (" \t\r\n") != std::string::npos)
|
||||
throw std::runtime_error ("Invalid signal name or unsupported multiplexing syntax.");
|
||||
|
||||
std::string work = TrimText (line);
|
||||
if (work.compare (0U, 4U, "SG_ ") != 0)
|
||||
throw std::runtime_error ("Invalid signal line: " + line);
|
||||
|
||||
work.erase (0U, 4U);
|
||||
|
||||
const std::string::size_type colonPos = work.find (':');
|
||||
if (colonPos == std::string::npos)
|
||||
throw std::runtime_error ("Signal line missing ':' : " + line);
|
||||
|
||||
signal.name = TrimText (work.substr (0U, colonPos));
|
||||
std::string rest = TrimText (work.substr (colonPos + 1U));
|
||||
|
||||
const std::string::size_type pipePos = rest.find ('|');
|
||||
const std::string::size_type atPos = rest.find ('@');
|
||||
const std::string::size_type signPos = rest.find_first_of ("+-", atPos);
|
||||
const std::string::size_type factorBegin = rest.find ('(');
|
||||
const std::string::size_type factorComma = rest.find (',', factorBegin);
|
||||
const std::string::size_type factorEnd = rest.find (')', factorComma);
|
||||
const std::string::size_type rangeBegin = rest.find ('[');
|
||||
const std::string::size_type rangeSep = rest.find ('|', rangeBegin);
|
||||
const std::string::size_type rangeEnd = rest.find (']', rangeSep);
|
||||
const std::string::size_type unitBegin = rest.find ('"', rangeEnd);
|
||||
const std::string::size_type unitEnd = rest.find ('"', unitBegin + 1U);
|
||||
|
||||
if ((pipePos == std::string::npos) ||
|
||||
(atPos == std::string::npos) ||
|
||||
(signPos == std::string::npos) ||
|
||||
(factorBegin == std::string::npos) ||
|
||||
(factorComma == std::string::npos) ||
|
||||
(factorEnd == std::string::npos) ||
|
||||
(rangeBegin == std::string::npos) ||
|
||||
(rangeSep == std::string::npos) ||
|
||||
(rangeEnd == std::string::npos) ||
|
||||
(unitBegin == std::string::npos) ||
|
||||
(unitEnd == std::string::npos))
|
||||
throw std::runtime_error ("Unsupported signal syntax: " + line);
|
||||
|
||||
signal.startBit = static_cast<std::uint32_t> (
|
||||
std::stoul (TrimText (rest.substr (0U, pipePos)))
|
||||
);
|
||||
|
||||
signal.length = static_cast<std::uint32_t> (
|
||||
std::stoul (TrimText (rest.substr (pipePos + 1U, atPos - pipePos - 1U)))
|
||||
);
|
||||
|
||||
{
|
||||
if ((atPos + 1U) >= rest.size())
|
||||
throw std::runtime_error ("Invalid endianness in signal: " + line);
|
||||
|
||||
const char endianChar = rest[atPos + 1U];
|
||||
signal.isLittleEndian = (endianChar == '1');
|
||||
}
|
||||
|
||||
{
|
||||
const char signChar = rest[signPos];
|
||||
signal.isSigned = (signChar == '-');
|
||||
}
|
||||
|
||||
signal.factor = std::stod (
|
||||
TrimText (rest.substr (factorBegin + 1U, factorComma - factorBegin - 1U))
|
||||
);
|
||||
|
||||
signal.offset = std::stod (
|
||||
TrimText (rest.substr (factorComma + 1U, factorEnd - factorComma - 1U))
|
||||
);
|
||||
|
||||
signal.minimum = std::stod (
|
||||
TrimText (rest.substr (rangeBegin + 1U, rangeSep - rangeBegin - 1U))
|
||||
);
|
||||
|
||||
signal.maximum = std::stod (
|
||||
TrimText (rest.substr (rangeSep + 1U, rangeEnd - rangeSep - 1U))
|
||||
);
|
||||
|
||||
signal.unit = rest.substr (unitBegin + 1U, unitEnd - unitBegin - 1U);
|
||||
|
||||
{
|
||||
const std::string receiversText = TrimText (rest.substr (unitEnd + 1U));
|
||||
signal.receivers = SplitReceivers (receiversText);
|
||||
}
|
||||
signal.startBit = ParseDecimal (fields[2].str(),
|
||||
std::numeric_limits<std::uint32_t>::max(), "signal start bit");
|
||||
signal.length = ParseDecimal (fields[3].str(), 64U, "signal length");
|
||||
if (signal.length == 0U)
|
||||
throw std::runtime_error ("Signal length must be within 1..64 bits.");
|
||||
signal.isLittleEndian = fields[4].str() == "1";
|
||||
signal.isSigned = fields[5].str() == "-";
|
||||
|
||||
const auto parsePhysical = [] (const std::string &text, const char *field) {
|
||||
static const std::regex number (
|
||||
R"(^[+-]?(?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+)(?:[eE][+-]?[0-9]+)?$)");
|
||||
if (!std::regex_match (text, number))
|
||||
throw std::runtime_error (std::string ("Invalid ") + field + ": " + text);
|
||||
std::size_t consumed = 0U;
|
||||
double value;
|
||||
try {
|
||||
value = std::stod (text, &consumed);
|
||||
} catch (const std::exception &) {
|
||||
throw std::runtime_error (std::string ("Invalid ") + field + ": " + text);
|
||||
}
|
||||
if (consumed != text.size() || !std::isfinite (value))
|
||||
throw std::runtime_error (std::string ("Invalid ") + field + ": " + text);
|
||||
return value;
|
||||
};
|
||||
signal.factor = parsePhysical (fields[6].str(), "signal factor");
|
||||
signal.offset = parsePhysical (fields[7].str(), "signal offset");
|
||||
signal.minimum = parsePhysical (fields[8].str(), "signal minimum");
|
||||
signal.maximum = parsePhysical (fields[9].str(), "signal maximum");
|
||||
if (signal.minimum > signal.maximum)
|
||||
throw std::runtime_error ("Signal minimum exceeds maximum.");
|
||||
signal.unit = fields[10].str();
|
||||
signal.receivers = SplitReceivers (fields[11].str());
|
||||
return signal;
|
||||
}
|
||||
|
||||
@@ -326,12 +283,20 @@ void DbcFrameHandler::Handle (const std::string &line, DbcParseContext &context)
|
||||
}
|
||||
|
||||
void DbcSignalHandler::Handle (const std::string &line, DbcParseContext &context) const {
|
||||
// Preserve the existing signal syntax policy during this refactor.
|
||||
if (line.compare (0U, 4U, "SG_ ") != 0)
|
||||
return;
|
||||
if (context.currentFrame == nullptr)
|
||||
throw std::runtime_error ("Signal found before any frame definition.");
|
||||
context.currentFrame->signals.push_back (ParseSignalLine (line));
|
||||
SignalInfo signal = ParseSignalLine (line);
|
||||
const std::uint64_t payloadBits = static_cast<std::uint64_t> (context.currentFrame->dlc) * 8U;
|
||||
std::uint64_t bit = signal.startBit;
|
||||
for (std::uint32_t index = 0U; index < signal.length; ++index) {
|
||||
if (bit >= payloadBits)
|
||||
throw std::runtime_error ("Signal exceeds the frame payload length.");
|
||||
if (signal.isLittleEndian)
|
||||
++bit;
|
||||
else
|
||||
bit = (bit % 8U == 0U) ? bit + 15U : bit - 1U;
|
||||
}
|
||||
context.currentFrame->signals.push_back (std::move (signal));
|
||||
}
|
||||
|
||||
void DbcCommentHandler::Handle (const std::string &line, DbcParseContext &context) const {
|
||||
|
||||
Reference in new issue
Block a user