百度Apollo系统学习-Cyber RT 注册启动模块
概述
首先我们明确Cyber到底做了些什么工作,这一点我们可以参考ROS,毕竟Cyber是ROS的一个替代品。同ROS一样,Cyber主要的作用就是一个消息中间件,它们需要管理不同的模块,并让它们互相之间可以高效通信。所以我们接下来主要关注其中一点:Cyber如何注册和启动一个个模块。
因为贴代码的话篇幅太大,所以文中给出了代码路径,读者可以对照着git阅读。同时因为这一块内容实在太多太杂乱,笔者本想画一些流程图但反而更难理解,所以希望读者还是能循着代码来跳转。
笔者发现现在很多解析这一块的博客和文章都忽略了对代码结构的理解而是过多关注在流程,所以本文也希望能让读者真正理解cyber如何管理一个个模块,对自己以后做系统架构或者理解一些大型开源项目都会有帮助。因为水平有限,如果文中有错误希望大家指出,谢谢!
代码结构
Cyber RT的代码设计模式是工厂方法模式,理解了这个模式有助于掌握Cyber RT的组织结构。
工厂方法模式
简而言之,工厂方法模式就是有两个总的抽象类,一个工厂基类(Factory),一个产品基类(Product)。每个不同的产品(ProductA/B)都需要给它实现一个工厂(FactoryA/B)。当我们需要产品的实例时,我们就可以调用相应的工厂类返回该产品实例(return new ProductA/B)。
CyberRT工厂方法模式的对应关系
- Product:
ComponentBase
,Cyber基于这个基类又分了两个子类Component, TimerComponent
,这两个子类可以视为Product。(代码位于cyber/component) - ProductA/B:
modules
里不同功能下的各种组件比如CameraComponent
,它们都继承自Component
或TimerComponent
。(代码位于modules/内各个功能下的xxx_component.h/cc) - Factory:
AbstractClassFactoryBase
,它的子类AbstractClassFactory
可以视为Factory。(代码位于cyber/class_loader/utility/class_factory.h) - FactoryA/B:
ClassFactory
。代码位于(cyber/class_loader/utility/class_factory.h) - Cyber这里有个非常非常tricky的地方,那就是
ClassLoader
这个类,它是类加载器用来加载动态库和实例化Product,它虽然没有继承ClassFactory
,但它里面大部分函数都最终调用了ClassFactory
的方法,所以我们暂时可以把ClassLoader
视作工厂,后面会专门介绍ClassLoader
CyberRT工厂方法模式的工厂类组织结构
我们知道当需要Component
实例时会调用工厂类的CreateObj
来返回实例,那我们是如何找到对应的工厂的呢,下面我们来回答这个问题。
- 工厂类的保存位置
在cyber/class_loader/utility/class_loader_utility.cc中,我们可以看到很多函数中都有static变量,而这些其实就是全局变量。保存有所有工厂的map就是其中一个static变量。 - 工厂类的保存结构
工厂类的保存结构为BaseToClassFactoryMapMap
,这是一个别名,展开后是map<string,map<string,utility::AbstractClassFactoryBase*>>
,可以看到这是一个双层嵌套的map。 - 如何获取工厂
我们想获取一个工厂类的指针的话,要先根据base_class_name
(目前我看到的全是ComponentBase
,所以这一层map可能是留给其他种类的组件吧)来得到map<string,utility::AbstractClassFactoryBase*>
,然后再根据实际的class_name
(在每个dag文件的每个components
或timer_components
中的class_name
字段中指定)来得到工厂类的