功能优化

This commit is contained in:
冯佳
2025-12-23 09:06:47 +08:00
parent cc9f363ff8
commit 3a5491c11e
8 changed files with 318 additions and 84 deletions

View File

@ -58,6 +58,36 @@ MenuNodeId menu_register_node_ex(
return 0;
}
MenuErrCode menu_register_nodes(
const MenuNode* nodes,
size_t count,
MenuNodeId* out_ids
) {
if (!nodes || count == 0) return MENU_ERR_INVALID_PARAM;
for (size_t i = 0; i < count; i++) {
MenuNode node = nodes[i];
MenuNodeId new_id = 0;
node.flags.is_enabled = true;
node.flags.is_visible = true;
node.flags.is_registered = false;
node.param_id = node.param_id ? node.param_id : 0;
node.permission_level = node.permission_level ? node.permission_level : 0;
MenuErrCode err = menu_core_register_node(&g_menu_ctx, &node, &new_id);
if (err != MENU_ERR_OK) {
return err;
}
if (out_ids) {
out_ids[i] = new_id;
}
}
return MENU_ERR_OK;
}
MenuErrCode menu_post_event(MenuEventType type, uint32_t param) {
return menu_event_post(&g_menu_ctx.event_queue, type, param);
}
@ -113,3 +143,74 @@ MenuErrCode menu_node_bind_param(MenuNodeId node_id, uint16_t param_id) {
node->param_id = param_id;
return MENU_ERR_OK;
}
// 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;
}
MenuErrCode menu_permission_set_current_role(uint8_t role_id) {
g_menu_ctx.permission.current_role_id = role_id;
return MENU_ERR_OK;
}
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;
}
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;
}
// Persistence functions
MenuErrCode menu_persistence_save(void) {
return menu_core_save_state(&g_menu_ctx);
}
MenuErrCode menu_persistence_load(void) {
return menu_core_load_state(&g_menu_ctx);
}
MenuErrCode menu_refresh(void) {
MenuErrCode err = menu_post_event(MENU_EVENT_RENDER, 0);
if (err != MENU_ERR_OK) {
return err;
}
return MENU_ERR_OK;
}

View File

@ -34,6 +34,13 @@ MenuNodeId menu_register_node_ex(
void* user_data
);
// Batch node registration
MenuErrCode menu_register_nodes(
const MenuNode* nodes,
size_t count,
MenuNodeId* out_ids
);
// Event Handling
MenuErrCode menu_post_event(MenuEventType type, uint32_t param);
@ -44,6 +51,19 @@ MenuErrCode menu_back(void);
// Parameter Binding
MenuErrCode menu_node_bind_param(MenuNodeId node_id, uint16_t param_id);
// Permission Management
MenuErrCode menu_permission_register_role(uint8_t role_id, const char* name, MenuPermissionLevel level);
MenuErrCode menu_permission_set_current_role(uint8_t role_id);
MenuErrCode menu_permission_update_node_level(MenuNodeId node_id, MenuPermissionLevel level);
bool menu_permission_check_node_access(MenuNodeId node_id);
// Persistence Management
MenuErrCode menu_persistence_save(void);
MenuErrCode menu_persistence_load(void);
// Dynamic menu updates
MenuErrCode menu_refresh(void);
#ifdef __cplusplus
}
#endif

View File

