优化一版

This commit is contained in:
冯佳
2025-12-23 09:45:36 +08:00
parent 3a5491c11e
commit cf1803dd77
15 changed files with 577 additions and 146 deletions

View File

@ -275,8 +275,11 @@ menu_register_nodes(nodes, sizeof(nodes)/sizeof(nodes[0]));
#### 5.2.3 事件处理 #### 5.2.3 事件处理
```c ```c
// 发布事件 // 发布事件 - 带优先级
menu_post_event(MENU_EVENT_KEY_UP, 0); menu_post_event(MENU_EVENT_KEY_UP, 0, MENU_EVENT_PRIORITY_NORMAL);
// 发布事件 - 简化方式(使用正常优先级)
menu_post_event_normal(MENU_EVENT_KEY_UP, 0);
// 主循环 // 主循环
while (1) { while (1) {

View File

@ -2,6 +2,8 @@
#include "../src/core/menu_core.h" #include "../src/core/menu_core.h"
#include "../src/core/menu_event.h" #include "../src/core/menu_event.h"
#include "../src/core/menu_stack.h" #include "../src/core/menu_stack.h"
#include "../src/core/menu_permission.h"
#include "../src/core/menu_persistence.h"
#include <stddef.h> #include <stddef.h>
static MenuCoreCtx g_menu_ctx; static MenuCoreCtx g_menu_ctx;
@ -88,8 +90,12 @@ MenuErrCode menu_register_nodes(
return MENU_ERR_OK; return MENU_ERR_OK;
} }
MenuErrCode menu_post_event(MenuEventType type, uint32_t param) { MenuErrCode menu_post_event(MenuEventType type, uint32_t param, MenuEventPriority priority) {
return menu_event_post(&g_menu_ctx.event_queue, type, param); return menu_event_post(&g_menu_ctx.event_queue, type, param, priority);
}
MenuErrCode menu_post_event_normal(MenuEventType type, uint32_t param) {
return menu_event_post(&g_menu_ctx.event_queue, type, param, MENU_EVENT_PRIORITY_NORMAL);
} }
MenuErrCode menu_enter(void) { MenuErrCode menu_enter(void) {
@ -134,7 +140,7 @@ MenuErrCode menu_enter(void) {
} }
MenuErrCode menu_back(void) { MenuErrCode menu_back(void) {
return menu_post_event(MENU_EVENT_KEY_ESC, 0); return menu_post_event_normal(MENU_EVENT_KEY_ESC, 0);
} }
MenuErrCode menu_node_bind_param(MenuNodeId node_id, uint16_t param_id) { MenuErrCode menu_node_bind_param(MenuNodeId node_id, uint16_t param_id) {
@ -146,69 +152,40 @@ MenuErrCode menu_node_bind_param(MenuNodeId node_id, uint16_t param_id) {
// Permission management functions // Permission management functions
MenuErrCode menu_permission_register_role(uint8_t role_id, const char* name, MenuPermissionLevel level) { MenuErrCode menu_permission_register_role(uint8_t role_id, const char* name, MenuPermissionLevel level) {
if (!name) return MENU_ERR_INVALID_PARAM; return menu_permission_register_role_impl(&g_menu_ctx, role_id, name, level);
if (g_menu_ctx.permission.role_count >= 8) return MENU_ERR_NO_MEM;
for (uint8_t i = 0; i < g_menu_ctx.permission.role_count; i++) {
if (g_menu_ctx.permission.roles[i].id == role_id) {
return MENU_ERR_FAIL;
}
}
MenuRole* role = &g_menu_ctx.permission.roles[g_menu_ctx.permission.role_count];
role->id = role_id;
role->name = name;
role->level = level;
g_menu_ctx.permission.role_count++;
return MENU_ERR_OK;
} }
MenuErrCode menu_permission_set_current_role(uint8_t role_id) { MenuErrCode menu_permission_set_current_role(uint8_t role_id) {
g_menu_ctx.permission.current_role_id = role_id; return menu_permission_set_current_role_impl(&g_menu_ctx, role_id);
return MENU_ERR_OK;
} }
MenuErrCode menu_permission_update_node_level(MenuNodeId node_id, MenuPermissionLevel level) { MenuErrCode menu_permission_update_node_level(MenuNodeId node_id, MenuPermissionLevel level) {
MenuNode* node = menu_core_get_node(&g_menu_ctx, node_id); return menu_permission_update_node_level_impl(&g_menu_ctx, node_id, level);
if (!node) return MENU_ERR_NOT_FOUND;
node->permission_level = level;
return MENU_ERR_OK;
} }
bool menu_permission_check_node_access(MenuNodeId node_id) { bool menu_permission_check_node_access(MenuNodeId node_id) {
MenuNode* node = menu_core_get_node(&g_menu_ctx, node_id); return menu_permission_check_node_access_impl(&g_menu_ctx, node_id);
if (!node) return false;
#if MENU_CONFIG_ENABLE_PERMISSION
MenuRole* role_node = NULL;
for (uint8_t i = 0; i < g_menu_ctx.permission.role_count; i++) {
if (g_menu_ctx.permission.roles[i].id == g_menu_ctx.permission.current_role_id) {
role_node = &g_menu_ctx.permission.roles[i];
break;
}
}
if (!role_node || role_node->level < node->permission_level) {
return false;
}
#endif
return true;
} }
// Persistence functions // Persistence functions
MenuErrCode menu_persistence_save(void) { MenuErrCode menu_persistence_save(void) {
return menu_core_save_state(&g_menu_ctx); return menu_persistence_save_impl(&g_menu_ctx);
} }
MenuErrCode menu_persistence_load(void) { MenuErrCode menu_persistence_load(void) {
return menu_core_load_state(&g_menu_ctx); return menu_persistence_load_impl(&g_menu_ctx);
}
MenuErrCode menu_persistence_clear(void) {
return menu_persistence_clear_impl(&g_menu_ctx);
}
bool menu_persistence_is_dirty(void) {
return menu_persistence_is_dirty_impl(&g_menu_ctx);
} }
MenuErrCode menu_refresh(void) { MenuErrCode menu_refresh(void) {
MenuErrCode err = menu_post_event(MENU_EVENT_RENDER, 0); MenuErrCode err = menu_post_event_normal(MENU_EVENT_RENDER, 0);
if (err != MENU_ERR_OK) { if (err != MENU_ERR_OK) {
return err; return err;
} }

View File

@ -42,7 +42,8 @@ MenuErrCode menu_register_nodes(
); );
// Event Handling // Event Handling
MenuErrCode menu_post_event(MenuEventType type, uint32_t param); MenuErrCode menu_post_event(MenuEventType type, uint32_t param, MenuEventPriority priority);
MenuErrCode menu_post_event_normal(MenuEventType type, uint32_t param);
// Navigation // Navigation
MenuErrCode menu_enter(void); // Enter root MenuErrCode menu_enter(void); // Enter root
@ -60,6 +61,8 @@ bool menu_permission_check_node_access(MenuNodeId node_id);
// Persistence Management // Persistence Management
MenuErrCode menu_persistence_save(void); MenuErrCode menu_persistence_save(void);
MenuErrCode menu_persistence_load(void); MenuErrCode menu_persistence_load(void);
MenuErrCode menu_persistence_clear(void);
bool menu_persistence_is_dirty(void);
// Dynamic menu updates // Dynamic menu updates
MenuErrCode menu_refresh(void); MenuErrCode menu_refresh(void);

View File

@ -81,6 +81,11 @@ void setup_menu(void) {
// Child of Settings: Brightness // Child of Settings: Brightness
MenuNodeId bri_node = menu_register_node(0, settings, "Brightness", on_enter_brightness, NULL); MenuNodeId bri_node = menu_register_node(0, settings, "Brightness", on_enter_brightness, NULL);
menu_node_bind_param(bri_node, 2); // Bind to Brightness param (ID 2) menu_node_bind_param(bri_node, 2); // Bind to Brightness param (ID 2)
// Set permission levels for nodes
menu_permission_update_node_level(settings, 1); // User level required for Settings
menu_permission_update_node_level(vol_node, 1); // User level required for Volume
menu_permission_update_node_level(bri_node, 1); // User level required for Brightness
} }
// Stub for binding param (will implement in API) // Stub for binding param (will implement in API)
@ -96,24 +101,48 @@ int main(void) {
menu_port_log("Menu System Demo Started"); menu_port_log("Menu System Demo Started");
menu_enter(); // Enter root menu_enter(); // Enter root
// Simulation Loop // Demo: Permission management
int steps = 0; menu_port_log("=== Permission Management Demo ===");
while (steps < 20) { menu_port_log("Current role: Guest");
menu_main_loop(0); menu_port_log("Trying to enter Settings...");
menu_post_event_normal(MENU_EVENT_KEY_ENTER, 0);
menu_main_loop(0);
// Simulate inputs menu_port_log("Switching to User role...");
if (steps == 2) { menu_permission_set_current_role(2); // Switch to User role
menu_port_log("User Press: ENTER"); menu_post_event_normal(MENU_EVENT_KEY_ENTER, 0);
menu_post_event(MENU_EVENT_KEY_ENTER, 0); menu_main_loop(0);
}
if (steps == 4) {
menu_port_log("User Press: DOWN");
menu_post_event(MENU_EVENT_KEY_DOWN, 0);
}
steps++; // Navigate to Volume setting
// sleep menu_post_event_normal(MENU_EVENT_KEY_DOWN, 0);
menu_main_loop(0);
menu_post_event_normal(MENU_EVENT_KEY_ENTER, 0);
menu_main_loop(0);
// Demo: Persistence
menu_port_log("=== Persistence Demo ===");
menu_port_log("Saving current state...");
menu_persistence_save();
menu_port_log("Exiting Volume menu...");
menu_post_event_normal(MENU_EVENT_MENU_EXIT, 0);
menu_main_loop(0);
menu_port_log("Loading saved state...");
menu_persistence_load();
menu_main_loop(0);
// Demo: Clear persistence
menu_port_log("=== Clear Persistence Demo ===");
menu_port_log("Clearing saved state...");
menu_persistence_clear();
menu_port_log("Trying to load cleared state (should fail)...");
MenuErrCode err = menu_persistence_load();
if (err != MENU_ERR_OK) {
menu_port_log("Load failed as expected (state was cleared)");
} }
menu_port_log("Menu System Demo Completed");
return 0; return 0;
} }

View File

@ -3,6 +3,8 @@
#include "menu_stack.h" #include "menu_stack.h"
#include "menu_hash.h" #include "menu_hash.h"
#include "menu_param.h" #include "menu_param.h"
#include "menu_permission.h"
#include "menu_persistence.h"
#include <string.h> #include <string.h>
// Internal helper to get node pointer from ID // Internal helper to get node pointer from ID
@ -18,6 +20,7 @@ MenuErrCode menu_core_init(MenuCoreCtx* ctx) {
menu_event_init(&ctx->event_queue); menu_event_init(&ctx->event_queue);
menu_stack_init(&ctx->stack); menu_stack_init(&ctx->stack);
menu_hash_init(ctx); menu_hash_init(ctx);
menu_permission_init_impl(ctx);
ctx->is_initialized = true; ctx->is_initialized = true;
ctx->current_state = MENU_STATE_INIT; ctx->current_state = MENU_STATE_INIT;
@ -153,13 +156,8 @@ MenuErrCode menu_core_unregister_node(MenuCoreCtx* ctx, MenuNodeId id) {
ctx->node_count--; ctx->node_count--;
ctx->free_node_count++; ctx->free_node_count++;
// Rebuild hash table since we can't easily remove from it // Remove node from hash table using optimized removal
menu_hash_init(ctx); menu_hash_remove(ctx, id, slot);
for (int i = 0; i < MENU_CONFIG_MAX_NODES; i++) {
if (ctx->nodes[i].flags.is_registered) {
menu_hash_insert(ctx, ctx->nodes[i].id, i);
}
}
return MENU_ERR_OK; return MENU_ERR_OK;
} }
@ -172,14 +170,7 @@ MenuErrCode menu_core_enter_node(MenuCoreCtx* ctx, MenuNodeId node_id) {
// Permission check // Permission check
#if MENU_CONFIG_ENABLE_PERMISSION #if MENU_CONFIG_ENABLE_PERMISSION
MenuRole* role_node = NULL; if (!menu_permission_check_node_access_impl(ctx, node_id)) {
for (uint8_t i = 0; i < ctx->permission.role_count; i++) {
if (ctx->permission.roles[i].id == ctx->permission.current_role_id) {
role_node = &ctx->permission.roles[i];
break;
}
}
if (!role_node || role_node->level < node->permission_level) {
return MENU_ERR_PERMISSION_DENIED; return MENU_ERR_PERMISSION_DENIED;
} }
#endif #endif
@ -366,51 +357,73 @@ MenuErrCode menu_core_handle_event(MenuCoreCtx* ctx, const MenuEvent* event) {
} }
break; break;
case MENU_EVENT_RENDER:
// Handle render event - this could trigger custom render callbacks or refresh the display
// For now, we'll just update the last refresh tick
ctx->last_refresh_tick = 0; // TODO: Use actual tick parameter from event
break;
case MENU_EVENT_PARAM_CHANGED:
// Handle parameter change event
// This could be used to update the display or trigger other actions when a parameter changes
break;
case MENU_EVENT_MENU_ENTER:
// Handle menu enter event
// This event is triggered when entering a menu node
break;
case MENU_EVENT_MENU_EXIT:
// Handle menu exit event
// This event is triggered when exiting a menu node
break;
case MENU_EVENT_STATE_CHANGED:
// Handle state change event
// This event is triggered when the menu state changes
break;
case MENU_EVENT_ERROR:
// Handle error event
// This event is triggered when an error occurs
ctx->current_state = MENU_STATE_ERROR;
ctx->error_code = (MenuErrCode)event->param;
break;
case MENU_EVENT_REFRESH:
// Handle refresh event
// This event is used to force a refresh of the menu display
break;
default: default:
// Handle custom events
if (event->type >= MENU_EVENT_CUSTOM_BASE) {
// Custom events can be handled by user-defined callbacks
// For now, we'll just ignore them
}
break; break;
} }
return MENU_ERR_OK; return MENU_ERR_OK;
} }
// calculate_checksum is now implemented in menu_persistence.c
// Keeping this for backward compatibility in case it's used elsewhere
static uint8_t calculate_checksum(const MenuPersistenceData* data) { static uint8_t calculate_checksum(const MenuPersistenceData* data) {
uint8_t checksum = 0; (void)data; // Suppress unused parameter warning
const uint8_t* bytes = (const uint8_t*)data; return 0;
size_t len = sizeof(MenuPersistenceData) - sizeof(data->checksum);
for (size_t i = 0; i < len; i++) {
checksum ^= bytes[i];
}
return checksum;
} }
MenuErrCode menu_core_save_state(MenuCoreCtx* ctx) { MenuErrCode menu_core_save_state(MenuCoreCtx* ctx) {
if (!ctx) return MENU_ERR_INVALID_PARAM; if (!ctx) return MENU_ERR_INVALID_PARAM;
MenuPersistenceData data = { return menu_persistence_save_impl(ctx);
.current_node_id = ctx->current_node_id,
.current_state = ctx->current_state,
.nav_path = ctx->nav_path,
.stack = ctx->stack,
.timestamp = 0,
.checksum = 0
};
data.checksum = calculate_checksum(&data);
// TODO: Implement actual storage write logic here
// This would typically write to EEPROM, Flash, or other non-volatile storage
// For now, we'll just mark as not dirty
ctx->persistence.dirty = false;
return MENU_ERR_OK;
} }
MenuErrCode menu_core_load_state(MenuCoreCtx* ctx) { MenuErrCode menu_core_load_state(MenuCoreCtx* ctx) {
if (!ctx) return MENU_ERR_INVALID_PARAM; if (!ctx) return MENU_ERR_INVALID_PARAM;
// TODO: Implement actual storage read logic here return menu_persistence_load_impl(ctx);
// This would typically read from EEPROM, Flash, or other non-volatile storage
// For now, we'll just return OK
return MENU_ERR_OK;
} }
void menu_core_loop(MenuCoreCtx* ctx, uint32_t tick) { void menu_core_loop(MenuCoreCtx* ctx, uint32_t tick) {

View File

@ -25,6 +25,10 @@ MenuErrCode menu_core_handle_event(MenuCoreCtx* ctx, const MenuEvent* event);
MenuErrCode menu_core_enter_node(MenuCoreCtx* ctx, MenuNodeId node_id); MenuErrCode menu_core_enter_node(MenuCoreCtx* ctx, MenuNodeId node_id);
MenuErrCode menu_core_exit_node(MenuCoreCtx* ctx, MenuNodeId node_id); MenuErrCode menu_core_exit_node(MenuCoreCtx* ctx, MenuNodeId node_id);
// Persistence management
MenuErrCode menu_core_save_state(MenuCoreCtx* ctx);
MenuErrCode menu_core_load_state(MenuCoreCtx* ctx);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -7,24 +7,56 @@ MenuErrCode menu_event_init(MenuEventQueue* queue) {
return MENU_ERR_OK; return MENU_ERR_OK;
} }
MenuErrCode menu_event_post(MenuEventQueue* queue, MenuEventType type, uint32_t param) { MenuErrCode menu_event_post(MenuEventQueue* queue, MenuEventType type, uint32_t param, MenuEventPriority priority) {
if (!queue) return MENU_ERR_INVALID_PARAM; if (!queue) return MENU_ERR_INVALID_PARAM;
if (queue->count >= MENU_CONFIG_EVENT_QUEUE_LEN) { if (queue->count >= MENU_CONFIG_EVENT_QUEUE_LEN) {
return MENU_ERR_QUEUE_FULL; return MENU_ERR_QUEUE_FULL;
} }
MenuEvent* evt = &queue->events[queue->tail]; // Create the new event
evt->type = type; MenuEvent new_event;
evt->param = param; new_event.type = type;
// timestamp handling can be added here if we have system tick access, new_event.param = param;
// for now left as 0 or handled by caller if passed in param (which is just 32bit) new_event.timestamp = 0; // timestamp handling can be added later if needed
// or we can add a tick parameter to post_event. new_event.priority = priority;
// For simplicity, we assume timestamp is not critical for now or set elsewhere.
evt->timestamp = 0;
queue->tail = (queue->tail + 1) % MENU_CONFIG_EVENT_QUEUE_LEN; if (queue->count == 0) {
queue->count++; // Queue is empty, add at tail
queue->events[queue->tail] = new_event;
queue->tail = (queue->tail + 1) % MENU_CONFIG_EVENT_QUEUE_LEN;
queue->count++;
} else {
// Find insertion position based on priority
uint8_t insert_pos = queue->tail;
uint8_t i = queue->head;
// Traverse the queue to find the first event with lower or equal priority
for (uint8_t j = 0; j < queue->count; j++) {
if (queue->events[i].priority < priority) {
insert_pos = i;
break;
}
i = (i + 1) % MENU_CONFIG_EVENT_QUEUE_LEN;
}
// Shift events to make room for the new event
if (insert_pos != queue->tail) {
uint8_t current = queue->tail;
uint8_t prev = (current - 1 + MENU_CONFIG_EVENT_QUEUE_LEN) % MENU_CONFIG_EVENT_QUEUE_LEN;
while (current != insert_pos) {
queue->events[current] = queue->events[prev];
current = prev;
prev = (prev - 1 + MENU_CONFIG_EVENT_QUEUE_LEN) % MENU_CONFIG_EVENT_QUEUE_LEN;
}
}
// Insert the new event
queue->events[insert_pos] = new_event;
queue->tail = (queue->tail + 1) % MENU_CONFIG_EVENT_QUEUE_LEN;
queue->count++;
}
return MENU_ERR_OK; return MENU_ERR_OK;
} }

View File

@ -8,7 +8,7 @@ extern "C" {
#endif #endif
MenuErrCode menu_event_init(MenuEventQueue* queue); MenuErrCode menu_event_init(MenuEventQueue* queue);
MenuErrCode menu_event_post(MenuEventQueue* queue, MenuEventType type, uint32_t param); MenuErrCode menu_event_post(MenuEventQueue* queue, MenuEventType type, uint32_t param, MenuEventPriority priority);
MenuErrCode menu_event_get(MenuEventQueue* queue, MenuEvent* event); MenuErrCode menu_event_get(MenuEventQueue* queue, MenuEvent* event);
bool menu_event_is_empty(const MenuEventQueue* queue); bool menu_event_is_empty(const MenuEventQueue* queue);

View File

@ -5,6 +5,12 @@
MenuErrCode menu_hash_init(MenuCoreCtx* ctx) { MenuErrCode menu_hash_init(MenuCoreCtx* ctx) {
if (!ctx) return MENU_ERR_INVALID_PARAM; if (!ctx) return MENU_ERR_INVALID_PARAM;
memset(ctx->hash_table, 0, sizeof(ctx->hash_table)); memset(ctx->hash_table, 0, sizeof(ctx->hash_table));
// Initialize hash_next_id to 0 for all nodes
for (int i = 0; i < MENU_CONFIG_MAX_NODES; i++) {
ctx->nodes[i].hash_next_id = 0;
}
return MENU_ERR_OK; return MENU_ERR_OK;
} }
@ -19,18 +25,10 @@ MenuErrCode menu_hash_insert(MenuCoreCtx* ctx, MenuNodeId id, uint16_t index) {
} else { } else {
MenuNode* prev_node = &ctx->nodes[current - 1]; MenuNode* prev_node = &ctx->nodes[current - 1];
while (prev_node->hash_next_id != 0) { while (prev_node->hash_next_id != 0) {
bool found = false; prev_node = &ctx->nodes[prev_node->hash_next_id - 1];
for (int i = 0; i < MENU_CONFIG_MAX_NODES; i++) {
if (ctx->nodes[i].flags.is_registered && ctx->nodes[i].id == prev_node->hash_next_id) {
prev_node = &ctx->nodes[i];
found = true;
break;
}
}
if (!found) return MENU_ERR_FAIL;
} }
prev_node->hash_next_id = id; prev_node->hash_next_id = index + 1;
} }
return MENU_ERR_OK; return MENU_ERR_OK;
@ -49,21 +47,40 @@ MenuNode* menu_hash_find(MenuCoreCtx* ctx, MenuNodeId id) {
return node; return node;
} }
bool found = false; current = node->hash_next_id;
for (int i = 0; i < MENU_CONFIG_MAX_NODES; i++) {
if (ctx->nodes[i].flags.is_registered && ctx->nodes[i].id == node->hash_next_id) {
for (int j = 0; j < MENU_CONFIG_MAX_NODES; j++) {
if (ctx->nodes[j].flags.is_registered && ctx->nodes[j].id == node->hash_next_id) {
current = j + 1;
found = true;
break;
}
}
break;
}
}
if (!found) break;
} }
return NULL; return NULL;
} }
MenuErrCode menu_hash_remove(MenuCoreCtx* ctx, MenuNodeId id, uint16_t index) {
if (!ctx) return MENU_ERR_INVALID_PARAM;
uint16_t hash_index = id % MENU_CONFIG_HASH_TABLE_SIZE;
uint16_t current = ctx->hash_table[hash_index];
uint16_t prev = 0;
while (current != 0) {
uint16_t node_index = current - 1;
if (node_index == index) {
if (prev == 0) {
// Remove head of the list
ctx->hash_table[hash_index] = ctx->nodes[node_index].hash_next_id;
} else {
// Remove from middle/end
ctx->nodes[prev - 1].hash_next_id = ctx->nodes[node_index].hash_next_id;
}
// Clear the hash_next_id for this node
ctx->nodes[node_index].hash_next_id = 0;
return MENU_ERR_OK;
}
prev = current;
current = ctx->nodes[node_index].hash_next_id;
}
return MENU_ERR_NOT_FOUND;
}

