当前位置: 首页 > news >正文

【DBCExcelConvent】CAN报文解析辅助工具之DBC与Excel互转

前言

   CAN总线翻译文件DBC是整车解析过程中非常核心的一部分,因此为了能被各大CAN工具解析,它也有自己的一套编码规则。但并不是无时无刻都有条件打开该文件,对于工程师而言。其实比较直观和通用的大多数还是Excel表格。因此,为了打通这两者之间的桥梁,根据规则特意开发了一个转换工具。而在本篇文章之前,已经通过Matlab编写了一套转换工具,但是为了更好的适配不同平台,本次基于QT重新开发并完善了这一套转换工具。

资源如下:

百度网盘:
链接: https://pan.baidu.com/s/1ARkRkPPo-EaalcE5eSv1RQ?pwd=f7z6 提取码: f7z6


前言

文章目录

一、工具介绍

1.1 Excel模板界面介绍

1.2 工具界面介绍

1.3 资源释放

二、使用步骤

2.1 模板生成

2.2 DBC转Excel

2.3 Excel转DBC

2.4 弹窗信息

三、工具开源框架

3.1 框架内容

3.2 字段信息定义

3.3 Excel转DBC思路

3.4 DBC转Excel思路

四、总结


一、工具介绍

        该工具是为了将CAN总线解析专用文件DBC转换成通用可读的Excel文件。具备CAN、CANFD、J1939三种类型的帧进行转换。但是,本工具仅支持单帧的报文进行转换,因此多路复用的报文暂时无法转换。此外。J1939目前仅是根据目前的开发需求释放出一些接口,并不能做到面面俱到,因此可能会有部分功能不适配。接下来将简单介绍一下该工具。

1.1 Excel模板界面介绍

        表头信息如下所示:

        该Excel的表头与以前所用的模板相差无异。只是对部分属性做了重映射和根据需求增加了一些属性。主要的变更点如下:

1、第2列 报文类型增加CAN、CANFD、J1939的下拉列表选项

2、第4列 报文发送类型根据DBC属性定义添加对应的下拉枚举选项

3、第6列 报文长度由于增加了CANFD,因此根据DBC文件中的属性定义添加下拉枚举选项

4、第8列 信号描述增加对报文的解释,添加方式与增加信号描述的方式一致

5、第21列 信号发送类型根据DBC属性定义添加对应的下拉枚举选项

6、第22列 SPN参数编号根据DBC J1939帧属性写入对应的十进制SPN值

7、第23列 信号类型根据DBC属性定义添加对应的下拉枚举选项

8、第24列 协议文档根据DBC J1939帧属性通过写入指定标识符来追溯报文与信号定义的来源

9、第25列第3行 节点根据DBC J1939帧属性添加5个字段定义,该字段只有为J1939帧时才有效

10、单元格填充规则

目标Excel存在以下使用规则,请务必遵守:

规则1:目标Excel的前3行和前24列是固定表头属性,不可更改,第24列后为节点属性,只能增加节点属性,不能添加其它元素

规则2:目标Excel的表头中文名称不可修改,英文名称可以自定义

规则3:目标Excel的单元格若具有下拉列表,则只能从下拉列表中选取

规则4:目标Excel的信号名称定义中间不要存在空格

规则5:目标Excel的数值一律以10进制数存储,此外仅支持0xA这种形式的16进制数拓展

规则6:目标Excel的信号解析末尾不需要添加分号

规则7:目标Excel的单元格描述除非必要,否则单元格内的任何内容必须以英文形式存储

TIps:使用工具前建议先仔细查阅说明文档。

1.2 工具界面介绍

      工具主界面如下所示:

如上所示:工具开放了5个字段信息,其作用如下:

1、文件选择 点击该按钮可实现目标文件的选择,目前仅支持.dbc/.xlsx文件。同时如果获取到有效路径会在下方的空白框中将路径打印出来。

2、模板生成 选择保存路径后再点击该按钮可创建内置的.dbc/.xlsx模板文件以便于快速了解。

3、START 选择文件路径和保存路径后再点击该按钮,工具会自动识别尾缀并开启转换。

