zsh_root
2025-12-10 8d662de2fd262b3a485f16e197cb4d0ca2a61cdf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
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);
    }
   
}