Modbus_Java编写ModbusTCP主从机示例

456 阅读3分钟

概述

使用Java创建ModbusMaster实例和ModbusSlave实例,并且ModbusMaster实例可以读取ModbusSlave实例中Holding寄存器的值

1.引入依赖

出于简便,直接使用springboot引入jlibmodbus组件

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

<!-- 方便构造随机值 -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.7</version>
</dependency>
<!-- 把字节数据转成易懂的16进制数据 -->
<dependency>
    <groupId>org.apache.commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
    <version>1.15</version>
</dependency>

2. ModbusSlave实例


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.ModbusIOException;
import com.intelligt.modbus.jlibmodbus.slave.ModbusSlave;
import com.intelligt.modbus.jlibmodbus.slave.ModbusSlaveFactory;
import com.intelligt.modbus.jlibmodbus.tcp.TcpParameters;
import com.intelligt.modbus.jlibmodbus.utils.FrameEvent;
import com.intelligt.modbus.jlibmodbus.utils.FrameEventListener;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang3.RandomUtils;

import java.net.InetAddress;
import java.net.UnknownHostException;

/**
 * ModbusSlaveExample
 */
public class ModbusSlaveExample {

    public static void main(String[] args) throws Exception {
        int sizeOfHoldingRegisters = 10;
        // 获取ModbusSlave实例
        ModbusSlave modbusSlave = getModbusSlave("127.0.0.1", 9527, sizeOfHoldingRegisters);

        // 随机设置寄存器的值
        println("开始随机设置寄存器的值");
        for (int i = 0; i < sizeOfHoldingRegisters; i++) {
            int val = RandomUtils.nextInt(0, 1000);
            modbusSlave.getDataHolder().writeHoldingRegister(i, val);
            println("地址偏移量:" + i + "   值:" + val);
        }
        println("完成随机设置寄存器的值");

        // 停止ModbusSlave实例
        close(modbusSlave);
    }

    private static void close(ModbusSlave slave) throws InterruptedException, ModbusIOException {
        if (slave.isListening()) {
            Runtime.getRuntime().addShutdownHook(new Thread(() -> {
                synchronized (slave) {
                    slave.notifyAll();
                }
            }));

            synchronized (slave) {
                slave.wait();
            }

            slave.shutdown();
            println("ModbusSlave停止完成");
        }
    }

    /**
     * @param host                   ModbusSlave IP
     * @param port                   ModbusSlave端口
     * @param sizeOfHoldingRegisters HoldingRegisters数量
     * @return ModbusSlave实例
     */
    private static ModbusSlave getModbusSlave(String host, Integer port, Integer sizeOfHoldingRegisters) throws UnknownHostException, ModbusIOException {
        // modbus全局设置
        Modbus.setLogLevel(Modbus.LogLevel.LEVEL_WARNINGS);
        // 构造ModbusSlave
        TcpParameters tcpParameters = new TcpParameters();
        tcpParameters.setHost(InetAddress.getByName(host)); // 指定ModbusSlave地址
        tcpParameters.setKeepAlive(true);
        tcpParameters.setPort(port); // 指定ModbusSlave端口
        ModbusSlave slave = ModbusSlaveFactory.createModbusSlaveTCP(tcpParameters);
        DataHolder holder = new DataHolder();
        holder.setHoldingRegisters(new ModbusHoldingRegisters(sizeOfHoldingRegisters));
        slave.setDataHolder(holder);
        slave.addListener(new FrameEventListener() {
            @Override
            public void frameSentEvent(FrameEvent event) {
                // println("frame send event: " + Hex.encodeHexString(event.getBytes()));
            }

            @Override
            public void frameReceivedEvent(FrameEvent event) {
                // println("frame receive event: " + Hex.encodeHexString(event.getBytes()));
            }
        });
        // 设置ModbusSlave的读超时时间,建议ModbusMaster的读超时时间小于该值
        // slave.setReadTimeout(1500);
        slave.setServerAddress(1); // ModbusSlave id
        slave.listen();
        println("ModbusSlave实例化完成");
        return slave;
    }

    private static void println(String msg) {
        System.out.println(">>> " + msg);
    }

}

备注:如果在springboot中使用,可以通过@PostConstruct初始化ModbusSlave实例,并通过@PreDestroy销毁实例。销毁代码如下:

@PreDestroy
public void destroy() {
    if (modbusSlave.isListening()) {
        try {
            modbusSlave.shutdown();
        } catch (ModbusIOException e) {
            log.error("modbus shutdown error");
        }
    }
}

3. ModbusMaster实例


