Closes #6
This commit is contained in:
1 parent
88b5883dda
commit
8d82b13dad
5 files changed
+227
-94
No files matched your search
@@ -532,9 +532,8 @@ one-based physical line number, including blank lines:
|
|||||||
example.dbc:3: Invalid CAN ID: invalid
|
example.dbc:3: Invalid CAN ID: invalid
|
||||||
```
|
```
|
||||||
|
|
||||||
This change validates frame definitions. Signal syntax and unsupported DBC
|
This change validates frame definitions. Signal definitions are also validated as described below; comprehensive
|
||||||
constructs retain their existing behavior; comprehensive validation is still
|
DBC grammar coverage is still planned. File-open errors contain the path but have no line number.
|
||||||
planned. File-open errors contain the path but have no line number.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -562,6 +561,35 @@ Parsed fields:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### Signal validation and parse errors
|
||||||
|
|
||||||
|
`SG_` statements are matched completely. Signal names must be nonempty and
|
||||||
|
contain no whitespace. Spaces and tabs are accepted between syntax elements;
|
||||||
|
byte order and signedness remain adjacent (`@0+`, `@0-`, `@1+`, or `@1-`).
|
||||||
|
A quoted unit (which may be empty) and a nonempty comma-separated receiver
|
||||||
|
list are required. Unexpected trailing fields are rejected.
|
||||||
|
|
||||||
|
Start bits must be unsigned decimal integers fitting `uint32_t`; lengths must
|
||||||
|
be within `1..64` bits. Signs, fractional values, numeric suffixes, and overflow
|
||||||
|
are rejected for these fields. Every signal bit must fit within the frame's
|
||||||
|
declared payload length, using Intel increasing bit positions or Motorola
|
||||||
|
sawtooth traversal. This uses the stored payload length without introducing
|
||||||
|
an additional CAN or CAN FD payload limit.
|
||||||
|
|
||||||
|
Factor, offset, minimum, and maximum accept signed decimal numbers, fractions,
|
||||||
|
and scientific notation. The entire numeric token must be valid; hexadecimal
|
||||||
|
numbers, NaN, infinity, conversion overflow, and conversion underflow are
|
||||||
|
rejected. Minimum must not exceed maximum. Zero and negative factors are allowed.
|
||||||
|
|
||||||
|
Multiplexing markers (`M`, `mN`, and combinations) are currently rejected rather
|
||||||
|
than interpreted as part of a signal name. Multiplexing support and duplicate
|
||||||
|
frame/signal detection remain separate work.
|
||||||
|
|
||||||
|
Invalid signal definitions stop parsing with `path:line: description`, including
|
||||||
|
when the signal exceeds the declared payload length.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Comments
|
## Comments
|
||||||
|
|
||||||
Frame comment example:
|
Frame comment example:
|
||||||
@@ -805,8 +833,8 @@ RegisterType<DbcValueHandler>("VAL_"); // after implementing DbcValueHandler
|
|||||||
Registration creates one handler; replacing a registration replaces that instance.
|
Registration creates one handler; replacing a registration replaces that instance.
|
||||||
Register during initialization before dispatching. A line is routed using its
|
Register during initialization before dispatching. A line is routed using its
|
||||||
full first whitespace-delimited keyword, so `BO_TX_BU_` cannot match `BO_`.
|
full first whitespace-delimited keyword, so `BO_TX_BU_` cannot match `BO_`.
|
||||||
Unregistered keywords remain ignored. Existing `SG_` and `CM_` syntax policies
|
Unregistered keywords remain ignored. `SG_` accepts spaces and tabs after its keyword; `CM_` retains its existing
|
||||||
are preserved, including requiring a literal space after those keywords.
|
policy requiring a literal space.
|
||||||
|
|
||||||
This factory handles statements inside DBC files. A separate format-level
|
This factory handles statements inside DBC files. A separate format-level
|
||||||
factory can be introduced when another file format is implemented. The decoder
|
factory can be introduced when another file format is implemented. The decoder
|
||||||
|
|||||||
+54
-89
@@ -5,6 +5,8 @@
|
|||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <charconv>
|
#include <charconv>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
#include <regex>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
std::uint32_t ParseDecimal (const std::string &text,
|
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) {
|
SignalInfo DbcSignalHandler::ParseSignalLine (const std::string &line) {
|
||||||
/*
|
// Match the complete statement before converting any numeric fields.
|
||||||
* Example:
|
static const std::regex syntax (
|
||||||
* SG_ EngineSpeed : 0|16@1+ (0.125,0) [0|8000] "rpm" ECU1,ECU2
|
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;
|
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);
|
signal.startBit = ParseDecimal (fields[2].str(),
|
||||||
if (work.compare (0U, 4U, "SG_ ") != 0)
|
std::numeric_limits<std::uint32_t>::max(), "signal start bit");
|
||||||
throw std::runtime_error ("Invalid signal line: " + line);
|
signal.length = ParseDecimal (fields[3].str(), 64U, "signal length");
|
||||||
|
if (signal.length == 0U)
|
||||||
work.erase (0U, 4U);
|
throw std::runtime_error ("Signal length must be within 1..64 bits.");
|
||||||
|
signal.isLittleEndian = fields[4].str() == "1";
|
||||||
const std::string::size_type colonPos = work.find (':');
|
signal.isSigned = fields[5].str() == "-";
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
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;
|
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 {
|
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)
|
if (context.currentFrame == nullptr)
|
||||||
throw std::runtime_error ("Signal found before any frame definition.");
|
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 {
|
void DbcCommentHandler::Handle (const std::string &line, DbcParseContext &context) const {
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
# Validate `SG_` signal definitions and report malformed or unsupported syntax
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
Following strict validation of `BO_` frame definitions, add equivalent validation for `SG_` signal definitions. Currently, some malformed statements are accepted silently, and unsupported syntax can produce incorrect decoding.
|
||||||
|
|
||||||
|
Examples of current behavior:
|
||||||
|
|
||||||
|
- `@2+` is interpreted as Motorola instead of being rejected.
|
||||||
|
- Numeric fields containing suffixes, such as `0junk` or `1junk`, are partially parsed and accepted.
|
||||||
|
- `SG_ Value m1 : ...` is treated as an ordinary signal named `Value m1`, without multiplexing conditions.
|
||||||
|
- A statement containing a tab after `SG_` is silently skipped.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
- Validate statement structure and require a nonempty signal name without whitespace.
|
||||||
|
- Accept spaces and tabs between syntax elements.
|
||||||
|
- Parse numeric fields completely, rejecting overflow and trailing characters; preserve support for fractional values and scientific notation in physical parameters.
|
||||||
|
- Accept only byte order `@0` or `@1` and signedness `+` or `-`.
|
||||||
|
- Require signal lengths within `1..64` bits.
|
||||||
|
- Validate that the signal fits within the frame's declared payload length, using the appropriate Intel or Motorola bit traversal.
|
||||||
|
- Reject NaN and infinite values for factor, offset, minimum, and maximum; require `minimum <= maximum`.
|
||||||
|
- Explicitly reject multiplexing markers `M`, `mN`, and their combinations until multiplexing is implemented.
|
||||||
|
- Preserve the error format `path:line: description`.
|
||||||
|
|
||||||
|
## Acceptance criteria
|
||||||
|
|
||||||
|
- [ ] Existing valid signal definitions continue to parse successfully.
|
||||||
|
- [ ] Malformed and unsupported definitions stop parsing with a clear error.
|
||||||
|
- [ ] Regression tests cover the cases above, numeric boundaries, and Intel/Motorola payload boundaries.
|
||||||
|
- [ ] Existing parser and decoder tests pass.
|
||||||
|
- [ ] README documents the new validation rules and limitations.
|
||||||
|
|
||||||
|
## Out of scope
|
||||||
|
|
||||||
|
Duplicate detection, multiplexing support, and `VAL_` support remain separate tasks.
|
||||||
|
|
||||||
|
## Suggested branch
|
||||||
|
|
||||||
|
`codex/validate-sg-definitions`
|
||||||
@@ -43,6 +43,9 @@ following the decoder instructions above, then run `./dbc_parser_test`.
|
|||||||
Parser tests cover standard and extended CAN IDs, whitespace, payload storage
|
Parser tests cover standard and extended CAN IDs, whitespace, payload storage
|
||||||
boundaries, missing fields, malformed and overflowing numbers, reserved ID bits,
|
boundaries, missing fields, malformed and overflowing numbers, reserved ID bits,
|
||||||
error paths and line numbers, and integration with signals and comments.
|
error paths and line numbers, and integration with signals and comments.
|
||||||
|
Signal tests cover complete syntax and numeric validation, scientific notation,
|
||||||
|
finite physical parameters and ordered ranges, spaces and tabs, receiver lists,
|
||||||
|
unsupported multiplexing, and Intel/Motorola declared-payload boundaries.
|
||||||
Temporary DBC fixtures are created in the system temporary directory and removed
|
Temporary DBC fixtures are created in the system temporary directory and removed
|
||||||
automatically.
|
automatically.
|
||||||
|
|
||||||
|
|||||||
@@ -100,6 +100,103 @@ int main() {
|
|||||||
check (std::string (error.what()).find (fixture.path.string() + ":2: ") == 0U,
|
check (std::string (error.what()).find (fixture.path.string() + ":2: ") == 0U,
|
||||||
"Signal error location");
|
"Signal error location");
|
||||||
}
|
}
|
||||||
|
const DbcParser signalParser;
|
||||||
|
const auto signalLine = [] (const std::string &start, const std::string &length,
|
||||||
|
const std::string &order, const std::string &factor,
|
||||||
|
const std::string &offset, const std::string &minimum,
|
||||||
|
const std::string &maximum) {
|
||||||
|
return "SG_ Value : " + start + "|" + length + "@" + order +
|
||||||
|
" (" + factor + "," + offset + ") [" + minimum + "|" + maximum + "] \"\" ECU";
|
||||||
|
};
|
||||||
|
const auto rejectSignal = [&] (const std::string &line, unsigned payload = 8U) {
|
||||||
|
fixture.Write ("BO_ 1 Frame: " + std::to_string (payload) + " ECU\n\n" + line + "\n");
|
||||||
|
try {
|
||||||
|
signalParser.ParseFile (fixture.path.string());
|
||||||
|
check (false, "Reject " + line);
|
||||||
|
} catch (const std::runtime_error &error) {
|
||||||
|
const std::string prefix = fixture.path.string() + ":3: ";
|
||||||
|
check (std::string (error.what()).find (prefix) == 0U &&
|
||||||
|
std::string (error.what()).size() > prefix.size(), "Reject " + line);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const std::string ordinary = signalLine ("0", "8", "1+", "1", "0", "0", "255");
|
||||||
|
for (const std::string value : {"-1", "+1", "0junk", "0x10", "1.5",
|
||||||
|
"4294967296", "999999999999999999999"}) {
|
||||||
|
rejectSignal (signalLine (value, "8", "1+", "1", "0", "0", "255"));
|
||||||
|
rejectSignal (signalLine ("0", value, "1+", "1", "0", "0", "255"));
|
||||||
|
}
|
||||||
|
for (const std::string length : {"0", "65"})
|
||||||
|
rejectSignal (signalLine ("0", length, "1+", "1", "0", "0", "255"));
|
||||||
|
for (const std::string order : {"2+", "9-", "1", "1x", "1++", "01+"})
|
||||||
|
rejectSignal (signalLine ("0", "8", order, "1", "0", "0", "255"));
|
||||||
|
for (const std::string value : {"1junk", "nan", "NaN", "inf", "-inf", "1e999", "1e-999", "0x1p2", "", "1e", "1.2.3"}) {
|
||||||
|
rejectSignal (signalLine ("0", "8", "1+", value, "0", "0", "255"));
|
||||||
|
rejectSignal (signalLine ("0", "8", "1+", "1", value, "0", "255"));
|
||||||
|
rejectSignal (signalLine ("0", "8", "1+", "1", "0", value, "255"));
|
||||||
|
rejectSignal (signalLine ("0", "8", "1+", "1", "0", "0", value));
|
||||||
|
}
|
||||||
|
rejectSignal (signalLine ("0", "8", "1+", "1", "0", "2", "1"));
|
||||||
|
for (const std::string name : {"", "Two Names", "Value M", "Value m1", "Value m1M"}) {
|
||||||
|
std::string line = ordinary;
|
||||||
|
line.replace (4U, 5U, name);
|
||||||
|
rejectSignal (line);
|
||||||
|
}
|
||||||
|
for (const std::string broken : {
|
||||||
|
"SG_", "SG_ Value", "SG_ Value : 0|8@1+ (1,0) [0|255] ECU",
|
||||||
|
"SG_ Value : 0|8@1+ (1,0) [0|255] \"\"",
|
||||||
|
"SG_ Value : 0|8@1+ (1,0) [0|255] \"\" ECU,",
|
||||||
|
"SG_ Value : 0|8@1+ (1,0) [0|255] \"\" ECU extra"})
|
||||||
|
rejectSignal (broken);
|
||||||
|
rejectSignal (signalLine ("64", "1", "1+", "1", "0", "0", "1"));
|
||||||
|
rejectSignal (signalLine ("63", "2", "1+", "1", "0", "0", "3"));
|
||||||
|
rejectSignal (signalLine ("56", "2", "0+", "1", "0", "0", "3"));
|
||||||
|
rejectSignal (signalLine ("4294967295", "64", "1+", "1", "0", "0", "1"));
|
||||||
|
rejectSignal (ordinary, 0U);
|
||||||
|
for (const char delimiter : {'|', '@', '(', ')', '[', ']', '"'}) {
|
||||||
|
std::string line = ordinary;
|
||||||
|
line.erase (line.find (delimiter), 1U);
|
||||||
|
rejectSignal (line);
|
||||||
|
}
|
||||||
|
fixture.Write ("BO_ 1 Frame: 8 ECU\n" +
|
||||||
|
signalLine ("0", "8", "1+", "0", "0", "0", "0") + "\n" +
|
||||||
|
signalLine ("8", "8", "1-", "-1", "0", "-255", "0") + "\n");
|
||||||
|
const auto factors = signalParser.ParseFile (fixture.path.string());
|
||||||
|
check (factors.frames[0].signals.size() == 2U &&
|
||||||
|
factors.frames[0].signals[0].factor == 0.0 &&
|
||||||
|
factors.frames[0].signals[1].factor == -1.0,
|
||||||
|
"Zero and negative factors and equal range boundaries");
|
||||||
|
|
||||||
|
struct SignalCase { const char *start; const char *length; const char *order; unsigned payload; };
|
||||||
|
const SignalCase validSignals[] = {
|
||||||
|
{"0", "1", "1+", 1U}, {"7", "1", "0-", 1U},
|
||||||
|
{"0", "64", "1+", 8U}, {"7", "64", "0-", 8U},
|
||||||
|
{"63", "1", "1+", 8U}, {"56", "1", "0+", 8U},
|
||||||
|
{"3", "12", "0+", 2U}, {"2039", "1", "1+", 255U}
|
||||||
|
};
|
||||||
|
for (const auto &test : validSignals) {
|
||||||
|
fixture.Write ("BO_ 1 Frame: " + std::to_string (test.payload) + " ECU\n" +
|
||||||
|
signalLine (test.start, test.length, test.order, ".5e+1", "-2.5E-1", "-1.", "+2e2") + "\n");
|
||||||
|
try {
|
||||||
|
const auto parsed = signalParser.ParseFile (fixture.path.string());
|
||||||
|
const auto &signal = parsed.frames.at (0).signals.at (0);
|
||||||
|
check (signal.startBit == std::stoul (test.start) &&
|
||||||
|
signal.length == std::stoul (test.length) &&
|
||||||
|
signal.isLittleEndian == (test.order[0] == '1') &&
|
||||||
|
signal.isSigned == (test.order[1] == '-') &&
|
||||||
|
signal.factor == 5.0 && signal.offset == -0.25 &&
|
||||||
|
signal.minimum == -1.0 && signal.maximum == 200.0,
|
||||||
|
"Valid signal boundary and numeric metadata");
|
||||||
|
} catch (const std::exception &error) { check (false, error.what()); }
|
||||||
|
}
|
||||||
|
fixture.Write ("BO_ 1 Frame: 8 ECU\n"
|
||||||
|
"SG_\tValue\t:\t0 | 8 @1+ ( 1 , 0 ) [ 0 | 255 ] \"rpm\" ECU1,\tECU2\n");
|
||||||
|
try {
|
||||||
|
const auto parsed = signalParser.ParseFile (fixture.path.string());
|
||||||
|
const auto &signal = parsed.frames.at (0).signals.at (0);
|
||||||
|
check (signal.name == "Value" && signal.unit == "rpm" &&
|
||||||
|
signal.receivers == std::vector<std::string> ({"ECU1", "ECU2"}),
|
||||||
|
"Signal tabs, spaces, and receiver list");
|
||||||
|
} catch (const std::exception &error) { check (false, error.what()); }
|
||||||
DbcDatabase dispatchDatabase;
|
DbcDatabase dispatchDatabase;
|
||||||
DbcParseContext context {dispatchDatabase, nullptr};
|
DbcParseContext context {dispatchDatabase, nullptr};
|
||||||
DbcStatementDispatcher dispatcher;
|
DbcStatementDispatcher dispatcher;
|
||||||
|
|||||||
Reference in new issue
Block a user