F´ ComFprime 通信子拓扑深度解析:F´ 帧协议上下行的轻量级通信栈实战指南
【免费下载链接】fprimeF´ - A flight software and embedded systems framework项目地址: https://gitcode.com/GitHub_Trending/fpr/fprime
导读
ComFprime 是 F´(F Prime)飞行软件与嵌入式系统框架内置的一组通信子拓扑(Subtopology),它把 F´ 轻量级通信栈中负责"组帧/解帧(framing/deframing)"的各个标准组件——Svc.FprimeFramer、Svc.FprimeDeframer、Svc.FprimeRouter、Svc.ComQueue、Svc.FrameAccumulator与Svc.BufferManager——预装配为两种可直接复用的拓扑变体,并统一通过ComFprimeConfig配置实例属性。本文基于 ComFprime SDD 并结合仓库源码,完整讲解两种变体的接线方式、全部配置项与底层实现细节;读完本文,你将能够在自己部署的 F´ 拓扑中正确复用 ComFprime,把 ByteStream 驱动(TCP/UDP/UART)或自定义通信接口接到 F´ 的帧协议上下行链路上。
1. 设计目标与需求概述
ComFprime 子拓扑聚焦于航天器侧的 F´ 帧协议通信栈,即把飞行软件内部产生的数据包(遥测、事件、文件)按 F Prime 格式组帧下行,并把外部传入的字节流解帧、路由为命令/文件上行。其需求来自 SDD 中的 SVC-COMFPRIME 系列:
| ID | 需求描述 | 验证方式 |
|---|---|---|
| SVC-COMFPRIME-001 | 提供 F´framer,将 COM 缓冲区转换为 F Prime 格式的帧化数据包下行 | Inspection |
| SVC-COMFPRIME-002 | 提供 F´deframer,将上行传入的数据包解析为 COM 缓冲区 | Inspection |
| SVC-COMFPRIME-003 | 提供 F´router,将解帧后的数据包(如命令/文件)路由进入飞行软件 | Inspection |
| SVC-COMFPRIME-004 | 提供内置Svc::ComStub的子拓扑变体,用于对接 ByteStream 驱动 | Inspection |
| SVC-COMFPRIME-005 | 提供期望部署方提供外部Svc::ComInterface的子拓扑变体 | Inspection |
| SVC-COMFPRIME-006 | 支持通过ComFprimeConfig配置实例属性(ID、队列深度、栈大小、优先级、CPU 亲和性) | Inspection |
范围界定:这些子拓扑只实现F´ 通信协议,不提供 CCSDS 组帧/解帧能力,也不包含 CDH(指令与数据管理)功能。需要 CCSDS 空间包/TC/TM 帧能力时,应选用同目录下的 ComCcsds 子拓扑(见 Svc/Subtopologies/ComCcsds/ComCcsds.fpp)。
2. 架构与核心组件
2.1 实例总览(公共构建块)
两种变体共享同一组核心组件,见下表(与 ComFprime.fpp 中的实例声明一一对应):
| 实例名 | 类型(Svc) | 种类 | 核心职责 |
|---|---|---|---|
framer | Svc.FprimeFramer | Passive | 将 F´ 数据组帧为可传输的数据包 |
deframer | Svc.FprimeDeframer | Passive | 从传入数据包中解出 F´ 数据 |
fprimeRouter | Svc.FprimeRouter | Passive | 将解帧数据(命令/文件等)路由进飞行软件 |
comQueue | Svc.ComQueue | Active | 为组帧排队数据(遥测、事件、文件等),带run调度口 |
commsBufferManager | Svc.BufferManager | Passive | 为子拓扑提供内存分配(缓冲池) |
frameAccumulator | Svc.FrameAccumulator | Passive | 解帧前按帧边界累积完整帧 |
comStub | Svc.ComStub | Passive | (仅变体 A)将Drv::ByteStreamDriverModel驱动适配为下行链路 |
各实例在 ComFprime.fpp 中的基地址偏移(以ComFprimeConfig.BASE_ID为基准)依次为:comQueue +0x00000、frameAccumulator +0x01000、commsBufferManager +0x02000、deframer +0x03000、framer +0x04000、fprimeRouter +0x05000、comStub +0x06000。基地址默认值为0x03000000(见 ComFprimeConfig.fpp),部署时应注意与拓扑中其他组件的地址空间不冲突。
2.2 两种变体
- 变体 A("带 ComStub"):子拓扑包含
Svc::ComStub,对外暴露 ByteStream 端口,可直接接到你的Drv::ByteStreamDriverModel(TCP/UDP/UART 等)驱动上。 - 变体 B("带外部 ComInterface"):子拓扑不包含
Svc::ComStub,需要部署方自行提供一个实现Svc.Com接口的组件(如Svc::ComStub、Svc::ComSplitter等),并将其与子拓扑的 framer/deframer 端口相连。
2.3 运行所需输入
根据 SDD 第 2.2 节,子拓扑正常工作需要三类接线:
- 速率组(Rate Groups):将调度器输出连接到
comQueue.run(Svc.ComQueue的调度口)。 - 传输端点(Transport Endpoint):
- 变体 A:在你的
Drv::ByteStreamDriverModel与子拓扑的ComStub之间接好ByteStream send/recv; - 变体 B:提供你自己的
Svc::ComInterface,并接到子拓扑的 framer/deframer 端口。
- 变体 A:在你的
- 飞行侧挂接(Flight-side hookups):将router输出(命令/文件)接入 CDH 栈(如命令分发器
Svc.CommandDispatcher、文件上行Svc.FileUplink),并将数据源(遥测/事件/文件下行)接入ComQueue。
3. 数据通路详解(从源码看数据流)
ComFprime.fpp 中的两个连接块清晰地刻画了数据流:
下行(Downlink)链路:
comQueue.dataOut -> framer.dataIn framer.dataReturnOut -> comQueue.dataReturnIn framer.bufferAllocate -> commsBufferManager.bufferGetCallee framer.bufferDeallocate -> commsBufferManager.bufferSendIn framer.comStatusOut -> comQueue.comStatusIn遥测/事件/文件数据先进入comQueue排队,comQueue出队后交给framer组帧;组帧所需的Fw::Buffer由commsBufferManager缓冲池分配/回收;链路状态(comStatus)回传给comQueue。
上行(Uplink)链路:
frameAccumulator.bufferDeallocate -> commsBufferManager.bufferSendIn frameAccumulator.bufferAllocate -> commsBufferManager.bufferGetCallee frameAccumulator.dataOut -> deframer.dataIn deframer.dataReturnOut -> frameAccumulator.dataReturnIn deframer.dataOut -> fprimeRouter.dataIn fprimeRouter.dataReturnOut -> deframer.dataReturnIn外部字节流先进frameAccumulator按 F´ 帧格式累积出完整帧,再交deframer解帧,解出的数据包由fprimeRouter路由到命令分发器或文件上行栈。
从Svc.ComQueue的接口定义(ComQueue.fpp)可以看到三个关键端口:comPacketQueueIn(异步输入,接收Fw.Com队列)、bufferQueueIn(异步输入,接收Fw.BufferSend)与run(调度口,Svc.Sched drop),其中run正是 SDD 所述需要速率组驱动的调度输入。
变体 A 的Subtopology通过import FramingSubtopology复用全部公共组件,仅额外增加comStub及三对端口连接(ComFprime.fpp):
- 下行:
framer.dataOut -> comStub.dataIn、comStub.dataReturnOut -> framer.dataReturnIn、comStub.comStatusOut -> framer.comStatusIn; - 上行:
comStub.dataOut -> frameAccumulator.dataIn、frameAccumulator.dataReturnOut -> comStub.dataReturnIn。
4. 使用方式:两种变体的接线模式
以下两个 FPP 接线示例取自 SDD 第 3 节,接线时请以 ComFprime.fpp 中的确切端口名为准(例如变体 A 的子拓扑端口实际为ComFprime.Subtopology.comQueueRun,而非ComFprime.Subtopology.comQueue.run)。
4.1 变体 A — 带Svc::ComStub(对接 ByteStream 驱动)
topology Flight { instance ComFprime.Subtopology # (A1) 提供 ByteStreamDriver 接口(例如 Drv.TcpClient) instance comDriver: ... # (A2) 调度 ComQueue connections RateGroups { rg.RateGroupMemberOut[0] -> ComFprime.Subtopology.comQueueRun } # (A3) 连接 ByteStream 驱动 <-> 子拓扑提供的 ComStub connections ComDriver { comDriver.$recv -> ComFprime.Subtopology.drvReceiveIn ComFprime.Subtopology.drvReceiveReturnOut -> comDriver.recvReturnIn ComFprime.Subtopology.drvSendOut -> comDriver.$send comDriver.ready -> ComFprime.Subtopology.drvConnected } }[!TIP] 若 ByteStream 驱动需要缓冲区管理,可使用
ComFprime.Subtopology.commsBufferGetCallee与ComFprime.Subtopology.commsBufferSendIn两个端口(分别对应commsBufferManager.bufferGetCallee/bufferSendIn,见 ComFprime.fpp)。
变体 A 对外暴露的完整端口清单(ComFprime.fpp)还包括:
- 命令路由:
commandOut(输出,路由后的命令包到命令分发器)、cmdResponseIn(输入,命令响应返回 router); - 文件上行:
fileUplinkOut(输出,上行文件包到文件处理栈)、fileUplinkReturnIn(输入,文件栈归还缓冲区所有权); - 排队输入(数组端口,按索引连接):
comPacketQueueIn(Fw::ComBuffer队列,如遥测/事件)、bufferQueueIn(Fw::Buffer队列,如文件下行)、bufferReturnOut(出队后归还Fw::Buffer所有权); - 驱动接口:
drvReceiveIn、drvReceiveReturnOut、drvSendOut、drvConnected(见上文 A3 接线); - 调度:
comQueueRun。
4.2 变体 B — 不带Svc::ComStub(外部提供 ComInterface)
topology Flight { import ComFprime.FramingSubtopology # (B1) 提供你自己的 ComInterface instance radio: ... # (B2) 调度 ComQueue connections RateGroups { rg.RateGroupMemberOut[0] -> ComFprime.comQueue.run } # (B3) 将你的 ComInterface 接到 ComFprime 的 framer/deframer connections Link { # Framer <-> ComInterface(下行) ComFprime.framer.dataOut -> radio.dataIn radio.dataReturnOut -> ComFprime.framer.dataReturnIn radio.comStatusOut -> ComFprime.framer.comStatusIn # ComInterface <-> FrameAccumulator(上行) radio.dataOut -> ComFprime.frameAccumulator.dataIn ComFprime.frameAccumulator.dataReturnOut -> radio.dataReturnIn } }[!NOTE] 在变体 B 中,
ComFprime.FramingSubtopology不定义comStub实例,且上行/下行与 ComInterface 的 5 个连接(2 个输出、3 个输入)必须由使用方在部署中自行建立,详见 ComFprime.fpp 顶部的用法注释。
5. 配置详解:ComFprimeConfig
SDD 明确要求只配置 ComFprime 子拓扑自身拥有的实例属性,所有配置旋钮统一位于:
Svc/Subtopologies/ComFprime/ComFprimeConfig/ComFprimeConfig.fpp5.1 组件属性(ComFprimeConfig.fpp)
下表汇总了 ComFprimeConfig.fpp 中的全部常量及其默认值,供实战参考:
| 配置项 | 所属模块 | 默认值 | 说明 |
|---|---|---|---|
BASE_ID | ComFprimeConfig | 0x03000000 | 子拓扑实例基地址,各组件在此基础上偏移 |
comQueue | QueueSizes | 50 | ComQueue消息队列深度 |
comQueue | StackSizes | 64 * 1024 | ComQueue任务栈大小 |
comQueue | Priorities | 29 | ComQueue任务的 RTOS 优先级 |
comQueue | CpuAffinities | Os.TASK_DEFAULT | CPU 亲和性,默认不绑定核心 |
events | QueueDepths | 100 | 事件子队列深度(最高优先级) |
tlm | QueueDepths | 500 | 遥测子队列深度 |
file | QueueDepths | 100 | 文件下行子队列深度 |
events | QueuePriorities | 0 | 事件子队列优先级(最高) |
tlm | QueuePriorities | 2 | 遥测子队列优先级 |
file | QueuePriorities | 1 | 文件子队列优先级 |
CpuAffinities默认值Os.TASK_DEFAULT表示不进行核绑定(无固定核心)。若部署硬件为多核且需要为通信任务分配专用核心,可在此改为具体核号。
5.2 ComQueue 排队配置(源码中的实际落地)
QueueDepths与QueuePriorities并不直接作为实例属性生效,而是通过ComQueue的configComponents阶段写入其QueueConfigurationTable(见 ComFprime.fpp):
Svc::ComQueue::QueueConfigurationTable configurationTable; // Events(最高优先级) configurationTable.entries[Ports_ComPacketQueue::EVENTS].depth = ComFprimeConfig::QueueDepths::events; configurationTable.entries[Ports_ComPacketQueue::EVENTS].priority = ComFprimeConfig::QueuePriorities::events; // Telemetry configurationTable.entries[Ports_ComPacketQueue::TELEMETRY].depth = ComFprimeConfig::QueueDepths::tlm; configurationTable.entries[Ports_ComPacketQueue::TELEMETRY].priority = ComFprimeConfig::QueuePriorities::tlm; // File Downlink Queue(buffer 类队列排在 packet 队列之后) configurationTable.entries[Ports_ComPacketQueue::NUM_CONSTANTS + Ports_ComBufferQueue::FILE].depth = ComFprimeConfig::QueueDepths::file; configurationTable.entries[Ports_ComPacketQueue::NUM_CONSTANTS + Ports_ComBufferQueue::FILE].priority = ComFprimeConfig::QueuePriorities::file; // 分配器标识为 0(MallocAllocator 会忽略该值) ComFprime::comQueue.configure(configurationTable, 0, ComFprime::Allocation::memAllocator);队列类型枚举定义于 ComFprime.fpp:Ports_ComPacketQueue含EVENTS、TELEMETRY(U8 枚举),Ports_ComBufferQueue含FILE。可见comQueue实际由三个子队列构成,events优先级 0 最高、tlm优先级 2 最低、file居中。注意:上表中的优先级是子队列内部相对优先级,与 5.1 中Priorities.comQueue = 29(RTOS 任务优先级)含义不同,二者并存、互不替代。
5.3 Buffer Manager 缓冲池配置(module BuffMgr)
module BuffMgr为commsBufferManager提供缓冲池(bins)常量(ComFprimeConfig.fpp):
| 常量 | 默认值 | 说明 |
|---|---|---|
frameAccumulatorSize | 2048 | frameAccumulator内部缓冲大小(字节) |
commsBuffSize | 2048 | bin[0] 普通通信缓冲大小(字节) |
commsFileBuffSize | 3000 | bin[1] 文件通信缓冲大小(字节) |
commsBuffCount | 20 | bin[0] 缓冲数量 |
commsFileBuffCount | 30 | bin[1] 缓冲数量 |
commsBuffMgrId | 200 | 缓冲池管理 ID |
这些常量在commsBufferManager的configComponents阶段写入Svc::BufferManager::BufferBins结构并调用setup(ComFprime.fpp):
memset(&ConfigObjects::ComFprime_commsBufferManager::bins, 0, sizeof(...bins)); ConfigObjects::ComFprime_commsBufferManager::bins.bins[0].bufferSize = ComFprimeConfig::BuffMgr::commsBuffSize; ConfigObjects::ComFprime_commsBufferManager::bins.bins[0].numBuffers = ComFprimeConfig::BuffMgr::commsBuffCount; ConfigObjects::ComFprime_commsBufferManager::bins.bins[1].bufferSize = ComFprimeConfig::BuffMgr::commsFileBuffSize; ConfigObjects::ComFprime_commsBufferManager::bins.bins[1].numBuffers = ComFprimeConfig::BuffMgr::commsFileBuffCount; ComFprime::commsBufferManager.setup(ComFprimeConfig::BuffMgr::commsBuffMgrId, 0, ComFprime::Allocation::memAllocator, bins);同理,frameAccumulator在configComponents阶段以FprimeFrameDetector作为帧检测器完成配置:
Svc::FrameDetectors::FprimeFrameDetector frameDetector; ComFprime::frameAccumulator.configure( ConfigObjects::ComFprime_frameAccumulator::frameDetector, 1, ComFprime::Allocation::memAllocator, ComFprimeConfig::BuffMgr::frameAccumulatorSize );5.4 分配器(Allocator)
整个子拓扑的内存分配统一走ComFprime::Allocation::memAllocator,其实现位于 ComFprimeSubtopologyConfig.cpp:
namespace ComFprime { namespace Allocation { // 该实例可在 ComFprime 子拓扑中替换为其他分配器 Fw::MallocAllocator mallocatorInstance; Fw::MemAllocator& memAllocator = mallocatorInstance; } // namespace Allocation } // namespace ComFprime默认使用Fw::MallocAllocator(堆分配)。对于安全关键/资源受限的嵌入式部署,可在 ComFprimeSubtopologyConfig.hpp 声明的ComFprime::Allocation命名空间中替换为静态内存池等自定义Fw::MemAllocator实现,无需改动子拓扑接线。
5.5 生命周期管理
comQueue、frameAccumulator、commsBufferManager三个带资源实例均注册了tearDownComponents阶段的清理逻辑(comQueue.cleanup()、frameAccumulator.cleanup()、commsBufferManager.cleanup(),见 ComFprime.fpp),框架会自动在拓扑拆除阶段释放其分配的资源,部署方无需额外处理。
5.6 辅助定义文件
子拓扑还附带两个轻量级辅助头文件,供部署的TopologyDefs.hpp/PingEntries.hpp复用:
- SubtopologyTopologyDefs.hpp:定义
ComFprime::SubtopologyState(当前为空结构,无需外部状态)与ComFprime::TopologyState; - PingEntries.hpp:定义
PingEntries命名空间(当前无支持 Ping 的组件,为空)。
6. 需求追溯矩阵
| 需求 ID | 满足者(实例/类型) |
|---|---|
| SVC-COMFPRIME-001 | framer—Svc.FprimeFramer |
| SVC-COMFPRIME-002 | deframer—Svc.FprimeDeframer |
| SVC-COMFPRIME-003 | router—Svc.FprimeRouter |
| SVC-COMFPRIME-004 | Subtopology |
| SVC-COMFPRIME-005 | FramingSubtopology |
| SVC-COMFPRIME-006 | ComFprimeConfig |
7. 部署自检清单
结合 SDD 与源码实现,接入 ComFprime 时建议逐项确认:
- 基地址唯一性:
ComFprimeConfig.BASE_ID(默认0x03000000)不与部署中其他组件地址空间重叠; - 速率组驱动:已把某个
RateGroupMemberOut接到comQueueRun(变体 A)/comQueue.run(变体 B),否则ComQueue不会输出排队的遥测/事件; - 传输端点完整接线:变体 A 的 4 根驱动线(
$recv、recvReturnIn、$send、ready)均已接好;变体 B 的 5 根 ComInterface 线(2 出 3 入)均已建立; - 飞行侧挂接:
commandOut/cmdResponseIn已连命令分发器,fileUplinkOut/fileUplinkReturnIn已连文件上行栈,遥测/事件源已连comPacketQueueIn,文件下行源已连bufferQueueIn; - 缓冲池容量匹配:
BuffMgr的缓冲大小/数量应满足最坏情况下的飞行遥测速率与文件传输并发需求,避免commsBufferManager分配失败导致丢帧。
通过以上配置,ComFprime 即可作为标准 F´ 通信栈的即插即用模块,与Drv.TcpClient/Drv.Udp/Drv.PosixUartDriver等 ByteStream 驱动协同工作。如需 CCSDS 协议栈,可参考 Svc/Subtopologies/ComCcsds 进行对比选型。
【免费下载链接】fprimeF´ - A flight software and embedded systems framework项目地址: https://gitcode.com/GitHub_Trending/fpr/fprime
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考