Files
DTU-HMI/README.md

376 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# DTU-HMI
PC 端 HMI 菜单逻辑模拟程序,纯 C 实现支持菜单树、LCD 显示、TCP 远程显示RemoDispBus 协议)及 UTF-8 汉字库。
---
## 快速开始
```powershell
mkdir build
cd build
cmake ..
cmake --build .
```
生成可执行文件 `DTU-HMI.exe`Windows`DTU-HMI`Linux
---
## 目录结构
```
DTU-HMI/
├── CMakeLists.txt
├── gen_utf8_hz12.py # 12×12 UTF-8 汉字库生成脚本
├── remo_disp_server.py # 远程显示 Python 服务端
├── include/
│ └── types.h
├── src/
│ ├── main.c
│ ├── thread_utils.c/h
│ ├── remoteDisplay.c/h
│ ├── TCP/tcp.c, tcp.h
│ └── Drv/
│ ├── menu.c/h
│ ├── display.c/h
│ ├── lcd.c/h
│ ├── Ascii.c/h
│ └── utf8_hz12_data.c/h # 由脚本生成
└── build/
```
---
## 环境要求
- **CMake** 3.10+
- **编译器**Windows 默认 MSVCLinux 需 GCC/Clang
- **编码**:源文件 UTF-8CMake 已配置 MSVC `/utf-8`
---
## 构建步骤
### Windows
```powershell
mkdir build
cd build
cmake ..
cmake --build .
```
### Linux / macOS
```bash
mkdir build
cd build
cmake ..
cmake --build .
```
---
## TCP 通信
程序监听端口 **7003**RemoDispBus 默认)。使用 `remo_disp_server.py` 连接后可实时查看 LCD 显存画面。协议支持 `CMD_INIT``CMD_LCDMEM``CMD_KEY``CMD_KEEPLIVE`
---
## 菜单系统
### 1. 概述
菜单由**静态表** `g_tMenuModelTab` 定义,经 `Menu_Main_Creat_01` 构建为**可遍历树** `g_tMenuItem[]`。每个结点有 `ptHigher/ptLower/ptBefore/ptBehind` 四个指针,同级首尾成环。
### 2. 数据结构
#### 2.1 静态菜单定义tagMenuModel
```c
typedef struct
{
uint8_t byClass; // 菜单分级标志 0/1/2/3
uint8_t byName[50]; // 菜单字符串
uint8_t byTip[50]; // 菜单提示文本
uint8_t byAttrib; // 菜单属性
uint16_t wPassword; // 访问密码0x0000 表示无密码
uint16_t wPara; // 菜单执行函数参数
FUNCPTR pfnWinProc; // 界面执行函数指针
} tagMenuModel, *tagPMenuModel;
/* 注意:表定义时顺序不能乱,需从 0 级开始,一级一级按顺序写入 */
const tagMenuModel g_tMenuModelTab[] = { ... };
```
#### 2.2 菜单遍历树tagMenuItem
```c
/* 每个菜单包含:一、上下前后等级关系;二、属性与内容;三、显示坐标 */
typedef struct _MENU_ITEM_
{
struct _MENU_ITEM_ *ptHigher; // 上级菜单指针
struct _MENU_ITEM_ *ptLower; // 下级菜单指针
struct _MENU_ITEM_ *ptBefore; // 同级上方指针
struct _MENU_ITEM_ *ptBehind; // 同级下方指针
uint8_t byClass;
uint8_t byName[50];
uint8_t byTip[50];
uint8_t byAttrib;
uint16_t wPassword;
uint16_t wPara;
FUNCPTR pfnWinProc;
uint16_t wPos, wNum;
uint16_t wSPosX, wSPosY, wEPosX, wEPosY;
} tagMenuItem, *tagPMenuItem;
tagMenuItem g_tMenuItem[300]; /* 所有菜单存储于此数组 */
```
### 3. 菜单构建示例
#### 3.1 静态表g_tMenuModelTab
```c
const tagMenuModel g_tMenuModelTab[] =
{
{ 0, " ", "", 0, 0x0000, 0, (FUNCPTR)Menu_NonPfunc },
{ 1, "装置信息", "查看装置信息", 0, 0x0000, EN_ANA_0, (FUNCPTR)MenuProc_See_AppInfo },
{ 1, "实时数据", "装置实时数据", 0, 0x0000, 0, (FUNCPTR)Menu_NonPfunc },
{ 2, "交流量", "查看遥测交流量", EN_MEA_AC, 0x0000, EN_ANA_0, (FUNCPTR)MenuProc_See_YC },
{ 2, "直流量", "查看遥测直流量", EN_MEA_DC, 0x0000, EN_ANA_0, (FUNCPTR)MenuProc_See_YC },
{ 2, "遥信量", "查看遥信开入量", EN_INPUT_RLY_ALL, 0x0000, EN_INPUT_0, (FUNCPTR)MenuProc_See_Input },
{ 1, "参数定值", "保护参数查看与修改", 0, 0x0000, 0, (FUNCPTR)Menu_NonPfunc },
{ 2, "整定", "整定装置保护参数", 0, 0x0000, 0, (FUNCPTR)Menu_NonPfunc },
{ 3, "参数", "查看设备参数定值", EN_FIGURE_SET, CN_USER_PWD, EN_SIDE_BASIC, (FUNCPTR)MenuProc_Set_Value },
{ 3, "定值", "设置装置数值定值", EN_FIGURE_SET, CN_USER_PWD, EN_SIDE_DEF, (FUNCPTR)MenuProc_Set_Value },
{ 3, "控制字", "设置装置控制字", EN_SOFT_SET, CN_USER_PWD, EN_SIDE_DEF, (FUNCPTR)MenuProc_Set_Value },
{ 3, "软压板", "设置软压板", 0, CN_USER_PWD, EN_SOFT_PRO, (FUNCPTR)MenuProc_Set_Soft },
{ 2, "查看", "查看装置保护参数", 0, 0x0000, 0, (FUNCPTR)Menu_NonPfunc },
{ 3, "参数", "设置设备参数定值", EN_FIGURE_SET, 0x0000, EN_SIDE_BASIC, (FUNCPTR)MenuProc_See_Set },
{ 3, "定值", "查看数值型定值", EN_FIGURE_SET, 0x0000, EN_SIDE_DEF, (FUNCPTR)MenuProc_See_Set },
{ 3, "控制字", "查看控制字定值", EN_SOFT_SET, 0x0000, EN_SIDE_DEF, (FUNCPTR)MenuProc_See_Set },
{ 3, "软压板", "查看软压板", 0, 0x0000, EN_SOFT_PRO, (FUNCPTR)MenuProc_See_Soft },
{ 1, "三遥设置", "", 0, 0x0000, 0, (FUNCPTR)Menu_NonPfunc },
{ 2, "遥测死区", "设置遥测量死区门槛", 0, CN_USER_PWD, 0, (FUNCPTR)MenuProc_YC_SetSqValue },
{ 2, "遥测系数", "设置遥测量微调系数", EN_MEA_ADJ, CN_USER_PWD, 0, (FUNCPTR)MenuProc_YC_SetAdjCoe },
{ 2, "遥信类型", "设置遥信类型", 0, CN_USER_PWD, 0, (FUNCPTR)MenuProc_YX_SetCommType },
{ 2, "遥信防抖", "设置遥信防抖时间", 0, CN_USER_PWD, 0, (FUNCPTR)MenuProc_YX_SetWidth },
{ 2, "双点遥信", "设置双点遥信虚端子", 0, CN_USER_PWD, 0, (FUNCPTR)MenuProc_YX_SetTwin },
{ 1, "装置维护", "", 0, 0x0000, 0, (FUNCPTR)Menu_NonPfunc },
{ 2, "时钟设置", "设置系统时钟", 0, CN_USER_PWD, 0, (FUNCPTR)MenuProc_Cfg_Time },
{ 2, "强制复归", "可复归未返回事件", EN_REV_FORCE, CN_USER_PWD, 0, (FUNCPTR)MenuProc_Cfg_RevEvent },
{ 2, "手动录波", "启动手动录波", 0, CN_USER_PWD, 0, (FUNCPTR)MenuProc_Cfg_ManualWave },
{ 2, "清除记录", "", 0, CN_USER_PWD, 0, (FUNCPTR)MenuProc_Cfg_ClrRec },
{ 1, "通讯参数", "", 0, 0x0000, 0, (FUNCPTR)Menu_NonPfunc },
{ 2, "通讯设置", "外部通讯设置", 0, CN_USER_PWD, 0, (FUNCPTR)MenuProc_Cfg_ComPara },
{ 2, "网口设置", "", 0, CN_USER_PWD, 0, (FUNCPTR)MenuProc_Cfg_EditIP },
{ 2, "SNTP设置", "", 0, CN_USER_PWD, 0, (FUNCPTR)MenuProc_Cfg_EditSntp },
{ 1, "记录查询", "查看各种装置记录", 0, 0x0000, 0, (FUNCPTR)Menu_NonPfunc },
{ 2, "SOE记录", "", 0, 0x0000, 0, (FUNCPTR)MenuProc_See_RecSOE },
{ 2, "事故记录", "", 0, 0x0000, 0, (FUNCPTR)MenuProc_See_RecAct },
{ 2, "操作记录", "", 0, 0x0000, 0, (FUNCPTR)MenuProc_See_RecOpt },
{ 2, "保护告警", "", 0, 0x0000, 0, (FUNCPTR)MenuProc_See_RecAlm },
{ 2, "保护启动", "", 0, 0x0000, 0, (FUNCPTR)MenuProc_See_RecStart },
{ 2, "遥控记录", "", 0, 0x0000, 0, (FUNCPTR)MenuProc_See_RecYK },
{ 2, "自检记录", "", 0, 0x0000, 0, (FUNCPTR)MenuProc_See_RecChk },
{ 2, "运行记录", "", 0, 0x0000, 0, (FUNCPTR)MenuProc_See_RecRun },
{ 2, "运行报告", "", 0, 0x0000, 0, (FUNCPTR)MenuProc_See_RecFault },
{ 0, "厂家设置", "设置装置相关参数", 0, CN_COP_PWD, 0, (FUNCPTR)Menu_NonPfunc },
{ 1, "元件配置", "配置元件配置", 0, CN_COP_PWD, EN_FACTORY_PASSWORD,(FUNCPTR)MenuProc_Cfg_CellConf },
{ 1, "恢复默认", "恢复默认元件定值参数", 0, CN_COP_PWD, 0, (FUNCPTR)Menu_NonPfunc },
{ 2, "全部恢复", "全部参数恢复默认", 0, CN_COP_PWD, EN_NO_USER_PWD, (FUNCPTR)MenuProc_AllInf_Default },
{ 2, "默认参数", "当前参数恢复默认", 0, CN_COP_PWD, EN_NO_USER_PWD, (FUNCPTR)MenuProc_Para_Default },
{ 2, "默认定值", "当前定值区恢复默认", 0, CN_COP_PWD, EN_NO_USER_PWD, (FUNCPTR)MenuProc_Set_Default },
{ 2, "软压板", "当前软压板恢复默认", 0, CN_COP_PWD, EN_NO_USER_PWD, (FUNCPTR)MenuProc_Resume_Soft },
{ 2, "元件配置", "元件配置恢复默认", 0, CN_COP_PWD, EN_NO_USER_PWD, (FUNCPTR)MenuProc_Cfg_CellDef },
{ 1, "交流显示", "交流显示方式设置", 0, CN_COP_PWD, 0, (FUNCPTR)MenuProc_Cfg_ShowAnaType },
{ 1, "装置调试", "调试装置", 0, 0x0000, 0, (FUNCPTR)Menu_NonPfunc },
{ 2, "虚拟遥信", "设置虚拟遥信值", 0, CN_COP_PWD, 0, (FUNCPTR)MenuProc_Dbg_XuYX },
{ 2, "交流虚遥测", "设置虚拟交流遥测值", EN_MEA_AC, CN_COP_PWD, EN_ANA_0, (FUNCPTR)MenuProc_Dbg_XuYC },
{ 2, "直流虚遥测", "设置虚拟直流遥测值", EN_MEA_DC, CN_COP_PWD, EN_ANA_0, (FUNCPTR)MenuProc_Dbg_XuYC },
{ 2, "电度虚遥测", "设置虚拟电度遥测值", EN_MEA_POWER, CN_COP_PWD, EN_ANA_0, (FUNCPTR)MenuProc_Dbg_XuYC },
{ 2, "动作虚事件", "设置虚拟动作事件", EN_ACT_REC, CN_COP_PWD, 0, (FUNCPTR)MenuProc_Dbg_XuEvent },
{ 2, "告警虚事件", "设置虚拟告警事件", EN_ALM_REC, CN_COP_PWD, 0, (FUNCPTR)MenuProc_Dbg_XuEvent },
{ 2, "动作出口", "进入此菜单保护退出", EN_OUTPUT_TRIP, CN_COP_PWD, EN_INPUT_0, (FUNCPTR)MenuProc_Dbg_Relay },
{ 2, "信号出口", "进入此菜单保护退出", EN_OUTPUT_SIGN, CN_COP_PWD, 0, (FUNCPTR)MenuProc_Dbg_Relay },
{ 1, "版本信息", "查看板件版本信息", 0, CN_COP_PWD, 0, (FUNCPTR)MenuProc_See_VersionBoard },
};
```
#### 3.2 解析后的树形结构
根据上表,`Menu_Main_Creat_01` 解析后的菜单树为:
```text
0 级目录 {
1.装置信息
1.实时数据 {
2.交流量
2.直流量
2.遥信量
}
1.参数定值 {
2.整定 {
3.参数
3.定值
3.控制字
3.软压板
}
2.查看 {
3.参数
3.定值
3.控制字
3.软压板
}
}
1.三遥设置 {
2.遥测死区
2.遥测系数
2.遥信类型
2.遥信防抖
2.双点遥信
}
1.装置维护 {
2.时钟设置
2.强制复归
2.手动录波
2.清除记录
2.通讯参数
2.通讯设置
2.网口设置
2.SNTP设置
}
1.记录查询 {
2.SOE记录
2.事故记录
2.操作记录
2.保护告警
2.保护启动
2.遥控记录
2.自检记录
2.运行记录
2.运行报告
}
}
0.厂家设置 {
1.元件配置
1.恢复默认 {
2.全部恢复
2.默认参数
2.默认定值
2.软压板
2.元件配置
}
1.交流显示
1.装置调试 {
2.虚拟遥信
2.交流虚遥测
2.直流虚遥测
2.电度虚遥测
2.动作虚事件
2.告警虚事件
2.动作出口
2.信号出口
}
1.版本信息
}
```
#### 3.3 单例解析示例:直流量
`{2,"直流量","查看遥测直流量",EN_MEA_DC,0x0000,EN_ANA_0,(FUNCPTR)MenuProc_See_YC}` 为例,构建后该菜单项的指针与属性为:
```text
{
ptHigher = 1.实时数据;
ptLower = NULL;
ptBefore = 2.交流量;
ptBehind = 2.遥信量;
byClass = 2;
byName = 直流量;
byTip = 查看遥测直流量;
byAttrib = EN_MEA_DC;
wPassword = 0x0000;
wPara = 0;
pfnWinProc = MenuProc_See_YC;
}
```
#### 3.4 同级首尾成环
同级菜单中,首尾通过 `ptBefore/ptBehind` 相连形成环。例如 2 级子菜单:
```text
2.虚拟遥信
2.交流虚遥测
2.直流虚遥测
2.电度虚遥测
2.动作虚事件
2.告警虚事件
2.动作出口
2.信号出口
```
其中:
- `2.虚拟遥信``ptBefore` 指向 `2.信号出口`(首的前一个是尾)
- `2.信号出口``ptBehind` 指向 `2.虚拟遥信`(尾的后一个是首)
### 4. 构建流程Menu_Main_Creat_01
`g_tMenuModelTab` 的定义顺序逐项处理,通过比较 `byCurClass``byNextClass` 设置每个菜单的层级指针。
#### 情况 1byCurClass < byNextClass下一项更深进入子菜单
```text
ptCurrent ptNextNode
(当前) (下一项,更深一级)
│ │
│ ptLower ──────────────────►│
│◄───────────────── ptHigher │
│ │
该级尾不变 新一级的 首=尾=ptNextNode
```
#### 情况 2byCurClass == byNextClass同级并列
```text
ptCurrent ─── ptBehind ──► ptNextNode
│ │
│ ptBefore ◄───────────┤
│ │
└──── ptHigher (同) ────────┘
该级 尾 更新为 ptNextNode
```
#### 情况 3byCurClass > byNextClass下一项更浅回到上层
```text
... byCurClass 级 ... byNextClass 级 ...
ptLast[byNextClass] 已存在
│ ptBehind ──► ptNextNode
│ │
│◄── ptBefore ────┤
│ ptHigher = 该级尾的 ptHigher
同时:从 byCurClass 到 byNextClass+1 各级首尾成环
ptLast[级]──►ptFirst[级]ptFirst[级]──►ptLast[级]
```
#### 最后:各级首尾成环
表遍历完后,从 **0 级到当前结点所在级**,把该级首尾连成环:
```text
ptFirst[级] ◄──────────────► ptLast[级]
│ │
└──── ptBehind ───────────────┘
◄──────── ptBefore ────────────┘
```