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

747 lines
27 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

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:convert';
import 'dart:collection';
import 'package:flutter/rendering.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:get_it/get_it.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';
import 'package:maibu_satabot_v2/features/devices/domain/usecases/generate_path_usecase.dart';
import 'package:maibu_satabot_v2/features/devices/domain/usecases/get_user_device_usecase.dart';
import 'package:maibu_satabot_v2/features/devices/data/models/work_record_entity.dart';
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';
import 'package:maibu_satabot_v2/features/devices/domain/usecases/update_devicename_usecase.dart';
import '../../../../core/consts/tcp_consts.dart';
import '../../../../core/logging/i_logger_service.dart';
import '../../../../core/logging/log_time.dart';
import '../../../../core/network/tcp/tcp_client.dart';
import '../../../../core/network/mqtt/domain/repositories/task_message_repository.dart';
import '../../data/models/device_add_path_point_model.dart';
import '../../data/models/device_work_area_param_model.dart';
import '../../domain/usecases/bind_device_usecase.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/get_work_records_by_site_id_usecase.dart';
import '../../domain/usecases/route_planning_usecase.dart';
import '../../services/path_planning_service.dart';
import 'device_status_bloc.dart';
import 'device_status_event.dart';
import 'devices_state.dart';
// 定义全局的TaskMessageRepository获取方式
TaskMessageRepository get _taskMessageRepo => GetIt.I<TaskMessageRepository>();
class DevicesCubit extends Cubit<DevicesState> {
final GetUserDeviceUseCase _getUserDeviceUseCase;
final GetDeviceLocationUseCase _getDeviceLocationUseCase;
final DeviceRepository repository;
final GetWorkRecordUseCase _getWorkRecordUseCase;
final GetWorkRecordsBySiteIdUseCase _getWorkRecordsBySiteIdUseCase;
final DeleteWorkRecordUseCase _deleteWorkRecordUseCase;
final BindDeviceUseCase _bindDeviceUseCase;
final UnbindDeviceUseCase _unbindDeviceUseCase;
final UpdateDevicenameUsecase _updateDevicename;
final SelectWorkRecordUseCase _selectWorkRecordUseCase;
final SaveWorkRecordUseCase _saveWorkRecordUseCase;
final GeneratePathUseCase _generatePathUseCase;
final RoutePlanningUseCase _routePlanningUseCase; //
final DeviceStatusBloc _deviceStatusBloc; // 🔥 新增字段
final TcpClient _tcpClient;
// 🔥 关键:注入全局服务
final PathPlanningService _pathPlanningService;
final ILoggerService _logger = GetIt.I<ILoggerService>();
DevicesCubit(
this.repository,
this._getUserDeviceUseCase,
this._getDeviceLocationUseCase,
this._getWorkRecordUseCase,
this._getWorkRecordsBySiteIdUseCase,
this._deleteWorkRecordUseCase,
this._unbindDeviceUseCase,
this._updateDevicename,
this._selectWorkRecordUseCase,
this._saveWorkRecordUseCase,
this._generatePathUseCase,
this._routePlanningUseCase,
this._bindDeviceUseCase,
this._deviceStatusBloc,
this._tcpClient,
this._pathPlanningService,
) : super(const DevicesState());
Future<void> unbindDevice(String deviceId, String deviceName) async {
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 ?? '解绑设备失败',
operationType: DeviceOperationType.none, // 操作结束重置
),
),
(successCode) {
// 核心修复:int 转 bool 条件判断
final isSuccess = successCode == 1; // 显式转为 bool
if (isSuccess) {
final updatedDevices = state.devices?.where((device) {
return device.deviceName != deviceId;
}).toList();
emit(
state.copyWith(
isLoading: false,
devices: updatedDevices,
selectedDevice: state.selectedDevice?.deviceName == deviceId
? null
: state.selectedDevice,
errorMessage: '',
operationType: DeviceOperationType.none,
),
);
} else {
emit(
state.copyWith(
isLoading: false,
errorMessage: '解绑失败:状态码 $successCode',
operationType: DeviceOperationType.none,
),
);
}
},
);
} catch (e) {
emit(
state.copyWith(
isLoading: false,
errorMessage: '解绑异常:${e.toString()}',
operationType: DeviceOperationType.none,
),
);
}
}
Future<void> updateDeviceName(String deviceId, String deviceName) async {
emit(
state.copyWith(
isLoading: true,
errorMessage: '',
operationType: DeviceOperationType.updateName,
),
);
try {
final params = UpdateDevicenameParams(deviceId, deviceName);
final result = await _updateDevicename.call(params);
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();
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,
),
);
}
},
);
} catch (e) {
emit(
state.copyWith(
isLoading: false,
errorMessage: '更新设备名称异常:${e.toString()}',
operationType: DeviceOperationType.none,
),
);
}
}
// 获取所有设备列表
// 获取所有设备列表
Future<void> fetchAllDevices(String username) async {
emit(state.copyWith(isLoading: true));
try {
// 🔥 关键步骤1:记录刷新前的选中设备标识(用 deviceName 作为唯一标识)
final String? oldSelectedDeviceName = state.selectedDevice?.deviceName;
// 网络请求获取新列表
var resultEither = await _getUserDeviceUseCase.call(
GetUserDeviceParams(username),
);
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,
),
);
},
);
} 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
final newSelected =
state.selectedDevice?.deviceName == updatedDevice.deviceName
? updatedDevice
: state.selectedDevice;
emit(state.copyWith(devices: newList, selectedDevice: newSelected));
}
/// 切换设备
Future<void> switchDevice(DeviceEntity device) async {
debugPrint('${LogTime.now()} 👆 [DevicesCubit] 开始切换设备: ${device.deviceName}');
// 保持现有列表,只改 loading
emit(state.copyWith(isLoading: true));
final result = await repository.switchDevice("app", device.deviceName);
result.fold(
// 失败处理
(l) {
debugPrint('${LogTime.now()} ❌ [DevicesCubit] 切换设备失败: ${device.deviceName}, 原因: ${l.message}');
emit(state.copyWith(isLoading: false, errorMessage: l.message));
selectDevice(device);
},
// 成功处理:改为 async 函数块
(r) async {
// 🔥 关键修复:不再断开 TCP 并重新连接
// TCP 已在登录时建立,设备切换只用 HTTP
// 避免创建新 TCP 连接发送 0x03 触发服务端推送 have_logged_in
// 重置设备状态 Bloc,清除旧设备图表数据
_deviceStatusBloc.add(DeviceStatusReset());
// 🔥 机器状态数据源已切换为MQTT:用与TCP相同的方式获取设备号(deviceName),
// 订阅 mower/{sn}/property/* 主题(内部自动退订旧设备、同SN去重)
debugPrint('${LogTime.now()} 📡 [DevicesCubit] HTTP切换成功,开始MQTT订阅: ${device.deviceName}');
_deviceStatusBloc.startMqttRealtimeListening(device.deviceName);
debugPrint('${LogTime.now()} ✅ 新设备切换完成(HTTP-only,无新TCP连接): ${device.deviceName}');
emit(state.copyWith(isLoading: false, selectedDevice: device));
},
);
}
/// 绑定设备
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(
// 失败处理
(failure) {
emit(
state.copyWith(
isLoading: false,
errorMessage: failure.message ?? '绑定设备失败',
),
);
// 抛出异常,携带后端返回的错误消息(如“设备不存在”)
throw Exception(failure.message ?? '绑定设备失败');
},
// 成功处理(返回 int 状态码)
(successCode) {
if (successCode == 1) {
emit(state.copyWith(isLoading: false, errorMessage: ''));
// 绑定成功后刷新设备列表
//fetchAllDevices(state.?.username ?? '');
} else {
emit(
state.copyWith(
isLoading: false,
errorMessage: '绑定失败:状态码 $successCode',
),
);
}
},
);
} catch (e) {
// emit(state.copyWith(
// isLoading: false,
// errorMessage: '绑定异常:${e.toString()}',
// ));
// 🔥 重新抛出,传递给 UI
rethrow;
}
}
/// 获取设备位置
Future<void> getDeviceLocation(DeviceEntity device) async {
emit(state.copyWith(isLoading: true));
final result = await _getDeviceLocationUseCase.call(device.deviceName);
result.fold(
(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(
(failure) =>
emit(state.copyWith(isLoading: false, errorMessage: failure.message)),
(records) => emit(state.copyWith(isLoading: false, workRecords: records)),
);
}
/// 根据场站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) {
print('🔍 [DevicesCubit] selectBySiteId 加载成功,记录数: ${records.length}');
// 将 WorkRecordEntity 转换为 Map<String, dynamic> 以兼容现有UI
final mappedRecords = records.map((record) {
// 🔥 核心修复:根据 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);
}
final mapped = <String, dynamic>{
'id': record.id.toString(),
'workName': record.workName,
'imgUrl': record.imgUrl ?? '',
'jsonData': jsonDataStr,
};
print(' ├─ [${record.workName}] id=${record.id}, jsonData长度=${jsonDataStr?.length ?? "null"}');
// 打印 jsonData 内部的 path/outer 信息
if (jsonDataStr != null && jsonDataStr.isNotEmpty) {
try {
final decoded = jsonDecode(jsonDataStr);
if (decoded is Map) {
print(' │ path类型=${decoded['path']?.runtimeType}, outer类型=${decoded['outer']?.runtimeType}, planModel=${decoded['planModel']}');
if (decoded['path'] is List) print(' │ path长度=${(decoded['path'] as List).length}');
if (decoded['outer'] is List) print(' │ outer长度=${(decoded['outer'] as List).length}');
}
} catch (_) {}
}
return mapped;
}).toList();
print('🔍 [DevicesCubit] 转换完成,emit workRecords 数量: ${mappedRecords.length}');
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();
}
/// 删除工作记录
Future<void> deleteWorkRecord(String workName) async {
emit(state.copyWith(isLoading: true));
final result = await _deleteWorkRecordUseCase(workName);
result.fold(
(failure) =>
emit(state.copyWith(isLoading: false, errorMessage: failure.message)),
(_) => emit(
state.copyWith(
isLoading: false,
workRecords: state.workRecords
?.where((record) => record != workName)
.toList(),
),
),
);
}
/// 加载指定作业名的路径数据
Future<void> loadSelectedPath(String workName) async {
emit(state.copyWith(isLoading: true));
debugPrint('📡 [loadSelectedPath] 开始查询, workName=$workName');
try {
final result = await _selectWorkRecordUseCase.call(workName);
result.fold(
(failure) {
debugPrint('❌ [loadSelectedPath] 接口失败: ${failure.message}');
emit(
state.copyWith(isLoading: false, errorMessage: failure.message),
);
},
(data) {
debugPrint('✅ [loadSelectedPath] 接口成功, 记录数: ${data.length}');
if (data.isNotEmpty) {
final first = data.first;
debugPrint(' ├─ 第一条记录 keys: ${first.keys.toList()}');
debugPrint(' ├─ path 类型: ${first['path']?.runtimeType}, 长度: ${(first['path'] as List?)?.length ?? "null"}');
debugPrint(' ├─ outer 类型: ${first['outer']?.runtimeType}, 长度: ${(first['outer'] as List?)?.length ?? "null"}');
debugPrint(' └─ planModel: ${first['planModel']}');
if (first['path'] is List && (first['path'] as List).isNotEmpty) {
debugPrint(' └─ path[0]: ${(first['path'] as List).first}');
}
if (first['outer'] is List && (first['outer'] as List).isNotEmpty) {
debugPrint(' └─ outer[0]: ${(first['outer'] as List).first}');
}
}
emit(state.copyWith(isLoading: false, pathData: data));
},
);
} catch (e) {
debugPrint('❌ [loadSelectedPath] 异常: $e');
emit(state.copyWith(isLoading: false, errorMessage: e.toString()));
}
}
/// 保存路径数据
Future<void> saveWorkRecord(
String workName,
String userId,
String jsonData,
) async {
emit(state.copyWith(isLoading: true));
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)),
);
}
/// generatePath
Future<void> generatePath({
required ReferencePoint reference,
required int heading,
required OuterBoundary outer,
required Map<String, HoleBoundary> holes,
required int workType,
}) async {
//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');
//});
emit(state.copyWith(isLoading: true));
final result = await _generatePathUseCase.execute(
reference: reference,
heading: heading,
outer: outer,
holes: holes,
workType: workType,
);
result.fold(
(failure) => emit(state.copyWith(errorMessage: failure.message)),
(pathData) => emit(state.copyWith(generatedPath: pathData)),
);
}
// 开始路径规划
Future<void> startRoutePlanning(
Queue<DeviceAddPathPointModel> locationQueue,
) async {
/// print("cubit层开始路径规划");
_logger.logWithLevel('开始路径规划');
// 清空全局 Service 中的队列
// _routePlanningUseCase.clearLocationQueue();
final List<DeviceAddPathPointModel> pathList = locationQueue.toList();
// print('✅ 队列转换为 List,长度:${pathList.length}');
_logger.logWithLevel('队列转换为 List,长度:${pathList.length}');
_pathPlanningService.updateQueue(pathList);
//print('✅ 队列更新成功');
_logger.logWithLevel('队列更新成功');
// 开始路径规划
emit(state.copyWith(isLoading: true));
//传入全局的Service 中路径规划队列
Queue<DeviceAddPathPointModel> queue = _pathPlanningService.getQueue();
//print('📦 从 Service 获取的队列长度:${queue.length}');
_logger.logWithLevel('从 Service 获取的队列长度:${queue.length}');
final result = await _routePlanningUseCase.startRoutePlanning(queue);
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();
// 🔥 重置 PathPlanningService 中的全局的队列
_pathPlanningService.clear();
result.fold(
(failure) => emit(
state.copyWith(
isLoading: false,
errorMessage: failure.message ?? '停止失败',
),
),
(_) => emit(state.copyWith(isLoading: false)),
);
}
void updateAppState(AppState appState) {
emit(state.copyWith(appState: appState));
(_routePlanningUseCase as dynamic).repository.setAppState(appState);
}
void finishWork() {
emit(state.copyWith(isFinshWork: true));
}
/// 重置完成状态
void resetWorkStatus() {
emit(state.copyWith(isFinshWork: false));
}
void setArrivedLocation(double latitude, double longitude) {
emit(state.copyWith(arriLatitude: latitude, arriLongitude: longitude));
// print('✅ [DevicesCubit] 监听更新已完成到达位置:Lat=$latitude, Lng=$longitude');
_logger.logWithLevel(
'✅ [DevicesCubit] 监听更新已完成到达位置:Lat=$latitude, Lng=$longitude',
);
}
//获取已到达的点的经纬度
(double, double) getArrivedLocation() {
return (state.arriLatitude ?? 0.0, state.arriLongitude ?? 0.0);
}
// 重置已到达记录
void resetArrivedLocation() {
emit(state.copyWith(arriLatitude: null, arriLongitude: null));
//print('🔄 [DevicesCubit] 已重置到达位置');
_logger.logWithLevel('🔄 [DevicesCubit] 已重置到达位置');
}
/// 🔥 获取当前选中的设备
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;
}
}
/// 🔥 启动MQTT到达点监听(用于路径规划动画)
/// [deviceId] - 目标设备ID,即targetDevice的deviceId
/// [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');
try {
// 启动MQTT订阅
await _taskMessageRepo.startListening(deviceId: deviceId, taskId: taskId);
// 设置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');
}
}
}