整定一版

This commit is contained in:
冯佳
2025-12-19 17:01:27 +08:00
parent 294a49f207
commit 8bddc34c88
43 changed files with 7273 additions and 956 deletions

30
demo/CMakeLists.txt Normal file
View File

@ -0,0 +1,30 @@
cmake_minimum_required(VERSION 3.10)
project(menu_demo C)
# 设置C标准
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED ON)
# 添加当前目录和上级目录作为包含目录
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_SOURCE_DIR}
)
# 收集示例所需的源文件
set(DEMO_SOURCES
demo.c
language.c
)
# 创建demo可执行程序
add_executable(menu_demo ${DEMO_SOURCES})
# 链接menu库
target_link_libraries(menu_demo PRIVATE menu)
# 安装规则
install(TARGETS menu_demo
RUNTIME DESTINATION bin
)

92
demo/build/CMakeCache.txt Normal file
View File

@ -0,0 +1,92 @@
# This is the CMakeCache file.
# For build in directory: e:/Jfen_work/local_git_code/menu/demo/build
# It was generated by CMake: C:/Users/ZHIZHANKEJI/AppData/Local/Programs/Python/Python311/Lib/site-packages/cmake/data/bin/cmake.exe
# You can edit this file to change values found and used by cmake.
# If you do not want to change any of the values, simply exit the editor.
# If you do want to change a value, simply edit, save, and exit the editor.
# The syntax for the file is as follows:
# KEY:TYPE=VALUE
# KEY is the name of a variable in the cache.
# TYPE is a hint to GUIs for the type of VALUE, DO NOT EDIT TYPE!.
# VALUE is the current value for the KEY.
########################
# EXTERNAL cache entries
########################
//Value Computed by CMake.
CMAKE_FIND_PACKAGE_REDIRECTS_DIR:STATIC=E:/Jfen_work/local_git_code/menu/demo/build/CMakeFiles/pkgRedirects
//Program used to build from makefiles.
CMAKE_MAKE_PROGRAM:STRING=nmake
//Value Computed by CMake
CMAKE_PROJECT_DESCRIPTION:STATIC=
//Value Computed by CMake
CMAKE_PROJECT_HOMEPAGE_URL:STATIC=
//Value Computed by CMake
CMAKE_PROJECT_NAME:STATIC=menu_demo
//Value Computed by CMake
demo_BINARY_DIR:STATIC=E:/Jfen_work/local_git_code/menu/demo/build
//Value Computed by CMake
demo_IS_TOP_LEVEL:STATIC=ON
//Value Computed by CMake
demo_SOURCE_DIR:STATIC=E:/Jfen_work/local_git_code/menu/demo
//Value Computed by CMake
menu_demo_BINARY_DIR:STATIC=E:/Jfen_work/local_git_code/menu/demo/build
//Value Computed by CMake
menu_demo_IS_TOP_LEVEL:STATIC=ON
//Value Computed by CMake
menu_demo_SOURCE_DIR:STATIC=E:/Jfen_work/local_git_code/menu/demo
########################
# INTERNAL cache entries
########################
//This is the directory where this CMakeCache.txt was created
CMAKE_CACHEFILE_DIR:INTERNAL=e:/Jfen_work/local_git_code/menu/demo/build
//Major version of cmake used to create the current loaded cache
CMAKE_CACHE_MAJOR_VERSION:INTERNAL=3
//Minor version of cmake used to create the current loaded cache
CMAKE_CACHE_MINOR_VERSION:INTERNAL=31
//Patch version of cmake used to create the current loaded cache
CMAKE_CACHE_PATCH_VERSION:INTERNAL=2
//Path to CMake executable.
CMAKE_COMMAND:INTERNAL=C:/Users/ZHIZHANKEJI/AppData/Local/Programs/Python/Python311/Lib/site-packages/cmake/data/bin/cmake.exe
//Path to cpack program executable.
CMAKE_CPACK_COMMAND:INTERNAL=C:/Users/ZHIZHANKEJI/AppData/Local/Programs/Python/Python311/Lib/site-packages/cmake/data/bin/cpack.exe
//Path to ctest program executable.
CMAKE_CTEST_COMMAND:INTERNAL=C:/Users/ZHIZHANKEJI/AppData/Local/Programs/Python/Python311/Lib/site-packages/cmake/data/bin/ctest.exe
//Path to cache edit program executable.
CMAKE_EDIT_COMMAND:INTERNAL=C:/Users/ZHIZHANKEJI/AppData/Local/Programs/Python/Python311/Lib/site-packages/cmake/data/bin/cmake-gui.exe
//Name of external makefile project generator.
CMAKE_EXTRA_GENERATOR:INTERNAL=
//Name of generator.
CMAKE_GENERATOR:INTERNAL=NMake Makefiles
//Generator instance identifier.
CMAKE_GENERATOR_INSTANCE:INTERNAL=
//Name of generator platform.
CMAKE_GENERATOR_PLATFORM:INTERNAL=
//Name of generator toolset.
CMAKE_GENERATOR_TOOLSET:INTERNAL=
//Source directory with the top level CMakeLists.txt file for this
// project
CMAKE_HOME_DIRECTORY:INTERNAL=E:/Jfen_work/local_git_code/menu/demo
//ADVANCED property for variable: CMAKE_MAKE_PROGRAM
CMAKE_MAKE_PROGRAM-ADVANCED:INTERNAL=1
//number of local generators
CMAKE_NUMBER_OF_MAKEFILES:INTERNAL=1
//Platform information initialized
CMAKE_PLATFORM_INFO_INITIALIZED:INTERNAL=1
//Path to CMake installation.
CMAKE_ROOT:INTERNAL=C:/Users/ZHIZHANKEJI/AppData/Local/Programs/Python/Python311/Lib/site-packages/cmake/data/share/cmake-3.31

View File

