528 lines
21 KiB
C++
528 lines
21 KiB
C++
// SPDX-License-Identifier: MIT
|
|
#include "dbc_statement_handler.h"
|
|
#include "text_utils.h"
|
|
#include "signal_layout.h"
|
|
#include <charconv>
|
|
#include <cmath>
|
|
#include <limits>
|
|
#include <regex>
|
|
#include <sstream>
|
|
#include <stdexcept>
|
|
|
|
namespace {
|
|
std::uint32_t ParseDecimal(const std::string &text,
|
|
std::uint32_t maximum,
|
|
const char *field) {
|
|
std::uint32_t value = 0U;
|
|
const std::from_chars_result parsed = std::from_chars(text.data(), text.data() + text.size(), value);
|
|
if (text.empty() || parsed.ec != std::errc() ||
|
|
parsed.ptr != text.data() + text.size() || value > maximum)
|
|
throw std::runtime_error(std::string("Invalid ") + field + ": " + text);
|
|
return value;
|
|
}
|
|
|
|
double ParsePhysicalValue(const std::string &text, const char *field) {
|
|
static const std::regex number(
|
|
R"(^[+-]?(?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+)(?:[eE][+-]?[0-9]+)?$)");
|
|
if (!std::regex_match(text, number))
|
|
throw std::runtime_error(std::string("Invalid ") + field + ": " + text);
|
|
std::size_t consumed = 0U;
|
|
double value;
|
|
try {
|
|
value = std::stod(text, &consumed);
|
|
} catch (const std::exception &) {
|
|
throw std::runtime_error(std::string("Invalid ") + field + ": " + text);
|
|
}
|
|
if (consumed != text.size() || !std::isfinite(value))
|
|
throw std::runtime_error(std::string("Invalid ") + field + ": " + text);
|
|
return value;
|
|
}
|
|
|
|
Multiplexing ParseMultiplexingMarker(const std::string &marker) {
|
|
Multiplexing result;
|
|
if (marker == "M")
|
|
result.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(), 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;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
RawSignalValue ParseValueKey(const std::string &number) {
|
|
RawSignalValue key;
|
|
if (!number.empty() && number[0] == '-') {
|
|
std::int64_t value = 0;
|
|
const std::from_chars_result parsed =
|
|
std::from_chars(number.data(), number.data() + number.size(), value);
|
|
if (parsed.ec != std::errc() || parsed.ptr != number.data() + number.size())
|
|
throw std::runtime_error("Invalid VAL_ integer: " + number);
|
|
key = NormalizeValueKey(RawSignalValue(value));
|
|
} else {
|
|
std::uint64_t value = 0;
|
|
const std::from_chars_result parsed =
|
|
std::from_chars(number.data(), number.data() + number.size(), value);
|
|
if (number.empty() || parsed.ec != std::errc() || parsed.ptr != number.data() + number.size())
|
|
throw std::runtime_error("Invalid VAL_ integer: " + number);
|
|
key = value;
|
|
}
|
|
return key;
|
|
}
|
|
|
|
std::string UnescapeDescription(const std::string "ed) {
|
|
std::string description;
|
|
for (std::size_t i = 0; i < quoted.size(); ++i) {
|
|
if (quoted[i] == '\\')
|
|
++i;
|
|
description += quoted[i];
|
|
}
|
|
return description;
|
|
}
|
|
|
|
FrameInfo *FindUniqueFrame(DbcDatabase &database, std::uint32_t canId, bool isExtended) {
|
|
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.");
|
|
frame = &candidate;
|
|
}
|
|
}
|
|
if (!frame)
|
|
throw std::runtime_error("Unresolved VAL_ frame reference.");
|
|
return frame;
|
|
}
|
|
|
|
SignalInfo *FindUniqueSignal(FrameInfo &frame, const std::string &signalName) {
|
|
SignalInfo *signal = nullptr;
|
|
for (SignalInfo &candidate : frame.signals) {
|
|
if (candidate.name == signalName) {
|
|
if (signal)
|
|
throw std::runtime_error("Ambiguous VAL_ signal reference: " + signalName);
|
|
signal = &candidate;
|
|
}
|
|
}
|
|
if (!signal)
|
|
throw std::runtime_error("Unresolved VAL_ signal reference: " + signalName);
|
|
return signal;
|
|
}
|
|
|
|
std::uint64_t NextSignalBit(std::uint64_t bit, bool isLittleEndian) {
|
|
if (isLittleEndian)
|
|
return bit + 1U;
|
|
// From bit 0, Motorola continues at bit 7 of the next byte (0 -> 15).
|
|
return bit % 8U == 0U ? bit + 15U : bit - 1U;
|
|
}
|
|
|
|
FrameInfo *FindFrameById(DbcDatabase &, std::uint32_t, bool);
|
|
SignalInfo *FindSignalByName(FrameInfo &, const std::string &);
|
|
} // namespace
|
|
|
|
namespace {
|
|
std::vector<std::string> SplitReceivers(const std::string &text) {
|
|
std::vector<std::string> receivers;
|
|
std::string token;
|
|
std::istringstream stream(text);
|
|
|
|
while (std::getline(stream, token, ',')) {
|
|
token = dbc_detail::TrimText(token);
|
|
if (!token.empty())
|
|
receivers.push_back(token);
|
|
}
|
|
|
|
return receivers;
|
|
}
|
|
|
|
void NormalizeCanId(std::uint32_t rawCanId, std::uint32_t &normalizedCanId, bool &isExtended) {
|
|
/*
|
|
* DBC commonly stores extended identifiers with bit 31 set.
|
|
* Example:
|
|
* raw id = 0x80000000 | actual_29_bit_id
|
|
*/
|
|
if ((rawCanId & 0x80000000U) != 0U) {
|
|
isExtended = true;
|
|
normalizedCanId = (rawCanId & 0x1FFFFFFFU);
|
|
} else {
|
|
isExtended = (rawCanId > 0x7FFU);
|
|
normalizedCanId = rawCanId;
|
|
}
|
|
}
|
|
|
|
std::uint32_t TryExtractPgn(std::uint32_t canId, bool isExtended, bool &hasPgn) {
|
|
hasPgn = false;
|
|
|
|
if (!isExtended)
|
|
return 0U;
|
|
|
|
if ((canId & 0x1FFFFFFFU) != canId)
|
|
return 0U;
|
|
|
|
const std::uint32_t pf = (canId >> 16U) & 0xFFU;
|
|
const std::uint32_t ps = (canId >> 8U) & 0xFFU;
|
|
const std::uint32_t dp = (canId >> 24U) & 0x01U;
|
|
|
|
std::uint32_t pgn = 0U;
|
|
|
|
if (pf < 240U)
|
|
pgn = (dp << 16U) | (pf << 8U);
|
|
else
|
|
pgn = (dp << 16U) | (pf << 8U) | ps;
|
|
|
|
hasPgn = true;
|
|
return pgn;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
FrameInfo DbcFrameHandler::ParseFrameLine(const std::string &line) {
|
|
/*
|
|
* Example:
|
|
* BO_ 256 EngineData: 8 EEC1
|
|
*/
|
|
|
|
std::istringstream stream(line);
|
|
std::string keyword;
|
|
std::string idText;
|
|
if (!(stream >> keyword >> idText) || keyword != "BO_")
|
|
throw std::runtime_error("Missing CAN ID in frame definition.");
|
|
|
|
const std::uint32_t rawCanId = ParseDecimal(
|
|
idText, std::numeric_limits<std::uint32_t>::max(), "CAN ID");
|
|
// Only bit 31 may accompany the actual 29-bit identifier.
|
|
if ((rawCanId & 0x60000000U) != 0U)
|
|
throw std::runtime_error("CAN ID exceeds the 29-bit identifier range.");
|
|
|
|
std::string nameText;
|
|
if (!std::getline(stream, nameText, ':') || stream.eof())
|
|
throw std::runtime_error("Missing ':' in frame definition.");
|
|
std::istringstream nameStream(nameText);
|
|
FrameInfo frame;
|
|
std::string extra;
|
|
if (!(nameStream >> frame.name) || (nameStream >> extra))
|
|
throw std::runtime_error("Missing or invalid frame name.");
|
|
|
|
std::string lengthText;
|
|
if (!(stream >> lengthText))
|
|
throw std::runtime_error("Missing payload length in frame definition.");
|
|
frame.dlc = static_cast<std::uint8_t>(ParseDecimal(
|
|
lengthText, std::numeric_limits<std::uint8_t>::max(), "payload length"));
|
|
if (!(stream >> frame.transmitter))
|
|
throw std::runtime_error("Missing transmitter in frame definition.");
|
|
if (stream >> extra)
|
|
throw std::runtime_error("Unexpected trailing field in frame definition: " + extra);
|
|
|
|
NormalizeCanId(rawCanId, frame.canId, frame.isExtended);
|
|
frame.pgn = TryExtractPgn(frame.canId, frame.isExtended, frame.hasPgn);
|
|
|
|
return frame;
|
|
}
|
|
|
|
SignalInfo DbcSignalHandler::ParseSignalLine(const std::string &line) {
|
|
// Match the complete statement before converting any numeric fields.
|
|
enum Field {
|
|
NameAndMarker = 1, StartBit, BitLength, ByteOrder, Signedness,
|
|
Factor, Offset, Minimum, Maximum, Unit, Receivers
|
|
};
|
|
static const std::regex syntax(
|
|
// Name and optional multiplexing marker.
|
|
R"dbc(^SG_[ \t]+([^:]+):[ \t]*)dbc"
|
|
// Bit position, length, byte order and signedness.
|
|
R"dbc(([^| \t]+)[ \t]*\|[ \t]*([^@ \t]+)[ \t]*@[ \t]*([01])([+-])[ \t]*)dbc"
|
|
// Scaling factor and offset.
|
|
R"dbc(\([ \t]*([^, \t]+)[ \t]*,[ \t]*([^ )\t]+)[ \t]*\)[ \t]*)dbc"
|
|
// Physical range.
|
|
R"dbc(\[[ \t]*([^| \t]+)[ \t]*\|[ \t]*([^\] \t]+)[ \t]*\][ \t]*)dbc"
|
|
// Unit and receivers.
|
|
R"dbc("([^"\r\n]*)"[ \t]+([^ \t,]+(?:[ \t]*,[ \t]*[^ \t,]+)*)[ \t]*$)dbc");
|
|
std::smatch fields;
|
|
if (!std::regex_match(line, fields, syntax))
|
|
throw std::runtime_error("Malformed or unsupported SG_ signal definition.");
|
|
|
|
SignalInfo signal;
|
|
std::istringstream nameFields(fields[NameAndMarker].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.");
|
|
signal.multiplexing = ParseMultiplexingMarker(marker);
|
|
|
|
signal.startBit = ParseDecimal(fields[StartBit].str(),
|
|
std::numeric_limits<std::uint32_t>::max(), "signal start bit");
|
|
signal.length = ParseDecimal(fields[BitLength].str(), 64U, "signal length");
|
|
if (signal.length == 0U)
|
|
throw std::runtime_error("Signal length must be within 1..64 bits.");
|
|
signal.isLittleEndian = fields[ByteOrder].str() == "1";
|
|
signal.isSigned = fields[Signedness].str() == "-";
|
|
|
|
signal.factor = ParsePhysicalValue(fields[Factor].str(), "signal factor");
|
|
signal.offset = ParsePhysicalValue(fields[Offset].str(), "signal offset");
|
|
signal.minimum = ParsePhysicalValue(fields[Minimum].str(), "signal minimum");
|
|
signal.maximum = ParsePhysicalValue(fields[Maximum].str(), "signal maximum");
|
|
if (signal.minimum > signal.maximum)
|
|
throw std::runtime_error("Signal minimum exceeds maximum.");
|
|
signal.unit = fields[Unit].str();
|
|
signal.receivers = SplitReceivers(fields[Receivers].str());
|
|
return signal;
|
|
}
|
|
|
|
void DbcCommentHandler::ParseCommentLine(const std::string &line, DbcDatabase &database) {
|
|
/*
|
|
* Examples:
|
|
* CM_ BO_ 256 "Frame comment";
|
|
* CM_ SG_ 256 EngineSpeed "Signal comment";
|
|
*/
|
|
|
|
std::istringstream stream(line);
|
|
std::string token;
|
|
stream >> token;
|
|
|
|
if (token != "CM_")
|
|
return;
|
|
|
|
stream >> token;
|
|
if (token != "BO_" && token != "SG_")
|
|
return;
|
|
|
|
std::uint32_t rawCanId = 0U;
|
|
std::uint32_t canId = 0U;
|
|
bool isExtended = false;
|
|
std::string signalName;
|
|
stream >> rawCanId;
|
|
if (token == "SG_")
|
|
stream >> signalName;
|
|
NormalizeCanId(rawCanId, canId, isExtended);
|
|
|
|
const std::string::size_type quoteBegin = line.find('"');
|
|
const std::string::size_type quoteEnd = line.rfind('"');
|
|
if (quoteBegin == std::string::npos || quoteEnd == std::string::npos || quoteEnd <= quoteBegin)
|
|
return;
|
|
|
|
FrameInfo *frame = FindFrameById(database, canId, isExtended);
|
|
if (frame == nullptr)
|
|
return;
|
|
const std::string comment = line.substr(quoteBegin + 1U, quoteEnd - quoteBegin - 1U);
|
|
if (token == "BO_") {
|
|
frame->comment = comment;
|
|
return;
|
|
}
|
|
SignalInfo *signal = FindSignalByName(*frame, signalName);
|
|
if (signal != nullptr)
|
|
signal->comment = comment;
|
|
}
|
|
|
|
namespace {
|
|
FrameInfo *FindFrameById(DbcDatabase &database,
|
|
std::uint32_t canId,
|
|
bool isExtended) {
|
|
for (FrameInfo &frame : database.frames) {
|
|
if (frame.canId == canId && frame.isExtended == isExtended)
|
|
return &frame;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
SignalInfo *FindSignalByName(FrameInfo &frame, const std::string &signalName) {
|
|
for (SignalInfo &signal : frame.signals) {
|
|
if (signal.name == signalName)
|
|
return &signal;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
}// namespace
|
|
|
|
void DbcFrameHandler::Handle(const std::string &line, DbcParseContext &context) const {
|
|
FrameInfo frame = ParseFrameLine(line);
|
|
if (FindFrameById(context.database, frame.canId, frame.isExtended) != nullptr)
|
|
throw std::runtime_error("Duplicate " + std::string(frame.isExtended ? "extended" : "standard") +
|
|
" frame CAN ID: " + std::to_string(frame.canId));
|
|
context.database.frames.push_back(std::move(frame));
|
|
context.currentFrame = &context.database.frames.back();
|
|
context.signalLines.emplace_back();
|
|
}
|
|
|
|
void DbcSignalHandler::Handle(const std::string &line, DbcParseContext &context) const {
|
|
if (context.currentFrame == nullptr)
|
|
throw std::runtime_error("Signal found before any frame definition.");
|
|
SignalInfo signal = ParseSignalLine(line);
|
|
if (FindSignalByName(*context.currentFrame, signal.name) != nullptr)
|
|
throw std::runtime_error("Duplicate signal name '" + signal.name +
|
|
"' in frame '" + context.currentFrame->name + "'.");
|
|
if (!IsValidSignalLayout(signal.startBit, signal.length, signal.isLittleEndian,
|
|
context.currentFrame->dlc))
|
|
throw std::runtime_error("Signal exceeds the frame payload length.");
|
|
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 {
|
|
ParseCommentLine(line, context.database);
|
|
}
|
|
|
|
DbcStatementDispatcher::DbcStatementDispatcher() {
|
|
RegisterType<DbcHeaderHandler>("VERSION");
|
|
RegisterType<DbcHeaderHandler>("NS_");
|
|
RegisterType<DbcHeaderHandler>("NS_:");
|
|
RegisterType<DbcHeaderHandler>("BS_");
|
|
RegisterType<DbcHeaderHandler>("BS_:");
|
|
RegisterType<DbcHeaderHandler>("BU_");
|
|
RegisterType<DbcHeaderHandler>("BU_:");
|
|
RegisterType<DbcUnsupportedMultiplexingHandler>("SG_MUL_VAL_");
|
|
RegisterType<DbcFrameHandler>("BO_");
|
|
RegisterType<DbcSignalHandler>("SG_");
|
|
RegisterType<DbcCommentHandler>("CM_");
|
|
RegisterType<DbcValueDescriptionHandler>("VAL_");
|
|
}
|
|
|
|
void DbcHeaderHandler::Handle(const std::string &, DbcParseContext &) const {}
|
|
|
|
bool DbcStatementDispatcher::Dispatch(const std::string &line,
|
|
DbcParseContext &context) const {
|
|
std::istringstream stream(line);
|
|
std::string keyword;
|
|
stream >> keyword;
|
|
const HandlerMap::const_iterator handler = m_handlers.find(keyword);
|
|
if (handler == m_handlers.end())
|
|
return false;
|
|
handler->second->Handle(line, context);
|
|
return true;
|
|
}
|
|
|
|
void DbcValueDescriptionHandler::Handle(const std::string &line, DbcParseContext &context) const {
|
|
static const std::regex header(R"(^VAL_[ \t]+([^ \t]+)[ \t]+([^ \t;]+)[ \t]+(.*)$)");
|
|
static const std::regex pair(R"dbc(^([^ \t]+)[ \t]+"((?:[^"\\]|\\["\\])*)")dbc");
|
|
std::smatch fields;
|
|
if (!std::regex_match(line, fields, header))
|
|
throw std::runtime_error("Malformed or unsupported VAL_ statement.");
|
|
const std::uint32_t rawId = ParseDecimal(fields[1].str(), std::numeric_limits<std::uint32_t>::max(), "VAL_ CAN ID");
|
|
if ((rawId & 0x60000000U) != 0U)
|
|
throw std::runtime_error("VAL_ CAN ID exceeds the 29-bit identifier range.");
|
|
PendingValueDescriptions pending;
|
|
NormalizeCanId(rawId, pending.canId, pending.isExtended);
|
|
pending.signalName = fields[2].str();
|
|
pending.lineNumber = context.lineNumber;
|
|
std::string rest = fields[3].str();
|
|
while (rest != ";") {
|
|
if (!std::regex_search(rest, fields, pair))
|
|
throw std::runtime_error("Malformed VAL_ value/description pair or missing semicolon.");
|
|
const std::string number = fields[1].str();
|
|
const RawSignalValue key = ParseValueKey(number);
|
|
const std::string description = UnescapeDescription(fields[2].str());
|
|
if (!pending.descriptions.emplace(key, description).second)
|
|
throw std::runtime_error("Duplicate VAL_ value key: " + number);
|
|
const std::size_t consumed = static_cast<std::size_t>(fields.length());
|
|
rest.erase(0, consumed);
|
|
if (!rest.empty() && rest[0] != ';' && rest[0] != ' ' && rest[0] != '\t')
|
|
throw std::runtime_error("Missing whitespace between VAL_ pairs.");
|
|
rest = dbc_detail::TrimText(rest);
|
|
}
|
|
if (pending.descriptions.empty())
|
|
throw std::runtime_error("VAL_ requires at least one value/description pair.");
|
|
context.pendingValues.push_back(std::move(pending));
|
|
}
|
|
|
|
void DbcParseContext::ResolveValueDescriptions(const std::string &filePath) {
|
|
for (const PendingValueDescriptions &pending: pendingValues) {
|
|
try {
|
|
FrameInfo *frame = FindUniqueFrame(database, pending.canId, pending.isExtended);
|
|
SignalInfo *signal = FindUniqueSignal(*frame, pending.signalName);
|
|
for (const ValueDescriptions::value_type &entry: pending.descriptions) {
|
|
if (!signal->valueDescriptions.insert(entry).second)
|
|
throw std::runtime_error("Duplicate VAL_ value key for signal: " + pending.signalName);
|
|
}
|
|
} catch (const std::exception &error) {
|
|
throw DbcDiagnosticError(DbcDiagnosticCode::InvalidValueDescription,
|
|
filePath, pending.lineNumber, error.what());
|
|
}
|
|
}
|
|
}
|
|
|
|
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);
|
|
bit = NextSignalBit(bit, signal.isLittleEndian);
|
|
}
|
|
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);
|
|
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 = 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.";
|
|
}
|
|
if (!error.empty())
|
|
throw DbcDiagnosticError(DbcDiagnosticCode::InvalidMultiplexing,
|
|
filePath, signalLines[frameIndex][index], error);
|
|
}
|
|
}
|
|
}
|