Spring boot JAVA 实现RS485的串口数据对接

1.初始化串口

package com.rtzh.server.ask.init;


import com.fazecast.jSerialComm.SerialPort;
import com.rtzh.server.ask.model.vo.RtGasInfoVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Arrays;

@Slf4j
@Component
public class ModbusRtuInit {


    @Value("${serial.port}")
    private String portDescriptor;

    private SerialPort serialPort;

    private InputStream serialInputStream;

    @PostConstruct
    public void init() {
        try {
            log.info("串口-初始化");
            // 1. 获取并配置串口
            serialPort = SerialPort.getCommPort(portDescriptor); // 从配置文件中读取
            serialPort.setBaudRate(9600);
            serialPort.setNumDataBits(8);
            serialPort.setNumStopBits(1);
             //win可不配置,liunx不配置可能会导致打不开端口
            serialPort.setFlowControl(SerialPort.FLOW_CONTROL_DISABLED);
            serialPort.setParity(SerialPort.NO_PARITY);
            // 2. 打开串口
            if (serialPort.openPort()) {
                log.info("串口-打开成功!");
                serialInputStream = serialPort.getInputStream();
                //监听消息
               // new Thread(this::getData).start();
            } else {
                log.error("串口-打开串口-失败");
            }
        } catch (Exception e) {
            log.error("串口-初始化-异常:", e);
        }
    }

    /**
     * 发送16进制消息请请求数据
     * @param hexString
     * @return
     * @throws IOException
     */
    public boolean sendSerial(String hexString) throws IOException {
        OutputStream outputStream = null;
        try {
            log.info("串口-写入数据:{}", hexString);
            outputStream = serialPort.getOutputStream();
            hexString = hexString.replaceAll("\\s+", "");
            // 使用BigInteger转换十六进制字符串为字节数组
            byte[] byteArray = new BigInteger(hexString, 16).toByteArray();
            outputStream.write(byteArray);
            outputStream.flush();
            log.info("串口-写入数据-完成");
        } catch (IOException e) {
            log.error("串口-写入数据-异常:", e);
            throw new RuntimeException(e);
        }finally {
            if(null != outputStream){
                outputStream.close();
            }
        }
        return true;
    }

    /**
     * 获取数据
     * @return
     */
    public RtGasInfoVO getData() {
        byte[] buffer = new byte[9];
        try {
            long startDate = System.currentTimeMillis();
            while (true) {
                if (serialInputStream.available() > 0) {
                    int numRead = serialInputStream.read(buffer);
                    if (numRead > 0) {
                        // 4. 处理收到的原始数据 (通常是字节数组)
                        // 5. 这里是关键:根据Modbus RTU协议解析收到的字节数据
                        // Modbus RTU帧通常包含:从站地址(1字节)、功能码(1字节)、数据、CRC校验(2字节)
                        // 示例:打印十六进制
                        String[] serialArr = bytesToHexString(buffer);
                        log.info("串口-processReceivedData-收到数据: {}" , Arrays.asList(serialArr));
                        if(null != serialArr && serialArr.length > 0){
                            String deviceNo = serialArr[0];
                            int i1 = Integer.parseInt(serialArr[3] + serialArr[4] + serialArr[5] + serialArr[6], 16);
                            BigDecimal dataDecimal = new BigDecimal(i1).divide(new BigDecimal("1000"), 2, BigDecimal.ROUND_HALF_UP);
                            RtGasInfoVO rtGasInfoVO = new RtGasInfoVO();
                            rtGasInfoVO.setDeviceNo(deviceNo);
                            rtGasInfoVO.setData(dataDecimal.doubleValue());
                            return rtGasInfoVO;
                        }
                    }
                    long endDate = System.currentTimeMillis();
                    //超过一分钟自动退出
                    if((endDate - startDate) > (60 * 1000)){
                        return null;
                    }
                }
                Thread.sleep(100); // 避免CPU空转
            }
        } catch (Exception e) {
            log.info("串口-异常:", e);
        }
        return null;
    }

