springboot 整合 modbus4j

pom.xml

XML 复制代码
<dependency>
   	<groupId>com.serotonim</groupId>
    <artifactId>modbus4j</artifactId>
</dependency>

配置类

java 复制代码
@Slf4j
@Configuration
@Component
@Import(com.serotonin.modbus4j.ModbusFactory.class)
public class ModbusConfig {

    @Value("${modbus.host}")
    private String host;

    @Value("${modbus.port}")
    private Integer port;

    /*@Autowired
    private ModbusFactory modbusFactory;*/

    /*@Resource
    private BeginPowerExchangeService beginPowerExchangeService;*/

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Bean
    public TcpMasterExtend modbusMaster() {
        IpParameters ipParameters = new IpParameters();
        ipParameters.setHost(host);
        ipParameters.setPort(port);

        /*// 原生TCP 协议
        ModbusMaster master = modbusFactory.createTcpMaster(ipParameters, true);*/
        //自定处理
        TcpMasterExtend master = new TcpMasterExtend(ipParameters,true,stringRedisTemplate);

        //设置超时时间
        master.setTimeout(3 * 1000);
        //设置重连次数
        master.setRetries(6);
        //初始化
        try {
            master.init();
        } catch (ModbusInitException e) {
            log.error("modbus 初始化异常:{}", e.getMessage());
        }
        return master;
    }
}

自定义 TcpMaster

java 复制代码
import cn.hutool.core.util.ReflectUtil;
import com.sany.swap.vo.RedisModule;
import com.serotonin.modbus4j.exception.ModbusTransportException;
import com.serotonin.modbus4j.ip.IpParameters;
import com.serotonin.modbus4j.ip.tcp.TcpMaster;
import com.serotonin.modbus4j.msg.*;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.time.Duration;
import java.time.LocalDateTime;


/**
 * @Method
 * @Author xuyuhao
 * @Version 1.0
 * @Description
 * @Return
 * @Exception
 * @Date 2021/4/14
 */
@Getter
@Setter
@Slf4j
public class TcpMasterExtend extends TcpMaster {
    //在线标识
    private volatile static boolean online = false;
    //异常时间
    private static LocalDateTime exceptionTime = null;
    //redis
    private StringRedisTemplate redisTemplate;

    /**
     * 初始构造方法
     *
     * @param params
     * @param keepAlive
     * @param redisTemplate
     */
    public TcpMasterExtend(IpParameters params,boolean keepAlive,
                           StringRedisTemplate redisTemplate) {
        super(params, keepAlive);
        this.redisTemplate = redisTemplate;
    }

    /**
     * 发送消息
     *
     * @param request
     * @return
     */
    public ModbusResponse sendEx(ModbusRequest request) throws ModbusTransportException {
        try {
            ModbusResponse response = null;

            if (request instanceof ReadHoldingRegistersRequest) {
                Integer startOffset = (Integer) ReflectUtil.getFieldValue(request, "startOffset");
                response = send(request);
                log.warn("PLC读取寄存器数据 ------> 位置:{},结果:{}", startOffset, null != response ? ((ReadHoldingRegistersResponse) response).getShortData() : "无");
            } else if (request instanceof WriteRegisterRequest || request instanceof WriteRegistersRequest) {
                response = send(request);
            }
            if (!online) {
                log.warn("PLC设备上线---------->断线时长:{}分钟", null == exceptionTime ? 0 : Duration.between(exceptionTime, LocalDateTime.now()).toMinutes());
            }
            redisTemplate.opsForValue().set(RedisModule.PLC_ONLINE_STATUS.get(), "true");
            online = true;
            return response;
        } catch (Exception e) {
            //如果是连接异常手动处理一下
            if (e.getCause() instanceof ConnectException || e.getCause() instanceof SocketTimeoutException) {
                if (online) {
                    exceptionTime = LocalDateTime.now();
                    log.warn("PLC设备发生连接异常------->{}", e);
                }
                destroy();
                online = false;
                redisTemplate.opsForValue().set(RedisModule.PLC_ONLINE_STATUS.get(), "false");
                return null;
            }
            //否则直接抛出
            throw e;
        }
    }
}

modbus操作类

java 复制代码
import com.serotonin.modbus4j.exception.ModbusTransportException;
import com.serotonin.modbus4j.msg.*;
import com.sany.swap.api.plc.service.BeginPowerExchangeService;
import com.sany.swap.service.plc.bean.TcpMasterExtend;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;

import javax.annotation.Resource;

/**
 * <p></p>
 *
 * @author zhangliang@gjkjjt.com
 * @version 1.0.0
 * @date 2020/6/26 9:43
 * <p>
 */
@Slf4j
@Configuration
public class Modbus4jUtils {

    @Autowired
    private TcpMasterExtend tcpMaster;

