DBC系列之如何使用python脚本实现excel-dbc(3)[下]

2023-11-06 20:11

本文主要是介绍DBC系列之如何使用python脚本实现excel-dbc(3)[下],希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

使用python脚本实现excel-dbc

  • 摘要
  • 1. 代码实现
    • 1.1 读取CAN通信矩阵
      • 1.1.1 导入模块
      • 1.1.2 读取文件,创建对象实例
    • 1.2 添加全局属性
      • 1.2.1 总线类型
      • 1.2.2 报文发送类型属性以及默认值
      • 1.2.3 报文发送周期属性
    • 1.3 处理信号属性
      • 1.3.1 读取对应工作簿
      • 1.3.2 创建新的frame
      • 1.3.3 封装函数
      • 1.3.4 遍历表格处理信号属性
    • 1.4 数据导出生成文件
  • 2. 运行代码测试脚本

摘要

基于上一部分《DBC系列之如何使用python脚本实现excel-dbc(3)[上]》,本文继续记录如何用python实现根据excel的CAN通信矩阵生成DBC文件。

1. 代码实现

开始前还是先准备通信矩阵文件
在这里插入图片描述
我的矩阵文件命名为demo_can_matrix.xlsx, 工作簿名为can_matrix。
然后同级目录下建立一个python文件,我命名为excel2dbc.py

1.1 读取CAN通信矩阵

1.1.1 导入模块

# 导入模块xlrd, canmatrx, demical
import xlrd
import canmatrix
import decimal

xlrd模块用于读取表格,cammatrix用于按一定的格式打包数据,demical模块提供了十进制浮点运算支持

1.1.2 读取文件,创建对象实例

# 输入文件
input_file = "demo_can_matrix.xlsx"
sheet_name = "can_matrix"
# 使用xlrd库读取excel文件
excel_obj = xlrd.open_workbook(input_file)
# 使用canmatrix库创建DBC实例
matrix_obj = canmatrix.CanMatrix()

excel文件名为demo_can_matrix.xlsx,矩阵所在的工作簿名字为can_matrix
先读取excel文件,创建一个对象。这个对象会包含表格中的工作簿
然后用canmatrix库创建一个matrix的对象,这个对象将对保存矩阵中的数据

1.2 添加全局属性

1.2.1 总线类型

# 总线类型
matrix_obj.add_global_defines("BusType", "STRING")
matrix_obj.add_define_default("BusType", "CAN")

这个总线类型用于描述这个DBC文件是CAN,LIN,MOST, FlexRay或其他类型的database.

1.2.2 报文发送类型属性以及默认值

# 报文发送类型属性以及默认值
matrix_obj.add_frame_defines("GenMsgSendType", 'ENUM "Cyclic", "Event"' )
matrix_obj.add_define_default("GenMsgSendType", "Cyclic")

1.2.3 报文发送周期属性

matrix_obj.add_frame_defines("GenMsgCycleTime", 'INT 0 65535')
matrix_obj.add_define_default("GenMsgCycleTime", "0")

1.3 处理信号属性

1.3.1 读取对应工作簿

# 获取对应canmatrix的工作簿,开始处理表格中的数据
sheet_obj = excel_obj.sheet_by_name(sheet_name)
if sheet_obj is None:raise "工作簿名错误,请检查"

1.3.2 创建新的frame

# 根据第1行有效信号描述,创建一个新的frame
frame_obj = create_new_frame(sheet_obj, 2)

Tips: 表格中的行和列都是从0开始的

1.3.3 封装函数

def create_new_frame(sheet_obj:xlrd.sheet.Sheet, row: int) -> canmatrix.Frame:if row <= 0:print("行错误,无法读取")return# 创建frame对象frame_obj = canmatrix.Frame()frame_obj.name = str(sheet_obj.cell_value(row, 5)).strip()transmitter = str(sheet_obj.cell_value(row, 1)).strip()frame_obj.add_transmitter(transmitter)message_type = str(sheet_obj.cell_value(row, 7)).strip().upper()if message_type == "P":frame_obj.add_attribute("GenMsgSendType", "Cyclic")try:frame_obj.cycle_time = int(sheet_obj.cell_value(row, 9))frame_obj.add_attribute("GenMsgCycleTime", frame_obj.cycle_time)except:print("第3行周期内容不合规")return Noneelif message_type == "E":# 由于demo矩阵中没有事件型,此处代码未写,这部分自行添加passtry:frame_obj.size = int(sheet_obj.cell_value(row, 10))except:print("第3行的DLC内容不合规")try:frame_obj.arbitration_id.id = int(sheet_obj.cell_value(row, 6), 16)except:print("第3行message id内容不合规")return frame_obj

由于还会根据报文ID的变换创建新的frame,这里需要把此函数封装起来,以便重用

1.3.4 遍历表格处理信号属性

