Apollo 3.5 Cyber 模塊啟動原理

Apollo 3.5 Cyber 模塊啟動原理

  • 簡介
    • CYBER_REGISTER_COMPONENT做了甚麼
    • cyber_launch又做了甚麼
    • mainboard又又做了甚麼
    • Component(s)又又又做了甚麼

簡介

在baidu apollo 3.5 捨棄ros, 更新到自行開發的cyber後。
整個模塊啟動的流程都不同了, 由以往的啟動binary就行, 變成要先編成一個shared object
再寫一個dag檔案,再寫一個xml案。最後再用cyber_launch start xxx.xml去啟動模塊

可是背後是發生了甚麼操作呢,這就是本文的動機了.
一些比較仔細的筆記,我都放了在這地址 。那邊說得低層一點,會直接說到代碼層。

CYBER_REGISTER_COMPONENT做了甚麼

先參考各個模塊是怎把自己注册到cyber框

MSF Localization:
class MSFLocalizationComponent final : public cyber::Component
CYBER_REGISTER_COMPONENT(MSFLocalizationComponent)

Monitor:
class Monitor : public apollo::cyber::TimerComponent
CYBER_REGISTER_COMPONENT(Monitor)

  1. 要先把class定義為ComponentBase Class的subclass (Component和TimerComponent都是ComponentBase的sub class).
  2. 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之後。

Apollo 3.5 Cyber 模塊啟動原理_第1张图片

cyber_launch又做了甚麼

那我們知道CYBER_REGISTER_COMPONENT,是把一個用來產生模塊object的class factory, 加到了cyber的一個單例對象中了.
那這個class factory是甚麼時候被用到呢?
是在mainboard/module_controller中用到的,不過別急。先了解一下cyber_launch吧,不然你也不知道mainboard是在甚麼時侯被用到的。
Apollo 3.5 Cyber 模塊啟動原理_第2张图片

以上為cyber_launch 流程
cyber_launch其實就是一個python腳本,用法為cyber_launch start/stop xxx.launch
主要就是解讀xml檔案,然後通過subprocess,用mainboard的binary檔,把對應的module一個一個啟動起來

mainboard又又做了甚麼

mainboard基本就是cyber在啟動模塊時的主程序
首先做一堆初始化,然後調用ModuleControllerInit
ModuleControllerInit中,它會把要加載的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檔案中定義的componenttimer component,一個個生成出來
ModuleController通過class_name,比如 “XXXComponent” 去找出要用的class factory,再用class factory去生成及Initialize對應的object.

Component(s)又又又做了甚麼

那components的object被創建出來以後,它們會如何工作呢?
那就要說到cyber中的scheduler,readertimer模組了。現在就先不說吧

你可能感兴趣的:(apollo)