嵌入式系统编程【免费下载链接】fprimeF´ - A flight software and embedded systems framework项目地址https://gitcode.com/gh_mirrors/fp/fprime点击查看免费下载F´F Prime是 NASA 开源的一款飞行软件与嵌入式系统框架组件之间通过**端口Port**进行解耦通信。本文以 Fw/Port/docs/sdd.md 为骨架结合 Fw/Port 目录下的源码实现系统讲解Fw::Port模块中PortBase、InputPortBase、OutputPortBase以及序列化端口的类层次、核心成员、连接判定、调用追踪与对象注册机制。读完本文你将掌握 F´ 端口的底层设计原理能够读懂自动生成的端口代码并知道如何通过FpConfig.h中的编译开关裁剪或启用端口跟踪、序列化等功能。1. 模块定位与类体系总览Fw::Port模块为整个架构中的**输入端口input ports与输出端口output ports**提供基类。按照 sdd.md 的定义这是架构中所有端口的基础层。从仓库源码看该模块由 5 个.cpp实现文件与对应头文件构成见 Fw/Port/CMakeLists.txt类头文件 / 实现职责Fw::PortBasePortBase.hpp / PortBase.cpp所有端口的公共基类保存连接对象、提供连接判定与调用追踪Fw::InputPortBaseInputPortBase.hpp / InputPortBase.cpp输入端口基类记录所属组件与端口编号提供序列化调用入口Fw::OutputPortBaseOutputPortBase.hpp / OutputPortBase.cpp输出端口基类负责把端口调用转发给已连接的输入端口Fw::InputSerializePortInputSerializePort.hpp / InputSerializePort.cpp仅透传序列化数据的输入端口Fw::OutputSerializePortOutputSerializePort.hpp / OutputSerializePort.cpp仅透传序列化数据的输出端口类继承关系如下PortBase继承自Fw::ObjBase后者位于 Fw/Obj/ObjBase.hppFw::ObjBase └── Fw::PortBase ├── Fw::InputPortBase │ └── Fw::InputSerializePort └── Fw::OutputPortBase └── Fw::OutputSerializePort从模块依赖Fw/Port/CMakeLists.txt 中的MOD_DEPS可以看到端口模块依赖Fw/Cfg配置、Fw/Types序列化与基础类型、Fw/Logger日志输出与Fw/Obj对象基类与注册表这正好对应了端口运行所需的配置宏、序列化缓冲、追踪日志打印和对象注册能力。2. Fw::PortBase一切端口的公共基类sdd.md 指出Fw::PortBase是 ISFIntegrated Software Framework类层次中的端口基类是所有端口的基类。源码中它直接继承Fw::ObjBase其核心设计围绕**“端口连接到某个对象”**这一模型展开。2.1 连接对象的保存与isConnected()判定// 摘自 Fw/Port/PortBase.hpp节选 class PortBase : public Fw::ObjBase { public: bool isConnected(); protected: Fw::ObjBase* m_connObj; // ! object port is connected to ... };m_connObj保存了端口所连接的对象指针通常是另一端组件的输入端口或组件本身。isConnected()的实现非常直接PortBase.cppbool PortBase::isConnected() { return m_connObj nullptr ? false : true; }也就是说只要m_connObj非空端口即视为已连接。这个判定是 F´ 运行时检查端口连接状态的标准手段——在调用输出端口前生成代码通常会先通过isConnected()判断是否需要实际调用避免对未连接端口发出空调用。2.2 端口调用追踪FW_PORT_TRACINGPortBase内置了一套可裁剪的**调用追踪tracing**机制由FW_PORT_TRACING宏控制默认开启见下文第 5 节。追踪逻辑位于 PortBase.cpp全局开关静态成员s_trace可通过PortBase::setTrace(bool)全局开启或关闭模块还导出一个 C 接口setConnTrace(bool trace)供外部调用见 PortBase.cpp。端口级覆盖ovrTrace(bool ovr, bool trace)允许对单个端口覆盖全局设置——当ovr为真时该端口仅依据自己的m_trace标志决定是否输出追踪信息否则跟随全局s_trace。追踪输出trace()方法在满足条件时通过Fw::Logger::logMsg打印。若启用了FW_OBJECT_NAMES输出格式为Trace: 对象名否则退化为打印端口地址Trace: %p。这套机制对调试组件间调用链非常实用当怀疑某条端口链路未按预期触发时可在运行时开启全局追踪观察每条端口调用的实际路径。2.3 字符串化输出在FW_OBJECT_NAMES与FW_OBJECT_TO_STRING均开启时PortBase::toString()会把端口描述为Port: 名称 C/NC-(连接对象名)其中C表示已连接Connected、NC表示未连接Not Connected见 PortBase.cpp。派生类各自重写了该实现用于调试日志与诊断输出。3. 输入端口与输出端口的分工F´ 端口通信模型中调用总是从输出端口发出由输入端口接收并转交所属组件。两个方向的基类因此承担不同职责。3.1 Fw::InputPortBase记录“端口属于谁”// 摘自 Fw/Port/InputPortBase.hpp节选 class InputPortBase : public PortBase { public: void setPortNum(NATIVE_INT_TYPE portNum); #if FW_PORT_SERIALIZATION virtual SerializeStatus invokeSerial(SerializeBufferBase buffer) 0; #endif protected: PassiveComponentBase* m_comp; // ! pointer to containing component NATIVE_INT_TYPE m_portNum; // ! port number in containing object };输入端口基类新增两个关键成员m_comp指向包含该端口的组件Fw::PassiveComponentBase见 Fw/Comp/PassiveComponentBase.hpp。当端口被调用时最终会回调到这个组件的处理函数。m_portNum该端口在所属组件内的端口编号默认初始化为-1见 InputPortBase.cpp通过setPortNum()设置。实现中使用FW_ASSERT(portNum 0)防止非法编号InputPortBase.cpp。当FW_PORT_SERIALIZATION开启时invokeSerial()被声明为纯虚函数要求每个具体输入端口实现“接收一个已序列化的调用缓冲”的能力——这是序列化端口调用的统一入口。3.2 Fw::OutputPortBase转发调用并支持序列化注册// 摘自 Fw/Port/OutputPortBase.hpp节选 class OutputPortBase : public PortBase { public: #if FW_PORT_SERIALIZATION 1 void registerSerialPort(InputPortBase* port); SerializeStatus invokeSerial(SerializeBufferBase buffer); #endif protected: #if FW_PORT_SERIALIZATION 1 Fw::InputPortBase* m_serPort; // ! pointer to port for serialized calls #endif };输出端口基类在FW_PORT_SERIALIZATION开启时提供了两条核心能力OutputPortBase.cppregisterSerialPort(InputPortBase* port)把输出端口注册到一个输入端口上用于后续的序列化调用。实现同时设置m_connObj与m_serPort即“序列化注册”本身也构成一次连接。invokeSerial(SerializeBufferBase buffer)把序列化调用转发给m_serPort对应的输入端口由输入端口侧的invokeSerial()真正执行回调。这两者的组合正是 F´ 支持“跨节点/跨进程序列化端口调用”即把一次端口调用打包成字节流发送到远端再还原执行的底层基础。4. 序列化端口串口透传与回调注册普通端口通过 C 函数指针直接调用而序列化端口SerializePort的目标是把调用数据打包进SerializeBufferBase后透传。sdd.md 未展开这部分但从源码可以清楚看到其设计意图——注释明确写道“SerializePort has no call interface. It is to pass through serialized data”序列化端口没有直接调用接口它用于透传序列化数据。4.1 Fw::InputSerializePort注册组件与回调// 摘自 Fw/Port/InputSerializePort.hpp节选 class InputSerializePort : public InputPortBase { public: SerializeStatus invokeSerial(SerializeBufferBase buffer); typedef void (*CompFuncPtr)(Fw::PassiveComponentBase* callComp, NATIVE_INT_TYPE portNum, SerializeBufferBase arg); void addCallComp(Fw::PassiveComponentBase* callComp, CompFuncPtr funcPtr); };InputSerializePort通过addCallComp()把“所属组件”和“回调函数指针”绑定在一起InputSerializePort.cpp。其invokeSerial()实现InputSerializePort.cpp有两点值得注意先FW_ASSERT(m_comp)与FW_ASSERT(m_func)确保组件与回调都已注册回调执行后直接返回FW_SERIALIZE_OK因为输入序列化端口不对缓冲做反序列化——注释说明普通输入端口会基于反序列化结果返回状态而序列化端口只是透传因此固定返回成功。4.2 Fw::OutputSerializePort纯透传OutputSerializePort是OutputPortBase的最小派生实现OutputSerializePort.cpp本身不新增接口仅完成init()与可选的toString()作为序列化输出链路的占位端点存在。序列化端口在 F´ 中主要用于需要把端口调用“编码后转发”的场景例如通过串口、网络把命令或遥测请求发送到另一个处理器接收端用InputSerializePort还原调用。5. 编译配置宏如何裁剪与开启端口能力sdd.md 明确指出对象注册特性由FW_OBJECT_REGISTRATION宏控制文档称位于FpConfig.hpp。在当前仓库中端口与对象相关的全部编译开关统一收敛在配置头文件 config/FpConfig.hFpConfig.hpp会引用它以下是端口模块直接相关的宏默认值与含义均来自 config/FpConfig.h宏默认值作用FW_OBJECT_NAMES1是否为每个对象实例分配并保存对象名增加内存但便于对象追踪与调试关闭时FW_OPTIONAL_NAME(name)会替换为空串FW_OBJECT_TO_STRING依赖FW_OBJECT_NAMES开启时默认1是否为生成对象提供toString()方法导出内部状态其生效前提是FW_OBJECT_NAMES 1否则强制为0FW_OBJECT_REGISTRATION1是否允许组件相关对象集中注册增加代码量与对象追踪能力对应第 6 节的对象注册机制FW_PORT_TRACING1是否启用端口调用追踪代码量更大但对执行过程有更高可见性对应PortBase::setTrace/ovrTrace/traceFW_PORT_SERIALIZATION1是否为端口生成序列化调用连接代码代码量更大但支持多节点系统下的调用序列化对应registerSerialPort/invokeSerial/addCallComp裁剪建议在资源受限的嵌入式目标上可将FW_PORT_TRACING或FW_PORT_SERIALIZATION置为0来减小二进制体积但需要确认部署中没有依赖序列化端口调用的链路否则对应代码会被整体裁剪掉。6. 对象注册机制Fw::ObjRegistry 概念sdd.md 第 2.2 节介绍了Fw::ObjRegistry它是对象注册表的虚基类该功能是可选特性由FW_OBJECT_REGISTRATION宏开关控制。其核心思想是注册表提供一种方式跟踪系统中已实例化的所有对象注册表可以查询Fw::Object的任何公开函数来获取实例信息而具体如何存储对象指针、如何产出实例数据则由派生类决定。在仓库源码中这一设计体现在两个层面基类侧Fw::ObjBaseFw/Obj/ObjBase.hpp内部有多处#if FW_OBJECT_REGISTRATION 1条件编译块第 23、68、109、114、140 行附近即对象注册的钩子逻辑随该宏开启而编译。实现侧Fw/Obj目录提供了具体注册表实现Fw/Obj/SimpleObjRegistry.hpp / SimpleObjRegistry.cpp它把“虚基类如何落地”具体化为一个简单对象注册表。因此开发者既可以直接使用SimpleObjRegistry也可以按 sdd.md 描述的模式派生自己的注册表覆盖对象指针的存储与实例数据产出方式实现自定义的对象追踪、资源审计或调试转储。7. 构建与模块依赖Fw::Port作为一个独立的 F´ 模块通过 Fw/Port/CMakeLists.txt 接入构建系统SOURCE_FILES列出 5 个源文件InputPortBase.cpp、InputSerializePort.cpp、OutputPortBase.cpp、OutputSerializePort.cpp、PortBase.cppMOD_DEPS声明依赖Fw/Cfg、Fw/Types、Fw/Logger、Fw/Obj最后调用register_fprime_module()注册模块。任何使用端口的组件模块都隐式经由自动生成的端口代码或显式地依赖本模块。这解释了为什么端口代码可以自由使用Fw::SerializeStatus、Fw::Logger::logMsg、Fw::ObjBase等能力——它们正是来自上述四个依赖模块。8. 使用与调试指引综合 sdd.md 与源码实现使用Fw::Port体系时的关键经验如下连接检查先行在调用输出端口前使用PortBase::isConnected()PortBase.hpp判断链路是否建立未连接时跳过调用避免无效调用与潜在的空指针访问。追踪端口调用编译期保证FW_PORT_TRACING 1默认开启运行时调用setConnTrace(true)或PortBase::setTrace(true)打开全局追踪需要聚焦单条链路时用ovrTrace(true, true)对该端口强制开启、ovrTrace(true, false)强制关闭接口见 PortBase.hpp。检查连接对象名启用FW_OBJECT_NAMES与FW_OBJECT_TO_STRING后可通过toString()输出的Port: name C-(obj)/NC标记快速确认端口连接目标PortBase.cpp。序列化链路需要跨节点转发端口调用时输出侧使用OutputPortBase::registerSerialPort()注册输入侧实现/复用invokeSerial()完成透传InputSerializePort通过addCallComp()绑定组件与回调InputSerializePort.hpp。对象跟踪开启FW_OBJECT_REGISTRATION默认开启后系统可借助Fw::Obj目录下的SimpleObjRegistry汇总所有实例化对象便于资源统计与泄漏排查。9. 变更记录依据 sdd.md 末尾的 Change Log本文档于 2016-04-24 建立初始版本当前仓库中该文档仍保留此单一历史记录其描述的类体系与宏开关在现有源码Fw/Port、config/FpConfig.h中均得到了一致实现。赞分享嵌入式系统编程【免费下载链接】fprimeF´ - A flight software and embedded systems framework项目地址https://gitcode.com/gh_mirrors/fp/fprime点击查看免费下载相关推荐F´ (F Prime) 事件日志端口深度解析Fw::Log 与 Fw::LogText 端口的设计、序列化与使用指南F´ F Prime 事件日志端口深度解析Fw::Log 与 Fw::LogText 端口的设计、序列化与使用指南 导读 本文聚焦 F´F Prime飞行嵌入式系统编程F´ 通信端口 Fw::Com 深度解析飞行软件中的二进制数据包传输与序列化机制F´ 通信端口 Fw::Com 深度解析飞行软件中的二进制数据包传输与序列化机制 Fw::Com 是 F´F Prime飞行软件与嵌入式系统框架中用于 传嵌入式系统编程F´ 命令体系详解Fw::Cmd / Fw::CmdResponse / Fw::CmdReg 端口设计、序列化与配置指南F´ 命令体系详解Fw::Cmd / Fw::CmdResponse / Fw::CmdReg 端口设计、序列化与配置指南 本篇技术指南以 F´F Prim嵌入式系统编程上一篇FakeStoreAPI 高级用法查询参数、排序限制和日期范围过滤下一篇Posio性能优化提升WebSocket连接效率的5个关键技术创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
