Modbus 写功能码切换专题
0x10 → 0x06 自动降级机制 · 实现细节与代码索引
当前 Modbus 驱动写保持寄存器时统一使用 0x10(Write Multiple Registers,写多个寄存器) 功能码。但部分 Modbus 从站设备不支持 0x10,仅支持 0x06(Write Single Register,写单个寄存器)。
这导致平台对这些设备下发控制指令时,会收到超时或异常响应,控制失效。需要增加功能码配置和自动切换能力,以兼容这类设备。
本实现参考 1.4主线/ibms-driver/ibms-driver-modbus-tcp 的写功能码切换设计,并做了两项关键增强:
场景 A(超时)不再立即降级,而是重试 3 次后才降级,避免网络抖动误判。
只有 0x06 实际写入成功才最终确认切换,防止降级误判。
为支持功能码切换,Modbus 驱动实例新增两项属性(写入 iot_driver_attribute):
| 属性 | attr_name | 类型 | 默认值 | 说明 |
|---|---|---|---|---|
| 写功能码设定 | writeFunctionCode | string | "0x10" | 控制下置使用哪个功能码:0x10(写多个)/ 0x06(写单个) |
| 写功能码切换模式 | writeCodeSwitchMode | string | "auto" | auto(自动降级)/ manual(手动,不降级) |
默认 0x10 + auto:优先用高效的 0x10 批量写,遇不支持的设备自动降级为 0x06,对用户完全透明。
由 ModbusTcpServer.writeRegisterWithFallback() 实现,区分两种失败场景:
| 场景 | 触发条件 | 处理 |
|---|---|---|
| 场景 A:超时无响应 | ModbusTransportException | 重试 3 次(每次 reConnected() + retryInterval)→ 全失败才降级 0x06 |
| 场景 B:异常响应 | 返回 false(isException()) | 直接降级 0x06(不重试) |
不重连不重试"] SB2 --> SB3{0x06 成功?} SB3 -->|是| Persist SB3 -->|否| FailB(["失败 ✗"]) SB1 -->|否 manual| FailB end style Start fill:#e8f0fe,stroke:#007aff,color:#1c1c1e style Done fill:#e8f8f0,stroke:#34c759,color:#1c1c1e style Persist fill:#e8f8f0,stroke:#34c759,stroke-width:2px,color:#1c1c1e style FailA fill:#fdecee,stroke:#ff3b30,color:#1c1c1e style FailB fill:#fdecee,stroke:#ff3b30,color:#1c1c1e
switchWriteFunctionCode() 只在 0x06 实际写入成功(isSuccess==true)时才调用,更新内存 volatile 对象 + 异步写平台库。这避免了因偶发网络问题导致的误切换——只有设备真正响应 0x06 成功,才认为它「确实只支持 0x06」。
功能码切换相关的常量集中在 constant/ValueConstant.java:
| 常量 | 值 | 用途 |
|---|---|---|
WRITE_CODE_0X10 | "0x10" | 写多个寄存器功能码 |
WRITE_CODE_0X06 | "0x06" | 写单个寄存器功能码 |
SWITCH_MODE_AUTO | "auto" | 自动降级模式 |
WRITE_TIMEOUT_MAX_RETRY | 3 | 超时最大重试次数 |
本特性涉及的代码改动分布在以下文件(均为 Modbus 驱动包内):
| 文件 | 修改内容 |
|---|---|
entity/ModbusTcpDriverInstance.java | 新增 writeFunctionCode(volatile)、writeCodeSwitchMode 字段 |
constant/ValueConstant.java | 新增 WRITE_CODE_0X10/WRITE_CODE_0X06/SWITCH_MODE_AUTO/WRITE_TIMEOUT_MAX_RETRY=3 |
util/BuildBean.java | buildDriverInstance() 读取新属性(默认 0x10/auto) |
server/ModbusTcpServer.java | write() 的 HOLDING 分支调 writeRegisterWithFallback();新增 writeSingleRegister() / switchWriteFunctionCode();修复 writeRegister() NPE |
base/SendService.java + 实现 | updateWriteFunctionCode() 平台同步(异步写库) |
// ModbusTcpServer.java — 功能码回退主入口 private boolean writeRegisterWithFallback(String str, List<ModbusTcpPoint> list) // 降级到单寄存器写(0x06),逐寄存器写入 private boolean writeSingleRegister(String str, List<ModbusTcpPoint> list) // 功能码降级确认(仅 0x06 成功时调用) private void switchWriteFunctionCode(String newFc)
对于已部署的存量项目,Modbus 驱动已存在但缺少这两项新属性。需执行属性升级 SQL 为每个项目的 Modbus 驱动补充属性行:
- 脚本位置:同目录
modbus写功能码切换_属性升级SQL.sql - 存储过程:
proc_upgrade_modbus_write_function_code() - 特性:幂等——按项目遍历
sys_project,已存在则跳过,可重复执行
| attr_name | attr_sort | 可选值 | 默认 |
|---|---|---|---|
writeFunctionCode | 13 | 写多个寄存器(0x10) / 写单个寄存器(0x06) | 0x10 |
writeCodeSwitchMode | 14 | auto / manual | auto |
完整写入 iot_driver_attribute 的全部列:driver_id, attr_display_name, attr_name, attr_description, attr_sort, data_type, option_check_type, option_element_type, option_required, option_length, option_max, option_min, option_select_data, project_id, driver_name, default_value, linkage_trigger, linkage_is_triggered。
本文档是 《Modbus TCP 驱动功能设计》 的实现细节专题,两者互补:
| 维度 | 主文档(功能设计) | 本文档(写功能码专题) |
|---|---|---|
| 定位 | 面向读者的全面讲解 | 面向维护者的实现索引 |
| 范围 | Modbus 驱动全部(连接/读/写/属性/FAQ) | 仅写功能码切换这一个特性 |
| 侧重 | 原理图解、流程时序、配置表 | 代码位置、常量、SQL 升级、方法签名 |
| 读者 | 想了解驱动如何工作的人 | 要修改/排查此特性的开发者 |
想理解「为什么」和「怎么用」→ 看主文档第 7 节;想知道「改了哪些代码」「怎么升级存量库」→ 看本文档。