Modbus通讯协议——Java通过Jlibmodbus实现Modbus Master(主机)TCP/RTU的Read;Slave(从机)TCP/RTU的Write

一、首先了解一下Java实现Modbus的三种jar包

只做参考,有能力的话还是建议根据需求自行编写协议工具类
1. modbus4: 支持Modbus-RTU、 Modbus-ASCl.Modbus-TCP三种协议,支持Modbus-RTU over Senal, Modbus.RTU over TCPUDP、 Modbus-ASCl over sera
和 Modbus-TCP over TCP/UDP。但是该工具是同步的不支持异步,实时性要求不强可以使用。
2. ililbmodbus:支持Modbus-RTU和Modbus-TCP两种协议,支持Modbus-RTU over Serial、Modbus-RTU over TCP
Modbus-TCP over TCP,Modbus-TCP内部通
过socket实现支持异步。Modbus-RTU Serial通过RXTX实现。
modbus-master-tcp:支持Modbus-TCP一种协议,支持Modbus-TCP over TCP,内部通过netty实现支持异步。
可以执行扩展使其支持Modbus-RTU over TCP和Moc
bus-RTU over Serial
以上三个工具包的所有连接都没有断线重连功能,所以使用时需要自行解决断线重

二、主要针对Jlibmodbus实现Modbus的主机与从机’

1、导入依赖

另附Maven仓库地址:https://mvnrepository.com/

        <!-- https://mvnrepository.com/artifact/com.intelligt.modbus/jlibmodbus -->
        <dependency>
            <groupId>com.intelligt.modbus</groupId>
            <artifactId>jlibmodbus</artifactId>
            <version>1.2.9.10</version>
        </dependency>

2、Master主机模式:

1)、Modbus-TCP

import com.intelligt.modbus.jlibmodbus.Modbus;
import com.intelligt.modbus.jlibmodbus.master.ModbusMaster;
import com.intelligt.modbus.jlibmodbus.master.ModbusMasterFactory;
import com.intelligt.modbus.jlibmodbus.tcp.TcpParameters;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.InetAddress;

public class JlibmodbusMasterTcpReader {
    public static void main(String[] args) throws Exception {
        TcpParameters tcpParameters = new TcpParameters();
        tcpParameters.setHost(InetAddress.getByName("127.0.0.1"));
        tcpParameters.setPort(502);
        tcpParameters.setKeepAlive(true);
        int[] num = {1};
        for (int n : num){
            int[] ints = new JlibmodbusMasterTcpReader().modbusReads(n,tcpParameters);
            System.out.println(ints[0]);
            int second = ints[0];
            int first = ints[1];
            System.out.println(ints[1]);
//            for (int m : ints){
//                System.out.print(m);
//            }
            //在进行字符串转化时,转化的16进制会存在缺少一位的情况,进行左侧补零操作
            String hexStr = addZeroForStr(Integer.toHexString(first),4,1)+""+addZeroForStr(Integer.toHexString(second),4,1);
            System.out.println(new BigDecimal(sfloat(hexStr)));
            System.out.println();
        }
    }

    public int[] modbusReads(int serverAddress, TcpParameters tcpParameters) throws Exception {

        ModbusMaster master = ModbusMasterFactory.createModbusMasterTCP(tcpParameters);
        master.setResponseTimeout(3000);
        master.connect();
        Modbus.setAutoIncrementTransactionId(true);
        int[] result = master.readInputRegisters(serverAddress,0, 10);
        master.disconnect();
        return result;
    }
    //将字符串类型的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;
    }
}

2)、Modbus-RTU

import com.intelligt.modbus.jlibmodbus.Modbus;
import com.intelligt.modbus.jlibmodbus.master.ModbusMaster;
import com.intelligt.modbus.jlibmodbus.master.ModbusMasterFactory;
import com.intelligt.modbus.jlibmodbus.serial.*;

import java.io.*;

public class JlibmodbusMasterRtuReader {

