F´ 框架 Ref 部署演示组件剖析:SendBuffApp 缓冲区发送与错误注入机制
2026/9/15 15:32:26 网站建设 项目流程

F´ 框架 Ref 部署演示组件剖析:SendBuffApp 缓冲区发送与错误注入机制

【免费下载链接】fprimeF´ - A flight software and embedded systems framework项目地址: https://gitcode.com/GitHub_Trending/fpr/fprime

Ref::SendBuffApp是 F´(F Prime)飞行软件框架参考部署(Ref 部署)中用于演示数据缓冲区(DataBuffer)传输链路的示例组件:它由速率组(Rate Group)调度驱动,周期性将序列化后的数据包通过Drv.DataBuffer端口发送给Ref::RecvBuffApp,并内置了错误注入、FATAL 事件生成、断言触发等故障演练能力。读完本文,你将掌握该组件在 TestDeploymentsProject/Ref/SendBuffApp/docs/sdd.md 之外完整的 FPP 模型定义、底层 C++ 实现逻辑、数据包格式与校验和协议,以及它如何在 Ref 拓扑中与速率组、BlockDriver、RecvBuffApp 协同工作。

Ref::SendBuffApp组件图

1. 组件定位与设计意图

根据组件 SDD(Software Design Description)文档,Ref::SendBuffApp是一个演示组件(demonstration component),其唯一职责是向配对组件Ref::RecvBuffApp发送数据缓冲区。SDD 明确描述了二者的单向协作关系:

TheRef::SendBuffAppis a demonstration component that sends data buffers toRef::RecvBuffApp(RecvBuffApp SDD)

在 Ref 部署的整体数据通路中,这对组件承担的角色是验证"发送方 → 中间驱动 → 接收方"的缓冲区流动闭环:SendBuffApp生成并发送数据包,BlockDriver模拟底层块设备驱动进行中转,RecvBuffApp接收并校验数据包。这一链路在 RefTopology 连接定义 中体现为两条直接连接:

sendBuffComp.Data -> blockDrv.BufferIn blockDrv.BufferOut -> recvBuffComp.Data

从部署角度,SendBuffApp在 instances.fpp 中被实例化为queued(队列型)组件,拥有独立的消息队列但无独立线程(由速率组调度驱动其处理):

instance sendBuffComp: Ref.SendBuff base id 0x10010000 \ queue size Default.QUEUE_SIZE

其中0x10010000是该实例所有命令、事件、遥测、参数 ID 的基地址(Base ID),queue size 10继承自模块默认队列大小常量。

2. 需求分析

SDD 第 2 节给出了组件必须满足的两条需求,其验证方式均为系统级测试(System test):

需求编号描述验证方法
ISF-RBF-001Ref::SendBuffApp组件应能够发送缓冲区系统测试
ISF-RBF-002Ref::SendBuffApp组件应提供注入错误的能力系统测试

这两条需求在源码层面均有直接对应实现:

  • ISF-RBF-001(发送缓冲区):由SchedIn_handlerthis->Data_out(0, this->m_testBuff)调用实现(见 SendBuffComponentImpl.cpp);
  • ISF-RBF-002(注入错误):由命令SB_INJECT_PKT_ERROR设置m_injectError标志位、并在下一次发包时破坏校验和数据实现(见本文第 5 节)。

需要说明的是,SDD 中"模块清单(Module Checklists)""单元测试(Unit Testing)"章节为空,说明该组件的验证目前主要依赖系统级链路测试与 Ref 部署的集成演示,而非独立的单元测试套件。

3. 组件模型:FPP 定义全解

组件的完整接口定义位于 SendBuffApp.fpp,其 FPP 模型将组件声明为queued component,并系统性地声明了端口、命令、事件、参数与遥测五个维度。

3.1 端口(Ports)

