重构远程遥控的实现逻辑链路,避免竟态的出现和乱序问题
This commit is contained in:
@@ -1,5 +1,3 @@
|
||||
import 'dart:ffi';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
@@ -32,41 +30,50 @@ class RemoteHttpDatasource {
|
||||
Future<bool> requestRemoteControlViaHttp( String deviceId, String currentDeviceId) async {
|
||||
final user = await _userStorage.getUser();
|
||||
if (user == null) {
|
||||
debugPrint('❌ [RemoteHttp] 用户未登录,无法请求远程控制权限');
|
||||
_logger.log('❌ [RemoteHttp] 用户未登录,无法请求远程控制权限');
|
||||
return false;
|
||||
}
|
||||
final token = user.token;
|
||||
final response = await _dio.post(
|
||||
'/forward/device/remoteControl',
|
||||
options: Options(
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer ${token}',
|
||||
},
|
||||
),
|
||||
data: {'deviceId': deviceId, 'platform': "app"});
|
||||
print("请求权限的数据,$deviceId,");
|
||||
|
||||
debugPrint('🔑 [RemoteHttp] 开始请求远程控制权限 - deviceId: $deviceId, platform: app');
|
||||
_logger.log('🔑 [RemoteHttp] 开始请求远程控制权限 - deviceId: $deviceId, platform: app');
|
||||
|
||||
try {
|
||||
final response = await _dio.post(
|
||||
'/forward/device/remoteControl',
|
||||
options: Options(
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer ${token}',
|
||||
},
|
||||
),
|
||||
data: {'deviceId': deviceId, 'platform': "app"});
|
||||
|
||||
debugPrint("📊 [RemoteHttp] HTTP响应状态码: ${response.statusCode}");
|
||||
_logger.log("📊 [RemoteHttp] HTTP响应状态码: ${response.statusCode}");
|
||||
|
||||
final responseData = response.data as Map<String, dynamic>;
|
||||
//debugPrint("📊 [HTTP 响应] 完整数据:$responseData");
|
||||
_logger.log("📊 [HTTP 响应] 完整数据:$responseData");
|
||||
debugPrint("📊 [RemoteHttp] 完整响应数据:$responseData");
|
||||
_logger.log("📊 [RemoteHttp] 完整响应数据:$responseData");
|
||||
|
||||
// 🔥 关键:先获取响应的 data 字段,再获取 remoteControl
|
||||
final dataField = responseData['data'] as Map<String, dynamic>?;
|
||||
if (dataField != null) {
|
||||
final bool hasRemoteControl = dataField['remoteControl'] as bool? ?? false;
|
||||
|
||||
//debugPrint("✅ [HTTP 响应] remoteControl=$hasRemoteControl");
|
||||
_logger.log("✅ [HTTP 响应] remoteControl=$hasRemoteControl");
|
||||
debugPrint("✅ [RemoteHttp] 解析成功 - remoteControl=$hasRemoteControl");
|
||||
_logger.log("✅ [RemoteHttp] 解析成功 - remoteControl=$hasRemoteControl");
|
||||
// 🔥 直接返回 remoteControl 的布尔值
|
||||
return hasRemoteControl;
|
||||
} else {
|
||||
//debugPrint("❌ [HTTP 响应] 缺少 data 字段");
|
||||
_logger.log("❌ [HTTP 响应] 缺少 data 字段");
|
||||
debugPrint("❌ [RemoteHttp] 缺少 data 字段,响应结构异常");
|
||||
_logger.log("❌ [RemoteHttp] 缺少 data 字段,响应结构异常");
|
||||
return false;
|
||||
}
|
||||
} catch (e) {
|
||||
// debugPrint('❌ [RemoteHttp] 解析响应失败:$e');
|
||||
_logger.log('❌ [RemoteHttp] 解析响应失败:$e');
|
||||
debugPrint('❌ [RemoteHttp] 请求远程控制权限失败:$e');
|
||||
_logger.log('❌ [RemoteHttp] 请求远程控制权限失败:$e');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:ffi';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import 'dart:ffi';
|
||||
|
||||
import '../../../../core/network/protocol_decoder.dart';
|
||||
import '../../domain/entities/machine_control_status_entity.dart';
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:ffi';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:dart_ping/dart_ping.dart';
|
||||
@@ -28,8 +27,10 @@ class RemoteControlCubit extends Cubit<RemoteControlState> {
|
||||
static const platform = MethodChannel('com.maibu.satabot/ping');
|
||||
int _currentPing = 50;
|
||||
final ILoggerService _logger = GetIt.I<ILoggerService>();
|
||||
DateTime? _lastStopTime; // 🔥 记录最后一次停止时间
|
||||
Timer? _clearStopTimeTimer; // 🔥 用于管理 _lastStopTime 的清除
|
||||
|
||||
// 🔥 参考Android版:使用成员变量存储摇杆值,避免state竞态
|
||||
int _currentOriginX = 0;
|
||||
int _currentOriginY = 0;
|
||||
|
||||
|
||||
|
||||
@@ -255,12 +256,19 @@ class RemoteControlCubit extends Cubit<RemoteControlState> {
|
||||
return;
|
||||
}
|
||||
|
||||
// ✅ 必须通过方法拿最新 state,不能直接读!
|
||||
final entity = _getCurrentControlEntity();
|
||||
// 🔥 参考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('⚠️ [定时器] 无权限,跳过发送 - hasPermission=${state.hasPermission}, isEmergency=${state.isEmergency}');
|
||||
debugPrint('⚠️ [定时器] 无权限,跳过发送');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -269,25 +277,17 @@ class RemoteControlCubit extends Cubit<RemoteControlState> {
|
||||
return;
|
||||
}
|
||||
|
||||
// 🔥 新增:如果刚停止5000ms内,暂停所有发送(包括零值)- 与_lastStopTime清除时间一致
|
||||
if (_lastStopTime != null) {
|
||||
final elapsed = DateTime.now().difference(_lastStopTime!).inMilliseconds;
|
||||
if (elapsed < 5000) {
|
||||
debugPrint('⚠️ [定时器] 停止保护期内,暂停所有发送 - 已过${elapsed}ms');
|
||||
return; // 保护期内不发送任何指令,让_sendStopCommandRepeatedly独占通道
|
||||
}
|
||||
// 🔥 关键修复:不在这里清空 _lastStopTime,让它继续生效以拦截 updateOriginX/Y
|
||||
}
|
||||
|
||||
_logger.logWithLevel('[定时器] 发送控制指令 - originX=${entity.originX}, originY=${entity.originY}');
|
||||
debugPrint('📤 [定时器] 准备发送 - originX=${entity.originX}, originY=${entity.originY}, hasPermission=${state.hasPermission}, _lastStopTime=$_lastStopTime');
|
||||
_repository.sendControlMachineCmd(entity);
|
||||
_logger.logWithLevel('[定时器] 发送控制指令 - originX=${snapshot.originX}, originY=${snapshot.originY}');
|
||||
debugPrint('📤 [定时器] 准备发送 - originX=${snapshot.originX}, originY=${snapshot.originY}');
|
||||
_repository.sendControlMachineCmd(snapshot);
|
||||
});
|
||||
}
|
||||
//通过方法拿最新 state
|
||||
MachineControlStatusEntity _getCurrentControlEntity() {
|
||||
_logger.logWithLevel('真实的发送的实体 - originX: ${state.controlEntity.originX}, originY: ${state.controlEntity.originY}');
|
||||
return state.controlEntity;
|
||||
// 🔥 关键修复:必须copyWith创建新对象,避免引用竞态条件
|
||||
final entity = state.controlEntity;
|
||||
_logger.logWithLevel('真实的发送的实体 - originX: ${entity.originX}, originY: ${entity.originY}');
|
||||
return entity.copyWith(); // 返回副本,不是引用
|
||||
}
|
||||
|
||||
// 3. 更新摇杆数据
|
||||
@@ -319,38 +319,24 @@ class RemoteControlCubit extends Cubit<RemoteControlState> {
|
||||
}
|
||||
|
||||
void updateOriginY(int y) {
|
||||
debugPrint('📥 [updateOriginY] 被调用 - y=$y, _lastStopTime=$_lastStopTime');
|
||||
// 🔥 关键修复:如果刚停止5000ms内,拒绝非零值的更新 - 与定时器保护期一致
|
||||
if (_lastStopTime != null) {
|
||||
final elapsed = DateTime.now().difference(_lastStopTime!).inMilliseconds;
|
||||
debugPrint('📥 [updateOriginY] 检查保护期 - elapsed=${elapsed}ms, y=$y');
|
||||
if (elapsed < 5000 && y != 0) {
|
||||
debugPrint('⚠️ [updateOriginY] 保护期内拒绝非零值 - y=$y, 已过${elapsed}ms');
|
||||
return; // 拒绝更新
|
||||
}
|
||||
} else {
|
||||
debugPrint('📥 [updateOriginY] _lastStopTime为null,跳过保护期检查');
|
||||
}
|
||||
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, _lastStopTime=$_lastStopTime');
|
||||
// 🔥 关键修复:如果刚停止5000ms内,拒绝非零值的更新 - 与定时器保护期一致
|
||||
if (_lastStopTime != null) {
|
||||
final elapsed = DateTime.now().difference(_lastStopTime!).inMilliseconds;
|
||||
debugPrint('📥 [updateOriginX] 检查保护期 - elapsed=${elapsed}ms, x=$x');
|
||||
if (elapsed < 5000 && x != 0) {
|
||||
debugPrint('⚠️ [updateOriginX] 保护期内拒绝非零值 - x=$x, 已过${elapsed}ms');
|
||||
return; // 拒绝更新
|
||||
}
|
||||
} else {
|
||||
debugPrint('📥 [updateOriginX] _lastStopTime为null,跳过保护期检查');
|
||||
}
|
||||
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');
|
||||
@@ -358,16 +344,13 @@ class RemoteControlCubit extends Cubit<RemoteControlState> {
|
||||
|
||||
/// 🔥 安全方法:同时清零双轴,确保只发送一次完全停止指令
|
||||
Future<void> stopAllMovement() async {
|
||||
debugPrint('🛑 [stopAllMovement] 开始执行 - 当前state: originX=${state.controlEntity.originX}, originY=${state.controlEntity.originY}');
|
||||
debugPrint('🛑 [stopAllMovement] 开始执行 - 当前成员变量: originX=$_currentOriginX, originY=$_currentOriginY');
|
||||
|
||||
// 🔥 关键修复1:取消旧的清除定时器,防止多个定时器竞争
|
||||
_clearStopTimeTimer?.cancel();
|
||||
// 🔥 参考Android版:直接清零成员变量
|
||||
_currentOriginX = 0;
|
||||
_currentOriginY = 0;
|
||||
|
||||
// 🔥 关键修复2:立即设置 _lastStopTime,防止在 stopAllMovement 执行期间 state 被更新
|
||||
_lastStopTime = DateTime.now();
|
||||
debugPrint('🛑 [stopAllMovement] _lastStopTime 已设置为 $_lastStopTime');
|
||||
|
||||
// 🔥 关键修复3:先暂停定时器,防止定时器在停止期间发送旧的运动指令
|
||||
// 🔥 关键修复:先暂停定时器,防止定时器在停止期间发送旧的运动指令
|
||||
_timer?.cancel();
|
||||
_timer = null; // 🔥 彻底清空,防止重复启动
|
||||
|
||||
@@ -380,16 +363,10 @@ class RemoteControlCubit extends Cubit<RemoteControlState> {
|
||||
debugPrint('🛑 [stopAllMovement] 双轴归零,发送停止指令 - originX=0, originY=0');
|
||||
await _sendStopCommandRepeatedly(updatedEntity); // ✅ 等待所有指令发送完成
|
||||
|
||||
// 🔥 恢复定时器(但保护期内不会发送任何指令)
|
||||
await Future.delayed(const Duration(milliseconds: 500)); // 🔥 等待500ms让下位机执行停止
|
||||
// 🔥 恢复定时器
|
||||
await Future.delayed(const Duration(milliseconds: 200));
|
||||
startControlLoop();
|
||||
|
||||
// 🔥 关键修复4:使用 Timer 而不是 Future.delayed,确保只有一个定时器在运行
|
||||
_clearStopTimeTimer = Timer(const Duration(seconds: 5), () {
|
||||
_lastStopTime = null;
|
||||
debugPrint('✅ [保护期] 5秒后自动清除标记');
|
||||
});
|
||||
|
||||
debugPrint('✅ [stopAllMovement] 执行完成');
|
||||
}
|
||||
|
||||
@@ -450,7 +427,6 @@ class RemoteControlCubit extends Cubit<RemoteControlState> {
|
||||
@override
|
||||
Future<void> close() {
|
||||
_timer?.cancel(); // 退出页面时务必销毁定时器
|
||||
_clearStopTimeTimer?.cancel(); // 🔥 清除保护期定时器
|
||||
_kickOutSub?.cancel();
|
||||
_stringMessageSub?.cancel();
|
||||
return super.close();
|
||||
|
||||
Reference in New Issue
Block a user