    public static void main(String[] args) throws Exception {
        SerialParameters params = new SerialParameters();
        params.setDevice("COM2"); // 串口名称
        params.setBaudRate(SerialPort.BaudRate.getBaudRate(9600)); // 波特率
        params.setDataBits(8); // 数据位
        params.setStopBits(1); // 停止位
        params.setParity(SerialPort.Parity.getParity(0)); // 无校验

        ModbusMaster master = ModbusMasterFactory.createModbusMasterRTU(params);
        Modbus.setAutoIncrementTransactionId(true);
        master.connect();

        int[] registers = master.readHoldingRegisters(1, 0, 10);

        BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("C:\\Users\\user\\Desktop\\data.txt"));
        for (int reg : registers){
            System.out.println(reg);
            bufferedWriter.write(reg + " ");
        }
        bufferedWriter.newLine();
        bufferedWriter.close();

        master.disconnect();
    }

}

3、Slave-从机模式

1)、Modbus-TCP

import com.intelligt.modbus.jlibmodbus.Modbus;
import com.intelligt.modbus.jlibmodbus.data.DataHolder;
import com.intelligt.modbus.jlibmodbus.data.ModbusCoils;
import com.intelligt.modbus.jlibmodbus.data.ModbusHoldingRegisters;
import com.intelligt.modbus.jlibmodbus.exception.IllegalDataAddressException;
import com.intelligt.modbus.jlibmodbus.exception.IllegalDataValueException;
import com.intelligt.modbus.jlibmodbus.slave.ModbusSlave;
import com.intelligt.modbus.jlibmodbus.slave.ModbusSlaveFactory;
import com.intelligt.modbus.jlibmodbus.tcp.TcpParameters;

import java.net.InetAddress;
import java.util.ArrayList;
import java.util.List;

