Parse the .dbc file and generate C++ code to represent classes/struct for each message( for target ECU )

4.9k Views Asked by At

I am trying to generate C++ code from a .dbc file.

e.g. A message is defined like following in .dbc file

BO_ 500 IO_DEBUG: 5 IO
 SG_ IO_DEBUG_test_unsigned : 0|8@1+ (1,0) [0|0] "" DBG
 SG_ IO_DEBUG_test_signed : 8|8@1- (1,-128) [0|0] "" DBG
 SG_ IO_DEBUG_test_float1 : 16|8@1+ (0.1,0) [0|0] "" DBG
 SG_ IO_DEBUG_test_float2 : 24|12@1+ (0.01,-20.48) [-20.48|20.47] "" DBG
 SG_ IO_DEBUG_test_enum : 38|2@1+ (1,0) [0|0] "" DBG

BA_ "FieldType" SG_ 500 IO_DEBUG_test_enum "IO_DEBUG_test_enum";
VAL_ 500 IO_DEBUG_test_enum 2 "IO_DEBUG_test2_enum_two" 1 "IO_DEBUG_test2_enum_one" ;

I am trying to generate C++ code something like this. Message name will become the Class name and all signals should become the members of the class along with data-types.

//IoDebug.h -- ProcessMessageInterface is an interface.

class IoDebug : public ProcessMessageInterface {
pubic:
   // ProcessMessageInterface implementation
   void processMessage();


private:
   uint8_t testUnSigned;
   int8_t  testSigned;
   float   testFloat1;
   float   testFloat2; 
   IO_DEBUG_test_enum testEnum;
};

//IoDebug.cpp

#include "IoDebug.h"

IoDebug::processMessage() 
{
   
}

Is there any dbc parser and code generation tool(s) exists which can generate code like above?

4

There are 4 best solutions below

0
On BEST ANSWER

Here is a python script which generates C++ code. You need to install cantools packages to run following script.

import cantools
import math

def build_name(name):
    nodes = name.split("_")
    nodes[0] = nodes[0].title()
    return "".join(nodes)

def signal_variable_name(signal_name):
    return "m_" + build_name(signal_name)

def isFloat(signal):
    return True if isinstance(signal.scale, float) else False

def signal_data_type(signal):
    if not signal.choices:
        if isFloat(signal):
            return "float"
        else:
            return "int" if signal.is_signed else "uint" + str((math.floor((signal.length - 1) / 8) + 1) * 8) + "_t"
    else:
        return signal.name

def initial_signal_value(signal):
    initial = 0
    if signal.initial:
        initial = signal.initial
    print("initial: "+str(initial))
    print(signal.choices)
    if signal.choices:
        return signal.name + "_" + signal.choices[initial]
    else:
        return initial

cpp_template = """
#include <string>

#include "{messagename}.h"

using namespace std;

{messagename}::{messagename}()
{{
}}

"""
header_template = """
#ifndef {message_h}
#define {message_h}

#include <stdint.h>
#include <iostream>

class {messagename} : public {messageparent} {{
public:
    {messagename}();

    bool processMessage();

private:
"""

# dbc file 
db = cantools.database.load_file("path_to_dummy.dbc")

# We can grow following list, add those messages for which we want to generate the code. 
messages_list=["IO_DEBUG"]

for message_name in messages_list:
    # massaging message_name here. 
    good_message_name = build_name(message_name)
    message = db.get_message_by_name(message_name)
    message_cpp_file = good_message_name+".cpp"
    context = {"messagename": good_message_name, "dbc_message_name": message_name}

    # writing code for C++ file.
    f = open(message_cpp_file, "w")
    f.write(cpp_template.format(**context))
    f.write("bool {}::processMessage() {{\n    return true;\n}}\n".format(good_message_name))
    # we can add more code here to auto-generate code inside above fucntion to process the signals.
    f.close()

    # writing code for header file.
    message_header_file = good_message_name+".h"
    f = open(message_header_file, "w")
    context["message_h"] = message_name.upper()+"_H"
    context["messageparent"] = "ProcessMessageInterface"
    f.write(header_template.format(**context))

    for signal in message.signals:
        f.write("    {} {};\n".format(signal_data_type(signal), signal_variable_name(signal.name)))

    f.write("\n};\n\n#endif // " + context["message_h"])
    f.write("\n")
    f.close()

run it as

python3 script.py

Above script will generate following header and cpp files.

IoDEBUG.h

#ifndef IO_DEBUG_H
#define IO_DEBUG_H

#include <stdint.h>
#include <iostream>

class IoDEBUG : public ProcessMessageInterface {
public:
    IoDEBUG();

    bool processMessage();

private:
    uint8_t m_IoDEBUGtestunsigned;
    int m_IoDEBUGtestsigned;
    float m_IoDEBUGtestfloat1;
    float m_IoDEBUGtestfloat2;
    IO_DEBUG_test_enum m_IoDEBUGtestenum;

};

#endif // IO_DEBUG_H

IoDEBUG.cpp

#include <string>

#include "IoDEBUG.h"

using namespace std;

IoDEBUG::IoDEBUG()
{
}

bool IoDEBUG::processMessage() {
    return true;
}
1
On

Please have a look at Soureforge's comFramework, too. It's likely close to what you need. Here, the concept is to make the output controlled by templates; the powerful template engine StringTemplate V4 is fed with the parsed DBC file. You can bring virtually everything from the DBC into C/C++ (messages, signals, attributes, enumerations, node names, etc.) Unfortunately, all samples still produce C. Migration to C++ is however trivial.

See https://sourceforge.net/projects/comframe/

1
On

This is the closest thing I have found: https://github.com/astand/c-coderdbc
It seems to generate code in roughly the same format as you desire.

There is also a website associated with it: https://coderdbc.com/ccoder/uploaddbc

There is also this other similar project: https://github.com/xR3b0rn/dbcppp
But I personally did not like the generated code, since it doesn't create structs for each CAN message, and instead parses each signal individually. This approach probably works fine, but isn't quite what you are looking for.

1
On

Convert .dbc files to header files and c files for microcontroller using this windows software:

https://github.com/HamidBakhtiary/DBC_to_header

in description there is link to download software.

du to 25 MB limit it is not possible to put it in github.