- 嵌入式
- 系统编程
【免费下载链接】fprime
F´ - A flight software and embedded systems framework
导读
本文以 F´(F Prime)飞行软件框架中的事件日志(Event Log)机制为核心,以TestLog测试组件的 Component Dictionary 文档(TestLog.md)为主线,完整还原了"XML 定义事件 → 自动代码生成 → 事件发送与接收 → 字典文档生成"的完整链路。读完本文,你将掌握如何在 F´ 组件 XML(Ai.xml)中声明带参数的事件、理解事件 ID 的十六进制编码规则、使用自动生成的log_ACTIVITY_LO_*发送 API,以及如何实现接收端的事件反序列化与日志端口对接。
一、什么是 Component Dictionary:TestLog.md 的定位
在 F´ 框架中,Component Dictionary(组件字典)是每个组件面向集成、测试与地面系统(GDS)的"说明书"。它以 Markdown 表格的形式,集中罗列组件对外暴露的命令(Commands)、遥测通道(Telemetry Channels)与事件(Events),是自动化工具链、GDS 配置和人工联调时共同依赖的权威数据源。
TestLog.md 正是Autocoders/Python/test/event_string测试组件生成的字典文档。该文档由自动化工具从组件的 XML 定义派生而来,其"事件列表"部分记录了TestLog组件声明的全部事件及其参数签名:
| Event Name | ID | Description | Arg Name | Arg Type | Arg Size | Description |
|---|---|---|---|---|---|---|
| SomeEvent | 100 (0x64) | A test event | ||||
| arg1 | I32 | The I32 command argument | ||||
| arg2 | Fw::LogStringArg& | 15 | The F32 command argument | |||
| arg3 | U8 | The U8 command argument |
这张表信息密度很高:事件名为SomeEvent,ID 为十进制的100、同时给出十六进制形式0x64,事件带有三个参数——arg1(I32)、arg2(Fw::LogStringArg&,即日志专用字符串类型,容量 15 字符)、arg3(U8)。后续章节将逐一揭示这些字段从何而来、如何被代码消费。
二、事件从哪来:XML 中的事件声明
字典文档不是手写的,而是由组件的 XML 描述(AI XML)驱动自动生成的。TestLog组件的完整定义位于 TestComponentAi.xml,其中与SomeEvent相关的声明如下:
<component name="TestLog" kind="passive" namespace="Somewhere"> <events> <!-- A test event --> <event id="100" name="SomeEvent" severity="ACTIVITY_LO" format_string = "My Event %d [%s] %d" > <comment>A test event</comment> <args> <arg name="arg1" type="I32"> <comment>The I32 command argument</comment> </arg> <arg name="arg2" type="string" size="15"> <comment>The F32 command argument</comment> </arg> <arg name="arg3" type="U8"> <comment>The U8 command argument</comment> </arg> </args> </event> </events> ... </component>逐字段对照字典表格,可以完整还原文档中各列的技术含义:
id="100":事件的唯一编号,在字典中同时呈现为十进制100与十六进制0x64。ID 在同一组件内必须唯一,是接收端与 GDS 识别事件的依据。name="SomeEvent":事件名称,字典中的 "Event Name" 列即取自这里。severity="ACTIVITY_LO":事件严重级别。F´ 预定义的级别包括ACTIVITY_LO(低活动级)、ACTIVITY_HI、WARNING_LO、WARNING_HI、FATAL、COMMAND、DIAGNOSTIC等,直接决定事件在Fw::LogSeverity中的取值。format_string="My Event %d [%s] %d":事件的格式化字符串,类似printf风格。三个占位符%d、%s、%d与三个参数一一对应,供地面系统、文本日志(Text Log)通道渲染人类可读的事件文本。arg name="arg1" type="I32"/arg name="arg3" type="U8":整型参数,直接映射为 C++ 基础类型。arg name="arg2" type="string" size="15":字符串参数。size指定容量上限,自动代码生成时会将其映射为Fw::LogStringArg&,这正是字典中 "Arg Type" 列出现Fw::LogStringArg&、"Arg Size" 列为15的原因。
组件还导入了三个与日志链路密切相关的端口类型(见 TestComponentAi.xml):Fw/Log/LogPortAi.xml(事件日志端口)、Fw/Log/LogTextPortAi.xml(文本日志端口)、Fw/Time/TimePortAi.xml(时间戳端口)。在<ports>段中,它们分别以Log(role="LogEvent")、LogText(role="LogTextEvent")、Time(role="TimeGet")三个输出端口的形式被实例化——事件在发出时自动携带时间戳,并通过这两个日志端口分发。
三、ID 与十六进制编码的生成规则
字典中100 (0x64)的双进制呈现并非手工填写,而是由 Markdown 字典模板程序化生成。查看模板 MdEventsTablePage.tmpl,可以看到 ID 的完整计算逻辑:
#for $id, $eventname, $severity, $format_string, $throttle, $comment in $events: ... #if len($id) == 1: #set $id = $id[0] #if "x" in $id: #set $id = int($id, 16) + $base_id #else #set $id = int($id) + $base_id #end if #set $hexid = hex($id) #end if从模板可以推断出几条关键规则:
- 支持十进制与十六进制两种书写方式:如果 XML 中 ID 字符串含有字符
x,按十六进制解析;否则按十进制解析。 - 支持基址偏移(
base_id):模板将解析出的 ID 加上组件级基址base_id得到最终 ID,这是 F´ 框架级与组件级事件编号冲突规避的通用手段。 - 十六进制自动派生:最终 ID 通过 Python 的
hex()生成0x64形式,与十进制一并写入表格,方便调试时按位识别事件族。
模板同时遍历$args为每个事件输出参数行——首行写事件名与 ID,后续每一行以| | | |前缀续写参数信息,这正是 TestLog.md 表格中"事件行 + 三个参数行"排布的直接来源。同目录下的events/EventBody.tmpl(EventBody.tmpl)则负责生成供 GDS 使用的 Python 事件描述模块,其中同样携带ID、SEVERITY、FORMAT_STRING与ARGUMENTS元数据,印证了字典与 GDS 数据共享同一份 XML 源头。
四、自动生成的发送 API:log_ACTIVITY_LO_SomeEvent
事件定义好之后,Autocoder 会为组件基类生成"事件发送"成员函数,命名规则为log_<SEVERITY>_<EventName>。在 TestLogImpl.cpp 中可以直观看到它的用法:
void TestLogImpl::sendEvent(I32 arg1, Fw::LogStringArg& arg2, U8 arg3) { printf("Sending event args %d, %s, %d\n", arg1, arg2.toChar(), arg3); this->log_ACTIVITY_LO_SomeEvent(arg1, arg2, arg3); }要点解析:
- 函数签名与 XML 参数严格对应:
arg1(I32)、arg2(Fw::LogStringArg&)、arg3(U8),三个实参按声明顺序传入,顺序与字典表格完全一致。 - 组件实现类的继承关系:
TestLogImpl继承自Somewhere::TestLogComponentBase(见 TestLogImpl.hpp),log_ACTIVITY_LO_SomeEvent即定义在该自动生成的基类中,实现类只需直接调用。命名空间Somewhere也来自 XML 的namespace属性,说明命名空间、组件名共同决定了生成代码的 C++ 符号。 - 参数类型映射规则:XML 中的
I32→I32、U8→U8、string size="15"→Fw::LogStringArg&。其中字符串类型Fw::LogStringArg定义在 LogString.hpp,其STRING_SIZE = FW_LOG_STRING_MAX_SIZE,内部使用定长char m_buf[...]存储,toChar()返回 C 风格字符串指针——这就是字典 "Arg Size = 15" 对应的实际容量语义。
事件发送后,基类会通过组件的Log输出端口(类型Fw::Log)与LogText输出端口(类型Fw::LogText)把事件分发出去,并借助Time端口(role="TimeGet")为事件打上时间戳。因此,任何事件在接收端都会携带事件 ID、时间戳、严重级别与序列化后的参数缓冲区。
五、接收端视角:事件的反序列化与参数顺序
事件最终由日志消费方通过输入端口接收。TestLogRecvImpl.cpp 展示了接收处理的典型范式:
void TestLogRecvImpl::logRecvPort_handler(NATIVE_INT_TYPE portNum, FwEventIdType id, Fw::Time &timeTag, const Fw::LogSeverity& severity, Fw::LogBuffer &args) { printf("Received log %d, Time (%d,%d:%d) severity %d\n", id, timeTag.getTimeBase(), timeTag.getSeconds(), timeTag.getUSeconds(), severity.e); I32 arg1; Fw::LogStringArg arg2; U8 arg3; // deserialize them in reverse order args.deserialize(arg3); args.deserialize(arg2); args.deserialize(arg1); printf("Args: %d \"%s\" %d\n", arg1, arg2.toChar(), arg3); }这里有三个极易踩坑的关键点:
- 参数以"逆序"反序列化:
Fw::LogBuffer中参数的序列化顺序与发送 API 的实参顺序相反(后进先出)。因此接收端必须依次先deserialize(arg3)、再deserialize(arg2)、最后deserialize(arg1),才能还原原始顺序。代码注释 "deserialize them in reverse order" 明确点出了这一约定。 - 事件元数据随参数一同送达:
FwEventIdType id对应字典中的事件 ID(100/0x64),Fw::Time &timeTag是发送端TimeGet端口注入的时间戳,Fw::LogSeverity对应 XML 中声明的ACTIVITY_LO。接收端可据此在日志系统中分流、过滤或渲染不同严重级别的事件。 - 字符串参数以
Fw::LogStringArg落位:反序列化目标必须是定长字符串类型Fw::LogStringArg,与发送端/字典中的类型完全一致,随后用toChar()输出。
六、测试与构建集成:字典背后的工程闭环
event_string不仅是演示代码,更是一个可编译、可测试的 Autocoder 回归用例。从 CMakeLists.txt 可以看到其工程组织:
SOURCE_FILES同时包含两个 XML(TestComponentAi.xml、TestPortAi.xml)与手写的 C++ 实现(TestLogImpl.cpp、TestLogRecvImpl.cpp、main.cpp),XML 会经由 Autocoder 生成TestComponentAc基类代码后参与编译。MOD_DEPS声明依赖Autocoders/Python/test/log_tester与Autocoders/Python/test/time_tester——前者提供了LogTextImpl文本日志接收基类(TestLogRecvImpl.hpp 中TestLogRecvImpl即继承自LogTextImpl),后者提供时间模拟能力。- 单元测试入口 main.cpp 实例化了
Somewhere::TestLogGTestBase测试基类,表明 Autocoder 同时为组件生成了 GTest 测试骨架,可对事件发送行为做自动化断言。
也就是说,从 XML 事件声明出发,一条流水线同时产出:C++ 组件基类(含log_ACTIVITY_LO_SomeEvent)、GTest 测试基类(TestLogGTestBase)、GDS 事件描述模块(EventBody.tmpl产物)以及本文章反复引用的 Markdown 组件字典(MdEventsTablePage.tmpl产物)。TestLog.md 正是这条流水线的最终可读产物之一,它把事件 ID、参数类型、参数容量等运行时关键信息以表格形式固化下来,成为跨角色(组件开发者、集成者、测试者、GDS 操作员)沟通的统一语言。
七、实践要点小结
- 声明事件:在组件 Ai.xml 的
<events>段使用<event id name severity format_string>声明,参数用<args>/<arg>描述,字符串参数务必给出size容量。 - 生成产物:重新运行 Autocoder(或重新构建 CMake 工程)后,自动获得
log_<SEVERITY>_<EventName>发送 API、GTest 基类与 Markdown 字典;字典中的 "Arg Type" 列(如Fw::LogStringArg&)即参数类型的最终 C++ 形态。 - 发送事件:在实现类中直接调用生成的发送函数,实参顺序与 XML 声明顺序一致。
- 接收事件:在日志接收端按"逆序"对
Fw::LogBuffer反序列化,并通过FwEventIdType、Fw::Time、Fw::LogSeverity获得事件身份、时间戳与严重级别。 - 核对字典:事件 ID 的十进制/十六进制双表示、参数列表均可作为集成调试与 GDS 建链的核对依据,确保 XML、生成代码与字典三方一致。
- 嵌入式
- 系统编程
【免费下载链接】fprime
F´ - A flight software and embedded systems framework
相关推荐
Activepieces 收到 Webhook 被拒绝报 413 Request Too Long 怎么调整负载上限?
Activepieces 收到 Webhook 被拒绝报 413 Request Too Long 怎么调整负载上限? Activepieces 的 Webho
嵌入式系统编程F´ 事件节流(Event Throttle)机制剖析:从 XML 定义、自动生成代码到组件字典与测试验证
F´ 事件节流(Event Throttle)机制剖析:从 XML 定义、自动生成代码到组件字典与测试验证 本文以仓库中 event_throttle 测试组件
嵌入式系统编程Envoy StringMatcher:五种字符串匹配模式与 Lua 自定义匹配器的配置与实现解析
Envoy StringMatcher:五种字符串匹配模式与 Lua 自定义匹配器的配置与实现解析 本文基于 Envoy 官方文档 string_matcher
嵌入式系统编程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考