YunaiV/ruoyi-vue-pro · error · RuntimeException

Modbus 读取失败 [slaveId=%d, identifier=%s, functionCode=%d, add

Error message

Modbus 读取失败 [slaveId=%d, identifier=%s, functionCode=%d, address=%d, count=%d]

What it means

Wraps ANY exception thrown during a Modbus TCP read transaction inside the connection's executeBlocking block: createReadRequest, transaction.execute(), or extractValues. j2mod's ModbusTCPTransaction throws on TCP connection loss, slave timeout, a Modbus exception response (illegal function/address/data value), or when the response object cannot be cast to the expected response subclass. The wrapping preserves slaveId/identifier/functionCode/address/count for diagnosis.

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

                                      Integer registerAddress,
                                      Integer registerCount,
                                      String identifier) {
        return connection.executeBlocking(tcpConnection -> {
            try {
                // 1. 创建请求
                ModbusRequest request = createReadRequest(functionCode, registerAddress, registerCount);
                request.setUnitID(slaveId);

                // 2. 执行事务(请求)
                ModbusTCPTransaction transaction = new ModbusTCPTransaction(tcpConnection);
                transaction.setRequest(request);
                transaction.execute();

                // 3. 解析响应
                ModbusResponse response = transaction.getResponse();
                return extractValues(response, functionCode);
            } catch (Exception e) {
                throw new RuntimeException(String.format("Modbus 读取失败 [slaveId=%d, identifier=%s, functionCode=%d, address=%d, count=%d]",
                        slaveId, identifier, functionCode, registerAddress, registerCount), e);
            }
        });
    }

    /**
     * 写入 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) {

View on GitHub (pinned to 0418084e22)

Solutions

  1. Inspect the wrapped cause (RuntimeException.getCause()) — a j2mod ExceptionResponse / ModbusException tells whether it is a slave exception (illegal address) vs network (connection reset).
  2. Verify the device is online and the TCP connection is up; ping/Telnet the device.
  3. Confirm slaveId, registerAddress, registerCount and functionCode against the device's register map datasheet.
  4. Reproduce with an independent Modbus tool (e.g. modpoll, QModMaster) reading the same slaveId/address/FC to isolate gateway vs device.
  5. Tune j2mod transaction timeout/retries if the device is slow.
Defensive patterns

Strategy: try-catch

Validate before calling

// before reading, confirm the connection is alive and the FC/address are valid
if (!connection.isOpen()) {
    return Future.failedFuture(new IllegalStateException("Modbus 连接未打开"));
}
if (functionCode == null || functionCode < 1 || functionCode > 4) {
    return Future.failedFuture(new IllegalArgumentException("读取功能码非法: " + functionCode));
}

Type guard

// valid read function codes only
boolean isValidReadFc(Integer fc) {
    return fc != null && fc >= 1 && fc <= 4;
}

Try / catch

// handle read failure on the returned Future; classify transient vs permanent
IotModbusTcpClientUtils.read(connection, slaveId, point)
    .onFailure(e -> {
        Throwable root = e.getCause() != null ? e.getCause() : e;
        if (root instanceof java.net.SocketException || root instanceof java.net.SocketTimeoutException) {
            // transient — schedule a retry after backoff
        } else {
            // permanent (illegal address / slave exception) — mark point as error, alert
        }
    });

Prevention

When it happens

Trigger: Slave device offline or TCP connection dropped; wrong slaveId; registerAddress/count out of the device's valid range; function code not implemented by the slave; network interruption mid-transaction; slave returns a Modbus exception code (e.g. 0x83 = illegal function on FC3).

Common situations: Device powered off; misconfigured slaveId or register map; flaky serial-to-TCP converter; polling a register the device does not expose; device reboot during poll; j2mod transaction timeout too short for a slow instrument.

Related errors


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