重构机器状态页面的数据获取设定保证数据及时显示

This commit is contained in:
2026-04-17 17:29:31 +08:00
parent 06c54ef546
commit b116e4bed3
4 changed files with 163 additions and 70 deletions

View File

@@ -16,73 +16,130 @@ class DeviceStatusBloc extends Bloc<DeviceStatusEvent, DeviceStatusState> {
final NetMessageDispatcher _dispatcher;
final ILoggerService _logger = GetIt.I<ILoggerService>();
// 持有订阅引用,仅在 close 时取消
StreamSubscription? _stringSub;
StreamSubscription? _jsonSub;
// 🔥 新增:标记是否已初始化订阅,防止重复订阅
bool _isSubscribed = false;
// 🔥 保存订阅引用,用于管理生命周期
StreamSubscription? _tcpSubscription;
DeviceStatusBloc(this._dispatcher) : super(DeviceStatusInitial()) {
// 1. 初始建立订阅(终身有效,除非 Bloc 关闭)
_setupStreamListeners();
// 🔥 核心改动:直接在构造函数中建立TCP监听,类似RemoteControlCubit
_initDirectTcpListener();
// 2. 注册事件处理
// 保留事件处理(用于手动重置等场景)
on<DeviceStatusReset>(_handleReset);
on<DeviceStatusLoaded>(_handleDeviceStatusLoaded);
on<PushMessageReceived>(_handlePushMessageReceived);
}
// 🔥 核心修复:订阅逻辑只执行一次,不再随意 cancel/relisten
void _setupStreamListeners() {
if (_isSubscribed) {
return; // 如果已经订阅过,直接返回,避免重复操作
// 🔥 新增:重新初始化TCP监听器(用于页面重新进入时调用)
void reinitializeListener() {
debugPrint('🔄 [DeviceStatusBloc] 重新初始化TCP监听器 - isClosed=$isClosed');
_logger.log('🔄 [DeviceStatusBloc] 重新初始化TCP监听器 - isClosed=$isClosed');
if (isClosed) {
debugPrint('❌ [DeviceStatusBloc] BLoC已关闭,无法重新初始化!');
_logger.log('❌ [DeviceStatusBloc] BLoC已关闭,无法重新初始化!');
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] 订阅建立完成,将持续监听数据流');
_logger.log('✅ [DeviceStatusBloc] 订阅建立完成,将持续监听数据流');
// 取消旧的订阅
debugPrint('📝 [DeviceStatusBloc] 取消旧订阅');
_tcpSubscription?.cancel();
// 重置状态为初始状态
debugPrint('📝 [DeviceStatusBloc] emit Initial状态');
emit(DeviceStatusInitial());
// 重新建立监听
debugPrint('📝 [DeviceStatusBloc] 调用 _initDirectTcpListener');
_initDirectTcpListener();
}
// 🔥 核心修复:重置时仅清空状态,绝对不再触碰订阅关系
// 🔥 新增:直接监听TCP 0x02指令,实时解析并emit状态
void _initDirectTcpListener() {
debugPrint('🔗 [DeviceStatusBloc] 初始化直接TCP监听器(类似RemoteControlCubit)');
_logger.log('🔗 [DeviceStatusBloc] 初始化直接TCP监听器');
_tcpSubscription = _dispatcher.onStringMessage().listen(
(message) {
//debugPrint('📩 [DeviceStatusBloc] 直接收到0x02数据,长度:${message.length}');
if (message.isEmpty) {
//debugPrint('⚠️ [DeviceStatusBloc] 消息为空,跳过');
return;
}
try {
// 🔥 直接解析,不经过事件转换
final fields = message.trim().split(',');
if (fields.length < 18) {
//debugPrint('⚠️ [DeviceStatusBloc] 字段不足:${fields.length},期望≥18');
if (!isClosed) {
emit(DeviceStatusError('字段不足,期望≥18,实际:${fields.length}'));
}
return;
}
final status = RunningStatusEntity.fromFields(fields);
final gps = GPSEntity(status.latitude, status.longitude);
//debugPrint('✅ [DeviceStatusBloc] 直接解析成功,更新状态:Lat=${gps.latitude}, Lng=${gps.longitude}');
_logger.log('✅ [DeviceStatusBloc] 直接解析成功,更新状态');
// 🔥 关键修复:BLoC有Equatable去重机制,必须创建新对象才能触发UI更新
if (!isClosed) {
// 创建全新的status和gps对象,绕过Equatable去重
final newStatus = RunningStatusEntity(
voltage: status.voltage,
leftTargetSpeed: status.leftTargetSpeed,
rightTargetSpeed: status.rightTargetSpeed,
leftMeasureSpeed: status.leftMeasureSpeed,
rightMeasureSpeed: status.rightMeasureSpeed,
leftCurrent: status.leftCurrent,
rightCurrent: status.rightCurrent,
leftMotorTemp: status.leftMotorTemp,
rightMotorTemp: status.rightMotorTemp,
chipTemp: status.chipTemp,
yaw: status.yaw,
pitch: status.pitch,
roll: status.roll,
satelliteCnt: status.satelliteCnt,
qual: status.qual,
headingStatus: status.headingStatus,
latitude: status.latitude,
longitude: status.longitude,
timestamp: status.timestamp,
knifeCuttingSpeed: status.knifeCuttingSpeed,
controlMode: status.controlMode,
battery: status.battery,
workingArea: status.workingArea,
obstacleFlag: status.obstacleFlag,
);
final newGps = GPSEntity(status.latitude, status.longitude);
emit(DeviceStatusUpdated(newStatus, newGps));
}
} catch (e, stack) {
//debugPrint('❌ [DeviceStatusBloc] 直接解析异常:$e\n$stack');
_logger.log('❌ [DeviceStatusBloc] 直接解析异常:$e');
if (!isClosed) {
emit(DeviceStatusError('解析失败:$e'));
}
}
},
onDone: () => debugPrint('⚠️ [DeviceStatusBloc] TCP流已结束(onDone)'),
onError: (e) => debugPrint('❌ [DeviceStatusBloc] TCP流错误:$e'),
);
debugPrint('✅ [DeviceStatusBloc] 直接TCP监听器已建立完成');
_logger.log('✅ [DeviceStatusBloc] 直接TCP监听器已建立完成');
}
// 🔥 重置时仅清空状态
Future<void> _handleReset(DeviceStatusReset event, Emitter<DeviceStatusState> emit) async {
// debugPrint('🔄 收到重置事件:仅清空状态,保持订阅活跃(不重连)');
_logger.log('🔄 收到重置事件:仅清空状态,保持订阅活跃(不重连)');
debugPrint('🔄 [DeviceStatusBloc] 收到重置事件:清空状态');
_logger.log('🔄 [DeviceStatusBloc] 收到重置事件:清空状态');
// 只 emit 初始状态,让 UI 清除旧设备的数据(如速度归零、轨迹清除)
// 只 emit 初始状态,让 UI 清除旧设备的数据
emit(DeviceStatusInitial());
// ❌ 严禁在此处调用 _setupStreamListeners() 或 cancel 订阅
// 因为 TCP 重连期间数据流可能一直在推送,cancel 会导致关键首包丢失
}
Future<void> _handleDeviceStatusLoaded(
@@ -130,15 +187,10 @@ class DeviceStatusBloc extends Bloc<DeviceStatusEvent, DeviceStatusState> {
@override
Future<void> close() {
// 只有在 Bloc 彻底销毁时才取消订阅
//debugPrint('🚫 [DeviceStatusBloc] 正在关闭,取消所有订阅 ');
// debugPrint('🚫 [DeviceStatusBloc] 正在关闭,取消所有订阅');
//debugPrint('🔥 [DeviceStatusBloc] 销毁堆栈跟踪:\n${StackTrace.current}');
// _stringSub?.cancel();
// _jsonSub?.cancel();
//return super.close();
debugPrint('🚫 [DeviceStatusBloc] 页面退出,仅取消TCP订阅(不关闭BLoC)');
_logger.log('🚫 [DeviceStatusBloc] 页面退出,仅取消TCP订阅(不关闭BLoC)');
_tcpSubscription?.cancel();
// 🔥 关键修复:不调用 super.close(),保持 BLoC 活跃
return Future.value();
}
}