View File

@ -10,6 +10,7 @@ extern "C" {
MenuErrCode menu_hash_init(MenuCoreCtx* ctx); MenuErrCode menu_hash_init(MenuCoreCtx* ctx);
MenuErrCode menu_hash_insert(MenuCoreCtx* ctx, MenuNodeId id, uint16_t index); MenuErrCode menu_hash_insert(MenuCoreCtx* ctx, MenuNodeId id, uint16_t index);
MenuNode* menu_hash_find(MenuCoreCtx* ctx, MenuNodeId id); MenuNode* menu_hash_find(MenuCoreCtx* ctx, MenuNodeId id);
MenuErrCode menu_hash_remove(MenuCoreCtx* ctx, MenuNodeId id, uint16_t index);
#ifdef __cplusplus #ifdef __cplusplus
} }

157
src/core/menu_permission.c Normal file
View File

@ -0,0 +1,157 @@
#include "menu_permission.h"
#include "menu_core.h"
#include "menu_hash.h"
#include <string.h>
MenuErrCode menu_permission_init_impl(MenuCoreCtx* ctx) {
if (!ctx) return MENU_ERR_INVALID_PARAM;
memset(&ctx->permission, 0, sizeof(ctx->permission));
ctx->permission.role_count = 0;
ctx->permission.current_role_id = 0;
// Register default roles
menu_permission_register_role_impl(ctx, 1, "Guest", 0);
menu_permission_register_role_impl(ctx, 2, "User", 1);
menu_permission_register_role_impl(ctx, 3, "Admin", 2);
// Set default role to Guest
ctx->permission.current_role_id = 1;
return MENU_ERR_OK;
}
MenuErrCode menu_permission_register_role_impl(MenuCoreCtx* ctx, uint8_t role_id, const char* name, MenuPermissionLevel level) {
if (!ctx || !name) return MENU_ERR_INVALID_PARAM;
if (ctx->permission.role_count >= 8) return MENU_ERR_NO_MEM;
// Check if role already exists
for (uint8_t i = 0; i < ctx->permission.role_count; i++) {
if (ctx->permission.roles[i].id == role_id) {
return MENU_ERR_FAIL;
}
}
MenuRole* role = &ctx->permission.roles[ctx->permission.role_count];
role->id = role_id;
role->name = name;
role->level = level;
ctx->permission.role_count++;
return MENU_ERR_OK;
}
MenuErrCode menu_permission_unregister_role_impl(MenuCoreCtx* ctx, uint8_t role_id) {
if (!ctx) return MENU_ERR_INVALID_PARAM;
for (uint8_t i = 0; i < ctx->permission.role_count; i++) {
if (ctx->permission.roles[i].id == role_id) {
// Shift roles up
for (uint8_t j = i; j < ctx->permission.role_count - 1; j++) {
ctx->permission.roles[j] = ctx->permission.roles[j + 1];
}
ctx->permission.role_count--;
// If we removed the current role, set to default
if (ctx->permission.current_role_id == role_id) {
ctx->permission.current_role_id = 1; // Guest role
}
return MENU_ERR_OK;
}
}
return MENU_ERR_NOT_FOUND;
}
MenuErrCode menu_permission_set_current_role_impl(MenuCoreCtx* ctx, uint8_t role_id) {
if (!ctx) return MENU_ERR_INVALID_PARAM;
// Check if role exists
if (!menu_permission_role_exists_impl(ctx, role_id)) {
return MENU_ERR_NOT_FOUND;
}
ctx->permission.current_role_id = role_id;
return MENU_ERR_OK;
}
uint8_t menu_permission_get_current_role_impl(MenuCoreCtx* ctx) {
if (!ctx) return 0;
return ctx->permission.current_role_id;
}
MenuErrCode menu_permission_update_node_level_impl(MenuCoreCtx* ctx, MenuNodeId node_id, MenuPermissionLevel level) {
if (!ctx) return MENU_ERR_INVALID_PARAM;
MenuNode* node = menu_core_get_node(ctx, node_id);
if (!node) return MENU_ERR_NOT_FOUND;
node->permission_level = level;
return MENU_ERR_OK;
}
bool menu_permission_check_node_access_impl(MenuCoreCtx* ctx, MenuNodeId node_id) {
if (!ctx) return false;
MenuNode* node = menu_core_get_node(ctx, node_id);
if (!node) return false;
#if MENU_CONFIG_ENABLE_PERMISSION
MenuRole* role_node = NULL;
for (uint8_t i = 0; i < ctx->permission.role_count; i++) {
if (ctx->permission.roles[i].id == ctx->permission.current_role_id) {
role_node = &ctx->permission.roles[i];
break;
}
}
if (!role_node || role_node->level < node->permission_level) {
return false;
}
#endif
return true;
}
MenuErrCode menu_permission_check_and_enter_node_impl(MenuCoreCtx* ctx, MenuNodeId node_id) {
if (!ctx) return MENU_ERR_INVALID_PARAM;
// Check permission first
if (!menu_permission_check_node_access_impl(ctx, node_id)) {
return MENU_ERR_PERMISSION_DENIED;
}
// If permission granted, enter the node
return menu_core_enter_node(ctx, node_id);
}
MenuRole* menu_permission_get_role_impl(MenuCoreCtx* ctx, uint8_t role_id) {
if (!ctx) return NULL;
for (uint8_t i = 0; i < ctx->permission.role_count; i++) {
if (ctx->permission.roles[i].id == role_id) {
return &ctx->permission.roles[i];
}
}
return NULL;
}
uint8_t menu_permission_get_role_count_impl(MenuCoreCtx* ctx) {
if (!ctx) return 0;
return ctx->permission.role_count;
}
bool menu_permission_role_exists_impl(MenuCoreCtx* ctx, uint8_t role_id) {
if (!ctx) return false;
for (uint8_t i = 0; i < ctx->permission.role_count; i++) {
if (ctx->permission.roles[i].id == role_id) {
return true;
}
}
return false;
}

