【modbus】modbus java程序读取

简介

本文介绍如何使用java程序简单读取modbus slave端程序。

相关代码、软件资源,可参考附录部分。

概念

Modbus是一种串行通信协议,是Modicon公司(现在的施耐德电气 Schneider Electric)于1979年为使用可编程逻辑控制器(PLC)通信而发表。Modbus已经成为工业领域通信协议的业界标准(De facto),并且现在是工业电子设备之间常用的连接方式。目前在和各大用电量采集厂商对接时,也几乎都用此协议进行沟通,因此对于做工业领域相关的技术人员,有必要对此协议进行一定的了解。

slave/master端 对于熟悉C/S、B/S模式的编程人员来说,modbus协议也同样分为两者,一个是slave端,一个是master端,我们一般编写采集程序可以认为是master端,而持续提供程序的端则称为slave端,常见的slave端可以使用modbus poll这类程序模拟,安装程序会放在文末附录最后。

modbus poll 常见的slave端模拟程序,可通过极简的配置模拟数据生成,方便数据采集人员本地调试。

modbus-master-tcp本文所采用的第三方数据读取包。

实例程序

1、编写读取程序(master端)

前面提到,读取程序一般充当master端,因此我们先编写主程序,用于采集指定slave端的数据。

这里slave端一般是我们本地的modbus poll工具,下一章节介绍此工具的简单使用。

pom

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>com.digitalpetri.modbus</groupId>
			<artifactId>modbus-master-tcp</artifactId>
			<version>1.1.1</version>
		</dependency>
	</dependencies>

主读取程序

package com.example.modbustest.md;

import com.digitalpetri.modbus.master.ModbusTcpMaster;
import com.digitalpetri.modbus.master.ModbusTcpMasterConfig;
import com.digitalpetri.modbus.requests.ReadCoilsRequest;
import com.digitalpetri.modbus.requests.ReadDiscreteInputsRequest;
import com.digitalpetri.modbus.requests.ReadHoldingRegistersRequest;
import com.digitalpetri.modbus.requests.ReadInputRegistersRequest;
import com.digitalpetri.modbus.responses.ReadCoilsResponse;
import com.digitalpetri.modbus.responses.ReadDiscreteInputsResponse;
import com.digitalpetri.modbus.responses.ReadHoldingRegistersResponse;
import com.digitalpetri.modbus.responses.ReadInputRegistersResponse;
import io.netty.buffer.ByteBuf;
import io.netty.util.ReferenceCountUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

/**
 * Desc:
 * ModBus组件
 *
 * @author JoyBlack
 * @email 1016037677@qq.com
 * @date 2023/1/6 下午 3:27
 */
public class ModBus {
    private ModbusTcpMaster master;

    private static final Logger logger = LoggerFactory.getLogger(ModBus.class);

    public ModBus(String address, int port) {
        ModbusTcpMasterConfig config = new ModbusTcpMasterConfig.Builder(address)
                .setPort(port)
                .setTimeout(Duration.ofSeconds(20))
                .build();
        master = new ModbusTcpMaster(config);
    }

    /**
     * Desc:
     * 释放资源
     *
     * @author JoyBlack
     * @email 1016037677@qq.com
     * @date 2023/1/5 上午 10:04
     */
    public void release() {
        if (master != null) {
            master.disconnect();
        }
        // Modbus.releaseSharedResources();
    }

    /**
     * Desc:
     * 读取HoldingRegister数据(address: 寄存器地址; quantity: 寄存器数量; unitId: id)
     *
     * @author JoyBlack
     * @email 1016037677@qq.com
     * @date 2023/1/5 上午 10:04
     */
    public Number readHoldingRegister(int address, int quantity, int unitId) throws Exception {
        Number result = null;
        CompletableFuture<ReadHoldingRegistersResponse> future = master.sendRequest(new ReadHoldingRegistersRequest(address, quantity), unitId);
        ReadHoldingRegistersResponse response = future.get(8, TimeUnit.SECONDS);
        if (response != null) {
            ByteBuf registers = response.getRegisters();
            result = registers.readFloat();
            ReferenceCountUtil.release(response);
        }
        return result;
    }

