对接远程遥控
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import 'dart:async';
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
@@ -7,9 +7,12 @@ 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';
|
||||
@@ -22,6 +25,7 @@ import '../../domain/usecase/remote_control_usecase.dart';
|
||||
class RemoteControlCubit extends Cubit<RemoteControlState> {
|
||||
final RemoteControlRepository _repository;
|
||||
final RequestControlPermissionUseCase _requestControlPermissionUseCase;
|
||||
final DeviceRepository _deviceRepository; // 🔥 注入设备仓库
|
||||
Timer? _timer;
|
||||
StreamSubscription? _kickOutSub; // 新增:用于管理监听生命周期
|
||||
final NetMessageDispatcher dispatcher;
|
||||
@@ -30,16 +34,28 @@ class RemoteControlCubit extends Cubit<RemoteControlState> {
|
||||
static const platform = MethodChannel('com.maibu.satabot/ping');
|
||||
int _currentPing = 50;
|
||||
final ILoggerService _logger = GetIt.I<ILoggerService>();
|
||||
|
||||
|
||||
// 🔥 参考Android版:使用成员变量存储摇杆值,避免state竞态
|
||||
int _currentOriginX = 0;
|
||||
int _currentOriginY = 0;
|
||||
|
||||
// 🔥 权限请求冷却期机制 - 防止Web端持续发送请求导致弹窗不断显示
|
||||
DateTime? _lastPermissionResponseTime; // 记录上次响应权限请求的时间
|
||||
static const _coolDownDuration = Duration(seconds: 5); // 冷却期5秒
|
||||
|
||||
// 🔥 同步锁 - 防止状态更新期间接收新请求导致重复弹窗
|
||||
bool _isProcessingPermissionRequest = false;
|
||||
|
||||
// 🔥 获取带时间戳的日志前缀
|
||||
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(
|
||||
@@ -52,54 +68,55 @@ class RemoteControlCubit extends Cubit<RemoteControlState> {
|
||||
_initDeviceStatusListener(); // 🔥 改为订阅 DeviceStatusBloc
|
||||
}
|
||||
|
||||
// 🔥 新增:订阅 DeviceStatusBloc 的状态流,而不是直接监听 TCP
|
||||
// 🔥 新增:订阅 DeviceStatusBloc 的状态流,而不是直接监听TCP
|
||||
void _initDeviceStatusListener() {
|
||||
_logger.logWithLevel('>>> [RemoteControl] begin 订阅 DeviceStatusBloc 状态流');
|
||||
// // _logger.logWithLevel('>>> [RemoteControl] begin 订阅 DeviceStatusBloc 状态流');
|
||||
_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 controlMode = deviceState.status.controlMode == '3'
|
||||
? '远程模式'
|
||||
: '本地模式';
|
||||
|
||||
final c = getNetworkDelay();
|
||||
emit(state.copyWith(
|
||||
runningStatusModel: state.runningStatusModel.copyWith(
|
||||
voltage: voltage.toString(),
|
||||
battery: battery.toString(),
|
||||
controlMode: controlMode,
|
||||
emit(
|
||||
state.copyWith(
|
||||
runningStatusModel: state.runningStatusModel.copyWith(
|
||||
voltage: voltage.toString(),
|
||||
battery: battery.toString(),
|
||||
controlMode: controlMode,
|
||||
),
|
||||
battery: int.tryParse(battery) ?? 0,
|
||||
ping: await c,
|
||||
),
|
||||
battery: int.tryParse(battery) ?? 0,
|
||||
ping: await c,
|
||||
));
|
||||
);
|
||||
|
||||
_logger.logWithLevel('✅ [RemoteControl] 从 DeviceStatusBloc 收到更新: 电压=$voltage, 电量=$battery, 模式=$controlMode');
|
||||
// // _logger.logWithLevel('✅ [RemoteControl] 从 DeviceStatusBloc 收到更新: 电压=$voltage, 电量=$battery, 模式=$controlMode');
|
||||
}
|
||||
});
|
||||
|
||||
_logger.logWithLevel('>>> [RemoteControl] ✅ DeviceStatusBloc 订阅已建立完成');
|
||||
// _logger.logWithLevel('>>> [RemoteControl] ✅ DeviceStatusBloc 订阅已建立完成');
|
||||
}
|
||||
|
||||
|
||||
// 🔥 超简单方法:传入 IP,得到 ping 值
|
||||
|
||||
|
||||
|
||||
|
||||
// 🔥 辅助方法:更新运行状态
|
||||
void _updateStatusFromDevice(RunningStatusModel newStatus) {
|
||||
|
||||
debugPrint('✅ [_updateStatusFromDevice] 收到运行状态更新:$newStatus');
|
||||
// // debugPrint('✅ [_updateStatusFromDevice] 收到运行状态更新:$newStatus');
|
||||
if (!isClosed) {
|
||||
emit(state.copyWith(
|
||||
runningStatusModel: newStatus,
|
||||
voltage: int.tryParse(newStatus.voltage) ?? 0,
|
||||
battery: int.tryParse(newStatus.battery) ?? 0,
|
||||
));
|
||||
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}%');
|
||||
// debugPrint('✅ [更新运行状态] 电压:${newStatus.voltage}V, 电量:${newStatus.battery}%');
|
||||
// _logger.logWithLevel('✅ [更新运行状态] 电压:${newStatus.voltage}V, 电量:${newStatus.battery}%');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,25 +131,40 @@ class RemoteControlCubit extends Cubit<RemoteControlState> {
|
||||
// });
|
||||
// }
|
||||
Future<void> _initPacketListener() async {
|
||||
//print('>>> [RemoteControl] begin 初始化 0x12 监听器');
|
||||
_logger.logWithLevel('>>> [RemoteControl] begin 0x12 监听器');
|
||||
final logPrefix = '${_getTimePrefix()} 🔍 [RemoteControl] [0x12监听器]';
|
||||
debugPrint('$logPrefix =========================================');
|
||||
debugPrint('$logPrefix 开始初始化 0x12 监听器');
|
||||
_logger.logWithLevel('$logPrefix 开始初始化 0x12 监听器', shouldLog: true);
|
||||
|
||||
_kickOutSub?.cancel(); // 防止重复监听
|
||||
|
||||
_kickOutSub = dispatcher.onCommand(0x12).listen((packet) {
|
||||
//print('>>> [RemoteControl] 收到 0x12 原始包,payload 长度=${packet.payload.length}');
|
||||
_logger.logWithLevel('>>> [RemoteControl] 收到 0x12 原始包,payload 长度=${packet.payload.length}');
|
||||
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,
|
||||
);
|
||||
|
||||
try {
|
||||
// 🔥 关键:手动去掉最后 2 个 CRC 字节
|
||||
String jsonString;
|
||||
if (packet.payload.length > 2) {
|
||||
jsonString = utf8.decode(packet.payload.sublist(0, packet.payload.length - 2));
|
||||
jsonString = utf8.decode(
|
||||
packet.payload.sublist(0, packet.payload.length - 2),
|
||||
);
|
||||
} else {
|
||||
jsonString = utf8.decode(packet.payload);
|
||||
}
|
||||
|
||||
//print('>>> [RemoteControl] 去除 CRC 后的 JSON: $jsonString');
|
||||
_logger.logWithLevel('>>> [RemoteControl] 去除 CRC 后的 JSON: $jsonString');
|
||||
debugPrint('$timeNow 🔍 [RemoteControl] [0x12监听器] JSON内容: $jsonString');
|
||||
_logger.logWithLevel(
|
||||
'$timeNow 🔍 [RemoteControl] [0x12监听器] 去除CRC后的JSON: $jsonString',
|
||||
shouldLog: true,
|
||||
);
|
||||
|
||||
final jsonMap = jsonDecode(jsonString);
|
||||
|
||||
@@ -141,69 +173,139 @@ class RemoteControlCubit extends Cubit<RemoteControlState> {
|
||||
final platform = jsonMap['platform'];
|
||||
final respondData = jsonMap['respond'];
|
||||
|
||||
//print('>>> [RemoteControl] 📋 requestType=$requestType, platform=$platform');
|
||||
_logger.logWithLevel('>>> [RemoteControl] 📋 requestType=$requestType, platform=$platform');
|
||||
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'];
|
||||
//print('>>> [RemoteControl] 📊 收到切换结果响应:switchResult = $switchResult');
|
||||
_logger.logWithLevel('>>> [RemoteControl] 📊 收到切换结果响应:switchResult = $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 失去控制权
|
||||
emit(state.copyWith(hasPermission: true, showPermissionRequestDialog: false));
|
||||
print('>>> [RemoteControl] ✅ 权限已切');
|
||||
// 切换成功,当前 APP 获得控制权
|
||||
debugPrint('$timeNow 🔍 [RemoteControl] [0x12监听器] ✅ APP获得控制权');
|
||||
debugPrint(
|
||||
'$timeNow 🔍 [RemoteControl] [0x12监听器] 设置 hasPermission = true',
|
||||
);
|
||||
emit(state.copyWith(hasPermission: true));
|
||||
} else {
|
||||
// 切换失败或拒绝,保持当前状态
|
||||
emit(state.copyWith(showPermissionRequestDialog: false));
|
||||
print('>>> [RemoteControl] ❌ 权限切换失败/被拒绝');
|
||||
// 切换失败或拒绝,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 =========================================');
|
||||
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') {
|
||||
//print('>>> [RemoteControl] 🚨 $platform 端请求控制权,打开弹窗询问用户');
|
||||
_logger.logWithLevel('>>> [RemoteControl] 🚨 $platform 端请求控制权,打开弹窗询问用户');
|
||||
if (!isClosed) {
|
||||
emit(state.copyWith(showPermissionRequestDialog: true));
|
||||
// 🔥 验证设备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 设备ID匹配,弹出权限请求对话框',
|
||||
shouldLog: true,
|
||||
);
|
||||
if (!isClosed) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
showPermissionRequestDialog: true,
|
||||
requestingDeviceId: requestDeviceId?.toString(),
|
||||
requestingPlatform: platform.toString(),
|
||||
),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
debugPrint('$webLogPrefix ⚠️ 弹窗已显示,忽略重复请求');
|
||||
_logger.logWithLevel(
|
||||
'$webLogPrefix ⚠️ 弹窗已显示,忽略重复请求',
|
||||
shouldLog: true,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
debugPrint('$webLogPrefix ⚠️ 设备ID不匹配,忽略');
|
||||
debugPrint(
|
||||
'$webLogPrefix currentDeviceId: $currentDeviceId, requestDeviceId: $requestDeviceId',
|
||||
);
|
||||
_logger.logWithLevel(
|
||||
'$webLogPrefix ⚠️ 设备ID不匹配,忽略请求',
|
||||
shouldLog: true,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
//print('>>> [RemoteControl] ℹ️ APP 自己的请求回显,忽略不弹窗');
|
||||
_logger.logWithLevel('>>> [RemoteControl] ℹ️ APP 自己的请求回显,忽略不弹窗');
|
||||
debugPrint('$webLogPrefix ℹ️ APP自己的请求回显或platform为空,忽略不弹窗');
|
||||
_logger.logWithLevel(
|
||||
'$webLogPrefix ℹ️ APP自己的请求回显,忽略',
|
||||
shouldLog: true,
|
||||
);
|
||||
}
|
||||
debugPrint('$webLogPrefix =========================================');
|
||||
}
|
||||
// 情况 3: 异地登录通知 - {"request":"have_logged_in",...}
|
||||
else if (requestType == 'have_logged_in') {
|
||||
//print('>>> [RemoteControl] ⚠️ 检测到异地登录,打开弹窗提示');
|
||||
_logger.logWithLevel('>>> [RemoteControl] ⚠️ 检测到异地登录,打开弹窗提示');
|
||||
debugPrint('${_getTimePrefix()} ⚠️ [RemoteControl] 检测到异地登录');
|
||||
_logger.logWithLevel(
|
||||
'${_getTimePrefix()} ⚠️ [RemoteControl] 检测到异地登录,打开弹窗提示',
|
||||
shouldLog: true,
|
||||
);
|
||||
if (!isClosed) {
|
||||
emit(state.copyWith(showPermissionRequestDialog: true));
|
||||
}
|
||||
}
|
||||
else {
|
||||
// print('>>> [RemoteControl] ℹ️ 未知类型的 0x12 包,忽略');
|
||||
_logger.logWithLevel('>>> [RemoteControl] ℹ️ 未知类型的 0x12 包,忽略');
|
||||
} else {
|
||||
debugPrint('${_getTimePrefix()} ℹ️ [RemoteControl] 未知类型的 0x12 包,忽略');
|
||||
_logger.logWithLevel(
|
||||
'${_getTimePrefix()} ℹ️ [RemoteControl] 未知类型的 0x12 包,忽略',
|
||||
shouldLog: true,
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
//print('>>> [RemoteControl] ❌ 解析失败:$e');
|
||||
_logger.logWithLevel('>>> [RemoteControl] ❌ 解析失败:$e');
|
||||
debugPrint('${_getTimePrefix()} ❌ [RemoteControl] 解析失败:$e');
|
||||
_logger.logWithLevel(
|
||||
'${_getTimePrefix()} ❌ [RemoteControl] 解析失败:$e',
|
||||
shouldLog: true,
|
||||
);
|
||||
}
|
||||
});
|
||||
final c = getNetworkDelay();
|
||||
emit(state.copyWith(
|
||||
ping: await c, // 这里直接使用异步返回的数值
|
||||
));
|
||||
//print('>>> [RemoteControl] ✅ 0x12 监听器已建立完成');
|
||||
_logger.logWithLevel('>>> [RemoteControl] ✅ 0x12 监听器已建立完成');
|
||||
}
|
||||
|
||||
|
||||
// 2. 开启 100ms 控制循环 (在进入遥控页面或点击“开始”时调用)
|
||||
/* void startControlLoop() {
|
||||
/* void startControlLoop() {
|
||||
_timer?.cancel();
|
||||
_timer = Timer.periodic(const Duration(milliseconds: 100), (timer) {
|
||||
// 🔥 关键修复:每次循环都重新读取最新的 state
|
||||
@@ -211,21 +313,27 @@ class RemoteControlCubit extends Cubit<RemoteControlState> {
|
||||
|
||||
// 🔥 安全检查:无权限或急停时不发送
|
||||
if (!state.hasPermission || state.isEmergency) {
|
||||
debugPrint('⚠️ [定时器] 跳过发送 - hasPermission=${state.hasPermission}, isEmergency=${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}');
|
||||
// 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 =========================================');
|
||||
|
||||
_timer = Timer.periodic(const Duration(milliseconds: 100), (timer) {
|
||||
if (isClosed) {
|
||||
// debugPrint('$logPrefix ❌ Cubit已关闭,取消定时器');
|
||||
timer.cancel();
|
||||
return;
|
||||
}
|
||||
@@ -239,28 +347,34 @@ class RemoteControlCubit extends Cubit<RemoteControlState> {
|
||||
ignitionStatus: state.controlEntity.ignitionStatus,
|
||||
isEmergency: state.isEmergency,
|
||||
);
|
||||
|
||||
|
||||
// 常规安全检查
|
||||
if (!state.hasPermission) {
|
||||
debugPrint('⚠️ [定时器] 无权限,跳过发送');
|
||||
// debugPrint('$logPrefix ⚠️ 无权限,跳过发送 - hasPermission=false');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (state.isEmergency) {
|
||||
debugPrint('🚨 [定时器] 急停状态,跳过发送');
|
||||
// debugPrint('$logPrefix 🚨 急停状态,跳过发送 - isEmergency=true');
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.logWithLevel('[定时器] 发送控制指令 - originX=${snapshot.originX}, originY=${snapshot.originY}');
|
||||
debugPrint('📤 [定时器] 准备发送 - originX=${snapshot.originX}, originY=${snapshot.originY}');
|
||||
|
||||
// 发送控制指令
|
||||
//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
|
||||
|
||||
//通过方法拿最新 state
|
||||
MachineControlStatusEntity _getCurrentControlEntity() {
|
||||
// 🔥 关键修复:必须copyWith创建新对象,避免引用竞态条件
|
||||
final entity = state.controlEntity;
|
||||
_logger.logWithLevel('真实的发送的实体 - originX: ${entity.originX}, originY: ${entity.originY}');
|
||||
// _logger.logWithLevel(
|
||||
// '真实的发送的实体 - originX: ${entity.originX}, originY: ${entity.originY}',
|
||||
// );
|
||||
return entity.copyWith(); // 返回副本,不是引用
|
||||
}
|
||||
|
||||
@@ -275,8 +389,10 @@ class RemoteControlCubit extends Cubit<RemoteControlState> {
|
||||
|
||||
// 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');
|
||||
// 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,
|
||||
@@ -284,85 +400,93 @@ class RemoteControlCubit extends Cubit<RemoteControlState> {
|
||||
emergency: emergency,
|
||||
);
|
||||
//emit(state.copyWith(controlEntity: updatedEntity));
|
||||
emit(state.copyWith(
|
||||
controlEntity: updatedEntity,
|
||||
isEmergency: emergency ?? state.isEmergency,
|
||||
));
|
||||
debugPrint('✅ [updateFunction] 状态已更新并 emit');
|
||||
_logger.logWithLevel('✅ [updateFunction] 状态已更新并 emit');
|
||||
emit(
|
||||
state.copyWith(
|
||||
controlEntity: updatedEntity,
|
||||
isEmergency: emergency ?? state.isEmergency,
|
||||
),
|
||||
);
|
||||
// debugPrint('>>> [updateFunction] 状态已更新到emit');
|
||||
// _logger.logWithLevel('>>> [updateFunction] 状态已更新到emit');
|
||||
}
|
||||
|
||||
void updateOriginY(int y) {
|
||||
debugPrint('📥 [updateOriginY] 被调用 - y=$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');
|
||||
// debugPrint('📝 [updateOriginY] state已更新- originY=$y');
|
||||
}
|
||||
|
||||
|
||||
void updateOriginX(int x) {
|
||||
debugPrint('📥 [updateOriginX] 被调用 - x=$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');
|
||||
// debugPrint('📝 [updateOriginX] state已更新- originX=$x');
|
||||
}
|
||||
|
||||
|
||||
/// 🔥 安全方法:同时清零双轴,确保只发送一次完全停止指令
|
||||
Future<void> stopAllMovement() async {
|
||||
debugPrint('🛑 [stopAllMovement] 开始执行 - 当前成员变量: originX=$_currentOriginX, originY=$_currentOriginY');
|
||||
|
||||
// debugPrint(
|
||||
// '🛑 [stopAllMovement] 开始执行- 当前成员变量: originX=$_currentOriginX, originY=$_currentOriginY',
|
||||
// );
|
||||
|
||||
// 🔥 参考Android版:直接清零成员变量
|
||||
_currentOriginX = 0;
|
||||
_currentOriginY = 0;
|
||||
|
||||
|
||||
// 🔥 关键修复:先暂停定时器,防止定时器在停止期间发送旧的运动指令
|
||||
_timer?.cancel();
|
||||
_timer = null; // 🔥 彻底清空,防止重复启动
|
||||
|
||||
_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); // ✅ 等待所有指令发送完成
|
||||
|
||||
// debugPrint('🛑 [stopAllMovement] 双轴归零,发送停止指令- originX=0, originY=0');
|
||||
await _sendStopCommandRepeatedly(updatedEntity); // 等待所有指令发送完成
|
||||
|
||||
// 🔥 恢复定时器
|
||||
await Future.delayed(const Duration(milliseconds: 200));
|
||||
startControlLoop();
|
||||
|
||||
debugPrint('✅ [stopAllMovement] 执行完成');
|
||||
|
||||
// debugPrint('>>> [stopAllMovement] 执行完成');
|
||||
}
|
||||
|
||||
/// 🔥 统一方法:连续发送30次停止指令,彻底清空TCP缓冲区
|
||||
Future<void> _sendStopCommandRepeatedly(MachineControlStatusEntity stopEntity) async {
|
||||
debugPrint('🛑 [紧急停止] 开始连续发送30次停止指令 - originX=${stopEntity.originX}, originY=${stopEntity.originY}');
|
||||
// 🔥 注意:_lastStopTime 已经在 stopAllMovement() 中设置了,这里不需要再设置
|
||||
|
||||
|
||||
/// 🔥 统一方法:连续发送10次停止指令,彻底清空TCP缓冲区
|
||||
Future<void> _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}次');
|
||||
// debugPrint('🛑 [紧急停止] 已发送第${i + 1}次');
|
||||
}
|
||||
if (i < 19) {
|
||||
await Future.delayed(const Duration(milliseconds: 5));
|
||||
}
|
||||
}
|
||||
|
||||
// 🔥 延迟后再发送10次(双重保险,对抗网络抖动)
|
||||
|
||||
// 🔥 延迟后再发10次(双重保险,对抗网络抖动)
|
||||
await Future.delayed(const Duration(milliseconds: 100));
|
||||
for (int i = 0; i < 10; i++) {
|
||||
_repository.sendControlMachineCmd(stopEntity);
|
||||
@@ -371,8 +495,8 @@ class RemoteControlCubit extends Cubit<RemoteControlState> {
|
||||
await Future.delayed(const Duration(milliseconds: 5));
|
||||
}
|
||||
}
|
||||
|
||||
debugPrint('✅ [紧急停止] 所有30次停止指令已发出');
|
||||
|
||||
// debugPrint('>>> [紧急停止] 所有20次停止指令已发出');
|
||||
}
|
||||
|
||||
// 5. 停止控制循环
|
||||
@@ -396,7 +520,8 @@ class RemoteControlCubit extends Cubit<RemoteControlState> {
|
||||
}
|
||||
|
||||
void toggleLeftPip() => emit(state.copyWith(showLeftPip: !state.showLeftPip));
|
||||
void toggleRightPip() => emit(state.copyWith(showRightPip: !state.showRightPip));
|
||||
void toggleRightPip() =>
|
||||
emit(state.copyWith(showRightPip: !state.showRightPip));
|
||||
|
||||
@override
|
||||
Future<void> close() {
|
||||
@@ -408,34 +533,85 @@ class RemoteControlCubit extends Cubit<RemoteControlState> {
|
||||
void updateChassisLift(int i) {}
|
||||
|
||||
void updateEmergency(bool bool) {
|
||||
debugPrint('🚨 [急停] ${bool ? "触发急停!" : "解除急停"}');
|
||||
// debugPrint('🚨 [急停] ${bool ? "触发急停!" : "解除急停"}');
|
||||
updateFunction(emergency: bool);
|
||||
}
|
||||
|
||||
void respondPermission(bool bool, String deviceId) {
|
||||
void respondPermission(bool agreed, String deviceId) {
|
||||
|
||||
|
||||
|
||||
// 🔥 记录响应时间,开启冷却期
|
||||
_lastPermissionResponseTime = DateTime.now();
|
||||
//debugPrint('$logPrefix 开启权限请求冷却期,持续${_coolDownDuration.inSeconds}秒');
|
||||
|
||||
// 1. 关闭弹窗(立即关闭,防止重复点击)
|
||||
emit(state.copyWith(showPermissionRequestDialog: false));
|
||||
|
||||
// 2. 发送响应到服务端
|
||||
_repository.respondPermission(bool, deviceId);
|
||||
|
||||
// 3. 根据用户选择更新控制状态
|
||||
if (bool) {
|
||||
// 用户同意 → APP 失去控制权,Web 端获得控制权
|
||||
emit(state.copyWith(hasPermission: false));
|
||||
//路由到首页home
|
||||
|
||||
} else {
|
||||
// 用户拒绝 → APP 继续保持控制权
|
||||
emit(state.copyWith(hasPermission: true));
|
||||
// 2. 发送响应到服务器
|
||||
//debugPrint('$logPrefix 📤 发送权限响应命令到服务器');
|
||||
try {
|
||||
// 🔥 关键调用:发送 TCP 指令
|
||||
debugPrint('{_coolDownDuration.inSeconds}秒');
|
||||
_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()} ====权限弹窗响应结束=====',
|
||||
);
|
||||
}
|
||||
|
||||
void requestControlPermissionS(String deviceName, String deviceId) async {
|
||||
// 1. 关闭弹窗
|
||||
emit(state.copyWith(showPermissionRequestDialog: false));
|
||||
Future<void> 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,
|
||||
);
|
||||
|
||||
// 2. 调用 UseCase
|
||||
// 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,
|
||||
@@ -445,51 +621,158 @@ class RemoteControlCubit extends Cubit<RemoteControlState> {
|
||||
|
||||
// 3. 处理结果
|
||||
result.fold(
|
||||
(failure) {
|
||||
//print('❌ [RemoteControl] 请求控制权限失败:${failure.message}');
|
||||
_logger.logWithLevel('❌ [RemoteControl] 请求控制权限失败:${failure.message}');
|
||||
// 可以在这里显示错误提示或重新打开弹窗
|
||||
(failure) {
|
||||
final failLogPrefix =
|
||||
'${_getTimePrefix()} 🔑 [RemoteControl] [请求权限接口-$source]';
|
||||
debugPrint('$failLogPrefix APP请求控制权限失败: ${failure.message}');
|
||||
debugPrint('$failLogPrefix 重新打开权限请求弹窗');
|
||||
_logger.logWithLevel(
|
||||
'$failLogPrefix APP请求控制权限失败: ${failure.message}',
|
||||
shouldLog: true,
|
||||
);
|
||||
emit(state.copyWith(showPermissionRequestDialog: true));
|
||||
|
||||
},
|
||||
(success) {
|
||||
// 更新状态
|
||||
//print('✅ [RemoteControl] 请求控制权限成功:$success');
|
||||
_logger.logWithLevel('✅ [RemoteControl] 请求控制权限成功:$success',shouldLog: true);
|
||||
///处理result
|
||||
if(success){
|
||||
emit(state.copyWith(hasPermission: true));
|
||||
}else{
|
||||
emit(state.copyWith(hasPermission: false));
|
||||
}
|
||||
print('✅ c:$success');
|
||||
// 权限申请已发送,等待 0x12 回包更新状态
|
||||
(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] =========================================',
|
||||
);
|
||||
}
|
||||
/// 发送底盘指令
|
||||
|
||||
/// 🔥 权限弹窗确认后调用- 发送TCP响应 + HTTP确认最终权限状态
|
||||
Future<void> 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}');
|
||||
// // debugPrint('>>> [底盘指令] ${i}');
|
||||
// _logger.logWithLevel('>>> [底盘指令] ${i}');
|
||||
updateFunction(lift: i);
|
||||
}
|
||||
/// 发送割刀指令
|
||||
|
||||
/// 发送割刀指令
|
||||
void sendMowerCommand(int i) {
|
||||
// debugPrint(' [割刀指令] ${i}');
|
||||
_logger.logWithLevel(' [割刀指令] ${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(mower:0, lift: 0, ignition: i, emergency: false);
|
||||
// debugPrint('>>> [点火指令] ${i}');
|
||||
// _logger.logWithLevel('>>> [点火指令] ${i}');
|
||||
updateFunction(ignition: i);
|
||||
}
|
||||
// 发送障碍物识别指令
|
||||
|
||||
// 发送障碍物识别指令
|
||||
void toggleObstacleRecognition() {
|
||||
emit(state.copyWith(obstacleRecognitionFlag: !state.obstacleRecognitionFlag));
|
||||
emit(
|
||||
state.copyWith(obstacleRecognitionFlag: !state.obstacleRecognitionFlag),
|
||||
);
|
||||
}
|
||||
|
||||
void toggleTopLeftExpand() {
|
||||
@@ -498,14 +781,14 @@ class RemoteControlCubit extends Cubit<RemoteControlState> {
|
||||
|
||||
Future<int> getNetworkDelay() async {
|
||||
try {
|
||||
// 直接 Ping 你的服务器 IP
|
||||
// 直接 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 一样的毫秒值
|
||||
// 返回和cmd 一样的毫秒值
|
||||
return data.response!.time!.inMilliseconds;
|
||||
} else {
|
||||
return 9999;
|
||||
@@ -514,14 +797,38 @@ class RemoteControlCubit extends Cubit<RemoteControlState> {
|
||||
return 9999;
|
||||
}
|
||||
}
|
||||
//app退出远程遥控界面释放权限
|
||||
|
||||
//app退出远程遥控界面释放权限
|
||||
Future<bool> 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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user