Files
2026-07-15 15:57:37 +08:00

18 KiB
Raw Permalink Blame History

模块拆分 - 应用执行层 (Docs/50_module-breakdown/mod-app.md)

本模块描述手环的 App 调度管理器AppManager、前台/后台应用架构、生命周期钩子OnStart, onRun, onClose, onEvent、事件分发机制、CPU占用控制以及各功能模块的页面渲染与状态机调度设计。


1. 模块职责说明

1.1 核心职责

职责领域 描述
应用生命周期管理 维护应用注册表,管理应用的启动、切换、关闭流程
前台/后台应用分离 支持前台应用独占屏幕和后台应用持续监听的双轨运行模式
事件分发路由 实现前台优先、后台兜底、未处理丢弃的事件分发策略
CPU占用控制 监控应用 onRun() 执行时间防止单个应用长时间占用CPU
应用状态维护 维护活跃应用指针、应用运行状态标记等核心数据结构

1.2 应用类型划分

系统将应用分为两大类,通过 AppType 枚举区分:

应用类型 标识 特性 生命周期
前台应用 APP_TYPE_FOREGROUND 独占屏幕显示,响应用户交互,一次只有一个活跃 OnStartonRun(循环) → onClose
后台应用 APP_TYPE_BACKGROUND 无屏幕显示,持续运行监听特定事件,可多个同时运行 注册后自动运行,无显式启动/关闭

1.3 应用列表

应用ID 应用名称 类型 职责描述
APP_ID_CLOCK ClockApp 前台 待机时钟应用,中央以 32x64px 超大点阵渲染当前时分,右上角渲染 16x32px 电量百分比及充电指示;后台监听射频报警信号
APP_ID_MENU MenuApp 前台 主菜单应用,单屏单条目显示交互,支持 ▲/▼ 键高亮切换,确认键进入对应子页面
APP_ID_PAIR PairApp 前台 配对与微调应用,对码捕捉到未配对 24 位射频码时拉起,提供微调序号选框,确认后存入持久化 Flash
APP_ID_ALARM AlarmApp 前台 传感器报警应用,被后台监测唤醒拉起,屏幕闪烁红色粗边框,调度马达振动波形与 LED 警示颜色
APP_ID_SOS SOSApp 前台 主动求救应用,短按/长按 SOS 物理按键拉起,周期性发射 0x08 (SOS) 无线电数据,触发最高优先级的持续马达震动及红色幻彩灯爆闪

2. 核心数据结构

2.1 应用描述符结构 (WristbandApp)

typedef struct {
    AppID app_id;           // 应用唯一标识 (APP_ID_CLOCK/MENU/PAIR/ALARM/SOS)
    AppType app_type;       // 应用类型 (APP_TYPE_FOREGROUND / APP_TYPE_BACKGROUND)
    char *app_name;         // 应用名称(用于调试日志输出)
    
    // 生命周期回调函数指针
    void (*OnStart)(void);         // 启动回调:应用切入前台,加载资源、初始化显示
    void (*onRun)(void);           // 循环回调:主循环轮询调用,执行非阻塞业务逻辑
    void (*onClose)(void);         // 关闭回调:应用退出前台,保存状态、清理外设
    EventResult (*onEvent)(SystemEvent evt); // 事件回调:接收并处理系统事件
    
    // 运行状态标记
    bit is_active;          // 当前是否活跃(仅对前台应用有效,标识是否在前台运行)
    bit is_running;         // 应用是否处于运行状态用于CPU占用控制标记是否允许调用onRun
} WristbandApp;

2.2 应用ID枚举

typedef enum {
    APP_ID_CLOCK,           // 0: 待机时钟应用
    APP_ID_MENU,            // 1: 主菜单应用
    APP_ID_PAIR,            // 2: 配对与微调应用
    APP_ID_ALARM,           // 3: 传感器报警应用
    APP_ID_SOS,             // 4: 主动求救应用
    APP_ID_MAX              // 5: 应用数量上限(用于数组大小)
} AppID;

2.3 应用类型枚举

typedef enum {
    APP_TYPE_FOREGROUND,    // 前台应用:独占屏幕和用户交互
    APP_TYPE_BACKGROUND     // 后台应用:无屏幕显示,仅监听事件
} AppType;

