整定一版
This commit is contained in:
107
src/core/menu_config.h
Normal file
107
src/core/menu_config.h
Normal file
@ -0,0 +1,107 @@
|
||||
/**
|
||||
**********************************************************************************************************************
|
||||
* @file menu_config.h
|
||||
* @brief 菜单组件核心配置文件
|
||||
* @author menu_component
|
||||
* @date 2025-12-19
|
||||
**********************************************************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef MENU_CONFIG_H
|
||||
#define MENU_CONFIG_H
|
||||
|
||||
/* Includes ----------------------------------------------------------------------------------------------------------*/
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/* 配置项 -----------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/* 菜单节点配置 */
|
||||
#define MENU_CONFIG_MAX_NODES 32U /* 最大菜单节点数 */
|
||||
#define MENU_CONFIG_HASH_TABLE_SIZE 31U /* 哈希表大小(必须为质数) */
|
||||
#define MENU_CONFIG_STACK_DEPTH 8U /* 菜单栈深度 */
|
||||
#define MENU_CONFIG_MAX_STATE_TRANSITIONS 16U /* 最大自定义状态转换规则数 */
|
||||
|
||||
/* 事件队列配置 */
|
||||
#define MENU_CONFIG_EVENT_QUEUE_LEN 16U /* 事件队列长度 */
|
||||
#define MENU_CONFIG_EVENT_MAX_PRIORITY 4U /* 事件最大优先级(0-3) */
|
||||
|
||||
/* 功能开关配置 */
|
||||
#define MENU_CONFIG_ENABLE_ASSERT 1U /* 是否启用断言 */
|
||||
#define MENU_CONFIG_ENABLE_DEBUG 1U /* 是否启用调试打印 */
|
||||
#define MENU_CONFIG_ENABLE_MEM_MONITOR 1U /* 是否启用内存监控 */
|
||||
#define MENU_CONFIG_ENABLE_PARAM 1U /* 是否启用参数管理 */
|
||||
#define MENU_CONFIG_ENABLE_LANG 1U /* 是否启用多语言支持 */
|
||||
#define MENU_CONFIG_ENABLE_MODBUS_MAP 1U /* 是否启用Modbus映射 */
|
||||
#define MENU_CONFIG_ENABLE_STATE_MACHINE_EXT 1U /* 是否启用状态机扩展 */
|
||||
#define MENU_CONFIG_ENABLE_PERMISSION 1U /* 是否启用权限管理 */
|
||||
#define MENU_CONFIG_ENABLE_PERSISTENCE 1U /* 是否启用状态持久化 */
|
||||
|
||||
/* 权限管理配置 */
|
||||
#define MENU_CONFIG_PERMISSION_MAX_ROLES 8U /* 最大支持的角色数量 */
|
||||
#define MENU_CONFIG_PERMISSION_MAX_LEVEL 16U /* 最大权限级别 */
|
||||
|
||||
/* 状态持久化配置 */
|
||||
#define MENU_CONFIG_PERSISTENCE_MAX_SIZE 256U /* 持久化数据的最大大小(字节) */
|
||||
#define MENU_CONFIG_PERSISTENCE_AUTO_SAVE_INTERVAL 5000U /* 自动保存间隔(ms) */
|
||||
#define MENU_CONFIG_PERSISTENCE_ENABLE_ENCRYPT 1U /* 是否启用持久化数据加密 */
|
||||
|
||||
/* 类型定义 ---------------------------------------------------------------------------------------------------------*/
|
||||
typedef uint16_t MenuNodeId; /* 菜单节点ID类型 */
|
||||
typedef uint8_t MenuState; /* 菜单状态类型 */
|
||||
typedef uint8_t MenuEventType; /* 事件类型 */
|
||||
typedef uint16_t MenuErrCode; /* 错误码类型 */
|
||||
|
||||
/* 状态定义 ---------------------------------------------------------------------------------------------------------*/
|
||||
#define MENU_STATE_INIT 0U /* 初始化状态 */
|
||||
#define MENU_STATE_NORMAL 1U /* 正常导航状态 */
|
||||
#define MENU_STATE_PARAM_EDIT 2U /* 参数编辑状态 */
|
||||
#define MENU_STATE_CONFIRM 3U /* 确认状态 */
|
||||
#define MENU_STATE_ERROR 4U /* 错误状态 */
|
||||
|
||||
/* 事件类型定义 -----------------------------------------------------------------------------------------------------*/
|
||||
#define MENU_EVENT_NONE 0U /* 无事件 */
|
||||
#define MENU_EVENT_KEY_UP 1U /* 上键事件 */
|
||||
#define MENU_EVENT_KEY_DOWN 2U /* 下键事件 */
|
||||
#define MENU_EVENT_KEY_ENTER 3U /* 确认键事件 */
|
||||
#define MENU_EVENT_KEY_ESC 4U /* 退出键事件 */
|
||||
#define MENU_EVENT_TIMEOUT 5U /* 超时事件 */
|
||||
#define MENU_EVENT_CUSTOM_BASE 10U /* 自定义事件基值 */
|
||||
|
||||
/* 错误码定义 -------------------------------------------------------------------------------------------------------*/
|
||||
#define MENU_ERR_OK 0U /* 成功 */
|
||||
#define MENU_ERR_INVALID_PARAM 1U /* 参数无效 */
|
||||
#define MENU_ERR_OUT_OF_MEMORY 2U /* 内存不足 */
|
||||
#define MENU_ERR_NODE_NOT_FOUND 3U /* 节点未找到 */
|
||||
#define MENU_ERR_QUEUE_FULL 4U /* 队列已满 */
|
||||
#define MENU_ERR_QUEUE_EMPTY 5U /* 队列为空 */
|
||||
#define MENU_ERR_INVALID_STATE 6U /* 无效状态 */
|
||||
#define MENU_ERR_OPERATION_FAILED 7U /* 操作失败 */
|
||||
#define MENU_ERR_NOT_INITIALIZED 8U /* 未初始化 */
|
||||
#define MENU_ERR_ALREADY_EXISTS 9U /* 已存在 */
|
||||
#define MENU_ERR_STACK_OVERFLOW 10U /* 栈溢出 */
|
||||
#define MENU_ERR_STACK_UNDERFLOW 11U /* 栈下溢 */
|
||||
#define MENU_ERR_STACK_EMPTY 12U /* 栈为空 */
|
||||
#define MENU_ERR_NAV_PATH_OVERFLOW 13U /* 导航路径溢出 */
|
||||
#define MENU_ERR_INVALID_CALLBACK 14U /* 无效回调函数 */
|
||||
#define MENU_ERR_INVALID_TRANSITION 15U /* 无效状态转换 */
|
||||
#define MENU_ERR_NOT_SUPPORTED 16U /* 不支持的操作 */
|
||||
|
||||
/* 断言定义 ---------------------------------------------------------------------------------------------------------*/
|
||||
#if MENU_CONFIG_ENABLE_ASSERT
|
||||
extern void menu_assert_failed(const char* expr, const char* file, uint32_t line);
|
||||
#define MENU_ASSERT(expr) ((expr) ? (void)0U : menu_assert_failed(#expr, __FILE__, __LINE__))
|
||||
#else
|
||||
#define MENU_ASSERT(expr) ((void)0U)
|
||||
#endif
|
||||
|
||||
/* 调试打印定义 -----------------------------------------------------------------------------------------------------*/
|
||||
#if MENU_CONFIG_ENABLE_DEBUG
|
||||
extern void menu_debug_print(const char* fmt, ...);
|
||||
#define MENU_DEBUG(fmt, ...) menu_debug_print(fmt, ##__VA_ARGS__)
|
||||
#else
|
||||
#define MENU_DEBUG(fmt, ...) ((void)0U)
|
||||
#endif
|
||||
|
||||
#endif /* MENU_CONFIG_H */
|
||||
778
src/core/menu_core.c
Normal file
778
src/core/menu_core.c
Normal file
@ -0,0 +1,778 @@
|
||||
/**
|
||||
**********************************************************************************************************************
|
||||
* @file menu_core.c
|
||||
* @brief 菜单组件核心功能实现
|
||||
* @author menu_component
|
||||
* @date 2025-12-19
|
||||
**********************************************************************************************************************
|
||||
*/
|
||||
|
||||
/* Includes ----------------------------------------------------------------------------------------------------------*/
|
||||
#include "menu_types.h"
|
||||
#include "menu_core.h"
|
||||
#include "menu_hash.h"
|
||||
#include "menu_event.h"
|
||||
#include "menu_stack.h"
|
||||
|
||||
#if MENU_CONFIG_ENABLE_PERMISSION
|
||||
#include "menu_permission.h"
|
||||
#endif
|
||||
|
||||
#if MENU_CONFIG_ENABLE_PERSISTENCE
|
||||
#include "menu_persistence.h"
|
||||
#endif
|
||||
|
||||
/* 私有函数原型 -----------------------------------------------------------------------------------------------------*/
|
||||
static MenuErrCode menu_core_init_nodes(MenuCoreCtx* core_ctx);
|
||||
static MenuNode* menu_core_get_free_node(MenuCoreCtx* core_ctx);
|
||||
static MenuErrCode menu_core_state_transition(MenuCoreCtx* core_ctx, MenuEventType event);
|
||||
static MenuErrCode menu_core_navigate_enter(MenuCoreCtx* core_ctx, MenuNodeId node_id);
|
||||
static MenuErrCode menu_core_navigate_back(MenuCoreCtx* core_ctx);
|
||||
static MenuErrCode menu_core_navigate_up(MenuCoreCtx* core_ctx);
|
||||
static MenuErrCode menu_core_navigate_down(MenuCoreCtx* core_ctx);
|
||||
|
||||
/* 全局变量 ---------------------------------------------------------------------------------------------------------*/
|
||||
static MenuCoreCtx sg_menu_core_ctx;
|
||||
|
||||
/* 状态转换规则定义 -------------------------------------------------------------------------------------------------*/
|
||||
#if MENU_CONFIG_ENABLE_STATE_MACHINE_EXT
|
||||
static MenuStateTransition sg_default_transitions[] = {
|
||||
// 初始状态转换
|
||||
{MENU_STATE_INIT, MENU_EVENT_KEY_ENTER, MENU_STATE_NORMAL, NULL, "Init to Normal"},
|
||||
{MENU_STATE_INIT, MENU_EVENT_TIMEOUT, MENU_STATE_ERROR, NULL, "Init timeout to Error"},
|
||||
|
||||
// 正常状态转换
|
||||
{MENU_STATE_NORMAL, MENU_EVENT_KEY_ENTER, MENU_STATE_PARAM_EDIT, NULL, "Normal to Param Edit"},
|
||||
{MENU_STATE_NORMAL, MENU_EVENT_KEY_ESC, MENU_STATE_NORMAL, NULL, "Normal to Normal"},
|
||||
{MENU_STATE_NORMAL, MENU_EVENT_KEY_UP, MENU_STATE_NORMAL, NULL, "Normal to Normal (Up)"},
|
||||
{MENU_STATE_NORMAL, MENU_EVENT_KEY_DOWN, MENU_STATE_NORMAL, NULL, "Normal to Normal (Down)"},
|
||||
|
||||
// 参数编辑状态转换
|
||||
{MENU_STATE_PARAM_EDIT, MENU_EVENT_KEY_ENTER, MENU_STATE_CONFIRM, NULL, "Param Edit to Confirm"},
|
||||
{MENU_STATE_PARAM_EDIT, MENU_EVENT_KEY_ESC, MENU_STATE_NORMAL, NULL, "Param Edit to Normal"},
|
||||
|
||||
// 确认状态转换
|
||||
{MENU_STATE_CONFIRM, MENU_EVENT_KEY_ENTER, MENU_STATE_NORMAL, NULL, "Confirm to Normal"},
|
||||
{MENU_STATE_CONFIRM, MENU_EVENT_KEY_ESC, MENU_STATE_PARAM_EDIT, NULL, "Confirm to Param Edit"},
|
||||
|
||||
// 错误状态转换
|
||||
{MENU_STATE_ERROR, MENU_EVENT_KEY_ESC, MENU_STATE_INIT, NULL, "Error to Init"},
|
||||
};
|
||||
|
||||
#define MENU_DEFAULT_TRANSITION_COUNT (sizeof(sg_default_transitions) / sizeof(MenuStateTransition))
|
||||
#endif
|
||||
|
||||
/* 函数实现 ---------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* @brief 初始化菜单核心
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_core_init(void)
|
||||
{
|
||||
MenuCoreCtx* core_ctx = &sg_menu_core_ctx;
|
||||
|
||||
// 初始化核心上下文
|
||||
core_ctx->is_initialized = false;
|
||||
core_ctx->current_node_id = 0;
|
||||
core_ctx->current_state = MENU_STATE_INIT;
|
||||
core_ctx->last_refresh_tick = 0;
|
||||
core_ctx->error_code = MENU_ERR_OK;
|
||||
core_ctx->error_msg = NULL;
|
||||
core_ctx->node_count = 0;
|
||||
core_ctx->free_node_count = MENU_CONFIG_MAX_NODES;
|
||||
|
||||
#if MENU_CONFIG_ENABLE_STATE_MACHINE_EXT
|
||||
core_ctx->custom_transition_count = 0;
|
||||
#endif
|
||||
|
||||
// 初始化各个组件
|
||||
menu_core_init_nodes(core_ctx);
|
||||
menu_hash_init(core_ctx);
|
||||
menu_stack_init(&core_ctx->stack, MENU_CONFIG_STACK_DEPTH);
|
||||
menu_nav_path_init(&core_ctx->nav_path, MENU_CONFIG_STACK_DEPTH);
|
||||
menu_event_queue_init(&core_ctx->event_queue);
|
||||
|
||||
#if MENU_CONFIG_ENABLE_STATE_MACHINE_EXT
|
||||
// 注册默认状态转换规则
|
||||
for (uint16_t i = 0; i < MENU_DEFAULT_TRANSITION_COUNT; i++) {
|
||||
core_ctx->custom_transitions[i] = sg_default_transitions[i];
|
||||
}
|
||||
core_ctx->custom_transition_count = MENU_DEFAULT_TRANSITION_COUNT;
|
||||
#endif
|
||||
|
||||
#if MENU_CONFIG_ENABLE_PERMISSION
|
||||
// 初始化权限管理模块
|
||||
menu_permission_init();
|
||||
#endif
|
||||
|
||||
#if MENU_CONFIG_ENABLE_PERSISTENCE
|
||||
// 初始化状态持久化模块
|
||||
menu_persistence_init();
|
||||
#endif
|
||||
|
||||
core_ctx->is_initialized = true;
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 反初始化菜单核心
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_core_deinit(void)
|
||||
{
|
||||
MenuCoreCtx* core_ctx = &sg_menu_core_ctx;
|
||||
|
||||
if (!core_ctx->is_initialized) {
|
||||
return MENU_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
// 清空事件队列
|
||||
menu_event_queue_clear(&core_ctx->event_queue);
|
||||
|
||||
// 清空栈和导航路径
|
||||
menu_stack_clear(&core_ctx->stack);
|
||||
menu_nav_path_clear(&core_ctx->nav_path);
|
||||
|
||||
// 重置哈希表
|
||||
menu_hash_init(core_ctx);
|
||||
|
||||
// 重置所有节点
|
||||
menu_core_init_nodes(core_ctx);
|
||||
|
||||
// 重置核心上下文
|
||||
core_ctx->is_initialized = false;
|
||||
core_ctx->current_node_id = 0;
|
||||
core_ctx->current_state = MENU_STATE_INIT;
|
||||
core_ctx->last_refresh_tick = 0;
|
||||
core_ctx->error_code = MENU_ERR_OK;
|
||||
core_ctx->error_msg = NULL;
|
||||
core_ctx->node_count = 0;
|
||||
core_ctx->free_node_count = MENU_CONFIG_MAX_NODES;
|
||||
|
||||
#if MENU_CONFIG_ENABLE_STATE_MACHINE_EXT
|
||||
core_ctx->custom_transition_count = 0;
|
||||
#endif
|
||||
|
||||
#if MENU_CONFIG_ENABLE_PERMISSION
|
||||
// 反初始化权限管理模块
|
||||
menu_permission_deinit();
|
||||
#endif
|
||||
|
||||
#if MENU_CONFIG_ENABLE_PERSISTENCE
|
||||
// 反初始化状态持久化模块
|
||||
menu_persistence_deinit();
|
||||
#endif
|
||||
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 初始化菜单节点池
|
||||
* @param core_ctx 菜单核心上下文
|
||||
* @return 错误码
|
||||
*/
|
||||
static MenuErrCode menu_core_init_nodes(MenuCoreCtx* core_ctx)
|
||||
{
|
||||
for (uint16_t i = 0; i < MENU_CONFIG_MAX_NODES; i++) {
|
||||
MenuNode* node = &core_ctx->nodes[i];
|
||||
|
||||
node->id = i + 1; // 节点ID从1开始
|
||||
node->parent_id = 0;
|
||||
node->name = NULL;
|
||||
node->enter_cb = NULL;
|
||||
node->exit_cb = NULL;
|
||||
node->first_child_id = 0;
|
||||
node->next_sibling_id = 0;
|
||||
node->prev_sibling_id = 0;
|
||||
node->hash_next_id = 0;
|
||||
|
||||
node->flags.is_registered = false;
|
||||
node->flags.is_selected = false;
|
||||
node->flags.is_visible = true; // 默认可见
|
||||
node->flags.is_enabled = true; // 默认启用
|
||||
|
||||
#if MENU_CONFIG_ENABLE_PARAM
|
||||
node->param_id = 0;
|
||||
#endif
|
||||
|
||||
#if MENU_CONFIG_ENABLE_PERMISSION
|
||||
node->permission_level = 0; // 默认权限级别为0(最低权限)
|
||||
#endif
|
||||
}
|
||||
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取一个空闲节点
|
||||
* @param core_ctx 菜单核心上下文
|
||||
* @return 空闲节点指针,没有空闲节点返回NULL
|
||||
*/
|
||||
static MenuNode* menu_core_get_free_node(MenuCoreCtx* core_ctx)
|
||||
{
|
||||
static uint16_t last_allocated_index = 0; // 记录上次分配的索引,实现循环分配
|
||||
|
||||
// 先从上次分配的位置开始查找,实现循环分配
|
||||
for (uint16_t i = 0; i < MENU_CONFIG_MAX_NODES; i++) {
|
||||
uint16_t index = (last_allocated_index + i) % MENU_CONFIG_MAX_NODES;
|
||||
MenuNode* node = &core_ctx->nodes[index];
|
||||
if (!node->flags.is_registered) {
|
||||
last_allocated_index = index;
|
||||
return node;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 注册菜单节点
|
||||
* @param node_id 节点ID(0表示自动分配)
|
||||
* @param parent_id 父节点ID
|
||||
* @param name 节点名称
|
||||
* @param enter_cb 进入回调
|
||||
* @param exit_cb 退出回调
|
||||
* @return 实际分配的节点ID
|
||||
*/
|
||||
MenuNodeId menu_core_register_node(MenuNodeId node_id, MenuNodeId parent_id, const char* name, MenuCallback enter_cb, MenuCallback exit_cb)
|
||||
{
|
||||
MenuCoreCtx* core_ctx = &sg_menu_core_ctx;
|
||||
|
||||
// 参数验证
|
||||
if (!core_ctx->is_initialized) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 检查父节点是否有效(如果不是根节点)
|
||||
if (parent_id != 0) {
|
||||
MenuNode* parent_node = menu_hash_find(core_ctx, parent_id);
|
||||
if (parent_node == NULL || !parent_node->flags.is_registered) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查节点名称是否有效
|
||||
if (name == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
MenuNode* node = NULL;
|
||||
|
||||
if (node_id == 0) {
|
||||
// 自动分配节点ID
|
||||
node = menu_core_get_free_node(core_ctx);
|
||||
if (node == NULL) {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
// 使用指定节点ID
|
||||
if (node_id > MENU_CONFIG_MAX_NODES || node_id < 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
node = &core_ctx->nodes[node_id - 1];
|
||||
if (node->flags.is_registered) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化节点
|
||||
node->parent_id = parent_id;
|
||||
node->name = name;
|
||||
node->enter_cb = enter_cb;
|
||||
node->exit_cb = exit_cb;
|
||||
|
||||
// 添加到父节点的子节点列表
|
||||
if (parent_id != 0) {
|
||||
MenuNode* parent_node = menu_hash_find(core_ctx, parent_id);
|
||||
if (parent_node != NULL) {
|
||||
if (parent_node->first_child_id == 0) {
|
||||
// 父节点没有子节点,直接添加
|
||||
parent_node->first_child_id = node->id;
|
||||
} else {
|
||||
// 查找父节点的最后一个子节点
|
||||
MenuNode* last_child = menu_hash_find(core_ctx, parent_node->first_child_id);
|
||||
while (last_child->next_sibling_id != 0) {
|
||||
last_child = menu_hash_find(core_ctx, last_child->next_sibling_id);
|
||||
}
|
||||
|
||||
// 添加到最后一个子节点的后面
|
||||
last_child->next_sibling_id = node->id;
|
||||
node->prev_sibling_id = last_child->id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 添加到哈希表
|
||||
menu_hash_add(core_ctx, node);
|
||||
|
||||
// 更新节点计数
|
||||
node->flags.is_registered = true;
|
||||
core_ctx->node_count++;
|
||||
core_ctx->free_node_count--;
|
||||
|
||||
// 如果是第一个节点,设置为当前节点
|
||||
if (core_ctx->current_node_id == 0) {
|
||||
core_ctx->current_node_id = node->id;
|
||||
menu_nav_path_add(&core_ctx->nav_path, node->id);
|
||||
// 初始化栈,将根节点压入栈中,确保栈和导航路径深度一致
|
||||
menu_stack_push(&core_ctx->stack, node->id);
|
||||
}
|
||||
|
||||
return node->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 注销菜单节点
|
||||
* @param node_id 节点ID
|
||||
* @return 错误码
|
||||
*/
|
||||
static MenuErrCode menu_core_unregister_node_recursive(MenuCoreCtx* core_ctx, MenuNodeId node_id);
|
||||
|
||||
/**
|
||||
* @brief 注销菜单节点
|
||||
* @param node_id 节点ID
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_core_unregister_node(MenuNodeId node_id)
|
||||
{
|
||||
MenuCoreCtx* core_ctx = &sg_menu_core_ctx;
|
||||
|
||||
// 参数验证
|
||||
if (!core_ctx->is_initialized) {
|
||||
return MENU_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
if (node_id == 0 || node_id > MENU_CONFIG_MAX_NODES) {
|
||||
return MENU_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
// 递归注销所有子节点
|
||||
return menu_core_unregister_node_recursive(core_ctx, node_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 递归注销菜单节点及其所有子节点
|
||||
* @param core_ctx 菜单核心上下文
|
||||
* @param node_id 节点ID
|
||||
* @return 错误码
|
||||
*/
|
||||
static MenuErrCode menu_core_unregister_node_recursive(MenuCoreCtx* core_ctx, MenuNodeId node_id)
|
||||
{
|
||||
MenuNode* node = menu_hash_find(core_ctx, node_id);
|
||||
if (node == NULL || !node->flags.is_registered) {
|
||||
return MENU_ERR_NODE_NOT_FOUND;
|
||||
}
|
||||
|
||||
// 递归注销所有子节点
|
||||
if (node->first_child_id != 0) {
|
||||
MenuNode* child_node = menu_hash_find(core_ctx, node->first_child_id);
|
||||
while (child_node != NULL) {
|
||||
MenuNodeId next_child_id = child_node->next_sibling_id;
|
||||
// 递归注销子节点
|
||||
menu_core_unregister_node_recursive(core_ctx, child_node->id);
|
||||
child_node = menu_hash_find(core_ctx, next_child_id);
|
||||
}
|
||||
}
|
||||
|
||||
// 从哈希表中移除
|
||||
menu_hash_remove(core_ctx, node);
|
||||
|
||||
// 从父节点的子节点列表中移除
|
||||
if (node->parent_id != 0) {
|
||||
MenuNode* parent_node = menu_hash_find(core_ctx, node->parent_id);
|
||||
if (parent_node != NULL) {
|
||||
if (parent_node->first_child_id == node_id) {
|
||||
// 移除的是第一个子节点
|
||||
parent_node->first_child_id = node->next_sibling_id;
|
||||
} else {
|
||||
// 移除的是中间或最后一个子节点
|
||||
if (node->prev_sibling_id != 0) {
|
||||
MenuNode* prev_sibling = menu_hash_find(core_ctx, node->prev_sibling_id);
|
||||
if (prev_sibling != NULL) {
|
||||
prev_sibling->next_sibling_id = node->next_sibling_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (node->next_sibling_id != 0) {
|
||||
MenuNode* next_sibling = menu_hash_find(core_ctx, node->next_sibling_id);
|
||||
if (next_sibling != NULL) {
|
||||
next_sibling->prev_sibling_id = node->prev_sibling_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 重置节点状态
|
||||
node->flags.is_registered = false;
|
||||
node->flags.is_selected = false;
|
||||
node->parent_id = 0;
|
||||
node->next_sibling_id = 0;
|
||||
node->prev_sibling_id = 0;
|
||||
node->first_child_id = 0;
|
||||
|
||||
// 更新节点计数
|
||||
core_ctx->node_count--;
|
||||
core_ctx->free_node_count++;
|
||||
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 状态转换处理
|
||||
* @param core_ctx 菜单核心上下文
|
||||
* @param event 事件类型
|
||||
* @return 错误码
|
||||
*/
|
||||
static MenuErrCode menu_core_state_transition(MenuCoreCtx* core_ctx, MenuEventType event)
|
||||
{
|
||||
MenuStateTransition default_transition = {0};
|
||||
MenuStateTransition* matched_transition = NULL;
|
||||
|
||||
// 1. 查找匹配的自定义状态转换规则
|
||||
#if MENU_CONFIG_ENABLE_STATE_MACHINE_EXT
|
||||
for (uint16_t i = 0; i < core_ctx->custom_transition_count; i++) {
|
||||
MenuStateTransition* transition = &core_ctx->custom_transitions[i];
|
||||
|
||||
if (transition->current_state == core_ctx->current_state && transition->event == event) {
|
||||
matched_transition = transition;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// 2. 如果没有找到匹配的自定义规则,使用默认状态转换
|
||||
if (matched_transition == NULL) {
|
||||
// 默认状态转换规则:保持当前状态不变
|
||||
default_transition.current_state = core_ctx->current_state;
|
||||
default_transition.event = event;
|
||||
default_transition.next_state = core_ctx->current_state; // 默认不改变状态
|
||||
default_transition.action = NULL;
|
||||
default_transition.desc = "默认状态转换";
|
||||
matched_transition = &default_transition;
|
||||
}
|
||||
|
||||
// 3. 先更新状态,再执行转换动作
|
||||
MenuState old_state = core_ctx->current_state;
|
||||
core_ctx->current_state = matched_transition->next_state;
|
||||
|
||||
// 4. 执行状态转换动作(如果有)
|
||||
if (matched_transition->action != NULL) {
|
||||
MenuErrCode err = matched_transition->action(core_ctx);
|
||||
if (err != MENU_ERR_OK) {
|
||||
// 动作执行失败,恢复原状态
|
||||
core_ctx->current_state = old_state;
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 导航进入子菜单
|
||||
* @param core_ctx 菜单核心上下文
|
||||
* @param node_id 节点ID
|
||||
* @return 错误码
|
||||
*/
|
||||
static MenuErrCode menu_core_navigate_enter(MenuCoreCtx* core_ctx, MenuNodeId node_id)
|
||||
{
|
||||
MenuNode* node = menu_hash_find(core_ctx, node_id);
|
||||
if (node == NULL) {
|
||||
return MENU_ERR_NODE_NOT_FOUND;
|
||||
}
|
||||
|
||||
// 如果当前节点没有子节点,直接返回
|
||||
if (node->first_child_id == 0) {
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
// 进入第一个子节点
|
||||
MenuNodeId target_node_id = node->first_child_id;
|
||||
MenuNode* target_node = menu_hash_find(core_ctx, target_node_id);
|
||||
if (target_node == NULL) {
|
||||
return MENU_ERR_NODE_NOT_FOUND;
|
||||
}
|
||||
|
||||
// 执行当前节点的退出回调
|
||||
MenuNode* curr_node = menu_hash_find(core_ctx, core_ctx->current_node_id);
|
||||
if (curr_node != NULL && curr_node->exit_cb != NULL) {
|
||||
curr_node->exit_cb(core_ctx->current_node_id, core_ctx);
|
||||
}
|
||||
|
||||
// 压入当前节点到栈中
|
||||
MenuErrCode err = menu_stack_push(&core_ctx->stack, core_ctx->current_node_id);
|
||||
if (err != MENU_ERR_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// 更新当前节点
|
||||
core_ctx->current_node_id = target_node_id;
|
||||
|
||||
// 添加到导航路径
|
||||
err = menu_nav_path_add(&core_ctx->nav_path, target_node_id);
|
||||
if (err != MENU_ERR_OK) {
|
||||
menu_stack_pop(&core_ctx->stack, &node_id);
|
||||
core_ctx->current_node_id = node_id;
|
||||
return err;
|
||||
}
|
||||
|
||||
// 执行新节点的进入回调
|
||||
if (target_node->enter_cb != NULL) {
|
||||
target_node->enter_cb(target_node_id, core_ctx);
|
||||
}
|
||||
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 导航返回上一级菜单
|
||||
* @param core_ctx 菜单核心上下文
|
||||
* @return 错误码
|
||||
*/
|
||||
static MenuErrCode menu_core_navigate_back(MenuCoreCtx* core_ctx)
|
||||
{
|
||||
// 执行当前节点的退出回调
|
||||
MenuNode* curr_node = menu_hash_find(core_ctx, core_ctx->current_node_id);
|
||||
if (curr_node != NULL && curr_node->exit_cb != NULL) {
|
||||
curr_node->exit_cb(core_ctx->current_node_id, core_ctx);
|
||||
}
|
||||
|
||||
MenuNodeId prev_node_id;
|
||||
MenuErrCode err;
|
||||
|
||||
// 如果栈不为空,弹出上一级节点ID
|
||||
if (!menu_stack_is_empty(&core_ctx->stack)) {
|
||||
err = menu_stack_pop(&core_ctx->stack, &prev_node_id);
|
||||
if (err != MENU_ERR_OK) {
|
||||
return err;
|
||||
}
|
||||
} else {
|
||||
// 栈为空,返回到根节点
|
||||
// 查找根节点
|
||||
prev_node_id = 0;
|
||||
for (uint16_t i = 0; i < MENU_CONFIG_MAX_NODES; i++) {
|
||||
MenuNode* node = &core_ctx->nodes[i];
|
||||
if (node->flags.is_registered && node->parent_id == 0) {
|
||||
prev_node_id = node->id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有找到根节点,返回错误
|
||||
if (prev_node_id == 0) {
|
||||
return MENU_ERR_NODE_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
// 从导航路径移除当前节点
|
||||
err = menu_nav_path_remove_last(&core_ctx->nav_path);
|
||||
if (err != MENU_ERR_OK) {
|
||||
// 如果导航路径移除失败,检查是否还有深度
|
||||
if (core_ctx->nav_path.depth > 0) {
|
||||
// 导航路径还有深度,直接更新当前节点
|
||||
core_ctx->current_node_id = prev_node_id;
|
||||
} else {
|
||||
// 导航路径已为空,添加根节点
|
||||
menu_nav_path_add(&core_ctx->nav_path, prev_node_id);
|
||||
}
|
||||
} else {
|
||||
// 导航路径移除成功,更新当前节点
|
||||
core_ctx->current_node_id = prev_node_id;
|
||||
}
|
||||
|
||||
// 执行上一级节点的进入回调
|
||||
MenuNode* prev_node = menu_hash_find(core_ctx, prev_node_id);
|
||||
if (prev_node != NULL && prev_node->enter_cb != NULL) {
|
||||
prev_node->enter_cb(prev_node_id, core_ctx);
|
||||
}
|
||||
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 导航向上选择
|
||||
* @param core_ctx 菜单核心上下文
|
||||
* @return 错误码
|
||||
*/
|
||||
static MenuErrCode menu_core_navigate_up(MenuCoreCtx* core_ctx)
|
||||
{
|
||||
MenuNode* curr_node = menu_hash_find(core_ctx, core_ctx->current_node_id);
|
||||
if (curr_node == NULL) {
|
||||
return MENU_ERR_NODE_NOT_FOUND;
|
||||
}
|
||||
|
||||
// 查找当前节点的父节点
|
||||
MenuNode* parent_node = menu_hash_find(core_ctx, curr_node->parent_id);
|
||||
|
||||
// 如果当前节点有父节点,在同级节点中向上切换
|
||||
if (parent_node != NULL) {
|
||||
// 查找同级的上一个兄弟节点
|
||||
if (curr_node->prev_sibling_id != 0) {
|
||||
core_ctx->current_node_id = curr_node->prev_sibling_id;
|
||||
} else if (parent_node->first_child_id != 0) {
|
||||
// 没有上一个兄弟节点,查找父节点的最后一个子节点(环形导航)
|
||||
MenuNode* last_child = menu_hash_find(core_ctx, parent_node->first_child_id);
|
||||
if (last_child != NULL) {
|
||||
// 查找最后一个子节点
|
||||
while (last_child->next_sibling_id != 0) {
|
||||
MenuNode* next_child = menu_hash_find(core_ctx, last_child->next_sibling_id);
|
||||
if (next_child == NULL) {
|
||||
break; // 防止无限循环,安全退出
|
||||
}
|
||||
last_child = next_child;
|
||||
}
|
||||
|
||||
core_ctx->current_node_id = last_child->id;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 如果当前节点是根节点,在其子节点中向上切换
|
||||
if (curr_node->first_child_id != 0) {
|
||||
// 查找最后一个子节点
|
||||
MenuNode* last_child = menu_hash_find(core_ctx, curr_node->first_child_id);
|
||||
if (last_child != NULL) {
|
||||
while (last_child->next_sibling_id != 0) {
|
||||
MenuNode* next_child = menu_hash_find(core_ctx, last_child->next_sibling_id);
|
||||
if (next_child == NULL) {
|
||||
break; // 防止无限循环,安全退出
|
||||
}
|
||||
last_child = next_child;
|
||||
}
|
||||
|
||||
core_ctx->current_node_id = last_child->id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 导航向下选择
|
||||
* @param core_ctx 菜单核心上下文
|
||||
* @return 错误码
|
||||
*/
|
||||
static MenuErrCode menu_core_navigate_down(MenuCoreCtx* core_ctx)
|
||||
{
|
||||
MenuNode* curr_node = menu_hash_find(core_ctx, core_ctx->current_node_id);
|
||||
if (curr_node == NULL) {
|
||||
return MENU_ERR_NODE_NOT_FOUND;
|
||||
}
|
||||
|
||||
// 查找当前节点的父节点
|
||||
MenuNode* parent_node = menu_hash_find(core_ctx, curr_node->parent_id);
|
||||
|
||||
// 如果当前节点有父节点,在同级节点中向下切换
|
||||
if (parent_node != NULL) {
|
||||
// 查找同级的下一个兄弟节点
|
||||
if (curr_node->next_sibling_id != 0) {
|
||||
core_ctx->current_node_id = curr_node->next_sibling_id;
|
||||
} else if (parent_node->first_child_id != 0) {
|
||||
// 没有下一个兄弟节点,选择父节点的第一个子节点(环形导航)
|
||||
core_ctx->current_node_id = parent_node->first_child_id;
|
||||
}
|
||||
} else {
|
||||
// 如果当前节点是根节点,在其子节点中向下切换
|
||||
if (curr_node->first_child_id != 0) {
|
||||
// 导航到第一个子节点
|
||||
MenuNode* first_child = menu_hash_find(core_ctx, curr_node->first_child_id);
|
||||
if (first_child != NULL) {
|
||||
core_ctx->current_node_id = first_child->id;
|
||||
}
|
||||
}
|
||||
// 如果根节点没有子节点,保持当前节点不变
|
||||
}
|
||||
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 菜单主循环
|
||||
* @param tick 当前系统时间(ms)
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_core_main_loop(uint32_t tick)
|
||||
{
|
||||
MenuCoreCtx* core_ctx = &sg_menu_core_ctx;
|
||||
|
||||
if (!core_ctx->is_initialized) {
|
||||
return MENU_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
// 更新最后刷新时间
|
||||
core_ctx->last_refresh_tick = tick;
|
||||
|
||||
// 处理事件队列
|
||||
while (!menu_event_queue_is_empty(&core_ctx->event_queue)) {
|
||||
MenuEvent event;
|
||||
MenuErrCode err = menu_event_queue_pop(&core_ctx->event_queue, &event);
|
||||
if (err != MENU_ERR_OK) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 处理事件
|
||||
switch (event.type) {
|
||||
case MENU_EVENT_KEY_UP:
|
||||
menu_core_navigate_up(core_ctx);
|
||||
break;
|
||||
case MENU_EVENT_KEY_DOWN:
|
||||
menu_core_navigate_down(core_ctx);
|
||||
break;
|
||||
case MENU_EVENT_KEY_ENTER:
|
||||
menu_core_navigate_enter(core_ctx, core_ctx->current_node_id);
|
||||
break;
|
||||
case MENU_EVENT_KEY_ESC:
|
||||
menu_core_navigate_back(core_ctx);
|
||||
break;
|
||||
default:
|
||||
// 其他事件通过状态机处理
|
||||
menu_core_state_transition(core_ctx, event.type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 处理自动保存
|
||||
#if MENU_CONFIG_ENABLE_PERSISTENCE
|
||||
menu_persistence_process_auto_save(tick);
|
||||
#endif
|
||||
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取菜单核心上下文
|
||||
* @return 菜单核心上下文指针
|
||||
*/
|
||||
MenuCoreCtx* menu_get_core_ctx(void)
|
||||
{
|
||||
return &sg_menu_core_ctx;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取当前选中的节点ID
|
||||
* @return 当前节点ID
|
||||
*/
|
||||
MenuNodeId menu_get_current_node_id(void)
|
||||
{
|
||||
return sg_menu_core_ctx.current_node_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取当前菜单状态
|
||||
* @return 当前菜单状态
|
||||
*/
|
||||
MenuState menu_get_current_state(void)
|
||||
{
|
||||
return sg_menu_core_ctx.current_state;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取导航深度
|
||||
* @return 导航深度
|
||||
*/
|
||||
uint8_t menu_core_get_nav_depth(void)
|
||||
{
|
||||
return sg_menu_core_ctx.nav_path.depth;
|
||||
}
|
||||
246
src/core/menu_core.h
Normal file
246
src/core/menu_core.h
Normal file
@ -0,0 +1,246 @@
|
||||
/**
|
||||
**********************************************************************************************************************
|
||||
* @file menu_core.h
|
||||
* @brief 菜单组件核心功能声明
|
||||
* @author menu_component
|
||||
* @date 2025-12-19
|
||||
**********************************************************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef MENU_CORE_H
|
||||
#define MENU_CORE_H
|
||||
|
||||
/* Includes ----------------------------------------------------------------------------------------------------------*/
|
||||
#include "menu_types.h"
|
||||
|
||||
/* 函数声明 ---------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* @brief 初始化菜单核心
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_core_init(void);
|
||||
|
||||
/**
|
||||
* @brief 反初始化菜单核心
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_core_deinit(void);
|
||||
|
||||
/**
|
||||
* @brief 注册菜单节点
|
||||
* @param node_id 节点ID(0表示自动分配)
|
||||
* @param parent_id 父节点ID
|
||||
* @param name 节点名称
|
||||
* @param enter_cb 进入回调
|
||||
* @param exit_cb 退出回调
|
||||
* @return 实际分配的节点ID
|
||||
*/
|
||||
MenuNodeId menu_core_register_node(MenuNodeId node_id, MenuNodeId parent_id, const char* name, MenuCallback enter_cb, MenuCallback exit_cb);
|
||||
|
||||
/**
|
||||
* @brief 注销菜单节点
|
||||
* @param node_id 节点ID
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_core_unregister_node(MenuNodeId node_id);
|
||||
|
||||
/**
|
||||
* @brief 获取菜单核心上下文
|
||||
* @return 菜单核心上下文指针
|
||||
*/
|
||||
struct MenuCoreCtx* menu_get_core_ctx(void);
|
||||
|
||||
/**
|
||||
* @brief 获取当前选中的节点ID
|
||||
* @return 当前节点ID
|
||||
*/
|
||||
MenuNodeId menu_get_current_node_id(void);
|
||||
|
||||
/**
|
||||
* @brief 获取当前菜单状态
|
||||
* @return 当前菜单状态
|
||||
*/
|
||||
MenuState menu_get_current_state(void);
|
||||
|
||||
/**
|
||||
* @brief 获取导航深度
|
||||
* @return 导航深度
|
||||
*/
|
||||
uint8_t menu_core_get_nav_depth(void);
|
||||
|
||||
/**
|
||||
* @brief 菜单主循环
|
||||
* @param tick 当前系统时间(ms)
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_core_main_loop(uint32_t tick);
|
||||
|
||||
/* 权限管理函数声明 -------------------------------------------------------------------------------------------------*/
|
||||
|
||||
#if MENU_CONFIG_ENABLE_PERMISSION
|
||||
|
||||
/**
|
||||
* @brief 初始化权限管理模块
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_permission_init(void);
|
||||
|
||||
/**
|
||||
* @brief 反初始化权限管理模块
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_permission_deinit(void);
|
||||
|
||||
/**
|
||||
* @brief 注册角色
|
||||
* @param role 角色结构体指针
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_permission_register_role(const MenuRole* role);
|
||||
|
||||
/**
|
||||
* @brief 注销角色
|
||||
* @param role_id 角色ID
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_permission_unregister_role(MenuRoleId role_id);
|
||||
|
||||
/**
|
||||
* @brief 设置当前角色
|
||||
* @param role_id 角色ID
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_permission_set_current_role(MenuRoleId role_id);
|
||||
|
||||
/**
|
||||
* @brief 获取当前角色
|
||||
* @return 当前角色ID
|
||||
*/
|
||||
MenuRoleId menu_permission_get_current_role(void);
|
||||
|
||||
/**
|
||||
* @brief 检查节点是否可访问
|
||||
* @param node_id 节点ID
|
||||
* @return true表示可访问,false表示不可访问
|
||||
*/
|
||||
bool menu_permission_check_node_access(MenuNodeId node_id);
|
||||
|
||||
/**
|
||||
* @brief 更新节点权限级别
|
||||
* @param node_id 节点ID
|
||||
* @param permission_level 权限级别
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_permission_update_node_level(MenuNodeId node_id, MenuPermissionLevel permission_level);
|
||||
|
||||
/**
|
||||
* @brief 获取节点权限级别
|
||||
* @param node_id 节点ID
|
||||
* @return 节点权限级别
|
||||
*/
|
||||
MenuPermissionLevel menu_permission_get_node_level(MenuNodeId node_id);
|
||||
|
||||
/**
|
||||
* @brief 更新所有节点的可见性和可用性
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_permission_update_all_nodes(void);
|
||||
|
||||
/**
|
||||
* @brief 获取角色信息
|
||||
* @param role_id 角色ID
|
||||
* @return 角色结构体指针,NULL表示角色不存在
|
||||
*/
|
||||
const MenuRole* menu_permission_get_role(MenuRoleId role_id);
|
||||
|
||||
#endif /* MENU_CONFIG_ENABLE_PERMISSION */
|
||||
|
||||
/* 状态持久化函数声明 -------------------------------------------------------------------------------------------------*/
|
||||
|
||||
#if MENU_CONFIG_ENABLE_PERSISTENCE
|
||||
|
||||
/**
|
||||
* @brief 初始化状态持久化模块
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_persistence_init(void);
|
||||
|
||||
/**
|
||||
* @brief 反初始化状态持久化模块
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_persistence_deinit(void);
|
||||
|
||||
/**
|
||||
* @brief 注册持久化回调函数
|
||||
* @param save_cb 保存回调函数
|
||||
* @param restore_cb 恢复回调函数
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_persistence_register_callback(MenuPersistenceCallback save_cb, MenuRestoreCallback restore_cb);
|
||||
|
||||
/**
|
||||
* @brief 启用或禁用自动保存
|
||||
* @param enable true表示启用,false表示禁用
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_persistence_set_auto_save(bool enable);
|
||||
|
||||
/**
|
||||
* @brief 获取自动保存状态
|
||||
* @return true表示启用,false表示禁用
|
||||
*/
|
||||
bool menu_persistence_get_auto_save(void);
|
||||
|
||||
/**
|
||||
* @brief 设置自动保存间隔
|
||||
* @param interval 自动保存间隔(ms)
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_persistence_set_auto_save_interval(uint32_t interval);
|
||||
|
||||
/**
|
||||
* @brief 获取自动保存间隔
|
||||
* @return 自动保存间隔(ms)
|
||||
*/
|
||||
uint32_t menu_persistence_get_auto_save_interval(void);
|
||||
|
||||
/**
|
||||
* @brief 手动保存菜单状态
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_persistence_save(void);
|
||||
|
||||
/**
|
||||
* @brief 恢复菜单状态
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_persistence_restore(void);
|
||||
|
||||
/**
|
||||
* @brief 检查持久化数据是否有效
|
||||
* @param data 持久化数据
|
||||
* @param size 数据大小
|
||||
* @return true表示有效,false表示无效
|
||||
*/
|
||||
bool menu_persistence_check_data_valid(const uint8_t* data, uint16_t size);
|
||||
|
||||
/**
|
||||
* @brief 处理自动保存(由菜单主循环调用)
|
||||
* @param tick 当前系统时间(ms)
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_persistence_process_auto_save(uint32_t tick);
|
||||
|
||||
/**
|
||||
* @brief 计算数据校验和
|
||||
* @param data 数据指针
|
||||
* @param size 数据大小
|
||||
* @return 校验和
|
||||
*/
|
||||
uint8_t menu_persistence_calculate_checksum(const uint8_t* data, uint16_t size);
|
||||
|
||||
#endif /* MENU_CONFIG_ENABLE_PERSISTENCE */
|
||||
|
||||
#endif /* MENU_CORE_H */
|
||||
186
src/core/menu_event.c
Normal file
186
src/core/menu_event.c
Normal file
@ -0,0 +1,186 @@
|
||||
/**
|
||||
**********************************************************************************************************************
|
||||
* @file menu_event.c
|
||||
* @brief 菜单组件事件队列实现
|
||||
* @author menu_component
|
||||
* @date 2025-12-19
|
||||
**********************************************************************************************************************
|
||||
*/
|
||||
|
||||
/* Includes ----------------------------------------------------------------------------------------------------------*/
|
||||
#include "menu_types.h"
|
||||
|
||||
/* 函数实现 ---------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* @brief 初始化事件队列
|
||||
* @param queue 事件队列指针
|
||||
*/
|
||||
void menu_event_queue_init(MenuEventQueue* queue)
|
||||
{
|
||||
MENU_ASSERT(queue != NULL);
|
||||
|
||||
queue->head = 0;
|
||||
queue->tail = 0;
|
||||
queue->count = 0;
|
||||
queue->reserved = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查事件队列是否为空
|
||||
* @param queue 事件队列指针
|
||||
* @return true为空,false不为空
|
||||
*/
|
||||
bool menu_event_queue_is_empty(const MenuEventQueue* queue)
|
||||
{
|
||||
MENU_ASSERT(queue != NULL);
|
||||
return (queue->count == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查事件队列是否已满
|
||||
* @param queue 事件队列指针
|
||||
* @return true已满,false未满
|
||||
*/
|
||||
bool menu_event_queue_is_full(const MenuEventQueue* queue)
|
||||
{
|
||||
MENU_ASSERT(queue != NULL);
|
||||
return (queue->count >= MENU_CONFIG_EVENT_QUEUE_LEN);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取事件队列中的元素数量
|
||||
* @param queue 事件队列指针
|
||||
* @return 元素数量
|
||||
*/
|
||||
uint8_t menu_event_queue_get_count(const MenuEventQueue* queue)
|
||||
{
|
||||
MENU_ASSERT(queue != NULL);
|
||||
return queue->count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 向事件队列添加事件
|
||||
* @param queue 事件队列指针
|
||||
* @param event 要添加的事件
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_event_queue_push(MenuEventQueue* queue, const MenuEvent* event)
|
||||
{
|
||||
MENU_ASSERT(queue != NULL);
|
||||
MENU_ASSERT(event != NULL);
|
||||
|
||||
// 检查事件优先级是否合法
|
||||
if (event->priority >= MENU_CONFIG_EVENT_MAX_PRIORITY) {
|
||||
return MENU_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
if (menu_event_queue_is_full(queue)) {
|
||||
// 队列已满,替换低优先级事件
|
||||
uint8_t lowest_prio = 0;
|
||||
uint8_t lowest_idx = queue->head;
|
||||
|
||||
// 找到最低优先级的事件
|
||||
for (uint8_t i = 0; i < MENU_CONFIG_EVENT_QUEUE_LEN; i++) {
|
||||
uint8_t idx = (queue->head + i) % MENU_CONFIG_EVENT_QUEUE_LEN;
|
||||
if (queue->buffer[idx].priority > lowest_prio) {
|
||||
lowest_prio = queue->buffer[idx].priority;
|
||||
lowest_idx = idx;
|
||||
}
|
||||
}
|
||||
|
||||
// 替换最低优先级事件
|
||||
queue->buffer[lowest_idx] = *event;
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
// 队列未满,添加到队尾
|
||||
queue->buffer[queue->tail] = *event;
|
||||
queue->tail = (queue->tail + 1) % MENU_CONFIG_EVENT_QUEUE_LEN;
|
||||
queue->count++;
|
||||
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从事件队列获取下一个事件
|
||||
* @param queue 事件队列指针
|
||||
* @param event 用于存储获取的事件
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_event_queue_pop(MenuEventQueue* queue, MenuEvent* event)
|
||||
{
|
||||
MENU_ASSERT(queue != NULL);
|
||||
MENU_ASSERT(event != NULL);
|
||||
|
||||
if (menu_event_queue_is_empty(queue)) {
|
||||
return MENU_ERR_QUEUE_EMPTY;
|
||||
}
|
||||
|
||||
// 寻找最高优先级的事件
|
||||
uint8_t highest_prio = MENU_CONFIG_EVENT_MAX_PRIORITY;
|
||||
uint8_t highest_idx = queue->head;
|
||||
|
||||
for (uint8_t i = 0; i < queue->count; i++) {
|
||||
uint8_t idx = (queue->head + i) % MENU_CONFIG_EVENT_QUEUE_LEN;
|
||||
if (queue->buffer[idx].priority < highest_prio) {
|
||||
highest_prio = queue->buffer[idx].priority;
|
||||
highest_idx = idx;
|
||||
}
|
||||
}
|
||||
|
||||
// 取出最高优先级事件
|
||||
*event = queue->buffer[highest_idx];
|
||||
|
||||
// 移除该事件,将后续事件前移
|
||||
for (uint8_t i = highest_idx; i != queue->tail; i = (i + 1) % MENU_CONFIG_EVENT_QUEUE_LEN) {
|
||||
uint8_t next_idx = (i + 1) % MENU_CONFIG_EVENT_QUEUE_LEN;
|
||||
if (next_idx == queue->tail) {
|
||||
break;
|
||||
}
|
||||
queue->buffer[i] = queue->buffer[next_idx];
|
||||
}
|
||||
|
||||
// 更新队列状态
|
||||
if (queue->tail > 0) {
|
||||
queue->tail--;
|
||||
} else {
|
||||
queue->tail = MENU_CONFIG_EVENT_QUEUE_LEN - 1;
|
||||
}
|
||||
queue->count--;
|
||||
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 清空事件队列
|
||||
* @param queue 事件队列指针
|
||||
*/
|
||||
void menu_event_queue_clear(MenuEventQueue* queue)
|
||||
{
|
||||
MENU_ASSERT(queue != NULL);
|
||||
|
||||
queue->head = 0;
|
||||
queue->tail = 0;
|
||||
queue->count = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 向事件队列推送一个简单事件
|
||||
* @param core_ctx 菜单核心上下文
|
||||
* @param type 事件类型
|
||||
* @param priority 事件优先级
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_core_post_event(struct MenuCoreCtx* core_ctx, MenuEventType type, uint8_t priority)
|
||||
{
|
||||
MenuEvent event;
|
||||
|
||||
event.type = type;
|
||||
event.priority = priority;
|
||||
event.target_node_id = core_ctx->current_node_id;
|
||||
event.data = NULL;
|
||||
event.timestamp = core_ctx->last_refresh_tick;
|
||||
|
||||
return menu_event_queue_push(&core_ctx->event_queue, &event);
|
||||
}
|
||||
76
src/core/menu_event.h
Normal file
76
src/core/menu_event.h
Normal file
@ -0,0 +1,76 @@
|
||||
/**
|
||||
**********************************************************************************************************************
|
||||
* @file menu_event.h
|
||||
* @brief 菜单组件事件队列功能声明
|
||||
* @author menu_component
|
||||
* @date 2025-12-19
|
||||
**********************************************************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef MENU_EVENT_H
|
||||
#define MENU_EVENT_H
|
||||
|
||||
/* Includes ----------------------------------------------------------------------------------------------------------*/
|
||||
#include "menu_types.h"
|
||||
|
||||
/* 函数声明 ---------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* @brief 初始化事件队列
|
||||
* @param queue 事件队列指针
|
||||
*/
|
||||
void menu_event_queue_init(MenuEventQueue* queue);
|
||||
|
||||
/**
|
||||
* @brief 检查事件队列是否为空
|
||||
* @param queue 事件队列指针
|
||||
* @return true为空,false不为空
|
||||
*/
|
||||
bool menu_event_queue_is_empty(const MenuEventQueue* queue);
|
||||
|
||||
/**
|
||||
* @brief 检查事件队列是否已满
|
||||
* @param queue 事件队列指针
|
||||
* @return true已满,false未满
|
||||
*/
|
||||
bool menu_event_queue_is_full(const MenuEventQueue* queue);
|
||||
|
||||
/**
|
||||
* @brief 获取事件队列中的元素数量
|
||||
* @param queue 事件队列指针
|
||||
* @return 元素数量
|
||||
*/
|
||||
uint8_t menu_event_queue_get_count(const MenuEventQueue* queue);
|
||||
|
||||
/**
|
||||
* @brief 向事件队列添加事件
|
||||
* @param queue 事件队列指针
|
||||
* @param event 要添加的事件
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_event_queue_push(MenuEventQueue* queue, const MenuEvent* event);
|
||||
|
||||
/**
|
||||
* @brief 从事件队列获取下一个事件
|
||||
* @param queue 事件队列指针
|
||||
* @param event 用于存储获取的事件
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_event_queue_pop(MenuEventQueue* queue, MenuEvent* event);
|
||||
|
||||
/**
|
||||
* @brief 清空事件队列
|
||||
* @param queue 事件队列指针
|
||||
*/
|
||||
void menu_event_queue_clear(MenuEventQueue* queue);
|
||||
|
||||
/**
|
||||
* @brief 向事件队列推送一个简单事件
|
||||
* @param core_ctx 菜单核心上下文
|
||||
* @param type 事件类型
|
||||
* @param priority 事件优先级
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_core_post_event(struct MenuCoreCtx* core_ctx, MenuEventType type, uint8_t priority);
|
||||
|
||||
#endif /* MENU_EVENT_H */
|
||||
141
src/core/menu_hash.c
Normal file
141
src/core/menu_hash.c
Normal file
@ -0,0 +1,141 @@
|
||||
/**
|
||||
**********************************************************************************************************************
|
||||
* @file menu_hash.c
|
||||
* @brief 菜单组件哈希表实现
|
||||
* @author menu_component
|
||||
* @date 2025-12-19
|
||||
**********************************************************************************************************************
|
||||
*/
|
||||
|
||||
/* Includes ----------------------------------------------------------------------------------------------------------*/
|
||||
#include "menu_types.h"
|
||||
|
||||
/* 私有函数原型 -----------------------------------------------------------------------------------------------------*/
|
||||
static uint16_t menu_hash_func(MenuNodeId node_id);
|
||||
|
||||
/* 函数实现 ---------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* @brief 优化的哈希函数,用于菜单节点ID
|
||||
* @param node_id 节点ID
|
||||
* @return 哈希值
|
||||
*/
|
||||
static uint16_t menu_hash_func(MenuNodeId node_id)
|
||||
{
|
||||
// 高性能哈希函数,针对连续节点ID优化,减少哈希冲突
|
||||
uint32_t hash = node_id;
|
||||
|
||||
// 使用FNV-1a哈希算法,具有良好的分布性和低冲突率
|
||||
hash = (hash ^ 0x811C9DC5) * 0x01000193;
|
||||
hash = (hash ^ (hash >> 16)) * 0x01000193;
|
||||
hash = hash ^ (hash >> 16);
|
||||
|
||||
return (uint16_t)(hash % MENU_CONFIG_HASH_TABLE_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 初始化哈希表
|
||||
* @param core_ctx 菜单核心上下文
|
||||
*/
|
||||
void menu_hash_init(struct MenuCoreCtx* core_ctx)
|
||||
{
|
||||
MENU_ASSERT(core_ctx != NULL);
|
||||
|
||||
// 初始化哈希表所有条目为0
|
||||
for (uint16_t i = 0; i < MENU_CONFIG_HASH_TABLE_SIZE; i++) {
|
||||
core_ctx->hash_table[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 将节点添加到哈希表
|
||||
* @param core_ctx 菜单核心上下文
|
||||
* @param node 要添加的节点
|
||||
*/
|
||||
void menu_hash_add(struct MenuCoreCtx* core_ctx, MenuNode* node)
|
||||
{
|
||||
MENU_ASSERT(core_ctx != NULL);
|
||||
MENU_ASSERT(node != NULL);
|
||||
|
||||
// 计算哈希值
|
||||
uint16_t hash = menu_hash_func(node->id);
|
||||
|
||||
// 将节点插入到哈希表对应位置的链表头部
|
||||
node->hash_next_id = core_ctx->hash_table[hash];
|
||||
core_ctx->hash_table[hash] = node->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从哈希表中移除节点
|
||||
* @param core_ctx 菜单核心上下文
|
||||
* @param node 要移除的节点
|
||||
*/
|
||||
void menu_hash_remove(struct MenuCoreCtx* core_ctx, MenuNode* node)
|
||||
{
|
||||
MENU_ASSERT(core_ctx != NULL);
|
||||
MENU_ASSERT(node != NULL);
|
||||
|
||||
// 计算哈希值
|
||||
uint16_t hash = menu_hash_func(node->id);
|
||||
|
||||
// 在链表中查找并移除节点
|
||||
MenuNodeId prev_id = 0;
|
||||
MenuNodeId curr_id = core_ctx->hash_table[hash];
|
||||
MenuNode* curr_node = NULL;
|
||||
|
||||
while (curr_id != 0) {
|
||||
curr_node = &core_ctx->nodes[curr_id - 1];
|
||||
|
||||
if (curr_node->id == node->id) {
|
||||
// 找到了节点,进行移除操作
|
||||
if (prev_id == 0) {
|
||||
// 移除的是链表头
|
||||
core_ctx->hash_table[hash] = node->hash_next_id;
|
||||
} else {
|
||||
// 移除的是链表中间节点
|
||||
MenuNode* prev_node = &core_ctx->nodes[prev_id - 1];
|
||||
prev_node->hash_next_id = node->hash_next_id;
|
||||
}
|
||||
|
||||
// 清空节点的哈希表链接
|
||||
node->hash_next_id = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
// 继续遍历
|
||||
prev_id = curr_id;
|
||||
curr_id = curr_node->hash_next_id;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 根据ID查找节点
|
||||
* @param core_ctx 菜单核心上下文
|
||||
* @param node_id 节点ID
|
||||
* @return 找到的节点指针,未找到返回NULL
|
||||
*/
|
||||
MenuNode* menu_hash_find(struct MenuCoreCtx* core_ctx, MenuNodeId node_id)
|
||||
{
|
||||
MENU_ASSERT(core_ctx != NULL);
|
||||
|
||||
// 计算哈希值
|
||||
uint16_t hash = menu_hash_func(node_id);
|
||||
|
||||
// 在链表中查找节点
|
||||
MenuNodeId curr_id = core_ctx->hash_table[hash];
|
||||
|
||||
while (curr_id != 0) {
|
||||
MenuNode* curr_node = &core_ctx->nodes[curr_id - 1];
|
||||
|
||||
if (curr_node->id == node_id) {
|
||||
// 找到了节点
|
||||
return curr_node;
|
||||
}
|
||||
|
||||
// 继续遍历
|
||||
curr_id = curr_node->hash_next_id;
|
||||
}
|
||||
|
||||
// 未找到节点
|
||||
return NULL;
|
||||
}
|
||||
46
src/core/menu_hash.h
Normal file
46
src/core/menu_hash.h
Normal file
@ -0,0 +1,46 @@
|
||||
/**
|
||||
**********************************************************************************************************************
|
||||
* @file menu_hash.h
|
||||
* @brief 菜单组件哈希表功能声明
|
||||
* @author menu_component
|
||||
* @date 2025-12-19
|
||||
**********************************************************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef MENU_HASH_H
|
||||
#define MENU_HASH_H
|
||||
|
||||
/* Includes ----------------------------------------------------------------------------------------------------------*/
|
||||
#include "menu_types.h"
|
||||
|
||||
/* 函数声明 ---------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* @brief 初始化哈希表
|
||||
* @param core_ctx 菜单核心上下文
|
||||
*/
|
||||
void menu_hash_init(struct MenuCoreCtx* core_ctx);
|
||||
|
||||
/**
|
||||
* @brief 将节点添加到哈希表
|
||||
* @param core_ctx 菜单核心上下文
|
||||
* @param node 要添加的节点
|
||||
*/
|
||||
void menu_hash_add(struct MenuCoreCtx* core_ctx, MenuNode* node);
|
||||
|
||||
/**
|
||||
* @brief 从哈希表中移除节点
|
||||
* @param core_ctx 菜单核心上下文
|
||||
* @param node 要移除的节点
|
||||
*/
|
||||
void menu_hash_remove(struct MenuCoreCtx* core_ctx, MenuNode* node);
|
||||
|
||||
/**
|
||||
* @brief 根据ID查找节点
|
||||
* @param core_ctx 菜单核心上下文
|
||||
* @param node_id 节点ID
|
||||
* @return 找到的节点指针,未找到返回NULL
|
||||
*/
|
||||
MenuNode* menu_hash_find(struct MenuCoreCtx* core_ctx, MenuNodeId node_id);
|
||||
|
||||
#endif /* MENU_HASH_H */
|
||||
350
src/core/menu_permission.c
Normal file
350
src/core/menu_permission.c
Normal file
@ -0,0 +1,350 @@
|
||||
/**
|
||||
**********************************************************************************************************************
|
||||
* @file menu_permission.c
|
||||
* @brief 菜单组件权限管理功能实现
|
||||
* @author menu_component
|
||||
* @date 2025-12-19
|
||||
**********************************************************************************************************************
|
||||
*/
|
||||
|
||||
/* Includes ----------------------------------------------------------------------------------------------------------*/
|
||||
#include "menu_permission.h"
|
||||
#include "menu_hash.h"
|
||||
|
||||
/* 私有函数原型 -----------------------------------------------------------------------------------------------------*/
|
||||
|
||||
#if MENU_CONFIG_ENABLE_PERMISSION
|
||||
|
||||
/**
|
||||
* @brief 获取角色在角色列表中的索引
|
||||
* @param core_ctx 菜单核心上下文
|
||||
* @param role_id 角色ID
|
||||
* @return 角色索引,-1表示未找到
|
||||
*/
|
||||
static int menu_permission_get_role_index(MenuCoreCtx* core_ctx, MenuRoleId role_id);
|
||||
|
||||
/**
|
||||
* @brief 更新单个节点的可见性和可用性
|
||||
* @param core_ctx 菜单核心上下文
|
||||
* @param node 节点指针
|
||||
* @return 错误码
|
||||
*/
|
||||
static MenuErrCode menu_permission_update_node(MenuCoreCtx* core_ctx, MenuNode* node);
|
||||
|
||||
/* 函数实现 ---------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* @brief 初始化权限管理模块
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_permission_init(void)
|
||||
{
|
||||
MenuCoreCtx* core_ctx = menu_get_core_ctx();
|
||||
|
||||
if (core_ctx == NULL) {
|
||||
return MENU_ERR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
// 初始化权限配置
|
||||
core_ctx->permission_config.current_role = 0;
|
||||
core_ctx->permission_config.role_count = 0;
|
||||
|
||||
// 注册默认角色(管理员角色,权限级别最高)
|
||||
MenuRole default_role = {
|
||||
.id = 0,
|
||||
.name = "Admin",
|
||||
.max_level = MENU_CONFIG_PERMISSION_MAX_LEVEL - 1
|
||||
};
|
||||
|
||||
return menu_permission_register_role(&default_role);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 反初始化权限管理模块
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_permission_deinit(void)
|
||||
{
|
||||
MenuCoreCtx* core_ctx = menu_get_core_ctx();
|
||||
|
||||
if (core_ctx == NULL) {
|
||||
return MENU_ERR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
// 清空角色列表
|
||||
core_ctx->permission_config.role_count = 0;
|
||||
core_ctx->permission_config.current_role = 0;
|
||||
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 注册角色
|
||||
* @param role 角色结构体指针
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_permission_register_role(const MenuRole* role)
|
||||
{
|
||||
MenuCoreCtx* core_ctx = menu_get_core_ctx();
|
||||
|
||||
if (core_ctx == NULL || role == NULL) {
|
||||
return MENU_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
if (core_ctx->permission_config.role_count >= MENU_CONFIG_PERMISSION_MAX_ROLES) {
|
||||
return MENU_ERR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
// 检查角色ID是否已存在
|
||||
if (menu_permission_get_role_index(core_ctx, role->id) >= 0) {
|
||||
return MENU_ERR_ALREADY_EXISTS;
|
||||
}
|
||||
|
||||
// 添加角色到列表
|
||||
core_ctx->permission_config.roles[core_ctx->permission_config.role_count] = *role;
|
||||
core_ctx->permission_config.role_count++;
|
||||
|
||||
// 更新所有节点的可见性和可用性
|
||||
return menu_permission_update_all_nodes();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 注销角色
|
||||
* @param role_id 角色ID
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_permission_unregister_role(MenuRoleId role_id)
|
||||
{
|
||||
MenuCoreCtx* core_ctx = menu_get_core_ctx();
|
||||
int index = menu_permission_get_role_index(core_ctx, role_id);
|
||||
|
||||
if (core_ctx == NULL) {
|
||||
return MENU_ERR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (index < 0) {
|
||||
return MENU_ERR_NODE_NOT_FOUND;
|
||||
}
|
||||
|
||||
// 如果当前角色被注销,切换到默认角色
|
||||
if (core_ctx->permission_config.current_role == role_id) {
|
||||
core_ctx->permission_config.current_role = 0;
|
||||
}
|
||||
|
||||
// 移除角色(将后续角色前移)
|
||||
for (uint8_t i = index; i < core_ctx->permission_config.role_count - 1; i++) {
|
||||
core_ctx->permission_config.roles[i] = core_ctx->permission_config.roles[i + 1];
|
||||
}
|
||||
core_ctx->permission_config.role_count--;
|
||||
|
||||
// 更新所有节点的可见性和可用性
|
||||
return menu_permission_update_all_nodes();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置当前角色
|
||||
* @param role_id 角色ID
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_permission_set_current_role(MenuRoleId role_id)
|
||||
{
|
||||
MenuCoreCtx* core_ctx = menu_get_core_ctx();
|
||||
|
||||
if (core_ctx == NULL) {
|
||||
return MENU_ERR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
// 检查角色是否存在
|
||||
if (menu_permission_get_role_index(core_ctx, role_id) < 0) {
|
||||
return MENU_ERR_NODE_NOT_FOUND;
|
||||
}
|
||||
|
||||
// 设置当前角色
|
||||
core_ctx->permission_config.current_role = role_id;
|
||||
|
||||
// 更新所有节点的可见性和可用性
|
||||
return menu_permission_update_all_nodes();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取当前角色
|
||||
* @return 当前角色ID
|
||||
*/
|
||||
MenuRoleId menu_permission_get_current_role(void)
|
||||
{
|
||||
MenuCoreCtx* core_ctx = menu_get_core_ctx();
|
||||
|
||||
if (core_ctx == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return core_ctx->permission_config.current_role;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查节点是否可访问
|
||||
* @param node_id 节点ID
|
||||
* @return true表示可访问,false表示不可访问
|
||||
*/
|
||||
bool menu_permission_check_node_access(MenuNodeId node_id)
|
||||
{
|
||||
MenuCoreCtx* core_ctx = menu_get_core_ctx();
|
||||
MenuNode* node = menu_hash_find(core_ctx, node_id);
|
||||
int role_index;
|
||||
|
||||
if (core_ctx == NULL || node == NULL || !node->flags.is_registered) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 获取当前角色索引
|
||||
role_index = menu_permission_get_role_index(core_ctx, core_ctx->permission_config.current_role);
|
||||
if (role_index < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查权限级别
|
||||
return (node->permission_level <= core_ctx->permission_config.roles[role_index].max_level);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 更新节点权限级别
|
||||
* @param node_id 节点ID
|
||||
* @param permission_level 权限级别
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_permission_update_node_level(MenuNodeId node_id, MenuPermissionLevel permission_level)
|
||||
{
|
||||
MenuCoreCtx* core_ctx = menu_get_core_ctx();
|
||||
MenuNode* node = menu_hash_find(core_ctx, node_id);
|
||||
|
||||
if (core_ctx == NULL || node == NULL || !node->flags.is_registered) {
|
||||
return MENU_ERR_NODE_NOT_FOUND;
|
||||
}
|
||||
|
||||
// 更新权限级别
|
||||
node->permission_level = permission_level;
|
||||
|
||||
// 更新节点的可见性和可用性
|
||||
return menu_permission_update_node(core_ctx, node);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取节点权限级别
|
||||
* @param node_id 节点ID
|
||||
* @return 节点权限级别
|
||||
*/
|
||||
MenuPermissionLevel menu_permission_get_node_level(MenuNodeId node_id)
|
||||
{
|
||||
MenuCoreCtx* core_ctx = menu_get_core_ctx();
|
||||
MenuNode* node = menu_hash_find(core_ctx, node_id);
|
||||
|
||||
if (core_ctx == NULL || node == NULL || !node->flags.is_registered) {
|
||||
return MENU_CONFIG_PERMISSION_MAX_LEVEL;
|
||||
}
|
||||
|
||||
return node->permission_level;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 更新所有节点的可见性和可用性
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_permission_update_all_nodes(void)
|
||||
{
|
||||
MenuCoreCtx* core_ctx = menu_get_core_ctx();
|
||||
|
||||
if (core_ctx == NULL) {
|
||||
return MENU_ERR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
// 遍历所有节点,更新可见性和可用性
|
||||
for (uint16_t i = 0; i < MENU_CONFIG_MAX_NODES; i++) {
|
||||
if (core_ctx->nodes[i].flags.is_registered) {
|
||||
menu_permission_update_node(core_ctx, &core_ctx->nodes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取角色信息
|
||||
* @param role_id 角色ID
|
||||
* @return 角色结构体指针,NULL表示角色不存在
|
||||
*/
|
||||
const MenuRole* menu_permission_get_role(MenuRoleId role_id)
|
||||
{
|
||||
MenuCoreCtx* core_ctx = menu_get_core_ctx();
|
||||
int index;
|
||||
|
||||
if (core_ctx == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
index = menu_permission_get_role_index(core_ctx, role_id);
|
||||
if (index < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &core_ctx->permission_config.roles[index];
|
||||
}
|
||||
|
||||
/* 私有函数实现 -----------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* @brief 获取角色在角色列表中的索引
|
||||
* @param core_ctx 菜单核心上下文
|
||||
* @param role_id 角色ID
|
||||
* @return 角色索引,-1表示未找到
|
||||
*/
|
||||
static int menu_permission_get_role_index(MenuCoreCtx* core_ctx, MenuRoleId role_id)
|
||||
{
|
||||
if (core_ctx == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 遍历角色列表,查找匹配的角色ID
|
||||
for (uint8_t i = 0; i < core_ctx->permission_config.role_count; i++) {
|
||||
if (core_ctx->permission_config.roles[i].id == role_id) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 更新单个节点的可见性和可用性
|
||||
* @param core_ctx 菜单核心上下文
|
||||
* @param node 节点指针
|
||||
* @return 错误码
|
||||
*/
|
||||
static MenuErrCode menu_permission_update_node(MenuCoreCtx* core_ctx, MenuNode* node)
|
||||
{
|
||||
int role_index;
|
||||
bool is_accessible;
|
||||
|
||||
if (core_ctx == NULL || node == NULL) {
|
||||
return MENU_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
// 获取当前角色索引
|
||||
role_index = menu_permission_get_role_index(core_ctx, core_ctx->permission_config.current_role);
|
||||
if (role_index < 0) {
|
||||
// 角色不存在,禁用节点
|
||||
node->flags.is_visible = false;
|
||||
node->flags.is_enabled = false;
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
// 检查节点是否可访问
|
||||
is_accessible = (node->permission_level <= core_ctx->permission_config.roles[role_index].max_level);
|
||||
|
||||
// 更新节点的可见性和可用性
|
||||
node->flags.is_visible = is_accessible;
|
||||
node->flags.is_enabled = is_accessible;
|
||||
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
#endif /* MENU_CONFIG_ENABLE_PERMISSION */
|
||||
96
src/core/menu_permission.h
Normal file
96
src/core/menu_permission.h
Normal file
@ -0,0 +1,96 @@
|
||||
/**
|
||||
**********************************************************************************************************************
|
||||
* @file menu_permission.h
|
||||
* @brief 菜单组件权限管理功能声明
|
||||
* @author menu_component
|
||||
* @date 2025-12-19
|
||||
**********************************************************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef MENU_PERMISSION_H
|
||||
#define MENU_PERMISSION_H
|
||||
|
||||
/* Includes ----------------------------------------------------------------------------------------------------------*/
|
||||
#include "menu_types.h"
|
||||
|
||||
/* 函数声明 ---------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
#if MENU_CONFIG_ENABLE_PERMISSION
|
||||
|
||||
/**
|
||||
* @brief 初始化权限管理模块
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_permission_init(void);
|
||||
|
||||
/**
|
||||
* @brief 反初始化权限管理模块
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_permission_deinit(void);
|
||||
|
||||
/**
|
||||
* @brief 注册角色
|
||||
* @param role 角色结构体指针
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_permission_register_role(const MenuRole* role);
|
||||
|
||||
/**
|
||||
* @brief 注销角色
|
||||
* @param role_id 角色ID
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_permission_unregister_role(MenuRoleId role_id);
|
||||
|
||||
/**
|
||||
* @brief 设置当前角色
|
||||
* @param role_id 角色ID
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_permission_set_current_role(MenuRoleId role_id);
|
||||
|
||||
/**
|
||||
* @brief 获取当前角色
|
||||
* @return 当前角色ID
|
||||
*/
|
||||
MenuRoleId menu_permission_get_current_role(void);
|
||||
|
||||
/**
|
||||
* @brief 检查节点是否可访问
|
||||
* @param node_id 节点ID
|
||||
* @return true表示可访问,false表示不可访问
|
||||
*/
|
||||
bool menu_permission_check_node_access(MenuNodeId node_id);
|
||||
|
||||
/**
|
||||
* @brief 更新节点权限级别
|
||||
* @param node_id 节点ID
|
||||
* @param permission_level 权限级别
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_permission_update_node_level(MenuNodeId node_id, MenuPermissionLevel permission_level);
|
||||
|
||||
/**
|
||||
* @brief 获取节点权限级别
|
||||
* @param node_id 节点ID
|
||||
* @return 节点权限级别
|
||||
*/
|
||||
MenuPermissionLevel menu_permission_get_node_level(MenuNodeId node_id);
|
||||
|
||||
/**
|
||||
* @brief 更新所有节点的可见性和可用性
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_permission_update_all_nodes(void);
|
||||
|
||||
/**
|
||||
* @brief 获取角色信息
|
||||
* @param role_id 角色ID
|
||||
* @return 角色结构体指针,NULL表示角色不存在
|
||||
*/
|
||||
const MenuRole* menu_permission_get_role(MenuRoleId role_id);
|
||||
|
||||
#endif /* MENU_CONFIG_ENABLE_PERMISSION */
|
||||
|
||||
#endif /* MENU_PERMISSION_H */
|
||||
441
src/core/menu_persistence.c
Normal file
441
src/core/menu_persistence.c
Normal file
@ -0,0 +1,441 @@
|
||||
/**
|
||||
**********************************************************************************************************************
|
||||
* @file menu_persistence.c
|
||||
* @brief 菜单组件状态持久化功能实现
|
||||
* @author menu_component
|
||||
* @date 2025-12-19
|
||||
**********************************************************************************************************************
|
||||
*/
|
||||
|
||||
/* Includes ----------------------------------------------------------------------------------------------------------*/
|
||||
#include "menu_persistence.h"
|
||||
|
||||
/* 私有函数原型 -----------------------------------------------------------------------------------------------------*/
|
||||
|
||||
#if MENU_CONFIG_ENABLE_PERSISTENCE
|
||||
|
||||
/**
|
||||
* @brief 构建持久化数据
|
||||
* @param core_ctx 菜单核心上下文
|
||||
* @param data 持久化数据指针
|
||||
* @return 错误码
|
||||
*/
|
||||
static MenuErrCode menu_persistence_build_data(MenuCoreCtx* core_ctx, MenuPersistenceData* data);
|
||||
|
||||
/**
|
||||
* @brief 应用持久化数据
|
||||
* @param core_ctx 菜单核心上下文
|
||||
* @param data 持久化数据指针
|
||||
* @return 错误码
|
||||
*/
|
||||
static MenuErrCode menu_persistence_apply_data(MenuCoreCtx* core_ctx, const MenuPersistenceData* data);
|
||||
|
||||
/**
|
||||
* @brief 加密持久化数据
|
||||
* @param data 要加密的数据
|
||||
* @param size 数据大小
|
||||
* @return 错误码
|
||||
*/
|
||||
static MenuErrCode menu_persistence_encrypt(uint8_t* data, uint16_t size);
|
||||
|
||||
/**
|
||||
* @brief 解密持久化数据
|
||||
* @param data 要解密的数据
|
||||
* @param size 数据大小
|
||||
* @return 错误码
|
||||
*/
|
||||
static MenuErrCode menu_persistence_decrypt(uint8_t* data, uint16_t size);
|
||||
|
||||
/* 函数实现 ---------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* @brief 初始化状态持久化模块
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_persistence_init(void)
|
||||
{
|
||||
MenuCoreCtx* core_ctx = menu_get_core_ctx();
|
||||
|
||||
if (core_ctx == NULL) {
|
||||
return MENU_ERR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
// 初始化持久化配置
|
||||
core_ctx->persistence_config.auto_save_enabled = true;
|
||||
core_ctx->persistence_config.auto_save_interval = MENU_CONFIG_PERSISTENCE_AUTO_SAVE_INTERVAL;
|
||||
core_ctx->persistence_config.last_save_tick = 0;
|
||||
core_ctx->persistence_config.encrypt_enabled = (MENU_CONFIG_PERSISTENCE_ENABLE_ENCRYPT == 1U);
|
||||
core_ctx->persistence_config.save_cb = NULL;
|
||||
core_ctx->persistence_config.restore_cb = NULL;
|
||||
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 反初始化状态持久化模块
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_persistence_deinit(void)
|
||||
{
|
||||
MenuCoreCtx* core_ctx = menu_get_core_ctx();
|
||||
|
||||
if (core_ctx == NULL) {
|
||||
return MENU_ERR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
// 清空持久化配置
|
||||
core_ctx->persistence_config.save_cb = NULL;
|
||||
core_ctx->persistence_config.restore_cb = NULL;
|
||||
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 注册持久化回调函数
|
||||
* @param save_cb 保存回调函数
|
||||
* @param restore_cb 恢复回调函数
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_persistence_register_callback(MenuPersistenceCallback save_cb, MenuRestoreCallback restore_cb)
|
||||
{
|
||||
MenuCoreCtx* core_ctx = menu_get_core_ctx();
|
||||
|
||||
if (core_ctx == NULL) {
|
||||
return MENU_ERR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
// 注册回调函数
|
||||
core_ctx->persistence_config.save_cb = save_cb;
|
||||
core_ctx->persistence_config.restore_cb = restore_cb;
|
||||
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 启用或禁用自动保存
|
||||
* @param enable true表示启用,false表示禁用
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_persistence_set_auto_save(bool enable)
|
||||
{
|
||||
MenuCoreCtx* core_ctx = menu_get_core_ctx();
|
||||
|
||||
if (core_ctx == NULL) {
|
||||
return MENU_ERR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
core_ctx->persistence_config.auto_save_enabled = enable;
|
||||
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取自动保存状态
|
||||
* @return true表示启用,false表示禁用
|
||||
*/
|
||||
bool menu_persistence_get_auto_save(void)
|
||||
{
|
||||
MenuCoreCtx* core_ctx = menu_get_core_ctx();
|
||||
|
||||
if (core_ctx == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return core_ctx->persistence_config.auto_save_enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置自动保存间隔
|
||||
* @param interval 自动保存间隔(ms)
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_persistence_set_auto_save_interval(uint32_t interval)
|
||||
{
|
||||
MenuCoreCtx* core_ctx = menu_get_core_ctx();
|
||||
|
||||
if (core_ctx == NULL) {
|
||||
return MENU_ERR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
core_ctx->persistence_config.auto_save_interval = interval;
|
||||
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取自动保存间隔
|
||||
* @return 自动保存间隔(ms)
|
||||
*/
|
||||
uint32_t menu_persistence_get_auto_save_interval(void)
|
||||
{
|
||||
MenuCoreCtx* core_ctx = menu_get_core_ctx();
|
||||
|
||||
if (core_ctx == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return core_ctx->persistence_config.auto_save_interval;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 手动保存菜单状态
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_persistence_save(void)
|
||||
{
|
||||
MenuCoreCtx* core_ctx = menu_get_core_ctx();
|
||||
MenuPersistenceData data;
|
||||
uint8_t* data_buf;
|
||||
uint16_t data_size = sizeof(MenuPersistenceData);
|
||||
MenuErrCode ret;
|
||||
|
||||
if (core_ctx == NULL) {
|
||||
return MENU_ERR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (core_ctx->persistence_config.save_cb == NULL) {
|
||||
return MENU_ERR_INVALID_CALLBACK;
|
||||
}
|
||||
|
||||
// 构建持久化数据
|
||||
ret = menu_persistence_build_data(core_ctx, &data);
|
||||
if (ret != MENU_ERR_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// 转换为字节数组
|
||||
data_buf = (uint8_t*)&data;
|
||||
|
||||
// 如果启用加密,加密数据
|
||||
if (core_ctx->persistence_config.encrypt_enabled) {
|
||||
ret = menu_persistence_encrypt(data_buf, data_size);
|
||||
if (ret != MENU_ERR_OK) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
// 调用保存回调函数
|
||||
ret = core_ctx->persistence_config.save_cb(data_buf, data_size);
|
||||
if (ret == MENU_ERR_OK) {
|
||||
// 更新上次保存时间
|
||||
core_ctx->persistence_config.last_save_tick = core_ctx->last_refresh_tick;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 恢复菜单状态
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_persistence_restore(void)
|
||||
{
|
||||
MenuCoreCtx* core_ctx = menu_get_core_ctx();
|
||||
uint8_t data_buf[MENU_CONFIG_PERSISTENCE_MAX_SIZE];
|
||||
uint16_t data_size = sizeof(MenuPersistenceData);
|
||||
MenuPersistenceData* data = (MenuPersistenceData*)data_buf;
|
||||
MenuErrCode ret;
|
||||
|
||||
if (core_ctx == NULL) {
|
||||
return MENU_ERR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (core_ctx->persistence_config.restore_cb == NULL) {
|
||||
return MENU_ERR_INVALID_CALLBACK;
|
||||
}
|
||||
|
||||
// 调用恢复回调函数
|
||||
ret = core_ctx->persistence_config.restore_cb(data_buf, &data_size);
|
||||
if (ret != MENU_ERR_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// 检查数据大小是否正确
|
||||
if (data_size != sizeof(MenuPersistenceData)) {
|
||||
return MENU_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
// 如果启用加密,解密数据
|
||||
if (core_ctx->persistence_config.encrypt_enabled) {
|
||||
ret = menu_persistence_decrypt(data_buf, data_size);
|
||||
if (ret != MENU_ERR_OK) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查数据是否有效
|
||||
if (!menu_persistence_check_data_valid(data_buf, data_size)) {
|
||||
return MENU_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
// 应用持久化数据
|
||||
return menu_persistence_apply_data(core_ctx, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查持久化数据是否有效
|
||||
* @param data 持久化数据
|
||||
* @param size 数据大小
|
||||
* @return true表示有效,false表示无效
|
||||
*/
|
||||
bool menu_persistence_check_data_valid(const uint8_t* data, uint16_t size)
|
||||
{
|
||||
const MenuPersistenceData* persistence_data;
|
||||
uint8_t calculated_checksum;
|
||||
|
||||
if (data == NULL || size != sizeof(MenuPersistenceData)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
persistence_data = (const MenuPersistenceData*)data;
|
||||
|
||||
// 计算校验和
|
||||
calculated_checksum = menu_persistence_calculate_checksum(data, size - sizeof(uint8_t));
|
||||
|
||||
// 检查校验和
|
||||
return (calculated_checksum == persistence_data->checksum);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 处理自动保存(由菜单主循环调用)
|
||||
* @param tick 当前系统时间(ms)
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_persistence_process_auto_save(uint32_t tick)
|
||||
{
|
||||
MenuCoreCtx* core_ctx = menu_get_core_ctx();
|
||||
|
||||
if (core_ctx == NULL) {
|
||||
return MENU_ERR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
// 如果启用自动保存,检查是否需要保存
|
||||
if (core_ctx->persistence_config.auto_save_enabled) {
|
||||
if ((tick - core_ctx->persistence_config.last_save_tick) >= core_ctx->persistence_config.auto_save_interval) {
|
||||
return menu_persistence_save();
|
||||
}
|
||||
}
|
||||
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 计算数据校验和
|
||||
* @param data 数据指针
|
||||
* @param size 数据大小
|
||||
* @return 校验和
|
||||
*/
|
||||
uint8_t menu_persistence_calculate_checksum(const uint8_t* data, uint16_t size)
|
||||
{
|
||||
uint8_t checksum = 0;
|
||||
|
||||
if (data == NULL || size == 0) {
|
||||
return checksum;
|
||||
}
|
||||
|
||||
// 计算简单的异或校验和
|
||||
for (uint16_t i = 0; i < size; i++) {
|
||||
checksum ^= data[i];
|
||||
}
|
||||
|
||||
return checksum;
|
||||
}
|
||||
|
||||
/* 私有函数实现 -----------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* @brief 构建持久化数据
|
||||
* @param core_ctx 菜单核心上下文
|
||||
* @param data 持久化数据指针
|
||||
* @return 错误码
|
||||
*/
|
||||
static MenuErrCode menu_persistence_build_data(MenuCoreCtx* core_ctx, MenuPersistenceData* data)
|
||||
{
|
||||
if (core_ctx == NULL || data == NULL) {
|
||||
return MENU_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
// 填充持久化数据
|
||||
data->current_node_id = core_ctx->current_node_id;
|
||||
data->current_state = core_ctx->current_state;
|
||||
data->nav_path = core_ctx->nav_path;
|
||||
data->stack = core_ctx->stack;
|
||||
data->timestamp = core_ctx->last_refresh_tick;
|
||||
|
||||
// 计算校验和(不包括校验和字段本身)
|
||||
data->checksum = menu_persistence_calculate_checksum((uint8_t*)data, sizeof(MenuPersistenceData) - sizeof(uint8_t));
|
||||
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 应用持久化数据
|
||||
* @param core_ctx 菜单核心上下文
|
||||
* @param data 持久化数据指针
|
||||
* @return 错误码
|
||||
*/
|
||||
static MenuErrCode menu_persistence_apply_data(MenuCoreCtx* core_ctx, const MenuPersistenceData* data)
|
||||
{
|
||||
if (core_ctx == NULL || data == NULL) {
|
||||
return MENU_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
// 恢复菜单状态
|
||||
core_ctx->current_node_id = data->current_node_id;
|
||||
core_ctx->current_state = data->current_state;
|
||||
core_ctx->nav_path = data->nav_path;
|
||||
core_ctx->stack = data->stack;
|
||||
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 加密持久化数据
|
||||
* @param data 要加密的数据
|
||||
* @param size 数据大小
|
||||
* @return 错误码
|
||||
*/
|
||||
static MenuErrCode menu_persistence_encrypt(uint8_t* data, uint16_t size)
|
||||
{
|
||||
// 简单的XOR加密实现
|
||||
// 实际应用中应使用更安全的加密算法
|
||||
uint8_t key = 0xA5; // 加密密钥
|
||||
|
||||
if (data == NULL || size == 0) {
|
||||
return MENU_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
for (uint16_t i = 0; i < size; i++) {
|
||||
data[i] ^= key;
|
||||
key = (key + data[i]) % 256; // 动态更新密钥
|
||||
}
|
||||
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 解密持久化数据
|
||||
* @param data 要解密的数据
|
||||
* @param size 数据大小
|
||||
* @return 错误码
|
||||
*/
|
||||
static MenuErrCode menu_persistence_decrypt(uint8_t* data, uint16_t size)
|
||||
{
|
||||
// 简单的XOR解密实现(与加密算法对称)
|
||||
uint8_t key = 0xA5; // 解密密钥
|
||||
uint8_t temp;
|
||||
|
||||
if (data == NULL || size == 0) {
|
||||
return MENU_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
for (uint16_t i = 0; i < size; i++) {
|
||||
temp = data[i];
|
||||
data[i] ^= key;
|
||||
key = (key + temp) % 256; // 动态更新密钥
|
||||
}
|
||||
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
#endif /* MENU_CONFIG_ENABLE_PERSISTENCE */
|
||||
103
src/core/menu_persistence.h
Normal file
103
src/core/menu_persistence.h
Normal file
@ -0,0 +1,103 @@
|
||||
/**
|
||||
**********************************************************************************************************************
|
||||
* @file menu_persistence.h
|
||||
* @brief 菜单组件状态持久化功能声明
|
||||
* @author menu_component
|
||||
* @date 2025-12-19
|
||||
**********************************************************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef MENU_PERSISTENCE_H
|
||||
#define MENU_PERSISTENCE_H
|
||||
|
||||
/* Includes ----------------------------------------------------------------------------------------------------------*/
|
||||
#include "menu_types.h"
|
||||
|
||||
/* 函数声明 ---------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
#if MENU_CONFIG_ENABLE_PERSISTENCE
|
||||
|
||||
/**
|
||||
* @brief 初始化状态持久化模块
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_persistence_init(void);
|
||||
|
||||
/**
|
||||
* @brief 反初始化状态持久化模块
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_persistence_deinit(void);
|
||||
|
||||
/**
|
||||
* @brief 注册持久化回调函数
|
||||
* @param save_cb 保存回调函数
|
||||
* @param restore_cb 恢复回调函数
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_persistence_register_callback(MenuPersistenceCallback save_cb, MenuRestoreCallback restore_cb);
|
||||
|
||||
/**
|
||||
* @brief 启用或禁用自动保存
|
||||
* @param enable true表示启用,false表示禁用
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_persistence_set_auto_save(bool enable);
|
||||
|
||||
/**
|
||||
* @brief 获取自动保存状态
|
||||
* @return true表示启用,false表示禁用
|
||||
*/
|
||||
bool menu_persistence_get_auto_save(void);
|
||||
|
||||
/**
|
||||
* @brief 设置自动保存间隔
|
||||
* @param interval 自动保存间隔(ms)
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_persistence_set_auto_save_interval(uint32_t interval);
|
||||
|
||||
/**
|
||||
* @brief 获取自动保存间隔
|
||||
* @return 自动保存间隔(ms)
|
||||
*/
|
||||
uint32_t menu_persistence_get_auto_save_interval(void);
|
||||
|
||||
/**
|
||||
* @brief 手动保存菜单状态
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_persistence_save(void);
|
||||
|
||||
/**
|
||||
* @brief 恢复菜单状态
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_persistence_restore(void);
|
||||
|
||||
/**
|
||||
* @brief 检查持久化数据是否有效
|
||||
* @param data 持久化数据
|
||||
* @param size 数据大小
|
||||
* @return true表示有效,false表示无效
|
||||
*/
|
||||
bool menu_persistence_check_data_valid(const uint8_t* data, uint16_t size);
|
||||
|
||||
/**
|
||||
* @brief 处理自动保存(由菜单主循环调用)
|
||||
* @param tick 当前系统时间(ms)
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_persistence_process_auto_save(uint32_t tick);
|
||||
|
||||
/**
|
||||
* @brief 计算数据校验和
|
||||
* @param data 数据指针
|
||||
* @param size 数据大小
|
||||
* @return 校验和
|
||||
*/
|
||||
uint8_t menu_persistence_calculate_checksum(const uint8_t* data, uint16_t size);
|
||||
|
||||
#endif /* MENU_CONFIG_ENABLE_PERSISTENCE */
|
||||
|
||||
#endif /* MENU_PERSISTENCE_H */
|
||||
215
src/core/menu_stack.c
Normal file
215
src/core/menu_stack.c
Normal file
@ -0,0 +1,215 @@
|
||||
/**
|
||||
**********************************************************************************************************************
|
||||
* @file menu_stack.c
|
||||
* @brief 菜单组件栈管理实现
|
||||
* @author menu_component
|
||||
* @date 2025-12-19
|
||||
**********************************************************************************************************************
|
||||
*/
|
||||
|
||||
/* Includes ----------------------------------------------------------------------------------------------------------*/
|
||||
#include "menu_types.h"
|
||||
|
||||
/* 函数实现 ---------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* @brief 初始化菜单栈
|
||||
* @param stack 栈指针
|
||||
* @param max_depth 最大深度
|
||||
*/
|
||||
void menu_stack_init(MenuStack* stack, uint8_t max_depth)
|
||||
{
|
||||
MENU_ASSERT(stack != NULL);
|
||||
|
||||
stack->top = 0;
|
||||
stack->max_depth = max_depth;
|
||||
|
||||
// 初始化栈元素为0
|
||||
for (uint8_t i = 0; i < max_depth; i++) {
|
||||
stack->items[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查栈是否为空
|
||||
* @param stack 栈指针
|
||||
* @return true为空,false不为空
|
||||
*/
|
||||
bool menu_stack_is_empty(const MenuStack* stack)
|
||||
{
|
||||
MENU_ASSERT(stack != NULL);
|
||||
return (stack->top == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查栈是否已满
|
||||
* @param stack 栈指针
|
||||
* @return true已满,false未满
|
||||
*/
|
||||
bool menu_stack_is_full(const MenuStack* stack)
|
||||
{
|
||||
MENU_ASSERT(stack != NULL);
|
||||
return (stack->top >= stack->max_depth);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取栈的当前深度
|
||||
* @param stack 栈指针
|
||||
* @return 当前深度
|
||||
*/
|
||||
uint8_t menu_stack_get_depth(const MenuStack* stack)
|
||||
{
|
||||
MENU_ASSERT(stack != NULL);
|
||||
return stack->top;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 向栈中压入元素
|
||||
* @param stack 栈指针
|
||||
* @param node_id 要压入的节点ID
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_stack_push(MenuStack* stack, MenuNodeId node_id)
|
||||
{
|
||||
MENU_ASSERT(stack != NULL);
|
||||
|
||||
if (menu_stack_is_full(stack)) {
|
||||
return MENU_ERR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
stack->items[stack->top] = node_id;
|
||||
stack->top++;
|
||||
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从栈中弹出元素
|
||||
* @param stack 栈指针
|
||||
* @param node_id 用于存储弹出的节点ID
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_stack_pop(MenuStack* stack, MenuNodeId* node_id)
|
||||
{
|
||||
MENU_ASSERT(stack != NULL);
|
||||
MENU_ASSERT(node_id != NULL);
|
||||
|
||||
if (menu_stack_is_empty(stack)) {
|
||||
return MENU_ERR_QUEUE_EMPTY;
|
||||
}
|
||||
|
||||
stack->top--;
|
||||
*node_id = stack->items[stack->top];
|
||||
stack->items[stack->top] = 0;
|
||||
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取栈顶元素
|
||||
* @param stack 栈指针
|
||||
* @param node_id 用于存储栈顶节点ID
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_stack_peek(const MenuStack* stack, MenuNodeId* node_id)
|
||||
{
|
||||
MENU_ASSERT(stack != NULL);
|
||||
MENU_ASSERT(node_id != NULL);
|
||||
|
||||
if (menu_stack_is_empty(stack)) {
|
||||
return MENU_ERR_QUEUE_EMPTY;
|
||||
}
|
||||
|
||||
*node_id = stack->items[stack->top - 1];
|
||||
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 清空栈
|
||||
* @param stack 栈指针
|
||||
*/
|
||||
void menu_stack_clear(MenuStack* stack)
|
||||
{
|
||||
MENU_ASSERT(stack != NULL);
|
||||
|
||||
stack->top = 0;
|
||||
|
||||
// 清空栈元素
|
||||
for (uint8_t i = 0; i < stack->max_depth; i++) {
|
||||
stack->items[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 初始化菜单导航路径
|
||||
* @param nav_path 导航路径指针
|
||||
* @param max_depth 最大深度
|
||||
*/
|
||||
void menu_nav_path_init(MenuNavPath* nav_path, uint8_t max_depth)
|
||||
{
|
||||
MENU_ASSERT(nav_path != NULL);
|
||||
|
||||
nav_path->depth = 0;
|
||||
nav_path->max_depth = max_depth;
|
||||
|
||||
// 初始化路径为0
|
||||
for (uint8_t i = 0; i < max_depth; i++) {
|
||||
nav_path->path[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 添加导航路径节点
|
||||
* @param nav_path 导航路径指针
|
||||
* @param node_id 要添加的节点ID
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_nav_path_add(MenuNavPath* nav_path, MenuNodeId node_id)
|
||||
{
|
||||
MENU_ASSERT(nav_path != NULL);
|
||||
|
||||
if (nav_path->depth >= nav_path->max_depth) {
|
||||
return MENU_ERR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
nav_path->path[nav_path->depth] = node_id;
|
||||
nav_path->depth++;
|
||||
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 移除导航路径最后一个节点
|
||||
* @param nav_path 导航路径指针
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_nav_path_remove_last(MenuNavPath* nav_path)
|
||||
{
|
||||
MENU_ASSERT(nav_path != NULL);
|
||||
|
||||
if (nav_path->depth == 0) {
|
||||
return MENU_ERR_QUEUE_EMPTY;
|
||||
}
|
||||
|
||||
nav_path->depth--;
|
||||
nav_path->path[nav_path->depth] = 0;
|
||||
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 清空导航路径
|
||||
* @param nav_path 导航路径指针
|
||||
*/
|
||||
void menu_nav_path_clear(MenuNavPath* nav_path)
|
||||
{
|
||||
MENU_ASSERT(nav_path != NULL);
|
||||
|
||||
nav_path->depth = 0;
|
||||
|
||||
// 清空路径
|
||||
for (uint8_t i = 0; i < nav_path->max_depth; i++) {
|
||||
nav_path->path[i] = 0;
|
||||
}
|
||||
}
|
||||
104
src/core/menu_stack.h
Normal file
104
src/core/menu_stack.h
Normal file
@ -0,0 +1,104 @@
|
||||
/**
|
||||
**********************************************************************************************************************
|
||||
* @file menu_stack.h
|
||||
* @brief 菜单组件栈管理功能声明
|
||||
* @author menu_component
|
||||
* @date 2025-12-19
|
||||
**********************************************************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef MENU_STACK_H
|
||||
#define MENU_STACK_H
|
||||
|
||||
/* Includes ----------------------------------------------------------------------------------------------------------*/
|
||||
#include "menu_types.h"
|
||||
|
||||
/* 函数声明 ---------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* @brief 初始化菜单栈
|
||||
* @param stack 栈指针
|
||||
* @param max_depth 最大深度
|
||||
*/
|
||||
void menu_stack_init(MenuStack* stack, uint8_t max_depth);
|
||||
|
||||
/**
|
||||
* @brief 检查栈是否为空
|
||||
* @param stack 栈指针
|
||||
* @return true为空,false不为空
|
||||
*/
|
||||
bool menu_stack_is_empty(const MenuStack* stack);
|
||||
|
||||
/**
|
||||
* @brief 检查栈是否已满
|
||||
* @param stack 栈指针
|
||||
* @return true已满,false未满
|
||||
*/
|
||||
bool menu_stack_is_full(const MenuStack* stack);
|
||||
|
||||
/**
|
||||
* @brief 获取栈的当前深度
|
||||
* @param stack 栈指针
|
||||
* @return 当前深度
|
||||
*/
|
||||
uint8_t menu_stack_get_depth(const MenuStack* stack);
|
||||
|
||||
/**
|
||||
* @brief 向栈中压入元素
|
||||
* @param stack 栈指针
|
||||
* @param node_id 要压入的节点ID
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_stack_push(MenuStack* stack, MenuNodeId node_id);
|
||||
|
||||
/**
|
||||
* @brief 从栈中弹出元素
|
||||
* @param stack 栈指针
|
||||
* @param node_id 用于存储弹出的节点ID
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_stack_pop(MenuStack* stack, MenuNodeId* node_id);
|
||||
|
||||
/**
|
||||
* @brief 获取栈顶元素
|
||||
* @param stack 栈指针
|
||||
* @param node_id 用于存储栈顶节点ID
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_stack_peek(const MenuStack* stack, MenuNodeId* node_id);
|
||||
|
||||
/**
|
||||
* @brief 清空栈
|
||||
* @param stack 栈指针
|
||||
*/
|
||||
void menu_stack_clear(MenuStack* stack);
|
||||
|
||||
/**
|
||||
* @brief 初始化菜单导航路径
|
||||
* @param nav_path 导航路径指针
|
||||
* @param max_depth 最大深度
|
||||
*/
|
||||
void menu_nav_path_init(MenuNavPath* nav_path, uint8_t max_depth);
|
||||
|
||||
/**
|
||||
* @brief 添加导航路径节点
|
||||
* @param nav_path 导航路径指针
|
||||
* @param node_id 要添加的节点ID
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_nav_path_add(MenuNavPath* nav_path, MenuNodeId node_id);
|
||||
|
||||
/**
|
||||
* @brief 移除导航路径最后一个节点
|
||||
* @param nav_path 导航路径指针
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_nav_path_remove_last(MenuNavPath* nav_path);
|
||||
|
||||
/**
|
||||
* @brief 清空导航路径
|
||||
* @param nav_path 导航路径指针
|
||||
*/
|
||||
void menu_nav_path_clear(MenuNavPath* nav_path);
|
||||
|
||||
#endif /* MENU_STACK_H */
|
||||
233
src/core/menu_types.h
Normal file
233
src/core/menu_types.h
Normal file
@ -0,0 +1,233 @@
|
||||
/**
|
||||
**********************************************************************************************************************
|
||||
* @file menu_types.h
|
||||
* @brief 菜单组件核心数据结构定义
|
||||
* @author menu_component
|
||||
* @date 2025-12-19
|
||||
**********************************************************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef MENU_TYPES_H
|
||||
#define MENU_TYPES_H
|
||||
|
||||
/* Includes ----------------------------------------------------------------------------------------------------------*/
|
||||
#include "menu_config.h"
|
||||
|
||||
/* 前置声明 ---------------------------------------------------------------------------------------------------------*/
|
||||
struct MenuNode;
|
||||
struct MenuCoreCtx;
|
||||
struct MenuEvent;
|
||||
struct MenuStateTransition;
|
||||
|
||||
/* 函数前向声明 -----------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* @brief 获取菜单核心上下文
|
||||
* @return 菜单核心上下文指针
|
||||
*/
|
||||
struct MenuCoreCtx* menu_get_core_ctx(void);
|
||||
|
||||
/* 回调函数类型定义 -------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* @brief 菜单进入回调函数类型
|
||||
* @param node_id 当前节点ID
|
||||
* @param core_ctx 菜单核心上下文
|
||||
* @return 错误码
|
||||
*/
|
||||
typedef MenuErrCode (*MenuCallback)(MenuNodeId node_id, struct MenuCoreCtx* core_ctx);
|
||||
|
||||
/**
|
||||
* @brief 状态转换动作函数类型
|
||||
* @param core_ctx 菜单核心上下文
|
||||
* @return 错误码
|
||||
*/
|
||||
typedef MenuErrCode (*MenuStateAction)(struct MenuCoreCtx* core_ctx);
|
||||
|
||||
/**
|
||||
* @brief 持久化回调函数类型
|
||||
* @param data 要持久化的数据
|
||||
* @param size 数据大小
|
||||
* @return 错误码
|
||||
*/
|
||||
typedef MenuErrCode (*MenuPersistenceCallback)(const uint8_t* data, uint16_t size);
|
||||
|
||||
/**
|
||||
* @brief 恢复回调函数类型
|
||||
* @param data 要恢复的数据
|
||||
* @param size 数据大小
|
||||
* @return 错误码
|
||||
*/
|
||||
typedef MenuErrCode (*MenuRestoreCallback)(uint8_t* data, uint16_t* size);
|
||||
|
||||
/* 权限管理相关定义 -------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* @brief 权限级别类型
|
||||
*/
|
||||
typedef uint8_t MenuPermissionLevel;
|
||||
|
||||
/**
|
||||
* @brief 角色ID类型
|
||||
*/
|
||||
typedef uint8_t MenuRoleId;
|
||||
|
||||
/**
|
||||
* @brief 角色结构体
|
||||
*/
|
||||
typedef struct {
|
||||
MenuRoleId id; ///< 角色ID
|
||||
const char* name; ///< 角色名称
|
||||
MenuPermissionLevel max_level; ///< 该角色的最大权限级别
|
||||
} MenuRole;
|
||||
|
||||
/**
|
||||
* @brief 权限配置结构体
|
||||
*/
|
||||
typedef struct {
|
||||
MenuRoleId current_role; ///< 当前角色ID
|
||||
MenuRole roles[MENU_CONFIG_PERMISSION_MAX_ROLES]; ///< 角色列表
|
||||
uint8_t role_count; ///< 角色数量
|
||||
} MenuPermissionConfig;
|
||||
|
||||
/* 核心数据结构定义 -------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* @brief 菜单节点结构体
|
||||
*/
|
||||
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;
|
||||
#if MENU_CONFIG_ENABLE_PARAM
|
||||
uint16_t param_id; ///< 绑定的参数ID(启用参数时有效)
|
||||
#endif
|
||||
#if MENU_CONFIG_ENABLE_PERMISSION
|
||||
MenuPermissionLevel permission_level; ///< 访问该菜单所需的权限级别
|
||||
#endif
|
||||
} MenuNode;
|
||||
|
||||
/**
|
||||
* @brief 菜单事件结构体
|
||||
*/
|
||||
typedef struct MenuEvent {
|
||||
MenuEventType type; ///< 事件类型
|
||||
uint8_t priority; ///< 事件优先级(0-3,0最高)
|
||||
MenuNodeId target_node_id; ///< 目标节点ID
|
||||
void* data; ///< 事件数据
|
||||
uint32_t timestamp; ///< 事件时间戳
|
||||
} MenuEvent;
|
||||
|
||||
/**
|
||||
* @brief 菜单事件队列结构体
|
||||
*/
|
||||
typedef struct MenuEventQueue {
|
||||
MenuEvent buffer[MENU_CONFIG_EVENT_QUEUE_LEN]; ///< 队列缓冲区
|
||||
uint8_t head; ///< 入队指针
|
||||
uint8_t tail; ///< 出队指针
|
||||
uint8_t count; ///< 队列元素数量
|
||||
uint8_t reserved; ///< 保留字节,用于对齐
|
||||
} MenuEventQueue;
|
||||
|
||||
/**
|
||||
* @brief 菜单导航路径结构体
|
||||
*/
|
||||
typedef struct MenuNavPath {
|
||||
MenuNodeId path[MENU_CONFIG_STACK_DEPTH]; ///< 导航路径
|
||||
uint8_t depth; ///< 当前深度
|
||||
uint8_t max_depth; ///< 最大深度
|
||||
} MenuNavPath;
|
||||
|
||||
/**
|
||||
* @brief 菜单栈结构体
|
||||
*/
|
||||
typedef struct MenuStack {
|
||||
MenuNodeId items[MENU_CONFIG_STACK_DEPTH]; ///< 栈元素
|
||||
uint8_t top; ///< 栈顶指针
|
||||
uint8_t max_depth; ///< 最大深度
|
||||
} MenuStack;
|
||||
|
||||
/* 状态持久化相关定义 -------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* @brief 持久化数据结构体
|
||||
*/
|
||||
typedef struct {
|
||||
MenuNodeId current_node_id; ///< 当前节点ID
|
||||
MenuState current_state; ///< 当前菜单状态
|
||||
MenuNavPath nav_path; ///< 当前导航路径
|
||||
MenuStack stack; ///< 当前菜单栈
|
||||
uint32_t timestamp; ///< 持久化时间戳
|
||||
uint8_t checksum; ///< 数据校验和
|
||||
} MenuPersistenceData;
|
||||
|
||||
/**
|
||||
* @brief 持久化配置结构体
|
||||
*/
|
||||
typedef struct {
|
||||
bool auto_save_enabled; ///< 是否启用自动保存
|
||||
uint32_t auto_save_interval; ///< 自动保存间隔(ms)
|
||||
uint32_t last_save_tick; ///< 上次保存时间
|
||||
bool encrypt_enabled; ///< 是否启用加密
|
||||
MenuPersistenceCallback save_cb; ///< 保存回调函数
|
||||
MenuRestoreCallback restore_cb; ///< 恢复回调函数
|
||||
} MenuPersistenceConfig;
|
||||
|
||||
/**
|
||||
* @brief 状态转换规则结构体
|
||||
*/
|
||||
typedef struct MenuStateTransition {
|
||||
MenuState current_state; ///< 当前状态
|
||||
MenuEventType event; ///< 触发事件
|
||||
MenuState next_state; ///< 下一个状态
|
||||
MenuStateAction action; ///< 状态转换动作
|
||||
const char* desc; ///< 状态转换描述(用于调试)
|
||||
} MenuStateTransition;
|
||||
|
||||
/**
|
||||
* @brief 菜单核心上下文结构体
|
||||
*/
|
||||
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; ///< 上次刷新时间(ms)
|
||||
bool is_initialized; ///< 是否已初始化
|
||||
MenuErrCode error_code; ///< 当前错误码
|
||||
const char* error_msg; ///< 当前错误信息
|
||||
uint16_t node_count; ///< 已注册节点数量
|
||||
uint16_t free_node_count; ///< 空闲节点数量
|
||||
// 状态机扩展
|
||||
#if MENU_CONFIG_ENABLE_STATE_MACHINE_EXT
|
||||
MenuStateTransition custom_transitions[MENU_CONFIG_MAX_STATE_TRANSITIONS]; ///< 自定义状态转换规则
|
||||
uint16_t custom_transition_count; ///< 自定义状态转换规则数量
|
||||
#endif
|
||||
// 权限管理
|
||||
#if MENU_CONFIG_ENABLE_PERMISSION
|
||||
MenuPermissionConfig permission_config; ///< 权限配置
|
||||
#endif
|
||||
// 状态持久化
|
||||
#if MENU_CONFIG_ENABLE_PERSISTENCE
|
||||
MenuPersistenceConfig persistence_config; ///< 持久化配置
|
||||
#endif
|
||||
} MenuCoreCtx;
|
||||
|
||||
#endif /* MENU_TYPES_H */
|
||||
228
src/lang/menu_lang.c
Normal file
228
src/lang/menu_lang.c
Normal file
@ -0,0 +1,228 @@
|
||||
/**
|
||||
**********************************************************************************************************************
|
||||
* @file menu_lang.c
|
||||
* @brief 菜单组件多语言支持模块实现
|
||||
* @author menu_component
|
||||
* @date 2025-12-19
|
||||
**********************************************************************************************************************
|
||||
*/
|
||||
|
||||
/* Includes ----------------------------------------------------------------------------------------------------------*/
|
||||
#include "menu_lang.h"
|
||||
|
||||
/* 配置参数 ---------------------------------------------------------------------------------------------------------*/
|
||||
#define MENU_LANG_MAX_PACKS 8U /* 最大支持的语言包数量 */
|
||||
|
||||
/* 全局变量 ---------------------------------------------------------------------------------------------------------*/
|
||||
static const LangPack* sg_lang_packs[MENU_LANG_MAX_PACKS];
|
||||
static uint8_t sg_lang_pack_count = 0;
|
||||
static LangId sg_current_lang_id = 0;
|
||||
|
||||
/* 函数实现 ---------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
#if MENU_CONFIG_ENABLE_LANG
|
||||
|
||||
/**
|
||||
* @brief 初始化多语言支持模块
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_lang_init(void)
|
||||
{
|
||||
// 初始化语言包数组
|
||||
for (uint8_t i = 0; i < MENU_LANG_MAX_PACKS; i++) {
|
||||
sg_lang_packs[i] = NULL;
|
||||
}
|
||||
|
||||
sg_lang_pack_count = 0;
|
||||
sg_current_lang_id = 0;
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 反初始化多语言支持模块
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_lang_deinit(void)
|
||||
{
|
||||
sg_lang_pack_count = 0;
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置当前语言
|
||||
* @param lang_id 语言ID
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_lang_set_current(LangId lang_id)
|
||||
{
|
||||
// 检查语言包是否已注册
|
||||
bool found = false;
|
||||
for (uint8_t i = 0; i < sg_lang_pack_count; i++) {
|
||||
if (sg_lang_packs[i]->id == lang_id) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
return MENU_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
sg_current_lang_id = lang_id;
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取当前语言ID
|
||||
* @return 当前语言ID
|
||||
*/
|
||||
LangId menu_lang_get_current(void)
|
||||
{
|
||||
return sg_current_lang_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 查找语言包
|
||||
* @param lang_id 语言ID
|
||||
* @return 语言包指针,未找到返回NULL
|
||||
*/
|
||||
static const LangPack* menu_lang_find_pack_internal(LangId lang_id)
|
||||
{
|
||||
for (uint8_t i = 0; i < sg_lang_pack_count; i++) {
|
||||
if (sg_lang_packs[i]->id == lang_id) {
|
||||
return sg_lang_packs[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 注册语言包
|
||||
* @param pack 语言包指针
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_lang_register_pack(const LangPack* pack)
|
||||
{
|
||||
if (pack == NULL) {
|
||||
return MENU_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
// 检查语言包是否已存在
|
||||
if (menu_lang_find_pack_internal(pack->id) != NULL) {
|
||||
return MENU_ERR_OPERATION_FAILED;
|
||||
}
|
||||
|
||||
// 检查语言包数量是否超过最大值
|
||||
if (sg_lang_pack_count >= MENU_LANG_MAX_PACKS) {
|
||||
return MENU_ERR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
// 注册语言包
|
||||
sg_lang_packs[sg_lang_pack_count] = pack;
|
||||
sg_lang_pack_count++;
|
||||
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 注销语言包
|
||||
* @param lang_id 语言ID
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_lang_unregister_pack(LangId lang_id)
|
||||
{
|
||||
// 查找语言包在数组中的位置
|
||||
uint8_t index = 0;
|
||||
bool found = false;
|
||||
|
||||
for (index = 0; index < sg_lang_pack_count; index++) {
|
||||
if (sg_lang_packs[index]->id == lang_id) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
return MENU_ERR_NODE_NOT_FOUND;
|
||||
}
|
||||
|
||||
// 将后续语言包前移
|
||||
for (uint8_t i = index; i < sg_lang_pack_count - 1; i++) {
|
||||
sg_lang_packs[i] = sg_lang_packs[i + 1];
|
||||
}
|
||||
|
||||
sg_lang_pack_count--;
|
||||
|
||||
// 如果当前语言被注销,切换到第一个语言包
|
||||
if (sg_current_lang_id == lang_id && sg_lang_pack_count > 0) {
|
||||
sg_current_lang_id = sg_lang_packs[0]->id;
|
||||
}
|
||||
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 根据字符串ID获取当前语言的字符串
|
||||
* @param str_id 字符串ID
|
||||
* @return 字符串指针,未找到返回NULL
|
||||
*/
|
||||
const char* menu_lang_get_str(StrId str_id)
|
||||
{
|
||||
// 获取当前语言包
|
||||
const LangPack* pack = menu_lang_find_pack_internal(sg_current_lang_id);
|
||||
if (pack == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// 在当前语言包中查找字符串
|
||||
for (uint16_t i = 0; i < pack->string_count; i++) {
|
||||
if (pack->strings[i].id == str_id) {
|
||||
return pack->strings[i].str;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果在当前语言包中未找到,尝试在第一个语言包中查找
|
||||
if (sg_lang_pack_count > 0 && pack != sg_lang_packs[0]) {
|
||||
pack = sg_lang_packs[0];
|
||||
for (uint16_t i = 0; i < pack->string_count; i++) {
|
||||
if (pack->strings[i].id == str_id) {
|
||||
return pack->strings[i].str;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 加载语言包
|
||||
* @param pack 语言包指针
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_lang_load_pack(const LangPack* pack)
|
||||
{
|
||||
if (pack == NULL) {
|
||||
return MENU_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
// 注册语言包
|
||||
MenuErrCode err = menu_lang_register_pack(pack);
|
||||
if (err != MENU_ERR_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// 设置为当前语言
|
||||
return menu_lang_set_current(pack->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 查找语言包
|
||||
* @param lang_id 语言ID
|
||||
* @return 语言包指针,未找到返回NULL
|
||||
*/
|
||||
const LangPack* menu_lang_find_pack(LangId lang_id)
|
||||
{
|
||||
return menu_lang_find_pack_internal(lang_id);
|
||||
}
|
||||
|
||||
#endif /* MENU_CONFIG_ENABLE_LANG */
|
||||
125
src/lang/menu_lang.h
Normal file
125
src/lang/menu_lang.h
Normal file
@ -0,0 +1,125 @@
|
||||
/**
|
||||
**********************************************************************************************************************
|
||||
* @file menu_lang.h
|
||||
* @brief 菜单组件多语言支持模块
|
||||
* @author menu_component
|
||||
* @date 2025-12-19
|
||||
**********************************************************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef MENU_LANG_H
|
||||
#define MENU_LANG_H
|
||||
|
||||
/* Includes ----------------------------------------------------------------------------------------------------------*/
|
||||
#include "../core/menu_config.h"
|
||||
|
||||
/* 类型定义 ---------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* @brief 语言ID类型
|
||||
*/
|
||||
typedef uint8_t LangId;
|
||||
|
||||
/**
|
||||
* @brief 字符串ID类型
|
||||
*/
|
||||
typedef uint16_t StrId;
|
||||
|
||||
/**
|
||||
* @brief 字符串项结构体
|
||||
*/
|
||||
typedef struct {
|
||||
StrId id; ///< 字符串ID
|
||||
const char* str; ///< 字符串内容
|
||||
} LangStrItem;
|
||||
|
||||
/**
|
||||
* @brief 语言包结构体
|
||||
*/
|
||||
typedef struct {
|
||||
LangId id; ///< 语言ID
|
||||
const char* name; ///< 语言名称(如"English"、"中文")
|
||||
const LangStrItem* strings; ///< 字符串数组
|
||||
uint16_t string_count; ///< 字符串数量
|
||||
} LangPack;
|
||||
|
||||
/* 函数声明 ---------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
#if MENU_CONFIG_ENABLE_LANG
|
||||
|
||||
/**
|
||||
* @brief 初始化多语言支持模块
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_lang_init(void);
|
||||
|
||||
/**
|
||||
* @brief 反初始化多语言支持模块
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_lang_deinit(void);
|
||||
|
||||
/**
|
||||
* @brief 设置当前语言
|
||||
* @param lang_id 语言ID
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_lang_set_current(LangId lang_id);
|
||||
|
||||
/**
|
||||
* @brief 获取当前语言ID
|
||||
* @return 当前语言ID
|
||||
*/
|
||||
LangId menu_lang_get_current(void);
|
||||
|
||||
/**
|
||||
* @brief 注册语言包
|
||||
* @param pack 语言包指针
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_lang_register_pack(const LangPack* pack);
|
||||
|
||||
/**
|
||||
* @brief 注销语言包
|
||||
* @param lang_id 语言ID
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_lang_unregister_pack(LangId lang_id);
|
||||
|
||||
/**
|
||||
* @brief 根据字符串ID获取当前语言的字符串
|
||||
* @param str_id 字符串ID
|
||||
* @return 字符串指针,未找到返回NULL
|
||||
*/
|
||||
const char* menu_lang_get_str(StrId str_id);
|
||||
|
||||
/**
|
||||
* @brief 加载语言包
|
||||
* @param pack 语言包指针
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_lang_load_pack(const LangPack* pack);
|
||||
|
||||
/**
|
||||
* @brief 查找语言包
|
||||
* @param lang_id 语言ID
|
||||
* @return 语言包指针,未找到返回NULL
|
||||
*/
|
||||
const LangPack* menu_lang_find_pack(LangId lang_id);
|
||||
|
||||
#else /* MENU_CONFIG_ENABLE_LANG */
|
||||
|
||||
/* 多语言功能未启用时的空实现 */
|
||||
#define menu_lang_init() MENU_ERR_OK
|
||||
#define menu_lang_deinit() MENU_ERR_OK
|
||||
#define menu_lang_set_current(...) MENU_ERR_OK
|
||||
#define menu_lang_get_current() 0
|
||||
#define menu_lang_register_pack(...) MENU_ERR_OK
|
||||
#define menu_lang_unregister_pack(...) MENU_ERR_OK
|
||||
#define menu_lang_get_str(id) ""
|
||||
#define menu_lang_load_pack(...) MENU_ERR_OK
|
||||
#define menu_lang_find_pack(...) NULL
|
||||
|
||||
#endif /* MENU_CONFIG_ENABLE_LANG */
|
||||
|
||||
#endif /* MENU_LANG_H */
|
||||
397
src/param/menu_param.c
Normal file
397
src/param/menu_param.c
Normal file
@ -0,0 +1,397 @@
|
||||
/**
|
||||
**********************************************************************************************************************
|
||||
* @file menu_param.c
|
||||
* @brief 菜单组件参数管理模块实现
|
||||
* @author menu_component
|
||||
* @date 2025-12-19
|
||||
**********************************************************************************************************************
|
||||
*/
|
||||
|
||||
/* Includes ----------------------------------------------------------------------------------------------------------*/
|
||||
#include "menu_param.h"
|
||||
#include "../core/menu_hash.h"
|
||||
|
||||
/* 配置参数 ---------------------------------------------------------------------------------------------------------*/
|
||||
#define MENU_PARAM_MAX_NUM 32U /* 最大参数数量 */
|
||||
|
||||
/* 全局变量 ---------------------------------------------------------------------------------------------------------*/
|
||||
static MenuParam sg_menu_params[MENU_PARAM_MAX_NUM];
|
||||
static uint16_t sg_menu_param_count = 0;
|
||||
|
||||
/* 函数实现 ---------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
#if MENU_CONFIG_ENABLE_PARAM
|
||||
|
||||
/**
|
||||
* @brief 初始化参数管理模块
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_param_init(void)
|
||||
{
|
||||
// 初始化参数数组
|
||||
for (uint16_t i = 0; i < MENU_PARAM_MAX_NUM; i++) {
|
||||
sg_menu_params[i].id = 0;
|
||||
sg_menu_params[i].name = NULL;
|
||||
sg_menu_params[i].type = PARAM_TYPE_UINT8;
|
||||
sg_menu_params[i].value = NULL;
|
||||
sg_menu_params[i].default_value = NULL;
|
||||
sg_menu_params[i].min_value = NULL;
|
||||
sg_menu_params[i].max_value = NULL;
|
||||
sg_menu_params[i].is_read_only = false;
|
||||
sg_menu_params[i].is_modified = false;
|
||||
}
|
||||
|
||||
sg_menu_param_count = 0;
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 反初始化参数管理模块
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_param_deinit(void)
|
||||
{
|
||||
sg_menu_param_count = 0;
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 查找参数
|
||||
* @param id 参数ID
|
||||
* @return 参数指针,未找到返回NULL
|
||||
*/
|
||||
MenuParam* menu_param_find_internal(uint16_t id)
|
||||
{
|
||||
for (uint16_t i = 0; i < sg_menu_param_count; i++) {
|
||||
if (sg_menu_params[i].id == id) {
|
||||
return &sg_menu_params[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 注册参数
|
||||
* @param id 参数ID
|
||||
* @param name 参数名称
|
||||
* @param type 参数类型
|
||||
* @param value 参数值指针
|
||||
* @param default_value 默认值指针
|
||||
* @param min_value 最小值指针
|
||||
* @param max_value 最大值指针
|
||||
* @param is_read_only 是否只读
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_param_register(uint16_t id, const char* name, ParamType type, void* value, void* default_value, void* min_value, void* max_value, bool is_read_only)
|
||||
{
|
||||
// 检查参数ID是否已存在
|
||||
if (menu_param_find_internal(id) != NULL) {
|
||||
return MENU_ERR_OPERATION_FAILED;
|
||||
}
|
||||
|
||||
// 检查参数数量是否超过最大值
|
||||
if (sg_menu_param_count >= MENU_PARAM_MAX_NUM) {
|
||||
return MENU_ERR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
// 检查参数值指针是否有效
|
||||
if (value == NULL) {
|
||||
return MENU_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
// 注册参数
|
||||
MenuParam* param = &sg_menu_params[sg_menu_param_count];
|
||||
param->id = id;
|
||||
param->name = name;
|
||||
param->type = type;
|
||||
param->value = value;
|
||||
param->default_value = default_value;
|
||||
param->min_value = min_value;
|
||||
param->max_value = max_value;
|
||||
param->is_read_only = is_read_only;
|
||||
param->is_modified = false;
|
||||
|
||||
sg_menu_param_count++;
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 注销参数
|
||||
* @param id 参数ID
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_param_unregister(uint16_t id)
|
||||
{
|
||||
MenuParam* param = menu_param_find_internal(id);
|
||||
if (param == NULL) {
|
||||
return MENU_ERR_NODE_NOT_FOUND;
|
||||
}
|
||||
|
||||
// 查找参数在数组中的位置
|
||||
uint16_t index = 0;
|
||||
for (index = 0; index < sg_menu_param_count; index++) {
|
||||
if (sg_menu_params[index].id == id) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 将后续参数前移
|
||||
for (uint16_t i = index; i < sg_menu_param_count - 1; i++) {
|
||||
sg_menu_params[i] = sg_menu_params[i + 1];
|
||||
}
|
||||
|
||||
sg_menu_param_count--;
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取参数值
|
||||
* @param id 参数ID
|
||||
* @param value 用于存储参数值的指针
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_param_get_value(uint16_t id, void* value)
|
||||
{
|
||||
MenuParam* param = menu_param_find_internal(id);
|
||||
if (param == NULL) {
|
||||
return MENU_ERR_NODE_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (value == NULL) {
|
||||
return MENU_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
// 根据参数类型获取值
|
||||
switch (param->type) {
|
||||
case PARAM_TYPE_INT8:
|
||||
*(int8_t*)value = *(int8_t*)param->value;
|
||||
break;
|
||||
case PARAM_TYPE_UINT8:
|
||||
*(uint8_t*)value = *(uint8_t*)param->value;
|
||||
break;
|
||||
case PARAM_TYPE_INT16:
|
||||
*(int16_t*)value = *(int16_t*)param->value;
|
||||
break;
|
||||
case PARAM_TYPE_UINT16:
|
||||
*(uint16_t*)value = *(uint16_t*)param->value;
|
||||
break;
|
||||
case PARAM_TYPE_INT32:
|
||||
*(int32_t*)value = *(int32_t*)param->value;
|
||||
break;
|
||||
case PARAM_TYPE_UINT32:
|
||||
*(uint32_t*)value = *(uint32_t*)param->value;
|
||||
break;
|
||||
case PARAM_TYPE_FLOAT:
|
||||
*(float*)value = *(float*)param->value;
|
||||
break;
|
||||
default:
|
||||
return MENU_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查参数值是否在允许范围内
|
||||
* @param param 参数指针
|
||||
* @param value 要检查的值
|
||||
* @return true为在范围内,false为超出范围
|
||||
*/
|
||||
static bool menu_param_check_range(MenuParam* param, const void* value)
|
||||
{
|
||||
if (param->min_value == NULL || param->max_value == NULL) {
|
||||
return true; // 没有设置范围限制
|
||||
}
|
||||
|
||||
switch (param->type) {
|
||||
case PARAM_TYPE_INT8:
|
||||
return (*(int8_t*)value >= *(int8_t*)param->min_value) && (*(int8_t*)value <= *(int8_t*)param->max_value);
|
||||
case PARAM_TYPE_UINT8:
|
||||
return (*(uint8_t*)value >= *(uint8_t*)param->min_value) && (*(uint8_t*)value <= *(uint8_t*)param->max_value);
|
||||
case PARAM_TYPE_INT16:
|
||||
return (*(int16_t*)value >= *(int16_t*)param->min_value) && (*(int16_t*)value <= *(int16_t*)param->max_value);
|
||||
case PARAM_TYPE_UINT16:
|
||||
return (*(uint16_t*)value >= *(uint16_t*)param->min_value) && (*(uint16_t*)value <= *(uint16_t*)param->max_value);
|
||||
case PARAM_TYPE_INT32:
|
||||
return (*(int32_t*)value >= *(int32_t*)param->min_value) && (*(int32_t*)value <= *(int32_t*)param->max_value);
|
||||
case PARAM_TYPE_UINT32:
|
||||
return (*(uint32_t*)value >= *(uint32_t*)param->min_value) && (*(uint32_t*)value <= *(uint32_t*)param->max_value);
|
||||
case PARAM_TYPE_FLOAT:
|
||||
return (*(float*)value >= *(float*)param->min_value) && (*(float*)value <= *(float*)param->max_value);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置参数值
|
||||
* @param id 参数ID
|
||||
* @param value 新的参数值
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_param_set_value(uint16_t id, const void* value)
|
||||
{
|
||||
MenuParam* param = menu_param_find_internal(id);
|
||||
if (param == NULL) {
|
||||
return MENU_ERR_NODE_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (value == NULL) {
|
||||
return MENU_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
// 检查参数是否只读
|
||||
if (param->is_read_only) {
|
||||
return MENU_ERR_OPERATION_FAILED;
|
||||
}
|
||||
|
||||
// 检查参数值是否在允许范围内
|
||||
if (!menu_param_check_range(param, value)) {
|
||||
return MENU_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
// 根据参数类型设置值
|
||||
switch (param->type) {
|
||||
case PARAM_TYPE_INT8:
|
||||
if (*(int8_t*)value != *(int8_t*)param->value) {
|
||||
*(int8_t*)param->value = *(int8_t*)value;
|
||||
param->is_modified = true;
|
||||
}
|
||||
break;
|
||||
case PARAM_TYPE_UINT8:
|
||||
if (*(uint8_t*)value != *(uint8_t*)param->value) {
|
||||
*(uint8_t*)param->value = *(uint8_t*)value;
|
||||
param->is_modified = true;
|
||||
}
|
||||
break;
|
||||
case PARAM_TYPE_INT16:
|
||||
if (*(int16_t*)value != *(int16_t*)param->value) {
|
||||
*(int16_t*)param->value = *(int16_t*)value;
|
||||
param->is_modified = true;
|
||||
}
|
||||
break;
|
||||
case PARAM_TYPE_UINT16:
|
||||
if (*(uint16_t*)value != *(uint16_t*)param->value) {
|
||||
*(uint16_t*)param->value = *(uint16_t*)value;
|
||||
param->is_modified = true;
|
||||
}
|
||||
break;
|
||||
case PARAM_TYPE_INT32:
|
||||
if (*(int32_t*)value != *(int32_t*)param->value) {
|
||||
*(int32_t*)param->value = *(int32_t*)value;
|
||||
param->is_modified = true;
|
||||
}
|
||||
break;
|
||||
case PARAM_TYPE_UINT32:
|
||||
if (*(uint32_t*)value != *(uint32_t*)param->value) {
|
||||
*(uint32_t*)param->value = *(uint32_t*)value;
|
||||
param->is_modified = true;
|
||||
}
|
||||
break;
|
||||
case PARAM_TYPE_FLOAT:
|
||||
if (*(float*)value != *(float*)param->value) {
|
||||
*(float*)param->value = *(float*)value;
|
||||
param->is_modified = true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return MENU_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 恢复参数默认值
|
||||
* @param id 参数ID
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_param_restore_default(uint16_t id)
|
||||
{
|
||||
MenuParam* param = menu_param_find_internal(id);
|
||||
if (param == NULL) {
|
||||
return MENU_ERR_NODE_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (param->default_value == NULL) {
|
||||
return MENU_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
// 检查参数是否只读
|
||||
if (param->is_read_only) {
|
||||
return MENU_ERR_OPERATION_FAILED;
|
||||
}
|
||||
|
||||
// 恢复默认值
|
||||
return menu_param_set_value(id, param->default_value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取参数类型
|
||||
* @param id 参数ID
|
||||
* @return 参数类型
|
||||
*/
|
||||
ParamType menu_param_get_type(uint16_t id)
|
||||
{
|
||||
MenuParam* param = menu_param_find_internal(id);
|
||||
if (param != NULL) {
|
||||
return param->type;
|
||||
}
|
||||
return PARAM_TYPE_UINT8;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查参数是否只读
|
||||
* @param id 参数ID
|
||||
* @return true为只读,false为可写
|
||||
*/
|
||||
bool menu_param_is_read_only(uint16_t id)
|
||||
{
|
||||
MenuParam* param = menu_param_find_internal(id);
|
||||
if (param != NULL) {
|
||||
return param->is_read_only;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查参数是否已修改
|
||||
* @param id 参数ID
|
||||
* @return true为已修改,false为未修改
|
||||
*/
|
||||
bool menu_param_is_modified(uint16_t id)
|
||||
{
|
||||
MenuParam* param = menu_param_find_internal(id);
|
||||
if (param != NULL) {
|
||||
return param->is_modified;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 清除参数修改标记
|
||||
* @param id 参数ID
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_param_clear_modified_flag(uint16_t id)
|
||||
{
|
||||
MenuParam* param = menu_param_find_internal(id);
|
||||
if (param == NULL) {
|
||||
return MENU_ERR_NODE_NOT_FOUND;
|
||||
}
|
||||
|
||||
param->is_modified = false;
|
||||
return MENU_ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 查找参数
|
||||
* @param id 参数ID
|
||||
* @return 参数指针,未找到返回NULL
|
||||
*/
|
||||
const MenuParam* menu_param_find(uint16_t id)
|
||||
{
|
||||
return menu_param_find_internal(id);
|
||||
}
|
||||
|
||||
#endif /* MENU_CONFIG_ENABLE_PARAM */
|
||||
159
src/param/menu_param.h
Normal file
159
src/param/menu_param.h
Normal file
@ -0,0 +1,159 @@
|
||||
/**
|
||||
**********************************************************************************************************************
|
||||
* @file menu_param.h
|
||||
* @brief 菜单组件参数管理模块
|
||||
* @author menu_component
|
||||
* @date 2025-12-19
|
||||
**********************************************************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef MENU_PARAM_H
|
||||
#define MENU_PARAM_H
|
||||
|
||||
/* Includes ----------------------------------------------------------------------------------------------------------*/
|
||||
#include "../core/menu_config.h"
|
||||
|
||||
/* 类型定义 ---------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* @brief 参数类型
|
||||
*/
|
||||
typedef enum {
|
||||
PARAM_TYPE_INT8, /*!< 8位有符号整数 */
|
||||
PARAM_TYPE_UINT8, /*!< 8位无符号整数 */
|
||||
PARAM_TYPE_INT16, /*!< 16位有符号整数 */
|
||||
PARAM_TYPE_UINT16, /*!< 16位无符号整数 */
|
||||
PARAM_TYPE_INT32, /*!< 32位有符号整数 */
|
||||
PARAM_TYPE_UINT32, /*!< 32位无符号整数 */
|
||||
PARAM_TYPE_FLOAT, /*!< 浮点数 */
|
||||
} ParamType;
|
||||
|
||||
/**
|
||||
* @brief 参数结构体
|
||||
*/
|
||||
typedef struct {
|
||||
uint16_t id; ///< 参数ID
|
||||
const char* name; ///< 参数名称
|
||||
ParamType type; ///< 参数类型
|
||||
void* value; ///< 参数值指针
|
||||
void* default_value; ///< 默认值指针
|
||||
void* min_value; ///< 最小值指针
|
||||
void* max_value; ///< 最大值指针
|
||||
bool is_read_only; ///< 是否只读
|
||||
bool is_modified; ///< 是否已修改
|
||||
} MenuParam;
|
||||
|
||||
/* 函数声明 ---------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
#if MENU_CONFIG_ENABLE_PARAM
|
||||
|
||||
/**
|
||||
* @brief 初始化参数管理模块
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_param_init(void);
|
||||
|
||||
/**
|
||||
* @brief 反初始化参数管理模块
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_param_deinit(void);
|
||||
|
||||
/**
|
||||
* @brief 注册参数
|
||||
* @param id 参数ID
|
||||
* @param name 参数名称
|
||||
* @param type 参数类型
|
||||
* @param value 参数值指针
|
||||
* @param default_value 默认值指针
|
||||
* @param min_value 最小值指针
|
||||
* @param max_value 最大值指针
|
||||
* @param is_read_only 是否只读
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_param_register(uint16_t id, const char* name, ParamType type, void* value, void* default_value, void* min_value, void* max_value, bool is_read_only);
|
||||
|
||||
/**
|
||||
* @brief 注销参数
|
||||
* @param id 参数ID
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_param_unregister(uint16_t id);
|
||||
|
||||
/**
|
||||
* @brief 获取参数值
|
||||
* @param id 参数ID
|
||||
* @param value 用于存储参数值的指针
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_param_get_value(uint16_t id, void* value);
|
||||
|
||||
/**
|
||||
* @brief 设置参数值
|
||||
* @param id 参数ID
|
||||
* @param value 新的参数值
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_param_set_value(uint16_t id, const void* value);
|
||||
|
||||
/**
|
||||
* @brief 恢复参数默认值
|
||||
* @param id 参数ID
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_param_restore_default(uint16_t id);
|
||||
|
||||
/**
|
||||
* @brief 获取参数类型
|
||||
* @param id 参数ID
|
||||
* @return 参数类型
|
||||
*/
|
||||
ParamType menu_param_get_type(uint16_t id);
|
||||
|
||||
/**
|
||||
* @brief 检查参数是否只读
|
||||
* @param id 参数ID
|
||||
* @return true为只读,false为可写
|
||||
*/
|
||||
bool menu_param_is_read_only(uint16_t id);
|
||||
|
||||
/**
|
||||
* @brief 检查参数是否已修改
|
||||
* @param id 参数ID
|
||||
* @return true为已修改,false为未修改
|
||||
*/
|
||||
bool menu_param_is_modified(uint16_t id);
|
||||
|
||||
/**
|
||||
* @brief 清除参数修改标记
|
||||
* @param id 参数ID
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_param_clear_modified_flag(uint16_t id);
|
||||
|
||||
/**
|
||||
* @brief 查找参数
|
||||
* @param id 参数ID
|
||||
* @return 参数指针,未找到返回NULL
|
||||
*/
|
||||
const MenuParam* menu_param_find(uint16_t id);
|
||||
|
||||
#else /* MENU_CONFIG_ENABLE_PARAM */
|
||||
|
||||
/* 参数管理功能未启用时的空实现 */
|
||||
#define menu_param_init() MENU_ERR_OK
|
||||
#define menu_param_deinit() MENU_ERR_OK
|
||||
#define menu_param_register(...) MENU_ERR_OK
|
||||
#define menu_param_unregister(...) MENU_ERR_OK
|
||||
#define menu_param_get_value(...) MENU_ERR_OK
|
||||
#define menu_param_set_value(...) MENU_ERR_OK
|
||||
#define menu_param_restore_default(...) MENU_ERR_OK
|
||||
#define menu_param_get_type(id) PARAM_TYPE_UINT8
|
||||
#define menu_param_is_read_only(id) false
|
||||
#define menu_param_is_modified(id) false
|
||||
#define menu_param_clear_modified_flag(...) MENU_ERR_OK
|
||||
#define menu_param_find(id) NULL
|
||||
|
||||
#endif /* MENU_CONFIG_ENABLE_PARAM */
|
||||
|
||||
#endif /* MENU_PARAM_H */
|
||||
Reference in New Issue
Block a user