package jiexi; import java.util.Arrays; public class Dell55AA51Parser { // 预期包头 private static final String EXPECTED_HEADER = "55AA51"; private static final ThreadLocal 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); } }