@ -0,0 +1,15 @@
set(CMAKE_HOST_SYSTEM "Windows-10.0.26200")
set(CMAKE_HOST_SYSTEM_NAME "Windows")
set(CMAKE_HOST_SYSTEM_VERSION "10.0.26200")
set(CMAKE_HOST_SYSTEM_PROCESSOR "AMD64")
set(CMAKE_SYSTEM "Windows-10.0.26200")
set(CMAKE_SYSTEM_NAME "Windows")
set(CMAKE_SYSTEM_VERSION "10.0.26200")
set(CMAKE_SYSTEM_PROCESSOR "AMD64")
set(CMAKE_CROSSCOMPILING "FALSE")
set(CMAKE_SYSTEM_LOADED 1)

View File

@ -0,0 +1,11 @@
---
events:
-
kind: "message-v1"
backtrace:
- "C:/Users/ZHIZHANKEJI/AppData/Local/Programs/Python/Python311/Lib/site-packages/cmake/data/share/cmake-3.31/Modules/CMakeDetermineSystem.cmake:205 (message)"
- "CMakeLists.txt:3 (project)"
message: |
The system is: Windows - 10.0.26200 - AMD64
...

View File

@ -0,0 +1 @@
# This file is generated by cmake for dependency checking of the CMakeCache.txt file

681
demo/demo.c Normal file
View File

