首页 > 其他分享 >modbus4j 实现modbus tcp通讯

modbus4j 实现modbus tcp通讯

时间:2023-01-04 18:13:56浏览次数:39  
标签:modbus4j int tcp modbus master param com slaveId

1. Maven依赖

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

2. TcpMaster类,用于生成ModbusMaster主类

import com.serotonin.modbus4j.ModbusFactory;
import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.exception.ModbusInitException;
import com.serotonin.modbus4j.ip.IpParameters;
 
 
public class ModbusTcpMaster {
    private static ModbusFactory modbusFactory;
 
    static {
        if (modbusFactory == null) {
            modbusFactory = new ModbusFactory();
        }
    }
 
    /**
     * 获取Tcp master
     * @param ip
     * @param port
     * @return
     */
    public static ModbusMaster getMaster(String ip, int port) {
        IpParameters params = new IpParameters();
        params.setHost(ip);
        params.setPort(port);
        //这个属性确定了协议帧是否是通过tcp封装的RTU结构,采用modbus tcp/ip时,要设为false, 采用modbus rtu over tcp/ip时,要设为true
        params.setEncapsulated(false);
        // 参数1:IP和端口信息 参数2:保持连接激活
         ModbusMaster master = null;
         master = modbusFactory.createTcpMaster(params, true);
        try {
            //设置超时时间
            master.setTimeout(500);
            //设置重连次数
            master.setRetries(2);
            //初始化
            master.init();
        } catch (ModbusInitException e) {
            e.printStackTrace();
        }
        return master;
    }
}

 

3. 读取类Modbus4jReader

import com.serotonin.modbus4j.BatchRead;
import com.serotonin.modbus4j.BatchResults;
import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.code.DataType;
import com.serotonin.modbus4j.exception.ErrorResponseException;
import com.serotonin.modbus4j.exception.ModbusInitException;
import com.serotonin.modbus4j.exception.ModbusTransportException;
import com.serotonin.modbus4j.locator.BaseLocator;
import com.serotonin.modbus4j.msg.*;
 
 
public class Modbus4jReadUtils {
 
    /**
     * 读(线圈)开关量数据
     * 功能码为:01; 读取开关量输出点的ON/OFF状态,可以读写的布尔类型(0x)---00001 至 0xxxx – 开关量输出
     * @param slaveId slaveId-从站编号-自行约定
     * @param offset  位置
     * @return 读取值-读取多少个
     */
    public boolean[] readCoilStatus(ModbusMaster master,int slaveId, int offset, int numberOfBits)
            throws ModbusTransportException, ErrorResponseException, ModbusInitException {
 
        ReadCoilsRequest request = new ReadCoilsRequest(slaveId, offset, numberOfBits);
        ReadCoilsResponse response = (ReadCoilsResponse) master.send(request);
        boolean[] booleans = response.getBooleanData();
        return valueRegroup(numberOfBits, booleans);
    }
 
 
    /**开关数据 读取外围设备输入的开关量
     * 功能码为:02;读取开关量输入点的ON/OFF状态,只能读的布尔类型(1x)---10001 至 1xxxx – 开关量输入
     * @param slaveId-从站编号-自行约定
     * @param offset-预访问的地址-地址范围:0-255
     * @param numberOfBits-读取多少个
     * @return
     * @throws ModbusTransportException
     * @throws ErrorResponseException
     * @throws ModbusInitException
     */
    public boolean[] readInputStatus(ModbusMaster master,int slaveId, int offset, int numberOfBits)
            throws ModbusTransportException, ErrorResponseException, ModbusInitException {
        ReadDiscreteInputsRequest request = new ReadDiscreteInputsRequest(slaveId, offset, numberOfBits);
        ReadDiscreteInputsResponse response = (ReadDiscreteInputsResponse) master.send(request);
        boolean[] booleans = response.getBooleanData();
        return valueRegroup(numberOfBits, booleans);
    }
 