# 遍历整个表格的所有行
for row in range(2, sheet_obj.nrows):try:message_id = int(sheet_obj.cell_value(row, 6), 16)except:print("第%d行message id不合规"%row)continueif message_id != frame_obj.arbitration_id.id:matrix_obj.add_frame(frame_obj)frame_obj = create_new_frame(sheet_obj, row)# 创建signal对象signal_obj = canmatrix.Signal()# 获取信号名字signal_obj.name = str(sheet_obj.cell_value(row, 3)).strip()# 获取信号接受节点if str(sheet_obj.cell_value(row, 2)) != "":receiver = str(sheet_obj.cell_value(row, 2)).strip()if receiver not in matrix_obj.ecus:matrix_obj.add_ecu(canmatrix.Ecu(receiver))signal_obj.add_receiver(receiver)frame_obj.add_receiver(receiver)# 获取信号起始位try:msb = int(sheet_obj.cell_value(row, 11))signal_obj.set_startbit(msb, bitNumbering= False, startLittle=False)except:print("第%d行msb内容不合规"%(row+1))# 获取信号长度try:signal_obj.size = int(sheet_obj.cell_value(row, 13))except:print("第%d行size内容不合规"%(row+1))# 获取信号大小端signal_endian = str(sheet_obj.cell_value(row, 14)).strip()if signal_endian == "Intel":signal_obj.is_little_endian = Trueelif signal_endian == "Motorola":signal_obj.is_little_endian = Falseelse:print("第%d行Byte Order内容不合规"%(row+1))# 获取信号初始值try:signal_obj.initial_value = int(sheet_obj.cell_value(row, 16), 16)except:signal_obj.initial_value = 0# 获取比例因子try:signal_obj.factor = decimal.Decimal(sheet_obj.cell_value(row, 17))except:signal_obj.factor = decimal.Decimal(1)print("第%d行factor内容不合规,已按默认值1处理"%(row+1))# 获取信号偏移量try:signal_obj.offset = decimal.Decimal(sheet_obj.cell_value(row, 18))except:signal_obj.offset = decimal.Decimal(0)print("第%d行offset内容不合规,已按默认值0处理"%(row+1))# 获取信号单位,将"/"处理为空signal_obj.unit = str(sheet_obj.cell_value(row, 21)).strip().replace("/","")# 获取信号值类型signal_value_type = str(sheet_obj.cell_value(row, 15)).strip().lower()if signal_value_type == "unsigned":signal_obj.is_float = Falsesignal_obj.is_signed = Falseelif signal_value_type == "signed":signal_obj.is_float = Falsesignal_obj.is_signed = Trueelse:signal_obj.is_float = Truesignal_obj.is_signed = False# 获取信号最小物理值try:phy_min_value = decimal.Decimal(sheet_obj.cell_value(row, 19))signal_obj.set_min(phy_min_value)except:# 按照size和偏移以及因子自动计算最小值signal_obj.set_min(signal_obj.calc_min())# 获取信号最大物理值try:phy_max_value = decimal.Decimal(sheet_obj.cell_value(row, 20))signal_obj.set_min(phy_max_value)except:# 按照size和偏移以及因子自动计算最大值signal_obj.set_min(signal_obj.calc_min())# 获取信号备注description = str(sheet_obj.cell_value(row, 4)).strip()signal_obj.add_comment(description)# 获取信号codingcoding_str = str(sheet_obj.cell_value(row, 22)).strip().replace("\"", "'")codings = coding_str.split("\n")for coding in codings:value_table = coding.strip().split(":")if len(value_table) > 1:value = value_table[0].strip()value_description = value_table[1].strip()if "-" in value:values = value.split("-")for n in values:try:signal_obj.add_values(int(n, 16), value_description)except:passelse:try:signal_obj.add_values(int(value, 16), value_description)except:passframe_obj.add_signal(signal_obj)
matrix_obj.add_frame(frame_obj)

代码中使用到try…except结构是为了防止表格内容中有其他内容影响生成,其实就是容错处理,实际使用时根据需要变换,代码是灵活的,不要拘泥于上述形式。
代码中用到了demical库,目的是将从表格中获取的字符串对象转换为十进制对象,factor,offset,最大小值都有可能为浮点数。
通过遍历处理,能够将矩阵中所有的信号都添加到matrix_obj中,接下来需要把这个数据导出为文件。

1.4 数据导出生成文件

# 导出到DBC文件
file_out = open("excel2dbc.dbc", "wb")
canmatrix.formats.dump(matrix_obj, file_out, "dbc", dbcExportEncoding="utf-8")
file_out.close()

使用cammatrix中dump()方法,可以将matrix_obj输出到文件中

2. 运行代码测试脚本

我使用的是VScode +python扩展来运行的,大家也可以自己使用自己喜欢的编辑器进行编辑,在cmd中运行也是可以的,根据自己的爱好来即可。推荐大家使用VScode或者pycharm,比较方便一点。
在这里插入图片描述
在这里插入图片描述

