解析 .dbc 文件并生成 C++ 代码以表示每个消息的类/结构(对于目标 ECU)

Posted

技术标签:

【中文标题】解析 .dbc 文件并生成 C++ 代码以表示每个消息的类/结构(对于目标 ECU)【英文标题】:Parse the .dbc file and generate C++ code to represent classes/struct for each message( for target ECU ) 【发布时间】:2021-02-01 06:55:38 【问题描述】:

我正在尝试从 .dbc 文件生成 C++ 代码。

例如消息在 .dbc 文件中定义如下

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" ;

我正在尝试生成类似这样的 C++ 代码。消息名称将成为类名称,所有信号都应与数据类型一起成为该类的成员。

//IoDebug.h -- ProcessMessageInterface 是一个接口。

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() 

   

是否存在可以生成上述代码的任何 dbc 解析器和代码生成工具?

【问题讨论】:

【参考方案1】:

这是我发现的最接近的东西: https://github.com/astand/c-coderdbc 它似乎生成的代码格式与您想要的大致相同。

还有一个与之相关的网站: https://coderdbc.com/ccoder/uploaddbc

还有这个其他类似的项目: https://github.com/xR3b0rn/dbcppp 但我个人不喜欢生成的代码,因为它不会为每个 CAN 消息创建结构,而是单独解析每个信号。这种方法可能效果很好,但并不是您想要的。

【讨论】:

感谢@Ian 回答这个问题。我还不能用上面提到的项目生成代码,但它看起来很有希望。短期溶胶。我能够想出一个解析 dbc 文件并生成 C++ 代码的 python 脚本。我将分享溶胶。几天后。谢谢。【参考方案2】:

这是一个生成 C++ 代码的 python 脚本。您需要安装 cantools 软件包才能运行以下脚本。

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()

运行它

python3 script.py

上面的脚本会生成以下头文件和cpp文件。

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;

【讨论】:

以上是关于解析 .dbc 文件并生成 C++ 代码以表示每个消息的类/结构(对于目标 ECU)的主要内容,如果未能解决你的问题,请参考以下文章

DBC格式解析(以文本形式打开)

DBC文件解析

是否可以使用 Qt(不使用 XSD 工具)从 xsd 生成 C++ 类?

DBC概述

在C++中如何用schema校验xml文件

经典CAN2.0 DBC快速切换为CANFD DBC