整定一版
This commit is contained in:
30
demo/CMakeLists.txt
Normal file
30
demo/CMakeLists.txt
Normal 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
92
demo/build/CMakeCache.txt
Normal 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
|
||||
|
||||
15
demo/build/CMakeFiles/3.31.2/CMakeSystem.cmake
Normal file
15
demo/build/CMakeFiles/3.31.2/CMakeSystem.cmake
Normal 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)
|
||||
11
demo/build/CMakeFiles/CMakeConfigureLog.yaml
Normal file
11
demo/build/CMakeFiles/CMakeConfigureLog.yaml
Normal 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
|
||||
...
|
||||
1
demo/build/CMakeFiles/cmake.check_cache
Normal file
1
demo/build/CMakeFiles/cmake.check_cache
Normal file
@ -0,0 +1 @@
|
||||
# This file is generated by cmake for dependency checking of the CMakeCache.txt file
|
||||
681
demo/demo.c
Normal file
681
demo/demo.c
Normal 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
525
demo/language.c
Normal 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
311
demo/language.h
Normal 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; // 父节点ID(MENU_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 */
|
||||
Reference in New Issue
Block a user