重构代码的架构设计,增加测试单元,提高代码可靠性
This commit is contained in:
244
docs/系统架构设计文档.md
Normal file
244
docs/系统架构设计文档.md
Normal file
@@ -0,0 +1,244 @@
|
||||
# DTU-HMI 系统架构设计文档
|
||||
|
||||
## 1. 文档目的
|
||||
|
||||
本文档用于描述 `DTU-HMI` 工程的整体架构、核心模块职责、关键数据流、线程与通信模型、构建与测试体系,作为后续开发、联调、测试与维护的统一基线。
|
||||
|
||||
## 2. 系统概述
|
||||
|
||||
`DTU-HMI` 是一个基于纯 C 实现的 PC 端 HMI 菜单逻辑模拟系统,目标是复现现场 DTU 设备的人机界面行为。
|
||||
系统支持本地菜单交互与远程显示协议(RemoDispBus)通信,主要包含:
|
||||
|
||||
- 菜单树构建与路由(多级菜单、同级循环导航)
|
||||
- LCD 显存与绘制(160x160 单色像素缓冲)
|
||||
- 按键输入(本地/远程按键注入)
|
||||
- TCP 服务(远程显示数据交互)
|
||||
- 跨平台线程与网络适配(Windows/Linux)
|
||||
|
||||
## 3. 架构目标与设计原则
|
||||
|
||||
- **可移植性**:通过 `tcp.c`、`thread_utils.c` 封装平台差异。
|
||||
- **可维护性**:按模块职责划分(菜单/显示/网络/线程/输入)。
|
||||
- **可测试性**:优先抽取并覆盖纯逻辑函数,逐步推进集成测试。
|
||||
- **低耦合高内聚**:上层业务通过明确接口调用下层能力。
|
||||
|
||||
## 4. 系统分层架构
|
||||
|
||||
```text
|
||||
+----------------------+
|
||||
| AppLayer |
|
||||
| main.c |
|
||||
+----------+-----------+
|
||||
|
|
||||
+--------------+--------------+
|
||||
| |
|
||||
v v
|
||||
+---------+----------+ +---------+------------------+
|
||||
| MenuLayer | | RemoteDisplayLayer |
|
||||
| menu.c | | remoteDisplay.c |
|
||||
+----+-----------+---+ +-----+-----------+----------+
|
||||
| | | |
|
||||
v v v v
|
||||
+------+-----+ +--+---------+ +---+----+ +---+------------------+
|
||||
|DisplayLayer| |InputLayer | |Input | |PlatformLayer |
|
||||
|lcd/Ascii/ | |key.c | |key.c | |tcp.c + thread_utils.c |
|
||||
|display.c | +------------+ +-------+ +------------------------+
|
||||
+------------+
|
||||
```
|
||||
|
||||
### 4.1 应用层
|
||||
|
||||
- 文件:`src/main.c`
|
||||
- 职责:
|
||||
- 系统初始化(菜单、按键、TCP 线程)
|
||||
- 主循环调度(菜单路由、周期显示刷新)
|
||||
- 生命周期管理(线程退出、网络清理)
|
||||
|
||||
### 4.2 菜单业务层
|
||||
|
||||
- 文件:`src/Drv/menu.c`、`src/Drv/menu.h`
|
||||
- 职责:
|
||||
- 基于静态菜单模型构建运行时菜单树
|
||||
- 处理按键驱动的菜单状态迁移
|
||||
- 执行菜单显示坐标计算与多级菜单渲染调度
|
||||
|
||||
### 4.3 显示层
|
||||
|
||||
- 文件:`src/Drv/lcd/lcd.c`、`src/Drv/lcd/lcd_draw.c`、`src/Drv/lcd/lcd_text.c`、`src/Drv/lcd/text_codec.c`、`src/Drv/lcd/ascii.c`、`src/Drv/display.c`
|
||||
- 职责:
|
||||
- 管理 LCD 显存 `g_tCVsr` 与像素绘制
|
||||
- 提供 ASCII/UTF-8 字符显示能力
|
||||
- 提供静态菜单模型定义 `g_tMenuModelTab`
|
||||
|
||||
### 4.4 输入层
|
||||
|
||||
- 文件:`src/Drv/key.c`、`src/Drv/key.h`
|
||||
- 职责:
|
||||
- 提供按键读写状态控制(消费式读取)
|
||||
- 接收远程模块写入的按键事件并供菜单模块读取
|
||||
|
||||
### 4.5 远程显示通信层
|
||||
|
||||
- 文件:`src/remoteDisplay.c`、`src/remoteDisplay.h`
|
||||
- 职责:
|
||||
- 实现 RemoDispBus 协议解析与回复
|
||||
- 提供 TCP 服务器线程入口与启动逻辑
|
||||
- 处理保活、初始化、按键下发、显存上传等命令
|
||||
|
||||
### 4.6 平台适配层
|
||||
|
||||
- 文件:`src/TCP/tcp.c`、`src/thread_utils.c`
|
||||
- 职责:
|
||||
- 提供跨平台 socket 与线程封装
|
||||
- 隔离 Windows/Linux API 差异
|
||||
|
||||
## 5. 核心数据结构设计
|
||||
|
||||
### 5.1 菜单模型与菜单树
|
||||
|
||||
- 静态菜单模型:`tagMenuModel`(定义于 `display.h`,数据在 `display.c`)
|
||||
- 运行时菜单项:`tagMenuItem`(定义于 `menu.c` 内)
|
||||
- 全局控制:`g_tMenuCtrl`、`g_tDspCtrl`
|
||||
|
||||
关键关系:
|
||||
|
||||
- `ptHigher` / `ptLower`:父子层级关系
|
||||
- `ptBefore` / `ptBehind`:同级双向关系(首尾成环)
|
||||
- `ptRoute[]`:当前路径缓存(0~3 级)
|
||||
|
||||
### 5.2 显示控制结构
|
||||
|
||||
- `tagScreenControl g_tCVsr`:
|
||||
- 显存缓冲 `pwbyLCDMemory`
|
||||
- 前景/背景色
|
||||
- ASCII 与汉字字体参数
|
||||
|
||||
### 5.3 远程按键结构
|
||||
|
||||
- `tagRKeyCtrl g_tRemoteKey`:
|
||||
- `byKeyValid`:是否有新按键
|
||||
- `byKeyValue`:按键值
|
||||
- `bUseRkey`:远程按键开关(当前实现中初始化为启用)
|
||||
|
||||
## 6. 关键业务流程
|
||||
|
||||
### 6.1 主循环流程
|
||||
|
||||
```text
|
||||
[系统初始化]
|
||||
|
|
||||
v
|
||||
[Menu_Route]
|
||||
|
|
||||
v
|
||||
[Sleep 20ms]
|
||||
|
|
||||
v
|
||||
[计数器累加]
|
||||
|
|
||||
v
|
||||
[是否到刷新周期?] --否--> [Menu_Route]
|
||||
|
|
||||
+--是--> [Menu_Show_Proc] --> [Menu_Route]
|
||||
```
|
||||
|
||||
### 6.2 菜单交互流程
|
||||
|
||||
- 输入来源:`Key_Read()`(含远程写入按键)
|
||||
- 行为:
|
||||
- 上/下:同级循环移动
|
||||
- 左/ESC:回退上级或退回主层
|
||||
- 右/确认:进入子级或执行叶子回调
|
||||
- 渲染:
|
||||
- `Menu_Show_Proc` 根据路径增量刷新或全量刷新
|
||||
|
||||
### 6.3 远程显示协议流程
|
||||
|
||||
```text
|
||||
[Accept客户端]
|
||||
|
|
||||
v
|
||||
[接收缓冲区累积]
|
||||
|
|
||||
v
|
||||
[parse_frame 校验解析]
|
||||
|
|
||||
+--成功--------> [按 cmd 分发] --> [send_reply 回包] --> [接收缓冲区累积]
|
||||
|
|
||||
+--失败/不完整--> [接收缓冲区累积]
|
||||
```
|
||||
|
||||
命令语义(RemoDispBus):
|
||||
|
||||
- `CMD_INIT`:返回 LCD 宽高与显存尺寸
|
||||
- `CMD_LCDMEM`:返回显存数据(支持起始地址)
|
||||
- `CMD_KEY`:注入远程按键到 `g_tRemoteKey`
|
||||
- `CMD_KEEPLIVE`:保活响应
|
||||
|
||||
## 7. 并发与线程模型
|
||||
|
||||
- 主线程:
|
||||
- 负责菜单路由与本地显示刷新
|
||||
- TCP 服务器线程:
|
||||
- 监听连接、解析协议、处理远程请求
|
||||
|
||||
共享状态:
|
||||
|
||||
- `g_tCVsr.pwbyLCDMemory`(远程读取 + 本地写入)
|
||||
- `g_tRemoteKey`(远程写入 + 菜单读取)
|
||||
|
||||
当前实现未使用锁机制,依赖业务访问模式降低冲突风险。
|
||||
后续若并发复杂度提升,建议引入细粒度互斥或无锁缓冲策略。
|
||||
|
||||
## 8. 构建与运行架构
|
||||
|
||||
- 构建系统:CMake(`C_STANDARD 99`)
|
||||
- 可执行目标:`DTU-HMI`
|
||||
- 平台链接:
|
||||
- Windows:`ws2_32`
|
||||
- Linux/macOS:`pthread`
|
||||
- 可选调试:`ENABLE_DEBUG=ON` 自动定义 `DEBUG` 宏
|
||||
|
||||
## 9. 测试架构
|
||||
|
||||
测试目录:`tests/`
|
||||
|
||||
- 框架:`ctest + 自定义断言宏`
|
||||
- 分层策略:
|
||||
- P0:纯逻辑单元测试(协议解析、UTF-8 解析、字库查找)
|
||||
- P1:状态/计算单测(按键、菜单、LCD 基础像素操作)
|
||||
- P2:集成测试(TCP 回环)
|
||||
|
||||
建议执行命令:
|
||||
|
||||
```bash
|
||||
cmake -S . -B build
|
||||
cmake --build build
|
||||
ctest --test-dir build -C Debug --output-on-failure
|
||||
```
|
||||
|
||||
## 10. 模块依赖关系(代码级)
|
||||
|
||||
- `main.c` 依赖:`menu`、`key`、`remoteDisplay`、`tcp`、`thread_utils`
|
||||
- `menu.c` 依赖:`lcd`、`display`、`key`
|
||||
- `remoteDisplay.c` 依赖:`lcd`、`key`、`tcp`、`thread_utils`
|
||||
- `lcd.c` 依赖:`ascii`
|
||||
- `display.c` 提供:静态菜单表(被 `menu.c` 使用)
|
||||
|
||||
## 11. 已知风险与改进建议
|
||||
|
||||
- **并发一致性风险**:远程线程与主线程共享状态无锁访问。
|
||||
- 建议:为显存快照与按键事件引入互斥保护或双缓冲。
|
||||
- **协议缓冲鲁棒性**:当前异常数据采用清空缓冲策略,存在丢包窗口。
|
||||
- 建议:增加更精细的帧边界恢复策略与统计日志。
|
||||
- **可测试性边界**:部分逻辑仍与全局状态耦合较深。
|
||||
- 建议:逐步引入接口注入(如 `TcpOps`、`delay_ms`)降低耦合。
|
||||
|
||||
## 12. 版本与维护
|
||||
|
||||
- 文档版本:v1.0
|
||||
- 适配代码基线:当前 `DTU-HMI` 仓库主干实现
|
||||
- 维护建议:
|
||||
- 每次新增模块或调整主流程时同步更新本文档
|
||||
- 测试策略更新需同步维护“第 9 章 测试架构”
|
||||
|
||||
264
docs/通信协议设计文档.md
Normal file
264
docs/通信协议设计文档.md
Normal file
@@ -0,0 +1,264 @@
|
||||
# DTU-HMI 通信协议设计文档
|
||||
|
||||
## 1. 文档说明
|
||||
|
||||
- 文档名称:`DTU-HMI` 通信协议设计文档
|
||||
- 协议名称:RemoDispBus(项目内实现)
|
||||
- 适用范围:`DTU-HMI` 与远程显示上位机(如 `remo_disp_server.py`)之间的 TCP 通信
|
||||
- 对应实现:`src/remoteDisplay.c`、`src/remoteDisplay.h`
|
||||
|
||||
## 2. 协议目标
|
||||
|
||||
本协议用于实现以下能力:
|
||||
|
||||
- 上位机与设备端建立会话并获取显示参数
|
||||
- 上位机按需读取 LCD 显存内容用于渲染
|
||||
- 上位机向设备端下发按键事件
|
||||
- 维持连接活性(保活)
|
||||
|
||||
## 3. 传输层与连接模型
|
||||
|
||||
- 传输层:TCP
|
||||
- 服务器角色:`DTU-HMI`(设备端)
|
||||
- 客户端角色:远程显示上位机
|
||||
- 默认监听端口:`7003`
|
||||
- 连接模式:单连接处理(当前连接断开后继续接受下一连接)
|
||||
|
||||
连接与处理流程(文字图):
|
||||
|
||||
```text
|
||||
[TcpServer_Listen:7003]
|
||||
|
|
||||
v
|
||||
[Accept 客户端连接]
|
||||
|
|
||||
v
|
||||
[接收并累积缓冲区数据]
|
||||
|
|
||||
v
|
||||
[解析完整协议帧]
|
||||
| 成功 | 不完整/非法
|
||||
v v
|
||||
[命令分发与处理] [继续接收数据]
|
||||
|
|
||||
v
|
||||
[发送应答帧]
|
||||
|
|
||||
v
|
||||
[继续处理当前连接,直到断开]
|
||||
```
|
||||
|
||||
## 4. 帧结构定义
|
||||
|
||||
### 4.1 通用帧格式
|
||||
|
||||
协议帧格式如下:
|
||||
|
||||
```text
|
||||
[TAG][CMD][LEN_H][LEN_L][DATA...][CRC]
|
||||
```
|
||||
|
||||
字段说明:
|
||||
|
||||
- `TAG`:1 字节,报文方向标记
|
||||
- `CMD`:1 字节,命令码
|
||||
- `LEN_H` + `LEN_L`:2 字节,大端,表示 `DATA` 长度
|
||||
- `DATA`:可变长,长度由 `LEN` 指定
|
||||
- `CRC`:1 字节,`DATA` 区逐字节异或
|
||||
|
||||
### 4.2 TAG 约定
|
||||
|
||||
- 客户端 -> 设备端:`0xAA`
|
||||
- 设备端 -> 客户端:`0xBB`
|
||||
|
||||
### 4.3 CRC 算法
|
||||
|
||||
- 初值:`0x00`
|
||||
- 计算范围:仅 `DATA` 字段
|
||||
- 算法:`crc = data[0] ^ data[1] ^ ... ^ data[n-1]`
|
||||
- `DATA` 长度为 0 时,CRC 结果为 `0x00`
|
||||
|
||||
## 5. 命令字定义
|
||||
|
||||
当前实现支持 4 个命令:
|
||||
|
||||
- `0x00`:`CMD_KEEPLIVE`
|
||||
- `0x01`:`CMD_INIT`
|
||||
- `0x02`:`CMD_KEY`
|
||||
- `0x03`:`CMD_LCDMEM`
|
||||
|
||||
## 6. 命令详细设计
|
||||
|
||||
### 6.1 CMD_KEEPLIVE(0x00)
|
||||
|
||||
#### 请求
|
||||
|
||||
- `DATA`:空(长度 0)
|
||||
|
||||
#### 响应
|
||||
|
||||
- `CMD`:`0x00`
|
||||
- `DATA`:空(长度 0)
|
||||
- 用于连接保活与链路探测
|
||||
|
||||
---
|
||||
|
||||
### 6.2 CMD_INIT(0x01)
|
||||
|
||||
#### 请求
|
||||
|
||||
- `DATA`:空(长度 0)
|
||||
|
||||
#### 响应
|
||||
|
||||
- `DATA` 长度:8 字节
|
||||
- 格式:
|
||||
|
||||
```text
|
||||
[LCD_W_H][LCD_W_L][LCD_H_H][LCD_H_L][MEM_B3][MEM_B2][MEM_B1][MEM_B0]
|
||||
```
|
||||
|
||||
字段含义:
|
||||
|
||||
- `LCD_W`:屏幕宽度(当前为 `160`)
|
||||
- `LCD_H`:屏幕高度(当前为 `160`)
|
||||
- `MEM`:显存总字节数(当前为 `160 * 160 = 25600`)
|
||||
|
||||
字节序:全部为大端编码
|
||||
|
||||
---
|
||||
|
||||
### 6.3 CMD_KEY(0x02)
|
||||
|
||||
#### 请求
|
||||
|
||||
- `DATA`:至少 1 字节
|
||||
- `DATA[0]`:按键值(如 `KEY_U/KEY_D/KEY_L/KEY_R/KEY_ENT/KEY_ESC`)
|
||||
|
||||
#### 处理行为
|
||||
|
||||
- 设备端将按键写入:
|
||||
- `g_tRemoteKey.byKeyValid = EN_KEY_FLAG_NEW`
|
||||
- `g_tRemoteKey.byKeyValue = DATA[0]`
|
||||
|
||||
#### 响应
|
||||
|
||||
- 当前实现:不发送显式响应帧
|
||||
- 建议:后续版本增加 ACK,以便上位机确认按键注入结果
|
||||
|
||||
---
|
||||
|
||||
### 6.4 CMD_LCDMEM(0x03)
|
||||
|
||||
#### 请求
|
||||
|
||||
- `DATA` 长度:建议 4 字节
|
||||
- 格式:`[ADDR_B3][ADDR_B2][ADDR_B1][ADDR_B0]`(大端起始地址)
|
||||
|
||||
若请求长度小于 4,设备端默认起始地址为 0。
|
||||
|
||||
#### 响应
|
||||
|
||||
- `DATA` 格式:
|
||||
|
||||
```text
|
||||
[ADDR_B3][ADDR_B2][ADDR_B1][ADDR_B0][LCD_MEM_SLICE...]
|
||||
```
|
||||
|
||||
- 前 4 字节回显起始地址
|
||||
- 后续为显存片段:
|
||||
- 若 `start_addr < LCD_DISPLAYMEMORYSIZE`,返回从该地址到末尾的全部显存
|
||||
- 若 `start_addr >= LCD_DISPLAYMEMORYSIZE`,仅返回 4 字节地址(无显存数据)
|
||||
|
||||
## 7. 帧解析与容错策略
|
||||
|
||||
设备端接收缓冲解析规则:
|
||||
|
||||
1. 至少 5 字节才可判定为候选帧(最小帧)
|
||||
2. 首字节必须是 `TAG_CLIENT(0xAA)`
|
||||
3. 根据 `LEN` 计算总帧长:`4 + len + 1`
|
||||
4. 缓冲长度不足总帧长时,继续接收
|
||||
5. CRC 不匹配则视为非法帧
|
||||
6. 成功解析后按 `consume` 字节从缓冲区移除
|
||||
|
||||
异常处理策略:
|
||||
|
||||
- 长时间无法成帧且缓冲接近上限(`4096-256`)时,清空缓冲防止越界
|
||||
- `recv` 返回 `0` 或 `<0`,认为连接结束,关闭当前客户端
|
||||
- 未知命令:回空应答(同命令码,空 `DATA`)
|
||||
|
||||
## 8. 协议示例报文
|
||||
|
||||
说明:以下示例均为十六进制字节流。
|
||||
|
||||
### 8.1 KEEPLIVE 请求/响应
|
||||
|
||||
- 请求:`AA 00 00 00 00`
|
||||
- `CRC=00`(空数据)
|
||||
- 响应:`BB 00 00 00 00`
|
||||
|
||||
### 8.2 INIT 请求/响应(示意)
|
||||
|
||||
- 请求:`AA 01 00 00 00`
|
||||
- 响应头:`BB 01 00 08 ... CRC`
|
||||
- 响应 `DATA` 示例(160x160,25600):
|
||||
- `00 A0 00 A0 00 00 64 00`
|
||||
|
||||
### 8.3 KEY 请求(上键示例)
|
||||
|
||||
- 若上键值为 `0x02`,请求可为:
|
||||
- `AA 02 00 01 02 02`
|
||||
- 其中末尾 CRC=`0x02`
|
||||
|
||||
### 8.4 LCDMEM 请求(从 0 地址读取)
|
||||
|
||||
- 请求:`AA 03 00 04 00 00 00 00 00`
|
||||
- `DATA` 为 4 字节地址 `0x00000000`
|
||||
- CRC=`00`
|
||||
- 响应:`BB 03 LEN_H LEN_L [00 00 00 00][显存数据...] CRC`
|
||||
|
||||
## 9. 状态与时序约定
|
||||
|
||||
推荐交互顺序:
|
||||
|
||||
```text
|
||||
1) 连接 TCP 7003
|
||||
2) 发送 CMD_INIT 获取屏幕参数
|
||||
3) 周期发送 CMD_LCDMEM 拉取显存
|
||||
4) 有用户操作时发送 CMD_KEY
|
||||
5) 周期发送 CMD_KEEPLIVE 保活
|
||||
```
|
||||
|
||||
## 10. 安全性与边界约束
|
||||
|
||||
当前协议属于内网轻量协议,未设计鉴权与加密机制。建议在生产化场景补充:
|
||||
|
||||
- 连接鉴权(口令/Token)
|
||||
- 传输加密(TLS 或应用层加密)
|
||||
- 命令频率限制与异常连接清理
|
||||
|
||||
## 11. 兼容性与扩展建议
|
||||
|
||||
- 保留 `CMD` 空间用于后续扩展
|
||||
- 建议新增统一 ACK/NACK 机制(含错误码)
|
||||
- 建议引入协议版本字段(可放在 `CMD_INIT` 响应或扩展头中)
|
||||
- 建议为 `CMD_KEY` 增加长度校验(当前默认读取 `DATA[0]`)
|
||||
|
||||
## 12. 与代码映射关系
|
||||
|
||||
- 帧解析:`parse_frame`
|
||||
- CRC 计算:`calc_crc`
|
||||
- 应答构造发送:`send_reply`
|
||||
- 命令处理:`handle_cmd_keeplive` / `handle_cmd_init` / `handle_cmd_key` / `handle_cmd_lcdmem`
|
||||
- 线程入口:`tcp_server_thread_fn`
|
||||
- 服务启动:`StartTcpServerThread`
|
||||
|
||||
## 13. 测试建议(协议方向)
|
||||
|
||||
建议将以下场景纳入自动化测试:
|
||||
|
||||
- 正常帧:4 类命令全部覆盖
|
||||
- 异常帧:错误 TAG、错误 CRC、截断帧、超长无效数据
|
||||
- 边界值:`LEN=0`、`start_addr=0`、`start_addr=LCD_DISPLAYMEMORYSIZE-1`、`start_addr>=LCD_DISPLAYMEMORYSIZE`
|
||||
- 连接稳定性:频繁重连、并发请求(若后续支持)
|
||||
|
||||
Reference in New Issue
Block a user