@ -8,7 +8,7 @@
// Globals
int32_t g_volume = 50;
int32_t g_brightness = 80;
uint8_t g_language = 0; // 0=EN, 1=CN
uint8_t g_language = 1; // 0=EN, 1=CN
// Callbacks
MenuErrCode on_enter_main(MenuNodeId id, struct MenuCoreCtx* ctx) {

View File

@ -8,14 +8,7 @@
// Internal helper to get node pointer from ID
MenuNode* menu_core_get_node(MenuCoreCtx* ctx, MenuNodeId id) {
if (!ctx || id == 0) return NULL;
// Simple linear scan for now.
// Optimization: Could use hash table if implemented to return index/pointer
for (int i = 0; i < MENU_CONFIG_MAX_NODES; i++) {
if (ctx->nodes[i].flags.is_registered && ctx->nodes[i].id == id) {
return &ctx->nodes[i];
}
}
return NULL;
return menu_hash_find(ctx, id);
}
MenuErrCode menu_core_init(MenuCoreCtx* ctx) {
@ -88,13 +81,89 @@ MenuErrCode menu_core_register_node(MenuCoreCtx* ctx, const MenuNode* node, Menu
}
// Add to hash
menu_hash_insert(ctx, new_node->id);
menu_hash_insert(ctx, new_node->id, slot);
if (out_id) *out_id = new_node->id;
return MENU_ERR_OK;
}
MenuErrCode menu_core_unregister_node(MenuCoreCtx* ctx, MenuNodeId id) {
if (!ctx || id == 0) return MENU_ERR_INVALID_PARAM;
MenuNode* node = menu_core_get_node(ctx, id);
if (!node) return MENU_ERR_NOT_FOUND;
// Find slot index
int slot = -1;
for (int i = 0; i < MENU_CONFIG_MAX_NODES; i++) {
if (ctx->nodes[i].flags.is_registered && ctx->nodes[i].id == id) {
slot = i;
break;
}
}
if (slot == -1) return MENU_ERR_NOT_FOUND;
// Update parent-child relationship
if (node->parent_id != 0) {
MenuNode* parent = menu_core_get_node(ctx, node->parent_id);
if (parent) {
// Check if this is the first child
if (parent->first_child_id == id) {
parent->first_child_id = node->next_sibling_id;
} else {
// Find previous sibling to update links
MenuNode* prev_sibling = menu_core_get_node(ctx, node->prev_sibling_id);
if (prev_sibling) {
prev_sibling->next_sibling_id = node->next_sibling_id;
}
}
}
}
// Update next sibling's prev_sibling_id
if (node->next_sibling_id != 0) {
MenuNode* next_sibling = menu_core_get_node(ctx, node->next_sibling_id);
if (next_sibling) {
next_sibling->prev_sibling_id = node->prev_sibling_id;
}
}
// Clear node data
ctx->nodes[slot].flags.is_registered = false;
ctx->nodes[slot].id = 0;
ctx->nodes[slot].parent_id = 0;
ctx->nodes[slot].first_child_id = 0;
ctx->nodes[slot].next_sibling_id = 0;
ctx->nodes[slot].prev_sibling_id = 0;
ctx->nodes[slot].hash_next_id = 0;
ctx->nodes[slot].flags.is_selected = false;
ctx->nodes[slot].flags.is_visible = false;
ctx->nodes[slot].flags.is_enabled = false;
ctx->nodes[slot].permission_level = 0;
ctx->nodes[slot].param_id = 0;
ctx->nodes[slot].user_data = NULL;
ctx->nodes[slot].name = NULL;
ctx->nodes[slot].enter_cb = NULL;
ctx->nodes[slot].exit_cb = NULL;
ctx->nodes[slot].render_cb = NULL;
// Update counts
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);
}
}
return MENU_ERR_OK;
}
MenuErrCode menu_core_enter_node(MenuCoreCtx* ctx, MenuNodeId node_id) {
if (!ctx) return MENU_ERR_INVALID_PARAM;
@ -103,7 +172,16 @@ MenuErrCode menu_core_enter_node(MenuCoreCtx* ctx, MenuNodeId node_id) {
// Permission check
#if MENU_CONFIG_ENABLE_PERMISSION
// TODO: Check 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 MENU_ERR_PERMISSION_DENIED;
}
#endif
// Call enter callback
@ -295,6 +373,46 @@ MenuErrCode menu_core_handle_event(MenuCoreCtx* ctx, const MenuEvent* event) {
return MENU_ERR_OK;
}
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;
}
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;
}
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;
}
void menu_core_loop(MenuCoreCtx* ctx, uint32_t tick) {
if (!ctx || !ctx->is_initialized) return;
@ -303,14 +421,20 @@ void menu_core_loop(MenuCoreCtx* ctx, uint32_t tick) {
MenuEvent evt;
if (menu_event_get(&ctx->event_queue, &evt) == MENU_ERR_OK) {
menu_core_handle_event(ctx, &evt);
ctx->persistence.dirty = true;
}
}
// Render
// Optimization: Add a render callback to the context or port
// For now we rely on the current node's render_cb
MenuNode* current = menu_core_get_node(ctx, ctx->current_node_id);
if (current && current->render_cb) {
current->render_cb(current->id, ctx);
}
// Auto-save if dirty
#if MENU_CONFIG_ENABLE_PERSISTENCE
if (ctx->persistence.dirty) {
menu_core_save_state(ctx);
}
#endif
}

View File

@ -13,6 +13,7 @@ MenuErrCode menu_core_init(MenuCoreCtx* ctx);
// Node management
MenuErrCode menu_core_register_node(MenuCoreCtx* ctx, const MenuNode* node, MenuNodeId* out_id);
MenuNode* menu_core_get_node(MenuCoreCtx* ctx, MenuNodeId id);
MenuErrCode menu_core_unregister_node(MenuCoreCtx* ctx, MenuNodeId id);
// Main loop
void menu_core_loop(MenuCoreCtx* ctx, uint32_t tick);

View File

