YunaiV/ruoyi-vue-pro · error · RuntimeException
功能码 {} 不支持写操作
Error message
功能码 {} 不支持写操作 What it means
Thrown by write() when createWriteRequest() returns null, which happens for read-only function codes FC_READ_DISCRETE_INPUTS (2) and FC_READ_INPUT_REGISTERS (4). By Modbus specification, discrete inputs and input registers are read-only and cannot be written. The framework's write path deliberately keys off the READ-style function code stored in the point config, and returns null (rather than throwing inside createWriteRequest) for the genuinely read-only types.
Source
Thrown at yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbus/common/utils/IotModbusTcpClientUtils.java:110
* 写入 Modbus 数据
*
* @param connection Modbus 连接
* @param slaveId 从站地址
* @param point 点位配置
* @param values 要写入的值
* @return 是否成功
*/
public static Future<Boolean> write(IotModbusTcpClientConnectionManager.ModbusConnection connection,
Integer slaveId,
IotModbusPointRespDTO point,
int[] values) {
return connection.executeBlocking(tcpConnection -> {
try {
// 1. 创建请求
ModbusRequest request = createWriteRequest(point.getFunctionCode(),
point.getRegisterAddress(), point.getRegisterCount(), values);
if (request == null) {
throw new RuntimeException("功能码 " + point.getFunctionCode() + " 不支持写操作");
}
request.setUnitID(slaveId);
// 2. 执行事务(请求)
ModbusTCPTransaction transaction = new ModbusTCPTransaction(tcpConnection);
transaction.setRequest(request);
transaction.execute();
return true;
} catch (Exception e) {
throw new RuntimeException(String.format("Modbus 写入失败 [slaveId=%d, identifier=%s, address=%d]",
slaveId, point.getIdentifier(), point.getRegisterAddress()), e);
}
});
}
/**
* 创建读取请求
*/View on GitHub (pinned to 0418084e22)
Solutions
- Change the point's functionCode to a writable type: FC3 (holding register, mapped to write FC6/16) for analog values or FC1 (coil, mapped to write FC5/15) for binary values.
- If the value is genuinely read-only, remove or reject the downstream write command at the service layer instead of letting it reach the Modbus write path.
- Validate writability before calling write (see defense).
Example fix
// before: point configured with read-only FC4 (input register), write throws // after: use FC3 (holding register) so the framework maps it to a write request point.setFunctionCode(3); // holding register — writable
Defensive patterns
Strategy: validation
Validate before calling
// before writing, check the point's function code is writable
boolean isWritableFc(Integer fc) {
// framework maps FC1 (coil) and FC3 (holding register) to write requests
return fc != null && (fc == 1 || fc == 3);
}
if (!isWritableFc(point.getFunctionCode())) {
return Future.failedFuture(new IllegalStateException(
"点位 " + point.getIdentifier() + " 功能码 " + point.getFunctionCode() + " 不支持写操作"));
} Type guard
boolean isWritablePoint(IotModbusPointRespDTO point) {
Integer fc = point.getFunctionCode();
return fc != null && (fc == 1 || fc == 3);
} Prevention
- Mark read-only points in the device model and reject write commands at the service layer before they reach Modbus.
- Use FC1 (coil) / FC3 (holding register) for any point that must accept writes.
- Validate functionCode writability when loading/refreshing device config so misconfiguration is caught early.
When it happens
Trigger: A downstream write/command targets a point whose config functionCode is 2 (discrete input) or 4 (input register); the device config incorrectly marks a sensor point as an input register when a holding register was intended.
Common situations: Operator configures a sensor point using FC4 (input register) but a rule/scene later tries to calibrate or write it; product model maps an actuator to the wrong Modbus table; misconfigured point type.
Related errors
- 不支持的功能码: {}
- [createProtocol][协议实例 %s 的协议类型 %s 暂不支持]
- Modbus 读取失败 [slaveId=%d, identifier=%s, functionCode=%d, add
- Modbus 写入失败 [slaveId=%d, identifier=%s, address=%d]
- MQTT Client 启动失败: 连接 Broker 失败
AI-assisted analysis of YunaiV/ruoyi-vue-pro@0418084e22 (2026-08-14).
Data as JSON: /api/errors/a7acd6711abbc0d8.
Report an issue: GitHub.