| | |
| | | package home; |
| | | import java.math.BigInteger; |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | |
| | | // 重命名为PacketParser,表示数据包解析器 |
| | | // 重命名为PacketParser,表示数据包解析器 |
| | | public class PacketParser { |
| | | // 添加十六进制字符数组常量 |
| | | // 添加十六进制字符数组常量 |
| | | private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray(); |
| | | // 缓冲区字节数组 |
| | | // 缓冲区字节数组 |
| | | private byte[] buffer = new byte[4096]; |
| | | // 当前缓冲区有效数据长度 |
| | | // 当前缓冲区有效数据长度 |
| | | private int bufferLength = 0; |
| | | |
| | | // 追加新数据到缓冲区 |
| | | // 追加新数据到缓冲区 |
| | | public void appendData(byte[] newData, int length) { |
| | | // 检查缓冲区是否足够 |
| | | // 检查缓冲区是否足够 |
| | | if (bufferLength + length > buffer.length) { |
| | | // 创建新的更大缓冲区 |
| | | // 创建新的更大缓冲区 |
| | | byte[] newBuffer = new byte[bufferLength + length]; |
| | | // 复制原有数据 |
| | | // 复制原有数据 |
| | | System.arraycopy(buffer, 0, newBuffer, 0, bufferLength); |
| | | buffer = newBuffer; |
| | | } |
| | | // 追加新数据 |
| | | // 追加新数据 |
| | | System.arraycopy(newData, 0, buffer, bufferLength, length); |
| | | // 更新缓冲区长度 |
| | | // 更新缓冲区长度 |
| | | bufferLength += length; |
| | | } |
| | | |
| | | // 新增方法:字节数组转十六进制字符串 |
| | | // 新增方法:字节数组转十六进制字符串 |
| | | public static String bytesToHexString(byte[] bytes) { |
| | | if (bytes == null) return ""; |
| | | char[] hexChars = new char[bytes.length * 2]; |
| | |
| | | return new String(hexChars); |
| | | } |
| | | |
| | | // 解析缓冲区中的所有完整数据包 |
| | | // 解析缓冲区中的所有完整数据包 |
| | | public List<DataPacket> parsePackets() { |
| | | List<DataPacket> packets = new ArrayList<>(); |
| | | int index = 0; // 当前解析位置索引 |
| | | int index = 0; // 当前解析位置索引 |
| | | |
| | | // 遍历缓冲区查找完整数据包 |
| | | while (index <= bufferLength - 4) { // 确保有足够数据检查包头 |
| | | // 检查包头标识 0x55 0xAA |
| | | // 遍历缓冲区查找完整数据包 |
| | | while (index <= bufferLength - 4) { // 确保有足够数据检查包头 |
| | | // 检查包头标识 0x55 0xAA |
| | | if (buffer[index] == 0x55 && (buffer[index + 1] & 0xFF) == 0xAA) { |
| | | // 获取包类型(第3字节) |
| | | // 获取包类型(第3字节) |
| | | int packetType = buffer[index + 2] & 0xFF; |
| | | // 获取数据长度(第4字节) |
| | | // 获取数据长度(第4字节) |
| | | int dataLenField = buffer[index + 3] & 0xFF; |
| | | // 计算完整包长度(包头+数据+校验) |
| | | int totalPacketLen = 2 + dataLenField + 2; // 增加2字节校验位 |
| | | // 计算完整包长度(包头+数据+校验) |
| | | int totalPacketLen = 2 + dataLenField + 2; // 增加2字节校验位 |
| | | |
| | | // 检查是否有足够数据构成完整包 |
| | | // 检查是否有足够数据构成完整包 |
| | | if (index + totalPacketLen > bufferLength) { |
| | | break; // 数据不足,等待更多数据 |
| | | break; // 数据不足,等待更多数据 |
| | | } |
| | | |
| | | // 提取完整数据包 |
| | | // 提取完整数据包 |
| | | byte[] packetData = new byte[totalPacketLen]; |
| | | System.arraycopy(buffer, index, packetData, 0, totalPacketLen); |
| | | |
| | | // 验证校验和 |
| | | // 验证校验和 |
| | | if (verifyChecksum(packetData)) { |
| | | // 解析单个数据包 |
| | | // 解析单个数据包 |
| | | DataPacket packet = parseSinglePacket(packetType, packetData); |
| | | if (packet != null) { |
| | | packets.add(packet); // 添加到结果列表 |
| | | packets.add(packet); // 添加到结果列表 |
| | | } |
| | | index += totalPacketLen; // 移动到下一个包 |
| | | index += totalPacketLen; // 移动到下一个包 |
| | | } else { |
| | | index++; // 校验失败,跳过当前字节 |
| | | index++; // 校验失败,跳过当前字节 |
| | | } |
| | | } else { |
| | | index++; // 非包头标识,继续查找 |
| | | index++; // 非包头标识,继续查找 |
| | | } |
| | | } |
| | | |
| | | // 清理已处理数据 |
| | | // 清理已处理数据 |
| | | if (index > 0) { |
| | | int remaining = bufferLength - index; |
| | | if (remaining > 0) { |
| | | // 移动剩余数据到缓冲区开头 |
| | | // 移动剩余数据到缓冲区开头 |
| | | System.arraycopy(buffer, index, buffer, 0, remaining); |
| | | } |
| | | bufferLength = remaining; // 更新缓冲区长度 |
| | | bufferLength = remaining; // 更新缓冲区长度 |
| | | } |
| | | |
| | | return packets; |
| | | } |
| | | |
| | | // 校验数据包 |
| | | // 校验数据包 |
| | | private boolean verifyChecksum(byte[] packet) { |
| | | int len = packet.length; |
| | | if (len < 4) return false; |
| | | |
| | | int sum = 0; |
| | | // 从数据类型开始到校验码前结束 (包头2字节已跳过) |
| | | // 从数据类型开始到校验码前结束 (包头2字节已跳过) |
| | | for (int i = 2; i < len - 2; i++) { |
| | | sum += packet[i] & 0xFF; |
| | | } |
| | | sum = ~sum & 0xFFFF; // 取反并保留16位 |
| | | sum = ~sum & 0xFFFF; // 取反并保留16位 |
| | | |
| | | // 获取包中的校验码 (小端模式) |
| | | // 获取包中的校验码 (小端模式) |
| | | int receivedChecksum = ((packet[len - 1] & 0xFF) << 8) | (packet[len - 2] & 0xFF); |
| | | |
| | | return sum == receivedChecksum; |
| | |
| | | |
| | | |
| | | |
| | | // 解析单个数据包 |
| | | // 解析单个数据包 |
| | | private DataPacket parseSinglePacket(int packetType, byte[] packet) { |
| | | |
| | | // 创建数据包对象(包含包类型) |
| | | // 创建数据包对象(包含包类型) |
| | | return new DataPacket(packetType, packet); |
| | | } |
| | | |
| | | // 数据包内部类 |
| | | // 数据包内部类 |
| | | public static class DataPacket { |
| | | private final int packetType; // 包类型 |
| | | private final byte[] packet;//包数据 |
| | | private final int packetType; // 包类型 |
| | | private final byte[] packet;//包数据 |
| | | |
| | | |
| | | public DataPacket(int packetType, byte[] packet) { |
| | |
| | | this.packet = packet; |
| | | } |
| | | |
| | | // 获取包类型 |
| | | // 获取包类型 |
| | | public int getPacketType() { |
| | | return packetType; |
| | | } |
| | | |
| | | // 包数据 |
| | | // 包数据 |
| | | public byte[] getPacket() { |
| | | return packet; |
| | | } |
| | | |
| | | } |
| | | |
| | | // HEX字符串转字节数组 |
| | | // HEX字符串转字节数组 |
| | | public static byte[] hexStringToBytes(String hex) { |
| | | // 移除空格 |
| | | // 移除空格 |
| | | hex = hex.replaceAll("\\s", ""); |
| | | int len = hex.length(); |
| | | // 创建结果数组 |
| | | // 创建结果数组 |
| | | byte[] data = new byte[len / 2]; |
| | | // 每两个字符转换一个字节 |
| | | // 每两个字符转换一个字节 |
| | | for (int i = 0; i < len; i += 2) { |
| | | // 高位转换 |
| | | // 高位转换 |
| | | int high = Character.digit(hex.charAt(i), 16) << 4; |
| | | // 低位转换 |
| | | // 低位转换 |
| | | int low = Character.digit(hex.charAt(i + 1), 16); |
| | | data[i / 2] = (byte) (high | low); |
| | | } |
| | | return data; |
| | | } |
| | | |
| | | |
| | | |
| | | |
| | | /**输出校验码*/ |
| | | |
| | | |
| | | /**输出校验码*/ |
| | | public static String calculateChecksum(String input) { |
| | | // 检查输入是否为空 |
| | | // 检查输入是否为空 |
| | | if (input == null) { |
| | | throw new IllegalArgumentException("输入不能为null"); |
| | | throw new IllegalArgumentException("输入不能为null"); |
| | | } |
| | | |
| | | // 移除所有空格 |
| | | // 移除所有空格 |
| | | String cleanInput = input.replaceAll("\\s", ""); |
| | | |
| | | // 验证处理后的字符串 |
| | | // 验证处理后的字符串 |
| | | if (cleanInput.length() < 8 || !cleanInput.startsWith("55AA")) { |
| | | throw new IllegalArgumentException("输入字符串必须以55AA开头且长度至少为8(去除空格后)"); |
| | | throw new IllegalArgumentException("输入字符串必须以55AA开头且长度至少为8(去除空格后)"); |
| | | } |
| | | |
| | | // 去掉包头(55AA)和最后4个字符 |
| | | // 去掉包头(55AA)和最后4个字符 |
| | | String dataPart = cleanInput.substring(4, cleanInput.length() - 4); |
| | | |
| | | // 检查中间部分长度是否为偶数 |
| | | // 检查中间部分长度是否为偶数 |
| | | if (dataPart.length() % 2 != 0) { |
| | | throw new IllegalArgumentException("校验出错中间部分长度必须是偶数(去除空格后)"); |
| | | throw new IllegalArgumentException("校验出错中间部分长度必须是偶数(去除空格后)"); |
| | | } |
| | | |
| | | int sum = 0; |
| | | // 每两个字符解析为一个字节 |
| | | // 每两个字符解析为一个字节 |
| | | for (int i = 0; i < dataPart.length(); i += 2) { |
| | | String byteStr = dataPart.substring(i, i + 2); |
| | | int byteValue = Integer.parseInt(byteStr, 16); |
| | | sum = (sum + byteValue) & 0xFFFF; // 保持16位范围 |
| | | sum = (sum + byteValue) & 0xFFFF; // 保持16位范围 |
| | | } |
| | | |
| | | // 取反并保持16位 |
| | | // 取反并保持16位 |
| | | int checksum = (~sum) & 0xFFFF; |
| | | |
| | | // 显式处理高位在前格式 |
| | | int lowByte= (checksum >>> 8) & 0xFF; // 高8位 |
| | | int highByte= checksum & 0xFF; // 低8位 |
| | | // 显式处理高位在前格式 |
| | | int lowByte= (checksum >>> 8) & 0xFF; // 高8位 |
| | | int highByte= checksum & 0xFF; // 低8位 |
| | | |
| | | // 格式化为4位十六进制字符串(大写),高位在前 |
| | | // 格式化为4位十六进制字符串(大写),高位在前 |
| | | return String.format("%02X%02X", highByte, lowByte); |
| | | } |
| | | |
| | | /** 将字节数组转为十六进制字符串,例如 [55 AA 01 02 ...] */ |
| | | /** 将字节数组转为十六进制字符串,例如 [55 AA 01 02 ...] */ |
| | | private static String bytesToHex(byte[] bytes) { |
| | | if (bytes == null) return "null"; |
| | | StringBuilder sb = new StringBuilder(bytes.length * 3); |
| | | for (byte b : bytes) { |
| | | sb.append(String.format("%02X ", b & 0xFF)); |
| | | } |
| | | // 去掉最后一个空格 |
| | | // 去掉最后一个空格 |
| | | return sb.substring(0, sb.length() - 1); |
| | | } |
| | | |