    /**
     * 读取保持寄存器数据
     * 功能码为:03 读取保持寄存器的数据,可以读写的数字类型(4x)---40001 至 4xxxx – 保持寄存器
     *
     **举例子说明:S7-200
     Smart PLC中,设置  [HoldStr~]=&VB1000;则对应的保持寄存器地址为VW1000\VW1002\VW10004
     **在java中对应的address为:0、1、2
     * @param slaveId slave Id-从站编号-自行约定
     * @param offset  位置
     * @param numberOfBits numberOfRegisters 寄存器个数  每个寄存器表示一个16位无符号整数 相当于一个short
     */
    public static short[] readHoldingRegister(ModbusMaster master,int slaveId, int offset, int numberOfBits)
            throws ModbusTransportException, ErrorResponseException, ModbusInitException {
        ReadHoldingRegistersRequest request = new ReadHoldingRegistersRequest(slaveId, offset, numberOfBits);
        ReadHoldingRegistersResponse response = (ReadHoldingRegistersResponse) master.send(request);
        return response.getShortData();
    }
 
 
    /**
     * 读取[03 Holding Register类型 2x]模拟量数据
     * @param slaveId slave Id
     * @param offset 位置
     * @param dataType 数据类型,来自com.serotonin.modbus4j.code.DataType
     * @return
     * @throws ModbusTransportException 异常
     * @throws ErrorResponseException 异常
     * @throws ModbusInitException 异常
     */
    public static Number readHoldingRegisterByDataType(ModbusMaster master,int slaveId, int offset, int dataType)
            throws ModbusTransportException, ErrorResponseException, ModbusInitException {
        // 03 Holding Register类型数据读取
        BaseLocator<Number> loc = BaseLocator.holdingRegister(slaveId, offset, dataType);
        Number value = master.getValue(loc);
        return value;
    }
 
    /**
     * 读取外围设备输入的数据
     * 功能码为:04 读取模拟量输入值,只能读的数字类型(3x)---30001 至 3xxxx – 模拟量输入
     *
     * 举例子说明:S7-200 Smart PLC中,模拟量输入寄存器AIW16\AIW18,则对应
     * java中对应的address为:8\9
     * @param slaveId slaveId-从站编号-自行约定
     * @param offset  位置-预访问的地址-地址范围:0-55
     */
    public short[] readInputRegisters(ModbusMaster master,int slaveId, int offset, int numberOfBits)
            throws ModbusTransportException, ErrorResponseException, ModbusInitException {
        ReadInputRegistersRequest request = new ReadInputRegistersRequest(slaveId, offset, numberOfBits);
        ReadInputRegistersResponse response = (ReadInputRegistersResponse) master.send(request);
        return response.getShortData();
    }
 
    /**
     * 批量读取 可以批量读取不同寄存器中数据
     */
    public static void batchRead(ModbusMaster master) throws ModbusTransportException, ErrorResponseException, ModbusInitException {
        BatchRead<Integer> batch = new BatchRead<Integer>();
        batch.addLocator(0, BaseLocator.holdingRegister(1, 1, DataType.TWO_BYTE_INT_SIGNED));
        batch.addLocator(1, BaseLocator.inputStatus(1, 0));
        batch.setContiguousRequests(true);
        BatchResults<Integer> results = master.send(batch);
        System.out.println("batchRead:" + results.getValue(0));
        System.out.println("batchRead:" + results.getValue(1));
    }
 
 
    /**
     * 批量读取 可以批量读取不同寄存器中数据
     */
    public static void batchReadTest(ModbusMaster master,int slaveId, int offset, int dataType) throws ModbusTransportException, ErrorResponseException, ModbusInitException {
        BatchRead<Integer> batch = new BatchRead<Integer>();
//        BaseLocator<Number> loc = BaseLocator.holdingRegister(slaveId, offset, dataType);
//        Number value = master.getValue(loc);
        batch.addLocator(0, BaseLocator.holdingRegister(1, 2, DataType.TWO_BYTE_INT_SIGNED));
        batch.addLocator(1, BaseLocator.inputStatus(1, 0));
        batch.addLocator(2, BaseLocator.holdingRegister(slaveId, offset, dataType));
        batch.setContiguousRequests(true);
        BatchResults<Integer> results = master.send(batch);
        System.out.println("batchRead:" + results.getValue(0));
        System.out.println("batchRead:" + results.getValue(1));
        System.out.println("batchRead:" + results.getValue(2));
    }
 
 
    /**
     * 数据重组
     * @param numberOfBits
     * @param values
     * @return
     */
    private boolean[] valueRegroup(int numberOfBits, boolean[] values) {
        boolean[] bs = new boolean[numberOfBits];
        int temp = 1;
        for (boolean b : values) {
            bs[temp - 1] = b;
            temp++;
            if (temp > numberOfBits)
                break;
        }
        return bs;
    }
}

 

