【Modbus】Java使用 modbus-master-tcp 读取和写入Modbus服务器数据

1、前言

在井下综采面需要用到工业级控制协议,采用了Modbus主站从站通讯方式,直接操作寄存器数据,实现读取和控制。

2、引用pom

        <dependency>
            <groupId>com.digitalpetri.modbus</groupId>
            <artifactId>modbus-master-tcp</artifactId>
            <version>1.1.0</version>
        </dependency>

3、上代码

package com.ruoyi.project.socket.underJava;

import com.digitalpetri.modbus.FunctionCode;
import com.digitalpetri.modbus.codec.Modbus;
import com.digitalpetri.modbus.master.ModbusTcpMaster;
import com.digitalpetri.modbus.master.ModbusTcpMasterConfig;
import com.digitalpetri.modbus.requests.*;
import com.digitalpetri.modbus.responses.*;
import com.ruoyi.project.utils.HexUtils;
import com.ruoyi.project.utils.LogHelper;
import io.netty.buffer.ByteBuf;
import io.netty.util.ReferenceCountUtil;

import java.util.Arrays;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

/**
 * @version: V1.0
 * @description: modbus TCP协议 数据读取写入
 * @date: 2021-02-04
 */
public class ModbusMasterTCPFromZhengMei {
    /**
     * 日志记录
     */
    private static LogHelper log = new LogHelper();
    /**
     * tcp连接对象
     */
    private static ModbusTcpMaster modbusTcpMaster;
    /**
     * modbus ip地址
     */
    private static final String IP = "192.168.19.130";
    /**
     * 端口
     */
    private static final Integer PORT = 502;
    /**
     * modubs从站ID
     */
    private static final Integer UNIT_ID = 1;
    /**
     * 成功代码
     */
    private static final String SUCCESS_CODE = "0x000000";
    /**
     * 与modubs连接异常
     */
    private static final String COON_FAIL_CODE = "0x000001";
    /**
     * 向modubs发送命令执行异常
     */
    private static final String EXEC_FAIL_CODE = "0x000002";

    /**
     * 数据写入失败
     */
    private static final String WRITE_FAIL_CODE = "0x000004";
    private static String logName = "ModbusMasterTCPFromZhengMei ";
    /**
     * @description: 初始化连接
     * @param:
     * @return: 结果值
     */
    private static String init() {
        try {
            if (modbusTcpMaster == null) {
                // 创建配置
                ModbusTcpMasterConfig config = new ModbusTcpMasterConfig.Builder(IP).setPort(PORT).build();
                // 新建连接
                modbusTcpMaster = new ModbusTcpMaster(config);
            }
            return SUCCESS_CODE;
        } catch (Exception e) {
            log.error("ModbusMasterTCP::init - " + e.getMessage() + "(0x000001)" +
                    "\r\n" + Arrays.toString(e.getStackTrace()));
            return COON_FAIL_CODE;
        }
    }

    /**
     * @description: 释放连接
     * @param:
     * @return: 结果值
     */
    private static String release() {
        try {
            if (modbusTcpMaster != null) {
                modbusTcpMaster.disconnect();
            }
            Modbus.releaseSharedResources();
            return SUCCESS_CODE;
        } catch (Exception e) {
            return COON_FAIL_CODE;
        }
    }

    /**
     * @param address 寄存器地址
     * @param value   写入值
     * @param unitId  id
     * @description: 写HoldingRegister数据
     * @return: 结果值
     */
    public static String writeHoldingRegisters(Integer address, Integer value, Integer unitId) {
        ModbusResponse modbusResponse;
        try {
            // 发送单个寄存器数据,一般是无符号16位值:比如10
            CompletableFuture<ModbusResponse> future = modbusTcpMaster.sendRequest(new WriteSingleRegisterRequest(address, value), unitId);

            //获取写入的响应流
            modbusResponse = future.get();
            if (modbusResponse == null) {
                System.out.println("FCSC-ExternalConnection WriteHoldingRegisters:modbusResponse is null ");
                return WRITE_FAIL_CODE;
            }
            //获取写入的响应FunctionCode
            FunctionCode functionCode = modbusResponse.getFunctionCode();
            System.out.println("FCSC-ExternalConnection functionCode=" + functionCode + " value=" + value);
            if (functionCode == FunctionCode.WriteSingleRegister) {
                return SUCCESS_CODE;
            } else {
                return WRITE_FAIL_CODE;
            }
        } catch (Exception e) {
            log.error("ModbusMasterTCP::writeHoldingRegisters - " + e.getMessage() + ",value=" + value + "(0x000002)"
                    + "\r\n" + Arrays.toString(e.getStackTrace()));
            e.printStackTrace();
            return EXEC_FAIL_CODE;
        } finally {
            // String releaseRes = release();
            // //如果释放连接失败,返回执行失败
            // if (!SUCCESS_CODE.equals(releaseRes)) {
            //     return releaseRes;
            // }
        }
    }

