DBC Framework
DBC parsing and CAN signal decoding framework
Loading...
Searching...
No Matches
dbc_decoder.cpp
Go to the documentation of this file.
1
12#include "dbc_decoder.h"
13#include "j1939.h"
14#include "signal_layout.h"
15#include <cmath>
16#include <stdexcept>
17#include <unordered_set>
18#include <utility>
19
20std::shared_ptr<const DecodeFrame> DbcDecoder::FindFrame(const DecodeDatabase &database,
21 std::uint32_t canId,
22 bool isExtended) const {
23 return ResolveFrame(database, canId, isExtended).definition;
24}
25
26PgnLookupResult DbcDecoder::ResolveFrame(const DecodeDatabase &database,
27 std::uint32_t canId, bool isExtended) {
28 const std::shared_ptr<const DecodeFrame> exact = database.FindFrame(FrameKey(canId, isExtended));
29 if (exact)
30 return {exact, false};
31 if (!isExtended || canId > 0x1FFFFFFFU)
32 return {};
33 return database.FindFrameByPgn(ExtractJ1939Pgn(canId));
34}
35
37 const RawCanFrame &frame) const {
38 return DecodeImpl(database, frame, nullptr);
39}
40
42 const RawCanFrame &frame,
43 const std::vector<SignalKey> &selection) const {
44 return DecodeImpl(database, frame, &selection);
45}
46
47DecodedFrameValue DbcDecoder::DecodeImpl(const DecodeDatabase &database,
48 const RawCanFrame &frame,
49 const std::vector<SignalKey> *selection) const {
50 DecodedFrameValue result;
51 result.key = FrameKey(frame.canId, frame.isExtended);
52 const PgnLookupResult match = ResolveFrame(database, frame.canId, frame.isExtended);
53 if (match.ambiguous) {
54 result.status = FrameDecodeStatus::AmbiguousFrame;
55 return result;
56 }
57 const std::shared_ptr<const DecodeFrame> definition = match.definition;
58 if (definition == nullptr)
59 return result;
60 result.key = definition->GetKey();
61
62 std::vector<const DecodeSignal *> selected;
63 if (selection != nullptr) {
64 selected.reserve(selection->size());
65 std::unordered_set<SignalKey, SignalKeyHasher> keys;
66 for (const SignalKey &key: *selection) {
67 if (!(key.frame == result.key))
68 throw std::invalid_argument("Selected signal belongs to another frame: " + key.name);
69 if (!keys.insert(key).second)
70 throw std::invalid_argument("Duplicate selected signal: " + key.name);
71 const std::shared_ptr<const DecodeSignal> signal = database.FindSignal(key);
72 if (!signal)
73 throw std::invalid_argument("Unknown selected signal: " + key.name);
74 selected.push_back(signal.get());
75 }
76 }
77
78 result.definition = definition;
79 result.valid = true;
80 result.status = frame.data.size() < definition->dlc
81 ? FrameDecodeStatus::InsufficientPayload
82 : FrameDecodeStatus::Valid;
83
84 const dbc_detail::MultiplexGraph &graph = database.GetMultiplexGraph(result.key);
85 const std::size_t count = selection != nullptr ? selected.size() : definition->signals.size();
86 result.signals.reserve(count);
87 for (std::size_t index = 0U; index < count; ++index) {
88 const DecodeSignal &signal = selection != nullptr ? *selected[index] : definition->signals[index];
89 const std::size_t signalIndex = static_cast<std::size_t>(&signal - definition->signals.data());
90 DecodedSignalValue decoded = DecodeOneSignal(signal, definition->dlc, frame,
91 ActivationStatus(signalIndex, *definition, frame, graph));
92 if (!decoded.valid && decoded.status != SignalDecodeStatus::Inactive &&
93 result.status == FrameDecodeStatus::Valid)
94 result.status = FrameDecodeStatus::SignalErrors;
95 decoded.definition = std::shared_ptr<const DecodeSignal>(definition, &signal);
96 decoded.key = SignalKey(result.key, signal.name);
97 result.signals.push_back(std::move(decoded));
98 }
99
100 return result;
101}
102
103bool DbcDecoder::IsValidDefinition(const DecodeSignal &signal, std::uint8_t dlc) {
104 if ((signal.byteOrder != ByteOrder::Intel && signal.byteOrder != ByteOrder::Motorola) ||
105 (signal.valueType != ValueType::Unsigned && signal.valueType != ValueType::Signed) ||
106 (signal.multiplexing.role != MultiplexRole::Unconditional &&
107 signal.multiplexing.role != MultiplexRole::Selector &&
108 signal.multiplexing.role != MultiplexRole::Branch &&
109 signal.multiplexing.role != MultiplexRole::BranchSelector))
110 return false;
111
112 return IsValidSignalLayout(signal.startBit, signal.length,
113 signal.byteOrder == ByteOrder::Intel, dlc);
114}
115
116SignalDecodeStatus DbcDecoder::ActivationStatus(std::size_t index,
117 const DecodeFrame &definition,
118 const RawCanFrame &frame,
119 const dbc_detail::MultiplexGraph &graph) {
120 // Inspect ancestors from the root so inactive paths never require child bytes.
121 std::vector<std::size_t> path;
122 while (graph.parents[index] != definition.signals.size()) {
123 path.push_back(index);
124 index = graph.parents[index];
125 }
126 for (std::vector<std::size_t>::const_reverse_iterator it = path.rbegin(); it != path.rend(); ++it) {
127 const DecodeSignal &selector = definition.signals[graph.parents[*it]];
128 std::uint64_t value = 0U;
129 if (!ExtractUnsigned(frame.data, selector, value))
130 return SignalDecodeStatus::SelectorUnavailable;
131 if (!graph.Matches(*it, value))
132 return SignalDecodeStatus::Inactive;
133 }
134 return SignalDecodeStatus::Valid;
135}
136
137DecodedSignalValue DbcDecoder::DecodeOneSignal(const DecodeSignal &signal,
138 std::uint8_t dlc,
139 const RawCanFrame &frame,
140 SignalDecodeStatus activation) {
141 DecodedSignalValue decoded;
142 std::uint64_t unsignedValue = 0U;
143
144 if (!IsValidDefinition(signal, dlc))
145 return decoded;
146 if (activation != SignalDecodeStatus::Valid) {
147 decoded.status = activation;
148 return decoded;
149 }
150 if (!ExtractUnsigned(frame.data, signal, unsignedValue)) {
151 decoded.status = SignalDecodeStatus::InsufficientPayload;
152 return decoded;
153 }
154
155 if (signal.valueType == ValueType::Signed)
156 decoded.rawValue = SignExtend(unsignedValue, signal.length);
157 else
158 decoded.rawValue = unsignedValue;
159
160 double numericValue;
161 if (const std::int64_t *signedValue = std::get_if<std::int64_t>(&decoded.rawValue))
162 numericValue = static_cast<double>(*signedValue);
163 else
164 numericValue = static_cast<double>(std::get<std::uint64_t>(decoded.rawValue));
165 decoded.physicalValue = numericValue * signal.factor + signal.offset;
166 if (!std::isfinite(decoded.physicalValue)) {
167 decoded.status = SignalDecodeStatus::PhysicalConversionFailed;
168 return decoded;
169 }
170 decoded.valid = true;
171 decoded.status = SignalDecodeStatus::Valid;
172
173 return decoded;
174}
175
176bool DbcDecoder::ExtractUnsigned(const std::vector<std::uint8_t> &data,
177 const DecodeSignal &signal,
178 std::uint64_t &value) {
179 if ((signal.length == 0U) || (signal.length > 64U))
180 return false;
181
182 if (signal.byteOrder == ByteOrder::Intel)
183 return ExtractIntel(data, signal.startBit, signal.length, value);
184
185 return ExtractMotorola(data, signal.startBit, signal.length, value);
186}
187
188bool DbcDecoder::ExtractIntel(const std::vector<std::uint8_t> &data,
189 std::uint32_t startBit,
190 std::uint32_t length,
191 std::uint64_t &value) {
192 value = 0U;
193
194 for (std::uint32_t bitIndex = 0U; bitIndex < length; ++bitIndex) {
195 const std::uint64_t absoluteBit = std::uint64_t(startBit) + bitIndex;
196 const std::uint64_t byteIndex = absoluteBit / 8U;
197 const std::uint32_t bitInByte = absoluteBit % 8U;
198
199 if (byteIndex >= data.size())
200 return false;
201
202 const std::uint64_t bitValue =
203 (static_cast<std::uint64_t>((data[byteIndex] >> bitInByte) & 0x01U) << bitIndex);
204
205 value |= bitValue;
206 }
207
208 return true;
209}
210
211bool DbcDecoder::ExtractMotorola(const std::vector<std::uint8_t> &data,
212 std::uint32_t startBit,
213 std::uint32_t length,
214 std::uint64_t &value) {
215 /*
216 * DBC Motorola bit numbering:
217 * - startBit points to the most significant bit of the signal
218 * - inside a byte, bit numbering goes 7..0
219 * - crossing byte boundary moves to the next byte, bit 7
220 * Example: a signal starting at 7 follows 7..0, 15..8, 23..16.
221 */
222
223 value = 0U;
224
225 std::size_t byteIndex = startBit / 8U;
226 std::uint32_t bitInByte = startBit % 8U;
227
228 for (std::uint32_t bitIndex = 0U; bitIndex < length; ++bitIndex) {
229 if (byteIndex >= data.size())
230 return false;
231
232 value <<= 1U;
233 value |= static_cast<std::uint64_t>((data[byteIndex] >> bitInByte) & 0x01U);
234
235 if (bitInByte == 0U) {
236 ++byteIndex;
237 bitInByte = 7U;
238 } else
239 --bitInByte;
240 }
241
242 return true;
243}
244
245std::int64_t DbcDecoder::SignExtend(std::uint64_t value, std::uint32_t bitLength) {
246 if ((bitLength == 0U) || (bitLength > 64U))
247 return 0;
248
249 const std::uint64_t signMask = std::uint64_t(1U) << (bitLength - 1U);
250 const std::uint64_t valueMask = (bitLength == 64U)
251 ? ~std::uint64_t(0U)
252 : (std::uint64_t(1U) << bitLength) - 1U;
253 value &= valueMask;
254
255 if ((value & signMask) == 0U)
256 return static_cast<std::int64_t>(value);
257
258 // The complemented magnitude fits int64_t, including for INT64_MIN.
259 return -1 - static_cast<std::int64_t>((~value) & valueMask);
260}
DecodedFrameValue Decode(const DecodeDatabase &database, const RawCanFrame &frame) const
Decode one raw CAN frame.
std::shared_ptr< const DecodeFrame > FindFrame(const DecodeDatabase &database, std::uint32_t canId, bool isExtended) const
Find exact CAN ID first, then unique PGN among explicitly J1939 definitions.
Immutable runtime snapshot. Copies share storage; reload replaces a snapshot. Owning lookup handles a...
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
PgnLookupResult FindFrameByPgn(std::uint32_t pgn) const
CAN frame decoding API and result types.
SignalDecodeStatus
Outcome of signal extraction and physical conversion.
Definition dbc_decoder.h:34
Runtime-ready frame definition.
Runtime-ready signal definition.
std::string name
ValueType valueType
std::uint32_t startBit
ByteOrder byteOrder
std::uint32_t length
Multiplexing multiplexing
Fully decoded frame.
Definition dbc_decoder.h:74
std::shared_ptr< const DecodeFrame > definition
Definition dbc_decoder.h:75
FrameDecodeStatus status
Definition dbc_decoder.h:79
One decoded signal value.
Definition dbc_decoder.h:55
RawSignalValue rawValue
Definition dbc_decoder.h:58
std::shared_ptr< const DecodeSignal > definition
Definition dbc_decoder.h:56
SignalDecodeStatus status
Definition dbc_decoder.h:61
Stable frame identifier: normalized CAN ID and frame type.
std::shared_ptr< const DecodeFrame > definition
Raw CAN frame used for runtime or trace decoding.
Definition dbc_decoder.h:25
std::vector< std::uint8_t > data
Definition dbc_decoder.h:28
std::uint32_t canId
Definition dbc_decoder.h:26
bool isExtended
Definition dbc_decoder.h:27