HelloWorld CPP
This chapter will introduce how to set up a most basic AimRT CPP project through a simple Demo.
This Demo will demonstrate the following basic functionalities:
Referencing AimRT via source code using CMake FetchContent;
Writing a basic
Module
based on the AimRT CPP interface;Using basic logging functionality;
Using basic configuration functionality;
Integrating the
Module
in App mode;Compiling the project and running the process to execute the logic in the
Module
.
STEP1: Ensure Local Environment Meets Requirements
First, ensure your local compilation environment and network environment meet the requirements. For details, refer to the requirements in Reference and Installation (CPP).
STEP2: Create Directory Structure and Add Basic Files
Create files according to the following directory structure:
├── CMakeLists.txt
├── cmake
│ └── GetAimRT.cmake
└── src
├── CMakeLists.txt
├── install
│ └── cfg
│ └── helloworld_cfg.yaml
├── module
│ └── helloworld_module
│ ├── CMakeLists.txt
│ ├── helloworld_module.cc
│ └── helloworld_module.h
└── app
└── helloworld_app
├── CMakeLists.txt
└── main.cc
Please note that this is just a reference directory structure and not mandatory. However, it is recommended to create separate folders for the following areas when setting up your own project:
install: Stores deployment configurations, startup scripts, etc.;
module: Stores business logic code;
app: In App mode, stores the main function where business modules are registered;
pkg: In Pkg mode, stores the entry methods for pkg dynamic libraries where business modules are registered;
File 1: /CMakeLists.txt
Root CMake file for building the project.
cmake_minimum_required(VERSION 3.24)
project(helloworld LANGUAGES C CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC")
endif()
include(cmake/GetAimRT.cmake)
add_subdirectory(src)
File 2: /cmake/GetAimRT.cmake
This file is used to fetch AimRT. Note that you need to change the GIT_TAG
version to the one you want to reference:
include(FetchContent)
FetchContent_Declare(
aimrt
GIT_REPOSITORY https://github.com/AimRT/aimrt.git
GIT_TAG v1.x.x)
FetchContent_GetProperties(aimrt)
if(NOT aimrt_POPULATED)
FetchContent_MakeAvailable(aimrt)
endif()
File 3: /src/CMakeLists.txt
References various subdirectories under src.
add_subdirectory(module/helloworld_module)
add_subdirectory(app/helloworld_app)
File 4: /src/module/helloworld_module/CMakeLists.txt
Creates the helloworld_module
static library.
file(GLOB_RECURSE src ${CMAKE_CURRENT_SOURCE_DIR}/*.cc)
add_library(helloworld_module STATIC)
add_library(helloworld::helloworld_module ALIAS helloworld_module)
target_sources(helloworld_module PRIVATE ${src})
target_include_directories(
helloworld_module
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/..)
# 引用aimrt_module_cpp_interface
target_link_libraries(
helloworld_module
PRIVATE yaml-cpp::yaml-cpp
PUBLIC aimrt::interface::aimrt_module_cpp_interface)
File 5: /src/app/helloworld_app/CMakeLists.txt
Creates the helloworld_app
executable.
file(GLOB_RECURSE src ${CMAKE_CURRENT_SOURCE_DIR}/*.cc)
add_executable(helloworld_app)
target_sources(helloworld_app PRIVATE ${src})
target_include_directories(
helloworld_app
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(
helloworld_app
PRIVATE aimrt::runtime::core
helloworld::helloworld_module)
STEP3: Write Business Code
Business logic is mainly carried by the Module. Refer to the following code to implement a simple Module that parses the input configuration file and prints some simple logs.
File 6: /src/module/helloworld_module/helloworld_module.h
#pragma once
#include "aimrt_module_cpp_interface/module_base.h"
class HelloWorldModule : public aimrt::ModuleBase {
public:
HelloWorldModule() = default;
~HelloWorldModule() override = default;
aimrt::ModuleInfo Info() const override {
return aimrt::ModuleInfo{.name = "HelloWorldModule"};
}
bool Initialize(aimrt::CoreRef core) override;
bool Start() override;
void Shutdown() override;
private:
aimrt::CoreRef core_;
};
File 7: /src/module/helloworld_module/helloworld_module.cc
#include "helloworld_module/helloworld_module.h"
#include "yaml-cpp/yaml.h"
bool HelloWorldModule::Initialize(aimrt::CoreRef core) {
// Save aimrt framework handle
core_ = core;
// Log
AIMRT_HL_INFO(core_.GetLogger(), "Init.");
try {
// Read cfg
auto file_path = core_.GetConfigurator().GetConfigFilePath();
if (!file_path.empty()) {
YAML::Node cfg_node = YAML::LoadFile(file_path.data());
for (const auto& itr : cfg_node) {
std::string k = itr.first.as<std::string>();
std::string v = itr.second.as<std::string>();
AIMRT_HL_INFO(core_.GetLogger(), "cfg [{} : {}]", k, v);
}
}
} catch (const std::exception& e) {
AIMRT_HL_ERROR(core_.GetLogger(), "Init failed, {}", e.what());
return false;
}
AIMRT_HL_INFO(core_.GetLogger(), "Init succeeded.");
return true;
}
bool HelloWorldModule::Start() {
AIMRT_HL_INFO(core_.GetLogger(), "Start succeeded.");
return true;
}
void HelloWorldModule::Shutdown() {
AIMRT_HL_INFO(core_.GetLogger(), "Shutdown succeeded.");
}
STEP4: Determine Deployment Plan and Configuration
We use App mode, manually write the Main function, and register the HelloWorldModule into the AimRT framework via hardcoding. Then, write a configuration to determine some runtime details.
File 8: /src/app/helloworld_app/main.cc
In the following example main function, we capture the kill signal to achieve graceful exit.
#include <csignal>
#include <iostream>
#include "core/aimrt_core.h"
#include "helloworld_module/helloworld_module.h"
using namespace aimrt::runtime::core;
AimRTCore *global_core_ptr_ = nullptr;
void SignalHandler(int sig) {
if (global_core_ptr_ && (sig == SIGINT || sig == SIGTERM)) {
global_core_ptr_->Shutdown();
return;
}
raise(sig);
};
int32_t main(int32_t argc, char **argv) {
signal(SIGINT, SignalHandler);
signal(SIGTERM, SignalHandler);
std::cout << "AimRT start." << std::endl;
try {
AimRTCore core;
global_core_ptr_ = &core;
// register module
HelloWorldModule helloworld_module;
core.GetModuleManager().RegisterModule(helloworld_module.NativeHandle());
AimRTCore::Options options;
options.cfg_file_path = argv[1];
core.Initialize(options);
core.Start();
core.Shutdown();
global_core_ptr_ = nullptr;
} catch (const std::exception &e) {
std::cout << "AimRT run with exception and exit. " << e.what() << std::endl;
return -1;
}
std::cout << "AimRT exit." << std::endl;
return 0;
}
File 9: /src/install/cfg/helloworld_cfg.yaml
The following is a simple example configuration file. Other contents in this configuration file will be introduced in subsequent chapters. Here, focus on two parts:
aimrt.log
node: Specifies some details about logging.HelloWorldModule
node: Configuration forHelloWorldModule
, which can be read in the module.
aimrt:
log: # log配置
core_lvl: INFO # 内核日志等级,可选项:Trace/Debug/Info/Warn/Error/Fatal/Off,不区分大小写
backends: # 日志后端
- type: console # 控制台日志
# 模块自定义配置,框架会为每个模块生成临时配置文件,开发者通过Configurator接口获取该配置文件路径
HelloWorldModule:
key1: val1
key2: val2
STEP5: Start and Test
After completing the code, execute the following commands on Linux to compile:
# cd to root path of project
cmake -B build
cd build
make -j
After compilation, copy the generated executable helloworld_app
and configuration file helloworld_cfg.yaml
to a directory, then execute the following command to run the process and observe the printed logs:
./helloworld_app ./helloworld_cfg.yaml