This commit is contained in:
deeaitch committed 2026-10-23 20:28:55 -04:00
1 parent a702cffd93
commit 8d06165979
18 files changed
+611 -215

No files matched your search

+1
View File
@@ -15,6 +15,7 @@ HEADERS += \
$$PWD/definition_key.h \
$$PWD/dbc_diagnostic.h \
$$PWD/multiplexing.h \
$$PWD/multiplex_graph.h \
$$PWD/dbc_database.h \
$$PWD/dbc_decode_builder.h \
$$PWD/dbc_decoder.h \
+29 -36
View File
@@ -81,12 +81,14 @@ DecodedFrameValue DbcDecoder::DecodeImpl(const DecodeDatabase &database,
? FrameDecodeStatus::InsufficientPayload
: FrameDecodeStatus::Valid;
const SelectorState selector = ReadSelector(*definition, frame);
const dbc_detail::MultiplexGraph &graph = database.GetMultiplexGraph(result.key);
const std::size_t count = selection != nullptr ? selected.size() : definition->signals.size();
result.signals.reserve(count);
for (std::size_t index = 0U; index < count; ++index) {
const DecodeSignal &signal = selection != nullptr ? *selected[index] : definition->signals[index];
DecodedSignalValue decoded = DecodeOneSignal(signal, definition->dlc, frame, selector);
const std::size_t signalIndex = static_cast<std::size_t>(&signal - definition->signals.data());
DecodedSignalValue decoded = DecodeOneSignal(signal, definition->dlc, frame,
ActivationStatus(signalIndex, *definition, frame, graph));
if (!decoded.valid && decoded.status != SignalDecodeStatus::Inactive &&
result.status == FrameDecodeStatus::Valid)
result.status = FrameDecodeStatus::SignalErrors;
@@ -103,56 +105,47 @@ bool DbcDecoder::IsValidDefinition(const DecodeSignal &signal, std::uint8_t dlc)
(signal.valueType != ValueType::Unsigned && signal.valueType != ValueType::Signed) ||
(signal.multiplexing.role != MultiplexRole::Unconditional &&
signal.multiplexing.role != MultiplexRole::Selector &&
signal.multiplexing.role != MultiplexRole::Branch))
signal.multiplexing.role != MultiplexRole::Branch &&
signal.multiplexing.role != MultiplexRole::BranchSelector))
return false;
return IsValidSignalLayout(signal.startBit, signal.length,
signal.byteOrder == ByteOrder::Intel, dlc);
}
DbcDecoder::SelectorState DbcDecoder::ReadSelector(const DecodeFrame &definition,
const RawCanFrame &frame) {
SelectorState selector;
std::size_t count = 0U;
for (const DecodeSignal &signal: definition.signals) {
if (signal.multiplexing.role == MultiplexRole::Selector) {
selector.definition = &signal;
++count;
}
SignalDecodeStatus DbcDecoder::ActivationStatus(std::size_t index,
const DecodeFrame &definition,
const RawCanFrame &frame,
const dbc_detail::MultiplexGraph &graph) {
// Inspect ancestors from the root so inactive paths never require child bytes.
std::vector<std::size_t> path;
while (graph.parents[index] != definition.signals.size()) {
path.push_back(index);
index = graph.parents[index];
}
if (count != 1U)
return selector;
const DecodeSignal &signal = *selector.definition;
selector.valid = signal.valueType == ValueType::Unsigned &&
IsValidDefinition(signal, definition.dlc);
selector.available = selector.valid && ExtractUnsigned(frame.data, signal, selector.value);
return selector;
for (std::vector<std::size_t>::const_reverse_iterator it = path.rbegin(); it != path.rend(); ++it) {
const DecodeSignal &selector = definition.signals[graph.parents[*it]];
std::uint64_t value = 0U;
if (!ExtractUnsigned(frame.data, selector, value))
return SignalDecodeStatus::SelectorUnavailable;
if (!graph.Matches(*it, value))
return SignalDecodeStatus::Inactive;
}
return SignalDecodeStatus::Valid;
}
DecodedSignalValue DbcDecoder::DecodeOneSignal(const DecodeSignal &signal,
std::uint8_t dlc,
const RawCanFrame &frame,
const SelectorState &selector) {
SignalDecodeStatus activation) {
DecodedSignalValue decoded;
std::uint64_t unsignedValue = 0U;
if (!IsValidDefinition(signal, dlc) ||
(signal.multiplexing.role != MultiplexRole::Unconditional && !selector.valid)) {
if (!IsValidDefinition(signal, dlc))
return decoded;
if (activation != SignalDecodeStatus::Valid) {
decoded.status = activation;
return decoded;
}
if (signal.multiplexing.role == MultiplexRole::Branch) {
if (selector.definition->length < 64U && signal.multiplexing.value >= (std::uint64_t(1U) << selector.definition->length)) {
return decoded;
}
if (!selector.available) {
decoded.status = SignalDecodeStatus::SelectorUnavailable;
return decoded;
}
if (signal.multiplexing.value != selector.value) {
decoded.status = SignalDecodeStatus::Inactive;
return decoded;
}
}
if (!ExtractUnsigned(frame.data, signal, unsignedValue)) {
decoded.status = SignalDecodeStatus::InsufficientPayload;
+6 -9
View File
@@ -17,6 +17,7 @@
#include <vector>
#include "decode_database.h"
#include "multiplex_graph.h"
/**
* @brief Raw CAN frame used for runtime or trace decoding.
@@ -128,18 +129,14 @@ private:
static bool IsValidDefinition(const DecodeSignal &signal, std::uint8_t dlc);
struct SelectorState {
const DecodeSignal *definition = nullptr;
bool valid = false;
bool available = false;
std::uint64_t value = 0U;
};
static SelectorState ReadSelector(const DecodeFrame &definition, const RawCanFrame &frame);
static SignalDecodeStatus ActivationStatus(std::size_t index,
const DecodeFrame &definition,
const RawCanFrame &frame,
const dbc_detail::MultiplexGraph &graph);
static DecodedSignalValue DecodeOneSignal(const DecodeSignal &signal,
std::uint8_t dlc,
const RawCanFrame &frame,
const SelectorState &selector);
SignalDecodeStatus activation);
static bool ExtractUnsigned(const std::vector<std::uint8_t> &data,
const DecodeSignal &signal,
+2 -1
View File
@@ -80,7 +80,7 @@ namespace {
DbcDiagnosticCode StatementErrorCode(const std::string &keyword) {
if (keyword == "SG_MUL_VAL_")
return DbcDiagnosticCode::UnsupportedStatement;
return DbcDiagnosticCode::InvalidMultiplexing;
if (keyword == "VAL_")
return DbcDiagnosticCode::InvalidValueDescription;
return DbcDiagnosticCode::InvalidStatement;
@@ -173,6 +173,7 @@ DbcParseResult DbcParser::ParseFileWithDiagnostics(const std::string &filePath)
try {
context.ResolveComments(filePath);
context.ResolveValueDescriptions(filePath);
context.ResolveMultiplexing(filePath);
context.ValidateMultiplexing(filePath);
context.ResolveAttributes(filePath, result.diagnostics);
} catch (const DbcDiagnosticError &error) {
+2 -2
View File
@@ -29,7 +29,7 @@ struct DbcParserOptions {
* - SG_
* - CM_ BO_
* - CM_ SG_
* - basic M / mN multiplexing
* - basic and extended M / mN / mNM / SG_MUL_VAL_ multiplexing
* - VAL_ signal value descriptions
* - BA_DEF_, BA_DEF_DEF_, BA_ typed attributes, frame format and cycle time
*
@@ -37,7 +37,7 @@ struct DbcParserOptions {
* Attribute statements support multiline fields and quoted strings, ending at
* a semicolon outside quoted text. Diagnostics use the first physical line.
*
* Reports unsupported statements as warnings, except SG_MUL_VAL_ which is
* Reports unsupported statements as warnings. Invalid multiplexing is
* an error. VERSION, NS_, BS_, BU_ and NS_ symbol declarations stay quiet.
*/
class DbcParser {
+82 -56
View File
@@ -1,8 +1,9 @@
// SPDX-License-Identifier: MIT
#include "dbc_statement_handler.h"
#include "j1939.h"
#include "text_utils.h"
#include "multiplex_graph.h"
#include "signal_layout.h"
#include "text_utils.h"
#include <charconv>
#include <cmath>
#include <limits>
@@ -59,12 +60,13 @@ namespace {
else if (!marker.empty()) {
if (marker[0] != 'm')
throw std::runtime_error("Unsupported multiplexing marker.");
const std::string value = marker.substr(1U);
const bool nested = marker.back() == 'M';
const std::string value = marker.substr(1U, marker.size() - (nested ? 2U : 1U));
const std::from_chars_result parsed =
std::from_chars(value.data(), value.data() + value.size(), result.value);
if (value.empty() || parsed.ec != std::errc() || parsed.ptr != value.data() + value.size())
throw std::runtime_error("Invalid or unsupported multiplexing marker.");
result.role = MultiplexRole::Branch;
result.role = nested ? MultiplexRole::BranchSelector : MultiplexRole::Branch;
}
return result;
@@ -100,31 +102,31 @@ namespace {
return description;
}
FrameInfo *FindUniqueFrame(DbcDatabase &database, std::uint32_t canId, bool isExtended) {
FrameInfo *FindUniqueFrame(DbcDatabase &database, std::uint32_t canId, bool isExtended, const std::string &statement = "VAL_") {
FrameInfo *frame = nullptr;
for (FrameInfo &candidate : database.frames) {
if (candidate.canId == canId && candidate.isExtended == isExtended) {
if (frame)
throw std::runtime_error("Ambiguous VAL_ frame reference.");
throw std::runtime_error("Ambiguous " + statement + " frame reference.");
frame = &candidate;
}
}
if (!frame)
throw std::runtime_error("Unresolved VAL_ frame reference.");
throw std::runtime_error("Unresolved " + statement + " frame reference.");
return frame;
}
SignalInfo *FindUniqueSignal(FrameInfo &frame, const std::string &signalName) {
SignalInfo *FindUniqueSignal(FrameInfo &frame, const std::string &signalName, const std::string &statement = "VAL_") {
SignalInfo *signal = nullptr;
for (SignalInfo &candidate : frame.signals) {
if (candidate.name == signalName) {
if (signal)
throw std::runtime_error("Ambiguous VAL_ signal reference: " + signalName);
throw std::runtime_error("Ambiguous " + statement + " signal reference: " + signalName);
signal = &candidate;
}
}
if (!signal)
throw std::runtime_error("Unresolved VAL_ signal reference: " + signalName);
throw std::runtime_error("Unresolved " + statement + " signal reference: " + signalName);
return signal;
}
@@ -375,7 +377,7 @@ DbcStatementDispatcher::DbcStatementDispatcher() {
RegisterType<DbcHeaderHandler>("BS_:");
RegisterType<DbcHeaderHandler>("BU_");
RegisterType<DbcHeaderHandler>("BU_:");
RegisterType<DbcUnsupportedMultiplexingHandler>("SG_MUL_VAL_");
RegisterType<DbcMultiplexingHandler>("SG_MUL_VAL_");
RegisterType<DbcFrameHandler>("BO_");
RegisterType<DbcSignalHandler>("SG_");
RegisterType<DbcCommentHandler>("CM_");
@@ -448,8 +450,63 @@ 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.");
void DbcMultiplexingHandler::Handle(const std::string &line, DbcParseContext &context) const {
static const std::regex syntax(R"(^SG_MUL_VAL_[ \t]+([0-9]+)[ \t]+([^ \t;]+)[ \t]+([^ \t;]+)[ \t]+(.+);[ \t]*$)");
static const std::regex rangeSyntax(R"(^[ \t]*([0-9]+)[ \t]*-[ \t]*([0-9]+)[ \t]*$)");
std::smatch fields;
if (!std::regex_match(line, fields, syntax))
throw std::runtime_error("Malformed SG_MUL_VAL_ statement.");
PendingMultiplexing pending;
NormalizeCanId(ParseCanId(fields[1].str()), pending.canId, pending.isExtended);
pending.signalName = fields[2].str();
pending.selectorName = fields[3].str();
pending.lineNumber = context.lineNumber;
const std::string list = fields[4].str();
std::size_t begin = 0U;
for (;;) {
const std::size_t comma = list.find(',', begin);
const std::string item = list.substr(begin, comma == std::string::npos ? comma : comma - begin);
if (!std::regex_match(item, fields, rangeSyntax))
throw std::runtime_error("Malformed multiplex range.");
MultiplexRange range;
const std::string lower = fields[1].str();
const std::string upper = fields[2].str();
const std::from_chars_result low = std::from_chars(lower.data(), lower.data() + lower.size(), range.minimum);
const std::from_chars_result high = std::from_chars(upper.data(), upper.data() + upper.size(), range.maximum);
if (low.ec != std::errc() || high.ec != std::errc() || range.minimum > range.maximum)
throw std::runtime_error("Invalid multiplex range.");
pending.ranges.push_back(range);
if (comma == std::string::npos)
break;
begin = comma + 1U;
}
context.pendingMultiplexing.push_back(std::move(pending));
}
void DbcParseContext::ResolveMultiplexing(const std::string &filePath) {
for (const PendingMultiplexing &pending: pendingMultiplexing) {
try {
FrameInfo *frame = FindUniqueFrame(database, pending.canId, pending.isExtended, "SG_MUL_VAL_");
SignalInfo *signal = FindUniqueSignal(*frame, pending.signalName, "SG_MUL_VAL_");
SignalInfo *selector = FindUniqueSignal(*frame, pending.selectorName, "SG_MUL_VAL_");
if (!IsMultiplexSelector(selector->multiplexing.role) || selector->isSigned)
throw std::runtime_error("Referenced signal must be an unsigned multiplex selector.");
if (!signal->multiplexing.selector.empty() && signal->multiplexing.selector != pending.selectorName)
throw std::runtime_error("Multiple parent selectors for one signal are unsupported.");
for (const MultiplexRange &range: pending.ranges) {
if (selector->length < 64U && range.maximum >= (std::uint64_t(1U) << selector->length))
throw std::runtime_error("Branch value exceeds selector raw range.");
}
signal->multiplexing.selector = pending.selectorName;
signal->multiplexing.ranges.insert(signal->multiplexing.ranges.end(), pending.ranges.begin(), pending.ranges.end());
if (!IsMultiplexSelector(signal->multiplexing.role))
signal->multiplexing.role = MultiplexRole::Branch;
else
signal->multiplexing.role = MultiplexRole::BranchSelector;
} catch (const std::exception &error) {
throw DbcDiagnosticError(DbcDiagnosticCode::InvalidMultiplexing, filePath, pending.lineNumber, error.what());
}
}
}
namespace {
@@ -463,39 +520,6 @@ namespace {
return bits;
}
bool AreMutuallyExclusive(const SignalInfo &first, const SignalInfo &second) {
return first.multiplexing.role == MultiplexRole::Branch &&
second.multiplexing.role == MultiplexRole::Branch &&
first.multiplexing.value != second.multiplexing.value;
}
std::string ValidateBranch(const SignalInfo &signal, const SignalInfo *selector) {
if (signal.multiplexing.role != MultiplexRole::Branch)
return {};
if (selector == nullptr)
return "Multiplexed branch has no selector.";
if (selector->length < 64U && signal.multiplexing.value >= (std::uint64_t(1U) << selector->length))
return "Branch value exceeds selector raw range.";
return {};
}
const SignalInfo *FindSelector(const FrameInfo &frame,
const std::vector<std::size_t> &lines,
const std::string &filePath) {
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 DbcDiagnosticError(DbcDiagnosticCode::InvalidMultiplexing,
filePath, lines[index],
"Multiplexing requires one unsigned selector per frame.");
selector = &signal;
}
return selector;
}
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);
@@ -512,18 +536,20 @@ namespace {
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 = FindSelector(frame, signalLines[frameIndex], filePath);
for (std::size_t index = 0U; index < frame.signals.size(); ++index) {
const SignalInfo &signal = frame.signals[index];
std::string error = ValidateBranch(signal, selector);
for (std::size_t previous = 0U; previous < index && error.empty(); ++previous) {
const SignalInfo &other = frame.signals[previous];
if (!AreMutuallyExclusive(signal, other) && SignalsOverlap(signal, other))
error = "Signals overlap without mutually exclusive multiplexing conditions.";
try {
const dbc_detail::MultiplexGraph graph(frame.signals);
for (std::size_t index = 0U; index < frame.signals.size(); ++index) {
const SignalInfo &signal = frame.signals[index];
if (IsMultiplexSelector(signal.multiplexing.role) && signal.isSigned)
throw dbc_detail::MultiplexError(index, "Multiplex selectors must be unsigned.");
for (std::size_t previous = 0U; previous < index; ++previous) {
if (!graph.AreMutuallyExclusive(index, previous) && SignalsOverlap(signal, frame.signals[previous]))
throw dbc_detail::MultiplexError(index, "Signals overlap without mutually exclusive multiplexing conditions.");
}
}
if (!error.empty())
throw DbcDiagnosticError(DbcDiagnosticCode::InvalidMultiplexing,
filePath, signalLines[frameIndex][index], error);
} catch (const dbc_detail::MultiplexError &error) {
throw DbcDiagnosticError(DbcDiagnosticCode::InvalidMultiplexing, filePath,
signalLines[frameIndex][error.index], error.what());
}
}
}
+15 -2
View File
@@ -46,6 +46,16 @@ struct PendingAttribute {
std::size_t lineNumber; /**< One-based physical statement line. */
};
/** @brief Deferred extended multiplexing condition and source location. */
struct PendingMultiplexing {
std::uint32_t canId;
bool isExtended;
std::string signalName;
std::string selectorName;
std::vector<MultiplexRange> ranges;
std::size_t lineNumber;
};
/** Mutable state belonging to one file parse. Handlers do not retain it. */
struct DbcParseContext {
DbcDatabase &database; /**< Parsed metadata under construction. */
@@ -54,6 +64,9 @@ struct DbcParseContext {
std::size_t lineNumber = 0U; /**< Physical line of the current statement. */
std::vector<PendingValueDescriptions> pendingValues = {}; /**< Unresolved raw value descriptions. */
std::vector<PendingMultiplexing> pendingMultiplexing = {};
void ResolveMultiplexing(const std::string &filePath);
std::vector<std::vector<std::size_t>> signalLines = {}; /**< Signal definition lines grouped by frame. */
/** @brief Validate selectors, branch ranges, and signal overlap. */
void ValidateMultiplexing(const std::string &filePath) const;
@@ -114,8 +127,8 @@ public:
void Handle(const std::string &line, DbcParseContext &context) const override;
};
/** @brief Reject unsupported extended multiplexing statements. */
class DbcUnsupportedMultiplexingHandler : public DbcStatementHandler {
/** @brief Parse extended multiplexing conditions for deferred resolution. */
class DbcMultiplexingHandler : public DbcStatementHandler {
public:
void Handle(const std::string &line, DbcParseContext &context) const override;
};
+16 -20
View File
@@ -3,6 +3,7 @@
#include "decode_database.h"
#include "j1939.h"
#include "multiplex_graph.h"
#include "signal_layout.h"
#include <bitset>
#include <cmath>
@@ -21,7 +22,8 @@ namespace {
throw std::invalid_argument("Invalid signal value type: " + signal.name);
if (signal.multiplexing.role != MultiplexRole::Unconditional &&
signal.multiplexing.role != MultiplexRole::Selector &&
signal.multiplexing.role != MultiplexRole::Branch)
signal.multiplexing.role != MultiplexRole::Branch &&
signal.multiplexing.role != MultiplexRole::BranchSelector)
throw std::invalid_argument("Invalid multiplexing role: " + signal.name);
if (!IsValidSignalLayout(signal.startBit, signal.length,
signal.byteOrder == ByteOrder::Intel, dlc))
@@ -45,13 +47,7 @@ namespace {
return bits;
}
bool AreMutuallyExclusive(const DecodeSignal &first, const DecodeSignal &second) {
return first.multiplexing.role == MultiplexRole::Branch &&
second.multiplexing.role == MultiplexRole::Branch &&
first.multiplexing.value != second.multiplexing.value;
}
void ValidateFrame(const DecodeFrame &frame) {
dbc_detail::MultiplexGraph ValidateFrame(const DecodeFrame &frame) {
const std::uint32_t maximumId = frame.isExtended ? 0x1FFFFFFFU : 0x7FFU;
if (frame.canId > maximumId)
throw std::invalid_argument("Invalid normalized CAN ID: " + std::to_string(frame.canId));
@@ -60,33 +56,28 @@ namespace {
throw std::invalid_argument("Invalid J1939 PGN metadata: " + frame.name);
std::unordered_set<std::string> names;
const DecodeSignal *selector = nullptr;
for (const DecodeSignal &signal: frame.signals) {
if (!names.insert(signal.name).second)
throw std::invalid_argument("Duplicate runtime signal key: " + signal.name);
ValidateSignal(signal, frame.dlc);
if (signal.multiplexing.role == MultiplexRole::Selector) {
if (selector != nullptr || signal.valueType != ValueType::Unsigned)
throw std::invalid_argument("Multiplexing requires one unsigned selector per frame");
selector = &signal;
}
if (IsMultiplexSelector(signal.multiplexing.role) && signal.valueType != ValueType::Unsigned)
throw std::invalid_argument("Multiplex selectors must be unsigned.");
}
const dbc_detail::MultiplexGraph graph(frame.signals);
std::vector<SignalBits> layouts;
layouts.reserve(frame.signals.size());
for (std::size_t index = 0U; index < frame.signals.size(); ++index) {
const DecodeSignal &signal = frame.signals[index];
if (signal.multiplexing.role == MultiplexRole::Branch &&
(selector == nullptr ||
(selector->length < 64U && signal.multiplexing.value >= (std::uint64_t(1U) << selector->length))))
throw std::invalid_argument("Missing selector or branch value outside selector range: " + signal.name);
const SignalBits bits = GetSignalBits(signal);
for (std::size_t previous = 0U; previous < index; ++previous) {
if (!AreMutuallyExclusive(signal, frame.signals[previous]) && (bits & layouts[previous]).any())
if (!graph.AreMutuallyExclusive(index, previous) && (bits & layouts[previous]).any())
throw std::invalid_argument("Overlapping signals: " + signal.name);
}
layouts.push_back(bits);
}
return graph;
}
}// namespace
@@ -95,8 +86,9 @@ DecodeDatabase::Storage::Storage(const std::vector<DecodeFrame> &definitions)
// Validate the complete snapshot before creating its lookup index.
std::unordered_set<FrameKey, FrameKeyHasher> keys;
keys.reserve(frames.size());
multiplexGraphs.reserve(frames.size());
for (const DecodeFrame &frame: frames) {
ValidateFrame(frame);
multiplexGraphs.push_back(ValidateFrame(frame));
if (!keys.insert(frame.GetKey()).second)
throw std::invalid_argument("Duplicate runtime frame key");
}
@@ -153,3 +145,7 @@ std::shared_ptr<const DecodeSignal> DecodeDatabase::FindSignal(const SignalKey &
}
return nullptr;
}
const dbc_detail::MultiplexGraph &DecodeDatabase::GetMultiplexGraph(const FrameKey &key) const {
return storage->multiplexGraphs[storage->frameIndexByKey.at(key)];
}
+6 -1
View File
@@ -14,6 +14,7 @@
#include "attributes.h"
#include "definition_key.h"
#include "multiplex_graph.h"
#include "multiplexing.h"
#include "value_descriptions.h"
#include <cstdint>
@@ -42,7 +43,7 @@ enum class ValueType {
* @brief Runtime-ready signal definition.
*/
struct DecodeSignal {
Multiplexing multiplexing; /**< Selector role and branch value. */
Multiplexing multiplexing; /**< Selector role, parent, and raw branch ranges. */
std::string name; /**< Signal name. */
std::uint32_t startBit = 0U; /**< DBC start bit. */
std::uint32_t length = 0U; /**< Signal length in bits. */
@@ -118,7 +119,11 @@ public:
std::shared_ptr<const DecodeSignal> FindSignal(const SignalKey &key) const;
private:
friend class DbcDecoder;
const dbc_detail::MultiplexGraph &GetMultiplexGraph(const FrameKey &key) const;
struct Storage {
std::vector<dbc_detail::MultiplexGraph> multiplexGraphs;
std::vector<DecodeFrame> frames;
std::unordered_map<FrameKey, std::size_t, FrameKeyHasher> frameIndexByKey;
+101
View File
@@ -0,0 +1,101 @@
// SPDX-License-Identifier: MIT
#ifndef MULTIPLEX_GRAPH_H
#define MULTIPLEX_GRAPH_H
#include "multiplexing.h"
#include <cstddef>
#include <stdexcept>
namespace dbc_detail {
/** Validation failure associated with a signal in declaration order. */
class MultiplexError : public std::invalid_argument {
public:
MultiplexError(std::size_t signalIndex, const std::string &message)
: std::invalid_argument(message), index(signalIndex) {}
std::size_t index;
};
/** Resolved parent links shared by parser validation and runtime decoding. */
class MultiplexGraph {
public:
template<class Signal>
explicit MultiplexGraph(const std::vector<Signal> &signals)
: parents(signals.size(), signals.size()), ranges(signals.size()) {
const std::size_t count = signals.size();
for (std::size_t index = 0U; index < count; ++index) {
const Multiplexing &mux = signals[index].multiplexing;
if (mux.role != MultiplexRole::Unconditional && mux.role != MultiplexRole::Selector &&
mux.role != MultiplexRole::Branch && mux.role != MultiplexRole::BranchSelector)
throw MultiplexError(index, "Invalid multiplexing role.");
if (mux.selector.empty() != mux.ranges.empty())
throw MultiplexError(index, "Selector name and ranges must be supplied together.");
if (mux.selector.empty() && !IsMultiplexBranch(mux.role))
continue;
std::size_t parent = count;
for (std::size_t candidate = 0U; candidate < count; ++candidate) {
const Multiplexing &other = signals[candidate].multiplexing;
const bool matches = !mux.selector.empty()
? signals[candidate].name == mux.selector
: other.role == MultiplexRole::Selector && other.selector.empty();
if (!matches)
continue;
if (parent != count)
throw MultiplexError(index, "Ambiguous multiplex selector; use SG_MUL_VAL_.");
parent = candidate;
}
if (parent == count)
throw MultiplexError(index, "Multiplexed branch has no selector.");
if (!IsMultiplexSelector(signals[parent].multiplexing.role))
throw MultiplexError(index, "Referenced signal is not a multiplex selector.");
parents[index] = parent;
ranges[index] = mux.selector.empty() ? std::vector<MultiplexRange>{{mux.value, mux.value}} : mux.ranges;
for (const MultiplexRange &range: ranges[index]) {
if (range.minimum > range.maximum)
throw MultiplexError(index, "Reversed multiplex range.");
if (signals[parent].length < 64U && range.maximum >= (std::uint64_t(1U) << signals[parent].length))
throw MultiplexError(index, "Branch value exceeds selector raw range.");
}
}
for (std::size_t index = 0U; index < count; ++index) {
std::size_t current = index;
for (std::size_t depth = 0U; current != count; ++depth) {
if (depth >= count)
throw MultiplexError(index, "Cyclic multiplex selector dependency.");
current = parents[current];
}
}
}
bool Matches(std::size_t index, std::uint64_t value) const {
for (const MultiplexRange &range: ranges[index]) {
if (range.minimum <= value && value <= range.maximum)
return true;
}
return false;
}
bool AreMutuallyExclusive(std::size_t first, std::size_t second) const {
const std::size_t count = parents.size();
for (std::size_t left = first; parents[left] != count; left = parents[left]) {
for (std::size_t right = second; parents[right] != count; right = parents[right]) {
if (parents[left] != parents[right])
continue;
bool intersects = false;
for (const MultiplexRange &a: ranges[left]) {
for (const MultiplexRange &b: ranges[right]) {
if (a.minimum <= b.maximum && b.minimum <= a.maximum)
intersects = true;
}
}
if (!intersects)
return true;
}
}
return false;
}
std::vector<std::size_t> parents;
std::vector<std::vector<MultiplexRange>> ranges;
};
}// namespace dbc_detail
#endif
+25 -6
View File
@@ -3,18 +3,37 @@
#define MULTIPLEXING_H
#include <cstdint>
#include <string>
#include <vector>
/** @brief Unconditional signal, selector, or single-value branch. */
/** @brief Signal participation in multiplexing. */
enum class MultiplexRole {
Unconditional,
Selector,
Branch
Branch,
BranchSelector
};
/** Basic multiplexing: one unsigned selector per frame, one raw value per branch. */
struct Multiplexing {
MultiplexRole role = MultiplexRole::Unconditional; /**< Signal participation in multiplexing. */
std::uint64_t value = 0U; /**< Required raw selector value; used only by branches. */
/** @brief Inclusive raw selector range, without expanding individual values. */
struct MultiplexRange {
std::uint64_t minimum = 0U;
std::uint64_t maximum = 0U;
};
/** @brief Basic markers and optional explicit extended multiplexing condition. */
struct Multiplexing {
MultiplexRole role = MultiplexRole::Unconditional;
std::uint64_t value = 0U; /**< mN fallback when no explicit ranges are supplied. */
std::string selector; /**< Parent selector name within the same frame. */
std::vector<MultiplexRange> ranges; /**< Alternatives for the named selector; override mN. */
};
inline bool IsMultiplexSelector(MultiplexRole role) {
return role == MultiplexRole::Selector || role == MultiplexRole::BranchSelector;
}
inline bool IsMultiplexBranch(MultiplexRole role) {
return role == MultiplexRole::Branch || role == MultiplexRole::BranchSelector;
}
#endif
+1 -1
View File
@@ -23,7 +23,7 @@
* @brief Describes one signal inside a DBC frame.
*/
struct SignalInfo {
Multiplexing multiplexing; /**< Selector role and branch value. */
Multiplexing multiplexing; /**< Selector role, parent, and raw branch ranges. */
std::string name; /**< Signal name. */
std::uint32_t startBit = 0U; /**< Start bit in DBC notation. */
std::uint32_t length = 0U; /**< Signal length in bits. */