Closes #29
This commit is contained in:
1 parent
a702cffd93
commit
8d06165979
18 files changed
+611
-215
No files matched your search
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user