在baidu apollo 3.5 捨棄ros, 更新到自行開發的cyber後。
整個模塊啟動的流程都不同了, 由以往的啟動binary就行, 變成要先編成一個shared object
再寫一個dag檔案,再寫一個xml案。最後再用cyber_launch start xxx.xml
去啟動模塊
可是背後是發生了甚麼操作呢,這就是本文的動機了.
一些比較仔細的筆記,我都放了在這地址 。那邊說得低層一點,會直接說到代碼層。
先參考各個模塊是怎把自己注册到cyber框
MSF Localization:
class MSFLocalizationComponent final : public cyber::Component
CYBER_REGISTER_COMPONENT(MSFLocalizationComponent)
Monitor:
class Monitor : public apollo::cyber::TimerComponent
CYBER_REGISTER_COMPONENT(Monitor)
CYBER_REGISTER_COMPONENT
注册模塊那CYBER_REGISTER_COMPONENT
下邊是我把CYBER_REGISTER_COMPONENT
真正會展開的東西放在一起,由下到上看
template <typename Derived, typename Base>
void RegisterClass(const std::string& class_name,
const std::string& base_class_name) {
// Study: GetCurLoadingLibraryName() is a singleton
// It value only changed in LoadLibrary in class_loader_utility
AINFO << "registerclass:" << class_name << "," << base_class_name << ","
<< GetCurLoadingLibraryName();
// Study: The only ClassFactory required the Derived Class (The modules real class)
// have a constructor with no argument
utility::AbstractClassFactory<Base>* new_class_factrory_obj =
new utility::ClassFactory<Derived, Base>(class_name, base_class_name);
new_class_factrory_obj->AddOwnedClassLoader(GetCurActiveClassLoader());
new_class_factrory_obj->SetRelativeLibraryPath(GetCurLoadingLibraryName());
GetClassFactoryMapMapMutex().lock();
ClassClassFactoryMap& factory_map =
GetClassFactoryMapByBaseClass(typeid(Base).name());
factory_map[class_name] = new_class_factrory_obj;
GetClassFactoryMapMapMutex().unlock();
}
// Study: Using a proxy class constructor and it static instantiate
// to call RegisterClass once the shared object that have called CYBER_REGISTER_COMPONENT
// have been loaded
#define CLASS_LOADER_REGISTER_CLASS_INTERNAL(Derived, Base, UniqueID) \
namespace { \
struct ProxyType##UniqueID { \
ProxyType##UniqueID() { \
apollo::cyber::class_loader::utility::RegisterClass( \
#Derived, #Base); \
} \
}; \
static ProxyType##UniqueID g_register_class_##UniqueID; \
}
#define CLASS_LOADER_REGISTER_CLASS_INTERNAL_1(Derived, Base, UniqueID) \
CLASS_LOADER_REGISTER_CLASS_INTERNAL(Derived, Base, UniqueID)
// Study: Assign a unique id to each registered component, avoid name collision
// register class macro
#define CLASS_LOADER_REGISTER_CLASS(Derived, Base) \
CLASS_LOADER_REGISTER_CLASS_INTERNAL_1(Derived, Base, __COUNTER__)
// Study: This is the macro that used in modules
// All modules is subclass of apollo::cyber::ComponentBase
#define CYBER_REGISTER_COMPONENT(name) \
CLASS_LOADER_REGISTER_CLASS(name, apollo::cyber::ComponentBase)
簡單而言,CYBER_REGISTER_COMPONENT
就是新建了一個static 變量, 而變量的constructor就是把一個class factory加到cyber/class_loader 中的一個字典. 要小心的是,這個static 變量instantiate的時間是其shared library被加載到cyber的mainboard之後。
那我們知道CYBER_REGISTER_COMPONENT,是把一個用來產生模塊object的class factory, 加到了cyber的一個單例對象中了.
那這個class factory是甚麼時候被用到呢?
是在mainboard/module_controller
中用到的,不過別急。先了解一下cyber_launch
吧,不然你也不知道mainboard
是在甚麼時侯被用到的。
以上為cyber_launch
流程
cyber_launch
其實就是一個python腳本,用法為cyber_launch start/stop xxx.launch
主要就是解讀xml檔案,然後通過subprocess,用mainboard
的binary檔,把對應的module
一個一個啟動起來
mainboard基本就是cyber在啟動模塊時的主程序
首先做一堆初始化,然後調用ModuleController
做Init
。
在ModuleController
的Init
中,它會把要加載的shared_library加載進來。在這時候就會自動調用CYBER_REGISTER_COMPONENT
去把在shared library中的component class factory都加到cyber中
bool ModuleController::LoadModule(const DagConfig& dag_config) {
const std::string work_root = common::WorkRoot();
for (auto module_config : dag_config.module_config()) {
std::string load_path;
if (module_config.module_library().front() == '/') {
load_path = module_config.module_library();
} else {
load_path =
common::GetAbsolutePath(work_root, module_config.module_library());
}
if (!common::PathExists(load_path)) {
AERROR << "Path not exist: " << load_path;
return false;
}
// Study: 在這把config中指定的shared library加載
// 他加載是用了POCO library
// https://vovkos.github.io/doxyrest-showcase/poco/sphinxdoc/class_Poco_SharedLibrary.html#details-doxid-class-poco-1-1-shared-library
using PocoLibraryPtr = std::shared_ptr<Poco::SharedLibrary>;
class_loader_manager_.LoadLibrary(load_path);
for (auto& component : module_config.components()) {
const std::string& class_name = component.class_name();
// Study: 在這創建Component object
std::shared_ptr<ComponentBase> base =
class_loader_manager_.CreateClassObj<ComponentBase>(class_name);
if (base == nullptr) {
return false;
}
// Study: 在這讓你的Component開始工作
if (!base->Initialize(component.config())) {
return false;
}
component_list_.emplace_back(std::move(base));
}
for (auto& component : module_config.timer_components()) {
const std::string& class_name = component.class_name();
// Study: 在這創建Component object
std::shared_ptr<ComponentBase> base =
class_loader_manager_.CreateClassObj<ComponentBase>(class_name);
if (base == nullptr) {
return false;
}
// Study: 在這讓你的Component開始工作
if (!base->Initialize(component.config())) {
return false;
}
component_list_.emplace_back(std::move(base));
}
}
return true;
}
然後把dag檔案中定義的component
或timer component
,一個個生成出來
ModuleController
通過class_name
,比如 “XXXComponent” 去找出要用的class factory,再用class factory去生成及Initialize
對應的object.
那components的object被創建出來以後,它們會如何工作呢?
那就要說到cyber中的scheduler
,reader
及timer
模組了。現在就先不說吧