@ -0,0 +1,681 @@
/**
**********************************************************************************************************************
* @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;
}

525
demo/language.c Normal file
View File

@ -0,0 +1,525 @@
#include "language.h"
/* 全局变量定义 ---------------------------------------------------------------------------------------------------*/
/**
* @brief 当前系统语言
*/
static SystemLang_e sg_current_language = SYSTEM_LANG_CHINESE;
/**
* @brief 中文文本数组
*/
static const char *sg_chinese_text[] = {
[TEXT_ENTER] = "进入",
[TEXT_EXIT] = "退出",
[TEXT_RETURN] = "返回",
[TEXT_OK] = "确定",
[TEXT_CANCEL] = "取消",
[TEXT_SAVE] = "保存",
[TEXT_LOAD] = "加载",
[TEXT_MAIN_MENU] = "主菜单",
[TEXT_MAIN_MEASUREMENT] = "测量",
[TEXT_MAIN_PROTECTION] = "保护",
[TEXT_MAIN_CONTROL] = "控制",
[TEXT_MAIN_SETTING] = "设置",
[TEXT_MAIN_RECORD] = "记录",
[TEXT_MAIN_HELP] = "帮助",
[TEXT_MAIN_ABOUT] = "关于",
[TEXT_MEASURE_VOLTAGE] = "电压",
[TEXT_MEASURE_CURRENT] = "电流",
[TEXT_MEASURE_POWER] = "功率",
[TEXT_MEASURE_ENERGY] = "能量",
[TEXT_MEASURE_FREQUENCY] = "频率",
[TEXT_MEASURE_TEMPERATURE] = "温度",
[TEXT_PROTECT_OVER_VOLTAGE] = "过电压保护",
[TEXT_PROTECT_UNDER_VOLTAGE] = "欠电压保护",
[TEXT_PROTECT_OVER_CURRENT] = "过电流保护",
[TEXT_PROTECT_SETTING] = "保护设置",
[TEXT_CONTROL_ON_OFF] = "开关控制",
[TEXT_CONTROL_START_STOP] = "启停控制",
[TEXT_CONTROL_AUTO_MANUAL] = "自动/手动",
[TEXT_SETTING_LANGUAGE] = "语言设置",
[TEXT_RECORD_EVENT] = "事件记录",
[TEXT_HELP_GUIDE] = "指南",
[TEXT_ABOUT_VERSION] = "版本信息",
[TEXT_PERSIST_SAVE_STATE] = "保存菜单状态",
[TEXT_PERSIST_RESTORE_STATE] = "恢复菜单状态",
[TEXT_PERSIST_SAVE_SUCCESS] = "保存成功",
[TEXT_PERSIST_SAVE_FAILED] = "保存失败",
[TEXT_PERSIST_RESTORE_SUCCESS] = "恢复成功",
[TEXT_PERSIST_RESTORE_FAILED] = "恢复失败",
[TEXT_ERROR] = "错误",
[TEXT_WARNING] = "警告",
[TEXT_SUCCESS] = "成功",
[TEXT_INFO] = "信息",
[TEXT_TEST] = "测试",
[TEXT_SELECT_OPTION] = "选择操作",
[TEXT_CURRENT] = "当前",
[TEXT_STATUS] = "状态",
[TEXT_SUMMARY] = "摘要",
[TEXT_EMPTY] = "",
[TEXT_CURRENT_SELECTION] = "当前选择",
[TEXT_OPERATIONS] = "操作",
[TEXT_SELECT_ITEM] = "选择项",
[TEXT_ENTER_SUBMENU] = "进入子菜单",
[TEXT_RETURN_PARENT] = "返回父菜单",
[TEXT_SELECT_PREVIOUS] = "选择上一项",
[TEXT_SELECT_NEXT] = "选择下一项",
[TEXT_START_DEFAULT] = "从默认开始",
[TEXT_REGISTER_CALLBACK_FAILED] = "注册回调失败",
[TEXT_RESTORE_MENU_STATE] = "恢复菜单状态",
[TEXT_RESTORE_MENU_STATE_FAILED] = "恢复菜单状态失败",
[TEXT_SAVE_MENU_STATE_FAILED] = "保存菜单状态失败",
[TEXT_UP_ARROW] = "上箭头",
[TEXT_DOWN_ARROW] = "下箭头",
[TEXT_LEFT_ARROW] = "左箭头",
[TEXT_RIGHT_ARROW] = "右箭头",
};
/**
* @brief 英文文本数组
*/
static const char *sg_english_text[] = {
[TEXT_ENTER] = "Enter",
[TEXT_EXIT] = "Exit",
[TEXT_RETURN] = "Return",
[TEXT_OK] = "OK",
[TEXT_CANCEL] = "Cancel",
[TEXT_SAVE] = "Save",
[TEXT_LOAD] = "Load",
[TEXT_MAIN_MENU] = "Main Menu",
[TEXT_MAIN_MEASUREMENT] = "Measurement",
[TEXT_MAIN_PROTECTION] = "Protection",
[TEXT_MAIN_CONTROL] = "Control",
[TEXT_MAIN_SETTING] = "Setting",
[TEXT_MAIN_RECORD] = "Record",
[TEXT_MAIN_HELP] = "Help",
[TEXT_MAIN_ABOUT] = "About",
[TEXT_MEASURE_VOLTAGE] = "Voltage",
[TEXT_MEASURE_CURRENT] = "Current",
[TEXT_MEASURE_POWER] = "Power",
[TEXT_MEASURE_ENERGY] = "Energy",
[TEXT_MEASURE_FREQUENCY] = "Frequency",
[TEXT_MEASURE_TEMPERATURE] = "Temperature",
[TEXT_PROTECT_OVER_VOLTAGE] = "Over Voltage Protection",
[TEXT_PROTECT_UNDER_VOLTAGE] = "Under Voltage Protection",
[TEXT_PROTECT_OVER_CURRENT] = "Over Current Protection",
[TEXT_PROTECT_SETTING] = "Protection Setting",
[TEXT_CONTROL_ON_OFF] = "On/Off",
[TEXT_CONTROL_START_STOP] = "Start/Stop",
[TEXT_CONTROL_AUTO_MANUAL] = "Auto/Manual",
[TEXT_SETTING_LANGUAGE] = "Language Setting",
[TEXT_RECORD_EVENT] = "Event Record",
[TEXT_HELP_GUIDE] = "Guide",
[TEXT_ABOUT_VERSION] = "Version Info",
[TEXT_PERSIST_SAVE_STATE] = "Save Menu State",
[TEXT_PERSIST_RESTORE_STATE] = "Restore Menu State",
[TEXT_PERSIST_SAVE_SUCCESS] = "Save Success",
[TEXT_PERSIST_SAVE_FAILED] = "Save Failed",
[TEXT_PERSIST_RESTORE_SUCCESS] = "Restore Success",
[TEXT_PERSIST_RESTORE_FAILED] = "Restore Failed",
[TEXT_ERROR] = "Error",
[TEXT_WARNING] = "Warning",
[TEXT_SUCCESS] = "Success",
[TEXT_INFO] = "Info",
[TEXT_TEST] = "Test",
[TEXT_SELECT_OPTION] = "Select Option",
[TEXT_CURRENT] = "Current",
[TEXT_STATUS] = "Status",
[TEXT_SUMMARY] = "Summary",
[TEXT_EMPTY] = "Empty",
[TEXT_CURRENT_SELECTION] = "Current Selection",
[TEXT_OPERATIONS] = "Operations",
[TEXT_SELECT_ITEM] = "Select Item",
[TEXT_ENTER_SUBMENU] = "Enter Submenu",
[TEXT_RETURN_PARENT] = "Return Parent",
[TEXT_SELECT_PREVIOUS] = "Select Previous",
[TEXT_SELECT_NEXT] = "Select Next",
[TEXT_START_DEFAULT] = "Start Default",
[TEXT_REGISTER_CALLBACK_FAILED] = "Register Callback Failed",
[TEXT_RESTORE_MENU_STATE] = "Restore Menu State",
[TEXT_RESTORE_MENU_STATE_FAILED] = "Restore Menu State Failed",
[TEXT_SAVE_MENU_STATE_FAILED] = "Save Menu State Failed",
[TEXT_UP_ARROW] = "Up Arrow",
[TEXT_DOWN_ARROW] = "Down Arrow",
[TEXT_LEFT_ARROW] = "Left Arrow",
[TEXT_RIGHT_ARROW] = "Right Arrow",
};
/**
* @brief 日语文本数组
*/
static const char *sg_japanese_text[] = {
[TEXT_ENTER] = "入る",
[TEXT_EXIT] = "退出",
[TEXT_RETURN] = "戻る",
[TEXT_OK] = "OK",
[TEXT_CANCEL] = "キャンセル",
[TEXT_SAVE] = "保存",
[TEXT_LOAD] = "読み込む",
[TEXT_MAIN_MENU] = "メインメニュー",
[TEXT_MAIN_MEASUREMENT] = "計測",
[TEXT_MAIN_PROTECTION] = "保護",
[TEXT_MAIN_CONTROL] = "制御",
[TEXT_MAIN_SETTING] = "設定",
[TEXT_MAIN_RECORD] = "記録",
[TEXT_MAIN_HELP] = "ヘルプ",
[TEXT_MAIN_ABOUT] = "について",
[TEXT_MEASURE_VOLTAGE] = "電圧",
[TEXT_MEASURE_CURRENT] = "電流",
[TEXT_MEASURE_POWER] = "電力",
[TEXT_MEASURE_ENERGY] = "エネルギー",
[TEXT_MEASURE_FREQUENCY] = "周波数",
[TEXT_MEASURE_TEMPERATURE] = "温度",
[TEXT_PROTECT_OVER_VOLTAGE] = "過電圧保護",
[TEXT_PROTECT_UNDER_VOLTAGE] = "不足電圧保護",
[TEXT_PROTECT_OVER_CURRENT] = "過電流保護",
[TEXT_PROTECT_SETTING] = "保護設定",
[TEXT_CONTROL_ON_OFF] = "オン/オフ",
[TEXT_CONTROL_START_STOP] = "スタート/ストップ",
[TEXT_CONTROL_AUTO_MANUAL] = "オート/マニュアル",
[TEXT_SETTING_LANGUAGE] = "言語設定",
[TEXT_RECORD_EVENT] = "イベント記録",
[TEXT_HELP_GUIDE] = "ガイド",
[TEXT_ABOUT_VERSION] = "バージョン情報",
[TEXT_PERSIST_SAVE_STATE] = "メニュー状態を保存",
[TEXT_PERSIST_RESTORE_STATE] = "メニュー状態を復元",
[TEXT_PERSIST_SAVE_SUCCESS] = "保存成功",
[TEXT_PERSIST_SAVE_FAILED] = "保存失敗",
[TEXT_PERSIST_RESTORE_SUCCESS] = "復元成功",
[TEXT_PERSIST_RESTORE_FAILED] = "復元失敗",
[TEXT_ERROR] = "エラー",
[TEXT_WARNING] = "警告",
[TEXT_SUCCESS] = "成功",
[TEXT_INFO] = "情報",
[TEXT_TEST] = "テスト",
[TEXT_SELECT_OPTION] = "オプションを選択",
[TEXT_CURRENT] = "現在",
[TEXT_STATUS] = "ステータス",
[TEXT_SUMMARY] = "サマリー",
[TEXT_EMPTY] = "",
[TEXT_CURRENT_SELECTION] = "現在の選択",
[TEXT_OPERATIONS] = "操作",
[TEXT_SELECT_ITEM] = "項目を選択",
[TEXT_ENTER_SUBMENU] = "サブメニューに入る",
[TEXT_RETURN_PARENT] = "親メニューに戻る",
[TEXT_SELECT_PREVIOUS] = "前の項目を選択",
[TEXT_SELECT_NEXT] = "次の項目を選択",
[TEXT_START_DEFAULT] = "デフォルトから開始",
[TEXT_REGISTER_CALLBACK_FAILED] = "コールバック登録失敗",
[TEXT_RESTORE_MENU_STATE] = "メニュー状態を復元",
[TEXT_RESTORE_MENU_STATE_FAILED] = "メニュー状態復元失敗",
[TEXT_SAVE_MENU_STATE_FAILED] = "メニュー状態保存失敗",
[TEXT_UP_ARROW] = "上矢印",
[TEXT_DOWN_ARROW] = "下矢印",
[TEXT_LEFT_ARROW] = "左矢印",
[TEXT_RIGHT_ARROW] = "右矢印",
};
/**
* @brief 德语文本数组
*/
static const char *sg_german_text[] = {
[TEXT_ENTER] = "Eingeben",
[TEXT_EXIT] = "Verlassen",
[TEXT_RETURN] = "Zurück",
[TEXT_OK] = "OK",
[TEXT_CANCEL] = "Abbrechen",
[TEXT_SAVE] = "Speichern",
[TEXT_LOAD] = "Laden",
[TEXT_MAIN_MENU] = "Hauptmenü",
[TEXT_MAIN_MEASUREMENT] = "Messung",
[TEXT_MAIN_PROTECTION] = "Schutz",
[TEXT_MAIN_CONTROL] = "Steuerung",
[TEXT_MAIN_SETTING] = "Einstellung",
[TEXT_MAIN_RECORD] = "Aufzeichnung",
[TEXT_MAIN_HELP] = "Hilfe",
[TEXT_MAIN_ABOUT] = "Über",
[TEXT_MEASURE_VOLTAGE] = "Spannung",
[TEXT_MEASURE_CURRENT] = "Strom",
[TEXT_MEASURE_POWER] = "Leistung",
[TEXT_MEASURE_ENERGY] = "Energie",
[TEXT_MEASURE_FREQUENCY] = "Frequenz",
[TEXT_MEASURE_TEMPERATURE] = "Temperatur",
[TEXT_PROTECT_OVER_VOLTAGE] = "Überspannungsschutz",
[TEXT_PROTECT_UNDER_VOLTAGE] = "Unterspannungsschutz",
[TEXT_PROTECT_OVER_CURRENT] = "Überstromschutz",
[TEXT_PROTECT_SETTING] = "Schutz-Einstellung",
[TEXT_CONTROL_ON_OFF] = "Ein/Aus",
[TEXT_CONTROL_START_STOP] = "Start/Stopp",
[TEXT_CONTROL_AUTO_MANUAL] = "Auto/Manuell",
[TEXT_SETTING_LANGUAGE] = "Spracheinstellung",
[TEXT_RECORD_EVENT] = "Ereignisaufzeichnung",
[TEXT_HELP_GUIDE] = "Anleitung",
[TEXT_ABOUT_VERSION] = "Versionsinfo",
[TEXT_PERSIST_SAVE_STATE] = "Menüzustand speichern",
[TEXT_PERSIST_RESTORE_STATE] = "Menüzustand wiederherstellen",
[TEXT_PERSIST_SAVE_SUCCESS] = "Speichern erfolgreich",
[TEXT_PERSIST_SAVE_FAILED] = "Speichern fehlgeschlagen",
[TEXT_PERSIST_RESTORE_SUCCESS] = "Wiederherstellen erfolgreich",
[TEXT_PERSIST_RESTORE_FAILED] = "Wiederherstellen fehlgeschlagen",
[TEXT_ERROR] = "Fehler",
[TEXT_WARNING] = "Warnung",
[TEXT_SUCCESS] = "Erfolg",
[TEXT_INFO] = "Info",
[TEXT_TEST] = "Test",
[TEXT_SELECT_OPTION] = "Option wählen",
[TEXT_CURRENT] = "Aktuell",
[TEXT_STATUS] = "Status",
[TEXT_SUMMARY] = "Zusammenfassung",
[TEXT_EMPTY] = "Leer",
[TEXT_CURRENT_SELECTION] = "Aktuelle Auswahl",
[TEXT_OPERATIONS] = "Operationen",
[TEXT_SELECT_ITEM] = "Element auswählen",
[TEXT_ENTER_SUBMENU] = "In Untermenü eintreten",
[TEXT_RETURN_PARENT] = "Zu übergeordnetem Menü zurückkehren",
[TEXT_SELECT_PREVIOUS] = "Vorheriges auswählen",
[TEXT_SELECT_NEXT] = "Nächstes auswählen",
[TEXT_START_DEFAULT] = "Standard starten",
[TEXT_REGISTER_CALLBACK_FAILED] = "Rückrufregistrierung fehlgeschlagen",
[TEXT_RESTORE_MENU_STATE] = "Menüzustand wiederherstellen",
[TEXT_RESTORE_MENU_STATE_FAILED] = "Wiederherstellung des Menüzustands fehlgeschlagen",
[TEXT_SAVE_MENU_STATE_FAILED] = "Speichern des Menüzustands fehlgeschlagen",
[TEXT_UP_ARROW] = "Pfeil nach oben",
[TEXT_DOWN_ARROW] = "Pfeil nach unten",
[TEXT_LEFT_ARROW] = "Pfeil nach links",
[TEXT_RIGHT_ARROW] = "Pfeil nach rechts",
};
/**
* @brief 语言文本映射表
*/
static const char **sg_language_text_map[] = {
[SYSTEM_LANG_CHINESE] = sg_chinese_text,
[SYSTEM_LANG_ENGLISH] = sg_english_text,
[SYSTEM_LANG_JAPANESE] = sg_japanese_text,
[SYSTEM_LANG_GERMAN] = sg_german_text,
};
/**
* @brief 全局菜单节点映射表
*/
const MenuNodeInfo_t menu_node_map[] = {
// 一级菜单
{MENU_NODE_ID_MAIN, MENU_NODE_ID_NONE, TEXT_MAIN_MENU, 1, MENU_NODE_ATTR_HAS_CHILDREN | MENU_NODE_ATTR_VISIBLE},
{MENU_NODE_ID_MEASUREMENT, MENU_NODE_ID_MAIN, TEXT_MAIN_MEASUREMENT, 1, MENU_NODE_ATTR_HAS_CHILDREN | MENU_NODE_ATTR_VISIBLE},
{MENU_NODE_ID_PROTECTION, MENU_NODE_ID_MAIN, TEXT_MAIN_PROTECTION, 1, MENU_NODE_ATTR_HAS_CHILDREN | MENU_NODE_ATTR_VISIBLE},
{MENU_NODE_ID_CONTROL, MENU_NODE_ID_MAIN, TEXT_MAIN_CONTROL, 1, MENU_NODE_ATTR_OPERABLE | MENU_NODE_ATTR_VISIBLE},
{MENU_NODE_ID_SETTING, MENU_NODE_ID_MAIN, TEXT_MAIN_SETTING, 1, MENU_NODE_ATTR_HAS_CHILDREN | MENU_NODE_ATTR_VISIBLE},
{MENU_NODE_ID_RECORD, MENU_NODE_ID_MAIN, TEXT_MAIN_RECORD, 1, MENU_NODE_ATTR_HAS_CHILDREN | MENU_NODE_ATTR_VISIBLE},
{MENU_NODE_ID_HELP, MENU_NODE_ID_MAIN, TEXT_MAIN_HELP, 1, MENU_NODE_ATTR_HAS_CHILDREN | MENU_NODE_ATTR_VISIBLE},
{MENU_NODE_ID_ABOUT, MENU_NODE_ID_MAIN, TEXT_MAIN_ABOUT, 1, MENU_NODE_ATTR_HAS_CHILDREN | MENU_NODE_ATTR_VISIBLE},
// 二级菜单:测量菜单子项
{MENU_NODE_ID_MEASUREMENT_VOLTAGE, MENU_NODE_ID_MEASUREMENT, TEXT_MEASURE_VOLTAGE, 2, MENU_NODE_ATTR_OPERABLE | MENU_NODE_ATTR_VISIBLE},
{MENU_NODE_ID_MEASUREMENT_CURRENT, MENU_NODE_ID_MEASUREMENT, TEXT_MEASURE_CURRENT, 2, MENU_NODE_ATTR_OPERABLE | MENU_NODE_ATTR_VISIBLE},
{MENU_NODE_ID_MEASUREMENT_POWER, MENU_NODE_ID_MEASUREMENT, TEXT_MEASURE_POWER, 2, MENU_NODE_ATTR_OPERABLE | MENU_NODE_ATTR_VISIBLE},
{MENU_NODE_ID_MEASUREMENT_ENERGY, MENU_NODE_ID_MEASUREMENT, TEXT_MEASURE_ENERGY, 2, MENU_NODE_ATTR_OPERABLE | MENU_NODE_ATTR_VISIBLE},
{MENU_NODE_ID_MEASUREMENT_FREQUENCY, MENU_NODE_ID_MEASUREMENT, TEXT_MEASURE_FREQUENCY, 2, MENU_NODE_ATTR_OPERABLE | MENU_NODE_ATTR_VISIBLE},
{MENU_NODE_ID_MEASUREMENT_TEMPERATURE, MENU_NODE_ID_MEASUREMENT, TEXT_MEASURE_TEMPERATURE, 2, MENU_NODE_ATTR_OPERABLE | MENU_NODE_ATTR_VISIBLE},
// 二级菜单:保护菜单子项
{MENU_NODE_ID_PROTECTION_OVER_VOLTAGE, MENU_NODE_ID_PROTECTION, TEXT_PROTECT_OVER_VOLTAGE, 2, MENU_NODE_ATTR_OPERABLE | MENU_NODE_ATTR_VISIBLE},
{MENU_NODE_ID_PROTECTION_UNDER_VOLTAGE, MENU_NODE_ID_PROTECTION, TEXT_PROTECT_UNDER_VOLTAGE, 2, MENU_NODE_ATTR_OPERABLE | MENU_NODE_ATTR_VISIBLE},
{MENU_NODE_ID_PROTECTION_OVER_CURRENT, MENU_NODE_ID_PROTECTION, TEXT_PROTECT_OVER_CURRENT, 2, MENU_NODE_ATTR_OPERABLE | MENU_NODE_ATTR_VISIBLE},
{MENU_NODE_ID_PROTECTION_SETTING, MENU_NODE_ID_PROTECTION, TEXT_PROTECT_SETTING, 2, MENU_NODE_ATTR_OPERABLE | MENU_NODE_ATTR_VISIBLE},
// 二级菜单:控制菜单子项
{MENU_NODE_ID_CONTROL_ON_OFF, MENU_NODE_ID_CONTROL, TEXT_CONTROL_ON_OFF, 2, MENU_NODE_ATTR_OPERABLE | MENU_NODE_ATTR_VISIBLE},
{MENU_NODE_ID_CONTROL_START_STOP, MENU_NODE_ID_CONTROL, TEXT_CONTROL_START_STOP, 2, MENU_NODE_ATTR_OPERABLE | MENU_NODE_ATTR_VISIBLE},
{MENU_NODE_ID_CONTROL_AUTO_MANUAL, MENU_NODE_ID_CONTROL, TEXT_CONTROL_AUTO_MANUAL, 2, MENU_NODE_ATTR_OPERABLE | MENU_NODE_ATTR_VISIBLE},
// 二级菜单:其他子项
{MENU_NODE_ID_SETTING_LANGUAGE, MENU_NODE_ID_SETTING, TEXT_SETTING_LANGUAGE, 2, MENU_NODE_ATTR_OPERABLE | MENU_NODE_ATTR_VISIBLE},
{MENU_NODE_ID_RECORD_EVENT, MENU_NODE_ID_RECORD, TEXT_RECORD_EVENT, 2, MENU_NODE_ATTR_OPERABLE | MENU_NODE_ATTR_VISIBLE},
{MENU_NODE_ID_HELP_GUIDE, MENU_NODE_ID_HELP, TEXT_HELP_GUIDE, 2, MENU_NODE_ATTR_OPERABLE | MENU_NODE_ATTR_VISIBLE},
{MENU_NODE_ID_ABOUT_VERSION, MENU_NODE_ID_ABOUT, TEXT_ABOUT_VERSION, 2, MENU_NODE_ATTR_OPERABLE | MENU_NODE_ATTR_VISIBLE},
};
/**
* @brief 菜单节点映射表大小
*/
const uint32_t menu_node_map_size = sizeof(menu_node_map) / sizeof(menu_node_map[0]);
/* 函数实现 ---------------------------------------------------------------------------------------------------------*/
/**
* @brief 初始化语言模块
*/
MenuErrCode language_init(void)
{
// 初始化核心菜单库语言模块
MenuErrCode ret = menu_lang_init();
if (ret != MENU_ERR_OK)
{
return ret;
}
// 设置默认语言
return set_language(SYSTEM_LANG_CHINESE);
}
/**
* @brief 根据文本ID获取对应语言的文本字符串
*/
const char* get_text(TextId_e text_id)
{
if (text_id >= TEXT_MAX)
{
return NULL;
}
if (sg_current_language >= SYSTEM_LANG_MAX)
{
return NULL;
}
return sg_language_text_map[sg_current_language][text_id];
}
/**
* @brief 设置当前系统语言
*/
MenuErrCode set_language(SystemLang_e lang)
{
if (lang >= SYSTEM_LANG_MAX)
{
return MENU_ERR_INVALID_PARAM;
}
sg_current_language = lang;
// 同步到核心菜单库
return menu_lang_set_current((LangId)lang);
}
/**
* @brief 获取当前系统语言
*/
SystemLang_e get_current_language(void)
{
return sg_current_language;
}
/**
* @brief 根据菜单节点ID获取对应的文本ID
*/
TextId_e get_menu_text_id(MenuNodeId_e node_id)
{
for (uint32_t i = 0; i < menu_node_map_size; i++)
{
if (menu_node_map[i].node_id == node_id)
{
return menu_node_map[i].text_id;
}
}
return TEXT_MAX;
}
/**
* @brief 根据菜单节点ID获取对应的父节点ID
*/
MenuNodeId_e get_menu_parent_id(MenuNodeId_e node_id)
{
for (uint32_t i = 0; i < menu_node_map_size; i++)
{
if (menu_node_map[i].node_id == node_id)
{
return menu_node_map[i].parent_id;
}
}
return MENU_NODE_ID_NONE;
}
/**
* @brief 检查菜单节点是否有子节点
*/
uint8_t menu_node_has_children(MenuNodeId_e node_id)
{
for (uint32_t i = 0; i < menu_node_map_size; i++)
{
if (menu_node_map[i].node_id == node_id)
{
return MENU_NODE_HAS_ATTR(menu_node_map[i].attributes, MENU_NODE_ATTR_HAS_CHILDREN);
}
}
return 0;
}
/**
* @brief 检查菜单节点是否可操作
*/
uint8_t menu_node_is_operable(MenuNodeId_e node_id)
{
for (uint32_t i = 0; i < menu_node_map_size; i++)
{
if (menu_node_map[i].node_id == node_id)
{
return MENU_NODE_HAS_ATTR(menu_node_map[i].attributes, MENU_NODE_ATTR_OPERABLE);
}
}
return 0;
}
/**
* @brief 获取菜单节点的显示文本
*/
const char* get_menu_node_text(MenuNodeId_e node_id)
{
TextId_e text_id = get_menu_text_id(node_id);
if (text_id == TEXT_MAX)
{
return NULL;
}
return get_text(text_id);
}

