A C/C++ DBC file parser based on boost.spirit
. This library is designed for decoding performance.
- very fast decoding
- verbose parser output in error case
- DBC is editable through C/C++ interface exported from the library
- read/write DBC file
- decode functionality for frames of arbitrarily byte length
- cantools like decoding
- KCD file format support
git clone --recurse-submodules https://github.com/xR3b0rn/dbcppp.git
cd dbcppp
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Release ..
make -j
make RunTests
make install
ldconfig # on Unix-systems only
# generate C source from DBC/KCD
dbcppp dbc2 --dbc=file.dbc --format=C
# beauty or merge DBC/KCD
dbcppp dbc2 --dbc=file1.dbc --dbc=file2.kcd --format=DBC
# print DBC/KCD in human readable format
dbcppp dbc2 --dbc=file1.dbc --dbc=file2.kcd --format=human
cantools like decoding:
candump any | dbcppp decode --bus=vcan0,file1.dbc --bus=vcan1,file2.dbc
- Examples
C++
#include <fstream>
#include <dbcppp/Network.h>
int main()
{
std::unique_ptr<dbcppp::INetwork> net;
{
std::ifstream idbc("your.dbc");
net = dbcppp::INetwork::LoadDBCFromIs(idbc);
}
std::unordered_map<uint64_t, const dbcppp::IMessage*> messages;
for (const dbcppp::IMessage& msg : net->Messages())
{
messages.insert(std::make_pair(msg.Id(), &msg));
}
can_frame frame;
while (1)
{
receive_frame_data(&frame);
auto iter = messages.find(frame.can_id);
if (iter != messages.end())
{
const dbcppp::IMessage* msg = iter->second;
std::cout << "Received Message: " << msg->Name() << "\n";
for (const dbcppp::ISignal& sig : msg->Signals())
{
const dbcppp::ISignal* mux_sig = msg->MuxSignal();
if (sig.MultiplexerIndicator() != dbcppp::ISignal::EMultiplexer::MuxValue ||
(mux_sig && mux_sig->Decode(frame.data) == sig.MultiplexerSwitchValue()))
{
std::cout << "\t" << sig.Name() << "=" << sig.RawToPhys(sig.Decode(frame.data)) << sig.Unit() << "\n";
}
}
}
}
}
C
#include <stdio.h>
#include <dbcppp/CApi.h>
int main()
{
const dbcppp_Nework* net = dbcppp_NetworkLoadDBCFromFile("your.dbc");
if (net)
{
can_frame frame;
while (1)
{
receive_can_frame_from_somewhere(&frame);
const dbcppp_Message* msg = nullptr;
auto n_msgs = dbcppp_NetworkMessages_Size(net);
for (uint64_t i = 0; i < n_msgs; i++)
{
const dbcppp_Message* msg = dbcppp_NetworkMessages_Get(i);
if (dbcppp_MessageId(tmp) == frame.can_id)
{
printf("Received message: %s\n", dbcppp_MessageGetName(msg));
dbcppp_MessageForEachSignal(msg, print_signal_data, &frame);
for (uint64_t i = 0; i < dbcppp_MessageSignals_Size(msg); i++)
{
const dbcppp_Signal* sig = dbcppp_MessageSignals_Get(msg, i);
uint64_t raw = dbcppp_SignalDecode(sig, frame.data);
double phys = dbcppp_SignalRawToPhys(sig, raw);
printf("\t%s=%f\n", dbcppp_SignalName(sig), phys);
}
}
}
}
}
dbcppp_NetworkFree(net);
}
- version
- new_symbols
- bit_timing
- nodes
- value_tables
- messages
- message_transmitters
- environment_variables
- environment_variables_data
- signal_types
- comments
- attribute_definitions
- attribute_defaults
- attribute_values
- value_descriptions
- signal_extended_value_type_list
- signal_groups
- signal_multiplexer_value
- sigtype_attr_list
- signal_type_refs
The signals decode function is using prestored masks and fixed offsets to speed up calculation, therefore the decoding-function should be almost as fast as a code generated decode function would be. The assembly of the decode
-function on its critical path (signed and byte swap must happen) looks like this (VS19 10.0.18362.0 compiler):
template <Alignment aAlignment, Signal::ByteOrder aByteOrder, Signal::ValueType aValueType, Signal::ExtendedValueType aExtendedValueType>
double template_decode(const Signal* sig, const void* nbytes) noexcept
00007FF8025BCA73 mov rax,rcx
00007FF8025BCA76 mov rcx,qword ptr [rcx+140h]
00007FF8025BCA7D xorps xmm0,xmm0
00007FF8025BCA80 bswap r8
00007FF8025BCA83 shr r8,cl
00007FF8025BCA86 and r8,qword ptr [rax+130h]
00007FF8025BCA8D mov rcx,qword ptr [rax+138h]
00007FF8025BCA94 mov rax,rcx
00007FF8025BCA97 or rcx,r8
00007FF8025BCA9A and rax,r8
00007FF8025BCA9D cmove rcx,r8
00007FF8025BCAA1 cvtsi2sd xmm0,rcx
00007FF8025BCAA6 ret
On the best path (no byteswap must take place and ExtendedValueType == Double) the decode function only has 5 instructions:
template <Alignment aAlignment, Signal::ByteOrder aByteOrder, Signal::ValueType aValueType, Signal::ExtendedValueType aExtendedValueType>
double template_decode(const Signal* sig, const void* nbytes) noexcept
00007FF8025BCAF0 mov rax,qword ptr [rdx]
00007FF8025BCAF3 mov qword ptr [rsp+8],rcx
00007FF8025BCAF8 mov qword ptr [sig],rax
00007FF8025BCAFD movsd xmm0,mmword ptr [data]
00007FF8025BCB03 ret
- tests for decoding function for float/double is failing on some machines (currently only confirmed for System/s390x)
- Vector_DBC Does basically the same, the biggest difference is that it uses
bison
instead ofboost::spirit
for grammar parsing - CAN BUS tools in Python 3 (cantools)