feat: 新增 edc-web Flask 前端管理系统 + 需求文档

- edc-web: Flask 项目骨架(设备管理、测试操作、测试信息三大页面)
- edc_server: 升级子模块(tb_serialnet 透传支持)
- docs: 测试工装EDC管理系统需求文档
This commit is contained in:
wangfq
2026-05-28 09:40:45 +08:00
parent 2bfb9602e4
commit 70dd3f8246
2295 changed files with 370008 additions and 1 deletions

View File

@@ -0,0 +1,249 @@
# edc-web 实施计划
## 目标
`vd_test_fixture` 项目中新增 `edc-web` Flask 前端 + 扩展 `edc_server` 后端,实现测试工装 EDC 的 Web 管理系统。
## 架构概览
```
┌──────────────────────┐ ┌──────────────────────┐
│ edc-web (Flask) │ │ edc_server (asyncio) │
│ 前端 Web 界面 │ │ 后端通信服务 │
│ REST API │ │ UDP/TCP + 轮询 │
└──────┬───────────────┘ └──────┬───────────────┘
│ │
└────────┬───────────────────┘
┌──────▼──────┐
│ MySQL │
│ edc 数据库 │
└─────────────┘
```
## 一、数据库变更
### 1.1 新增表 `tb_serialnet`(透传发送表)
```sql
CREATE TABLE IF NOT EXISTS `tb_serialnet` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`dnt_id` INT NOT NULL COMMENT 'FK → dnt_info.id',
`send_pkg` VARCHAR(380) DEFAULT '' COMMENT '发送指令包(hex)',
`rcv_pkg` VARCHAR(380) DEFAULT '' COMMENT '接收指令包(hex)',
`state` TINYINT DEFAULT 0 COMMENT '0未发送, 1已发送, 2已完成, 3超时失败',
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP,
`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX `idx_dnt_state` (`dnt_id`, `state`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
```
**修改文件**: `edc_server/src/models.py` — 在 `_create_tables()` 中添加建表语句
### 1.2 新增 tb_serialnet CRUD 函数
`models.py` 中添加:
| 函数 | 用途 |
|------|------|
| `get_pending_serialnet(dnt_id)` | 获取该设备 state=0 的第一个待发送记录 |
| `mark_serialnet_sent(id)` | 标记为 state=1 (已发送) |
| `mark_serialnet_done(id, rcv_pkg)` | 标记为 state=2 (收到回复) |
| `mark_serialnet_timeout(id)` | 标记为 state=3 (超时失败) |
| `get_serialnet_stats(dnt_id)` | 返回 total/sent/done/failed 计数 |
| `insert_serialnet(dnt_id, send_pkg)` | 插入新指令 |
## 二、edc_server 扩展
### 2.1 新增 `serialnet_loop()` 轮询任务
`handlers.py` 中新增:
```python
async def serialnet_loop():
"""轮询 tb_serialnet下发待发送的透传指令"""
while True:
for device_id, dnt_id in list(_registry.items()):
record = await get_pending_serialnet(dnt_id)
if record:
# 构造 SerialNet JSON → 通过 UDP 发送
# 更新 state=1
...
await asyncio.sleep(0.2)
```
**关键点**
- 同一设备同一时间只处理一条(避免冲突)
- 需要在 UDP transport 上发送数据 — 这要求 server.py 把 transport 暴露出来
### 2.2 server.py 改造
-`EDCProtocol` 的 transport 保存到全局/模块级变量,供 `serialnet_loop` 使用
- TCP handler 也需要能发送 SerialNet → 需要保存 TCP writer 映射 `{device_id: writer}`
### 2.3 B2 响应匹配 tb_serialnet
`tsreport_handler` 收到 B2 数据包后,除了写入 `tb_state_tst`,还要:
- 查找该设备 state=1 的 tb_serialnet 记录
- 更新为 state=2写入 rcv_pkg
### 2.4 超时检测
`serialnet_loop` 中检查 state=1 且超过 10 秒的记录 → 标记为 state=3
## 三、edc-web Flask 应用
### 3.1 目录结构
```
vd_test_fixture/edc-web/
├── run.py # 入口
├── requirements.txt # Flask, pymysql, ...
├── app/
│ ├── __init__.py # Flask 工厂
│ ├── config.py # 配置
│ ├── models.py # 数据库操作(同步 pymysql
│ ├── routes/
│ │ ├── __init__.py
│ │ ├── devices.py # 设备页面 API
│ │ ├── test_op.py # 测试操作 API
│ │ └── test_data.py # 测试信息 API
│ ├── templates/
│ │ ├── base.html # 基模板(菜单)
│ │ ├── devices.html # 设备列表页
│ │ ├── test_op.html # 测试操作页
│ │ └── test_data.html # 测试信息页
│ └── static/
│ ├── css/
│ │ └── style.css
│ └── js/
│ ├── devices.js
│ ├── test_op.js
│ └── test_data.js
```
### 3.2 页面路由
| 路由 | 页面 | 说明 |
|------|------|------|
| `/` | devices.html | 设备列表(默认首页) |
| `/test/<int:dnt_id>` | test_op.html | 测试操作页 |
| `/test-data` | test_data.html | 测试信息页 |
### 3.3 REST API
#### 设备相关
| Method | Path | 说明 |
|--------|------|------|
| GET | `/api/devices` | 获取 dnt_info 列表(含在线状态) |
| PUT | `/api/devices/<id>/name` | 修改终端名称 |
| GET | `/api/devices/<id>/status` | 获取设备最新测试状态 |
#### 测试操作
| Method | Path | 说明 |
|--------|------|------|
| POST | `/api/command` | 发送单次指令 {dnt_id, cmd} |
| POST | `/api/automation/start` | 开始自动化 {dnt_id, count} |
| POST | `/api/automation/stop` | 停止自动化 {dnt_id} |
| GET | `/api/automation/<dnt_id>/progress` | 获取进度 {total, done, failed, remaining} |
| GET | `/api/automation/<dnt_id>/averages` | 获取平均值 |
#### 测试信息
| Method | Path | 说明 |
|--------|------|------|
| GET | `/api/test-data` | 分页查询测试数据(join dnt_info + tb_state_tst) |
| GET | `/api/test-data/export` | 导出 CSV |
### 3.4 前端实现要点
#### 设备页面 (`devices.html`)
- 表格列: 设备编码(serial)、名称(name,可编辑)、IP、在线状态(state)、版本(version)、操作(测试按钮)
- 名称编辑: 点击名称单元格 → 变为 input → 回车/blur 提交 PUT
- 在线状态: state=1 绿色"在线", state=0 灰色"离线"
- 操作列: "测试" 按钮 → 跳转 `/test/<dnt_id>`
#### 测试操作页 (`test_op.html`)
- **左侧操作区**:
- 指令按钮: 开始测试(B0)、测试复原(B1)、电机前进(BA)、电机后退(BB)、电机停止(BC)
- 点击 → POST /api/command → edc_server 通过 SerialNet 发送
- 自动化区域:
- 测试次数 input + 开始/结束按钮
- 进度条(完成/总数 + 失败数)
- 实现: 前端轮询 /api/automation/<id>/progress (1s间隔)
- 开始后: 前端每次 state=2 时自动插入下一条 0xB0
- 超时: 前端 JS 计时器10秒后 state 仍是 1 → 标记为失败
- **右侧信息区**:
- 最近一次测试数据显示(实时刷新)
- 平均值显示: 峰峰值、开始工作频率、进入工作频率、进入距离、离开距离、进入速度、离开速度
- 平均值只计算成功记录(不计失败的 state=3
#### 测试信息页 (`test_data.html`)
- 表格: JOIN dnt_info.serial + tb_state_tst 全部字段
- 分页: 后端 LIMIT/OFFSET
- 搜索: 按 serial、日期范围筛选
- 导出: CSV 下载
## 四、自动化流程详解
```
前端点击"开始"(次数=N)
├─ 清空前端平均值缓存
├─ POST /api/automation/start (dnt_id, count=N)
│ └─ 后端插入第1条 tb_serialnet (state=0, send_pkg=0xB0)
├─ 前端开始轮询 (1s间隔)
│ │
│ ├─ GET /api/automation/<id>/progress
│ │ └─ 返回 {total:N, done:M, failed:F, remaining:R}
│ │
│ └─ 前端逻辑:
│ - 如果 state=2 (完成) → done++ → 如果 done < N: 插入下一条 0xB0
│ - 如果 state=1 超过10秒 → 标记 timeout → failed++
│ - 更新进度条
│ - 如果 done + failed >= N 或 用户点击"结束" → 停止
└─ edc_server 后台:
├─ serialnet_loop: state=0 → 发送 UDP SerialNet → state=1
└─ tsreport_handler: 收到 B2 → 写入 tb_state_tst → 匹配 state=1 → state=2
```
## 五、DG430 指令构造
所有指令基于 DG430 串口协议,通过 SerialNet 透传:
| 按钮 | 命令 | hex 数据包 (addr=0x01) | 备注 |
|------|------|------------------------|------|
| 开始测试 | 0xB0 | `7F 81 01 B0 30 32` | |
| 测试复原 | 0xB1 | `7F 81 01 B1 31 33` | |
| 电机前进 | 0xBA | `7F 81 01 BA 3A 3C` | |
| 电机后退 | 0xBB | `7F 81 01 BB 3B 3D` | |
| 电机停止 | 0xBC | `7F 81 01 BC 3C 3E` | |
addr=0x01 → ADDR字段=0x80+0x01=0x81
XOR: 0x81 ^ 0x01 ^ 0xB0 = 0x30, SUM: (0x81+0x01+0xB0) & 0xFF = 0x32
## 六、实施步骤
| 步骤 | 内容 | 文件 |
|------|------|------|
| 1 | 添加 tb_serialnet 表 + CRUD 函数 | `edc_server/src/models.py` |
| 2 | 新增 serialnet_loop + 改造 server.py (暴露 transport) | `edc_server/src/handlers.py`, `server.py` |
| 3 | B2 响应匹配 tb_serialnet | `edc_server/src/handlers.py` |
| 4 | 创建 edc-web 项目骨架 (Flask 工厂) | `edc-web/app/__init__.py` |
| 5 | 设备页面 API + 前端 | `edc-web/app/routes/devices.py`, `templates/devices.html` |
| 6 | 测试操作页 API + 前端 | `edc-web/app/routes/test_op.py`, `templates/test_op.html` |
| 7 | 测试信息页 API + 前端 | `edc-web/app/routes/test_data.py`, `templates/test_data.html` |
| 8 | 集成测试 | - |
## 七、注意事项
1. **防止冲突**: tb_serialnet 对同一设备同时只处理一条 (state=0/1 只有一条)
2. **超时处理**: 前端 JS 10 秒定时器 + 后端 serialnet_loop 也做超时兜底
3. **平均值计算**: 只计算 state=2 (成功) 的记录,排除 state=3 (失败)
4. **edc_server UDP 发送**: 目前 edc_server 只接收 UDP需要在 EDCProtocol 中保存 transport 引用
5. **edc-web 用同步 pymysql**: Flask 是同步框架,用 pymysql 直接连 MySQL与 edc_server 的 aiomysql 不冲突(同库不同连接)