public class JlibmodbusSlaveTcpWriter {
    public static void main(String[] args) {
        try {
            // 设置从机TCP参数
            TcpParameters tcpParameters = new TcpParameters();
            // 设置TCP的ip地址
            InetAddress address = InetAddress.getByName("127.0.0.1");
            // getLocalHost()返回的是本机地址
            // tcpParameters.setHost(InetAddress.getLocalHost());
            // 为从机TCP设置上述ip地址参数
            tcpParameters.setHost(address);
            // 设置从机TCP的是否长连接,通俗点讲就是一直保持连接,一次连接完下次就不要在连接了
            tcpParameters.setKeepAlive(true);
            // 设置从机TCP的端口
            tcpParameters.setPort(Modbus.TCP_PORT);
            // 创建一个从机
            ModbusSlave slave = ModbusSlaveFactory.createModbusSlaveTCP(tcpParameters);
            // 设置控制台输出主机和从机命令交互日志
            Modbus.setLogLevel(Modbus.LogLevel.LEVEL_DEBUG);
            // 创建从机的寄存器
            MyOwnDataHolder dh = new MyOwnDataHolder();
            // 为从机寄存器添加监听事件,这里的监听事件主要是主机如果发送写命令修改从机则控制台输出
            dh.addEventListener(new ModbusEventListener() {
                @Override
                public void onWriteToSingleCoil(int address, boolean value) {
                    System.out
                            .print("onWriteToSingleCoil: address " + address + ", value " + value);
                }
                @Override
                public void onWriteToMultipleCoils(int address, int quantity, boolean[] values) {
                    System.out.print("onWriteToMultipleCoils: address " + address + ", quantity "
                            + quantity);
                }
                @Override
                public void onWriteToSingleHoldingRegister(int address, int value) {
                    System.out.print("onWriteToSingleHoldingRegister: address " + address
                            + ", value " + value);
                }
                @Override
                public void onWriteToMultipleHoldingRegisters(int address, int quantity,
                                                              int[] values) {
                    System.out.print("onWriteToMultipleHoldingRegisters: address " + address
                            + ", quantity " + quantity);
                }
            });

            // 为从机设置寄存器
            slave.setDataHolder(dh);
            // 设置从机的读超时时间,建议主机读的超时时间小于该值
            slave.setReadTimeout(1500);
            // 设置从机寄存器的03和04功能码对应的数值寄存器
            ModbusHoldingRegisters hr = new ModbusHoldingRegisters(10);
            // 修改数值寄存器对应位置的值,第一个参数代表寄存器地址,第二个代表修改的数值
            hr.set(0, 12345);
            hr.set(1, 54321);
            // 设置从机寄存器的01和02功能码对应的位寄存器,即只有false和true值(或0和1)
            ModbusCoils mc = new ModbusCoils(16);
            // 设置对应位寄存器地址的位值
            mc.set(0, true);
            // 为从机设置04功能码对应的数值寄存器
            slave.getDataHolder().setInputRegisters(hr);
            // 为从机设置03功能码对应的数值寄存器
            slave.getDataHolder().setHoldingRegisters(hr);
            // 为从机设置01功能码对应的数值寄存器
//            slave.getDataHolder().setCoils(mc);
            // 为从机设置从机服务地址slaveid
            slave.setServerAddress(1);
            // 开启从机监听事件,必须要这一句
            slave.listen();

            //这部分代码主要是设置Java虚拟机关闭的时候需要做的事情,即本程序关闭的时候需要做的事情,直接使用即可
            if (slave.isListening()) {
                Runtime.getRuntime().addShutdownHook(new Thread() {
                    @Override
                    public void run() {
                        synchronized (slave) {
                            slave.notifyAll();
                        }
                    }
                });

                synchronized (slave) {
                    slave.wait();
                }
                /*
                 * using master-branch it should be #slave.close();
                 */
                slave.shutdown();
            }
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 监听接口
    public interface ModbusEventListener {
        void onWriteToSingleCoil(int address, boolean value);
        void onWriteToMultipleCoils(int address, int quantity, boolean[] values);
        void onWriteToSingleHoldingRegister(int address, int value);
        void onWriteToMultipleHoldingRegisters(int address, int quantity, int[] values);
    }
    // 寄存器类定义
    public static class MyOwnDataHolder extends DataHolder {
        final List<ModbusEventListener> modbusEventListenerList = new ArrayList<ModbusEventListener>();
        public MyOwnDataHolder() {
            // you can place the initialization code here
            /*
             * something like that: setHoldingRegisters(new
             * SimpleHoldingRegisters(10)); setCoils(new Coils(128)); ... etc.
             */
        }
        public void addEventListener(ModbusEventListener listener) {
            modbusEventListenerList.add(listener);
        }
        public boolean removeEventListener(ModbusEventListener listener) {
            return modbusEventListenerList.remove(listener);
        }
        @Override
        public void writeHoldingRegister(int offset, int value) throws IllegalDataAddressException,
                IllegalDataValueException {
            for (ModbusEventListener l : modbusEventListenerList) {
                l.onWriteToSingleHoldingRegister(offset, value);
            }
            super.writeHoldingRegister(offset, value);
        }
        @Override
        public void writeHoldingRegisterRange(int offset, int[] range)
                throws IllegalDataAddressException, IllegalDataValueException {
            for (ModbusEventListener l : modbusEventListenerList) {
                l.onWriteToMultipleHoldingRegisters(offset, range.length, range);
            }
            super.writeHoldingRegisterRange(offset, range);
        }
        @Override
        public void writeCoil(int offset, boolean value) throws IllegalDataAddressException,
                IllegalDataValueException {
            for (ModbusEventListener l : modbusEventListenerList) {
                l.onWriteToSingleCoil(offset, value);
            }
            super.writeCoil(offset, value);
        }
        @Override
        public void writeCoilRange(int offset, boolean[] range) throws IllegalDataAddressException,
                IllegalDataValueException {
            for (ModbusEventListener l : modbusEventListenerList) {
                l.onWriteToMultipleCoils(offset, range.length, range);
            }
            super.writeCoilRange(offset, range);
        }
    }
}

2)、Modbus-RTU

import com.intelligt.modbus.jlibmodbus.Modbus;
import com.intelligt.modbus.jlibmodbus.data.DataHolder;
import com.intelligt.modbus.jlibmodbus.data.ModbusHoldingRegisters;
import com.intelligt.modbus.jlibmodbus.exception.IllegalDataAddressException;
import com.intelligt.modbus.jlibmodbus.exception.IllegalDataValueException;
import com.intelligt.modbus.jlibmodbus.exception.ModbusIOException;
import com.intelligt.modbus.jlibmodbus.serial.SerialParameters;
import com.intelligt.modbus.jlibmodbus.serial.SerialPort;
import com.intelligt.modbus.jlibmodbus.serial.SerialPortException;
import com.intelligt.modbus.jlibmodbus.slave.ModbusSlave;
import com.intelligt.modbus.jlibmodbus.slave.ModbusSlaveFactory;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class JlibmodbusSlaveRtuWriter {

    public static void main(String[] args) throws SerialPortException, IllegalDataValueException, IllegalDataAddressException, ModbusIOException {
        SerialParameters serialParameters = new SerialParameters();
        serialParameters.setDevice("COM1");
        serialParameters.setBaudRate(SerialPort.BaudRate.getBaudRate(9600));
        serialParameters.setDataBits(8);
        serialParameters.setStopBits(1);
        serialParameters.setParity(SerialPort.Parity.NONE);

        ModbusSlave modbusSlave = ModbusSlaveFactory.createModbusSlaveRTU(serialParameters);
        modbusSlave.setReadTimeout(2400);
        Modbus.setLogLevel(Modbus.LogLevel.LEVEL_DEBUG);
        MyDataHolder myDataHolder = new MyDataHolder();
        myDataHolder.addEventListener(new EventListeners() {
            @Override
            public void readHoldingRegister(int address) {
                System.out.println("使用03功能码读取保持寄存器的数据 address = " + address);
            }
            @Override
            public void readHoldingRegisterRange(int address, int quantity) {
                try {
                    ModbusHoldingRegisters integers = updateHoldingRegisters(address, quantity);
                    modbusSlave.getDataHolder().setHoldingRegisters(integers);
                }catch (Exception e){
                    e.printStackTrace();
                }
                System.out.println("使用03功能码范围读取保持寄存器的数据 address = " + address + " 读取寄存器个数:" + quantity);
            }
            @Override
            public void writeHoldingRegister(int address, int value) throws IllegalDataValueException, IllegalDataAddressException {
                System.out.println("使用03功能码写入保持寄存器 address = " + address + " 值:" + value);
            }
            @Override
            public void writeHoldingRegisterRange(int address, int[] values) {
                System.out.println("使用03功能码批量写入保持寄存器 address = " + address + " 值:" + values.toString());
            }
        });
        modbusSlave.setDataHolder(myDataHolder);
        modbusSlave.setServerAddress(1);
        // ModbusHoldingRegisters mm = new ModbusHoldingRegisters(10);
        // mm.set(0, 1223);
        // mm.set(1, 321);
        // modbusSlave.getDataHolder().setHoldingRegisters(mm);
        modbusSlave.listen();
    }
    public interface EventListeners{
        void readHoldingRegister(int address);
        void readHoldingRegisterRange(int address, int quantity);
        void writeHoldingRegister(int address, int value) throws IllegalDataValueException, IllegalDataAddressException;
        void writeHoldingRegisterRange(int address, int[] values);
    }
    public static class MyDataHolder extends DataHolder{
        final List<EventListeners> eventListenerList = new ArrayList<>();
        public void addEventListener(EventListeners eventListener){
            eventListenerList.add(eventListener);
        }
        public boolean removeEventListener(EventListeners eventListener){
            return eventListenerList.remove(eventListener);
        }
        @Override
        public int readHoldingRegister(int offset) throws IllegalDataAddressException {
            for (EventListeners e : eventListenerList){
                e.readHoldingRegister(offset);
            }
            return super.readHoldingRegister(offset);
        }
        @Override
        public int[] readHoldingRegisterRange(int offset, int quantity) throws IllegalDataAddressException {
            for (EventListeners e : eventListenerList){
                e.readHoldingRegisterRange(offset, quantity);
            }
            return super.readHoldingRegisterRange(offset, quantity);
        }
        @Override
        public void writeHoldingRegister(int offset, int value) throws IllegalDataAddressException, IllegalDataValueException {
            for (EventListeners e : eventListenerList){
                e.writeHoldingRegister(offset, value);
            }
            super.writeHoldingRegister(offset, value);
        }
        @Override
        public void writeHoldingRegisterRange(int offset, int[] range) throws IllegalDataAddressException, IllegalDataValueException {
            for (EventListeners e : eventListenerList){
                e.writeHoldingRegisterRange(offset, range);
            }
            super.writeHoldingRegisterRange(offset, range);
        }
    }
    // 为从机设置03功能码对应的数值寄存器
    public static ModbusHoldingRegisters updateHoldingRegisters(int offset, int quantity) throws IllegalDataAddressException, IllegalDataValueException {
        List<Float> list =  Arrays.asList(10.1f,20.3f,89.5f,73.353f);
        ModbusHoldingRegisters hr = new ModbusHoldingRegisters(10000);
        // 修改数值寄存器对应位置的值,第一个参数代表寄存器地址,第二个代表修改的数值
        //hr.set有几个方法,根据自己要赋值的数据类型选择,此处示例的是赋值float类型,一个float是4个字节,32bit,对应2个寄存器所以i*2
        for(int i=0;i<list.size();i++){
            hr.setFloat32At(i*2, list.get(i));
        }
        return hr;
    }
}

Modbus通讯协议(四)——Java实现ModbusTCP Slave(从机)_java搭建modbus slave-CSDN博客

https://blog.51cto.com/u_13229/8775755

https://blog.51cto.com/u_16099169/8318121

