提示:可以使用Modbus slave工具模拟Modbus数据站并添加一些数据。
1、pom引入依赖

<dependency>
	 <groupId>com.intelligt.modbus</groupId>
	 <artifactId>jlibmodbus</artifactId>
	 <version>1.2.9.7</version>
</dependency>

2、创建modbus主机连接

public class JlibModbusBase {
    static ModbusMaster modbusMaster;
    public static void initJlibModbusMaster(String ip) {
        // 如果连接不存在,创建一个主机连接
        if (ObjectUtils.isEmpty(modbusMaster)) {
            // 设置主机TCP参数
            TcpParameters tcpParameters = new TcpParameters();
            // 设置TCP的ip地址-本地地址
            InetAddress address = null;
            try {
                address = InetAddress.getByName(ip);
            } catch (UnknownHostException e) {
                throw new RuntimeException(e);
            }
            tcpParameters.setHost(address);
            // TCP设置长连接
            tcpParameters.setKeepAlive(true);
            // TCP设置端口,这里设置是默认端口502
            tcpParameters.setPort(502);
            modbusMaster = ModbusMasterFactory.createModbusMasterTCP(tcpParameters);
            Modbus.setAutoIncrementTransactionId(true);
        }
    }
    public static void release() {
        if (modbusMaster != null) {
            try {
                modbusMaster.disconnect();
            } catch (ModbusIOException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

3、通过连接获取功能码0x04与0x03的数据

public class JlibModbusBaseTool {
    /**
     * "功能码0x04"
     * 读取HoldingRegister数据
     *
     * @param slaveId  寄存器地址ID
     * @param offset   寄存器读取开始地址
     * @param quantity 读取寄存器数量
     * @return int数组
     */
    public static int[] readInputRegisters(int slaveId, int offset, int quantity) {
        try {
            return modbusMaster.readInputRegisters(slaveId, offset, quantity);
        } catch (ModbusProtocolException | ModbusNumberException | ModbusIOException e) {
            throw new RuntimeException(e);
        } finally {
            release();
        }
    }
    /**
     * "功能码0x03"
     * 读取HoldingRegister数据
     *
     * @param slaveId  寄存器地址ID
     * @param offset   寄存器读取开始地址
     * @param quantity 读取寄存器数量
     * @return int数组
     */
    public static int[] readHoldingRegisters(int slaveId, int offset, int quantity) {
        try {
            return modbusMaster.readHoldingRegisters(slaveId, offset, quantity);
        } catch (ModbusProtocolException | ModbusNumberException | ModbusIOException e) {
            throw new RuntimeException(e);
        } finally {
            release();
        }
    }

4、根据具体业务情况,进行方法的调用与数据解析。在本人实际业务中,存在两个IP地址(实际中上文的主机连接,可以根据IP的不同,实时创建不同的连接)。因为读取寄存器数量时,最多可以读取124个寄存器。所以根据业务实际情况,进行多次的循环取数。

//读取的寄存器数量,一次读取两台设备寄存器数据
//因为实际使用中数据为float数据,下文还会进行数据的转换
int quantity = 96;
int[] result = null;
int salveId = 0;
//获取数据
result = JlibModbusBaseTool.readInputRegisters(salveId,offset,quantity);
//首先将int[]转化为集合,然后将读取到的数据集合,分成两个集合
List<Integer> intList = Arrays.stream(result).boxed().toList();
int LIMIT_TOW = 48;
ArrayList<List<Integer>> dataList = new ArrayList<>();
Stream.iterate(0, n -> n + 1).limit(countStep(intList.size(), LIMIT_TOW)).forEach(i -> {
  dataList.add(intList.stream().skip(i * LIMIT_TOW).limit(LIMIT_TOW).collect(Collectors.toList()));
});
//计算分组数量
public static Integer countStep(Integer size,int limt) {
   return (size + limt - 1) / limt;
}
//然后读取int[]数据,转化为浮点类型
// 两个数据一组,组成16进制字符转,转化为float数据类型
public static CcElectricityMeterNew convertFloat(int[] array, CcElectricityMeterNew meterNew){
   for (int i = 0; i < array.length; i++) {
       if ((i+1)%2==0) {
           int first = array[i-1];
           int second = array[i];
           //在进行字符串转化时,转化的16进制会存在缺少一位的情况,进行左侧补零操作
           String hexStr = addZeroForStr(Integer.toHexString(first),4,1)+""+addZeroForStr(Integer.toHexString(second),4,1);
           System.out.println(new BigDecimal(sfloat(hexStr)));
       }
   }
   return meterNew;
}
//将字符串类型的16进制数据,转化为float字符串
private static String sfloat(String str){
      Float value =  Float.intBitsToFloat(new BigInteger(str, 16).intValue());
      return String.valueOf(value);
  }

/**
  * 给字符串的左补0或右补0
  * @param str  要处理的字符串
  * @param length 补0后字符串总长度
  * @return 返回补零字符串
  */
 public static String addZeroForStr(String str, int length,int type) {
     int strLen = str.length();
     if (strLen < length) {
         while (strLen < length) {
             StringBuffer sb = new StringBuffer();
           		if(type==1){
                    // 左补0
                    sb.append("0").append(str);
                }else if(type==2){
                    //右补0
                    sb.append(str).append("0");
                }
             str = sb.toString();
             strLen = str.length();
         }
     }
     return str;
 }

备注:使用jlibmodbus读取Modbus TCP数据是非常简单的,在使用中需要根据自己需要的数据类型进行转化;同时一次获取寄存器的数量需要注意,合理进行循环的数据获取。在进行modbus数据获取时,碰到一个问题,在大量进行重复连接数据获取时,modbus存在Connection reset 的问题,导致数据获取无法进行后续数据获取中断,暂时没有找到问题的原因,如果有知道原理的大神,希望给与解答!!!