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

279 lines
9.3 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;
RemoteControlCubit(this._repository, this._requestControlPermissionUseCase, this.dispatcher)
: super(
RemoteControlState(
controlEntity: MachineControlStatusEntity(),
runningStatusModel: RunningStatusModel(),
),
) {
_initPacketListener();
}
// 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() {}
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(); // 退出页面时务必销毁定时器
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);
}
}