/** * @file dbc_decoder.h * @brief * * Created: 2026-03-13 * Author: Deeaitch (Dim. Himro) * * Licensed under the MIT License. * See LICENSE file in the project root for full license text. */ #ifndef DBC_DECODER_H #define DBC_DECODER_H #include #include #include #include "decode_database.h" /** * @brief Raw CAN frame used for runtime or trace decoding. */ struct RawCanFrame { std::uint32_t canId = 0U; /**< Normalized CAN ID. */ bool isExtended = false; /**< true for extended frame. */ std::vector data; /**< Payload bytes. */ RawCanFrame() = default; }; enum class SignalDecodeStatus { InvalidDefinition, InsufficientPayload, SelectorUnavailable, Inactive, Valid, PhysicalConversionFailed }; /** Frame summary: short payload takes precedence over individual signal errors. */ enum class FrameDecodeStatus { UnknownFrame, InsufficientPayload, SignalErrors, Valid, AmbiguousFrame }; /** * @brief One decoded signal value. */ struct DecodedSignalValue { std::shared_ptr definition; /**< Owning immutable definition. */ SignalKey key; /**< Stable application identifier. */ RawSignalValue rawValue = std::uint64_t(0U); /**< Exact raw value for Valid or PhysicalConversionFailed. */ double physicalValue = 0.0; /**< Conversion result, possibly NaN/Inf on failure. */ bool valid = false; /**< true if decoding succeeded. */ SignalDecodeStatus status = SignalDecodeStatus::InvalidDefinition; /** Lookup using the raw integer; nullptr for invalid or unmapped values. */ const std::string *GetValueDescription() const { return valid && definition ? FindValueDescription(definition->valueDescriptions, rawValue) : nullptr; } DecodedSignalValue() = default; }; /** * @brief Fully decoded frame. */ struct DecodedFrameValue { std::shared_ptr definition; /**< Owning immutable definition. */ FrameKey key; /**< Matched definition key; received key if unmatched. */ std::vector signals; /**< Decoded signal values. */ bool valid = false; /**< true if frame was matched. */ FrameDecodeStatus status = FrameDecodeStatus::UnknownFrame; /**< Inspect signals for individual failures. */ DecodedFrameValue() = default; }; /** * @brief Runtime CAN decoder using prebuilt decode database. */ class DbcDecoder { public: /** * @brief Find exact CAN ID first, then unique PGN among explicitly J1939 definitions. * @param database Runtime decode database. * @param canId Normalized CAN ID. * @param isExtended true for extended frame. * @return Owning immutable handle, or nullptr for missing/ambiguous matches. Safe across reload. */ std::shared_ptr FindFrame(const DecodeDatabase &database, std::uint32_t canId, bool isExtended) const; /** * @brief Decode one raw CAN frame. * @param database Runtime decode database. * @param frame Raw CAN frame. * Short payloads decode available signals; bytes beyond declared DLC are ignored. * @return Decoded frame value. */ DecodedFrameValue Decode(const DecodeDatabase &database, const RawCanFrame &frame) const; /** * @brief Decode selected signals in selection order using stable keys. * An empty selection returns no signals. Multiplex selectors are read internally. * Frame status summarizes selected signals; short payload still takes precedence. * Unknown/ambiguous frames return before checking the selection. * For PGN matches, selection keys must belong to the matched DBC definition. * @throws std::invalid_argument for a foreign frame key, unknown signal, or duplicate key. */ DecodedFrameValue Decode(const DecodeDatabase &database, const RawCanFrame &frame, const std::vector &selection) const; private: static PgnLookupResult ResolveFrame(const DecodeDatabase &database, std::uint32_t canId, bool isExtended); DecodedFrameValue DecodeImpl(const DecodeDatabase &database, const RawCanFrame &frame, const std::vector *selection) const; static bool IsValidDefinition(const DecodeSignal &signal, std::uint8_t dlc); 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, std::uint8_t dlc, const RawCanFrame &frame, const SelectorState &selector); static bool ExtractUnsigned(const std::vector &data, const DecodeSignal &signal, std::uint64_t &value); static bool ExtractIntel(const std::vector &data, std::uint32_t startBit, std::uint32_t length, std::uint64_t &value); static bool ExtractMotorola(const std::vector &data, std::uint32_t startBit, std::uint32_t length, std::uint64_t &value); static std::int64_t SignExtend(std::uint64_t value, std::uint32_t bitLength); }; #endif /* DBC_DECODER_H */