YunaiV/yudao-cloud · error · RuntimeException

功能码 {} 不支持写操作

Error message

功能码 {} 不支持写操作

What it means

IotModbusTcpClientUtils.write builds a write request from the point's functionCode; createWriteRequest returns null for codes it cannot turn into a write operation (only coil-writing and register-writing codes are mapped), and this null becomes a RuntimeException '功能码 x 不支持写操作'. In other words the IoT point (thing model data point) is configured with a read-oriented function code (e.g. 02 read discrete inputs, 03/04 read registers) but a write command was issued against it.

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 477be9dd49)

Solutions

  1. Set the point's function code to a writable one: 05/06 (single) or 0F/10 (multiple) for write operations
  2. Verify the downlink command maps to the correct point identifier (not a read-only sensor point)
  3. Expose writability in your thing-model metadata so the UI blocks write attempts on read-only points

Example fix

// before: point configured with read FC
{"identifier":"pump_switch","functionCode":2,"accessMode":"rw"} // write -> RuntimeException

// after
{"identifier":"pump_switch","functionCode":5,"accessMode":"rw"} // FC5 = write single coil
Defensive patterns

Strategy: validation

Validate before calling

Set<Integer> WRITABLE_FC = Set.of(5, 6, 15, 16);
if (!WRITABLE_FC.contains(point.getFunctionCode())) {
    throw new IllegalArgumentException("点位 functionCode=" + point.getFunctionCode()
        + " 为只读,不能执行写操作 (需 05/06/0F/10)");
}

Type guard

boolean isWritableFunctionCode(Integer fc) {
    return fc != null && Set.of(5, 6, 15, 16).contains(fc);
}

Prevention

When it happens

Trigger: Calling a device thing-model service/property-set whose point has functionCode 02/03/04; point misconfigured at creation with a read FC where a write FC (05/06/0F/10) was intended; downlink command mapped to the wrong data point identifier.

Common situations: Thing model points imported with read FCs then reused as writable properties; UI letting users pick any FC for a writable point; device point identifier mismatch routing a write to a read-only point.

Related errors


AI-assisted analysis of YunaiV/yudao-cloud@477be9dd49 (2026-08-14). Data as JSON: /api/errors/2f9da9db6be93854. Report an issue: GitHub.