Closes #10
This commit is contained in:
1 parent
a533d9aa00
commit
a0938cb29c
14 files changed
+375
-13
No files matched your search
@@ -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 \
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -64,6 +64,7 @@ DbcDatabase DbcParser::ParseFile(const std::string &filePath) const {
|
||||
}
|
||||
|
||||
context.ResolveValueDescriptions(filePath);
|
||||
context.ValidateMultiplexing(filePath);
|
||||
return database;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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:
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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
|
||||
@@ -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. */
|
||||
|
||||
Reference in new issue
Block a user