From e3026f61bb4ebc132bea02ad709ad276808f9228 Mon Sep 17 00:00:00 2001 From: mmc <1556375442@qq.com> Date: Fri, 17 Apr 2026 10:00:39 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BB=88=E4=BA=8E=E8=A7=A3=E5=86=B3=E4=BA=86?= =?UTF-8?q?=20=E7=AC=AC=E4=BA=8C=E6=AC=A1=E4=BD=9C=E4=B8=9A=E4=B9=8B?= =?UTF-8?q?=E5=90=8E=E7=9A=84=20=E8=B7=AF=E5=BE=84=E4=B8=8D=E6=B8=85?= =?UTF-8?q?=E9=99=A4=E7=9A=84=E9=97=AE=E9=A2=98=20=EF=BC=8C=E4=B8=80?= =?UTF-8?q?=E7=9B=B4=E4=BF=9D=E7=95=99=E7=9D=80=E4=B8=8A=E4=B8=80=E6=AC=A1?= =?UTF-8?q?=E4=BD=9C=E4=B8=9A=E5=8F=91=E9=80=81=E7=9A=84=E5=AE=8C=E6=88=90?= =?UTF-8?q?=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/core/network/net_message_dispatcher.dart | 117 ++++++++---------- .../route_planning_repository_impl.dart | 3 + .../presentation/bloc/devices_cubit.dart | 10 +- .../widgets/common/tracepoint.dart | 108 ++++++++-------- .../widgets/map/testmap_pages.dart | 88 +++++++------ .../widgets/work_params_card.dart | 1 - 6 files changed, 157 insertions(+), 170 deletions(-) diff --git a/lib/core/network/net_message_dispatcher.dart b/lib/core/network/net_message_dispatcher.dart index 09d3e640..8ad1673f 100644 --- a/lib/core/network/net_message_dispatcher.dart +++ b/lib/core/network/net_message_dispatcher.dart @@ -23,13 +23,11 @@ class NetMessageDispatcher { final ILoggerService _logger = GetIt.I(); - // 🔥 新增:ACK 握手状态管理 (仅新模式使用) - int _expectedPointIndex = -1; // 期望收到的点编号 - bool _waitingForAck = false; // 是否正在等待 ACK 确认 - bool _waitingForArrived = false; // 是否正在等待到达确认 - int _currentPointIndex = -1; // 当前发送的点索引 - + int _expectedPointIndex = -1; // 期望收到的点编号 + bool _waitingForAck = false; // 是否正在等待 ACK 确认 + bool _waitingForArrived = false; // 是否正在等待到达确认 + int _currentPointIndex = -1; // 当前发送的点索引 // 🔥 新增:设置期望收到的点编号(供 PathPlanner 调用) void setExpectedPointIndex(int index) { @@ -38,7 +36,6 @@ class NetMessageDispatcher { _logger.log('[Dispatcher-ACK] 设置期望点编号:$index'); } - NetMessageDispatcher(this.tcpClient, this.routePlanningRepository, this._pathPlanningService, {this.getAppState}); /// 过滤特定指令的流 @@ -58,12 +55,12 @@ class NetMessageDispatcher { _logger.log('✅ 解码 0x02 成功:$result'); return result; } catch (e) { - // print('❌ 解码 0x02 失败:$e, 原始字节:${p.payload}'); + // print('❌ 解码 0x02 失败:$e, 原始字节:${p.payload}'); _logger.log('❌ 解码 0x02 失败:$e, 原始字节:${p.payload}'); return ''; // 返回空字符串,避免流中断 } }); - /* ///return onCommand(0x02).map((p) => utf8.decode(p.payload));*/ + /* ///return onCommand(0x02).map((p) => utf8.decode(p.payload));*/ } /// 示例解析方法:将 0x12 指令解析为 JSON 并转为模型 @@ -72,15 +69,14 @@ class NetMessageDispatcher { // } Stream> onJsonMessage(int cmd) { //print('🔵 [Dispatcher] 开始监听 0x${cmd.toRadixString(16)} 指令的 JSON 流'); -_logger.log('🔵 [Dispatcher] 监听 0x${cmd.toRadixString(16)} 指令的 JSON 流'); + _logger.log('🔵 [Dispatcher] 监听 0x${cmd.toRadixString(16)} 指令的 JSON 流'); return onCommand(cmd).map((p) { try { final jsonString = utf8.decode(p.payload); //print('🔵 [Dispatcher] 收到 0x${cmd.toRadixString(16)} 原始数据: $jsonString'); -_logger - .log('🔵 [Dispatcher] 监听 0x${cmd.toRadixString(16)} 指令的 JSON 流'); + _logger.log('🔵 [Dispatcher] 监听 0x${cmd.toRadixString(16)} 指令的 JSON 流'); final jsonMap = jsonDecode(jsonString); - // print('🔵 [Dispatcher] JSON 解析成功:$jsonMap'); + // print('🔵 [Dispatcher] JSON 解析成功:$jsonMap'); _logger.log('🔵 [Dispatcher] JSON 解析成功:$jsonMap'); return jsonMap; @@ -94,19 +90,19 @@ _logger //监听路径规划的开始作业的指令的应答 -// if (data[0] == (byte) 0xAB && data[1] == (byte) 0xAA && data[2] == (byte) 0x01) { -// System.out.println("=====下位机回复成功=====" + data[0] + " " + data[1] + " " + data[2] + " " + data[3] + " " + -// data[4] + " " + data[5] + " " + data[6] + " " + data[7] + " " + data[8] + " " + data[9]); -// if (data[5] == (byte) 0x01) { -// Point point = new Point(); -// //发送下一个指令 -// } + // if (data[0] == (byte) 0xAB && data[1] == (byte) 0xAA && data[2] == (byte) 0x01) { + // System.out.println("=====下位机回复成功=====" + data[0] + " " + data[1] + " " + data[2] + " " + data[3] + " " + + // data[4] + " " + data[5] + " " + data[6] + " " + data[7] + " " + data[8] + " " + data[9]); + // if (data[5] == (byte) 0x01) { + // Point point = new Point(); + // //发送下一个指令 + // } /// 监听路径规划的开始作业指令的应答 (0x01 指令) /// 协议格式:AB AA 01 [DATA] CRC(2) AA AB /// 验证头部:data[0]=0xAB, data[1]=0xAA, data[2]=0x01 /// 检查状态位:data[5] == 0x01 表示回复成功 -/* Stream onPathPlanningResponse() { + /* Stream onPathPlanningResponse() { final devicesCubit = sl(); debugPrint('[Dispatcher] 开始监听路径规划指令应答 (CMD: 0x01)'); @@ -196,7 +192,7 @@ _logger return false; }); }*/ - ///新版的ack和现有的融合 + ///新版的ack和现有的融合 /// 监听路径规划指令应答 (现有逻辑 - 不修改) /// 协议格式:AB AA 01 [DATA] CRC(2) AA AB /// 验证头部:data[0]=0xAB, data[1]=0xAA, data[2]=0x01 @@ -205,7 +201,7 @@ _logger final devicesCubit = sl(); //debugPrint('[Dispatcher] 开始监听路径规划指令应答 (CMD: 0x01) - 模式:${PathPlanningConfig.getModeDescription()}'); -_logger.logWithLevel('[Dispatcher] 开始监听路径规划指令应答 (CMD: 0x01) - 模式:${PathPlanningConfig.getModeDescription()}'); + _logger.logWithLevel('[Dispatcher] 开始监听路径规划指令应答 (CMD: 0x01) - 模式:${PathPlanningConfig.getModeDescription()}'); // 🔥 根据配置决定使用哪个流 if (PathPlanningConfig.useAckHandshake) { //debugPrint('⚙️ [Dispatcher] 使用 ACK 握手机制'); @@ -219,7 +215,7 @@ _logger.logWithLevel('[Dispatcher] 开始监听路径规划指令应答 (CMD: 0x final currentState = getAppState!(); if (currentState.toString() == 'AppState.none') { //debugPrint('⚠️ [Dispatcher] 当前 AppState 为 none,停止处理路径规划指令'); - _logger.logWithLevel('⚠️ [Dispatcher] 当前 AppState 为 none,停止处理路径规划指令'); + _logger.logWithLevel('⚠️ [Dispatcher] 当前 AppState 为 none,停止处理路径规划指令'); return false; } } @@ -228,26 +224,24 @@ _logger.logWithLevel('[Dispatcher] 开始监听路径规划指令应答 (CMD: 0x 0xAB, 0xAA, 0x01, ...packet.payload, 0x00, 0x00, // CRC 占位 - 0xAA, 0xAB + 0xAA, 0xAB, ]; // 验证头部协议 - if (fullData[0] == 0xAB && - fullData[1] == 0xAA && - fullData[2] == 0x01) { - - if(fullData[5] == 0x02){ - // debugPrint('下位机回复收到指令 - 完整数据包:${fullData.join(" ")}'); + if (fullData[0] == 0xAB && fullData[1] == 0xAA && fullData[2] == 0x01) { + if (fullData[5] == 0x02) { + // debugPrint('下位机回复收到指令 - 完整数据包:${fullData.join(" ")}'); _logger.logWithLevel('下位机回复收到指令 - 完整数据包:${fullData.join(" ")}'); } debugPrint('[Dispatcher] 下位机回复成功 - 完整数据包:${fullData.join(" ")}'); // 检查状态位 (索引 5 对应 payload 的第 2 个字节) if (fullData.length > 5 && fullData[5] == 0x01) { - // debugPrint('✅ [Dispatcher] 状态位验证通过:0x01 - 可以发送下一个指令'); + // debugPrint('✅ [Dispatcher] 状态位验证通过:0x01 - 可以发送下一个指令'); _logger.logWithLevel('✅ [Dispatcher] 状态位验证通过:0x01 - 可以发送下一个指令'); + ///取全局的路径规划发送实体 queue /// 发送下一个指令 移除上一条数据 - var queue = _pathPlanningService.getQueue(); + var queue = _pathPlanningService.getQueue(); // 🔥 关键:必须先检查空队列! if (queue.isEmpty) { devicesCubit.finishWork(); @@ -260,31 +254,33 @@ _logger.logWithLevel('[Dispatcher] 开始监听路径规划指令应答 (CMD: 0x _logger.logWithLevel('⚠️ [Dispatcher] 队列已经为空,跳过本次响应处理'); return false; } + ///更新完成 jwd var deviceAddPathPointModel = queue.first; var d = deviceAddPathPointModel.latitude; var e = deviceAddPathPointModel.longitude; - devicesCubit.setArrivedLocation( - deviceAddPathPointModel.latitude, - deviceAddPathPointModel.longitude - ); - // debugPrint("准确更新完成的路径点经纬度,$d,$e"); + devicesCubit.setArrivedLocation(deviceAddPathPointModel.latitude, deviceAddPathPointModel.longitude); + // debugPrint("准确更新完成的路径点经纬度,$d,$e"); _logger.logWithLevel("准确更新完成路径点经纬度,$d,$e"); //去除上一条数据 queue.removeFirst(); + /// _pathPlanningService.updateQueue(queue as List); _pathPlanningService.updateQueue(queue.toList()); DateTime now = DateTime.now(); - String timeStr = "${now.hour.toString().padLeft(2, '0')}:" + String timeStr = + "${now.hour.toString().padLeft(2, '0')}:" "${now.minute.toString().padLeft(2, '0')}:" "${now.second.toString().padLeft(2, '0')}"; //debugPrint(' $timeStr [Dispatcher] 准备触发下一个路径点发送,队列剩余:${queue.length}'); _logger.logWithLevel(' $timeStr [Dispatcher] 触发下一个路径点发送,队列剩余:${queue.length}'); + ///如果还剩 0 个数据 就停止发送 - if (queue.length==0) { + if (queue.length == 0) { //debugPrint('⚠️ [Dispatcher] 队列已空,停止发送'); _logger.logWithLevel('⚠️ [Dispatcher] 队列已空,停止发送'); + devicesCubit.setArrivedLocation(0.0, 0.0); // final devicesCubit = sl(); devicesCubit.finishWork(); @@ -293,6 +289,7 @@ _logger.logWithLevel('[Dispatcher] 开始监听路径规划指令应答 (CMD: 0x }); // 手动停止 routePlanningRepository.stopRoutePlanning(); + //debugPrint('⚠️ [Dispatcher] 队列已空,发送停止指令已发送'); _logger.logWithLevel('⚠️ [Dispatcher] 状态位异常:${fullData.length > 5 ? fullData[5].toRadixString(16) : "N/A"}'); return false; @@ -304,7 +301,7 @@ _logger.logWithLevel('[Dispatcher] 开始监听路径规划指令应答 (CMD: 0x _logger.logWithLevel('⚠️ [Dispatcher] 状态位异常:${fullData.length > 5 ? fullData[5].toRadixString(16) : "N/A"}'); } } else { - // debugPrint('❌ [Dispatcher] 协议头验证失败'); + // debugPrint('❌ [Dispatcher] 协议头验证失败'); _logger.logWithLevel('❌ [Dispatcher] 协议头验证失败'); } @@ -313,14 +310,11 @@ _logger.logWithLevel('[Dispatcher] 开始监听路径规划指令应答 (CMD: 0x } } - - - /// 监听特定状态的回复(可复用) /// [commandCode] 指令码 /// [statusIndex] 状态字节在 payload 中的索引 /// [expectedStatus] 期望的状态值 -/* Stream onStatusResponse({ + /* Stream onStatusResponse({ required int commandCode, required int statusIndex, required int expectedStatus, @@ -343,8 +337,8 @@ _logger.logWithLevel('[Dispatcher] 开始监听路径规划指令应答 (CMD: 0x }); }*/ -/// 监听特定状态的回复(ack握手机制) -// 🔥 新增:ACK 握手机制 (新模式) + /// 监听特定状态的回复(ack握手机制) + // 🔥 新增:ACK 握手机制 (新模式) /// 监听路径规划指令应答,实现完整的 ACK 握手机制 /// 协议格式:AB AA 01 [DATA] CRC(2) AA AB /// 上传协议(0x01 路径规划上传): @@ -373,14 +367,11 @@ _logger.logWithLevel('[Dispatcher] 开始监听路径规划指令应答 (CMD: 0x 0xAB, 0xAA, 0x01, ...packet.payload, 0x00, 0x00, // CRC 占位 - 0xAA, 0xAB + 0xAA, 0xAB, ]; // 验证头部协议 - if (fullData[0] == 0xAB && - fullData[1] == 0xAA && - fullData[2] == 0x01) { - + if (fullData[0] == 0xAB && fullData[1] == 0xAA && fullData[2] == 0x01) { debugPrint('[Dispatcher-ACK] 收到路径规划应答 - 完整数据包:${fullData.join(" ")}'); // 🔥 检查数据包长度是否足够 @@ -406,10 +397,9 @@ _logger.logWithLevel('[Dispatcher] 开始监听路径规划指令应答 (CMD: 0x // ✅ 下位机回复"收到指令" debugPrint('✅ [Dispatcher-ACK] ACK 确认收到 - 点编号:$pointIndex'); _waitingForAck = false; - _waitingForArrived = true; // 开始等待"已到达"信号 - return false; // 继续监听,不返回给流 - } - else if (status == 0x01) { + _waitingForArrived = true; // 开始等待"已到达"信号 + return false; // 继续监听,不返回给流 + } else if (status == 0x01) { // ✅ 下位机回复"已到达" debugPrint('✅ [Dispatcher-ACK] 已到达确认 - 点编号:$pointIndex'); _waitingForArrived = false; @@ -430,10 +420,7 @@ _logger.logWithLevel('[Dispatcher] 开始监听路径规划指令应答 (CMD: 0x var deviceAddPathPointModel = queue.first; var d = deviceAddPathPointModel.latitude; var e = deviceAddPathPointModel.longitude; - devicesCubit.setArrivedLocation( - deviceAddPathPointModel.latitude, - deviceAddPathPointModel.longitude - ); + devicesCubit.setArrivedLocation(deviceAddPathPointModel.latitude, deviceAddPathPointModel.longitude); debugPrint(" 准确更新完成的路径点经纬度:$d, $e"); // 移除已完成的点 @@ -441,7 +428,8 @@ _logger.logWithLevel('[Dispatcher] 开始监听路径规划指令应答 (CMD: 0x _pathPlanningService.updateQueue(queue.toList()); DateTime now = DateTime.now(); - String timeStr = "${now.hour.toString().padLeft(2, '0')}:" + String timeStr = + "${now.hour.toString().padLeft(2, '0')}:" "${now.minute.toString().padLeft(2, '0')}:" "${now.second.toString().padLeft(2, '0')}"; @@ -461,9 +449,8 @@ _logger.logWithLevel('[Dispatcher] 开始监听路径规划指令应答 (CMD: 0x // 🔥 发送下一个点(触发 ACK 握手循环) routePlanningRepository.startRoutePlanning(queue); - return false; // 不返回给流,避免重复处理 - } - else { + return false; // 不返回给流,避免重复处理 + } else { debugPrint('⚠️ [Dispatcher-ACK] 未知状态位:0x${status.toRadixString(16)}'); return false; } @@ -473,6 +460,4 @@ _logger.logWithLevel('[Dispatcher] 开始监听路径规划指令应答 (CMD: 0x } }); } - - } 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 a5d985d2..2f36db69 100644 --- a/lib/features/devices/data/repositories/route_planning_repository_impl.dart +++ b/lib/features/devices/data/repositories/route_planning_repository_impl.dart @@ -4,6 +4,7 @@ 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'; +import 'package:maibu_satabot_v2/features/devices/presentation/bloc/devices_cubit.dart'; import '../../../../core/di/injection.dart'; import '../../../../core/env/path_planning_config.dart'; import '../../../../core/network/net_message_dispatcher.dart'; @@ -235,6 +236,8 @@ class PathPlanner { // print("🗑️ 清空待发送队列,剩余 ${locationQueue.length} 个点"); _logger.log("[清空待发送队列,剩余 ${locationQueue.length} 个点]"); locationQueue.clear(); + final devicesCubit = sl(); + devicesCubit.setArrivedLocation(0.0, 0.0); var entity = new RoutePlanSendEntity(commandType: 0x01, pointCounts: 0, targetLatitude: 0, targetLongitude: 0, speed: 0); // print("📡 发送停止指令到设备..."); diff --git a/lib/features/devices/presentation/bloc/devices_cubit.dart b/lib/features/devices/presentation/bloc/devices_cubit.dart index e3a934c4..26a1f4f9 100644 --- a/lib/features/devices/presentation/bloc/devices_cubit.dart +++ b/lib/features/devices/presentation/bloc/devices_cubit.dart @@ -47,7 +47,6 @@ class DevicesCubit extends Cubit { final ILoggerService _logger = GetIt.I(); - DevicesCubit( this.repository, this._getUserDeviceUseCase, @@ -232,7 +231,7 @@ class DevicesCubit extends Cubit { // 🔥 关键修复 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,清除旧设备图表数据 @@ -354,12 +353,12 @@ class DevicesCubit extends Cubit { // 开始路径规划 Future startRoutePlanning(Queue locationQueue) async { - /// print("cubit层开始路径规划"); + /// print("cubit层开始路径规划"); _logger.logWithLevel('开始路径规划'); // 清空全局 Service 中的队列 // _routePlanningUseCase.clearLocationQueue(); final List pathList = locationQueue.toList(); - // print('✅ 队列转换为 List,长度:${pathList.length}'); + // print('✅ 队列转换为 List,长度:${pathList.length}'); _logger.logWithLevel('队列转换为 List,长度:${pathList.length}'); _pathPlanningService.updateQueue(pathList); @@ -397,6 +396,7 @@ class DevicesCubit extends Cubit { Future 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))); @@ -418,7 +418,7 @@ class DevicesCubit extends Cubit { 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'); } diff --git a/lib/features/home/presentation/widgets/common/tracepoint.dart b/lib/features/home/presentation/widgets/common/tracepoint.dart index 67ae54c6..9d73d3fd 100644 --- a/lib/features/home/presentation/widgets/common/tracepoint.dart +++ b/lib/features/home/presentation/widgets/common/tracepoint.dart @@ -1,12 +1,13 @@ -import 'dart:convert'; import 'dart:async'; +import 'dart:convert'; +import 'package:flutter/foundation.dart'; // ======================== 枚举定义 ======================== /// 轨迹模式枚举 enum TPMode { - NAVIGATION, // 导航模式:绘制规划已完成路径和当前路径 - LOCATION, // 定位模式:仅绘制当前点(永远只有1个) - TRACK, // 轨迹模式:实时绘制历史轨迹 + NAVIGATION, // 导航模式: 绘制规划已完成路径和当前路径 + LOCATION, // 定位模式: 绘制当前点 + TRACK, // 轨迹模式: 实时绘制历史轨迹 } /// 导航模式下的操作动作枚举 @@ -28,8 +29,6 @@ class TryLock { void release() { _locked = false; } - - bool get isLocked => _locked; } // ======================== 环形队列实现 ======================== @@ -157,6 +156,7 @@ class CircQueue { } } +// ======================== 轨迹点核心类 ======================== class TracePoint { late final CircQueue _queue; List _tracePoint = []; @@ -164,9 +164,11 @@ class TracePoint { late final TryLock _lock; TPMode _mode = TPMode.LOCATION; + // 回调事件(对应JS原版emit) void Function(T)? onCurrentPointUpdated; void Function(T)? onCompletePointAdded; + // 测试专用方法 bool tryLock() => _lock.tryLock(); void release() => _lock.release(); @@ -176,7 +178,7 @@ class TracePoint { reset(); } - /// 复位所有状态 + /// 复位所有状态(和JS完全一致) void reset() { _queue.clear(); _tracePoint.clear(); @@ -184,7 +186,7 @@ class TracePoint { _lock.release(); } - /// 设置模式 + /// 设置工作模式 void setMode(TPMode mode) { reset(); _mode = mode; @@ -194,9 +196,13 @@ class TracePoint { return _mode; } - /// 添加/更新轨迹点 + /// 添加/更新点(核心API,和JS逻辑完全一致) void upsert(T point, [TPAction act = TPAction.UPDATE]) { - // 严格按模式分发 + if (point == null) { + debugPrint('upsert: point is null'); + return; + } + switch (_mode) { case TPMode.NAVIGATION: if (act == TPAction.UPDATE) { @@ -205,103 +211,89 @@ class TracePoint { _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); _lock.release(); return trace; } else { - print('getTracePoint: lock!!!'); + debugPrint('getTracePoint: lock!!!'); return null; } } - /// 更新当前点(LOCATION 永远只有1个) + /// 更新当前点(导航/定位模式共用) void _updateCurrentPoint(T point) { - if (_lock.tryLock()) { - try { - switch (_mode) { - case TPMode.NAVIGATION: - if (_tracePoint.isNotEmpty) { - _tracePoint = _tracePoint.sublist(0, _completePointIndex); - _tracePoint.add(point); - onCurrentPointUpdated?.call(point); - } - break; + if (!_lock.tryLock()) { + debugPrint('updateCurrentPoint: lock!!!, discard current point'); + return; + } - case TPMode.LOCATION: - // 核心:永远清空,只保留最新一个 - _tracePoint.clear(); + try { + switch (_mode) { + case TPMode.NAVIGATION: + if (_tracePoint.isEmpty) { + debugPrint('updateCurrentPoint: wait first completed point, discard'); + } else { + _tracePoint = _tracePoint.sublist(0, _completePointIndex); _tracePoint.add(point); onCurrentPointUpdated?.call(point); - break; - - default: - break; - } - } finally { - _lock.release(); + } + break; + case TPMode.LOCATION: + _tracePoint = [point]; + onCurrentPointUpdated?.call(point); + break; + case TPMode.TRACK: + break; } + } finally { + _lock.release(); } } - /// 添加完成点 + /// 添加完成点(导航/轨迹模式共用) void _addCompletePoint(T point) { 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'); + debugPrint('addCompletePoint failure: $e'); } finally { _lock.release(); } } else { - if (_mode != TPMode.TRACK) { - // TRACK 模式不加队列,直接丢弃旧的保证流畅 - _queue.enter(point); - } - print('addCompletePoint: lock!!!'); + // 锁失败 → 入队列等待(JS原版核心逻辑) + _queue.enter(point); + debugPrint('addCompletePoint: lock!!!, enter queue'); } } - /// 内部添加点 + /// 内部真正执行添加点逻辑(和JS完全一致) void _addPoint(T point) { - 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 (hasCurrent) { + if (hasCurrentPoint) { _tracePoint = _tracePoint.sublist(0, _completePointIndex); - _tracePoint.add(point); + _tracePoint.insert(_completePointIndex, 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 d8283d6a..1cdcab48 100644 --- a/lib/features/home/presentation/widgets/map/testmap_pages.dart +++ b/lib/features/home/presentation/widgets/map/testmap_pages.dart @@ -660,11 +660,6 @@ class _MapPageEnterpriseState extends State { // 3. 打印文件/Blob 字段 (核心:这里包含 file 和 workRecord) debugPrint('📁 上传文件/Blob 数量: ${request.files.length}'); for (var file in request.files) { - debugPrint('------------------------------------'); - debugPrint(' 字段名 (name): ${file.field}'); - debugPrint(' 文件名 (filename): ${file.filename}'); - debugPrint(' 内容类型 (contentType): ${file.contentType}'); - // 特殊处理:如果是 workRecord,打印其内容 if (file.field == 'workRecord' && file is http.MultipartFile) { // 将 ByteStream 转为 Uint8List 以查看内容 @@ -685,7 +680,6 @@ class _MapPageEnterpriseState extends State { }); } } - debugPrint('===================================='); } Future _savePlotData(String plotName, String? imgBase64) async { @@ -697,11 +691,9 @@ class _MapPageEnterpriseState extends State { //ToastUtils.showError(context, '用户ID为空,无法保存!'); return; } - debugPrint("===打印生成的路径开始 "); for (var i = 0; i < typedPathList.length; i++) { debugPrint(typedPathList[i].toString()); } - debugPrint("===打印生成的路径结束"); // 2. 构造 SavePath 数据 (严格对应你的 JS 结构) final Map savePath = { //'img': imgBase64 ?? '', // 截图的 Base64 字符串 @@ -771,7 +763,6 @@ class _MapPageEnterpriseState extends State { final http.StreamedResponse response = await request.send(); final String responseBody = await response.stream.bytesToString(); - debugPrint('保存地块响应体: $responseBody'); if (response.statusCode == 200) { _showPageToast(message: "地块「$plotName」保存成功!", type: ToastType.success); @@ -1269,7 +1260,6 @@ class _MapPageEnterpriseState extends State { List outerList = []; final rawOuter = parsedJson['outer']; - debugPrint("$rawOuter rawOuterrawOuterrawOuter"); if (rawOuter is String) { try { outerList = jsonDecode(rawOuter) as List; @@ -1406,7 +1396,7 @@ class _MapPageEnterpriseState extends State { _traceManager.upsert(_currentWgsLatLng as PlotPoint, TPAction.UPDATE); tracePoint = _traceManager.getTracePoint(); gctracePoint = batchWgs84ToGcj02(tracePoint!); - _logger.log("[转换后gctracePoint]: $gctracePoint"); + _logger.log("[当前轨迹模式][转换后gctracePoint]: ${gctracePoint!.length}"); _logger.log("[当前轨迹模式]:${_traceManager.getMode()}"); // 这里必须加 () //_currentLatLng = gcjPoint; // ✅ 状态变量在setState内更新 @@ -1637,9 +1627,11 @@ class _MapPageEnterpriseState extends State { ], ), - if (gctracePoint!.isNotEmpty) + // 只在 ≥2 个点时才显示图层,否则直接消失! + if (gctracePoint != null && gctracePoint!.length >= 2) PolylineLayer( - polylines: [Polyline(points: gctracePoint!, color: Color.fromARGB(255, 6, 187, 232), strokeWidth: 3, isDotted: false)], + key: const ValueKey('trace_polyline'), + polylines: [Polyline(points: gctracePoint!, color: const Color.fromARGB(255, 6, 187, 232), strokeWidth: 3)], ), /// 当前定位 Marker @@ -1700,20 +1692,28 @@ class _MapPageEnterpriseState extends State { /// 开始作业 void _startWork() async { - _traceManager.reset(); - if (startWorkList.isEmpty) { _showPageToast(message: "作业列表为空,请重新选择路径", type: ToastType.info); //ToastUtils.showInfo(context, '作业列表为空,请重新选择路径'); return; } - _traceManager.setMode(TPMode.NAVIGATION); + _traceManager.setMode(TPMode.LOCATION); + //_traceManager.reset(); + tracePoint = _traceManager.getTracePoint(); + gctracePoint = batchWgs84ToGcj02(tracePoint!); + _logger.log("[当前轨迹模式][转换后gctracePoint]开始作业: $tracePoint"); + _logger.log("[当前轨迹模式][转换后gctracePoint]开始作业: $gctracePoint"); + _logger.log("[当前轨迹模式][转换后gctracePoint]开始作业= ${gctracePoint!.length}"); setState(() { isStopWork = false; isStartWork = true; // 🔥 关键:停止作业标志 _workStatus = WorkStatus.working; + _traceManager.reset(); + tracePoint?.clear(); + gctracePoint?.clear(); + //_traceManager.setMode(TPMode.NAVIGATION); }); // 🔥 核心修复:先更新 AppState 为 routePlanning @@ -1728,9 +1728,6 @@ class _MapPageEnterpriseState extends State { final Queue pathQueue = Queue.from(typedList); - debugPrint('🚀 开始作业:${_selectedPlot?.plotName ?? "未命名"},路径点数量:${pathQueue.length}'); - debugPrint('第一个点 WGS84: ${pathQueue.first.latitude}, ${pathQueue.first.longitude}'); - // 步骤 3:调用 Cubit 方法(类型匹配) await context.read().startRoutePlanning(pathQueue); @@ -1758,23 +1755,32 @@ class _MapPageEnterpriseState extends State { if (mounted) { setState(() { - isStartWork = false; // 🔥 关键:停止作业标志 + isStartWork = false; _workStatus = WorkStatus.idle; _showHeadingWarn = false; _showControlModeWarn = false; - isStopWork = true; //已经点击过停止 + isStopWork = true; isreceiveFirstCompletePoint = false; + + // 1. 立刻切换模式(阻止新的点再进入 NAVIGATION 逻辑) _traceManager.setMode(TPMode.LOCATION); + }); + } + + await context.read().stopRoutePlanning(); + _saveDataToLocal(); + await Future.delayed(const Duration(milliseconds: 300)); + if (mounted) { + setState(() { + // 2. 延迟后重置 + 清空(彻底绝杀残留点) _traceManager.reset(); + tracePoint?.clear(); + gctracePoint?.clear(); tracePoint = []; gctracePoint = []; }); } - //context.read().updateAppState(AppState.none); - await context.read().stopRoutePlanning(); - - _saveDataToLocal(); // 🔥 保存暂停状态 _showPageToast(message: "作业已停止", type: ToastType.error); } @@ -2113,9 +2119,6 @@ class _MapPageEnterpriseState extends State { // ========== 抽象:生成路径的核心函数 ========== Future _generatePath({bool showTips = true}) async { - debugPrint("机器打点孔洞_obstacleHoles$_obstacleHoles"); - debugPrint("机器打点孔洞_robotModeObsWgsPoints$_robotModeObsWgsPoints"); - if (_currentWorkMode == WorkMode.custom) { setState(() { _saveBoxOpen = true; @@ -2338,16 +2341,21 @@ class _MapPageEnterpriseState extends State { LatLng? targetLatLng; if (arriLatitude != null && arriLongitude != null) { targetLatLng = LatLng(arriLatitude, arriLongitude); + _logger.log("当前轨迹模式: ${_traceManager.getMode()},收到完成点坐标: $targetLatLng"); if (__isValidLatLng(targetLatLng)) { //debugPrint("${targetLatLng}收到完成点arriLatitude"); - if (!isreceiveFirstCompletePoint && isStartWork && _workStatus == WorkStatus.working) { - debugPrint("收到完成点arriLatitude"); - + //if (!isreceiveFirstCompletePoint && isStartWork && _workStatus == WorkStatus.working) { + debugPrint("收到完成点arriLatitude"); + if (_traceManager.getMode() != TPMode.NAVIGATION) { _traceManager.setMode(TPMode.NAVIGATION); - isreceiveFirstCompletePoint = true; + debugPrint("切换到导航模式"); } - _traceManager.upsert(targetLatLng, TPAction.ADD); + isreceiveFirstCompletePoint = true; + //} + if (isStartWork && _workStatus == WorkStatus.working) { + _traceManager.upsert(targetLatLng, TPAction.ADD); + } } } @@ -2366,11 +2374,15 @@ class _MapPageEnterpriseState extends State { _workStatus = WorkStatus.idle; isStopWork = true; isreceiveFirstCompletePoint = false; + + tracePoint = []; + gctracePoint = []; + tracePoint?.clear(); + gctracePoint?.clear(); }); _showPageToast(message: "作业已停止", type: ToastType.error); - tracePoint = []; - gctracePoint = []; - // 延迟重置轨迹管理器 + + // 延迟重置轨迹管理器 Future.delayed(const Duration(seconds: 1), () { if (mounted) { _traceManager.setMode(TPMode.LOCATION); @@ -2600,7 +2612,6 @@ class _MapPageEnterpriseState extends State { setState(() { _directionBoxOpen = true; }); - debugPrint('外部处理设置按钮点击'); }, onLandTap: () { debugPrint('外部处理地块标签点击'); @@ -2621,12 +2632,9 @@ class _MapPageEnterpriseState extends State { initialOptimalHeading: true, initialDirection: 0.0, onValueChanged: (result) { - debugPrint('最优航向:${result['optimalHeading']},角度:${result['direction']}'); _angle = result['optimalHeading'] == true ? -1 : result['direction']; - debugPrint('外部处理航线方向设置,当前角度:${_angle}'); if (_markedPoints.length >= 3 && _currentWorkMode != null) { - debugPrint('外部处理航线方向设置11222:${_angle}'); _generatePath(showTips: false); } }, diff --git a/lib/features/home/presentation/widgets/work_params_card.dart b/lib/features/home/presentation/widgets/work_params_card.dart index 46bbc953..35b235b6 100644 --- a/lib/features/home/presentation/widgets/work_params_card.dart +++ b/lib/features/home/presentation/widgets/work_params_card.dart @@ -109,7 +109,6 @@ class _WorkParamsCardState extends State { // ====================== 核心:根据时间戳计算时长 ====================== // 修复时间解析:适配 20-1-11-31 08:00:00 格式 void _calculateWorkingTime(String timestampStr) { - debugPrint('⏱️ [时间戳] 原始字符串: $timestampStr'); if (timestampStr.isEmpty) return; try {