Closes #32
This commit is contained in:
1 parent
e44529007a
commit
f60faea2e7
35 files changed
+2361
-2145
No files matched your search
@@ -33,9 +33,8 @@ namespace {
|
||||
}
|
||||
|
||||
double ParsePhysicalValue(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))
|
||||
if (text.empty() || (text[0] == '+' &&
|
||||
(text.size() == 1U || text[1] == '+' || text[1] == '-')))
|
||||
throw std::runtime_error(std::string("Invalid ") + field + ": " + text);
|
||||
// Floating-point from_chars is locale-independent and does not accept
|
||||
// a leading '+', which the DBC numeric grammar permits.
|
||||
@@ -53,6 +52,58 @@ namespace {
|
||||
return value;
|
||||
}
|
||||
|
||||
// Iterative scanning keeps stack use independent of the statement length.
|
||||
class SignalTokens {
|
||||
public:
|
||||
SignalTokens(const std::string &text, std::size_t position) : text(text), position(position) {}
|
||||
|
||||
void SkipSpaces() {
|
||||
while (position < text.size() && (text[position] == ' ' || text[position] == '\t'))
|
||||
++position;
|
||||
}
|
||||
|
||||
char Character() {
|
||||
if (position == text.size())
|
||||
throw std::runtime_error("Incomplete SG_ signal definition.");
|
||||
return text[position++];
|
||||
}
|
||||
|
||||
void Expect(char expected) {
|
||||
SkipSpaces();
|
||||
if (Character() != expected)
|
||||
throw std::runtime_error("Malformed SG_ signal definition.");
|
||||
}
|
||||
|
||||
std::string Until(char delimiter) {
|
||||
const std::size_t end = text.find(delimiter, position);
|
||||
if (end == std::string::npos)
|
||||
throw std::runtime_error("Missing delimiter in SG_ signal definition.");
|
||||
const std::string value = text.substr(position, end - position);
|
||||
position = end + 1U;
|
||||
return value;
|
||||
}
|
||||
|
||||
std::vector<std::string> Receivers() {
|
||||
if (position == text.size() || (text[position] != ' ' && text[position] != '\t'))
|
||||
throw std::runtime_error("Missing receiver separator in SG_ signal definition.");
|
||||
std::vector<std::string> receivers;
|
||||
for (;;) {
|
||||
const std::size_t end = text.find(',', position);
|
||||
const std::string receiver = dbc_detail::TrimText(text.substr(position, end - position));
|
||||
if (receiver.empty() || receiver.find_first_of(" \t\r\n") != std::string::npos)
|
||||
throw std::runtime_error("Invalid SG_ receiver list.");
|
||||
receivers.push_back(receiver);
|
||||
if (end == std::string::npos)
|
||||
return receivers;
|
||||
position = end + 1U;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const std::string &text;
|
||||
std::size_t position = 0U;
|
||||
};
|
||||
|
||||
Multiplexing ParseMultiplexingMarker(const std::string &marker) {
|
||||
Multiplexing result;
|
||||
if (marker == "M")
|
||||
@@ -142,20 +193,6 @@ namespace {
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
std::vector<std::string> SplitReceivers(const std::string &text) {
|
||||
std::vector<std::string> receivers;
|
||||
std::string token;
|
||||
std::istringstream stream(text);
|
||||
|
||||
while (std::getline(stream, token, ',')) {
|
||||
token = dbc_detail::TrimText(token);
|
||||
if (!token.empty())
|
||||
receivers.push_back(token);
|
||||
}
|
||||
|
||||
return receivers;
|
||||
}
|
||||
|
||||
void NormalizeCanId(std::uint32_t rawCanId, std::uint32_t &normalizedCanId, bool &isExtended) {
|
||||
/*
|
||||
* DBC commonly stores extended identifiers with bit 31 set.
|
||||
@@ -206,28 +243,12 @@ FrameInfo DbcFrameHandler::ParseFrameLine(const std::string &line) {
|
||||
}
|
||||
|
||||
SignalInfo DbcSignalHandler::ParseSignalLine(const std::string &line) {
|
||||
// Match the complete statement before converting any numeric fields.
|
||||
enum Field {
|
||||
NameAndMarker = 1, StartBit, BitLength, ByteOrder, Signedness,
|
||||
Factor, Offset, Minimum, Maximum, Unit, Receivers
|
||||
};
|
||||
static const std::regex syntax(
|
||||
// Name and optional multiplexing marker.
|
||||
R"dbc(^SG_[ \t]+([^:]+):[ \t]*)dbc"
|
||||
// Bit position, length, byte order and signedness.
|
||||
R"dbc(([^| \t]+)[ \t]*\|[ \t]*([^@ \t]+)[ \t]*@[ \t]*([01])([+-])[ \t]*)dbc"
|
||||
// Scaling factor and offset.
|
||||
R"dbc(\([ \t]*([^, \t]+)[ \t]*,[ \t]*([^ )\t]+)[ \t]*\)[ \t]*)dbc"
|
||||
// Physical range.
|
||||
R"dbc(\[[ \t]*([^| \t]+)[ \t]*\|[ \t]*([^\] \t]+)[ \t]*\][ \t]*)dbc"
|
||||
// Unit and receivers.
|
||||
R"dbc("([^"\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.");
|
||||
|
||||
if (line.size() < 4U || line.compare(0U, 3U, "SG_") != 0 ||
|
||||
(line[3] != ' ' && line[3] != '\t'))
|
||||
throw std::runtime_error("Malformed SG_ signal definition.");
|
||||
SignalTokens tokens(line, 3U);
|
||||
SignalInfo signal;
|
||||
std::istringstream nameFields(fields[NameAndMarker].str());
|
||||
std::istringstream nameFields(tokens.Until(':'));
|
||||
std::string marker;
|
||||
std::string extra;
|
||||
nameFields >> signal.name >> marker;
|
||||
@@ -235,22 +256,32 @@ SignalInfo DbcSignalHandler::ParseSignalLine(const std::string &line) {
|
||||
throw std::runtime_error("Invalid signal name or multiplexing marker.");
|
||||
signal.multiplexing = ParseMultiplexingMarker(marker);
|
||||
|
||||
signal.startBit = ParseDecimal(fields[StartBit].str(),
|
||||
signal.startBit = ParseDecimal(dbc_detail::TrimText(tokens.Until('|')),
|
||||
std::numeric_limits<std::uint32_t>::max(), "signal start bit");
|
||||
signal.length = ParseDecimal(fields[BitLength].str(), 64U, "signal length");
|
||||
signal.length = ParseDecimal(dbc_detail::TrimText(tokens.Until('@')), 64U, "signal length");
|
||||
if (signal.length == 0U)
|
||||
throw std::runtime_error("Signal length must be within 1..64 bits.");
|
||||
signal.isLittleEndian = fields[ByteOrder].str() == "1";
|
||||
signal.isSigned = fields[Signedness].str() == "-";
|
||||
tokens.SkipSpaces();
|
||||
const char byteOrder = tokens.Character();
|
||||
const char signedness = tokens.Character();
|
||||
if ((byteOrder != '0' && byteOrder != '1') || (signedness != '+' && signedness != '-'))
|
||||
throw std::runtime_error("Invalid SG_ byte order or signedness.");
|
||||
signal.isLittleEndian = byteOrder == '1';
|
||||
signal.isSigned = signedness == '-';
|
||||
|
||||
signal.factor = ParsePhysicalValue(fields[Factor].str(), "signal factor");
|
||||
signal.offset = ParsePhysicalValue(fields[Offset].str(), "signal offset");
|
||||
signal.minimum = ParsePhysicalValue(fields[Minimum].str(), "signal minimum");
|
||||
signal.maximum = ParsePhysicalValue(fields[Maximum].str(), "signal maximum");
|
||||
tokens.Expect('(');
|
||||
signal.factor = ParsePhysicalValue(dbc_detail::TrimText(tokens.Until(',')), "signal factor");
|
||||
signal.offset = ParsePhysicalValue(dbc_detail::TrimText(tokens.Until(')')), "signal offset");
|
||||
tokens.Expect('[');
|
||||
signal.minimum = ParsePhysicalValue(dbc_detail::TrimText(tokens.Until('|')), "signal minimum");
|
||||
signal.maximum = ParsePhysicalValue(dbc_detail::TrimText(tokens.Until(']')), "signal maximum");
|
||||
if (signal.minimum > signal.maximum)
|
||||
throw std::runtime_error("Signal minimum exceeds maximum.");
|
||||
signal.unit = fields[Unit].str();
|
||||
signal.receivers = SplitReceivers(fields[Receivers].str());
|
||||
tokens.Expect('"');
|
||||
signal.unit = tokens.Until('"');
|
||||
if (signal.unit.find_first_of("\r\n") != std::string::npos)
|
||||
throw std::runtime_error("Invalid newline in SG_ unit.");
|
||||
signal.receivers = tokens.Receivers();
|
||||
return signal;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user