# 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 轮询链路仍能持续推送数据