4、路径选择 点击该按钮可实现选择目标文件的保存路径,同时如果获取到有效路径会在下方的空白框中将路径打印出来。

5、转换信息 该字段仅显示作用,不可点击。但是其下方的空白可打印转换过程中流程信息和文件校验信息。

1.3 资源释放

      该工具的资源包最终以.7z的压缩包形式释放,其中会包含如下资源:

1、使用说明文档是对该工具使用方法的总结和指导

2、工程文件,释放部分代码供自主开发参考

3、工具的可执行文件.exe,双击打开即可使用,部分需要管理员授权

4、readme 资源包说明,以上资源如有缺漏可联系开发人员补充

二、使用步骤

2.1 模板生成

第一步 选择保存路径

      打开工具后点击路径选择按钮会出现选择文件夹的弹窗,如下所示

      如果是有效路径,会在下方的空白框中打印相关信息。

第二步 点击模板生成

       点击模板生成按钮后等待运行,转换过程中会将一些信息打印在最下方的空白框中。如下所示:

        若成功转换后会弹出路径索引的弹窗,点击是会直接打开前面选择的保存路径,否则不会跳转。

第三步 复制或者直接打开使用

        在指定路径下找到模板文件,建议先试着打开看文件是否异常,如果遇到文件异常始终无法修复,可以联系开发人员解决。

2.2 DBC转Excel

第一步 选择目标文件

       打开工具后点击文件选择按钮选择目标.dbc文件,此处直接用模板文件演示。

         如果检索到有效路径,则会在下方空白框中打印相关信息。

第二步 选择保存路径

       此处与模板生成的操作一致,便不重复解释。

第三步 点击START

        完成目标文件的选择和存储路径的选择后即可点击START开始转换。工具会自动识别目标文件的后缀并进行对应的转换

        与生成模板一样,转换过程中会在下方空白框打印信息,同时转换完成会出现弹窗选择是否直接跳转目标文件夹。

第四步 目标文件查找

       生成的目标文件名称格式为.dbc的完整文件名+_DM.xlsx。在保存路径下找到该文件即可使用,或者直接在下方信息框中找到完整的路径。

2.3 Excel转DBC

 第一步 选择目标文件

       打开工具后点击文件选择按钮选择目标.xlsx文件,此处直接用上一步生成的文件演示。

         如果检索到有效路径,则会在下方空白框中打印相关信息。

第二步 选择保存路径

       此处与模板生成的操作一致,便不重复解释。

第三步 点击START

        完成目标文件的选择和存储路径的选择后即可点击START开始转换。工具会自动识别目标文件的后缀并进行对应的转换

        与生成Excel一样,转换过程中会在下方空白框打印信息,同时转换完成会出现弹窗选择是否直接跳转目标文件夹。

第四步 目标文件查找

       生成的目标文件名称格式为.xlsx的完整文件名+_DM.dbc。在保存路径下找到该文件即可使用,或者直接在下方信息框中找到完整的路径。

2.4 弹窗信息

1、该弹窗在点击start后检索到无效路径时出现

2、该弹窗在点击start后检索到文件已被打开或无权限访问时出现

3、该弹窗在点击start后检索到Excel表头中文规范异常时出现

4、该弹窗在点击start后检索到Excel表头节点J1939字段信息丢失时出现

三、工具开源框架

        为方便各位感兴趣的朋友们自主开发,现开源一部分程序供参考。但是不鼓励在该软件基础上直接二次开发,仅是作为入门指导使用。

3.1 框架内容

释放的资源改动内容如下:

1、仅读取DBC文件的BU_/BO_段并进行转换

2、取消信息打印,可在代码自行添加调试

3、取消模板生成按钮

4、取消弹窗信息

3.2 字段信息定义

1、定义支持转换的报文与信号的最大数量

#define MsgMax 20
#define SigMax 20

2、枚举定义各列以便于后续开发灵活列校验

