4. ARXML 基础:AUTOSAR XML 文件结构、数据类型定义、组件描述

ARXML,全称 AUTOSAR XML。说白了,它就是 AUTOSAR 世界的通用语言。所有工具链之间的数据交换,都靠它。

我刚接触 AUTOSAR 时,第一反应是:这玩意儿怎么这么复杂?一个配置文件动辄上万行。后来做项目多了才明白,ARXML 的结构其实非常规整。你只要掌握了它的骨架,剩下的就是往里面填内容。

4.1 ARXML 文件结构

一个标准的 ARXML 文件,长什么样?我直接给你看一个最小示例:

<?xml version="1.0" encoding="utf-8"?>
<AUTOSAR>
  <AR-PACKAGES>
    <AR-PACKAGE>
      <SHORT-NAME>MyProject</SHORT-NAME>
      <ELEMENTS>
        <!-- 这里放具体的 AUTOSAR 元素 -->
      </ELEMENTS>
    </AR-PACKAGE>
  </AR-PACKAGES>
</AUTOSAR>

根节点永远是 <AUTOSAR>。里面包着一层 <AR-PACKAGES>,再往下是具体的 <AR-PACKAGE>。每个 Package 就是一个命名空间,用来组织你的模块。

我的习惯: 每个模块建一个独立的 AR-PACKAGE。比如 SWC 放一个包,ECU 配置放另一个包。这样后期维护时,你不会在一堆乱麻里找东西。

AR-PACKAGE 内部,核心是 <ELEMENTS> 标签。所有具体的定义——数据类型、组件、接口——都放在这里面。

4.2 数据类型定义

AUTOSAR 的数据类型体系,分三层。我画了张图,帮你理清关系:

AUTOSAR 数据类型三层体系 应用数据类型 ApplicationDataType 例:车速、温度、扭矩 实现数据类型 ImplementationDataType 例:uint8、sint16、float32 基础类型 BaseType 例:8位无符号、16位有符号 映射关系: 应用数据类型 → 实现数据类型 → 基础类型。工具链自动完成映射,你只需定义前两层。 ARXML 示例:定义一个车速数据类型 <APPLICATION-DATA-TYPE> <SHORT-NAME>VehicleSpeed</SHORT-NAME> <CATEGORY>VALUE</CATEGORY> </APPLICATION-DATA-TYPE> 注:CATEGORY 常用值有 VALUE、ARRAY、RECORD

为什么要分三层?我举个例子。你在应用层定义了一个"车速"变量,单位是 km/h。这是应用数据类型。到了底层,它可能被实现为一个 16 位无符号整数。这是实现数据类型。再往下,这个 16 位无符号整数在芯片上怎么存储?这是基础类型。

三层分离的好处是:应用层不用关心底层实现。你换了个芯片,基础类型变了,但应用层的"车速"定义不用改。

4.2.1 应用数据类型 (ApplicationDataType)

这是最上层,直接对应物理世界的概念。定义方式很简单:

<APPLICATION-DATA-TYPE>
  <SHORT-NAME>EngineTemp</SHORT-NAME>
  <CATEGORY>VALUE</CATEGORY>
  <SW-DATA-DEF-PROPS>
    <SW-IMPL-POLICY>STANDARD</SW-IMPL-POLICY>
  </SW-DATA-DEF-PROPS>
</APPLICATION-DATA-TYPE>

CATEGORY 字段很关键。VALUE 表示标量,ARRAY 表示数组,RECORD 表示结构体。我见过不少新手把 RECORD 当 VALUE 用,结果工具链报错找半天。

4.2.2 实现数据类型 (ImplementationDataType)

这一层决定了变量在内存中的实际表示。看个例子:

<IMPLEMENTATION-DATA-TYPE>
  <SHORT-NAME>uint16_Impl</SHORT-NAME>
  <CATEGORY>VALUE</CATEGORY>
  <BASE-TYPE-REF DEST="SW-BASE-TYPE">/BaseTypes/uint16</BASE-TYPE-REF>
</IMPLEMENTATION-DATA-TYPE>

这里引用了基础类型 uint16。基础类型通常由芯片厂商提供,你不需要自己定义。

避坑指南: 我曾经在一个项目中,把实现数据类型的长度定义错了。应用层传过来一个 0-100 的值,底层却只分配了 8 位。结果跑到 101 就溢出了。排查了整整两天。所以,定义数据类型时,一定要确认取值范围和精度。

4.3 组件描述

组件,就是 AUTOSAR 里的软件单元。一个组件可以是一个传感器驱动、一个控制算法、一个诊断模块。