    /**
     * Desc:
     * 读取InputRegisters模拟量数据(address: 寄存器地址; quantity: 寄存器数量; unitId: id)
     *
     * @author JoyBlack
     * @email 1016037677@qq.com
     * @date 2023/1/5 上午 10:04
     */
    public Number readInputRegister(int address, int quantity, int unitId) throws Exception {
        Number result = null;
        CompletableFuture<ReadInputRegistersResponse> future = master
                .sendRequest(new ReadInputRegistersRequest(address, quantity), unitId);
        // 工具类做的同步返回.实际使用推荐结合业务进行异步处理
        ReadInputRegistersResponse readInputRegistersResponse = future.get();
        if (readInputRegistersResponse != null) {
            ByteBuf buf = readInputRegistersResponse.getRegisters();
            result = buf.readFloat();
            ReferenceCountUtil.release(readInputRegistersResponse);
        }
        return result;
    }

    /**
     * Desc:
     * 读取Coils开关量
     *
     * @author JoyBlack
     * @email 1016037677@qq.com
     * @date 2023/1/5 上午 10:04
     */
    public Boolean readCoils(int address, int quantity, int unitId)
            throws Exception {
        Boolean result = null;
        CompletableFuture<ReadCoilsResponse> future = master.sendRequest(new ReadCoilsRequest(address, quantity),
                unitId);
        ReadCoilsResponse readCoilsResponse = future.get();
        if (readCoilsResponse != null) {
            ByteBuf buf = readCoilsResponse.getCoilStatus();
            result = buf.readBoolean();
            ReferenceCountUtil.release(readCoilsResponse);
        }
        return result;
    }

    /**
     * Desc:
     * 读取readDiscreteInputs开关量
     *
     * @author JoyBlack
     * @email 1016037677@qq.com
     * @date 2023/1/5 上午 10:04
     */
    public Boolean readDiscreteInputs(int address, int quantity, int unitId)
            throws Exception {
        Boolean result = null;
        CompletableFuture<ReadDiscreteInputsResponse> future = master
                .sendRequest(new ReadDiscreteInputsRequest(address, quantity), unitId);
        ReadDiscreteInputsResponse discreteInputsResponse = future.get();
        if (discreteInputsResponse != null) {
            ByteBuf buf = discreteInputsResponse.getInputStatus();
            result = buf.readBoolean();
            ReferenceCountUtil.release(discreteInputsResponse);
        }
        return result;
    }

    public Result<Map<Integer, Number>> run(int start, int end, int quantity, int unitId) {
        Map<Integer, Number> result = new HashMap<>();
        try {
            for (int i = start; i <= end; i += 2) {
                Number value = readHoldingRegister(i, quantity, unitId);
                if ("infinity".equals(value.toString().toLowerCase())) {
                    logger.error(String.format("点位[%s]数据转换失败,设置为默认值 null", i));
                    result.put(i, null);
                } else {
                    result.put(i, value);
                }
            }
            return new Result<Map<Integer, Number>>().ok(result);
        } catch (Exception e) {
            String msg = String.format("【监控数据采集任务】获取监控数据失败:%s, start = %d, end = %d.", e.getMessage(), start, end);
            logger.error(msg);
            Result<Map<Integer, Number>> error = new Result<Map<Integer, Number>>().error(msg);
            return error;
        }
    }

