一、項目簡介

AppKit是一個專注於嵌入式Linux應用開發的C++14框架,它的核心目標是提升開發效率和應用健壯性。AppKit提供了一套經過驗證的工具集,覆蓋了線程管理、定時器、文件IO、串口通信、網絡通信、CAN總線、GPIO控制等嵌入式開發中的高頻需求。

https://gitee.com/newgolo/appkit

  1. 雙環境支持: 同時支持ROS和非ROS環境編譯,既能用於機器人項目,也能用於傳統嵌入式應用
  2. 跨平台構建: 內置zbuild編譯系統,支持x86_64和aarch64平台的本地編譯與交叉編譯
  3. 工程化實踐

集成了fmt、spdlog、json、yaml-cpp等成熟的第三方庫,避免重複造輪子

二、整體架構

2.1 頂層架構

AppKit的設計哲學是"分層解耦,按需組合"。整個項目分為三個核心層次:

一個面向嵌入式Linux C++的應用開發框架_#c++

  • 解耦合:核心庫不依賴第三方庫的具體實現,通過接口隔離
  • 可擴展:新增功能模塊只需遵循統一的構建規範,無需修改框架核心

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編譯輸出

一個面向嵌入式Linux C++的應用開發框架_ci_02

關鍵設計點

  1. environ目錄:這是整個構建系統的大腦。envsetup.sh通過source的方式注入環境變量,讓用户在shell中就能調用zbuild_setup等函數。這種設計比傳統的Makefile更靈活,可以根據平台動態配置。
  2. setup.cmake:每個package的CMakeLists.txt都會include這個文件。它統一管理了編譯選項、依賴路徑、宏定義等,避免了各模塊的配置不一致。
  3. prebuilt機制:交叉編譯時,先將庫安裝到prebuilt目錄,其他模塊再從這裏鏈接。這避免了交叉編譯環境下的路徑混亂問題。

2.3 構建系統設計

AppKit支持三種編譯模式:

一個面向嵌入式Linux C++的應用開發框架_#include_03

zbuild的核心機制

通過環境變量ZBUILD_ENABLE來區分編譯模式。當ZBUILD_ENABLE=1時,所有package的CMakeLists.txt都會走非ROS的編譯分支:

一個面向嵌入式Linux C++的應用開發框架_#include_04

這種設計的精妙之處在於:同一份CMakeLists.txt,通過環境變量控制就能適配兩種完全不同的構建系統。避免了維護兩套構建腳本的麻煩。

三、部分代碼

3.1 線程封裝:Runnable模式

AppKit的線程設計採用了經典的Runnable模式:

一個面向嵌入式Linux C++的應用開發框架_#c++_05

使用示例

一個面向嵌入式Linux C++的應用開發框架_#include_06

設計優勢

  • 線程邏輯與線程對象分離,符合單一職責原則
  • run()函數可以訪問Thread對象,方便檢查退出標誌
  • 支持線程克隆,適合需要動態創建多個相同類型線程的場景

3.2 定時器管理:統一調度

嵌入式系統中常常需要管理大量定時器,如果每個定時器都開一個線程,資源消耗很大。AppKit採用了單線程集中調度的方式:

一個面向嵌入式Linux C++的應用開發框架_ci_07

關鍵點

  • 所有定時器共享一個線程,每1ms檢查一次
  • 定時器回調在TimerManager的線程中執行,注意不要阻塞
  • 適合週期在100ms以上的定時任務,對於高精度定時建議使用硬件定時器

3.3 應用框架:Component模式

AppKit提供了一個輕量級的應用框架,類似於ROS的節點概念:

一個面向嵌入式Linux C++的應用開發框架_ci_08

這種設計讓應用的生命週期管理變得清晰:

一個面向嵌入式Linux C++的應用開發框架_#include_09

優勢

  • 統一的生命週期管理,不會遺漏資源釋放
  • 任務自動在後台線程池中執行,無需手動管理線程
  • 支持多組件組合,構建複雜應用

四、實戰示例

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;
        }
    }
}

一個面向嵌入式Linux C++的應用開發框架_ci_10

一個面向嵌入式Linux C++的應用開發框架_ci_11

五、總結

5.1 值得學習的地方

  • Runnable模式的線程封裝:比直接使用std::thread更符合OOP思想,線程邏輯可複用、可測試
  • Component生命週期管理:四階段(init/start/stop/deinit)讓資源管理不再混亂
  • TimerManager的單線程調度:以較小的代價支持多定時器,適合資源受限的嵌入式系統
  • 環境變量驅動的構建系統:靈活性高於硬編碼的Makefile,易於擴展

......