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;
    }
}
相关推荐
Yvemil715 分钟前
《开启微服务之旅:Spring Boot 从入门到实践》(三)
java
Anna。。16 分钟前
Java入门2-idea 第五章:IO流(java.io包中)
java·开发语言·intellij-idea
计算机学长felix37 分钟前
基于SpringBoot的“交流互动系统”的设计与实现(源码+数据库+文档+PPT)
spring boot·毕业设计
.生产的驴38 分钟前
SpringBoot 对接第三方登录 手机号登录 手机号验证 微信小程序登录 结合Redis SaToken
java·spring boot·redis·后端·缓存·微信小程序·maven
爱上语文40 分钟前
宠物管理系统:Dao层
java·开发语言·宠物
顽疲43 分钟前
springboot vue 会员收银系统 含源码 开发流程
vue.js·spring boot·后端
王ASC1 小时前
SpringMVC的URL组成,以及URI中对/斜杠的处理,解决IllegalStateException: Ambiguous mapping
java·mvc·springboot·web
撒呼呼1 小时前
# 起步专用 - 哔哩哔哩全模块超还原设计!(内含接口文档、数据库设计)
数据库·spring boot·spring·mvc·springboot
是小崔啊1 小时前
开源轮子 - Apache Common
java·开源·apache
因我你好久不见1 小时前
springboot java ffmpeg 视频压缩、提取视频帧图片、获取视频分辨率
java·spring boot·ffmpeg