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;
}
注意:写入读取都是用的串口流完成