2.4 事件处理返回值

typedef enum {
    EVENT_HANDLED,          // 事件已处理,停止继续分发
    EVENT_IGNORED           // 事件未处理,继续传递给下一个应用
} EventResult;

3. 应用管理器核心接口

3.1 初始化与注册接口

接口名称 函数签名 功能描述
AppManager_Init void AppManager_Init(void) 初始化应用管理器,初始化应用注册表,设置默认活跃应用为 ClockApp
AppManager_RegisterApp void AppManager_RegisterApp(AppID app_id, AppType app_type, char *app_name, AppStartFunc on_start, AppRunFunc on_run, AppCloseFunc on_close, AppEventFunc on_event) 注册应用到注册表,绑定生命周期回调函数

3.2 应用切换接口

接口名称 函数签名 功能描述
AppManager_StartApp void AppManager_StartApp(AppID app_id) 启动指定应用:关闭当前活跃应用 → 切换活跃应用指针 → 启动新应用
AppManager_CloseApp void AppManager_CloseApp(void) 关闭当前活跃应用回退到默认应用ClockApp
AppManager_GetActiveAppID AppID AppManager_GetActiveAppID(void) 获取当前活跃应用的 ID

3.3 事件分发接口

接口名称 函数签名 功能描述
AppManager_DispatchEvent void AppManager_DispatchEvent(SystemEvent evt) 分发事件到应用层:前台优先 → 后台兜底 → 未处理丢弃
AppManager_RunActiveApp void AppManager_RunActiveApp(void) 运行当前活跃应用的 onRun 回调(带 CPU 占用控制)

4. 应用生命周期调度机制

4.1 生命周期状态转换

graph LR
    subgraph 前台应用状态
        Idle["未注册 / 空闲"] -->|注册| Registered["已注册"]
        Registered -->|StartApp| Active["活跃中 (OnStart)"]
        Active -->|onRun循环| Running["运行中"]
        Running -->|StartApp(其他)| Closing["关闭中 (onClose)"]
        Closing -->|切换完成| Active
        Running -->|CloseApp| Closing
        Closing -->|回退完成| Default["默认状态 (ClockApp)"]
    end
    
    subgraph 后台应用状态
        BG_Idle["未注册"] -->|注册| BG_Running["持续运行"]
        BG_Running -->|接收事件| BG_Handling["事件处理中"]
        BG_Handling -->|处理完成| BG_Running
    end

4.2 生命周期回调语义

回调函数 调用时机 职责要求
OnStart() 应用切入前台时 初始化显示、加载资源、重置状态变量
onRun() 主循环每轮调用 执行非阻塞业务逻辑(如动画更新、超时检测),必须控制执行时间
onClose() 应用退出前台时 保存状态、清理外设(关屏、停振、关闭射频等)
onEvent(evt) 收到事件时 处理事件,返回 EVENT_HANDLEDEVENT_IGNORED

4.3 应用切换流程

AppManager_StartApp(target_app_id) 执行流程:

1. 参数有效性检查
   ├── app_id >= APP_ID_MAX → 返回错误
   ├── 目标应用非前台应用 → 返回错误
   └── 目标应用未注册 (OnStart == NULL) → 返回错误

2. 目标应用已是活跃应用 → 直接返回

3. 关闭当前活跃应用
   └── active_app->onClose() → 清理外设、保存状态

4. 切换活跃应用指针
   └── active_app = &app_registry[target_app_id]

5. 启动新应用
   └── active_app->OnStart() → 初始化显示、加载资源

5. 事件分发机制

5.1 事件分发规则

事件在应用层的分发遵循以下顺序,前台应用优先处理,后台应用作为兜底:

事件分发流程 (AppManager_DispatchEvent):

