[汽车电子/网络/CAN/Python] CAN 报文 的 ASC LOG 与 BLF 文件转换工具

需求描述

  • CAN 报文 ASC 文件 与 BLF 的相互转换。

代码实现

  • python 3.10

前置依赖

requirements.txt (第三方开源库)

can
python-can
cantools

注:getopt / os / sys / time 均是python内置模块

DatetimeUtils.py

CanLogFormatConverter.py

#!/usr/bin/python3
 
import os
import sys
import time
import getopt
import can
import cantools

#from can import ASCReader
#from can import ASCWriter
#from can import BLFReader # 使用方式: ascLog = can.BLFReader(blfFile)

import DatetimeUtils

"""
CAN 报文日志 的 ASC LOG 与 BLF 文件格式转换器
@updateTime 2025-07-16 20:21
@installation
    pip install requirements.txt
@usage
    python CanLogFormatConverter.py -i "inputfile.blf" -o "outputfile.asc"
@help 
    python CanLogFormatConverter.py -h
@reference-doc
  [1] Python将CAN数据.asc文件转化为.blf文件 - CSDN - https://blog.csdn.net/PlutoZuo/article/details/137821984
  [2] CAN数据格式-BLF - Zhihu - https://zhuanlan.zhihu.com/p/37318040
  [3] cantools - github - https://github.com/aheit/cantools
  [4] Caone转换asc格式到blf格式 - CSDN - https://blog.csdn.net/weixin_47649405/article/details/129276760
  [5] Blf2asc Demo - github - https://github.com/informagico/blf2asc
"""
 
def asc2blf(sourceAscLogFile, sinkBlfFile):
    with open(sourceAscLogFile, 'r') as inF:
        inFile = can.io.ASCReader(inF)
        with open(sinkBlfFile, 'wb') as outF:
            outFile = can.io.BLFWriter(outF)
            for msg in inFile:
                outFile.on_message_received(msg)
            outFile.stop()
    print('well done! ')
    # input() # 等待用户输入

def asc2blf(sourceAscLogFile, sinkBlfFile):
    with open(sourceAscLogFile, 'r') as inF:
        ascReader = can.ASCReader(inF) # can.io.ASCReader(inF)
        stopTimestamp = 0;
        with open(sinkBlfFile, 'wb') as outF:
            blfWriter = can.io.BLFWriter(outF)
            #blfWrite = can.BLFWriter(outF)
            for msg in ascReader:
                blfWriter.on_message_received(msg)
                stopTimestamp = msg.timestamp * 1000; # 1. 不断更新停止时间; 2. ASC 文件中 CAN 帧的时间戳单位是【秒】,故须乘以 1000,达到毫秒级

            # 获取起始时间戳(毫秒级) : 1. ascReader.date 的样例值: 'May 8 08:21:23.576 pm 2025'; 2. 注意,只有等待开始 for 循环读取 Message 时或之后, ASCReader.date 才会被解析到值 (坑点)
            startTimestamp = DatetimeUtils.datetimeStrToTimestamp(ascReader.date)

            stopTimestamp = startTimestamp + stopTimestamp;
            blfWriter.start_timestamp = startTimestamp/1000; # 不主动设置时,默认为 0 (1970-01-01) eg: 1746706883.576
            blfWriter.stop_timestamp = stopTimestamp/1000; # 不主动设置时,默认为 0 (1970-01-01) eg: 1746706883.585
            #blfWriter.object_count = None; # 此字段无需设置, blfWriter 会在真正写入文件中自动计算
            blfWriter.stop()
    print(f"Well done! sourceAscLogFile:{sourceAscLogFile}, sinkBlfFile:{sinkBlfFile}")

def blf2asc(sourceBlfFile, sinkAscLogFile):
    with open(sourceBlfFile, 'r') as inF:
        inFile = can.io.BLFReader(inF)
        with open(sinkAscLogFile, 'wb') as outF:
            outFile = can.io.ASCWriter(outF)
            for msg in inFile:
                outFile.on_message_received(msg)
            outFile.stop()
    print(f"Well done! sourceBlfFile:{sourceBlfFile}, sinkAscLogFile:{sinkAscLogFile}")
    # input() # 等待用户输入



