123 lines
3.0 KiB
Dart
123 lines
3.0 KiB
Dart
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;
|
|
}
|
|
}
|