目录
- 一、工程结构
- 二、源码说明
- /CMakeLists.txt
- /cmake/GetAimRT.cmake
- /src/CMakeLists.txt
- /src/module/helloworld_module/CMakeLists.txt
- /src/app/helloworld_app/CMakeLists.txt
- /src/install/cfg/helloworld_cfg.yaml
- /src/module/helloworld_module/helloworld_module.h
- /src/module/helloworld_module/helloworld_module.cc
- /src/app/helloworld_app/main.cc
- 三、编译与运行
官方 Hello World 文档链接:https://docs.aimrt.org/tutorials/quick_start/helloworld_cpp.html
这里对其增加一些说明。
目前 AimRT 仅支持从源码安装,并且对于第三方依赖,也是通过拉取源码的方式安装,该方式通过 CMake 的FetchContent 实现,虽然该方式可以增强AimRT环境配置的兼容性,但对于封闭网络开发与网络不好的用户不太友好,而且部署一次环境是局部生效的,同一台电脑再新建一个工程还需要拉取源码安装一次。
该 Hello World Demo 涉及以下内容:
- 基于 CMake FetchContent 通过源码引用 AimRT;
- 编写一个基础的基于 AimRT CPP 接口的
Module
; - 使用基础的日志功能;
- 使用基础的配置功能;
- 以 App 模式集成
Module
; - 编译项目,并运行进程以执行
Module
中的逻辑。
一、工程结构
├── CMakeLists.txt
├── cmake
│ └── GetAimRT.cmake # 基于 CMake FetchContent 通过源码引用 AimRT
└── src├── CMakeLists.txt├── install # 存放部署时的一些配置、启动脚本等│ └── cfg│ └── helloworld_cfg.yaml # AimRT配置文件 ├── module # 存放业务逻辑代码│ └── helloworld_module│ ├── CMakeLists.txt│ ├── helloworld_module.cc # Module源文件│ └── helloworld_module.h # Module头文件└── app # 以App模式集成Module└── helloworld_app ├── CMakeLists.txt└── main.cc
二、源码说明
/CMakeLists.txt
根 CMake ,用于构建工程。
# 指定项目所需的最低CMake版本
cmake_minimum_required(VERSION 3.24)# 定义项目的名称为helloworld,并指定该项目将使用C和C++语言
project(helloworld LANGUAGES C CXX)# 设置项目使用的C++标准为C++20
set(CMAKE_CXX_STANDARD 20)
# 要求CMake确保编译器支持指定的C++标准。
# 如果编译器不支持该标准,CMake配置将失败。
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 禁用编译器特定的扩展
set(CMAKE_CXX_EXTENSIONS OFF)# 包含一个名为GetAimRT.cmake的CMake模块
# 用于通过源码引用 AimRT
include(cmake/GetAimRT.cmake)# 添加并处理src子目录
add_subdirectory(src)
/cmake/GetAimRT.cmake
通过源码引用 AimRT
# 包含FetchContent模块,该模块提供了一系列函数和宏,用于从远程仓库获取内容
include(FetchContent)message("get aimrt ...")# 使用FetchContent_Declare函数声明一个名为aimrt的外部项目
FetchContent_Declare(aimrtGIT_REPOSITORY https://github.com/AimRT/aimrt.git # 项目仓库链接GIT_TAG v0.9.2) # 项目版本# 获取aimrt项目属性
# 用于检查项目是否已经被下载、配置和构建
FetchContent_GetProperties(aimrt)# 检查aimrt项目是否已经下载并准备好用于构建
# 如果项目尚未准备好,则调用FetchContent_MakeAvailable函数下载、配置和构建该项目,
# 并将其添加到当前项目的构建系统中,使其可用
if(NOT aimrt_POPULATED)FetchContent_MakeAvailable(aimrt)
endif()
/src/CMakeLists.txt
引用 src 下的各个子目录
add_subdirectory(module/helloworld_module)
add_subdirectory(app/helloworld_app)
/src/module/helloworld_module/CMakeLists.txt
创建helloworld_module
静态库
# 递归地查找当前源目录(${CMAKE_CURRENT_SOURCE_DIR})下所有以.cc结尾的文件,
# 并将这些文件的路径列表赋值给变量src
file(GLOB_RECURSE src ${CMAKE_CURRENT_SOURCE_DIR}/*.cc)# 创建一个名为helloworld_module的静态库目标
add_library(helloworld_module STATIC)
# 为helloworld_module静态库创建一个别名目标helloworld::helloworld_module
# 别名目标允许以更具命名空间风格的方式引用库,这在大型项目中尤其有用,可以避免名称冲突
add_library(helloworld::helloworld_module ALIAS helloworld_module)# 将之前通过file(GLOB_RECURSE ...)找到的源文件(变量src)添加到helloworld_module目标的私有源文件中
# PRIVATE意味这些文件仅对helloworld_module目标本身可见,不会传播到依赖于它的其他目标
target_sources(helloworld_module PRIVATE ${src})# 为helloworld_module目标添加公共头文件目录
# 公共头文件目录意味着这些目录不仅可用于构建helloworld_module本身,
# 还可用于构建依赖于helloworld_module的任何目标
target_include_directories(helloworld_modulePUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/..)# 指定helloworld_module目标需要链接的库
target_link_libraries(helloworld_module# yaml-cpp库是私有依赖项,仅用于构建helloworld_module本身,不会传播到依赖于它的其他目标PRIVATE yaml-cpp::yaml-cpp # 公共依赖项,不仅用于构建helloworld_module,还用于构建依赖于helloworld_module的任何目标PUBLIC aimrt::interface::aimrt_module_cpp_interface)
/src/app/helloworld_app/CMakeLists.txt
创建helloworld_app
可执行文件
# 递归地查找当前源目录(${CMAKE_CURRENT_SOURCE_DIR})下所有以.cc结尾的文件,
# 并将这些文件的路径列表赋值给变量src
file(GLOB_RECURSE src ${CMAKE_CURRENT_SOURCE_DIR}/*.cc)# 添加名为helloworld_app的可执行文件
add_executable(helloworld_app)# 为helloworld_app目标指定源文件
target_sources(helloworld_app PRIVATE ${src})# 为helloworld_app目标指定头文件
target_include_directories(helloworld_appPRIVATE ${CMAKE_CURRENT_SOURCE_DIR})# 为helloworld_app目标指定链接库
target_link_libraries(helloworld_appPRIVATE aimrt::runtime::core helloworld::helloworld_module)
/src/install/cfg/helloworld_cfg.yaml
配置文件
aimrt:log: # log配置core_lvl: Debug # 内核日志等级,可选项:Trace/Debug/Info/Warn/Error/Fatal/Off,不区分大小写backends: # 日志后端- type: console # 控制台日志options:color: true # 是否要彩色打印module_filter: "(.*)" # 支持以正则表达式的形式,来配置哪些模块的日志可以通过本后端处理pattern: "[%c.%f][%l][%t][%n][%G:%R @%F] %v" # 日志格式化输出- type: rotate_file # 将日志打印到文件中options:path: ./log # 日志文件存放目录filename: examples_cpp_hello_world.log # 日志文件名称# 模块自定义业务配置,以模块名称为节点名
HelloWorldModule:name: "HelloWorldModule"array: - name: helloenable: true- name: worldenable: false
/src/module/helloworld_module/helloworld_module.h
Module头文件
// 防止头文件在当前编译单元中被多次引用
#pragma once#include "aimrt_module_cpp_interface/module_base.h"// 定义一个 HelloWorldModule 类,继承自aimrt::ModuleBase
class HelloWorldModule : public aimrt::ModuleBase
{
public:HelloWorldModule() = default;~HelloWorldModule() override = default;// 模块信息,包括name、version、author、description等aimrt::ModuleInfo Info() const override{return aimrt::ModuleInfo{.name = "HelloWorldModule",.major_version = 0,.minor_version = 1,.patch_version = 0,.build_version = 0,.author = "vistar",.description = "AimRT hello world model"};}// 初始化模块资源bool Initialize(aimrt::CoreRef core) override;// 启动模块bool Start() override;// 关闭模块void Shutdown() override;private:// 返回一个日志记录器实例auto GetLogger() { return core_.GetLogger(); }private:aimrt::CoreRef core_;aimrt::parameter::ParameterHandleRef parameter_handle_;
};
/src/module/helloworld_module/helloworld_module.cc
Module源文件
#include "helloworld_module/helloworld_module.h"
#include "yaml-cpp/yaml.h"bool HelloWorldModule::Initialize(aimrt::CoreRef core)
{// Save aimrt framework handlecore_ = core;// LogAIMRT_INFO("Init HelloWorldModule.");try{// Read cfgauto file_path = core_.GetConfigurator().GetConfigFilePath();if (!file_path.empty()){// 将配置写入到临时文件中,使用YAML-CPP加载配置YAML::Node config = YAML::LoadFile(file_path.data());std::string moduleName = config["name"].as<std::string>();AIMRT_INFO("moduleName: {}", moduleName);for (const auto &itemNode : config["array"]){std::string itemName = itemNode["name"].as<std::string>();bool enable = itemNode["enable"].as<bool>();AIMRT_INFO("name: {}, enable: {}", itemName, enable);}}}catch (const std::exception &e){AIMRT_ERROR("Init failed, {}", e.what());return false;}AIMRT_INFO("Init HelloWorldModule succeeded.");return true;
}bool HelloWorldModule::Start()
{AIMRT_INFO("Start HelloWorldModule succeeded.");return true;
}void HelloWorldModule::Shutdown()
{AIMRT_INFO("Shutdown HelloWorldModule succeeded.");
}
/src/app/helloworld_app/main.cc
#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)
{// 注册 ctrl+c 信号监听,用 SignalHandler 函数处理signal(SIGINT, SignalHandler);// 注册 kill 信号监听,用 SignalHandler 函数处理signal(SIGTERM, SignalHandler);std::cout << "AimRT start." << std::endl;try{// 实例化 AimRTCoreAimRTCore core;global_core_ptr_ = &core;// register moduleHelloWorldModule helloworld_module;core.GetModuleManager().RegisterModule(helloworld_module.NativeHandle());// 通过命令行参数读取配置文件路径AimRTCore::Options options;options.cfg_file_path = argv[1];// 初始化AimRT,初始化注册的 Modulecore.Initialize(options);// 启动AimRT,启动注册的 Module// 阻塞主线程等待结束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;
}
三、编译与运行
编译工程和普通编译CMake工程操作一样:
# 在根CMakeList.txt同级目录执行
# 生成构建文件,配置项目
cmake -B build# 编译和链接可执行文件和库文件
cd build
make -j
运行AimRT可执行文件,需要传入配置文件。
编译完成后,将生成的可执行文件helloworld_app
和配置文件helloworld_cfg.yaml
拷贝到一个目录下,然后执行以下命令运行进程:
./helloworld_app helloworld_cfg.yaml
欢迎加QQ群,一起讨论学习:894013891