开发环境:Spring Boot
通讯设备:读卡器
协议波特率:19200
- 1.测试接口代码
@Autowired
private CommunicationUtils communicationUtils;
/**
* 执行指令
* @param comNo 串口号
* @param instruct 指令
* @return 接收指令
*/
@RequestMapping(value = "/get/address", method = RequestMethod.GET)
public String readAddress(@RequestParam(value = "comNo", required = true) String comNo,
@RequestParam(value = "instruct", required = false) String instruct) {
return communicationUtils.send(instruct, comNo);
}
- 2.串口管理类
import gnu.io.*;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.TooManyListenersException;
/**
* 串口管理
*
* @author yangle
*/
@SuppressWarnings("all")
@Slf4j
public class SerialPortManager {
/**
* 查找所有可用端口
* @return 可用端口名称列表
*/
public static final List<String> findPorts() {
// 获得当前所有可用串口
Enumeration<CommPortIdentifier> portList = CommPortIdentifier.getPortIdentifiers();
List<String> portNameList = new ArrayList<String>();
// 将可用串口名添加到List并返回该List
while (portList.hasMoreElements()) {
String portName = portList.nextElement().getName();
portNameList.add(portName);
}
return portNameList;
}
/**
* 打开串口
*
* @param portName
* 端口名称
* @param baudrate
* 波特率
* @return 串口对象
* @throws PortInUseException
* 串口已被占用
*/
public static final SerialPort openPort(String portName, int baudrate) throws PortInUseException {
try {
// 通过端口名识别端口
CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier(portName);
// 打开端口,并给端口名字和一个timeout(打开操作的超时时间)
CommPort commPort = portIdentifier.open(portName, 2000);
// 判断是不是串口
if (commPort instanceof SerialPort) {
SerialPort serialPort = (SerialPort) commPort;
try {
// 设置一下串口的波特率等参数
// 数据位:8
// 停止位:1
// 校验位:None
serialPort.setSerialPortParams(baudrate, SerialPort.DATABITS_8, SerialPort.STOPBITS_1,
SerialPort.PARITY_NONE);
} catch (UnsupportedCommOperationException e) {
e.printStackTrace();
}
return serialPort;
}
} catch (NoSuchPortException e1) {
e1.printStackTrace();
}
return null;
}
/**
* 关闭串口
*
* @param serialport
* 待关闭的串口对象
*/
public static void closePort(SerialPort serialPort) {
if (serialPort != null) {
serialPort.close();
}
}
/**
* 往串口发送数据
*
* @param serialPort
* 串口对象
* @param order
* 待发送数据
*/
public static void sendToPort(SerialPort serialPort, byte[] order) {
OutputStream out = null;
try {
out = serialPort.getOutputStream();
out.write(order);
out.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (out != null) {
out.close();
out = null;
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 从串口读取数据
*
* @param serialPort
* 当前已建立连接的SerialPort对象
* @return 读取到的数据
*/
public static byte[] readFromPort(SerialPort serialPort) {
InputStream in = null;
byte[] bytes = {};
try {
in = serialPort.getInputStream();
// 缓冲区大小为一个字节
byte[] readBuffer = new byte[1];
int bytesNum = in.read(readBuffer);
while (bytesNum > 0) {
bytes = ArrayUtils.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 port
* 串口对象
* @param listener
* 串口存在有效数据监听
*/
public static void addListener(SerialPort serialPort, DataAvailableListener listener) {
try {
// 给串口添加监听器
serialPort.addEventListener(new SerialPortListener(listener));
// 设置当有数据到达时唤醒监听接收线程
serialPort.notifyOnDataAvailable(true);
// 设置当通信中断时唤醒中断线程
serialPort.notifyOnBreakInterrupt(true);
} catch (TooManyListenersException e) {
e.printStackTrace();
}
}
/**
* 串口监听
*/
public static class SerialPortListener implements SerialPortEventListener {
private DataAvailableListener mDataAvailableListener;
public SerialPortListener(DataAvailableListener mDataAvailableListener) {
this.mDataAvailableListener = mDataAvailableListener;
}
public void serialEvent(SerialPortEvent serialPortEvent) {
switch (serialPortEvent.getEventType()) {
case SerialPortEvent.DATA_AVAILABLE: // 1.串口存在有效数据
if (mDataAvailableListener != null) {
mDataAvailableListener.dataAvailable();
}
break;
case SerialPortEvent.OUTPUT_BUFFER_EMPTY: // 2.输出缓冲区已清空
break;
case SerialPortEvent.CTS: // 3.清除待发送数据
break;
case SerialPortEvent.DSR: // 4.待发送数据准备好了
break;
case SerialPortEvent.RI: // 5.振铃指示
break;
case SerialPortEvent.CD: // 6.载波检测
break;
case SerialPortEvent.OE: // 7.溢位(溢出)错误
break;
case SerialPortEvent.PE: // 8.奇偶校验错误
break;
case SerialPortEvent.FE: // 9.帧错误
break;
case SerialPortEvent.BI: // 10.通讯中断
log.error("与串口设备通讯中断");
break;
default:
break;
}
}
}
/**
* 串口存在有效数据监听
*/
public interface DataAvailableListener {
/**
* 串口存在有效数据
*/
void dataAvailable();
}
}
- 3.通讯工具类
import gnu.io.PortInUseException;
import gnu.io.SerialPort;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* Coding by 李炯 on 2019/9/2 15:10
*/
@Slf4j
@Component
public class CommunicationUtils {
public String send(String instruct, String comNo) {
String[] result = {""};//指令返回结果
// Setp 1 :init串口对象
SerialPort mSerialport = null;
try {
mSerialport = SerialPortManager.openPort(comNo, 19200);
// Setp 2 :发送数据
SerialPortManager.sendToPort(mSerialport, ByteUtils.hexStr2Byte(instruct));
// Setp 3 :添加串口监听
SerialPort finalMSerialport = mSerialport;
SerialPortManager.addListener(mSerialport, new SerialPortManager.DataAvailableListener() {
@Override
public void dataAvailable() {
byte[] data = null;
try {
if (finalMSerialport == null) {
log.info("串口对象为空,监听失败!");
} else {
data = SerialPortManager.readFromPort(finalMSerialport); // 读取串口数据
result[0] = ByteUtils.byteArrayToHexString(data);// 以十六进制的形式接收数据
}
} catch (Exception e) {
log.error(e.toString());
// 发生读取错误时显示错误信息后退出系统
System.exit(0);
}
}
});
// Setp 4 :关闭串口
Thread.sleep(300);// 这里是为了监听返回的数据,50也可以,保险起见设置200
SerialPortManager.closePort(mSerialport);
} catch (PortInUseException e) {
log.error("串口:" + comNo + " 打开异常");
log.error(e.getMessage());
} catch (InterruptedException e) {
log.error("Thread.sleep 打开异常");
}
return result[0];
}
}
- 3.字节数组转换类
import lombok.extern.slf4j.Slf4j;
import java.nio.ByteBuffer;
import java.util.Locale;
/**
* Byte转换工具
*
* @author yangle
*/
@Slf4j
public class ByteUtils {
/**
* 十六进制字符串转byte[]
* 作用:用于发送指令(给串口设备)
* @param hex 十六进制字符串
* @return byte[]
*/
public static byte[] hexStr2Byte(String hex) {
if (hex == null) {
return new byte[] {};
}
// 奇数位补0
if (hex.length() % 2 != 0) {
hex = "0" + hex;
}
int length = hex.length();
ByteBuffer buffer = ByteBuffer.allocate(length / 2);
int index=0;
String indexStr="";
try {
for (int i = 0; i < length; i++) {
String hexStr = hex.charAt(i) + "";
i++;
hexStr += hex.charAt(i);
indexStr= hexStr;
index = i;
byte b = (byte) Integer.parseInt(hexStr, 16);
buffer.put(b);
log.debug("ByteUtils.hexStr2Byte ----> indexStr:"+indexStr);
log.debug("ByteUtils.hexStr2Byte ----> b:"+b);
}
}catch (Exception e){
log.error("ByteUtils.hexStr2Byte ----> indexStr:"+indexStr);
log.error("ByteUtils.hexStr2Byte ----> index:"+index);
}
return buffer.array();
}
/**
* byte[]转十六进制字符串
* 作用:用于接收指令(接收的byte[]转换成HEX)
* @param array byte[]
* @return 十六进制字符串
*/
public static String byteArrayToHexString(byte[] array) {
if (array == null) {
return "";
}
StringBuffer buffer = new StringBuffer();
for (int i = 0; i < array.length; i++) {
buffer.append(byteToHex(array[i]));
}
return buffer.toString();
}
/**
* byte转十六进制字符
* @param b byte
* @return 十六进制字符
*/
public static String byteToHex(byte b) {
String hex = Integer.toHexString(b & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
return hex.toUpperCase(Locale.getDefault());
}
}
- 4.数组工具类
/**
* 数组工具
*
* @author yangle
*/
public class ArrayUtils {
/**
* 合并数组
*
* @param firstArray 第一个数组
* @param secondArray 第二个数组
* @return 合并后的数组
*/
public static byte[] concat(byte[] firstArray, byte[] secondArray) {
if (firstArray == null || secondArray == null) {
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;
}
}
效果
说明:效果图中返回是指令解析对象
上面代码运行后返回应该只有