#连接库初步完成

This commit is contained in:
CC
2026-01-27 18:44:49 +08:00
parent 8c39f0daf8
commit 949743972c
82 changed files with 1187 additions and 650 deletions

View File

@@ -1,47 +1,100 @@
import 'dart:async';
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/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 '../../domain/entities/machine_control_status_entity.dart';
import '../../domain/entities/machine_control_entity.dart';
import '../../domain/repositories/remote_control_repository.dart';
class RemoteControlCubit extends Cubit<RemoteControlState> {
final RemoteControlRepository _repository;
// 定时器与流订阅管理
Timer? _timer;
StreamSubscription<RunningStatusEntity>? _statusSubscription;
StreamSubscription<PacketEntity>? _responseSubscription;
RemoteControlCubit(this._repository)
: super(
RemoteControlState(
controlEntity: MachineControlStatusEntity(),
runningStatusModel: RunningStatusModel(),
),
) {
: super(
RemoteControlState(
controlEntity: MachineControlEntity(),
runningStatusEntity: RunningStatusEntity(),
),
) {
// 构造时立即开启监听
_initPacketListener();
_initRunningStatusListener();
}
// 1. 初始化回包监听 (如 0x12 权限)
// ==========================================
// 1. 数据监听逻辑 (Data Listening)
// ==========================================
/// 监听原始回包 (处理如 0x12 权限申请等指令)
void _initPacketListener() {
_repository.responseStream.listen((packet) {
if (packet.command == 0x12) {
// 根据负载判断是否有权限,更新状态
emit(state.copyWith(hasPermission: true));
_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" : "",
));
}
});
}
// 2. 开启 100ms 控制循环 (在进入遥控页面或点击“开始”时调用)
/// 监听运行状态回包 (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) {
// 核心调用:直接把 state 里的实体丢给 repository
_repository.sendControlMachineCmd(state.controlEntity);
_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));
}
// 3. 更新摇杆数据
/// 停止控制循环
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(),
@@ -50,53 +103,65 @@ class RemoteControlCubit extends Cubit<RemoteControlState> {
emit(state.copyWith(controlEntity: updatedEntity));
}
// 4. 更新功能开关 (比如割刀速度、灯光、点火等)
void updateFunction({int? mower, int? lift, int? ignition, bool? emergency}) {
final updatedEntity = state.controlEntity.copyWith(
mower: mower,
lift: lift,
ignition: ignition,
emergency: emergency,
);
/// 更新原点 Y 轴 (针对特定的 UI 逻辑)
void updateOriginY(int y) {
final updatedEntity = state.controlEntity.copyWith(y: y);
emit(state.copyWith(controlEntity: updatedEntity));
}
void updateOriginY(int y) {}
// 5. 停止控制循环
void stopControlLoop() {
_timer?.cancel();
_timer = null;
emit(state.copyWith(status: RemoteControlStatus.initial));
/// 更新底盘升降
void updateChassisLift(int level) {
final updatedEntity = state.controlEntity.copyWith(lift: level);
emit(state.copyWith(controlEntity: updatedEntity));
}
void toggleLock() {}
/// 更新急停状态
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 requestControlPermission() {
// 1. 关闭弹窗
emit(state.copyWith(showPermissionRequestDialog: false));
// 2. 这里执行你发送 0x12 指令的逻辑
// _sendProtocolData(0x12, ...);
/// 发起/响应权限请求
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 toggleRightPip() => emit(state.copyWith(showRightPip: !state.showRightPip));
// ==========================================
// 4. 销毁与清理 (Cleanup)
// ==========================================
@override
Future<void> close() {
_timer?.cancel(); // 退出页面时务必销毁定时器
_timer?.cancel();
_statusSubscription?.cancel();
_responseSubscription?.cancel();
return super.close();
}
void updateChassisLift(int i) {}
void updateEmergency(bool bool) {}
void respondPermission(bool bool) {}
}
}

View File

@@ -1,88 +1,85 @@
import 'package:equatable/equatable.dart';
import 'package:maibu_satabot_v2/features/remote_control/data/models/running_status_model.dart';
import '../../domain/entities/machine_control_status_entity.dart';
import '../../domain/entities/machine_control_entity.dart';
import '../../domain/entities/running_status_entity.dart';
enum RemoteControlStatus { initial, controlling, error }
enum ControlMode { TCP, BLE, LOCAL, NONE, OTHER }
class RemoteControlState extends Equatable {
// --- 核心业务状态 ---
final RemoteControlStatus status;
final MachineControlStatusEntity controlEntity; // 控制业务实体
final RunningStatusModel runningStatusModel;
//状态业务实体
final String? errorMessage;
final bool hasPermission; // 是否获得了 0x12 权限
final bool isEmergency;
final bool isLocked;
final int ping;
final int battery;
final int voltage;
final String permissionPlatform;
final String currentPlatform;
final bool showPermissionRequestDialog;
final bool showLeftPip;
final bool showRightPip;
final MachineControlEntity controlEntity; // 发送给机器的控制业务实体
final RunningStatusEntity runningStatusEntity; // 机器回传的状态业务实体
final String obstacleFlag; //障碍物标志位
// --- 权限与平台逻辑 ---
final bool hasPermission; // 0x12 权限状态
final String permissionPlatform; // 权限来源平台 (如 "Web", "App")
final String currentPlatform; // 当前操作平台
final bool showPermissionRequestDialog; // 是否显示权限请求弹窗
// --- 安全与锁定 ---
final bool isEmergency; // 紧急停止状态
final bool isLocked; // 控制锁定状态
final String obstacleFlag; // 障碍物标志位文字 (对应 Entity 里的 0/1)
// --- 网络与连接 ---
final int ping; // 延迟/心跳
final String? errorMessage; // 错误提示
// --- UI 视图配置 ---
final bool showLeftPip; // 左侧画中画显示
final bool showRightPip; // 右侧画中画显示
const RemoteControlState({
this.status = RemoteControlStatus.initial,
required this.controlEntity,
this.errorMessage,
required this.runningStatusEntity,
this.hasPermission = false,
this.isEmergency = false,
this.isLocked = false,
this.ping = 0,
this.battery = 0,
this.voltage = 0,
this.permissionPlatform = '',
this.currentPlatform = '',
this.showPermissionRequestDialog = false,
this.isEmergency = false,
this.isLocked = false,
this.obstacleFlag = '无',
this.ping = 0,
this.errorMessage,
this.showLeftPip = true,
this.showRightPip = true,
this.obstacleFlag = '',
required this.runningStatusModel,
});
// 方便 UI 更新部分属性
// 辅助属性:判断当前是否具备物理驾驶条件
bool get canDrive => hasPermission && !isEmergency && !isLocked;
RemoteControlState copyWith({
RemoteControlStatus? status,
MachineControlStatusEntity? controlEntity,
String? errorMessage,
MachineControlEntity? controlEntity,
RunningStatusEntity? runningStatusEntity,
bool? hasPermission,
bool? isEmergency,
bool? isLocked,
int? ping,
int? battery,
int? voltage,
String? permissionPlatform,
String? currentPlatform,
bool? showPermissionRequestDialog,
bool? isEmergency,
bool? isLocked,
String? obstacleFlag,
int? ping,
String? errorMessage,
bool? showLeftPip,
bool? showRightPip,
RunningStatusModel? runningStatusModel,
String? obstacleFlag,
}) {
return RemoteControlState(
status: status ?? this.status,
controlEntity: controlEntity ?? this.controlEntity,
errorMessage: errorMessage ?? this.errorMessage,
runningStatusEntity: runningStatusEntity ?? this.runningStatusEntity,
hasPermission: hasPermission ?? this.hasPermission,
isEmergency: isEmergency ?? this.isEmergency,
isLocked: isLocked ?? this.isLocked,
ping: ping ?? this.ping,
battery: battery ?? this.battery,
voltage: voltage ?? this.voltage,
permissionPlatform: permissionPlatform ?? this.permissionPlatform,
currentPlatform: currentPlatform ?? this.currentPlatform,
showPermissionRequestDialog:
showPermissionRequestDialog ?? this.showPermissionRequestDialog,
showPermissionRequestDialog: showPermissionRequestDialog ?? this.showPermissionRequestDialog,
isEmergency: isEmergency ?? this.isEmergency,
isLocked: isLocked ?? this.isLocked,
obstacleFlag: obstacleFlag ?? this.obstacleFlag,
ping: ping ?? this.ping,
errorMessage: errorMessage ?? this.errorMessage,
showLeftPip: showLeftPip ?? this.showLeftPip,
showRightPip: showRightPip ?? this.showRightPip,
runningStatusModel: runningStatusModel ?? this.runningStatusModel,
obstacleFlag: obstacleFlag ?? this.obstacleFlag,
);
}
@@ -90,19 +87,17 @@ class RemoteControlState extends Equatable {
List<Object?> get props => [
status,
controlEntity,
errorMessage,
runningStatusEntity,
hasPermission,
isEmergency,
isLocked,
ping,
battery,
voltage,
permissionPlatform,
currentPlatform,
showPermissionRequestDialog,
isEmergency,
isLocked,
obstacleFlag,
ping,
errorMessage,
showLeftPip,
showRightPip,
runningStatusModel,
obstacleFlag,
];
}
}

View File

@@ -4,7 +4,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
import 'package:maibu_satabot_v2/core/consts/tcp_consts.dart';
import '../../../../core/app/app_user_cubit.dart';
import '../../../auth/presentation/bloc/app_user_cubit.dart';
import '../../../devices/presentation/bloc/devices_cubit.dart';
import '../bloc/remote_control_cubit.dart';
import '../bloc/remote_control_state.dart';

View File

@@ -2,9 +2,9 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.dart';
import 'package:go_router/go_router.dart';
import 'package:maibu_satabot_v2/features/remote_control/presentation/bloc/remote_control_state.dart';
import 'package:maibu_satabot_v2/features/remote_control/presentation/widgets/status_chip.dart';
import '../../../../core/network/enums/control_mode.dart';
import '../../../devices/presentation/bloc/devices_cubit.dart';
import '../bloc/remote_control_cubit.dart';
@@ -60,12 +60,11 @@ class TopStatusBar extends StatelessWidget {
const Spacer(),
// TODO
_buildControlModeChip(,remoteState.runningStatusModel.controlMode),
_buildControlModeChip(remoteState.runningStatusEntity.controlMode),
const SizedBox(width: 8),
_buildVoltageChip(remoteState.voltage),
_buildVoltageChip(remoteState.runningStatusEntity.voltage),
const SizedBox(width: 8),
@@ -74,7 +73,7 @@ class TopStatusBar extends StatelessWidget {
const SizedBox(width: 8),
// 6. 电量 (对应 StatusChipRight)
_buildBatteryChip(remoteState.battery),
_buildBatteryChip(remoteState.runningStatusEntity.battery),
],
),
);
@@ -138,9 +137,7 @@ class TopStatusBar extends StatelessWidget {
case ControlMode.OTHER:
displayText = '其他模式';
break;
default:
displayText = '未知模式';
}
}
return StatusChip(
text: displayText,
@@ -150,7 +147,7 @@ class TopStatusBar extends StatelessWidget {
);
}
Widget _buildVoltageChip(int voltage) {
Widget _buildVoltageChip(double voltage) {
return StatusChip(
text: "$voltage V",
color: Color(0xFFFACC15).withValues(alpha: 0.6),
@@ -159,7 +156,7 @@ class TopStatusBar extends StatelessWidget {
);
}
Widget _buildBatteryChip(int level) {
Widget _buildBatteryChip(double level) {
// 1. 根据数值判断图片路径 (逻辑与之前一致)
String svgPath;
Color color;