路径规划修复

This commit is contained in:
mmc
2026-04-15 16:38:54 +08:00
parent 803b9bd861
commit 4c11f06513
3 changed files with 114 additions and 148 deletions

View File

@@ -16,17 +16,17 @@ class RoutePlanningRepositoryImpl implements RoutePlanningRepository {
late PathPlanner _planner;
final ILoggerService _logger = GetIt.I<ILoggerService>();
// 🔥 新增:存储 AppState 的引用
dynamic _appState;
RoutePlanningRepositoryImpl({required this.tcp}) { // ✅ 加 {required} 和类型注解
RoutePlanningRepositoryImpl({required this.tcp}) {
// ✅ 加 {required} 和类型注解
_planner = PathPlanner(tcp);
}
@override
Future<void> startRoutePlanning(Queue<DeviceAddPathPointModel> locationQueue) async {
// 直接传入 DeviceAddPathPointModel 列表(无需转换)
// print("[底层开始发送指令了]");
// print("[底层开始发送指令了]");
_logger.log("开始路径规划");
final List<DeviceAddPathPointModel> 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<DeviceAddPathPointModel> 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<NetMessageDispatcher>());
} 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)...]");
}
}

View File

@@ -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<T> {
final int capacity; // 队列容量
final List<T?> buffer; // 存储缓冲区
int head = 0; // 队头索引
int tail = 0; // 队尾索引
bool isFull = false; // 队列满标志
final bool deepCopy; // 是否深拷贝元素
final int maxMemoryBytes; // 最大内存限制(字节)
int currentMemoryBytes = 0; // 当前已使用内存
final int capacity;
final List<T?> 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<T?>.filled(capacity, null);
CircQueue(this.capacity, {this.deepCopy = true, this.maxMemoryBytes = 1024 * 1024}) : buffer = List<T?>.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> {
}
}
/// 深拷贝对象
T? _deepClone(T? item) {
if (!deepCopy || item == null) return item;
try {
@@ -89,10 +76,8 @@ class CircQueue<T> {
}
}
/// 入队操作
bool enter(T item) {
final itemSize = _estimateSize(item);
// 检查队列满或内存超限
if (isFullFn() || (currentMemoryBytes + itemSize > maxMemoryBytes)) {
return false;
}
@@ -106,7 +91,6 @@ class CircQueue<T> {
return true;
}
/// 出队操作
T? out() {
if (isEmpty()) return null;
@@ -119,7 +103,6 @@ class CircQueue<T> {
return _deepClone(item);
}
/// 丢弃指定数量的元素
bool discard(int len) {
for (int i = 0; i < len; i++) {
if (isEmpty()) return false;
@@ -135,12 +118,10 @@ class CircQueue<T> {
return true;
}
/// 获取队列元素个数
int getDepth() {
return isFull ? capacity : (tail + capacity - head) % capacity;
}
/// 异步入队(支持超时)
Future<bool> send(T item, {int timeoutMs = 0}) async {
final start = DateTime.now().millisecondsSinceEpoch;
while (!enter(item)) {
@@ -152,7 +133,6 @@ class CircQueue<T> {
return true;
}
/// 异步出队(支持超时)
Future<T?> recv({int timeoutMs = 0}) async {
final start = DateTime.now().millisecondsSinceEpoch;
while (true) {
@@ -165,7 +145,6 @@ class CircQueue<T> {
}
}
/// 转换为列表(用于调试/序列化)
List<T?> toList() {
final result = <T?>[];
int i = head;
@@ -179,22 +158,18 @@ class CircQueue<T> {
}
class TracePoint<T> {
// 私有成员变量
late final CircQueue<T> _queue;
List<T> _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<T>(queueCapacity, deepCopy: true, maxMemoryBytes: maxMemoryBytes);
_lock = TryLock();
@@ -204,12 +179,12 @@ class TracePoint<T> {
/// 复位所有状态
void reset() {
_queue.clear();
_tracePoint = [];
_tracePoint.clear();
_completePointIndex = 0;
_lock.release();
}
/// 设置工作模式
/// 设置模式
void setMode(TPMode mode) {
reset();
_mode = mode;
@@ -221,20 +196,29 @@ class TracePoint<T> {
/// 添加/更新轨迹点
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<T>? getTracePoint() {
if (_lock.tryLock()) {
final trace = List<T>.from(_tracePoint);
@@ -246,63 +230,48 @@ class TracePoint<T> {
}
}
/// 更新当前点(私有方法)
/// 更新当前点(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<T> {
_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);
}

View File

@@ -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<MapPageEnterprise> {
final ILoggerService _logger = GetIt.I<ILoggerService>();
final _dispatcher = sl<NetMessageDispatcher>();
bool _isRefreshing = false; // 新增:页面刷新状态标志
bool _isVideoDialogOpen = false; // 控制视频弹窗显示
@@ -270,6 +274,8 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
// 新增:加载本地存储数据方法
Future<void> _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<MapPageEnterprise> {
// ========== 新增:页面刷新初始化方法 ==========
Future<void> _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<MapPageEnterprise> {
_workStatus = WorkStatus.idle;
isStartWork = false;
isStopWork = false;
_mapCenter = _getDefaultValidCenter();
// 轨迹管理器重置
_traceManager.reset();
@@ -1407,6 +1415,8 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
_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<MapPageEnterprise> {
}
void _stopWork() async {
_logger.log('按下停止按钮');
if (mounted) {
setState(() {
isStartWork = false; // 🔥 关键:停止作业标志
@@ -1761,6 +1773,10 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
_showControlModeWarn = false;
isStopWork = true; //已经点击过停止
isreceiveFirstCompletePoint = false;
_traceManager.setMode(TPMode.LOCATION);
_traceManager.reset();
tracePoint = [];
gctracePoint = [];
});
}
@@ -1769,10 +1785,6 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
_saveDataToLocal(); // 🔥 保存暂停状态
_showPageToast(message: "作业已停止", type: ToastType.error);
_traceManager.setMode(TPMode.LOCATION);
_traceManager.reset();
tracePoint = [];
gctracePoint = [];
}
/// 继续作业
@@ -2336,8 +2348,10 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
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;
}