注意:RXTX库发送和接受数据是通过InputStream和OutputStream来获取和发送数据的,并且RXTX库只适用于JDK1.8_2及以下的版本
1.POM文件的导入
<dependency>
<groupId>com.fazecast</groupId>
<artifactId>jSerialComm</artifactId>
<version>2.10.5</version>
</dependency>
2.写一个实体类
@Component
@AllArgsConstructor
@NoArgsConstructor
@Data
public class SerialEntity {
@ApiModelProperty(value = "端口名称")
private String PortName = "COM9";
@ApiModelProperty(value = "请求指令")
private String powerOffCommand;
private int BAUDRATE = 2400;
private int DATALENGTH = 8;
private int STOPBIT = 1;
private String PARITY = "NONE"
@ApiModelProperty(name="过载故障")
private char Overloadwarning;
@ApiModelProperty(name="过温故障")
private char Overtemperature;
@ApiModelProperty(name="电池过低故障")
private char Batterylow;
@ApiModelProperty(name="省电模式名称")
private String Batterymode;
@ApiModelProperty(name="剩余电量")
private String BatteryVoltage;
}
3.common工具类
package com.ruoyi.equipment.serialportcommon;
public class common {
public static String HEX_STRING = "0123456789ABCDEF";
public static final String NONE = "无";
public static final String ODD = "奇";
public static final String EVEN = "偶";
public static final String FORMAT_HEX="HEX";
}
4.SerialUtil配置类
package com.zkyq.acss.comm;
import gnu.io.SerialPort;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
public class SerialUtil {
/**
* 转为 HEX
* @param str
* @return
*/
public static String toHex(String str){
StringBuffer sbf = new StringBuffer();
byte[] b = str.getBytes(StandardCharsets.UTF_8);
for (int i = 0; i < b.length; i++) {
String hex = Integer.toHexString(b[i] & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
sbf.append(hex.toUpperCase() + " ");
}
return sbf.toString().trim();
}
/**
*
* @param hex
* @return
*/
public static String toStr(String hex) {
return new String(hexToByte(hex));
}
/**
* 转 HEX 字节
* @param hex
* @return
*/
public static byte[] hexToByte(String hex){
hex = hex.toUpperCase().replace(" ","");
ByteArrayOutputStream bao = new ByteArrayOutputStream(hex.length() / 2);
// 将每2位16进制整数组装成一个字节
for (int i = 0; i < hex.length(); i += 2) {
bao.write((common.HEX_STRING.indexOf(hex.charAt(i)) << 4 | common.HEX_STRING.indexOf(hex.charAt(i + 1))));
}
return bao.toByteArray();
}
/**
* 获取校验位配置
* @param checkBit
* @return
*/
public static int getParity(String checkBit){
if (common.NONE.equals(checkBit)){
return SerialPort.PARITY_NONE;
} else if (common.ODD.equals(checkBit)){
return SerialPort.PARITY_ODD;
} else if (common.EVEN.equals(checkBit)){
return SerialPort.PARITY_EVEN;
} else {
return SerialPort.PARITY_NONE;
}
}
/**
* 读取数据
* @param in
* @return
*/
public static byte[] readFromPort(InputStream in) {
byte[] bytes = {};
try {
// 缓冲区大小为一个字节
byte[] readBuffer = new byte[1];
int bytesNum = in.read(readBuffer);
while (bytesNum > 0) {
bytes = concat(bytes, readBuffer);
bytesNum = in.read(readBuffer);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (in != null) {
in.close();
in = null;
}
} catch (IOException e) {
e.printStackTrace();
}
}
return bytes;
}
/**
* 字节转换
* @param format
* @param b
* @return
*/
public static String printHexString(String format, byte[] b) {
String result = new String(b);
if (common.FORMAT_HEX.equals(format)){
return SerialUtil.toHex(result);
}
return result;
}
/**
* 合并数组
*
* @param firstArray 第一个数组
* @param secondArray 第二个数组
* @return 合并后的数组
*/
public static byte[] concat(byte[] firstArray, byte[] secondArray) {
if (firstArray == null || secondArray == null) {
if (firstArray != null) {
return firstArray;
}
if (secondArray != null) {
return secondArray;
}
return null;
}
byte[] bytes = new byte[firstArray.length + secondArray.length];
System.arraycopy(firstArray, 0, bytes, 0, firstArray.length);
System.arraycopy(secondArray, 0, bytes, firstArray.length, secondArray.length);
return bytes;
}
}
5.RSConfig功能实现类
package com.zkyq.acss.config;
import com.zkyq.acss.comm.SerialUtil;
import com.zkyq.acss.entity.SerialEntity;
import gnu.io.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.TooManyListenersException;
import java.util.concurrent.ConcurrentHashMap;
/*
* 用于配置和管理RS-232(或其他串行通信接口)端口的连接。主要功能包括打开端口、关闭端口和发送数据
* */
@Slf4j
@Component
public class Rs232Config {
// 定义一个常量,用于表示延迟时间,单位为毫秒
/* private static final int DELAY_TIME = 1000;*/
// 使用Spring的@Autowired注解,自动装配SerialPortConfig的实例
// 使用Spring的@Autowired注解,自动装配SerialEntity的实例
private SerialUtil serialUtil;
/**
* 缓存端口实例
* 使用ConcurrentHashMap以支持并发访问
*/
private Map<String, SerialPort> serialPortMap = new ConcurrentHashMap<>(16);
public boolean closePort(SerialEntity serial) {
// 从map中移除并获取对应的SerialPort实例
SerialPort serialPort = serialPortMap.remove(serial.getPortName());
if (null != serialPort) {
// 关闭串口
serialPort.close();
System.out.println("关闭串口成功");
}
return false;
}
public boolean openPort(SerialEntity serial) {
if (serial == null) {
log.error(("serial is null"));
return false;
}
String portName = serial.getPortName();
System.out.println("端口号" + portName);
CommPortIdentifier portIdentifier = null;
SerialPort serialPort = null;
int bitRate = 0, dataBit = 0, stopBit = 0, parity = 0;
try {
// 获取串口对应的CommPortIdentifier对象
portIdentifier = CommPortIdentifier.getPortIdentifier(portName);
System.out.println("portIdentifier" + portIdentifier);
// 打开串口,并获取SerialPort对象
if (portIdentifier.isCurrentlyOwned()) {
System.err.println("Error: Port is currently in use");
} else {
// 打开串口,并设置延迟时间为DELAY_TIME
serialPort = (SerialPort) portIdentifier.open("", 1000);
// 设置监听器生效,当有数据时通知
// notifyOnDataAvailable(true) 表示当有数据到达串口时,会触发数据到达事件
serialPort.notifyOnDataAvailable(true);
// 从serial对象中获取串口参数(这里假设serial对象包含了这些参数)
// 比特率
bitRate = serial.getBAUDRATE();
// 数据位
dataBit = serial.getDATALENGTH();
// 停止位
stopBit = serial.getSTOPBIT();
// 校验位,通过SerialUtil工具类转换为字符串表示
parity = SerialUtil.getParity(serial.getPARITY());
// 设置串口参数:比特率、数据位、停止位、奇偶校验位
serialPort.setSerialPortParams(bitRate, dataBit, stopBit, parity);
}
// 注意:这里假设SerialPort.getParity()是存在的,实际上应该是直接传入int类型的校验位
/* System.out.println("是否获取到数据"+serial.getPARITY().toString());*/
} catch (PortInUseException | NoSuchPortException e) {
log.error("Open CommPortIdentifier {} Exception:", serial.getPortName(), e);
return false;
} catch (UnsupportedCommOperationException e) {
log.error("Set SerialPortParams BitRate {} DataBit {} StopBit {} Parity {} Exception:", bitRate, dataBit, stopBit, parity, e);
return false;
}
// 设置当前串口的输入输出流
InputStream input;
OutputStream output;
try {
input = serialPort.getInputStream();
output = serialPort.getOutputStream();
} catch (IOException e) {
log.error("Get serialPort data stream exception:", e);
return false;
} catch (Exception e) {
log.error("An error occurred while opening the port:", e);
return false;
}
// 给当前串口添加一个监听器
try {
serialPort.addEventListener(new Serial232Listener(input, output, serial.getFormat()));
} catch (TooManyListenersException e) {
log.error("Get serialPort data stream exception:", e);
return false;
}
serialPortMap.put(portName, serialPort);
return true;
}
/**
* 关闭端口
*
* @param portId 端口ID(但注意,在方法内部被硬编码为"COM5")
*/
public SerialEntity readData(String portId) {
SerialEntity serial = new SerialEntity();
SerialPort serialPort = serialPortMap.get(portId);;
InputStream input;
try {
input = serialPort.getInputStream();
System.out.println("input" + input);
byte[] bytes = serialUtil.readFromPort(input);
System.out.println("bytes: " + bytes );
/* char[] charArray = new char[bytes.length];*/
System.out.println("bytes: " );
// 创建一个StringBuilder来构建字符串,这比使用String连接更高效
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
// 直接将字节转换为字符(假设是ASCII编码)
char asciiChar = (char) b;
sb.append(asciiChar);
}
// 将StringBuilder转换为String并打印
String resultString = sb.toString();
System.out.println("Received string: " + resultString); // 假设这会是"210.6"或其他有效的文本字符串
/* String[] strArray = resultString.split("\\s+");*/
char seven = resultString.charAt(7);
char eight = resultString.charAt(8);
char tweleve = resultString.charAt(12);
serial.setBatterylow(seven);
serial.setOverloadwarning(eight);
serial.setOvertemperature(tweleve);
System.out.println(serial.getBatterylow());
System.out.println(serial.getOverloadwarning());
System.out.println(serial.getOvertemperature());
} catch (IOException e) {
throw new RuntimeException(e);
}
return serial;
}
public SerialEntity readData1(String portId) {
SerialEntity serial = new SerialEntity();
SerialPort serialPort = serialPortMap.get(portId);;
InputStream input;
try {
input = serialPort.getInputStream();
System.out.println("input" + input);
byte[] bytes = SerialUtil.readFromPort(input);
System.out.println("bytes: " );
// 创建一个StringBuilder来构建字符串,这比使用String连接更高效
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
// 直接将字节转换为字符(假设是ASCII编码)
char asciiChar = (char) b;
sb.append(asciiChar);
}
// 将StringBuilder转换为String并打印
String resultString = sb.toString();
System.out.println("Received string: " + resultString); // 假设这会是"210.6"或其他有效的文本字符串
String[] strArray = resultString.split("\\s+");
// 检查数组长度以确保有足够的部分
if (strArray.length > 1) {
// 获取第二个部分(索引为1)
String firstPart = strArray[2];
serial.setBatteryVoltage(firstPart);
System.out.println(serial.getBatteryVoltage());
} else {
System.out.println("Not enough parts in the string.");
}
} catch (IOException e) {
throw new RuntimeException(e);
}
return serial;
}
public SerialEntity readData2(String portId) {
SerialEntity serial = new SerialEntity();
SerialPort serialPort = serialPortMap.get(portId);;
InputStream input;
try {
input = serialPort.getInputStream();
System.out.println("input" + input);
byte[] bytes = SerialUtil.readFromPort(input);
System.out.println("bytes: " );
// 创建一个StringBuilder来构建字符串,这比使用String连接更高效
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
// 直接将字节转换为字符(假设是ASCII编码)
char asciiChar = (char) b;
sb.append(asciiChar);
}
// 将StringBuilder转换为String并打印
String resultString = sb.toString();
System.out.println("Received string: " + resultString); // 假设这会是"210.6"或其他有效的文本字符串
String[] strArray = resultString.split("\\s+");
// 检查数组长度以确保有足够的部分
if (strArray.length > 0) {
// 获取第二个部分(索引为1)
String firstPart = strArray[0];
serial.setBatterymode(firstPart);
System.out.println(serial.getBatterymode());
} else {
System.out.println("Not enough parts in the string.");
}
} catch (IOException e) {
throw new RuntimeException(e);
}
return serial;
}
public boolean sendData(String portId, String message) {
SerialPort serialPort = serialPortMap.get(portId);
System.out.println("serialPort" + serialPort);
if (null == serialPort){
return false;
}
OutputStream output = null;
try {
byte[] bytes = message.getBytes(StandardCharsets.UTF_8);
output = serialPort.getOutputStream();
output.write(bytes);
output.flush();
return true;
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (null != output){
try {
output.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
6.UpsManager逻辑处理类
package com.zkyq.acss.config;
import com.zkyq.acss.entity.SerialEntity;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@Slf4j
@Component
public class UPSManager {
// 使用Spring的自动装配特性注入Rs232Config的实例
@Autowired
private Rs232Config rs232Config;
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
// 关闭UPS连接的端口,并发送关机命令
public boolean closePort(SerialEntity serial) {
try {
if (rs232Config != null) {
rs232Config.openPort(serial);
boolean closeport = rs232Config.sendData(serial.getPortName(), serial.getPowerOffCommand());
System.out.println("closeport" + closeport);
boolean portclosed = rs232Config.closePort(serial);
if (portclosed) {
System.out.println("已发送指令");
}
} else {
System.out.println("rs232Config is null");
return false;
}
} catch (RuntimeException e) {
e.printStackTrace();
return false;
}
return true;
}
public boolean openPort(SerialEntity serial) {
// 打开COM5端口(注意:这里假设portId参数未被使用)
if (serial == null) {
System.out.println("SerialEntity 对象为空,无法打开端口");
return false;
}
if (rs232Config == null) {
System.out.println("rs232Config 对象为空,无法操作端口" + rs232Config);
return false;
}
boolean portOpened = rs232Config.openPort(serial);
if (portOpened) {
rs232Config.sendData(serial.getPortName(), serial.getPowerOffCommand());
System.out.println("已发送指令");
} else {
System.out.println("端口未打开");
return false;
}
rs232Config.closePort(serial);
return true;
}
// 获取UPS的当前状态
public SerialEntity CurrentStatus(SerialEntity serial) {
SerialEntity SS = new SerialEntity();
try{
Thread.sleep(1200);
rs232Config.openPort(serial);
rs232Config.sendData(serial.getPortName(), serial.getPowerOffCommand());
SS = rs232Config.readData(serial.getPortName());
rs232Config.closePort(serial);
} catch (InterruptedException e) {
System.out.println("线程被中断");
}
return SS;
}
public SerialEntity ReaminBatteryTime (SerialEntity serial) {
SerialEntity SS = new SerialEntity();
// 设置UPS电池剩余时间请求命令
/* statusRequestCommand = "F\r\n";*/
try {
Thread.sleep(500);
rs232Config.openPort(serial);
rs232Config.sendData(serial.getPortName(), serial.getPowerOffCommand());
SS = rs232Config.readData1(serial.getPortName());
rs232Config.closePort(serial);
// 这里只是模拟了一个成功的请求,并返回true
} catch (InterruptedException e) {
System.out.println("线程被中断");
}
return SS;
}
public SerialEntity CurrentMode (SerialEntity serial){
SerialEntity SS = new SerialEntity();
// 设置UPS当前模式请求命令
rs232Config.openPort(serial);
rs232Config.sendData(serial.getPortName(), serial.getPowerOffCommand());
SS = rs232Config.readData2(serial.getPortName());
rs232Config.closePort(serial);
// 这里只是模拟了一个成功的请求,并返回true
return SS;
}
public boolean openPortAfterMinutes (SerialEntity serial){
scheduler.schedule(() -> {
try {
/* UPSManager upsManager = new UPSManager(); */
if (serial == null) {
System.out.println("SerialEntity 对象为空,无法打开端口");
}
if (rs232Config == null) {
System.out.println("rs232Config 对象为空,无法操作端口" + rs232Config);
// 注意:这里也不需要 return false
}
boolean portOpened = rs232Config.openPort(serial);
if (portOpened) {
rs232Config.sendData(serial.getPortName(), serial.getPowerOffCommand());
System.out.println("已发送指令");
} else {
System.out.println("端口未打开");
// 注意:这里也不需要 return false
}
rs232Config.closePort(serial);
// 注意:其他代码逻辑...
// 注意:这里不需要 return true,因为 Runnable 没有返回值
} catch (Exception e) {
e.printStackTrace(); // 处理异常
}
}, serial.getMinutes(), TimeUnit.MINUTES);
// 返回 true 表示操作已成功安排(但不一定已完成)
return true;
}
public boolean closePortAfterMinutes (SerialEntity serial ){ // 假设你需要知道哪个端口需要被关闭
scheduler.schedule(() -> {
try {
/* UPSManager upsManager = new UPSManager();*/
try {
if (rs232Config != null) {
rs232Config.openPort(serial);
boolean closeport = rs232Config.sendData(serial.getPortName(), serial.getPowerOffCommand());
System.out.println("closeport" + closeport);
boolean portclosed = rs232Config.closePort(serial);
if (portclosed) {
System.out.println("已发送指令");
}
} else {
System.out.println("rs232Config is null");
}
} catch (RuntimeException e) {
e.printStackTrace();
}
// 在 minutes 分钟后执行 closePort
} catch (Exception e) {
e.printStackTrace(); // 处理异常
}
}, serial.getMinutes(), TimeUnit.MINUTES);
// 返回 true 表示操作已成功安排(但不一定已完成)
return true;
}
}
7.RS232Controller请求处理类
package com.zkyq.acss.controller;
import com.zkyq.acss.config.UPSManager;
import com.zkyq.acss.entity.SerialEntity;
import com.zkyq.acss.util.ResponseObj;
import gnu.io.NoSuchPortException;
import io.swagger.annotations.Api;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
/*它用于处理与RS-232串口通信相关的HTTP请求*/
@RestController
@RequestMapping("/UPS/RS232")
@Api(tags = "UPS接入企业端")
public class Rs232Controller {
@Autowired
UPSManager upsManager;
/* @Autowired
TimeService timeService;*/
/**
* 监听端口
*
* @param
*/
@PostMapping("/open")
public ResponseObj open(@RequestBody SerialEntity serial) {
return ResponseObj.success(upsManager.openPort(serial));
}
@PostMapping("/close")
public ResponseObj close(@RequestBody SerialEntity serial) {
/* serialController.getSerialPortList();*/
return ResponseObj.success(upsManager.closePort(serial));
}
@PostMapping("/CurrentStatus")
public ResponseObj CurrentStatus(@RequestBody SerialEntity serial) throws IOException, NoSuchPortException {
return ResponseObj.success(upsManager.CurrentStatus(serial));
}
@PostMapping("/ReaminBatteryTime")
public ResponseObj ReaminBatteryTime(@RequestBody SerialEntity serial) {
/* serialController.getSerialPortList();*/
return ResponseObj.success(upsManager.ReaminBatteryTime(serial));
}
@PostMapping("/CurrentMode")
public ResponseObj CurrentMode(@RequestBody SerialEntity serial) {
return ResponseObj.success(upsManager.CurrentMode(serial));
}
//设置几分钟后自动开启
@PostMapping("/time")
public ResponseObj Time(@RequestBody SerialEntity serial) {
return ResponseObj.success(upsManager.openPortAfterMinutes(serial));
}
//设置几分钟后自动关闭
@PostMapping("/time2")
public ResponseObj Time2(@RequestBody SerialEntity serial) {
return ResponseObj.success( upsManager.closePortAfterMinutes(serial));
}
}