
嵌入式系统编程【免费下载链接】fprimeF´ - A flight software and embedded systems framework项目地址https://gitcode.com/gh_mirrors/fpri/fprime点击查看免费下载本文以 F´Flight Software Framework仓库中的 Fw/Obj/docs/sdd.md 设计说明文档为主体结合Fw::ObjBase、Fw::ObjRegistry的源码实现、FpConfig.hpp编译开关以及拓扑自动生成代码系统讲解 F´ 对象体系的根节点设计、对象命名存储、可选的对象注册机制及其工程实践。读完本文你将掌握Fw::Obj模块的类层次定位、三个核心配置宏FW_OBJECT_NAMES、FW_OBJECT_TO_STRING、FW_OBJECT_REGISTRATION的作用与取值影响以及如何利用SimpleObjRegistry在运行时转储全系统对象清单用于调试。1. 模块概览与架构定位Fw::Obj是 F´ 体系中最基础的模块之一。根据 Fw/Obj/docs/sdd.md 的说明它包含两个关键类型Fw::ObjBase定义 F´ 架构中对象的根类是所有组件Component与端口Port的共同基类。它负责存储对象名称并在已注册对象注册表object registry时执行对象注册。Fw::ObjRegistry对象注册表的纯虚基类。它是一个可选特性由FW_OBJECT_REGISTRATION宏控制该宏定义于 config/FpConfig.hpp原文档写作FpConfig.hpp在仓库中其路径为config/FpConfig.hpp。其核心思想是通过一个注册表跟踪系统中已实例化的所有对象注册表可以调用Fw::ObjBase的公有接口查询实例信息而具体如何存储对象指针、如何产出实例数据由派生类自行实现。模块的仓库构成非常精简参见 Fw/Obj/README 与目录结构文件职责Fw/Obj/ObjBase.hppFw::ObjBase与Fw::ObjRegistry的类声明Fw/Obj/ObjBase.cpp上述两个类的实现Fw/Obj/SimpleObjRegistry.hpp一个最基础的对象注册表实现声明Fw/Obj/SimpleObjRegistry.cpp基础对象注册表实现定义Fw/Obj/CMakeLists.txt模块构建脚本Fw/Obj/docs/sdd.md模块软件设计说明本文主体需要说明原 sdd.md 中多处将根类写作Fw::Object而当前仓库源码中的类名统一为Fw::ObjBase见 Fw/Obj/ObjBase.hpp这是设计文档早期命名与现行实现的差异本文以源码为准。2. 核心类 Fw::ObjBase根类的设计与实现2.1 类层次中的位置从源码继承关系可以确认Fw::ObjBase确实是组件与端口的共同祖先Fw/Port/PortBase.hpp 中class PortBase : public Fw::ObjBase所有端口基类都派生自它Fw/Comp/PassiveComponentBase.hpp 中class PassiveComponentBase : public Fw::ObjBase被动组件基类同样派生自它其构造函数在 PassiveComponentBase.cpp 中显式调用Fw::ObjBase(name)完成初始化Fw/Port/PortBase.cpp 中端口构造时以Fw::ObjBase(nullptr)初始化并在后续通过setObjName设置名称——这与ObjBase构造函数对空指针的容错逻辑见下相呼应。也就是说整个 F´ 系统中每一个组件实例、每一个端口实例本质上都是一个Fw::ObjBase对象因此把对象注册表放在这一层就能无差别地跟踪全部系统对象。2.2 职责一对象名称的存储与读写当FW_OBJECT_NAMES 1时默认开启见 config/FpConfig.hppFw::ObjBase会在每个实例内分配一个固定大小的名字缓冲区m_objName其大小由FW_OBJ_NAME_MAX_SIZE决定默认 80 字节见 config/FpConfig.hpp并提供两个接口const char* getObjName()返回对象名称指针ObjBase.hppvoid setObjName(const char* name)将传入字符串拷贝进内部缓冲区ObjBase.hpp。其实现细节在 ObjBase.cppvoid ObjBase::setObjName(const char* name) { strncpy(this-m_objName, name, sizeof(this-m_objName)); this-m_objName[sizeof(this-m_objName)-1] 0; // 强制以 \0 结尾 }注意两点工程细节使用strncpy并强制在缓冲区末尾写0保证超长名称被截断后依然是以 NUL 结尾的合法 C 字符串FpConfig.hpp注释明确Larger names will be truncated即超过 80 字节的名称会被截断构造函数对空指针做了容错当传入nullptr时自动命名为NoNameObjBase.cpp。当FW_OBJECT_NAMES 0时名称缓冲区与相关接口全部被编译掉构造函数为空实现以节省内存——适用于存储极度受限的嵌入式目标。此时可配合FW_OPTIONAL_NAME(name)宏config/FpConfig.hpp它在名称功能关闭时自动把字符串替换为空串从而避免无意义的字符串常量占用二进制体积。2.3 职责二toString 对象描述在FW_OBJECT_NAMES 1且FW_OBJECT_TO_STRING 1默认开启见 config/FpConfig.hpp时Fw::ObjBase提供一个虚函数virtual void toString(char* str, NATIVE_INT_TYPE size);默认实现ObjBase.cpp输出Obj: name格式的字符串并对size 0的情况使用FW_ASSERT断言保护。该方法的用途是供派生类重写以返回对象自身的描述信息——例如组件可以重写为包含实例号、组件类型等信息的描述。目标缓冲区大小由FW_OBJ_TO_STRING_BUFFER_SIZE配置默认 255 字节见 config/FpConfig.hpp。注意该宏只有在FW_OBJECT_NAMES开启时才会生效二者存在依赖关系。3. 对象注册机制 Fw::ObjRegistry 与注册流程3.1 编译开关与纯虚基类对象注册是可选的由FW_OBJECT_REGISTRATION宏控制默认开启见 config/FpConfig.hpp#ifndef FW_OBJECT_REGISTRATION #define FW_OBJECT_REGISTRATION 1 // 是否允许对象集中注册更多代码、更强的对象跟踪能力 #endif当该宏为 1 时Fw/Obj/ObjBase.hpp 中定义纯虚基类class ObjRegistry { public: virtual void regObject(ObjBase* obj) 0; // 对象注册时被调用的纯虚函数 virtual ~ObjRegistry(); };regObject是注册表的唯一纯虚接口它通过ObjBase::setObjRegistry()设置的静态指针被调用传入对象实例指针至于如何使用该指针完全由派生注册表决定——这正是原 sdd.md 强调的实际存储方法与数据产出方式留给派生类的设计。3.2 注册调用链setObjRegistry → init → regObjectFw::ObjBase内部持有一个静态指针s_objRegistryObjBase.hpp并提供静态函数setObjRegistry(ObjRegistry* reg)来设置它ObjBase.hpp实现见 ObjBase.cpp。每个对象初始化时调用受保护的init()ObjBase.hpp其实现ObjBase.cppvoid ObjBase::init() { #if FW_OBJECT_REGISTRATION if (ObjBase::s_objRegistry) { ObjBase::s_objRegistry-regObject(this); } #endif }由此形成完整的注册调用链SimpleObjRegistry 构造或用户代码→ ObjBase::setObjRegistry(registry) → 各组件/端口构造完成后调用 ObjBase::init() → s_objRegistry 非空 → registry-regObject(this) // 对象被登记原文档与头文件注释还明确给出一个重要限制setObjRegistry的调用可能不可重入、非线程安全且框架自带的SimpleObjRegistry就是不可重入的ObjBase.hpp。因此注册表实例化应放在启动早期的单线程上下文中完成。3.3 配置宏速查表与Fw::Obj相关的全部配置宏集中在 config/FpConfig.hpp整理如下宏默认值作用位置FW_OBJECT_NAMES1是否为对象存储名称更多内存可用于对象跟踪FpConfig.hppFW_OBJECT_TO_STRING1依赖FW_OBJECT_NAMES是否生成toString()方法转储对象内部信息更多代码FpConfig.hppFW_OBJECT_REGISTRATION1是否启用对象集中注册更多代码、更强对象跟踪FpConfig.hppFW_OBJ_NAME_MAX_SIZE80对象名缓冲区字节数超长截断FpConfig.hppFW_OBJ_TO_STRING_BUFFER_SIZE255toString()输出缓冲区大小FpConfig.hppFW_OBJ_SIMPLE_REG_ENTRIES500简单注册表可存储的对象数量上限FpConfig.hppFW_OBJ_SIMPLE_REG_BUFF_SIZE255转储对象名/描述时的缓冲区大小应 ≥FW_OBJ_NAME_MAX_SIZEFpConfig.hpp可见这些宏之间存在依赖链FW_OBJECT_TO_STRING依赖FW_OBJECT_NAMES注册表相关容量宏依赖FW_OBJECT_REGISTRATION。为降低嵌入式二进制体积可将三者同时置 0但会失去对象跟踪与转储能力。4. 参考实现 SimpleObjRegistry默认对象注册表SimpleObjRegistry是框架提供的最基本注册表实现Fw/Obj/SimpleObjRegistry.hpp其头文件注释明确它是默认实现与示例实例化时它会用setObjRegistry()把自己注册进ObjBase的静态指针此后对象随实例化过程逐个登记并可通过dump()列出注册表中的对象。4.1 自注册的生命周期构造函数SimpleObjRegistry.cppSimpleObjRegistry::SimpleObjRegistry() { ObjBase::setObjRegistry(this); // 将自己设为全局注册表 this-m_numEntries 0; for (NATIVE_INT_TYPE entry 0; entry FW_OBJ_SIMPLE_REG_ENTRIES; entry) { this-m_objPtrArray[entry] nullptr; } }析构函数则调用ObjBase::setObjRegistry(nullptr)解除注册SimpleObjRegistry.cpp。存储结构是一个定长指针数组m_objPtrArray[FW_OBJ_SIMPLE_REG_ENTRIES]默认 500 个槽位regObject实现为顺序追加并带断言保护void SimpleObjRegistry::regObject(ObjBase* obj) { FW_ASSERT(this-m_numEntries FW_OBJ_SIMPLE_REG_ENTRIES); // 槽位耗尽即断言失败 this-m_objPtrArray[this-m_numEntries] obj; }这意味着当系统对象总数超过 500 时FW_ASSERT会直接触发断言——这是嵌入式框架宁可失败也不要静默越界的典型做法。若部署的系统对象较多应相应调大FW_OBJ_SIMPLE_REG_ENTRIES。4.2 转储dump与清理cleardump()遍历已登记对象通过Fw::Logger::logMsg打印每个对象的序号、指针地址及描述SimpleObjRegistry.cpp。输出内容随编译开关组合变化FW_OBJECT_NAMES FW_OBJECT_TO_STRING开启调用toString()输出形如Entry: %d Ptr: %p Str: %s仅FW_OBJECT_NAMES开启调用getObjName()输出Entry: %d Ptr: %p Name: %s两者皆关仅输出Entry: %d Ptr: %p。当FW_OBJECT_NAMES 1时还提供按名称定向转储的dump(const char* objName)SimpleObjRegistry.cpp用strncmp精确匹配对象名后打印clear()则简单地将计数清零SimpleObjRegistry.cpp实现软清空。5. 在 F´ 工程中的实际应用拓扑自动生成与调试入口5.1 拓扑代码生成中的注册表实例化F´ 的部署工程通常通过 FPP 建模生成拓扑代码自动生成模板会默认实例化一个SimpleObjRegistry。以 publicTopologyCpp.tmpl 为例simpleReg_ptr new Fw::SimpleObjRegistry();模板随后在构造完所有组件、完成端口连接并启动活动组件之后调用dumparch()publicTopologyCpp.tmpl。而dumparch()与dumpobj()这两个 C 链接函数由 includes1TopologyCpp.tmpl 生成extern C { void dumparch(); void dumpobj(const char* objName); } void dumparch() { simpleReg_ptr-dump(); } void dumpobj(const char* objName) { simpleReg_ptr-dump(objName); }也就是说每个由自动生成模板搭建的 F´ 部署天然具备两个调试入口dumparch()转储整个架构中已注册的全部对象数量上限 500dumpobj(name)按名称查询并转储单个对象。这一机制让开发者无需额外打日志即可在运行时检查所有组件与端口实例是否按预期创建、名称是否正确。实际部署代码中也能看到SimpleObjRegistry的直接引用例如 RPI/Top/Components.hpp 在树莓派示例部署中#include Fw/Obj/SimpleObjRegistry.hpp其拓扑文件会生成对应extern实例。5.2 组件/端口如何被登记由于所有组件PassiveComponentBase等与端口PortBase的基类都是Fw::ObjBase且构造函数链最终调用ObjBase构造函数、初始化阶段调用init()因此只要注册表已设置系统中每个组件与端口实例都会被自动登记无需组件作者编写任何额外代码。这正是 sdd.md 所说对象初始化时执行注册register objects when they are initialized在框架层的落地。6. 使用建议与注意事项注册表应尽早、单线程地建立setObjRegistry不可重入且非线程安全ObjBase.hpp推荐在主函数早期、组件实例化之前完成SimpleObjRegistry的创建。容量与资源权衡FW_OBJ_SIMPLE_REG_ENTRIES默认 500与FW_OBJ_NAME_MAX_SIZE默认 80会随对象数量线性占用内存小型嵌入式目标可关闭FW_OBJECT_NAMES/FW_OBJECT_TO_STRING/FW_OBJECT_REGISTRATION换取体积但会失去dumparch()调试能力。名称截断是预期行为setObjName对超长名称静默截断并保证 NUL 结尾若需要更长的对象名请调整FW_OBJ_NAME_MAX_SIZE且注意FW_OBJ_SIMPLE_REG_BUFF_SIZE应不小于名称长度避免转储缓冲区溢出。注册表溢出即断言SimpleObjRegistry槽位耗尽会触发FW_ASSERT这是框架显式失败优于静默错误的工程选择部署前应核对系统对象总数。toString是可扩展点派生类可通过重写toString提供比默认Obj: name更丰富的对象描述如实例号、类型、关键状态从而让dumparch()输出更有诊断价值。7. 变更日志根据 Fw/Obj/docs/sdd.md 记录的模块设计文档变更历史日期说明2016-04-24初始版本该模块自 2016 年随 F´ 早期版本当时写作 ISF引入核心设计——根类 可选注册表——沿用至今是 F´ 对象体系稳定性的直接体现。仓库中ObjBase.hpp/.cpp的版权头Copyright 2016, California Institute of Technology与文档时间线一致进一步印证了其设计演进脉络。赞分享嵌入式系统编程【免费下载链接】fprimeF´ - A flight software and embedded systems framework项目地址https://gitcode.com/gh_mirrors/fpri/fprime点击查看免费下载相关推荐F´ 框架的 Fw::Obj 模块深度解析对象基类与对象注册机制F´ 框架的 Fw::Obj 模块深度解析对象基类与对象注册机制 导读 Fw::Obj 是 F´F Prime飞行软件与嵌入式系统框架中定义 对象体系根基嵌入式系统编程F´ 框架对象体系解析Fw::ObjBase 与 Fw::ObjRegistry 对象注册机制完全指南F´ 框架对象体系解析Fw::ObjBase 与 Fw::ObjRegistry 对象注册机制完全指南 导读 Fw::Obj 是 F´F Prime飞行软嵌入式系统编程F´ 框架 Fw::Port 端口基类深度解析输入/输出端口、对象注册与端口追踪机制F´ 框架 Fw::Port 端口基类深度解析输入/输出端口、对象注册与端口追踪机制 导读 本文围绕 F´F Prime飞行软件与嵌入式系统框架中的 Fw嵌入式系统编程上一篇Folia Electron架构剖析主进程、预加载与渲染器分工指南下一篇Nix 官方 Docker 镜像使用与定制指南从拉取运行到非 root 构建创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考