enum ExcelMsgCloum
{Colum_Msg_Name = 1,Colum_Msg_Type,Colum_Msg_ID,Colum_Msg_TxType,Colum_Msg_CycleTime,Colum_Msg_Length,Colum_Msg_SignalNam,Colum_Msg_SignalDescription,Colum_Msg_SignalValueDescription,Colum_Msg_ByteOrder,Colum_Msg_StartByte,Colum_Msg_StartBit,Colum_Msg_BitLength,Colum_Msg_DataType,Colum_Msg_SignalResolution,Colum_Msg_SignalOffset,Colum_Msg_SignalPhyMinValue,Colum_Msg_SignalPhyMaxValue,Colum_Msg_SignalInitValue,Colum_Msg_SignalUnit,Colum_Msg_SignalTxType,//J1939Colum_Msg_SigSPNVal,Colum_Msg_SigType1939,Colum_Msg_SigSAEDocument,Colum_Msg_NodeStart,
};

3、定义节点相关的存储信息便于后续直接调用


typedef struct DbcMsgNode
{uint8_t Msg_Nodenum;QString Msg_NodeName[20];}ST_READNODE_DBC_T;

 4、定义接收节点信息便于后续直接调用


typedef struct Msg_SignalRxNode
{uint8_t SigRxNodenum;QString SigNodeName;
}ST_SIGRXNODE_DBC_T;

5、定义数据暂存信息用来存储字段关键信息


typedef struct DbcMessage
{QString Msg_ID;QString Msg_Name;QString Msg_Length;QString Msg_TxNode;DbcMsgNode NodeList;}ST_READMSG_DBC_T;

6、定义DBC总体信息用来存储宏观数据信息


typedef struct DbcFifo
{uint16_t signalstotlenum;uint16_t Messagetotlenum;
}ST_MSGFIFO_DBC_T;

7、定义写入Excel的接口定义用来读写excel单元格信息


typedef struct ExcelMessage
{QString Excel_MsgName;QString Excel_MsgID;QString Excel_MsgLength;QString Excel_TxNode;}ST_WRITEMSG_EXCEL_T;

3.3 Excel转DBC思路

第一步 从excel单元格获取信息

1、写入路径   QString path = filePath;2、调用接口获取文件句柄以进行离线操作QXlsx::Document xlsx(path);3、调用QFile对文件进行读写等操作QFile file(path);4、打开excel 选中默认sheetfile.exists();xlsx.selectSheet(xlsx.sheetNames().first());

第二步 将读取的信息分类存储

//读取数据 按照报文分组存放 xlsx.read(3,1).toString() NodeListfor (int i = 0; i < SigMax; ++i){DbcMsg[i] = DbcMessage();   // 调用默认构造}for(int i = Colum_Msg_NodeStart;i <= lastCol;i ++ ){DbcMsg[0].NodeList.Msg_NodeName[i - Colum_Msg_NodeStart] = xlsx.read(2,i).toString();DbcMsg[0].NodeList.Msg_Nodenum ++;}

第三步 按照DBC格式将关键信息拼接

//BO_:用于定义报文信息,格式为 BO_ ID Message: Length TxNodeQString DBCFifoMessages;QString NodeMessages;QString eol = "\n";for(uint8_t i = 0;i <DbcMsg[0].NodeList.Msg_Nodenum;i ++){NodeMessages = NodeMessages + DbcMsg[0].NodeList.Msg_NodeName[i]+" ";}DBCFifoMessages = "BU_:" + NodeMessages + eol;DBCFifoMessages += eol;

第四步 创建DBC文件并写入

    // 1. 组装完整路径QString fullPath = savePath+"/"+file_name+"_DM.dbc";QString DBCFifoMessages = DBC_NSMsg();DBCFifoMessages += DBC_BUMsg();DBCFifoMessages += DBC_BOSGMsg();// 2. 写文件QFile file(fullPath);file.open(QIODevice::WriteOnly | QIODevice::Text);QTextStream out(&file);out << DBCFifoMessages;file.close();

3.4 DBC转Excel思路

第一步 字段信息定义---可直接调用3.3中所描述的定义

第二步 读取DBC文件

//以文本形式读取DBC文件QList<DbcMessage> msgList;QFile dbcfile(filePath);dbcfile.open(QIODevice::ReadOnly | QIODevice::Text);

第三步 根据字段信息编写正则表达式

