This commit is contained in:
deeaitch committed 2026-10-07 18:32:51 -04:00
1 parent a533d9aa00
commit a0938cb29c
14 files changed
+375 -13

No files matched your search

+59 -7
View File
@@ -381,6 +381,7 @@ Fields:
- `rawValue`
- `physicalValue`
- `valid`
- `status`
---
@@ -444,7 +445,7 @@ Main runtime decoder class.
Responsibilities:
- find a frame definition by CAN ID
- decode all signals in a frame
- decode unconditional signals and the active multiplexed branch
- extract raw values
- sign-extend signed values
- convert raw values into physical values
@@ -465,6 +466,7 @@ The current parser supports the following DBC elements:
- `CM_ BO_`
- `CM_ SG_`
- `VAL_` signal value descriptions
- basic `M` / `mN` multiplexing
---
@@ -560,15 +562,64 @@ 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.
Basic multiplexing markers `M` and `mN` are supported as described below.
Overlapping signals are rejected unless their branch values differ.
Duplicate frame/signal name detection remains separate work.
Invalid signal definitions stop parsing with `path:line: description`, including
when the signal exceeds the declared payload length.
---
## Basic multiplexing
```dbc
BO_ 256 Status: 3 ECU
SG_ Mode M : 0|8@1+ (1,0) [0|255] "" ECU
SG_ Temperature m1 : 8|8@1- (1,0) [-128|127] "C" ECU
SG_ Pressure m2 : 8|8@1+ (2,0) [0|510] "kPa" ECU
SG_ Counter : 16|8@1+ (1,0) [0|255] "" ECU
```
`SignalInfo` and `DecodeSignal` contain `multiplexing`: a `MultiplexRole`
(`Unconditional`, `Selector`, or `Branch`) and an exact `uint64_t` branch `value`.
Each frame may have one unsigned selector (`M`); a branch (`mN`) references that
selector implicitly. The selector may appear after its branches. `N` must contain
only decimal digits, fit `uint64_t`, and fit the selector's bit width. Leading
zeros are accepted. A selector without branches is allowed. Signed selectors,
missing selectors, multiple selectors, nested/combined markers, and extended
`SG_MUL_VAL_` statements are rejected with the originating `path:line`.
The dispatcher registers the unsupported statement handler through `TypeFactory`.
Signal bits may overlap only between branches with different selector values.
This validation uses Intel or Motorola bit traversal, including mixed byte orders.
It also rejects overlapping unconditional signals. Metadata survives the runtime
and tree builders; the optional Qt adapter does not add multiplexing roles yet.
The shared decoder extracts the selector before processing branches, compares its
**raw unsigned value**, and leaves result order equal to definition order. Factor,
offset, and `VAL_` descriptions do not influence branch selection. Unconditional
signals continue to decode. An unmapped selector value leaves all branches inactive.
Each result has `SignalDecodeStatus status`:
| Status | Meaning |
| --- | --- |
| `Valid` | Raw and physical values are available; `valid == true` |
| `Inactive` | Selector is available and chooses another branch |
| `SelectorUnavailable` | Branch activation is unknown because selector bytes are missing |
| `InsufficientPayload` | Selected or unconditional signal bytes are missing |
| `InvalidDefinition` | Invalid bit length or missing/invalid selector in manually built runtime metadata |
All statuses except `Valid` have `valid == false`; their numeric fields contain
no decoded data and `GetValueDescription()` returns nullptr. Check `status` to
distinguish inactive branches from errors. Consumers constructing runtime metadata
manually must obey the same non-overlap rules as the parser. Extended and nested
multiplexing are outside this stage and are explicitly rejected, rather than
silently decoded as unconditional signals.
---
## Comments
Frame comment example:
@@ -631,7 +682,8 @@ RawCanFrame
↓
Find frame in DecodeDatabase
↓
For each signal:
Extract multiplexor selector first (when present)
For each unconditional or active signal:
extract raw bits
apply sign extension if needed
convert to physical value
@@ -895,7 +947,7 @@ This is still a minimal implementation.
Not supported yet:
- multiplexed signals
- extended and nested multiplexing (`SG_MUL_VAL_`, `mNM`)
- named `VAL_TABLE_` tables and environment-variable value descriptions
- `BA_` attributes
- `BA_DEF_` definitions
@@ -949,7 +1001,7 @@ Recommended additions:
Recommended additions:
- multiplexing support
- basic multiplexing (implemented)
- attribute parsing
- richer J1939 support
- CSV export of decoded traces
+18
View File
@@ -6,6 +6,24 @@ existing Git tag; subsequent changes are listed as unreleased.
## Unreleased
### 2026-10-07
#### Added
- Basic `M` / `mN` multiplexing metadata in parsed, tree, and runtime definitions.
- Deferred selector and overlap validation with source locations: one unsigned
selector, representable branch values, and overlaps only between exclusive branches.
- Selector-first decoding using exact raw values, preserving definition order and
unconditional signals. Explicit statuses distinguish inactive branches, missing
selector data, insufficient payloads, and malformed runtime metadata.
- Registered `SG_MUL_VAL_` rejection through TypeFactory; extended and nested
multiplexing remain unsupported. Added parser/decoder regression coverage and
documented supported syntax and consumer behavior.
#### Changed
- Overlapping unconditional signals are now rejected during parsing.
### 2026-10-06
#### Added
+1
View File
@@ -7,6 +7,7 @@ SOURCES += \
$$PWD/tree_node.cpp
HEADERS += \
$$PWD/multiplexing.h \
$$PWD/dbc_database.h \
$$PWD/dbc_decode_builder.h \
$$PWD/dbc_decoder.h \
+1
View File
@@ -31,6 +31,7 @@ DecodeDatabase DbcDecodeBuilder::Build(const DbcDatabase &source) const {
const SignalInfo &sourceSignal = sourceFrame.signals[signalIndex];
DecodeSignal targetSignal;
targetSignal.multiplexing = sourceSignal.multiplexing;
targetSignal.name = sourceSignal.name;
targetSignal.startBit = sourceSignal.startBit;
targetSignal.length = sourceSignal.length;
+37 -2
View File
@@ -39,6 +39,19 @@ DecodedFrameValue DbcDecoder::Decode(const DecodeDatabase &database,
result.definition = definition;
result.valid = true;
const DecodeSignal *selector = nullptr;
std::size_t selectorCount = 0U;
for (const DecodeSignal &signal : definition->signals) {
if (signal.multiplexing.role == MultiplexRole::Selector) {
selector = &signal;
++selectorCount;
}
}
const bool selectorDefinitionValid = selectorCount == 1U &&
selector->valueType == ValueType::Unsigned && selector->length > 0U && selector->length <= 64U;
std::uint64_t selectorValue = 0U;
const bool selectorAvailable = selectorDefinitionValid && ExtractUnsigned(frame.data, *selector, selectorValue);
for (std::size_t signalIndex = 0U; signalIndex < definition->signals.size(); ++signalIndex) {
const DecodeSignal &signal = definition->signals[signalIndex];
DecodedSignalValue decoded;
@@ -46,8 +59,29 @@ DecodedFrameValue DbcDecoder::Decode(const DecodeDatabase &database,
decoded.definition = &signal;
if (signal.length == 0U || signal.length > 64U ||
(signal.multiplexing.role != MultiplexRole::Unconditional && !selectorDefinitionValid)) {
result.signals.push_back(decoded);
continue;
}
if (signal.multiplexing.role == MultiplexRole::Branch) {
if (selector->length < 64U && signal.multiplexing.value >= (std::uint64_t(1U) << selector->length)) {
result.signals.push_back(decoded);
continue;
}
if (!selectorAvailable) {
decoded.status = SignalDecodeStatus::SelectorUnavailable;
result.signals.push_back(decoded);
continue;
}
if (signal.multiplexing.value != selectorValue) {
decoded.status = SignalDecodeStatus::Inactive;
result.signals.push_back(decoded);
continue;
}
}
if (!ExtractUnsigned(frame.data, signal, unsignedValue)) {
decoded.valid = false;
decoded.status = SignalDecodeStatus::InsufficientPayload;
result.signals.push_back(decoded);
continue;
}
@@ -58,12 +92,13 @@ DecodedFrameValue DbcDecoder::Decode(const DecodeDatabase &database,
decoded.rawValue = unsignedValue;
double numericValue;
if (const auto *signedValue = std::get_if<std::int64_t>(&decoded.rawValue))
if (const std::int64_t *signedValue = std::get_if<std::int64_t>(&decoded.rawValue))
numericValue = static_cast<double>(*signedValue);
else
numericValue = static_cast<double>(std::get<std::uint64_t>(decoded.rawValue));
decoded.physicalValue = numericValue * signal.factor + signal.offset;
decoded.valid = true;
decoded.status = SignalDecodeStatus::Valid;
result.signals.push_back(decoded);
}
+9
View File
@@ -31,6 +31,14 @@ struct RawCanFrame {
}
};
enum class SignalDecodeStatus {
InvalidDefinition,
InsufficientPayload,
SelectorUnavailable,
Inactive,
Valid
};
/**
* @brief One decoded signal value.
*/
@@ -39,6 +47,7 @@ struct DecodedSignalValue {
RawSignalValue rawValue; /**< Exact signed or unsigned raw value. */
double physicalValue; /**< Converted physical value. */
bool valid; /**< true if decoding succeeded. */
SignalDecodeStatus status = SignalDecodeStatus::InvalidDefinition;
/** Lookup using the raw integer; nullptr for invalid or unmapped values. */
const std::string *GetValueDescription() const {
+1
View File
@@ -64,6 +64,7 @@ DbcDatabase DbcParser::ParseFile(const std::string &filePath) const {
}
context.ResolveValueDescriptions(filePath);
context.ValidateMultiplexing(filePath);
return database;
}
+88 -3
View File
@@ -169,9 +169,24 @@ SignalInfo DbcSignalHandler::ParseSignalLine(const std::string &line) {
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::istringstream nameFields(fields[1].str());
std::string marker;
std::string extra;
nameFields >> signal.name >> marker;
if (signal.name.empty() || (nameFields >> extra))
throw std::runtime_error("Invalid signal name or multiplexing marker.");
if (marker == "M")
signal.multiplexing.role = MultiplexRole::Selector;
else if (!marker.empty()) {
if (marker[0] != 'm')
throw std::runtime_error("Unsupported multiplexing marker.");
const std::string value = marker.substr(1U);
const std::from_chars_result parsed = std::from_chars(
value.data(), value.data() + value.size(), signal.multiplexing.value);
if (value.empty() || parsed.ec != std::errc() || parsed.ptr != value.data() + value.size())
throw std::runtime_error("Invalid or unsupported multiplexing marker.");
signal.multiplexing.role = MultiplexRole::Branch;
}
signal.startBit = ParseDecimal(fields[2].str(),
std::numeric_limits<std::uint32_t>::max(), "signal start bit");
@@ -281,6 +296,7 @@ namespace {
void DbcFrameHandler::Handle(const std::string &line, DbcParseContext &context) const {
context.database.frames.push_back(ParseFrameLine(line));
context.currentFrame = &context.database.frames.back();
context.signalLines.emplace_back();
}
void DbcSignalHandler::Handle(const std::string &line, DbcParseContext &context) const {
@@ -298,6 +314,7 @@ void DbcSignalHandler::Handle(const std::string &line, DbcParseContext &context)
bit = (bit % 8U == 0U) ? bit + 15U : bit - 1U;
}
context.currentFrame->signals.push_back(std::move(signal));
context.signalLines.back().push_back(context.lineNumber);
}
void DbcCommentHandler::Handle(const std::string &line, DbcParseContext &context) const {
@@ -306,6 +323,7 @@ void DbcCommentHandler::Handle(const std::string &line, DbcParseContext &context
}
DbcStatementDispatcher::DbcStatementDispatcher() {
RegisterType<DbcUnsupportedMultiplexingHandler>("SG_MUL_VAL_");
RegisterType<DbcFrameHandler>("BO_");
RegisterType<DbcSignalHandler>("SG_");
RegisterType<DbcCommentHandler>("CM_");
@@ -408,3 +426,70 @@ void DbcParseContext::ResolveValueDescriptions(const std::string &filePath) {
}
}
}
void DbcUnsupportedMultiplexingHandler::Handle(const std::string &, DbcParseContext &) const {
throw std::runtime_error("Extended and nested multiplexing are unsupported.");
}
namespace {
std::vector<std::uint64_t> SignalBits(const SignalInfo &signal) {
std::vector<std::uint64_t> bits;
std::uint64_t bit = signal.startBit;
for (std::uint32_t index = 0U; index < signal.length; ++index) {
bits.push_back(bit);
if (signal.isLittleEndian)
++bit;
else
bit = bit % 8U == 0U ? bit + 15U : bit - 1U;
}
return bits;
}
bool SignalsOverlap(const SignalInfo &first, const SignalInfo &second) {
const std::vector<std::uint64_t> firstBits = SignalBits(first);
const std::vector<std::uint64_t> secondBits = SignalBits(second);
for (const std::uint64_t firstBit : firstBits) {
for (const std::uint64_t secondBit : secondBits) {
if (firstBit == secondBit)
return true;
}
}
return false;
}
}
void DbcParseContext::ValidateMultiplexing(const std::string &filePath) const {
for (std::size_t frameIndex = 0U; frameIndex < database.frames.size(); ++frameIndex) {
const FrameInfo &frame = database.frames[frameIndex];
const SignalInfo *selector = nullptr;
for (std::size_t index = 0U; index < frame.signals.size(); ++index) {
const SignalInfo &signal = frame.signals[index];
if (signal.multiplexing.role != MultiplexRole::Selector)
continue;
if (selector != nullptr || signal.isSigned)
throw std::runtime_error(filePath + ":" + std::to_string(signalLines[frameIndex][index]) +
": Multiplexing requires one unsigned selector per frame.");
selector = &signal;
}
for (std::size_t index = 0U; index < frame.signals.size(); ++index) {
const SignalInfo &signal = frame.signals[index];
std::string error;
if (signal.multiplexing.role == MultiplexRole::Branch) {
if (selector == nullptr)
error = "Multiplexed branch has no selector.";
else if (selector->length < 64U && signal.multiplexing.value >= (std::uint64_t(1U) << selector->length))
error = "Branch value exceeds selector raw range.";
}
for (std::size_t previous = 0U; previous < index && error.empty(); ++previous) {
const SignalInfo &other = frame.signals[previous];
const bool exclusive = signal.multiplexing.role == MultiplexRole::Branch &&
other.multiplexing.role == MultiplexRole::Branch &&
signal.multiplexing.value != other.multiplexing.value;
if (!exclusive && SignalsOverlap(signal, other))
error = "Signals overlap without mutually exclusive multiplexing conditions.";
}
if (!error.empty())
throw std::runtime_error(filePath + ":" + std::to_string(signalLines[frameIndex][index]) + ": " + error);
}
}
}
+8
View File
@@ -22,6 +22,9 @@ struct DbcParseContext {
std::size_t lineNumber = 0U;
std::vector<PendingValueDescriptions> pendingValues = {};
std::vector<std::vector<std::size_t>> signalLines = {};
void ValidateMultiplexing(const std::string &filePath) const;
void ResolveValueDescriptions(const std::string &filePath);
};
@@ -60,6 +63,11 @@ public:
void Handle(const std::string &line, DbcParseContext &context) const override;
};
class DbcUnsupportedMultiplexingHandler : public DbcStatementHandler {
public:
void Handle(const std::string &line, DbcParseContext &context) const override;
};
/** Exact-key dispatch to handlers created once per registration via TypeFactory. */
class DbcStatementDispatcher {
public:
+2
View File
@@ -13,6 +13,7 @@
#define DECODE_DATABASE_H
#include "value_descriptions.h"
#include "multiplexing.h"
#include <cstdint>
#include <string>
#include <unordered_map>
@@ -38,6 +39,7 @@ enum class ValueType {
* @brief Runtime-ready signal definition.
*/
struct DecodeSignal {
Multiplexing multiplexing;
std::string name; /**< Signal name. */
std::uint32_t startBit; /**< DBC start bit. */
std::uint32_t length; /**< Signal length in bits. */
+19
View File
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: MIT
#ifndef MULTIPLEXING_H
#define MULTIPLEXING_H
#include <cstdint>
enum class MultiplexRole {
Unconditional,
Selector,
Branch
};
/** Basic multiplexing: one unsigned selector per frame, one raw value per branch. */
struct Multiplexing {
MultiplexRole role = MultiplexRole::Unconditional;
std::uint64_t value = 0U;
};
#endif
+2
View File
@@ -13,6 +13,7 @@
#define SIGNAL_INFO_H
#include "value_descriptions.h"
#include "multiplexing.h"
#include <cstdint>
#include <string>
#include <vector>
@@ -21,6 +22,7 @@
* @brief Describes one signal inside a DBC frame.
*/
struct SignalInfo {
Multiplexing multiplexing;
std::string name; /**< Signal name. */
std::uint32_t startBit; /**< Start bit in DBC notation. */
std::uint32_t length; /**< Signal length in bits. */
+6
View File
@@ -72,3 +72,9 @@ Enable `CONFIG+=dbc_qt` in the root qmake invocation to build `dbc_qt` and
QAbstractItemModelTester, verify navigation across columns, invalid/foreign
indexes, metadata and exact VAL_ integers, and persistent-index invalidation
when reloading or clearing the tree. No GUI/display server is needed.
Basic multiplexing coverage includes selector declarations after branches,
Intel/Motorola branch overlap, signed active values and VAL_ lookup, unconditional
signals, inactive branches, missing selector/branch payloads, exact UINT64_MAX
selector matching, malformed runtime selectors, source locations, and rejection
of invalid markers, selector references/ranges, overlaps, and SG_MUL_VAL_.
+124 -1
View File
@@ -63,6 +63,8 @@ private:
}
}
private Q_SLOTS:
void multiplexing();
void multiplexingBoundaries();
void validFrames();
void invalidFrames();
void integration();
@@ -153,6 +155,127 @@ void DbcParserTest::integration() {
}
}
void DbcParserTest::multiplexing() {
Fixture fixture;
const std::string selector = "SG_ Mode M : 0|8@1+ (10,3) [0|255] \"\" ECU\n";
const std::string first = "SG_ First m1 : 8|8@1- (2,0) [-128|127] \"V\" ECU\n";
const std::string second = "SG_ Second m2 : 15|8@0+ (1,0) [0|255] \"\" ECU\n";
const std::string common = "SG_ Common : 16|8@1+ (1,0) [0|255] \"\" ECU\n";
fixture.Write("BO_ 1 Frame: 3 ECU\n" + first + second + common + selector +
"VAL_ 1 First -1 \"Negative\";\n");
const DbcDatabase parsed = DbcParser().ParseFile(fixture.path.string());
const DecodeDatabase runtime = DbcDecodeBuilder().Build(parsed);
CHECK(parsed.frames[0].signals[3].multiplexing.role == MultiplexRole::Selector,
"Selector declared after branches");
const std::unique_ptr<TreeNode> tree = DbcTreeBuilder().Build(parsed);
CHECK(tree->GetChild(0U)->GetChild(0U)->GetSignal()->multiplexing.value == 1U,
"Tree preserves multiplexing");
RawCanFrame raw;
raw.canId = 1U;
raw.data = {1U, 255U, 7U};
DecodedFrameValue result = DbcDecoder().Decode(runtime, raw);
CHECK(result.signals[0].valid && std::get<std::int64_t>(result.signals[0].rawValue) == -1 &&
result.signals[0].physicalValue == -2 && *result.signals[0].GetValueDescription() == "Negative",
"Active signed branch and VAL_ use exact raw selector");
CHECK(!result.signals[1].valid && result.signals[1].status == SignalDecodeStatus::Inactive &&
result.signals[1].GetValueDescription() == nullptr && result.signals[2].valid,
"Inactive branch and unconditional signal");
raw.data[0] = 2U;
result = DbcDecoder().Decode(runtime, raw);
CHECK(result.signals[0].status == SignalDecodeStatus::Inactive && result.signals[1].valid &&
std::get<std::uint64_t>(result.signals[1].rawValue) == 255U,
"Motorola branch becomes active");
raw.data = {3U};
result = DbcDecoder().Decode(runtime, raw);
CHECK(result.signals[0].status == SignalDecodeStatus::Inactive &&
result.signals[1].status == SignalDecodeStatus::Inactive &&
result.signals[2].status == SignalDecodeStatus::InsufficientPayload,
"Unmapped selector leaves branches inactive even with missing bytes");
raw.data = {1U};
result = DbcDecoder().Decode(runtime, raw);
CHECK(result.signals[0].status == SignalDecodeStatus::InsufficientPayload,
"Active branch requires payload");
raw.data.clear();
result = DbcDecoder().Decode(runtime, raw);
CHECK(result.signals[0].status == SignalDecodeStatus::SelectorUnavailable &&
result.signals[3].status == SignalDecodeStatus::InsufficientPayload,
"Unavailable selector does not imply inactive branch");
const std::vector<std::string> invalid = {
first,
selector + "SG_ Other M : 16|8@1+ (1,0) [0|255] \"\" ECU\n",
"SG_ Mode M : 0|8@1- (1,0) [-128|127] \"\" ECU\n",
selector + "SG_ TooLarge m256 : 8|8@1+ (1,0) [0|255] \"\" ECU\n",
selector + first + "SG_ Same m1 : 15|8@0+ (1,0) [0|255] \"\" ECU\n",
selector + "SG_ Collision m1 : 0|8@1+ (1,0) [0|255] \"\" ECU\n",
selector + first + "SG_ Always : 8|8@1+ (1,0) [0|255] \"\" ECU\n",
"SG_MUL_VAL_ 1 First Mode 1-2;\n"
};
for (const std::string &signals : invalid) {
fixture.Write("BO_ 1 Frame: 3 ECU\n" + signals);
bool rejected = false;
try {
DbcParser().ParseFile(fixture.path.string());
} catch (const std::runtime_error &error) {
rejected = std::string(error.what()).find(fixture.path.string() + ":") == 0U;
}
CHECK(rejected, "Invalid multiplexing rejected with location");
}
for (const std::string marker : {"m", "m-1", "m+1", "m1M", "m18446744073709551616", "M m1"})
RejectSignal(fixture, "SG_ Bad " + marker + " : 8|8@1+ (1,0) [0|255] \"\" ECU");
}
void DbcParserTest::multiplexingBoundaries() {
Fixture fixture;
fixture.Write("BO_ 1 Frame: 9 ECU\n"
"SG_ Branch m18446744073709551615 : 64|8@1+ (1,0) [0|255] \"\" ECU\n"
"SG_ Mode M : 7|64@0+ (0,5) [0|1] \"\" ECU\n");
const DbcDatabase parsed = DbcParser().ParseFile(fixture.path.string());
DecodeDatabase runtime = DbcDecodeBuilder().Build(parsed);
CHECK(runtime.frames[0].signals[0].multiplexing.value == std::numeric_limits<std::uint64_t>::max(),
"Exact uint64 branch metadata");
RawCanFrame raw;
raw.canId = 1U;
raw.data = std::vector<std::uint8_t>(9U, 255U);
DecodedFrameValue result = DbcDecoder().Decode(runtime, raw);
CHECK(result.signals[0].valid && result.signals[1].valid &&
std::get<std::uint64_t>(result.signals[1].rawValue) == std::numeric_limits<std::uint64_t>::max(),
"Motorola uint64 selector compares raw value before zero scaling");
raw.data[7] = 254U;
result = DbcDecoder().Decode(runtime, raw);
CHECK(result.signals[0].status == SignalDecodeStatus::Inactive,
"Adjacent large integers remain distinct");
runtime.frames[0].signals[1].length = 0U;
result = DbcDecoder().Decode(runtime, raw);
CHECK(result.signals[0].status == SignalDecodeStatus::InvalidDefinition &&
result.signals[1].status == SignalDecodeStatus::InvalidDefinition,
"Malformed runtime selector distinguished from payload errors");
runtime.frames[0].signals.pop_back();
result = DbcDecoder().Decode(runtime, raw);
CHECK(result.signals[0].status == SignalDecodeStatus::InvalidDefinition,
"Missing runtime selector");
fixture.Write("BO_ 1 Frame: 2 ECU\n\n"
"SG_ Branch m2 : 8|8@1+ (1,0) [0|255] \"\" ECU\n"
"SG_ Mode M : 0|1@1+ (1,0) [0|1] \"\" ECU\n");
bool rejected = false;
try {
DbcParser().ParseFile(fixture.path.string());
} catch (const std::runtime_error &error) {
rejected = std::string(error.what()).find(fixture.path.string() + ":3: ") == 0U;
}
CHECK(rejected, "Deferred validation reports branch source line");
fixture.Write("BO_ 1 Frame: 2 ECU\n"
"SG_ A : 8|8@1+ (1,0) [0|255] \"\" ECU\n"
"SG_ B : 15|8@0+ (1,0) [0|255] \"\" ECU\n");
rejected = false;
try {
DbcParser().ParseFile(fixture.path.string());
} catch (const std::runtime_error &) {
rejected = true;
}
CHECK(rejected, "Ordinary overlapping signals rejected");
}
void DbcParserTest::invalidSignals() {
Fixture fixture;
const std::string ordinary = SignalLine("0", "8", "1+", "1", "0", "0", "255");
@@ -172,7 +295,7 @@ void DbcParserTest::invalidSignals() {
RejectSignal(fixture, SignalLine("0", "8", "1+", "1", "0", "0", value));
}
RejectSignal(fixture, SignalLine("0", "8", "1+", "1", "0", "2", "1"));
for (const std::string name: {"", "Two Names", "Value M", "Value m1", "Value m1M"}) {
for (const std::string name: {"", "Two Names", "Value m1M"}) {
std::string line = ordinary;
line.replace(4U, 5U, name);
RejectSignal(fixture, line);