import com.intelligt.modbus.jlibmodbus.Modbus;
import com.intelligt.modbus.jlibmodbus.exception.ModbusIOException;
import com.intelligt.modbus.jlibmodbus.master.ModbusMaster;
import com.intelligt.modbus.jlibmodbus.master.ModbusMasterFactory;
import com.intelligt.modbus.jlibmodbus.msg.request.ReadHoldingRegistersRequest;
import com.intelligt.modbus.jlibmodbus.msg.response.ReadHoldingRegistersResponse;
import com.intelligt.modbus.jlibmodbus.tcp.TcpParameters;
import com.intelligt.modbus.jlibmodbus.utils.FrameEvent;
import com.intelligt.modbus.jlibmodbus.utils.FrameEventListener;

import java.net.InetAddress;
import java.net.UnknownHostException;

/**
 * ModbusMasterExample
 */
public class ModbusMasterExample {

    public static void main(String[] args) throws Exception {
        // 获取ModbusMaster实例
        ModbusMaster master = getModbusMaster("127.0.0.1", 9527);

        // 模拟请求
        println("开始模拟请求并打印输出");
        for (int i = 0; i < 2; i++) {
            println("=====第" + (i + 1) + "遍=====");
            // 读取多个Holding寄存器
            ReadHoldingRegistersRequest request = new ReadHoldingRegistersRequest();
            request.setTransactionId(i);
            request.setProtocolId(Modbus.PROTOCOL_ID);
            request.setServerAddress(1); // ModbusSlave id
            int offset = 0;
            request.setStartAddress(offset); // 每次请求起始地址(偏移量)
            int quantity = 10; // 每次请求数量
            request.setQuantity(quantity);
            ReadHoldingRegistersResponse response = (ReadHoldingRegistersResponse) master.processRequest(request);

            // 打印输出
            for (int j = 0; j < quantity; j++) {
                int val = response.getHoldingRegisters().getInt16At(j);
                println("地址偏移量:" + (offset + j) + "   值:" + val);
            }
            // 短暂暂停。注意:测试发现设置为1000毫秒时会出现后续请求失败
            Thread.sleep(500L);
        }

        println("ModbusMaster停止完成");
    }

    /**
     * @param host ModbusSlave IP
     * @param port ModbusSlave端口
     * @return ModbusMaster实例
     */
    private static ModbusMaster getModbusMaster(String host, Integer port) throws UnknownHostException, ModbusIOException {
        // modbus全局设置
        Modbus.setLogLevel(Modbus.LogLevel.LEVEL_WARNINGS);
        Modbus.setAutoIncrementTransactionId(true);
        // 构造ModbusMaster
        TcpParameters tcpParameters = new TcpParameters();
        tcpParameters.setHost(InetAddress.getByName(host)); // 指定ModbusSlave地址
        tcpParameters.setKeepAlive(true);
        tcpParameters.setPort(port); // 指定ModbusSlave端口
        ModbusMaster master = ModbusMasterFactory.createModbusMasterTCP(tcpParameters);
        // master.setResponseTimeout(2000);
        master.addListener(new FrameEventListener() {
            @Override
            public void frameSentEvent(FrameEvent event) {
                // println("frame send event: " + Hex.encodeHexString(event.getBytes()));
            }

            @Override
            public void frameReceivedEvent(FrameEvent event) {
                // println("frame receive event: " + Hex.encodeHexString(event.getBytes()));
            }
        });
        master.connect();
        println("ModbusMaster实例化完成");
        return master;
    }

    private static void println(String msg) {
        System.out.println(">>> " + msg);
    }

}

4. 执行结果

ModbusSlave输出如下:

>>> ModbusSlave实例化完成
>>> 开始随机设置寄存器的值
>>> 地址偏移量:0   值:498
>>> 地址偏移量:1   值:196
>>> 地址偏移量:2   值:701
>>> 地址偏移量:3   值:99
>>> 地址偏移量:4   值:681
>>> 地址偏移量:5   值:213
>>> 地址偏移量:6   值:855
>>> 地址偏移量:7   值:365
>>> 地址偏移量:8   值:848
>>> 地址偏移量:9   值:390
>>> 完成随机设置寄存器的值

ModbusMaster输出如下:

>>> ModbusMaster实例化完成
>>> 开始模拟请求并打印输出
>>> =====第1遍=====
>>> 地址偏移量:0   值:498
>>> 地址偏移量:1   值:196
>>> 地址偏移量:2   值:701
>>> 地址偏移量:3   值:99
>>> 地址偏移量:4   值:681
>>> 地址偏移量:5   值:213
>>> 地址偏移量:6   值:855
>>> 地址偏移量:7   值:365
>>> 地址偏移量:8   值:848
>>> 地址偏移量:9   值:390
>>> =====第2遍=====
>>> 地址偏移量:0   值:498
>>> 地址偏移量:1   值:196
>>> 地址偏移量:2   值:701
>>> 地址偏移量:3   值:99
>>> 地址偏移量:4   值:681
>>> 地址偏移量:5   值:213
>>> 地址偏移量:6   值:855
>>> 地址偏移量:7   值:365
>>> 地址偏移量:8   值:848
>>> 地址偏移量:9   值:390
>>> ModbusMaster停止完成