import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'dart:math'; import 'package:dart_ping/dart_ping.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:get_it/get_it.dart'; import 'package:maibu_satabot_v2/features/devices/domain/entities/device_entity.dart'; import 'package:maibu_satabot_v2/features/devices/domain/repositories/device_repository.dart'; import 'package:maibu_satabot_v2/features/devices/presentation/bloc/device_status_bloc.dart'; import 'package:maibu_satabot_v2/features/devices/presentation/bloc/device_status_state.dart'; import 'package:maibu_satabot_v2/features/remote_control/data/models/running_status_model.dart'; import 'package:maibu_satabot_v2/features/remote_control/domain/usecase/request_control_permission_usecase.dart'; import 'package:maibu_satabot_v2/features/remote_control/presentation/bloc/remote_control_state.dart'; import '../../../../core/logging/i_logger_service.dart'; import '../../../../core/network/net_message_dispatcher.dart'; import '../../../devices/domain/entities/running_status_entity.dart'; import '../../domain/entities/machine_control_status_entity.dart'; import '../../domain/repositories/remote_control_repository.dart'; import '../../domain/usecase/remote_control_usecase.dart'; class RemoteControlCubit extends Cubit { final RemoteControlRepository _repository; final RequestControlPermissionUseCase _requestControlPermissionUseCase; final DeviceRepository _deviceRepository; // 🔥 注入设备仓库 Timer? _timer; StreamSubscription? _kickOutSub; // 新增:用于管理监听生命周期 final NetMessageDispatcher dispatcher; final DeviceStatusBloc deviceStatusBloc; // 🔥 注入 DeviceStatusBloc StreamSubscription? _deviceStatusSub; // 🔥 订阅 DeviceStatusBloc 的状态流 static const platform = MethodChannel('com.maibu.satabot/ping'); int _currentPing = 50; final ILoggerService _logger = GetIt.I(); // 🔥 参考Android版:使用成员变量存储摇杆值,避免state竞态 int _currentOriginX = 0; int _currentOriginY = 0; // 🔥 权限请求处理标志位 - 防止竞态条件,避免重复显示弹窗 bool _isProcessingPermissionRequest = false; // 🔥 模拟数据推送定时器 // Timer? _simulationTimer; // 🔥 获取带时间戳的日志前缀 String _getTimePrefix() { final now = DateTime.now(); return '[${now.hour.toString().padLeft(2, '0')}:${now.minute.toString().padLeft(2, '0')}:${now.second.toString().padLeft(2, '0')}.${now.millisecond.toString().padLeft(3, '0')}]'; } RemoteControlCubit( this._repository, this._requestControlPermissionUseCase, this._deviceRepository, // 🔥 注入 this.dispatcher, this.deviceStatusBloc, // 🔥 注入 ) : super( RemoteControlState( controlEntity: MachineControlStatusEntity(), runningStatusModel: RunningStatusModel(), ), ) { _initPacketListener(); _initDeviceStatusListener(); // 🔥 改为订阅 DeviceStatusBloc } // 🔥 新增:订阅 DeviceStatusBloc 的状态流,而不是直接监听TCP // 类全局变量 DateTime? _lastUiUpdateTime; String? _cacheVoltage; String? _cacheBattery; String? _cacheCtrlMode; int? _cachePing; void _initDeviceStatusListener() { _deviceStatusSub?.cancel(); _deviceStatusSub = deviceStatusBloc.stream.listen((deviceState) async { if (deviceState is DeviceStatusUpdated) { // 第一步:所有数据先存入缓存,不管来多频繁都存最新值 final voltage = deviceState.status.voltage; final battery = deviceState.status.battery; final controlMode = deviceState.status.controlMode == '3' ? '远程模式' : '本地模式'; final c = await getNetworkDelay(); _cacheVoltage = voltage.toString(); _cacheBattery = battery.toString(); _cacheCtrlMode = controlMode; _cachePing = c; // 500ms节流,不到时间不刷新UI final now = DateTime.now(); if (_lastUiUpdateTime != null && now.difference(_lastUiUpdateTime!) < const Duration(milliseconds: 500)) { return; } _lastUiUpdateTime = now; // 间隔达标,统一一次刷新UI emit( state.copyWith( runningStatusModel: state.runningStatusModel.copyWith( voltage: _cacheVoltage, battery: _cacheBattery, controlMode: _cacheCtrlMode, ), battery: int.tryParse(_cacheBattery ?? '') ?? 0, ping: _cachePing, // 🔥 标记为设备状态更新 updateType: 'device_status', ), ); } }); } // 🔥 超简单方法:传入 IP,得到 ping 值 // 🔥 模拟设备状态更新 - 用于测试 // void simulateDeviceStatusUpdate({ // String? voltage, // String? battery, // String? controlMode, // int? ping, // // 🔥 是否随机生成数据 // bool random = false, // }) { // // 如果开启随机模式,生成随机数据 // final rand = Random(); /* if (random) { voltage = (22.0 + rand.nextDouble() * 4.0).toStringAsFixed( 1, ); // 22.0-26.0V battery = (rand.nextInt(100) + 1).toString(); // 1-100% controlMode = rand.nextBool() ? '远程模式' : '本地模式'; ping = rand.nextInt(150) + 20; // 20-170ms } */ /* debugPrint( '🔧 [模拟设备状态更新] voltage=$voltage V, battery=$battery%, controlMode=$controlMode, ping=$ping ms', ); */ // 更新缓存 /* if (voltage != null) _cacheVoltage = voltage; if (battery != null) _cacheBattery = battery; if (controlMode != null) _cacheCtrlMode = controlMode; if (ping != null) _cachePing = ping; // 直接触发状态更新(跳过节流,立即更新) emit( state.copyWith( runningStatusModel: state.runningStatusModel.copyWith( voltage: _cacheVoltage, battery: _cacheBattery, controlMode: _cacheCtrlMode, ), battery: int.tryParse(_cacheBattery ?? '') ?? 0, ping: _cachePing, // 🔥 标记为设备状态更新 updateType: 'device_status', ), ); } */ // 🔥 开始模拟设备状态推送(随机数据) /* void startSimulation({int intervalMs = 500}) { // 如果已经在运行,先停止 stopSimulation(); // debugPrint('🔔 [模拟推送] 开始模拟设备状态推送,间隔:${intervalMs}ms'); // 立即发送一次初始数据 simulateDeviceStatusUpdate(random: true); // 定时推送随机数据 _simulationTimer = Timer.periodic(Duration(milliseconds: intervalMs), ( timer, ) { if (!isClosed) { simulateDeviceStatusUpdate(random: true); } else { stopSimulation(); } }); } */ // 🔥 停止模拟设备状态推送 /* void stopSimulation() { if (_simulationTimer != null) { _simulationTimer!.cancel(); _simulationTimer = null; debugPrint('🔔 [模拟推送] 已停止模拟设备状态推送'); } } */ // 🔥 辅助方法:更新运行状态 void _updateStatusFromDevice(RunningStatusModel newStatus) { // // debugPrint('✅ [_updateStatusFromDevice] 收到运行状态更新:$newStatus'); if (!isClosed) { emit( state.copyWith( runningStatusModel: newStatus, voltage: int.tryParse(newStatus.voltage) ?? 0, battery: int.tryParse(newStatus.battery) ?? 0, ), ); // debugPrint('✅ [更新运行状态] 电压:${newStatus.voltage}V, 电量:${newStatus.battery}%'); // _logger.logWithLevel('✅ [更新运行状态] 电压:${newStatus.voltage}V, 电量:${newStatus.battery}%'); } } // 🔥 辅助方法:更新运行状态 // 1. 初始化回包监听 (如 0x12 权限) // void _initPacketListener() { // _repository.responseStream.listen((packet) { // if (packet.command == 0x12) { // // 根据负载判断是否有权限,更新状态 // emit(state.copyWith(hasPermission: true)); // } // }); // } Future _initPacketListener() async { final logPrefix = '${_getTimePrefix()} 🔍 [RemoteControl] [0x12监听器]'; debugPrint('$logPrefix ========================================='); debugPrint('$logPrefix 开始初始化 0x12 监听器'); _logger.logWithLevel('$logPrefix 开始初始化 0x12 监听器', shouldLog: true); _kickOutSub?.cancel(); // 防止重复监听 _kickOutSub = dispatcher.onCommand(0x12).listen((packet) { final timeNow = _getTimePrefix(); debugPrint('$timeNow 🔍 [RemoteControl] [0x12监听器] ✅ 收到 0x12 原始包'); /* debugPrint( '$timeNow 🔍 [RemoteControl] [0x12监听器] payload长度: ${packet.payload.length}', );*/ _logger.logWithLevel( '$timeNow 🔍 [RemoteControl] [0x12监听器] 收到 0x12 原始包,payload长度=${packet.payload.length}', shouldLog: true, ); // 先解析JSON判断是否为响应格式 String jsonStringForCheck; try { if (packet.payload.length > 2) { jsonStringForCheck = utf8.decode( packet.payload.sublist(0, packet.payload.length - 2), ); } else { jsonStringForCheck = utf8.decode(packet.payload); } final jsonMap = jsonDecode(jsonStringForCheck); final respondData = jsonMap['respond']; // 如果是响应格式,继续处理(更新权限状态) // 如果是请求格式且弹窗已显示,忽略 if (respondData == null && state.showPermissionRequestDialog) { debugPrint('$timeNow 🔍 [RemoteControl] [0x12监听器] ⚠️ 弹窗已显示(状态),忽略请求'); return; } } catch (e) { // 解析失败,继续处理 } try { // 🔥 关键:手动去掉最后 2 个 CRC 字节 String jsonString; if (packet.payload.length > 2) { jsonString = utf8.decode( packet.payload.sublist(0, packet.payload.length - 2), ); } else { jsonString = utf8.decode(packet.payload); } debugPrint('$timeNow 🔍 [RemoteControl] [0x12监听器] JSON内容: $jsonString'); _logger.logWithLevel( '$timeNow 🔍 [RemoteControl] [0x12监听器] 去除CRC后的JSON: $jsonString', shouldLog: true, ); final jsonMap = jsonDecode(jsonString); // 🔥 区分两种数据格式 final requestType = jsonMap['request']; final platform = jsonMap['platform']; final respondData = jsonMap['respond']; debugPrint( '$timeNow 🔍 [RemoteControl] [0x12监听器] requestType: $requestType, platform: $platform, hasRespond: ${respondData != null}', ); // 情况 1: 响应格式 - {"respond":{"switchResult":true,"deviceId":"...","holder":"you"}} if (respondData != null && respondData is Map) { // 🔥 收到响应格式,更新权限状态 final switchResult = respondData['switchResult']; /* debugPrint('$timeNow 🔍 [RemoteControl] [0x12监听器] 📊 收到切换结果响应'); debugPrint( '$timeNow 🔍 [RemoteControl] [0x12监听器] switchResult: $switchResult', );*/ _logger.logWithLevel( '$timeNow 🔍 [RemoteControl] [0x12监听器] 📊 收到切换结果响应 - switchResult: $switchResult', shouldLog: true, ); if (!isClosed) { if (switchResult == true) { // 切换成功,当前 APP 获得控制权 debugPrint('$timeNow 🔍 [RemoteControl] [0x12监听器] ✅ APP获得控制权'); debugPrint( '$timeNow 🔍 [RemoteControl] [0x12监听器] 设置 hasPermission = true', ); emit(state.copyWith(hasPermission: true)); } else { // 切换失败或拒绝,APP 失去控制权 debugPrint('$timeNow 🔍 [RemoteControl] [0x12监听器] ❌ APP失去控制权'); debugPrint( '$timeNow 🔍 [RemoteControl] [0x12监听器] 设置 hasPermission = false', ); emit( state.copyWith( hasPermission: false, // 🔥 修复:不强制关闭弹窗,让弹窗由用户操作控制 ), ); } } } // 情况 2: 请求格式 - {"request":"switch_control","deviceId":"...","platform":"web",...} else if (requestType == 'switch_control') { final requestDeviceId = jsonMap['deviceId']; final webLogPrefix = '${_getTimePrefix()} 🚨 [RemoteControl] [Web端权限请求]'; // 🔥 新增:记录收到请求的时间,便于排查是否为后端持续推送 debugPrint( '$webLogPrefix ⚡️⚡️⚡️ 收到Web端权限请求 - ${DateTime.now().toString()}', ); _logger.logWithLevel( '$webLogPrefix ⚡️⚡️⚡️ 收到Web端权限请求 - ${DateTime.now().toString()}', shouldLog: true, ); debugPrint('$webLogPrefix ========================================='); debugPrint('$webLogPrefix 收到 switch_control 请求'); debugPrint('$webLogPrefix platform: $platform'); debugPrint('$webLogPrefix requestDeviceId: $requestDeviceId'); _logger.logWithLevel( '$webLogPrefix 收到 switch_control 请求 - platform: $platform, deviceId: $requestDeviceId', shouldLog: true, ); // 🔥 关键判断:只有当是其他平台(web)请求时才弹窗 if (platform != null && platform.toString().toLowerCase() != 'app') { // 🔥 验证设备ID是否与当前控制的 targetDevice 一致 final currentDeviceId = state.targetDevice?.deviceName; debugPrint('$webLogPrefix 当前控制设备ID: $currentDeviceId'); if (currentDeviceId != null && requestDeviceId == currentDeviceId) { debugPrint('$webLogPrefix 设备ID匹配'); // 🔥 只有当弹窗还没显示时才弹出,防止重复弹窗叠加 if (state.showPermissionRequestDialog) { debugPrint('$webLogPrefix ⚠️ 弹窗已显示,忽略重复请求'); _logger.logWithLevel( '$webLogPrefix ⚠️ 弹窗已显示,忽略重复请求', shouldLog: true, ); return; } debugPrint('$webLogPrefix 弹出权限请求对话框'); _logger.logWithLevel( '$webLogPrefix 设备ID匹配,弹出权限请求对话框', shouldLog: true, ); if (!isClosed) { emit( state.copyWith( showPermissionRequestDialog: true, requestingDeviceId: requestDeviceId?.toString(), requestingPlatform: platform.toString(), // 🔥 标记为弹窗状态更新 updateType: 'permission_dialog', ), ); } } else { debugPrint('$webLogPrefix ⚠️ 设备ID不匹配,忽略'); debugPrint( '$webLogPrefix currentDeviceId: $currentDeviceId, requestDeviceId: $requestDeviceId', ); _logger.logWithLevel( '$webLogPrefix ⚠️ 设备ID不匹配,忽略请求', shouldLog: true, ); } } else { debugPrint('$webLogPrefix ℹ️ APP自己的请求回显或platform为空,忽略不弹窗'); _logger.logWithLevel( '$webLogPrefix ℹ️ APP自己的请求回显,忽略', shouldLog: true, ); } debugPrint('$webLogPrefix ========================================='); } // 情况 3: 异地登录通知 - {"request":"have_logged_in",...} else if (requestType == 'have_logged_in') { debugPrint('${_getTimePrefix()} ⚠️ [RemoteControl] 检测到异地登录'); _logger.logWithLevel( '${_getTimePrefix()} ⚠️ [RemoteControl] 检测到异地登录,打开弹窗提示', shouldLog: true, ); // 🔥 只有当弹窗还没显示时才弹出 if (!isClosed && !state.showPermissionRequestDialog) { emit(state.copyWith(showPermissionRequestDialog: true)); } } else { debugPrint('${_getTimePrefix()} ℹ️ [RemoteControl] 未知类型的 0x12 包,忽略'); _logger.logWithLevel( '${_getTimePrefix()} ℹ️ [RemoteControl] 未知类型的 0x12 包,忽略', shouldLog: true, ); } } catch (e) { debugPrint('${_getTimePrefix()} ❌ [RemoteControl] 解析失败:$e'); _logger.logWithLevel( '${_getTimePrefix()} ❌ [RemoteControl] 解析失败:$e', shouldLog: true, ); } }); } // 2. 开启 100ms 控制循环 (在进入遥控页面或点击“开始”时调用) /* void startControlLoop() { _timer?.cancel(); _timer = Timer.periodic(const Duration(milliseconds: 100), (timer) { // 🔥 关键修复:每次循环都重新读取最新的 state final currentEntity = state.controlEntity; // 🔥 安全检查:无权限或急停时不发送 if (!state.hasPermission || state.isEmergency) { // debugPrint('⚠️ [定时器] 跳过发送 - hasPermission=${state.hasPermission}, isEmergency=${state.isEmergency}'); return; } // debugPrint('⏰ [定时器] 发送控制指令 - originX=${currentEntity.originX}, originY=${currentEntity.originY}'); // _logger.logWithLevel('真实的发送的实体 - originX: ${currentEntity.originX}, originY: ${currentEntity.originY}'); _repository.sendControlMachineCmd(currentEntity); }); emit(state.copyWith(status: RemoteControlStatus.controlling)); }*/ void startControlLoop() { _timer?.cancel(); final logPrefix = '⏰ [RemoteControl] [控制循环]'; //debugPrint('$logPrefix ========================================='); //debugPrint('$logPrefix 启动控制循环,间隔: 100ms'); ///debugPrint('$logPrefix ========================================='); // 🔥 启动模拟设备状态推送(用于测试) // startSimulation(); _timer = Timer.periodic(const Duration(milliseconds: 100), (timer) { if (isClosed) { // debugPrint('$logPrefix ❌ Cubit已关闭,取消定时器'); timer.cancel(); return; } // 🔥 参考Android版:直接读取成员变量,避免state竞态 final snapshot = MachineControlStatusEntity( originX: _currentOriginX, originY: _currentOriginY, chassisLift: state.controlEntity.chassisLift, mowerSpeed: state.controlEntity.mowerSpeed, ignitionStatus: state.controlEntity.ignitionStatus, isEmergency: state.isEmergency, ); // 常规安全检查 if (!state.hasPermission) { // debugPrint('$logPrefix ⚠️ 无权限,跳过发送 - hasPermission=false'); return; } if (state.isEmergency) { // debugPrint('$logPrefix 🚨 急停状态,跳过发送 - isEmergency=true'); return; } // 发送控制指令 //debugPrint( // '$logPrefix 📤 发送控制指令: originX=${snapshot.originX}, originY=${snapshot.originY}, mower=${snapshot.mowerSpeed}, lift=${snapshot.chassisLift}, ignition=${snapshot.ignitionStatus}, emergency=${snapshot.isEmergency}', // ); _repository.sendControlMachineCmd(snapshot); //debugPrint('$logPrefix ✅ 控制指令已发送'); }); } //通过方法拿最新 state MachineControlStatusEntity _getCurrentControlEntity() { // 🔥 关键修复:必须copyWith创建新对象,避免引用竞态条件 final entity = state.controlEntity; // _logger.logWithLevel( // '真实的发送的实体 - originX: ${entity.originX}, originY: ${entity.originY}', // ); return entity.copyWith(); // 返回副本,不是引用 } // 3. 更新摇杆数据 void updateJoystick(double x, double y) { final updatedEntity = state.controlEntity.copyWith( x: x.toInt(), y: y.toInt(), ); emit(state.copyWith(controlEntity: updatedEntity)); } // 4. 更新功能开关 (比如割刀速度、灯光、点火等) void updateFunction({int? mower, int? lift, int? ignition, bool? emergency}) { // debugPrint('🔧 [updateFunction] 调用 - mower: $mower, lift: $lift, ignition: $ignition, emergency: $emergency'); // _logger.logWithLevel( // '🔧 [updateFunction] 调用 - mower: $mower, lift: $lift, ignition: $ignition, emergency: $emergency', // ); final updatedEntity = state.controlEntity.copyWith( mower: mower, lift: lift, ignition: ignition, emergency: emergency, ); //emit(state.copyWith(controlEntity: updatedEntity)); emit( state.copyWith( controlEntity: updatedEntity, isEmergency: emergency ?? state.isEmergency, ), ); // debugPrint('>>> [updateFunction] 状态已更新到emit'); // _logger.logWithLevel('>>> [updateFunction] 状态已更新到emit'); } void updateOriginY(int y) { // debugPrint('📥 [updateOriginY] 被调用- y=$y'); // 🔥 参考Android版:直接更新成员变量 _currentOriginY = y; // 同时更新state(用于UI显示) final updatedEntity = state.controlEntity.copyWith(y: y); emit(state.copyWith(controlEntity: updatedEntity)); // debugPrint('📝 [updateOriginY] state已更新- originY=$y'); } void updateOriginX(int x) { // debugPrint('📥 [updateOriginX] 被调用- x=$x'); // 🔥 参考Android版:直接更新成员变量 _currentOriginX = x; // 同时更新state(用于UI显示) final updatedEntity = state.controlEntity.copyWith(x: x); emit(state.copyWith(controlEntity: updatedEntity)); // debugPrint('📝 [updateOriginX] state已更新- originX=$x'); } /// 🔥 安全方法:同时清零双轴,确保只发送一次完全停止指令 Future stopAllMovement() async { // debugPrint( // '🛑 [stopAllMovement] 开始执行- 当前成员变量: originX=$_currentOriginX, originY=$_currentOriginY', // ); // 🔥 参考Android版:直接清零成员变量 _currentOriginX = 0; _currentOriginY = 0; // 🔥 关键修复:先暂停定时器,防止定时器在停止期间发送旧的运动指令 _timer?.cancel(); _timer = null; // 🔥 彻底清空,防止重复启用 final updatedEntity = state.controlEntity.copyWith(x: 0, y: 0); // 一次性更新双轴(即使无权限也要更新本地state) emit(state.copyWith(controlEntity: updatedEntity)); // 🔥 安全底线:停止指令必须无视权限强制发送! // debugPrint('🛑 [stopAllMovement] 双轴归零,发送停止指令- originX=0, originY=0'); await _sendStopCommandRepeatedly(updatedEntity); // 等待所有指令发送完成 // 🔥 恢复定时器 await Future.delayed(const Duration(milliseconds: 200)); startControlLoop(); // debugPrint('>>> [stopAllMovement] 执行完成'); } /// 🔥 统一方法:连续发送10次停止指令,彻底清空TCP缓冲区 Future _sendStopCommandRepeatedly( MachineControlStatusEntity stopEntity, ) async { // debugPrint( // '🛑 [紧急停止] 开始连续发送10次停止指令- originX=${stopEntity.originX}, originY=${stopEntity.originY}', // ); // 🔥 注意:_lastStopTime 已经在stopAllMovement() 中设置了,这里不需要再设置 int sentCount = 0; // 🔥 关键修复:每次发送间隔5ms,避免TCP合并/丢弃 for (int i = 0; i < 20; i++) { _repository.sendControlMachineCmd(stopEntity); sentCount++; if (i % 5 == 0) { // debugPrint('🛑 [紧急停止] 已发送第${i + 1}次'); } if (i < 19) { await Future.delayed(const Duration(milliseconds: 5)); } } // 🔥 延迟后再发10次(双重保险,对抗网络抖动) await Future.delayed(const Duration(milliseconds: 100)); for (int i = 0; i < 10; i++) { _repository.sendControlMachineCmd(stopEntity); sentCount++; if (i < 9) { await Future.delayed(const Duration(milliseconds: 5)); } } // debugPrint('>>> [紧急停止] 所有20次停止指令已发出'); } // 5. 停止控制循环 void stopControlLoop() { _timer?.cancel(); _timer = null; emit(state.copyWith(status: RemoteControlStatus.initial)); } void toggleLock() { emit(state.copyWith(isLocked: !state.isLocked)); } void togglePermissionDialog(bool show) { emit(state.copyWith(showPermissionRequestDialog: show)); } void requestControlPermission() { // 1. 关闭弹窗 emit(state.copyWith(showPermissionRequestDialog: false)); } void toggleLeftPip() => emit(state.copyWith(showLeftPip: !state.showLeftPip)); void toggleRightPip() => emit(state.copyWith(showRightPip: !state.showRightPip)); @override Future close() { _timer?.cancel(); _deviceStatusSub?.cancel(); // 🔥 取消订阅 DeviceStatusBloc return super.close(); } void updateChassisLift(int i) {} void updateEmergency(bool bool) { // debugPrint('🚨 [急停] ${bool ? "触发急停!" : "解除急停"}'); updateFunction(emergency: bool); } void respondPermission(bool agreed, String deviceId) { // 1. 关闭弹窗(立即关闭,防止重复点击) emit(state.copyWith(showPermissionRequestDialog: false)); // 2. 发送响应到服务器 try { _repository.respondPermission(agreed, deviceId); } catch (e) { debugPrint(' ❌ TCP权限响应指令发送失败: $e'); debugPrint(' ❌ 错误类型: ${e.runtimeType}'); _logger.logWithLevel(' TCP权限响应指令发送失败 $e', shouldLog: true); rethrow; } // 3. 根据用户选择更新控制状态 if (agreed) { emit( state.copyWith( hasPermission: false, showPermissionRequestDialog: false, ), ); } else { // 用户拒绝 APP 继续保持控制权 // 🔥 必须同时设置 showPermissionRequestDialog: false,防止状态回退 emit( state.copyWith(hasPermission: true, showPermissionRequestDialog: false), ); } debugPrint('${_getTimePrefix()} ====权限弹窗响应结束====='); } Future requestControlPermissionS( String deviceName, String deviceId, { String source = '自动', }) async { final timePrefix = _getTimePrefix(); final logPrefix = '$timePrefix 🔑 [RemoteControl] [请求权限接口-$source]'; debugPrint('$logPrefix ========================================='); debugPrint('$logPrefix 开始请求控制权'); debugPrint('$logPrefix deviceName: $deviceName'); debugPrint('$logPrefix platform: $deviceId'); debugPrint( '$logPrefix 当前状态 hasPermission=${state.hasPermission}, showDialog=${state.showPermissionRequestDialog}', ); _logger.logWithLevel( '$logPrefix 开始请求控制权- deviceName: $deviceName, platform: $deviceId', shouldLog: true, ); // 1. 只有当弹窗不是因Web 端请求权限而显示时,才关闭弹窗 // 避免 Web 端请求权限的弹窗被自动关闭(一闪而过的问题) if (state.requestingPlatform == null) { debugPrint('$logPrefix 关闭权限请求弹窗 (非Web端触发)'); emit(state.copyWith(showPermissionRequestDialog: false)); } else { debugPrint('$logPrefix 保留弹窗 (Web端请求触发)'); } // 2. 调用 UseCase 获取 HTTP 返回的完整权限信息 debugPrint('$logPrefix 调用 HTTP 接口查询权限状态..'); final result = await _requestControlPermissionUseCase( RequestControlPermissionParams( deviceName: deviceName, deviceId: deviceId, ), ); // 3. 处理结果 result.fold( (failure) { final failLogPrefix = '${_getTimePrefix()} 🔑 [RemoteControl] [请求权限接口-$source]'; debugPrint('$failLogPrefix APP请求控制权限失败: ${failure.message}'); _logger.logWithLevel( '$failLogPrefix APP请求控制权限失败: ${failure.message}', shouldLog: true, ); // 🔥 修复:HTTP请求失败时不要自动打开弹窗,避免形成循环 // emit(state.copyWith(showPermissionRequestDialog: true)); debugPrint('$failLogPrefix HTTP请求失败,不自动打开弹窗'); }, (permissionInfo) async { final bool hasPermission = permissionInfo['hasPermission'] as bool? ?? false; final String? owner = permissionInfo['owner'] as String?; final successLogPrefix = '${_getTimePrefix()} 🔑 [RemoteControl] [请求权限接口-$source]'; debugPrint( '$successLogPrefix APP HTTP返回 - hasPermission=$hasPermission, owner=$owner', ); _logger.logWithLevel( '$successLogPrefix APP HTTP返回 - hasPermission=$hasPermission, owner=$owner', shouldLog: true, ); // 🔥 关键逻辑: 如果没有权限 或owner为null,则发送TCP 请求 if (!hasPermission || owner == null) { debugPrint('$successLogPrefix ⚠️ APP无权限或owner为null,发送TCP请求...'); _logger.logWithLevel( '$successLogPrefix ⚠️ APP无权限或owner为null,发送TCP请求', shouldLog: true, ); // 🔥 发送TCP 0x12 权限请求指令 _repository.sendTcpPermissionRequest(deviceName); // 等待 TCP 回包(通过监听器更新状态 debugPrint('$successLogPrefix 📡 TCP请求已发送,等待回包确认'); } else { debugPrint('$successLogPrefix APP已有权限,直接更新UI'); _logger.logWithLevel( '$successLogPrefix APP已有权限,直接更新UI', shouldLog: true, ); debugPrint( '$successLogPrefix 当前 hasPermission 状态: ${state.hasPermission}', ); // 🔥 正确的状态更新:使用最新状态 if (state.hasPermission != true) { debugPrint('$successLogPrefix 🔄 更新 hasPermission = true'); emit(state.copyWith(hasPermission: true)); debugPrint('$successLogPrefix ✅ hasPermission 状态已更新为 true'); } else { debugPrint('$successLogPrefix ⚠️ hasPermission 已是 true,无需更新'); // 强制触发UI刷新:通过临时改变其他属性 emit(state.copyWith(ping: state.ping + 1)); emit(state.copyWith(ping: state.ping)); } } }, ); debugPrint( '${_getTimePrefix()} 🔑 [RemoteControl] [请求权限接口-$source] =========================================', ); } /// 🔥 重置弹窗状态 - 在弹窗关闭后调用(已简化,不再需要标志位) void resetPermissionCoolDown() { // 标志位已移除,此方法保留以保持向后兼容性 } Future confirmPermissionResponse( String deviceName, String platform, bool agreed, ) async { final timePrefix = _getTimePrefix(); final logPrefix = '$timePrefix 🔑 [RemoteControl] [权限确认]'; debugPrint('$logPrefix ========进入TCP发送================================='); debugPrint('$logPrefix ⚡️ confirmPermissionResponse 方法被调用'); debugPrint('$logPrefix 用户操作: ${agreed ? "同意" : "拒绝"}'); debugPrint('$logPrefix deviceName: $deviceName'); debugPrint('$logPrefix platform: $platform'); respondPermission(agreed, deviceName); // 2. 调用 HTTP 接口获取最终权限状态 debugPrint('$logPrefix 📡 调用 HTTP 接口确认最终权限状态..'); /* final result = await _requestControlPermissionUseCase( RequestControlPermissionParams( deviceName: deviceName, deviceId: platform, ), );*/ // 3. 根据 HTTP 返回的真实权限状态更新UI /* result.fold( (failure) { final failLogPrefix = '${_getTimePrefix()} 🔑 [RemoteControl] [权限确认]'; debugPrint('$failLogPrefix APP HTTP请求失败: ${failure.message}'); debugPrint('$failLogPrefix ⚠️ 保持当前状态不变'); }, (permissionInfo) { final bool hasPermission = permissionInfo['hasPermission'] as bool? ?? false; final String? owner = permissionInfo['owner'] as String?; final successLogPrefix = '${_getTimePrefix()} 🔑 [RemoteControl] [权限确认]'; debugPrint( '$successLogPrefix APP HTTP返回真实权限状态 hasPermission=$hasPermission, owner=$owner', ); debugPrint( '$successLogPrefix 🔄 正在更新UI - hasPermission: ${state.hasPermission} -> $hasPermission', ); // 🔥 直接用HTTP 返回的权限状态覆盖 // emit(state.copyWith(hasPermission: hasPermission)); debugPrint( '$successLogPrefix 📊 UI已同步完成- 当前 hasPermission=$hasPermission', ); }, );*/ debugPrint( '${_getTimePrefix()} 🔑 [RemoteControl] [权限确认] =========================================', ); } /// 发送底盘指令 void sendChassisCommand(int i) { // // debugPrint('>>> [底盘指令] ${i}'); // _logger.logWithLevel('>>> [底盘指令] ${i}'); updateFunction(lift: i); } /// 发送割刀指令 void sendMowerCommand(int i) { // // debugPrint('>>> [割刀指令] ${i}'); // _logger.logWithLevel('>>> [割刀指令] ${i}'); updateFunction(mower: i); } /// 发送点火指令 void sendFireCommand(int i) { //void updateFunction({int? mower, int? lift, int? ignition, bool? emergency}) // updateFunction(mower:0, lift: 0, ignition: i, emergency: false); // debugPrint('>>> [点火指令] ${i}'); // _logger.logWithLevel('>>> [点火指令] ${i}'); updateFunction(ignition: i); } // 发送障碍物识别指令 void toggleObstacleRecognition() { emit( state.copyWith(obstacleRecognitionFlag: !state.obstacleRecognitionFlag), ); } void toggleTopLeftExpand() { emit(state.copyWith(topRightIsExpanded: !state.topRightIsExpanded)); } Future getNetworkDelay() async { try { // 直接 Ping 你的服务器IP final ping = Ping('1.95.137.212', count: 1, timeout: 1); // 等待一次结果 final data = await ping.stream.first; if (data.response != null && data.response!.time != null) { // 返回和cmd 一样的毫秒值 return data.response!.time!.inMilliseconds; } else { return 9999; } } catch (e) { return 9999; } } //app退出远程遥控界面释放权限 Future releasePermission(String platform) async { return await _repository.releasePermission(platform); } /// 🔥 设置待控制的设备(从机器人列表点击进入时调用) void setTargetDevice(DeviceEntity device) { // debugPrint('🎯 [RemoteControl] 设置待控制设备 ${device.deviceName}'); // _logger.logWithLevel('🎯 [RemoteControl] 设置待控制设备 ${device.deviceName}'); // 🔥 通知后端订阅该设备 _deviceRepository.switchDevice("app", device.deviceName).then((result) { result.fold( (failure) { // debugPrint('>>> [RemoteControl] 切换设备失败: ${failure.message}'); // _logger.logWithLevel('>>> [RemoteControl] 切换设备失败: ${failure.message}'); }, (success) { // debugPrint('>>> [RemoteControl] 切换设备成功, code: $success'); // _logger.logWithLevel('>>> [RemoteControl] 切换设备成功, code: $success'); }, ); }); emit(state.copyWith(targetDevice: device)); } /// 🔥 清除待控制设备(退出远程控制页时调用) void clearTargetDevice() { // debugPrint('🧹 [RemoteControl] 清除待控制设备'); // _logger.logWithLevel('🧹 [RemoteControl] 清除待控制设备'); emit(state.copyWith(targetDevice: null)); } }