YunaiV/ruoyi-vue-pro · error · RuntimeException

Modbus 写入失败 [slaveId=%d, identifier=%s, address=%d]

Error message

Modbus 写入失败 [slaveId=%d, identifier=%s, address=%d]

What it means

Wraps ANY exception during a Modbus TCP write transaction inside executeBlocking: createWriteRequest, transaction.execute(), or setUnitID. Like the read sibling (error 44), j2mod throws on connection loss, slave timeout, illegal data address/value exception responses, etc. The message includes slaveId/identifier/registerAddress.

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:120

                                        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);
            }
        });
    }

    /**
     * 创建读取请求
     */
    @SuppressWarnings("EnhancedSwitchMigration")
    private static ModbusRequest createReadRequest(Integer functionCode, Integer address, Integer count) {
        switch (functionCode) {
            case FC_READ_COILS:
                return new ReadCoilsRequest(address, count);
            case FC_READ_DISCRETE_INPUTS:
                return new ReadInputDiscretesRequest(address, count);
            case FC_READ_HOLDING_REGISTERS:
                return new ReadMultipleRegistersRequest(address, count);
            case FC_READ_INPUT_REGISTERS:

View on GitHub (pinned to 0418084e22)

Solutions

  1. Inspect the wrapped cause (getCause()) to distinguish a Modbus exception response (illegal address/value) from a network failure (connection reset/timeout).
  2. Verify the device is online and slaveId/registerAddress match the writable register map.
  3. Confirm values[] length matches point.registerCount and each value fits the register (0-65535 for holding registers, 0/1 for coils).
  4. Reproduce with an independent Modbus write tool to isolate gateway vs device.
  5. Add retry-with-backoff for transient connection drops.
Defensive patterns

Strategy: try-catch

Validate before calling

// before writing, validate connection + address range + value bounds
if (!connection.isOpen()) {
    return Future.failedFuture(new IllegalStateException("Modbus 连接未打开"));
}
if (point.getRegisterAddress() == null || point.getRegisterAddress() < 0) {
    return Future.failedFuture(new IllegalArgumentException("寄存器地址非法"));
}
for (int v : values) {
    if (point.getFunctionCode() == 1) { if (v != 0 && v != 1) return Future.failedFuture(...); }
    else if (v < 0 || v > 0xFFFF) { return Future.failedFuture(new IllegalArgumentException("寄存器值越界")); }
}

Try / catch

// handle write failure; classify and retry only transient errors
IotModbusTcpClientUtils.write(connection, slaveId, point, values)
    .onFailure(e -> {
        Throwable root = e.getCause() != null ? e.getCause() : e;
        if (root instanceof java.net.SocketException) {
            // transient — retry once after reconnect
        } else {
            // slave exception (illegal address/value) — do not retry; surface to user
        }
    });

Prevention

When it happens

Trigger: Slave offline or connection dropped mid-write; registerAddress out of range; the slave rejects the value (illegal data value exception); network interruption; writing to a read-only address on the device; count/values mismatch causing a malformed request.

Common situations: Device offline when a command is issued; writing an out-of-range value; device reboot during write; actuator address misconfigured; device rejects value beyond its accepted range.

Related errors


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