View File

@ -0,0 +1,31 @@
#ifndef MENU_PERMISSION_H
#define MENU_PERMISSION_H
#include <stdint.h>
#include <stdbool.h>
#include "menu_types.h"
#ifdef __cplusplus
extern "C" {
#endif
// Permission management functions
MenuErrCode menu_permission_init_impl(MenuCoreCtx* ctx);
MenuErrCode menu_permission_register_role_impl(MenuCoreCtx* ctx, uint8_t role_id, const char* name, MenuPermissionLevel level);
MenuErrCode menu_permission_unregister_role_impl(MenuCoreCtx* ctx, uint8_t role_id);
MenuErrCode menu_permission_set_current_role_impl(MenuCoreCtx* ctx, uint8_t role_id);
uint8_t menu_permission_get_current_role_impl(MenuCoreCtx* ctx);
MenuErrCode menu_permission_update_node_level_impl(MenuCoreCtx* ctx, MenuNodeId node_id, MenuPermissionLevel level);
bool menu_permission_check_node_access_impl(MenuCoreCtx* ctx, MenuNodeId node_id);
MenuErrCode menu_permission_check_and_enter_node_impl(MenuCoreCtx* ctx, MenuNodeId node_id);
// Role query functions
MenuRole* menu_permission_get_role_impl(MenuCoreCtx* ctx, uint8_t role_id);
uint8_t menu_permission_get_role_count_impl(MenuCoreCtx* ctx);
bool menu_permission_role_exists_impl(MenuCoreCtx* ctx, uint8_t role_id);
#ifdef __cplusplus
}
#endif
#endif // MENU_PERMISSION_H

