This commit is contained in:
deeaitch committed 2026-10-07 18:50:11 -04:00
1 parent a0938cb29c
commit 3dd9230fca
21 files changed
+491 -450

No files matched your search

+1
View File
@@ -19,4 +19,5 @@ HEADERS += \
$$PWD/signal_info.h \ $$PWD/signal_info.h \
$$PWD/value_descriptions.h \ $$PWD/value_descriptions.h \
$$PWD/tree_node.h \ $$PWD/tree_node.h \
$$PWD/text_utils.h \
$$PWD/typefactory.h $$PWD/typefactory.h
+2 -4
View File
@@ -14,8 +14,7 @@
DecodeDatabase DbcDecodeBuilder::Build(const DbcDatabase &source) const { DecodeDatabase DbcDecodeBuilder::Build(const DbcDatabase &source) const {
DecodeDatabase result; DecodeDatabase result;
for (std::size_t frameIndex = 0U; frameIndex < source.frames.size(); ++frameIndex) { for (const FrameInfo &sourceFrame : source.frames) {
const FrameInfo &sourceFrame = source.frames[frameIndex];
DecodeFrame targetFrame; DecodeFrame targetFrame;
targetFrame.name = sourceFrame.name; targetFrame.name = sourceFrame.name;
@@ -27,8 +26,7 @@ DecodeDatabase DbcDecodeBuilder::Build(const DbcDatabase &source) const {
targetFrame.transmitter = sourceFrame.transmitter; targetFrame.transmitter = sourceFrame.transmitter;
targetFrame.comment = sourceFrame.comment; targetFrame.comment = sourceFrame.comment;
for (std::size_t signalIndex = 0U; signalIndex < sourceFrame.signals.size(); ++signalIndex) { for (const SignalInfo &sourceSignal : sourceFrame.signals) {
const SignalInfo &sourceSignal = sourceFrame.signals[signalIndex];
DecodeSignal targetSignal; DecodeSignal targetSignal;
targetSignal.multiplexing = sourceSignal.multiplexing; targetSignal.multiplexing = sourceSignal.multiplexing;
+70 -63
View File
@@ -39,73 +39,80 @@ DecodedFrameValue DbcDecoder::Decode(const DecodeDatabase &database,
result.definition = definition; result.definition = definition;
result.valid = true; result.valid = true;
const DecodeSignal *selector = nullptr; const SelectorState selector = ReadSelector(*definition, frame);
std::size_t selectorCount = 0U; for (const DecodeSignal &signal : definition->signals)
for (const DecodeSignal &signal : definition->signals) { result.signals.push_back(DecodeOneSignal(signal, frame, selector));
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;
std::uint64_t unsignedValue = 0U;
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.status = SignalDecodeStatus::InsufficientPayload;
result.signals.push_back(decoded);
continue;
}
if (signal.valueType == ValueType::Signed)
decoded.rawValue = SignExtend(unsignedValue, signal.length);
else
decoded.rawValue = unsignedValue;
double numericValue;
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);
}
return result; return result;
} }
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;
}
}
if (count != 1U)
return selector;
const DecodeSignal &signal = *selector.definition;
selector.valid = signal.valueType == ValueType::Unsigned &&
signal.length > 0U && signal.length <= 64U;
selector.available = selector.valid && ExtractUnsigned(frame.data, signal, selector.value);
return selector;
}
DecodedSignalValue DbcDecoder::DecodeOneSignal(const DecodeSignal &signal,
const RawCanFrame &frame,
const SelectorState &selector) {
DecodedSignalValue decoded;
std::uint64_t unsignedValue = 0U;
decoded.definition = &signal;
if (signal.length == 0U || signal.length > 64U ||
(signal.multiplexing.role != MultiplexRole::Unconditional && !selector.valid)) {
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;
return decoded;
}
if (signal.valueType == ValueType::Signed)
decoded.rawValue = SignExtend(unsignedValue, signal.length);
else
decoded.rawValue = unsignedValue;
double numericValue;
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;
return decoded;
}
bool DbcDecoder::ExtractUnsigned(const std::vector<std::uint8_t> &data, bool DbcDecoder::ExtractUnsigned(const std::vector<std::uint8_t> &data,
const DecodeSignal &signal, const DecodeSignal &signal,
std::uint64_t &value) { std::uint64_t &value) {
+25 -19
View File
@@ -22,13 +22,11 @@
* @brief Raw CAN frame used for runtime or trace decoding. * @brief Raw CAN frame used for runtime or trace decoding.
*/ */
struct RawCanFrame { struct RawCanFrame {
std::uint32_t canId; /**< Normalized CAN ID. */ std::uint32_t canId = 0U; /**< Normalized CAN ID. */
bool isExtended; /**< true for extended frame. */ bool isExtended = false; /**< true for extended frame. */
std::vector<std::uint8_t> data; /**< Payload bytes. */ std::vector<std::uint8_t> data; /**< Payload bytes. */
RawCanFrame() RawCanFrame() = default;
: canId(0U), isExtended(false), data() {
}
}; };
enum class SignalDecodeStatus { enum class SignalDecodeStatus {
@@ -43,10 +41,10 @@ enum class SignalDecodeStatus {
* @brief One decoded signal value. * @brief One decoded signal value.
*/ */
struct DecodedSignalValue { struct DecodedSignalValue {
const DecodeSignal *definition; /**< Signal definition. */ const DecodeSignal *definition = nullptr; /**< Signal definition. */
RawSignalValue rawValue; /**< Exact signed or unsigned raw value. */ RawSignalValue rawValue = std::uint64_t(0U); /**< Exact signed or unsigned raw value. */
double physicalValue; /**< Converted physical value. */ double physicalValue = 0.0; /**< Converted physical value. */
bool valid; /**< true if decoding succeeded. */ bool valid = false; /**< true if decoding succeeded. */
SignalDecodeStatus status = SignalDecodeStatus::InvalidDefinition; SignalDecodeStatus status = SignalDecodeStatus::InvalidDefinition;
/** Lookup using the raw integer; nullptr for invalid or unmapped values. */ /** Lookup using the raw integer; nullptr for invalid or unmapped values. */
@@ -54,22 +52,18 @@ struct DecodedSignalValue {
return valid && definition ? FindValueDescription(definition->valueDescriptions, rawValue) : nullptr; return valid && definition ? FindValueDescription(definition->valueDescriptions, rawValue) : nullptr;
} }
DecodedSignalValue() DecodedSignalValue() = default;
: definition(nullptr), rawValue(std::uint64_t(0U)), physicalValue(0.0), valid(false) {
}
}; };
/** /**
* @brief Fully decoded frame. * @brief Fully decoded frame.
*/ */
struct DecodedFrameValue { struct DecodedFrameValue {
const DecodeFrame *definition; /**< Frame definition. */ const DecodeFrame *definition = nullptr; /**< Frame definition. */
std::vector<DecodedSignalValue> signals; /**< Decoded signal values. */ std::vector<DecodedSignalValue> signals; /**< Decoded signal values. */
bool valid; /**< true if frame was matched. */ bool valid = false; /**< true if frame was matched. */
DecodedFrameValue() DecodedFrameValue() = default;
: definition(nullptr), signals(), valid(false) {
}
}; };
/** /**
@@ -98,6 +92,18 @@ public:
const RawCanFrame &frame) const; const RawCanFrame &frame) const;
private: private:
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 DecodedSignalValue DecodeOneSignal(const DecodeSignal &signal,
const RawCanFrame &frame,
const SelectorState &selector);
static bool ExtractUnsigned(const std::vector<std::uint8_t> &data, static bool ExtractUnsigned(const std::vector<std::uint8_t> &data,
const DecodeSignal &signal, const DecodeSignal &signal,
std::uint64_t &value); std::uint64_t &value);
+2 -27
View File
@@ -12,31 +12,10 @@
#include "dbc_parser.h" #include "dbc_parser.h"
#include "dbc_statement_handler.h" #include "dbc_statement_handler.h"
#include <cctype> #include "text_utils.h"
#include <fstream> #include <fstream>
#include <stdexcept> #include <stdexcept>
namespace {
/**
* @brief Remove leading and trailing spaces.
* @param text Input text.
* @return Trimmed text.
*/
std::string TrimText(const std::string &text) {
std::string::size_type begin = 0U;
while ((begin < text.size()) &&
std::isspace(static_cast<unsigned char>(text[begin])))
++begin;
std::string::size_type end = text.size();
while ((end > begin) &&
std::isspace(static_cast<unsigned char>(text[end - 1U])))
--end;
return text.substr(begin, end - begin);
}
}// namespace
DbcDatabase DbcParser::ParseFile(const std::string &filePath) const { DbcDatabase DbcParser::ParseFile(const std::string &filePath) const {
std::ifstream input(filePath.c_str()); std::ifstream input(filePath.c_str());
if (!input.is_open()) if (!input.is_open())
@@ -50,7 +29,7 @@ DbcDatabase DbcParser::ParseFile(const std::string &filePath) const {
std::string line; std::string line;
while (std::getline(input, line)) { while (std::getline(input, line)) {
++lineNumber; ++lineNumber;
line = Trim(line); line = dbc_detail::TrimText(line);
if (line.empty()) if (line.empty())
continue; continue;
@@ -67,7 +46,3 @@ DbcDatabase DbcParser::ParseFile(const std::string &filePath) const {
context.ValidateMultiplexing(filePath); context.ValidateMultiplexing(filePath);
return database; return database;
} }
std::string DbcParser::Trim(const std::string &text) {
return TrimText(text);
}
+1 -3
View File
@@ -24,11 +24,11 @@
* - SG_ * - SG_
* - CM_ BO_ * - CM_ BO_
* - CM_ SG_ * - CM_ SG_
* - basic M / mN multiplexing
* - VAL_ signal value descriptions * - VAL_ signal value descriptions
* *
* Ignores: * Ignores:
* - attributes * - attributes
* - multiplexing
* - named VAL_TABLE_ tables * - named VAL_TABLE_ tables
*/ */
class DbcParser { class DbcParser {
@@ -42,8 +42,6 @@ public:
*/ */
DbcDatabase ParseFile(const std::string &filePath) const; DbcDatabase ParseFile(const std::string &filePath) const;
private:
static std::string Trim(const std::string &text);
}; };
#endif /* DBC_PARSER_H */ #endif /* DBC_PARSER_H */
+193 -176
View File
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
#include "dbc_statement_handler.h" #include "dbc_statement_handler.h"
#include <cctype> #include "text_utils.h"
#include <charconv> #include <charconv>
#include <cmath> #include <cmath>
#include <limits> #include <limits>
@@ -13,7 +13,7 @@ namespace {
std::uint32_t maximum, std::uint32_t maximum,
const char *field) { const char *field) {
std::uint32_t value = 0U; std::uint32_t value = 0U;
const auto parsed = std::from_chars(text.data(), text.data() + text.size(), value); const std::from_chars_result parsed = std::from_chars(text.data(), text.data() + text.size(), value);
if (text.empty() || parsed.ec != std::errc() || if (text.empty() || parsed.ec != std::errc() ||
parsed.ptr != text.data() + text.size() || value > maximum) parsed.ptr != text.data() + text.size() || value > maximum)
throw std::runtime_error(std::string("Invalid ") + field + ": " + text); throw std::runtime_error(std::string("Invalid ") + field + ": " + text);
@@ -37,27 +37,92 @@ namespace {
return value; return value;
} }
/** Multiplexing ParseMultiplexingMarker(const std::string &marker) {
* @brief Remove leading and trailing spaces. Multiplexing result;
* @param text Input text. if (marker == "M")
* @return Trimmed text. result.role = MultiplexRole::Selector;
*/ else if (!marker.empty()) {
std::string TrimText(const std::string &text) { if (marker[0] != 'm')
std::string::size_type begin = 0U; throw std::runtime_error("Unsupported multiplexing marker.");
while ((begin < text.size()) && const std::string value = marker.substr(1U);
std::isspace(static_cast<unsigned char>(text[begin]))) const std::from_chars_result parsed =
++begin; 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;
}
std::string::size_type end = text.size(); return result;
while ((end > begin) &&
std::isspace(static_cast<unsigned char>(text[end - 1U])))
--end;
return text.substr(begin, end - begin);
} }
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 &quoted) {
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); FrameInfo *FindFrameById(DbcDatabase &, std::uint32_t, bool);
SignalInfo *FindSignalByName(FrameInfo &, const std::string &); SignalInfo *FindSignalByName(FrameInfo &, const std::string &);
}// namespace } // namespace
namespace { namespace {
std::vector<std::string> SplitReceivers(const std::string &text) { std::vector<std::string> SplitReceivers(const std::string &text) {
@@ -66,7 +131,7 @@ namespace {
std::istringstream stream(text); std::istringstream stream(text);
while (std::getline(stream, token, ',')) { while (std::getline(stream, token, ',')) {
token = TrimText(token); token = dbc_detail::TrimText(token);
if (!token.empty()) if (!token.empty())
receivers.push_back(token); receivers.push_back(token);
} }
@@ -74,14 +139,12 @@ namespace {
return receivers; return receivers;
} }
void NormalizeCanId(std::uint32_t rawCanId, void NormalizeCanId(std::uint32_t rawCanId, std::uint32_t &normalizedCanId, bool &isExtended) {
std::uint32_t &normalizedCanId,
bool &isExtended) {
/* /*
* DBC commonly stores extended identifiers with bit 31 set. * DBC commonly stores extended identifiers with bit 31 set.
* Example: * Example:
* raw id = 0x80000000 | actual_29_bit_id * raw id = 0x80000000 | actual_29_bit_id
*/ */
if ((rawCanId & 0x80000000U) != 0U) { if ((rawCanId & 0x80000000U) != 0U) {
isExtended = true; isExtended = true;
normalizedCanId = (rawCanId & 0x1FFFFFFFU); normalizedCanId = (rawCanId & 0x1FFFFFFFU);
@@ -115,7 +178,7 @@ namespace {
return pgn; return pgn;
} }
}// namespace } // namespace
FrameInfo DbcFrameHandler::ParseFrameLine(const std::string &line) { FrameInfo DbcFrameHandler::ParseFrameLine(const std::string &line) {
/* /*
@@ -162,48 +225,50 @@ FrameInfo DbcFrameHandler::ParseFrameLine(const std::string &line) {
SignalInfo DbcSignalHandler::ParseSignalLine(const std::string &line) { SignalInfo DbcSignalHandler::ParseSignalLine(const std::string &line) {
// Match the complete statement before converting any numeric fields. // 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( static const std::regex syntax(
R"dbc(^SG_[ \t]+([^:]+):[ \t]*([^| \t]+)[ \t]*\|[ \t]*([^@ \t]+)[ \t]*@[ \t]*([01])([+-])[ \t]*\([ \t]*([^, \t]+)[ \t]*,[ \t]*([^ )\t]+)[ \t]*\)[ \t]*\[[ \t]*([^| \t]+)[ \t]*\|[ \t]*([^\] \t]+)[ \t]*\][ \t]*"([^"\r\n]*)"[ \t]+([^ \t,]+(?:[ \t]*,[ \t]*[^ \t,]+)*)[ \t]*$)dbc"); // 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; std::smatch fields;
if (!std::regex_match(line, fields, syntax)) if (!std::regex_match(line, fields, syntax))
throw std::runtime_error("Malformed or unsupported SG_ signal definition."); throw std::runtime_error("Malformed or unsupported SG_ signal definition.");
SignalInfo signal; SignalInfo signal;
std::istringstream nameFields(fields[1].str()); std::istringstream nameFields(fields[NameAndMarker].str());
std::string marker; std::string marker;
std::string extra; std::string extra;
nameFields >> signal.name >> marker; nameFields >> signal.name >> marker;
if (signal.name.empty() || (nameFields >> extra)) if (signal.name.empty() || (nameFields >> extra))
throw std::runtime_error("Invalid signal name or multiplexing marker."); throw std::runtime_error("Invalid signal name or multiplexing marker.");
if (marker == "M") signal.multiplexing = ParseMultiplexingMarker(marker);
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(), signal.startBit = ParseDecimal(fields[StartBit].str(),
std::numeric_limits<std::uint32_t>::max(), "signal start bit"); std::numeric_limits<std::uint32_t>::max(), "signal start bit");
signal.length = ParseDecimal(fields[3].str(), 64U, "signal length"); signal.length = ParseDecimal(fields[BitLength].str(), 64U, "signal length");
if (signal.length == 0U) if (signal.length == 0U)
throw std::runtime_error("Signal length must be within 1..64 bits."); throw std::runtime_error("Signal length must be within 1..64 bits.");
signal.isLittleEndian = fields[4].str() == "1"; signal.isLittleEndian = fields[ByteOrder].str() == "1";
signal.isSigned = fields[5].str() == "-"; signal.isSigned = fields[Signedness].str() == "-";
signal.factor = ParsePhysicalValue(fields[6].str(), "signal factor"); signal.factor = ParsePhysicalValue(fields[Factor].str(), "signal factor");
signal.offset = ParsePhysicalValue(fields[7].str(), "signal offset"); signal.offset = ParsePhysicalValue(fields[Offset].str(), "signal offset");
signal.minimum = ParsePhysicalValue(fields[8].str(), "signal minimum"); signal.minimum = ParsePhysicalValue(fields[Minimum].str(), "signal minimum");
signal.maximum = ParsePhysicalValue(fields[9].str(), "signal maximum"); signal.maximum = ParsePhysicalValue(fields[Maximum].str(), "signal maximum");
if (signal.minimum > signal.maximum) if (signal.minimum > signal.maximum)
throw std::runtime_error("Signal minimum exceeds maximum."); throw std::runtime_error("Signal minimum exceeds maximum.");
signal.unit = fields[10].str(); signal.unit = fields[Unit].str();
signal.receivers = SplitReceivers(fields[11].str()); signal.receivers = SplitReceivers(fields[Receivers].str());
return signal; return signal;
} }
@@ -222,71 +287,52 @@ void DbcCommentHandler::ParseCommentLine(const std::string &line, DbcDatabase &d
return; return;
stream >> token; stream >> token;
if (token != "BO_" && token != "SG_")
return;
if (token == "BO_") { std::uint32_t rawCanId = 0U;
std::uint32_t rawCanId = 0U; std::uint32_t canId = 0U;
std::uint32_t canId = 0U; bool isExtended = false;
bool isExtended = false; std::string signalName;
stream >> rawCanId;
stream >> rawCanId; if (token == "SG_")
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)
frame->comment = line.substr(quoteBegin + 1U, quoteEnd - quoteBegin - 1U);
} else if (token == "SG_") {
std::uint32_t rawCanId = 0U;
std::uint32_t canId = 0U;
bool isExtended = false;
std::string signalName;
stream >> rawCanId;
stream >> signalName; stream >> signalName;
NormalizeCanId(rawCanId, canId, isExtended);
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;
const std::string::size_type quoteBegin = line.find('"'); FrameInfo *frame = FindFrameById(database, canId, isExtended);
const std::string::size_type quoteEnd = line.rfind('"'); if (frame == nullptr)
return;
if ((quoteBegin == std::string::npos) || const std::string comment = line.substr(quoteBegin + 1U, quoteEnd - quoteBegin - 1U);
(quoteEnd == std::string::npos) || if (token == "BO_") {
(quoteEnd <= quoteBegin)) frame->comment = comment;
return; return;
FrameInfo *frame = FindFrameById(database, canId, isExtended);
if (frame != nullptr) {
SignalInfo *signal = FindSignalByName(*frame, signalName);
if (signal != nullptr)
signal->comment = line.substr(quoteBegin + 1U, quoteEnd - quoteBegin - 1U);
}
} }
SignalInfo *signal = FindSignalByName(*frame, signalName);
if (signal != nullptr)
signal->comment = comment;
} }
namespace { namespace {
FrameInfo *FindFrameById(DbcDatabase &database, FrameInfo *FindFrameById(DbcDatabase &database,
std::uint32_t canId, std::uint32_t canId,
bool isExtended) { bool isExtended) {
for (std::size_t index = 0U; index < database.frames.size(); ++index) { for (FrameInfo &frame : database.frames) {
if ((database.frames[index].canId == canId) && if (frame.canId == canId && frame.isExtended == isExtended)
(database.frames[index].isExtended == isExtended)) return &frame;
return &database.frames[index];
} }
return nullptr; return nullptr;
} }
SignalInfo *FindSignalByName(FrameInfo &frame, const std::string &signalName) { SignalInfo *FindSignalByName(FrameInfo &frame, const std::string &signalName) {
for (std::size_t index = 0U; index < frame.signals.size(); ++index) { for (SignalInfo &signal : frame.signals) {
if (frame.signals[index].name == signalName) if (signal.name == signalName)
return &frame.signals[index]; return &signal;
} }
return nullptr; return nullptr;
@@ -308,10 +354,7 @@ void DbcSignalHandler::Handle(const std::string &line, DbcParseContext &context)
for (std::uint32_t index = 0U; index < signal.length; ++index) { for (std::uint32_t index = 0U; index < signal.length; ++index) {
if (bit >= payloadBits) if (bit >= payloadBits)
throw std::runtime_error("Signal exceeds the frame payload length."); throw std::runtime_error("Signal exceeds the frame payload length.");
if (signal.isLittleEndian) bit = NextSignalBit(bit, signal.isLittleEndian);
++bit;
else
bit = (bit % 8U == 0U) ? bit + 15U : bit - 1U;
} }
context.currentFrame->signals.push_back(std::move(signal)); context.currentFrame->signals.push_back(std::move(signal));
context.signalLines.back().push_back(context.lineNumber); context.signalLines.back().push_back(context.lineNumber);
@@ -335,7 +378,7 @@ bool DbcStatementDispatcher::Dispatch(const std::string &line,
std::istringstream stream(line); std::istringstream stream(line);
std::string keyword; std::string keyword;
stream >> keyword; stream >> keyword;
const auto handler = m_handlers.find(keyword); const HandlerMap::const_iterator handler = m_handlers.find(keyword);
if (handler == m_handlers.end()) if (handler == m_handlers.end())
return false; return false;
handler->second->Handle(line, context); handler->second->Handle(line, context);
@@ -348,7 +391,7 @@ void DbcValueDescriptionHandler::Handle(const std::string &line, DbcParseContext
std::smatch fields; std::smatch fields;
if (!std::regex_match(line, fields, header)) if (!std::regex_match(line, fields, header))
throw std::runtime_error("Malformed or unsupported VAL_ statement."); throw std::runtime_error("Malformed or unsupported VAL_ statement.");
const auto rawId = ParseDecimal(fields[1].str(), std::numeric_limits<std::uint32_t>::max(), "VAL_ CAN ID"); const std::uint32_t rawId = ParseDecimal(fields[1].str(), std::numeric_limits<std::uint32_t>::max(), "VAL_ CAN ID");
if ((rawId & 0x60000000U) != 0U) if ((rawId & 0x60000000U) != 0U)
throw std::runtime_error("VAL_ CAN ID exceeds the 29-bit identifier range."); throw std::runtime_error("VAL_ CAN ID exceeds the 29-bit identifier range.");
PendingValueDescriptions pending; PendingValueDescriptions pending;
@@ -360,34 +403,15 @@ void DbcValueDescriptionHandler::Handle(const std::string &line, DbcParseContext
if (!std::regex_search(rest, fields, pair)) if (!std::regex_search(rest, fields, pair))
throw std::runtime_error("Malformed VAL_ value/description pair or missing semicolon."); throw std::runtime_error("Malformed VAL_ value/description pair or missing semicolon.");
const std::string number = fields[1].str(); const std::string number = fields[1].str();
RawSignalValue key; const RawSignalValue key = ParseValueKey(number);
if (!number.empty() && number[0] == '-') { const std::string description = UnescapeDescription(fields[2].str());
std::int64_t value = 0;
const auto 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 auto 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;
}
std::string description;
const std::string quoted = fields[2].str();
for (std::size_t i = 0; i < quoted.size(); ++i) {
if (quoted[i] == '\\')
++i;
description += quoted[i];
}
if (!pending.descriptions.emplace(key, description).second) if (!pending.descriptions.emplace(key, description).second)
throw std::runtime_error("Duplicate VAL_ value key: " + number); throw std::runtime_error("Duplicate VAL_ value key: " + number);
const auto consumed = static_cast<std::size_t>(fields.length()); const std::size_t consumed = static_cast<std::size_t>(fields.length());
rest.erase(0, consumed); rest.erase(0, consumed);
if (!rest.empty() && rest[0] != ';' && rest[0] != ' ' && rest[0] != '\t') if (!rest.empty() && rest[0] != ';' && rest[0] != ' ' && rest[0] != '\t')
throw std::runtime_error("Missing whitespace between VAL_ pairs."); throw std::runtime_error("Missing whitespace between VAL_ pairs.");
rest = TrimText(rest); rest = dbc_detail::TrimText(rest);
} }
if (pending.descriptions.empty()) if (pending.descriptions.empty())
throw std::runtime_error("VAL_ requires at least one value/description pair."); throw std::runtime_error("VAL_ requires at least one value/description pair.");
@@ -395,29 +419,11 @@ void DbcValueDescriptionHandler::Handle(const std::string &line, DbcParseContext
} }
void DbcParseContext::ResolveValueDescriptions(const std::string &filePath) { void DbcParseContext::ResolveValueDescriptions(const std::string &filePath) {
for (const auto &pending: pendingValues) { for (const PendingValueDescriptions &pending: pendingValues) {
try { try {
FrameInfo *frame = nullptr; FrameInfo *frame = FindUniqueFrame(database, pending.canId, pending.isExtended);
for (auto &candidate: database.frames) { SignalInfo *signal = FindUniqueSignal(*frame, pending.signalName);
if (candidate.canId == pending.canId && candidate.isExtended == pending.isExtended) { for (const ValueDescriptions::value_type &entry: pending.descriptions) {
if (frame)
throw std::runtime_error("Ambiguous VAL_ frame reference.");
frame = &candidate;
}
}
if (!frame)
throw std::runtime_error("Unresolved VAL_ frame reference.");
SignalInfo *signal = nullptr;
for (auto &candidate: frame->signals) {
if (candidate.name == pending.signalName) {
if (signal)
throw std::runtime_error("Ambiguous VAL_ signal reference: " + pending.signalName);
signal = &candidate;
}
}
if (!signal)
throw std::runtime_error("Unresolved VAL_ signal reference: " + pending.signalName);
for (const auto &entry: pending.descriptions) {
if (!signal->valueDescriptions.insert(entry).second) if (!signal->valueDescriptions.insert(entry).second)
throw std::runtime_error("Duplicate VAL_ value key for signal: " + pending.signalName); throw std::runtime_error("Duplicate VAL_ value key for signal: " + pending.signalName);
} }
@@ -437,14 +443,43 @@ namespace {
std::uint64_t bit = signal.startBit; std::uint64_t bit = signal.startBit;
for (std::uint32_t index = 0U; index < signal.length; ++index) { for (std::uint32_t index = 0U; index < signal.length; ++index) {
bits.push_back(bit); bits.push_back(bit);
if (signal.isLittleEndian) bit = NextSignalBit(bit, signal.isLittleEndian);
++bit;
else
bit = bit % 8U == 0U ? bit + 15U : bit - 1U;
} }
return bits; 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 std::runtime_error(filePath + ":" + std::to_string(lines[index]) +
": Multiplexing requires one unsigned selector per frame.");
selector = &signal;
}
return selector;
}
bool SignalsOverlap(const SignalInfo &first, const SignalInfo &second) { bool SignalsOverlap(const SignalInfo &first, const SignalInfo &second) {
const std::vector<std::uint64_t> firstBits = SignalBits(first); const std::vector<std::uint64_t> firstBits = SignalBits(first);
const std::vector<std::uint64_t> secondBits = SignalBits(second); const std::vector<std::uint64_t> secondBits = SignalBits(second);
@@ -461,31 +496,13 @@ namespace {
void DbcParseContext::ValidateMultiplexing(const std::string &filePath) const { void DbcParseContext::ValidateMultiplexing(const std::string &filePath) const {
for (std::size_t frameIndex = 0U; frameIndex < database.frames.size(); ++frameIndex) { for (std::size_t frameIndex = 0U; frameIndex < database.frames.size(); ++frameIndex) {
const FrameInfo &frame = database.frames[frameIndex]; const FrameInfo &frame = database.frames[frameIndex];
const SignalInfo *selector = nullptr; const SignalInfo *selector = FindSelector(frame, signalLines[frameIndex], filePath);
for (std::size_t index = 0U; index < frame.signals.size(); ++index) { for (std::size_t index = 0U; index < frame.signals.size(); ++index) {
const SignalInfo &signal = frame.signals[index]; const SignalInfo &signal = frame.signals[index];
if (signal.multiplexing.role != MultiplexRole::Selector) std::string error = ValidateBranch(signal, 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) { for (std::size_t previous = 0U; previous < index && error.empty(); ++previous) {
const SignalInfo &other = frame.signals[previous]; const SignalInfo &other = frame.signals[previous];
const bool exclusive = signal.multiplexing.role == MultiplexRole::Branch && if (!AreMutuallyExclusive(signal, other) && SignalsOverlap(signal, other))
other.multiplexing.role == MultiplexRole::Branch &&
signal.multiplexing.value != other.multiplexing.value;
if (!exclusive && SignalsOverlap(signal, other))
error = "Signals overlap without mutually exclusive multiplexing conditions."; error = "Signals overlap without mutually exclusive multiplexing conditions.";
} }
if (!error.empty()) if (!error.empty())
+2 -1
View File
@@ -83,8 +83,9 @@ public:
bool Dispatch(const std::string &line, DbcParseContext &context) const; bool Dispatch(const std::string &line, DbcParseContext &context) const;
private: private:
using HandlerMap = std::unordered_map<std::string, std::shared_ptr<DbcStatementHandler>>;
TypeFactory<std::string, DbcStatementHandler> m_factory; TypeFactory<std::string, DbcStatementHandler> m_factory;
std::unordered_map<std::string, std::shared_ptr<DbcStatementHandler>> m_handlers; HandlerMap m_handlers;
}; };
#endif /* DBC_STATEMENT_HANDLER_H */ #endif /* DBC_STATEMENT_HANDLER_H */
+5 -7
View File
@@ -12,15 +12,13 @@
#include "dbc_tree_builder.h" #include "dbc_tree_builder.h"
std::unique_ptr<TreeNode> DbcTreeBuilder::Build(const DbcDatabase &database) const { std::unique_ptr<TreeNode> DbcTreeBuilder::Build(const DbcDatabase &database) const {
std::unique_ptr<TreeNode> root(new TreeNode()); std::unique_ptr<TreeNode> root = std::make_unique<TreeNode>();
for (std::size_t frameIndex = 0U; frameIndex < database.frames.size(); ++frameIndex) { for (const FrameInfo &frame : database.frames) {
const FrameInfo &frame = database.frames[frameIndex]; std::unique_ptr<TreeNode> frameNode = std::make_unique<TreeNode>(frame);
std::unique_ptr<TreeNode> frameNode(new TreeNode(frame));
for (std::size_t signalIndex = 0U; signalIndex < frame.signals.size(); ++signalIndex) { for (const SignalInfo &signal : frame.signals) {
const SignalInfo &signal = frame.signals[signalIndex]; std::unique_ptr<TreeNode> signalNode = std::make_unique<TreeNode>(signal);
std::unique_ptr<TreeNode> signalNode(new TreeNode(signal));
frameNode->AddChild(std::move(signalNode)); frameNode->AddChild(std::move(signalNode));
} }
+27 -33
View File
@@ -40,54 +40,48 @@ enum class ValueType {
*/ */
struct DecodeSignal { struct DecodeSignal {
Multiplexing multiplexing; Multiplexing multiplexing;
std::string name; /**< Signal name. */ std::string name; /**< Signal name. */
std::uint32_t startBit; /**< DBC start bit. */ std::uint32_t startBit = 0U; /**< DBC start bit. */
std::uint32_t length; /**< Signal length in bits. */ std::uint32_t length = 0U; /**< Signal length in bits. */
ByteOrder byteOrder; /**< Intel or Motorola. */ ByteOrder byteOrder = ByteOrder::Intel; /**< Intel or Motorola. */
ValueType valueType; /**< Signed or unsigned. */ ValueType valueType = ValueType::Unsigned; /**< Signed or unsigned. */
double factor; /**< Scaling factor. */ double factor = 1.0; /**< Scaling factor. */
double offset; /**< Physical offset. */ double offset = 0.0; /**< Physical offset. */
double minimum; /**< Minimum physical value. */ double minimum = 0.0; /**< Minimum physical value. */
double maximum; /**< Maximum physical value. */ double maximum = 0.0; /**< Maximum physical value. */
std::string unit; /**< Physical unit. */ std::string unit; /**< Physical unit. */
std::vector<std::string> receivers; /**< Receivers. */ std::vector<std::string> receivers; /**< Receivers. */
ValueDescriptions valueDescriptions; /**< Raw integer state descriptions. */ ValueDescriptions valueDescriptions; /**< Raw integer state descriptions. */
std::string comment; /**< Comment. */ std::string comment; /**< Comment. */
DecodeSignal() DecodeSignal() = default;
: name(), startBit(0U), length(0U), byteOrder(ByteOrder::Intel), valueType(ValueType::Unsigned), factor(1.0), offset(0.0), minimum(0.0), maximum(0.0), unit(), receivers(), comment() {
}
}; };
/** /**
* @brief Runtime-ready frame definition. * @brief Runtime-ready frame definition.
*/ */
struct DecodeFrame { struct DecodeFrame {
std::string name; /**< Frame name. */ std::string name; /**< Frame name. */
std::uint32_t canId; /**< Normalized CAN ID. */ std::uint32_t canId = 0U; /**< Normalized CAN ID. */
bool isExtended; /**< true for extended frame. */ bool isExtended = false; /**< true for extended frame. */
std::uint8_t dlc; /**< Payload length. */ std::uint8_t dlc = 0U; /**< Payload length. */
std::uint32_t pgn; /**< PGN if available. */ std::uint32_t pgn = 0U; /**< PGN if available. */
bool hasPgn; /**< true if PGN is valid. */ bool hasPgn = false; /**< true if PGN is valid. */
std::string transmitter; /**< Transmitter ECU. */ std::string transmitter; /**< Transmitter ECU. */
std::string comment; /**< Frame comment. */ std::string comment; /**< Frame comment. */
std::vector<DecodeSignal> signals; /**< Signal definitions. */ std::vector<DecodeSignal> signals; /**< Signal definitions. */
DecodeFrame() DecodeFrame() = default;
: name(), canId(0U), isExtended(false), dlc(0U), pgn(0U), hasPgn(false), transmitter(), comment(), signals() {
}
}; };
/** /**
* @brief Key for fast frame lookup. * @brief Key for fast frame lookup.
*/ */
struct FrameKey { struct FrameKey {
std::uint32_t canId; std::uint32_t canId = 0U;
bool isExtended; bool isExtended = false;
FrameKey() FrameKey() = default;
: canId(0U), isExtended(false) {
}
FrameKey(std::uint32_t id, bool extended) FrameKey(std::uint32_t id, bool extended)
: canId(id), isExtended(extended) { : canId(id), isExtended(extended) {
+10 -12
View File
@@ -22,19 +22,17 @@
* @brief Describes one CAN frame from a DBC file. * @brief Describes one CAN frame from a DBC file.
*/ */
struct FrameInfo { struct FrameInfo {
std::string name; /**< Frame name. */ std::string name; /**< Frame name. */
std::uint32_t canId; /**< Normalized CAN identifier. */ std::uint32_t canId = 0U; /**< Normalized CAN identifier. */
bool isExtended; /**< true for extended frame. */ bool isExtended = false; /**< true for extended frame. */
std::uint32_t pgn; /**< J1939 PGN if applicable. */ std::uint32_t pgn = 0U; /**< J1939 PGN if applicable. */
bool hasPgn; /**< true if PGN was derived. */ bool hasPgn = false; /**< true if PGN was derived. */
std::uint8_t dlc; /**< Frame payload length. */ std::uint8_t dlc = 0U; /**< Frame payload length. */
std::string transmitter; /**< Transmitter ECU name. */ std::string transmitter; /**< Transmitter ECU name. */
std::string comment; /**< Optional frame comment. */ std::string comment; /**< Optional frame comment. */
std::vector<SignalInfo> signals; /**< Signals contained in the frame. */ std::vector<SignalInfo> signals; /**< Signals contained in the frame. */
FrameInfo() FrameInfo() = default;
: name(), canId(0U), isExtended(false), pgn(0U), hasPgn(false), dlc(0U), transmitter(), comment(), signals() {
}
}; };
#endif /* FRAME_INFO_H */ #endif /* FRAME_INFO_H */
+14 -16
View File
@@ -23,23 +23,21 @@
*/ */
struct SignalInfo { struct SignalInfo {
Multiplexing multiplexing; Multiplexing multiplexing;
std::string name; /**< Signal name. */ std::string name; /**< Signal name. */
std::uint32_t startBit; /**< Start bit in DBC notation. */ std::uint32_t startBit = 0U; /**< Start bit in DBC notation. */
std::uint32_t length; /**< Signal length in bits. */ std::uint32_t length = 0U; /**< Signal length in bits. */
bool isLittleEndian; /**< true for Intel, false for Motorola. */ bool isLittleEndian = true; /**< true for Intel, false for Motorola. */
bool isSigned; /**< true if signal is signed. */ bool isSigned = false; /**< true if signal is signed. */
double factor; /**< Scaling factor. */ double factor = 1.0; /**< Scaling factor. */
double offset; /**< Physical offset. */ double offset = 0.0; /**< Physical offset. */
double minimum; /**< Minimum physical value. */ double minimum = 0.0; /**< Minimum physical value. */
double maximum; /**< Maximum physical value. */ double maximum = 0.0; /**< Maximum physical value. */
std::string unit; /**< Physical unit. */ std::string unit; /**< Physical unit. */
std::vector<std::string> receivers; /**< Signal receivers. */ std::vector<std::string> receivers; /**< Signal receivers. */
ValueDescriptions valueDescriptions; /**< Raw integer state descriptions. */ ValueDescriptions valueDescriptions; /**< Raw integer state descriptions. */
std::string comment; /**< Optional signal comment. */ std::string comment; /**< Optional signal comment. */
SignalInfo() SignalInfo() = default;
: name(), startBit(0U), length(0U), isLittleEndian(true), isSigned(false), factor(1.0), offset(0.0), minimum(0.0), maximum(0.0), unit(), receivers(), comment() {
}
}; };
#endif /* SIGNAL_INFO_H */ #endif /* SIGNAL_INFO_H */
+23
View File
@@ -0,0 +1,23 @@
#ifndef DBC_TEXT_UTILS_H
#define DBC_TEXT_UTILS_H
#include <cctype>
#include <string>
namespace dbc_detail {
inline std::string TrimText(const std::string &text) {
std::string::size_type begin = 0U;
while ((begin < text.size()) &&
std::isspace(static_cast<unsigned char>(text[begin])))
++begin;
std::string::size_type end = text.size();
while ((end > begin) &&
std::isspace(static_cast<unsigned char>(text[end - 1U])))
--end;
return text.substr(begin, end - begin);
}
}
#endif
+3 -3
View File
@@ -12,15 +12,15 @@
#include "tree_node.h" #include "tree_node.h"
TreeNode::TreeNode() TreeNode::TreeNode()
: m_type(NodeType::Root), m_name("dbc"), m_children(), m_frame(), m_signal() { : m_type(NodeType::Root), m_name("dbc") {
} }
TreeNode::TreeNode(const FrameInfo &frame) TreeNode::TreeNode(const FrameInfo &frame)
: m_type(NodeType::Frame), m_name(frame.name), m_children(), m_frame(new FrameInfo(frame)), m_signal() { : m_type(NodeType::Frame), m_name(frame.name), m_frame(std::make_unique<FrameInfo>(frame)) {
} }
TreeNode::TreeNode(const SignalInfo &signal) TreeNode::TreeNode(const SignalInfo &signal)
: m_type(NodeType::Signal), m_name(signal.name), m_children(), m_frame(), m_signal(new SignalInfo(signal)) { : m_type(NodeType::Signal), m_name(signal.name), m_signal(std::make_unique<SignalInfo>(signal)) {
} }
void TreeNode::AddChild(std::unique_ptr<TreeNode> child) { void TreeNode::AddChild(std::unique_ptr<TreeNode> child) {
+2 -2
View File
@@ -48,8 +48,8 @@
* factory.registerType<Derived1>("one"); * factory.registerType<Derived1>("one");
* factory.registerType<Derived2>("two"); * factory.registerType<Derived2>("two");
* *
* auto a = factory.create("one", 10); * std::shared_ptr<Base> a = factory.create("one", 10);
* auto b = factory.creator("two")(10); // advanced API: get creator function * std::shared_ptr<Base> b = factory.creator("two")(10); // advanced API: get creator function
* *
* (void)a->get(); * (void)a->get();
* (void)b->get(); * (void)b->get();
+2 -2
View File
@@ -11,7 +11,7 @@ using RawSignalValue = std::variant<std::int64_t, std::uint64_t>;
/** Canonical keys: negative int64_t, nonnegative uint64_t. */ /** Canonical keys: negative int64_t, nonnegative uint64_t. */
inline RawSignalValue NormalizeValueKey(const RawSignalValue &value) { inline RawSignalValue NormalizeValueKey(const RawSignalValue &value) {
if (const auto *number = std::get_if<std::int64_t>(&value)) { if (const std::int64_t *number = std::get_if<std::int64_t>(&value)) {
if (*number >= 0) if (*number >= 0)
return static_cast<std::uint64_t>(*number); return static_cast<std::uint64_t>(*number);
} }
@@ -23,7 +23,7 @@ using ValueDescriptions = std::map<RawSignalValue, std::string>;
/** Returns a borrowed string, or nullptr; never converts integers to double. */ /** Returns a borrowed string, or nullptr; never converts integers to double. */
inline const std::string *FindValueDescription(const ValueDescriptions &descriptions, inline const std::string *FindValueDescription(const ValueDescriptions &descriptions,
const RawSignalValue &rawValue) { const RawSignalValue &rawValue) {
const auto found = descriptions.find(NormalizeValueKey(rawValue)); const ValueDescriptions::const_iterator found = descriptions.find(NormalizeValueKey(rawValue));
return found == descriptions.end() ? nullptr : &found->second; return found == descriptions.end() ? nullptr : &found->second;
} }
+2 -3
View File
@@ -80,15 +80,14 @@ static void PrintDecodedFrame(const DecodedFrameValue &decoded) {
std::cout << "Decoded frame: " << decoded.definition->name << "\n"; std::cout << "Decoded frame: " << decoded.definition->name << "\n";
for (std::size_t index = 0U; index < decoded.signals.size(); ++index) { for (const DecodedSignalValue &signal : decoded.signals) {
const DecodedSignalValue &signal = decoded.signals[index];
if ((signal.definition == nullptr) || !signal.valid) if ((signal.definition == nullptr) || !signal.valid)
continue; continue;
std::cout << " " << signal.definition->name std::cout << " " << signal.definition->name
<< " raw="; << " raw=";
if (const auto *signedValue = std::get_if<std::int64_t>(&signal.rawValue)) if (const std::int64_t *signedValue = std::get_if<std::int64_t>(&signal.rawValue))
std::cout << *signedValue; std::cout << *signedValue;
else else
std::cout << std::get<std::uint64_t>(signal.rawValue); std::cout << std::get<std::uint64_t>(signal.rawValue);
+41 -22
View File
@@ -5,6 +5,23 @@
#include <limits> #include <limits>
#include <stdexcept> #include <stdexcept>
namespace {
QVariantList ToVariantList(const ValueDescriptions &values) {
QVariantList descriptions;
for (const ValueDescriptions::value_type &entry : values) {
QVariantMap pair;
if (const std::int64_t *number = std::get_if<std::int64_t>(&entry.first))
pair.insert("value", QVariant::fromValue(static_cast<qlonglong>(*number)));
else
pair.insert("value", QVariant::fromValue(
static_cast<qulonglong>(std::get<std::uint64_t>(entry.first))));
pair.insert("description", QString::fromStdString(entry.second));
descriptions.append(pair);
}
return descriptions;
}
} // namespace
DbcTreeModel::DbcTreeModel(QObject *parent) : QAbstractItemModel(parent) {} DbcTreeModel::DbcTreeModel(QObject *parent) : QAbstractItemModel(parent) {}
void DbcTreeModel::SetTree(std::unique_ptr<TreeNode> root) { void DbcTreeModel::SetTree(std::unique_ptr<TreeNode> root) {
@@ -60,38 +77,40 @@ QVariant DbcTreeModel::data(const QModelIndex &item, int role) const {
return {}; return {};
const FrameInfo *frame = node->GetFrame(); const FrameInfo *frame = node->GetFrame();
const SignalInfo *signal = node->GetSignal(); const SignalInfo *signal = node->GetSignal();
const QString comment = QString::fromStdString(frame ? frame->comment : signal ? signal->comment QString comment;
: ""); if (frame != nullptr)
comment = QString::fromStdString(frame->comment);
else if (signal != nullptr)
comment = QString::fromStdString(signal->comment);
if (role == Qt::ToolTipRole || role == CommentRole) if (role == Qt::ToolTipRole || role == CommentRole)
return comment; return comment;
if (role == NodeTypeRole) if (role == NodeTypeRole)
return static_cast<int>(node->GetType()); return static_cast<int>(node->GetType());
if (role == ValueDescriptionsRole && signal) { if (role == ValueDescriptionsRole && signal) {
QVariantList descriptions; return ToVariantList(signal->valueDescriptions);
for (const auto &entry: signal->valueDescriptions) {
QVariantMap pair;
if (const auto *number = std::get_if<std::int64_t>(&entry.first))
pair.insert("value", QVariant::fromValue(static_cast<qlonglong>(*number)));
else
pair.insert("value", QVariant::fromValue(static_cast<qulonglong>(std::get<std::uint64_t>(entry.first))));
pair.insert("description", QString::fromStdString(entry.second));
descriptions.append(pair);
}
return descriptions;
} }
if (frame) { if (frame) {
if (role == CanIdRole) return frame->canId; if (role == CanIdRole)
if (role == PayloadLengthRole) return static_cast<int>(frame->dlc); return frame->canId;
if (role == ExtendedRole) return frame->isExtended; if (role == PayloadLengthRole)
return static_cast<int>(frame->dlc);
if (role == ExtendedRole)
return frame->isExtended;
} }
if (signal) { if (signal) {
if (role == StartBitRole) return signal->startBit; if (role == StartBitRole)
if (role == BitLengthRole) return signal->length; return signal->startBit;
if (role == LittleEndianRole) return signal->isLittleEndian; if (role == BitLengthRole)
if (role == SignedRole) return signal->isSigned; return signal->length;
if (role == UnitRole) return QString::fromStdString(signal->unit); if (role == LittleEndianRole)
return signal->isLittleEndian;
if (role == SignedRole)
return signal->isSigned;
if (role == UnitRole)
return QString::fromStdString(signal->unit);
} }
if (role != Qt::DisplayRole) return {}; if (role != Qt::DisplayRole)
return {};
switch (item.column()) { switch (item.column()) {
case Name: case Name:
return QString::fromStdString(node->GetName()); return QString::fromStdString(node->GetName());
+28 -21
View File
@@ -49,7 +49,7 @@ void DbcDecoderTest::decode_data() {
{"Intel signed regression", ByteOrder::Intel, 0U, 16U, ValueType::Signed, {0xFE, 0xFF}, true, -2, 0.5, 10.0, 9.0}, {"Intel signed regression", ByteOrder::Intel, 0U, 16U, ValueType::Signed, {0xFE, 0xFF}, true, -2, 0.5, 10.0, 9.0},
{"Intel short payload", ByteOrder::Intel, 0U, 16U, ValueType::Unsigned, {0x12}, false, 0, 1.0, 0.0, 0.0}}; {"Intel short payload", ByteOrder::Intel, 0U, 16U, ValueType::Unsigned, {0x12}, false, 0, 1.0, 0.0, 0.0}};
for (const auto &test: cases) for (const DecodeCase &test: cases)
QTest::newRow(test.name) << test; QTest::newRow(test.name) << test;
} }
@@ -76,19 +76,25 @@ void DbcDecoderTest::decode() {
frame.canId = definition.canId; frame.canId = definition.canId;
frame.data = test.data; frame.data = test.data;
const DecodedFrameValue result = DbcDecoder().Decode(database, frame); const DecodedFrameValue result = DbcDecoder().Decode(database, frame);
const bool passed = result.valid && QVERIFY2(result.valid, test.name);
(result.definition == &database.frames[0]) && QCOMPARE(result.definition, &database.frames[0]);
(result.signals.size() == 1U) && QCOMPARE(result.signals.size(), std::size_t(1U));
(result.signals[0].definition == &database.frames[0].signals[0]) && const DecodedSignalValue &decoded = result.signals[0];
(result.signals[0].valid == test.valid) && QCOMPARE(decoded.definition, &database.frames[0].signals[0]);
(!test.valid || (((test.valueType == ValueType::Signed) QCOMPARE(decoded.valid, test.valid);
? (std::get_if<std::int64_t>(&result.signals[0].rawValue) && if (!test.valid)
std::get<std::int64_t>(result.signals[0].rawValue) == test.rawValue) return;
: (std::get_if<std::uint64_t>(&result.signals[0].rawValue) &&
std::get<std::uint64_t>(result.signals[0].rawValue) == if (test.valueType == ValueType::Signed) {
static_cast<std::uint64_t>(test.rawValue))) && const std::int64_t *raw = std::get_if<std::int64_t>(&decoded.rawValue);
(result.signals[0].physicalValue == test.physicalValue))); QVERIFY(raw != nullptr);
QVERIFY2(passed, test.name); QCOMPARE(*raw, test.rawValue);
} else {
const std::uint64_t *raw = std::get_if<std::uint64_t>(&decoded.rawValue);
QVERIFY(raw != nullptr);
QCOMPARE(*raw, static_cast<std::uint64_t>(test.rawValue));
}
QCOMPARE(decoded.physicalValue, test.physicalValue);
} }
void DbcDecoderTest::boundaries() { void DbcDecoderTest::boundaries() {
@@ -115,13 +121,13 @@ void DbcDecoderTest::boundaries() {
const unsigned shift = (order == ByteOrder::Intel ? byte : 7U - byte) * 8U; const unsigned shift = (order == ByteOrder::Intel ? byte : 7U - byte) * 8U;
frame.data.push_back(static_cast<std::uint8_t>(bits >> shift)); frame.data.push_back(static_cast<std::uint8_t>(bits >> shift));
} }
const auto result = DbcDecoder().Decode(database, frame); const DecodedFrameValue result = DbcDecoder().Decode(database, frame);
const std::int64_t signedExpected = bits <= 0x7FFFFFFFFFFFFFFFULL const std::int64_t signedExpected = bits <= 0x7FFFFFFFFFFFFFFFULL
? static_cast<std::int64_t>(bits) ? static_cast<std::int64_t>(bits)
: std::numeric_limits<std::int64_t>::min() + : std::numeric_limits<std::int64_t>::min() +
static_cast<std::int64_t>(bits - 0x8000000000000000ULL); static_cast<std::int64_t>(bits - 0x8000000000000000ULL);
const auto &decoded = result.signals.at(0U); const DecodedSignalValue &decoded = result.signals.at(0U);
const auto expected = type == ValueType::Unsigned const RawSignalValue expected = type == ValueType::Unsigned
? std::variant<std::int64_t, std::uint64_t>(bits) ? std::variant<std::int64_t, std::uint64_t>(bits)
: std::variant<std::int64_t, std::uint64_t>(signedExpected); : std::variant<std::int64_t, std::uint64_t>(signedExpected);
const double physical = (type == ValueType::Unsigned const double physical = (type == ValueType::Unsigned
@@ -131,7 +137,7 @@ void DbcDecoderTest::boundaries() {
10.0; 10.0;
std::ostringstream actualText; std::ostringstream actualText;
std::ostringstream expectedText; std::ostringstream expectedText;
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))
actualText << *signedValue; actualText << *signedValue;
else else
actualText << std::get<std::uint64_t>(decoded.rawValue); actualText << std::get<std::uint64_t>(decoded.rawValue);
@@ -139,9 +145,10 @@ void DbcDecoderTest::boundaries() {
expectedText << bits; expectedText << bits;
else else
expectedText << signedExpected; expectedText << signedExpected;
QVERIFY2(decoded.valid && decoded.rawValue == expected && QVERIFY(decoded.valid);
decoded.physicalValue == physical && actualText.str() == expectedText.str(), QVERIFY(decoded.rawValue == expected);
expectedText.str().c_str()); QCOMPARE(decoded.physicalValue, physical);
QCOMPARE(actualText.str(), expectedText.str());
} }
} }
} }
+36 -34
View File
@@ -92,16 +92,17 @@ void DbcParserTest::validFrames() {
{"BO_ 2147483649 Frame: 8 ECU", 1U, true, 8U}, {"BO_ 2147483649 Frame: 8 ECU", 1U, true, 8U},
{"BO_ 2684354559 Frame: 8 ECU", 0x1FFFFFFFU, true, 8U}, {"BO_ 2684354559 Frame: 8 ECU", 0x1FFFFFFFU, true, 8U},
{" BO_\t256\tFrame :\t8\tECU \r", 256U, false, 8U}}; {" BO_\t256\tFrame :\t8\tECU \r", 256U, false, 8U}};
for (const auto &test: valid) { for (const ValidCase &test: valid) {
fixture.Write(std::string(test.line) + '\n'); fixture.Write(std::string(test.line) + '\n');
try { try {
const auto database = DbcParser().ParseFile(fixture.path.string()); const DbcDatabase database = DbcParser().ParseFile(fixture.path.string());
CHECK(database.frames.size() == 1U && database.frames[0].name == "Frame" && QCOMPARE(database.frames.size(), std::size_t(1U));
database.frames[0].canId == test.id && const FrameInfo &frame = database.frames[0];
database.frames[0].isExtended == test.extended && QCOMPARE(frame.name, std::string("Frame"));
database.frames[0].dlc == test.length && QCOMPARE(frame.canId, test.id);
database.frames[0].transmitter == "ECU", QCOMPARE(frame.isExtended, test.extended);
test.line); QCOMPARE(static_cast<unsigned>(frame.dlc), test.length);
QCOMPARE(frame.transmitter, std::string("ECU"));
} catch (const std::exception &error) { CHECK(false, error.what()); } } catch (const std::exception &error) { CHECK(false, error.what()); }
} }
} }
@@ -119,7 +120,7 @@ void DbcParserTest::invalidFrames() {
"BO_ 1 Frame: -1 ECU", "BO_ 1 Frame: +8 ECU", "BO_ 1 Frame: 8x ECU", "BO_ 1 Frame: -1 ECU", "BO_ 1 Frame: +8 ECU", "BO_ 1 Frame: 8x ECU",
"BO_ 1 Frame: 8.5 ECU", "BO_ 1 Frame: 256 ECU", "BO_ 1 Frame: 8.5 ECU", "BO_ 1 Frame: 256 ECU",
"BO_ 1 Frame: 4294967296 ECU", "BO_\tinvalid"}; "BO_ 1 Frame: 4294967296 ECU", "BO_\tinvalid"};
for (const auto *line: invalid) { for (const char *line: invalid) {
fixture.Write(std::string("VERSION \"test\"\n\n") + line + '\n'); fixture.Write(std::string("VERSION \"test\"\n\n") + line + '\n');
try { try {
DbcParser().ParseFile(fixture.path.string()); DbcParser().ParseFile(fixture.path.string());
@@ -140,7 +141,7 @@ void DbcParserTest::integration() {
" SG_ Speed : 0|16@1+ (0.5,1) [0|100] \"rpm\" ECU\n" " SG_ Speed : 0|16@1+ (0.5,1) [0|100] \"rpm\" ECU\n"
"CM_ BO_ 256 \"Frame comment\";\nCM_ SG_ 256 Speed \"Signal comment\";\n" "CM_ BO_ 256 \"Frame comment\";\nCM_ SG_ 256 Speed \"Signal comment\";\n"
"BO_ 257 Second: 8 ECU\n"); "BO_ 257 Second: 8 ECU\n");
const auto database = DbcParser().ParseFile(fixture.path.string()); const DbcDatabase database = DbcParser().ParseFile(fixture.path.string());
CHECK(database.frames.size() == 2U && database.frames[0].signals.size() == 1U && CHECK(database.frames.size() == 2U && database.frames[0].signals.size() == 1U &&
database.frames[0].comment == "Frame comment" && database.frames[0].comment == "Frame comment" &&
database.frames[0].signals[0].comment == "Signal comment", database.frames[0].signals[0].comment == "Signal comment",
@@ -324,7 +325,7 @@ void DbcParserTest::validSignals() {
fixture.Write("BO_ 1 Frame: 8 ECU\n" + fixture.Write("BO_ 1 Frame: 8 ECU\n" +
SignalLine("0", "8", "1+", "0", "0", "0", "0") + "\n" + SignalLine("0", "8", "1+", "0", "0", "0", "0") + "\n" +
SignalLine("8", "8", "1-", "-1", "0", "-255", "0") + "\n"); SignalLine("8", "8", "1-", "-1", "0", "-255", "0") + "\n");
const auto factors = signalParser.ParseFile(fixture.path.string()); const DbcDatabase factors = signalParser.ParseFile(fixture.path.string());
CHECK(factors.frames[0].signals.size() == 2U && CHECK(factors.frames[0].signals.size() == 2U &&
factors.frames[0].signals[0].factor == 0.0 && factors.frames[0].signals[0].factor == 0.0 &&
factors.frames[0].signals[1].factor == -1.0, factors.frames[0].signals[1].factor == -1.0,
@@ -345,26 +346,27 @@ void DbcParserTest::validSignals() {
{"56", "1", "0+", 8U}, {"56", "1", "0+", 8U},
{"3", "12", "0+", 2U}, {"3", "12", "0+", 2U},
{"2039", "1", "1+", 255U}}; {"2039", "1", "1+", 255U}};
for (const auto &test: validSignals) { for (const SignalCase &test: validSignals) {
fixture.Write("BO_ 1 Frame: " + std::to_string(test.payload) + " ECU\n" + fixture.Write("BO_ 1 Frame: " + std::to_string(test.payload) + " ECU\n" +
SignalLine(test.start, test.length, test.order, ".5e+1", "-2.5E-1", "-1.", "+2e2") + "\n"); SignalLine(test.start, test.length, test.order, ".5e+1", "-2.5E-1", "-1.", "+2e2") + "\n");
try { try {
const auto parsed = signalParser.ParseFile(fixture.path.string()); const DbcDatabase parsed = signalParser.ParseFile(fixture.path.string());
const auto &signal = parsed.frames.at(0).signals.at(0); const SignalInfo &signal = parsed.frames.at(0).signals.at(0);
CHECK(signal.startBit == std::stoul(test.start) && QCOMPARE(signal.startBit, static_cast<std::uint32_t>(std::stoul(test.start)));
signal.length == std::stoul(test.length) && QCOMPARE(signal.length, static_cast<std::uint32_t>(std::stoul(test.length)));
signal.isLittleEndian == (test.order[0] == '1') && QCOMPARE(signal.isLittleEndian, test.order[0] == '1');
signal.isSigned == (test.order[1] == '-') && QCOMPARE(signal.isSigned, test.order[1] == '-');
signal.factor == 5.0 && signal.offset == -0.25 && QCOMPARE(signal.factor, 5.0);
signal.minimum == -1.0 && signal.maximum == 200.0, QCOMPARE(signal.offset, -0.25);
"Valid signal boundary and numeric metadata"); QCOMPARE(signal.minimum, -1.0);
QCOMPARE(signal.maximum, 200.0);
} catch (const std::exception &error) { CHECK(false, error.what()); } } catch (const std::exception &error) { CHECK(false, error.what()); }
} }
fixture.Write("BO_ 1 Frame: 8 ECU\n" fixture.Write("BO_ 1 Frame: 8 ECU\n"
"SG_\tValue\t:\t0 | 8 @1+ ( 1 , 0 ) [ 0 | 255 ] \"rpm\" ECU1,\tECU2\n"); "SG_\tValue\t:\t0 | 8 @1+ ( 1 , 0 ) [ 0 | 255 ] \"rpm\" ECU1,\tECU2\n");
try { try {
const auto parsed = signalParser.ParseFile(fixture.path.string()); const DbcDatabase parsed = signalParser.ParseFile(fixture.path.string());
const auto &signal = parsed.frames.at(0).signals.at(0); const SignalInfo &signal = parsed.frames.at(0).signals.at(0);
CHECK(signal.name == "Value" && signal.unit == "rpm" && CHECK(signal.name == "Value" && signal.unit == "rpm" &&
signal.receivers == std::vector<std::string>({"ECU1", "ECU2"}), signal.receivers == std::vector<std::string>({"ECU1", "ECU2"}),
"Signal tabs, spaces, and receiver list"); "Signal tabs, spaces, and receiver list");
@@ -405,7 +407,7 @@ void DbcParserTest::independentSessions() {
"Independent parse context"); "Independent parse context");
} }
fixture.Write("BO_ 2 Second: 8 ECU\n"); fixture.Write("BO_ 2 Second: 8 ECU\n");
const auto recovered = parser.ParseFile(fixture.path.string()); const DbcDatabase recovered = parser.ParseFile(fixture.path.string());
CHECK(recovered.frames.size() == 1U && recovered.frames[0].canId == 2U, CHECK(recovered.frames.size() == 1U && recovered.frames[0].canId == 2U,
"Fresh parse after error"); "Fresh parse after error");
} }
@@ -424,9 +426,9 @@ void DbcParserTest::valueDescriptions() {
"BO_ 2048 Unsigned: 8 ECU\n" "BO_ 2048 Unsigned: 8 ECU\n"
"SG_ State : 0|64@1+ (1,0) [0|2e20] \"\" ECU\n" "SG_ State : 0|64@1+ (1,0) [0|2e20] \"\" ECU\n"
"VAL_ 2147485696 State 9223372036854775808 \"High\" 18446744073709551615 \"Full\";\n"); "VAL_ 2147485696 State 9223372036854775808 \"High\" 18446744073709551615 \"Full\";\n");
const auto parsed = DbcParser().ParseFile(fixture.path.string()); const DbcDatabase parsed = DbcParser().ParseFile(fixture.path.string());
const auto runtime = DbcDecodeBuilder().Build(parsed); const DecodeDatabase runtime = DbcDecodeBuilder().Build(parsed);
const auto tree = DbcTreeBuilder().Build(parsed); const std::unique_ptr<TreeNode> tree = DbcTreeBuilder().Build(parsed);
CHECK(tree->GetChild(0)->GetSignal() == nullptr, "Frame node"); CHECK(tree->GetChild(0)->GetSignal() == nullptr, "Frame node");
for (std::size_t index = 0; index < parsed.frames.size(); ++index) { for (std::size_t index = 0; index < parsed.frames.size(); ++index) {
CHECK(tree->GetChild(index)->GetChild(0)->GetSignal()->valueDescriptions == CHECK(tree->GetChild(index)->GetChild(0)->GetSignal()->valueDescriptions ==
@@ -444,9 +446,9 @@ void DbcParserTest::valueDescriptions() {
const std::string expected[] = {"Off", "On", "Error with spaces; and \"quotes\"", ""}; const std::string expected[] = {"Off", "On", "Error with spaces; and \"quotes\"", ""};
for (unsigned value = 0; value < 5; ++value) { for (unsigned value = 0; value < 5; ++value) {
frame.data[0] = value; frame.data[0] = value;
const auto decoded = DbcDecoder().Decode(runtime, frame).signals[0]; const DecodedSignalValue decoded = DbcDecoder().Decode(runtime, frame).signals[0];
CHECK(decoded.valid && decoded.physicalValue == value * 2 + 10, "Numeric scaling unchanged"); CHECK(decoded.valid && decoded.physicalValue == value * 2 + 10, "Numeric scaling unchanged");
const auto description = decoded.GetValueDescription(); const std::string *description = decoded.GetValueDescription();
CHECK(value < 4 ? description && *description == expected[value] : !description, CHECK(value < 4 ? description && *description == expected[value] : !description,
"Raw lookup and unknown value"); "Raw lookup and unknown value");
} }
@@ -455,21 +457,21 @@ void DbcParserTest::valueDescriptions() {
std::uint64_t bits; std::uint64_t bits;
const char *description; const char *description;
}; };
for (const auto &test: {Boundary{0x8000000000000000ULL, "Minimum"}, for (const Boundary &test: {Boundary{0x8000000000000000ULL, "Minimum"},
Boundary{0xFFFFFFFFFFFFFFFFULL, "Negative"}, Boundary{0xFFFFFFFFFFFFFFFFULL, "Negative"},
Boundary{0x7FFFFFFFFFFFFFFFULL, "Maximum"}}) { Boundary{0x7FFFFFFFFFFFFFFFULL, "Maximum"}}) {
for (unsigned byte = 0; byte < 8; ++byte) for (unsigned byte = 0; byte < 8; ++byte)
frame.data[byte] = static_cast<std::uint8_t>(test.bits >> (byte * 8)); frame.data[byte] = static_cast<std::uint8_t>(test.bits >> (byte * 8));
const auto decoded = DbcDecoder().Decode(runtime, frame).signals[0]; const DecodedSignalValue decoded = DbcDecoder().Decode(runtime, frame).signals[0];
CHECK(decoded.GetValueDescription() && *decoded.GetValueDescription() == test.description, CHECK(decoded.GetValueDescription() && *decoded.GetValueDescription() == test.description,
"Signed boundaries and distinct extended frame"); "Signed boundaries and distinct extended frame");
} }
frame.canId = 2048; frame.canId = 2048;
for (const auto &test: {Boundary{0x8000000000000000ULL, "High"}, for (const Boundary &test: {Boundary{0x8000000000000000ULL, "High"},
Boundary{0xFFFFFFFFFFFFFFFFULL, "Full"}}) { Boundary{0xFFFFFFFFFFFFFFFFULL, "Full"}}) {
for (unsigned byte = 0; byte < 8; ++byte) for (unsigned byte = 0; byte < 8; ++byte)
frame.data[byte] = static_cast<std::uint8_t>(test.bits >> (byte * 8)); frame.data[byte] = static_cast<std::uint8_t>(test.bits >> (byte * 8));
const auto decoded = DbcDecoder().Decode(runtime, frame).signals[0]; const DecodedSignalValue decoded = DbcDecoder().Decode(runtime, frame).signals[0];
CHECK(decoded.GetValueDescription() && *decoded.GetValueDescription() == test.description, CHECK(decoded.GetValueDescription() && *decoded.GetValueDescription() == test.description,
"Unsigned boundaries"); "Unsigned boundaries");
} }
@@ -494,7 +496,7 @@ void DbcParserTest::invalidValueDescriptions() {
"VAL_ 4294967296 State 0 \"Bad ID\";", "VAL_ Environment 0 \"Unsupported\";", "VAL_ 4294967296 State 0 \"Bad ID\";", "VAL_ Environment 0 \"Unsupported\";",
"VAL_ 1 State 0 \"Bad\\q escape\";"}) { "VAL_ 1 State 0 \"Bad\\q escape\";"}) {
fixture.Write(definitions + statement + '\n'); fixture.Write(definitions + statement + '\n');
const auto line = statement.find('\n') == std::string::npos ? 3 : 4; const int line = statement.find('\n') == std::string::npos ? 3 : 4;
try { try {
DbcParser().ParseFile(fixture.path.string()); DbcParser().ParseFile(fixture.path.string());
CHECK(false, statement); CHECK(false, statement);
+2 -2
View File
@@ -16,7 +16,7 @@ int main() {
frame.signals.push_back(signal); frame.signals.push_back(signal);
database.frames.push_back(frame); database.frames.push_back(frame);
database.frames.push_back(frame); database.frames.push_back(frame);
auto root = DbcTreeBuilder().Build(database); std::unique_ptr<TreeNode> root = DbcTreeBuilder().Build(database);
Check(root->GetParent() == nullptr && root->GetRow() == 0); Check(root->GetParent() == nullptr && root->GetRow() == 0);
root->AddChild(nullptr); root->AddChild(nullptr);
Check(root->GetChildCount() == 2 && !root->GetChild(2)); Check(root->GetChildCount() == 2 && !root->GetChild(2));
@@ -28,6 +28,6 @@ int main() {
Check(leaf->GetParent() == child && leaf->GetRow() == position); Check(leaf->GetParent() == child && leaf->GetRow() == position);
} }
} }
auto transferred = std::move(root); std::unique_ptr<TreeNode> transferred = std::move(root);
Check(transferred->GetChild(0)->GetParent() == transferred.get()); Check(transferred->GetChild(0)->GetParent() == transferred.get());
} }