4. 写入类Modbus4jWriter

import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.exception.ErrorResponseException;
import com.serotonin.modbus4j.exception.ModbusInitException;
import com.serotonin.modbus4j.exception.ModbusTransportException;
import com.serotonin.modbus4j.locator.BaseLocator;
import com.serotonin.modbus4j.msg.*;
 
 
public class Modbus4jWriteUtils {
 
    /**
     * 写单个(线圈)开关量数据
     * 功能码为:05,开关量输出点Q置位或复位,写入数据到真机的DO类型的寄存器上面,可以读写的布尔类型(0x)
     * @param slaveId     slave的ID
     * @param writeOffset 位置-预访问的地址-地址范围:0-255
     * @param writeValue  值-置位则为1,复位则为0
     * @return 是否写入成功
     */
    public boolean writeCoil(ModbusMaster master,int slaveId, int writeOffset, boolean writeValue)
            throws ModbusTransportException, ModbusInitException {
        // 创建请求
        WriteCoilRequest request = new WriteCoilRequest(slaveId, writeOffset, writeValue);
        // 发送请求并获取响应对象
        WriteCoilResponse response = (WriteCoilResponse) master.send(request);
        return !response.isException();
    }
 
    /**
     * 写多个开关量数据(线圈)
     * 功能码为:0F,写多个开关量数据(线圈)
     * @param slaveId     slaveId
     * @param startOffset 开始位置
     * @param bdata       写入的数据
     * @return 是否写入成功
     */
    public boolean writeCoils(ModbusMaster master,int slaveId, int startOffset, boolean[] bdata)
            throws ModbusTransportException, ModbusInitException {
        // 创建请求
        WriteCoilsRequest request = new WriteCoilsRequest(slaveId, startOffset, bdata);
        // 发送请求并获取响应对象
        WriteCoilsResponse response = (WriteCoilsResponse) master.send(request);
        return !response.isException();
 
    }
 
    /***
     *  保持寄存器写单个
     *  功能码为:06,将数据写入至V存储器, 数据到真机,数据类型是Int,可以读写的数字类型(4x)
     * @param slaveId slaveId
     * @param writeOffset 开始位置
     * @param writeValue 写入的数据
     */
    public static boolean writeRegister(ModbusMaster master,int slaveId, int writeOffset, short writeValue)
            throws ModbusTransportException, ModbusInitException {
        // 创建请求对象
        WriteRegisterRequest request = new WriteRegisterRequest(slaveId, writeOffset, writeValue);
        // 发送请求并获取响应对象
        WriteRegisterResponse response = (WriteRegisterResponse) master.send(request);
        return !response.isException();
 
    }
 