109
src/core/menu_persistence.c Normal file
View File

@ -0,0 +1,109 @@
#include "menu_persistence.h"
#include "menu_types.h"
#include "menu_core.h"
#include <string.h>
// Mock storage buffer for persistence (simulating non-volatile storage)
static MenuPersistenceData g_persistence_storage = {0};
static bool g_storage_initialized = false;
// Calculate checksum for integrity verification
static uint16_t calculate_checksum(const MenuPersistenceData* data) {
if (!data) return 0;
const uint8_t* bytes = (const uint8_t*)data;
uint16_t checksum = 0;
uint16_t len = sizeof(MenuPersistenceData) - sizeof(data->checksum);
for (uint16_t i = 0; i < len; i++) {
checksum ^= bytes[i];
}
return checksum;
}
MenuErrCode menu_persistence_init_impl(MenuCoreCtx* ctx) {
if (!ctx) return MENU_ERR_INVALID_PARAM;
ctx->persistence.dirty = false;
// Initialize storage if not already initialized
if (!g_storage_initialized) {
memset(&g_persistence_storage, 0, sizeof(g_persistence_storage));
g_storage_initialized = true;
}
return MENU_ERR_OK;
}
MenuErrCode menu_persistence_save_impl(MenuCoreCtx* ctx) {
if (!ctx) return MENU_ERR_INVALID_PARAM;
MenuPersistenceData data = {
.current_node_id = ctx->current_node_id,
.current_state = ctx->current_state,
.nav_path = ctx->nav_path,
.stack = ctx->stack,
.timestamp = 0, // In a real system, this would be a actual timestamp
.checksum = 0
};
// Calculate checksum for integrity verification
data.checksum = calculate_checksum(&data);
// Write to mock storage
memcpy(&g_persistence_storage, &data, sizeof(MenuPersistenceData));
// Mark as not dirty
ctx->persistence.dirty = false;
return MENU_ERR_OK;
}
MenuErrCode menu_persistence_load_impl(MenuCoreCtx* ctx) {
if (!ctx) return MENU_ERR_INVALID_PARAM;
MenuPersistenceData data;
// Read from mock storage
memcpy(&data, &g_persistence_storage, sizeof(MenuPersistenceData));
// Verify checksum for integrity
uint16_t expected_checksum = calculate_checksum(&data);
if (data.checksum != expected_checksum) {
return MENU_ERR_CORRUPTED_DATA;
}
// Verify that the current_node_id exists in the node table
MenuNode* node = menu_core_get_node(ctx, data.current_node_id);
if (!node) {
return MENU_ERR_INVALID_NODE;
}
// Load the state into the context
ctx->current_node_id = data.current_node_id;
ctx->current_state = data.current_state;
ctx->nav_path = data.nav_path;
ctx->stack = data.stack;
ctx->persistence.dirty = false;
return MENU_ERR_OK;
}
MenuErrCode menu_persistence_clear_impl(MenuCoreCtx* ctx) {
if (!ctx) return MENU_ERR_INVALID_PARAM;
// Clear mock storage
memset(&g_persistence_storage, 0, sizeof(g_persistence_storage));
g_persistence_storage.checksum = calculate_checksum(&g_persistence_storage);
ctx->persistence.dirty = true;
return MENU_ERR_OK;
}
bool menu_persistence_is_dirty_impl(MenuCoreCtx* ctx) {
if (!ctx) return false;
return ctx->persistence.dirty;
}

