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

+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. */