dbc에서 m_id들을 찾기
- 목적: 차량 형상 관리 관점에서 설계 사양 (dbc)과 실차의 형상(m_id_info)을 비교한다.
- dbc 파일에서 m_id_info의 m_id들을 찾는다.
- 찾아야할 m_id들은 ~mid_info.xlsx에 저장되어 있다.
- cantools라는 Python 모듈을 이용하여 dbc의 내용을 읽는다.
- 찾기와 비교를 쉽게 하기 위해서 pandas라는 Python 모듈을 이용한다.
- xlsx의 내용을 df_car라는 dataframe(df)으로 만든다.
- dbc의 내용을 df_dbc라는 df로 만든다.
- 두 df를 합친다.
- 양쪽 df 모두에 포함되어 있고, dlc(data length code)가 동일하면 ok
- 양쪽 df 모두에 포함되어 있는데, dlc가 다르면 nok
- df_dbc에는 있는데 df_car에 없으면 missing
- df_car에 있는데 df_dbc에 없으면 unidentified
- 로 분류한다.
from pathlib import Path
import cantools
import pandas as pd
ldf is not supported
xls is not supported
xlsx is not supported
찾아야 할 m_id
xlsx_m_info = Path(r'C:/data/tosun/projects/uds_vehicle_config/Logging/Bus/uds_vehicle_config_2025_01_19_10_34_14_m_info.xlsx')
df_car = pd.read_excel(xlsx_m_info, sheet_name='Sheet1')
df_car[['m_id', 'm_id_int', 'dlc', 'd_ts']].sample(3)
|
m_id |
m_id_int |
dlc |
d_ts |
29 |
394 |
916 |
8 |
0.02 |
44 |
52A |
1322 |
8 |
0.20 |
36 |
4E6 |
1254 |
8 |
0.10 |
m_id를 찾을 dbc
# dbc 파일
dbc = Path(r'C:\data\tosun\reference\dbc\20240809_venue\venue_esc.dbc')
db = cantools.database.load_file(dbc, strict=False)
db.messages
[message('calc', 0x222, False, 8, {None: '[P] Periodic'}),
message('ESP12', 0x220, False, 8, {None: '[P] Periodic'}),
message('SAS11', 0x2b0, False, 5, {None: '[P] Periodic'}),
message('WHL_SPD11', 0x386, False, 8, {None: '[P] Periodic'})]
df_dbc를 만든다.
messages = [(message.name, hex(message.frame_id), message.frame_id, message.length) for message in db.messages]
df_dbc = pd.DataFrame(messages, columns=['name', 'm_id', 'm_id_int', 'dlc'])
df_dbc
|
name |
m_id |
m_id_int |
dlc |
0 |
calc |
0x222 |
546 |
8 |
1 |
ESP12 |
0x220 |
544 |
8 |
2 |
SAS11 |
0x2b0 |
688 |
5 |
3 |
WHL_SPD11 |
0x386 |
902 |
8 |
df_dbc와 df_m_id_info를 합친다.
df = pd.merge(df_dbc, df_car[['m_id_int', 'dlc', 'd_ts']], on='m_id_int', how='outer', suffixes=['_dbc', '_car'], indicator=True)
df['m_id'] = df['m_id_int'].apply(lambda x: f'0x{x:03X}')
df['dlc_check'] = df['dlc_dbc'] == df['dlc_car']
df.sample(3)
|
name |
m_id |
m_id_int |
dlc_dbc |
dlc_car |
d_ts |
_merge |
dlc_check |
58 |
NaN |
0x5CF |
1487 |
NaN |
8.0 |
0.10 |
right_only |
False |
20 |
NaN |
0x329 |
809 |
NaN |
8.0 |
0.01 |
right_only |
False |
10 |
NaN |
0x164 |
356 |
NaN |
4.0 |
0.01 |
right_only |
False |
# columns of interest
coi = ['m_id', 'name', 'dlc_dbc', 'dlc_car', 'dlc_check']
# df_dbc와 df_car에 모두 있고 dlc가 같은 것
df_ok = df.loc[(df['_merge'] == 'both') & (df['dlc_check'] == True), coi]
# 메시지를 송신하는 제어기를 찾는다.
df_ok['ecu_tx'] = df_ok['m_id'].apply(lambda x: db.get_message_by_frame_id(int(x, 16)).senders)
# 메시지를 수신하는 제어기를 찾는다.
df_ok['ecu_rx'] = df_ok['m_id'].apply(lambda x: db.get_message_by_frame_id(int(x, 16)).receivers)
# df_dbc와 df_car에 모두 있는데 dlc가 다른 것
df_nok = df.loc[(df['_merge'] == 'both') & (df['dlc_check'] == False), coi]
df_nok['ecu_tx'] = df_nok['m_id'].apply(lambda x: db.get_message_by_frame_id(int(x, 16)).senders)
df_nok['ecu_rx'] = df_nok['m_id'].apply(lambda x: db.get_message_by_frame_id(int(x, 16)).receivers)
# df_dbc에 있는데 df_car에 없는 것
df_missing = df.loc[df['_merge'] == 'left_only', coi]
df_missing['ecu_tx'] = df_missing['m_id'].apply(lambda x: db.get_message_by_frame_id(int(x, 16)).senders)
df_missing['ecu_rx'] = df_missing['m_id'].apply(lambda x: db.get_message_by_frame_id(int(x, 16)).receivers)
# df_car에 있는데 df_dbc에 없는 것
df_unidentified = df.loc[df['_merge'] == 'right_only', coi]
print(f'{len(df_ok)} messages are found in both dbc and m_info with the same dlc')
df_ok
2 messages are found in both dbc and m_info with the same dlc
|
m_id |
name |
dlc_dbc |
dlc_car |
dlc_check |
ecu_tx |
ecu_rx |
13 |
0x220 |
ESP12 |
8.0 |
8.0 |
True |
[ESC] |
{GST, EMS, SAS} |
27 |
0x386 |
WHL_SPD11 |
8.0 |
8.0 |
True |
[ESC] |
{GST, EMS, SAS} |
print(f'{len(df_nok)} messages are found in both dbc and m_info with different dlc')
df_nok
1 messages are found in both dbc and m_info with different dlc
|
m_id |
name |
dlc_dbc |
dlc_car |
dlc_check |
ecu_tx |
ecu_rx |
18 |
0x2B0 |
SAS11 |
5.0 |
6.0 |
False |
[SAS] |
{ESC} |
print(f'{len(df_missing)} messages are found in car but not in dbc')
df_missing
1 messages are found in car but not in dbc
|
m_id |
name |
dlc_dbc |
dlc_car |
dlc_check |
ecu_tx |
ecu_rx |
14 |
0x222 |
calc |
8.0 |
NaN |
False |
[ESC] |
{GST, EMS, SAS} |
print(f'{len(df_unidentified)} messages are found in car but not in dbc')
df_unidentified.sample(3) # 많아서 3개만 출력한다.
56 messages are found in car but not in dbc
|
m_id |
name |
dlc_dbc |
dlc_car |
dlc_check |
41 |
0x502 |
NaN |
NaN |
4.0 |
False |
51 |
0x553 |
NaN |
NaN |
8.0 |
False |
46 |
0x53E |
NaN |
NaN |
6.0 |
False |
결론
- "blf 파일에서 m_id, dlc, d_ts 추출하기"에서 CAN 버스에 있는 메시지들을 찾았다.
- 이 메시지들이 dbc 파일에 있는 지 확인하였다.
- dbc에도 있고 차에도 있고 사양(예제에서는 dlc만 확인하였다.)도 같은 경우, 문제 없다. (case OK)
- dbc에도 있고 차에도 있는데 사양이 다른 경우, 문제이다. 점검이 필요하다. (case NOK)
- dbc에는 있는데 차에는 없는 경우, 문제이다. 용도가 있어서 dbc에 포함하였거나, dbc에 오류가 있거나, 제어기에 오류가 있다. (case Missing)
- dbc에는 없는데 차에는 있는 경우, 문제이다. dbc에 오류가 있거나, 제어기에 오류가 있다. (case Unidentified)
- dbc와 제어기를 점검하고 필요에 따라 수정하고 이 스크립트를 실행하여 결과를 확인하는 절차를 반복하는 방식으로 설계 형상과 실제 형상을 맞춰나갈 수 있다.
- dbc를 최신 상태로 유지하는 것은 차량 형상 관리를 위해서 중요하다.
- 툴 없이 차량 형상 관리를 한다는 것은 무모한 일이라고 생각한다.