    /**
     * 保持寄存器写入多个模拟量数据
     * 功能码为:16,将数据写入至多个V存储器,写入数据到真机,数据类型是short[],可以读写的数字类型(4x)
     * @param slaveId     modbus的slaveID
     * @param startOffset 起始位置偏移量值
     * @param sdata       写入的数据
     * @return 返回是否写入成功
     */
    public boolean writeRegisters(ModbusMaster master,int slaveId, int startOffset, short[] sdata)
            throws ModbusTransportException, ModbusInitException {
        // 创建请求对象
        WriteRegistersRequest request = new WriteRegistersRequest(slaveId, startOffset, sdata);
        // 发送请求并获取响应对象
        WriteRegistersResponse response = (WriteRegistersResponse) master.send(request);
        return !response.isException();
    }
 
    /**
     * 根据类型写数据(如:写入Float类型的模拟量、Double类型模拟量、整数类型Short、Integer、Long)
     *
     * @param value    写入值
     * @param dataType com.serotonin.modbus4j.code.DataType
     */
    public static void writeHoldingRegister(ModbusMaster master,int slaveId, int offset, Number value, int dataType)
            throws ModbusTransportException, ErrorResponseException, ModbusInitException {
        // 类型
        BaseLocator<Number> locator = BaseLocator.holdingRegister(slaveId, offset, dataType);
        master.setValue(locator, value);
    }
}

 

标签:modbus4j,int,tcp,modbus,master,param,com,slaveId
From: https://www.cnblogs.com/fangts/p/17025638.html

相关文章

  • linux抓包工具tcpdump
    1、安装tcpdumpyum-yinstalltcpdump#查看指定网卡的设备,显示端口号对应服务tcpdump-nnt-iens192|head-10#查看ens33网卡设备,对应22端口服务的传输信息(-t不显示......
  • tcpdump抓包命令
    简介:tcpdump是一个可以根据需求来抓取网络上传输的数据包的工具常用的命令选项有:-c:设定抓取的数量-i:指定监听的网口-w:将抓取的数据包保存到文件-s:截取报文的内容,默认......
  • TCP
    引言TCP提供面向连接的、可靠的字节流服务在一个TCP连接中,仅有两方进行彼此通信TCP通过下列方式提供可靠性应用数据被分割成TCP认为最适合发送的数据块。这和UDP不同......
  • 了解一下基本的tcp代理配置
    我们首先用一个简单例子了解一下基本的tcp代理配置worker_processes1;#nginxworker数量error_loglogs/error.log;#指定错误日志文件路径events{......
  • TCP IP网络编程(13) Linux下epoll与多线程
    优于select的epoll1.epoll的理解与应用  select服用方法由来已久,在《TCP/IP网络编程(6)》中,介绍了如何使用select方法实现IO复用。但是利用该技术后,无论如何优化程......
  • TCP和UDP协议之间的区别,前端基础面试题
    前端基础面试题,TCP和UDP协议之间的区别tcp和udp作为传输层的两个协议,主要区别:1,tcp是面向链接的,(http协议握手)就类似打电话要先建立拨号,在进行链接。而udp在发送前......
  • 第十五章《网络编程》第3节:基于TCP协议的网络编程
    ​如果希望开发一个类似于QQ那样的即时通信软件,就必须使用基于TCP协议的编程技术。基于TCP协议的编程可以实现在通信两端建立虚拟网络链路,这样的话通信两端的程序就能通过虚......
  • 一个TCP连接可以发送多少个HTTP请求?
    想要回答这个问题先要知道这5个问题:1.TCP连接是否会在一个请求结束后就断开链接?在HTTP1.0中一个服务器在发送完一个http响应后会断开tcp链接,每次请求都会重新建立和断开t......
  • TCP的三次握手四次挥手详解
    想要了解TCP的三次握手和四次挥手,首先要了解TCP的头部结构TCP的头部结构如下16位源端口号:客户端地址信息16位目标端口号:服务端地址信息32位序列号:请求报文端数据流子节开......
  • TCP IP网络编程 IO分离
    IO流分离  在《套接字与标准IO》中介绍,调用fopen函数打开文件后,可以与文件进行交换数据。因此可以说调用fopen函数之后创建了"流(stream)",此处的"指"数据流动,但是通常......