优化一版

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 事件处理
```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) {

View File

@ -2,6 +2,8 @@
#include "../src/core/menu_core.h"
#include "../src/core/menu_event.h"
#include "../src/core/menu_stack.h"
#include "../src/core/menu_permission.h"
#include "../src/core/menu_persistence.h"
#include <stddef.h>
static MenuCoreCtx g_menu_ctx;
@ -88,8 +90,12 @@ MenuErrCode menu_register_nodes(
return MENU_ERR_OK;
}
MenuErrCode menu_post_event(MenuEventType type, uint32_t param) {
return menu_event_post(&g_menu_ctx.event_queue, type, param);
MenuErrCode menu_post_event(MenuEventType type, uint32_t param, MenuEventPriority priority) {
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) {
@ -134,7 +140,7 @@ MenuErrCode menu_enter(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) {
@ -146,69 +152,40 @@ MenuErrCode menu_node_bind_param(MenuNodeId node_id, uint16_t param_id) {
// Permission management functions
MenuErrCode menu_permission_register_role(uint8_t role_id, const char* name, MenuPermissionLevel level) {
if (!name) return MENU_ERR_INVALID_PARAM;
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;
return menu_permission_register_role_impl(&g_menu_ctx, role_id, name, level);
}
MenuErrCode menu_permission_set_current_role(uint8_t role_id) {
g_menu_ctx.permission.current_role_id = role_id;
return MENU_ERR_OK;
return menu_permission_set_current_role_impl(&g_menu_ctx, role_id);
}
MenuErrCode menu_permission_update_node_level(MenuNodeId node_id, MenuPermissionLevel level) {
MenuNode* node = menu_core_get_node(&g_menu_ctx, node_id);
if (!node) return MENU_ERR_NOT_FOUND;
node->permission_level = level;
return MENU_ERR_OK;
return menu_permission_update_node_level_impl(&g_menu_ctx, node_id, level);
}
bool menu_permission_check_node_access(MenuNodeId node_id) {
MenuNode* node = menu_core_get_node(&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;
return menu_permission_check_node_access_impl(&g_menu_ctx, node_id);
}
// Persistence functions
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) {
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 err = menu_post_event(MENU_EVENT_RENDER, 0);
MenuErrCode err = menu_post_event_normal(MENU_EVENT_RENDER, 0);
if (err != MENU_ERR_OK) {
return err;
}

View File

@ -42,7 +42,8 @@ MenuErrCode menu_register_nodes(
);
// 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
MenuErrCode menu_enter(void); // Enter root
@ -60,6 +61,8 @@ bool menu_permission_check_node_access(MenuNodeId node_id);
// Persistence Management
MenuErrCode menu_persistence_save(void);
MenuErrCode menu_persistence_load(void);
MenuErrCode menu_persistence_clear(void);
bool menu_persistence_is_dirty(void);
// Dynamic menu updates
MenuErrCode menu_refresh(void);

View File

@ -81,6 +81,11 @@ void setup_menu(void) {
// Child of Settings: Brightness
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)
// 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)
@ -96,24 +101,48 @@ int main(void) {
menu_port_log("Menu System Demo Started");
menu_enter(); // Enter root
// Simulation Loop
int steps = 0;
while (steps < 20) {
menu_main_loop(0);
// Demo: Permission management
menu_port_log("=== Permission Management Demo ===");
menu_port_log("Current role: Guest");
menu_port_log("Trying to enter Settings...");
menu_post_event_normal(MENU_EVENT_KEY_ENTER, 0);
menu_main_loop(0);
// Simulate inputs
if (steps == 2) {
menu_port_log("User Press: ENTER");
menu_post_event(MENU_EVENT_KEY_ENTER, 0);
}
if (steps == 4) {
menu_port_log("User Press: DOWN");
menu_post_event(MENU_EVENT_KEY_DOWN, 0);
}
menu_port_log("Switching to User role...");
menu_permission_set_current_role(2); // Switch to User role
menu_post_event_normal(MENU_EVENT_KEY_ENTER, 0);
menu_main_loop(0);
steps++;
// sleep
// Navigate to Volume setting
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;
}

View File

@ -3,6 +3,8 @@
#include "menu_stack.h"
#include "menu_hash.h"
#include "menu_param.h"
#include "menu_permission.h"
#include "menu_persistence.h"
#include <string.h>
// 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_stack_init(&ctx->stack);
menu_hash_init(ctx);
menu_permission_init_impl(ctx);
ctx->is_initialized = true;
ctx->current_state = MENU_STATE_INIT;
@ -153,13 +156,8 @@ MenuErrCode menu_core_unregister_node(MenuCoreCtx* ctx, MenuNodeId id) {
ctx->node_count--;
ctx->free_node_count++;
// Rebuild hash table since we can't easily remove from it
menu_hash_init(ctx);
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);
}
}
// Remove node from hash table using optimized removal
menu_hash_remove(ctx, id, slot);
return MENU_ERR_OK;
}
@ -172,14 +170,7 @@ MenuErrCode menu_core_enter_node(MenuCoreCtx* ctx, MenuNodeId node_id) {
// Permission check
#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) {
if (!menu_permission_check_node_access_impl(ctx, node_id)) {
return MENU_ERR_PERMISSION_DENIED;
}
#endif
@ -366,51 +357,73 @@ MenuErrCode menu_core_handle_event(MenuCoreCtx* ctx, const MenuEvent* event) {
}
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:
// 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;
}
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) {
uint8_t checksum = 0;
const uint8_t* bytes = (const uint8_t*)data;
size_t len = sizeof(MenuPersistenceData) - sizeof(data->checksum);
for (size_t i = 0; i < len; i++) {
checksum ^= bytes[i];
}
return checksum;
(void)data; // Suppress unused parameter warning
return 0;
}
MenuErrCode menu_core_save_state(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,
.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;
return menu_persistence_save_impl(ctx);
}
MenuErrCode menu_core_load_state(MenuCoreCtx* ctx) {
if (!ctx) return MENU_ERR_INVALID_PARAM;
// TODO: Implement actual storage read logic here
// This would typically read from EEPROM, Flash, or other non-volatile storage
// For now, we'll just return OK
return MENU_ERR_OK;
return menu_persistence_load_impl(ctx);
}
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_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
}
#endif

View File

@ -7,24 +7,56 @@ MenuErrCode menu_event_init(MenuEventQueue* queue) {
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->count >= MENU_CONFIG_EVENT_QUEUE_LEN) {
return MENU_ERR_QUEUE_FULL;
}
MenuEvent* evt = &queue->events[queue->tail];
evt->type = type;
evt->param = param;
// timestamp handling can be added here if we have system tick access,
// for now left as 0 or handled by caller if passed in param (which is just 32bit)
// or we can add a tick parameter to post_event.
// For simplicity, we assume timestamp is not critical for now or set elsewhere.
evt->timestamp = 0;
// Create the new event
MenuEvent new_event;
new_event.type = type;
new_event.param = param;
new_event.timestamp = 0; // timestamp handling can be added later if needed
new_event.priority = priority;
queue->tail = (queue->tail + 1) % MENU_CONFIG_EVENT_QUEUE_LEN;
queue->count++;
if (queue->count == 0) {
// 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;
}

View File

@ -8,7 +8,7 @@ extern "C" {
#endif
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);
bool menu_event_is_empty(const MenuEventQueue* queue);

View File

@ -5,6 +5,12 @@
MenuErrCode menu_hash_init(MenuCoreCtx* ctx) {
if (!ctx) return MENU_ERR_INVALID_PARAM;
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;
}
@ -19,18 +25,10 @@ MenuErrCode menu_hash_insert(MenuCoreCtx* ctx, MenuNodeId id, uint16_t index) {
} else {
MenuNode* prev_node = &ctx->nodes[current - 1];
while (prev_node->hash_next_id != 0) {
bool found = false;
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 = &ctx->nodes[prev_node->hash_next_id - 1];
}
prev_node->hash_next_id = id;
prev_node->hash_next_id = index + 1;
}
return MENU_ERR_OK;
@ -49,21 +47,40 @@ MenuNode* menu_hash_find(MenuCoreCtx* ctx, MenuNodeId id) {
return node;
}
bool found = false;
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;
current = node->hash_next_id;
}
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_insert(MenuCoreCtx* ctx, MenuNodeId id, uint16_t index);
MenuNode* menu_hash_find(MenuCoreCtx* ctx, MenuNodeId id);
MenuErrCode menu_hash_remove(MenuCoreCtx* ctx, MenuNodeId id, uint16_t index);
#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_UNDERFLOW,
MENU_ERR_NOT_INIT,
MENU_ERR_CORRUPTED_DATA,
MENU_ERR_INVALID_NODE,
} MenuErrCode;
// Menu States
@ -57,7 +59,15 @@ typedef enum {
MENU_EVENT_KEY_ESC,
MENU_EVENT_TIMEOUT,
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;
// Callback function types
@ -96,11 +106,20 @@ typedef struct {
uint8_t top;
} 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
typedef struct {
MenuEventType type;
uint32_t param;
uint32_t timestamp;
MenuEventPriority priority;
} MenuEvent;
// Event Queue