"""
BLF 转 ASC LOG 的实现方式2
 
def blf2asc(sourceBlfFile, sinkAscLogFile):
    # convert the file
    reader = can.BLFReader(sourceBlfFile)
    ascLog = list(reader)
 
    outFile = open(sinkAscLogFile, "w")
 
    for msg in ascLog:
        msg = str(msg)
        msg = msg[0:30] + msg[30:100].upper() + msg[100:]
 
        outFile.write(msg + "\n")
 
    outFile.close()
"""
 
def main(argv):
    # [1] get command line arguments
    sourceFile = "" #'can-1.asc'
    outFile = "" # 'can-1.blf'
    try:
        opts, args = getopt.getopt(argv, "hi:o:", ["inFile=", "outFile="])
    except getopt.GetoptError:
        print("CanLogFormatConverter.py -i <sourceFile> -o <outFile>")
        sys.exit(2)
    for opt, arg in opts:
        if opt == "-h":
            print("CanLogFormatConverter.py -i <sourceFile> -o <outFile>")
            sys.exit()
        elif opt in ("-i", "--inFile"):
            sourceFile = arg
        elif opt in ("-o", "--outFile"):
            outFile = arg
 
    print()
    if sourceFile.lower().endswith(".asc"): # asc => blf
       print(f'Asc Log File(Source):{sourceFile}, Blf File(Sink):{outFile}')
       asc2blf(sourceFile, outFile)
    else: # blf => asc
       print(f'Blf File(Source):{sourceFile}, Asc Log File(Sink):{outFile}')
       blf2asc(sourceFile, outFile)
 

# -i "dataset/can-1.asc" -o "dataset/can-1.blf"
if __name__ == "__main__":
    main(sys.argv[1:])
  • 效果图

用 CANoe 打开 blf 或 asc log 文件 (时间单位: 秒,例如: 1.000000)

运行效果

CASE : ASC 转 BLF

ASC 源文件

  • 要点
  1. 12小时值

错误示范: Thu May 8 20:21:23.576 pm 2025
正确示范: Thu May 8 08:21:23.576 pm 2025

date Thu May 8 08:21:23.576 pm 2025
base hex timestamps absolute
no internal events logged
// version 18.3.0
  0.000000 1 221        Rx d 8 01 02 03 04 05 06 07 08
  0.000000 1 582        Rx d 8 01 02 03 04 05 06 07 08
  1.008000 1 221        Rx d 8 01 02 03 04 05 06 07 08
  1.008000 1 582        Rx d 8 01 02 03 04 05 06 07 08
  2.015000 1 221        Rx d 8 01 02 03 04 05 06 07 08
  2.015000 1 582        Rx d 8 01 02 03 04 05 06 07 08
  3.022000 1 221        Rx d 8 01 02 03 04 05 06 07 08
  3.022000 1 582        Rx d 8 01 02 03 04 05 06 07 08
  4.028000 1 221        Rx d 8 01 02 03 04 05 06 07 08
  4.028000 1 582        Rx d 8 01 02 03 04 05 06 07 08
  5.036000 1 221        Rx d 8 01 02 03 04 05 06 07 08
  5.036000 1 582        Rx d 8 01 02 03 04 05 06 07 08
  6.038000 1 221        Rx d 8 01 02 03 04 05 06 07 08
  6.038000 1 582        Rx d 8 01 02 03 04 05 06 07 08
  7.045000 1 221        Rx d 8 01 02 03 04 05 06 07 08
  7.045000 1 582        Rx d 8 01 02 03 04 05 06 07 08
  8.045000 1 221        Rx d 8 01 02 03 04 05 06 07 08
  8.045000 1 582        Rx d 8 01 02 03 04 05 06 07 08
  9.049000 1 221        Rx d 8 01 02 03 04 05 06 07 08
  9.049000 1 582        Rx d 8 01 02 03 04 05 06 07 08

ReadBlfFileDemo.py : 读取转换后的 BLF 文件

#!/usr/bin/python3

# -*- coding: utf-8 -*-
#####
"""
# author:vehicle_ma, 2023/2/10
"""
######

import can
import cantools
import time
import datetime
import pytz

baseDir = "D:\\Workspace\\CodeRepositories\\xxx\\dataset\\"
sourceBlfFile = baseDir + "can-1.blf";

filename = sourceBlfFile
blfReader = can.BLFReader(filename)      #blf对象

