5#include <QTemporaryFile>
11 const std::string fixture = R
"DBC(BO_ 291 Standard: 2 SenderA
12 SG_ Mode M : 0|8@1+ (1,0) [0|255] "" ReceiverA
13 SG_ State m3 : 8|8@1+ (2,1) [1|511] "km/h" ReceiverA,ReceiverB
14BO_ 2147483939 Extended: 1 SenderB
15 SG_ State : 7|8@0- (0.5,-3) [-67|60.5] "deg C" ReceiverB
16BO_ 2565866562 J1939: 0 SenderC
17CM_ BO_ 291 "Standard frame comment";
18CM_ SG_ 291 Mode "Selector comment";
19CM_ SG_ 291 State "Standard signal comment";
20CM_ BO_ 2147483939 "Extended frame comment";
21CM_ SG_ 2147483939 State "Extended signal comment";
22VAL_ 291 Mode 3 "Selected";
23VAL_ 291 State 4 "Standard four";
24VAL_ 2147483939 State -2 "Extended minus two";
29 if (!file.open())
throw std::runtime_error(
"Cannot create DBC fixture");
30 if (file.write(text.data(),
static_cast<qint64
>(text.size())) !=
static_cast<qint64
>(text.size()) || !file.flush())
31 throw std::runtime_error(
"Cannot write DBC fixture");
39 standard.
name =
"Standard";
40 standard.
canId = 291U;
43 standard.
comment =
"Standard frame comment";
50 mode.
comment =
"Selector comment";
63 state.
receivers = {
"ReceiverA",
"ReceiverB"};
64 state.
comment =
"Standard signal comment";
66 standard.signals = {mode, state};
69 extended.
name =
"Extended";
70 extended.
canId = 291U;
75 extended.
comment =
"Extended frame comment";
86 state.
comment =
"Extended signal comment";
88 extended.signals = {state};
92 j1939.
canId = 0x18F00442U;
100 template<
class Signal>
101 void CheckSignalMetadata(
const Signal &actual,
const SignalInfo &expected) {
102 QCOMPARE(actual.name, expected.
name);
103 QCOMPARE(actual.startBit, expected.
startBit);
104 QCOMPARE(actual.length, expected.
length);
105 QCOMPARE(actual.factor, expected.
factor);
106 QCOMPARE(actual.offset, expected.
offset);
107 QCOMPARE(actual.minimum, expected.
minimum);
108 QCOMPARE(actual.maximum, expected.
maximum);
109 QCOMPARE(actual.unit, expected.
unit);
110 QVERIFY(actual.receivers == expected.
receivers);
111 QCOMPARE(actual.comment, expected.
comment);
118 CheckSignalMetadata(actual, expected);
124 CheckSignalMetadata(actual, expected);
129 template<
class Frame>
130 void CheckFrame(
const Frame &actual,
const FrameInfo &expected) {
131 QCOMPARE(actual.name, expected.
name);
132 QCOMPARE(actual.canId, expected.
canId);
133 QCOMPARE(actual.isExtended, expected.
isExtended);
134 QCOMPARE(actual.dlc, expected.
dlc);
135 QCOMPARE(actual.pgn, expected.
pgn);
136 QCOMPARE(actual.hasPgn, expected.
hasPgn);
137 QCOMPARE(actual.transmitter, expected.
transmitter);
138 QCOMPARE(actual.comment, expected.
comment);
139 QCOMPARE(actual.signals.size(), expected.signals.size());
140 for (std::size_t i = 0; i < expected.signals.size(); ++i)
141 CheckSignal(actual.signals[i], expected.signals[i]);
145 QCOMPARE(root.
GetType(), NodeType::Root);
147 QVERIFY(root.
GetFrame() ==
nullptr);
150 for (std::size_t i = 0; i < expected.
frames.size(); ++i) {
152 QVERIFY(frame !=
nullptr);
153 QCOMPARE(frame->
GetType(), NodeType::Frame);
155 QCOMPARE(frame->
GetRow(), i);
157 QVERIFY(frame->
GetFrame() !=
nullptr);
163 QVERIFY(signal !=
nullptr);
164 QCOMPARE(signal->
GetType(), NodeType::Signal);
166 QCOMPARE(signal->
GetRow(), j);
167 QCOMPARE(signal->
GetName(), expected.
frames[i].signals[j].name);
169 QVERIFY(signal->
GetFrame() ==
nullptr);
179 QVERIFY(runtime.
FindFrame(key) ==
nullptr);
181 QVERIFY(
DbcDecoder().FindFrame(runtime, key.canId, key.isExtended) ==
nullptr);
183 raw.
canId = key.canId;
187 QVERIFY(!decoded.
valid);
188 QCOMPARE(decoded.
status, FrameDecodeStatus::UnknownFrame);
190 QVERIFY(decoded.signals.empty());
191 QVERIFY(decoded.
key == key);
195class PipelineTest :
public QObject {
198 void metadataLookupDecodeAndTree();
199 void emptyDatabase_data();
200 void emptyDatabase();
201 void databaseReplacement();
204void PipelineTest::metadataLookupDecodeAndTree() {
213 QVERIFY(tree !=
nullptr);
214 CheckTree(*tree, expected);
215 for (std::size_t i = 0; i < expected.
frames.size(); ++i) {
219 const std::shared_ptr<const DecodeFrame> found = runtime.
FindFrame(key);
220 QVERIFY(found !=
nullptr);
221 QCOMPARE(found.get(), &runtime.
GetFrames()[i]);
222 QCOMPARE(
DbcDecoder().FindFrame(runtime, key.canId, key.isExtended).get(), found.get());
223 for (std::size_t j = 0; j < found->signals.size(); ++j) {
224 const std::shared_ptr<const DecodeSignal> signal = runtime.
FindSignal(
SignalKey(key, found->signals[j].name));
225 QVERIFY(signal !=
nullptr);
226 QCOMPARE(signal.get(), &found->signals[j]);
236 QVERIFY(standard.
valid);
237 QCOMPARE(standard.
status, FrameDecodeStatus::Valid);
240 QCOMPARE(standard.signals.size(), std::size_t(2));
241 for (std::size_t i = 0; i < standard.signals.size(); ++i) {
243 QVERIFY(signal.
valid);
244 QCOMPARE(signal.
status, SignalDecodeStatus::Valid);
245 QVERIFY(signal.
key ==
SignalKey(standard.
key, i == 0 ?
"Mode" :
"State"));
247 QCOMPARE(std::get<std::uint64_t>(signal.
rawValue), i == 0 ? 3U : 4U);
250 QCOMPARE(*signal.
GetValueDescription(), std::string(i == 0 ?
"Selected" :
"Standard four"));
255 QVERIFY(extended.
valid);
256 QCOMPARE(extended.
status, FrameDecodeStatus::Valid);
259 QCOMPARE(extended.signals.size(), std::size_t(1));
261 QVERIFY(signal.
valid);
262 QCOMPARE(signal.
status, SignalDecodeStatus::Valid);
265 QCOMPARE(std::get<std::int64_t>(signal.
rawValue), std::int64_t(-2));
270 raw.
canId = 0x18F00442U;
273 QVERIFY(j1939.
valid);
274 QCOMPARE(j1939.
status, FrameDecodeStatus::Valid);
277 QVERIFY(j1939.signals.empty());
280void PipelineTest::emptyDatabase_data() {
281 QTest::addColumn<QString>(
"text");
282 QTest::newRow(
"empty-file") << QString();
283 QTest::newRow(
"headers-only") << QString::fromUtf8(
"VERSION \"empty\"\nNS_ :\nBS_:\nBU_: Sender\n");
286void PipelineTest::emptyDatabase() {
287 QFETCH(QString, text);
294 CheckUnknown(runtime,
false);
295 CheckUnknown(runtime,
true);
297 QVERIFY(tree !=
nullptr);
301void PipelineTest::databaseReplacement() {
310 QVERIFY(saved.
valid);
311 QCOMPARE(saved.signals.size(), std::size_t(2));
312 QVERIFY(tree !=
nullptr);
313 std::unique_ptr<TreeNode> savedTree = std::move(tree);
315 parsed = ParseFixture(R
"DBC(BO_ 291 Replacement: 1 NewSender
316 SG_ NewState : 0|8@1+ (3,-1) [-1|764] "V" NewReceiver
317CM_ BO_ 291 "Replacement frame";
318CM_ SG_ 291 NewState "Replacement signal";
319VAL_ 291 NewState 4 "New four";
324 replacement.name = "Replacement";
325 replacement.
canId = 291U;
326 replacement.
dlc = 1U;
328 replacement.
comment =
"Replacement frame";
330 state.
name =
"NewState";
338 state.
comment =
"Replacement signal";
340 replacement.signals = {state};
346 QVERIFY(tree !=
nullptr);
347 QCOMPARE(runtime.
GetFrames().size(), std::size_t(1));
348 CheckFrame(runtime.
GetFrames()[0], replacement);
349 CheckTree(*tree, expected);
350 CheckUnknown(runtime,
true);
356 QVERIFY(current.
valid);
357 QCOMPARE(current.
status, FrameDecodeStatus::Valid);
358 QCOMPARE(current.signals.size(), std::size_t(1));
359 QVERIFY(current.signals[0].
valid);
362 QCOMPARE(std::get<std::uint64_t>(current.signals[0].rawValue), std::uint64_t(4));
363 QCOMPARE(current.signals[0].physicalValue, 11.0);
364 QVERIFY(current.signals[0].GetValueDescription() !=
nullptr);
365 QCOMPARE(*current.signals[0].GetValueDescription(), std::string(
"New four"));
367 parsed = ParseFixture(
"");
372 QVERIFY(tree !=
nullptr);
374 CheckUnknown(runtime,
false);
375 CheckUnknown(runtime,
true);
377 QVERIFY(runtime.
FindSignal(current.signals[0].
key) ==
nullptr);
379 CheckFrame(*saved.
definition, ExpectedMetadata().frames[0]);
380 CheckTree(*savedTree, ExpectedMetadata());
381 QCOMPARE(saved.signals[1].physicalValue, 9.0);
382 QVERIFY(saved.signals[1].GetValueDescription() !=
nullptr);
383 QCOMPARE(*saved.signals[1].GetValueDescription(), std::string(
"Standard four"));
386int RunPipelineTests(
int argc,
char **argv) {
388 return QTest::qExec(&test, argc, argv);
391#include "dbc_pipeline_test.moc"
Converts parsed DBC data into runtime decode database.
DecodeDatabase Build(const DbcDatabase &source) const
Build runtime decode database.
Runtime CAN decoder using prebuilt decode database.
DecodedFrameValue Decode(const DecodeDatabase &database, const RawCanFrame &frame) const
Decode one raw CAN frame.
Builds a simple tree from parsed DBC database.
std::unique_ptr< TreeNode > Build(const DbcDatabase &database) const
Build tree representation of parsed DBC data.
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
const std::vector< DecodeFrame > & GetFrames() const
Tree node for later use in model/view or other hierarchy consumers.
const SignalInfo * GetSignal() const
Get signal payload if node is signal.
const TreeNode * GetChild(std::size_t index) const
Get child by index.
const TreeNode * GetParent() const
const std::string & GetName() const
Get display name.
std::size_t GetChildCount() const
Get child count.
std::size_t GetRow() const
NodeType GetType() const
Get node type.
const FrameInfo * GetFrame() const
Get frame payload if node is frame.
Runtime decode database builder.
CAN frame decoding API and result types.
DBC file parser and interpretation options.
ByteOrder
Signal byte order used for runtime decoding.
ValueType
Signal numeric type.
Parsed DBC content stored in a simple internal form.
std::vector< FrameInfo > frames
Parsed database and diagnostics; the database is empty on error.
std::vector< DbcDiagnostic > diagnostics
bool HasErrors() const
Return whether any diagnostic has error severity.
Runtime-ready signal definition.
std::shared_ptr< const DecodeFrame > definition
One decoded signal value.
std::shared_ptr< const DecodeSignal > definition
SignalDecodeStatus status
const std::string * GetValueDescription() const
Describes one CAN frame from a DBC file.
Stable frame identifier: normalized CAN ID and frame type.
Raw CAN frame used for runtime or trace decoding.
std::vector< std::uint8_t > data
Describes one signal inside a DBC frame.
std::vector< std::string > receivers
ValueDescriptions valueDescriptions
Multiplexing multiplexing