# 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/` | test_op.html | 测试操作页 | | `/test-data` | test_data.html | 测试信息页 | ### 3.3 REST API #### 设备相关 | Method | Path | 说明 | |--------|------|------| | GET | `/api/devices` | 获取 dnt_info 列表(含在线状态) | | PUT | `/api/devices//name` | 修改终端名称 | | GET | `/api/devices//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//progress` | 获取进度 {total, done, failed, remaining} | | GET | `/api/automation//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/` #### 测试操作页 (`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 → 标记为失败 - **右侧信息区**: - 最近一次测试数据显示(实时刷新) - 平均值显示: 峰峰值、开始工作频率、进入工作频率、进入距离、离开距离、进入速度、离开速度 - 平均值只计算成功记录(不计失败的 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//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 不冲突(同库不同连接)