乐于分享
好东西不私藏

用AI和古法编程写一个解析DBC文件的软件

用AI和古法编程写一个解析DBC文件的软件

DBC文件是Vector公司定义的CAN通讯数据格式的文件,类似于串口通讯的格式哪些数据位表示哪些指令状态.汽车的供应商可以根据整车厂提供的这个文件确定自己的ECU模块需要用哪几个ID和整车通讯及接收哪些指令反馈哪些状态数据等,具体包含的信息类容有:
  1. 网络节点:描述当前网络有哪些ECU,以BU_开头 如:BU_: Airbag BMS
  2. 报文定义:CAN帧的ID和长度,以BO_开头,如:BO_ 512 VehicleSpeed: 8 ECU_Engine
  3. 信号定义:定义了该信号(车速,模式等)在CAN帧中位置和位宽长度单位.以SG_开头,如SG_ VehicleSpeed: 0|16@0+ (0.01,0) [0|250] “km/h” ECU_BMS,ECU_ESP
  4. 值表:非连续信号的单位吧(挡位,模式等),以VAL_开头,如VAL_ 512 LightStatus 0 “Off” 1 “On” 2 “Error”;
  5. 注释与属性:报文的发送周期等.
DBC文件的原始内容是比较难懂的,需要第三方的工具软件(CANdb++)才能查看和编辑,也有用python 安装cantool库把DBC文件转为excel的.如今完全可以用AI写一个工具软件解析DBC文件实现可视化.
丢给AI的软件需求如下:
  • 使用网页前端开发,这样可以免安装
  • 有一个加载按钮从本地加载dbc文件
  • 解析dbc文件格式
  • 显示当前网络的拓扑结构
  • 能够导出CSV文件
  • 能导出信号节点的每一帧C语言结构体,ECU开发一般是C语言
