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 "j1939.h"
6#include "signal_layout.h"
7#include <bitset>
8#include <cmath>
9#include <limits>
10#include <stdexcept>
11#include <unordered_set>
12
13namespace {
14 constexpr std::size_t MaximumPayloadBits = std::numeric_limits<std::uint8_t>::max() * 8U;
15 using SignalBits = std::bitset<MaximumPayloadBits>;
16
17 void ValidateSignal(const DecodeSignal &signal, std::uint8_t dlc) {
18 if (signal.byteOrder != ByteOrder::Intel && signal.byteOrder != ByteOrder::Motorola)
19 throw std::invalid_argument("Invalid signal byte order: " + signal.name);
20 if (signal.valueType != ValueType::Unsigned && signal.valueType != ValueType::Signed)
21 throw std::invalid_argument("Invalid signal value type: " + signal.name);
22 if (signal.multiplexing.role != MultiplexRole::Unconditional &&
23 signal.multiplexing.role != MultiplexRole::Selector &&
24 signal.multiplexing.role != MultiplexRole::Branch)
25 throw std::invalid_argument("Invalid multiplexing role: " + signal.name);
26 if (!IsValidSignalLayout(signal.startBit, signal.length,
27 signal.byteOrder == ByteOrder::Intel, dlc))
28 throw std::invalid_argument("Invalid signal length or payload position: " + signal.name);
29 if (!std::isfinite(signal.factor) || !std::isfinite(signal.offset) ||
30 !std::isfinite(signal.minimum) || !std::isfinite(signal.maximum) ||
31 signal.minimum > signal.maximum)
32 throw std::invalid_argument("Invalid signal numeric parameters: " + signal.name);
33 }
34
35 SignalBits GetSignalBits(const DecodeSignal &signal) {
36 SignalBits bits;
37 const bool littleEndian = signal.byteOrder == ByteOrder::Intel;
38 const std::uint64_t start = SignalStartCoordinate(signal.startBit, littleEndian);
39 for (std::uint32_t index = 0U; index < signal.length; ++index) {
40 const std::uint64_t coordinate = start + index;
41 const std::uint64_t bit = littleEndian ? coordinate
42 : coordinate / 8U * 8U + 7U - coordinate % 8U;
43 bits.set(static_cast<std::size_t>(bit));
44 }
45 return bits;
46 }
47
48 bool AreMutuallyExclusive(const DecodeSignal &first, const DecodeSignal &second) {
49 return first.multiplexing.role == MultiplexRole::Branch &&
50 second.multiplexing.role == MultiplexRole::Branch &&
51 first.multiplexing.value != second.multiplexing.value;
52 }
53
54 void ValidateFrame(const DecodeFrame &frame) {
55 const std::uint32_t maximumId = frame.isExtended ? 0x1FFFFFFFU : 0x7FFU;
56 if (frame.canId > maximumId)
57 throw std::invalid_argument("Invalid normalized CAN ID: " + std::to_string(frame.canId));
58
59 if (frame.hasPgn && (!frame.isExtended || frame.pgn != ExtractJ1939Pgn(frame.canId)))
60 throw std::invalid_argument("Invalid J1939 PGN metadata: " + frame.name);
61
62 std::unordered_set<std::string> names;
63 const DecodeSignal *selector = nullptr;
64 for (const DecodeSignal &signal: frame.signals) {
65 if (!names.insert(signal.name).second)
66 throw std::invalid_argument("Duplicate runtime signal key: " + signal.name);
67 ValidateSignal(signal, frame.dlc);
68 if (signal.multiplexing.role == MultiplexRole::Selector) {
69 if (selector != nullptr || signal.valueType != ValueType::Unsigned)
70 throw std::invalid_argument("Multiplexing requires one unsigned selector per frame");
71 selector = &signal;
72 }
73 }
74
75 std::vector<SignalBits> layouts;
76 layouts.reserve(frame.signals.size());
77 for (std::size_t index = 0U; index < frame.signals.size(); ++index) {
78 const DecodeSignal &signal = frame.signals[index];
79 if (signal.multiplexing.role == MultiplexRole::Branch &&
80 (selector == nullptr ||
81 (selector->length < 64U && signal.multiplexing.value >= (std::uint64_t(1U) << selector->length))))
82 throw std::invalid_argument("Missing selector or branch value outside selector range: " + signal.name);
83 const SignalBits bits = GetSignalBits(signal);
84 for (std::size_t previous = 0U; previous < index; ++previous) {
85 if (!AreMutuallyExclusive(signal, frame.signals[previous]) && (bits & layouts[previous]).any())
86 throw std::invalid_argument("Overlapping signals: " + signal.name);
87 }
88 layouts.push_back(bits);
89 }
90 }
91}// namespace
92
93DecodeDatabase::Storage::Storage(const std::vector<DecodeFrame> &definitions)
94 : frames(definitions) {
95 // Validate the complete snapshot before creating its lookup index.
96 std::unordered_set<FrameKey, FrameKeyHasher> keys;
97 keys.reserve(frames.size());
98 for (const DecodeFrame &frame: frames) {
99 ValidateFrame(frame);
100 if (!keys.insert(frame.GetKey()).second)
101 throw std::invalid_argument("Duplicate runtime frame key");
102 }
103 frameIndexByKey.reserve(frames.size());
104 for (std::size_t index = 0U; index < frames.size(); ++index) {
105 frameIndexByKey.emplace(frames[index].GetKey(), index);
106 if (frames[index].hasPgn)
107 frameIndicesByPgn[frames[index].pgn].push_back(index);
108 }
109}
110
111DecodeDatabase::DecodeDatabase(const std::vector<DecodeFrame> &frames)
112 : storage(std::make_shared<const Storage>(frames)) {
113}
114
115const std::vector<DecodeFrame> &DecodeDatabase::GetFrames() const {
116 static const std::vector<DecodeFrame> empty;
117 return storage ? storage->frames : empty;
118}
119
120std::shared_ptr<const DecodeFrame> DecodeDatabase::FindFrame(const FrameKey &key) const {
121 if (!storage)
122 return nullptr;
123 const std::unordered_map<FrameKey, std::size_t, FrameKeyHasher>::const_iterator it =
124 storage->frameIndexByKey.find(key);
125 if (it == storage->frameIndexByKey.end())
126 return nullptr;
127 return std::shared_ptr<const DecodeFrame>(storage, &storage->frames[it->second]);
128}
129
131 PgnLookupResult result;
132 if (!storage)
133 return result;
134 const std::unordered_map<std::uint32_t, std::vector<std::size_t>>::const_iterator it =
135 storage->frameIndicesByPgn.find(pgn);
136 if (it == storage->frameIndicesByPgn.end())
137 return result;
138 if (it->second.size() != 1U) {
139 result.ambiguous = true;
140 return result;
141 }
142 result.definition = std::shared_ptr<const DecodeFrame>(storage, &storage->frames[it->second[0]]);
143 return result;
144}
145
146std::shared_ptr<const DecodeSignal> DecodeDatabase::FindSignal(const SignalKey &key) const {
147 const std::shared_ptr<const DecodeFrame> frame = FindFrame(key.frame);
148 if (!frame)
149 return nullptr;
150 for (const DecodeSignal &signal: frame->signals) {
151 if (signal.name == key.name)
152 return std::shared_ptr<const DecodeSignal>(frame, &signal);
153 }
154 return nullptr;
155}
std::shared_ptr< const DecodeSignal > FindSignal(const SignalKey &key) const
Return an owning signal handle, or an empty handle for a missing key.
std::shared_ptr< const DecodeFrame > FindFrame(const FrameKey &key) const
const std::vector< DecodeFrame > & GetFrames() const
PgnLookupResult FindFrameByPgn(std::uint32_t pgn) const
Runtime definitions and immutable indexed database.
Runtime-ready frame definition.
FrameKey GetKey() const
Return the stable normalized frame identifier.
std::uint32_t pgn
std::uint8_t dlc
std::uint32_t canId
std::string name
Runtime-ready signal definition.
std::string name
ValueType valueType
std::uint32_t startBit
ByteOrder byteOrder
std::uint32_t length
Multiplexing multiplexing
Stable frame identifier: normalized CAN ID and frame type.
MultiplexRole role
std::uint64_t value
std::shared_ptr< const DecodeFrame > definition
FrameKey frame
std::string name