PX4模块设计之十七:ModuleBase模块
Posted lida2003
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PX4模块设计之十七:ModuleBase模块相关的知识,希望对你有一定的参考价值。
PX4模块设计之十七:ModuleBase模块
1. ModuleBase模块介绍
ModuleBase模块是一个基础类,主要完成各个模块需要的基础功能,比如:start, stop, status命令,但是目前尚不支持多实例(类似mavlink模块多实例应用)。
当继承自该类的模块:
- 必须完成以下函数实现:
- static int task_spawn(int argc, char *argv[])
- static T *instantiate(int argc, char *argv[])
- static int custom_command(int argc, char *argv[])
- static int print_usage(const char *reason = nullptr)
- 根据情况重构以下函数实现:
- virtual int print_status()
- virtual void run()
- virtual void request_stop()
2. ModuleBase类介绍
这是一个UML绘图工具绘制的ModuleBase类定义(给出一个整体的picture)。
注:由于PX4的代码已经都完成了实现,从代码上逆向回来的UML图,所以感觉很细(很具体化);设计上的一些内容(注释,考虑,场景分析等)看不太出来。如果自上而下的UML设计,会注意到有非常多的描述。这里只是用了UML画个图,做个整体的了解。知道这个类有哪些函数、参量,以及public/protected/private属性。所以不再去考虑和分析设计这个类的时候设计人员的想法(也无从得知)。
3. ModuleBase类功能介绍
3.1 模块入口
ModuleBase::main
├──> <argc <= 1 且 命令参数非 "-h"/"help"/"info"/"usage">
│ └──> return T::print_usage(); //【必须实现】的函数
├──> <命令参数 "start">
│ └──> return start_command_base(argc - 1, argv + 1);
├──> <命令参数 "status">
│ └──> return status_command();
├──> <命令参数 "stop">
│ └──> return stop_command();
├──> lock_module(); // Lock here, as the method could access _object.
├──> int ret = T::custom_command(argc - 1, argv + 1); //【必须实现】的函数
└──> unlock_module();
在PX4飞控系统代码中模块入口采用的是_main来作为app应用入口的。这里只是ModuleBase::main,在实际符号链接之前要做转换。我们以logger模块为例:
int logger_main(int argc, char *argv[])
// logger currently assumes little endian
int num = 1;
if (*(char *)&num != 1)
PX4_ERR("Logger only works on little endian!\\n");
return 1;
return Logger::main(argc, argv);
此时logger_main是logger模块的应用入口,然后【PX4模块设计之十一:Built-In框架】通过CMakelist将logger模块的应用入口编译和链接到系统中。
注:main的写法与【Linux应用程序之Helloworld入门】里面的入口main一致,符合常规习惯。
3.2 模块启动
检查是否已在运行,如果尚未运行,则调用T::task_spawn()。
ModuleBase::start_command_base
├──> lock_module();
├──> <is_running()>
│ └──> PX4_ERR("Task already running");
├──> <else>
│ └──> ret = T::task_spawn(argc, argv); //【必须实现】的函数
└──> unlock_module();
3.3 模块停止
检查模块是否正在运行,如果正在运行,则请求模块停止并等待任务完成。
ModuleBase::stop_command
├──> lock_module();
├──> <is_running()>
│ ├──> T *object = _object.load();
│ ├──> <object>
│ │ ├──> object->request_stop(); // 发布stop应用指令
│ │ └──> do while (_task_id != -1);
│ │ ├──> unlock_module();
│ │ ├──> px4_usleep(10000); // 10 ms, 定期循环查询_task_id是否==-1(满足条件表明模块自己已经优雅退出)
│ │ ├──> lock_module();
│ │ └──> <++i > 500 && _task_id != -1> // wait at most 5 sec
│ │ ├──> PX4_ERR("timeout, forcing stop");
│ │ ├──> <_task_id != task_id_is_work_queue> // 如果已经创建task,需要强制删除任务
│ │ │ └──> px4_task_delete(_task_id);
│ │ ├──> _task_id = -1;
│ │ ├──> delete _object.load();
│ │ ├──> _object.store(nullptr);
│ │ └──> break
│ └──> <else>
│ └──> _task_id = -1;
└──> unlock_module();
3.4 状态查询
检查是否正在运行,如果正在运行,则调用print_status()。
ModuleBase::status_command
├──> lock_module();
├──> <is_running() && _object.load()>
│ ├──> T *object = _object.load();
│ └──> ret = object->print_status();
├──> <else>
│ └──> PX4_ERR("failed to instantiate object");
└──> unlock_module();
3.5 任务回调
创建任务以后,ModuleBase通过px4_task_spawn_cmd封装了任务入口函数run_trampoline。
ModuleBase::run_trampoline
├──> T *object = T::instantiate(argc, argv); //【必须实现】的函数
├──> _object.store(object);
├──> <object>
│ └──> object->run();
├──> <else>
│ └──> PX4_ERR("failed to instantiate object");
└──> exit_and_cleanup
3.6 辅助函数
static bool is_running() //判断模块是否已经启动
bool should_exit() //判断模块是否需要退出
static void exit_and_cleanup() //清除当前模块对象(清空指针)
static int wait_until_running(int timeout_ms = 1000) //等待实例初始化完成,可以在task_spawn最初阶段
static T *get_instance() // 获取当前模块实例
4. 总结
ModuleBase类总体上看类似一个简单封装,将模块设计的基本框架搭建好了,完成最为基本的start/stop/status功能。关于具体是否创建任务(线程),还是使用工作队列,ModuleBase类都没有具体给出。
关于模块是否采用任务(T::task_spawn内部实现),还是采用WorkQueue(继承WorkItem实现),在后续章节中再做深入。
5. 参考资料
【1】PX4开源软件框架简明简介
【2】Linux应用程序之Helloworld入门
【3】PX4模块设计之十一:Built-In框架
【4】PX4模块设计之十三:WorkQueue设计
以上是关于PX4模块设计之十七:ModuleBase模块的主要内容,如果未能解决你的问题,请参考以下文章