┌──────────────────────────────────────────────────────────────┐
│  Step 1: 前台应用优先处理                                    │
│  ┌────────────────────────────────────────────────────────┐ │
│  │ if (active_app != NULL && active_app->is_active)       │ │
│  │ {                                                      │ │
│  │     result = active_app->onEvent(evt);                 │ │
│  │     if (result == EVENT_HANDLED)                       │ │
│  │         return;  // 前台应用已处理,结束分发            │ │
│  │ }                                                      │ │
│  └────────────────────────────────────────────────────────┘ │
├──────────────────────────────────────────────────────────────┤
│  Step 2: 后台应用兜底处理                                    │
│  ┌────────────────────────────────────────────────────────┐ │
│  │ for (i = 0; i < APP_ID_MAX; i++)                      │ │
│  │ {                                                      │ │
│  │     bg_app = &app_registry[i];                         │ │
│  │     // 仅处理后台应用                                   │ │
│  │     if (bg_app->app_type != APP_TYPE_BACKGROUND ||    │ │
│  │         bg_app->onEvent == NULL)                       │ │
│  │         continue;                                     │ │
│  │                                                       │ │
│  │     result = bg_app->onEvent(evt);                    │ │
│  │     if (result == EVENT_HANDLED)                      │ │
│  │         return;  // 某后台应用已处理,结束分发          │ │
│  │ }                                                      │ │
│  └────────────────────────────────────────────────────────┘ │
├──────────────────────────────────────────────────────────────┤
│  Step 3: 事件丢弃                                           │
│  ┌────────────────────────────────────────────────────────┐ │
│  │ // 前台和后台应用均未处理,事件被丢弃                   │ │
│  └────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────┘

5.2 事件处理示例

以下为各应用的典型事件处理行为:

事件类型 ClockApp MenuApp PairApp AlarmApp SOSApp
KEY_EVENT_UP_CLICK IGNORED HANDLED (上移) IGNORED HANDLED (清除) IGNORED
KEY_EVENT_DOWN_CLICK IGNORED HANDLED (下移) IGNORED HANDLED (清除) IGNORED
KEY_EVENT_SOS_CLICK HANDLED (启动SOS) IGNORED IGNORED HANDLED (清除) IGNORED
KEY_EVENT_UP_DOWN_COMB HANDLED (启动Menu) IGNORED IGNORED IGNORED IGNORED
KEY_EVENT_SOS_LONG HANDLED (绑定IPC) IGNORED HANDLED (保存) IGNORED HANDLED (退出)
KEY_EVENT_DOWN_LONG IGNORED HANDLED (返回) HANDLED (退出) IGNORED HANDLED (退出)

6. CPU 占用控制机制

6.1 设计目的

为防止单个应用的 onRun() 回调长时间占用 CPU 导致系统响应迟钝或卡死,框架引入 执行时间监控机制

6.2 控制参数

参数名称 定义 默认值 说明
APP_ONRUN_MAX_TICKS onRun 最大执行时间阈值 5 (ms) 超过此时间则暂停应用运行

6.3 监控机制

AppManager_RunActiveApp() 执行流程:

1. 检查应用状态
   └── if (active_app == NULL || !active_app->is_active) → 直接返回

2. 记录开始时间
   └── start_tick = ms_tick

3. 执行应用的 onRun() 回调
   └── active_app->onRun()

4. 计算执行耗时
   └── elapsed_ticks = ms_tick - start_tick

5. 判断是否超时
   ├── if (elapsed_ticks > APP_ONRUN_MAX_TICKS)
   │   └── active_app->is_running = 0  // 暂停应用
   └── else
       └── active_app->is_running = 1  // 继续运行

6.4 超时恢复

  • 超时后应用的 is_running 标记被置为 0下一次主循环将跳过该应用的 onRun() 调用
  • 应用可在下一次循环中自动恢复运行(is_runningonRun 调用前被检查,调用后被更新)
  • 此机制不会杀死应用,仅暂停其循环执行,允许应用在后续恢复

7. 应用实现细节

7.1 ClockApp时钟应用

职责:待机时钟显示、后台射频监测

生命周期实现

回调 实现逻辑
OnStart 初始化状态为 NORMAL设置重绘标志
onRun 分钟变化时重绘时钟;后台监听射频信号,匹配到已配对传感器触发报警
onClose 清理资源
onEvent 处理组合键进入菜单、SOS 按键进入求救模式、长按绑定 IPC

7.2 MenuApp菜单应用

