Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d7e72e1d12 |
No files matched your search
@@ -1,65 +0,0 @@
|
|||||||
BasedOnStyle: LLVM
|
|
||||||
AccessModifierOffset: -4
|
|
||||||
AlignAfterOpenBracket: Align
|
|
||||||
AlignConsecutiveAssignments: None
|
|
||||||
AlignOperands: Align
|
|
||||||
AllowAllArgumentsOnNextLine: false
|
|
||||||
AllowAllConstructorInitializersOnNextLine: false
|
|
||||||
AllowAllParametersOfDeclarationOnNextLine: false
|
|
||||||
AllowShortBlocksOnASingleLine: Always
|
|
||||||
AllowShortCaseLabelsOnASingleLine: false
|
|
||||||
AllowShortFunctionsOnASingleLine: All
|
|
||||||
AllowShortIfStatementsOnASingleLine: Always
|
|
||||||
AllowShortLambdasOnASingleLine: All
|
|
||||||
AllowShortLoopsOnASingleLine: true
|
|
||||||
AlwaysBreakAfterReturnType: None
|
|
||||||
AlwaysBreakTemplateDeclarations: Yes
|
|
||||||
BreakBeforeBraces: Custom
|
|
||||||
BraceWrapping:
|
|
||||||
AfterCaseLabel: false
|
|
||||||
AfterClass: false
|
|
||||||
AfterControlStatement: Never
|
|
||||||
AfterEnum: false
|
|
||||||
AfterFunction: false
|
|
||||||
AfterNamespace: false
|
|
||||||
AfterUnion: false
|
|
||||||
BeforeCatch: false
|
|
||||||
BeforeElse: false
|
|
||||||
IndentBraces: false
|
|
||||||
SplitEmptyFunction: false
|
|
||||||
SplitEmptyRecord: true
|
|
||||||
BreakBeforeBinaryOperators: None
|
|
||||||
BreakBeforeTernaryOperators: true
|
|
||||||
BreakConstructorInitializers: BeforeColon
|
|
||||||
BreakInheritanceList: BeforeColon
|
|
||||||
ColumnLimit: 0
|
|
||||||
CompactNamespaces: false
|
|
||||||
ContinuationIndentWidth: 8
|
|
||||||
IndentCaseLabels: true
|
|
||||||
IndentPPDirectives: None
|
|
||||||
IndentWidth: 4
|
|
||||||
KeepEmptyLinesAtTheStartOfBlocks: true
|
|
||||||
MaxEmptyLinesToKeep: 2
|
|
||||||
NamespaceIndentation: All
|
|
||||||
ObjCSpaceAfterProperty: false
|
|
||||||
ObjCSpaceBeforeProtocolList: true
|
|
||||||
PointerAlignment: Right
|
|
||||||
ReflowComments: false
|
|
||||||
SpaceAfterCStyleCast: true
|
|
||||||
SpaceAfterLogicalNot: false
|
|
||||||
SpaceAfterTemplateKeyword: false
|
|
||||||
SpaceBeforeAssignmentOperators: true
|
|
||||||
SpaceBeforeCpp11BracedList: false
|
|
||||||
SpaceBeforeCtorInitializerColon: true
|
|
||||||
SpaceBeforeInheritanceColon: true
|
|
||||||
SpaceBeforeParens: ControlStatements
|
|
||||||
SpaceBeforeRangeBasedForLoopColon: false
|
|
||||||
SpaceInEmptyParentheses: false
|
|
||||||
SpacesBeforeTrailingComments: 0
|
|
||||||
SpacesInAngles: false
|
|
||||||
SpacesInCStyleCastParentheses: false
|
|
||||||
SpacesInContainerLiterals: false
|
|
||||||
SpacesInParentheses: false
|
|
||||||
SpacesInSquareBrackets: false
|
|
||||||
TabWidth: 4
|
|
||||||
UseTab: Never
|
|
||||||
@@ -532,8 +532,9 @@ 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 definitions are also validated as described below; comprehensive
|
This change validates frame definitions. Signal syntax and unsupported DBC
|
||||||
DBC grammar coverage is still planned. File-open errors contain the path but have no line number.
|
constructs retain their existing behavior; comprehensive validation is still
|
||||||
|
planned. File-open errors contain the path but have no line number.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -561,35 +562,6 @@ 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:
|
||||||
@@ -833,8 +805,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. `SG_` accepts spaces and tabs after its keyword; `CM_` retains its existing
|
Unregistered keywords remain ignored. Existing `SG_` and `CM_` syntax policies
|
||||||
policy requiring a literal space.
|
are preserved, including requiring a literal space after those keywords.
|
||||||
|
|
||||||
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
|
||||||
|
|||||||
+2
-4
@@ -60,8 +60,7 @@ DecodedFrameValue DbcDecoder::Decode(const DecodeDatabase &database,
|
|||||||
decoded.physicalValue =
|
decoded.physicalValue =
|
||||||
std::visit ([&signal] (auto rawValue) {
|
std::visit ([&signal] (auto rawValue) {
|
||||||
return (static_cast<double> (rawValue) * signal.factor) + signal.offset;
|
return (static_cast<double> (rawValue) * signal.factor) + signal.offset;
|
||||||
},
|
}, decoded.rawValue);
|
||||||
decoded.rawValue);
|
|
||||||
decoded.valid = true;
|
decoded.valid = true;
|
||||||
|
|
||||||
result.signals.push_back (decoded);
|
result.signals.push_back (decoded);
|
||||||
@@ -145,8 +144,7 @@ std::int64_t DbcDecoder::SignExtend(std::uint64_t value, std::uint32_t bitLength
|
|||||||
|
|
||||||
const std::uint64_t signMask = std::uint64_t (1U) << (bitLength - 1U);
|
const std::uint64_t signMask = std::uint64_t (1U) << (bitLength - 1U);
|
||||||
const std::uint64_t valueMask = (bitLength == 64U)
|
const std::uint64_t valueMask = (bitLength == 64U)
|
||||||
? ~std::uint64_t(0U)
|
? ~std::uint64_t (0U) : (std::uint64_t (1U) << bitLength) - 1U;
|
||||||
: (std::uint64_t(1U) << bitLength) - 1U;
|
|
||||||
value &= valueMask;
|
value &= valueMask;
|
||||||
|
|
||||||
if ((value & signMask) == 0U)
|
if ((value & signMask) == 0U)
|
||||||
|
|||||||
+11
-4
@@ -12,9 +12,9 @@
|
|||||||
#ifndef DBC_DECODER_H
|
#ifndef DBC_DECODER_H
|
||||||
#define DBC_DECODER_H
|
#define DBC_DECODER_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "decode_database.h"
|
#include "decode_database.h"
|
||||||
|
|
||||||
@@ -27,7 +27,9 @@ struct RawCanFrame {
|
|||||||
std::vector<std::uint8_t> data; /**< Payload bytes. */
|
std::vector<std::uint8_t> data; /**< Payload bytes. */
|
||||||
|
|
||||||
RawCanFrame()
|
RawCanFrame()
|
||||||
: canId(0U), isExtended(false), data() {
|
: canId (0U)
|
||||||
|
, isExtended (false)
|
||||||
|
, data() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -41,7 +43,10 @@ struct DecodedSignalValue {
|
|||||||
bool valid; /**< true if decoding succeeded. */
|
bool valid; /**< true if decoding succeeded. */
|
||||||
|
|
||||||
DecodedSignalValue()
|
DecodedSignalValue()
|
||||||
: definition(nullptr), rawValue(std::uint64_t(0U)), physicalValue(0.0), valid(false) {
|
: definition (nullptr)
|
||||||
|
, rawValue (std::uint64_t (0U))
|
||||||
|
, physicalValue (0.0)
|
||||||
|
, valid (false) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -54,7 +59,9 @@ struct DecodedFrameValue {
|
|||||||
bool valid; /**< true if frame was matched. */
|
bool valid; /**< true if frame was matched. */
|
||||||
|
|
||||||
DecodedFrameValue()
|
DecodedFrameValue()
|
||||||
: definition(nullptr), signals(), valid(false) {
|
: definition (nullptr)
|
||||||
|
, signals()
|
||||||
|
, valid (false) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -12,9 +12,9 @@
|
|||||||
#include "dbc_parser.h"
|
#include "dbc_parser.h"
|
||||||
#include "dbc_statement_handler.h"
|
#include "dbc_statement_handler.h"
|
||||||
|
|
||||||
#include <cctype>
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include <cctype>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
/**
|
/**
|
||||||
@@ -35,7 +35,7 @@ namespace {
|
|||||||
|
|
||||||
return text.substr (begin, end - begin);
|
return text.substr (begin, end - begin);
|
||||||
}
|
}
|
||||||
}// namespace
|
}
|
||||||
|
|
||||||
DbcDatabase DbcParser::ParseFile (const std::string &filePath) const {
|
DbcDatabase DbcParser::ParseFile (const std::string &filePath) const {
|
||||||
std::ifstream input (filePath.c_str());
|
std::ifstream input (filePath.c_str());
|
||||||
|
|||||||
+94
-59
@@ -1,12 +1,10 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
#include "dbc_statement_handler.h"
|
#include "dbc_statement_handler.h"
|
||||||
#include <cctype>
|
|
||||||
#include <charconv>
|
|
||||||
#include <cmath>
|
|
||||||
#include <limits>
|
|
||||||
#include <regex>
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include <cctype>
|
||||||
|
#include <charconv>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
std::uint32_t ParseDecimal (const std::string &text,
|
std::uint32_t ParseDecimal (const std::string &text,
|
||||||
@@ -40,7 +38,7 @@ namespace {
|
|||||||
}
|
}
|
||||||
FrameInfo *FindFrameById (DbcDatabase &, std::uint32_t, bool);
|
FrameInfo *FindFrameById (DbcDatabase &, std::uint32_t, bool);
|
||||||
SignalInfo *FindSignalByName (FrameInfo &, const std::string &);
|
SignalInfo *FindSignalByName (FrameInfo &, const std::string &);
|
||||||
}// namespace
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
std::vector<std::string> SplitReceivers (const std::string &text) {
|
std::vector<std::string> SplitReceivers (const std::string &text) {
|
||||||
@@ -98,7 +96,7 @@ namespace {
|
|||||||
return pgn;
|
return pgn;
|
||||||
}
|
}
|
||||||
|
|
||||||
}// namespace
|
}
|
||||||
|
|
||||||
FrameInfo DbcFrameHandler::ParseFrameLine (const std::string &line) {
|
FrameInfo DbcFrameHandler::ParseFrameLine (const std::string &line) {
|
||||||
/*
|
/*
|
||||||
@@ -144,50 +142,95 @@ 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.
|
/*
|
||||||
static const std::regex syntax(
|
* Example:
|
||||||
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");
|
* SG_ EngineSpeed : 0|16@1+ (0.125,0) [0|8000] "rpm" ECU1,ECU2
|
||||||
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.");
|
|
||||||
|
|
||||||
signal.startBit = ParseDecimal(fields[2].str(),
|
std::string work = TrimText (line);
|
||||||
std::numeric_limits<std::uint32_t>::max(), "signal start bit");
|
if (work.compare (0U, 4U, "SG_ ") != 0)
|
||||||
signal.length = ParseDecimal(fields[3].str(), 64U, "signal length");
|
throw std::runtime_error ("Invalid signal line: " + line);
|
||||||
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) {
|
work.erase (0U, 4U);
|
||||||
static const std::regex number(
|
|
||||||
R"(^[+-]?(?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+)(?:[eE][+-]?[0-9]+)?$)");
|
const std::string::size_type colonPos = work.find (':');
|
||||||
if (!std::regex_match(text, number))
|
if (colonPos == std::string::npos)
|
||||||
throw std::runtime_error(std::string("Invalid ") + field + ": " + text);
|
throw std::runtime_error ("Signal line missing ':' : " + line);
|
||||||
std::size_t consumed = 0U;
|
|
||||||
double value;
|
signal.name = TrimText (work.substr (0U, colonPos));
|
||||||
try {
|
std::string rest = TrimText (work.substr (colonPos + 1U));
|
||||||
value = std::stod(text, &consumed);
|
|
||||||
} catch (const std::exception &) {
|
const std::string::size_type pipePos = rest.find ('|');
|
||||||
throw std::runtime_error(std::string("Invalid ") + field + ": " + text);
|
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');
|
||||||
}
|
}
|
||||||
if (consumed != text.size() || !std::isfinite(value))
|
|
||||||
throw std::runtime_error(std::string("Invalid ") + field + ": " + text);
|
{
|
||||||
return value;
|
const char signChar = rest[signPos];
|
||||||
};
|
signal.isSigned = (signChar == '-');
|
||||||
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.factor = std::stod (
|
||||||
signal.maximum = parsePhysical(fields[9].str(), "signal maximum");
|
TrimText (rest.substr (factorBegin + 1U, factorComma - factorBegin - 1U))
|
||||||
if (signal.minimum > signal.maximum)
|
);
|
||||||
throw std::runtime_error("Signal minimum exceeds maximum.");
|
|
||||||
signal.unit = fields[10].str();
|
signal.offset = std::stod (
|
||||||
signal.receivers = SplitReceivers(fields[11].str());
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
return signal;
|
return signal;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -275,7 +318,7 @@ namespace {
|
|||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}// namespace
|
}
|
||||||
|
|
||||||
void DbcFrameHandler::Handle (const std::string &line, DbcParseContext &context) const {
|
void DbcFrameHandler::Handle (const std::string &line, DbcParseContext &context) const {
|
||||||
context.database.frames.push_back (ParseFrameLine (line));
|
context.database.frames.push_back (ParseFrameLine (line));
|
||||||
@@ -283,20 +326,12 @@ 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.");
|
||||||
SignalInfo signal = ParseSignalLine(line);
|
context.currentFrame->signals.push_back (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 {
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ public:
|
|||||||
class DbcFrameHandler : public DbcStatementHandler {
|
class DbcFrameHandler : public DbcStatementHandler {
|
||||||
public:
|
public:
|
||||||
void Handle (const std::string &line, DbcParseContext &context) const override;
|
void Handle (const std::string &line, DbcParseContext &context) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static FrameInfo ParseFrameLine (const std::string &line);
|
static FrameInfo ParseFrameLine (const std::string &line);
|
||||||
};
|
};
|
||||||
@@ -30,7 +29,6 @@ private:
|
|||||||
class DbcSignalHandler : public DbcStatementHandler {
|
class DbcSignalHandler : public DbcStatementHandler {
|
||||||
public:
|
public:
|
||||||
void Handle (const std::string &line, DbcParseContext &context) const override;
|
void Handle (const std::string &line, DbcParseContext &context) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static SignalInfo ParseSignalLine (const std::string &line);
|
static SignalInfo ParseSignalLine (const std::string &line);
|
||||||
};
|
};
|
||||||
@@ -38,7 +36,6 @@ private:
|
|||||||
class DbcCommentHandler : public DbcStatementHandler {
|
class DbcCommentHandler : public DbcStatementHandler {
|
||||||
public:
|
public:
|
||||||
void Handle (const std::string &line, DbcParseContext &context) const override;
|
void Handle (const std::string &line, DbcParseContext &context) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void ParseCommentLine (const std::string &line, DbcDatabase &database);
|
static void ParseCommentLine (const std::string &line, DbcDatabase &database);
|
||||||
};
|
};
|
||||||
|
|||||||
+27
-6
@@ -12,10 +12,10 @@
|
|||||||
#ifndef DECODE_DATABASE_H
|
#ifndef DECODE_DATABASE_H
|
||||||
#define DECODE_DATABASE_H
|
#define DECODE_DATABASE_H
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Signal byte order used for runtime decoding.
|
* @brief Signal byte order used for runtime decoding.
|
||||||
@@ -51,7 +51,18 @@ struct DecodeSignal {
|
|||||||
std::string comment; /**< Comment. */
|
std::string comment; /**< Comment. */
|
||||||
|
|
||||||
DecodeSignal()
|
DecodeSignal()
|
||||||
: name(), startBit(0U), length(0U), byteOrder(ByteOrder::Intel), valueType(ValueType::Unsigned), factor(1.0), offset(0.0), minimum(0.0), maximum(0.0), unit(), receivers(), comment() {
|
: name()
|
||||||
|
, startBit (0U)
|
||||||
|
, length (0U)
|
||||||
|
, byteOrder (ByteOrder::Intel)
|
||||||
|
, valueType (ValueType::Unsigned)
|
||||||
|
, factor (1.0)
|
||||||
|
, offset (0.0)
|
||||||
|
, minimum (0.0)
|
||||||
|
, maximum (0.0)
|
||||||
|
, unit()
|
||||||
|
, receivers()
|
||||||
|
, comment() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -70,7 +81,15 @@ struct DecodeFrame {
|
|||||||
std::vector<DecodeSignal> signals; /**< Signal definitions. */
|
std::vector<DecodeSignal> signals; /**< Signal definitions. */
|
||||||
|
|
||||||
DecodeFrame()
|
DecodeFrame()
|
||||||
: name(), canId(0U), isExtended(false), dlc(0U), pgn(0U), hasPgn(false), transmitter(), comment(), signals() {
|
: name()
|
||||||
|
, canId (0U)
|
||||||
|
, isExtended (false)
|
||||||
|
, dlc (0U)
|
||||||
|
, pgn (0U)
|
||||||
|
, hasPgn (false)
|
||||||
|
, transmitter()
|
||||||
|
, comment()
|
||||||
|
, signals() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -82,11 +101,13 @@ struct FrameKey {
|
|||||||
bool isExtended;
|
bool isExtended;
|
||||||
|
|
||||||
FrameKey()
|
FrameKey()
|
||||||
: canId(0U), isExtended(false) {
|
: canId (0U)
|
||||||
|
, isExtended (false) {
|
||||||
}
|
}
|
||||||
|
|
||||||
FrameKey (std::uint32_t id, bool extended)
|
FrameKey (std::uint32_t id, bool extended)
|
||||||
: canId(id), isExtended(extended) {
|
: canId (id)
|
||||||
|
, isExtended (extended) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator== (const FrameKey &other) const {
|
bool operator== (const FrameKey &other) const {
|
||||||
|
|||||||
+10
-2
@@ -12,9 +12,9 @@
|
|||||||
#ifndef FRAME_INFO_H
|
#ifndef FRAME_INFO_H
|
||||||
#define FRAME_INFO_H
|
#define FRAME_INFO_H
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
#include "signal_info.h"
|
#include "signal_info.h"
|
||||||
|
|
||||||
@@ -33,7 +33,15 @@ struct FrameInfo {
|
|||||||
std::vector<SignalInfo> signals; /**< Signals contained in the frame. */
|
std::vector<SignalInfo> signals; /**< Signals contained in the frame. */
|
||||||
|
|
||||||
FrameInfo()
|
FrameInfo()
|
||||||
: name(), canId(0U), isExtended(false), pgn(0U), hasPgn(false), dlc(0U), transmitter(), comment(), signals() {
|
: name()
|
||||||
|
, canId (0U)
|
||||||
|
, isExtended (false)
|
||||||
|
, pgn (0U)
|
||||||
|
, hasPgn (false)
|
||||||
|
, dlc (0U)
|
||||||
|
, transmitter()
|
||||||
|
, comment()
|
||||||
|
, signals() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,40 +0,0 @@
|
|||||||
# 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`
|
|
||||||
@@ -9,16 +9,16 @@
|
|||||||
* See LICENSE file in the project root for full license text.
|
* See LICENSE file in the project root for full license text.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <iomanip>
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <iomanip>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
#include "dbc_decode_builder.h"
|
|
||||||
#include "dbc_decoder.h"
|
|
||||||
#include "dbc_parser.h"
|
#include "dbc_parser.h"
|
||||||
#include "dbc_tree_builder.h"
|
#include "dbc_tree_builder.h"
|
||||||
|
#include "dbc_decode_builder.h"
|
||||||
|
#include "dbc_decoder.h"
|
||||||
|
|
||||||
static void PrintTree (const TreeNode *node, int indent) {
|
static void PrintTree (const TreeNode *node, int indent) {
|
||||||
if (node == nullptr)
|
if (node == nullptr)
|
||||||
|
|||||||
+13
-2
@@ -12,9 +12,9 @@
|
|||||||
#ifndef SIGNAL_INFO_H
|
#ifndef SIGNAL_INFO_H
|
||||||
#define SIGNAL_INFO_H
|
#define SIGNAL_INFO_H
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Describes one signal inside a DBC frame.
|
* @brief Describes one signal inside a DBC frame.
|
||||||
@@ -34,7 +34,18 @@ struct SignalInfo {
|
|||||||
std::string comment; /**< Optional signal comment. */
|
std::string comment; /**< Optional signal comment. */
|
||||||
|
|
||||||
SignalInfo()
|
SignalInfo()
|
||||||
: name(), startBit(0U), length(0U), isLittleEndian(true), isSigned(false), factor(1.0), offset(0.0), minimum(0.0), maximum(0.0), unit(), receivers(), comment() {
|
: name()
|
||||||
|
, startBit (0U)
|
||||||
|
, length (0U)
|
||||||
|
, isLittleEndian (true)
|
||||||
|
, isSigned (false)
|
||||||
|
, factor (1.0)
|
||||||
|
, offset (0.0)
|
||||||
|
, minimum (0.0)
|
||||||
|
, maximum (0.0)
|
||||||
|
, unit()
|
||||||
|
, receivers()
|
||||||
|
, comment() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -43,9 +43,6 @@ 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.
|
||||||
|
|
||||||
|
|||||||
+34
-19
@@ -20,23 +20,41 @@ struct DecodeCase {
|
|||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
const DecodeCase cases[] = {
|
const DecodeCase cases[] = {
|
||||||
{"Motorola reproduction", ByteOrder::Motorola, 7U, 16U, ValueType::Unsigned, {0x12, 0x34}, true, 0x1234, 1.0, 0.0, 4660.0},
|
{"Motorola reproduction", ByteOrder::Motorola, 7U, 16U, ValueType::Unsigned,
|
||||||
{"Motorola within byte", ByteOrder::Motorola, 5U, 4U, ValueType::Unsigned, {0xAC}, true, 11, 1.0, 0.0, 11.0},
|
{0x12, 0x34}, true, 0x1234, 1.0, 0.0, 4660.0},
|
||||||
{"Motorola unaligned crossing", ByteOrder::Motorola, 3U, 12U, ValueType::Unsigned, {0xAB, 0xCD}, true, 0xBCD, 1.0, 0.0, 3021.0},
|
{"Motorola within byte", ByteOrder::Motorola, 5U, 4U, ValueType::Unsigned,
|
||||||
{"Motorola starts at bit zero", ByteOrder::Motorola, 0U, 2U, ValueType::Unsigned, {0x01, 0x80}, true, 3, 1.0, 0.0, 3.0},
|
{0xAC}, true, 11, 1.0, 0.0, 11.0},
|
||||||
{"Motorola second byte", ByteOrder::Motorola, 15U, 16U, ValueType::Unsigned, {0xFF, 0x12, 0x34}, true, 0x1234, 1.0, 0.0, 4660.0},
|
{"Motorola unaligned crossing", ByteOrder::Motorola, 3U, 12U, ValueType::Unsigned,
|
||||||
{"Motorola signed scaled", ByteOrder::Motorola, 7U, 16U, ValueType::Signed, {0xFF, 0xFE}, true, -2, 0.5, 10.0, 9.0},
|
{0xAB, 0xCD}, true, 0xBCD, 1.0, 0.0, 3021.0},
|
||||||
{"Motorola 64 bits", ByteOrder::Motorola, 7U, 64U, ValueType::Unsigned, {0, 0, 0, 0, 0, 0, 0x12, 0x34}, true, 0x1234, 1.0, 0.0, 4660.0},
|
{"Motorola starts at bit zero", ByteOrder::Motorola, 0U, 2U, ValueType::Unsigned,
|
||||||
{"Motorola 64 bits high bit", ByteOrder::Motorola, 7U, 64U, ValueType::Unsigned, {0x40, 0, 0, 0, 0, 0, 0, 0}, true, 0x4000000000000000LL, 1.0, 0.0, 4611686018427387904.0},
|
{0x01, 0x80}, true, 3, 1.0, 0.0, 3.0},
|
||||||
{"Motorola short payload", ByteOrder::Motorola, 7U, 16U, ValueType::Unsigned, {0x12}, false, 0, 1.0, 0.0, 0.0},
|
{"Motorola second byte", ByteOrder::Motorola, 15U, 16U, ValueType::Unsigned,
|
||||||
{"Motorola empty payload", ByteOrder::Motorola, 7U, 8U, ValueType::Unsigned, {}, false, 0, 1.0, 0.0, 0.0},
|
{0xFF, 0x12, 0x34}, true, 0x1234, 1.0, 0.0, 4660.0},
|
||||||
{"Motorola large start", ByteOrder::Motorola, std::numeric_limits<std::uint32_t>::max(), 8U, ValueType::Unsigned, {0xFF}, false, 0, 1.0, 0.0, 0.0},
|
{"Motorola signed scaled", ByteOrder::Motorola, 7U, 16U, ValueType::Signed,
|
||||||
{"Motorola zero length", ByteOrder::Motorola, 7U, 0U, ValueType::Unsigned, {0xFF}, false, 0, 1.0, 0.0, 0.0},
|
{0xFF, 0xFE}, true, -2, 0.5, 10.0, 9.0},
|
||||||
|
{"Motorola 64 bits", ByteOrder::Motorola, 7U, 64U, ValueType::Unsigned,
|
||||||
|
{0, 0, 0, 0, 0, 0, 0x12, 0x34}, true, 0x1234, 1.0, 0.0, 4660.0},
|
||||||
|
{"Motorola 64 bits high bit", ByteOrder::Motorola, 7U, 64U, ValueType::Unsigned,
|
||||||
|
{0x40, 0, 0, 0, 0, 0, 0, 0}, true, 0x4000000000000000LL,
|
||||||
|
1.0, 0.0, 4611686018427387904.0},
|
||||||
|
{"Motorola short payload", ByteOrder::Motorola, 7U, 16U, ValueType::Unsigned,
|
||||||
|
{0x12}, false, 0, 1.0, 0.0, 0.0},
|
||||||
|
{"Motorola empty payload", ByteOrder::Motorola, 7U, 8U, ValueType::Unsigned,
|
||||||
|
{}, false, 0, 1.0, 0.0, 0.0},
|
||||||
|
{"Motorola large start", ByteOrder::Motorola,
|
||||||
|
std::numeric_limits<std::uint32_t>::max(), 8U, ValueType::Unsigned,
|
||||||
|
{0xFF}, false, 0, 1.0, 0.0, 0.0},
|
||||||
|
{"Motorola zero length", ByteOrder::Motorola, 7U, 0U, ValueType::Unsigned,
|
||||||
|
{0xFF}, false, 0, 1.0, 0.0, 0.0},
|
||||||
{"Motorola excessive length", ByteOrder::Motorola, 7U, 65U, ValueType::Unsigned,
|
{"Motorola excessive length", ByteOrder::Motorola, 7U, 65U, ValueType::Unsigned,
|
||||||
std::vector<std::uint8_t> (9U, 0xFF), false, 0, 1.0, 0.0, 0.0},
|
std::vector<std::uint8_t> (9U, 0xFF), false, 0, 1.0, 0.0, 0.0},
|
||||||
{"Intel regression", ByteOrder::Intel, 0U, 16U, ValueType::Unsigned, {0x12, 0x34}, true, 0x3412, 1.0, 0.0, 13330.0},
|
{"Intel regression", ByteOrder::Intel, 0U, 16U, ValueType::Unsigned,
|
||||||
{"Intel signed regression", ByteOrder::Intel, 0U, 16U, ValueType::Signed, {0xFE, 0xFF}, true, -2, 0.5, 10.0, 9.0},
|
{0x12, 0x34}, true, 0x3412, 1.0, 0.0, 13330.0},
|
||||||
{"Intel short payload", ByteOrder::Intel, 0U, 16U, ValueType::Unsigned, {0x12}, false, 0, 1.0, 0.0, 0.0}};
|
{"Intel signed regression", ByteOrder::Intel, 0U, 16U, ValueType::Signed,
|
||||||
|
{0xFE, 0xFF}, true, -2, 0.5, 10.0, 9.0},
|
||||||
|
{"Intel short payload", ByteOrder::Intel, 0U, 16U, ValueType::Unsigned,
|
||||||
|
{0x12}, false, 0, 1.0, 0.0, 0.0}
|
||||||
|
};
|
||||||
|
|
||||||
int failures = 0;
|
int failures = 0;
|
||||||
for (const DecodeCase &test : cases) {
|
for (const DecodeCase &test : cases) {
|
||||||
@@ -112,10 +130,7 @@ int main() {
|
|||||||
? std::variant<std::int64_t, std::uint64_t> (bits)
|
? std::variant<std::int64_t, std::uint64_t> (bits)
|
||||||
: std::variant<std::int64_t, std::uint64_t> (signedExpected);
|
: std::variant<std::int64_t, std::uint64_t> (signedExpected);
|
||||||
const double physical = (type == ValueType::Unsigned
|
const double physical = (type == ValueType::Unsigned
|
||||||
? static_cast<double>(bits)
|
? static_cast<double> (bits) : static_cast<double> (signedExpected)) * 0.5 + 10.0;
|
||||||
: static_cast<double>(signedExpected)) *
|
|
||||||
0.5 +
|
|
||||||
10.0;
|
|
||||||
std::ostringstream actualText;
|
std::ostringstream actualText;
|
||||||
std::ostringstream expectedText;
|
std::ostringstream expectedText;
|
||||||
std::visit ([&actualText] (auto raw) { actualText << raw; }, decoded.rawValue);
|
std::visit ([&actualText] (auto raw) { actualText << raw; }, decoded.rawValue);
|
||||||
|
|||||||
+15
-135
@@ -9,11 +9,9 @@
|
|||||||
|
|
||||||
struct Fixture {
|
struct Fixture {
|
||||||
std::filesystem::path path = std::filesystem::temp_directory_path() /
|
std::filesystem::path path = std::filesystem::temp_directory_path() /
|
||||||
("dbc_parser_test_" + std::to_string(std::chrono::steady_clock::now().time_since_epoch().count()) + ".dbc");
|
("dbc_parser_test_" + std::to_string (
|
||||||
~Fixture() {
|
std::chrono::steady_clock::now().time_since_epoch().count()) + ".dbc");
|
||||||
std::error_code error;
|
~Fixture() { std::error_code error; std::filesystem::remove (path, error); }
|
||||||
std::filesystem::remove(path, error);
|
|
||||||
}
|
|
||||||
void Write (const std::string &text) const {
|
void Write (const std::string &text) const {
|
||||||
std::ofstream output (path);
|
std::ofstream output (path);
|
||||||
output << text;
|
output << text;
|
||||||
@@ -39,17 +37,9 @@ int main() {
|
|||||||
std::size_t count = 0U;
|
std::size_t count = 0U;
|
||||||
const auto check = [&] (bool passed, const std::string &name) {
|
const auto check = [&] (bool passed, const std::string &name) {
|
||||||
++count;
|
++count;
|
||||||
if (!passed) {
|
if (!passed) { ++failures; std::cerr << "FAIL: " << name << '\n'; }
|
||||||
++failures;
|
|
||||||
std::cerr << "FAIL: " << name << '\n';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
struct ValidCase {
|
|
||||||
const char *line;
|
|
||||||
std::uint32_t id;
|
|
||||||
bool extended;
|
|
||||||
unsigned length;
|
|
||||||
};
|
};
|
||||||
|
struct ValidCase { const char *line; std::uint32_t id; bool extended; unsigned length; };
|
||||||
const ValidCase valid[] = {
|
const ValidCase valid[] = {
|
||||||
{"BO_ 0 Frame: 0 ECU", 0U, false, 0U},
|
{"BO_ 0 Frame: 0 ECU", 0U, false, 0U},
|
||||||
{"BO_ 2047 Frame: 8 ECU", 2047U, false, 8U},
|
{"BO_ 2047 Frame: 8 ECU", 2047U, false, 8U},
|
||||||
@@ -57,7 +47,8 @@ int main() {
|
|||||||
{"BO_ 536870911 Frame: 255 ECU", 0x1FFFFFFFU, true, 255U},
|
{"BO_ 536870911 Frame: 255 ECU", 0x1FFFFFFFU, true, 255U},
|
||||||
{"BO_ 2147483649 Frame: 8 ECU", 1U, true, 8U},
|
{"BO_ 2147483649 Frame: 8 ECU", 1U, true, 8U},
|
||||||
{"BO_ 2684354559 Frame: 8 ECU", 0x1FFFFFFFU, true, 8U},
|
{"BO_ 2684354559 Frame: 8 ECU", 0x1FFFFFFFU, true, 8U},
|
||||||
{" BO_\t256\tFrame :\t8\tECU \r", 256U, false, 8U}};
|
{" BO_\t256\tFrame :\t8\tECU \r", 256U, false, 8U}
|
||||||
|
};
|
||||||
for (const auto &test : valid) {
|
for (const auto &test : valid) {
|
||||||
fixture.Write (std::string (test.line) + '\n');
|
fixture.Write (std::string (test.line) + '\n');
|
||||||
try {
|
try {
|
||||||
@@ -66,8 +57,7 @@ int main() {
|
|||||||
database.frames[0].canId == test.id &&
|
database.frames[0].canId == test.id &&
|
||||||
database.frames[0].isExtended == test.extended &&
|
database.frames[0].isExtended == test.extended &&
|
||||||
database.frames[0].dlc == test.length &&
|
database.frames[0].dlc == test.length &&
|
||||||
database.frames[0].transmitter == "ECU",
|
database.frames[0].transmitter == "ECU", test.line);
|
||||||
test.line);
|
|
||||||
} catch (const std::exception &error) { check (false, error.what()); }
|
} catch (const std::exception &error) { check (false, error.what()); }
|
||||||
}
|
}
|
||||||
const char *invalid[] = {
|
const char *invalid[] = {
|
||||||
@@ -80,7 +70,8 @@ int main() {
|
|||||||
"BO_ 2684354560 Frame: 8 ECU", "BO_ 4294967295 Frame: 8 ECU",
|
"BO_ 2684354560 Frame: 8 ECU", "BO_ 4294967295 Frame: 8 ECU",
|
||||||
"BO_ 1 Frame: -1 ECU", "BO_ 1 Frame: +8 ECU", "BO_ 1 Frame: 8x ECU",
|
"BO_ 1 Frame: -1 ECU", "BO_ 1 Frame: +8 ECU", "BO_ 1 Frame: 8x ECU",
|
||||||
"BO_ 1 Frame: 8.5 ECU", "BO_ 1 Frame: 256 ECU",
|
"BO_ 1 Frame: 8.5 ECU", "BO_ 1 Frame: 256 ECU",
|
||||||
"BO_ 1 Frame: 4294967296 ECU", "BO_\tinvalid"};
|
"BO_ 1 Frame: 4294967296 ECU", "BO_\tinvalid"
|
||||||
|
};
|
||||||
for (const auto *line : invalid) {
|
for (const auto *line : invalid) {
|
||||||
fixture.Write (std::string ("VERSION \"test\"\n\n") + line + '\n');
|
fixture.Write (std::string ("VERSION \"test\"\n\n") + line + '\n');
|
||||||
try {
|
try {
|
||||||
@@ -89,8 +80,7 @@ int main() {
|
|||||||
} catch (const std::runtime_error &error) {
|
} catch (const std::runtime_error &error) {
|
||||||
const std::string prefix = fixture.path.string() + ":3: ";
|
const std::string prefix = fixture.path.string() + ":3: ";
|
||||||
check (std::string (error.what()).find (prefix) == 0U &&
|
check (std::string (error.what()).find (prefix) == 0U &&
|
||||||
std::string(error.what()).size() > prefix.size(),
|
std::string (error.what()).size() > prefix.size(), line);
|
||||||
line);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fixture.Write (
|
fixture.Write (
|
||||||
@@ -101,8 +91,7 @@ int main() {
|
|||||||
const auto database = DbcParser().ParseFile (fixture.path.string());
|
const auto database = DbcParser().ParseFile (fixture.path.string());
|
||||||
check (database.frames.size() == 2U && database.frames[0].signals.size() == 1U &&
|
check (database.frames.size() == 2U && database.frames[0].signals.size() == 1U &&
|
||||||
database.frames[0].comment == "Frame comment" &&
|
database.frames[0].comment == "Frame comment" &&
|
||||||
database.frames[0].signals[0].comment == "Signal comment",
|
database.frames[0].signals[0].comment == "Signal comment", "Integration");
|
||||||
"Integration");
|
|
||||||
fixture.Write ("\nSG_ Speed : invalid\n");
|
fixture.Write ("\nSG_ Speed : invalid\n");
|
||||||
try {
|
try {
|
||||||
DbcParser().ParseFile (fixture.path.string());
|
DbcParser().ParseFile (fixture.path.string());
|
||||||
@@ -111,112 +100,6 @@ 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;
|
||||||
@@ -224,13 +107,11 @@ int main() {
|
|||||||
check (dispatcher.Dispatch ("TEST_ first", context) &&
|
check (dispatcher.Dispatch ("TEST_ first", context) &&
|
||||||
dispatcher.Dispatch ("TEST_ second", context) &&
|
dispatcher.Dispatch ("TEST_ second", context) &&
|
||||||
CountingHandler::instances == 1 && CountingHandler::calls == 2 &&
|
CountingHandler::instances == 1 && CountingHandler::calls == 2 &&
|
||||||
dispatchDatabase.frames.size() == 2U,
|
dispatchDatabase.frames.size() == 2U, "Factory dispatch and handler reuse");
|
||||||
"Factory dispatch and handler reuse");
|
|
||||||
check (!dispatcher.Dispatch ("TEST_OTHER data", context) &&
|
check (!dispatcher.Dispatch ("TEST_OTHER data", context) &&
|
||||||
!dispatcher.Dispatch ("BO_TX_BU_ 1 : ECU;", context) &&
|
!dispatcher.Dispatch ("BO_TX_BU_ 1 : ECU;", context) &&
|
||||||
!dispatcher.Dispatch ("VAL_ 1 Speed 0 \"Stopped\";", context) &&
|
!dispatcher.Dispatch ("VAL_ 1 Speed 0 \"Stopped\";", context) &&
|
||||||
CountingHandler::calls == 2,
|
CountingHandler::calls == 2, "Exact and unknown keyword dispatch");
|
||||||
"Exact and unknown keyword dispatch");
|
|
||||||
const DbcParser parser;
|
const DbcParser parser;
|
||||||
fixture.Write ("BO_ 1 First: 8 ECU\n");
|
fixture.Write ("BO_ 1 First: 8 ECU\n");
|
||||||
check (parser.ParseFile (fixture.path.string()).frames.size() == 1U, "First parse");
|
check (parser.ParseFile (fixture.path.string()).frames.size() == 1U, "First parse");
|
||||||
@@ -240,8 +121,7 @@ int main() {
|
|||||||
check (false, "Independent parse context");
|
check (false, "Independent parse context");
|
||||||
} catch (const std::runtime_error &error) {
|
} catch (const std::runtime_error &error) {
|
||||||
check (std::string (error.what()).find ("Signal found before any frame") !=
|
check (std::string (error.what()).find ("Signal found before any frame") !=
|
||||||
std::string::npos,
|
std::string::npos, "Independent parse context");
|
||||||
"Independent parse context");
|
|
||||||
}
|
}
|
||||||
fixture.Write ("BO_ 2 Second: 8 ECU\n");
|
fixture.Write ("BO_ 2 Second: 8 ECU\n");
|
||||||
const auto recovered = parser.ParseFile (fixture.path.string());
|
const auto recovered = parser.ParseFile (fixture.path.string());
|
||||||
|
|||||||
+15
-3
@@ -12,15 +12,27 @@
|
|||||||
#include "tree_node.h"
|
#include "tree_node.h"
|
||||||
|
|
||||||
TreeNode::TreeNode()
|
TreeNode::TreeNode()
|
||||||
: m_type(NodeType::Root), m_name("dbc"), m_children(), m_frame(), m_signal() {
|
: m_type (NodeType::Root)
|
||||||
|
, m_name ("dbc")
|
||||||
|
, m_children()
|
||||||
|
, m_frame()
|
||||||
|
, m_signal() {
|
||||||
}
|
}
|
||||||
|
|
||||||
TreeNode::TreeNode (const FrameInfo &frame)
|
TreeNode::TreeNode (const FrameInfo &frame)
|
||||||
: m_type(NodeType::Frame), m_name(frame.name), m_children(), m_frame(new FrameInfo(frame)), m_signal() {
|
: m_type (NodeType::Frame)
|
||||||
|
, m_name (frame.name)
|
||||||
|
, m_children()
|
||||||
|
, m_frame (new FrameInfo (frame))
|
||||||
|
, m_signal() {
|
||||||
}
|
}
|
||||||
|
|
||||||
TreeNode::TreeNode (const SignalInfo &signal)
|
TreeNode::TreeNode (const SignalInfo &signal)
|
||||||
: m_type(NodeType::Signal), m_name(signal.name), m_children(), m_frame(), m_signal(new SignalInfo(signal)) {
|
: m_type (NodeType::Signal)
|
||||||
|
, m_name (signal.name)
|
||||||
|
, m_children()
|
||||||
|
, m_frame()
|
||||||
|
, m_signal (new SignalInfo (signal)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void TreeNode::AddChild (std::unique_ptr<TreeNode> child) {
|
void TreeNode::AddChild (std::unique_ptr<TreeNode> child) {
|
||||||
|
|||||||
+2
-2
@@ -12,10 +12,10 @@
|
|||||||
#ifndef TREE_NODE_H
|
#ifndef TREE_NODE_H
|
||||||
#define TREE_NODE_H
|
#define TREE_NODE_H
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
#include "frame_info.h"
|
#include "frame_info.h"
|
||||||
#include "signal_info.h"
|
#include "signal_info.h"
|
||||||
|
|||||||
Reference in new issue
Block a user