198 lines
6.3 KiB
Dart
198 lines
6.3 KiB
Dart
import 'dart:async';
|
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
import 'package:maibu_satabot_v2/core/logging/i_logger_service.dart';
|
|
import 'package:maibu_satabot_v2/core/protocol/packet_entity.dart';
|
|
import 'package:maibu_satabot_v2/features/remote_control/domain/entities/running_status_entity.dart';
|
|
import 'package:maibu_satabot_v2/features/remote_control/presentation/bloc/remote_control_state.dart';
|
|
import '../../../../core/di/injection.dart';
|
|
import '../../../connectivity/presentation/bloc/connectivity_cubit.dart';
|
|
import '../../domain/entities/machine_control_entity.dart';
|
|
import '../../domain/repositories/remote_control_repository.dart';
|
|
|
|
class RemoteControlCubit extends Cubit<RemoteControlState> {
|
|
final RemoteControlRepository _repository;
|
|
final ConnectivityCubit connectivityCubit;
|
|
|
|
// 定时器与流订阅管理
|
|
Timer? _timer;
|
|
StreamSubscription<RunningStatusEntity>? _statusSubscription;
|
|
StreamSubscription<PacketEntity>? _responseSubscription;
|
|
|
|
RemoteControlCubit(this._repository,this.connectivityCubit)
|
|
: super(
|
|
RemoteControlState(
|
|
controlEntity: MachineControlEntity(),
|
|
runningStatusEntity: RunningStatusEntity(),
|
|
),
|
|
) {
|
|
// 构造时立即开启监听
|
|
_initPacketListener();
|
|
_initRunningStatusListener();
|
|
}
|
|
|
|
// ==========================================
|
|
// 1. 数据监听逻辑 (Data Listening)
|
|
// ==========================================
|
|
|
|
/// 监听原始回包 (处理如 0x12 权限申请等指令)
|
|
void _initPacketListener() {
|
|
_responseSubscription?.cancel();
|
|
_responseSubscription = _repository.responseStream.listen((packet) {
|
|
// 处理权限申请回包 (假设 0x12)
|
|
if (packet.cmdType == 0x12) {
|
|
// 假设 payload[0] == 1 表示获得权限,这里可以解析出平台信息
|
|
final bool granted = packet.payload.isNotEmpty && packet.payload[0] == 0x01;
|
|
emit(state.copyWith(
|
|
hasPermission: granted,
|
|
// 假设协议中后续字节带有平台名称
|
|
permissionPlatform: granted ? "Other Device" : "",
|
|
));
|
|
}
|
|
});
|
|
}
|
|
|
|
/// 监听运行状态回包 (0x02) 并更新 UI
|
|
void _initRunningStatusListener() {
|
|
_statusSubscription?.cancel();
|
|
_statusSubscription = _repository.runningStatusStream.listen((entity) {
|
|
// 同步硬件状态到 Bloc State
|
|
emit(state.copyWith(
|
|
runningStatusEntity: entity,
|
|
obstacleFlag: entity.obstacleFlag == 1 ? "检测到障碍物" : "无",
|
|
// 如果硬件上报了急停或锁定,这里也可以同步更新状态
|
|
isEmergency: entity.obstacleFlag == 1,
|
|
));
|
|
}, onError: (e) {
|
|
emit(state.copyWith(errorMessage: "运行状态流监听异常: $e"));
|
|
});
|
|
}
|
|
|
|
// ==========================================
|
|
// 2. 控制循环逻辑 (Control Loop)
|
|
// ==========================================
|
|
|
|
/// 启动 100ms 高频控制循环
|
|
void startControlLoop() {
|
|
_timer?.cancel();
|
|
_timer = Timer.periodic(const Duration(milliseconds: 100), (timer) async {
|
|
// 核心:在具备驾驶条件(有权限、未锁定、非急停)时发送指令
|
|
if (state.canDrive) {
|
|
try {
|
|
await _repository.sendControlMachineCmd(state.controlEntity);
|
|
} catch (e) {
|
|
print("发送控制指令失败: $e");
|
|
}
|
|
}
|
|
});
|
|
emit(state.copyWith(status: RemoteControlStatus.controlling));
|
|
}
|
|
|
|
/// 停止控制循环
|
|
void stopControlLoop() {
|
|
_timer?.cancel();
|
|
_timer = null;
|
|
emit(state.copyWith(status: RemoteControlStatus.initial));
|
|
}
|
|
|
|
// ==========================================
|
|
// 3. UI 交互方法 (UI Actions)
|
|
// ==========================================
|
|
|
|
/// 更新摇杆坐标 (x, y 为 -100 到 100)
|
|
void updateJoystick(double x, double y) {
|
|
final updatedEntity = state.controlEntity.copyWith(
|
|
x: x.toInt(),
|
|
y: y.toInt(),
|
|
);
|
|
emit(state.copyWith(controlEntity: updatedEntity));
|
|
}
|
|
|
|
/// 更新原点 Y 轴 (针对特定的 UI 逻辑)
|
|
void updateOriginY(int y) {
|
|
final updatedEntity = state.controlEntity.copyWith(y: y);
|
|
emit(state.copyWith(controlEntity: updatedEntity));
|
|
}
|
|
|
|
/// 更新底盘升降
|
|
void updateChassisLift(int level) {
|
|
final updatedEntity = state.controlEntity.copyWith(lift: level);
|
|
emit(state.copyWith(controlEntity: updatedEntity));
|
|
}
|
|
|
|
/// 更新急停状态
|
|
void updateEmergency(bool emergency) {
|
|
final updatedEntity = state.controlEntity.copyWith(emergency: emergency);
|
|
emit(state.copyWith(
|
|
controlEntity: updatedEntity,
|
|
isEmergency: emergency,
|
|
));
|
|
}
|
|
|
|
/// 更新割刀/功能开关
|
|
void updateMower(int speed) {
|
|
final updatedEntity = state.controlEntity.copyWith(mower: speed);
|
|
emit(state.copyWith(controlEntity: updatedEntity));
|
|
}
|
|
|
|
/// 切换控制锁定
|
|
void toggleLock() {
|
|
emit(state.copyWith(isLocked: !state.isLocked));
|
|
}
|
|
|
|
/// 权限请求弹窗控制
|
|
void togglePermissionDialog(bool show) {
|
|
emit(state.copyWith(showPermissionRequestDialog: show));
|
|
}
|
|
|
|
/// 发起/响应权限请求
|
|
void respondPermission(bool accept) {
|
|
// 这里可以添加发送 0x12 响应包给硬件的逻辑
|
|
emit(state.copyWith(
|
|
hasPermission: accept,
|
|
showPermissionRequestDialog: false,
|
|
));
|
|
}
|
|
|
|
/// 切换画中画显示
|
|
void toggleLeftPip() => emit(state.copyWith(showLeftPip: !state.showLeftPip));
|
|
void toggleRightPip() => emit(state.copyWith(showRightPip: !state.showRightPip));
|
|
|
|
void toggleTopLeftExpand() {
|
|
emit(state.copyWith(topRightIsExpanded: !state.topRightIsExpanded));
|
|
}
|
|
|
|
void toggleObstacleRecognition() {
|
|
emit(state.copyWith(obstacleRecognitionFlag: !state.obstacleRecognitionFlag));
|
|
}
|
|
|
|
void initVideoUrls(String main, String ai) {
|
|
emit(state.copyWith(
|
|
mainStreamUrl: main,
|
|
aiStreamUrl: ai,
|
|
isVideoEnabled: true,
|
|
));
|
|
}
|
|
|
|
void disposeVideo() {
|
|
emit(state.copyWith(
|
|
mainStreamUrl: "",
|
|
aiStreamUrl: "",
|
|
isVideoEnabled: false,
|
|
obstacleRecognitionFlag: false,
|
|
));
|
|
sl<ILoggerService>().log("Cubit: 视频地址已置空,准备断开连接");
|
|
}
|
|
|
|
// ==========================================
|
|
// 4. 销毁与清理 (Cleanup)
|
|
// ==========================================
|
|
|
|
@override
|
|
Future<void> close() {
|
|
_timer?.cancel();
|
|
_statusSubscription?.cancel();
|
|
_responseSubscription?.cancel();
|
|
return super.close();
|
|
}
|
|
|
|
} |