| | |
| | | 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 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; |
| | |
| | | } |
| | | |
| | | /** |
| | | * 解析55AA51数据包 |
| | | * @param message 原始十六进制字符串 |
| | | * @return 解析结果(解析失败返回null) |
| | | * 解析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) |
| | | // 检查包头 (55AA51) |
| | | if (cleanedMessage.length < 6 || |
| | | !new String(cleanedMessage, 0, 6).equals(EXPECTED_HEADER)) { |
| | | return null; |
| | |
| | | result.reset(); |
| | | |
| | | try { |
| | | // 解析数据长度 (位置6-7),转换为十进制 |
| | | // 解析数据长度 (位置6-7),转换为十进制 |
| | | result.dataLength = HexUtils.fastHexToByte(cleanedMessage[6], cleanedMessage[7]); |
| | | |
| | | // 计算期望的消息长度 |
| | | int expectedCharLength = 6 + // 包头(3字节) |
| | | 2 + // 数据长度(1字节) |
| | | result.dataLength * 2 + // 数据内容 |
| | | 4; // 校验和(2字节) |
| | | // 计算期望的消息长度 |
| | | int expectedCharLength = 6 + // 包头(3字节) |
| | | 2 + // 数据长度(1字节) |
| | | result.dataLength * 2 + // 数据内容 |
| | | 4; // 校验和(2字节) |
| | | |
| | | if (cleanedMessage.length != expectedCharLength) { |
| | | System.err.println("Data length mismatch: expected " + expectedCharLength + |
| | |
| | | return null; |
| | | } |
| | | |
| | | // 解析基站ID (位置8-11,2字节,低位在前) |
| | | // 解析基站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); // 转换为十进制字符串 |
| | | result.baseStationId = String.valueOf(baseIdValue); // 转换为十进制字符串 |
| | | |
| | | // 解析标签ID (位置12-15,2字节,低位在前) |
| | | // 解析标签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); // 转换为十进制字符串 |
| | | result.tagId = String.valueOf(tagIdValue); // 转换为十进制字符串 |
| | | |
| | | // 解析包序 (位置16-17),转换为十进制 |
| | | // 解析包序 (位置16-17),转换为十进制 |
| | | result.packetSequence = HexUtils.fastHexToByte(cleanedMessage[16], cleanedMessage[17]); |
| | | |
| | | // 解析测距距离 (位置18-21,2字节,低位在前),转换为十进制 |
| | | // 解析测距距离 (位置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字节,低位在前),转换为十进制 |
| | | // 解析水平角度 (位置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度 |
| | | // 解析俯仰角度 (位置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),转换为十进制 |
| | | // 解析信号强度 (位置30-31),转换为十进制 |
| | | result.signalStrength = HexUtils.fastHexToByte(cleanedMessage[30], cleanedMessage[31]); |
| | | |
| | | // 解析角度置信度 (位置32-33),转换为十进制 |
| | | // 解析角度置信度 (位置32-33),转换为十进制 |
| | | result.angleConfidence = HexUtils.fastHexToByte(cleanedMessage[32], cleanedMessage[33]); |
| | | |
| | | // 解析标签电量 (位置34-35),转换为十进制 |
| | | // 解析标签电量 (位置34-35),转换为十进制 |
| | | result.tagBattery = HexUtils.fastHexToByte(cleanedMessage[34], cleanedMessage[35]); |
| | | |
| | | // 解析设备状态 (位置36-39,2字节,低位在前) |
| | | // 解析设备状态 (位置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字节,低位在前),转换为十进制 |
| | | // 解析气压值 (位置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字节) |
| | | // 解析保留字段 (位置44-51,4字节) |
| | | result.reserved = new String(cleanedMessage, 44, 8); |
| | | |
| | | // 解析校验和 (最后4个字符) |
| | | // 解析校验和 (最后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); |
| | |
| | | } |
| | | |
| | | /** |
| | | * 将十六进制字符串转换为字节数组 |
| | | * 将十六进制字符串转换为字节数组 |
| | | */ |
| | | private static byte[] hexStringToByteArray(String s) { |
| | | int len = s.length(); |
| | |
| | | } |
| | | |
| | | /** |
| | | * 验证数据包校验和 |
| | | * 从数据类型字段开始到保留字段结束,校验和为双字节补码 |
| | | * 验证数据包校验和 |
| | | * 从数据类型字段开始到保留字段结束,校验和为双字节补码 |
| | | */ |
| | | private static boolean verifyChecksum(byte[] packet) { |
| | | int len = packet.length; |
| | | if (len < 4) return false; |
| | | |
| | | int sum = 0; |
| | | // 从数据类型字段开始到保留字段结束 (跳过包头2字节) |
| | | // 从数据类型字段开始到保留字段结束 (跳过包头2字节) |
| | | for (int i = 3; i < len - 2; i++) { |
| | | sum += packet[i] & 0xFF; |
| | | } |
| | | sum = ~sum & 0xFFFF; // 取反并保留16位 |
| | | sum = ~sum & 0xFFFF; // 取反并保留16位 |
| | | |
| | | // 获取接收到的校验和 (低位在前) |
| | | // 获取接收到的校验和 (低位在前) |
| | | int receivedChecksum = ((packet[len - 2] & 0xFF) << 8) | (packet[len - 1] & 0xFF); |
| | | |
| | | return sum == receivedChecksum; |