提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
文章摘要
文章探讨如何使用 Python 将 Excel 文件转换为 DBC 格式。这个过程包括从 Excel 表读取数据,并将其转换为适合 DBC 文件的结构化文本格式。
以下是本篇文章正文内容
一、基本介绍
1.CAN通信
CAN通信和DBC文件编辑是汽车电子领域的重要部分。CAN(Controller Area Network)是一种串行通信协议,广泛用于汽车和工业应用。它允许ECU(电子控制单元)之间进行实时数据交换。
2.DBC文件
1. 简介
- DBC文件是CAN数据库文件,定义了CAN网络的信号和消息。
- 包含标识符、信号名称、单位、范围等信息。
2. 编辑工具
- CANdb++ Editor:Vector提供的工具,用于创建和编辑DBC文件。
- 功能:定义消息和信号、设置信号值、添加注释等。
3. 基本操作
- 创建消息:设置标识符和数据长度。
- 添加信号:定义信号名称、起始位、长度、缩放因子和偏移量。
- 设置属性:例如信号的发送类型和周期时间。
参考链接:https://blog.csdn.net/qq_40552138/article/details/133464278
二、转换思路框架
1.基于DBC文件(.dbc)可以用文本格式(.txt)格式打开的思路,对照标准DBC文件的文本格式来进行转换
参考链接:https://bbs.huaweicloud.com/blogs/319492
2.Excel 文件处理:提取Excel信息,填入到文本相对应位置(具体位置,参考完整代码)
3.创建文件选择和处理的简单 GUI
# 保存路径窗口
def open_save_dialog():
save_path = filedialog.askdirectory()
if save_path:
save_entry.delete(0, tk.END)
save_entry.insert(0, save_path)
# 选取文件窗口
def open_file_dialog():
file_paths = filedialog.askopenfilenames(filetypes=[("Excel files", "*.xls;*.xlsx;*.xlsm")])
if file_paths:
file_entry.delete(0, tk.END)
file_entry.insert(0, ";".join(file_paths))
if __name__ == '__main__':
root = tk.Tk()
root.title("Excel2DBC")
# 选择文档
file_frame = tk.Frame(root)
file_frame.pack(padx=10, pady=10)
file_label = tk.Label(file_frame, text="选择需要转换的Excel文档:")
file_label.pack(side=tk.LEFT)
file_entry = tk.Entry(file_frame, width=50)
file_entry.pack(side=tk.LEFT, padx=5)
browse_button = tk.Button(file_frame, text="Browse", command=open_file_dialog)
browse_button.pack(side=tk.LEFT)
# 选择保存路径
save_frame = tk.Frame(root)
save_frame.pack(padx=10, pady=10)
save_label = tk.Label(save_frame, text="选择保存路径:")
save_label.pack(side=tk.LEFT)
save_entry = tk.Entry(save_frame, width=50)
save_entry.pack(side=tk.LEFT, padx=5)
save_button = tk.Button(save_frame, text="Browse", command=open_save_dialog)
save_button.pack(side=tk.LEFT)
# 程序处理按钮写入
process_button = tk.Button(root, text="Process", command=process_file)
process_button.pack(pady=10)
root.mainloop()
三、代码实现
1.导入库
import xlrd
import re
import os
import tkinter as tk
from tkinter import filedialog, messagebox
2.GUI窗口实现
# 保存路径窗口
def open_save_dialog():
save_path = filedialog.askdirectory()
if save_path:
save_entry.delete(0, tk.END)
save_entry.insert(0, save_path)
# 选取文件窗口
def open_file_dialog():
file_paths = filedialog.askopenfilenames(filetypes=[("Excel files", "*.xls;*.xlsx;*.xlsm")])
if file_paths:
file_entry.delete(0, tk.END)
file_entry.insert(0, ";".join(file_paths))
if __name__ == '__main__':
root = tk.Tk()
root.title("Excel2DBC")
# 选择文档
file_frame = tk.Frame(root)
file_frame.pack(padx=10, pady=10)
file_label = tk.Label(file_frame, text="选择需要转换的Excel文档:")
file_label.pack(side=tk.LEFT)
file_entry = tk.Entry(file_frame, width=50)
file_entry.pack(side=tk.LEFT, padx=5)
browse_button = tk.Button(file_frame, text="Browse", command=open_file_dialog)
browse_button.pack(side=tk.LEFT)
# 选择保存路径
save_frame = tk.Frame(root)
save_frame.pack(padx=10, pady=10)
save_label = tk.Label(save_frame, text="选择保存路径:")
save_label.pack(side=tk.LEFT)
save_entry = tk.Entry(save_frame, width=50)
save_entry.pack(side=tk.LEFT, padx=5)
save_button = tk.Button(save_frame, text="Browse", command=open_save_dialog)
save_button.pack(side=tk.LEFT)
# 程序处理按钮写入
process_button = tk.Button(root, text="Process", command=process_file)
process_button.pack(pady=10)
root.mainloop()
3.Excel文件处理实现
# 文件处理函数
def process_file():
file_paths = file_entry.get().split(";")
save_path = save_entry.get()
if not file_paths or file_paths == ['']:
messagebox.showerror("Error", "Please select one or more files.")
return
if not save_path:
messagebox.showerror("Error", "Please select a save directory.")
return
try:
for file_path in file_paths:
file_name = os.path.basename(file_path)
save_file_path = os.path.join(save_path, file_name.rsplit('.', 1)[0] + '.dbc')
process_excel_file(file_path, save_file_path)
messagebox.showinfo("Success", f"Processed data saved to {save_path}")
except Exception as e:
messagebox.showerror("Error", str(e))
# 读取xls文件指定工作表的数据
def read_xls(file_path, sheet_index):
workbook = xlrd.open_workbook(file_path, encoding_override='gbk') # 使用GBK编码读取文件
sheet = workbook.sheet_by_index(sheet_index)
data = []
for row_idx in range(sheet.nrows):
row = sheet.row_values(row_idx)
data.append(row)
return data
以下代码需要根据自己的Excel表格信息进行修改,来读取需要填入文本中的关键信息。
代码注释中###的内容是需要在文本中填充的内容,也是需要在Exccel中进行提取的
需要注意:
单纯Copy无效:相关信息已进行删改处理,
仅提供思路,供以参考,欢迎指正!!!
# 处理工作表的数据
def process_excel_file(file_path, output_path):
data_1 = read_xls(file_path, 0) # 第一个工作表,索引为0
data_2 = read_xls(file_path, 1) # 第二个工作表,索引为1
data_3 = read_xls(file_path, 2) # 第三个工作表,索引为2
data_4 = read_xls(file_path, 3) # 第四个工作表,索引为3
# 根据表头来选取需要填入文本中的信息
header_1 = data_1[0]
header_2 = data_2[0]
header_3 = data_3[0]
header_4 = data_4[1]
# 创建combined_data
combined_data = "\n".join(header) + "\n"
### 报文帧网络节点(表2)
Node = data_2[1][header_2.index('Node')]
if len(data_2[1]) >= len(header_2):
combined_data += f'BU_: {Node} Vector__XXX\n' \
f'\n'
### 报文帧部分
for row_data_4 in data_4[2:]:
# 报文帧部分转换(表4)
Name = row_data_4[header_4.index('Name')]
ZCU_PR = row_data_4[26]
if len(row_data_4) >= len(header_4):
if Name:
MsgID = hex_to_decimal(row_data_4[header_4.index('MsgID')])
if ZCU_PR == 'R':
ZCU_PR = data_4[1][27]
else:
ZCU_PR = data_4[1][26]
# 报文信息:报文ID、报文名称、报文长度(单位:字节/Byte)
combined_data += f'\n' \
f'BO_ {MsgID} {Name}: {convert_int(MsgLength)} {ZCU_PR}\n'
else:
StartBit_convert = lsb_to_msb(StartBit, BitLength)
if ZCU_PR == 'R':
ZCU_PR = data_4[1][26]
else:
ZCU_PR = data_4[1][27]
# 报文下的信号信息:信号短名、起始位、信号长度(单位:位/Bit)、信号字节顺序(0:Motorola|1:Inter)、
# 信号数值类型、信号精度、信号偏移量、信号物理最小值、信号物理最大值、信号单位、信号的接收节点
combined_data += f' SG_ {SignalName_Short} : {StartBit_convert}|{BitLength}@{get_byte_order(ByteOrder)}{get_data_type(DataType)} ' \
f'({Resolution},{Offset}) [{SignalMinValuePhy}|{SignalMaxValuePhy}] "{Unit}" ' \
f'{ZCU_PR}\n'
else:
continue
# 添加空行
combined_data += f'\n\n\n\n\n'
### 注解部分
for row_data_4 in data_4[2:]:
# 注解部分转换(表4)
Name = row_data_4[header_4.index('Name')]
MsgID = row_data_4[header_4.index('MsgID')]
if len(row_data_4) >= len(header_4):
if Name:
combined_data += f'\n'
MsgID4 = hex_to_decimal(MsgID)
else:
if SignalDescription == '':
combined_data += ''
else:
# 注解:CM_为关键字,表示注解;SG_为注解的对象类型,表示消息类型;报文ID;信号短名;信号描述
combined_data += f'\nCM_ SG_ {MsgID4} {SignalName_Short} "{SignalDescription}";'
else:
continue
### 属性部分1:属性定义BA_DEF_ 和 属性初始值BA_DEF_DEF_ 信息
combined_data += "\n"
combined_data += "\n".join(BA_DEF_info) + "\n"
### 属性部分2:通用属性的定义
for row_data_1 in data_1[1:]:
# 属性部分转换(表1)
ILTxTimeout = row_data_1[header_1.index('ILTxTimeout')]
if len(row_data_1) >= len(header_1):
combined_data += f'\nBA_ "ILTxTimeout" {ILTxTimeout};' \
f'\nBA_ "DBName" "{DBName}";'
else:
continue
# 添加空行
combined_data += f'\n'
### 属性部分3:AUTOSAR网络管理属性
for row_data_2 in data_2[1:]:
# 属性部分转换(表2)
Node_id = row_data_2[header_2.index('Node')]
NmNode = yes_no_to_binary(row_data_2[header_2.index('NmNode')])
if len(row_data_2) >= len(header_2):
combined_data += f'\nBA_ "NmNode" BU_ {Node_id} {NmNode};' \
f'\nBA_ "NmAsrCanMsgCycleOffset" BU_ {Node_id} {CycleOffset};' \
f'\nBA_ "NmStationAddress" BU_ {Node_id} {NmStationAddress};' \
f'\nBA_ "NmAsrNodeIdentifier" BU_ {Node_id} {NmAsrNodeIdentifier};' \
f'\nBA_ "NodeLayerModules" BU_ {Node_id} "{NodeLayerModules}";' \
f'\nBA_ "ILUsed" BU_ {Node_id} {ILUsed};' \
f'\nBA_ "NmAsrCanMsgReducedTime" BU_ {Node_id} {NmAsrCanMsgReducedTime};' \
f'\nBA_ "NmAsrNode" BU_ {Node_id} {NmAsrNode};' \
f'\n'
else:
continue
# 添加空行
combined_data += f'\n'
### 属性部分4:Com属性1
i = 1
for row_data_4 in data_4[2:]:
# 属性部分转换(表4)
Name = row_data_4[header_4.index('Name')]
GenSigSendType = getMsgSendtype(row_data_4[header_4.index('SignalSendType')])
if Name:
for row_data_3 in data_3[i:]:
# 属性部分转换(表3)
GenMsgSendType = getMsgSendtype(row_data_3[header_3.index('GenMsgSendType')])
GenMsgStartDelayTime = row_data_3[header_3.index('GenMsgStartDelayTime(ms)')]
MsgID_4 = hex_to_decimal(MsgID)
combined_data += f'BA_ "GenMsgSendType" BO_ {MsgID_4} {GenMsgSendType};\n' \
f'\n'
i += 1
break
else:
GenSigTimeoutValue = hex_to_decimal(row_data_4[header_4.index('GenSigTImeoutValue (Hex)')])
GenSigInactiveValue = hex_to_decimal(row_data_4[header_4.index('InactiveValue(Hex)')])
GenSigStartValue = hex_to_decimal(row_data_4[header_4.index('InitialValue(Hex)')])
combined_data += f'BA_ "GenSigSendType" SG_ {MsgID_4} {SignalName_Short} {GenSigSendType};\n' \
f'\n'
### 属性部分5:Com属性2
for row_data_4 in data_4[2:]:
GenSigTimeoutTime = convert_int(row_data_4[header_4.index('GenSigTimeoutTime (ms)')])
ZCU_PR = row_data_4[26]
if len(row_data_4) >= len(header_4): # 确保行中至少有需要的列数
if Name != '':
MsgID_4 = hex_to_decimal(row_data_4[header_4.index('MsgID')])
else:
if ZCU_PR == 'R':
ZCU_PR = data_4[1][26]
combined_data += f'BA_REL_ "GenSigTimeoutTime" BU_SG_REL_ {ZCU_PR} SG_ {MsgID_4} {SignalName} {GenSigTimeoutTime};\n'
else:
continue
### 数值表部分(值列表)
for row_data_4 in data_4[2:]:
# 数值表部分转换(表4)
SignalName = row_data_4[header_4.index('SignalName(Short Name)')]
# 去除 SignalValueDescription 中的换行符
SignalValueDescription = re.sub(r'0x([0-9A-Fa-f]+):\s*(.*?)(?=\s*0x[0-9A-Fa-f]+:|\s*$)', format_signal_description,
SignalValueDescription)
# 确保每个描述之间有一个空格
SignalValueDescription = ' '.join(re.split(r'\s+', SignalValueDescription.strip()))
if len(row_data_4) >= len(header_4):
if Name != '':
MsgID_4 = hex_to_decimal(row_data_4[header_4.index('MsgID')])
else:
if SignalValueDescription != '':
combined_data += f'VAL_ {MsgID_4} {SignalName} {SignalValueDescription} ;\n'
else:
continue
combined_data += f'\n'
write_txt(output_path, combined_data)
上述代码还有很多函数未陈列:比如16进制转10进制、lsb格式转msb格式、判断报文发送类型、byte_order的判断等,这里提供一个lsb转换公式。其余部分函数可关注私信交流!
# 定义LSB到MSB转换函数(Excel文件中的是LSB格式,CANdb++里边打开是MSB格式)
# 转换公式:temp = (start_bit + 7 - 2 * (start_bit % 8) - bit_length + 1)
# msb_start_bit = temp + 7 - 2 * (temp % 8)
总结
以上就是Excel 转换为 DBC 文件的实现,相关内容已做删改。作为入行新手,如有不足之处,还请指正!完整代码可通过关注私信获取,希望对大家有所帮助,感谢支持!
标签:转换,DBC,Excel,header,tk,file,save,data,row From: https://blog.csdn.net/bbu1227368213989/article/details/140985967