为让AI更容易理解需求可以先丢一个已有的DBC文件给AI。
下面是AI做出的软件:
还没加载DBC文件的界面,导出CSV和C语言结构体按钮都灰色无效.
加载DBC文件后的自动解析DBC文件,这个核心功能AI一步就能到位,网络拓扑图的绘制的代码调试好几次一直调不好,人为古法编程才能搞定.
AI解析DBC的核心代码:
function parseDbcContent(content) {dbcData = { nodes: [], messages: [], version""bitTimingnull };const lines = content.split('\n');let currentMessage = null;for (let i = 0; i < lines.length; i++) {const line = lines[i].trim();if (!line || line.startsWith('//') || line.startsWith('/*')) continue;if (line.startsWith('VERSION')) {  const match = line.match(/VERSION\s+"([^"]*)"/);  if (match) dbcData.version = match[1];  } else if (line.startsWith('BU_:')) {   const nodes = line.substring(4).trim().split(/\s+/);   dbcData.nodes = nodes.filter(n => n && n !== 'Vector__XXX'); } else if (line.startsWith('BO_')) {    if (currentMessage) dbcData.messages.push(currentMessage);     const match = line.match(/BO_\s+(\d+)\s+(\w+)\s*:\s*(\d+)\s+(\w+)/);    if (match) {      currentMessage = {                            idparseInt(match[1]),                            name: match[2],                            lengthparseInt(match[3]),                            transmitter: match[4],                            signals: []                        };                    } else {                        currentMessage = null;                    }                } else if (line.startsWith('SG_') && currentMessage) {                    const match = line.match(/SG_\s+([\w_]+)\s*:\s*(\d+)\|(\d+)@(\d+)([+-])\s*\(([^,]+),([^)]+)\)\s*\[([^|]+)\|([^\]]+)\]\s*"([^"]*)"\s*(.*)/);                    if (match) {                        currentMessage.signals.push({                            name: match[1],                            startBitparseInt(match[2]),                            lengthparseInt(match[3]),                            endiannessparseInt(match[4]),                            signed: match[5] === '-',                            factorparseFloat(match[6]),                            offsetparseFloat(match[7]),                            minparseFloat(match[8]),                            maxparseFloat(match[9]),                            unit: match[10],                            receivers: match[11].trim().split(/\s*,\s*/).filter(r => r && r !== 'Vector__XXX')                        });                    }                }            }            if (currentMessage) dbcData.messages.push(currentMessage);            dbcData.messages.sort((a, b) => a.id - b.id);            if (dbcData.nodes.length === 0) {                const nodeSet = new Set();                dbcData.messages.forEach(msg => {                    if (msg.transmitter) nodeSet.add(msg.transmitter);                    msg.signals.forEach(sig => sig.receivers.forEach(rec => nodeSet.add(rec)));                });                dbcData.nodes = Array.from(nodeSet);            }        }
不到60行代码就能完成了DBC文件的基本解析,之所以只说基本是因为还有值表等属性没有解析.
信号列表节点标注了节点名称和报文数量,还有搜索功能.
节点的所有报文,表明了报文ID,信号数量
信号列表,标明了信号的起始位和位宽,范围,单位
导出C语言结构体
/* ================================================================ *  发送节点: BMS_MQB (3 个报文) * ================================================================ *//** * @brief CAN报文: BMS_Hybrid_01 * @note  帧ID: 0x65C (1628 DEC) *        发送节点: BMS_MQB *        数据长度: 8 字节 *        接收节点: Gateway_MQB */typedef union{    uint8_t raw[8];    struct    {        uint16_t   reserved0:               13;        uint8_t    BMS_HYB_ASV_hinten_Status: 1/**< 起始位:13, 长度:1 */        uint8_t    BMS_HYB_ASV_vorne_Status: 1/**< 起始位:14, 长度:1 */        uint8_t    BMS_HYB_KD_Fehler:       1/**< 起始位:15, 长度:1 */        uint8_t    BMS_HYB_BattFanSpd:      1/**< 起始位:16, 长度:4 */        uint8_t    reserved1:               3;        uint8_t    BMS_HYB_VentilationReq:  1/**< 起始位:20, 长度:1 */        uint8_t    BMS_HYB_Kuehlung_Anf:    2/**< 起始位:22, 长度:2 */        uint8_t    reserved2:               1;        uint8_t    BMS_HYB_Temp_vor_Verd:   1/**< 起始位:24, 长度:8 */        uint8_t    reserved3:               4;        uint8_t    BMS_HYB_BattFanSpd:      3/**< 起始位:16, 长度:4 */        uint8_t    BMS_HYB_Temp_nach_Verd:  1/**< 起始位:32, 长度:8 */        uint8_t    BMS_HYB_Temp_vor_Verd:   7/**< 起始位:24, 长度:8 */        uint8_t    BMS_Temperatur:          1/**< 起始位:40, 长度:8 */        uint8_t    BMS_HYB_Temp_nach_Verd:  7/**< 起始位:32, 长度:8 */        uint8_t    BMS_Temperatur_Ansaugluft: 1/**< 起始位:48, 长度:8 */        uint8_t    BMS_Temperatur:          7/**< 起始位:40, 长度:8 */        uint8_t    BMS_IstSpannung_HV:      1/**< 起始位:56, 长度:8 */        uint8_t    BMS_Temperatur_Ansaugluft: 7/**< 起始位:48, 长度:8 */    } fields;} CAN_BMS_Hybrid_01_t;
可以看出自动生成结构体的信号还是不对,可能要古法编程才能搞定。
感受
解析dbc的这个软件的功能需求难度只能算中等,要AI独自完成这个任务还略显吃力,AI的优点是快速写出基本的代码框架,再就是不能把所有需求一次性丢给AI,只能一步步加需求,所以还消耗时间的。中间还出现一些bug,不管让AI怎么改都改不好,需要人工找bug和写代码,愿AI越来越强大吧.