@ The rate group scheduler input sync input port SchedIn: Svc.Sched @ The data buffer output output port Data: Drv.DataBuffer
端口方向类型说明
SchedIn同步输入Svc.Sched速率组调度输入,每个调度周期触发一次发送检查
Data输出Drv.DataBuffer数据缓冲区输出端口,连接至BlockDriver.BufferIn

此外组件声明了 F´ 组件的标准特殊端口集合:命令接收(CmdDisp)、命令注册(CmdReg)、命令响应(CmdStatus)、事件(Log)、文本事件(LogText)、时间获取(Time)、遥测(Tlm)、参数获取/设置(ParamGet/ParamSet)。

3.2 命令(Commands)

组件共定义 4 条异步命令,覆盖正常启动、错误注入、故障演练三大场景:

async command SB_START_PKTS opcode 0 # 开始发送数据包 async command SB_INJECT_PKT_ERROR opcode 1 # 在下一个数据包中注入错误 async command SB_GEN_FATAL(arg1: U32, arg2: U32, arg3: U32) opcode 2 # 产生一条 FATAL 事件 async command SB_GEN_ASSERT(arg1..arg6: U32) opcode 3 # 触发一次断言(FW_ASSERT)

结合实例 Base ID0x10010000,四条命令的实际命令码分别为0x100100000x100100010x100100020x10010003,在 GDS 中下发时即使用这些完整命令码。

3.3 事件(Events)

| 事件 | 严重级别 | ID | 格式 | | ---- | -------- | -- | ---- | |FirstPacketSent| ACTIVITY_HI | 0 |First packet ID {} received| |PacketErrorInserted| WARNING_HI | 1 |Inserted error in packet ID {}| |BuffSendParameterUpdated| ACTIVITY_LO | 2 |BuffSend Parameter {} was updated| |SendBuffFatal| FATAL | 3 |Test Fatal: {} {} {}|

其中SendBuffFatal用于演示 F´ 的 FATAL 事件处理机制——当该事件发出时,部署中配置的 FatalHandler 会接管并执行故障响应(如断言失败、系统停机等)。

3.4 参数(Parameters)

param parameter3: U8 default 12 id 0 set opcode 10 save opcode 11 param parameter4: F32 default 13.14 id 1 set opcode 12 save opcode 13

两个参数用于演示 F´ 参数系统的 set/save 双命令机制:set opcode对应运行时修改参数值的命令,save opcode对应将当前值持久化到参数数据库的命令。

3.5 遥测(Telemetry)

| 通道 | 类型 | ID | 更新策略 | | ---- | ---- | -- | -------- | |PacketsSent| U64 | 0 | 每次发送递增 | |NumErrorsInjected| U32 | 1 | update on change(仅变化时上报) | |Parameter3| U8 | 2 | update on change | |Parameter4| F32 | 3 | update on change | |SendState|ActiveState| 4 | 每次调度上报 |

ActiveState枚举仅含两个状态:SEND_IDLE(空闲)与SEND_ACTIVE(发送中),用于 GDS 地面站观察组件当前是否处于发包状态。

4. 核心实现:调度驱动的发送流水线

组件实现类SendBuffImpl继承自自动生成的SendBuffComponentBase,核心状态量包括发送使能标志m_sendPackets、错误注入标志m_injectError、当前包 IDm_currPacketId、已发包数m_buffsSent与已注入错误数m_errorsInjected(见 SendBuffComponentImpl.hpp)。

4.1 调度入口:SchedIn_handler

SendBuffApp本身是 queued 组件,没有自己的线程,其处理完全由速率组通过SchedIn端口驱动。SchedIn_handler(SendBuffComponentImpl.cpp)的执行分两个阶段:

阶段一:先清空消息队列

