Files
flutterApp/lib/features/devices/presentation/bloc/devices_cubit.dart

305 lines
12 KiB
Dart
Raw Normal View History

import 'dart:collection';
import 'package:flutter/rendering.dart';
2026-01-18 20:21:14 +08:00
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:maibu_satabot_v2/features/devices/domain/entities/device_entity.dart';
import 'package:maibu_satabot_v2/features/devices/domain/repositories/device_repository.dart';
2026-02-28 12:46:29 +08:00
import 'package:maibu_satabot_v2/features/devices/domain/usecases/generate_path_usecase.dart';
2026-01-18 20:21:14 +08:00
import 'package:maibu_satabot_v2/features/devices/domain/usecases/get_user_device_usecase.dart';
2026-02-28 09:45:53 +08:00
import 'package:maibu_satabot_v2/features/devices/domain/usecases/save_work_record_usecase.dart';
import 'package:maibu_satabot_v2/features/devices/domain/usecases/select_work_record_usecase.dart';
import 'package:maibu_satabot_v2/features/devices/domain/usecases/unbind_device_usecase.dart';
2026-02-27 21:04:07 +08:00
import 'package:maibu_satabot_v2/features/devices/domain/usecases/update_devicename_usecase.dart';
2026-01-18 20:21:14 +08:00
import '../../data/models/device_add_path_point_model.dart';
2026-02-28 12:46:29 +08:00
import '../../data/models/device_work_area_param_model.dart';
import '../../domain/usecases/delete_work_record_usecase.dart';
import '../../domain/usecases/get_device_location_usecase.dart';
import '../../domain/usecases/get_work_record_usecase.dart';
import '../../domain/usecases/route_planning_usecase.dart';
2026-01-18 20:21:14 +08:00
import 'devices_state.dart';
class DevicesCubit extends Cubit<DevicesState> {
final GetUserDeviceUseCase _getUserDeviceUseCase;
final GetDeviceLocationUseCase _getDeviceLocationUseCase;
2026-01-18 20:21:14 +08:00
final DeviceRepository repository;
final GetWorkRecordUseCase _getWorkRecordUseCase;
final DeleteWorkRecordUseCase _deleteWorkRecordUseCase;
final UnbindDeviceUseCase _unbindDeviceUseCase;
2026-02-27 21:04:07 +08:00
final UpdateDevicenameUsecase _updateDevicename;
final SelectWorkRecordUseCase _selectWorkRecordUseCase;
2026-02-28 09:45:53 +08:00
final SaveWorkRecordUseCase _saveWorkRecordUseCase;
2026-02-28 12:46:29 +08:00
final GeneratePathUseCase _generatePathUseCase;
final RoutePlanningUseCase _routePlanningUseCase; //
2026-01-18 20:21:14 +08:00
DevicesCubit(
this.repository,
this._getUserDeviceUseCase,
this._getDeviceLocationUseCase,
this._getWorkRecordUseCase,
this._deleteWorkRecordUseCase,
this._unbindDeviceUseCase,
2026-02-27 21:04:07 +08:00
this._updateDevicename,
2026-02-28 09:55:14 +08:00
this._selectWorkRecordUseCase,
2026-02-28 09:45:53 +08:00
this._saveWorkRecordUseCase,
2026-03-02 13:25:21 +08:00
this._generatePathUseCase,
this._routePlanningUseCase
) : super(const DevicesState());
Future<void> unbindDevice(String deviceId, String deviceName) async {
2026-03-02 13:25:21 +08:00
emit(state.copyWith(isLoading: true, errorMessage: '', operationType: DeviceOperationType.unbind));
try {
final params = UnbindDeviceParams(deviceId, deviceName);
final result = await _unbindDeviceUseCase.call(params);
result.fold(
(failure) => emit(
state.copyWith(
isLoading: false,
errorMessage: failure.message ?? '解绑设备失败',
2026-02-27 21:04:07 +08:00
operationType: DeviceOperationType.none, // 操作结束重置
),
),
(successCode) {
2026-02-27 14:16:29 +08:00
// 核心修复:int 转 bool 条件判断
final isSuccess = successCode == 1; // 显式转为 bool
if (isSuccess) {
final updatedDevices = state.devices?.where((device) {
return device.deviceName != deviceId;
}).toList();
2026-02-27 14:16:29 +08:00
emit(
state.copyWith(
isLoading: false,
devices: updatedDevices,
2026-03-02 13:25:21 +08:00
selectedDevice: state.selectedDevice?.deviceName == deviceId ? null : state.selectedDevice,
2026-02-27 14:16:29 +08:00
errorMessage: '',
2026-02-27 21:04:07 +08:00
operationType: DeviceOperationType.none,
2026-02-27 14:16:29 +08:00
),
);
} else {
2026-03-02 13:25:21 +08:00
emit(state.copyWith(isLoading: false, errorMessage: '解绑失败:状态码 $successCode', operationType: DeviceOperationType.none));
2026-02-27 14:16:29 +08:00
}
},
);
} catch (e) {
2026-03-02 13:25:21 +08:00
emit(state.copyWith(isLoading: false, errorMessage: '解绑异常:${e.toString()}', operationType: DeviceOperationType.none));
2026-02-27 21:04:07 +08:00
}
}
Future<void> updateDeviceName(String deviceId, String deviceName) async {
2026-03-02 13:25:21 +08:00
emit(state.copyWith(isLoading: true, errorMessage: '', operationType: DeviceOperationType.updateName));
2026-02-27 21:04:07 +08:00
try {
final params = UpdateDevicenameParams(deviceId, deviceName);
final result = await _updateDevicename.call(params);
2026-03-02 13:25:21 +08:00
result.fold((failure) => emit(state.copyWith(isLoading: false, errorMessage: failure.message ?? '更新设备名称失败', operationType: DeviceOperationType.none)), (
successCode,
) {
// 核心修复:int 转 bool 条件判断
print('更新设备名称结果代码: $successCode'); // 调试输出结果代码
final isSuccess = successCode == 1; // 显式转为 bool
if (isSuccess) {
//final updatedDevices = state.devices?.map((device) {
// return device.deviceName == deviceId ? device.copyWith(deviceName: deviceName) : device;
//}).toList();
2026-02-27 21:04:07 +08:00
2026-03-02 13:25:21 +08:00
emit(
state.copyWith(
isLoading: false,
//devices: updatedDevices,
//selectedDevice: state.selectedDevice?.deviceName == deviceId ? state.selectedDevice?.copyWith(deviceName: deviceName) : state.selectedDevice,
errorMessage: '',
operationType: DeviceOperationType.none,
),
);
} else {
emit(state.copyWith(isLoading: false, errorMessage: '更新设备名称失败:状态码 $successCode', operationType: DeviceOperationType.none));
}
});
2026-02-27 21:04:07 +08:00
} catch (e) {
2026-03-02 13:25:21 +08:00
emit(state.copyWith(isLoading: false, errorMessage: '更新设备名称异常:${e.toString()}', operationType: DeviceOperationType.none));
}
}
2026-01-18 20:21:14 +08:00
// 获取所有设备列表
Future<void> fetchAllDevices(String username) async {
emit(state.copyWith(isLoading: true));
try {
// 网络请求获取列表
2026-03-02 13:25:21 +08:00
var resultEither = await _getUserDeviceUseCase.call(GetUserDeviceParams(username));
2026-01-18 20:21:14 +08:00
resultEither.fold(
(failure) => emit(
state.copyWith(
isLoading: false,
errorMessage: failure.message, //
2026-01-18 20:21:14 +08:00
),
),
(deviceList) {
2026-03-02 13:25:21 +08:00
emit(state.copyWith(devices: deviceList, selectedDevice: deviceList.isNotEmpty ? deviceList.first : null, isLoading: false));
2026-01-18 20:21:14 +08:00
},
);
} catch (e) {
emit(state.copyWith(isLoading: false, errorMessage: e.toString()));
}
}
// 切换当前选中的设备
void selectDevice(DeviceEntity device) {
emit(state.copyWith(selectedDevice: device));
}
// 更新单个设备的状态(例如从 Tcp 收到实时电量更新)
void updateDeviceStatus(DeviceEntity updatedDevice) {
final newList = state.devices.map((d) {
return d.deviceName == updatedDevice.deviceName ? updatedDevice : d;
}).toList();
// 如果更新的是当前选中的设备,也要同步更新 selectedDevice
2026-03-02 13:25:21 +08:00
final newSelected = state.selectedDevice?.deviceName == updatedDevice.deviceName ? updatedDevice : state.selectedDevice;
2026-01-18 20:21:14 +08:00
emit(state.copyWith(devices: newList, selectedDevice: newSelected));
}
/// 切换设备
2026-01-18 20:21:14 +08:00
Future<void> switchDevice(DeviceEntity device) async {
// 保持现有列表,只改 loading
emit(state.copyWith(isLoading: true));
final result = await repository.switchDevice("app", device.deviceName);
result.fold((l) {
emit(state.copyWith(isLoading: false, errorMessage: l.message));
selectDevice(device);
}, (r) => emit(state.copyWith(isLoading: false, selectedDevice: device)));
}
/// 获取设备位置
Future<void> getDeviceLocation(DeviceEntity device) async {
emit(state.copyWith(isLoading: true));
final result = await _getDeviceLocationUseCase.call(device.deviceName);
result.fold(
2026-03-02 13:25:21 +08:00
(failure) => emit(state.copyWith(isLoading: false, errorMessage: failure.message)),
(location) => emit(state.copyWith(isLoading: false, deviceLatitude: location.latitude, deviceLongitude: location.longitude)),
);
}
///获取记录
Future<void> loadWorkRecords(String userId) async {
emit(state.copyWith(isLoading: true));
final result = await _getWorkRecordUseCase(userId);
result.fold(
2026-03-02 13:25:21 +08:00
(failure) => emit(state.copyWith(isLoading: false, errorMessage: failure.message)),
(records) => emit(state.copyWith(isLoading: false, workRecords: records)),
);
}
/// 删除工作记录
Future<void> deleteWorkRecord(String workName) async {
emit(state.copyWith(isLoading: true));
final result = await _deleteWorkRecordUseCase(workName);
result.fold(
2026-03-02 13:25:21 +08:00
(failure) => emit(state.copyWith(isLoading: false, errorMessage: failure.message)),
(_) => emit(state.copyWith(isLoading: false, workRecords: state.workRecords?.where((record) => record != workName).toList())),
);
}
2026-02-28 09:55:14 +08:00
/// 加载指定作业名的路径数据
Future<void> loadSelectedPath(String workName) async {
emit(state.copyWith(isLoading: true));
try {
final result = await _selectWorkRecordUseCase.call(workName);
result.fold(
2026-03-02 13:25:21 +08:00
(failure) => emit(state.copyWith(isLoading: false, errorMessage: failure.message)),
2026-02-28 09:55:14 +08:00
(data) => emit(state.copyWith(isLoading: false, pathData: data)),
);
} catch (e) {
emit(state.copyWith(isLoading: false, errorMessage: e.toString()));
}
}
2026-03-02 13:25:21 +08:00
2026-02-28 09:45:53 +08:00
/// 保存路径数据
Future<void> saveWorkRecord(String workName, String userId, String jsonData) async {
emit(state.copyWith(isLoading: true));
2026-03-02 13:25:21 +08:00
final result = await _saveWorkRecordUseCase.call(workName: workName, userId: userId, jsonData: jsonData);
result.fold((failure) => emit(state.copyWith(isLoading: false, errorMessage: failure.message)), (data) => emit(state.copyWith(isLoading: false)));
2026-02-28 09:45:53 +08:00
}
2026-03-02 13:25:21 +08:00
2026-02-28 12:46:29 +08:00
/// generatePath
Future<void> generatePath({
required ReferencePoint reference,
required int heading,
required OuterBoundary outer,
2026-03-03 09:40:29 +08:00
required Map<String, HoleBoundary> holes,
2026-02-28 12:46:29 +08:00
required int workType,
}) async {
2026-03-03 09:40:29 +08:00
//holes.asMap().forEach((index, hole) {
// final pointsStr = hole.position.map((p) => "(${p.lat.toStringAsFixed(6)}, ${p.lon.toStringAsFixed(6)})").join(', ');
// debugPrint('第${index + 1}组holes:$pointsStr');
//});
2026-02-28 12:46:29 +08:00
emit(state.copyWith(isLoading: true));
2026-03-02 13:25:21 +08:00
final result = await _generatePathUseCase.execute(reference: reference, heading: heading, outer: outer, holes: holes, workType: workType);
2026-02-28 12:46:29 +08:00
2026-03-02 13:25:21 +08:00
result.fold((failure) => emit(state.copyWith(errorMessage: failure.message)), (pathData) => emit(state.copyWith(generatedPath: pathData)));
2026-02-28 12:46:29 +08:00
}
// 开始路径规划
Future<void> startRoutePlanning(Queue<DeviceAddPathPointModel> locationQueue) async {
emit(state.copyWith(isLoading: true));
final result = await _routePlanningUseCase.startRoutePlanning(locationQueue);
result.fold(
(failure) => emit(state.copyWith(
isLoading: false,
errorMessage: failure.message ?? '路径规划启动失败',
)),
(_) => emit(state.copyWith(
isLoading: false,
errorMessage: '',
)),
);
}
// 暂停
Future<void> pauseRoutePlanning() async {
emit(state.copyWith(isLoading: true));
final result = await _routePlanningUseCase.pauseRPWork();
result.fold(
(failure) => emit(state.copyWith(
isLoading: false,
errorMessage: failure.message ?? '暂停失败',
)),
(_) => emit(state.copyWith(isLoading: false)),
);
}
// 恢复
Future<void> resumeRoutePlanning() async {
emit(state.copyWith(isLoading: true));
final result = await _routePlanningUseCase.resumeRPWork();
result.fold(
(failure) => emit(state.copyWith(
isLoading: false,
errorMessage: failure.message ?? '恢复失败',
)),
(_) => emit(state.copyWith(isLoading: false)),
);
}
// 停止
Future<void> stopRoutePlanning() async {
emit(state.copyWith(isLoading: true));
final result = await _routePlanningUseCase.stopRoutePlanning();
result.fold(
(failure) => emit(state.copyWith(
isLoading: false,
errorMessage: failure.message ?? '停止失败',
)),
(_) => emit(state.copyWith(isLoading: false)),
);
}
2026-01-18 20:21:14 +08:00
}