组件描述的核心,是定义它的"接口"——它需要什么数据,输出什么数据。我习惯把组件想象成一个黑盒子。你不需要知道里面怎么实现的,只要知道它跟外界怎么交互。

4.3.1 原子软件组件 (Atomic SWC)

最常用的组件类型。一个 Atomic SWC 包含:

  • 端口 (Ports):组件与外界的通信接口
  • 内部行为 (Internal Behavior):组件的运行实体,比如 Runnable
  • 数据映射:端口数据与内部变量的对应关系

看一个完整的组件定义示例:

<ATOMIC-SW-COMPONENT-TYPE>
  <SHORT-NAME>SpeedController</SHORT-NAME>
  <PORTS>
    <R-PORT-PROTOTYPE>
      <SHORT-NAME>SpeedInput</SHORT-NAME>
      <REQUIRED-COM-SPECS>
        <NONQUEUED-RECEIVER-COM-SPEC/>
      </REQUIRED-COM-SPECS>
    </R-PORT-PROTOTYPE>
    <P-PORT-PROTOTYPE>
      <SHORT-NAME>ControlOutput</SHORT-NAME>
    </P-PORT-PROTOTYPE>
  </PORTS>
  <INTERNAL-BEHAVIORS>
    <SWC-INTERNAL-BEHAVIOR>
      <SHORT-NAME>Behavior</SHORT-NAME>
      <EVENTS>
        <TIMING-EVENT>
          <SHORT-NAME>PeriodicRun</SHORT-NAME>
          <START-ON-EVENT-REF DEST="RUNNABLE-ENTITY">/SpeedController/Runnable1</START-ON-EVENT-REF>
          <PERIOD>0.01</PERIOD>
        </TIMING-EVENT>
      </EVENTS>
      <RUNNABLES>
        <RUNNABLE-ENTITY>
          <SHORT-NAME>Runnable1</SHORT-NAME>
          <CAN-BE-INVOKED-CONCURRENTLY>false</CAN-BE-INVOKED-CONCURRENTLY>
        </RUNNABLE-ENTITY>
      </RUNNABLES>
    </SWC-INTERNAL-BEHAVIOR>
  </INTERNAL-BEHAVIORS>
</ATOMIC-SW-COMPONENT-TYPE>

这个组件叫 SpeedController。它有一个输入端口 SpeedInput,一个输出端口 ControlOutput。内部有一个 Runnable,每 10 毫秒执行一次。

4.3.2 端口与接口

端口分两种:

端口类型 方向 典型用途
R-Port (Required) 输入 读取传感器数据、接收其他组件的计算结果
P-Port (Provided) 输出 发送控制指令、发布计算结果
PR-Port (Required/Provided) 双向 客户端-服务器通信中的请求/响应

端口必须绑定一个接口。接口定义了端口上传输的数据结构。常用的接口类型有:

  • Sender-Receiver 接口:数据发布/订阅模式。一个发,多个收。适合周期性数据,比如车速、转速。
  • Client-Server 接口:请求/响应模式。一个客户端调用,一个服务器响应。适合诊断请求、参数配置。
经验之谈: 我建议你尽量用 Sender-Receiver 接口。Client-Server 虽然灵活,但会引入同步等待,容易导致时序问题。除非你明确需要请求-响应模式,否则别碰它。

4.3.3 组件间的连接

组件定义好了,怎么把它们连起来?通过 <SW-CONNECTOR>:

<SW-CONNECTOR>
  <SHORT-NAME>Conn_SpeedToControl</SHORT-NAME>
  <STARTING-PORT-REF DEST="R-PORT-PROTOTYPE">/SpeedController/SpeedInput</STARTING-PORT-REF>
  <ENDING-PORT-REF DEST="P-PORT-PROTOTYPE">/SensorComponent/SpeedOutput</ENDING-PORT-REF>
</SW-CONNECTOR>

这个连接把 SensorComponent 的输出端口 SpeedOutput,连到了 SpeedController 的输入端口 SpeedInput。数据流就通了。

嗯,这里要注意:连接时,两个端口的数据类型必须一致。否则工具链会报类型不匹配的错误。我见过有人把 uint8 连到 uint16 上,编译能过,但运行时数据截断了。

4.4 本章小结

ARXML 的核心就三件事:

  1. 文件结构:AUTOSAR 根节点 → AR-PACKAGES → 具体元素
  2. 数据类型:应用数据类型 → 实现数据类型 → 基础类型,三层映射
  3. 组件描述:定义端口、接口、内部行为,然后通过连接器组装