MsgDispatchStatus stat = MSG_DISPATCH_OK; while (MSG_DISPATCH_OK == stat) { stat = this->doDispatch(); // 处理队列中的命令等消息 FW_ASSERT(stat != MSG_DISPATCH_ERROR); }

由于组件是 queued 类型,下发到它的命令先进入消息队列;调度回调第一件事就是doDispatch()把所有积压消息(如SB_START_PKTSSB_INJECT_PKT_ERROR)处理完毕,确保命令对状态标志的修改在本周期发包判断之前生效。

阶段二:按使能状态构造并发送数据包

if (this->m_sendPackets) { if (!this->m_firstPacketSent) { this->m_firstPacketSent = true; this->log_ACTIVITY_HI_FirstPacketSent(this->m_currPacketId); this->tlmWrite_NumErrorsInjected(this->m_errorsInjected); } this->m_testBuff.resetSer(); // 重置序列化指针 this->m_testBuff.serializeFrom(this->m_currPacketId); // 1) 写入包 ID this->m_currPacketId++; this->m_buffsSent++; this->tlmWrite_PacketsSent(this->m_buffsSent); // 2) 更新遥测 // 3) 填充 24 字节测试数据 U8 testData[24]; memset(testData, 0xFF, sizeof(testData)); // 4) 计算累加和校验 U32 csum = 0; for (U32 byte = 0; byte < sizeof(testData); byte++) { csum += testData[byte]; } // 5) 按需注入错误 if (this->m_injectError) { ... } // 6) 序列化数据与校验和并发送 this->m_testBuff.serializeFrom(testData, sizeof(testData)); this->m_testBuff.serializeFrom(csum); this->Data_out(0, this->m_testBuff); // 通过输出端口发出 } this->m_invocations++; this->tlmWrite_SendState(this->m_state);

由此可归纳出SendBuffApp 数据包的内存布局(这也是它与 RecvBuffApp 之间的隐式线协议):

偏移字段大小说明
0Packet ID4 字节(U32)单调递增的包序号
4Test Data24 字节初始全部为0xFF的测试数据
28Checksum4 字节(U32)24 字节数据的字节累加和

4.2 错误注入机制(ISF-RBF-002 的实现)

SB_INJECT_PKT_ERROR命令处理器只做一件事:把m_injectErrortrue并立即回OK响应。真正的破坏动作发生在下一个调度周期的发包流程中:

if (this->m_injectError) { this->m_injectError = false; // 一次性标志,用完即复位 this->m_errorsInjected++; testData[5] = 0; // 破坏第 6 字节(由 0xFF 改为 0x00) this->log_WARNING_HI_PacketErrorInserted(this->m_currPacketId - 1); }

由于校验和是在破坏数据之前计算的,破坏后发出的数据包其校验和必然与接收方重新计算的结果不符,从而保证错误一定能被对端检测到——这是演示链路错误检测功能的关键设计。该机制同时满足了需求 ISF-RBF-002 中"提供注入错误能力"的要求。

4.3 参数更新回调

parameterUpdated回调在参数被修改时触发,根据参数 ID 读取新值并同步到遥测通道,同时发送BuffSendParameterUpdated事件:

case PARAMID_PARAMETER3: { U8 val = this->paramGet_parameter3(valid); this->tlmWrite_Parameter3(val); break; } case PARAMID_PARAMETER4: { F32 val = this->paramGet_parameter4(valid); this->tlmWrite_Parameter4(val); break; } default: FW_ASSERT(0, id);

5. 与接收方 RecvBuffApp 的校验闭环

理解SendBuffApp离不开它的对端RecvBuffApp。接收组件在 RecvBuffApp.fpp 中定义了PacketStat结构体作为遥测载体,其Data_handler(RecvBuffComponentImpl.cpp)按发送方的序列化顺序逆向解析:先反序列化包 ID,再读取 24 字节数据与校验和,然后独立重算校验和并比对:

U32 sum = 0; for (U32 byte = 0; byte < size; byte++) { sum += testData[byte]; } if (sum != csum) { this->m_stats.set_BuffErr(++this->m_errBuffs); this->log_WARNING_HI_PacketChecksumError(id); this->m_stats.set_PacketStatus(PacketRecvStatus::PACKET_STATE_ERRORS); }

发送方的PacketErrorInserted与接收方的PacketChecksumError两条告警事件在 GDS 中成对出现,形成完整的"注入 → 传输 → 检出"可观测闭环。接收方还维护了PACKET_STATE_NO_PACKETSPACKET_STATE_OKPACKET_STATE_ERRORS三态统计,并通过PktState遥测上报接收数、错误数与状态。

6. 在 Ref 拓扑中的调度与连接

SendBuffApp的调度来源是速率组 2 的成员端口 1(topology.fpp):

rateGroup2Comp.RateGroupMemberOut[1] -> sendBuffComp.SchedIn

链路为:linuxTimer.CycleOutrateGroupDriverComp.CycleInrateGroup2Comp.CycleInsendBuffComp.SchedIn。因此SendBuffApp以速率组 2 的频率被周期驱动,每个周期检查一次m_sendPackets标志并决定是否发包。

与中间驱动BlockDriver(BlockDriver.fpp)的连接为:

sendBuffComp.Data -> blockDrv.BufferIn blockDrv.BufferOut -> recvBuffComp.Data

BlockDriver是一个 active 组件,其BufferIn为异步输入端口,缓冲区经其内部处理(模拟块设备读写)后从BufferOut转发给RecvBuffApp,构成了完整的"生成方 → 驱动 → 消费方"三段式数据流演示。

7. 构建与运行演示

组件以独立 F´ 模块的形式构建,CMakeLists.txt 展示了 F´ 模块注册的标准三要素:FPP 自动编码输入、实现源文件、头文件:

register_fprime_module( AUTOCODER_INPUTS "${CMAKE_CURRENT_LIST_DIR}/SendBuffApp.fpp" SOURCES "${CMAKE_CURRENT_LIST_DIR}/SendBuffComponentImpl.cpp" HEADERS "${CMAKE_CURRENT_LIST_DIR}/SendBuff.hpp" "${CMAKE_CURRENT_LIST_DIR}/SendBuffComponentImpl.hpp" )

其中SendBuff.hpp只是为兼容旧式命名(Ref::SendBuff)而做的类型别名封装:typedef SendBuffImpl SendBuff;

在 Ref 部署下运行演示(假设已按 docs/INSTALL.md 完成环境搭建)的典型流程为:

  1. TestDeploymentsProject/Ref目录下执行fprime-util build构建部署;
  2. 执行fprime-util run启动应用,并同时启动 GDS(fprime-gds)连接地面站;
  3. 在 GDS 命令面板下发命令sendBuffComp.SB_START_PKTS(命令码0x10010000),观察FirstPacketSent事件与PacketsSent遥测开始递增;
  4. 下发sendBuffComp.SB_INJECT_PKT_ERROR(命令码0x10010001),观察发送方产生PacketErrorInserted告警、接收方产生PacketChecksumError告警,NumErrorsInjected与接收方BuffErr计数同步 +1;
  5. 可进一步下发SB_GEN_FATAL(带 3 个 U32 参数)与SB_GEN_ASSERT(带 6 个 U32 参数)体验 FATAL 事件与断言触发的故障路径;
  6. 通过parameter3/parameter4的 set/save 命令(opcode 偏移 10/11、12/13)观察参数更新事件与遥测回读。

8. 小结

Ref::SendBuffApp虽名为"演示组件",却是理解 F´ 框架多项核心机制的理想样例:它展示了 queued 组件如何在速率组驱动下通过doDispatch()消费命令队列、Drv.DataBuffer的序列化/反序列化用法、"校验和 + 一次性错误注入标志"的故障注入设计模式,以及命令、事件、参数、遥测在单个组件内的完整声明与实现范式。配合 RecvBuffApp SDD 与 Ref 拓扑定义,开发者可以快速掌握在 F´ 中搭建"发送方—驱动—接收方"缓冲区链路并验证错误处理逻辑的标准方法。

【免费下载链接】fprimeF´ - A flight software and embedded systems framework项目地址: https://gitcode.com/GitHub_Trending/fpr/fprime

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

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

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

立即咨询