F´ 事件日志(Event Log)机制深度解析:从 XML 定义、自动代码生成到 Component Dictionary
2026/9/24 21:39:31 网站建设 项目流程
  • 嵌入式
  • 系统编程

【免费下载链接】fprime

F´ - A flight software and embedded systems framework

项目地址:https://gitcode.com/gh_mirrors/fp/fprime
点击查看免费下载

导读

本文以 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 NameIDDescriptionArg NameArg TypeArg SizeDescription
SomeEvent100 (0x64)A test event
arg1I32The I32 command argument
arg2Fw::LogStringArg&15The F32 command argument
arg3U8The 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_HIWARNING_LOWARNING_HIFATALCOMMANDDIAGNOSTIC等,直接决定事件在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

从模板可以推断出几条关键规则:

  1. 支持十进制与十六进制两种书写方式:如果 XML 中 ID 字符串含有字符x,按十六进制解析;否则按十进制解析。
  2. 支持基址偏移(base_id:模板将解析出的 ID 加上组件级基址base_id得到最终 ID,这是 F´ 框架级与组件级事件编号冲突规避的通用手段。
  3. 十六进制自动派生:最终 ID 通过 Python 的hex()生成0x64形式,与十进制一并写入表格,方便调试时按位识别事件族。

模板同时遍历$args为每个事件输出参数行——首行写事件名与 ID,后续每一行以| | | |前缀续写参数信息,这正是 TestLog.md 表格中"事件行 + 三个参数行"排布的直接来源。同目录下的events/EventBody.tmpl(EventBody.tmpl)则负责生成供 GDS 使用的 Python 事件描述模块,其中同样携带IDSEVERITYFORMAT_STRINGARGUMENTS元数据,印证了字典与 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 中的I32I32U8U8string 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); }

这里有三个极易踩坑的关键点:

  1. 参数以"逆序"反序列化Fw::LogBuffer中参数的序列化顺序与发送 API 的实参顺序相反(后进先出)。因此接收端必须依次先deserialize(arg3)、再deserialize(arg2)、最后deserialize(arg1),才能还原原始顺序。代码注释 "deserialize them in reverse order" 明确点出了这一约定。
  2. 事件元数据随参数一同送达FwEventIdType id对应字典中的事件 ID(100/0x64),Fw::Time &timeTag是发送端TimeGet端口注入的时间戳,Fw::LogSeverity对应 XML 中声明的ACTIVITY_LO。接收端可据此在日志系统中分流、过滤或渲染不同严重级别的事件。
  3. 字符串参数以Fw::LogStringArg落位:反序列化目标必须是定长字符串类型Fw::LogStringArg,与发送端/字典中的类型完全一致,随后用toChar()输出。

六、测试与构建集成:字典背后的工程闭环

event_string不仅是演示代码,更是一个可编译、可测试的 Autocoder 回归用例。从 CMakeLists.txt 可以看到其工程组织:

  • SOURCE_FILES同时包含两个 XML(TestComponentAi.xml、TestPortAi.xml)与手写的 C++ 实现(TestLogImpl.cppTestLogRecvImpl.cppmain.cpp),XML 会经由 Autocoder 生成TestComponentAc基类代码后参与编译。
  • MOD_DEPS声明依赖Autocoders/Python/test/log_testerAutocoders/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 操作员)沟通的统一语言。

七、实践要点小结

  1. 声明事件:在组件 Ai.xml 的<events>段使用<event id name severity format_string>声明,参数用<args>/<arg>描述,字符串参数务必给出size容量。
  2. 生成产物:重新运行 Autocoder(或重新构建 CMake 工程)后,自动获得log_<SEVERITY>_<EventName>发送 API、GTest 基类与 Markdown 字典;字典中的 "Arg Type" 列(如Fw::LogStringArg&)即参数类型的最终 C++ 形态。
  3. 发送事件:在实现类中直接调用生成的发送函数,实参顺序与 XML 声明顺序一致。
  4. 接收事件:在日志接收端按"逆序"对Fw::LogBuffer反序列化,并通过FwEventIdTypeFw::TimeFw::LogSeverity获得事件身份、时间戳与严重级别。
  5. 核对字典:事件 ID 的十进制/十六进制双表示、参数列表均可作为集成调试与 GDS 建链的核对依据,确保 XML、生成代码与字典三方一致。
  • 嵌入式
  • 系统编程

【免费下载链接】fprime

F´ - A flight software and embedded systems framework

项目地址:https://gitcode.com/gh_mirrors/fp/fprime
点击查看免费下载
上一篇:3大核心功能重构Wallpaper Engine资源处理:RePKG给开发者的效率提升指南
下一篇:LeagueAkari:重新定义英雄联盟体验的智能辅助工具

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询