这张图是生成后的两个DBC的比较,可以看到VERSION属性和NS_属性不一样,VERSION属性canmtrix自行添加了,如果我们需要自己改,可以在脚本中添加代码实现。NS_属性用CANdb++打开并保存一次DBC即可,CANdb++会自动添加此部分属性。
除了这部分。其他的部分基本上一致,第二张图圈出的数据精度不一致,这通过CANdb++打开并保存后会自动适配。
通过使用python脚本,能够很方便地大量的报文和信号生成DBC,提高效率。
以上代码仅仅是适配演示的demo矩阵,和真正使用的CAN矩阵还有很大差别,但是基本上都属性和操作已经在脚本上体现了,其他需要添加的部分请大家应用时自行添加。
代码不能保证完全没有问题,可能依然还有些隐藏的问题,大家在使用过程中请仔细理解,文章中有需要的地方也请大家指出,我会及时修改采纳大家的意见。

这篇关于DBC系列之如何使用python脚本实现excel-dbc(3)[下]的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/358810

相关文章

使用Java将各种数据写入Excel表格的操作示例

《使用Java将各种数据写入Excel表格的操作示例》在数据处理与管理领域,Excel凭借其强大的功能和广泛的应用,成为了数据存储与展示的重要工具,在Java开发过程中,常常需要将不同类型的数据,本文... 目录前言安装免费Java库1. 写入文本、或数值到 Excel单元格2. 写入数组到 Excel表格

redis中使用lua脚本的原理与基本使用详解

《redis中使用lua脚本的原理与基本使用详解》在Redis中使用Lua脚本可以实现原子性操作、减少网络开销以及提高执行效率,下面小编就来和大家详细介绍一下在redis中使用lua脚本的原理... 目录Redis 执行 Lua 脚本的原理基本使用方法使用EVAL命令执行 Lua 脚本使用EVALSHA命令

Python中pywin32 常用窗口操作的实现

《Python中pywin32常用窗口操作的实现》本文主要介绍了Python中pywin32常用窗口操作的实现,pywin32主要的作用是供Python开发者快速调用WindowsAPI的一个... 目录获取窗口句柄获取最前端窗口句柄获取指定坐标处的窗口根据窗口的完整标题匹配获取句柄根据窗口的类别匹配获取句

利用Python打造一个Excel记账模板

《利用Python打造一个Excel记账模板》这篇文章主要为大家详细介绍了如何使用Python打造一个超实用的Excel记账模板,可以帮助大家高效管理财务,迈向财富自由之路,感兴趣的小伙伴快跟随小编一... 目录设置预算百分比超支标红预警记账模板功能介绍基础记账预算管理可视化分析摸鱼时间理财法碎片时间利用财

Java 中的 @SneakyThrows 注解使用方法(简化异常处理的利与弊)

《Java中的@SneakyThrows注解使用方法(简化异常处理的利与弊)》为了简化异常处理,Lombok提供了一个强大的注解@SneakyThrows,本文将详细介绍@SneakyThro... 目录1. @SneakyThrows 简介 1.1 什么是 Lombok?2. @SneakyThrows

在 Spring Boot 中实现异常处理最佳实践

《在SpringBoot中实现异常处理最佳实践》本文介绍如何在SpringBoot中实现异常处理,涵盖核心概念、实现方法、与先前查询的集成、性能分析、常见问题和最佳实践,感兴趣的朋友一起看看吧... 目录一、Spring Boot 异常处理的背景与核心概念1.1 为什么需要异常处理?1.2 Spring B

Python中的Walrus运算符分析示例详解

《Python中的Walrus运算符分析示例详解》Python中的Walrus运算符(:=)是Python3.8引入的一个新特性,允许在表达式中同时赋值和返回值,它的核心作用是减少重复计算,提升代码简... 目录1. 在循环中避免重复计算2. 在条件判断中同时赋值变量3. 在列表推导式或字典推导式中简化逻辑

python处理带有时区的日期和时间数据

《python处理带有时区的日期和时间数据》这篇文章主要为大家详细介绍了如何在Python中使用pytz库处理时区信息,包括获取当前UTC时间,转换为特定时区等,有需要的小伙伴可以参考一下... 目录时区基本信息python datetime使用timezonepandas处理时区数据知识延展时区基本信息

Python位移操作和位运算的实现示例

《Python位移操作和位运算的实现示例》本文主要介绍了Python位移操作和位运算的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一... 目录1. 位移操作1.1 左移操作 (<<)1.2 右移操作 (>>)注意事项:2. 位运算2.1

如何在 Spring Boot 中实现 FreeMarker 模板

《如何在SpringBoot中实现FreeMarker模板》FreeMarker是一种功能强大、轻量级的模板引擎,用于在Java应用中生成动态文本输出(如HTML、XML、邮件内容等),本文... 目录什么是 FreeMarker 模板?在 Spring Boot 中实现 FreeMarker 模板1. 环