133 lines
4.9 KiB
Dart
133 lines
4.9 KiB
Dart
import 'dart:async';
|
||
import 'dart:convert';
|
||
import 'package:flutter/cupertino.dart';
|
||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||
import 'package:maibu_satabot_v2/features/devices/domain/entities/running_status_entity.dart';
|
||
import 'package:maibu_satabot_v2/features/devices/domain/entities/gps_entity.dart';
|
||
import 'package:maibu_satabot_v2/core/network/protocol_decoder.dart';
|
||
|
||
import '../../../../core/network/net_message_dispatcher.dart';
|
||
import 'device_status_event.dart';
|
||
import 'device_status_state.dart';
|
||
|
||
class DeviceStatusBloc extends Bloc<DeviceStatusEvent, DeviceStatusState> {
|
||
final NetMessageDispatcher _dispatcher;
|
||
|
||
// 持有订阅引用,仅在 close 时取消
|
||
StreamSubscription? _stringSub;
|
||
StreamSubscription? _jsonSub;
|
||
|
||
// 🔥 新增:标记是否已初始化订阅,防止重复订阅
|
||
bool _isSubscribed = false;
|
||
|
||
DeviceStatusBloc(this._dispatcher) : super(DeviceStatusInitial()) {
|
||
// 1. 初始建立订阅(终身有效,除非 Bloc 关闭)
|
||
_setupStreamListeners();
|
||
|
||
// 2. 注册事件处理
|
||
on<DeviceStatusReset>(_handleReset);
|
||
on<DeviceStatusLoaded>(_handleDeviceStatusLoaded);
|
||
on<PushMessageReceived>(_handlePushMessageReceived);
|
||
}
|
||
|
||
// 🔥 核心修复:订阅逻辑只执行一次,不再随意 cancel/relisten
|
||
void _setupStreamListeners() {
|
||
if (_isSubscribed) {
|
||
return; // 如果已经订阅过,直接返回,避免重复操作
|
||
}
|
||
|
||
debugPrint('🔗 [DeviceStatusBloc] 初始化 TCP 数据流订阅(终身有效)');
|
||
|
||
// 订阅字符串流 (0x02)
|
||
_stringSub = _dispatcher.onStringMessage().listen(
|
||
(jsonString) {
|
||
debugPrint('Bloc层已经!!收到 0x02 数据事件,长度:${jsonString.length}');
|
||
if (!isClosed) {
|
||
add(DeviceStatusLoaded(jsonString));
|
||
}
|
||
},
|
||
onDone: () => debugPrint('⚠️ [Bloc] 0x02 流已结束 (onDone) - 这通常意味着底层 TCP 彻底关闭'),
|
||
onError: (e) => debugPrint('❌ [Bloc] 0x02 流发生错误:$e'),
|
||
);
|
||
|
||
// 订阅 JSON 流 (0x12)
|
||
// _jsonSub = _dispatcher.onJsonMessage(0x12).listen(
|
||
// (jsonData) {
|
||
// debugPrint('📩 [Bloc] 收到 JSON 数据事件:$jsonData');
|
||
// if (!isClosed) {
|
||
// add(PushMessageReceived(jsonData));
|
||
// }
|
||
// },
|
||
// onDone: () => debugPrint('⚠️ [Bloc] JSON 流已结束 (onDone)'),
|
||
// onError: (e) => debugPrint('❌ [Bloc] JSON 流发生错误:$e'),
|
||
// );
|
||
|
||
_isSubscribed = true;
|
||
debugPrint('✅ [DeviceStatusBloc] 订阅建立完成,将持续监听数据流');
|
||
}
|
||
|
||
// 🔥 核心修复:重置时仅清空状态,绝对不再触碰订阅关系
|
||
Future<void> _handleReset(DeviceStatusReset event, Emitter<DeviceStatusState> emit) async {
|
||
debugPrint('🔄 收到重置事件:仅清空状态,保持订阅活跃(不重连)');
|
||
|
||
// 只 emit 初始状态,让 UI 清除旧设备的数据(如速度归零、轨迹清除)
|
||
emit(DeviceStatusInitial());
|
||
|
||
// ❌ 严禁在此处调用 _setupStreamListeners() 或 cancel 订阅
|
||
// 因为 TCP 重连期间数据流可能一直在推送,cancel 会导致关键首包丢失
|
||
}
|
||
|
||
Future<void> _handleDeviceStatusLoaded(
|
||
DeviceStatusLoaded event,
|
||
Emitter<DeviceStatusState> emit,
|
||
) async {
|
||
try {
|
||
debugPrint('🔍 开始解析数据:${event.jsonString}');
|
||
final fields = event.jsonString.trim().split(',');
|
||
|
||
if (fields.length < 18) {
|
||
debugPrint('⚠️ 字段不足:${fields.length}');
|
||
emit(DeviceStatusError('字段不足,期望 ≥18,实际:${fields.length}'));
|
||
return;
|
||
}
|
||
|
||
final status = RunningStatusEntity.fromFields(fields);
|
||
final gps = GPSEntity(status.latitude, status.longitude);
|
||
|
||
debugPrint('✅ 解析成功,更新状态:Lat=${gps.latitude}, Lng=${gps.longitude}');
|
||
emit(DeviceStatusUpdated(status, gps));
|
||
} catch (e, stack) {
|
||
debugPrint('❌ 解析异常:$e\n$stack');
|
||
emit(DeviceStatusError('解析失败:$e'));
|
||
}
|
||
}
|
||
|
||
Future<void> _handlePushMessageReceived(
|
||
PushMessageReceived event,
|
||
Emitter<DeviceStatusState> emit,
|
||
) async {
|
||
try {
|
||
final eventStr = event.jsonData['event'] ?? '';
|
||
final deviceId = event.jsonData['deviceId'] ?? '未知';
|
||
debugPrint('收到推送事件:$eventStr, 设备:$deviceId');
|
||
// 这里可以根据需要 emit 新状态
|
||
} catch (e) {
|
||
emit(DeviceStatusError('解析推送消息失败:$e'));
|
||
}
|
||
}
|
||
|
||
@override
|
||
Future<void> close() {
|
||
// 只有在 Bloc 彻底销毁时才取消订阅
|
||
//debugPrint('🚫 [DeviceStatusBloc] 正在关闭,取消所有订阅 ');
|
||
// debugPrint('🚫 [DeviceStatusBloc] 正在关闭,取消所有订阅');
|
||
//debugPrint('🔥 [DeviceStatusBloc] 销毁堆栈跟踪:\n${StackTrace.current}');
|
||
// _stringSub?.cancel();
|
||
// _jsonSub?.cancel();
|
||
//return super.close();
|
||
return Future.value();
|
||
|
||
|
||
}
|
||
}
|