emcp/.trae/documents/2026-05-25-modbus-backend-refactor-plan.md

329 lines
13 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.

# Modbus 新协议后端改造说明
## Summary
本次改造目标是让后端完全按 [显示界面modbus协议寄存器定义.md](file:///d:/Trae_space/EMCP/document/%E6%98%BE%E7%A4%BA%E7%95%8C%E9%9D%A2modbus%E5%8D%8F%E8%AE%AE%E5%AF%84%E5%AD%98%E5%99%A8%E5%AE%9A%E4%B9%89.md) 的新版协议工作,并取消 `backend/config` 作为配置真值来源的角色。
本次计划确认的边界如下:
- 只调整后端代码,不修改前端代码。
- 配置页面首次加载时,后端需先从下位机通过 Modbus 读取参数并返回。
- 界面保存配置时,后端只动态写入下位机,不再把配置写入 `backend/config` 下的 `device.json`、`channel.json`、`setting.json`。
- 协议文档中没有定义寄存器的现有配置接口,本次先保留,并继续沿用原本地配置逻辑作为过渡。
- 先生成本地说明文档,用户确认后再开始改代码。
---
## Current State Analysis
### 1. 当前接口层现状
当前配置接口集中在 `backend/app/api/routes/config.py`
- 保留协议可能覆盖的接口:
- `GET/POST /api/config/channel`
- `GET/POST /api/config/line_alarm_setting`
- `GET/POST /api/config/ai_alarm_setting`
- `GET/POST /api/config/system`
- 当前存在但新版协议未覆盖的接口:
- `GET/POST /api/config/device`
- `POST /api/config/device/password`
- `GET/POST /api/config/device/net`
- `GET/POST /api/config/device/uart`
根据用户本轮决策,上述“协议未定义寄存器”的配置接口在本次后端改造先保留,并继续沿用原有本地配置文件逻辑。
### 2. 当前业务层现状
当前业务主要集中在 `backend/app/services/platform_service.py`,存在两个关键问题:
- 配置读取默认来自本地 JSON
- `get_device_config()`
- `get_channel_config()`
- `get_line_alarm_setting()`
- `get_ai_alarm_setting()`
- `get_system_config()`
- 配置保存采用“先写本地 JSON再下发设备”的双写模式
- `save_device_config()`
- `save_channel_config()`
- `save_line_alarm_setting()`
- `save_ai_alarm_setting()`
- `save_system_config()`
这与“首次从下位机读取显示、保存时只写下位机、不再落本地配置文件”的新需求直接冲突。
此外,实时数据一次值计算当前仍依赖 `setting.json -> line_alarm_setting[].transformer_change[]` 中的 PT/CT 变比;如果配置文件取消真值角色,则这部分变比来源也必须切到设备回读数据。
### 3. 当前设备适配层现状
真实 Modbus 设备适配集中在 `backend/app/adapters/device_client.py`,当前实现与新版协议的差异点如下:
- 已符合或基本可复用:
- 四路实时数据输入寄存器读取
- AI 输入 `0x00D0 ~ 0x00DE`
- B 码对时 `0x00E0 ~ 0x00E2`
- 开入读取 `0x00E3`
- 开出线圈写 `0x0F`
- 系统时间同步 `0x0000`
- 明显不符合新版协议:
- 当前事件总寄存器仍按旧逻辑读取 `0x00E4`,新版应为 `0x00E5`
- 当前“读事件”按旧逻辑读保持寄存器 `0x0000 ~ 0x0003`,新版应改为读输入寄存器 `0x0100`,长度 `5 * n`
- 当前 AI/AO/AI 报警写入地址仍是旧版:
- AI 通道:`0x004C`,新版应为 `0x0054`
- AO 通道:`0x006A`,新版应为 `0x0072`
- AI 报警:`0x0088`,新版应为 `0x0090`
- 当前线路定值写入长度按 14 寄存器/每路处理,未包含 2 个变比参数;新版总长度为 64并明确包含变比参数
- 当前没有“参数区回读”能力,只有写寄存器,没有从设备读取参数区并还原为 API 返回结构
### 4. 当前配置仓储现状
`backend/app/repositories/json_config_repo.py` 目前负责:
-`device.json`
-`channel.json`
-`setting.json`
- 读上述 JSON 作为配置真值
按照本次需求,`backend/config` 中的配置文件不再承担运行期配置真值角色;对应仓储将不再被配置接口和业务服务依赖,只能保留给极少数非配置场景,或在后续整理中弱化/下线。
### 5. 当前测试现状
- `backend/tests/test_api.py` 大量依赖本地 JSON 写入与读取断言
- `backend/tests/test_modbus_client.py` 使用 FakeTransport 验证旧版寄存器地址、旧版事件读取方式和旧版参数写入地址
这些测试在本次改造后会大面积失效,必须同步调整。
---
## Proposed Changes
### 1. 调整配置接口边界
#### 文件
- `backend/app/api/routes/config.py`
#### 改动内容
- 保留并继续支持的接口分为两类:
- **按新版协议改造为纯 Modbus 真值**
- `GET/POST /api/config/channel`
- `GET/POST /api/config/line_alarm_setting`
- `GET/POST /api/config/ai_alarm_setting`
- `GET/POST /api/config/system`
- **协议未定义寄存器、先保留原本地逻辑**
- `GET/POST /api/config/device`
- `POST /api/config/device/password`
- `GET/POST /api/config/device/net`
- `GET/POST /api/config/device/uart`
#### 原因
- 新协议文档已覆盖的配置项,需切换为“设备寄存器即真值”
- 新协议文档未覆盖的配置项,用户明确要求先保留接口,避免立即影响现有前端调用
- 这些未定义项本轮继续沿用本地配置文件逻辑,待后续协议补齐后再迁移
### 2. 配置读取改为“首次从下位机读”
#### 文件
- `backend/app/services/platform_service.py`
- `backend/app/adapters/device_client.py`
#### 改动内容
- 移除 `get_channel_config()``channel.json` 的依赖,改为调用设备侧“参数区回读”能力
- 移除 `get_line_alarm_setting()``setting.json` 的依赖,改为读取线路定值寄存器并按 `line_no` 解析
- 移除 `get_ai_alarm_setting()``setting.json` 的依赖,改为读取 AI 报警寄存器并还原 API 结构
- 移除 `get_system_config()``setting.json` 的依赖,改为从设备读取可回读的系统参数
- PT/CT 变比不再从 `setting.json` 读取,而改为从线路定值寄存器回读结果中提取
#### 原因
- 配置页面首次加载必须从下位机读取显示
- `backend/config` 不再保存配置真值
### 3. 配置保存改为“只写下位机,不写本地 JSON”
#### 文件
- `backend/app/services/platform_service.py`
- `backend/app/repositories/json_config_repo.py`
#### 改动内容
- 删除以下方法中的本地 JSON 写入逻辑:
- `save_channel_config()`
- `save_line_alarm_setting()`
- `save_ai_alarm_setting()`
- `save_system_config()`
- 返回值改为仅描述 Modbus 写入结果,不再返回本地 `save_path`
- 业务层不再把配置结果回写 `channel.json` / `setting.json`
- `JsonConfigRepository` 不再作为上述“协议已定义项”的运行期依赖
- `device/device password/device net/device uart` 相关接口暂不纳入此调整,仍继续使用本地配置文件逻辑
#### 原因
- 用户已明确要求取消本地配置文件落盘
- 配置应以设备当前寄存器值为唯一真值
### 4. 按新版协议更新寄存器地址和事件机制
#### 文件
- `backend/app/adapters/device_client.py`
#### 改动内容
- 更新常量地址:
- 事件总寄存器:由旧值改为 `0x00E5`
- 读事件起始地址:改为 `0x0100`
- AI 通道写入起始地址:改为 `0x0054`
- AO 通道写入起始地址:改为 `0x0072`
- AI 报警写入起始地址:改为 `0x0090`
- 新增/修改事件读取实现:
- 先读 `0x00E5`
- 若值为 `n > 0`,按 `0x04 + 0x0100 + 5*n` 读取输入寄存器
- 解析事件结构:时间戳、毫秒、线路号、事件类型、事件代码、动作值
- 读完后通过 `0x03 + 0x0100 + n` 发送“读事件成功回复”
- 新增/修改事件映射逻辑:
- 保护类
- 动作类
- 报警类
- 操作类
- AI 越限类
#### 原因
- 当前事件实现仍停留在旧协议,和最新文档不一致
### 5. 补齐参数区回读能力
#### 文件
- `backend/app/adapters/device_client.py`
- `backend/app/services/platform_service.py`
- `backend/app/schemas/platform.py`
#### 改动内容
-`device_client.py` 中新增参数回读方法,例如:
- 读取串口设置寄存器块
- 读取线路定值设置寄存器块
- 读取 AI 通道设置寄存器块
- 读取 AO 通道设置寄存器块
- 读取 AI 报警设置寄存器块
- 读取系统设置寄存器块(仅限协议已定义项)
- 将回读寄存器块解析成后端当前接口使用的结构:
- `ChannelConfigIn`
- `LineAlarmSettingIn`
- `AiAlarmSettingIn`
- `SystemConfigIn` 的可支持子集
- 根据新版协议更新线路定值寄存器长度与解析方式:
- 从原先 7 类规则扩展到包含 2 个变比参数
- PT/CT 变比参与一次值换算
#### 原因
- 没有回读能力,就无法满足“参数页首次加载先从下位机读取显示”
### 6. 处理不再受协议支持的字段
#### 文件
- `backend/app/schemas/platform.py`
- `backend/app/services/platform_service.py`
- `backend/app/api/routes/config.py`
#### 改动内容
- 识别协议未覆盖字段并做“分层处理”
-`SystemConfigIn` 中协议未定义的字段(如 `brightness`、`screen_saver`)进行重新评估:
- 若协议无寄存器,则从“纯 Modbus 真值范围”中剥离
- `GET/POST /config/system` 需按协议实际可支持字段精简或拆分
- 对历史仅本地保存的密码、网卡、版本号等结构:
- 本轮不从后端 API 中移除
- 继续保留 schema 与接口
- 继续沿用本地配置文件逻辑作为兼容过渡
- 在计划与代码实现中明确区分两类配置:
- 协议已定义项:首次回读设备、保存只写设备、不落本地
- 协议未定义项:接口先保留,继续本地逻辑
#### 原因
- 用户已确认:协议未定义寄存器的配置接口先保留,不直接移除,并继续沿用原本地配置逻辑
### 7. 重写测试,验证“纯设备真值”模式
#### 文件
- `backend/tests/test_modbus_client.py`
- `backend/tests/test_api.py`
#### 改动内容
- 更新 `test_modbus_client.py`
- 使用新版地址与长度断言
- 验证事件总寄存器 `0x00E5`
- 验证“读事件内容 `0x0100` + 成功回复 `0x0100`”完整链路
- 验证 AI/AO/AI报警新地址
- 验证线路定值包含变比参数
- 更新 `test_api.py`
- 删除“协议已定义项”对本地 JSON 写入/读取的依赖型断言
- 改成 Mock/Stub 设备回读数据断言
- 增加“首次 GET 来自设备回读、POST 只触发设备写入”的测试
- 保留“协议未定义接口”的现有本地逻辑测试,或按兼容过渡方式调整断言
#### 原因
- 当前测试体系大量基于本地 JSON 真值,必须同步迁移
---
## Assumptions & Decisions
### 已确认决策
- 只修改后端,前端本次不动
- 按新版协议文档改后端 Modbus 行为
- `backend/config` 不再保存配置参数真值
- 参数页面首次加载时,后端先从下位机读取显示
- 配置保存时,后端只动态写入下位机
- 协议未定义寄存器的现有配置接口,本次先保留
- 协议未定义寄存器的现有配置接口,本次继续沿用原本地配置逻辑
### 当前计划中的实现假设
1. `line_alarm_setting` 中的 2 个变比参数可映射到现有 `transformer_change` 结构,或需在 schema 中进一步显式化为 PT/CT 变比字段。
2. `system` 配置若协议只定义时间同步,则该接口最终可能会缩减为仅时间设置相关能力。
3. 实时数据一次值计算仍由后端负责,但 PT/CT 变比来源将变更为设备回读参数,而非本地 JSON。
4. `json_config_repo.py` 会退出“协议已定义项”的配置主链路,但仍保留给协议未定义接口使用。
---
## Verification Steps
1. 代码层验证
- 协议已定义项的配置 GET 接口都不再访问 `backend/config/*.json`
- 协议已定义项的配置 POST 接口都不再写本地 JSON
- `device_client.py` 的寄存器常量与新版文档一致
2. 自动化测试验证
- `backend/tests/test_modbus_client.py` 通过
- `backend/tests/test_api.py` 通过
- 删除或更新“协议已定义项”依赖本地配置文件真值的旧断言
- 保留或改造“协议未定义项”的本地逻辑断言
3. 联调验证
- 手工调用协议已定义项的配置 GET 接口时,能从下位机回读数据
- 手工调用协议已定义项的配置 POST 接口时,只触发 Modbus 写,不产生本地配置文件变更
- 手工调用协议未定义项接口时,仍保持当前本地逻辑可用
- 事件轮询流程符合:
- 轮询 `0x00E5`
-`0x0100`
- 成功回复 `0x0100`
4. 回归验证
- 实时数据、设备状态、报警历史、开出控制功能不因本次改造而退化
- WebSocket 轮询链路仍能持续推送数据