重构远程遥控的实现逻辑链路,避免竟态的出现和乱序问题

This commit is contained in:
2026-04-17 13:36:54 +08:00
parent 4ee48a115e
commit 06c54ef546
4 changed files with 66 additions and 86 deletions

View File

@@ -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;
}
}

View File

@@ -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';

View File

@@ -1,5 +1,3 @@
import 'dart:ffi';
import '../../../../core/network/protocol_decoder.dart';
import '../../domain/entities/machine_control_status_entity.dart';

View File

@@ -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();