第1章:UA(用户应用)设计
各位同学,今天我们来聊聊ARINC 661标准里最核心的部分——UA设计。说白了,UA就是座舱显示系统的“大脑”,它负责处理飞行数据,然后告诉CDS该怎么画。
我刚开始接触ARINC 661时,总觉得UA和CDS的边界很模糊。后来做了几个项目才明白,这个边界一旦划清楚,整个系统设计就顺了。
1.1 UA的职责
UA的全称是User Application,用户应用。它的职责可以概括为三件事:
- 数据采集与处理:从飞机总线(比如ARINC 429、AFDX)获取原始数据,进行必要的计算和转换
- 状态管理:维护显示系统需要的各种状态变量,比如当前高度、航向、告警状态
- 与CDS通信:按照ARINC 661协议,把处理好的数据封装成标准消息,发给CDS
记住一个原则:UA只管“有什么数据”,不管“数据怎么画”。画的事情交给CDS。
我在项目中遇到过一位同事,非要在UA里写绘图逻辑,结果CDS升级时两边都要改,折腾了整整两周。嗯,从那以后我定了个规矩:UA里绝对不出现任何与像素、坐标相关的代码。
1.2 UA与CDS的交互流程
UA和CDS的交互,其实就是一个“请求-响应”的循环。你想想看,飞行员操作屏幕,或者飞机状态发生变化,UA都需要及时响应。
典型的交互流程是这样的:
- CDS发送事件:飞行员触摸屏幕,CDS把触摸事件打包成ARINC 661消息,发给UA
- UA处理事件:UA解析消息,更新内部状态,可能还会触发一些逻辑(比如切换页面)
- UA发送更新:UA把需要更新的控件数据封装成消息,发回给CDS
- CDS渲染:CDS收到消息后,更新对应的控件显示
这个循环每秒钟可能发生几十次。我建议你在设计时,给每个交互步骤打上时间戳,方便后期排查性能问题。
个人经验:我曾经在一个项目中,UA和CDS之间的通信延迟超过了100ms,结果飞行员反映屏幕“卡顿”。后来发现是UA端的数据封装太慢,优化后降到了20ms以内。
1.3 UA端的数据封装与解析
这部分是实操的重点。ARINC 661定义了一套标准的消息格式,UA需要严格按照这个格式来封装和解析数据。
消息的基本结构是这样的:
// ARINC 661消息头
typedef struct {
uint16_t messageId; // 消息ID
uint16_t length; // 消息总长度
uint32_t timestamp; // 时间戳(可选)
} A661MessageHeader;
// 控件更新消息
typedef struct {
A661MessageHeader header;
uint32_t widgetId; // 控件ID
uint16_t parameterId; // 参数ID
uint8_t data[]; // 参数值
} A661SetParameterMessage;
封装数据时,我习惯这样做:
- 先确定要更新哪个控件(widgetId)
- 再确定要更新哪个参数(parameterId)
- 最后把参数值按标准格式填入data字段
举个例子,更新一个高度显示控件:
// 封装高度数据
void packAltitudeData(uint32_t widgetId, float altitude) {
A661SetParameterMessage msg;
msg.header.messageId = 0x0102; // SetParameter消息ID
msg.header.length = sizeof(msg) + sizeof(float);
msg.widgetId = widgetId;
msg.parameterId = 0x0001; // 高度参数ID
memcpy(msg.data, &altitude, sizeof(float));
// 发送给CDS
sendToCDS(&msg, msg.header.length);
}
解析CDS发来的事件消息,步骤正好反过来:
// 解析触摸事件
void parseTouchEvent(uint8_t* buffer, uint32_t length) {
A661EventMessage* event = (A661EventMessage*)buffer;
// 检查消息ID
if (event->header.messageId != 0x0201) {
// 不是事件消息,忽略
return;
}
// 获取事件类型和参数
uint16_t eventType = event->eventType;
uint32_t widgetId = event->widgetId;
// 根据事件类型处理
switch (eventType) {
case TOUCH_PRESS:
handlePress(widgetId);
break;
case TOUCH_RELEASE:
handleRelease(widgetId);
break;
default:
break;
}
}
注意:数据封装时,字节序(Endianness)是个容易踩坑的地方。ARINC 661默认使用大端序,但有些嵌入式平台是小端序。我曾经因为这个bug,排查了整整一天才发现是字节序搞反了。
最后,我给大家一个避坑指南:
- 消息ID别搞混:每个消息类型都有固定的ID,建议用枚举定义,别用魔数
- 长度字段要准确:length字段包含消息头本身,别算错了
- 数据类型要匹配:CDS端期望什么类型,UA就发什么类型,别把float当int发
好了,UA设计的基础内容就这些。下一章我们会深入UA的内部状态机设计,到时候再聊。