Files
kuaishai2/lib/features/device/services/json_protocol.dart
Developer 3ab2232845 refactor(device): 替换消息ID生成器为UUID实现
- 移除自定义时间戳+随机数ID生成逻辑
- 集成uuid包依赖并配置版本
- 使用Uuid.v4()替换原有next()方法实现
- 更新MessageIdGenerator类文档注释
- 在JSON协议层添加设备日志警告输出
- 修改pubspec.yaml添加uuid依赖声明
2026-06-04 16:45:06 +08:00

97 lines
3.2 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'dart:convert';
import 'dart:typed_data';
import 'device_log.dart';
import 'device_message.dart';
/// JSON 协议层帧编解码器
///
/// 由于串口为字节流,需要在 JSON 字符串之外增加帧定界。
/// 采用「4 字节大端长度前缀 + UTF-8 JSON + 换行符」的简单可靠方案:
/// ```
/// [LEN(4B BE)] [JSON_UTF8...] ['\n']
/// ```
/// - LENJSON 部分的字节数(不含 LEN 自身与换行符)
/// - JSON完整 `DeviceMessage.toJson()` 序列化结果
/// - 换行符:辅助日志/调试,便于人工嗅探串口
///
/// 解析器按累积缓冲区增量解码,单次返回一条已完整解析的消息。
class JsonProtocolService {
static const int _lengthPrefixSize = 4;
static const int _maxFrameBytes = 64 * 1024;
final MessageIdGenerator _idGenerator = MessageIdGenerator();
final List<int> _buffer = [];
/// 生成下一个消息 ID
String nextId() => _idGenerator.next();
/// 将消息编码为可下发的字节流
Uint8List encode(DeviceMessage message) {
final json = utf8.encode(message.encode());
if (json.isEmpty) {
throw ArgumentError('encoded JSON is empty');
}
if (json.length > _maxFrameBytes) {
throw ArgumentError('message too large: ${json.length}B');
}
final buf = Uint8List(_lengthPrefixSize + json.length + 1);
// 大端长度
final len = json.length;
buf[0] = (len >> 24) & 0xFF;
buf[1] = (len >> 16) & 0xFF;
buf[2] = (len >> 8) & 0xFF;
buf[3] = len & 0xFF;
buf.setRange(_lengthPrefixSize, _lengthPrefixSize + json.length, json);
buf[buf.length - 1] = 0x0A; // '\n'
return buf;
}
/// 尝试从累积缓冲区解析一条完整消息
///
/// 返回 (message, consumedBytes)。consumedBytes 表示已消费字节数,
/// 调用方应从缓冲区中移除。
/// 不足一帧时返回 (null, 0)。
(DeviceMessage?, int) tryDecode(List<int> incoming) {
if (incoming.isEmpty) return (null, 0);
_buffer.addAll(incoming);
// 防止缓冲区无限增长
if (_buffer.length > _maxFrameBytes * 2) {
_buffer.removeRange(0, _buffer.length - _maxFrameBytes);
}
if (_buffer.length < _lengthPrefixSize) return (null, 0);
final len = (_buffer[0] << 24) |
(_buffer[1] << 16) |
(_buffer[2] << 8) |
_buffer[3];
if (len <= 0 || len > _maxFrameBytes) {
DeviceLog.warn('tryDecode: 异常长度=$len 丢弃首字节 0x${_buffer[0].toRadixString(16).padLeft(2, '0')}');
_buffer.removeAt(0);
return (null, 0);
}
final totalNeeded = _lengthPrefixSize + len + 1; // +1 换行符
if (_buffer.length < totalNeeded) return (null, 0);
final jsonBytes = _buffer.sublist(_lengthPrefixSize, _lengthPrefixSize + len);
final tail = _buffer[_lengthPrefixSize + len];
// 换行符不是必需的,缺失也接受;存在则跳过
final consumed = tail == 0x0A ? totalNeeded : totalNeeded - 1;
_buffer.removeRange(0, consumed);
try {
final json = utf8.decode(jsonBytes);
final msg = DeviceMessage.decode(json);
return (msg, consumed);
} catch (_) {
return (null, consumed);
}
}
/// 重置内部缓冲区(断线/异常时调用)
void reset() => _buffer.clear();
}