【AimRT】AimRT Hello World
目录
- 一、工程结构
- 二、源码说明
- /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(
aimrt
GIT_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_module
PUBLIC ${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_app
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
# 为helloworld_app目标指定链接库
target_link_libraries(
helloworld_app
PRIVATE 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: hello
enable: true
- name: world
enable: 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 handle
core_ = core;
// Log
AIMRT_INFO("Init HelloWorldModule.");
try
{
// Read cfg
auto 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
{
// 实例化 AimRTCore
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];
// 初始化AimRT,初始化注册的 Module
core.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