pythonでcanログを変換(BLF形式、ASCII形式→エクセル)
初めに
前回に続いて、今回はCANログをエクセルファイルとして解析しやすい形に変換していきます。
サンプルのログファイルは以下から取得しました。
CANログのフォーマット
ASCII形式のCANログをベースに、CANログとして出力されるフレームごとの情報のフォーマット内容を確認しておきます。
ログフォーマットの詳細についてはCanalyzerインストール後、”C:\Program Files\Vector CANalyzer 16\Doc”に格納されているCAN_LOG_TRIGGER_ASC_Format.pdfを確認してください。
インストーラの取得は以下から。
クラシックCAN
以下はクラシックCANのログの一例です。
各要素の詳細については以下になります。
要素 | 説明 |
---|---|
Timestamp | CANメッセージが送信または受信された時点の時間情報 |
Channel | CANバスに接続された物理的な通信経路または回線 |
ID(Identifier) | CANメッセージの識別子 |
Dir(Direction) | CANメッセージの送信方向を示す Tx:送信 Rx:受信 |
DLC(Data Length Code) | CANメッセージのデータフィールドの長さを示すための値 |
Data | データフィールド |
Length | バスを占有するフレームの送受信時間(ナノ秒単位) *今回の解析対象外(詳細についてはCAN_LOG_TRIGGER_ASC_Format.pdf参照) |
BitCount | フレームが送受信されたbit数 *今回の解析対象外(詳細についてはCAN_LOG_TRIGGER_ASC_Format.pdf参照) |
ID(末尾) | 前述のIDの十進数値 |
拡張フレームの場合は以下のようになり、差分はIDの末尾にxが付くだけです。
CANFD
続いてCANFDのログの一例です。
要素 | 説明 |
---|---|
Timestamp | CANメッセージが送信または受信された時点の時間情報 |
Channel | CANバスに接続された物理的な通信経路または回線 |
Dir(Direction) | CANメッセージの送信方向を示す Tx:送信 Rx:受信 |
ID(Identifier) | CANメッセージの識別子 |
BRS | CANフレームのBRS(Bit Rate Switch)ビットを意味する BRSビットはデータフィールドのビットレートを切り替えるためのフラグ 0:データフィールドのビットレートは通常のCAN通信と同じ速度で転送される 1:データフィールドのビットレートが高速な速度で転送される *今回の解析対象外(詳細についてはCAN_LOG_TRIGGER_ASC_Format.pdf参照) |
ESI | CANフレームのESI(Error State Indicator)を意味する ESIはエラーステートを示すフラグ 0:エラーステートは通常の状態(異常なし) 1:エラーステートがアクティブ(異常あり) *今回の解析対象外(詳細についてはCAN_LOG_TRIGGER_ASC_Format.pdf参照) |
DLC(Data Length Code) | CANメッセージのデータフィールドの長さを示すための値 |
DataLength | メッセージの有効な長さ(byte単位) |
Data | データフィールド |
MessageDuration | バスを占有するフレームの送受信時間(ナノ秒単位) *今回の解析対象外(詳細についてはCAN_LOG_TRIGGER_ASC_Format.pdf参照) |
MessageLength | フレームが送受信されたbit数 *今回の解析対象外(詳細についてはCAN_LOG_TRIGGER_ASC_Format.pdf参照) |
Flags | 各種CANフレームにおけるフラグ情報 *今回の解析対象外(詳細についてはCAN_LOG_TRIGGER_ASC_Format.pdf参照) |
CRC | メッセージのチェックサム *今回の解析対象外(詳細についてはCAN_LOG_TRIGGER_ASC_Format.pdf参照) |
BitTimingConfArb | アービトレーション領域のビットタイミングパラメータ *今回の解析対象外(詳細についてはCAN_LOG_TRIGGER_ASC_Format.pdf参照) |
BitTimingConfData | データ領域のビットタイミングパラメータ *今回の解析対象外(詳細についてはCAN_LOG_TRIGGER_ASC_Format.pdf参照) |
BitTimingConfExtArb | アービトレーション領域のビットタイミング *今回の解析対象外(詳細についてはCAN_LOG_TRIGGER_ASC_Format.pdf参照) |
BitTimingConfExtData | データ領域のビットタイミング *今回の解析対象外(詳細についてはCAN_LOG_TRIGGER_ASC_Format.pdf参照) |
CAN FDもClassickCAN同様、拡張フレームの場合はIDの末尾にxが付きます。
実装
今回は前述のログ情報すべてではなく、ライブラリを利用して抽出可能な情報のみエクセルへ出力します。
canライブラリ(can.io.ASCReader、can.io.BLFReader)
前述のCANログに含まれる各種情報を、フレーム単位で取り出せるようにしてくれる機能をcanライブラリは提供してくれています。
今回使用するのはcan.io.ASCReaderまたはcan.io.BLFReaderのいずれかとなり、ログの形式に応じて使いわけます。
本クラスを使用することで、任意のログファイルからメッセージごとに以下のような情報を抽出することが可能です。
要素 | 説明 |
---|---|
timestamp | CANメッセージが送信または受信された時点の時間情報 |
arbitration_id | CANメッセージの識別子 |
channel | CANバスに接続された物理的な通信経路または回線 |
dlc | CANメッセージのデータフィールドの長さを示すための値 |
data | データフィールド |
error_state_indicater | False:エラーステートは通常の状態(異常なし) True:エラーステートがアクティブ(異常あり) |
is_extended_id | False:標準ID True:拡張ID |
is_fd | False:Classick CAN True:CAN FD |
is_error_frame | False:正常フレーム True:異常フレーム |
is_remote_frame | False:リモートフレーム以外 True:リモートフレーム(データなし) |
is_rx | False:Tx True:Rx |
詳細については以下公式から確認してください。
src
FILE_PATHのファイルをblfファイルにすれば、blfファイルからのエクセル生成も可能です。
import pandas as pd
import datetime
import can
FILE_PATH = "sample\\asc\\logfile_errorframes.asc"
TEMP_DIC = {"TIMESTAMP":"-","FRAME":"-","ID":"-","CHANNEL":"-","DIR":"-","DLC":"-",
"D00":"-","D01":"-","D02":"-","D03":"-","D04":"-","D05":"-","D06":"-","D07":"-","D08":"-","D09":"-",
"D10":"-","D11":"-","D12":"-","D13":"-","D14":"-","D15":"-","D16":"-","D17":"-","D18":"-","D19":"-",
"D20":"-","D21":"-","D22":"-","D23":"-","D24":"-","D25":"-","D26":"-","D27":"-","D28":"-","D29":"-",
"D30":"-","D31":"-","D32":"-","D33":"-","D34":"-","D35":"-","D36":"-","D37":"-","D38":"-","D39":"-",
"D40":"-","D41":"-","D42":"-","D43":"-","D44":"-","D45":"-","D46":"-","D47":"-","D48":"-","D49":"-",
"D50":"-","D51":"-","D52":"-","D53":"-","D54":"-","D55":"-","D56":"-","D57":"-","D58":"-","D59":"-",
"D60":"-","D61":"-","D62":"-","D63":"-"}
def main():
# ASCII形式
if ".asc" in FILE_PATH:
with open(FILE_PATH, 'r') as f_in:
asciidata = can.io.ASCReader(f_in)
read_log(asciidata)
# BLF形式
if ".blf" in FILE_PATH:
with open(FILE_PATH, 'rb') as f_in:
blfdata = can.io.BLFReader(f_in)
read_log(blfdata)
def read_log(log_info):
# 初期化
excel = make_excel(TEMP_DIC)
for msg in log_info:
input_dic = TEMP_DIC
# timestamp
input_dic["TIMESTAMP"] = msg.timestamp
if True == msg.is_error_frame:
input_dic["FRAME"] = "Error Frame"
else:
# フレーム種別
if False == msg.is_fd and False == msg.is_extended_id:
input_dic["FRAME"] = "CAN標準フォーマット"
input_dic["ID"] = '{:04x}'.format(msg.arbitration_id).upper()
if True == msg.is_fd and False == msg.is_extended_id:
input_dic["FRAME"] = "CAN FD標準フォーマット"
input_dic["ID"] = '{:04x}'.format(msg.arbitration_id).upper()
if False == msg.is_fd and True == msg.is_extended_id:
input_dic["FRAME"] = "CAN拡張フォーマット"
input_dic["ID"] = '{:08x}'.format(msg.arbitration_id).upper()
if True == msg.is_fd and True == msg.is_extended_id:
input_dic["FRAME"] = "CAN FD拡張フォーマット"
input_dic["ID"] = '{:08x}'.format(msg.arbitration_id).upper()
# channel
input_dic["CHANNEL"] = msg.channel
# dir
if True == msg.is_rx:
input_dic["DIR"] = "Rx"
else:
input_dic["DIR"] = "Tx"
# dlc
input_dic["DLC"] = msg.dlc
# data
for i, data in enumerate(msg.data):
input_dic["D"+str('{:02d}'.format(i))] = '{:02x}'.format(data).upper()
print(msg.equals)
excel.add_inf(input_dic)
# エクセルの保存
now = datetime.datetime.now()
file_name = 'canlog_convert_{}.xlsx'.format(now.strftime('%Y%m%d_%H%M%S'))
excel.save_file(file_name,"sample1")
return
class make_excel:
# エクセルヘッダ記入
def __init__( self , init_dic) :
key_list = init_dic.keys()
self.df = pd.DataFrame(columns=key_list)
# 行追加
def add_inf ( self , add_dict ) :
self.df = self.df.append( add_dict , ignore_index=True)
return
# ファイル保存
def save_file( self , file_name , title ):
self.df.to_excel(file_name, sheet_name=title)
return
if __name__ == "__main__":
main()
実行結果
実行結果としてはこんな感じで出力されます。
変換前のASCIIファイルは以下。
date Sam Sep 30 15:06:13.191 2017
base hex timestamps absolute
internal events logged
// version 9.0.0
Begin Triggerblock Sam Sep 30 15:06:13.191 2017
0.000000 Start of measurement
0.015991 CAN 1 Status:chip status error passive - TxErr: 132 RxErr: 0
0.015991 CAN 2 Status:chip status error active
2.501000 1 ErrorFrame
2.501010 1 ErrorFrame ECC: 10100010
2.501020 2 ErrorFrame Flags = 0xe CodeExt = 0x20a2 Code = 0x82 ID = 0 DLC = 0 Position = 5 Length = 11300
2.520002 3 200 Tx r Length = 1704000 BitCount = 145 ID = 88888888x
2.584921 4 300 Tx r 8 Length = 1704000 BitCount = 145 ID = 88888888x
3.098426 1 18EBFF00x Rx d 8 01 A0 0F A6 60 3B D1 40 Length = 273910 BitCount = 141 ID = 418119424x
3.197693 1 18EBFF00x Rx d 8 03 E1 00 4B FF FF 3C 0F Length = 283910 BitCount = 146 ID = 418119424x
17.876976 1 6F8 Rx d 8 FF 00 0C FE 00 00 00 00 Length = 239910 BitCount = 124 ID = 1784
20.105214 2 18EBFF00x Rx d 8 01 A0 0F A6 60 3B D1 40 Length = 273925 BitCount = 141 ID = 418119424x
20.155119 2 18EBFF00x Rx d 8 02 1F DE 80 25 DF C0 2B Length = 272152 BitCount = 140 ID = 418119424x
20.204671 2 18EBFF00x Rx d 8 03 E1 00 4B FF FF 3C 0F Length = 283910 BitCount = 146 ID = 418119424x
20.248887 2 18EBFF00x Rx d 8 04 00 4B FF FF FF FF FF Length = 283925 BitCount = 146 ID = 418119424x
End TriggerBlock