Excel 转换为 DBC 文件


文章摘要

文章探讨如何使用 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 文件的实现,相关内容已做删改。作为入行新手,如有不足之处,还请指正!完整代码可通过关注私信获取,希望对大家有所帮助,感谢支持!

  • 12
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要将Excel文件转换DBC文件,你需要使用一些第三方库来读取Excel文件并将其转换DBC格式。下面是一个使用Python的例子。 首先,你需要安装以下两个库: 1. xlrd:用于读取Excel文件。 2. cantools:用于转换DBC文件。 你可以使用以下命令来安装它们: ``` pip install xlrd cantools ``` 接下来,你可以使用以下示例代码将Excel文件转换DBC文件: ```python import xlrd import cantools # 读取Excel文件 workbook = xlrd.open_workbook('example.xlsx') worksheet = workbook.sheet_by_index(0) # 创建一个 CAN 数据库对象 db = cantools.database.Database() # 设置数据库名称和版本号 db.add_attribute('name', 'Example') db.add_attribute('version', '1.0') # 解析工作表中的每一行数据,添加到 CAN 数据库对象中 for row in range(worksheet.nrows): message_id = worksheet.cell_value(row, 0) message_name = worksheet.cell_value(row, 1) message_length = worksheet.cell_value(row, 2) message_signals = worksheet.cell_value(row, 3) message = cantools.database.Message( message_name, message_id, length=int(message_length), signals=message_signals.split(', ') ) db.add_message(message) # 将 CAN 数据库对象保存为 DBC 文件 with open('example.dbc', 'w') as fout: fout.write(db.as_dbc_string()) ``` 在上面的示例中,我们首先使用`xlrd`库读取Excel文件(这里假设文件名为`example.xlsx`),然后使用`cantools`库创建一个空的 CAN 数据库对象。接下来,我们解析Excel文件中的每一行数据,并将其添加到 CAN 数据库对象中。最后,我们使用`as_dbc_string()`方法将 CAN 数据库对象转换DBC 格式,并将其保存到一个文件中(这里假设文件名为`example.dbc`)。 请注意,本示例仅用于说明如何使用PythonExcel文件转换DBC文件。实际情况下,你需要根据Excel文件的结构和内容进行适当的修改。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值