digitalpetri / modbus

Modbus TCP, Modbus RTU/TCP, and Modbus RTU/Serial for Java 17+.
Eclipse Public License 2.0
653 stars 222 forks source link

Could I use one common ModbusTcpMaster for multi-thread sending diffrent requests ? Would these threads be blocked ? #50

Closed MengMengDaXiaoJi closed 1 year ago

MengMengDaXiaoJi commented 1 year ago

My step is like this.

  1. create a common ModbusTcpMaster instance.

    public CompletableFuture<ModbusTcpMaster> createModbusConnector(String ipAddr, int port) {
        if (modbusMaster == null) {
            ModbusTcpMasterConfig masterConfig = new ModbusTcpMasterConfig.Builder(ipAddr).setPort(port).setTimeout(Duration.parse(TIMEOUT_DURATION)).build();
            modbusMaster = new ModbusTcpMaster(masterConfig);
        }
        return modbusMaster.connect();
    }
  2. then invoke collectModbusData for multi-thread

    public CompletableFuture<Boolean> collectModbusData(ModbusNetworkAddress address, ModbusParamWrapper paramWrapper) throws ExecutionException, InterruptedException, TimeoutException {
        List<CompletableFuture<Boolean>> futureList = new ArrayList<>();
        ModbusMasterUtil modbusMasterUtil = new ModbusMasterUtil();
        modbusMasterUtil.createModbusConnector(address.getIpAddr(), address.getPort());
        futureList.add(collectModbusData(modbusMasterUtil, paramWrapper.getCoilParamSet(), ModbusParam.CODE_READ_COIL));
        futureList.add(collectModbusData(modbusMasterUtil, paramWrapper.getDiscreteInputParamSet(), ModbusParam.CODE_READ_DISCRETE_INPUT));
        futureList.add(collectModbusData(modbusMasterUtil, paramWrapper.getHoldingRegisterParamSet(), ModbusParam.CODE_READ_HOLDING_REGISTER));
        futureList.add(collectModbusData(modbusMasterUtil, paramWrapper.getInputRegisterParamSet(), ModbusParam.CODE_READ_INPUT_REGISTER));
        CompletableFuture<Boolean> future = matchFutureList(futureList);
    //        future.whenCompleteAsync((result, throwable) -> {
    //            modbusMasterUtil.disposeModbusConnector();
    //            if (throwable != null) {
    //                future.completeExceptionally(throwable);
    //            }
    //        });
        return future;
    }
  3. collectModbusData function will create a supplyAsync process use customized thread pool "modbusExecutor".

    public CompletableFuture<Boolean> collectModbusData(ModbusMasterUtil modbusMasterUtil, ModbusParam modbusParam, int code) {
        CompletableFuture<Boolean> future = CompletableFuture.supplyAsync(() -> {
            CompletableFuture<int[]> registerFuture = null;
            switch (code) {
                case ModbusParam.CODE_READ_COIL:
                    registerFuture = modbusMasterUtil.readCoils(modbusParam.getSlaveId(), modbusParam.getAddress(), modbusParam.getQuantity());
                    break;
                case ModbusParam.CODE_READ_DISCRETE_INPUT:
                    registerFuture = modbusMasterUtil.readDiscreteInputs(modbusParam.getSlaveId(), modbusParam.getAddress(), modbusParam.getQuantity());
                    break;
                case ModbusParam.CODE_READ_HOLDING_REGISTER:
                    registerFuture = modbusMasterUtil.readHoldingRegisters(modbusParam.getSlaveId(), modbusParam.getAddress(), modbusParam.getQuantity());
                    break;
                case ModbusParam.CODE_READ_INPUT_REGISTER:
                    registerFuture = modbusMasterUtil.readInputRegisters(modbusParam.getSlaveId(), modbusParam.getAddress(), modbusParam.getQuantity());
                    break;
                default:
                    break;
            }
            if (registerFuture != null) {
                try {
                    int[] registerValues = registerFuture.get();
                    if (registerValues != null && registerValues.length > 0) {
                        modbusParam.setRegisterValues(registerValues);
                        return true;
                    } else {
                        return false;
                    }
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                } catch (ExecutionException e) {
                    throw new RuntimeException(e);
                }
            } else {
                return false;
            }
        }, modbusExecutor);
        return future;
    }
  4. finally, they will invoke into ModbusMasterUtil class and sendRequest.

    public CompletableFuture<int[]> readCoils(int slaveId, int address, int quantity) {
        CompletableFuture<ReadCoilsResponse> futureResponse = modbusMaster.sendRequest(new ReadCoilsRequest(address, quantity),
                slaveId);
        return futureResponse.thenApply(response -> {
            ByteBuf byteBuf = response.getCoilStatus();
            int[] values = new int[quantity];
            int minimum = Math.min(quantity, byteBuf.capacity() * 8);
            for (int i = 0; i < minimum; i += 8) {
                setBooleanArray(byteBuf.readUnsignedByte(), values, i, Math.min(minimum - i, 8));
            }
            ReferenceCountUtil.release(response);
            return values;
        });
    }
    public CompletableFuture<int[]> readDiscreteInputs(int slaveId, int address, int quantity) {
        CompletableFuture<ReadDiscreteInputsResponse> futureResponse = modbusMaster.sendRequest(new ReadDiscreteInputsRequest(address, quantity),
                slaveId);
        return futureResponse.thenApply(response -> {
            ByteBuf byteBuf = response.getInputStatus();
            int[] values = new int[quantity];
            int minimum = Math.min(quantity, byteBuf.capacity() * 8);
            for (int i = 0; i < minimum; i += 8) {
                setBooleanArray(byteBuf.readUnsignedByte(), values, i, Math.min(minimum - i, 8));
            }
            ReferenceCountUtil.release(response);
            return values;
        });
    }
    public CompletableFuture<int[]> readHoldingRegisters(int slaveId, int address, int quantity) {
        CompletableFuture<ReadHoldingRegistersResponse> futureResponse = modbusMaster.sendRequest(new ReadHoldingRegistersRequest(address, quantity),
                slaveId);
        return futureResponse.thenApply(response -> {
            ByteBuf byteBuf = response.getRegisters();
            int[] values = new int[quantity];
            for (int i = 0; i < byteBuf.capacity() / 2; i++) {
                values[i] = byteBuf.readUnsignedShort();
            }
            ReferenceCountUtil.release(response);
            return values;
        });
    }
    public CompletableFuture<int[]> readInputRegisters(int slaveId, int address, int quantity) {
        CompletableFuture<ReadInputRegistersResponse> futureResponse = modbusMaster.sendRequest(new ReadInputRegistersRequest(address, quantity),
                slaveId);
        return futureResponse.thenApply(response -> {
            ByteBuf byteBuf = response.getRegisters();
            int[] values = new int[quantity];
            for (int i = 0; i < byteBuf.capacity() / 2; i++) {
                values[i] = byteBuf.readUnsignedShort();
            }
            ReferenceCountUtil.release(response);
            return values;
        });
    }
kevinherron commented 1 year ago

The Master instance is thread safe.

MengMengDaXiaoJi commented 1 year ago

Thanks for your kind help.

The Master instance is thread safe.