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

  1. 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.
  2. 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.
  3. 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

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


AI-assisted analysis of YunaiV/ruoyi-vue-pro@0418084e22 (2026-08-14). Data as JSON: /api/errors/a7acd6711abbc0d8. Report an issue: GitHub.