digitalpetri / modbus

High-performance, non-blocking, zero-buffer-copying Modbus for Java

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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

MengMengDaXiaoJi opened this issue · comments

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();
    }
  1. 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;
    }
  1. 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;
    }
  1. 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;
        });
    }

The Master instance is thread safe.

Thanks for your kind help.

The Master instance is thread safe.