一、項目簡介
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>
using namespace 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();
} else if (ci.asString() == "02") {
tm.stop();
} else if (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();
} else if (ci.asString() == "02") {
//
} else if (ci.asString() == "q" || ci.asString() == "quit") {
break;
}
}
}
五、總結
5.1 值得學習的地方
- Runnable模式的線程封裝:比直接使用
std::thread更符合OOP思想,線程邏輯可複用、可測試 - Component生命週期管理:四階段(init/start/stop/deinit)讓資源管理不再混亂
- TimerManager的單線程調度:以較小的代價支持多定時器,適合資源受限的嵌入式系統
- 環境變量驅動的構建系統:靈活性高於硬編碼的Makefile,易於擴展
......