java 集成 ModbusTCP/RTU 使用Modbus4J


自己摸索了一周并测试可用,做了一些总结,希望可以帮到你! ^ _ ^

1.导入maven

<dependencies>
	<dependency>
          <groupId>com.infiniteautomation</groupId>
          <artifactId>modbus4j</artifactId>
          <version>3.0.3</version>
      </dependency>
  </dependencies>

<repositories>
    <repository>
        <releases>
            <enabled>false</enabled>
        </releases>
        <snapshots>
            <enabled>true</enabled>
        </snapshots>
        <id>ias-snapshots</id>
        <name>Infinite Automation Snapshot Repository</name>
        <url>https://maven.mangoautomation.net/repository/ias-snapshot/</url>
    </repository>
    <repository>
        <releases>
            <enabled>true</enabled>
        </releases>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
        <id>ias-releases</id>
        <name>Infinite Automation Release Repository</name>
        <url>https://maven.mangoautomation.net/repository/ias-release/</url>
    </repository>
</repositories>

2.添加Modbus4jUtils.java

@Slf4j
public class Modbus4jUtils {
    /**
     * 工厂。
     */
    static ModbusFactory modbusFactory;

    //建立链接
    static public ModbusMaster tcpMaster;

    static {
        if (modbusFactory == null) {
            modbusFactory = new ModbusFactory();
            tcpMaster = getMaster("192.168.2.100", 10001);  // 填写要连接的TCP server
        }
    }

    /**
     * 获取master
     *
     * @return
     * @throws ModbusInitException
     */
    public static ModbusMaster getMaster(String ip, Integer port) {
        	IpParameters params = new IpParameters();
       		params.setHost(ip);
       		params.setPort(port);

			// RTU 协议-设备是modbus rtu就用他 注意哦
	        // params.setEncapsulated(true);
	        // ModbusMaster master = modbusFactory.createTcpMaster(params, true);
	        // TCP 协议
	        ModbusMaster master = modbusFactory.createTcpMaster(params, false);

	        try {
	            //设置超时时间---发现设置他没用,还会报错,所以屏蔽
				// master.setTimeout(5000);
	            //设置重连次数---发现设置他没用,还会报错,所以屏蔽
				//master.setRetries(3);
	            //初始化
	            master.init();
	        } catch (ModbusInitException e) {
	            e.printStackTrace();
	        }
	        return master;
	    }

	/**
     * 读取保持寄存器 功能码[03]
     *
     * @param start     开始地址
     * @param readLenth 读取数量
     * @return
     * @throws ModbusInitException
     */
    public static ByteQueue modbusTCP03(int slaveId, ModbusMaster tcpMaster, int start, int readLenth) throws ModbusInitException {
        //发送请求
        ModbusRequest modbusRequest = null;
        try {
            modbusRequest = new ReadHoldingRegistersRequest(slaveId, start, readLenth);//功能码03
        } catch (ModbusTransportException e) {
            e.printStackTrace();
        }
        //收到响应
        ModbusResponse modbusResponse = null;
        try {
            modbusResponse = tcpMaster.send(modbusRequest);
        } catch (ModbusTransportException e) {
            e.printStackTrace();
        }
        ByteQueue byteQueue = new ByteQueue(12);
        modbusResponse.write(byteQueue);
        System.out.println("功能码:" + modbusRequest.getFunctionCode());
        System.out.println("从站地址:" + modbusRequest.getSlaveId());
        System.out.println("开始地址:" + start);
        System.out.println("收到的响应信息大小:" + byteQueue.size());
        System.out.println("收到的响应信息值:" + byteQueue);
        return byteQueue;
    }