    public static void main(String[] args) {
        try {
            ModBus modBus = new ModBus("127.0.0.1", 502);
            for (int i = 0; i < 10; i++) {
                Result<Map<Integer, Number>> run = modBus.run(0, 9, 2, 1);
                System.out.println(run.getData());
                Thread.sleep(1000);
            }
            // 释放资源
            modBus.release();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

辅助返回类

package com.example.modbustest.md;

import java.io.Serializable;

/**
 * 响应数据
 */
public class Result<T> implements Serializable {
    private static final long serialVersionUID = 1L;
    /**
     * 编码:0表示成功,其他值表示失败
     */
    private int code = 0;
    /**
     * 消息内容
     */
    private String msg = "success";
    /**
     * 响应数据
     */
    private T data;

    public Result<T> ok(T data) {
        this.setData(data);
        return this;
    }

    public boolean success() {
        return code == 0;
    }

    public Result<T> error() {

        return this;
    }

    public Result<T> error(int code) {
        return this;
    }

    public Result<T> error(int code, String msg) {
        this.code = code;
        this.msg = msg;
        return this;
    }

    public Result<T> error(String msg) {
        this.code = 500;
        this.msg = msg;
        return this;
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }
}

2、配置数据生成(slave端)

主程序编写完成后,需要启动一个slave端,方便主程序读取数据。我们使用modbus poll充当。

1、安装modbus poll:下载附录指定的程序后,执行安装。

2、运行程序,可以得到如下图所示的界面
在这里插入图片描述
3、右键内容页面,选择Slave Definition,设置slave寄存器数量、读取数据方式(F=03)
在这里插入图片描述
在这里插入图片描述
4、点击OK后,初始化为10行的内容页
在这里插入图片描述
5、右键存储数据单元格:设置数据类型、大小端模式,我们一般设置为Float(占用两个寄存器,4个字节)以及大端模式
在这里插入图片描述

6、双击存储数据单元格:设置数据内容、自增(动态变化方便客户端查询)
在这里插入图片描述
点击OK,完成设置。发现数据在不停的增长,通过此方式可以依次设置不同地址的寄存器存储的数据。由于此数据使用了两个寄存器(地址0和地址1),因此我们只能从地址为2的寄存器开始设置.如下图所示:
在这里插入图片描述
7、按照步骤5和6,依次设置剩余4个数值的数据,刚好使用玩完10个寄存器(我们的采集程序刚好采集5个数值,对应0-9 10个寄存器地址)
在这里插入图片描述

3、测试数据读取

1、点击java程序中的ModBus类,执行其main方法,该程序会间隔1秒读取10次本地配置的modbus poll生成的10个数值,执行结果如下图所示:
在这里插入图片描述

结语

本文只是做了一个简单介绍,若需要将程序应用到实际项目,还需要将其进行拓展,比如数据的读取频率、多线程辅助、消息缓冲队列还实现实际的生产任务。相关代码、软件资源,可参考附录部分。

附录

  • modbus poll 安装程序:链接: https://pan.baidu.com/s/1a5eZpwYbCy60XuM12PlMpQ?pwd=zhao 提取码: zhao
  • 代码地址:https://github.com/joyblack/modbus-test.git
  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
以下是Java Modbus RTU读取数据的步骤: 1. 导入相关的Java Modbus库,例如j2mod、jamod等。 2. 创建ModbusMaster实例,用于与Modbus从站通信。 3. 创建ModbusSerialTransaction实例,用于在串行通信中执行Modbus事务。 4. 创建读取寄存器的请求对象,例如ReadInputRegistersRequest或ReadMultipleRegistersRequest。 5. 设置请求对象的起始地址和寄存器数量。 6. 执行Modbus事务,发送请求并等待响应。 7. 从响应中获取读取到的数据。 下面是一个示例代码,演示如何使用j2mod库读取Modbus从站的保持寄存器: ```java import java.net.*; import java.io.*; import net.wimpi.modbus.*; import net.wimpi.modbus.io.*; import net.wimpi.modbus.msg.*; import net.wimpi.modbus.net.*; import net.wimpi.modbus.util.*; public class ModbusRTUReader { public static void main(String[] args) { try { // 创建串口连接 SerialConnection connection = new SerialConnection( new SerialParameters( "/dev/ttyUSB0", // 串口名称 9600, // 波特率 8, // 数据位 "None", // 奇偶校验 1 // 停止位 ) ); connection.open(); // 创建Modbus主站 ModbusMaster master = new ModbusMasterRTU(connection); // 创建读取保持寄存器的请求 ReadMultipleRegistersRequest request = new ReadMultipleRegistersRequest(0, 10); // 执行Modbus事务 ModbusSerialTransaction transaction = new ModbusSerialTransaction(master); transaction.setRequest(request); transaction.execute(); // 获取响应数据 ReadMultipleRegistersResponse response = (ReadMultipleRegistersResponse) transaction.getResponse(); int[] values = response.getRegisters().getValues(); // 输出读取到的数据 for (int i = 0; i < values.length; i++) { System.out.println("Register " + i + ": " + values[i]); } // 关闭连接 connection.close(); } catch (Exception e) { e.printStackTrace(); } } } ```
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值