package chuankou;
|
|
import java.util.ArrayList;
|
import java.util.Arrays;
|
import java.util.List;
|
|
public class SerialDataReceiver {
|
private static final int BUFFER_SIZE = 1024;
|
private static final int MIN_PACKET_LENGTH = 9;
|
private static final byte[] START_MARKER = {(byte) 0xDD, (byte) 0xCC};
|
|
// 使用非静态成员避免多线程环境下的竞争条件
|
private byte[] dataBuffer = new byte[BUFFER_SIZE];
|
private int bufferPosition = 0;
|
private final List<byte[]> reusablePackets = new ArrayList<>();
|
|
/**
|
* 实例方法:接收串口原始数据并解析完整数据包
|
* @param rawData 原始数据
|
* @param debugEnabled 是否启用调试
|
* @param maxRawDataPrintLength 最大打印长度
|
* @return 解析出的完整数据包列表,如果没有完整包则返回空列表
|
*/
|
public List<byte[]> receiveData(byte[] rawData, boolean debugEnabled, int maxRawDataPrintLength) {
|
reusablePackets.clear();
|
|
if (rawData == null || rawData.length == 0) {
|
return reusablePackets;
|
}
|
|
// 打印原始接收数据(调试用)
|
if (debugEnabled) {
|
printRawData("收到串口原始数据", rawData, maxRawDataPrintLength);
|
}
|
|
// 检查缓冲区容量,动态处理
|
if (!ensureBufferCapacity(rawData.length)) {
|
// 缓冲区不足时,清理并重新开始
|
if (debugEnabled) {
|
System.out.println("缓冲区不足,清空缓冲区重新开始");
|
}
|
bufferPosition = 0;
|
}
|
|
// 将数据添加到缓冲区
|
System.arraycopy(rawData, 0, dataBuffer, bufferPosition, rawData.length);
|
bufferPosition += rawData.length;
|
|
// 处理缓冲区中的数据并收集完整包
|
processBuffer(reusablePackets, debugEnabled);
|
|
return new ArrayList<>(reusablePackets);
|
}
|
|
/**
|
* 确保缓冲区有足够容量,如不够则尝试压缩
|
*/
|
private boolean ensureBufferCapacity(int required) {
|
if (bufferPosition + required <= dataBuffer.length) {
|
return true;
|
}
|
|
// 尝试通过压缩缓冲区来腾出空间
|
int startIndex = findStartMarker();
|
if (startIndex > 0) {
|
compactBuffer(startIndex);
|
return bufferPosition + required <= dataBuffer.length;
|
}
|
|
return false;
|
}
|
|
/**
|
* 处理缓冲区中的数据,解析完整数据包
|
*/
|
private void processBuffer(List<byte[]> completePackets, boolean debugEnabled) {
|
while (bufferPosition >= MIN_PACKET_LENGTH) {
|
// 查找起始标记
|
int startIndex = findStartMarker();
|
if (startIndex == -1) {
|
// 没有找到起始标记,清空无效数据
|
if (debugEnabled) {
|
System.out.println("未找到起始标记,清空缓冲区");
|
}
|
bufferPosition = 0;
|
return;
|
}
|
|
// 检查是否有足够的数据读取数据长度
|
if (startIndex + 4 > bufferPosition) {
|
// 数据不足,等待更多数据
|
compactBuffer(startIndex);
|
return;
|
}
|
|
// 读取数据长度 (大端序)
|
int dataLength = ((dataBuffer[startIndex + 2] & 0xFF) << 8) |
|
(dataBuffer[startIndex + 3] & 0xFF);
|
int totalPacketLength = 2 + 2 + dataLength + 2; // 起始标记2 + 数据长度2 + 数据内容 + CRC2
|
|
// 检查数据长度有效性
|
if (dataLength < 0 || totalPacketLength > BUFFER_SIZE) {
|
if (debugEnabled) {
|
System.out.println("无效数据长度: " + dataLength + ", 跳过起始字节");
|
}
|
// 跳过错误的起始标记,继续查找
|
compactBuffer(startIndex + 1);
|
continue;
|
}
|
|
// 检查是否收到完整数据包
|
if (startIndex + totalPacketLength > bufferPosition) {
|
// 数据包不完整,等待更多数据
|
compactBuffer(startIndex);
|
return;
|
}
|
|
// 提取完整数据包
|
byte[] packet = Arrays.copyOfRange(dataBuffer, startIndex, startIndex + totalPacketLength);
|
|
if (debugEnabled) {
|
System.out.println("解析到完整数据包: " + bytesToHex(packet));
|
}
|
|
// 添加到返回列表
|
completePackets.add(packet);
|
|
// 移动缓冲区位置
|
int remaining = bufferPosition - (startIndex + totalPacketLength);
|
if (remaining > 0) {
|
System.arraycopy(dataBuffer, startIndex + totalPacketLength,
|
dataBuffer, 0, remaining);
|
}
|
bufferPosition = remaining;
|
}
|
}
|
|
/**
|
* 查找起始标记位置
|
*/
|
private int findStartMarker() {
|
for (int i = 0; i <= bufferPosition - START_MARKER.length; i++) {
|
if (dataBuffer[i] == START_MARKER[0] && dataBuffer[i + 1] == START_MARKER[1]) {
|
return i;
|
}
|
}
|
return -1;
|
}
|
|
/**
|
* 压缩缓冲区,将有效数据移到开头
|
*/
|
private void compactBuffer(int startIndex) {
|
if (startIndex > 0 && startIndex < bufferPosition) {
|
System.arraycopy(dataBuffer, startIndex, dataBuffer, 0,
|
bufferPosition - startIndex);
|
bufferPosition -= startIndex;
|
}
|
}
|
|
/**
|
* 打印原始数据(调试用)
|
*/
|
private void printRawData(String prefix, byte[] data, int maxPrintLength) {
|
if (data == null || data.length == 0) {
|
System.out.println(prefix + ": 空数据");
|
return;
|
}
|
|
StringBuilder sb = new StringBuilder();
|
sb.append(prefix).append(" [长度: ").append(data.length).append("]: ");
|
|
int printLength = Math.min(data.length, maxPrintLength);
|
for (int i = 0; i < printLength; i++) {
|
sb.append(String.format("%02X ", data[i]));
|
}
|
|
if (data.length > maxPrintLength) {
|
sb.append("... [截断,总长度: ").append(data.length).append("]");
|
}
|
|
System.out.println(sb.toString());
|
}
|
|
/**
|
* 工具方法:字节数组转十六进制字符串
|
*/
|
private String bytesToHex(byte[] bytes) {
|
StringBuilder sb = new StringBuilder();
|
for (byte b : bytes) {
|
sb.append(String.format("%02X ", b));
|
}
|
return sb.toString().trim();
|
}
|
|
/**
|
* 清空缓冲区(避免内存泄漏)
|
*/
|
public void clearBuffer() {
|
bufferPosition = 0;
|
// 可选:清空缓冲区内容
|
Arrays.fill(dataBuffer, (byte) 0);
|
}
|
|
/**
|
* 获取当前缓冲区状态
|
*/
|
public int getBufferStatus() {
|
return bufferPosition;
|
}
|
|
/**
|
* 获取缓冲区容量
|
*/
|
public int getBufferCapacity() {
|
return dataBuffer.length;
|
}
|
}
|