  • 7
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
要使用ModbusTCP协议在局域网内的两台主机上通讯,你需要使用Python Modbus-TK模块来实现Modbus-TK模块支持Modbus TCPRTU协议,并且能够在Windows和Linux操作系统上运行。 下面是一个简单的示例,演示如何使用Modbus-TK模块在两台主机上通讯: 1. 安装Modbus-TK模块 你可以使用pip命令来安装Modbus-TK模块。在终端中输入以下命令: ``` pip install modbus_tk ``` 2. 设置Modbus服务器 在要作为Modbus服务器的主机上,你需要编写一个Python脚本来设置Modbus服务器。以下是一个示例脚本: ```python import modbus_tk import modbus_tk.defines as cst from modbus_tk import modbus_tcp, hooks # 创建Modbus TCP服务器 server = modbus_tcp.TcpServer() # 定义Modbus数据存储区 data_store = modbus_tk.modbus.Slave(0x01) # 设置数据存储区的值 data_store.set_values(cst.COILS, 0, [1, 0, 1, 0]) data_store.set_values(cst.HOLDING_REGISTERS, 0, [10, 20, 30, 40]) # 将数据存储区添加到Modbus服务器 server.add_slave(1, data_store) # 启动Modbus服务器 server.start() ``` 上面的代码创建了一个Modbus TCP服务器,并定义了一个数据存储区。数据存储区包含4个线圈和4个保持寄存器。你可以根据需要修改数据存储区的值。 3. 连接到Modbus服务器 在另一台主机上,你需要编写一个Python脚本来连接到Modbus服务器并读取或写入数据。以下是一个示例脚本: ```python import modbus_tk import modbus_tk.defines as cst from modbus_tk import modbus_tcp, hooks # 创建Modbus TCP主机 master = modbus_tcp.TcpMaster(host='192.168.1.100', port=502) # 连接到Modbus服务器 master.open() # 读取线圈的值 coils = master.execute(1, cst.READ_COILS, 0, 4) # 读取保持寄存器的值 registers = master.execute(1, cst.READ_HOLDING_REGISTERS, 0, 4) # 打印读取的值 print(coils) print(registers) # 关闭连接 master.close() ``` 上面的代码创建了一个Modbus TCP主机并连接到Modbus服务器。然后,它读取服务器上的线圈和保持寄存器的值,并将它们打印出来。 你需要将上面的代码中的IP地址和端口号替换为你的Modbus服务器的IP地址和端口号。 这就是使用Modbus-TK模块通过Modbus TCP协议在局域网内的两台主机上通讯的基本步骤。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值