一、项目简介
AppKit是一个专注于嵌入式Linux应用开发的C++14框架,它的核心目标是提升开发效率和应用健壮性。AppKit提供了一套经过验证的工具集,覆盖了线程管理、定时器、文件IO、串口通信、网络通信、CAN总线、GPIO控制等嵌入式开发中的高频需求。
https://gitee.com/newgolo/appkit
双环境支持: 同时支持ROS和非ROS环境编译,既能用于机器人项目,也能用于传统嵌入式应用
跨平台构建: 内置zbuild编译系统,支持x86_64和aarch64平台的本地编译与交叉编译
工程化实践:
集成了fmt、spdlog、json、yaml-cpp等成熟的第三方库,避免重复造轮子
二、整体架构
2.1 顶层架构
AppKit的设计哲学是"分层解耦,按需组合"。整个项目分为三个核心层次:
解耦合:核心库不依赖第三方库的具体实现,通过接口隔离
可扩展:新增功能模块只需遵循统一的构建规范,无需修改框架核心
2.2 目录结构解析
项目采用了标准化的ROS工作空间布局,即使在非ROS环境下也保持了这种结构:
appkit/
├── environ/ # 环境配置中枢
│ ├── cmake/ # CMake工具集
│ │ ├── zbuild.cmake # zbuild函数定义
│ │ ├── finder.cmake # 依赖查找
│ │ └── common.cmake # 通用配置
│ ├── config/ # 平台配置文件
│ │ ├── linux-amd64.sh # x64配置
│ │ └── linux-arm64.sh # ARM64配置
│ ├── zbuild/ # zbuild工具脚本
│ └── envsetup.sh # 环境初始化脚本
├── src/
│ ├── appkit/ # 核心库源码
│ │ ├── inc/ # 公共头文件
│ │ └── src/ # 实现代码
│ ├── third_party/ # 第三方依赖
│ ├── demos/ # 示例程序
│ └── include/ # 全局公共头文件
├── prebuilt/ # 预编译库存放
│ ├── linux-amd64/
│ └── linux-arm64/
├── zbuild/ # 非ROS编译入口
└── ws_output/ # ROS编译输出
关键设计点:
environ目录:这是整个构建系统的大脑。envsetup.sh通过source的方式注入环境变量,让用户在shell中就能调用zbuild_setup等函数。这种设计比传统的Makefile更灵活,可以根据平台动态配置。
setup.cmake:每个package的CMakeLists.txt都会include这个文件。它统一管理了编译选项、依赖路径、宏定义等,避免了各模块的配置不一致。
prebuilt机制:交叉编译时,先将库安装到prebuilt目录,其他模块再从这里链接。这避免了交叉编译环境下的路径混乱问题。
2.3 构建系统设计
AppKit支持三种编译模式:
zbuild的核心机制:
通过环境变量ZBUILD_ENABLE来区分编译模式。当ZBUILD_ENABLE=1时,所有package的CMakeLists.txt都会走非ROS的编译分支:
这种设计的精妙之处在于:同一份CMakeLists.txt,通过环境变量控制就能适配两种完全不同的构建系统。避免了维护两套构建脚本的麻烦。
三、部分代码
3.1 线程封装:Runnable模式
AppKit的线程设计采用了经典的Runnable模式:
使用示例:
设计优势:线程逻辑与线程对象分离,符合单一职责原则run()函数可以访问Thread对象,方便检查退出标志支持线程克隆,适合需要动态创建多个相同类型线程的场景
3.2 定时器管理:统一调度
嵌入式系统中常常需要管理大量定时器,如果每个定时器都开一个线程,资源消耗很大。AppKit采用了单线程集中调度的方式:
关键点:
- 所有定时器共享一个线程,每1ms检查一次定时器回调在TimerManager的线程中执行,注意不要阻塞适合周期在100ms以上的定时任务,对于高精度定时建议使用硬件定时器
3.3 应用框架:Component模式
AppKit提供了一个轻量级的应用框架,类似于ROS的节点概念:
这种设计让应用的生命周期管理变得清晰:
优势:
- 统一的生命周期管理,不会遗漏资源释放任务自动在后台线程池中执行,无需手动管理线程支持多组件组合,构建复杂应用
四、实战示例
4.1 定时器
#include <appkit/async.h>
#include <appkit/console.h>
#include <appkit/datetime.h>
#include <appkit/strutil.h>
#include <appkit/timer.h>
#include <appkit/tracer.h>
usingnamespace appkit;
void test_timer_manager() {
Timer t1(500, true);
Timer t2(1000, true);
TimerManager tm;
tm.registerTimer(t1, []() { TRACE_INFO("500 ms timer timeout!"); });
tm.registerTimer(t2, []() { TRACE_INFO("1000 ms timer timeout!"); });
while (1) {
TRACE_YELLOW("01. start.");
TRACE_YELLOW("02. stop.");
TRACE_YELLOW("input q to quit!");
ConsoleIn ci;
ci.waitInput();
if (ci.asString() == "01") {
tm.start();
} elseif (ci.asString() == "02") {
tm.stop();
} elseif (ci.asString() == "q" || ci.asString() == "quit") {
break;
}
}
tm.stop();
}
void test_timer() {
while (1) {
TRACE_YELLOW("----------timer test--------------");
TRACE_YELLOW("01. timer manager test.");
TRACE_YELLOW(" q. quit");
TRACE_YELLOW("please input selection:");
ConsoleIn ci;
ci.waitInput();
if (ci.asString() == "01") {
test_timer_manager();
} elseif (ci.asString() == "02") {
//
} elseif (ci.asString() == "q" || ci.asString() == "quit") {
break;
}
}
}
五、总结
5.1 值得学习的地方
Runnable模式的线程封装:比直接使用std::thread更符合OOP思想,线程逻辑可复用、可测试
Component生命周期管理:四阶段(init/start/stop/deinit)让资源管理不再混乱
TimerManager的单线程调度:以较小的代价支持多定时器,适合资源受限的嵌入式系统
环境变量驱动的构建系统:灵活性高于硬编码的Makefile,易于扩展
668