
F´ 框架的 Fw::Obj 模块深度解析对象基类与对象注册机制【免费下载链接】fprimeF´ - A flight software and embedded systems framework项目地址: https://gitcode.com/GitHub_Trending/fpr/fprime导读Fw::Obj是 F´F Prime飞行软件与嵌入式系统框架中定义对象体系根基的核心模块。它通过Fw::ObjBase为框架内所有组件Component与端口Port提供统一的基类身份并通过可选的Fw::ObjRegistry对象注册机制在系统启动时集中登记所有已实例化对象方便开发者查询实例数量、名称与运行期信息。读完本文你将掌握Fw::ObjBase的类层次定位、FW_OBJECT_NAMES/FW_OBJECT_REGISTRATION等编译期宏的开关语义、SimpleObjRegistry参考实现的运行原理以及如何在自研部署中接入对象注册与对象转字符串toString能力。1. Fw::Obj 模块定位与类层次依据 Fw/Obj/docs/sdd.md 的说明Fw::Obj模块包含两个核心类型Fw::ObjBaseF´ 类层次中的根基类是框架内所有组件与端口的共同祖先负责存储对象名称并在已注册对象注册器时执行对象登记Fw::ObjRegistry一个纯虚基类提供对象注册的抽象接口具体存储与数据产出方式交由派生类实现。在源码中Fw/Obj/ObjBase.hpp 同时声明了这两个类Fw/Obj/ObjBase.cpp 提供实现Fw/Obj/README 简要标注了本目录还包含SimpleObjRegistry.hpp/.cpp一个非常基础的对象注册器。从类层次看Fw::ObjBase位于继承链最顶端向下直接派生组件基类与端口基类相关继承关系在源码中可以明确印证组件侧Fw::PassiveComponentBase : public Fw::ObjBase见 Fw/Comp/PassiveComponentBase.hpp再向下为Fw::QueuedComponentBase : public PassiveComponentBase见 Fw/Comp/QueuedComponentBase.hpp以及Fw::ActiveComponentBase : public QueuedComponentBase见 Fw/Comp/ActiveComponentBase.cpp端口侧Fw::PortBase : public Fw::ObjBase见 Fw/Port/PortBase.hpp。也就是说F´ 中任意组件实例或端口实例都天然继承自Fw::ObjBase因此对象注册机制能够以统一方式遍历整个框架运行期对象图。Fw::Obj模块的构建清单位于 Fw/Obj/CMakeLists.txt它把ObjBase.cpp与SimpleObjRegistry.cpp注册为Fw_Obj模块的源文件并声明对Fw_Types、Fw_Logger的模块依赖。2. Fw::ObjBase根基类的能力与默认行为2.1 成员与接口一览Fw/Obj/ObjBase.hpp 中Fw::ObjBase的公开与保护接口由三个编译期宏控制其全部成员如下成员可见性说明受控宏const char* getObjName()public返回对象名称m_objName.toChar()的结果FW_OBJECT_NAMESvoid setObjName(const char* name)public将传入字符串拷贝进对象名缓冲区FW_OBJECT_NAMESvirtual void toString(char* str, FwSizeType size)public生成对象描述字符串默认格式为Obj: nameFW_OBJECT_NAMESFW_OBJECT_TO_STRINGstatic void setObjRegistry(ObjRegistry* reg)public静态方法设置全局对象注册器指针FW_OBJECT_REGISTRATIONObjBase(const char* name)protected构造函数只允许派生类调用空指针时回退为NoName始终存在virtual ~ObjBase()protected空析构函数始终存在void init()protected初始化入口若注册器已设置则调用regObject(this)登记自身始终存在Fw::ObjectName m_objNameprotected存储对象名称的成员FW_OBJECT_NAMESstatic ObjRegistry* s_objRegistryprivate静态全局注册器指针默认nullptrFW_OBJECT_REGISTRATION2.2 构造函数与名称管理构造函数在 Fw/Obj/ObjBase.cpp 中的实现体现了空指针安全处理ObjBase::ObjBase(const char* objName) { if (nullptr objName) { this-setObjName(NoName); } else { this-setObjName(objName); } }当FW_OBJECT_NAMES 1时构造函数才真正存储名称m_objName为Fw::ObjectName类型其缓冲区大小由FW_OBJ_NAME_BUFFER_SIZE决定默认 80 字节见 default/config/FpConfig.h当FW_OBJECT_NAMES 0时构造函数退化为空实现不占用任何名称存储空间。setObjName()直接调用this-m_objName name;而getObjName()返回m_objName.toChar()。从实现上看名称的读写均为同步拷贝/读取不涉及动态内存分配符合嵌入式实时系统对确定性内存行为的要求。2.3 init() 与对象登记时机init()是 F´ 对象生命周期中的关键节点其实现如下void ObjBase::init() { #if FW_OBJECT_REGISTRATION if (ObjBase::s_objRegistry) { ObjBase::s_objRegistry-regObject(this); } #endif }也就是说对象注册不是发生在构造函数中而是发生在init()阶段。这一点与 F´ 组件模型的整体设计一致——组件通过init(instance)完成初始化例如 Fw/Comp/ActiveComponentBase.cpp 中的ActiveComponentBase::init()会级联调用QueuedComponentBase::init(instance)静态注册器指针若已被设置init()便会把this交给注册器。从源码结构看这一设计可以推断是为了避免在静态构造阶段就依赖尚未初始化的全局注册器从而保证初始化顺序可控。2.4 默认 toString() 实现当FW_OBJECT_NAMES与FW_OBJECT_TO_STRING同时开启时基类提供可被派生类覆盖的toString()默认实现void ObjBase::toString(char* str, FwSizeType size) { FW_ASSERT(size 0); FW_ASSERT(str ! nullptr); Fw::FormatStatus formatStatus Fw::ExternalString(str, ...) .format(Obj: %s, this-m_objName.toChar()); if (formatStatus ! Fw::FormatStatus::SUCCESS) { str[0] 0; } }默认格式为Obj: name格式化失败时目标缓冲区过小等会置空字符串避免留下未定义内容。组件基类PassiveComponentBase覆写了该方法格式串变为Comp: %s见 Fw/Comp/PassiveComponentBase.cpp而ActiveComponentBase进一步覆写为ActComp: %s见 Fw/Comp/ActiveComponentBase.cpp。由此可见toString()是一条可随类层次逐步增强的对象自描述链。3. Fw::ObjRegistry注册器抽象接口Fw::ObjRegistry仅当FW_OBJECT_REGISTRATION 1时才被声明是一个纯虚基类class ObjRegistry { public: virtual void regObject(ObjBase* obj) 0; // 纯虚必须由派生类实现 virtual ~ObjRegistry(); };其语义要点regObject(ObjBase* obj)是唯一需要派生类实现的方法框架通过ObjBase::setObjRegistry()设置的静态指针在ObjBase::init()中被调用实际存储Fw::ObjBase指针的方式、以及如何产出实例数据完全由派生类决定对应 sdd.md 中留给派生类的表述注册器是单例式的静态成员s_objRegistry在整个进程中只有一份后设置的注册器会覆盖先前的指针Fw/Obj/ObjBase.cpp 中setObjRegistry()仅做赋值。值得注意的约束在 Fw/Obj/ObjBase.hpp 的注释中明确给出setObjRegistry()与注册流程不可重入、非线程安全且仓库提供的SimpleObjRegistry即非可重入实现。在多数嵌入式部署中注册发生在启动阶段、单线程上下文中该约束通常可接受但在多线程系统中由开发者自行保证调用时机。4. SimpleObjRegistry仓库自带的参考实现4.1 设计思路Fw/Obj/SimpleObjRegistry.hpp 头文件注释说明了它的定位给出一个默认实现和对象注册器的示例。其核心特性构造时自动调用ObjBase::setObjRegistry(this)自我注册为全局注册器Fw/Obj/SimpleObjRegistry.cpp之后所有ObjBase::init()的对象都会登记到它内部内部使用定长指针数组ObjBase* m_objPtrArray[FW_OBJ_SIMPLE_REG_ENTRIES]保存对象指针无动态分配提供dump()列出全部条目、dump(const char* objName)按名称过滤需FW_OBJECT_NAMES、clear()重置条目计数等工具方法析构时调用setObjRegistry(nullptr)解除全局注册避免悬挂指针。4.2 登记与校验逻辑regObject()采用先断言后写入的策略void SimpleObjRegistry::regObject(ObjBase* obj) { FW_ASSERT(this-m_numEntries FW_OBJ_SIMPLE_REG_ENTRIES); this-m_objPtrArray[this-m_numEntries] obj; }当条目数达到上限FW_OBJ_SIMPLE_REG_ENTRIES默认 500见 default/config/FpConstants.fpp时会触发FW_ASSERT防止越界写。dump()同样在遍历时对条目数与指针做断言保证输出过程的安全性。4.3 dump() 的输出行为dump()根据编译宏组合输出不同格式Fw/Obj/SimpleObjRegistry.cpp宏组合输出格式示例FW_OBJECT_NAMESFW_OBJECT_TO_STRING均开Entry: idx Ptr: p Str: toString结果Entry: 0 Ptr: 0x... Str: Comp: cmdDisp仅FW_OBJECT_NAMESEntry: idx Ptr: p Name: nameEntry: 0 Ptr: 0x... Name: cmdDisp两者均关Entry: idx Ptr: p仅输出序号与指针输出通过Fw::Logger::log()打印。可以看到toString()/FW_OBJECT_TO_STRING的价值正是在这里体现它让dump()不仅能列出指针还能输出每个对象的自描述信息便于运行期排障。4.4 配置常量SimpleObjRegistry依赖两个可在工程配置中调整的常量定义于 default/config/FpConstants.fpp常量默认值作用FW_OBJ_SIMPLE_REG_ENTRIES500注册器内部指针数组容量即最多可登记的对象数FW_OBJ_SIMPLE_REG_BUFF_SIZE255dump()中用于承载toString()结果的临时缓冲区大小对于对象数量可能超过 500 的大型部署需要调大FW_OBJ_SIMPLE_REG_ENTRIES否则regObject()会触发断言。5. 三个核心编译宏开关语义与配置位置对象体系的功能开关集中在FpConfig.h中default/config/FpConfig.hsdd.md 明确指出FW_OBJECT_REGISTRATION宏即位于此文件。与之配套的宏完整清单如下宏默认值作用依赖FW_OBJECT_NAMES1开是否为每个对象存储名称增加内存与代码量便于追踪对象无FW_OBJECT_TO_STRING1开是否生成toString()方法以转储对象内部信息增加代码量必须FW_OBJECT_NAMES 1FW_OBJECT_REGISTRATION1开是否启用对象注册能力增加代码量获得对象追踪能力无FW_OBJ_NAME_BUFFER_SIZE80对象名缓冲区大小超长名称被截断自动编码器限制为 80FW_OBJECT_NAMES 1FW_OBJ_SIMPLE_REG_ENTRIES500简单注册器数组容量FW_OBJECT_REGISTRATION 1FW_OBJ_SIMPLE_REG_BUFF_SIZE255简单注册器dump()临时缓冲区大小FW_OBJECT_REGISTRATION 1需要注意的联动关系源码级确认当FW_OBJECT_NAMES 0时FW_OBJECT_TO_STRING会被强制置 0见 default/config/FpConfig.h 第 54–61 行因为toString()的默认实现依赖对象名FW_OBJECT_REGISTRATION关闭后ObjRegistry类、setObjRegistry()、regObject()等符号全部被编译期剔除init()也不做任何登记动作二进制体积随之减小辅助宏FW_OPTIONAL_NAME(string)在FW_OBJECT_NAMES 0时展开为空字符串用于在不启用对象名时剥离组件名相关的字符串字面量进一步节省空间。这些开关的取舍原则调试与追踪优先则全部开启资源受限的飞行硬件上则按需裁剪。更完整的宏表格与说明还可在 docs/user-manual/framework/configuring-fprime.md 的 Object Settings 一节找到表 35–37其中包含队列命名FW_QUEUE_*、任务命名FW_TASK_*等关联配置。6. 典型使用方式与生命周期时序综合 sdd.md 与源码实现一个典型的对象注册使用流程如下配置阶段在工程FpConfig.h中确认FW_OBJECT_REGISTRATION、FW_OBJECT_NAMES、FW_OBJECT_TO_STRING均为 1默认即是注册器创建在拓扑/启动代码中实例化一个Fw::SimpleObjRegistry或自研的ObjRegistry派生类其构造函数自动把自己挂到ObjBase的静态指针上对象初始化逐个调用组件/端口的init()如代码生成的组件基类init(instance)每次调用都会触发regObject(this)完成登记查询与排障调用dump()或dump(objName)打印对象清单或利用getObjName()、toString()对单个对象取信息收尾可选注册器析构时自动解除全局注册。从 Fw/Obj/SimpleObjRegistry.cpp 的实现可以看到完整闭环构造函数setObjRegistry(this)→ 对象init()时regObject(this)→ 查询dump()→ 析构setObjRegistry(nullptr)。需要特别强调的是时序要求注册器必须先于所有待登记对象的init()创建。若对象先init()而后才创建注册器先前的对象将错过登记init()只在s_objRegistry非空时才登记。这在启动代码中属于典型的一次性初始化顺序约束。7. 扩展与自研注册器的思路ObjRegistry是纯虚基类框架只约束regObject()接口存储与查询策略完全开放。仓库的SimpleObjRegistry提供了三种可扩展方向存储结构定长数组 → 链表、哈希表或按类型分桶以支持更多对象与更快查询查询能力目前仅有全量dump()与按名称过滤的dump(name)可扩展按类型如组件/端口、按实例 ID 过滤或提供迭代器让外部代码对全部对象统一执行某操作这正是 sdd.md 中registry 可查询Fw::Object的任意公开函数的体现输出通道Fw::Logger::log()可替换为事件Event、遥测Telemetry通道让对象清单可被地面系统回收。自研注册器应继承Fw::ObjRegistry并实现regObject()同时注意注册调用链非线程安全若需要并发登记应在派生实现中自行加锁Os::Mutex。8. 小结Fw::Obj模块虽然代码量小却是 F´ 对象体系的地基Fw::ObjBase统一了组件与端口的身份名称与生命周期入口init()Fw::ObjRegistry定义了集中登记对象的抽象SimpleObjRegistry则提供了一个可直接使用、可裁剪、可参考的默认实现。配合 default/config/FpConfig.h 中的宏开关开发者可以在代码体积最小化与运行期对象全图可见之间按部署需求自由取舍。对希望深入框架源码的读者建议按 Fw/Obj/ObjBase.hpp → Fw/Obj/ObjBase.cpp → Fw/Obj/SimpleObjRegistry.cpp → Fw/Comp/PassiveComponentBase.cpp 的顺序阅读即可完整理解从根基类到具体组件基类的对象能力逐层增强过程。【免费下载链接】fprimeF´ - A flight software and embedded systems framework项目地址: https://gitcode.com/GitHub_Trending/fpr/fprime创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考