优化掉核心功能中print打印影响性能的代码用日志对象代替打印
This commit is contained in:
@@ -30,6 +30,7 @@ class AuthCubit extends Cubit<AuthState> {
|
||||
final AppUserCubit appCubit;
|
||||
final NetMessageDispatcher dispatcher;
|
||||
final AuthTcpDatasource _authTcpDatasource;
|
||||
final ILoggerService _logger = GetIt.I<ILoggerService>();
|
||||
|
||||
StreamSubscription? _kickOutSub; // 新增:用于管理监听生命周期
|
||||
|
||||
@@ -74,7 +75,7 @@ class AuthCubit extends Cubit<AuthState> {
|
||||
|
||||
/// 退出登录 (主动或被动)
|
||||
Future<void> logout() async {
|
||||
print("退出登录");
|
||||
//print("退出登录");
|
||||
await storage.deleteUser();
|
||||
tcp.disconnect();
|
||||
appCubit.clearAuth();
|
||||
@@ -99,13 +100,13 @@ class AuthCubit extends Cubit<AuthState> {
|
||||
// }
|
||||
/// TCP 指令监听
|
||||
void _listenToAuthResponse() {
|
||||
print('>>> [AUTH] begin 指令监听:');
|
||||
_logger.logWithLevel('[AUTH] 监听 TCP 0x12 指令...', level: 'DEBUG');
|
||||
_kickOutSub?.cancel(); // 防止重复监听
|
||||
|
||||
// 直接监听原始数据包,自己处理 JSON 解析(去掉 CRC 字节)
|
||||
_kickOutSub = dispatcher.onCommand(0x12).listen((packet) {
|
||||
print('>>> [AUTH] 收到 0x12 原始包,payload 长度=${packet.payload.length}, 内容=${packet.payload}');
|
||||
|
||||
_logger.logWithLevel('[AUTH] 获取原始包:$packet', level: 'DEBUG');
|
||||
_logger.logWithLevel('>>> [AUTH] 收到 0x12 原始包,payload 长度=${packet.payload.length}, 内容=${packet.payload}', level: 'DEBUG');
|
||||
try {
|
||||
// 🔥 关键:手动去掉最后 2 个 CRC 字节
|
||||
String jsonString;
|
||||
@@ -114,25 +115,23 @@ class AuthCubit extends Cubit<AuthState> {
|
||||
} else {
|
||||
jsonString = utf8.decode(packet.payload);
|
||||
}
|
||||
|
||||
print('>>> [AUTH] 去除 CRC 后的 JSON: $jsonString');
|
||||
_logger.logWithLevel('[AUTH] 获取 JSON: $jsonString', level: 'DEBUG');
|
||||
|
||||
final jsonMap = jsonDecode(jsonString);
|
||||
// print('>>> [AUTH] JSON 解析成功:$jsonMap');
|
||||
_logger.logWithLevel('[AUTH] 获取 JSON Map: $jsonMap', level: 'DEBUG');
|
||||
|
||||
final respond = jsonMap['respond'] ?? '';
|
||||
if (respond == 'have_logged_in') {
|
||||
print('>>> [AUTH] ⚠️ 检测到异地登录 (respond=have_logged_in),开始退出...');
|
||||
_logger.logWithLevel('[AUTH] ⚠️ 检测到异地登录 (respond=have_logged_in),开始退出...', level: 'WARN');
|
||||
logout();
|
||||
} else {
|
||||
// print('>>> [AUTH] ℹ️ 收到其他 0x12 消息,忽略:respond=$respond');
|
||||
_logger.logWithLevel('[AUTH] ⚠️ 检测到异地登录 (respond=$respond),开始退出...', level: 'WARN');
|
||||
}
|
||||
} catch (e) {
|
||||
//print('>>> [AUTH] ❌ 解析失败:$e');
|
||||
_logger.logWithLevel('[AUTH] ❌ JSON 解析失败:$e', level: 'ERROR');
|
||||
}
|
||||
});
|
||||
|
||||
print('>>> [AUTH] ✅ 0x12 监听器已建立完成');
|
||||
_logger.logWithLevel('[AUTH] 监听 TCP 0x12 指令...', level: 'DEBUG');
|
||||
}
|
||||
|
||||
|
||||
@@ -146,21 +145,20 @@ class AuthCubit extends Cubit<AuthState> {
|
||||
|
||||
/// 🔥 息屏/后台后恢复到前台时的重连方法
|
||||
Future<void> reconnectAfterResume() async {
|
||||
print('📱 [AUTH] 检测到应用恢复到前台,检查 TCP 连接状态...');
|
||||
|
||||
_logger.logWithLevel('[AUTH] 检测到应用恢复到前台,检查 TCP 连接状态...', level: 'DEBUG');
|
||||
// 如果 TCP 未连接,则执行重连
|
||||
if (!tcp.isConnected) {
|
||||
print('⚠️ [AUTH] TCP 未连接,开始重连...');
|
||||
_logger.logWithLevel('[AUTH] TCP 未连接,开始重连...',
|
||||
level: 'DEBUG');
|
||||
try {
|
||||
await tcp.connect(host: TCPConsts.TCP_IP, port: TCPConsts.TCP_PORT);
|
||||
tcp.startHeartbeat(interval: const Duration(seconds: 4));
|
||||
print('✅ [AUTH] TCP 重连成功!');
|
||||
_logger.logWithLevel('[AUTH] TCP 重连成功!', level: 'DEBUG');
|
||||
} catch (e) {
|
||||
print('❌ [AUTH] TCP 重连失败:$e');
|
||||
// 可以选择重试或者通知用户
|
||||
_logger.logWithLevel('[AUTH] TCP 重连失败:$e', level: 'ERROR');
|
||||
}
|
||||
} else {
|
||||
print('✅ [AUTH] TCP 连接正常,无需重连');
|
||||
_logger.logWithLevel('[AUTH] TCP 已连接,无需重连', level: 'DEBUG');
|
||||
// 可选:发送一个心跳包确认连接有效
|
||||
tcp.sendHeartbeat();
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@ import '../../../domain/entities/device_location_entity.dart';
|
||||
class DeviceHttpDatasourceImpl implements DeviceHttpDatasource {
|
||||
final Dio dio;
|
||||
final UserStorage _userStorage; // 🔥 新增字段
|
||||
final ILoggerService _logger = GetIt.I<ILoggerService>();
|
||||
|
||||
|
||||
// 🔥 修改构造函数,注入 UserStorage
|
||||
DeviceHttpDatasourceImpl(this.dio, this._userStorage);
|
||||
@@ -22,7 +24,7 @@ class DeviceHttpDatasourceImpl implements DeviceHttpDatasource {
|
||||
// 🔥 辅助方法:获取 Token
|
||||
Future<String?> _getToken() async {
|
||||
final user = await _userStorage.getUser();
|
||||
debugPrint('用户信息: $user');
|
||||
_logger.logWithLevel('用户信息: $user', level: 'INFO');
|
||||
return user?.token;
|
||||
}
|
||||
|
||||
@@ -94,7 +96,8 @@ class DeviceHttpDatasourceImpl implements DeviceHttpDatasource {
|
||||
Future<int> switchDevice(String platform, String deviceId) async {
|
||||
final logger = GetIt.I<ILoggerService>() as SentryLoggerImpl;
|
||||
final token = await _getToken();
|
||||
print('用户点击switchDevice方法开始触发: $token');
|
||||
// print('用户点击switchDevice方法开始触发: $token');
|
||||
_logger.logWithLevel('用户点击switchDevice方法开始触发', level: 'DEBUG');
|
||||
logger.logWithLevel('用户点击switchDevice方法开始触发', level: 'DEBUG');
|
||||
logger.logWithLevel('用户点击switchDevice方法API 请求开始', level: 'INFO', data: {'url': 'https://serviceri.satabot.com/iot/device/switchDevice'});
|
||||
var response = await dio.post(
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:maibu_satabot_v2/common.dart';
|
||||
|
||||
import '../../../../../core/logging/i_logger_service.dart';
|
||||
import '../../../../../core/storage/user_storage.dart';
|
||||
import '../../models/device_add_path_point_model.dart';
|
||||
import '../path_http_datasource.dart';
|
||||
@@ -10,21 +12,23 @@ import 'package:logger/logger.dart';
|
||||
|
||||
class PathHttpDatasourceImpl implements PathHttpDatasource {
|
||||
final UserStorage _userStorage;
|
||||
final ILoggerService _logger = GetIt.I<ILoggerService>();
|
||||
|
||||
PathHttpDatasourceImpl(this._userStorage);
|
||||
|
||||
|
||||
|
||||
@override
|
||||
Future<List<DeviceAddPathPointModel>> generatePathRaw({required Map<String, dynamic> body}) async {
|
||||
try {
|
||||
// 1. 先打印基础信息
|
||||
print('=== 最终发送给后端的请求体 ===');
|
||||
print('请求体类型:${body.runtimeType}');
|
||||
_logger.logWithLevel( '请求体类型:${body.runtimeType}',level: 'INFO');
|
||||
|
||||
// 2. 格式化打印JSON(带缩进,清晰展示嵌套结构)
|
||||
final jsonString = const JsonEncoder.withIndent(' ').convert(body);
|
||||
|
||||
final _jsonString = jsonEncode(body);
|
||||
print('完整JSON请求体:\n$jsonString');
|
||||
// print('完整JSON请求体:\n$jsonString');
|
||||
_logger.logWithLevel( '请求体:$_jsonString',level: 'INFO');
|
||||
|
||||
// 3. 可选:单独打印holes的JSON(重点关注)
|
||||
//final holesJson = body['holes'] as List;
|
||||
@@ -38,9 +42,11 @@ class PathHttpDatasourceImpl implements PathHttpDatasource {
|
||||
//}
|
||||
} catch (e) {
|
||||
// 防止JSON序列化失败导致崩溃
|
||||
print('打印请求体失败:$e');
|
||||
//print('打印请求体失败:$e');
|
||||
_logger.logWithLevel( '打印请求体失败:$e',level: 'ERROR');
|
||||
// 降级打印原始body(虽然格式乱,但能看基础数据)
|
||||
print('原始body数据:$body');
|
||||
// print('原始body数据:$body');
|
||||
_logger.logWithLevel( '原始body数据:$body',level: 'ERROR');
|
||||
}
|
||||
|
||||
final token = (await _userStorage.getUser())?.token;
|
||||
@@ -58,7 +64,8 @@ class PathHttpDatasourceImpl implements PathHttpDatasource {
|
||||
final decoded = jsonDecode(response.body) as Map<String, dynamic>;
|
||||
if (decoded['success'] == true) {
|
||||
final pathJson = decoded['data']?['path'];
|
||||
print('路径数据: $pathJson'); // 调试输出路径数据
|
||||
// print('路径数据: $pathJson'); // 调试输出路径数据
|
||||
_logger.logWithLevel( '路径数据: $pathJson',level: 'INFO');
|
||||
if (pathJson == null) {
|
||||
throw Exception('API returned null for "path"');
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ class RoutePlanSendEntity {
|
||||
byteData.setInt8(offset++, endFlag2); // 0xAB → -85
|
||||
var uint8list = byteData.buffer.asUint8List();
|
||||
// 关键:直接返回原始二进制字节(不做任何映射/转换)
|
||||
print("发送的指令字节数组,$uint8list");
|
||||
// print("发送的指令字节数组,$uint8list");
|
||||
return byteData.buffer.asUint8List();
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import 'dart:convert';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:fpdart/fpdart.dart';
|
||||
import 'package:maibu_satabot_v2/core/storage/user_storage.dart';
|
||||
@@ -9,23 +10,29 @@ import 'package:maibu_satabot_v2/features/devices/domain/entities/device_run_hos
|
||||
import 'package:maibu_satabot_v2/features/devices/domain/errors/device_failure.dart';
|
||||
import 'package:maibu_satabot_v2/features/devices/domain/repositories/device_hostrity_work_repository.dart';
|
||||
|
||||
import '../../../../core/logging/i_logger_service.dart';
|
||||
|
||||
class DeviceHostrityWorkRepositoryImpl implements DeviceHostrityWorkRepositoryRepository {
|
||||
final Dio client;
|
||||
final UserStorage userStorage;
|
||||
final ILoggerService _logger = GetIt.I<ILoggerService>();
|
||||
|
||||
DeviceHostrityWorkRepositoryImpl({required this.client, required this.userStorage});
|
||||
|
||||
@override
|
||||
Future<Either<DeviceFailure, List<DeviceRunStatisticsEntity>>> getDeviceHRunStatistics({required String deviceId}) async {
|
||||
try {
|
||||
debugPrint('开始获取用户信息...');
|
||||
// debugPrint('开始获取用户信息...');
|
||||
_logger.logWithLevel('开始获取用户信息...', level: 'info');
|
||||
final user = await userStorage.getUser();
|
||||
if (user == null || user.token == null) {
|
||||
debugPrint('用户未登录或缺少 token');
|
||||
// debugPrint('用户未登录或缺少 token');
|
||||
_logger.logWithLevel('用户未登录或缺少 token', level: 'error');
|
||||
return Left(DeviceFailure.unauthorized(message: 'User not logged in or token is missing'));
|
||||
}
|
||||
final token = user.token!;
|
||||
debugPrint('用户信息获取成功,Token: $token');
|
||||
// debugPrint('用户信息获取成功,Token: $token');
|
||||
_logger.logWithLevel('用户信息获取成功,Token: $token', level: 'info');
|
||||
|
||||
final response = await client.get(
|
||||
'https://serviceri.satabot.com/iot/device/getDeviceRunStatistics?deviceId=$deviceId',
|
||||
@@ -49,18 +56,22 @@ class DeviceHostrityWorkRepositoryImpl implements DeviceHostrityWorkRepositoryRe
|
||||
.toList();
|
||||
return Right(statistics);
|
||||
} else {
|
||||
debugPrint('服务器返回错误码: ${jsonBody['code']}');
|
||||
//debugPrint('服务器返回错误码: ${jsonBody['code']}');
|
||||
_logger.logWithLevel('服务器返回错误码: ${jsonBody['code']}', level: 'error');
|
||||
return Left(DeviceFailure.serverError(message: '服务器返回错误码: ${jsonBody['code']}'));
|
||||
}
|
||||
} else {
|
||||
debugPrint('网络请求失败,状态码: ${response.statusCode}');
|
||||
// debugPrint('网络请求失败,状态码: ${response.statusCode}');
|
||||
_logger.logWithLevel('网络请求失败,状态码: ${response.statusCode}', level: 'error');
|
||||
return Left(DeviceFailure.networkError(message: '网络请求失败,状态码: ${response.statusCode}'));
|
||||
}
|
||||
} on DioException catch (e) {
|
||||
debugPrint('Dio 请求失败: ${e.message}');
|
||||
//debugPrint('Dio 请求失败: ${e.message}');
|
||||
_logger.logWithLevel('Dio 请求失败: ${e.message}', level: 'error');
|
||||
return Left(DeviceFailure.networkError(message: e.message ?? '未知错误'));
|
||||
} catch (e) {
|
||||
debugPrint('请求过程中发生异常: $e');
|
||||
// debugPrint('请求过程中发生异常: $e');
|
||||
_logger.logWithLevel('请求过程中发生异常: $e', level: 'error');
|
||||
return Left(DeviceFailure.unknownError(message: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:isar_community/isar.dart';
|
||||
import 'package:maibu_satabot_v2/core/logging/i_logger_service.dart';
|
||||
import 'package:maibu_satabot_v2/features/devices/data/models/device_add_path_point_model.dart';
|
||||
@@ -13,6 +14,8 @@ import '../models/route_plan_send_entity.dart';
|
||||
class RoutePlanningRepositoryImpl implements RoutePlanningRepository {
|
||||
final TcpClient tcp;
|
||||
late PathPlanner _planner;
|
||||
final ILoggerService _logger = GetIt.I<ILoggerService>();
|
||||
|
||||
|
||||
// 🔥 新增:存储 AppState 的引用
|
||||
dynamic _appState;
|
||||
@@ -23,7 +26,8 @@ class RoutePlanningRepositoryImpl implements RoutePlanningRepository {
|
||||
@override
|
||||
Future<void> startRoutePlanning(Queue<DeviceAddPathPointModel> locationQueue) async {
|
||||
// 直接传入 DeviceAddPathPointModel 列表(无需转换)
|
||||
print("[底层开始发送指令了]");
|
||||
// print("[底层开始发送指令了]");
|
||||
_logger.log("开始路径规划");
|
||||
final List<DeviceAddPathPointModel> locations = locationQueue.toList();
|
||||
_planner.startRoutePlanning(locations);
|
||||
}
|
||||
@@ -60,6 +64,7 @@ class RoutePlanningRepositoryImpl implements RoutePlanningRepository {
|
||||
// true = 使用新的 ACK 握手机制 (将来)
|
||||
// false = 使用现有逻辑 (现在)
|
||||
class PathPlanningMode {
|
||||
|
||||
static bool useAckHandshake = false; // 默认使用现有逻辑
|
||||
}
|
||||
|
||||
@@ -68,7 +73,7 @@ class PathPlanner {
|
||||
final Queue<DeviceAddPathPointModel> locationQueue = Queue();
|
||||
final TcpClient tcpClient;
|
||||
bool isStart = false;
|
||||
|
||||
final ILoggerService _logger = GetIt.I<ILoggerService>();
|
||||
// 新增:作业控制状态
|
||||
bool _isPaused = false;
|
||||
bool _isStopped = false;
|
||||
@@ -80,7 +85,7 @@ class PathPlanner {
|
||||
PathPlanner(this.tcpClient);
|
||||
|
||||
void sendNextLocation() {
|
||||
print("[在发送指令sendNextLocation方法中]");
|
||||
_logger.log("[发送sendNextLocation方法指令]");
|
||||
// if (isStart && locationQueue.isEmpty) {
|
||||
// isStart = false;
|
||||
// print("[track]");
|
||||
@@ -89,17 +94,19 @@ class PathPlanner {
|
||||
// }
|
||||
// 关键检查:暂停或停止时不再发送
|
||||
if (_isPaused) {
|
||||
print("⏸️ 当前处于暂停状态,停止发送指令");
|
||||
_logger.log("[暂停状态],停止发送指令");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_isStopped || (isStart && locationQueue.isEmpty)) {
|
||||
isStart = false;
|
||||
if (_isStopped) {
|
||||
print("⏹️ 已停止作业,清空队列");
|
||||
//print("⏹️ 已停止作业,清空队列");
|
||||
_logger.log("[路径点发送完毕]");
|
||||
locationQueue.clear(); // 清空剩余队列
|
||||
} else {
|
||||
print("[路径点发送完毕]");
|
||||
//print("[路径点发送完毕]");
|
||||
_logger.log("[路径点发送完毕]");
|
||||
}
|
||||
_isStopped = false; // 重置停止标志
|
||||
return;
|
||||
@@ -114,7 +121,8 @@ class PathPlanner {
|
||||
speed: 1000,
|
||||
);
|
||||
|
||||
print("====Lat:${entity.latitude} ====Lng:${entity.longitude}");
|
||||
//print("====Lat:${entity.latitude} ====Lng:${entity.longitude}");
|
||||
_logger.log("[发送指令]");
|
||||
tcpClient.sendPathPoint( routePlanSendEntity);
|
||||
isStart = true;
|
||||
}
|
||||
@@ -130,43 +138,51 @@ class PathPlanner {
|
||||
isStart = true;
|
||||
_isPaused = false; // 🔥 只在第一次调用时重置
|
||||
_isStopped = false; // 🔥 只在第一次调用时重置
|
||||
print("[首次加载] 队列长度:${locations.length}");
|
||||
// print("[首次加载] 队列长度:${locations.length}");
|
||||
_logger.log("[首次加载] 队列长度:${locations.length}");
|
||||
} else {
|
||||
// 🔥 后续调用(来自 Dispatcher)只更新队列,不重置状态
|
||||
print("[更新队列] 当前处于 ${_isPaused ? '暂停' : _isStopped ? '停止' : '工作'} 状态");
|
||||
//print("[更新队列] 当前处于 ${_isPaused ? '暂停' : _isStopped ? '停止' : '工作'} 状态");
|
||||
_logger.log("[更新队列] 当前处于 ${_isPaused ? '暂停' : _isStopped ? '停止' : '工作'} 状态");
|
||||
if (!_isPaused && !_isStopped) {
|
||||
// 如果不是暂停/停止状态,才添加新数据
|
||||
locationQueue.addAll(locations);
|
||||
}
|
||||
}
|
||||
|
||||
print("[发送指令要转换类型了完成]");
|
||||
// print("[发送指令要转换类型了完成]");
|
||||
_logger.log("[发送指令要转换类型了完成]");
|
||||
|
||||
// 🔥 关键检查:如果是暂停/停止状态,直接返回
|
||||
if (_isPaused) {
|
||||
print("⏸️ 当前处于暂停状态,拒绝发送指令");
|
||||
// print("⏸️ 当前处于暂停状态,拒绝发送指令");
|
||||
_logger.log("[暂停状态]");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_isStopped) {
|
||||
print("⏹️ 当前处于停止状态,拒绝发送指令");
|
||||
// print("⏹️ 当前处于停止状态,拒绝发送指令");
|
||||
_logger.log("[停止状态]");
|
||||
return;
|
||||
}
|
||||
print("[发送指令要转换类型了完成]");
|
||||
// sendNextLocation();
|
||||
// 🔥 根据全局开关选择使用哪套发送逻辑
|
||||
if (PathPlanningConfig.useAckHandshake) {
|
||||
print("🔥 使用 ACK 握手机制发送路径点");
|
||||
// print("🔥 使用 ACK 握手机制发送路径点");
|
||||
_logger.log("[使用 ACK 握手机制发送路径点]");
|
||||
sendNextLocationWithAck(sl<NetMessageDispatcher>());
|
||||
} else {
|
||||
print("🔥 使用现有逻辑发送路径点");
|
||||
// print("🔥 使用现有逻辑发送路径点");
|
||||
_logger.log("[使用现有逻辑发送路径点]");
|
||||
sendNextLocation();
|
||||
}
|
||||
|
||||
}
|
||||
/// 暂停
|
||||
void pauseRPWork() {
|
||||
print("⏸️ 暂停作业,设置_isPaused = true");
|
||||
//print("⏸️ 暂停作业,设置_isPaused = true");
|
||||
_logger.log("[暂停作业,设置_isPaused = true]");
|
||||
_isPaused = true;
|
||||
|
||||
var entity = new RoutePlanSendEntity(
|
||||
@@ -176,12 +192,14 @@ class PathPlanner {
|
||||
targetLongitude: 0,
|
||||
speed: 0,
|
||||
);
|
||||
print("📡 发送暂停指令到设备...");
|
||||
// print("📡 发送暂停指令到设备...");
|
||||
_logger.log("[发送暂停指令到设备...]");
|
||||
tcpClient.sendDeviceStateChange(entity);
|
||||
}
|
||||
/// 恢复
|
||||
void resumeRPWork() {
|
||||
print("▶️ 恢复作业,设置_isPaused = false");
|
||||
//print("▶️ 恢复作业,设置_isPaused = false");
|
||||
_logger.log("[恢复作业,设置_isPaused = false]");
|
||||
_isPaused = false;
|
||||
|
||||
var entity = new RoutePlanSendEntity(
|
||||
@@ -191,7 +209,8 @@ class PathPlanner {
|
||||
targetLongitude: 0,
|
||||
speed: 0,
|
||||
);
|
||||
print("📡 发送恢复指令到设备...");
|
||||
// print("📡 发送恢复指令到设备...");
|
||||
_logger.log("[发送恢复指令到设备...]");
|
||||
tcpClient.sendDeviceStateChange(entity);
|
||||
|
||||
// 恢复后继续发送下一个点
|
||||
@@ -200,7 +219,8 @@ class PathPlanner {
|
||||
// sendNextLocation();
|
||||
// }
|
||||
if (locationQueue.isNotEmpty) {
|
||||
print("📍 恢复发送下一个路径点");
|
||||
//print("📍 恢复发送下一个路径点");
|
||||
_logger.log("[恢复发送下一个路径点]");
|
||||
if (PathPlanningConfig.useAckHandshake) {
|
||||
sendNextLocationWithAck(sl<NetMessageDispatcher>());
|
||||
} else {
|
||||
@@ -210,12 +230,14 @@ class PathPlanner {
|
||||
}
|
||||
/// 停止
|
||||
void stopRoutePlanning() {
|
||||
print("⏹️ 停止作业,设置_isStopped = true");
|
||||
// print("⏹️ 停止作业,设置_isStopped = true");
|
||||
_logger.log("[停止作业,设置_isStopped = true]");
|
||||
_isStopped = true;
|
||||
_isPaused = false; // 清除暂停状态
|
||||
|
||||
// 🔥 核心修复:清空队列
|
||||
print("🗑️ 清空待发送队列,剩余 ${locationQueue.length} 个点");
|
||||
// print("🗑️ 清空待发送队列,剩余 ${locationQueue.length} 个点");
|
||||
_logger.log("[清空待发送队列,剩余 ${locationQueue.length} 个点]");
|
||||
locationQueue.clear();
|
||||
|
||||
|
||||
@@ -228,7 +250,8 @@ class PathPlanner {
|
||||
targetLongitude: 0,
|
||||
speed: 0,
|
||||
);
|
||||
print("📡 发送停止指令到设备...");
|
||||
// print("📡 发送停止指令到设备...");
|
||||
_logger.log("[发送停止指令到设备...]");
|
||||
tcpClient.sendDeviceStateChange(entity);
|
||||
// 发送完成工作播报指令
|
||||
sendWorkCompleteBroadcast();
|
||||
@@ -243,32 +266,38 @@ class PathPlanner {
|
||||
targetLongitude: 0,
|
||||
speed: 0,
|
||||
);
|
||||
print("发送完成工作播报指令...");
|
||||
// print("发送完成工作播报指令...");
|
||||
_logger.log("[发送完成工作播报指令...]");
|
||||
tcpClient.sendDeviceStateChange(entity);
|
||||
}
|
||||
|
||||
// 🔥 新增:ACK 握手机制的发送方法 (新模式)
|
||||
void sendNextLocationWithAck(NetMessageDispatcher dispatcher) {
|
||||
print("[在发送指令 sendNextLocationWithAck 方法中 - ACK 模式]");
|
||||
// print("[在发送指令 sendNextLocationWithAck 方法中 - ACK 模式]");
|
||||
_logger.log("[在发送指令 sendNextLocationWithAck 方法中 - ACK 模式]");
|
||||
// 🔥 关键检查:如果正在等待 ACK,不要重复发送
|
||||
if (_waitingForAck) {
|
||||
print("⏳ 正在等待 ACK 确认,跳过发送");
|
||||
//print("⏳ 正在等待 ACK 确认,跳过发送");
|
||||
_logger.log("[正在等待 ACK 确认,跳过发送]");
|
||||
return;
|
||||
}
|
||||
|
||||
// 🔥 关键检查:暂停或停止时不再发送
|
||||
if (_isPaused) {
|
||||
print("⏸️ 当前处于暂停状态,停止发送指令");
|
||||
//print("⏸️ 当前处于暂停状态,停止发送指令");
|
||||
_logger.log("[暂停状态]");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_isStopped || (isStart && locationQueue.isEmpty)) {
|
||||
isStart = false;
|
||||
if (_isStopped) {
|
||||
print("️ 已停止作业,清空队列");
|
||||
// print("️ 已停止作业,清空队列");
|
||||
_logger.log("[已停止作业,清空队列]");
|
||||
locationQueue.clear(); // 清空剩余队列
|
||||
} else {
|
||||
print("[路径点发送完毕]");
|
||||
_logger.log("[路径点发送完毕]");
|
||||
}
|
||||
_isStopped = false; // 重置停止标志
|
||||
return;
|
||||
@@ -287,14 +316,17 @@ class PathPlanner {
|
||||
speed: 1000,
|
||||
);
|
||||
|
||||
print("====Lat:${entity.latitude} ====Lng:${entity.longitude}");
|
||||
//print("====Lat:${entity.latitude} ====Lng:${entity.longitude}");
|
||||
_logger.log("[发送路径点] Lat:${entity.latitude} Lng:${entity.longitude}");
|
||||
tcpClient.sendPathPoint( routePlanSendEntity);
|
||||
isStart = true;
|
||||
|
||||
// 🔥 关键:标记为正在等待 ACK
|
||||
_waitingForAck = true;
|
||||
dispatcher.setExpectedPointIndex(pointIndex); // 🔥 通知 Dispatcher
|
||||
print(" 已锁定发送,等待 ACK 确认 (0x02)...");
|
||||
//sprint(" 已锁定发送,等待 ACK 确认 (0x02)...");
|
||||
_logger.log("[已锁定发送,等待 ACK 确认 (0x02)...]");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,16 +2,20 @@ import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:maibu_satabot_v2/features/devices/domain/entities/running_status_entity.dart';
|
||||
import 'package:maibu_satabot_v2/features/devices/domain/entities/gps_entity.dart';
|
||||
import 'package:maibu_satabot_v2/core/network/protocol_decoder.dart';
|
||||
|
||||
import '../../../../core/logging/i_logger_service.dart';
|
||||
import '../../../../core/network/net_message_dispatcher.dart';
|
||||
import 'device_status_event.dart';
|
||||
import 'device_status_state.dart';
|
||||
|
||||
class DeviceStatusBloc extends Bloc<DeviceStatusEvent, DeviceStatusState> {
|
||||
final NetMessageDispatcher _dispatcher;
|
||||
final ILoggerService _logger = GetIt.I<ILoggerService>();
|
||||
|
||||
|
||||
// 持有订阅引用,仅在 close 时取消
|
||||
StreamSubscription? _stringSub;
|
||||
@@ -38,6 +42,7 @@ class DeviceStatusBloc extends Bloc<DeviceStatusEvent, DeviceStatusState> {
|
||||
|
||||
debugPrint('🔗 [DeviceStatusBloc] 初始化 TCP 数据流订阅(终身有效)');
|
||||
|
||||
|
||||
// 订阅字符串流 (0x02)
|
||||
_stringSub = _dispatcher.onStringMessage().listen(
|
||||
(jsonString) {
|
||||
@@ -63,12 +68,15 @@ class DeviceStatusBloc extends Bloc<DeviceStatusEvent, DeviceStatusState> {
|
||||
// );
|
||||
|
||||
_isSubscribed = true;
|
||||
debugPrint('✅ [DeviceStatusBloc] 订阅建立完成,将持续监听数据流');
|
||||
// debugPrint('✅ [DeviceStatusBloc] 订阅建立完成,将持续监听数据流');
|
||||
_logger.log('✅ [DeviceStatusBloc] 订阅建立完成,将持续监听数据流');
|
||||
}
|
||||
|
||||
// 🔥 核心修复:重置时仅清空状态,绝对不再触碰订阅关系
|
||||
Future<void> _handleReset(DeviceStatusReset event, Emitter<DeviceStatusState> emit) async {
|
||||
debugPrint('🔄 收到重置事件:仅清空状态,保持订阅活跃(不重连)');
|
||||
// debugPrint('🔄 收到重置事件:仅清空状态,保持订阅活跃(不重连)');
|
||||
_logger.log('🔄 收到重置事件:仅清空状态,保持订阅活跃(不重连)');
|
||||
|
||||
|
||||
// 只 emit 初始状态,让 UI 清除旧设备的数据(如速度归零、轨迹清除)
|
||||
emit(DeviceStatusInitial());
|
||||
@@ -82,7 +90,8 @@ class DeviceStatusBloc extends Bloc<DeviceStatusEvent, DeviceStatusState> {
|
||||
Emitter<DeviceStatusState> emit,
|
||||
) async {
|
||||
try {
|
||||
debugPrint('🔍 开始解析数据:${event.jsonString}');
|
||||
// debugPrint('🔍 开始解析数据:${event.jsonString}');
|
||||
_logger.log('🔍 开始解析数据:${event.jsonString}');
|
||||
final fields = event.jsonString.trim().split(',');
|
||||
|
||||
if (fields.length < 18) {
|
||||
@@ -94,10 +103,12 @@ class DeviceStatusBloc extends Bloc<DeviceStatusEvent, DeviceStatusState> {
|
||||
final status = RunningStatusEntity.fromFields(fields);
|
||||
final gps = GPSEntity(status.latitude, status.longitude);
|
||||
|
||||
debugPrint('✅ 解析成功,更新状态:Lat=${gps.latitude}, Lng=${gps.longitude}');
|
||||
//debugPrint('✅ 解析成功,更新状态:Lat=${gps.latitude}, Lng=${gps.longitude}');
|
||||
_logger.log('✅ 解析成功,更新状态:Lat=${gps.latitude}, Lng=${gps.longitude}');
|
||||
emit(DeviceStatusUpdated(status, gps));
|
||||
} catch (e, stack) {
|
||||
debugPrint('❌ 解析异常:$e\n$stack');
|
||||
//debugPrint('❌ 解析异常:$e\n$stack');
|
||||
_logger.log('❌ 解析异常:$e\n$stack');
|
||||
emit(DeviceStatusError('解析失败:$e'));
|
||||
}
|
||||
}
|
||||
@@ -109,7 +120,8 @@ class DeviceStatusBloc extends Bloc<DeviceStatusEvent, DeviceStatusState> {
|
||||
try {
|
||||
final eventStr = event.jsonData['event'] ?? '';
|
||||
final deviceId = event.jsonData['deviceId'] ?? '未知';
|
||||
debugPrint('收到推送事件:$eventStr, 设备:$deviceId');
|
||||
// debugPrint('收到推送事件:$eventStr, 设备:$deviceId');
|
||||
_logger.log('收到推送事件:$eventStr, 设备:$deviceId');
|
||||
// 这里可以根据需要 emit 新状态
|
||||
} catch (e) {
|
||||
emit(DeviceStatusError('解析推送消息失败:$e'));
|
||||
|
||||
@@ -13,6 +13,7 @@ import 'package:maibu_satabot_v2/features/devices/domain/usecases/unbind_device_
|
||||
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/network/tcp/tcp_client.dart';
|
||||
import '../../data/models/device_add_path_point_model.dart';
|
||||
import '../../data/models/device_work_area_param_model.dart';
|
||||
@@ -44,6 +45,9 @@ class DevicesCubit extends Cubit<DevicesState> {
|
||||
// 🔥 关键:注入全局服务
|
||||
final PathPlanningService _pathPlanningService;
|
||||
|
||||
final ILoggerService _logger = GetIt.I<ILoggerService>();
|
||||
|
||||
|
||||
DevicesCubit(
|
||||
this.repository,
|
||||
this._getUserDeviceUseCase,
|
||||
@@ -115,7 +119,8 @@ class DevicesCubit extends Cubit<DevicesState> {
|
||||
successCode,
|
||||
) {
|
||||
// 核心修复:int 转 bool 条件判断
|
||||
print('更新设备名称结果代码: $successCode'); // 调试输出结果代码
|
||||
//print('更新设备名称结果代码: $successCode'); // 调试输出结果代码
|
||||
_logger.logWithLevel('更新设备名称结果代码: $successCode');
|
||||
final isSuccess = successCode == 1; // 显式转为 bool
|
||||
if (isSuccess) {
|
||||
//final updatedDevices = state.devices?.map((device) {
|
||||
@@ -218,7 +223,8 @@ class DevicesCubit extends Cubit<DevicesState> {
|
||||
// 🔥 关键修复 1:先强制断开旧连接!
|
||||
// 这一步会销毁旧 Socket,清除旧 Listener,防止旧数据继续推送
|
||||
if (_tcpClient.isConnected) {
|
||||
debugPrint('🛑 检测到已连接,先断开旧 TCP 连接...');
|
||||
//debugPrint('🛑 检测到已连接,先断开旧 TCP 连接...');
|
||||
_logger.logWithLevel('🔥 检测到已连接,先断开旧 TCP 连接...');
|
||||
_tcpClient.disconnects(forSwitch: true);
|
||||
// 稍微等待一下,确保底层 Socket 资源释放 (可选,但推荐)
|
||||
// await Future.delayed(const Duration(milliseconds: 100));
|
||||
@@ -226,7 +232,8 @@ class DevicesCubit extends Cubit<DevicesState> {
|
||||
|
||||
// 🔥 关键修复 2:发起新连接
|
||||
// connect 方法内部会自动调用 _sendAuthPacket -> 获取设备列表 -> 自动订阅当前选中的设备
|
||||
debugPrint('🔌 开始重新新连接 TCP,将自动订阅新设备:${device.deviceName}');
|
||||
// debugPrint('🔌 开始重新新连接 TCP,将自动订阅新设备:${device.deviceName}');
|
||||
_logger.logWithLevel('🔌 启动重新新连接 TCP,将自动订阅新设备:${device.deviceName}');
|
||||
await _tcpClient.connectBySwitch(host: TCPConsts.TCP_IP, port: TCPConsts.TCP_PORT, deviceName: device.deviceName);
|
||||
// 🔥 关键修复 3:重置设备状态 Bloc,清除旧设备图表数据
|
||||
_deviceStatusBloc.add(DeviceStatusReset());
|
||||
@@ -347,20 +354,24 @@ class DevicesCubit extends Cubit<DevicesState> {
|
||||
|
||||
// 开始路径规划
|
||||
Future<void> startRoutePlanning(Queue<DeviceAddPathPointModel> locationQueue) async {
|
||||
print("cubit层开始路径规划");
|
||||
/// print("cubit层开始路径规划");
|
||||
_logger.logWithLevel('开始路径规划');
|
||||
// 清空全局 Service 中的队列
|
||||
// _routePlanningUseCase.clearLocationQueue();
|
||||
final List<DeviceAddPathPointModel> pathList = locationQueue.toList();
|
||||
print('✅ 队列转换为 List,长度:${pathList.length}');
|
||||
// print('✅ 队列转换为 List,长度:${pathList.length}');
|
||||
_logger.logWithLevel('队列转换为 List,长度:${pathList.length}');
|
||||
|
||||
_pathPlanningService.updateQueue(pathList);
|
||||
print('✅ 队列更新成功');
|
||||
//print('✅ 队列更新成功');
|
||||
_logger.logWithLevel('队列更新成功');
|
||||
|
||||
// 开始路径规划
|
||||
emit(state.copyWith(isLoading: true));
|
||||
//传入全局的Service 中路径规划队列
|
||||
Queue<DeviceAddPathPointModel> queue = _pathPlanningService.getQueue();
|
||||
print('📦 从 Service 获取的队列长度:${queue.length}');
|
||||
//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 ?? '路径规划启动失败')),
|
||||
@@ -407,7 +418,8 @@ class DevicesCubit extends Cubit<DevicesState> {
|
||||
|
||||
void setArrivedLocation(double latitude, double longitude) {
|
||||
emit(state.copyWith(arriLatitude: latitude, arriLongitude: longitude));
|
||||
print('✅ [DevicesCubit] 监听更新已完成到达位置:Lat=$latitude, Lng=$longitude');
|
||||
// print('✅ [DevicesCubit] 监听更新已完成到达位置:Lat=$latitude, Lng=$longitude');
|
||||
_logger.logWithLevel('✅ [DevicesCubit] 监听更新已完成到达位置:Lat=$latitude, Lng=$longitude');
|
||||
}
|
||||
|
||||
//获取已到达的点的经纬度
|
||||
@@ -418,6 +430,7 @@ class DevicesCubit extends Cubit<DevicesState> {
|
||||
// 重置已到达记录
|
||||
void resetArrivedLocation() {
|
||||
emit(state.copyWith(arriLatitude: null, arriLongitude: null));
|
||||
print('🔄 [DevicesCubit] 已重置到达位置');
|
||||
//print('🔄 [DevicesCubit] 已重置到达位置');
|
||||
_logger.logWithLevel('🔄 [DevicesCubit] 已重置到达位置');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// lib/features/devices/domain/services/path_planning_service.dart
|
||||
import 'dart:collection';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import '../../../core/logging/i_logger_service.dart';
|
||||
import '../data/models/device_add_path_point_model.dart';
|
||||
|
||||
class PathPlanningService {
|
||||
@@ -8,6 +10,8 @@ class PathPlanningService {
|
||||
factory PathPlanningService() => _instance;
|
||||
PathPlanningService._internal();
|
||||
|
||||
final ILoggerService _logger = GetIt.I<ILoggerService>();
|
||||
|
||||
// 🔥 核心:全局唯一的可变队列
|
||||
final Queue<DeviceAddPathPointModel> locationQueue = Queue();
|
||||
|
||||
@@ -18,8 +22,10 @@ class PathPlanningService {
|
||||
/// 🔥 存:生成路径后立即存储
|
||||
void updateQueue(List<DeviceAddPathPointModel> locations) {
|
||||
debugPrint('进入[PathPlanningService]updateQueue方法尝试更新空队列');
|
||||
_logger.logWithLevel('进入[PathPlanningService]updateQueue方法尝试更新空队列');
|
||||
if (locations.isEmpty) {
|
||||
debugPrint('⚠️ [PathPlanningService] 尝试更新空队列');
|
||||
_logger.logWithLevel('⚠️ [PathPlanningService] 尝试更新空队列');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -28,20 +34,26 @@ class PathPlanningService {
|
||||
currentIndex = 0;
|
||||
totalCount = locations.length;
|
||||
|
||||
debugPrint('✅ [PathPlanningService] 队列已更新,共 $totalCount 个点');
|
||||
//debugPrint('✅ [PathPlanningService] 队列已更新,共 $totalCount 个点');
|
||||
_logger.logWithLevel('✅ [PathPlanningService] 队列已更新,共 $totalCount 个点');
|
||||
}
|
||||
|
||||
/// 🔥 取:发送时取出第一个点
|
||||
DeviceAddPathPointModel? removeFirst() {
|
||||
if (locationQueue.isEmpty) {
|
||||
debugPrint('⚠️ [PathPlanningService] 队列为空');
|
||||
//debugPrint('⚠️ [PathPlanningService] 队列为空');
|
||||
_logger.logWithLevel('⚠️ [PathPlanningService] 队列为空');
|
||||
return null;
|
||||
}
|
||||
|
||||
final point = locationQueue.removeFirst();
|
||||
currentIndex++;
|
||||
|
||||
debugPrint(
|
||||
// debugPrint(
|
||||
// '📍 [PathPlanningService] 取出第 $currentIndex/$totalCount 个点:'
|
||||
// '${point.latitude}, ${point.longitude}',
|
||||
// );
|
||||
_logger.logWithLevel(
|
||||
'📍 [PathPlanningService] 取出第 $currentIndex/$totalCount 个点:'
|
||||
'${point.latitude}, ${point.longitude}',
|
||||
);
|
||||
@@ -63,7 +75,8 @@ class PathPlanningService {
|
||||
isRunning = false;
|
||||
currentIndex = 0;
|
||||
totalCount = 0;
|
||||
debugPrint('🧹 [PathPlanningService] 队列已清空');
|
||||
//debugPrint('🧹 [PathPlanningService] 队列已清空');
|
||||
_logger.logWithLevel('🧹 [PathPlanningService] 队列已清空');
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -23,11 +23,11 @@ class MyCubit extends Cubit<MyState> {
|
||||
try {
|
||||
final params = UpdateNameParams(nickName);
|
||||
final result = await _updateNameUsecase(params);
|
||||
print("$result 修改名称结构");
|
||||
//print("$result 修改名称结构");
|
||||
|
||||
// 4. 恢复 fold 逻辑,处理 UseCase 返回结果(关键:更新 UI 状态)
|
||||
result.fold((failure) => emit(state.copyWith(isLoading: false, errorMessage: failure.message ?? '修改昵称失败')), (successCode) {
|
||||
print("$successCode 修改名称结构");
|
||||
//print("$successCode 修改名称结构");
|
||||
final isSuccess = successCode == 200;
|
||||
if (isSuccess) {
|
||||
emit(state.copyWith(isLoading: false, errorMessage: '', nickName: nickName));
|
||||
|
||||
@@ -2,12 +2,15 @@ import 'dart:ffi';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
|
||||
import '../../../../core/logging/i_logger_service.dart';
|
||||
import '../../../../core/storage/user_storage.dart';
|
||||
|
||||
class RemoteHttpDatasource {
|
||||
final Dio _dio;
|
||||
final UserStorage _userStorage; // 🔥 新增字段
|
||||
final ILoggerService _logger = GetIt.I<ILoggerService>();
|
||||
|
||||
|
||||
RemoteHttpDatasource(this._dio, this._userStorage);
|
||||
@@ -44,23 +47,26 @@ class RemoteHttpDatasource {
|
||||
print("请求权限的数据,$deviceId,");
|
||||
try {
|
||||
final responseData = response.data as Map<String, dynamic>;
|
||||
debugPrint("📊 [HTTP 响应] 完整数据:$responseData");
|
||||
//debugPrint("📊 [HTTP 响应] 完整数据:$responseData");
|
||||
_logger.log("📊 [HTTP 响应] 完整数据:$responseData");
|
||||
|
||||
// 🔥 关键:先获取响应的 data 字段,再获取 remoteControl
|
||||
final dataField = responseData['data'] as Map<String, dynamic>?;
|
||||
if (dataField != null) {
|
||||
final bool hasRemoteControl = dataField['remoteControl'] as bool? ?? false;
|
||||
|
||||
debugPrint("✅ [HTTP 响应] remoteControl=$hasRemoteControl");
|
||||
|
||||
//debugPrint("✅ [HTTP 响应] remoteControl=$hasRemoteControl");
|
||||
_logger.log("✅ [HTTP 响应] remoteControl=$hasRemoteControl");
|
||||
// 🔥 直接返回 remoteControl 的布尔值
|
||||
return hasRemoteControl;
|
||||
} else {
|
||||
debugPrint("❌ [HTTP 响应] 缺少 data 字段");
|
||||
//debugPrint("❌ [HTTP 响应] 缺少 data 字段");
|
||||
_logger.log("❌ [HTTP 响应] 缺少 data 字段");
|
||||
return false;
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('❌ [RemoteHttp] 解析响应失败:$e');
|
||||
// debugPrint('❌ [RemoteHttp] 解析响应失败:$e');
|
||||
_logger.log('❌ [RemoteHttp] 解析响应失败:$e');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,9 @@ import 'dart:convert';
|
||||
import 'dart:ffi';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
|
||||
import '../../../../core/logging/i_logger_service.dart';
|
||||
import '../../../../core/network/protocol_decoder.dart';
|
||||
import '../../../../core/network/tcp/tcp_client.dart';
|
||||
import '../../../../core/protocol/machine_protocol_codec.dart';
|
||||
@@ -19,6 +21,7 @@ class RemoteControlRepositoryImpl implements RemoteControlRepository {
|
||||
final DiffSteerUseCase _diffSteer;
|
||||
final RemoteHttpDatasource _remoteHttp;
|
||||
final RemoteTcpDatasource _remoteTcp;
|
||||
final ILoggerService _logger = GetIt.I<ILoggerService>();
|
||||
|
||||
|
||||
RemoteControlRepositoryImpl(this._tcpClient, this._diffSteer, this._remoteHttp, this._remoteTcp);
|
||||
@@ -34,9 +37,13 @@ class RemoteControlRepositoryImpl implements RemoteControlRepository {
|
||||
final speeds = _diffSteer.calculate(status.originX, status.originY);
|
||||
|
||||
|
||||
debugPrint('🎮 [摇杆数据] originX: ${status.originX}, originY: ${status.originY}');
|
||||
debugPrint('⚙️ [电机速度] left: ${speeds['left']}, right: ${speeds['right']}');
|
||||
debugPrint('🚨 [急停状态] emergency: ${status.isEmergency ? 1 : 0}');
|
||||
//debugPrint('🎮 [摇杆数据] originX: ${status.originX}, originY: ${status.originY}');
|
||||
_logger.logWithLevel('⚙️ [电机速度] left: ${speeds['left']}, right: ${speeds['right']}');
|
||||
|
||||
//debugPrint('⚙️ [电机速度] left: ${speeds['left']}, right: ${speeds['right']}');
|
||||
_logger.logWithLevel('⚙️ [电机速度] left: ${speeds['left']}, right: ${speeds['right']}');
|
||||
//debugPrint('🚨 [急停状态] emergency: ${status.isEmergency ? 1 : 0}');
|
||||
_logger.logWithLevel('🚨 [急停状态] emergency: ${status.isEmergency ? 1 : 0}');
|
||||
|
||||
// 2. 调用 Codec:仅生成协议要求的 8 字节 Payload 负载数据
|
||||
final payload = MachineProtocolCodec.encodeRemoteControlPayload(
|
||||
@@ -65,10 +72,12 @@ class RemoteControlRepositoryImpl implements RemoteControlRepository {
|
||||
Future<bool> requestControlPermission( String deviceName, String deviceId) async {
|
||||
try {
|
||||
_remoteTcp.sendSwitchControlRequest(deviceName);
|
||||
debugPrint('🔑 [RemoteControl] TCP已发送权限请求');
|
||||
// debugPrint('🔑 [RemoteControl] TCP已发送权限请求');
|
||||
_logger.logWithLevel('🔑 [RemoteControl] TCP已发送权限请求');
|
||||
return await _remoteHttp.requestRemoteControlViaHttp(deviceName, deviceId);
|
||||
} catch (e) {
|
||||
debugPrint('❌ [RemoteControl] 权限请求失败:$e');
|
||||
//sdebugPrint('❌ [RemoteControl] 权限请求失败:$e');
|
||||
_logger.logWithLevel('❌ [RemoteControl] 权限请求失败:$e');
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,9 +7,11 @@ import 'package:dart_ping/dart_ping.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:maibu_satabot_v2/features/remote_control/data/models/running_status_model.dart';
|
||||
import 'package:maibu_satabot_v2/features/remote_control/presentation/bloc/remote_control_state.dart';
|
||||
|
||||
import '../../../../core/logging/i_logger_service.dart';
|
||||
import '../../../../core/network/net_message_dispatcher.dart';
|
||||
import '../../../devices/domain/entities/running_status_entity.dart';
|
||||
import '../../domain/entities/machine_control_status_entity.dart';
|
||||
@@ -25,6 +27,8 @@ class RemoteControlCubit extends Cubit<RemoteControlState> {
|
||||
StreamSubscription? _stringMessageSub; //
|
||||
static const platform = MethodChannel('com.maibu.satabot/ping');
|
||||
int _currentPing = 50;
|
||||
final ILoggerService _logger = GetIt.I<ILoggerService>();
|
||||
|
||||
|
||||
|
||||
RemoteControlCubit(this._repository, this._requestControlPermissionUseCase, this.dispatcher)
|
||||
@@ -39,26 +43,32 @@ class RemoteControlCubit extends Cubit<RemoteControlState> {
|
||||
}
|
||||
|
||||
void _initStringMessageListener() {
|
||||
print('>>> [RemoteControl] begin 初始化 0x02 字符串监听器');
|
||||
//print('>>> [RemoteControl] begin 初始化 0x02 字符串监听器');
|
||||
_logger.logWithLevel('>>> [RemoteControl] begin 初始化 0x02 字符串监听器');
|
||||
_stringMessageSub?.cancel();
|
||||
|
||||
_stringMessageSub = dispatcher.onStringMessage().listen((message) async {
|
||||
print('>>> [RemoteControl] 收到 0x02 字符串推送:$message');
|
||||
// print('>>> [RemoteControl] 收到 0x02 字符串推送:$message');
|
||||
_logger.logWithLevel('>>> [RemoteControl] 收到 0x02 字符串推送:$message');
|
||||
|
||||
if (message.isEmpty) {
|
||||
print('⚠️ [RemoteControl] 消息为空,跳过解析');
|
||||
//print('⚠️ [RemoteControl] 消息为空,跳过解析');
|
||||
_logger.logWithLevel('⚠️ [RemoteControl] 消息为为空,跳过解析');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 🔥 关键:使用与 DeviceStatusBloc 相同的解析方法
|
||||
print('>>> [RemoteControl] 🔍 开始解析数据(使用 fromFields)...');
|
||||
// print('>>> [RemoteControl] 🔍 开始解析数据(使用 fromFields)...');
|
||||
_logger.logWithLevel('>>> [RemoteControl] 🔍 开始解析数据(使用 fromFields)...');
|
||||
|
||||
final fields = message.trim().split(',');
|
||||
print('>>> [RemoteControl] 字段数量:${fields.length}');
|
||||
//print('>>> [RemoteControl] 字段数量:${fields.length}');
|
||||
_logger.logWithLevel('>>> [RemoteControl] 字段数量:${fields.length}');
|
||||
|
||||
if (fields.length < 18) {
|
||||
print('⚠️ [RemoteControl] 字段不足:${fields.length},期望 ≥18');
|
||||
//print('⚠️ [RemoteControl] 字段不足:${fields.length},期望 ≥18');
|
||||
_logger.logWithLevel('⚠️ [RemoteControl] 字段不足:${fields.length},期望 ≥18');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -83,13 +93,15 @@ class RemoteControlCubit extends Cubit<RemoteControlState> {
|
||||
|
||||
|
||||
} catch (e, stackTrace) {
|
||||
print('>>> [RemoteControl] ❌ 解析失败:$e');
|
||||
// print('>>> [RemoteControl] ❌ 解析失败:$e');
|
||||
_logger.logWithLevel('>>> [RemoteControl] ❌ 解析失败:$e');
|
||||
// print('>>> [RemoteControl] ❌ 堆栈跟踪:$stackTrace');
|
||||
// print('>>> [RemoteControl] ❌ 原始消息:$message');
|
||||
}
|
||||
});
|
||||
|
||||
print('>>> [RemoteControl] ✅ 0x02 字符串监听器已建立完成');
|
||||
//print('>>> [RemoteControl] ✅ 0x02 字符串监听器已建立完成');
|
||||
_logger.logWithLevel('>>> [RemoteControl] ✅ 0x02 字符串监听器已建立完成');
|
||||
}
|
||||
|
||||
|
||||
@@ -109,7 +121,8 @@ class RemoteControlCubit extends Cubit<RemoteControlState> {
|
||||
battery: int.tryParse(newStatus.battery) ?? 0,
|
||||
));
|
||||
|
||||
debugPrint('✅ [更新运行状态] 电压:${newStatus.voltage}V, 电量:${newStatus.battery}%');
|
||||
// debugPrint('✅ [更新运行状态] 电压:${newStatus.voltage}V, 电量:${newStatus.battery}%');
|
||||
_logger.logWithLevel('✅ [更新运行状态] 电压:${newStatus.voltage}V, 电量:${newStatus.battery}%');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,11 +137,13 @@ class RemoteControlCubit extends Cubit<RemoteControlState> {
|
||||
// });
|
||||
// }
|
||||
Future<void> _initPacketListener() async {
|
||||
print('>>> [RemoteControl] begin 初始化 0x12 监听器');
|
||||
//print('>>> [RemoteControl] begin 初始化 0x12 监听器');
|
||||
_logger.logWithLevel('>>> [RemoteControl] begin 0x12 监听器');
|
||||
_kickOutSub?.cancel(); // 防止重复监听
|
||||
|
||||
_kickOutSub = dispatcher.onCommand(0x12).listen((packet) {
|
||||
print('>>> [RemoteControl] 收到 0x12 原始包,payload 长度=${packet.payload.length}');
|
||||
//print('>>> [RemoteControl] 收到 0x12 原始包,payload 长度=${packet.payload.length}');
|
||||
_logger.logWithLevel('>>> [RemoteControl] 收到 0x12 原始包,payload 长度=${packet.payload.length}');
|
||||
|
||||
try {
|
||||
// 🔥 关键:手动去掉最后 2 个 CRC 字节
|
||||
@@ -139,7 +154,8 @@ class RemoteControlCubit extends Cubit<RemoteControlState> {
|
||||
jsonString = utf8.decode(packet.payload);
|
||||
}
|
||||
|
||||
print('>>> [RemoteControl] 去除 CRC 后的 JSON: $jsonString');
|
||||
//print('>>> [RemoteControl] 去除 CRC 后的 JSON: $jsonString');
|
||||
_logger.logWithLevel('>>> [RemoteControl] 去除 CRC 后的 JSON: $jsonString');
|
||||
|
||||
final jsonMap = jsonDecode(jsonString);
|
||||
|
||||
@@ -148,12 +164,14 @@ class RemoteControlCubit extends Cubit<RemoteControlState> {
|
||||
final platform = jsonMap['platform'];
|
||||
final respondData = jsonMap['respond'];
|
||||
|
||||
print('>>> [RemoteControl] 📋 requestType=$requestType, platform=$platform');
|
||||
//print('>>> [RemoteControl] 📋 requestType=$requestType, platform=$platform');
|
||||
_logger.logWithLevel('>>> [RemoteControl] 📋 requestType=$requestType, platform=$platform');
|
||||
|
||||
// 情况 1: 响应格式 - {"respond":{"switchResult":true,"deviceId":"...","holder":"you"}}
|
||||
if (respondData != null && respondData is Map) {
|
||||
final switchResult = respondData['switchResult'];
|
||||
print('>>> [RemoteControl] 📊 收到切换结果响应:switchResult = $switchResult');
|
||||
//print('>>> [RemoteControl] 📊 收到切换结果响应:switchResult = $switchResult');
|
||||
_logger.logWithLevel('>>> [RemoteControl] 📊 收到切换结果响应:switchResult = $switchResult');
|
||||
|
||||
if (!isClosed) {
|
||||
if (switchResult == true) {
|
||||
@@ -171,33 +189,39 @@ class RemoteControlCubit extends Cubit<RemoteControlState> {
|
||||
else if (requestType == 'switch_control') {
|
||||
// 🔥 关键判断:只有当是其他平台(web)请求时才弹窗
|
||||
if (platform != null && platform.toString().toLowerCase() != 'app') {
|
||||
print('>>> [RemoteControl] 🚨 $platform 端请求控制权,打开弹窗询问用户');
|
||||
//print('>>> [RemoteControl] 🚨 $platform 端请求控制权,打开弹窗询问用户');
|
||||
_logger.logWithLevel('>>> [RemoteControl] 🚨 $platform 端请求控制权,打开弹窗询问用户');
|
||||
if (!isClosed) {
|
||||
emit(state.copyWith(showPermissionRequestDialog: true));
|
||||
}
|
||||
} else {
|
||||
print('>>> [RemoteControl] ℹ️ APP 自己的请求回显,忽略不弹窗');
|
||||
//print('>>> [RemoteControl] ℹ️ APP 自己的请求回显,忽略不弹窗');
|
||||
_logger.logWithLevel('>>> [RemoteControl] ℹ️ APP 自己的请求回显,忽略不弹窗');
|
||||
}
|
||||
}
|
||||
// 情况 3: 异地登录通知 - {"request":"have_logged_in",...}
|
||||
else if (requestType == 'have_logged_in') {
|
||||
print('>>> [RemoteControl] ⚠️ 检测到异地登录,打开弹窗提示');
|
||||
//print('>>> [RemoteControl] ⚠️ 检测到异地登录,打开弹窗提示');
|
||||
_logger.logWithLevel('>>> [RemoteControl] ⚠️ 检测到异地登录,打开弹窗提示');
|
||||
if (!isClosed) {
|
||||
emit(state.copyWith(showPermissionRequestDialog: true));
|
||||
}
|
||||
}
|
||||
else {
|
||||
print('>>> [RemoteControl] ℹ️ 未知类型的 0x12 包,忽略');
|
||||
// print('>>> [RemoteControl] ℹ️ 未知类型的 0x12 包,忽略');
|
||||
_logger.logWithLevel('>>> [RemoteControl] ℹ️ 未知类型的 0x12 包,忽略');
|
||||
}
|
||||
} catch (e) {
|
||||
print('>>> [RemoteControl] ❌ 解析失败:$e');
|
||||
//print('>>> [RemoteControl] ❌ 解析失败:$e');
|
||||
_logger.logWithLevel('>>> [RemoteControl] ❌ 解析失败:$e');
|
||||
}
|
||||
});
|
||||
final c = getNetworkDelay();
|
||||
emit(state.copyWith(
|
||||
ping: await c, // 这里直接使用异步返回的数值
|
||||
));
|
||||
print('>>> [RemoteControl] ✅ 0x12 监听器已建立完成');
|
||||
//print('>>> [RemoteControl] ✅ 0x12 监听器已建立完成');
|
||||
_logger.logWithLevel('>>> [RemoteControl] ✅ 0x12 监听器已建立完成');
|
||||
}
|
||||
|
||||
|
||||
@@ -222,7 +246,8 @@ class RemoteControlCubit extends Cubit<RemoteControlState> {
|
||||
|
||||
// 4. 更新功能开关 (比如割刀速度、灯光、点火等)
|
||||
void updateFunction({int? mower, int? lift, int? ignition, bool? emergency}) {
|
||||
debugPrint('🔧 [updateFunction] 调用 - mower: $mower, lift: $lift, ignition: $ignition, emergency: $emergency');
|
||||
// debugPrint('🔧 [updateFunction] 调用 - mower: $mower, lift: $lift, ignition: $ignition, emergency: $emergency');
|
||||
_logger.logWithLevel('🔧 [updateFunction] 调用 - mower: $mower, lift: $lift, ignition: $ignition, emergency: $emergency');
|
||||
final updatedEntity = state.controlEntity.copyWith(
|
||||
mower: mower,
|
||||
lift: lift,
|
||||
@@ -235,6 +260,7 @@ class RemoteControlCubit extends Cubit<RemoteControlState> {
|
||||
isEmergency: emergency ?? state.isEmergency,
|
||||
));
|
||||
debugPrint('✅ [updateFunction] 状态已更新并 emit');
|
||||
_logger.logWithLevel('✅ [updateFunction] 状态已更新并 emit');
|
||||
}
|
||||
|
||||
void updateOriginY(int y) {
|
||||
@@ -322,14 +348,16 @@ class RemoteControlCubit extends Cubit<RemoteControlState> {
|
||||
// 3. 处理结果
|
||||
result.fold(
|
||||
(failure) {
|
||||
print('❌ [RemoteControl] 请求控制权限失败:${failure.message}');
|
||||
//print('❌ [RemoteControl] 请求控制权限失败:${failure.message}');
|
||||
_logger.logWithLevel('❌ [RemoteControl] 请求控制权限失败:${failure.message}');
|
||||
// 可以在这里显示错误提示或重新打开弹窗
|
||||
emit(state.copyWith(showPermissionRequestDialog: true));
|
||||
|
||||
},
|
||||
(success) {
|
||||
// 更新状态
|
||||
print('✅ [RemoteControl] 请求控制权限成功:$success');
|
||||
//print('✅ [RemoteControl] 请求控制权限成功:$success');
|
||||
_logger.logWithLevel('✅ [RemoteControl] 请求控制权限成功:$success');
|
||||
///处理result
|
||||
if(success){
|
||||
emit(state.copyWith(hasPermission: true));
|
||||
@@ -343,12 +371,14 @@ class RemoteControlCubit extends Cubit<RemoteControlState> {
|
||||
}
|
||||
/// 发送底盘指令
|
||||
void sendChassisCommand(int i) {
|
||||
debugPrint(' [底盘指令] ${i}');
|
||||
// debugPrint(' [底盘指令] ${i}');
|
||||
_logger.logWithLevel(' [底盘指令] ${i}');
|
||||
updateFunction(lift: i);
|
||||
}
|
||||
/// 发送割刀指令
|
||||
void sendMowerCommand(int i) {
|
||||
debugPrint(' [割刀指令] ${i}');
|
||||
// debugPrint(' [割刀指令] ${i}');
|
||||
_logger.logWithLevel(' [割刀指令] ${i}');
|
||||
updateFunction(mower: i);
|
||||
}
|
||||
/// 发送点火指令
|
||||
@@ -356,6 +386,7 @@ class RemoteControlCubit extends Cubit<RemoteControlState> {
|
||||
//void updateFunction({int? mower, int? lift, int? ignition, bool? emergency})
|
||||
// updateFunction(mower:0, lift: 0, ignition: i, emergency: false);
|
||||
debugPrint(' [点火指令] ${i}');
|
||||
_logger.logWithLevel(' [点火指令] ${i}');
|
||||
updateFunction(ignition: i);
|
||||
}
|
||||
// 发送障碍物识别指令
|
||||
|
||||
Reference in New Issue
Block a user