
前言
近期在生产环境中遇到了直接将源码内容生成观测量、标定量的需求,起因是在测试过程中需要动态修改某些参数并测试效果,需要将一部分变量搓成观测量、标定量,但考虑到供应商模型版本、模型业务逻辑管理混乱,且模型A2L生成规则、ECU地址匹配规则不明等问题,临时应付一下手动编辑A2L进行了处理,但考虑到这种场景可能并不是个例,之后大概率也会遇到,于是搓个一劳永逸工具的想法就应运而生了,我给它起名为 SrcToA2L
;
项目简介
顾名思义,SrcToA2L
是用来将源码中的内容转换为A2L文件中标定量、观测量的工具,通常这些内容借助 Simulink
模型的 AUTOSAR
组件或其他工具直接生成,但某些场合下还是比较麻烦的,借助这个工具,可以简单的将源码中的标记内容处理并生成到A2L中作为观测量、标定量使用;
SrcToA2L
是一个非GUI程序,需要通过终端传入参数进行调用,可以通过将 SrcToA2L.exe
所在目录添加到 PATH
的方式实现让任意位置运行的终端都可以调用,或根据特定工程需要将SrcToA2L.exe
复制一份并编写 bat
脚本实现一键处理,本工具可以很方便地集成入 CI/CD
工作流;
工具支持结构体类型解析(内嵌结构体成员的结构体除外)、地址匹配、4字节地址对齐、常量宏定义识别、记录布局补全;
如何使用
你可以在项目仓库的 Test
目录下找到准备好的测试用文件,整个使用说明都将基于这些文件运行:
Test 测试用文件目录 ├─typedef.h 测试的类型定义头文件,含有一个简单的非同成员类型结构体 ├─source_code.c 测试用源文件,含有需要输出的标定量、观测量 ├─Minimal_A2L.a2l 测试用最小体积A2L,CANape等Vector工具可读 └─test_map.map 测试用map文件,由Green Hills编译器生成,只裁剪了测试用例用到的部分
工具需要知道你要将哪些内容生成为标定量,哪些内容生成为观测量,为此你需要按照如下示例将特定的标记段插入原始代码中:
// .\Test\source_code.c
4 collapsed lines
#include "stdint.h"#include "typedef.h"
#define VALUE_S 2
/*start_of_calibrations*/uint8_t c_u8;uint16_t c_u16;const volatile float c_float;/*end_of_calibrations*/
/*start_of_measurements*/double m_double = 1.23;uint16_t m_u16[2];
Test_Struct S[VALUE_S];/*end_of_measurements*/
如上,在 calibrations
标记段中的内容将被解析为标定量,在 measurements
标记段中的内容将会被解析为观测量,它们以注释形式插入源码,不会影响原始代码编译过程;
使用终端运行如下指令即可将 .c
、.h
中标记段内容解析为对应的观测量、标定量:
SrcToA2L.exe .\Test\typedef.h .\Test\source_code.c -r .\Test\Minimal_A2L.a2l -m .\Test\test_map.map
其中,紧跟着 -r
与 -m
后的参数分别指定了要合并写入的A2L文件与用于查找地址的map文件,命令执行完成后会在传入的A2L文件所在目录生成中间件与合并后的A2L文件,但 -r
与 -m
并不是必要的,你也可以只执行如下命令:
SrcToA2L.exe .\Test\typedef.h .\Test\source_code.c
这条指令将仅从传入的源文件、头文件中提取需要生成的内容,并将结果输出到当前调用命令行的工作目录下生成的临时中间件 Middleware.txt
;
通过CANape打开测试用例生成的 [NEW]Minimal_A2L.a2l
可以看到 .\Test\source_code.c
中的标定量、观测量已经正确生成:
指令参数详解
SrcToA2L
的行为受传入的指令参数控制,其中 -X
形式的参数为控制类型参数,共有以下几种:
-r
指定为合并写入的参考A2L文件;-m
指定为用于查找地址的map文件;-a
指定字节对齐长度(仅为1或2的正整次幂),不指定或参数错误时默认为4字节对齐;
其它传入的参数视为用于解析的源文件、头文件,A2L与map文件仅传入的最后一个有效;
SrcToA2L
不会破坏任何用于输入的文件,仅以只读模式打开;
它是如何工作的?
在以只读模式打开所有输入文件后,首先会进行宏定义扫描及类型识别,并将受支持的宏定义和类型记录下来;
随后,工具重新完整扫描输入的源文件、头文件,如果存在参考A2L输入,工具会扫描整个A2L文件查找记录布局(RECORD_LAYOUT),查找是否已经存在需要使用的基础类型记录布局,如果存在缺失部分,会在随后生成的中间件(XXX.middleware.txt)中进行补全,如果不存在参考A2L输入,则直接将所有要用到的记录布局全部输出到中间件中;
完成记录布局补全后,工具会开始解析源文件、头文件中的标记段内容,生成对应的观测量、标定量到输出到中间件,并在这个过程中进行地址匹配、字节对齐等操作;
最后,如果存在参考A2L输入,工具会新建一个以参考A2L名称关联的新文件,将中间件与其合并;
所有的由 SrcToA2L
生成的内容都可以在中间件中找到;
那么,在哪里才能下载到?
你可以在下列项目仓库找到 SrcToA2L
的全部源码以及预构建完成的可执行文件:
注意事项
本工具为了省略多余操作,除需特定处理的 calibrations
与 measurements
标记段外,结构体、宏定义、地址匹配等的解析识别是通过全文扫描提取关键字进行的,由于代码风格千奇百怪,解析处理过程有可能会遇到预料之外的BUG(尤其是在生产环境下),为避免发生此类情况发生,最好事先将用于处理的代码进行格式化,或特别留意 define
、typedef
等关键字周边的情况;
如有意外情况发生,可以参考终端日志查看问题发生位置以修改输入文件,或对本工具源码进行修改,构建适应特定环境的版本;
类型识别
由于嵌入式平台对类型长度敏感,本工具仅支持部分stdint.h
中字节长度固定的标准类型,并将部分常用类型解析为固定长度,如下所示:
typedef enum{ TYPE_UNKNOWN, // 未知类型或不支持类型 STRUCTURE, // 结构体类型 UBYTE, // uint8_t,bool,boolean_t 8 位 1字节 UWORD, // uint16_t 16位 2字节 ULONG, // uint32_t 32位 4字节 SBYTE, // int8_t 8 位 1字节 SWORD, // int16_t 16位 2字节 SLONG, // int32_t,int 32位 4字节 FLOAT32, // float 32位 4字节 FLOAT64, // double 64位 8字节} variable_type_enum;
对于结构体,其所有子成员必须为支持的基础数据类型,且不存在结构体成员(目前没有支持嵌套结构体的计划,除非我在生产环境遇到了);
类型匹配详情可以在以下函数中查看,你也可以修改这个函数以支持更多的类型:
// 解析变量类型variable_type_enum solve_variable_type(const char *type_str){ // 基础类型解析31 collapsed lines
{ if (!strcmp(type_str, "bool")) return UBYTE; if (!strcmp(type_str, "boolean_t")) return UBYTE;
if (!strcmp(type_str, "uint8_t")) return UBYTE;
if (!strcmp(type_str, "uint16_t")) return UWORD;
if (!strcmp(type_str, "uint32_t")) return ULONG;
if (!strcmp(type_str, "int8_t")) return SBYTE;
if (!strcmp(type_str, "int16_t")) return SWORD;
if (!strcmp(type_str, "int")) return SLONG; if (!strcmp(type_str, "int32_t")) return SLONG;
if (!strcmp(type_str, "float")) return FLOAT32; if (!strcmp(type_str, "double")) return FLOAT64; }
// 遍历复合类型9 collapsed lines
type_node *target_node = type_list_head; while (target_node != nullptr) { if (!strcmp(type_str, target_node->type_name_str)) return target_node->type; target_node = target_node->p_next; }
return TYPE_UNKNOWN;}
常量宏识别
对于宏定义,仅支持正整值直接宏定义,不支持嵌套宏定义,如下所示:
#define VALUE_1 100 // √#define VALUE_2 -2 // X#define VALUE_3 VALUE_1 // X
关于构建
本项目所使用的构建平台及环境为如下:
OS:Windows11 Professional 24H2 (26100.4652)
Tool Chain:mingw-w64\i686-14.2.0-release-win32-dwarf-ucrt-rt_v12-rev2
构建关系由 Makefile
组织,使用 C17
、C++17
标准,但由于并未使用高级特性,构建标准可自行调整(按理说 C98
、C++98
应该也行);
完善构建环境后,在工作目录下使用 make
、mingw32-make
即可进行构建;
本工具仅使用标准库,完全具备跨平台支持,但在其他平台、环境下需要对 Makefile
进行少量修改;
目前已知使用64位或某些分发版本的编译器时会出现 size_t
类型不统一导致的编译警告,但不影响主要功能;