一次大的提交
This commit is contained in:
122
lib/core/bluetooth/protocol_parser.dart
Normal file
122
lib/core/bluetooth/protocol_parser.dart
Normal file
@@ -0,0 +1,122 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
class BlePacket {
|
||||
final int command;
|
||||
final Uint8List payload;
|
||||
const BlePacket({required this.command, required this.payload});
|
||||
}
|
||||
|
||||
/// 有状态的 BLE 协议解析器
|
||||
/// 支持分片接收:多次调用 [append] 累积数据,[parse] 提取完整帧
|
||||
class ProtocolParser {
|
||||
static const int _header1 = 0xAB;
|
||||
static const int _header2 = 0xAA;
|
||||
static const int _tail1 = 0xAA;
|
||||
static const int _tail2 = 0xAB;
|
||||
|
||||
final List<int> _buffer = [];
|
||||
|
||||
/// 清空缓冲区
|
||||
void clear() {
|
||||
_buffer.clear();
|
||||
}
|
||||
|
||||
/// 获取缓冲区长度
|
||||
int get bufferLength => _buffer.length;
|
||||
|
||||
/// 打包命令为字节帧(用于发送)
|
||||
/// CRC16 覆盖 command + payload 确保整帧完整性
|
||||
static Uint8List pack(int command, List<int> payload) {
|
||||
final dataToCheck = [command, ...payload];
|
||||
final crc = _crc16(dataToCheck);
|
||||
final builder = BytesBuilder()
|
||||
..addByte(_header1)
|
||||
..addByte(_header2)
|
||||
..addByte(command)
|
||||
..add(payload)
|
||||
..addByte(crc & 0xFF)
|
||||
..addByte((crc >> 8) & 0xFF)
|
||||
..addByte(_tail1)
|
||||
..addByte(_tail2);
|
||||
return builder.takeBytes();
|
||||
}
|
||||
|
||||
/// 追加新接收的数据到缓冲区
|
||||
void append(List<int> data) {
|
||||
_buffer.addAll(data);
|
||||
}
|
||||
|
||||
/// 从缓冲区解析所有完整的数据包
|
||||
/// 未完成的帧保留在缓冲区等待后续数据
|
||||
List<BlePacket> parse() {
|
||||
final List<BlePacket> packets = [];
|
||||
|
||||
while (_buffer.length >= 6) {
|
||||
// 1. 查找帧头
|
||||
int headIdx = -1;
|
||||
for (int i = 0; i < _buffer.length - 1; i++) {
|
||||
if (_buffer[i] == _header1 && _buffer[i + 1] == _header2) {
|
||||
headIdx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (headIdx == -1) {
|
||||
// 无有效帧头,清空缓冲区
|
||||
_buffer.clear();
|
||||
break;
|
||||
}
|
||||
|
||||
// 移除帧头前的垃圾数据
|
||||
if (headIdx > 0) _buffer.removeRange(0, headIdx);
|
||||
|
||||
// 2. 查找帧尾
|
||||
int tailIdx = -1;
|
||||
for (int i = 2; i < _buffer.length - 1; i++) {
|
||||
if (_buffer[i] == _tail1 && _buffer[i + 1] == _tail2) {
|
||||
tailIdx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (tailIdx == -1) {
|
||||
// 帧尾未找到,可能是分片数据,等待更多数据
|
||||
break;
|
||||
}
|
||||
|
||||
// 3. 提取数据包
|
||||
if (_buffer.length >= 5) {
|
||||
final cmd = _buffer[2];
|
||||
final payload = Uint8List.fromList(_buffer.sublist(3, tailIdx));
|
||||
packets.add(BlePacket(command: cmd, payload: payload));
|
||||
}
|
||||
|
||||
// 4. 移除已处理的帧
|
||||
_buffer.removeRange(0, tailIdx + 2);
|
||||
}
|
||||
|
||||
return packets;
|
||||
}
|
||||
|
||||
/// 便捷方法:追加数据并立即解析
|
||||
List<BlePacket> appendAndParse(List<int> data) {
|
||||
append(data);
|
||||
return parse();
|
||||
}
|
||||
|
||||
/// CRC16-Modbus: polynomial=0x8005, init=0xFFFF, refIn/refOut=true
|
||||
static int _crc16(List<int> data) {
|
||||
int crc = 0xFFFF;
|
||||
for (final b in data) {
|
||||
crc ^= b;
|
||||
for (int j = 0; j < 8; j++) {
|
||||
if ((crc & 0x0001) != 0) {
|
||||
crc = (crc >> 1) ^ 0xA001;
|
||||
} else {
|
||||
crc >>= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user