View File

@ -0,0 +1,36 @@
/*
* menu_persistence.h
*
* Created on: 2024-07-22
* Author: Jfen
*/
#ifndef MENU_PERSISTENCE_H
#define MENU_PERSISTENCE_H
#ifdef __cplusplus
extern "C" {
#endif
#include "menu_types.h"
// Persistence management functions
MenuErrCode menu_persistence_init_impl(MenuCoreCtx* ctx);
MenuErrCode menu_persistence_save_impl(MenuCoreCtx* ctx);
MenuErrCode menu_persistence_load_impl(MenuCoreCtx* ctx);
MenuErrCode menu_persistence_clear_impl(MenuCoreCtx* ctx);
bool menu_persistence_is_dirty_impl(MenuCoreCtx* ctx);
// Storage write/read callbacks for platform-specific implementation
typedef MenuErrCode (*MenuPersistenceWriteFunc)(const void* data, size_t size);
typedef MenuErrCode (*MenuPersistenceReadFunc)(void* data, size_t size);
MenuErrCode menu_persistence_register_storage_impl(MenuCoreCtx* ctx,
MenuPersistenceWriteFunc write_func,
MenuPersistenceReadFunc read_func);
#ifdef __cplusplus
}
#endif
#endif // MENU_PERSISTENCE_H

