# 菜单组件设计文档 ## 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设计使其易于集成到各种应用中。 通过合理使用菜单组件,可以快速构建出高效、可靠的嵌入式菜单界面,提高设备的用户体验和可维护性。