    @Resource
    private BeginPowerExchangeService beginPowerExchangeService;

    /**
     * @Title readCoilStatus
     * @Description: 读(线圈)开关量数据,相当于功能码:01H-读线圈状态
     * @params: [ip, slaveId, offset, numberOfRegister]
     * @return: boolean[]
     * @throws:
     */
    public boolean[] readCoilStatus(int slaveId, int offset, int numberOfRegister) {

        boolean[] booleans = null;
        try {
            ReadCoilsRequest request = new ReadCoilsRequest(slaveId, offset, numberOfRegister);
            ReadCoilsResponse response = (ReadCoilsResponse) tcpMaster.sendEx(request);
            if (response.isException()) {
                log.error("readCoilStatus response: message=" + response.getExceptionMessage());
            } else {
                booleans = response.getBooleanData();
            }
            beginPowerExchangeService.changePlcOnlineStatus(1);
        } catch (ModbusTransportException e) {
            beginPowerExchangeService.changePlcOnlineStatus(0);
            log.error("modbus 连接异常:{}", e.getMessage());
        }
        return valueRegroup(numberOfRegister, booleans);
    }

    /**
     * @Title readInputStatus
     * @Description: 读取外围设备输入的开关量,相当于功能码:02H-读离散输入状态
     * @params: [ip, offset, numberOfRegister]
     * @return: boolean[]
     * @throws:
     */
    public boolean[] readInputStatus(int slaveId, int offset, int numberOfRegister) {
        boolean[] booleans = null;
        try {
            ReadDiscreteInputsRequest request = new ReadDiscreteInputsRequest(slaveId, offset, numberOfRegister);
            ReadDiscreteInputsResponse response = (ReadDiscreteInputsResponse) tcpMaster.sendEx(request);
            if (response.isException()) {
                log.error("readInputStatus response: message=" + response.getExceptionMessage());
            } else {
                booleans = response.getBooleanData();
            }
            beginPowerExchangeService.changePlcOnlineStatus(1);
        } catch (ModbusTransportException e) {
            beginPowerExchangeService.changePlcOnlineStatus(0);
            log.error("modbus连接异常:{}", e.getMessage());
        }
        return valueRegroup(numberOfRegister, booleans);
    }

    /**
     * @Title readHoldingRegister
     * @Description: 读取保持寄存器数据,相当于功能码:03H-读保持寄存器
     * @params: [ip, offset, numberOfRegister]
     * @return: short[]
     * @throws:
     */
    public short[] readHoldingRegister(int slaveId, int offset, int numberOfRegister) {
        short[] result = null;
        try {
            ReadHoldingRegistersRequest request = new ReadHoldingRegistersRequest(slaveId, offset, numberOfRegister);
            ReadHoldingRegistersResponse response = (ReadHoldingRegistersResponse) tcpMaster.sendEx(request);
            if (response.isException()) {
                log.error("readHoldingRegister response: message=" + response.getExceptionMessage());
            } else {
                result = response.getShortData();
            }
            beginPowerExchangeService.changePlcOnlineStatus(1);
        } catch (ModbusTransportException e) {
            log.error("modbus 连接异常:{}", e.getMessage());
            beginPowerExchangeService.changePlcOnlineStatus(0);
        }
        return result;
    }

    /**
     * @Title readInputRegisters
     * @Description: 读取外围设备输入的数据,相当于功能码:04H-读输入寄存器
     * @params: [ip, offset, numberOfRegister]
     * @return: short[]
     * @throws:
     */
    public short[] readInputRegisters(int slaveId, int offset, int numberOfRegister) {
        short[] result = null;
        try {
            ReadInputRegistersRequest request = new ReadInputRegistersRequest(slaveId, offset, numberOfRegister);
            ReadInputRegistersResponse response = (ReadInputRegistersResponse) tcpMaster.sendEx(request);

            if (response.isException()) {
                log.error("readInputRegisters response: message=" + response.getExceptionMessage());
            } else {
                result = response.getShortData();
            }
            beginPowerExchangeService.changePlcOnlineStatus(1);
        } catch (ModbusTransportException e) {
            log.error("modbus 连接异常:{}", e.getMessage());
            beginPowerExchangeService.changePlcOnlineStatus(0);
        }
        return result;
    }

    /**
     * @Title writeCoil
     * @Description: 写单个(线圈)开关量数据,相当于功能码:05H-写单个线圈
     * @params: [ip, writeOffset, writeValue]
     * @return: boolean
     * @throws:
     */
    public boolean writeCoil(int slaveId, int writeOffset, boolean writeValue) {
        boolean result = false;
        try {
            WriteCoilRequest request = new WriteCoilRequest(slaveId, writeOffset, writeValue);
            WriteCoilResponse response = (WriteCoilResponse) tcpMaster.sendEx(request);
            if (response.isException()) {
                log.error("writeCoil response: message=" + response.getExceptionMessage());
            } else {
                result = !response.isException();
            }
            beginPowerExchangeService.changePlcOnlineStatus(1);
        } catch (ModbusTransportException e) {
            log.error("modbus 连接异常:{}", e.getMessage());
            beginPowerExchangeService.changePlcOnlineStatus(0);
        }
        return result;
    }

