package jiexi;
|
import java.util.Arrays;
|
|
public class Dell55AA1FParser {
|
// 常量定义
|
private static final String EXPECTED_HEADER = "55AA1F"; // 协议头
|
private static final int MIN_PACKET_LENGTH = 36; // 最小包长度(字符数): 固定字段18字节=36字符
|
private static final int FIXED_FIELDS_LENGTH = 11; // 从标签ID到保留字段的固定长度(字节)
|
private static final ThreadLocal<ParseResult> RESULT_CACHE =
|
ThreadLocal.withInitial(ParseResult::new);
|
|
// 解析结果类
|
public static class ParseResult {
|
public int dataLength; // 数据长度
|
public int messageType; // 消息类型
|
public String tagId; // 标签ID(2字节)
|
public int distance; // 距离(厘米)
|
public int angle; // 角度(度)
|
public int signalQuality; // 信号质量(0-255)
|
public int buttonPressed; // 按钮状态
|
public int power; // 电量
|
public String reserved; // 保留字段
|
public String userData; // 用户数据
|
public String checksum; // 校验和
|
|
public void reset() {
|
dataLength = 0;
|
messageType = 0;
|
tagId = "";
|
distance = 0;
|
angle = 0;
|
signalQuality = 0;
|
buttonPressed = 0;
|
power = 0;
|
reserved = "";
|
userData = "";
|
checksum = "";
|
}
|
}
|
|
/**
|
* 解析55AA1F协议数据
|
* @param message 十六进制字符串
|
* @return 解析结果(错误时返回null)
|
*/
|
public static ParseResult parse(String message, String ip, int port) {
|
if (message == null || message.isEmpty()) {
|
System.err.println("Empty message from " + ip + ":" + port);
|
return null;
|
}
|
|
// 清洗数据:移除所有非十六进制字符
|
char[] cleanedMessage = cleanMessage(message);
|
|
if (cleanedMessage == null) {
|
System.err.println("Invalid characters in message from " + ip + ":" + port);
|
return null;
|
}
|
|
// 检查最小长度
|
if (cleanedMessage.length < MIN_PACKET_LENGTH) {
|
System.err.println("Message too short from " + ip + ":" + port +
|
". Expected at least " + MIN_PACKET_LENGTH +
|
" characters, got " + cleanedMessage.length);
|
return null;
|
}
|
|
// 协议头校验 (55AA1F)
|
if (cleanedMessage.length < 6 ||
|
!new String(cleanedMessage, 0, 6).equals(EXPECTED_HEADER)) {
|
System.err.println("Invalid header in message from " + ip + ":" + port);
|
return null;
|
}
|
|
ParseResult result = RESULT_CACHE.get();
|
result.reset();
|
|
try {
|
// 解析数据长度 (位置6-7)
|
result.dataLength = HexUtils.fastHexToByte(cleanedMessage[6], cleanedMessage[7]);
|
|
// 计算期望的总字符长度
|
int expectedCharLength = 14 + result.dataLength * 2; // 14=包头6+消息类型2+数据长度2+校验和4
|
|
// if (cleanedMessage.length < expectedCharLength) {
|
// System.err.println("Incomplete message from " + ip + ":" + port +
|
// ". Expected " + expectedCharLength +
|
// " characters, got " + cleanedMessage.length);
|
// return null;
|
// }
|
|
// 解析消息类型 (位置4-5)
|
result.messageType = HexUtils.fastHexToByte(cleanedMessage[4], cleanedMessage[5]);
|
|
// 解析标签ID (位置8-11),直接取字符串
|
result.tagId = new String(cleanedMessage, 8, 4);
|
|
// 检查是否有足够长度解析固定字段
|
if (cleanedMessage.length < 30) { // 需要至少15字节=30字符
|
System.err.println("Message too short for fixed fields from " + ip + ":" + port);
|
return null;
|
}
|
|
// 解析距离 (位置12-15, 2字节小端整数)
|
int distLow = HexUtils.fastHexToByte(cleanedMessage[12], cleanedMessage[13]);
|
int distHigh = HexUtils.fastHexToByte(cleanedMessage[14], cleanedMessage[15]);
|
result.distance = (distHigh << 8) | distLow;
|
|
// 解析角度 (位置16-19, 2字节小端有符号整数)
|
int angleLow = HexUtils.fastHexToByte(cleanedMessage[16], cleanedMessage[17]);
|
int angleHigh = HexUtils.fastHexToByte(cleanedMessage[18], cleanedMessage[19]);
|
short angleShort = (short) ((angleHigh << 8) | angleLow);
|
result.angle = angleShort;
|
|
// 解析信号质量 (位置20-21)
|
result.signalQuality = HexUtils.fastHexToByte(cleanedMessage[20], cleanedMessage[21]);
|
|
// 解析按钮状态 (位置22-23)
|
result.buttonPressed = HexUtils.fastHexToByte(cleanedMessage[22], cleanedMessage[23]);
|
|
// 解析电量 (位置24-25)
|
result.power = HexUtils.fastHexToByte(cleanedMessage[24], cleanedMessage[25]);
|
|
// 解析保留字段 (位置26-29)
|
result.reserved = new String(cleanedMessage, 26, 4);
|
|
// 解析用户数据
|
// int userDataStart = 30;
|
// int userDataCharLength = (result.dataLength - FIXED_FIELDS_LENGTH) * 2;
|
// if (userDataCharLength > 0) {
|
// if (userDataStart + userDataCharLength <= cleanedMessage.length) {
|
// result.userData = new String(cleanedMessage, userDataStart, userDataCharLength);
|
// } else {
|
// System.err.println("User data truncated in message from " + ip + ":" + port);
|
// result.userData = "";
|
// }
|
// } else {
|
// result.userData = "";
|
// }
|
|
// 解析校验和 (最后4个字符)
|
result.checksum = new String(cleanedMessage, cleanedMessage.length - 4, 4);
|
|
// 验证校验和
|
// byte[] packetBytes = hexStringToByteArray(new String(cleanedMessage));
|
// if (!verifyChecksum(packetBytes)) {
|
// System.err.println("Checksum verification failed for packet from " + ip + ":" + port);
|
// return null;
|
// }
|
|
} catch (IndexOutOfBoundsException | NumberFormatException e) {
|
System.err.println("Parsing error in 55AA1F packet from " + ip + ":" + port);
|
e.printStackTrace();
|
return null;
|
}
|
|
return result;
|
}
|
|
/**
|
* 将十六进制字符串转换为字节数组
|
*/
|
private static byte[] hexStringToByteArray(String s) {
|
int len = s.length();
|
byte[] data = new byte[len / 2];
|
for (int i = 0; i < len; i += 2) {
|
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
|
+ Character.digit(s.charAt(i+1), 16));
|
}
|
return data;
|
}
|
|
/**
|
* 校验数据包
|
* 去掉包头求和取反,校验码为小端模式
|
*/
|
private static boolean verifyChecksum(byte[] packet) {
|
int len = packet.length;
|
if (len < 4) return false;
|
|
int sum = 0;
|
// 从消息类型开始到校验码前结束 (跳过包头2字节)
|
for (int i = 2; i < len - 2; i++) {
|
sum += packet[i] & 0xFF;
|
}
|
sum = ~sum & 0xFFFF; // 取反并保留16位
|
|
// 获取包中的校验码 (小端模式)
|
int receivedChecksum = ((packet[len - 2] & 0xFF) << 8) | (packet[len - 1] & 0xFF);
|
|
return sum == receivedChecksum;
|
}
|
|
private static char[] cleanMessage(String message) {
|
char[] cleaned = new char[message.length()];
|
int j = 0;
|
for (char c : message.toCharArray()) {
|
if (Character.isDigit(c) || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f')) {
|
cleaned[j++] = Character.toUpperCase(c);
|
}
|
}
|
if (j == 0) return null;
|
return Arrays.copyOf(cleaned, j);
|
}
|
|
}
|