681 lines
23 KiB
C
681 lines
23 KiB
C
/**
|
||
**********************************************************************************************************************
|
||
* @file demo.c
|
||
* @brief 菜单组件示例程序 - 多语言版本
|
||
* @author menu_component
|
||
* @date 2025-12-19
|
||
**********************************************************************************************************************
|
||
*/
|
||
|
||
#include "../api/menu_api.h"
|
||
#include "../port/menu_port.h"
|
||
#include "language.h"
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include <time.h>
|
||
|
||
#if defined(_WIN32) || defined(_WIN64)
|
||
#include <conio.h>
|
||
#define NON_BLOCKING_GETCHAR() _getch()
|
||
#define IS_KEY_AVAILABLE() _kbhit()
|
||
#else
|
||
#include <sys/time.h>
|
||
#include <sys/select.h>
|
||
#include <unistd.h>
|
||
#include <termios.h>
|
||
|
||
// 保存原始终端设置
|
||
static struct termios sg_original_termios;
|
||
|
||
/**
|
||
* @brief 设置终端为无缓冲模式
|
||
*/
|
||
static void set_nonblocking_terminal(void)
|
||
{
|
||
struct termios new_termios;
|
||
tcgetattr(STDIN_FILENO, &sg_original_termios);
|
||
new_termios = sg_original_termios;
|
||
new_termios.c_lflag &= ~(ICANON | ECHO); // 关闭行缓冲和回显
|
||
new_termios.c_cc[VMIN] = 1; // 至少读取1个字符
|
||
new_termios.c_cc[VTIME] = 0; // 无超时
|
||
tcsetattr(STDIN_FILENO, TCSANOW, &new_termios);
|
||
}
|
||
|
||
/**
|
||
* @brief 恢复终端为原始模式
|
||
*/
|
||
static void restore_terminal(void)
|
||
{
|
||
tcsetattr(STDIN_FILENO, TCSANOW, &sg_original_termios);
|
||
}
|
||
|
||
/**
|
||
* @brief 检查是否有按键输入
|
||
*/
|
||
static bool IS_KEY_AVAILABLE(void)
|
||
{
|
||
struct timeval tv;
|
||
fd_set fds;
|
||
tv.tv_sec = 0;
|
||
tv.tv_usec = 0;
|
||
FD_ZERO(&fds);
|
||
FD_SET(STDIN_FILENO, &fds);
|
||
return select(STDIN_FILENO + 1, &fds, NULL, NULL, &tv) > 0;
|
||
}
|
||
|
||
/**
|
||
* @brief 无缓冲获取按键
|
||
*/
|
||
static char NON_BLOCKING_GETCHAR(void)
|
||
{
|
||
return getchar();
|
||
}
|
||
#endif
|
||
|
||
/* 使用新的菜单节点ID枚举 */
|
||
#define MENU_ID_MAIN MENU_NODE_ID_MAIN
|
||
#define MENU_ID_ITEM1 MENU_NODE_ID_MEASUREMENT
|
||
#define MENU_ID_ITEM2 MENU_NODE_ID_PROTECTION
|
||
#define MENU_ID_ITEM3 MENU_NODE_ID_CONTROL
|
||
#define MENU_ID_SUB1 MENU_NODE_ID_SETTING
|
||
#define MENU_ID_SUB2 MENU_NODE_ID_RECORD
|
||
#define MENU_ID_LANGUAGE MENU_NODE_ID_HELP
|
||
#define MENU_ID_GRANDCHILD1 MENU_NODE_ID_ABOUT
|
||
#define MENU_ID_GRANDCHILD2 (MENU_NODE_ID_ABOUT + 1)
|
||
|
||
/* 测量菜单子项 */
|
||
#define MENU_ID_MEASUREMENT_VOLTAGE MENU_NODE_ID_MEASUREMENT_VOLTAGE
|
||
#define MENU_ID_MEASUREMENT_CURRENT MENU_NODE_ID_MEASUREMENT_CURRENT
|
||
#define MENU_ID_MEASUREMENT_POWER MENU_NODE_ID_MEASUREMENT_POWER
|
||
#define MENU_ID_MEASUREMENT_ENERGY MENU_NODE_ID_MEASUREMENT_ENERGY
|
||
#define MENU_ID_MEASUREMENT_FREQUENCY MENU_NODE_ID_MEASUREMENT_FREQUENCY
|
||
#define MENU_ID_MEASUREMENT_TEMPERATURE MENU_NODE_ID_MEASUREMENT_TEMPERATURE
|
||
|
||
/* 保护菜单子项 */
|
||
#define MENU_ID_PROTECTION_OVER_VOLTAGE MENU_NODE_ID_PROTECTION_OVER_VOLTAGE
|
||
#define MENU_ID_PROTECTION_UNDER_VOLTAGE MENU_NODE_ID_PROTECTION_UNDER_VOLTAGE
|
||
#define MENU_ID_PROTECTION_OVER_CURRENT MENU_NODE_ID_PROTECTION_OVER_CURRENT
|
||
#define MENU_ID_PROTECTION_SETTING MENU_NODE_ID_PROTECTION_SETTING
|
||
|
||
/* 控制菜单子项 */
|
||
#define MENU_ID_CONTROL_ON_OFF MENU_NODE_ID_CONTROL_ON_OFF
|
||
#define MENU_ID_CONTROL_START_STOP MENU_NODE_ID_CONTROL_START_STOP
|
||
#define MENU_ID_CONTROL_AUTO_MANUAL MENU_NODE_ID_CONTROL_AUTO_MANUAL
|
||
|
||
/* 设置菜单子项 */
|
||
#define MENU_ID_SETTING_LANGUAGE MENU_NODE_ID_SETTING_LANGUAGE
|
||
|
||
/* 持久化函数声明 */
|
||
static MenuErrCode demo_persistence_save(const uint8_t* data, uint16_t size);
|
||
static MenuErrCode demo_persistence_restore(uint8_t* data, uint16_t* size);
|
||
|
||
/* 全局变量 */
|
||
static uint32_t sg_tick = 0;
|
||
static bool sg_is_menu_active = false;
|
||
static SystemLang_e sg_current_language = SYSTEM_LANG_CHINESE;
|
||
|
||
/* 菜单回调函数 */
|
||
|
||
/**
|
||
* @brief 主菜单进入回调
|
||
*/
|
||
static MenuErrCode menu_enter_main(MenuNodeId node_id, struct MenuCoreCtx* core_ctx)
|
||
{
|
||
(void)node_id;
|
||
(void)core_ctx;
|
||
sg_is_menu_active = true;
|
||
printf("%s: %s\n", get_text(TEXT_ENTER), get_text(TEXT_MAIN_MENU));
|
||
return MENU_ERR_OK;
|
||
}
|
||
|
||
/**
|
||
* @brief 主菜单退出回调
|
||
*/
|
||
static MenuErrCode menu_exit_main(MenuNodeId node_id, struct MenuCoreCtx* core_ctx)
|
||
{
|
||
(void)node_id;
|
||
(void)core_ctx;
|
||
sg_is_menu_active = false;
|
||
printf("%s: %s\n", get_text(TEXT_EXIT), get_text(TEXT_MAIN_MENU));
|
||
return MENU_ERR_OK;
|
||
}
|
||
|
||
/**
|
||
* @brief 测量菜单进入回调
|
||
*/
|
||
static MenuErrCode menu_enter_item1(MenuNodeId node_id, struct MenuCoreCtx* core_ctx)
|
||
{
|
||
(void)node_id;
|
||
(void)core_ctx;
|
||
printf("%s: %s\n", get_text(TEXT_ENTER), get_text(TEXT_MAIN_MEASUREMENT));
|
||
return MENU_ERR_OK;
|
||
}
|
||
|
||
/**
|
||
* @brief 保护菜单进入回调
|
||
*/
|
||
static MenuErrCode menu_enter_item2(MenuNodeId node_id, struct MenuCoreCtx* core_ctx)
|
||
{
|
||
(void)node_id;
|
||
(void)core_ctx;
|
||
printf("%s: %s\n", get_text(TEXT_ENTER), get_text(TEXT_MAIN_PROTECTION));
|
||
return MENU_ERR_OK;
|
||
}
|
||
|
||
/**
|
||
* @brief 控制菜单进入回调
|
||
*/
|
||
static MenuErrCode menu_enter_item3(MenuNodeId node_id, struct MenuCoreCtx* core_ctx)
|
||
{
|
||
(void)node_id;
|
||
(void)core_ctx;
|
||
printf("%s: %s\n", get_text(TEXT_ENTER), get_text(TEXT_MAIN_CONTROL));
|
||
return MENU_ERR_OK;
|
||
}
|
||
|
||
/**
|
||
* @brief 语言设置进入回调(声明)
|
||
*/
|
||
static MenuErrCode menu_enter_language(MenuNodeId node_id, struct MenuCoreCtx* core_ctx);
|
||
|
||
/**
|
||
* @brief 设置菜单进入回调(声明)
|
||
*/
|
||
static MenuErrCode menu_enter_setting(MenuNodeId node_id, struct MenuCoreCtx* core_ctx);
|
||
|
||
/**
|
||
* @brief 重新注册菜单节点,用于语言切换
|
||
*/
|
||
static void re_register_menu_nodes(void)
|
||
{
|
||
// 先注销所有节点
|
||
menu_deinit();
|
||
menu_init();
|
||
|
||
// 主菜单
|
||
MenuNodeId main_node_id = menu_register_node(0, 0, get_text(TEXT_MAIN_MENU), menu_enter_main, menu_exit_main);
|
||
|
||
// 菜单项
|
||
MenuNodeId item1_node_id = menu_register_node(0, main_node_id, get_text(TEXT_MAIN_MEASUREMENT), menu_enter_item1, NULL);
|
||
MenuNodeId item2_node_id = menu_register_node(0, main_node_id, get_text(TEXT_MAIN_PROTECTION), menu_enter_item2, NULL);
|
||
MenuNodeId item3_node_id = menu_register_node(0, main_node_id, get_text(TEXT_MAIN_CONTROL), menu_enter_item3, NULL);
|
||
MenuNodeId sub1_node_id = menu_register_node(0, main_node_id, get_text(TEXT_MAIN_SETTING), menu_enter_setting, NULL);
|
||
|
||
// 测量菜单子项
|
||
menu_register_node(0, item1_node_id, get_text(TEXT_MEASURE_VOLTAGE), NULL, NULL);
|
||
menu_register_node(0, item1_node_id, get_text(TEXT_MEASURE_CURRENT), NULL, NULL);
|
||
menu_register_node(0, item1_node_id, get_text(TEXT_MEASURE_POWER), NULL, NULL);
|
||
menu_register_node(0, item1_node_id, get_text(TEXT_MEASURE_ENERGY), NULL, NULL);
|
||
menu_register_node(0, item1_node_id, get_text(TEXT_MEASURE_FREQUENCY), NULL, NULL);
|
||
menu_register_node(0, item1_node_id, get_text(TEXT_MEASURE_TEMPERATURE), NULL, NULL);
|
||
|
||
// 保护菜单子项
|
||
menu_register_node(0, item2_node_id, get_text(TEXT_PROTECT_OVER_VOLTAGE), NULL, NULL);
|
||
menu_register_node(0, item2_node_id, get_text(TEXT_PROTECT_UNDER_VOLTAGE), NULL, NULL);
|
||
menu_register_node(0, item2_node_id, get_text(TEXT_PROTECT_OVER_CURRENT), NULL, NULL);
|
||
menu_register_node(0, item2_node_id, get_text(TEXT_PROTECT_SETTING), NULL, NULL);
|
||
|
||
// 控制菜单子项
|
||
menu_register_node(0, item3_node_id, get_text(TEXT_CONTROL_ON_OFF), NULL, NULL);
|
||
menu_register_node(0, item3_node_id, get_text(TEXT_CONTROL_START_STOP), NULL, NULL);
|
||
menu_register_node(0, item3_node_id, get_text(TEXT_CONTROL_AUTO_MANUAL), NULL, NULL);
|
||
|
||
// 设置菜单子项
|
||
menu_register_node(0, sub1_node_id, get_text(TEXT_SETTING_LANGUAGE), menu_enter_language, NULL);
|
||
|
||
// 重新注册持久化回调函数
|
||
menu_persistence_register_callback(demo_persistence_save, demo_persistence_restore);
|
||
}
|
||
|
||
/**
|
||
* @brief 设置菜单进入回调
|
||
*/
|
||
static MenuErrCode menu_enter_setting(MenuNodeId node_id, struct MenuCoreCtx* core_ctx)
|
||
{
|
||
(void)node_id;
|
||
(void)core_ctx;
|
||
printf("%s: %s\n", get_text(TEXT_ENTER), get_text(TEXT_MAIN_SETTING));
|
||
return MENU_ERR_OK;
|
||
}
|
||
|
||
/**
|
||
* @brief 语言设置进入回调
|
||
*/
|
||
static MenuErrCode menu_enter_language(MenuNodeId node_id, struct MenuCoreCtx* core_ctx)
|
||
{
|
||
(void)node_id;
|
||
(void)core_ctx;
|
||
printf("%s: %s\n", get_text(TEXT_ENTER), get_text(TEXT_SETTING_LANGUAGE));
|
||
|
||
// 切换语言
|
||
if (sg_current_language == SYSTEM_LANG_CHINESE)
|
||
{
|
||
sg_current_language = SYSTEM_LANG_ENGLISH;
|
||
set_language(SYSTEM_LANG_ENGLISH);
|
||
printf("%s: English\n", get_text(TEXT_SETTING_LANGUAGE));
|
||
}
|
||
else
|
||
{
|
||
sg_current_language = SYSTEM_LANG_CHINESE;
|
||
set_language(SYSTEM_LANG_CHINESE);
|
||
printf("%s: 中文\n", get_text(TEXT_SETTING_LANGUAGE));
|
||
}
|
||
|
||
// 重新注册菜单节点,更新菜单名称
|
||
re_register_menu_nodes();
|
||
|
||
return MENU_ERR_OK;
|
||
}
|
||
|
||
/**
|
||
* @brief 默认获取系统时间函数
|
||
*/
|
||
static uint32_t demo_get_tick(void)
|
||
{
|
||
return sg_tick++;
|
||
}
|
||
|
||
/**
|
||
* @brief 默认打印函数
|
||
*/
|
||
static void demo_printf(const char* fmt, va_list args)
|
||
{
|
||
vprintf(fmt, args);
|
||
}
|
||
|
||
/**
|
||
* @brief 持久化数据保存回调函数
|
||
* @param data 要保存的数据
|
||
* @param size 数据大小
|
||
* @return 错误码
|
||
*/
|
||
static MenuErrCode demo_persistence_save(const uint8_t* data, uint16_t size)
|
||
{
|
||
FILE* fp = NULL;
|
||
MenuErrCode ret = MENU_ERR_OK;
|
||
|
||
// 打开文件进行写入
|
||
fp = fopen("menu_persistence.dat", "wb");
|
||
if (fp == NULL) {
|
||
printf("%s: %s\n", get_text(TEXT_ERROR), "打开文件失败");
|
||
return MENU_ERR_OPERATION_FAILED;
|
||
}
|
||
|
||
// 写入数据
|
||
if (fwrite(data, 1, size, fp) != size) {
|
||
printf("%s: %s\n", get_text(TEXT_ERROR), "写入文件失败");
|
||
ret = MENU_ERR_OPERATION_FAILED;
|
||
}
|
||
|
||
// 关闭文件
|
||
fclose(fp);
|
||
return ret;
|
||
}
|
||
|
||
/**
|
||
* @brief 持久化数据恢复回调函数
|
||
* @param data 恢复数据缓冲区
|
||
* @param size 数据大小
|
||
* @return 错误码
|
||
*/
|
||
static MenuErrCode demo_persistence_restore(uint8_t* data, uint16_t* size)
|
||
{
|
||
FILE* fp = NULL;
|
||
MenuErrCode ret = MENU_ERR_OK;
|
||
size_t read_size = 0;
|
||
|
||
// 打开文件进行读取
|
||
fp = fopen("menu_persistence.dat", "rb");
|
||
if (fp == NULL) {
|
||
printf("%s: %s\n", get_text(TEXT_WARNING), "文件不存在,使用默认设置");
|
||
return MENU_ERR_OPERATION_FAILED;
|
||
}
|
||
|
||
// 获取文件大小
|
||
fseek(fp, 0, SEEK_END);
|
||
read_size = ftell(fp);
|
||
fseek(fp, 0, SEEK_SET);
|
||
|
||
// 检查文件大小是否合法
|
||
if (read_size > *size) {
|
||
printf("%s: %s\n", get_text(TEXT_ERROR), "文件过大");
|
||
fclose(fp);
|
||
return MENU_ERR_INVALID_PARAM;
|
||
}
|
||
|
||
// 读取数据
|
||
if (fread(data, 1, read_size, fp) != read_size) {
|
||
printf("%s: %s\n", get_text(TEXT_ERROR), "读取文件失败");
|
||
ret = MENU_ERR_OPERATION_FAILED;
|
||
} else {
|
||
// 更新实际读取的大小
|
||
*size = (uint16_t)read_size;
|
||
}
|
||
|
||
// 关闭文件
|
||
fclose(fp);
|
||
return ret;
|
||
}
|
||
|
||
/**
|
||
* @brief 硬件驱动配置
|
||
*/
|
||
static MenuPortDriver sg_demo_driver = {
|
||
.printf = demo_printf,
|
||
.get_tick = demo_get_tick,
|
||
.delay_ms = NULL,
|
||
.display = NULL,
|
||
.key_scan = NULL,
|
||
.irq_ctrl = NULL,
|
||
.error_handler = NULL,
|
||
.modbus_send = NULL,
|
||
.modbus_receive = NULL,
|
||
.persistence_save = demo_persistence_save,
|
||
.persistence_restore = demo_persistence_restore,
|
||
};
|
||
|
||
/**
|
||
* @brief 演示菜单导航
|
||
*/
|
||
/**
|
||
* @brief 显示当前菜单的子菜单项
|
||
*/
|
||
static void display_menu_items(void)
|
||
{
|
||
MenuNodeId curr_node_id = menu_get_current_node();
|
||
const MenuNode* curr_node = menu_find_node(curr_node_id);
|
||
|
||
if (curr_node == NULL) {
|
||
printf("\n无效节点\n");
|
||
return;
|
||
}
|
||
|
||
// 清屏(可选,根据终端支持情况)
|
||
printf("\033[2J\033[H");
|
||
|
||
// 显示当前菜单路径
|
||
printf("%s\n", get_text(TEXT_MAIN_MENU));
|
||
printf("==================\n");
|
||
|
||
// 显示当前菜单状态
|
||
printf("%s: %s\n", get_text(TEXT_CURRENT), curr_node->name ? curr_node->name : "Unnamed");
|
||
|
||
// 显示导航路径
|
||
uint8_t path_depth = menu_get_nav_depth();
|
||
MenuNodeId path[MENU_CONFIG_STACK_DEPTH];
|
||
menu_get_nav_path(path, MENU_CONFIG_STACK_DEPTH);
|
||
|
||
printf("%s: ", get_text(TEXT_STATUS));
|
||
for (uint8_t i = 0; i < path_depth; i++) {
|
||
const MenuNode* path_node = menu_find_node(path[i]);
|
||
if (path_node != NULL) {
|
||
printf("%s", path_node->name ? path_node->name : "Unnamed");
|
||
if (i < path_depth - 1) {
|
||
printf(" -> ");
|
||
}
|
||
}
|
||
}
|
||
printf("\n\n");
|
||
|
||
// 显示当前菜单的子菜单项
|
||
printf("%s:\n", get_text(TEXT_SUMMARY));
|
||
|
||
if (curr_node->first_child_id != 0) {
|
||
// 遍历所有子节点
|
||
const MenuNode* child_node = menu_find_node(curr_node->first_child_id);
|
||
uint8_t item_index = 1;
|
||
|
||
while (child_node != NULL) {
|
||
// 子菜单仅显示列表,不高亮选中项(高亮在同级节点中处理)
|
||
printf(" %d. %s\n", item_index, child_node->name ? child_node->name : "Unnamed");
|
||
|
||
MenuNodeId next_id = child_node->next_sibling_id;
|
||
if (next_id == 0) {
|
||
break;
|
||
}
|
||
child_node = menu_find_node(next_id);
|
||
item_index++;
|
||
}
|
||
} else {
|
||
printf(" %s\n", get_text(TEXT_EMPTY));
|
||
}
|
||
|
||
// 显示当前节点在同级节点中的位置
|
||
const MenuNode* parent_node = menu_find_node(curr_node->parent_id);
|
||
if (parent_node != NULL) {
|
||
printf("\n%s:\n", get_text(TEXT_CURRENT_SELECTION));
|
||
|
||
// 遍历所有兄弟节点
|
||
const MenuNode* sibling_node = menu_find_node(parent_node->first_child_id);
|
||
uint8_t item_index = 1;
|
||
|
||
while (sibling_node != NULL) {
|
||
// 检查当前显示的节点是否为选中节点
|
||
bool is_selected = (sibling_node->id == curr_node_id);
|
||
|
||
// 高亮显示当前选中的菜单项
|
||
if (is_selected) {
|
||
printf(" **> %d. %s <**\n",
|
||
item_index, sibling_node->name ? sibling_node->name : "Unnamed");
|
||
} else {
|
||
printf(" %d. %s\n", item_index, sibling_node->name ? sibling_node->name : "Unnamed");
|
||
}
|
||
|
||
MenuNodeId next_id = sibling_node->next_sibling_id;
|
||
if (next_id == 0) {
|
||
break;
|
||
}
|
||
sibling_node = menu_find_node(next_id);
|
||
item_index++;
|
||
}
|
||
}
|
||
|
||
// 显示菜单操作提示
|
||
printf("\n%s:\n", get_text(TEXT_OPERATIONS));
|
||
printf(" ↑/%s: %s\n", get_text(TEXT_DOWN_ARROW), get_text(TEXT_SELECT_ITEM));
|
||
printf(" %s: %s\n", get_text(TEXT_ENTER), get_text(TEXT_ENTER_SUBMENU));
|
||
printf(" Esc/%s: %s\n", get_text(TEXT_LEFT_ARROW), get_text(TEXT_RETURN_PARENT));
|
||
printf(" q: %s\n", get_text(TEXT_EXIT));
|
||
printf("==================\n");
|
||
}
|
||
|
||
/**
|
||
* @brief 演示菜单导航
|
||
*/
|
||
static void demo_menu_navigation(void)
|
||
{
|
||
char key = 0;
|
||
|
||
// 设置终端为无缓冲模式(仅在非Windows平台)
|
||
#ifndef _WIN32
|
||
set_nonblocking_terminal();
|
||
#endif
|
||
|
||
printf("\n%s\n", get_text(TEXT_MAIN_MENU));
|
||
printf("==================\n");
|
||
printf("%s: %s\n", get_text(TEXT_SELECT_OPTION), get_text(TEXT_MAIN_MENU));
|
||
printf("Enter: %s\n", get_text(TEXT_ENTER));
|
||
printf("Esc: %s\n", get_text(TEXT_EXIT));
|
||
printf("q: %s\n", get_text(TEXT_EXIT));
|
||
printf("%s: %s\n", get_text(TEXT_UP_ARROW), get_text(TEXT_SELECT_PREVIOUS));
|
||
printf("%s: %s\n", get_text(TEXT_DOWN_ARROW), get_text(TEXT_SELECT_NEXT));
|
||
printf("%s: %s\n", get_text(TEXT_RIGHT_ARROW), get_text(TEXT_ENTER_SUBMENU));
|
||
printf("%s: %s\n", get_text(TEXT_LEFT_ARROW), get_text(TEXT_RETURN_PARENT));
|
||
printf("==================\n");
|
||
|
||
// 初始化显示
|
||
display_menu_items();
|
||
|
||
while (1) {
|
||
// 持续处理菜单主循环,确保事件得到及时处理
|
||
menu_main_loop(demo_get_tick());
|
||
|
||
// 检查是否有按键输入
|
||
if (IS_KEY_AVAILABLE()) {
|
||
// 获取按键输入(无缓冲)
|
||
key = NON_BLOCKING_GETCHAR();
|
||
|
||
// 处理退出键
|
||
if (key == 'q' || key == 'Q') {
|
||
#ifndef _WIN32
|
||
restore_terminal();
|
||
#endif
|
||
return;
|
||
}
|
||
// 处理回车键
|
||
else if (key == '\n' || key == '\r') {
|
||
// 不管节点是否有子菜单,都处理回车事件
|
||
menu_post_event(MENU_EVENT_KEY_ENTER, 1);
|
||
|
||
// 处理菜单主循环
|
||
menu_main_loop(demo_get_tick());
|
||
|
||
// 显示当前菜单状态和菜单项
|
||
display_menu_items();
|
||
}
|
||
// 处理Esc键和箭头键
|
||
else if (key == '\x1B') {
|
||
// 读取完整的Esc序列
|
||
char esc_key = NON_BLOCKING_GETCHAR();
|
||
if (esc_key == '[') {
|
||
key = NON_BLOCKING_GETCHAR();
|
||
switch (key) {
|
||
case 'A': // 上箭头 - 选择上一项
|
||
menu_post_event(MENU_EVENT_KEY_UP, 1);
|
||
break;
|
||
case 'B': // 下箭头 - 选择下一项
|
||
menu_post_event(MENU_EVENT_KEY_DOWN, 1);
|
||
break;
|
||
case 'C': // 右箭头 - 进入子菜单
|
||
menu_post_event(MENU_EVENT_KEY_ENTER, 1);
|
||
break;
|
||
case 'D': // 左箭头 - 返回上一级菜单
|
||
menu_post_event(MENU_EVENT_KEY_ESC, 1);
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
|
||
// 处理菜单主循环
|
||
menu_main_loop(demo_get_tick());
|
||
|
||
// 显示当前菜单状态和菜单项
|
||
display_menu_items();
|
||
} else {
|
||
// 单独的Esc键 - 返回上一级菜单
|
||
menu_post_event(MENU_EVENT_KEY_ESC, 1);
|
||
|
||
// 处理菜单主循环
|
||
menu_main_loop(demo_get_tick());
|
||
|
||
// 显示当前菜单状态和菜单项
|
||
display_menu_items();
|
||
}
|
||
}
|
||
// 处理普通字符输入
|
||
else {
|
||
// 可以根据需要添加其他字符处理逻辑
|
||
}
|
||
}
|
||
|
||
// 短延迟,避免CPU占用过高
|
||
#ifdef _WIN32
|
||
Sleep(10);
|
||
#else
|
||
usleep(10000); // 10ms
|
||
#endif
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief 主函数
|
||
*/
|
||
int main(int argc, char* argv[])
|
||
{
|
||
(void)argc;
|
||
(void)argv;
|
||
|
||
// 初始化多语言系统
|
||
set_language(SYSTEM_LANG_CHINESE);
|
||
|
||
printf("%s\n", get_text(TEXT_MAIN_MENU));
|
||
printf("==================\n");
|
||
|
||
// 初始化硬件端口层
|
||
menu_port_init(&sg_demo_driver);
|
||
|
||
// 初始化菜单组件
|
||
MenuErrCode err = menu_init();
|
||
if (err != MENU_ERR_OK) {
|
||
printf("%s\n", get_text(TEXT_TEST));
|
||
return EXIT_FAILURE;
|
||
}
|
||
|
||
// 注册菜单节点
|
||
// 主菜单
|
||
MenuNodeId main_node_id = menu_register_node(0, 0, get_text(TEXT_MAIN_MENU), menu_enter_main, menu_exit_main);
|
||
|
||
// 菜单项
|
||
MenuNodeId item1_node_id = menu_register_node(0, main_node_id, get_text(TEXT_MAIN_MEASUREMENT), menu_enter_item1, NULL);
|
||
MenuNodeId item2_node_id = menu_register_node(0, main_node_id, get_text(TEXT_MAIN_PROTECTION), menu_enter_item2, NULL);
|
||
MenuNodeId item3_node_id = menu_register_node(0, main_node_id, get_text(TEXT_MAIN_CONTROL), menu_enter_item3, NULL);
|
||
MenuNodeId sub1_node_id = menu_register_node(0, main_node_id, get_text(TEXT_MAIN_SETTING), menu_enter_setting, NULL);
|
||
|
||
// 测量菜单子项
|
||
menu_register_node(0, item1_node_id, get_text(TEXT_MEASURE_VOLTAGE), NULL, NULL);
|
||
menu_register_node(0, item1_node_id, get_text(TEXT_MEASURE_CURRENT), NULL, NULL);
|
||
menu_register_node(0, item1_node_id, get_text(TEXT_MEASURE_POWER), NULL, NULL);
|
||
menu_register_node(0, item1_node_id, get_text(TEXT_MEASURE_ENERGY), NULL, NULL);
|
||
menu_register_node(0, item1_node_id, get_text(TEXT_MEASURE_FREQUENCY), NULL, NULL);
|
||
menu_register_node(0, item1_node_id, get_text(TEXT_MEASURE_TEMPERATURE), NULL, NULL);
|
||
|
||
// 保护菜单子项
|
||
menu_register_node(0, item2_node_id, get_text(TEXT_PROTECT_OVER_VOLTAGE), NULL, NULL);
|
||
menu_register_node(0, item2_node_id, get_text(TEXT_PROTECT_UNDER_VOLTAGE), NULL, NULL);
|
||
menu_register_node(0, item2_node_id, get_text(TEXT_PROTECT_OVER_CURRENT), NULL, NULL);
|
||
menu_register_node(0, item2_node_id, get_text(TEXT_PROTECT_SETTING), NULL, NULL);
|
||
|
||
// 控制菜单子项
|
||
menu_register_node(0, item3_node_id, get_text(TEXT_CONTROL_ON_OFF), NULL, NULL);
|
||
menu_register_node(0, item3_node_id, get_text(TEXT_CONTROL_START_STOP), NULL, NULL);
|
||
menu_register_node(0, item3_node_id, get_text(TEXT_CONTROL_AUTO_MANUAL), NULL, NULL);
|
||
|
||
// 设置菜单子项
|
||
menu_register_node(0, sub1_node_id, get_text(TEXT_SETTING_LANGUAGE), menu_enter_language, NULL);
|
||
|
||
// 注册持久化回调函数
|
||
err = menu_persistence_register_callback(demo_persistence_save, demo_persistence_restore);
|
||
if (err != MENU_ERR_OK) {
|
||
printf("%s: %s\n", get_text(TEXT_ERROR), get_text(TEXT_REGISTER_CALLBACK_FAILED));
|
||
}
|
||
|
||
// 尝试恢复之前的菜单状态
|
||
err = menu_persistence_restore();
|
||
if (err == MENU_ERR_OK) {
|
||
printf("%s: %s\n", get_text(TEXT_SUCCESS), get_text(TEXT_RESTORE_MENU_STATE));
|
||
} else {
|
||
printf("%s: %s: Error %d\n", get_text(TEXT_ERROR), get_text(TEXT_RESTORE_MENU_STATE_FAILED), err);
|
||
printf("%s\n", get_text(TEXT_START_DEFAULT));
|
||
}
|
||
|
||
printf("%s\n", get_text(TEXT_TEST));
|
||
|
||
// 运行菜单演示
|
||
demo_menu_navigation();
|
||
|
||
// 保存当前菜单状态
|
||
err = menu_persistence_save();
|
||
if (err == MENU_ERR_OK) {
|
||
printf("%s: %s\n", get_text(TEXT_SUCCESS), get_text(TEXT_SAVE_MENU_STATE));
|
||
} else {
|
||
printf("%s: %s: Error %d\n", get_text(TEXT_ERROR), get_text(TEXT_SAVE_MENU_STATE_FAILED), err);
|
||
}
|
||
|
||
// 清理资源
|
||
menu_deinit();
|
||
menu_port_deinit();
|
||
|
||
printf("\n%s\n", get_text(TEXT_EXIT));
|
||
return EXIT_SUCCESS;
|
||
} |