    /**
     * @param address  寄存器地址
     * @param quantity 写位数
     * @param values   写入值
     * @description: 写HoldingRegister数据
     * @return: 结果值
     */
    public static String WriteMultipleRegisters(Integer address, Integer quantity, byte[] values) {
        try {
            WriteMultipleRegistersRequest request = new WriteMultipleRegistersRequest(address, quantity, values);
            // 发送单个寄存器数据,一般是无符号16位值:比如10
            CompletableFuture<ModbusResponse> future = modbusTcpMaster.sendRequest(request, UNIT_ID);
            ModbusResponse modbusResponse;
            //获取写入的响应流
            modbusResponse = future.get();
            if (modbusResponse == null) {
                System.out.println("FCSC-ExternalConnection WriteMultipleRegisters:modbusResponse is null ");
                return WRITE_FAIL_CODE;
            }
            //获取写入的响应FunctionCode
            FunctionCode functionCode = modbusResponse.getFunctionCode();
            System.out.println("FCSC-ExternalConnection functionCode.getCode()===" + functionCode.getCode() + "=" + functionCode);
            if (functionCode == FunctionCode.WriteMultipleRegisters) {
                return SUCCESS_CODE;
            } else {
                return WRITE_FAIL_CODE;
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
            return EXEC_FAIL_CODE;
        } catch (ExecutionException e) {
            e.printStackTrace();
            return EXEC_FAIL_CODE;
        } finally {
            // String releaseRes = release();
            // //如果释放连接失败,返回执行失败
            // if (!SUCCESS_CODE.equals(releaseRes)) {
            //     return releaseRes;
            // }
        }
    }

    /**
     * @description: 写入数据
     * @param: address 寄存器地址
     * @param: value 写入值
     * @return: 结果值
     */
    public static String writeByteData(byte[] values) {
        String initRes = init();
        //如果初始化失败,则立即返回
        if (!SUCCESS_CODE.equals(initRes)) {
            return initRes;
        }
        String writeRes = WriteMultipleRegisters(916, 2, values);
        //如果写入失败,返回
        if (!SUCCESS_CODE.equals(writeRes)) {
            return writeRes;
        }
        return SUCCESS_CODE;
    }

    /**
     * @description: 写入数据
     * @param: address 寄存器地址
     * @param: value 写入值
     * @return: 结果值
     */
    public static String writeData(Integer address, Integer value) {
        String initRes = init();
        //如果初始化失败,则立即返回
        if (!SUCCESS_CODE.equals(initRes)) {
            return initRes;
        }

        String writeRes = writeHoldingRegisters(address, value, UNIT_ID);
        //如果写入失败,返回
        if (!SUCCESS_CODE.equals(writeRes)) {
            return writeRes;
        }
        return SUCCESS_CODE;
    }


    /**
     * @description: writeDemo
     * @param:
     * @return:
     */
    public static void writeDemo() {
        // 初始化资源
        init();
        Random random = new Random();
        int value = random.nextInt(100) + 1;
        System.out.println("write value=" + value);
        try {
            writeHoldingRegisters(222, value, UNIT_ID);
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 释放资源
        release();
    }

    /**
     * @description: readDemo
     * @param:
     * @return:
     */
    public static void readDemo() {
        try {
            // 初始化资源
            init();
            System.out.println("readDemo=" + readHoldingRegisters(222, 4, 1));
            release();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 读取Coils开关量
     *
     * @param address  寄存器开始地址
     * @param quantity 数量
     * @param unitId   ID
     * @return 读取值
     * @throws InterruptedException 异常
     * @throws ExecutionException   异常
     */
    public static Boolean readCoils(int address, int quantity, int unitId)
            throws InterruptedException, ExecutionException {
        Boolean result = null;
        CompletableFuture<ReadCoilsResponse> future = modbusTcpMaster.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;
    }

    /**
     * 读取readDiscreteInputs开关量
     *
     * @param address  寄存器开始地址
     * @param quantity 数量
     * @param unitId   ID
     * @return 读取值
     * @throws InterruptedException 异常
     * @throws ExecutionException   异常
     */
    public static Boolean readDiscreteInputs(int address, int quantity, int unitId)
            throws InterruptedException, ExecutionException {
        Boolean result = null;
        CompletableFuture<ReadDiscreteInputsResponse> future = modbusTcpMaster
                .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;
    }

    /**
     * 读取HoldingRegister数据
     *
     * @param address  寄存器地址
     * @param quantity 寄存器数量
     * @param unitId   id
     * @return 读取结果
     * @throws InterruptedException 异常
     * @throws ExecutionException   异常
     */
    public static Number readHoldingRegisters(int address, int quantity, int unitId)
            throws InterruptedException, ExecutionException {
        Number result = null;
        CompletableFuture<ReadHoldingRegistersResponse> future = modbusTcpMaster
                .sendRequest(new ReadHoldingRegistersRequest(address, quantity), unitId);
        ReadHoldingRegistersResponse readHoldingRegistersResponse = future.get();// 工具类做的同步返回.实际使用推荐结合业务进行异步处理
        if (readHoldingRegistersResponse != null) {
            ByteBuf buf = readHoldingRegistersResponse.getRegisters();
            byte[] bytes = new byte[buf.capacity()];
            buf.readBytes(bytes, 0, buf.capacity());
            // System.out.println("bytes=" + Arrays.toString(bytes));
            result = HexUtils.bytes2Short(bytes, 0);
            ReferenceCountUtil.release(readHoldingRegistersResponse);
        }
        return result;
    }

    public static Integer readData(Integer address, Integer quantity) throws Exception {
        String initRes = init();
        //如果初始化失败,则立即返回
        if (!SUCCESS_CODE.equals(initRes)) {
            return -5;
        }
        Number number = readHoldingRegisters(address, quantity, UNIT_ID);
        return number.intValue();
    }

    /**
     * 读取InputRegisters模拟量数据
     *
     * @param address  寄存器开始地址
     * @param quantity 数量
     * @param unitId   ID
     * @return 读取值
     * @throws InterruptedException 异常
     * @throws ExecutionException   异常
     */
    public static Number readInputRegisters(int address, int quantity, int unitId)
            throws InterruptedException, ExecutionException {
        Number result = null;
        CompletableFuture<ReadInputRegistersResponse> future = modbusTcpMaster
                .sendRequest(new ReadInputRegistersRequest(address, quantity), unitId);
        ReadInputRegistersResponse readInputRegistersResponse = future.get();// 工具类做的同步返回.实际使用推荐结合业务进行异步处理
        if (readInputRegistersResponse != null) {
            ByteBuf buf = readInputRegistersResponse.getRegisters();
            result = buf.readDouble();
            ReferenceCountUtil.release(readInputRegistersResponse);
        }
        return result;
    }

    /**
     * @description: writeDemo2
     * @param:
     * @return:
     */
    public static void writeDemo2() {
        Random random = new Random();
        int value = random.nextInt(100) + 1;
        System.out.println("ready write value=" + value);
        String res = writeData(222, value);
        System.out.println("res=" + res);
    }

    public static void writeDemo3() {
        byte[] bytes = new byte[]{0, 2, 0, 3};
        String res = writeByteData(bytes);
        System.out.println(res);
    }

    public static byte[] double2Bytes(double d) {
        long value = Double.doubleToRawLongBits(d);
        byte[] byteRet = new byte[8];
        for (int i = 0; i < 8; i++) {
            byteRet[i] = (byte) ((value >> 8 * i) & 0xff);
        }
        return byteRet;
    }

    public static String writeCoils(int address, boolean value) {
        try {
            init();
            WriteSingleCoilRequest writeSingleCoilRequest = new WriteSingleCoilRequest(address, value);
            CompletableFuture<ModbusResponse> request = modbusTcpMaster.sendRequest(writeSingleCoilRequest, UNIT_ID);
            ModbusResponse modbusResponse = request.get();
            if (modbusResponse == null) {
                System.out.println(logName + "writeCoils:modbusResponse is null ");
                return WRITE_FAIL_CODE;
            }
            FunctionCode functionCode = modbusResponse.getFunctionCode();
            System.out.println(logName + "writeCoils address=" + address + " value=" + value + " functionCode=" + functionCode);
            if (functionCode == FunctionCode.WriteSingleCoil) {
                return SUCCESS_CODE;
            } else {
                return WRITE_FAIL_CODE;
            }
        } catch (Exception e) {
            log.error(logName + "writeCoils - " + e.getMessage() + ",address" + address + ",value=" + value + "(0x000002)"
                    + "\r\n" + Arrays.toString(e.getStackTrace()));
            System.out.println(logName + "writeCoils - " + e.getMessage());
            return WRITE_FAIL_CODE;
        }
    }

    /**
     * @description: main
     * @param:
     * @return:
     */
    public static void main(String[] args) {
        // writeDemo();
        // readDemo();
        writeDemo3();
        release();
    }
}