525 lines
18 KiB
Markdown
525 lines
18 KiB
Markdown
# 菜单组件设计文档
|
||
|
||
## 1. 概述
|
||
|
||
### 1.1 产品简介
|
||
菜单组件是一个基于状态机的轻量级菜单系统,设计用于嵌入式设备的人机交互界面。它提供了灵活的菜单节点管理、事件驱动的导航机制、权限控制和状态持久化等功能,支持多语言和模块化扩展。
|
||
|
||
### 1.2 设计目标
|
||
- **轻量级**:静态内存分配,无动态内存依赖,适合资源受限的嵌入式设备
|
||
- **模块化**:清晰的分层设计,各功能模块可独立开关
|
||
- **可扩展**:支持自定义状态转换、事件处理和功能扩展
|
||
- **易使用**:简洁的API接口,便于集成到各种应用中
|
||
- **高可靠**:完善的错误处理和状态管理
|
||
|
||
### 1.3 主要特性
|
||
- 基于状态机的菜单导航
|
||
- 静态菜单节点池管理
|
||
- 事件驱动的交互机制
|
||
- 权限管理系统
|
||
- 状态持久化
|
||
- 多语言支持
|
||
- 内存监控
|
||
- 模块化设计,功能可配置
|
||
|
||
## 2. 系统架构
|
||
|
||
### 2.1 分层设计
|
||
菜单组件采用分层架构设计,从下到上分为:
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────┐
|
||
│ 应用层 (Application) │
|
||
├─────────────────────────────────────────────────────────────────┤
|
||
│ API 层 (menu_api) │
|
||
├─────────────────────────────────────────────────────────────────┤
|
||
│ 核心层 (Core) │ 权限管理 │ 持久化 │ 多语言 │ 参数管理 │
|
||
│ ┌─────────────┐ │ ┌─────────┐ │ ┌───────┐ │ ┌───────┐ │ ┌───────┐ │
|
||
│ │ menu_core │ │ │ menu_per│ │ │ menu_per│ │ │ menu_lang │ │ │ menu_param│ │
|
||
│ │ menu_event │ │ │ mission │ │ │ sistence│ │ └───────┘ │ │ └───────┘ │
|
||
│ │ menu_stack │ │ └─────────┘ │ └───────┘ │ │ │
|
||
│ │ menu_hash │ │ │ │ │
|
||
│ └─────────────┘ │ │ │ │
|
||
├─────────────────────────────────────────────────────────────────┤
|
||
│ 端口层 (menu_port) │
|
||
├─────────────────────────────────────────────────────────────────┤
|
||
│ 硬件抽象层 (HAL) │
|
||
└─────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 2.2 模块关系
|
||
|
||
各模块之间的依赖关系如下:
|
||
|
||
- **API层**:对外提供统一的菜单操作接口,依赖核心层和各功能模块
|
||
- **核心层**:包含菜单的核心逻辑,如节点管理、事件处理、状态机等
|
||
- **功能模块**:基于核心层扩展,提供特定功能,如权限管理、持久化等
|
||
- **端口层**:提供平台相关的接口实现,如调试打印、内存操作等
|
||
|
||
## 3. 核心组件设计
|
||
|
||
### 3.1 菜单核心 (menu_core)
|
||
|
||
#### 3.1.1 功能描述
|
||
菜单核心是整个组件的心脏,负责:
|
||
- 菜单节点的注册、注销和管理
|
||
- 状态机的运行和状态转换
|
||
- 事件的处理和分发
|
||
- 导航路径的管理
|
||
|
||
#### 3.1.2 核心流程
|
||
|
||
```mermaid
|
||
stateDiagram-v2
|
||
[*] --> INIT: 菜单初始化
|
||
INIT --> NORMAL: 菜单进入
|
||
NORMAL --> PARAM_EDIT: 进入参数编辑
|
||
PARAM_EDIT --> NORMAL: 保存参数
|
||
PARAM_EDIT --> CONFIRM: 请求确认
|
||
CONFIRM --> NORMAL: 确认操作
|
||
CONFIRM --> PARAM_EDIT: 取消操作
|
||
NORMAL --> ERROR: 发生错误
|
||
ERROR --> NORMAL: 恢复正常
|
||
NORMAL --> [*]: 菜单退出
|
||
```
|
||
|
||
### 3.2 事件管理 (menu_event)
|
||
|
||
#### 3.2.1 功能描述
|
||
事件管理模块负责:
|
||
- 事件的发布和订阅
|
||
- 事件队列的管理
|
||
- 事件优先级的处理
|
||
|
||
#### 3.2.2 事件处理流程
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
A[事件发布] --> B{事件队列满?}
|
||
B -->|是| C[返回错误]
|
||
B -->|否| D[入队事件]
|
||
D --> E[唤醒事件处理]
|
||
E --> F{有事件?}
|
||
F -->|是| G[出队最高优先级事件]
|
||
G --> H[处理事件]
|
||
H --> I{状态转换?}
|
||
I -->|是| J[执行状态转换]
|
||
I -->|否| K[执行事件回调]
|
||
J --> F
|
||
K --> F
|
||
F -->|否| L[等待下一个事件]
|
||
```
|
||
|
||
### 3.3 节点管理 (menu_core + menu_hash)
|
||
|
||
#### 3.3.1 功能描述
|
||
节点管理模块负责:
|
||
- 菜单节点的注册和注销
|
||
- 节点的查找和遍历
|
||
- 节点关系的维护(父子、兄弟关系)
|
||
- 节点的哈希索引
|
||
|
||
#### 3.3.2 节点数据结构
|
||
|
||
菜单节点采用静态数组管理,每个节点包含:
|
||
- 节点ID、父节点ID
|
||
- 节点名称和回调函数
|
||
- 子节点和兄弟节点的引用
|
||
- 节点状态标志(选中、可见、启用等)
|
||
- 权限级别(可选)
|
||
- 参数ID(可选)
|
||
|
||
### 3.4 导航管理 (menu_stack + menu_core)
|
||
|
||
#### 3.4.1 功能描述
|
||
导航管理模块负责:
|
||
- 菜单的上下导航
|
||
- 菜单的进入和退出
|
||
- 导航栈的管理
|
||
- 导航路径的记录
|
||
|
||
#### 3.4.2 导航流程
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
A[当前节点] --> B{按下上键?}
|
||
B -->|是| C[切换到前一个兄弟节点]
|
||
B -->|否| D{按下下键?}
|
||
D -->|是| E[切换到后一个兄弟节点]
|
||
D -->|否| F{按下确认键?}
|
||
F -->|是| G[进入子节点/执行操作]
|
||
F -->|否| H{按下退出键?}
|
||
H -->|是| I[返回父节点]
|
||
H -->|否| J[处理其他事件]
|
||
C --> K[更新当前节点]
|
||
E --> K
|
||
G --> K
|
||
I --> K
|
||
J --> K
|
||
K --> A
|
||
```
|
||
|
||
## 4. 数据结构设计
|
||
|
||
### 4.1 核心数据结构
|
||
|
||
#### 4.1.1 菜单节点 (MenuNode)
|
||
```c
|
||
typedef struct MenuNode {
|
||
MenuNodeId id; ///< 节点ID(唯一)
|
||
MenuNodeId parent_id; ///< 父节点ID(根节点为0)
|
||
const char* name; ///< 菜单名称(或多语言索引)
|
||
MenuCallback enter_cb; ///< 进入回调
|
||
MenuCallback exit_cb; ///< 退出回调
|
||
MenuNodeId first_child_id; ///< 第一个子节点ID
|
||
MenuNodeId next_sibling_id; ///< 下一个兄弟节点ID
|
||
MenuNodeId prev_sibling_id; ///< 上一个兄弟节点ID
|
||
MenuNodeId hash_next_id; ///< 哈希表下一个节点ID
|
||
struct {
|
||
bool is_registered : 1; ///< 是否已注册
|
||
bool is_selected : 1; ///< 是否被选中
|
||
bool is_visible : 1; ///< 是否可见
|
||
bool is_enabled : 1; ///< 是否启用
|
||
unsigned int reserved : 4; ///< 保留位
|
||
} flags;
|
||
MenuPermissionLevel permission_level; ///< 权限级别
|
||
uint16_t param_id; ///< 绑定的参数ID
|
||
} MenuNode;
|
||
```
|
||
|
||
#### 4.1.2 菜单核心上下文 (MenuCoreCtx)
|
||
```c
|
||
typedef struct MenuCoreCtx {
|
||
MenuNode nodes[MENU_CONFIG_MAX_NODES]; ///< 静态菜单节点池
|
||
MenuNodeId hash_table[MENU_CONFIG_HASH_TABLE_SIZE]; ///< 哈希表
|
||
MenuStack stack; ///< 菜单导航栈
|
||
MenuNavPath nav_path; ///< 菜单导航路径
|
||
MenuEventQueue event_queue; ///< 事件队列
|
||
MenuNodeId current_node_id; ///< 当前选中的节点ID
|
||
MenuState current_state; ///< 当前菜单状态
|
||
uint32_t last_refresh_tick; ///< 上次刷新时间
|
||
bool is_initialized; ///< 是否已初始化
|
||
MenuErrCode error_code; ///< 当前错误码
|
||
const char* error_msg; ///< 当前错误信息
|
||
uint16_t node_count; ///< 已注册节点数量
|
||
uint16_t free_node_count; ///< 空闲节点数量
|
||
// 功能模块扩展字段...
|
||
} MenuCoreCtx;
|
||
```
|
||
|
||
### 4.2 状态和事件定义
|
||
|
||
| 状态名称 | 状态值 | 描述 |
|
||
|---------|-------|------|
|
||
| MENU_STATE_INIT | 0 | 初始化状态 |
|
||
| MENU_STATE_NORMAL | 1 | 正常导航状态 |
|
||
| MENU_STATE_PARAM_EDIT | 2 | 参数编辑状态 |
|
||
| MENU_STATE_CONFIRM | 3 | 确认状态 |
|
||
| MENU_STATE_ERROR | 4 | 错误状态 |
|
||
|
||
| 事件名称 | 事件值 | 描述 |
|
||
|---------|-------|------|
|
||
| MENU_EVENT_NONE | 0 | 无事件 |
|
||
| MENU_EVENT_KEY_UP | 1 | 上键事件 |
|
||
| MENU_EVENT_KEY_DOWN | 2 | 下键事件 |
|
||
| MENU_EVENT_KEY_ENTER | 3 | 确认键事件 |
|
||
| MENU_EVENT_KEY_ESC | 4 | 退出键事件 |
|
||
| MENU_EVENT_TIMEOUT | 5 | 超时事件 |
|
||
| MENU_EVENT_CUSTOM_BASE | 10 | 自定义事件基值 |
|
||
|
||
## 5. 接口设计
|
||
|
||
### 5.1 API 概述
|
||
|
||
菜单组件提供了简洁的API接口,主要分为以下几类:
|
||
|
||
| API类别 | 主要功能 | 文件位置 |
|
||
|---------|---------|---------|
|
||
| 初始化与反初始化 | 组件的初始化和反初始化 | menu_api.h |
|
||
| 节点管理 | 节点的注册、注销和更新 | menu_api.h |
|
||
| 事件处理 | 事件的发布和处理 | menu_api.h |
|
||
| 导航操作 | 菜单的上下导航、进入退出 | menu_api.h |
|
||
| 状态查询 | 获取当前状态、节点和路径 | menu_api.h |
|
||
| 权限管理 | 角色注册、权限检查 | menu_api.h |
|
||
| 持久化 | 状态的保存和恢复 | menu_api.h |
|
||
|
||
### 5.2 核心API示例
|
||
|
||
#### 5.2.1 初始化菜单
|
||
```c
|
||
MenuErrCode ret = menu_init();
|
||
if (ret != MENU_ERR_OK) {
|
||
// 处理初始化错误
|
||
}
|
||
```
|
||
|
||
#### 5.2.2 注册菜单节点
|
||
```c
|
||
// 注册单个节点
|
||
MenuNodeId node_id = menu_register_node(
|
||
0, // 自动分配节点ID
|
||
parent_node_id, // 父节点ID
|
||
"Menu Name", // 节点名称
|
||
enter_callback, // 进入回调
|
||
exit_callback // 退出回调
|
||
);
|
||
|
||
// 批量注册节点
|
||
const struct MenuNode nodes[] = {
|
||
{1, 0, "Main Menu", main_enter, main_exit},
|
||
{2, 1, "Sub Menu 1", sub1_enter, sub1_exit},
|
||
{3, 1, "Sub Menu 2", sub2_enter, sub2_exit},
|
||
};
|
||
menu_register_nodes(nodes, sizeof(nodes)/sizeof(nodes[0]));
|
||
```
|
||
|
||
#### 5.2.3 事件处理
|
||
```c
|
||
// 发布事件
|
||
menu_post_event(MENU_EVENT_KEY_UP, 0);
|
||
|
||
// 主循环
|
||
while (1) {
|
||
uint32_t tick = get_system_tick();
|
||
menu_main_loop(tick);
|
||
// 其他任务...
|
||
}
|
||
```
|
||
|
||
## 6. 功能特性
|
||
|
||
### 6.1 权限管理
|
||
|
||
#### 6.1.1 功能描述
|
||
权限管理模块允许:
|
||
- 注册不同的角色和权限级别
|
||
- 为菜单节点设置访问权限
|
||
- 检查当前角色是否有权访问特定节点
|
||
- 动态切换角色
|
||
|
||
#### 6.1.2 权限检查流程
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
A[访问节点请求] --> B{权限功能启用?}
|
||
B -->|否| C[允许访问]
|
||
B -->|是| D[获取当前角色]
|
||
D --> E[获取节点权限级别]
|
||
E --> F{角色权限 >= 节点权限?}
|
||
F -->|是| C
|
||
F -->|否| G[拒绝访问]
|
||
```
|
||
|
||
### 6.2 状态持久化
|
||
|
||
#### 6.2.1 功能描述
|
||
状态持久化模块允许:
|
||
- 保存当前菜单状态到非易失性存储
|
||
- 恢复上次保存的菜单状态
|
||
- 支持自动保存和手动保存
|
||
- 可配置的保存间隔
|
||
|
||
#### 6.2.2 持久化数据结构
|
||
|
||
```c
|
||
typedef struct {
|
||
MenuNodeId current_node_id; ///< 当前节点ID
|
||
MenuState current_state; ///< 当前菜单状态
|
||
MenuNavPath nav_path; ///< 当前导航路径
|
||
MenuStack stack; ///< 当前菜单栈
|
||
uint32_t timestamp; ///< 持久化时间戳
|
||
uint8_t checksum; ///< 数据校验和
|
||
} MenuPersistenceData;
|
||
```
|
||
|
||
### 6.3 多语言支持
|
||
|
||
#### 6.3.1 功能描述
|
||
多语言支持模块允许:
|
||
- 注册不同语言的字符串
|
||
- 切换当前语言
|
||
- 根据当前语言获取对应的字符串
|
||
- 支持动态加载语言包
|
||
|
||
### 6.4 内存监控
|
||
|
||
#### 6.4.1 功能描述
|
||
内存监控模块允许:
|
||
- 获取已使用和空闲的节点数量
|
||
- 获取最大节点数量
|
||
- 监控内存使用情况
|
||
|
||
## 7. 配置与定制
|
||
|
||
### 7.1 配置选项
|
||
|
||
菜单组件提供了丰富的配置选项,通过修改 `menu_config.h` 文件可以定制:
|
||
|
||
| 配置项 | 描述 | 默认值 |
|
||
|-------|------|-------|
|
||
| MENU_CONFIG_MAX_NODES | 最大菜单节点数 | 32 |
|
||
| MENU_CONFIG_STACK_DEPTH | 菜单栈深度 | 8 |
|
||
| MENU_CONFIG_EVENT_QUEUE_LEN | 事件队列长度 | 16 |
|
||
| MENU_CONFIG_ENABLE_PERMISSION | 启用权限管理 | 1 |
|
||
| MENU_CONFIG_ENABLE_PERSISTENCE | 启用状态持久化 | 1 |
|
||
| MENU_CONFIG_ENABLE_LANG | 启用多语言支持 | 1 |
|
||
| MENU_CONFIG_ENABLE_DEBUG | 启用调试打印 | 1 |
|
||
|
||
### 7.2 功能模块开关
|
||
|
||
通过配置宏可以启用或禁用特定功能模块:
|
||
|
||
```c
|
||
// 启用权限管理
|
||
#define MENU_CONFIG_ENABLE_PERMISSION 1U
|
||
|
||
// 禁用多语言支持
|
||
#define MENU_CONFIG_ENABLE_LANG 0U
|
||
```
|
||
|
||
## 8. 示例用法
|
||
|
||
### 8.1 基础菜单示例
|
||
|
||
```c
|
||
#include "menu_api.h"
|
||
|
||
// 回调函数
|
||
MenuErrCode main_enter(MenuNodeId node_id, struct MenuCoreCtx* core_ctx) {
|
||
printf("Enter Main Menu\n");
|
||
return MENU_ERR_OK;
|
||
}
|
||
|
||
MenuErrCode main_exit(MenuNodeId node_id, struct MenuCoreCtx* core_ctx) {
|
||
printf("Exit Main Menu\n");
|
||
return MENU_ERR_OK;
|
||
}
|
||
|
||
// 初始化菜单
|
||
void menu_example_init(void) {
|
||
// 初始化菜单组件
|
||
menu_init();
|
||
|
||
// 注册菜单节点
|
||
MenuNodeId main_node = menu_register_node(0, 0, "Main Menu", main_enter, main_exit);
|
||
MenuNodeId sub1_node = menu_register_node(0, main_node, "Sub Menu 1", NULL, NULL);
|
||
MenuNodeId sub2_node = menu_register_node(0, main_node, "Sub Menu 2", NULL, NULL);
|
||
|
||
// 进入菜单
|
||
menu_enter();
|
||
}
|
||
|
||
// 主循环
|
||
void menu_example_loop(uint32_t tick) {
|
||
menu_main_loop(tick);
|
||
}
|
||
```
|
||
|
||
### 8.2 带权限管理的示例
|
||
|
||
```c
|
||
#include "menu_api.h"
|
||
|
||
void permission_example(void) {
|
||
// 初始化菜单
|
||
menu_init();
|
||
|
||
// 注册角色
|
||
const MenuRole roles[] = {
|
||
{0, "Guest", 1}, // 访客角色,权限级别1
|
||
{1, "User", 5}, // 用户角色,权限级别5
|
||
{2, "Admin", 10}, // 管理员角色,权限级别10
|
||
};
|
||
|
||
for (int i = 0; i < sizeof(roles)/sizeof(roles[0]); i++) {
|
||
menu_permission_register_role(&roles[i]);
|
||
}
|
||
|
||
// 设置当前角色
|
||
menu_permission_set_current_role(0); // 初始为访客角色
|
||
|
||
// 注册菜单节点,设置不同权限级别
|
||
MenuNodeId main_node = menu_register_node(0, 0, "Main", NULL, NULL);
|
||
MenuNodeId public_node = menu_register_node(0, main_node, "Public", NULL, NULL);
|
||
MenuNodeId user_node = menu_register_node(0, main_node, "User", NULL, NULL);
|
||
MenuNodeId admin_node = menu_register_node(0, main_node, "Admin", NULL, NULL);
|
||
|
||
// 设置节点权限级别
|
||
menu_permission_update_node_level(public_node, 1); // 所有人可访问
|
||
menu_permission_update_node_level(user_node, 5); // 用户及以上可访问
|
||
menu_permission_update_node_level(admin_node, 10); // 仅管理员可访问
|
||
|
||
// 检查权限
|
||
bool can_access = menu_permission_check_node_access(admin_node); // 返回false(当前是访客角色)
|
||
|
||
// 切换角色
|
||
menu_permission_set_current_role(2); // 切换到管理员角色
|
||
can_access = menu_permission_check_node_access(admin_node); // 返回true
|
||
}
|
||
```
|
||
|
||
## 9. 目录结构
|
||
|
||
```
|
||
menu/
|
||
├── api/ # API接口层
|
||
│ ├── menu_api.h # 对外API头文件
|
||
│ └── menu_api.c # API实现
|
||
├── src/ # 源代码层
|
||
│ ├── core/ # 核心模块
|
||
│ │ ├── menu_core.h/c # 核心逻辑
|
||
│ │ ├── menu_event.h/c # 事件处理
|
||
│ │ ├── menu_stack.h/c # 栈管理
|
||
│ │ ├── menu_hash.h/c # 哈希表
|
||
│ │ ├── menu_types.h # 类型定义
|
||
│ │ └── menu_config.h # 配置文件
|
||
│ ├── param/ # 参数管理
|
||
│ │ ├── menu_param.h/c
|
||
│ └── lang/ # 多语言支持
|
||
│ ├── menu_lang.h/c
|
||
├── port/ # 端口层
|
||
│ ├── menu_port.h/c
|
||
├── examples/ # 示例代码
|
||
│ ├── language/ # 多语言示例
|
||
│ └── hmi/ # HMI示例
|
||
├── demo/ # 演示程序
|
||
│ ├── demo.c
|
||
│ └── CMakeLists.txt
|
||
├── test/ # 测试代码
|
||
│ ├── menu_test.c
|
||
│ └── CMakeLists.txt
|
||
├── CMakeLists.txt # CMake构建文件
|
||
└── README.md # 设计文档
|
||
```
|
||
|
||
## 10. 构建与集成
|
||
|
||
### 10.1 构建方式
|
||
菜单组件使用CMake进行构建,支持多种编译器和平台:
|
||
|
||
```bash
|
||
mkdir build
|
||
cd build
|
||
cmake ..
|
||
make
|
||
```
|
||
|
||
### 10.2 集成到应用
|
||
|
||
1. 将菜单组件的源文件添加到应用项目中
|
||
2. 包含必要的头文件(主要是 `menu_api.h`)
|
||
3. 根据需要配置 `menu_config.h`
|
||
4. 调用API初始化和使用菜单组件
|
||
|
||
## 11. 版本历史
|
||
|
||
| 版本号 | 发布日期 | 主要变更 |
|
||
|-------|---------|---------|
|
||
| 1.0.0 | 2025-12-19 | 初始版本 |
|
||
| 1.0.1 | 2025-12-20 | 修复了节点查找的bug,优化了事件处理流程 |
|
||
|
||
## 12. 总结
|
||
|
||
菜单组件是一个功能丰富、易于扩展的轻量级菜单系统,适合各种嵌入式设备的人机交互需求。它的模块化设计允许根据项目需求定制功能,静态内存分配使其适合资源受限的环境,完善的API设计使其易于集成到各种应用中。
|
||
|
||
通过合理使用菜单组件,可以快速构建出高效、可靠的嵌入式菜单界面,提高设备的用户体验和可维护性。 |