diff --git a/lib/features/devices/data/repositories/route_planning_repository_impl.dart b/lib/features/devices/data/repositories/route_planning_repository_impl.dart index 460c8430..a5d985d2 100644 --- a/lib/features/devices/data/repositories/route_planning_repository_impl.dart +++ b/lib/features/devices/data/repositories/route_planning_repository_impl.dart @@ -16,17 +16,17 @@ class RoutePlanningRepositoryImpl implements RoutePlanningRepository { late PathPlanner _planner; final ILoggerService _logger = GetIt.I(); - // 🔥 新增:存储 AppState 的引用 dynamic _appState; - RoutePlanningRepositoryImpl({required this.tcp}) { // ✅ 加 {required} 和类型注解 + RoutePlanningRepositoryImpl({required this.tcp}) { + // ✅ 加 {required} 和类型注解 _planner = PathPlanner(tcp); } @override Future startRoutePlanning(Queue locationQueue) async { // 直接传入 DeviceAddPathPointModel 列表(无需转换) - // print("[底层开始发送指令了]"); + // print("[底层开始发送指令了]"); _logger.log("开始路径规划"); final List locations = locationQueue.toList(); _planner.startRoutePlanning(locations); @@ -55,6 +55,7 @@ class RoutePlanningRepositoryImpl implements RoutePlanningRepository { // TODO: implement getCurrentAppState return _appState; } + void setAppState(dynamic state) { _appState = state; } @@ -64,8 +65,7 @@ class RoutePlanningRepositoryImpl implements RoutePlanningRepository { // true = 使用新的 ACK 握手机制 (将来) // false = 使用现有逻辑 (现在) class PathPlanningMode { - - static bool useAckHandshake = false; // 默认使用现有逻辑 + static bool useAckHandshake = false; // 默认使用现有逻辑 } // PathPlanner @@ -78,9 +78,8 @@ class PathPlanner { bool _isPaused = false; bool _isStopped = false; // 新增:ACK 握手状态 (仅新模式使用) - bool _waitingForAck = false; // 是否正在等待 ACK 确认 - int _currentPointIndex = 0; // 🔥 当前发送的点编号(从 0 开始递增) - + bool _waitingForAck = false; // 是否正在等待 ACK 确认 + int _currentPointIndex = 0; // 🔥 当前发送的点编号(从 0 开始递增) PathPlanner(this.tcpClient); @@ -102,7 +101,7 @@ class PathPlanner { isStart = false; if (_isStopped) { //print("⏹️ 已停止作业,清空队列"); - _logger.log("[路径点发送完毕]"); + _logger.log("[路径点发送完毕] 停止"); locationQueue.clear(); // 清空剩余队列 } else { //print("[路径点发送完毕]"); @@ -123,10 +122,11 @@ class PathPlanner { //print("====Lat:${entity.latitude} ====Lng:${entity.longitude}"); _logger.log("[发送指令]"); - tcpClient.sendPathPoint( routePlanSendEntity); + tcpClient.sendPathPoint(routePlanSendEntity); isStart = true; } -/// 开始 + + /// 开始 void startRoutePlanning(List locations) { /// locationQueue.addAll(locations); /// isStart = true; @@ -138,30 +138,36 @@ class PathPlanner { isStart = true; _isPaused = false; // 🔥 只在第一次调用时重置 _isStopped = false; // 🔥 只在第一次调用时重置 - // print("[首次加载] 队列长度:${locations.length}"); + // print("[首次加载] 队列长度:${locations.length}"); _logger.log("[首次加载] 队列长度:${locations.length}"); } else { // 🔥 后续调用(来自 Dispatcher)只更新队列,不重置状态 //print("[更新队列] 当前处于 ${_isPaused ? '暂停' : _isStopped ? '停止' : '工作'} 状态"); - _logger.log("[更新队列] 当前处于 ${_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; } @@ -169,47 +175,36 @@ class PathPlanner { // sendNextLocation(); // 🔥 根据全局开关选择使用哪套发送逻辑 if (PathPlanningConfig.useAckHandshake) { - // print("🔥 使用 ACK 握手机制发送路径点"); + // print("🔥 使用 ACK 握手机制发送路径点"); _logger.log("[使用 ACK 握手机制发送路径点]"); sendNextLocationWithAck(sl()); } else { - // print("🔥 使用现有逻辑发送路径点"); + // print("🔥 使用现有逻辑发送路径点"); _logger.log("[使用现有逻辑发送路径点]"); sendNextLocation(); } - } + /// 暂停 void pauseRPWork() { //print("⏸️ 暂停作业,设置_isPaused = true"); _logger.log("[暂停作业,设置_isPaused = true]"); _isPaused = true; - var entity = new RoutePlanSendEntity( - commandType: 0x01, - pointCounts: 2, - targetLatitude: 0, - targetLongitude: 0, - speed: 0, - ); - // print("📡 发送暂停指令到设备..."); + var entity = new RoutePlanSendEntity(commandType: 0x01, pointCounts: 2, targetLatitude: 0, targetLongitude: 0, speed: 0); + // print("📡 发送暂停指令到设备..."); _logger.log("[发送暂停指令到设备...]"); tcpClient.sendDeviceStateChange(entity); } + /// 恢复 void resumeRPWork() { //print("▶️ 恢复作业,设置_isPaused = false"); _logger.log("[恢复作业,设置_isPaused = false]"); _isPaused = false; - var entity = new RoutePlanSendEntity( - commandType: 0x01, - pointCounts: 3, - targetLatitude: 0, - targetLongitude: 0, - speed: 0, - ); - // print("📡 发送恢复指令到设备..."); + var entity = new RoutePlanSendEntity(commandType: 0x01, pointCounts: 3, targetLatitude: 0, targetLongitude: 0, speed: 0); + // print("📡 发送恢复指令到设备..."); _logger.log("[发送恢复指令到设备...]"); tcpClient.sendDeviceStateChange(entity); @@ -228,52 +223,38 @@ 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(); - - - - var entity = new RoutePlanSendEntity( - commandType: 0x01, - pointCounts: 0, - targetLatitude: 0, - targetLongitude: 0, - speed: 0, - ); - // print("📡 发送停止指令到设备..."); - _logger.log("[发送停止指令到设备...]"); + var entity = new RoutePlanSendEntity(commandType: 0x01, pointCounts: 0, targetLatitude: 0, targetLongitude: 0, speed: 0); + // print("📡 发送停止指令到设备..."); + _logger.log("[发送停止指令到设备...] 按下停止按钮"); tcpClient.sendDeviceStateChange(entity); // 发送完成工作播报指令 - sendWorkCompleteBroadcast(); + //sendWorkCompleteBroadcast(); } //发送完成工作播报指令 void sendWorkCompleteBroadcast() { - var entity = new RoutePlanSendEntity( - commandType: 0x01, - pointCounts: 0xFF, - targetLatitude: 0, - targetLongitude: 0, - speed: 0, - ); - // print("发送完成工作播报指令..."); + var entity = new RoutePlanSendEntity(commandType: 0x01, pointCounts: 0xFF, targetLatitude: 0, targetLongitude: 0, speed: 0); + // print("发送完成工作播报指令..."); _logger.log("[发送完成工作播报指令...]"); tcpClient.sendDeviceStateChange(entity); } -// 🔥 新增:ACK 握手机制的发送方法 (新模式) + // 🔥 新增:ACK 握手机制的发送方法 (新模式) void sendNextLocationWithAck(NetMessageDispatcher dispatcher) { - // print("[在发送指令 sendNextLocationWithAck 方法中 - ACK 模式]"); + // print("[在发送指令 sendNextLocationWithAck 方法中 - ACK 模式]"); _logger.log("[在发送指令 sendNextLocationWithAck 方法中 - ACK 模式]"); // 🔥 关键检查:如果正在等待 ACK,不要重复发送 if (_waitingForAck) { @@ -292,7 +273,7 @@ class PathPlanner { if (_isStopped || (isStart && locationQueue.isEmpty)) { isStart = false; if (_isStopped) { - // print("️ 已停止作业,清空队列"); + // print("️ 已停止作业,清空队列"); _logger.log("[已停止作业,清空队列]"); locationQueue.clear(); // 清空剩余队列 } else { @@ -318,17 +299,13 @@ class PathPlanner { //print("====Lat:${entity.latitude} ====Lng:${entity.longitude}"); _logger.log("[发送路径点] Lat:${entity.latitude} Lng:${entity.longitude}"); - tcpClient.sendPathPoint( routePlanSendEntity); + tcpClient.sendPathPoint(routePlanSendEntity); isStart = true; // 🔥 关键:标记为正在等待 ACK _waitingForAck = true; - dispatcher.setExpectedPointIndex(pointIndex); // 🔥 通知 Dispatcher + dispatcher.setExpectedPointIndex(pointIndex); // 🔥 通知 Dispatcher //sprint(" 已锁定发送,等待 ACK 确认 (0x02)..."); _logger.log("[已锁定发送,等待 ACK 确认 (0x02)...]"); - } - } - - diff --git a/lib/features/home/presentation/widgets/common/tracepoint.dart b/lib/features/home/presentation/widgets/common/tracepoint.dart index a7986387..67ae54c6 100644 --- a/lib/features/home/presentation/widgets/common/tracepoint.dart +++ b/lib/features/home/presentation/widgets/common/tracepoint.dart @@ -5,7 +5,7 @@ import 'dart:async'; /// 轨迹模式枚举 enum TPMode { NAVIGATION, // 导航模式:绘制规划已完成路径和当前路径 - LOCATION, // 定位模式:仅绘制当前点 + LOCATION, // 定位模式:仅绘制当前点(永远只有1个) TRACK, // 轨迹模式:实时绘制历史轨迹 } @@ -19,58 +19,46 @@ enum TPAction { class TryLock { bool _locked = false; - /// 尝试获取锁 - /// return: true-获取成功,false-已被锁定 bool tryLock() { if (_locked) return false; _locked = true; return true; } - /// 释放锁 void release() { _locked = false; } - /// 检查是否已锁定 bool get isLocked => _locked; } // ======================== 环形队列实现 ======================== class CircQueue { - final int capacity; // 队列容量 - final List buffer; // 存储缓冲区 - int head = 0; // 队头索引 - int tail = 0; // 队尾索引 - bool isFull = false; // 队列满标志 - final bool deepCopy; // 是否深拷贝元素 - final int maxMemoryBytes; // 最大内存限制(字节) - int currentMemoryBytes = 0; // 当前已使用内存 + final int capacity; + final List buffer; + int head = 0; + int tail = 0; + bool isFull = false; + final bool deepCopy; + final int maxMemoryBytes; + int currentMemoryBytes = 0; - CircQueue( - this.capacity, { - this.deepCopy = true, - this.maxMemoryBytes = 1024 * 1024, // 默认1MB - }) : buffer = List.filled(capacity, null); + CircQueue(this.capacity, {this.deepCopy = true, this.maxMemoryBytes = 1024 * 1024}) : buffer = List.filled(capacity, null); - /// 清空队列 void clear() { head = tail; isFull = false; currentMemoryBytes = 0; } - /// 判断队列是否为空 bool isEmpty() { return head == tail && !isFull; } - /// 判断队列是否已满 bool isFullFn() { return isFull; } - /// 估算对象大小(JSON序列化后的字节长度) int _estimateSize(T item) { try { return utf8.encode(jsonEncode(item)).length; @@ -79,7 +67,6 @@ class CircQueue { } } - /// 深拷贝对象 T? _deepClone(T? item) { if (!deepCopy || item == null) return item; try { @@ -89,10 +76,8 @@ class CircQueue { } } - /// 入队操作 bool enter(T item) { final itemSize = _estimateSize(item); - // 检查队列满或内存超限 if (isFullFn() || (currentMemoryBytes + itemSize > maxMemoryBytes)) { return false; } @@ -106,7 +91,6 @@ class CircQueue { return true; } - /// 出队操作 T? out() { if (isEmpty()) return null; @@ -119,7 +103,6 @@ class CircQueue { return _deepClone(item); } - /// 丢弃指定数量的元素 bool discard(int len) { for (int i = 0; i < len; i++) { if (isEmpty()) return false; @@ -135,12 +118,10 @@ class CircQueue { return true; } - /// 获取队列元素个数 int getDepth() { return isFull ? capacity : (tail + capacity - head) % capacity; } - /// 异步入队(支持超时) Future send(T item, {int timeoutMs = 0}) async { final start = DateTime.now().millisecondsSinceEpoch; while (!enter(item)) { @@ -152,7 +133,6 @@ class CircQueue { return true; } - /// 异步出队(支持超时) Future recv({int timeoutMs = 0}) async { final start = DateTime.now().millisecondsSinceEpoch; while (true) { @@ -165,7 +145,6 @@ class CircQueue { } } - /// 转换为列表(用于调试/序列化) List toList() { final result = []; int i = head; @@ -179,22 +158,18 @@ class CircQueue { } class TracePoint { - // 私有成员变量 late final CircQueue _queue; List _tracePoint = []; int _completePointIndex = 0; late final TryLock _lock; TPMode _mode = TPMode.LOCATION; - // 公开的回调(无下划线,外部可访问) void Function(T)? onCurrentPointUpdated; void Function(T)? onCompletePointAdded; - // 测试用方法 bool tryLock() => _lock.tryLock(); void release() => _lock.release(); - /// 构造函数 TracePoint({int queueCapacity = 5, int maxMemoryBytes = 1024 * 1024}) { _queue = CircQueue(queueCapacity, deepCopy: true, maxMemoryBytes: maxMemoryBytes); _lock = TryLock(); @@ -204,12 +179,12 @@ class TracePoint { /// 复位所有状态 void reset() { _queue.clear(); - _tracePoint = []; + _tracePoint.clear(); _completePointIndex = 0; _lock.release(); } - /// 设置工作模式 + /// 设置模式 void setMode(TPMode mode) { reset(); _mode = mode; @@ -221,20 +196,29 @@ class TracePoint { /// 添加/更新轨迹点 void upsert(T point, [TPAction act = TPAction.UPDATE]) { + // 严格按模式分发 switch (_mode) { case TPMode.NAVIGATION: - act == TPAction.UPDATE ? _updateCurrentPoint(point) : _addCompletePoint(point); + if (act == TPAction.UPDATE) { + _updateCurrentPoint(point); + } else { + _addCompletePoint(point); + } break; + case TPMode.LOCATION: + // LOCATION 永远只保留最新一个点 _updateCurrentPoint(point); break; + case TPMode.TRACK: + // TRACK 每个点都直接添加,不进队列 _addCompletePoint(point); break; } } - /// 获取轨迹点列表(深拷贝,线程安全) + /// 获取轨迹列表 List? getTracePoint() { if (_lock.tryLock()) { final trace = List.from(_tracePoint); @@ -246,63 +230,48 @@ class TracePoint { } } - /// 更新当前点(私有方法) + /// 更新当前点(LOCATION 永远只有1个) void _updateCurrentPoint(T point) { - if (point == null) { - print('updateCurrentPoint: point is NULL'); - return; - } - if (_lock.tryLock()) { try { switch (_mode) { case TPMode.NAVIGATION: - if (_tracePoint.isEmpty) { - print('updateCurrentPoint: tracePoint is empty , wait first completed point, discard current point: $point'); - } else { + if (_tracePoint.isNotEmpty) { _tracePoint = _tracePoint.sublist(0, _completePointIndex); _tracePoint.add(point); - // 触发公开的回调(无下划线) onCurrentPointUpdated?.call(point); } break; + case TPMode.LOCATION: - if (_tracePoint.isEmpty) { - _tracePoint.add(point); - } else { - _tracePoint[0] = point; - } - // 触发公开的回调(无下划线) + // 核心:永远清空,只保留最新一个 + _tracePoint.clear(); + _tracePoint.add(point); onCurrentPointUpdated?.call(point); break; + default: break; } } finally { _lock.release(); } - } else { - print('updateCurrentPoint: lock!!!, discard current point: $point'); } } - /// 添加完成点(私有方法) + /// 添加完成点 void _addCompletePoint(T point) { - if (point == null) { - print('addCompletePoint: point is NULL'); - return; - } - if (_lock.tryLock()) { try { + // 先消费队列 while (!_queue.isEmpty()) { final queuedPoint = _queue.out(); if (queuedPoint != null) { _addPoint(queuedPoint); } } + // 添加当前点 _addPoint(point); - // 触发公开的回调(无下划线) onCompletePointAdded?.call(point); } catch (e) { print('addCompletePoint failure: $e'); @@ -310,23 +279,29 @@ class TracePoint { _lock.release(); } } else { - _queue.enter(point); - print('addCompletePoint: lock!!!, enter queue, wait disposing: $point'); + if (_mode != TPMode.TRACK) { + // TRACK 模式不加队列,直接丢弃旧的保证流畅 + _queue.enter(point); + } + print('addCompletePoint: lock!!!'); } } - /// 内部添加点逻辑(私有方法) + /// 内部添加点 void _addPoint(T point) { - if (point == null) { - print('point NULL:'); + if (_mode == TPMode.LOCATION) { + // LOCATION 强制只保留1个 + _tracePoint.clear(); + _tracePoint.add(point); + _completePointIndex = 1; return; } - final hasCurrentPoint = _tracePoint.isNotEmpty && _completePointIndex < _tracePoint.length; + final hasCurrent = _tracePoint.isNotEmpty && _completePointIndex < _tracePoint.length; - if (hasCurrentPoint) { + if (hasCurrent) { _tracePoint = _tracePoint.sublist(0, _completePointIndex); - _tracePoint.insert(_completePointIndex, point); + _tracePoint.add(point); } else { _tracePoint.add(point); } diff --git a/lib/features/home/presentation/widgets/map/testmap_pages.dart b/lib/features/home/presentation/widgets/map/testmap_pages.dart index f3b72073..a03c27d8 100644 --- a/lib/features/home/presentation/widgets/map/testmap_pages.dart +++ b/lib/features/home/presentation/widgets/map/testmap_pages.dart @@ -9,6 +9,7 @@ import 'package:flutter/rendering.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_map/flutter_map.dart'; import 'package:geolocator/geolocator.dart'; +import 'package:get_it/get_it.dart'; import 'package:go_router/go_router.dart'; import 'package:http/http.dart' as http; import 'package:latlong2/latlong.dart'; @@ -46,6 +47,7 @@ import 'package:shared_preferences/shared_preferences.dart'; import 'dart:ui' as ui; import '../path_list_pages.dart'; +import 'package:maibu_satabot_v2/core/logging/i_logger_service.dart'; // 定义轨迹点类型(经纬度) typedef PlotPoint = LatLng; @@ -92,6 +94,8 @@ class MapPageEnterprise extends StatefulWidget { } class _MapPageEnterpriseState extends State { + final ILoggerService _logger = GetIt.I(); + final _dispatcher = sl(); bool _isRefreshing = false; // 新增:页面刷新状态标志 bool _isVideoDialogOpen = false; // 控制视频弹窗显示 @@ -270,6 +274,8 @@ class _MapPageEnterpriseState extends State { // 新增:加载本地存储数据方法 Future _loadSavedData() async { try { + _traceManager.setMode(TPMode.LOCATION); + final prefs = await SharedPreferences.getInstance(); final workStatusStr = prefs.getString(kSavedWorkStatus); if (workStatusStr != null) { @@ -929,9 +935,12 @@ class _MapPageEnterpriseState extends State { // ========== 新增:页面刷新初始化方法 ========== Future _handleRefresh() async { if (_workStatus != WorkStatus.idle) { - _showPageToast(message: "请先停止作业再刷新页面哦!", type: ToastType.warn); - return; + _stopWork(); } + //if (_workStatus != WorkStatus.idle) { + // _showPageToast(message: "请先停止作业再刷新页面哦!", type: ToastType.warn); + // return; + //} _cancelAllToast(); if (_isRefreshing) return; @@ -1026,7 +1035,6 @@ class _MapPageEnterpriseState extends State { _workStatus = WorkStatus.idle; isStartWork = false; isStopWork = false; - _mapCenter = _getDefaultValidCenter(); // 轨迹管理器重置 _traceManager.reset(); @@ -1407,6 +1415,8 @@ class _MapPageEnterpriseState extends State { _traceManager.upsert(_currentWgsLatLng as PlotPoint, TPAction.UPDATE); tracePoint = _traceManager.getTracePoint(); gctracePoint = batchWgs84ToGcj02(tracePoint!); + _logger.log("[转换后gctracePoint]: $gctracePoint"); + _logger.log("[当前轨迹模式]:${_traceManager.getMode()}"); // 这里必须加 () //_currentLatLng = gcjPoint; // ✅ 状态变量在setState内更新 if (_isValidLatLng(gcjPoint.latitude, gcjPoint.longitude)) { @@ -1753,6 +1763,8 @@ class _MapPageEnterpriseState extends State { } void _stopWork() async { + _logger.log('按下停止按钮'); + if (mounted) { setState(() { isStartWork = false; // 🔥 关键:停止作业标志 @@ -1761,6 +1773,10 @@ class _MapPageEnterpriseState extends State { _showControlModeWarn = false; isStopWork = true; //已经点击过停止 isreceiveFirstCompletePoint = false; + _traceManager.setMode(TPMode.LOCATION); + _traceManager.reset(); + tracePoint = []; + gctracePoint = []; }); } @@ -1769,10 +1785,6 @@ class _MapPageEnterpriseState extends State { _saveDataToLocal(); // 🔥 保存暂停状态 _showPageToast(message: "作业已停止", type: ToastType.error); - _traceManager.setMode(TPMode.LOCATION); - _traceManager.reset(); - tracePoint = []; - gctracePoint = []; } /// 继续作业 @@ -2336,8 +2348,10 @@ class _MapPageEnterpriseState extends State { if (arriLatitude != null && arriLongitude != null) { targetLatLng = LatLng(arriLatitude, arriLongitude); if (__isValidLatLng(targetLatLng)) { - debugPrint("${targetLatLng}收到完成点arriLatitude"); - if (!isreceiveFirstCompletePoint) { + //debugPrint("${targetLatLng}收到完成点arriLatitude"); + if (!isreceiveFirstCompletePoint && isStartWork && _workStatus == WorkStatus.working) { + debugPrint("收到完成点arriLatitude"); + _traceManager.setMode(TPMode.NAVIGATION); isreceiveFirstCompletePoint = true; }