package jiexi;
|
|
import java.util.Arrays;
|
|
public class Dell55AA51Parser {
|
// 预期包头
|
private static final String EXPECTED_HEADER = "55AA51";
|
private static final ThreadLocal<ParseResult> RESULT_CACHE =
|
ThreadLocal.withInitial(ParseResult::new);
|
|
// 解析结果类
|
public static class ParseResult {
|
public int dataLength; // 数据长度(十进制)
|
public String baseStationId; // 基站ID(2字节,低位在前)
|
public String tagId; // 标签ID(2字节,低位在前)
|
public int packetSequence; // 包序(十进制)
|
public int distance; // 测距距离(十进制)
|
public int horizontalAngle; // 水平角度(十进制)
|
public int verticalAngle; // 俯仰角度(十进制,有符号)
|
public int signalStrength; // 信号强度(十进制)
|
public int angleConfidence; // 角度置信度(十进制)
|
public int tagBattery; // 标签电量(十进制)
|
public int deviceStatus; // 设备状态
|
public int airPressure; // 气压值(十进制)
|
public String reserved; // 保留字段
|
public String checksum; // 校验和
|
|
public void reset() {
|
dataLength = 0;
|
baseStationId = "";
|
tagId = "";
|
packetSequence = 0;
|
distance = 0;
|
horizontalAngle = 0;
|
verticalAngle = 0;
|
signalStrength = 0;
|
angleConfidence = 0;
|
tagBattery = 0;
|
deviceStatus = 0;
|
airPressure = 0;
|
reserved = "";
|
checksum = "";
|
}
|
|
@Override
|
public String toString() {
|
return "ParseResult{" +
|
"dataLength=" + dataLength +
|
", baseStationId='" + baseStationId + '\'' +
|
", tagId='" + tagId + '\'' +
|
", packetSequence=" + packetSequence +
|
", distance=" + distance +
|
", horizontalAngle=" + horizontalAngle +
|
", verticalAngle=" + verticalAngle +
|
", signalStrength=" + signalStrength +
|
", angleConfidence=" + angleConfidence +
|
", tagBattery=" + tagBattery +
|
", deviceStatus=" + deviceStatus +
|
", airPressure=" + airPressure +
|
", reserved='" + reserved + '\'' +
|
", checksum='" + checksum + '\'' +
|
'}';
|
}
|
}
|
|
/**
|
* 解析55AA51数据包
|
* @param message 原始十六进制字符串
|
* @return 解析结果(解析失败返回null)
|
*/
|
public static ParseResult parse(String message, String ip, int port) {
|
if (message == null || message.isEmpty()) {
|
return null;
|
}
|
|
// 清理消息:移除非十六进制字符
|
char[] cleanedMessage = cleanMessage(message);
|
|
if (cleanedMessage == null) {
|
return null;
|
}
|
|
// 检查包头 (55AA51)
|
if (cleanedMessage.length < 6 ||
|
!new String(cleanedMessage, 0, 6).equals(EXPECTED_HEADER)) {
|
return null;
|
}
|
|
ParseResult result = RESULT_CACHE.get();
|
result.reset();
|
|
try {
|
// 解析数据长度 (位置6-7),转换为十进制
|
result.dataLength = HexUtils.fastHexToByte(cleanedMessage[6], cleanedMessage[7]);
|
|
// 计算期望的消息长度
|
int expectedCharLength = 6 + // 包头(3字节)
|
2 + // 数据长度(1字节)
|
result.dataLength * 2 + // 数据内容
|
4; // 校验和(2字节)
|
|
if (cleanedMessage.length != expectedCharLength) {
|
System.err.println("Data length mismatch: expected " + expectedCharLength +
|
", got " + cleanedMessage.length);
|
return null;
|
}
|
|
// 解析基站ID (位置8-11,2字节,低位在前)
|
int baseIdLow = HexUtils.fastHexToByte(cleanedMessage[8], cleanedMessage[9]);
|
int baseIdHigh = HexUtils.fastHexToByte(cleanedMessage[10], cleanedMessage[11]);
|
int baseIdValue = (baseIdHigh << 8) | baseIdLow;
|
result.baseStationId = String.valueOf(baseIdValue); // 转换为十进制字符串
|
|
// 解析标签ID (位置12-15,2字节,低位在前)
|
int tagIdLow = HexUtils.fastHexToByte(cleanedMessage[12], cleanedMessage[13]);
|
int tagIdHigh = HexUtils.fastHexToByte(cleanedMessage[14], cleanedMessage[15]);
|
int tagIdValue = (tagIdHigh << 8) | tagIdLow;
|
result.tagId = String.valueOf(tagIdValue); // 转换为十进制字符串
|
|
// 解析包序 (位置16-17),转换为十进制
|
result.packetSequence = HexUtils.fastHexToByte(cleanedMessage[16], cleanedMessage[17]);
|
|
// 解析测距距离 (位置18-21,2字节,低位在前),转换为十进制
|
int distLow = HexUtils.fastHexToByte(cleanedMessage[18], cleanedMessage[19]);
|
int distHigh = HexUtils.fastHexToByte(cleanedMessage[20], cleanedMessage[21]);
|
result.distance = (distHigh << 8) | distLow;
|
|
// 解析水平角度 (位置22-25,2字节,低位在前),转换为十进制
|
int hAngleLow = HexUtils.fastHexToByte(cleanedMessage[22], cleanedMessage[23]);
|
int hAngleHigh = HexUtils.fastHexToByte(cleanedMessage[24], cleanedMessage[25]);
|
result.horizontalAngle = (hAngleHigh << 8) | hAngleLow;
|
|
// 解析俯仰角度 (位置26-29,2字节,低位在前),转换为十进制
|
// 注意:这是有符号数,范围-180到180度
|
int vAngleLow = HexUtils.fastHexToByte(cleanedMessage[26], cleanedMessage[27]);
|
int vAngleHigh = HexUtils.fastHexToByte(cleanedMessage[28], cleanedMessage[29]);
|
short vAngleShort = (short) ((vAngleHigh << 8) | vAngleLow);
|
result.verticalAngle = vAngleShort;
|
|
// 解析信号强度 (位置30-31),转换为十进制
|
result.signalStrength = HexUtils.fastHexToByte(cleanedMessage[30], cleanedMessage[31]);
|
|
// 解析角度置信度 (位置32-33),转换为十进制
|
result.angleConfidence = HexUtils.fastHexToByte(cleanedMessage[32], cleanedMessage[33]);
|
|
// 解析标签电量 (位置34-35),转换为十进制
|
result.tagBattery = HexUtils.fastHexToByte(cleanedMessage[34], cleanedMessage[35]);
|
|
// 解析设备状态 (位置36-39,2字节,低位在前)
|
int statusLow = HexUtils.fastHexToByte(cleanedMessage[36], cleanedMessage[37]);
|
int statusHigh = HexUtils.fastHexToByte(cleanedMessage[38], cleanedMessage[39]);
|
result.deviceStatus = (statusHigh << 8) | statusLow;
|
|
// 解析气压值 (位置40-43,2字节,低位在前),转换为十进制
|
int pressureLow = HexUtils.fastHexToByte(cleanedMessage[40], cleanedMessage[41]);
|
int pressureHigh = HexUtils.fastHexToByte(cleanedMessage[42], cleanedMessage[43]);
|
result.airPressure = (pressureHigh << 8) | pressureLow;
|
|
// 解析保留字段 (位置44-51,4字节)
|
result.reserved = new String(cleanedMessage, 44, 8);
|
|
// 解析校验和 (最后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 55AA51 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 = 3; 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);
|
}
|
|
|
}
|