职责:主菜单导航

生命周期实现

回调 实现逻辑
OnStart 初始化菜单选择项为 0关闭射频显示菜单页面
onRun 无持续运行逻辑
onClose 重新开启射频接收
onEvent 处理上/下键切换菜单项、确认键进入配对、长按返回时钟

7.3 PairApp配对应用

职责:传感器配对与保存

生命周期实现

回调 实现逻辑
OnStart 进入等待状态,开启射频接收,显示雷达搜索页面
onRun 更新雷达动画;监听射频信号捕获对码;检测超时
onClose 关闭射频,重置配对状态
onEvent 处理长按退出、确认保存对码

7.4 AlarmApp报警应用

职责:入侵警报显示与响应

生命周期实现

回调 实现逻辑
OnStart 设置状态为 ALARM显示报警页面重置报警定时器
onRun 控制报警边框闪烁效果
onClose 停止报警效果马达、LED恢复正常状态
onEvent 任何按键清除报警

7.5 SOSApp求救应用

职责:主动求救模式

生命周期实现

回调 实现逻辑
OnStart 设置状态为 SOS_EMITTED显示 SOS 页面
onRun 每 500ms 发射一次射频求救帧;控制红色边框闪烁
onClose 停止求救效果马达、LED恢复正常状态
onEvent 长按任意键解除求救

8. 页面渲染接口

接口名称 函数签名 所属应用 功能描述
UI_ShowClockPage void UI_ShowClockPage(SystemState state, u8 hour, u8 min) ClockApp 绘制时钟页面,包含时间显示和电量指示
UI_ShowPairMenuPage void UI_ShowPairMenuPage(u8 selected_item) MenuApp 绘制配对菜单页面,显示选中项图标和名称
UI_ShowPairWaitPage void UI_ShowPairWaitPage(u8 frame) PairApp 绘制雷达搜索页面,包含动画帧
UI_ShowPairConfirmPage void UI_ShowPairConfirmPage(u32 addr) PairApp 绘制配对确认页面,显示捕获到的射频地址
UI_ShowPairSuccessPage void UI_ShowPairSuccessPage(u32 addr) PairApp 绘制配对成功页面
UI_ShowPairFailPage void UI_ShowPairFailPage(u8 reason) PairApp 绘制配对失败页面
UI_ShowAlarmPage void UI_ShowAlarmPage(char *name, u8 severity) AlarmApp 绘制报警页面,包含红色边框和传感器名称
UI_ShowSosPage void UI_ShowSosPage(void) SOSApp 绘制 SOS 求救页面
UI_ShowBindingPage void UI_ShowBindingPage(void) ClockApp 绘制 IPC 绑定页面

9. 应用注册表结构

应用管理器维护一个静态数组 app_registry[APP_ID_MAX],存储所有已注册的应用:

static WristbandApp app_registry[APP_ID_MAX];
static WristbandApp *active_app = NULL;  // 当前活跃前台应用指针
#define DEFAULT_APP_ID APP_ID_CLOCK      // 默认应用为时钟

注册流程:

  1. 系统初始化时调用 AppManager_Init() 初始化注册表
  2. 各应用模块调用 AppManager_RegisterApp() 注册自身
  3. 最后调用 AppManager_Init() 设置默认活跃应用

10. 主循环集成

主循环中应用管理器的调用顺序:

while(1)
{
    // 1. 处理串口调试输入
    cmd = Uart_RxChar();
    if (cmd != '\0')
        // 解析命令,生成事件,调用 EventQueue_Push 或 EventQueue_InsertFront
    
    // 2. 处理按键事件缓冲区
    if (key_event_buf != KEY_EVENT_NONE)
        // 生成事件,根据优先级选择入队(Push)或插队(InsertFront)
        key_event_buf = KEY_EVENT_NONE;
    
    // 3. 处理事件队列
    while (!EventQueue_IsEmpty())
    {
        evt = EventQueue_Pop();
        AppManager_DispatchEvent(evt);
    }
    
    // 4. 运行当前活跃应用带CPU占用控制
    AppManager_RunActiveApp();
    
    // 5. 其他系统监控如LED/马达状态输出)
}