2026-06-09 14:39:55 +08:00
|
|
|
|
import 'dart:convert';
|
2026-03-02 14:11:04 +08:00
|
|
|
|
import 'dart:collection';
|
|
|
|
|
|
|
2026-02-28 09:01:05 +08:00
|
|
|
|
import 'package:flutter/rendering.dart';
|
2026-01-18 20:21:14 +08:00
|
|
|
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
2026-03-09 08:38:29 +08:00
|
|
|
|
import 'package:get_it/get_it.dart';
|
2026-01-18 20:21:14 +08:00
|
|
|
|
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-06-09 14:39:55 +08:00
|
|
|
|
import 'package:maibu_satabot_v2/features/devices/data/models/work_record_entity.dart';
|
2026-02-28 09:45:53 +08:00
|
|
|
|
import 'package:maibu_satabot_v2/features/devices/domain/usecases/save_work_record_usecase.dart';
|
2026-02-28 09:01:05 +08:00
|
|
|
|
import 'package:maibu_satabot_v2/features/devices/domain/usecases/select_work_record_usecase.dart';
|
2026-02-27 13:17:09 +08:00
|
|
|
|
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
|
|
|
|
|
2026-03-05 21:07:33 +08:00
|
|
|
|
import '../../../../core/consts/tcp_consts.dart';
|
2026-04-15 08:43:39 +08:00
|
|
|
|
import '../../../../core/logging/i_logger_service.dart';
|
2026-03-05 21:07:33 +08:00
|
|
|
|
import '../../../../core/network/tcp/tcp_client.dart';
|
2026-06-24 14:44:16 +08:00
|
|
|
|
import '../../../../core/network/mqtt/domain/repositories/task_message_repository.dart';
|
2026-03-02 14:11:04 +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';
|
2026-03-04 10:22:12 +08:00
|
|
|
|
import '../../domain/usecases/bind_device_usecase.dart';
|
2026-02-26 16:20:02 +08:00
|
|
|
|
import '../../domain/usecases/delete_work_record_usecase.dart';
|
2026-02-26 08:45:37 +08:00
|
|
|
|
import '../../domain/usecases/get_device_location_usecase.dart';
|
2026-02-26 16:20:02 +08:00
|
|
|
|
import '../../domain/usecases/get_work_record_usecase.dart';
|
2026-06-09 14:39:55 +08:00
|
|
|
|
import '../../domain/usecases/get_work_records_by_site_id_usecase.dart';
|
2026-03-02 14:11:04 +08:00
|
|
|
|
import '../../domain/usecases/route_planning_usecase.dart';
|
2026-03-06 11:24:33 +08:00
|
|
|
|
import '../../services/path_planning_service.dart';
|
2026-03-05 21:07:33 +08:00
|
|
|
|
import 'device_status_bloc.dart';
|
|
|
|
|
|
import 'device_status_event.dart';
|
2026-01-18 20:21:14 +08:00
|
|
|
|
import 'devices_state.dart';
|
|
|
|
|
|
|
2026-06-24 14:44:16 +08:00
|
|
|
|
// 定义全局的TaskMessageRepository获取方式
|
|
|
|
|
|
TaskMessageRepository get _taskMessageRepo => GetIt.I<TaskMessageRepository>();
|
|
|
|
|
|
|
2026-01-18 20:21:14 +08:00
|
|
|
|
class DevicesCubit extends Cubit<DevicesState> {
|
|
|
|
|
|
final GetUserDeviceUseCase _getUserDeviceUseCase;
|
2026-02-26 08:45:37 +08:00
|
|
|
|
final GetDeviceLocationUseCase _getDeviceLocationUseCase;
|
2026-01-18 20:21:14 +08:00
|
|
|
|
final DeviceRepository repository;
|
2026-02-26 16:20:02 +08:00
|
|
|
|
final GetWorkRecordUseCase _getWorkRecordUseCase;
|
2026-06-09 14:39:55 +08:00
|
|
|
|
final GetWorkRecordsBySiteIdUseCase _getWorkRecordsBySiteIdUseCase;
|
2026-02-26 16:20:02 +08:00
|
|
|
|
final DeleteWorkRecordUseCase _deleteWorkRecordUseCase;
|
2026-03-04 10:22:12 +08:00
|
|
|
|
final BindDeviceUseCase _bindDeviceUseCase;
|
2026-02-27 13:17:09 +08:00
|
|
|
|
final UnbindDeviceUseCase _unbindDeviceUseCase;
|
2026-02-27 21:04:07 +08:00
|
|
|
|
final UpdateDevicenameUsecase _updateDevicename;
|
2026-02-28 09:01:05 +08:00
|
|
|
|
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;
|
2026-03-02 14:11:04 +08:00
|
|
|
|
final RoutePlanningUseCase _routePlanningUseCase; //
|
2026-03-05 21:07:33 +08:00
|
|
|
|
final DeviceStatusBloc _deviceStatusBloc; // 🔥 新增字段
|
|
|
|
|
|
final TcpClient _tcpClient;
|
2026-03-06 11:24:33 +08:00
|
|
|
|
// 🔥 关键:注入全局服务
|
|
|
|
|
|
final PathPlanningService _pathPlanningService;
|
|
|
|
|
|
|
2026-04-15 08:43:39 +08:00
|
|
|
|
final ILoggerService _logger = GetIt.I<ILoggerService>();
|
|
|
|
|
|
|
2026-02-27 13:17:09 +08:00
|
|
|
|
DevicesCubit(
|
|
|
|
|
|
this.repository,
|
|
|
|
|
|
this._getUserDeviceUseCase,
|
|
|
|
|
|
this._getDeviceLocationUseCase,
|
|
|
|
|
|
this._getWorkRecordUseCase,
|
2026-06-09 14:39:55 +08:00
|
|
|
|
this._getWorkRecordsBySiteIdUseCase,
|
2026-02-27 13:17:09 +08:00
|
|
|
|
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,
|
2026-03-05 10:01:02 +08:00
|
|
|
|
this._routePlanningUseCase,
|
|
|
|
|
|
this._bindDeviceUseCase,
|
2026-03-17 10:54:33 +08:00
|
|
|
|
this._deviceStatusBloc,
|
|
|
|
|
|
this._tcpClient,
|
|
|
|
|
|
this._pathPlanningService,
|
2026-02-27 13:17:09 +08:00
|
|
|
|
) : super(const DevicesState());
|
2026-02-26 16:20:02 +08:00
|
|
|
|
|
2026-02-27 13:17:09 +08:00
|
|
|
|
Future<void> unbindDevice(String deviceId, String deviceName) async {
|
2026-06-09 14:39:55 +08:00
|
|
|
|
emit(
|
|
|
|
|
|
state.copyWith(
|
|
|
|
|
|
isLoading: true,
|
|
|
|
|
|
errorMessage: '',
|
|
|
|
|
|
operationType: DeviceOperationType.unbind,
|
|
|
|
|
|
),
|
|
|
|
|
|
);
|
2026-02-27 13:17:09 +08:00
|
|
|
|
|
|
|
|
|
|
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, // 操作结束重置
|
2026-02-27 13:17:09 +08:00
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
(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 13:17:09 +08:00
|
|
|
|
|
2026-02-27 14:16:29 +08:00
|
|
|
|
emit(
|
|
|
|
|
|
state.copyWith(
|
|
|
|
|
|
isLoading: false,
|
|
|
|
|
|
devices: updatedDevices,
|
2026-06-09 14:39:55 +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-06-09 14:39:55 +08:00
|
|
|
|
emit(
|
|
|
|
|
|
state.copyWith(
|
|
|
|
|
|
isLoading: false,
|
|
|
|
|
|
errorMessage: '解绑失败:状态码 $successCode',
|
|
|
|
|
|
operationType: DeviceOperationType.none,
|
|
|
|
|
|
),
|
|
|
|
|
|
);
|
2026-02-27 14:16:29 +08:00
|
|
|
|
}
|
2026-02-27 13:17:09 +08:00
|
|
|
|
},
|
|
|
|
|
|
);
|
|
|
|
|
|
} catch (e) {
|
2026-06-09 14:39:55 +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-06-09 14:39:55 +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-06-09 14:39:55 +08:00
|
|
|
|
result.fold(
|
|
|
|
|
|
(failure) => emit(
|
|
|
|
|
|
state.copyWith(
|
|
|
|
|
|
isLoading: false,
|
|
|
|
|
|
errorMessage: failure.message ?? '更新设备名称失败',
|
|
|
|
|
|
operationType: DeviceOperationType.none,
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
(successCode) {
|
|
|
|
|
|
// 核心修复:int 转 bool 条件判断
|
|
|
|
|
|
//print('更新设备名称结果代码: $successCode'); // 调试输出结果代码
|
|
|
|
|
|
_logger.logWithLevel('更新设备名称结果代码: $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-06-09 14:39:55 +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-06-09 14:39:55 +08:00
|
|
|
|
emit(
|
|
|
|
|
|
state.copyWith(
|
|
|
|
|
|
isLoading: false,
|
|
|
|
|
|
errorMessage: '更新设备名称异常:${e.toString()}',
|
|
|
|
|
|
operationType: DeviceOperationType.none,
|
|
|
|
|
|
),
|
|
|
|
|
|
);
|
2026-02-27 13:17:09 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-01-18 20:21:14 +08:00
|
|
|
|
|
2026-03-05 10:01:02 +08:00
|
|
|
|
// 获取所有设备列表
|
2026-01-18 20:21:14 +08:00
|
|
|
|
// 获取所有设备列表
|
|
|
|
|
|
Future<void> fetchAllDevices(String username) async {
|
|
|
|
|
|
emit(state.copyWith(isLoading: true));
|
2026-03-05 10:01:02 +08:00
|
|
|
|
|
2026-01-18 20:21:14 +08:00
|
|
|
|
try {
|
2026-03-05 10:01:02 +08:00
|
|
|
|
// 🔥 关键步骤1:记录刷新前的选中设备标识(用 deviceName 作为唯一标识)
|
|
|
|
|
|
final String? oldSelectedDeviceName = state.selectedDevice?.deviceName;
|
|
|
|
|
|
|
|
|
|
|
|
// 网络请求获取新列表
|
2026-06-09 14:39:55 +08:00
|
|
|
|
var resultEither = await _getUserDeviceUseCase.call(
|
|
|
|
|
|
GetUserDeviceParams(username),
|
|
|
|
|
|
);
|
2026-03-05 10:01:02 +08:00
|
|
|
|
|
2026-06-09 14:39:55 +08:00
|
|
|
|
resultEither.fold(
|
|
|
|
|
|
(failure) => emit(
|
|
|
|
|
|
state.copyWith(isLoading: false, errorMessage: failure.message),
|
|
|
|
|
|
),
|
|
|
|
|
|
(deviceList) {
|
|
|
|
|
|
// 🔥 关键步骤2:匹配新列表中对应的旧选中设备
|
|
|
|
|
|
DeviceEntity? newSelectedDevice;
|
|
|
|
|
|
if (oldSelectedDeviceName != null && deviceList.isNotEmpty) {
|
|
|
|
|
|
// 在新列表中查找和旧选中设备名称一致的设备
|
|
|
|
|
|
newSelectedDevice = deviceList.firstWhere(
|
|
|
|
|
|
(device) => device.deviceName == oldSelectedDeviceName,
|
|
|
|
|
|
// 如果找不到(如设备已解绑),返回 null
|
|
|
|
|
|
orElse: () => deviceList.first, // 兜底:选中第一个
|
|
|
|
|
|
);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 无旧选中设备,默认选中第一个
|
|
|
|
|
|
newSelectedDevice = deviceList.isNotEmpty ? deviceList.first : null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 🔥 关键步骤3:更新状态,使用匹配后的选中设备
|
|
|
|
|
|
emit(
|
|
|
|
|
|
state.copyWith(
|
|
|
|
|
|
devices: deviceList,
|
|
|
|
|
|
selectedDevice: newSelectedDevice, // 保留旧选中设备
|
|
|
|
|
|
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-06-09 14:39:55 +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-02-27 13:17:09 +08:00
|
|
|
|
|
2026-02-26 16:20:02 +08:00
|
|
|
|
/// 切换设备
|
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);
|
|
|
|
|
|
|
2026-03-05 21:07:33 +08:00
|
|
|
|
result.fold(
|
|
|
|
|
|
// 失败处理
|
2026-03-17 10:54:33 +08:00
|
|
|
|
(l) {
|
2026-03-05 21:07:33 +08:00
|
|
|
|
emit(state.copyWith(isLoading: false, errorMessage: l.message));
|
|
|
|
|
|
selectDevice(device);
|
|
|
|
|
|
},
|
|
|
|
|
|
// 成功处理:改为 async 函数块
|
2026-03-17 10:54:33 +08:00
|
|
|
|
(r) async {
|
2026-07-02 16:04:30 +08:00
|
|
|
|
// 🔥 关键修复:不再断开 TCP 并重新连接
|
|
|
|
|
|
// TCP 已在登录时建立,设备切换只用 HTTP
|
|
|
|
|
|
// 避免创建新 TCP 连接发送 0x03 触发服务端推送 have_logged_in
|
|
|
|
|
|
|
|
|
|
|
|
// 重置设备状态 Bloc,清除旧设备图表数据
|
|
|
|
|
|
_deviceStatusBloc.add(DeviceStatusReset());
|
2026-03-17 10:54:33 +08:00
|
|
|
|
|
2026-07-02 16:04:30 +08:00
|
|
|
|
debugPrint('✅ 新设备切换完成(HTTP-only,无新TCP连接): ${device.deviceName}');
|
2026-03-17 10:54:33 +08:00
|
|
|
|
|
|
|
|
|
|
emit(state.copyWith(isLoading: false, selectedDevice: device));
|
2026-03-05 21:07:33 +08:00
|
|
|
|
},
|
|
|
|
|
|
);
|
2026-01-18 20:21:14 +08:00
|
|
|
|
}
|
2026-03-05 10:01:02 +08:00
|
|
|
|
|
2026-03-04 10:22:12 +08:00
|
|
|
|
/// 绑定设备
|
|
|
|
|
|
Future<void> bindDevice(String deviceId, String deviceAlias) async {
|
|
|
|
|
|
emit(state.copyWith(isLoading: true, errorMessage: ''));
|
|
|
|
|
|
try {
|
|
|
|
|
|
final params = BindDeviceParams(deviceId, deviceAlias);
|
|
|
|
|
|
final result = await _bindDeviceUseCase.call(params);
|
|
|
|
|
|
result.fold(
|
|
|
|
|
|
// 失败处理
|
2026-03-17 10:54:33 +08:00
|
|
|
|
(failure) {
|
2026-06-09 14:39:55 +08:00
|
|
|
|
emit(
|
|
|
|
|
|
state.copyWith(
|
|
|
|
|
|
isLoading: false,
|
|
|
|
|
|
errorMessage: failure.message ?? '绑定设备失败',
|
|
|
|
|
|
),
|
|
|
|
|
|
);
|
2026-03-05 10:00:18 +08:00
|
|
|
|
// 抛出异常,携带后端返回的错误消息(如“设备不存在”)
|
|
|
|
|
|
throw Exception(failure.message ?? '绑定设备失败');
|
|
|
|
|
|
},
|
2026-03-04 10:22:12 +08:00
|
|
|
|
// 成功处理(返回 int 状态码)
|
2026-03-05 10:01:02 +08:00
|
|
|
|
(successCode) {
|
2026-03-04 10:22:12 +08:00
|
|
|
|
if (successCode == 1) {
|
|
|
|
|
|
emit(state.copyWith(isLoading: false, errorMessage: ''));
|
|
|
|
|
|
// 绑定成功后刷新设备列表
|
2026-03-05 10:01:02 +08:00
|
|
|
|
//fetchAllDevices(state.?.username ?? '');
|
2026-03-04 10:22:12 +08:00
|
|
|
|
} else {
|
2026-06-09 14:39:55 +08:00
|
|
|
|
emit(
|
|
|
|
|
|
state.copyWith(
|
|
|
|
|
|
isLoading: false,
|
|
|
|
|
|
errorMessage: '绑定失败:状态码 $successCode',
|
|
|
|
|
|
),
|
|
|
|
|
|
);
|
2026-03-04 10:22:12 +08:00
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
);
|
|
|
|
|
|
} catch (e) {
|
2026-03-05 10:00:18 +08:00
|
|
|
|
// emit(state.copyWith(
|
|
|
|
|
|
// isLoading: false,
|
|
|
|
|
|
// errorMessage: '绑定异常:${e.toString()}',
|
|
|
|
|
|
// ));
|
|
|
|
|
|
// 🔥 重新抛出,传递给 UI
|
|
|
|
|
|
rethrow;
|
2026-03-04 10:22:12 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-26 16:20:02 +08:00
|
|
|
|
/// 获取设备位置
|
2026-02-26 08:45:37 +08:00
|
|
|
|
Future<void> getDeviceLocation(DeviceEntity device) async {
|
|
|
|
|
|
emit(state.copyWith(isLoading: true));
|
|
|
|
|
|
final result = await _getDeviceLocationUseCase.call(device.deviceName);
|
|
|
|
|
|
result.fold(
|
2026-06-09 14:39:55 +08:00
|
|
|
|
(failure) =>
|
|
|
|
|
|
emit(state.copyWith(isLoading: false, errorMessage: failure.message)),
|
|
|
|
|
|
(location) => emit(
|
|
|
|
|
|
state.copyWith(
|
|
|
|
|
|
isLoading: false,
|
|
|
|
|
|
deviceLatitude: location.latitude,
|
|
|
|
|
|
deviceLongitude: location.longitude,
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
2026-02-26 08:45:37 +08:00
|
|
|
|
);
|
|
|
|
|
|
}
|
2026-02-27 13:17:09 +08:00
|
|
|
|
|
2026-02-26 16:20:02 +08:00
|
|
|
|
///获取记录
|
|
|
|
|
|
Future<void> loadWorkRecords(String userId) async {
|
|
|
|
|
|
emit(state.copyWith(isLoading: true));
|
|
|
|
|
|
final result = await _getWorkRecordUseCase(userId);
|
|
|
|
|
|
result.fold(
|
2026-06-09 14:39:55 +08:00
|
|
|
|
(failure) =>
|
|
|
|
|
|
emit(state.copyWith(isLoading: false, errorMessage: failure.message)),
|
2026-02-27 13:17:09 +08:00
|
|
|
|
(records) => emit(state.copyWith(isLoading: false, workRecords: records)),
|
2026-02-26 16:20:02 +08:00
|
|
|
|
);
|
|
|
|
|
|
}
|
2026-02-27 13:17:09 +08:00
|
|
|
|
|
2026-06-09 14:39:55 +08:00
|
|
|
|
/// 根据场站ID获取工作记录(XML格式接口)
|
|
|
|
|
|
Future<void> loadWorkRecordsBySiteId(int siteId) async {
|
|
|
|
|
|
print('🔍 [DevicesCubit] 开始加载场站ID=$siteId的工作记录');
|
|
|
|
|
|
emit(state.copyWith(isLoading: true));
|
|
|
|
|
|
final result = await _getWorkRecordsBySiteIdUseCase(siteId);
|
|
|
|
|
|
result.fold(
|
|
|
|
|
|
(failure) {
|
|
|
|
|
|
print('❌ [DevicesCubit] 加载失败: ${failure.message}');
|
|
|
|
|
|
emit(state.copyWith(isLoading: false, errorMessage: failure.message));
|
|
|
|
|
|
},
|
|
|
|
|
|
(records) {
|
2026-07-11 16:46:53 +08:00
|
|
|
|
// print('🔍 [DevicesCubit] 加载成功,记录数: ${records.length}');
|
2026-06-09 14:39:55 +08:00
|
|
|
|
// 将 WorkRecordEntity 转换为 Map<String, dynamic> 以兼容现有UI
|
|
|
|
|
|
final mappedRecords = records.map((record) {
|
2026-07-11 16:46:53 +08:00
|
|
|
|
// 🔥 核心修复:根据 jsonData 类型决定存储方式
|
|
|
|
|
|
// XML接口返回的已是 JSON 字符串,直接使用,避免二次序列化丢失路径数据
|
|
|
|
|
|
// JSON接口返回的 WorkRecordJsonData 对象,仍需序列化
|
|
|
|
|
|
String? jsonDataStr;
|
|
|
|
|
|
if (record.jsonData is String) {
|
|
|
|
|
|
jsonDataStr = record.jsonData as String;
|
|
|
|
|
|
} else if (record.jsonData is WorkRecordJsonData) {
|
|
|
|
|
|
jsonDataStr = _workRecordJsonDataToJson(record.jsonData as WorkRecordJsonData);
|
|
|
|
|
|
}
|
2026-06-09 14:39:55 +08:00
|
|
|
|
return <String, dynamic>{
|
|
|
|
|
|
'id': record.id.toString(),
|
|
|
|
|
|
'workName': record.workName,
|
|
|
|
|
|
'imgUrl': record.imgUrl ?? '',
|
2026-07-11 16:46:53 +08:00
|
|
|
|
'jsonData': jsonDataStr,
|
2026-06-09 14:39:55 +08:00
|
|
|
|
};
|
|
|
|
|
|
}).toList();
|
2026-07-11 16:46:53 +08:00
|
|
|
|
// print('🔍 [DevicesCubit] 转换后的数据: $mappedRecords');
|
2026-06-09 14:39:55 +08:00
|
|
|
|
emit(state.copyWith(isLoading: false, workRecords: mappedRecords));
|
|
|
|
|
|
},
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 将 WorkRecordJsonData 转换为 JSON 字符串
|
|
|
|
|
|
String _workRecordJsonDataToJson(dynamic jsonData) {
|
|
|
|
|
|
// 将 jsonData 对象序列化为 JSON 字符串供UI使用
|
|
|
|
|
|
if (jsonData is WorkRecordJsonData) {
|
|
|
|
|
|
return jsonEncode({
|
|
|
|
|
|
'name': jsonData.name,
|
|
|
|
|
|
'path': jsonData.path,
|
|
|
|
|
|
'outer': jsonData.outer,
|
|
|
|
|
|
'img': jsonData.img,
|
|
|
|
|
|
'planModel': jsonData.planModel,
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
return jsonData.toString();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-26 16:20:02 +08:00
|
|
|
|
/// 删除工作记录
|
|
|
|
|
|
Future<void> deleteWorkRecord(String workName) async {
|
|
|
|
|
|
emit(state.copyWith(isLoading: true));
|
|
|
|
|
|
final result = await _deleteWorkRecordUseCase(workName);
|
|
|
|
|
|
result.fold(
|
2026-06-09 14:39:55 +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-26 16:20:02 +08:00
|
|
|
|
);
|
|
|
|
|
|
}
|
2026-02-28 09:55:14 +08:00
|
|
|
|
|
2026-02-28 09:01:05 +08:00
|
|
|
|
/// 加载指定作业名的路径数据
|
|
|
|
|
|
Future<void> loadSelectedPath(String workName) async {
|
|
|
|
|
|
emit(state.copyWith(isLoading: true));
|
|
|
|
|
|
try {
|
|
|
|
|
|
final result = await _selectWorkRecordUseCase.call(workName);
|
|
|
|
|
|
result.fold(
|
2026-06-09 14:39:55 +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)),
|
2026-02-28 09:01:05 +08:00
|
|
|
|
);
|
|
|
|
|
|
} 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
|
|
|
|
/// 保存路径数据
|
2026-06-09 14:39:55 +08:00
|
|
|
|
Future<void> saveWorkRecord(
|
|
|
|
|
|
String workName,
|
|
|
|
|
|
String userId,
|
|
|
|
|
|
String jsonData,
|
|
|
|
|
|
) async {
|
2026-02-28 09:45:53 +08:00
|
|
|
|
emit(state.copyWith(isLoading: true));
|
2026-06-09 14:39:55 +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-06-09 14:39:55 +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-06-09 14:39:55 +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
|
|
|
|
}
|
2026-03-05 10:01:02 +08:00
|
|
|
|
|
|
|
|
|
|
// 开始路径规划
|
2026-06-09 14:39:55 +08:00
|
|
|
|
Future<void> startRoutePlanning(
|
|
|
|
|
|
Queue<DeviceAddPathPointModel> locationQueue,
|
|
|
|
|
|
) async {
|
2026-04-17 10:00:39 +08:00
|
|
|
|
/// print("cubit层开始路径规划");
|
2026-04-15 08:43:39 +08:00
|
|
|
|
_logger.logWithLevel('开始路径规划');
|
2026-03-06 11:24:33 +08:00
|
|
|
|
// 清空全局 Service 中的队列
|
|
|
|
|
|
// _routePlanningUseCase.clearLocationQueue();
|
2026-03-11 10:01:36 +08:00
|
|
|
|
final List<DeviceAddPathPointModel> pathList = locationQueue.toList();
|
2026-04-17 10:00:39 +08:00
|
|
|
|
// print('✅ 队列转换为 List,长度:${pathList.length}');
|
2026-04-15 08:43:39 +08:00
|
|
|
|
_logger.logWithLevel('队列转换为 List,长度:${pathList.length}');
|
2026-03-11 10:01:36 +08:00
|
|
|
|
|
|
|
|
|
|
_pathPlanningService.updateQueue(pathList);
|
2026-04-15 08:43:39 +08:00
|
|
|
|
//print('✅ 队列更新成功');
|
|
|
|
|
|
_logger.logWithLevel('队列更新成功');
|
2026-03-11 10:01:36 +08:00
|
|
|
|
|
2026-03-06 11:24:33 +08:00
|
|
|
|
// 开始路径规划
|
2026-03-02 14:11:04 +08:00
|
|
|
|
emit(state.copyWith(isLoading: true));
|
2026-03-06 11:24:33 +08:00
|
|
|
|
//传入全局的Service 中路径规划队列
|
2026-03-17 10:54:33 +08:00
|
|
|
|
Queue<DeviceAddPathPointModel> queue = _pathPlanningService.getQueue();
|
2026-04-15 08:43:39 +08:00
|
|
|
|
//print('📦 从 Service 获取的队列长度:${queue.length}');
|
|
|
|
|
|
_logger.logWithLevel('从 Service 获取的队列长度:${queue.length}');
|
2026-03-06 11:24:33 +08:00
|
|
|
|
final result = await _routePlanningUseCase.startRoutePlanning(queue);
|
2026-03-02 14:11:04 +08:00
|
|
|
|
result.fold(
|
2026-06-09 14:39:55 +08:00
|
|
|
|
(failure) => emit(
|
|
|
|
|
|
state.copyWith(
|
|
|
|
|
|
isLoading: false,
|
|
|
|
|
|
errorMessage: failure.message ?? '路径规划启动失败',
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
2026-03-05 10:01:02 +08:00
|
|
|
|
(_) => emit(state.copyWith(isLoading: false, errorMessage: '')),
|
2026-03-02 14:11:04 +08:00
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-05 10:01:02 +08:00
|
|
|
|
// 暂停
|
2026-03-02 14:11:04 +08:00
|
|
|
|
Future<void> pauseRoutePlanning() async {
|
|
|
|
|
|
emit(state.copyWith(isLoading: true));
|
|
|
|
|
|
final result = await _routePlanningUseCase.pauseRPWork();
|
2026-06-09 14:39:55 +08:00
|
|
|
|
result.fold(
|
|
|
|
|
|
(failure) => emit(
|
|
|
|
|
|
state.copyWith(
|
|
|
|
|
|
isLoading: false,
|
|
|
|
|
|
errorMessage: failure.message ?? '暂停失败',
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
(_) => emit(state.copyWith(isLoading: false)),
|
|
|
|
|
|
);
|
2026-03-02 14:11:04 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-05 10:01:02 +08:00
|
|
|
|
// 恢复
|
2026-03-02 14:11:04 +08:00
|
|
|
|
Future<void> resumeRoutePlanning() async {
|
|
|
|
|
|
emit(state.copyWith(isLoading: true));
|
|
|
|
|
|
final result = await _routePlanningUseCase.resumeRPWork();
|
2026-06-09 14:39:55 +08:00
|
|
|
|
result.fold(
|
|
|
|
|
|
(failure) => emit(
|
|
|
|
|
|
state.copyWith(
|
|
|
|
|
|
isLoading: false,
|
|
|
|
|
|
errorMessage: failure.message ?? '恢复失败',
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
(_) => emit(state.copyWith(isLoading: false)),
|
|
|
|
|
|
);
|
2026-03-02 14:11:04 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-05 10:01:02 +08:00
|
|
|
|
// 停止
|
2026-03-02 14:11:04 +08:00
|
|
|
|
Future<void> stopRoutePlanning() async {
|
|
|
|
|
|
emit(state.copyWith(isLoading: true));
|
|
|
|
|
|
final result = await _routePlanningUseCase.stopRoutePlanning();
|
2026-04-17 10:00:39 +08:00
|
|
|
|
|
2026-03-12 16:41:11 +08:00
|
|
|
|
// 🔥 重置 PathPlanningService 中的全局的队列
|
|
|
|
|
|
_pathPlanningService.clear();
|
2026-06-09 14:39:55 +08:00
|
|
|
|
result.fold(
|
|
|
|
|
|
(failure) => emit(
|
|
|
|
|
|
state.copyWith(
|
|
|
|
|
|
isLoading: false,
|
|
|
|
|
|
errorMessage: failure.message ?? '停止失败',
|
|
|
|
|
|
),
|
|
|
|
|
|
),
|
|
|
|
|
|
(_) => emit(state.copyWith(isLoading: false)),
|
|
|
|
|
|
);
|
2026-03-02 14:11:04 +08:00
|
|
|
|
}
|
2026-03-11 10:01:36 +08:00
|
|
|
|
|
|
|
|
|
|
void updateAppState(AppState appState) {
|
|
|
|
|
|
emit(state.copyWith(appState: appState));
|
|
|
|
|
|
(_routePlanningUseCase as dynamic).repository.setAppState(appState);
|
|
|
|
|
|
}
|
2026-03-12 13:51:14 +08:00
|
|
|
|
|
|
|
|
|
|
void finishWork() {
|
|
|
|
|
|
emit(state.copyWith(isFinshWork: true));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 重置完成状态
|
|
|
|
|
|
void resetWorkStatus() {
|
|
|
|
|
|
emit(state.copyWith(isFinshWork: false));
|
|
|
|
|
|
}
|
2026-03-17 10:54:33 +08:00
|
|
|
|
|
2026-03-12 15:40:07 +08:00
|
|
|
|
void setArrivedLocation(double latitude, double longitude) {
|
|
|
|
|
|
emit(state.copyWith(arriLatitude: latitude, arriLongitude: longitude));
|
2026-04-17 10:00:39 +08:00
|
|
|
|
// print('✅ [DevicesCubit] 监听更新已完成到达位置:Lat=$latitude, Lng=$longitude');
|
2026-06-09 14:39:55 +08:00
|
|
|
|
_logger.logWithLevel(
|
|
|
|
|
|
'✅ [DevicesCubit] 监听更新已完成到达位置:Lat=$latitude, Lng=$longitude',
|
|
|
|
|
|
);
|
2026-03-12 15:40:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//获取已到达的点的经纬度
|
|
|
|
|
|
(double, double) getArrivedLocation() {
|
|
|
|
|
|
return (state.arriLatitude ?? 0.0, state.arriLongitude ?? 0.0);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 重置已到达记录
|
|
|
|
|
|
void resetArrivedLocation() {
|
|
|
|
|
|
emit(state.copyWith(arriLatitude: null, arriLongitude: null));
|
2026-04-15 08:43:39 +08:00
|
|
|
|
//print('🔄 [DevicesCubit] 已重置到达位置');
|
|
|
|
|
|
_logger.logWithLevel('🔄 [DevicesCubit] 已重置到达位置');
|
2026-03-12 15:40:07 +08:00
|
|
|
|
}
|
2026-06-16 17:34:00 +08:00
|
|
|
|
|
|
|
|
|
|
/// 🔥 获取当前选中的设备
|
|
|
|
|
|
DeviceEntity? getSelectedDevice() {
|
|
|
|
|
|
return state.selectedDevice;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 🔥 异步获取当前选中的设备(兼容 RemoteControlCubit 接口)
|
|
|
|
|
|
Future<DeviceEntity?> getDevice() async {
|
|
|
|
|
|
return state.selectedDevice;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 🔥 判断是否有选中设备
|
|
|
|
|
|
bool hasSelectedDevice() {
|
|
|
|
|
|
return state.selectedDevice != null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 🔥 获取选中设备名称(安全获取,返回空字符串而非null)
|
|
|
|
|
|
String getSelectedDeviceName() {
|
|
|
|
|
|
return state.selectedDevice?.deviceName ?? '';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 🔥 获取选中设备ID(安全获取,返回空字符串而非null)
|
|
|
|
|
|
String getSelectedDeviceId() {
|
|
|
|
|
|
return state.selectedDevice?.deviceName ?? '';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 🔥 清除选中设备
|
|
|
|
|
|
void clearSelectedDevice() {
|
|
|
|
|
|
debugPrint('🧹 [DevicesCubit] 清除选中设备');
|
|
|
|
|
|
_logger.logWithLevel('🧹 [DevicesCubit] 清除选中设备');
|
|
|
|
|
|
emit(state.copyWith(selectedDevice: null));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 🔥 检查设备是否在列表中
|
|
|
|
|
|
bool isDeviceInList(String deviceName) {
|
|
|
|
|
|
return state.devices.any((device) => device.deviceName == deviceName);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 🔥 根据设备名称查找设备
|
|
|
|
|
|
DeviceEntity? findDeviceByName(String deviceName) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
return state.devices.firstWhere(
|
|
|
|
|
|
(device) => device.deviceName == deviceName,
|
|
|
|
|
|
orElse: () => throw Exception('Device not found'),
|
|
|
|
|
|
);
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-06-24 14:44:16 +08:00
|
|
|
|
|
|
|
|
|
|
/// 🔥 启动MQTT到达点监听(用于路径规划动画)
|
|
|
|
|
|
/// [deviceId] - 目标设备ID,即targetDevice的deviceId
|
2026-08-07 08:49:29 +08:00
|
|
|
|
/// [taskId] - 任务ID,用于订阅 task/{taskId}/status 和 task/{taskId}/arrive
|
|
|
|
|
|
Future<void> startListeningMqttArrive({required String deviceId, required int taskId}) async {
|
|
|
|
|
|
debugPrint('📡 [DevicesCubit] 启动MQTT到达点监听 - deviceId: $deviceId, taskId: $taskId');
|
|
|
|
|
|
_logger.logWithLevel('📡 [DevicesCubit] 启动MQTT到达点监听 - deviceId: $deviceId, taskId: $taskId');
|
2026-06-24 14:44:16 +08:00
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 启动MQTT订阅
|
2026-08-07 08:49:29 +08:00
|
|
|
|
await _taskMessageRepo.startListening(deviceId: deviceId, taskId: taskId);
|
2026-06-24 14:44:16 +08:00
|
|
|
|
|
|
|
|
|
|
// 设置DeviceStatusBloc监听的设备ID
|
|
|
|
|
|
_deviceStatusBloc.setListeningDeviceId(deviceId);
|
|
|
|
|
|
|
|
|
|
|
|
debugPrint('✅ [DevicesCubit] MQTT到达点监听已启动');
|
|
|
|
|
|
_logger.logWithLevel('✅ [DevicesCubit] MQTT到达点监听已启动');
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
debugPrint('❌ [DevicesCubit] 启动MQTT监听失败: $e');
|
|
|
|
|
|
_logger.logWithLevel('❌ [DevicesCubit] 启动MQTT监听失败: $e');
|
|
|
|
|
|
rethrow;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 🔥 停止MQTT到达点监听
|
|
|
|
|
|
Future<void> stopListeningMqttArrive() async {
|
|
|
|
|
|
debugPrint('🛑 [DevicesCubit] 停止MQTT到达点监听');
|
|
|
|
|
|
_logger.logWithLevel('🛑 [DevicesCubit] 停止MQTT到达点监听');
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
await _taskMessageRepo.stopListening();
|
|
|
|
|
|
_deviceStatusBloc.setListeningDeviceId('');
|
|
|
|
|
|
debugPrint('✅ [DevicesCubit] MQTT到达点监听已停止');
|
|
|
|
|
|
_logger.logWithLevel('✅ [DevicesCubit] MQTT到达点监听已停止');
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
debugPrint('❌ [DevicesCubit] 停止MQTT监听失败: $e');
|
|
|
|
|
|
_logger.logWithLevel('❌ [DevicesCubit] 停止MQTT监听失败: $e');
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-01-18 20:21:14 +08:00
|
|
|
|
}
|