//BU_正则表达式 QRegularExpression buRegex("^\\s*BU_\\:\\s*(.+)$");//BO_正则表达式QRegularExpression boRegex("^\\s*BO_\\s+(\\d+)\\s+(\\w+)\\s*:\\s*(\\d+)\\s*(\\w*)");//开启文本数据流QTextStream in(&dbcfile);

第四步 按行遍历文本

    while (!in.atEnd()){QString line = in.readLine().trimmed();// 跳过注释行和空行if (line.startsWith("//") || line.isEmpty()){continue;}// 匹配xx段QRegularExpressionMatch matchBU = buRegex.match(line);QRegularExpressionMatch matchBO = boRegex.match(line);if(matchBU.hasMatch()){QStringList nodeList = matchBU.captured(1).split(' ', QString::SkipEmptyParts);msg[0].NodeList.Msg_Nodenum = nodeList.size();for (int i = 0; i < nodeList.size(); ++i){msg[0].NodeList.Msg_NodeName[i] = nodeList.at(i);msg_excel.NodeList.Msg_NodeName[i] = msg[0].NodeList.Msg_NodeName[i];}}else if (matchBO.hasMatch()){lineCnt ++;msg[lineCnt-1].Msg_ID = matchBO.captured(1);msg[lineCnt-1].Msg_Name = matchBO.captured(2);msg[lineCnt-1].Msg_Length = matchBO.captured(3);msg[lineCnt-1].Msg_TxNode = matchBO.captured(4);msgList.append(msg[lineCnt-1]);}else{}}

第五步 创建表格

    using namespace QXlsx;QXlsx::Document xlsx;          // 新建工作簿QString sheetName = "DBC_Data";uint16_t ExcelEndRowNum = DBCMSGFIFO.Messagetotlenum+DBCMSGFIFO.signalstotlenum+2+DBCMSGFIFO.Messagetotlenum;    //报文数+信号数+(隔断-1)+表头(3)/******** 定义表头 ********/    QStringList headers ={"Msg Name\n报文名称","Msg Type\n报文类型","Msg ID\n报文标识符","Msg Send Type\n报文发送类型","Msg Cycle Time\n报文周期时间","Msg Length\n报文长度","Signal Name\n信号名称","Signal Description\n信号描述","Signal Value Description\n信号值描述","Byte Order\n排列格式","Start Byte\n起始字节","Start Bit\n起始位","Bit Length\n信号长度","Data Type\n数据类型", "Resolution\n精度","Offset\n偏移量","Signal PhyMinValue\n物理最小值","Signal PhyMaxValue\n物理最大值", "Signal Initial Value\n初始值", "Unit\n单位","Sig Send Type\n信号发送类型","SPN\n参数编号","SigType\n信号类型","SAEDocument\n协议文档","Node\n节点",};int lastCol = Colum_Msg_NodeStart + msg_excel.NodeList.Msg_Nodenum - 1;//总列数/********  创建工作簿,并切换到指定 Sheet ********/if (!xlsx.selectSheet(sheetName)){xlsx.addSheet(sheetName);    // 如果 Sheet 已存在则切换,不存在则自动新建}for(int col = 1;col <= (Colum_Msg_NodeStart-1);++ col){xlsx.write(1, col,QVariant());xlsx.write(2, col,QVariant());xlsx.mergeCells(QXlsx::CellRange(1,col,2,col));}/******** 写入表头 ********/for (int c = 0; c < headers.size(); ++c){xlsx.write(1, c + 1, headers.at(c));}xlsx.mergeCells(QXlsx::CellRange(1, Colum_Msg_NodeStart, 1, lastCol));for(uint8_t i = Colum_Msg_NodeStart;i <= lastCol;i ++){xlsx.write(2, i, QVariant());xlsx.write(2, i, msg_excel.NodeList.Msg_NodeName[i-Colum_Msg_NodeStart]);xlsx.write(3, i, "FunctionInstance:\nFunction:\nSystem:\nIndustryGroup:\nStationAddress:");}xlsx.write(3, 1, "NmJ1939节点属性 请勿删除");xlsx.mergeCells(QXlsx::CellRange(3, 1, 3, Colum_Msg_NodeStart-1));

第六步 写入数据

    int targetRow = 4;for(int i = 1;i <= lastCol;i ++)//lie{for(int j = targetRow;j <= ExcelEndRowNum;j ++)//hang{xlsx.write(j, i, "");}}for(uint8_t i = 0;i < DBCMSGFIFO.Messagetotlenum;i ++){//预处理数据WExcelData.Excel_MsgName = msg[i].Msg_Name;//2566834757 msg[i].Msg_IDWExcelData.Excel_MsgID = "0x"+QString::number(GetMessageID(msg[i].Msg_ID),16).toUpper();WExcelData.Excel_MsgLength = msg[i].Msg_Length;WExcelData.Excel_TxNode = msg[i].Msg_TxNode;xlsx.write(targetRow,Colum_Msg_Name,WExcelData.Excel_MsgName);//列1xlsx.write(targetRow,Colum_Msg_ID,WExcelData.Excel_MsgID);//列3xlsx.write(targetRow,Colum_Msg_Length,WExcelData.Excel_MsgLength.toUInt(nullptr, 10));//列6//空白格渲染for(uint8_t q = (Colum_Msg_Length+1);q <= lastCol;q ++){xlsx.write(targetRow,q,"");//列6}for(uint8_t n = 0;n < msg_excel.NodeList.Msg_Nodenum;n ++){if(WExcelData.Excel_TxNode == msg_excel.NodeList.Msg_NodeName[n]){xlsx.write(targetRow,Colum_Msg_NodeStart+n,"S");//列25+xbreak;}}targetRow = targetRow+2;}/******** 6. 保存文件 ********/QString fullPath = savePath+"/"+file_name+"_DM.xlsx";xlsx.saveAs(fullPath);

四、总结

   总体来说,该工具借助QT开发平台实现了DBC与Excel互转的功能,通过对比前系列基于Matlab开发的软件体系可以发现其核心比如正则表达式都是大差不差的,主要就是设计思路以及如何去挖掘平台的功能来辅助实现我们需要的功能。

   因此,本系列的宗旨就是鼓励自主创新,而不建议依赖于某一较完整的框架进行二次开发。这也是只开源部分代码的原因所在。

   最后的最后,我们始终坚信"授人以鱼不如授人以渔的道理",同时也期待各界大佬前来指正与交流。

http://www.dtcms.com/a/353196.html

相关文章:

  • 使用k8s实现部署MySQL的主从复制
  • 【LeetCode - 每日1题】求网格最长V形对角线段的长度
  • 页面跳转html
  • HTML响应式设计的颜色选择器,适配各种屏幕尺寸
  • rk3588 ubuntu20.04屏幕显示问题解决
  • CPU-IO-网络-内核参数的调优
  • AOSP 编译系统 (Android build system)
  • 嵌入式C语言进阶:位操作的艺术与实战
  • 【测试】pytest测试环境搭建
  • Linux 离线环境下 Anaconda3 与核心机器学习库(scikit-learn/OpenCV/PyTorch)安装配置指南
  • 解决Visual Studio中UWP设计器无法显示的问题:需升级至Windows 11 24H2
  • 【SQL优化案例】SQL执行频率问题与优化效果预期
  • NumPy/PyTorch/C char数组内存排布
  • 网站防爆破安全策略分析
  • python项目开发:创建虚拟环境
  • 利用机器学习优化Backtrader策略原理与实践
  • 深入解析函数栈帧创建与销毁
  • 斯塔克工业技术日志:用基础模型打造 “战甲级” 结构化 AI 功能
  • 预测模型及超参数:1.传统机器学习:SVR与KNN
  • 网页版云手机怎么样
  • Enduro 克隆游戏 — 基于 HTML、CSS 与 JavaScript 的完整教程模板
  • 23种设计模式——单例模式(Singleton)​详解
  • 金仓数据库文档系统全面升级:用户体验焕然一新
  • CPU、IO、网络与内核参数调优
  • Linux 性能调优实战:CPU、磁盘 I/O、网络与内核参数
  • 系统架构设计师备考第8天——嵌入式系统
  • 工业网络安全:保护制造系统和数据
  • Linux 系统CPU-IO-网络-内核参数的调优
  • 【学习笔记】GB 42250-2022标准解析
  • 手写MyBatis第36弹:MyBatis执行流程中SQL命令类型解析