DBC Framework
DBC parsing and CAN signal decoding framework
Loading...
Searching...
No Matches
decode_database.cpp
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2026 Dim Himro
3
4#include "decode_database.h"
5#include "signal_layout.h"
6#include <bitset>
7#include <cmath>
8#include <limits>
9#include <stdexcept>
10#include <unordered_set>
11
12namespace {
13 constexpr std::size_t MaximumPayloadBits = std::numeric_limits<std::uint8_t>::max() * 8U;
14 using SignalBits = std::bitset<MaximumPayloadBits>;
15
16 void ValidateSignal(const DecodeSignal &signal, std::uint8_t dlc) {
17 if (signal.byteOrder != ByteOrder::Intel && signal.byteOrder != ByteOrder::Motorola)
18 throw std::invalid_argument("Invalid signal byte order: " + signal.name);
19 if (signal.valueType != ValueType::Unsigned && signal.valueType != ValueType::Signed)
20 throw std::invalid_argument("Invalid signal value type: " + signal.name);
21 if (signal.multiplexing.role != MultiplexRole::Unconditional &&
22 signal.multiplexing.role != MultiplexRole::Selector &&
23 signal.multiplexing.role != MultiplexRole::Branch)
24 throw std::invalid_argument("Invalid multiplexing role: " + signal.name);
25 if (!IsValidSignalLayout(signal.startBit, signal.length,
26 signal.byteOrder == ByteOrder::Intel, dlc))
27 throw std::invalid_argument("Invalid signal length or payload position: " + signal.name);
28 if (!std::isfinite(signal.factor) || !std::isfinite(signal.offset) ||
29 !std::isfinite(signal.minimum) || !std::isfinite(signal.maximum) ||
30 signal.minimum > signal.maximum)
31 throw std::invalid_argument("Invalid signal numeric parameters: " + signal.name);
32 }
33
34 SignalBits GetSignalBits(const DecodeSignal &signal) {
35 SignalBits bits;
36 const bool littleEndian = signal.byteOrder == ByteOrder::Intel;
37 const std::uint64_t start = SignalStartCoordinate(signal.startBit, littleEndian);
38 for (std::uint32_t index = 0U; index < signal.length; ++index) {
39 const std::uint64_t coordinate = start + index;
40 const std::uint64_t bit = littleEndian ? coordinate
41 : coordinate / 8U * 8U + 7U - coordinate % 8U;
42 bits.set(static_cast<std::size_t>(bit));
43 }
44 return bits;
45 }
46
47 bool AreMutuallyExclusive(const DecodeSignal &first, const DecodeSignal &second) {
48 return first.multiplexing.role == MultiplexRole::Branch &&
49 second.multiplexing.role == MultiplexRole::Branch &&
50 first.multiplexing.value != second.multiplexing.value;
51 }
52
53 void ValidateFrame(const DecodeFrame &frame) {
54 const std::uint32_t maximumId = frame.isExtended ? 0x1FFFFFFFU : 0x7FFU;
55 if (frame.canId > maximumId)
56 throw std::invalid_argument("Invalid normalized CAN ID: " + std::to_string(frame.canId));
57
58 std::unordered_set<std::string> names;
59 const DecodeSignal *selector = nullptr;
60 for (const DecodeSignal &signal: frame.signals) {
61 if (!names.insert(signal.name).second)
62 throw std::invalid_argument("Duplicate runtime signal key: " + signal.name);
63 ValidateSignal(signal, frame.dlc);
64 if (signal.multiplexing.role == MultiplexRole::Selector) {
65 if (selector != nullptr || signal.valueType != ValueType::Unsigned)
66 throw std::invalid_argument("Multiplexing requires one unsigned selector per frame");
67 selector = &signal;
68 }
69 }
70
71 std::vector<SignalBits> layouts;
72 layouts.reserve(frame.signals.size());
73 for (std::size_t index = 0U; index < frame.signals.size(); ++index) {
74 const DecodeSignal &signal = frame.signals[index];
75 if (signal.multiplexing.role == MultiplexRole::Branch &&
76 (selector == nullptr ||
77 (selector->length < 64U && signal.multiplexing.value >= (std::uint64_t(1U) << selector->length))))
78 throw std::invalid_argument("Missing selector or branch value outside selector range: " + signal.name);
79 const SignalBits bits = GetSignalBits(signal);
80 for (std::size_t previous = 0U; previous < index; ++previous) {
81 if (!AreMutuallyExclusive(signal, frame.signals[previous]) && (bits & layouts[previous]).any())
82 throw std::invalid_argument("Overlapping signals: " + signal.name);
83 }
84 layouts.push_back(bits);
85 }
86 }
87}// namespace
88
89DecodeDatabase::Storage::Storage(const std::vector<DecodeFrame> &definitions)
90 : frames(definitions) {
91 // Validate the complete snapshot before creating its lookup index.
92 for (const DecodeFrame &frame: frames)
93 ValidateFrame(frame);
94 frameIndexByKey.reserve(frames.size());
95 for (std::size_t index = 0U; index < frames.size(); ++index) {
96 if (!frameIndexByKey.emplace(frames[index].GetKey(), index).second)
97 throw std::invalid_argument("Duplicate runtime frame key");
98 }
99}
100
101DecodeDatabase::DecodeDatabase(const std::vector<DecodeFrame> &frames)
102 : storage(std::make_shared<const Storage>(frames)) {
103}
104
105const std::vector<DecodeFrame> &DecodeDatabase::GetFrames() const {
106 static const std::vector<DecodeFrame> empty;
107 return storage ? storage->frames : empty;
108}
109
110std::shared_ptr<const DecodeFrame> DecodeDatabase::FindFrame(const FrameKey &key) const {
111 if (!storage)
112 return nullptr;
113 const std::unordered_map<FrameKey, std::size_t, FrameKeyHasher>::const_iterator it =
114 storage->frameIndexByKey.find(key);
115 if (it == storage->frameIndexByKey.end())
116 return nullptr;
117 return std::shared_ptr<const DecodeFrame>(storage, &storage->frames[it->second]);
118}
119
120std::shared_ptr<const DecodeSignal> DecodeDatabase::FindSignal(const SignalKey &key) const {
121 const std::shared_ptr<const DecodeFrame> frame = FindFrame(key.frame);
122 if (!frame)
123 return nullptr;
124 for (const DecodeSignal &signal: frame->signals) {
125 if (signal.name == key.name)
126 return std::shared_ptr<const DecodeSignal>(frame, &signal);
127 }
128 return nullptr;
129}
std::shared_ptr< const DecodeFrame > FindFrame(const FrameKey &key) const
const std::vector< DecodeFrame > & GetFrames() const
Created: 2026-03-13 Author: Deeaitch (Dim. Himro)
Runtime-ready frame definition.
std::uint8_t dlc
std::uint32_t canId
Runtime-ready signal definition.
std::string name
ValueType valueType
std::uint32_t startBit
ByteOrder byteOrder
std::uint32_t length
Stable frame identifier: normalized CAN ID and frame type.