F´ Svc::ComQueue 通信队列组件深度解析:优先级缓冲队列、状态机与通信适配器协议实现

发布时间:2026/9/24 14:54:22
F´ Svc::ComQueue 通信队列组件深度解析:优先级缓冲队列、状态机与通信适配器协议实现 F´ Svc::ComQueue 通信队列组件深度解析优先级缓冲队列、状态机与通信适配器协议实现【免费下载链接】fprimeF´ - A flight software and embedded systems framework项目地址: https://gitcode.com/gh_mirrors/fp/fprimeSvc::ComQueue是 F´F Prime飞行软件与嵌入式系统框架中的一个 active 组件它以优先级缓冲队列的形式缓存Fw::Com与Fw::Buffer两类消息并依据通信适配器communication adapter协议在收到Fw::Success::SUCCESS信号时按优先级顺序出队转发。本篇文章以 Svc/ComQueue/docs/sdd.md 为骨架结合 ComQueue.cpp、ComQueue.hpp、ComQueue.fpp 等仓库源码完整讲解其设计意图、状态机、配置方式、端口行为、遥测/事件定义以及单元测试验证方法帮助开发者理解如何在部署中接入并使用该组件。1. 组件概述与设计动机Svc::ComQueue是一个 F´ active 组件ComQueue.fpp 第 13 行active component ComQueue其核心功能是充当多种缓冲类型的优先级队列消息入队后只有在收到Fw::Success::SUCCESS信号时才会被取出并向下游转发转发顺序遵循优先级收到Fw::Success::FAILURE信号时队列将暂停发送直到收到下一个SUCCESS信号为止每个传入的Fw::Com与Fw::Buffer端口在初始化时通过传入的配置表configuration table分别配置队列深度depth与队列优先级priority最高优先级来源端口的消息最先被服务共享同一优先级的多个端口之间采用轮询round-robin算法平衡调度。从源码看ComQueue的设计定位是与通信适配器接口实例协同工作并实现其中定义的通信队列协议communication queue protocol。它解耦了消息生产方与通信链路使得上游组件如 Framer、FileUplink 等产生下行消息的组件不必关心底层通信链路的忙闲状态只需向队列投递消息再由通信适配器反馈发送结果。2. 设计假设Assumptions该组件的正确工作依赖以下前提来自 Svc/ComQueue/docs/sdd.md 第 2 节到达某一指定端口的一组缓冲buffers本身是按优先级顺序排列的收到Fw::Success::SUCCESS信号即代表数据已被成功发送通信适配器com adapter负责失败数据的任何重传系统中包含实现了通信适配器接口的下游组件。这意味着ComQueue自身不做重传、不感知底层链路细节它只负责缓存与在许可信号到来时按序吐出一条消息这一件事链路可靠性与重传策略被明确委派给通信适配器。3. 需求规格Requirementssdd.md 第 3 节列出了组件需求SVC-COMQUEUE-001 至 009这些需求同时也是单元测试的验证依据需求编号描述理由验证方式SVC-COMQUEUE-001应在入端口上缓存收到的Fw::Buffer与Fw::ComBuffer队列存在的目的是存储消息单元测试SVC-COMQUEUE-002收到Fw::Success::SUCCESS信号时应恰好输出一条Fw::Buffer或Fw::ComBuffer消息遵循通信适配器接口协议单元测试SVC-COMQUEUE-003收到Fw::Success::FAILURE应暂停发送并在下一个SUCCESS信号到来时恢复不应向失败的通信适配器发送数据单元测试SVC-COMQUEUE-004应支持可配置数量的Fw::Com与Fw::Buffer输入端口使组件可适配不同项目检查InspectionSVC-COMQUEUE-005应响应Fw::Success::SUCCESS选择并发送下一条最高优先级的Fw::Buffer/Fw::ComBuffer消息遵循通信适配器接口协议单元测试SVC-COMQUEUE-006应在run端口调用时周期性遥测上报每个端口的排队消息数提供有用的遥测信息单元测试SVC-COMQUEUE-007当配置深度被超过时应为对应端口发出队列溢出事件并丢弃消息需要指示非正常事件单元测试SVC-COMQUEUE-008应对相同优先级端口实现轮询平衡策略允许项目在相近优先级队列间平衡负载单元测试SVC-COMQUEUE-009应跟踪并抑制throttle每个端口的队列溢出事件防止队列溢出事件洪泛单元测试4. 组件设计Design4.1 端口PortsComQueue的端口在 ComQueue.fpp 中定义汇总如下类型名称端口类型用途outputcomQueueSendFw.ComFw::ComBuffer输出端口outputbuffQueueSendFw.BufferSendFw::Buffer输出端口async inputcomStatusInFw.SuccessCondition接收状态信号async inputcomQueueIn[ComQueueComPorts] Fw.Com接收Fw::ComBuffer的端口数组async inputbuffQueueIn[ComQueueBufferPorts] Fw.BufferSend接收Fw::Buffer的端口数组async inputrunSvc.Sched调度遥测输出eventLogFw.Log事件输出text eventLogTextFw.LogText文本事件输出time getTimeFw.Time获取时间telemetryTlmFw.Tlm遥测输出值得注意的实现细节见 ComQueue.cpp 第 131-143 行comQueueIn_handler收到Fw::ComBuffer后直接以端口号作为队列号调用enqueuebuffQueueIn_handler则将端口号加上COM_PORT_COUNT作为队列号queueNum portNum COM_PORT_COUNT即**Fw::Com队列与Fw::Buffer队列在内部是连续编号的前COM_PORT_COUNT个队列存放Fw::ComBuffer后续队列存放Fw::Buffer**两个输入端口数组在 FPP 中均声明为drop超出的消息直接丢弃。4.2 内部状态StateComQueue维护以下状态ComQueue.hpp 第 67-90 行、ComQueue.cpp 第 182-190 行m_queuesTypes::Queue数组存放每个端口的消息底层是 Utils/Types/Queue.hpp 实现的定长消息 FIFO 环形队列m_prioritizedListSvc::ComQueue::QueueMetadata实例保存按优先级排序的队列元数据depth、priority、index、msgSizem_stateSvc::ComQueue::SendState实例表示组件当前状态见 4.2.1 状态机m_throttle布尔数组用于抑制每个端口的队列溢出事件详见 4.7 事件与需求 SVC-COMQUEUE-009。QueueMetadata结构中index指向底层未排序的队列对象下标msgSize在configure时按队列类型计算下标小于COM_PORT_COUNT的队列消息大小为sizeof(Fw::ComBuffer)否则为sizeof(Fw::Buffer)ComQueue.cpp 第 88 行。4.2.1 状态机State Machine组件运行一个简单的两状态状态机初始状态为WAITING状态描述WAITING正在等待SUCCESS信号之后再尝试发送可用缓冲READY当前没有排队的缓冲收到新缓冲时将立即发送状态转换规则如下结合 ComQueue.cpp 第 145-164 行comStatusIn_handler实现收到SUCCESS且当前为WAITING状态切换为READY并调用processQueue()若队列中有消息则发送一条并回到WAITING若无消息则保持在READY收到FAILURE保持/回到WAITING状态不发送任何消息在READY状态下收到新缓冲enqueue后会立即调用processQueue()将消息发送出去ComQueue.cpp 第 204-206 行处于WAITING状态时收到的消息只入队、不发送除WAITING外的其他状态收到状态信号会触发FW_ASSERT(0, this-m_state)即正常运行时不可能出现设计上保证收到状态信号时组件必然处于WAITING。4.3 模型配置常量Model ConfigurationComQueue依赖两个在 config/AcConstants.fpp 中定义的常量第 33-37 行 Used for number of Fw::Com type ports supported by Svc::ComQueue constant ComQueueComPorts 2 Used for number of Fw::Buffer type ports supported by Svc::ComQueue constant ComQueueBufferPorts 1ComQueueComPortscomQueueIn端口数组中Fw.Com类型端口数量ComQueueBufferPortsbuffQueueIn端口数组中Fw.BufferSend类型端口数量。这两个常量同时被 ComQueue.fpp 中的端口数组定义与两个遥测数组类型ComQueueDepth [ComQueueComPorts] U32、BuffQueueDepth [ComQueueBufferPorts] U32引用是组件接口规模与内部队列数量的最终决定因素。默认配置为 2 个Fw::Com端口 1 个Fw::Buffer端口。4.4 运行时配置Runtime Setup使用ComQueue需要完成两步第一步构造与初始化。与其他 F´ active 组件一样调用构造函数ComQueue(const char* const compName)与init(const NATIVE_INT_TYPE queueDepth, const NATIVE_INT_TYPE instance 0)。构造函数会将m_state初始化为WAITING、将所有节流标志m_throttle[i]置为falseComQueue.cpp 第 24-34 行。第二步调用configure方法传入三个参数void configure(QueueConfigurationTable queueConfig, NATIVE_UINT_TYPE allocationId, Fw::MemAllocator allocator);其中QueueConfigurationTable包含TOTAL_PORT_COUNT即COM_PORT_COUNT BUFFER_PORT_COUNT个QueueConfigurationEntry每个条目由depth队列深度非负整数0 表示禁用该队列与priority优先级[0, TOTAL_PORT_COUNT)区间的整数数值越小越先被服务允许重复组成ComQueue.hpp 第 34-65 行。sdd.md指出configure会完成以下校验与初始化源码 ComQueue.cpp 第 49-126 行给出了精确实现保存分配器相关成员m_allocator、m_allocationId校验优先级合法性对每个配置条目断言priority TOTAL_PORT_COUNT否则FW_ASSERT触发第 73-76 行按优先级构建排序后的元数据列表外层循环从0到TOTAL_PORT_COUNT递增扫描优先级值内层遍历整个配置表将匹配当前优先级的条目按序写入m_prioritizedList同时计算每个队列的msgSize并累加totalAllocation Σ(depth × msgSize)第 68-93 行分配单一内存块以recoverable false调用m_allocator-allocate(allocationId, totalAllocation, recoverable)一次性申请全部队列存储第 94-96 行为每个队列划分子内存并调用setup按优先级顺序遍历元数据列表将分配块按depth × msgSize逐段划分给对应m_queues[entry.index].setup(...)深度为 0 的队列跳过 setup第 101-120 行内存一致性校验最终断言allocationOffset totalAllocation确保整块内存被精确使用完毕第 122-125 行。此外还提供cleanup()方法在m_allocator与m_allocation均非空时调用deallocate释放内存第 42-47 行。4.5 端口处理函数Port Handlers4.5.1 buffQueueInbuffQueueIn_handler接收一个Fw::Buffer和端口号ComQueue.cpp 第 137-143 行断言端口号位于[0, BUFFER_PORT_COUNT)区间计算队列号queueNum portNum COM_PORT_COUNT并断言其小于TOTAL_PORT_COUNT将缓冲入队到m_queues[queueNum]若队列已满返回警告触发溢出事件。若组件正处于READY状态缓冲入队后会立即调用processQueue()处理该队列这对应需求 SVC-COMQUEUE-002 的立即发送语义。4.5.2 comQueueIncomQueueIn_handler接收一个Fw::ComBuffer和端口号第 131-135 行逻辑与buffQueueIn对称断言端口号位于[0, COM_PORT_COUNT)区间以端口号直接作为队列号入队到m_queues[portNum]队列满时返回警告。同样地READY状态下入队后会立即处理。4.5.3 comStatusIncomStatusIn_handler接收Fw::Success状态第 145-164 行触发状态机转换完整语义见 4.2.1 状态机。其核心逻辑是WAITING SUCCESS→READY并processQueue()WAITING FAILURE→ 保持WAITING其他组合 → 断言失败。4.5.4 runrun_handler用于调度遥测输出第 166-182 行读取每个Fw::ComBuffer队列的**高水位标记high-water mark**并通过tlmWrite_comQueueDepth遥测上报读取每个Fw::Buffer队列的高水位并通过tlmWrite_buffQueueDepth上报每次上报后调用clear_high_water_mark()清零因此遥测值代表自上次run调用以来的峰值队列深度。高水位能力来自底层 Utils/Types/Queue.hpp 的get_high_water_mark()/clear_high_water_mark()接口该队列封装了CircularBuffer实现定长消息的 FIFO 存储。4.6 遥测Telemetry遥测在 ComQueue.fpp 中定义id 0、1汇总如下名称类型描述comQueueDepthSvc.ComQueueDepth[ComQueueComPorts] U32存放Fw::ComBuffer的队列高水位深度buffQueueDepthSvc.BuffQueueDepth[ComQueueBufferPorts] U32存放Fw::Buffer的队列高水位深度4.7 事件Events名称描述QueueOverflowWARNING_HI事件当队列无法容纳新入队消息时触发FPP 定义如下ComQueue.fpp 第 57-63 行event QueueOverflow( queueType: QueueType The Queue data type index: U32 index of overflowed queue ) \ severity warning high \ format The {} queue at index {} overflowed实现要点需求 SVC-COMQUEUE-009 的体现ComQueue.cpp 第 188-207 行enqueue函数入队返回FW_SERIALIZE_NO_ROOM_LEFT且对应节流标志m_throttle[queueNum]为false时才发出QueueOverflow事件并将节流标志置true当该队列后续成功发送一条消息processQueue中成功出队后会复位节流标志第 251 行允许再次上报——即每个端口在相邻两次溢出之间只上报一次事件防止事件洪泛深度为 0 的队列被禁用的队列收到任何消息都会溢出FPP 中端口声明为drop也提供了消息丢弃语义的兜底。4.8 辅助函数Helper Functions4.8.1 sendComBuffer保存并发送Fw::ComBuffer消息到comQueueSend输出端口随后将发送状态置为WAITINGComQueue.cpp 第 209-213 行。调用前断言当前状态为READY发送调用为comQueueSend_out(0, comBuffer, 0)。4.8.2 sendBuffer保存并发送Fw::Buffer消息到buffQueueSend输出端口随后将状态置为WAITING第 215-220 行。注释特别说明预期缓冲已被清理因为组件要么转移了所有权、要么已释放该缓冲。4.8.3 processQueueprocessQueue是优先级选择与轮询平衡的核心算法第 222-269 行在一个受队列总数约束的有界循环中完成断言当前状态为READY按优先级顺序遍历m_prioritizedList跳过getQueueSize() 0的空队列找到第一条可用消息若entry.index COM_PORT_COUNT出队并调用sendComBuffer否则出队并调用sendBuffer复位刚发送队列的节流标志记录本次发送的优先级sendPriority然后跳出循环轮询旋转从刚发送的条目之后开始到同优先级条目段的末尾为止逐个与前一条目交换把刚被服务的队列旋转到该优先级段的末尾其余同优先级队列依次前移——这实现了同优先级队列间的 round-robin 平衡需求 SVC-COMQUEUE-008。单元测试 Svc/ComQueue/test/ut/ComQueueTester.cpp 对上述行为做了完整覆盖testQueueSend逐端口入队并通过emitOneAndCheck发送一次SUCCESS验证每条消息按序输出testQueuePause连续发送多个FAILURE状态后验证队列暂停发送随后的SUCCESS才放行一条消息需求 SVC-COMQUEUE-003testPrioritySend为各端口配置不同优先级priority TOTAL_PORT_COUNT - i - 1并令两个端口共享优先级 1验证消息按优先级顺序输出且共享优先级队列被轮询平衡需求 SVC-COMQUEUE-005/008testQueueOverflow将队列深度配置为 2 并故意多塞 2 条消息验证溢出事件恰好上报 1 次、节流生效、出队一条后节流复位可再次上报并通过invoke_to_run验证comQueueDepth/buffQueueDepth遥测与期望深度一致需求 SVC-COMQUEUE-006/007/009testReadyFirst先发SUCCESS进入READY后入队验证消息被立即转发对应状态机READY分支。5. 典型集成与使用流程在部署中接入ComQueue的典型流程如下调整配置常量按项目需求修改 config/AcConstants.fpp 中的ComQueueComPorts与ComQueueBufferPorts重新生成组件代码后端口数组规模随之改变构造与初始化在部署拓扑中实例化组件并调用init(queueDepth, instance)填充并传入配置表按先Fw::Com端口、后Fw::Buffer端口的顺序填充QueueConfigurationTable每个条目指定 depth 与 priority连同allocationId与一个Fw::MemAllocator如Fw::MallocAllocator测试代码即采用该分配器一起传给configure连接端口将消息生产方连接到comQueueIn/buffQueueIn将comQueueSend/buffQueueSend连接到通信适配器将适配器的发送结果反馈连接到comStatusIn将run连接到周期调度rate group以定时上报队列水位遥测生命周期管理组件退出时调用cleanup()释放通过分配器申请的内存。6. 小结Svc::ComQueue以极小的状态机WAITING/READY实现了按优先级缓存、按许可信号逐条转发的通信队列协议SUCCESS驱动发送、FAILURE暂停、同优先级轮询平衡、溢出事件节流抑制并通过高水位遥测暴露队列负载。其设计将队列深度、端口数量、优先级均配置化ComQueue.hpp 的QueueConfigurationTable与 config/AcConstants.fpp 的端口常量既满足通信适配器接口的协议要求又保持了多项目间的可适配性。配合 Svc/ComQueue/test/ut/ComQueueTester.cpp 中的五个核心测试用例开发者可以在任意 F´ 部署中安全地复用它来解耦消息生产与链路发送之间的时序约束。【免费下载链接】fprimeF´ - A flight software and embedded systems framework项目地址: https://gitcode.com/gh_mirrors/fp/fprime创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考