当前位置: 首页 > article >正文

springboot 整合 modbus4j

pom.xml

<dependency>
   	<groupId>com.serotonim</groupId>
    <artifactId>modbus4j</artifactId>
</dependency>

配置类

@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

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操作类

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;
    }
}


http://www.kler.cn/a/376818.html

相关文章:

  • leetcode hot100【LeetCode 3. 无重复字符的最长子串】java实现
  • 杂货 | 每日资讯 | 2024.11.1
  • nodejs入门教程19:nodejs dns模块
  • 市场分化!汽车零部件「变天」
  • Nature Methods | 新型三维光场显微成像技术
  • 2.数组越界访问如何调试HardFault错误
  • Flutter图片控件(七)
  • 个人学习React Native的实际意义探讨
  • 练习LabVIEW第三十三题
  • 【C++】1968. 输出ascii码对应的字符
  • PAT甲级-1052 Linked List Sorting
  • #渗透测试#SRC漏洞挖掘# 信息收集-Shodan进阶之Mongodb未授权访问
  • sql进阶篇
  • 华为云安装docker
  • GNSS和PTP时间同步的基础原理介绍
  • Android 百度面经
  • Golang反射原理
  • 问:Redis常见性能问题及解法?
  • LeetCode每日一题3226---使两个整数相等的位更改次数
  • C#WPF使用CommunityToolkit.Mvvm库
  • jmeter基础01-1_环境准备-windows系统安装jdk
  • 基本查询【MySQL】
  • token无感刷新+处理并发的后端方案
  • Vue3+TypeScript+Vite 后台管理项目_登录页面开发实战
  • 项目实战:基于Linux的Flappy bird游戏开发
  • 第二届开放原子大赛-开源工业软件算法集成大赛即将启动!