    /**
     * @Title writeCoils
     * @Description: 写多个开关量数据(线圈),相当于功能码:0FH-写多个线圈
     * @params: [ip, startOffset, data]
     * @return: boolean
     * @throws:
     */
    public boolean writeCoils(int slaveId, int startOffset, boolean[] data) {
        boolean result = false;
        try {
            WriteCoilsRequest request = new WriteCoilsRequest(slaveId, startOffset, data);
            WriteCoilsResponse response = (WriteCoilsResponse) tcpMaster.sendEx(request);

            if (response.isException()) {
                log.error("writeCoils response: message=" + response.getExceptionMessage());
            } else {
                result = !response.isException();
            }
            beginPowerExchangeService.changePlcOnlineStatus(1);
        } catch (ModbusTransportException e) {
            log.error("modbus 连接异常:{}", e.getMessage());
            beginPowerExchangeService.changePlcOnlineStatus(0);
        }
        return result;
    }

    /**
     * @Title writeHoldingRegister
     * @Description: 写单个保持寄存器,相当于功能码:06H-写单个保持寄存器
     * @params: [slaveId, writeOffset, writeValue]
     * @return: boolean true 成功  false 失败
     * @throws:
     */
    public boolean writeHoldingRegister(int slaveId, int writeOffset, short writeValue) {
        boolean result = false;
        try {
            WriteRegisterRequest request = new WriteRegisterRequest(slaveId, writeOffset, writeValue);
            WriteRegisterResponse response = (WriteRegisterResponse) tcpMaster.sendEx(request);
            if (response.isException()) {
                log.error("writeHoldingRegister response: message=" + response.getExceptionMessage());
            } else {
                result = !response.isException();
            }
            beginPowerExchangeService.changePlcOnlineStatus(1);
        } catch (ModbusTransportException e) {
            log.error("modbus 连接异常:{}", e.getMessage());
            beginPowerExchangeService.changePlcOnlineStatus(0);
        }
        return result;

    }

    /**
     * @Title writeHoldingRegisters
     * @Description: 写多个保持寄存器,相当于功能码:10H-写多个保持寄存器
     * @params: [ip, slaveId, startOffset, data]
     * @return: boolean
     * @throws:
     */
    public boolean writeHoldingRegisters(int slaveId, int startOffset, short[] data) {
        boolean result = false;
        try {
            WriteRegistersRequest request = new WriteRegistersRequest(slaveId, startOffset, data);
            WriteRegistersResponse response = (WriteRegistersResponse) tcpMaster.sendEx(request);
            if (response.isException()) {
                log.error("writeHoldingRegisters response: message=" + response.getExceptionMessage());
            } else {
                result = !response.isException();
            }
            beginPowerExchangeService.changePlcOnlineStatus(1);
        } catch (ModbusTransportException e) {
            log.error("modbus 连接异常:{}", e.getMessage());
            beginPowerExchangeService.changePlcOnlineStatus(0);
        }
        return result;
    }


    /**
     * @Title valueRegroup
     * @Description: 转换工具,将Boolean转换成0,1
     * @params: [numberOfBits, values]
     * @return: boolean[]
     * @throws:
     */
    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;
    }
}
相关推荐
许苑向上9 分钟前
最详细【Elasticsearch】Elasticsearch Java API + Spring Boot集成 实战入门(基础篇)
java·数据库·spring boot·elasticsearch
柳叶寒14 分钟前
医院信息化与智能化系统(17)
java·nacos·gateway·全栈·项目
首席架构师专栏16 分钟前
吊打面试官系列:final、finally、finalize 有什么区别?
java
hxj..26 分钟前
【算法】动态规划
java·算法·动态规划
哎呦没33 分钟前
Spring Boot技术实现的导师双选系统架构
spring boot
世间万物皆对象38 分钟前
Java 基础教学:高级特性与实战-集合框架
java·开发语言
不爱吃米饭_42 分钟前
如何优雅处理异常?处理异常的原则
java·开发语言·spring boot
李少兄1 小时前
IntelliJ IDEA 中创建目录时 `.` 和 `/` 的区别
java·ide·intellij-idea
V+zmm101341 小时前
社区养老服务小程序ssm+论文源码调试讲解
java·服务器·前端·javascript·小程序·毕业设计·1024程序员节
李少兄1 小时前
如何在 IntelliJ IDEA 中调整 `Ctrl+/` 快捷键生成注释的位置
java·ide·intellij-idea