	/*
     * 写 多个寄存器 功能码[16] - 相当厂家文档写单个和多个。一样可以
     * WriteCoilRequest  05
     * WriteRegisterRequest 06
     * WriteRegistersRequest 16
     *
     */
    public static ByteQueue modbusTCP16(int slaveId, ModbusMaster tcpMaster, int writeOffset, short[] data) throws Exception {
        WriteRegistersRequest writeRegistersRequest = null;
        //收到响应
        ModbusResponse modbusResponse = null;
        try {
            writeRegistersRequest = new WriteRegistersRequest(slaveId, writeOffset, data);
            modbusResponse = tcpMaster.send(writeRegistersRequest);
            if (modbusResponse.isException()) {
                System.out.println("Exception response: message=" + modbusResponse.getExceptionMessage());
            } else {
                System.out.println("Success");
            }
        } catch (ModbusTransportException e) {
            e.printStackTrace();
        }
        ByteQueue byteQueue = new ByteQueue(12);
        modbusResponse.write(byteQueue);
        System.out.println("功能码:" + writeRegistersRequest.getFunctionCode());
        System.out.println("从站地址:" + writeRegistersRequest.getSlaveId());
        System.out.println("收到的响应信息大小:" + byteQueue.size());
        System.out.println("收到的响应信息值:" + byteQueue);
        return byteQueue;
    }

 
	 public static void main(String[] args) {
        try {
				// start 需要将16进制转10机制哦
	            int start = 0;
	            // 设备通讯地址-看情况需要转换会10机制哦
	            int slaveId = 1;
				// 读 电表
	            start = get16to10("aa00"); //  总电能-这个值看厂家文档
	            // 参数 2 是2个寄存器-这个值看厂家文档
	            ByteQueue byteQueue = modbusTCP03(slaveId , tcpMaster, start, 2);		            
	            // 这是把 '响应的报文' 做一些转换,更多转换参考文章下面
	            byte[] bytes1 = byteQueue.peekAll();
	            // 这里是对 '响应的报文' 截取后面4位
	            byte[] bytes2 = Arrays.copyOfRange(bytes1, bytes1.length - 4, bytes1.length);
	            System.out.println(Arrays.toString(bytes2));
	            System.out.println(toDouble(bytes2));

	            // 写 电表
	            start = get16to10("aa01"); // 电器控制值-这个值看厂家文档
	            short[] fenZha = {0x00}; // 分闸-这个值看厂家文档
	            // short[] hrzha = {(short) 0b1111111111111111}; // 合闸-这个值看厂家文档 是这个0x01/0xff ,但是不行!
	            ByteQueue byteQueue = modbusTCP16(slaveId, tcpMaster, start, fenZha);

		   } catch (Exception e) {
		            e.printStackTrace();
		   }
    	}


}

一些字节转换,肯定用得上!精髓

	/**
     * 将byte数组转换为浮点数
     */
    public static double toDouble(byte[] bytes) {
        return ByteBuffer.wrap(bytes).getFloat();
    }

    /**
     * 将byte数组转换为浮点数-并保留最后两位小数点
     */
    public static double toDouble00(byte[] bytes) {
        double f = ByteBuffer.wrap(bytes).getFloat();
        return toDouble00(f);
    }

    /**
     * double 保留最后两位小数点
     */
    public static double toDouble00(double f) {
        BigDecimal b = new BigDecimal(f);
        // 是小数点后只有两位的双精度类型数据
        double f1 = b.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
        return f1;
    }

    /**
     * 将16进制转10进制-s去掉前缀0x
     * 也可以手动改后面16参数,改自己要的字节
     */
    public static int get16to10(String s) {
        return Integer.parseInt(s, 16);
    }

    /**
     * 将byte[]转为各种进制的字符串
     *
     * @param bytes byte[]
     * @param radix 基数可以转换进制的范围,从Character.MIN_RADIX到Character.MAX_RADIX,超出范围后变为10进制
     * @return 转换后的字符串
     * <p>
     * 类型 占用 bit(位)
     * byte(字节) 8
     * short(短整型) 16
     * int(整型) 32
     * long(长整型) 64
     * float(单精度浮点型) 32
     * double(双精度浮点型) 64
     * char(字符) 16
     * boolean(布尔型) 1
     */
    public static String binary(byte[] bytes, int radix) {
        return new BigInteger(1, bytes).toString(radix);// 这里的1代表正数
    }

    /**
     * 将byte数组转换为整数
     * 转换为bit后,最左边的那位表示,符号位(有符号/无符号)
     */
    public static int bytesToInt(byte[] bs) {
        int a = 0;
        for (int i = bs.length - 1; i >= 0; i--) {
            a += bs[i] * Math.pow(255, bs.length - i - 1);
        }
        return a;
    }

    /**
     * 将byte数组转换为short
     */
    public static short bytesToshort(byte[] b) {
        short l = 0;
        for (int i = 0; i < 2; i++) {
            l <<= 8; //<<=和我们的 +=是一样的,意思就是 l = l << 8
            l |= (b[i] & 0xff); //和上面也是一样的  l = l | (b[i]&0xff)
        }
        return l;
    }

好了!以上是 读 / 写 测试,谢谢

  • 5
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值