311
demo/language.h Normal file
View File

@ -0,0 +1,311 @@
#ifndef LANGUAGE_H
#define LANGUAGE_H
/* Includes ----------------------------------------------------------------------------------------------------------*/
#include "../../src/lang/menu_lang.h"
#include "../../src/core/menu_core.h"
/* 类型定义 ---------------------------------------------------------------------------------------------------------*/
/**
* @brief 系统语言枚举
* @note 与核心菜单库语言ID映射便于扩展
*/
typedef enum
{
SYSTEM_LANG_CHINESE = 0, // 中文(默认)
SYSTEM_LANG_ENGLISH, // 英文
SYSTEM_LANG_JAPANESE, // 日语
SYSTEM_LANG_GERMAN, // 德语
SYSTEM_LANG_MAX, // 语言类型总数(仅用于遍历/边界判断)
} SystemLang_e;
/**
* @brief 文本ID枚举按模块分级
* @note 采用模块化命名,便于扩展和维护
* 分级规则:
* - 基础文本(0x0000~0x00FF)
* - 主菜单(0x0100~0x01FF)
* - 测量菜单(0x0200~0x02FF)
* - 保护菜单(0x0300~0x03FF)
* - 控制菜单(0x0400~0x04FF)
* - 设置菜单(0x0500~0x05FF)
* - 记录菜单(0x0600~0x06FF)
* - 帮助菜单(0x0700~0x07FF)
* - 关于菜单(0x0800~0x08FF)
* - 持久化相关(0x0900~0x09FF)
*/
typedef enum
{
// 基础文本0x0000 ~ 0x00FF
TEXT_ENTER = 0x0000, // 进入
TEXT_EXIT, // 退出
TEXT_RETURN, // 返回
TEXT_OK, // 确定
TEXT_CANCEL, // 取消
TEXT_SAVE, // 保存
TEXT_LOAD, // 加载
// 主菜单文本0x0100 ~ 0x01FF
TEXT_MAIN_MENU = 0x0100, // 主菜单
TEXT_MAIN_MEASUREMENT, // 测量
TEXT_MAIN_PROTECTION, // 保护
TEXT_MAIN_CONTROL, // 控制
TEXT_MAIN_SETTING, // 设置
TEXT_MAIN_RECORD, // 记录
TEXT_MAIN_HELP, // 帮助
TEXT_MAIN_ABOUT, // 关于
// 测量菜单文本0x0200 ~ 0x02FF
TEXT_MEASURE_VOLTAGE = 0x0200, // 电压
TEXT_MEASURE_CURRENT, // 电流
TEXT_MEASURE_POWER, // 功率
TEXT_MEASURE_ENERGY, // 能量
TEXT_MEASURE_FREQUENCY, // 频率
TEXT_MEASURE_TEMPERATURE, // 温度
// 保护菜单文本0x0300 ~ 0x03FF
TEXT_PROTECT_OVER_VOLTAGE = 0x0300, // 过电压保护
TEXT_PROTECT_UNDER_VOLTAGE, // 欠电压保护
TEXT_PROTECT_OVER_CURRENT, // 过电流保护
TEXT_PROTECT_SETTING, // 保护设置
// 控制菜单文本0x0400 ~ 0x04FF
TEXT_CONTROL_ON_OFF = 0x0400, // 开关控制
TEXT_CONTROL_START_STOP, // 启停控制
TEXT_CONTROL_AUTO_MANUAL, // 自动/手动切换
// 设置菜单文本0x0500 ~ 0x05FF
TEXT_SETTING_LANGUAGE = 0x0500, // 语言设置
// 记录菜单文本0x0600 ~ 0x06FF
TEXT_RECORD_EVENT = 0x0600, // 事件记录
// 帮助菜单文本0x0700 ~ 0x07FF
TEXT_HELP_GUIDE = 0x0700, // 指南
// 关于菜单文本0x0800 ~ 0x08FF
TEXT_ABOUT_VERSION = 0x0800, // 版本信息
// 持久化相关文本0x0900 ~ 0x09FF
TEXT_PERSIST_SAVE_STATE = 0x0900, // 保存菜单状态
TEXT_PERSIST_RESTORE_STATE, // 恢复菜单状态
TEXT_PERSIST_SAVE_SUCCESS, // 保存成功
TEXT_PERSIST_SAVE_FAILED, // 保存失败
TEXT_PERSIST_RESTORE_SUCCESS, // 恢复成功
TEXT_PERSIST_RESTORE_FAILED, // 恢复失败
// 提示信息0x0A00 ~ 0x0AFF
TEXT_ERROR = 0x0A00, // 错误
TEXT_WARNING, // 警告
TEXT_SUCCESS, // 成功
TEXT_INFO, // 信息
TEXT_TEST, // 测试
TEXT_SELECT_OPTION, // 选择操作
TEXT_CURRENT, // 当前
TEXT_STATUS, // 状态
TEXT_SUMMARY, // 摘要
TEXT_EMPTY, // 空
TEXT_CURRENT_SELECTION, // 当前选择
TEXT_OPERATIONS, // 操作
TEXT_SELECT_ITEM, // 选择项
TEXT_ENTER_SUBMENU, // 进入子菜单
TEXT_RETURN_PARENT, // 返回父菜单
TEXT_SELECT_PREVIOUS, // 选择上一项
TEXT_SELECT_NEXT, // 选择下一项
TEXT_START_DEFAULT, // 从默认开始
TEXT_REGISTER_CALLBACK_FAILED, // 注册回调失败
TEXT_RESTORE_MENU_STATE, // 恢复菜单状态
TEXT_RESTORE_MENU_STATE_FAILED, // 恢复菜单状态失败
TEXT_SAVE_MENU_STATE_FAILED, // 保存菜单状态失败
TEXT_SAVE_MENU_STATE, // 保存菜单状态
// 操作提示0x0B00 ~ 0x0BFF
TEXT_UP_ARROW = 0x0B00, // 上箭头
TEXT_DOWN_ARROW, // 下箭头
TEXT_LEFT_ARROW, // 左箭头
TEXT_RIGHT_ARROW, // 右箭头
TEXT_MAX = 0x0FFF, // 所有文本ID最大值用于边界检查
} TextId_e;
/**
* @brief 菜单节点ID枚举
* @note 采用模块化命名,便于扩展和维护
* 分级规则:
* - 一级菜单(0x1000~0x1FFF)
* - 二级菜单(0x2000~0x2FFF)
* - 三级菜单(0x3000~0x3FFF)
* - 四级菜单(0x4000~0x4FFF)
*/
typedef enum
{
// 无效节点/根节点标记
MENU_NODE_ID_NONE = 0x0000,
// 一级菜单0x1000 ~ 0x1FFF
MENU_NODE_ID_MAIN = 0x1000, // 主菜单
MENU_NODE_ID_MEASUREMENT, // 测量菜单
MENU_NODE_ID_PROTECTION, // 保护菜单
MENU_NODE_ID_CONTROL, // 控制菜单
MENU_NODE_ID_SETTING, // 设置菜单
MENU_NODE_ID_RECORD, // 记录菜单
MENU_NODE_ID_HELP, // 帮助菜单
MENU_NODE_ID_ABOUT, // 关于菜单
// 二级菜单测量菜单子项0x2000 ~ 0x20FF
MENU_NODE_ID_MEASUREMENT_VOLTAGE = 0x2000, // 电压测量
MENU_NODE_ID_MEASUREMENT_CURRENT, // 电流测量
MENU_NODE_ID_MEASUREMENT_POWER, // 功率测量
MENU_NODE_ID_MEASUREMENT_ENERGY, // 能量测量
MENU_NODE_ID_MEASUREMENT_FREQUENCY, // 频率测量
MENU_NODE_ID_MEASUREMENT_TEMPERATURE, // 温度测量
// 二级菜单保护菜单子项0x2100 ~ 0x21FF
MENU_NODE_ID_PROTECTION_OVER_VOLTAGE = 0x2100, // 过电压保护
MENU_NODE_ID_PROTECTION_UNDER_VOLTAGE, // 欠电压保护
MENU_NODE_ID_PROTECTION_OVER_CURRENT, // 过电流保护
MENU_NODE_ID_PROTECTION_SETTING, // 保护设置
// 二级菜单控制菜单子项0x2200 ~ 0x22FF
MENU_NODE_ID_CONTROL_ON_OFF = 0x2200, // 开关控制
MENU_NODE_ID_CONTROL_START_STOP, // 启停控制
MENU_NODE_ID_CONTROL_AUTO_MANUAL, // 自动/手动切换
// 二级菜单设置菜单子项0x2300 ~ 0x23FF
MENU_NODE_ID_SETTING_LANGUAGE = 0x2300, // 语言设置
// 二级菜单记录菜单子项0x2400 ~ 0x24FF
MENU_NODE_ID_RECORD_EVENT = 0x2400, // 事件记录
// 二级菜单帮助菜单子项0x2500 ~ 0x25FF
MENU_NODE_ID_HELP_GUIDE = 0x2500, // 指南
// 二级菜单关于菜单子项0x2600 ~ 0x26FF
MENU_NODE_ID_ABOUT_VERSION = 0x2600, // 版本信息
MENU_NODE_ID_MAX = 0x4FFF, // 菜单节点ID最大值
} MenuNodeId_e;
/**
* @brief 菜单节点属性枚举
* @note 使用位掩码方式,便于组合和扩展
*/
typedef enum
{
MENU_NODE_ATTR_NONE = 0x00, // 无属性
MENU_NODE_ATTR_HAS_CHILDREN = 0x01, // 有子节点
MENU_NODE_ATTR_OPERABLE = 0x02, // 可操作
MENU_NODE_ATTR_VISIBLE = 0x04, // 可见
MENU_NODE_ATTR_EDITABLE = 0x08, // 可编辑
} MenuNodeAttr_e;
/**
* @brief 菜单节点信息结构体
* @note 用于建立菜单节点ID与文本ID、父子关系、属性等映射
* 采用更紧凑的设计,便于扩展
*/
typedef struct
{
MenuNodeId_e node_id; // 当前菜单节点ID唯一标识
MenuNodeId_e parent_id; // 父节点IDMENU_NODE_ID_NONE表示无父节点
TextId_e text_id; // 对应显示的文本ID
uint8_t level; // 菜单层级1=一级2=二级3=三级4=四级)
uint8_t attributes; // 节点属性使用MenuNodeAttr_e位掩码组合
} MenuNodeInfo_t;
/* 宏定义 ---------------------------------------------------------------------------------------------------------*/
/**
* @brief 菜单节点属性检查宏
* @param attr 节点属性
* @param flag 要检查的属性标志
* @return 1表示属性存在0表示属性不存在
*/
#define MENU_NODE_HAS_ATTR(attr, flag) ((attr) & (flag))
/**
* @brief 菜单框架配置常量
*/
#define MENU_CFG_MAX_DEPTH 4 // 最大菜单深度(一级~四级)
#define MENU_CFG_MAX_NODES 50 // 最大菜单节点数
#define MENU_CFG_MAX_TEXT_LEN 50 // 最大文本长度(字符数)
/* 全局变量声明 ---------------------------------------------------------------------------------------------------*/
/**
* @brief 全局菜单节点映射表
* @note 所有菜单节点的关联关系集中在这里维护,修改时仅需调整此表
* 按一级菜单、二级菜单的顺序排列,便于阅读和维护
*/
extern const MenuNodeInfo_t menu_node_map[];
/**
* @brief 菜单节点映射表大小
*/
extern const uint32_t menu_node_map_size;
/* 函数声明 ---------------------------------------------------------------------------------------------------------*/
/**
* @brief 初始化语言模块
* @return MenuErrCode 操作结果
*/
MenuErrCode language_init(void);
/**
* @brief 根据文本ID获取对应语言的文本字符串
* @param text_id 文本ID
* @return const char* 对应语言的文本字符串若ID无效返回NULL
*/
const char* get_text(TextId_e text_id);
/**
* @brief 设置当前系统语言
* @param lang 要设置的语言
* @return MenuErrCode 操作结果
*/
MenuErrCode set_language(SystemLang_e lang);
/**
* @brief 获取当前系统语言
* @return SystemLang_e 当前使用的语言
*/
SystemLang_e get_current_language(void);
/**
* @brief 根据菜单节点ID获取对应的文本ID
* @param node_id 菜单节点ID
* @return TextId_e 对应的文本ID若节点不存在返回TEXT_MAX
*/
TextId_e get_menu_text_id(MenuNodeId_e node_id);
/**
* @brief 根据菜单节点ID获取对应的父节点ID
* @param node_id 菜单节点ID
* @return MenuNodeId_e 对应的父节点ID若节点不存在返回MENU_NODE_ID_NONE
*/
MenuNodeId_e get_menu_parent_id(MenuNodeId_e node_id);
/**
* @brief 检查菜单节点是否有子节点
* @param node_id 菜单节点ID
* @return uint8_t 1=有子节点0=无子节点/节点不存在
*/
uint8_t menu_node_has_children(MenuNodeId_e node_id);
/**
* @brief 检查菜单节点是否可操作
* @param node_id 菜单节点ID
* @return uint8_t 1=可操作0=不可操作/节点不存在
*/
uint8_t menu_node_is_operable(MenuNodeId_e node_id);
/**
* @brief 获取菜单节点的显示文本
* @param node_id 菜单节点ID
* @return const char* 对应语言的文本字符串若节点无效返回NULL
*/
const char* get_menu_node_text(MenuNodeId_e node_id);
#endif /* LANGUAGE_H */