print(f"file:{blfReader.file}, fileSize:{blfReader.file_size}, uncompressedSize:{blfReader.uncompressed_size}")
print(f"objectCount:{blfReader.object_count}")

timeZone = 'Asia/Shanghai';
startTimestamp=blfReader.start_timestamp*1000 # eg: 1746706883576
startTime=datetime.datetime.fromtimestamp(startTimestamp/1000, pytz.timezone(timeZone)) # 毫秒级时间戳 转 时间字符串(指定时区:UTC+8)
endTimestamp=blfReader.stop_timestamp*1000 # eg: 1746706892625
endTime=datetime.datetime.fromtimestamp(endTimestamp/1000, pytz.timezone(timeZone))
print(f"startTime:{startTime}|{startTimestamp}ms, stopTime:{endTime}|{endTimestamp}ms")

messageIdSet=set() # 存放去重后的所有的 MessageId
messageCount=0;
for msg in blfReader:
    messageId = msg.arbitration_id             # message id
    messageTimestamp = msg.timestamp           # blf包中保存的报文时间戳
    messageData = msg.data                     # blf包中保存的数据,直接打印是加密的数字字符码
    messageDlc = msg.dlc                       # blf包中保存的报文长度,如8,64
    #dict_data = db.decode_message(msg.arbitration_id, msg.data)    #对数据进行解码

    if(messageCount >= 10): # 展示前 N 个 Message
        break;
    else:
        print(f"message:{msg}")
        # print(f"messageId:{messageId}, messageTimestamp:{messageTimestamp},dlc:{messageDlc}, originData:{messageData}")
        #print('message data     ', dict_data)

    messageIdSet.add(messageId)
    messageCount += 1;

print(f"messageCount:{messageCount}, messageIdSet.length:{ len(messageIdSet)}");

对读取/验证转换后的BLF文件:

file:<_io.BufferedReader name='D:\\Workspace\\CodeRepositories\\xxxx\\can-1.blf'>, fileSize:326, uncompressedSize:1136
objectCount:20
startTime:2025-05-08 20:21:23.576000+08:00|1746706883576.0ms, stopTime:2025-05-08 20:21:32.625000+08:00|1746706892625.0ms
message:Timestamp: 1746706883.576000    ID:      221    S Rx                DL:  8    01 02 03 04 05 06 07 08     Channel: 0
message:Timestamp: 1746706883.576000    ID:      582    S Rx                DL:  8    01 02 03 04 05 06 07 08     Channel: 0
message:Timestamp: 1746706884.584000    ID:      221    S Rx                DL:  8    01 02 03 04 05 06 07 08     Channel: 0
message:Timestamp: 1746706884.584000    ID:      582    S Rx                DL:  8    01 02 03 04 05 06 07 08     Channel: 0
message:Timestamp: 1746706885.591000    ID:      221    S Rx                DL:  8    01 02 03 04 05 06 07 08     Channel: 0
message:Timestamp: 1746706885.591000    ID:      582    S Rx                DL:  8    01 02 03 04 05 06 07 08     Channel: 0
message:Timestamp: 1746706886.598000    ID:      221    S Rx                DL:  8    01 02 03 04 05 06 07 08     Channel: 0
message:Timestamp: 1746706886.598000    ID:      582    S Rx                DL:  8    01 02 03 04 05 06 07 08     Channel: 0
message:Timestamp: 1746706887.604000    ID:      221    S Rx                DL:  8    01 02 03 04 05 06 07 08     Channel: 0
message:Timestamp: 1746706887.604000    ID:      582    S Rx                DL:  8    01 02 03 04 05 06 07 08     Channel: 0
messageCount:10, messageIdSet.length:2

X 参考文献

blf2asc/blf2asc.py

打开caone,选择tools工具栏,选择logging file conversation

pip install python-can

import can
log = can.BLFReader(blf)

Python处理CAN总线的库主要有python-can和cantools。这里我的CAN总线数据保存为asc格式,database保存为dbc格式。

pip install cantools python-can
dbc = cantools.db.load_file(dbc_file)
log_data = can.BLFReader(f)
dec = dbc.decode_message(msg.arbitration_id, msg.data)

posted @ 2025-05-30 11:11  千千寰宇  阅读(737)  评论(0)    收藏  举报