@ -8,64 +8,26 @@ MenuErrCode menu_hash_init(MenuCoreCtx* ctx) {
return MENU_ERR_OK;
}
MenuErrCode menu_hash_insert(MenuCoreCtx* ctx, MenuNodeId id) {
MenuErrCode menu_hash_insert(MenuCoreCtx* ctx, MenuNodeId id, uint16_t index) {
if (!ctx) return MENU_ERR_INVALID_PARAM;
uint16_t index = id % MENU_CONFIG_HASH_TABLE_SIZE;
MenuNodeId current = ctx->hash_table[index];
uint16_t hash_index = id % MENU_CONFIG_HASH_TABLE_SIZE;
uint16_t current = ctx->hash_table[hash_index];
if (current == 0) {
ctx->hash_table[index] = id;
ctx->hash_table[hash_index] = index + 1;
} else {
// Collision handling: append to the end of the chain
// Note: 0 is used as invalid ID/null pointer in this static array context
// This implies valid node IDs must start from 1.
// Find the node structure for 'current'
// This requires access to the node array which is in ctx
// but we need to find the node index from the ID if ID != index
// In this system, we usually assume ID corresponds to index if allocated sequentially,
// but since we support arbitrary IDs (maybe?), we need to be careful.
// HOWEVER, the design says "Static node pool", so usually we just iterate.
// BUT wait, if we are building a hash table, we want O(1) access.
// Let's assume ID is NOT the index in the array, but a unique handle.
// But we need to find the MenuNode* from ID to set hash_next_id.
// Since we are inside menu_hash_insert, we probably just added the node
// so we might know where it is, but this function takes ID.
// We need a way to look up the MenuNode* from ID to set the next pointer.
// If we can't find it efficiently, this hash insert is slow.
// Actually, looking at `MenuCoreCtx`, `nodes` is an array.
// Does `nodes[i].id` == i?
// If the user provides arbitrary IDs, we need to scan the array to find the node
// to set its `hash_next_id`.
// That defeats the purpose of the hash table if insertion is O(N).
// But insertion happens only at init. Lookup is frequent.
// Let's implement finding the node by scanning for insertion.
MenuNode* prev_node = NULL;
// Find the head of the chain
for (int i = 0; i < ctx->node_count; i++) {
if (ctx->nodes[i].id == current) {
prev_node = &ctx->nodes[i];
break;
}
}
if (!prev_node) return MENU_ERR_NOT_FOUND; // Should not happen
MenuNode* prev_node = &ctx->nodes[current - 1];
while (prev_node->hash_next_id != 0) {
MenuNodeId next_id = prev_node->hash_next_id;
bool found = false;
for (int i = 0; i < ctx->node_count; i++) {
if (ctx->nodes[i].id == next_id) {
prev_node = &ctx->nodes[i];
found = true;
break;
}
}
if (!found) return MENU_ERR_FAIL; // Broken chain
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->hash_next_id = id;
@ -74,26 +36,34 @@ MenuErrCode menu_hash_insert(MenuCoreCtx* ctx, MenuNodeId id) {
return MENU_ERR_OK;
}
MenuNodeId menu_hash_find(MenuCoreCtx* ctx, MenuNodeId id) {
if (!ctx) return 0;
MenuNode* menu_hash_find(MenuCoreCtx* ctx, MenuNodeId id) {
if (!ctx) return NULL;
uint16_t index = id % MENU_CONFIG_HASH_TABLE_SIZE;
MenuNodeId current = ctx->hash_table[index];
uint16_t hash_index = id % MENU_CONFIG_HASH_TABLE_SIZE;
uint16_t current = ctx->hash_table[hash_index];
while (current != 0) {
if (current == id) return current;
uint16_t node_index = current - 1;
MenuNode* node = &ctx->nodes[node_index];
if (node->flags.is_registered && node->id == id) {
return node;
}
// Move to next
bool found = false;
for (int i = 0; i < ctx->node_count; i++) {
if (ctx->nodes[i].id == current) {
current = ctx->nodes[i].hash_next_id;
found = true;
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 0; // Not found
return NULL;
}

View File

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

View File

@ -17,6 +17,13 @@ struct MenuCoreCtx;
typedef uint16_t MenuNodeId;
typedef uint8_t MenuPermissionLevel;
// Role structure
typedef struct {
uint8_t id;
const char* name;
MenuPermissionLevel level;
} MenuRole;
// Error codes
typedef enum {
MENU_ERR_OK = 0,
@ -110,10 +117,19 @@ typedef struct {
uint8_t depth;
} MenuNavPath;
typedef struct MenuPersistenceData {
MenuNodeId current_node_id;
MenuState current_state;
MenuNavPath nav_path;
MenuStack stack;
uint32_t timestamp;
uint8_t checksum;
} MenuPersistenceData;
// Core Context
typedef struct MenuCoreCtx {
MenuNode nodes[MENU_CONFIG_MAX_NODES];
MenuNodeId hash_table[MENU_CONFIG_HASH_TABLE_SIZE];
uint16_t hash_table[MENU_CONFIG_HASH_TABLE_SIZE];
MenuStack stack;
MenuNavPath nav_path;
MenuEventQueue event_queue;
@ -132,6 +148,8 @@ typedef struct MenuCoreCtx {
// Permission module data
struct {
uint8_t current_role_id;
MenuRole roles[8];
uint8_t role_count;
} permission;
// Persistence module data