- edc-web: Flask 项目骨架(设备管理、测试操作、测试信息三大页面) - edc_server: 升级子模块(tb_serialnet 透传支持) - docs: 测试工装EDC管理系统需求文档
9.8 KiB
9.8 KiB
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(透传发送表)
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 中新增:
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//progress (1s间隔)
- 开始后: 前端每次 state=2 时自动插入下一条 0xB0
- 超时: 前端 JS 计时器,10秒后 state 仍是 1 → 标记为失败
- 指令按钮: 开始测试(B0)、测试复原(B1)、电机前进(BA)、电机后退(BB)、电机停止(BC)
-
右侧信息区:
- 最近一次测试数据显示(实时刷新)
- 平均值显示: 峰峰值、开始工作频率、进入工作频率、进入距离、离开距离、进入速度、离开速度
- 平均值只计算成功记录(不计失败的 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 | 集成测试 | - |
七、注意事项
- 防止冲突: tb_serialnet 对同一设备同时只处理一条 (state=0/1 只有一条)
- 超时处理: 前端 JS 10 秒定时器 + 后端 serialnet_loop 也做超时兜底
- 平均值计算: 只计算 state=2 (成功) 的记录,排除 state=3 (失败)
- edc_server UDP 发送: 目前 edc_server 只接收 UDP,需要在 EDCProtocol 中保存 transport 引用
- edc-web 用同步 pymysql: Flask 是同步框架,用 pymysql 直接连 MySQL,与 edc_server 的 aiomysql 不冲突(同库不同连接)