diff --git a/core/core_files.pri b/core/core_files.pri index 4d03125..0fb9f27 100644 --- a/core/core_files.pri +++ b/core/core_files.pri @@ -19,4 +19,5 @@ HEADERS += \ $$PWD/signal_info.h \ $$PWD/value_descriptions.h \ $$PWD/tree_node.h \ + $$PWD/text_utils.h \ $$PWD/typefactory.h diff --git a/core/dbc_decode_builder.cpp b/core/dbc_decode_builder.cpp index 2dc98c7..7504af5 100644 --- a/core/dbc_decode_builder.cpp +++ b/core/dbc_decode_builder.cpp @@ -14,8 +14,7 @@ DecodeDatabase DbcDecodeBuilder::Build(const DbcDatabase &source) const { DecodeDatabase result; - for (std::size_t frameIndex = 0U; frameIndex < source.frames.size(); ++frameIndex) { - const FrameInfo &sourceFrame = source.frames[frameIndex]; + for (const FrameInfo &sourceFrame : source.frames) { DecodeFrame targetFrame; targetFrame.name = sourceFrame.name; @@ -27,8 +26,7 @@ DecodeDatabase DbcDecodeBuilder::Build(const DbcDatabase &source) const { targetFrame.transmitter = sourceFrame.transmitter; targetFrame.comment = sourceFrame.comment; - for (std::size_t signalIndex = 0U; signalIndex < sourceFrame.signals.size(); ++signalIndex) { - const SignalInfo &sourceSignal = sourceFrame.signals[signalIndex]; + for (const SignalInfo &sourceSignal : sourceFrame.signals) { DecodeSignal targetSignal; targetSignal.multiplexing = sourceSignal.multiplexing; diff --git a/core/dbc_decoder.cpp b/core/dbc_decoder.cpp index 2124b3c..4301764 100644 --- a/core/dbc_decoder.cpp +++ b/core/dbc_decoder.cpp @@ -39,73 +39,80 @@ DecodedFrameValue DbcDecoder::Decode(const DecodeDatabase &database, result.definition = definition; result.valid = true; - const DecodeSignal *selector = nullptr; - std::size_t selectorCount = 0U; - for (const DecodeSignal &signal : definition->signals) { - if (signal.multiplexing.role == MultiplexRole::Selector) { - selector = &signal; - ++selectorCount; - } - } - const bool selectorDefinitionValid = selectorCount == 1U && - selector->valueType == ValueType::Unsigned && selector->length > 0U && selector->length <= 64U; - std::uint64_t selectorValue = 0U; - const bool selectorAvailable = selectorDefinitionValid && ExtractUnsigned(frame.data, *selector, selectorValue); - - for (std::size_t signalIndex = 0U; signalIndex < definition->signals.size(); ++signalIndex) { - const DecodeSignal &signal = definition->signals[signalIndex]; - DecodedSignalValue decoded; - 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(&decoded.rawValue)) - numericValue = static_cast(*signedValue); - else - numericValue = static_cast(std::get(decoded.rawValue)); - decoded.physicalValue = numericValue * signal.factor + signal.offset; - decoded.valid = true; - decoded.status = SignalDecodeStatus::Valid; - - result.signals.push_back(decoded); - } + const SelectorState selector = ReadSelector(*definition, frame); + for (const DecodeSignal &signal : definition->signals) + result.signals.push_back(DecodeOneSignal(signal, frame, selector)); 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(&decoded.rawValue)) + numericValue = static_cast(*signedValue); + else + numericValue = static_cast(std::get(decoded.rawValue)); + decoded.physicalValue = numericValue * signal.factor + signal.offset; + decoded.valid = true; + decoded.status = SignalDecodeStatus::Valid; + + return decoded; +} + bool DbcDecoder::ExtractUnsigned(const std::vector &data, const DecodeSignal &signal, std::uint64_t &value) { diff --git a/core/dbc_decoder.h b/core/dbc_decoder.h index b6c62ab..4ab5f56 100644 --- a/core/dbc_decoder.h +++ b/core/dbc_decoder.h @@ -22,13 +22,11 @@ * @brief Raw CAN frame used for runtime or trace decoding. */ struct RawCanFrame { - std::uint32_t canId; /**< Normalized CAN ID. */ - bool isExtended; /**< true for extended frame. */ - std::vector data; /**< Payload bytes. */ + std::uint32_t canId = 0U; /**< Normalized CAN ID. */ + bool isExtended = false; /**< true for extended frame. */ + std::vector data; /**< Payload bytes. */ - RawCanFrame() - : canId(0U), isExtended(false), data() { - } + RawCanFrame() = default; }; enum class SignalDecodeStatus { @@ -43,10 +41,10 @@ enum class SignalDecodeStatus { * @brief One decoded signal value. */ struct DecodedSignalValue { - const DecodeSignal *definition; /**< Signal definition. */ - RawSignalValue rawValue; /**< Exact signed or unsigned raw value. */ - double physicalValue; /**< Converted physical value. */ - bool valid; /**< true if decoding succeeded. */ + const DecodeSignal *definition = nullptr; /**< Signal definition. */ + RawSignalValue rawValue = std::uint64_t(0U); /**< Exact signed or unsigned raw value. */ + double physicalValue = 0.0; /**< Converted physical value. */ + bool valid = false; /**< true if decoding succeeded. */ SignalDecodeStatus status = SignalDecodeStatus::InvalidDefinition; /** 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; } - DecodedSignalValue() - : definition(nullptr), rawValue(std::uint64_t(0U)), physicalValue(0.0), valid(false) { - } + DecodedSignalValue() = default; }; /** * @brief Fully decoded frame. */ struct DecodedFrameValue { - const DecodeFrame *definition; /**< Frame definition. */ - std::vector signals; /**< Decoded signal values. */ - bool valid; /**< true if frame was matched. */ + const DecodeFrame *definition = nullptr; /**< Frame definition. */ + std::vector signals; /**< Decoded signal values. */ + bool valid = false; /**< true if frame was matched. */ - DecodedFrameValue() - : definition(nullptr), signals(), valid(false) { - } + DecodedFrameValue() = default; }; /** @@ -98,6 +92,18 @@ public: const RawCanFrame &frame) const; 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 &data, const DecodeSignal &signal, std::uint64_t &value); diff --git a/core/dbc_parser.cpp b/core/dbc_parser.cpp index b0ea5a0..ee10755 100644 --- a/core/dbc_parser.cpp +++ b/core/dbc_parser.cpp @@ -12,31 +12,10 @@ #include "dbc_parser.h" #include "dbc_statement_handler.h" -#include +#include "text_utils.h" #include #include -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(text[begin]))) - ++begin; - - std::string::size_type end = text.size(); - while ((end > begin) && - std::isspace(static_cast(text[end - 1U]))) - --end; - - return text.substr(begin, end - begin); - } -}// namespace - DbcDatabase DbcParser::ParseFile(const std::string &filePath) const { std::ifstream input(filePath.c_str()); if (!input.is_open()) @@ -50,7 +29,7 @@ DbcDatabase DbcParser::ParseFile(const std::string &filePath) const { std::string line; while (std::getline(input, line)) { ++lineNumber; - line = Trim(line); + line = dbc_detail::TrimText(line); if (line.empty()) continue; @@ -67,7 +46,3 @@ DbcDatabase DbcParser::ParseFile(const std::string &filePath) const { context.ValidateMultiplexing(filePath); return database; } - -std::string DbcParser::Trim(const std::string &text) { - return TrimText(text); -} diff --git a/core/dbc_parser.h b/core/dbc_parser.h index 606eb4b..fcf3322 100644 --- a/core/dbc_parser.h +++ b/core/dbc_parser.h @@ -24,11 +24,11 @@ * - SG_ * - CM_ BO_ * - CM_ SG_ + * - basic M / mN multiplexing * - VAL_ signal value descriptions * * Ignores: * - attributes - * - multiplexing * - named VAL_TABLE_ tables */ class DbcParser { @@ -42,8 +42,6 @@ public: */ DbcDatabase ParseFile(const std::string &filePath) const; -private: - static std::string Trim(const std::string &text); }; #endif /* DBC_PARSER_H */ diff --git a/core/dbc_statement_handler.cpp b/core/dbc_statement_handler.cpp index d69690d..44d8cc2 100644 --- a/core/dbc_statement_handler.cpp +++ b/core/dbc_statement_handler.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT #include "dbc_statement_handler.h" -#include +#include "text_utils.h" #include #include #include @@ -13,7 +13,7 @@ namespace { std::uint32_t maximum, const char *field) { 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() || parsed.ptr != text.data() + text.size() || value > maximum) throw std::runtime_error(std::string("Invalid ") + field + ": " + text); @@ -37,27 +37,92 @@ namespace { return value; } - /** - * @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(text[begin]))) - ++begin; + 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; + } - std::string::size_type end = text.size(); - while ((end > begin) && - std::isspace(static_cast(text[end - 1U]))) - --end; - - return text.substr(begin, end - begin); + 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 namespace { std::vector SplitReceivers(const std::string &text) { @@ -66,7 +131,7 @@ namespace { std::istringstream stream(text); while (std::getline(stream, token, ',')) { - token = TrimText(token); + token = dbc_detail::TrimText(token); if (!token.empty()) receivers.push_back(token); } @@ -74,14 +139,12 @@ namespace { return receivers; } - void NormalizeCanId(std::uint32_t rawCanId, - std::uint32_t &normalizedCanId, - bool &isExtended) { + 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 - */ + * 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); @@ -115,7 +178,7 @@ namespace { return pgn; } -}// namespace +} // namespace 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) { // 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( - 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; if (!std::regex_match(line, fields, syntax)) throw std::runtime_error("Malformed or unsupported SG_ signal definition."); SignalInfo signal; - std::istringstream nameFields(fields[1].str()); + 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."); - if (marker == "M") - signal.multiplexing.role = MultiplexRole::Selector; - else if (!marker.empty()) { - if (marker[0] != 'm') - throw std::runtime_error("Unsupported multiplexing marker."); - const std::string value = marker.substr(1U); - const std::from_chars_result parsed = std::from_chars( - value.data(), value.data() + value.size(), signal.multiplexing.value); - if (value.empty() || parsed.ec != std::errc() || parsed.ptr != value.data() + value.size()) - throw std::runtime_error("Invalid or unsupported multiplexing marker."); - signal.multiplexing.role = MultiplexRole::Branch; - } + signal.multiplexing = ParseMultiplexingMarker(marker); - signal.startBit = ParseDecimal(fields[2].str(), + signal.startBit = ParseDecimal(fields[StartBit].str(), std::numeric_limits::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) throw std::runtime_error("Signal length must be within 1..64 bits."); - signal.isLittleEndian = fields[4].str() == "1"; - signal.isSigned = fields[5].str() == "-"; + signal.isLittleEndian = fields[ByteOrder].str() == "1"; + signal.isSigned = fields[Signedness].str() == "-"; - signal.factor = ParsePhysicalValue(fields[6].str(), "signal factor"); - signal.offset = ParsePhysicalValue(fields[7].str(), "signal offset"); - signal.minimum = ParsePhysicalValue(fields[8].str(), "signal minimum"); - signal.maximum = ParsePhysicalValue(fields[9].str(), "signal maximum"); + 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[10].str(); - signal.receivers = SplitReceivers(fields[11].str()); + signal.unit = fields[Unit].str(); + signal.receivers = SplitReceivers(fields[Receivers].str()); return signal; } @@ -222,71 +287,52 @@ void DbcCommentHandler::ParseCommentLine(const std::string &line, DbcDatabase &d return; stream >> token; + if (token != "BO_" && token != "SG_") + return; - if (token == "BO_") { - std::uint32_t rawCanId = 0U; - std::uint32_t canId = 0U; - bool isExtended = false; - - stream >> rawCanId; - 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; + 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); - 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('"'); - 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) { - SignalInfo *signal = FindSignalByName(*frame, signalName); - if (signal != nullptr) - signal->comment = line.substr(quoteBegin + 1U, quoteEnd - quoteBegin - 1U); - } + 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 (std::size_t index = 0U; index < database.frames.size(); ++index) { - if ((database.frames[index].canId == canId) && - (database.frames[index].isExtended == isExtended)) - return &database.frames[index]; + 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 (std::size_t index = 0U; index < frame.signals.size(); ++index) { - if (frame.signals[index].name == signalName) - return &frame.signals[index]; + for (SignalInfo &signal : frame.signals) { + if (signal.name == signalName) + return &signal; } 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) { if (bit >= payloadBits) throw std::runtime_error("Signal exceeds the frame payload length."); - if (signal.isLittleEndian) - ++bit; - else - bit = (bit % 8U == 0U) ? bit + 15U : bit - 1U; + bit = NextSignalBit(bit, signal.isLittleEndian); } context.currentFrame->signals.push_back(std::move(signal)); context.signalLines.back().push_back(context.lineNumber); @@ -335,7 +378,7 @@ bool DbcStatementDispatcher::Dispatch(const std::string &line, std::istringstream stream(line); std::string keyword; stream >> keyword; - const auto handler = m_handlers.find(keyword); + const HandlerMap::const_iterator handler = m_handlers.find(keyword); if (handler == m_handlers.end()) return false; handler->second->Handle(line, context); @@ -348,7 +391,7 @@ void DbcValueDescriptionHandler::Handle(const std::string &line, DbcParseContext std::smatch fields; if (!std::regex_match(line, fields, header)) throw std::runtime_error("Malformed or unsupported VAL_ statement."); - const auto rawId = ParseDecimal(fields[1].str(), std::numeric_limits::max(), "VAL_ CAN ID"); + const std::uint32_t rawId = ParseDecimal(fields[1].str(), std::numeric_limits::max(), "VAL_ CAN ID"); if ((rawId & 0x60000000U) != 0U) throw std::runtime_error("VAL_ CAN ID exceeds the 29-bit identifier range."); PendingValueDescriptions pending; @@ -360,34 +403,15 @@ void DbcValueDescriptionHandler::Handle(const std::string &line, DbcParseContext 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(); - RawSignalValue key; - if (!number.empty() && number[0] == '-') { - 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]; - } + 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 auto consumed = static_cast(fields.length()); + const std::size_t consumed = static_cast(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 = TrimText(rest); + rest = dbc_detail::TrimText(rest); } if (pending.descriptions.empty()) 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) { - for (const auto &pending: pendingValues) { + for (const PendingValueDescriptions &pending: pendingValues) { try { - FrameInfo *frame = nullptr; - for (auto &candidate: database.frames) { - if (candidate.canId == pending.canId && candidate.isExtended == pending.isExtended) { - 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) { + 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); } @@ -437,14 +443,43 @@ namespace { std::uint64_t bit = signal.startBit; for (std::uint32_t index = 0U; index < signal.length; ++index) { bits.push_back(bit); - if (signal.isLittleEndian) - ++bit; - else - bit = bit % 8U == 0U ? bit + 15U : bit - 1U; + 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 &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) { const std::vector firstBits = SignalBits(first); const std::vector secondBits = SignalBits(second); @@ -461,31 +496,13 @@ 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 = nullptr; + 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]; - if (signal.multiplexing.role != MultiplexRole::Selector) - continue; - if (selector != nullptr || signal.isSigned) - throw std::runtime_error(filePath + ":" + std::to_string(signalLines[frameIndex][index]) + - ": Multiplexing requires one unsigned selector per frame."); - selector = &signal; - } - for (std::size_t index = 0U; index < frame.signals.size(); ++index) { - const SignalInfo &signal = frame.signals[index]; - std::string error; - if (signal.multiplexing.role == MultiplexRole::Branch) { - if (selector == nullptr) - error = "Multiplexed branch has no selector."; - else if (selector->length < 64U && signal.multiplexing.value >= (std::uint64_t(1U) << selector->length)) - error = "Branch value exceeds selector raw range."; - } + std::string error = ValidateBranch(signal, selector); for (std::size_t previous = 0U; previous < index && error.empty(); ++previous) { const SignalInfo &other = frame.signals[previous]; - const bool exclusive = signal.multiplexing.role == MultiplexRole::Branch && - other.multiplexing.role == MultiplexRole::Branch && - signal.multiplexing.value != other.multiplexing.value; - if (!exclusive && SignalsOverlap(signal, other)) + if (!AreMutuallyExclusive(signal, other) && SignalsOverlap(signal, other)) error = "Signals overlap without mutually exclusive multiplexing conditions."; } if (!error.empty()) diff --git a/core/dbc_statement_handler.h b/core/dbc_statement_handler.h index c0512fa..9b141bf 100644 --- a/core/dbc_statement_handler.h +++ b/core/dbc_statement_handler.h @@ -83,8 +83,9 @@ public: bool Dispatch(const std::string &line, DbcParseContext &context) const; private: + using HandlerMap = std::unordered_map>; TypeFactory m_factory; - std::unordered_map> m_handlers; + HandlerMap m_handlers; }; #endif /* DBC_STATEMENT_HANDLER_H */ diff --git a/core/dbc_tree_builder.cpp b/core/dbc_tree_builder.cpp index 9013ae5..d9551dd 100644 --- a/core/dbc_tree_builder.cpp +++ b/core/dbc_tree_builder.cpp @@ -12,15 +12,13 @@ #include "dbc_tree_builder.h" std::unique_ptr DbcTreeBuilder::Build(const DbcDatabase &database) const { - std::unique_ptr root(new TreeNode()); + std::unique_ptr root = std::make_unique(); - for (std::size_t frameIndex = 0U; frameIndex < database.frames.size(); ++frameIndex) { - const FrameInfo &frame = database.frames[frameIndex]; - std::unique_ptr frameNode(new TreeNode(frame)); + for (const FrameInfo &frame : database.frames) { + std::unique_ptr frameNode = std::make_unique(frame); - for (std::size_t signalIndex = 0U; signalIndex < frame.signals.size(); ++signalIndex) { - const SignalInfo &signal = frame.signals[signalIndex]; - std::unique_ptr signalNode(new TreeNode(signal)); + for (const SignalInfo &signal : frame.signals) { + std::unique_ptr signalNode = std::make_unique(signal); frameNode->AddChild(std::move(signalNode)); } diff --git a/core/decode_database.h b/core/decode_database.h index 69c60de..671947f 100644 --- a/core/decode_database.h +++ b/core/decode_database.h @@ -40,54 +40,48 @@ enum class ValueType { */ struct DecodeSignal { Multiplexing multiplexing; - std::string name; /**< Signal name. */ - std::uint32_t startBit; /**< DBC start bit. */ - std::uint32_t length; /**< Signal length in bits. */ - ByteOrder byteOrder; /**< Intel or Motorola. */ - ValueType valueType; /**< Signed or unsigned. */ - double factor; /**< Scaling factor. */ - double offset; /**< Physical offset. */ - double minimum; /**< Minimum physical value. */ - double maximum; /**< Maximum physical value. */ - std::string unit; /**< Physical unit. */ - std::vector receivers; /**< Receivers. */ - ValueDescriptions valueDescriptions; /**< Raw integer state descriptions. */ - std::string comment; /**< Comment. */ + std::string name; /**< Signal name. */ + std::uint32_t startBit = 0U; /**< DBC start bit. */ + std::uint32_t length = 0U; /**< Signal length in bits. */ + ByteOrder byteOrder = ByteOrder::Intel; /**< Intel or Motorola. */ + ValueType valueType = ValueType::Unsigned; /**< Signed or unsigned. */ + double factor = 1.0; /**< Scaling factor. */ + double offset = 0.0; /**< Physical offset. */ + double minimum = 0.0; /**< Minimum physical value. */ + double maximum = 0.0; /**< Maximum physical value. */ + std::string unit; /**< Physical unit. */ + std::vector receivers; /**< Receivers. */ + ValueDescriptions valueDescriptions; /**< Raw integer state descriptions. */ + std::string comment; /**< Comment. */ - DecodeSignal() - : 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() { - } + DecodeSignal() = default; }; /** * @brief Runtime-ready frame definition. */ struct DecodeFrame { - std::string name; /**< Frame name. */ - std::uint32_t canId; /**< Normalized CAN ID. */ - bool isExtended; /**< true for extended frame. */ - std::uint8_t dlc; /**< Payload length. */ - std::uint32_t pgn; /**< PGN if available. */ - bool hasPgn; /**< true if PGN is valid. */ - std::string transmitter; /**< Transmitter ECU. */ - std::string comment; /**< Frame comment. */ - std::vector signals; /**< Signal definitions. */ + std::string name; /**< Frame name. */ + std::uint32_t canId = 0U; /**< Normalized CAN ID. */ + bool isExtended = false; /**< true for extended frame. */ + std::uint8_t dlc = 0U; /**< Payload length. */ + std::uint32_t pgn = 0U; /**< PGN if available. */ + bool hasPgn = false; /**< true if PGN is valid. */ + std::string transmitter; /**< Transmitter ECU. */ + std::string comment; /**< Frame comment. */ + std::vector signals; /**< Signal definitions. */ - DecodeFrame() - : name(), canId(0U), isExtended(false), dlc(0U), pgn(0U), hasPgn(false), transmitter(), comment(), signals() { - } + DecodeFrame() = default; }; /** * @brief Key for fast frame lookup. */ struct FrameKey { - std::uint32_t canId; - bool isExtended; + std::uint32_t canId = 0U; + bool isExtended = false; - FrameKey() - : canId(0U), isExtended(false) { - } + FrameKey() = default; FrameKey(std::uint32_t id, bool extended) : canId(id), isExtended(extended) { diff --git a/core/frame_info.h b/core/frame_info.h index ef695f6..63da875 100644 --- a/core/frame_info.h +++ b/core/frame_info.h @@ -22,19 +22,17 @@ * @brief Describes one CAN frame from a DBC file. */ struct FrameInfo { - std::string name; /**< Frame name. */ - std::uint32_t canId; /**< Normalized CAN identifier. */ - bool isExtended; /**< true for extended frame. */ - std::uint32_t pgn; /**< J1939 PGN if applicable. */ - bool hasPgn; /**< true if PGN was derived. */ - std::uint8_t dlc; /**< Frame payload length. */ - std::string transmitter; /**< Transmitter ECU name. */ - std::string comment; /**< Optional frame comment. */ - std::vector signals; /**< Signals contained in the frame. */ + std::string name; /**< Frame name. */ + std::uint32_t canId = 0U; /**< Normalized CAN identifier. */ + bool isExtended = false; /**< true for extended frame. */ + std::uint32_t pgn = 0U; /**< J1939 PGN if applicable. */ + bool hasPgn = false; /**< true if PGN was derived. */ + std::uint8_t dlc = 0U; /**< Frame payload length. */ + std::string transmitter; /**< Transmitter ECU name. */ + std::string comment; /**< Optional frame comment. */ + std::vector signals; /**< Signals contained in the frame. */ - FrameInfo() - : name(), canId(0U), isExtended(false), pgn(0U), hasPgn(false), dlc(0U), transmitter(), comment(), signals() { - } + FrameInfo() = default; }; #endif /* FRAME_INFO_H */ diff --git a/core/signal_info.h b/core/signal_info.h index 5afb8c0..16bf7f7 100644 --- a/core/signal_info.h +++ b/core/signal_info.h @@ -23,23 +23,21 @@ */ struct SignalInfo { Multiplexing multiplexing; - std::string name; /**< Signal name. */ - std::uint32_t startBit; /**< Start bit in DBC notation. */ - std::uint32_t length; /**< Signal length in bits. */ - bool isLittleEndian; /**< true for Intel, false for Motorola. */ - bool isSigned; /**< true if signal is signed. */ - double factor; /**< Scaling factor. */ - double offset; /**< Physical offset. */ - double minimum; /**< Minimum physical value. */ - double maximum; /**< Maximum physical value. */ - std::string unit; /**< Physical unit. */ - std::vector receivers; /**< Signal receivers. */ - ValueDescriptions valueDescriptions; /**< Raw integer state descriptions. */ - std::string comment; /**< Optional signal comment. */ + std::string name; /**< Signal name. */ + std::uint32_t startBit = 0U; /**< Start bit in DBC notation. */ + std::uint32_t length = 0U; /**< Signal length in bits. */ + bool isLittleEndian = true; /**< true for Intel, false for Motorola. */ + bool isSigned = false; /**< true if signal is signed. */ + double factor = 1.0; /**< Scaling factor. */ + double offset = 0.0; /**< Physical offset. */ + double minimum = 0.0; /**< Minimum physical value. */ + double maximum = 0.0; /**< Maximum physical value. */ + std::string unit; /**< Physical unit. */ + std::vector receivers; /**< Signal receivers. */ + ValueDescriptions valueDescriptions; /**< Raw integer state descriptions. */ + std::string comment; /**< Optional signal comment. */ - SignalInfo() - : name(), startBit(0U), length(0U), isLittleEndian(true), isSigned(false), factor(1.0), offset(0.0), minimum(0.0), maximum(0.0), unit(), receivers(), comment() { - } + SignalInfo() = default; }; #endif /* SIGNAL_INFO_H */ diff --git a/core/text_utils.h b/core/text_utils.h new file mode 100644 index 0000000..a5ad3f8 --- /dev/null +++ b/core/text_utils.h @@ -0,0 +1,23 @@ +#ifndef DBC_TEXT_UTILS_H +#define DBC_TEXT_UTILS_H + +#include +#include + +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(text[begin]))) + ++begin; + + std::string::size_type end = text.size(); + while ((end > begin) && + std::isspace(static_cast(text[end - 1U]))) + --end; + + return text.substr(begin, end - begin); + } +} + +#endif diff --git a/core/tree_node.cpp b/core/tree_node.cpp index 7bb19ee..98cd7a5 100644 --- a/core/tree_node.cpp +++ b/core/tree_node.cpp @@ -12,15 +12,15 @@ #include "tree_node.h" 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) - : 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(frame)) { } 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(signal)) { } void TreeNode::AddChild(std::unique_ptr child) { diff --git a/core/typefactory.h b/core/typefactory.h index b29d14f..094ff77 100644 --- a/core/typefactory.h +++ b/core/typefactory.h @@ -48,8 +48,8 @@ * factory.registerType("one"); * factory.registerType("two"); * - * auto a = factory.create("one", 10); - * auto b = factory.creator("two")(10); // advanced API: get creator function + * std::shared_ptr a = factory.create("one", 10); + * std::shared_ptr b = factory.creator("two")(10); // advanced API: get creator function * * (void)a->get(); * (void)b->get(); diff --git a/core/value_descriptions.h b/core/value_descriptions.h index 03b8696..7d95201 100644 --- a/core/value_descriptions.h +++ b/core/value_descriptions.h @@ -11,7 +11,7 @@ using RawSignalValue = std::variant; /** Canonical keys: negative int64_t, nonnegative uint64_t. */ inline RawSignalValue NormalizeValueKey(const RawSignalValue &value) { - if (const auto *number = std::get_if(&value)) { + if (const std::int64_t *number = std::get_if(&value)) { if (*number >= 0) return static_cast(*number); } @@ -23,7 +23,7 @@ using ValueDescriptions = std::map; /** Returns a borrowed string, or nullptr; never converts integers to double. */ inline const std::string *FindValueDescription(const ValueDescriptions &descriptions, 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; } diff --git a/examples/main.cpp b/examples/main.cpp index f920761..914869c 100644 --- a/examples/main.cpp +++ b/examples/main.cpp @@ -80,15 +80,14 @@ static void PrintDecodedFrame(const DecodedFrameValue &decoded) { std::cout << "Decoded frame: " << decoded.definition->name << "\n"; - for (std::size_t index = 0U; index < decoded.signals.size(); ++index) { - const DecodedSignalValue &signal = decoded.signals[index]; + for (const DecodedSignalValue &signal : decoded.signals) { if ((signal.definition == nullptr) || !signal.valid) continue; std::cout << " " << signal.definition->name << " raw="; - if (const auto *signedValue = std::get_if(&signal.rawValue)) + if (const std::int64_t *signedValue = std::get_if(&signal.rawValue)) std::cout << *signedValue; else std::cout << std::get(signal.rawValue); diff --git a/qt/dbc_tree_model.cpp b/qt/dbc_tree_model.cpp index ab08e1d..d59686b 100644 --- a/qt/dbc_tree_model.cpp +++ b/qt/dbc_tree_model.cpp @@ -5,6 +5,23 @@ #include #include +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(&entry.first)) + pair.insert("value", QVariant::fromValue(static_cast(*number))); + else + pair.insert("value", QVariant::fromValue( + static_cast(std::get(entry.first)))); + pair.insert("description", QString::fromStdString(entry.second)); + descriptions.append(pair); + } + return descriptions; + } +} // namespace + DbcTreeModel::DbcTreeModel(QObject *parent) : QAbstractItemModel(parent) {} void DbcTreeModel::SetTree(std::unique_ptr root) { @@ -60,38 +77,40 @@ QVariant DbcTreeModel::data(const QModelIndex &item, int role) const { return {}; const FrameInfo *frame = node->GetFrame(); 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) return comment; if (role == NodeTypeRole) return static_cast(node->GetType()); if (role == ValueDescriptionsRole && signal) { - QVariantList descriptions; - for (const auto &entry: signal->valueDescriptions) { - QVariantMap pair; - if (const auto *number = std::get_if(&entry.first)) - pair.insert("value", QVariant::fromValue(static_cast(*number))); - else - pair.insert("value", QVariant::fromValue(static_cast(std::get(entry.first)))); - pair.insert("description", QString::fromStdString(entry.second)); - descriptions.append(pair); - } - return descriptions; + return ToVariantList(signal->valueDescriptions); } if (frame) { - if (role == CanIdRole) return frame->canId; - if (role == PayloadLengthRole) return static_cast(frame->dlc); - if (role == ExtendedRole) return frame->isExtended; + if (role == CanIdRole) + return frame->canId; + if (role == PayloadLengthRole) + return static_cast(frame->dlc); + if (role == ExtendedRole) + return frame->isExtended; } if (signal) { - if (role == StartBitRole) return signal->startBit; - if (role == BitLengthRole) return signal->length; - if (role == LittleEndianRole) return signal->isLittleEndian; - if (role == SignedRole) return signal->isSigned; - if (role == UnitRole) return QString::fromStdString(signal->unit); + if (role == StartBitRole) + return signal->startBit; + if (role == BitLengthRole) + return signal->length; + 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()) { case Name: return QString::fromStdString(node->GetName()); diff --git a/tests/dbc_decoder_test.cpp b/tests/dbc_decoder_test.cpp index ff08c1d..34ec8d1 100644 --- a/tests/dbc_decoder_test.cpp +++ b/tests/dbc_decoder_test.cpp @@ -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 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; } @@ -76,19 +76,25 @@ void DbcDecoderTest::decode() { frame.canId = definition.canId; frame.data = test.data; const DecodedFrameValue result = DbcDecoder().Decode(database, frame); - const bool passed = result.valid && - (result.definition == &database.frames[0]) && - (result.signals.size() == 1U) && - (result.signals[0].definition == &database.frames[0].signals[0]) && - (result.signals[0].valid == test.valid) && - (!test.valid || (((test.valueType == ValueType::Signed) - ? (std::get_if(&result.signals[0].rawValue) && - std::get(result.signals[0].rawValue) == test.rawValue) - : (std::get_if(&result.signals[0].rawValue) && - std::get(result.signals[0].rawValue) == - static_cast(test.rawValue))) && - (result.signals[0].physicalValue == test.physicalValue))); - QVERIFY2(passed, test.name); + QVERIFY2(result.valid, test.name); + QCOMPARE(result.definition, &database.frames[0]); + QCOMPARE(result.signals.size(), std::size_t(1U)); + const DecodedSignalValue &decoded = result.signals[0]; + QCOMPARE(decoded.definition, &database.frames[0].signals[0]); + QCOMPARE(decoded.valid, test.valid); + if (!test.valid) + return; + + if (test.valueType == ValueType::Signed) { + const std::int64_t *raw = std::get_if(&decoded.rawValue); + QVERIFY(raw != nullptr); + QCOMPARE(*raw, test.rawValue); + } else { + const std::uint64_t *raw = std::get_if(&decoded.rawValue); + QVERIFY(raw != nullptr); + QCOMPARE(*raw, static_cast(test.rawValue)); + } + QCOMPARE(decoded.physicalValue, test.physicalValue); } void DbcDecoderTest::boundaries() { @@ -115,13 +121,13 @@ void DbcDecoderTest::boundaries() { const unsigned shift = (order == ByteOrder::Intel ? byte : 7U - byte) * 8U; frame.data.push_back(static_cast(bits >> shift)); } - const auto result = DbcDecoder().Decode(database, frame); + const DecodedFrameValue result = DbcDecoder().Decode(database, frame); const std::int64_t signedExpected = bits <= 0x7FFFFFFFFFFFFFFFULL ? static_cast(bits) : std::numeric_limits::min() + static_cast(bits - 0x8000000000000000ULL); - const auto &decoded = result.signals.at(0U); - const auto expected = type == ValueType::Unsigned + const DecodedSignalValue &decoded = result.signals.at(0U); + const RawSignalValue expected = type == ValueType::Unsigned ? std::variant(bits) : std::variant(signedExpected); const double physical = (type == ValueType::Unsigned @@ -131,7 +137,7 @@ void DbcDecoderTest::boundaries() { 10.0; std::ostringstream actualText; std::ostringstream expectedText; - if (const auto *signedValue = std::get_if(&decoded.rawValue)) + if (const std::int64_t *signedValue = std::get_if(&decoded.rawValue)) actualText << *signedValue; else actualText << std::get(decoded.rawValue); @@ -139,9 +145,10 @@ void DbcDecoderTest::boundaries() { expectedText << bits; else expectedText << signedExpected; - QVERIFY2(decoded.valid && decoded.rawValue == expected && - decoded.physicalValue == physical && actualText.str() == expectedText.str(), - expectedText.str().c_str()); + QVERIFY(decoded.valid); + QVERIFY(decoded.rawValue == expected); + QCOMPARE(decoded.physicalValue, physical); + QCOMPARE(actualText.str(), expectedText.str()); } } } diff --git a/tests/dbc_parser_test.cpp b/tests/dbc_parser_test.cpp index 04bb17b..dbea4e0 100644 --- a/tests/dbc_parser_test.cpp +++ b/tests/dbc_parser_test.cpp @@ -92,16 +92,17 @@ void DbcParserTest::validFrames() { {"BO_ 2147483649 Frame: 8 ECU", 1U, true, 8U}, {"BO_ 2684354559 Frame: 8 ECU", 0x1FFFFFFFU, true, 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'); try { - const auto database = DbcParser().ParseFile(fixture.path.string()); - CHECK(database.frames.size() == 1U && database.frames[0].name == "Frame" && - database.frames[0].canId == test.id && - database.frames[0].isExtended == test.extended && - database.frames[0].dlc == test.length && - database.frames[0].transmitter == "ECU", - test.line); + const DbcDatabase database = DbcParser().ParseFile(fixture.path.string()); + QCOMPARE(database.frames.size(), std::size_t(1U)); + const FrameInfo &frame = database.frames[0]; + QCOMPARE(frame.name, std::string("Frame")); + QCOMPARE(frame.canId, test.id); + QCOMPARE(frame.isExtended, test.extended); + QCOMPARE(static_cast(frame.dlc), test.length); + QCOMPARE(frame.transmitter, std::string("ECU")); } 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: 8.5 ECU", "BO_ 1 Frame: 256 ECU", "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'); try { 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" "CM_ BO_ 256 \"Frame comment\";\nCM_ SG_ 256 Speed \"Signal comment\";\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 && database.frames[0].comment == "Frame comment" && database.frames[0].signals[0].comment == "Signal comment", @@ -324,7 +325,7 @@ void DbcParserTest::validSignals() { fixture.Write("BO_ 1 Frame: 8 ECU\n" + SignalLine("0", "8", "1+", "0", "0", "0", "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 && factors.frames[0].signals[0].factor == 0.0 && factors.frames[0].signals[1].factor == -1.0, @@ -345,26 +346,27 @@ void DbcParserTest::validSignals() { {"56", "1", "0+", 8U}, {"3", "12", "0+", 2U}, {"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" + SignalLine(test.start, test.length, test.order, ".5e+1", "-2.5E-1", "-1.", "+2e2") + "\n"); try { - const auto parsed = signalParser.ParseFile(fixture.path.string()); - const auto &signal = parsed.frames.at(0).signals.at(0); - CHECK(signal.startBit == std::stoul(test.start) && - signal.length == std::stoul(test.length) && - signal.isLittleEndian == (test.order[0] == '1') && - signal.isSigned == (test.order[1] == '-') && - signal.factor == 5.0 && signal.offset == -0.25 && - signal.minimum == -1.0 && signal.maximum == 200.0, - "Valid signal boundary and numeric metadata"); + const DbcDatabase parsed = signalParser.ParseFile(fixture.path.string()); + const SignalInfo &signal = parsed.frames.at(0).signals.at(0); + QCOMPARE(signal.startBit, static_cast(std::stoul(test.start))); + QCOMPARE(signal.length, static_cast(std::stoul(test.length))); + QCOMPARE(signal.isLittleEndian, test.order[0] == '1'); + QCOMPARE(signal.isSigned, test.order[1] == '-'); + QCOMPARE(signal.factor, 5.0); + QCOMPARE(signal.offset, -0.25); + QCOMPARE(signal.minimum, -1.0); + QCOMPARE(signal.maximum, 200.0); } catch (const std::exception &error) { CHECK(false, error.what()); } } fixture.Write("BO_ 1 Frame: 8 ECU\n" "SG_\tValue\t:\t0 | 8 @1+ ( 1 , 0 ) [ 0 | 255 ] \"rpm\" ECU1,\tECU2\n"); try { - const auto parsed = signalParser.ParseFile(fixture.path.string()); - const auto &signal = parsed.frames.at(0).signals.at(0); + const DbcDatabase parsed = signalParser.ParseFile(fixture.path.string()); + const SignalInfo &signal = parsed.frames.at(0).signals.at(0); CHECK(signal.name == "Value" && signal.unit == "rpm" && signal.receivers == std::vector({"ECU1", "ECU2"}), "Signal tabs, spaces, and receiver list"); @@ -405,7 +407,7 @@ void DbcParserTest::independentSessions() { "Independent parse context"); } 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, "Fresh parse after error"); } @@ -424,9 +426,9 @@ void DbcParserTest::valueDescriptions() { "BO_ 2048 Unsigned: 8 ECU\n" "SG_ State : 0|64@1+ (1,0) [0|2e20] \"\" ECU\n" "VAL_ 2147485696 State 9223372036854775808 \"High\" 18446744073709551615 \"Full\";\n"); - const auto parsed = DbcParser().ParseFile(fixture.path.string()); - const auto runtime = DbcDecodeBuilder().Build(parsed); - const auto tree = DbcTreeBuilder().Build(parsed); + const DbcDatabase parsed = DbcParser().ParseFile(fixture.path.string()); + const DecodeDatabase runtime = DbcDecodeBuilder().Build(parsed); + const std::unique_ptr tree = DbcTreeBuilder().Build(parsed); CHECK(tree->GetChild(0)->GetSignal() == nullptr, "Frame node"); for (std::size_t index = 0; index < parsed.frames.size(); ++index) { 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\"", ""}; for (unsigned value = 0; value < 5; ++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"); - const auto description = decoded.GetValueDescription(); + const std::string *description = decoded.GetValueDescription(); CHECK(value < 4 ? description && *description == expected[value] : !description, "Raw lookup and unknown value"); } @@ -455,21 +457,21 @@ void DbcParserTest::valueDescriptions() { std::uint64_t bits; const char *description; }; - for (const auto &test: {Boundary{0x8000000000000000ULL, "Minimum"}, + for (const Boundary &test: {Boundary{0x8000000000000000ULL, "Minimum"}, Boundary{0xFFFFFFFFFFFFFFFFULL, "Negative"}, Boundary{0x7FFFFFFFFFFFFFFFULL, "Maximum"}}) { for (unsigned byte = 0; byte < 8; ++byte) frame.data[byte] = static_cast(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, "Signed boundaries and distinct extended frame"); } frame.canId = 2048; - for (const auto &test: {Boundary{0x8000000000000000ULL, "High"}, + for (const Boundary &test: {Boundary{0x8000000000000000ULL, "High"}, Boundary{0xFFFFFFFFFFFFFFFFULL, "Full"}}) { for (unsigned byte = 0; byte < 8; ++byte) frame.data[byte] = static_cast(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, "Unsigned boundaries"); } @@ -494,7 +496,7 @@ void DbcParserTest::invalidValueDescriptions() { "VAL_ 4294967296 State 0 \"Bad ID\";", "VAL_ Environment 0 \"Unsupported\";", "VAL_ 1 State 0 \"Bad\\q escape\";"}) { 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 { DbcParser().ParseFile(fixture.path.string()); CHECK(false, statement); diff --git a/tests/tree_node_test.cpp b/tests/tree_node_test.cpp index 75c0979..e067b05 100644 --- a/tests/tree_node_test.cpp +++ b/tests/tree_node_test.cpp @@ -16,7 +16,7 @@ int main() { frame.signals.push_back(signal); database.frames.push_back(frame); database.frames.push_back(frame); - auto root = DbcTreeBuilder().Build(database); + std::unique_ptr root = DbcTreeBuilder().Build(database); Check(root->GetParent() == nullptr && root->GetRow() == 0); root->AddChild(nullptr); Check(root->GetChildCount() == 2 && !root->GetChild(2)); @@ -28,6 +28,6 @@ int main() { Check(leaf->GetParent() == child && leaf->GetRow() == position); } } - auto transferred = std::move(root); + std::unique_ptr transferred = std::move(root); Check(transferred->GetChild(0)->GetParent() == transferred.get()); }