PX4模块设计之十七:ModuleBase模块

Posted lida2003

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PX4模块设计之十七:ModuleBase模块相关的知识,希望对你有一定的参考价值。

PX4模块设计之十七:ModuleBase模块

1. ModuleBase模块介绍

ModuleBase模块是一个基础类,主要完成各个模块需要的基础功能,比如:start, stop, status命令,但是目前尚不支持多实例(类似mavlink模块多实例应用)。

当继承自该类的模块:

  • 必须完成以下函数实现:
  1. static int task_spawn(int argc, char *argv[])
  2. static T *instantiate(int argc, char *argv[])
  3. static int custom_command(int argc, char *argv[])
  4. static int print_usage(const char *reason = nullptr)
  • 根据情况重构以下函数实现:
  1. virtual int print_status()
  2. virtual void run()
  3. 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模块的主要内容,如果未能解决你的问题,请参考以下文章

PX4模块设计之十六:Hardfault模块

PX4模块设计之十八:Logger模块

PX4模块设计之十五:PX4 Log设计

PX4模块设计之十:PX4启动过程

PX4模块设计之十二:High Resolution Timer设计

PX4模块设计之十三:WorkQueue设计