View File

@ -37,6 +37,8 @@ typedef enum {
MENU_ERR_STACK_OVERFLOW, MENU_ERR_STACK_OVERFLOW,
MENU_ERR_STACK_UNDERFLOW, MENU_ERR_STACK_UNDERFLOW,
MENU_ERR_NOT_INIT, MENU_ERR_NOT_INIT,
MENU_ERR_CORRUPTED_DATA,
MENU_ERR_INVALID_NODE,
} MenuErrCode; } MenuErrCode;
// Menu States // Menu States
@ -57,7 +59,15 @@ typedef enum {
MENU_EVENT_KEY_ESC, MENU_EVENT_KEY_ESC,
MENU_EVENT_TIMEOUT, MENU_EVENT_TIMEOUT,
MENU_EVENT_RENDER, // Optimization: Render event MENU_EVENT_RENDER, // Optimization: Render event
MENU_EVENT_CUSTOM_BASE = 10,
// Extended event types for more complex interactions
MENU_EVENT_PARAM_CHANGED, // Parameter value changed
MENU_EVENT_MENU_ENTER, // Entered a menu node
MENU_EVENT_MENU_EXIT, // Exited a menu node
MENU_EVENT_STATE_CHANGED, // Menu state changed
MENU_EVENT_ERROR, // Error occurred
MENU_EVENT_REFRESH, // Force refresh the menu
MENU_EVENT_CUSTOM_BASE = 20, // Custom events start from here
} MenuEventType; } MenuEventType;
// Callback function types // Callback function types
@ -96,11 +106,20 @@ typedef struct {
uint8_t top; uint8_t top;
} MenuStack; } MenuStack;
// Event priority levels
typedef enum {
MENU_EVENT_PRIORITY_LOW = 0,
MENU_EVENT_PRIORITY_NORMAL,
MENU_EVENT_PRIORITY_HIGH,
MENU_EVENT_PRIORITY_URGENT,
} MenuEventPriority;
// Event structure // Event structure
typedef struct { typedef struct {
MenuEventType type; MenuEventType type;
uint32_t param; uint32_t param;
uint32_t timestamp; uint32_t timestamp;
MenuEventPriority priority;
} MenuEvent; } MenuEvent;
// Event Queue // Event Queue