Files
flutterApp/lib/features/remote_control/presentation/bloc/remote_control_cubit.dart

343 lines
12 KiB
Dart
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'dart:async';
import 'dart:convert';
import 'dart:ffi';
import 'package:flutter/cupertino.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:maibu_satabot_v2/features/remote_control/data/models/running_status_model.dart';
import 'package:maibu_satabot_v2/features/remote_control/presentation/bloc/remote_control_state.dart';
import '../../../../core/network/net_message_dispatcher.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<RemoteControlState> {
final RemoteControlRepository _repository;
final RequestControlPermissionUseCase _requestControlPermissionUseCase;
Timer? _timer;
StreamSubscription? _kickOutSub; // 新增:用于管理监听生命周期
final NetMessageDispatcher dispatcher;
StreamSubscription? _deviceStatusSub;//🔥 新增:用于监听 DeviceStat
RemoteControlCubit(this._repository, this._requestControlPermissionUseCase, this.dispatcher)
: super(
RemoteControlState(
controlEntity: MachineControlStatusEntity(),
runningStatusModel: RunningStatusModel(),
),
) {
_initPacketListener();
}
// 🔥 新增:监听 DeviceStatusBloc 的状态更新
void subscribeToDeviceStatus(Stream deviceStatusStream) {
_deviceStatusSub?.cancel();
_deviceStatusSub = deviceStatusStream.listen((deviceState) {
try {
// 🔥 关键:从 DeviceStatusBloc 的 state 中提取数据
// 这里假设 deviceState 是 DevicesCubit 的 state
// 你需要根据实际的 DevicesCubit state 类型来调整
debugPrint('📡 [收到设备状态更新] deviceState: $deviceState');
// 如果 DevicesCubit 的 state 中有 runningStatusModel 或类似字段
// 方式 1:直接同步整个 RunningStatusModel
if (deviceState is Map && deviceState.containsKey('runningStatusModel')) {
final newStatus = deviceState['runningStatusModel'] as RunningStatusModel;
_updateStatusFromDevice(newStatus);
}
// 方式 2:如果 DevicesCubit 直接暴露 voltage, battery 等字段
else if (deviceState is Map) {
final voltage = deviceState['voltage'] as int? ?? 0;
final battery = deviceState['battery'] as int? ?? 0;
emit(state.copyWith(
voltage: voltage,
battery: battery,
));
debugPrint('📡 [同步设备状态] 电压:${voltage}V, 电量:${battery}%');
}
} catch (e) {
debugPrint('❌ [同步设备状态失败] $e');
}
});
debugPrint('✅ [DeviceStatus] 已建立监听');
}
// 🔥 辅助方法:更新运行状态
void _updateStatusFromDevice(RunningStatusModel newStatus) {
if (!isClosed) {
emit(state.copyWith(
runningStatusModel: newStatus,
voltage:int.tryParse(newStatus.voltage) ?? 0,
battery: int.tryParse(newStatus.battery) ?? 0,
// 如果需要同步其他字段,在这里添加
// controlMode: newStatus.controlMode,
));
debugPrint('✅ [更新运行状态] 电压:${newStatus.voltage}V, 电量:${newStatus.battery}%');
}
}
// 1. 初始化回包监听 (如 0x12 权限)
// void _initPacketListener() {
// _repository.responseStream.listen((packet) {
// if (packet.command == 0x12) {
// // 根据负载判断是否有权限,更新状态
// emit(state.copyWith(hasPermission: true));
// }
// });
// }
void _initPacketListener() {
print('>>> [RemoteControl] begin 初始化 0x12 监听器');
_kickOutSub?.cancel(); // 防止重复监听
_kickOutSub = dispatcher.onCommand(0x12).listen((packet) {
print('>>> [RemoteControl] 收到 0x12 原始包,payload 长度=${packet.payload.length}');
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);
}
print('>>> [RemoteControl] 去除 CRC 后的 JSON: $jsonString');
final jsonMap = jsonDecode(jsonString);
// 🔥 区分两种数据格式
final requestType = jsonMap['request'];
final platform = jsonMap['platform'];
final respondData = jsonMap['respond'];
print('>>> [RemoteControl] 📋 requestType=$requestType, platform=$platform');
// 情况 1: 响应格式 - {"respond":{"switchResult":true,"deviceId":"...","holder":"you"}}
if (respondData != null && respondData is Map) {
final switchResult = respondData['switchResult'];
print('>>> [RemoteControl] 📊 收到切换结果响应:switchResult = $switchResult');
if (!isClosed) {
if (switchResult == true) {
// 切换成功,当前 APP 失去控制权
emit(state.copyWith(hasPermission: true, showPermissionRequestDialog: false));
print('>>> [RemoteControl] ✅ 权限已切');
} else {
// 切换失败或拒绝,保持当前状态
emit(state.copyWith(showPermissionRequestDialog: false));
print('>>> [RemoteControl] ❌ 权限切换失败/被拒绝');
}
}
}
// 情况 2: 请求格式 - {"request":"switch_control","deviceId":"...","platform":"web",...}
else if (requestType == 'switch_control') {
// 🔥 关键判断:只有当是其他平台(web)请求时才弹窗
if (platform != null && platform.toString().toLowerCase() != 'app') {
print('>>> [RemoteControl] 🚨 $platform 端请求控制权,打开弹窗询问用户');
if (!isClosed) {
emit(state.copyWith(showPermissionRequestDialog: true));
}
} else {
print('>>> [RemoteControl] ℹ️ APP 自己的请求回显,忽略不弹窗');
}
}
// 情况 3: 异地登录通知 - {"request":"have_logged_in",...}
else if (requestType == 'have_logged_in') {
print('>>> [RemoteControl] ⚠️ 检测到异地登录,打开弹窗提示');
if (!isClosed) {
emit(state.copyWith(showPermissionRequestDialog: true));
}
}
else {
print('>>> [RemoteControl] ℹ️ 未知类型的 0x12 包,忽略');
}
} catch (e) {
print('>>> [RemoteControl] ❌ 解析失败:$e');
}
});
print('>>> [RemoteControl] ✅ 0x12 监听器已建立完成');
}
// 2. 开启 100ms 控制循环 (在进入遥控页面或点击“开始”时调用)
void startControlLoop() {
_timer?.cancel();
_timer = Timer.periodic(const Duration(milliseconds: 100), (timer) {
// 核心调用:直接把 state 里的实体丢给 repository
_repository.sendControlMachineCmd(state.controlEntity);
});
emit(state.copyWith(status: RemoteControlStatus.controlling));
}
// 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');
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');
}
void updateOriginY(int y) {
final updatedEntity = state.controlEntity.copyWith(
y: y,
);
emit(state.copyWith(controlEntity: updatedEntity));
}
void updateOriginX(int x) {
final updatedEntity = state.controlEntity.copyWith(
x: x,
);
emit(state.copyWith(controlEntity: updatedEntity));
}
// 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<void> close() {
_timer?.cancel(); // 退出页面时务必销毁定时器
_deviceStatusSub?.cancel(); // 🔥 确保清理订阅
return super.close();
}
void updateChassisLift(int i) {}
void updateEmergency(bool bool) {
debugPrint('🚨 [急停] ${bool ? "触发急停!" : "解除急停"}');
updateFunction(emergency: bool);
}
void respondPermission(bool bool, String deviceId) {
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));
}
}
void requestControlPermissionS(String deviceName, String deviceId) async {
// 1. 关闭弹窗
emit(state.copyWith(showPermissionRequestDialog: false));
// 2. 调用 UseCase
final result = await _requestControlPermissionUseCase(
RequestControlPermissionParams(
deviceName: deviceName,
deviceId: deviceId,
),
);
// 3. 处理结果
result.fold(
(failure) {
print('❌ [RemoteControl] 请求控制权限失败:${failure.message}');
// 可以在这里显示错误提示或重新打开弹窗
emit(state.copyWith(showPermissionRequestDialog: true));
},
(success) {
// 更新状态
print('✅ [RemoteControl] 请求控制权限成功:$success');
///处理result
if(success){
emit(state.copyWith(hasPermission: true));
}else{
emit(state.copyWith(hasPermission: false));
}
print('✅ c:$success');
// 权限申请已发送,等待 0x12 回包更新状态
},
);
}
/// 发送底盘指令
void sendChassisCommand(int i) {
debugPrint(' [底盘指令] ${i}');
updateFunction(lift: i);
}
/// 发送割刀指令
void sendMowerCommand(int i) {
debugPrint(' [割刀指令] ${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}');
updateFunction(ignition: i);
}
// 发送障碍物识别指令
void toggleObstacleRecognition() {
emit(state.copyWith(obstacleRecognitionFlag: !state.obstacleRecognitionFlag));
}
void toggleTopLeftExpand() {
emit(state.copyWith(topRightIsExpanded: !state.topRightIsExpanded));
}
}