    @PreDestroy
    public void cleanup() {
        // 6. 应用关闭时释放串口资源
        if (serialPort != null && serialPort.isOpen()) {
            serialPort.closePort();
            System.out.println("串口已关闭。");
            log.info("串口-已关闭");
        }
    }

    public String[] bytesToHexString(byte[] src){
        String[] arr = new String[9];
        if (src == null || src.length <= 0) {
            return null;
        }
        for (int i = 0; i < src.length; i++) {
            int v = src[i] & 0xFF;
            String hv = Integer.toHexString(v);
            if (hv.length() < 2) {
                arr[i]= "00";
            }
            arr[i]= hv.length() == 1 ? "0"+ hv : hv;
        }
        return arr;
    }


    public static void main(String[] args) {
        System.out.println(Integer.parseInt("000051a3", 16));
    }
}

2.配置参数

serial:
  port: COM3

modbus:
  instructArray:
    - 01 03 9C 42 00 02 4A 4F
    - 02 03 9C 42 00 02 4A 7C
    - 03 03 9C 42 00 02 4B AD
    - 04 03 9C 42 00 02 4A 1A

3.定时任务调用

package com.rtzh.server.ask.task;

import com.rtzh.server.ask.init.ModbusRtuInit;
import com.rtzh.server.ask.model.bo.InstructArrayBO;
import com.rtzh.server.ask.model.entity.RtGasInfoEntity;
import com.rtzh.server.ask.model.vo.RtGasInfoVO;
import com.rtzh.server.ask.service.RtGasInfoService;
import com.rtzh.server.common.constant.RedisKeyConstant;
import com.rtzh.server.common.util.RedisUtil;
import com.rtzh.server.common.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.Date;

@Slf4j
@Component
public class GetGasInfoTask {

    @Resource
    private InstructArrayBO instructArrayBO;

    @Resource
    private RedisUtil redisUtil;

    @Resource
    private ModbusRtuInit modbusRtuInit;

    @Resource
    private RtGasInfoService rtGasInfoService;

    @Scheduled(cron = "0 0/1 * * * ?")
    public void getDiscernResult(){

        log.info("气体检测-获取数据-开始");
        try {
            String redisTaskLock = RedisKeyConstant.GET_GAS_TASK_LOCK;
            String redisLock = redisUtil.getString(redisTaskLock);
            if(StringUtils.isNotBlank(redisLock)) {
                log.info("气体检测-获取数据-未获取到锁,稍后再试");
                return ;
            }
            redisUtil.set(redisTaskLock, 1, 60 * 1);
            RtGasInfoEntity rtGasInfoEntity = new RtGasInfoEntity();
            for(String instruct : instructArrayBO.getInstructArray()){
                modbusRtuInit.sendSerial(instruct);
                RtGasInfoVO rtuInitData = modbusRtuInit.getData();
                if(null != rtuInitData){
                    String deviceNo = rtuInitData.getDeviceNo();
                    Double data = rtuInitData.getData();
                    if(deviceNo.equals("01")){
                        //氧气
                        rtGasInfoEntity.setOxygen(data);
                    } else if (deviceNo.equals("02")) {
                        //一氧化碳
                        rtGasInfoEntity.setCarbonMonoxide(data);
                    } else if (deviceNo.equals("03")) {
                        //可燃气体
                        rtGasInfoEntity.setCombustibleGas(data);
                    } else if (deviceNo.equals("04")) {
                        //硫化氢
                        rtGasInfoEntity.setHydrogenSulfide(data);
                    }
                }else {
                    log.error("气体检测-获取数据-异常,为获取到气体数据-代码:{}", instruct);
                    return;
                }
            }
            Date date = new Date();
            rtGasInfoEntity.setCreateDate(date);
            rtGasInfoEntity.setUpdateDate(date);
            rtGasInfoService.save(rtGasInfoEntity);
            redisUtil.del(redisTaskLock);
        } catch (IOException e) {
            log.error("气体检测-获取数据-异常:", e);
        }
    }
}

4.发送参数实体类

package com.rtzh.server.ask.model.bo;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Data
@Component
@ConfigurationProperties(prefix = "modbus")
public class InstructArrayBO {

    private String[] instructArray;
}

注意:写入读取都是用的串口流完成

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值