前言
a2l文件是汽车行业常见的用于标定的数据库文件。在开发过程中,通常由MATLAB或者Excel生成a2l文件,但变量的地址是需要load elf文件去更新的,一般使用的是Vector的CANape工具。
下面记录和分享一下通过脚本去更新a2l文件。
1 使用CANoe中的ASAP2Updater
1.1 ASAP2Updater工具路径
一般在CANoe安装路径下,比如:
C:\Program Files\Vector CANoe 12.0\ASAP2Updater\Exec\ASAP2Updater.exe
同级目录下存在手册Manual.pdf。
1.2 使用方式
参考“Manual.pdf”手册中的3.2章节(如上图所示),则可在命令行中执行:
C:\Program Files\Vector CANoe 12.0\ASAP2Updater\Exec\ASAP2Updater.exe -I Test.a2l -A Test.elf -O Gen.a2l
需要注意的是,读取elf文件时,需要指定elf的格式,目前常用的是ELF 32 Bit。这个和在Vector工具ASAP2 Studio中选择elf格式一样的,如下图。
参考手册中的3.4章节,需要创建一个名称为“UPDATER.INI”的文件,在其中写入如下信息,并放到执行命令行的路径中。
[OPTIONS]
MAP_FORMAT = 31
如果有其他options需要设定,请参考手册。
2 使用Python
2.1 处理a2l文件
2.1.1 三方库
使用pya2l,参考: https://pypi.org/project/pya2l/
2.1.2 读取a2l文件
pya2l读取a2l文件后的格式是tree,这个格式不方便读取和编辑,可以转换成json,再到dict。参考以下步骤可读入a2l并转换成dict。
1) 导入json和pya2l中的A2lParser
import json
from pya2l.parser import A2lParser as Parser
2)读入a2l
self.parser = Parser()
# 读取a2l文件,二进制。
with open(self.a2l_file, "rb") as f:
byte = f.read()
f.close()
# a2l转换成内部的tree。
r_ast = self.parser.tree_from_a2l(byte)
# tree转换成json,方便修改
r_json_bytes = self.parser.json_from_tree(r_ast)
# load json,转换成字典,方便修改。
self.r_json_py = json.loads(r_json_bytes.decode())
- tree_from_a2l()读取的是byte格式数据,不是字符串。
- json_from_tree()可将tree转换成json格式。
- json.loads()将json格式转为字典,便于编辑。
2.1.3 保存a2l文件
保存成a2l文件的方式和读取a2l的步骤刚好相反,可参考如下:
# json转换成tree。
w_json = json.dumps(self.r_json_py, indent=4)
w_ast = self.parser.tree_from_json(w_json.encode())
# tree转换成a2l。
w_a2l = self.parser.a2l_from_tree(w_ast, indent=4).decode() # SYMBOL_LINK没了
w_a2l = self.add_SYMBOL_LINK(w_a2l)
print(w_a2l, file=open(a2l_file, "w"))
- json.dumps()将字典转换为json。
- tree_from_json()将json转成tree,用于保存成a2l。
- a2l_from_tree()将tree转换成byte格式,后面加个decode()即可转为str。
注意 a2l_from_tree()返回的文本中丢失了SYMBOL_LINK,Vector工具更新地址会用到。可以用正则表达式添加,参考如下:
regex = re.compile(r"(/begin (MEASUREMENT|CHARACTERISTIC) ([^ ]+) .*)")
str_out = regex.sub(r"\1" + " SYMBOL_LINK " + r'"\3" 0', str_in)
2.1.4 读取a2l文件中的RECORD_LAYOUT
RECORD_LAYOUT是a2l中的数据类型。读取数据类型,便于对比a2l和elf变量类型是否相同。
a2l中的基础类型一般是UBYTE、UWORD这种,在此基础上可以定义成其他的类型,比如Scalar_UBYTE。其本质还是基础类型,所以可以用一个字典表示这些类型,方便后面查找和对比。{basetype: set(data types)}
打印的示例如下:
record_dict: {'FLOAT32_IEEE': {'Lookup1D_FLOAT32_IEEE', 'FLOAT32_IEEE', 'Scalar_FLOAT32_IEEE', 'Axis_FLOAT32_IEEE'}, 'UBYTE': {'UBYTE', 'Scalar_UBYTE'}, 'ULONG': {'Scalar_ULONG', 'ULONG'}, 'UWORD': {'Scalar_UWORD', 'UWORD'}}
读取RECORD_LAYOUT,保存成上述数据格式的示例代码如下:
def read_record(self):
"""
a2l中的RECORD_LAYOUT,数据类型。
"""
project = self.r_json_py.get("PROJECT")
modules = project.get("MODULE")
for module in modules: # MODULE长度是1
records = module.get("RECORD_LAYOUT")
for record in records:
name = record["Name"]["Value"]
fnc = record.get("FNC_VALUES") or record.get("AXIS_PTS_X")
data_type = fnc["DataType"]["Value"]
if not self.record_dict.get(data_type):
self.record_dict[data_type] = {
data_type}
self.record_dict[data_type].add(name)
2.1.5 读取a2l文件中的变量信息
a2l中的变量信息有两种,分别是标定量(CHARACTERISTIC)和观测量(MEASUREMENT)。这两种变量在a2l中的节点有所差别,但对于地址和数据类型的处理还是一样的。
1)CHARACTERISTIC的信息读取和写入
project = self.r_json_py.get("PROJECT")
modules = project.get("MODULE")
for module in modules: # MODULE长度是1
characteristics = module.get("CHARACTERISTIC")
for variable in characteristics:
# a2l中变量的属性读取
name = variable["Name"]["Value"]
d