• python脚本制作、生成、创建一个CANdbc文件


    最近在工作中,有同事拿了一个excel的dbc表格,在用官方的dbc工具一个一个创建信号,大概看了一下共累计20多个节点,300多个信号,居然在手动处理,顿感无语。。

    于是在网络上搜相关的dbc 通过脚本生成方式,竟然没搜到!那只能全网首发一个给广大汽车软件同行谋个福利。

    (经过国内一番搜索,一无所获。于是乎转谷歌搜索,在cantools 这个库官网文件下找到这么一个指令:)

    1
    2
    3
    import cantools
    db = cantools.database.load_file('xxxx.dbc')
    cantools.database.dump_file(db, 'bar.dbc')

    上文代码实现了一个读取加转存,读取了一个dbc并转而生成了一个dbc,于是自行分析读取的candbc数据结构,再结合chatgpt给出的示例,进行重构数据。最终简单的示例代码如下:

    # -*- coding: utf-8 -*-
    """
    Created on Wed Dec 27 18:09:18 2023
    
    @author: xm
    
    如何创建生成canfd还待挖掘
    
    """
    
    import cantools
    
    # 定义一个消息
    message1 = cantools.db.Message( #cantools.database.can.message.Message
        frame_id=0x19FB5101, #435900673
        name='PFC_VacReport',
        length=8,
        bus_name='PFC',
        header_byte_order='big_endian',
        is_extended_frame=True,
        # is_multiplexed=True,
        # refresh=True,
        is_fd=False,
        send_type='cyclic',
        senders=['PFC'],
        cycle_time=20,
        comment='input voltage',
        unused_bit_pattern=255,
        # unpack_container=True,
        signals=[
            cantools.db.Signal(
                name='P_VacRpt_VacRms_A',
                start=0,
                length=12,
                is_signed=False,
                scale=0.1,
                offset=0,
                minimum=0,
                maximum=409.5,
                unit='V',
                invalid=None,
                initial=0,
                # is_multiplexer=False,
                # is_float=False,
                receivers=['IFB','LC'],
                # multiplexer_signal=None,
                # multiplexer_ids=None,
                byte_order='little_endian', #little_endian big_endian
                comment='valid voltage phase A',
            ),
            cantools.db.Signal(
                name='P_VacRpt_VacRms_B',
                start=12,
                length=12,
                is_signed=False,
                scale=0.1,
                offset=0,
                minimum=0,
                maximum=409.5,
                unit='V',
                invalid=None,
                initial=0,
                # is_multiplexer=False,
                # is_float=False,
                receivers=['IFB','LC'],
                # multiplexer_signal=None,
                # multiplexer_ids=None,
                byte_order='little_endian',
                comment='valid voltage phase b',
            ),
            cantools.db.Signal(
                name='P_VacRpt_VacRms_C',
                start=24,
                length=12,
                is_signed=False,
                scale=0.1,
                offset=0,
                minimum=0,
                maximum=409.5,
                unit='V',
                invalid=None,
                initial=0,
                # is_multiplexer=False,
                # is_float=False,
                receivers=['IFB','LC'],
                # multiplexer_signal=None,
                # multiplexer_ids=None,
                byte_order='little_endian',
                comment='valid voltage phase c',
            ),
        ]
    )
    
    
    
    node1 = cantools.db.Node(
        name='PFC'
    )
    
    
    
    # 定义一个数据库
    database = cantools.db.Database(
        messages=[message1],
        nodes=[node1,],
        version='1.0'
    )
    
    # 保存为DBC文件
    cantools.database.dump_file(database, 'example.dbc')
    View Code

    接下来就是解析对应的excel,创建不同的msg和signal再进行组合了。这里要注意,如果excel内涉及合并单元格,以及公式计算,需要用下文代码中的两个 In【0】部分来处理掉

    # -*- coding: utf-8 -*-
    """
    Created on Wed Dec 27 20:35:47 2023
    
    @author: xm Azhe
    
    """
    
    import pandas as pd
    import cantools
    import openpyxl
    
    file_path = r'D:\代码\dbc文件处理\xx.xlsx'
    
    # In[0] 文件改造,合并单元格填充
    # 打开Excel文件
    workbook = openpyxl.load_workbook(file_path)
    
    # 遍历所有工作表
    for worksheet in workbook.worksheets:
        # 获取所有合并单元格
        merged_cells = worksheet.merged_cells
        # if worksheet.title=='IFB协议E2E ':
        #     print(merged_cells)
        # 遍历合并单元格
        for merged_cell in list(merged_cells):
    
            # 获取合并单元格的起始行、列和结束行、列
            start_row, start_col, end_row, end_col = merged_cell.min_row,merged_cell.min_col,merged_cell.max_row,merged_cell.max_col,
            # 拆分
            worksheet.unmerge_cells(start_row=start_row,start_column=start_col,end_row=end_row,end_column=end_col)
             # 获取合并单元格的值
            cell_value = worksheet.cell(start_row, start_col).value
            # if worksheet.title=='IFB协议E2E ':
                # print(merged_cell)
                # print('\n')
            # 将合并单元格内的所有单元格都替换为该值
            for row in range(start_row, end_row + 1):
                for col in range(start_col, end_col + 1):
                    worksheet.cell(row, col).value = cell_value
    file_path1=file_path.replace('.xlsx','_dealed.xlsx')
    # 保存Excel文件
    workbook.save(file_path1)
    
    # In[0] excel内包含公式,需要刷新公式,否则会读取为nan
    import win32com.client
    # Start an instance of Excel
    xlapp = win32com.client.DispatchEx("Excel.Application")
    # Open the workbook in said instance of Excel
    wb = xlapp.workbooks.open(file_path1)
    # Optional, e.g. if you want to debug
    # xlapp.Visible = True
    # Refresh all data connections.
    wb.RefreshAll()
    wb.Save()
    # Quit
    xlapp.Quit()
    
    
    # In[1] df读取,删除信号名称为空,忽略第一行
    def str_to_num(s):
        try:
            return int(s)
        except ValueError:
            try:
                return float(s)
            except ValueError:
                return s
            
    df={}
    for s in pd.ExcelFile(file_path1).sheet_names:
        # print(s)
        if ('协议' in s) & (s!='协议说明'):
            df[s] = pd.read_excel(file_path1, sheet_name=s,skiprows=1, engine='openpyxl')
            df[s] = df[s].dropna(subset=['数据名称'])
            
    
    
    node_list=[]
    msg_list=[]
    # cnt=1
    for s in list(df.keys()):
        node_list.append(cantools.db.Node(name=s.replace(' ','').replace('协议E2E','')))
        msg_list_tmp=list(df[s]['ID'].drop_duplicates())
        for i in msg_list_tmp:
            # if s=='LVDC协议E2E':
            #     cnt1=1
            #     print('y')
            signals_list_tmp=[]
            df_sig_list_tmp=list(df[s]['数据名称'][df[s]['ID']==i])
            for j in df_sig_list_tmp:
                signals_list_tmp.append(
                    cantools.db.Signal(
                        name=j,
                        start=str_to_num(df[s]['起始位'][df[s]['数据名称']==j]),
                        length=str_to_num(df[s]['长度'][df[s]['数据名称']==j]),
                        is_signed=False,
                        scale=df[s]['精度'][df[s]['数据名称']==j].values[0],
                        offset=str_to_num(df[s]['OFFSET'][df[s]['数据名称']==j]),
                        minimum=df[s]['Min'][df[s]['数据名称']==j].values[0],
                        maximum=df[s]['Max'][df[s]['数据名称']==j].values[0],
                        unit=str(df[s]['单位'][df[s]['数据名称']==j].values[0]),
                        invalid=None, #未采用dbc
                        initial=str_to_num(df[s]['Initial'][df[s]['数据名称']==j]),
                        # is_multiplexer=False,
                        # is_float=False,
                        receivers=df[s]['接收'][df[s]['数据名称']==j].values[0].split('&'),
                        # multiplexer_signal=None,
                        # multiplexer_ids=None,
                        byte_order='little_endian', #little_endian big_endian
                        comment=str(df[s]['说明'][df[s]['数据名称']==j].values[0]),
                        # comments=None
                    ))
    
            msg_list.append(cantools.db.Message( #cantools.database.can.message.Message
                frame_id=int(i[:i.index("\n")], 16),
                name=i[i.index("(")+1:i.index(")")],
                length=8,
                bus_name=df[s]['发送'][df[s]['ID']==i].values[0],
                header_byte_order='big_endian',
                is_extended_frame=True,
                is_fd=False,
                send_type='cyclic',
                senders=[df[s]['发送'][df[s]['ID']==i].values[0]],
                cycle_time=str_to_num(df[s]['周期'][df[s]['ID']==i].values[0][:df[s]['周期'][df[s]['ID']==i].values[0].index("ms")]),
                comment=str(df[s]['备注'][df[s]['ID']==i].values[0]),
                unused_bit_pattern=255,
                # unpack_container=True,
                signals=signals_list_tmp
                ))
            # if s=='LVDC协议E2E':
            #     cnt1=cnt1+1
            #     if cnt1==3:
            #         break
        # cnt=cnt+1
        # if cnt==5:
        #     break
    
    database = cantools.db.Database(
        # messages=[message1],
        messages=msg_list,
        nodes=node_list,
        version='1.0'
    )
        
    cantools.database.dump_file(database, file_path.split('\\')[-1].replace('.xlsx','.dbc').replace('.xls','.dbc').replace('.csv','.dbc'))    
    View Code

    应该是全网首发了哈,有制作dbc的小伙伴们有福了,记得点赞,使用中有问题可以评论联系,会看的。

     

  • 相关阅读:
    JDBC:PreparedStatement 插入BLOB类型的数据,PreparedStatement 批量处理,Connection 事务处理
    【Linux系列】深入理解 CURL 命令及其在网络请求中的应用
    vue2.7 火影版本,难道只是vue3的过度版?
    顺序表专题
    使用HTML CSS制作静态网站【中秋节】
    Spring MVC源码详解
    基于安卓聊天APP的设计与实现
    分布式事务之Seata介绍
    双一流高校全球通邮经验:Coremail安全海外中继提升科研效率
    【AJAX是什么】&【AJAX的基本使用】
  • 原文地址:https://www.cnblogs.com/techs-wenzhe/p/17943020