问题: - 当用户在路径列表中切换到另一条路径时,3D视图正确切换,路径点编辑列表也正确显示 - 但增加路径点和修改路径点操作仍使用上一个路径的数据,因为PathPlanningManager.CurrentRoute没有同步更新 修复: - 在PathEditingViewModel.SelectedPathRoute属性setter中添加逻辑 - 当UI选中路径变化时,查找对应的Core路径对象并调用_pathPlanningManager.SetCurrentRoute()同步后台数据 - 确保UI路径切换和后台CurrentRoute保持一致,解决增加/修改路径点操作的数据不同步问题 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
2484 lines
112 KiB
C#
2484 lines
112 KiB
C#
using System;
|
||
using System.Collections.ObjectModel;
|
||
using System.ComponentModel;
|
||
using System.Windows;
|
||
using System.Windows.Input;
|
||
using System.Threading.Tasks;
|
||
using System.Linq;
|
||
using System.IO;
|
||
using Microsoft.Win32;
|
||
using Autodesk.Navisworks.Api;
|
||
using NavisworksTransport.Core;
|
||
using NavisworksTransport.UI.WPF.Collections;
|
||
using NavisworksTransport.UI.WPF.Models;
|
||
using NavisworksTransport.UI.WPF.Commands;
|
||
using NavisworksTransport.Utils;
|
||
using NavisworksTransport.Commands;
|
||
using NavisworksTransport.PathPlanning;
|
||
|
||
namespace NavisworksTransport.UI.WPF.ViewModels
|
||
{
|
||
/// <summary>
|
||
/// 路径编辑页面专用ViewModel
|
||
/// 暂时使用空实现,避免与主LogisticsControlViewModel功能重复
|
||
/// TODO: 后续将从LogisticsControlViewModelcopy.cs迁移完整业务逻辑
|
||
/// </summary>
|
||
public class PathEditingViewModel : ViewModelBase
|
||
{
|
||
#region 私有字段
|
||
|
||
private PathPlanningManager _pathPlanningManager;
|
||
private readonly UIStateManager _uiStateManager;
|
||
private readonly PathDataManager _pathDataManager;
|
||
|
||
// 路径集合
|
||
private ObservableCollection<PathRouteViewModel> _pathRoutes;
|
||
private PathRouteViewModel _selectedPathRoute;
|
||
private PathPointViewModel _selectedPathPoint;
|
||
private bool _isPathEditMode;
|
||
|
||
// 自动路径规划参数
|
||
private string _autoPathStartPoint = "未选择";
|
||
private string _autoPathEndPoint = "未选择";
|
||
private string _autoPathStatus = "就绪";
|
||
private string _pathEditTipText = "提示:点击[添加路径点]后,在3D视图中点击可通行的物流模型来添加路径点";
|
||
private Point3D _startPoint3D;
|
||
private Point3D _endPoint3D;
|
||
private bool _hasStartPoint = false;
|
||
private bool _hasEndPoint = false;
|
||
private bool _isSelectingStartPoint = false;
|
||
private bool _isSelectingEndPoint = false;
|
||
|
||
// 车辆参数 - 改为三个独立参数
|
||
private double _vehicleLength = 1.0; // 车辆长度(米)
|
||
private double _vehicleWidth = 1.0; // 车辆宽度(米)
|
||
private double _vehicleHeight = 2.0; // 车辆高度(米)
|
||
private double _safetyMargin = 0.25; // 安全间隙(米)
|
||
|
||
// 网格大小参数
|
||
private bool _isGridSizeManuallyEnabled = false; // 是否启用手动网格大小设置
|
||
private double _gridSize = 0.5; // 网格大小(米)
|
||
|
||
// 自动路径起点和终点的路径对象引用(用于正确的ID管理)
|
||
private PathRoute _autoPathStartPointRoute = null;
|
||
private PathRoute _autoPathEndPointRoute = null;
|
||
|
||
// 路径文件管理
|
||
private string _pathFileStatus = "未保存";
|
||
|
||
#endregion
|
||
|
||
#region 公共属性
|
||
|
||
public ObservableCollection<PathRouteViewModel> PathRoutes
|
||
{
|
||
get => _pathRoutes;
|
||
set => SetProperty(ref _pathRoutes, value);
|
||
}
|
||
|
||
public PathRouteViewModel SelectedPathRoute
|
||
{
|
||
get => _selectedPathRoute;
|
||
set
|
||
{
|
||
if (SetProperty(ref _selectedPathRoute, value))
|
||
{
|
||
// 路径变更时清除选中的路径点
|
||
SelectedPathPoint = null;
|
||
|
||
// 🔧 修复:同步PathPlanningManager的CurrentRoute
|
||
if (_pathPlanningManager != null && value != null)
|
||
{
|
||
// 查找对应的Core路径对象
|
||
var coreRoute = _pathPlanningManager.Routes?.FirstOrDefault(r => r.Name == value.Name);
|
||
if (coreRoute != null)
|
||
{
|
||
_pathPlanningManager.SetCurrentRoute(coreRoute);
|
||
LogManager.Info($"UI路径切换:已同步PathPlanningManager的CurrentRoute到 {value.Name}");
|
||
}
|
||
else
|
||
{
|
||
LogManager.Warning($"UI路径切换:未找到对应的Core路径 {value.Name}");
|
||
}
|
||
}
|
||
else if (_pathPlanningManager != null && value == null)
|
||
{
|
||
// 如果选择了null,也要同步清除CurrentRoute
|
||
_pathPlanningManager.SetCurrentRoute(null);
|
||
LogManager.Info("UI路径切换:已清除PathPlanningManager的CurrentRoute");
|
||
}
|
||
|
||
// 更新命令状态
|
||
OnPropertyChanged(nameof(CanExecuteStartEdit));
|
||
OnPropertyChanged(nameof(CanExecuteEndEdit));
|
||
OnPropertyChanged(nameof(CanExecuteClearPath));
|
||
OnPropertyChanged(nameof(CanExecuteExportPath));
|
||
OnPropertyChanged(nameof(CanExecuteSaveAsPath));
|
||
OnPropertyChanged(nameof(CanExecuteModifyPoint));
|
||
|
||
// 实现路径选择时的可视化切换
|
||
UpdatePathVisualization();
|
||
}
|
||
}
|
||
}
|
||
|
||
public PathPointViewModel SelectedPathPoint
|
||
{
|
||
get => _selectedPathPoint;
|
||
set
|
||
{
|
||
if (SetProperty(ref _selectedPathPoint, value))
|
||
{
|
||
// 更新修改路径点按钮的启用状态
|
||
OnPropertyChanged(nameof(CanExecuteModifyPoint));
|
||
}
|
||
}
|
||
}
|
||
/// <summary>
|
||
/// 更新路径可视化显示:清理其他路径,只显示当前选中的路径
|
||
/// </summary>
|
||
private async void UpdatePathVisualization()
|
||
{
|
||
await SafeExecuteAsync(() =>
|
||
{
|
||
try
|
||
{
|
||
if (PathPointRenderPlugin.Instance == null)
|
||
{
|
||
LogManager.Warning("PathPointRenderPlugin实例为空,无法更新路径可视化");
|
||
return;
|
||
}
|
||
|
||
// 1. 清理所有现有的路径可视化
|
||
PathPointRenderPlugin.Instance.ClearAllPaths();
|
||
LogManager.WriteLog("[路径可视化] 已清理所有现有路径显示");
|
||
|
||
// 2. 如果有选中的路径,则显示该路径
|
||
if (_selectedPathRoute != null && _selectedPathRoute.Points.Count > 0)
|
||
{
|
||
// 查找对应的Core路径对象
|
||
var coreRoute = _pathPlanningManager?.Routes?.FirstOrDefault(r => r.Name == _selectedPathRoute.Name);
|
||
if (coreRoute != null)
|
||
{
|
||
// 使用PathPlanningManager的绘制方法来保持一致性
|
||
_pathPlanningManager.DrawRouteVisualization(coreRoute, isAutoPath: false);
|
||
LogManager.Info($"[路径可视化] 已显示选中路径: {_selectedPathRoute.Name},包含 {_selectedPathRoute.Points.Count} 个点");
|
||
}
|
||
else
|
||
{
|
||
LogManager.Warning($"[路径可视化] 未找到对应的Core路径: {_selectedPathRoute.Name}");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
LogManager.WriteLog("[路径可视化] 没有选中的路径,仅清理显示");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[路径可视化] 更新路径可视化失败: {ex.Message}", ex);
|
||
}
|
||
}, "更新路径可视化");
|
||
}
|
||
|
||
public bool IsPathEditMode
|
||
{
|
||
get => _isPathEditMode;
|
||
set => SetProperty(ref _isPathEditMode, value);
|
||
}
|
||
|
||
#region 自动路径规划属性
|
||
|
||
public string AutoPathStartPoint
|
||
{
|
||
get => _autoPathStartPoint;
|
||
set => SetProperty(ref _autoPathStartPoint, value);
|
||
}
|
||
|
||
public string AutoPathEndPoint
|
||
{
|
||
get => _autoPathEndPoint;
|
||
set => SetProperty(ref _autoPathEndPoint, value);
|
||
}
|
||
|
||
public string AutoPathStatus
|
||
{
|
||
get => _autoPathStatus;
|
||
set => SetProperty(ref _autoPathStatus, value);
|
||
}
|
||
/// <summary>
|
||
/// 路径编辑提示文本
|
||
/// </summary>
|
||
public string PathEditTipText
|
||
{
|
||
get => _pathEditTipText;
|
||
set => SetProperty(ref _pathEditTipText, value);
|
||
}
|
||
|
||
public double VehicleLength
|
||
{
|
||
get => _vehicleLength;
|
||
set
|
||
{
|
||
if (SetProperty(ref _vehicleLength, value))
|
||
{
|
||
OnPropertyChanged(nameof(CanExecuteAutoPlanPath));
|
||
}
|
||
}
|
||
}
|
||
|
||
public double VehicleWidth
|
||
{
|
||
get => _vehicleWidth;
|
||
set
|
||
{
|
||
if (SetProperty(ref _vehicleWidth, value))
|
||
{
|
||
OnPropertyChanged(nameof(CanExecuteAutoPlanPath));
|
||
}
|
||
}
|
||
}
|
||
|
||
public double VehicleHeight
|
||
{
|
||
get => _vehicleHeight;
|
||
set
|
||
{
|
||
if (SetProperty(ref _vehicleHeight, value))
|
||
{
|
||
OnPropertyChanged(nameof(CanExecuteAutoPlanPath));
|
||
}
|
||
}
|
||
}
|
||
|
||
public double SafetyMargin
|
||
{
|
||
get => _safetyMargin;
|
||
set
|
||
{
|
||
// 限制精度到2位小数
|
||
var roundedValue = Math.Round(value, 2);
|
||
if (SetProperty(ref _safetyMargin, roundedValue))
|
||
{
|
||
OnPropertyChanged(nameof(CanExecuteAutoPlanPath));
|
||
}
|
||
}
|
||
}
|
||
|
||
public bool IsGridSizeManuallyEnabled
|
||
{
|
||
get => _isGridSizeManuallyEnabled;
|
||
set
|
||
{
|
||
if (SetProperty(ref _isGridSizeManuallyEnabled, value))
|
||
{
|
||
OnPropertyChanged(nameof(CanExecuteAutoPlanPath));
|
||
}
|
||
}
|
||
}
|
||
|
||
public double GridSize
|
||
{
|
||
get => _gridSize;
|
||
set
|
||
{
|
||
// 限制精度到1位小数
|
||
var roundedValue = Math.Round(value, 1);
|
||
if (SetProperty(ref _gridSize, roundedValue))
|
||
{
|
||
OnPropertyChanged(nameof(CanExecuteAutoPlanPath));
|
||
}
|
||
}
|
||
}
|
||
|
||
public bool IsSelectingStartPoint
|
||
{
|
||
get => _isSelectingStartPoint;
|
||
set => SetProperty(ref _isSelectingStartPoint, value);
|
||
}
|
||
|
||
public bool IsSelectingEndPoint
|
||
{
|
||
get => _isSelectingEndPoint;
|
||
set => SetProperty(ref _isSelectingEndPoint, value);
|
||
}
|
||
|
||
#endregion
|
||
|
||
public string PathFileStatus
|
||
{
|
||
get => _pathFileStatus;
|
||
set => SetProperty(ref _pathFileStatus, value);
|
||
}
|
||
|
||
public PathPlanningManager PathPlanningManager
|
||
{
|
||
get => _pathPlanningManager;
|
||
set
|
||
{
|
||
if (_pathPlanningManager != value)
|
||
{
|
||
// 取消旧的事件订阅
|
||
if (_pathPlanningManager != null)
|
||
{
|
||
UnsubscribeFromPathPlanningManager();
|
||
}
|
||
|
||
_pathPlanningManager = value;
|
||
|
||
// 订阅新的事件
|
||
if (_pathPlanningManager != null)
|
||
{
|
||
SubscribeToPathPlanningManager();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 命令
|
||
|
||
public ICommand NewPathCommand { get; private set; }
|
||
public ICommand DeletePathCommand { get; private set; }
|
||
public ICommand RenamePathCommand { get; private set; }
|
||
public ICommand StartEditCommand { get; private set; }
|
||
public ICommand EndEditCommand { get; private set; }
|
||
public ICommand ClearPathCommand { get; private set; }
|
||
public ICommand DeletePointCommand { get; private set; }
|
||
public ICommand ModifyPointCommand { get; private set; }
|
||
public ICommand CancelModifyPointCommand { get; private set; }
|
||
public ICommand SelectStartPointCommand { get; private set; }
|
||
public ICommand SelectEndPointCommand { get; private set; }
|
||
public ICommand AutoPlanPathCommand { get; private set; }
|
||
public ICommand ClearAutoPathCommand { get; private set; }
|
||
public ICommand ImportPathCommand { get; private set; }
|
||
public ICommand ExportPathCommand { get; private set; }
|
||
public ICommand SaveAsPathCommand { get; private set; }
|
||
|
||
#endregion
|
||
|
||
#region Can Execute属性
|
||
|
||
public bool CanExecuteAutoPlanPath => _hasStartPoint && _hasEndPoint &&
|
||
VehicleLength > 0 && VehicleWidth > 0 && VehicleHeight > 0 &&
|
||
SafetyMargin >= 0;
|
||
|
||
public bool CanExecuteStartEdit => SelectedPathRoute != null &&
|
||
(_pathPlanningManager?.PathEditState == PathEditState.Viewing);
|
||
|
||
public bool CanExecuteEndEdit => (_pathPlanningManager?.PathEditState == PathEditState.Creating ||
|
||
_pathPlanningManager?.PathEditState == PathEditState.Editing ||
|
||
_pathPlanningManager?.PathEditState == PathEditState.AddingPoints ||
|
||
_pathPlanningManager?.PathEditState == PathEditState.EditingPoint);
|
||
|
||
public bool CanExecuteClearPath => SelectedPathRoute != null && SelectedPathRoute.Points.Count > 0;
|
||
|
||
public bool CanExecuteExportPath => _pathPlanningManager?.Routes?.Count > 0;
|
||
|
||
public bool CanExecuteSaveAsPath => SelectedPathRoute != null && SelectedPathRoute.Points.Count > 0;
|
||
|
||
public bool CanExecuteModifyPoint => SelectedPathPoint != null &&
|
||
_pathPlanningManager?.PathEditState != PathEditState.EditingPoint;
|
||
public bool CanExecuteCancelModifyPoint => _pathPlanningManager?.PathEditState == PathEditState.EditingPoint;
|
||
|
||
#endregion
|
||
|
||
#region 构造函数
|
||
|
||
public PathEditingViewModel() : base()
|
||
{
|
||
try
|
||
{
|
||
_uiStateManager = UIStateManager.Instance;
|
||
_pathDataManager = new PathDataManager();
|
||
// 不在构造函数中创建PathPlanningManager,由外部设置
|
||
_pathPlanningManager = null;
|
||
|
||
if (_uiStateManager == null)
|
||
{
|
||
throw new InvalidOperationException("UIStateManager初始化失败");
|
||
}
|
||
|
||
// PathPlanningManager将在外部设置,这里不需要检查
|
||
|
||
// 初始化集合
|
||
PathRoutes = new ObservableCollection<PathRouteViewModel>();
|
||
|
||
// 初始化命令
|
||
InitializeCommands();
|
||
|
||
// 注意:不在这里订阅PathPlanningManager事件,
|
||
// 因为PathPlanningManager还没有设置,事件订阅在PathPlanningManager属性setter中处理
|
||
|
||
LogManager.Info("PathEditingViewModel构造函数执行完成,已迁移原有业务逻辑");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"PathEditingViewModel构造函数异常: {ex.Message}", ex);
|
||
AutoPathStatus = "初始化失败,请检查日志";
|
||
throw;
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 初始化方法
|
||
|
||
private void InitializeCommands()
|
||
{
|
||
NewPathCommand = new RelayCommand(async () => await ExecuteNewPathAsync());
|
||
DeletePathCommand = new RelayCommand(async () => await ExecuteDeletePathAsync());
|
||
RenamePathCommand = new RelayCommand(async () => await ExecuteRenamePathAsync());
|
||
StartEditCommand = new RelayCommand(async () => await ExecuteAddPathPointAsync(), () => CanExecuteStartEdit);
|
||
EndEditCommand = new RelayCommand(async () => await ExecuteEndEditAsync(), () => CanExecuteEndEdit);
|
||
ClearPathCommand = new RelayCommand(async () => await ExecuteClearPathAsync(), () => CanExecuteClearPath);
|
||
DeletePointCommand = new RelayCommand<PathPointViewModel>(async (point) => await ExecuteDeletePointAsync(point));
|
||
ModifyPointCommand = new RelayCommand(async () => await ExecuteModifyPointAsync(), () => CanExecuteModifyPoint);
|
||
CancelModifyPointCommand = new RelayCommand(async () => await ExecuteCancelModifyPointAsync(), () => CanExecuteCancelModifyPoint);
|
||
SelectStartPointCommand = new RelayCommand(async () => await ExecuteSelectStartPointAsync());
|
||
SelectEndPointCommand = new RelayCommand(async () => await ExecuteSelectEndPointAsync());
|
||
AutoPlanPathCommand = new RelayCommand(async () => await ExecuteAutoPlanPathAsync(), () => CanExecuteAutoPlanPath);
|
||
ClearAutoPathCommand = new RelayCommand(async () => await ExecuteClearAutoPathAsync());
|
||
ImportPathCommand = new RelayCommand(async () => await ExecuteImportPathAsync());
|
||
ExportPathCommand = new RelayCommand(async () => await ExecuteExportPathAsync(), () => CanExecuteExportPath);
|
||
SaveAsPathCommand = new RelayCommand(async () => await ExecuteSaveAsPathAsync(), () => CanExecuteSaveAsPath);
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 命令实现 - 从LogisticsControlViewModelcopy.cs迁移
|
||
|
||
#region 路径管理命令
|
||
|
||
private async Task ExecuteNewPathAsync()
|
||
{
|
||
await SafeExecuteAsync(() =>
|
||
{
|
||
AutoPathStatus = "正在创建新路径...";
|
||
|
||
// 清除所有现有路径的可视化显示
|
||
if (PathPointRenderPlugin.Instance != null)
|
||
{
|
||
try
|
||
{
|
||
PathPointRenderPlugin.Instance.ClearAllPaths();
|
||
LogManager.Info("新建路径:已清除所有现有路径可视化显示");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"新建路径:清除现有路径可视化失败: {ex.Message}", ex);
|
||
throw; // 重新抛出异常,让上层处理
|
||
}
|
||
}
|
||
|
||
if (_pathPlanningManager != null)
|
||
{
|
||
var newRoute = _pathPlanningManager.StartCreatingNewRoute();
|
||
|
||
if (newRoute != null)
|
||
{
|
||
// 创建对应的 WPF ViewModel
|
||
var newPathViewModel = new PathRouteViewModel
|
||
{
|
||
Name = newRoute.Name,
|
||
Description = newRoute.Description,
|
||
IsActive = true
|
||
};
|
||
|
||
// 转换路径点
|
||
foreach (var corePoint in newRoute.Points)
|
||
{
|
||
var wpfPoint = new PathPointViewModel
|
||
{
|
||
Id = corePoint.Id,
|
||
Name = corePoint.Name,
|
||
X = corePoint.X,
|
||
Y = corePoint.Y,
|
||
Z = corePoint.Z,
|
||
Type = corePoint.Type
|
||
};
|
||
newPathViewModel.Points.Add(wpfPoint);
|
||
}
|
||
|
||
// 添加到 UI 列表并选中
|
||
PathRoutes.Add(newPathViewModel);
|
||
SelectedPathRoute = newPathViewModel;
|
||
|
||
// 强制重新初始化ToolPlugin以确保获得鼠标焦点,新建路径需要订阅事件
|
||
if (!ForceReinitializeToolPlugin(subscribeToEvents: true))
|
||
{
|
||
AutoPathStatus = "ToolPlugin初始化失败,请重试";
|
||
LogManager.Error("新建路径:ToolPlugin初始化失败");
|
||
return;
|
||
}
|
||
|
||
AutoPathStatus = $"已进入新建路径模式: {newRoute.Name} - 请在3D视图中点击设置路径点";
|
||
LogManager.Info($"开始新建路径: {newRoute.Name},已强制重新初始化ToolPlugin获取鼠标焦点");
|
||
|
||
// 不再显示对话框,状态文本已提供足够的反馈
|
||
}
|
||
else
|
||
{
|
||
AutoPathStatus = "创建新路径失败:没有可通行的物流模型";
|
||
LogManager.Error("创建新路径失败:没有可通行的物流模型");
|
||
MessageBox.Show("创建新路径失败:没有找到任何可通行的物流模型。\n请先为模型设置可通行的物流属性,然后再尝试创建路径。", "错误",
|
||
MessageBoxButton.OK, MessageBoxImage.Warning);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
AutoPathStatus = "路径规划管理器未初始化";
|
||
LogManager.Error("路径规划管理器未初始化");
|
||
}
|
||
}, "新建路径");
|
||
}
|
||
|
||
private async Task ExecuteDeletePathAsync()
|
||
{
|
||
if (SelectedPathRoute == null) return;
|
||
|
||
await SafeExecuteAsync(() =>
|
||
{
|
||
var pathName = SelectedPathRoute.Name;
|
||
AutoPathStatus = $"正在删除路径: {pathName}...";
|
||
|
||
// 清理当前路径的3D可视化显示(使用新的统一API)
|
||
if (PathPointRenderPlugin.Instance != null)
|
||
{
|
||
try
|
||
{
|
||
// 使用路径名找到对应的Core路径,获取其ID进行精确清理
|
||
string pathIdToRemove = null;
|
||
if (_pathPlanningManager != null)
|
||
{
|
||
var coreRoute = _pathPlanningManager.Routes.FirstOrDefault(r => r.Name == pathName);
|
||
if (coreRoute != null)
|
||
{
|
||
pathIdToRemove = coreRoute.Id;
|
||
LogManager.Info($"删除路径:找到Core路径ID: {pathIdToRemove}");
|
||
|
||
// 使用新的API精确删除该路径
|
||
PathPointRenderPlugin.Instance.RemovePath(pathIdToRemove);
|
||
LogManager.Info($"删除路径:已清除路径 {pathName} (ID: {pathIdToRemove}) 的可视化显示");
|
||
}
|
||
else
|
||
{
|
||
LogManager.Warning($"删除路径:未找到路径 {pathName} 对应的Core路径,无法清理可视化");
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"删除路径:清除路径可视化失败: {ex.Message}", ex);
|
||
throw; // 重新抛出异常,让上层处理
|
||
}
|
||
}
|
||
|
||
// 通知PathPlanningManager删除对应的路径
|
||
if (_pathPlanningManager != null)
|
||
{
|
||
var coreRoute = _pathPlanningManager.Routes.FirstOrDefault(r => r.Name == pathName);
|
||
if (coreRoute != null)
|
||
{
|
||
_pathPlanningManager.ModifiableRoutes.Remove(coreRoute);
|
||
}
|
||
}
|
||
|
||
// 更新UI
|
||
PathRoutes.Remove(SelectedPathRoute);
|
||
SelectedPathRoute = null;
|
||
AutoPathStatus = $"已完全删除路径: {pathName}";
|
||
}, "删除路径");
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 自动路径规划命令
|
||
|
||
private async Task ExecuteSelectStartPointAsync()
|
||
{
|
||
await SafeExecuteAsync(() =>
|
||
{
|
||
LogManager.Info("=== 开始执行选择起点命令 ===");
|
||
|
||
if (_pathPlanningManager == null)
|
||
{
|
||
LogManager.Error("PathPlanningManager为null,无法选择起点");
|
||
AutoPathStatus = "路径规划管理器未初始化";
|
||
return;
|
||
}
|
||
|
||
// 立即清理现有的起点标记(使用正确的路径ID)
|
||
if (PathPointRenderPlugin.Instance != null && _autoPathStartPointRoute != null)
|
||
{
|
||
PathPointRenderPlugin.Instance.RemovePath(_autoPathStartPointRoute.Id);
|
||
LogManager.WriteLog($"[选择起点] 已清除之前的起点标记,ID: {_autoPathStartPointRoute.Id}");
|
||
_autoPathStartPointRoute = null; // 清除引用
|
||
}
|
||
|
||
// 重置状态
|
||
_hasStartPoint = false;
|
||
AutoPathStartPoint = "未选择";
|
||
|
||
LogManager.Info("PathPlanningManager已初始化,开始设置选择状态");
|
||
IsSelectingStartPoint = true;
|
||
IsSelectingEndPoint = false;
|
||
AutoPathStatus = "请在3D视图中点击选择起点...";
|
||
|
||
// 进入自动路径规划模式 - 动态事件订阅
|
||
LogManager.Info("进入自动路径规划模式,启用动态事件订阅");
|
||
|
||
// 1. 禁用PathPlanningManager的鼠标处理
|
||
_pathPlanningManager.DisableMouseHandling();
|
||
|
||
// 2. 启用PathEditingViewModel的事件订阅
|
||
PathClickToolPlugin.MouseClicked -= OnAutoPathMouseClicked; // 先取消避免重复
|
||
PathClickToolPlugin.MouseClicked += OnAutoPathMouseClicked;
|
||
LogManager.Info("已启用PathEditingViewModel的鼠标事件订阅");
|
||
|
||
// 强制重新初始化ToolPlugin以确保获得鼠标焦点,自动路径不需要PathPlanningManager订阅事件
|
||
if (!ForceReinitializeToolPlugin(subscribeToEvents: false))
|
||
{
|
||
AutoPathStatus = "ToolPlugin初始化失败,请重试";
|
||
return;
|
||
}
|
||
|
||
// 在STA线程中调用Navisworks API
|
||
_pathPlanningManager.StartClickTool(PathPointType.StartPoint);
|
||
LogManager.Info("=== 选择起点命令设置完成,已启用动态事件订阅,等待用户点击 ===");
|
||
}, "选择起点");
|
||
}
|
||
|
||
private async Task ExecuteSelectEndPointAsync()
|
||
{
|
||
await SafeExecuteAsync(() =>
|
||
{
|
||
LogManager.Info("=== 开始执行选择终点命令 ===");
|
||
|
||
if (_pathPlanningManager == null)
|
||
{
|
||
LogManager.Error("PathPlanningManager为null,无法选择终点");
|
||
AutoPathStatus = "路径规划管理器未初始化";
|
||
return;
|
||
}
|
||
|
||
// 立即清理现有的终点标记(使用正确的路径ID)
|
||
if (PathPointRenderPlugin.Instance != null && _autoPathEndPointRoute != null)
|
||
{
|
||
PathPointRenderPlugin.Instance.RemovePath(_autoPathEndPointRoute.Id);
|
||
LogManager.WriteLog($"[选择终点] 已清除之前的终点标记,ID: {_autoPathEndPointRoute.Id}");
|
||
_autoPathEndPointRoute = null; // 清除引用
|
||
}
|
||
|
||
// 重置状态
|
||
_hasEndPoint = false;
|
||
AutoPathEndPoint = "未选择";
|
||
|
||
LogManager.Info("PathPlanningManager已初始化,开始设置选择状态");
|
||
IsSelectingStartPoint = false;
|
||
IsSelectingEndPoint = true;
|
||
AutoPathStatus = "请在3D视图中点击选择终点...";
|
||
|
||
// 进入自动路径规划模式 - 动态事件订阅
|
||
LogManager.Info("进入自动路径规划模式,启用动态事件订阅");
|
||
|
||
// 1. 禁用PathPlanningManager的鼠标处理
|
||
_pathPlanningManager.DisableMouseHandling();
|
||
|
||
// 2. 启用PathEditingViewModel的事件订阅
|
||
PathClickToolPlugin.MouseClicked -= OnAutoPathMouseClicked; // 先取消避免重复
|
||
PathClickToolPlugin.MouseClicked += OnAutoPathMouseClicked;
|
||
LogManager.Info("已启用PathEditingViewModel的鼠标事件订阅");
|
||
|
||
// 强制重新初始化ToolPlugin以确保获得鼠标焦点,自动路径不需要PathPlanningManager订阅事件
|
||
if (!ForceReinitializeToolPlugin(subscribeToEvents: false))
|
||
{
|
||
AutoPathStatus = "ToolPlugin初始化失败,请重试";
|
||
return;
|
||
}
|
||
|
||
// 在STA线程中调用Navisworks API
|
||
_pathPlanningManager.StartClickTool(PathPointType.EndPoint);
|
||
LogManager.Info("=== 选择终点命令设置完成,已启用动态事件订阅,等待用户点击 ===");
|
||
}, "选择终点");
|
||
}
|
||
|
||
private async Task ExecuteAutoPlanPathAsync()
|
||
{
|
||
await SafeExecuteAsync(async () =>
|
||
{
|
||
if (!_hasStartPoint || !_hasEndPoint)
|
||
{
|
||
AutoPathStatus = "请先选择起点和终点";
|
||
return;
|
||
}
|
||
|
||
AutoPathStatus = "正在计算最优路径...";
|
||
LogManager.Info("=== 开始执行自动路径规划 ===");
|
||
|
||
// 确保在开始自动路径规划前清理任何残留的事件订阅
|
||
LogManager.WriteLog("[自动路径规划] 规划前清理事件订阅状态");
|
||
CleanupAutoPathEventSubscriptions();
|
||
if (_pathPlanningManager != null)
|
||
{
|
||
_pathPlanningManager.StopClickTool();
|
||
}
|
||
|
||
// 清理所有临时路径,确保干净的起始状态
|
||
LogManager.WriteLog("[自动路径规划] 清理所有临时路径和历史渲染对象");
|
||
ClearTemporaryAutoPathMarkers();
|
||
if (PathPointRenderPlugin.Instance != null)
|
||
{
|
||
PathPointRenderPlugin.Instance.ClearAllPaths(); // 清理所有可能的历史路径
|
||
LogManager.WriteLog("[自动路径规划] 已清理所有历史路径对象");
|
||
}
|
||
|
||
// 调用PathPlanningManager的自动路径规划功能
|
||
var startPathPoint = new PathPoint
|
||
{
|
||
Name = "自动起点",
|
||
Position = _startPoint3D,
|
||
Type = PathPointType.StartPoint
|
||
};
|
||
var endPathPoint = new PathPoint
|
||
{
|
||
Name = "自动终点",
|
||
Position = _endPoint3D,
|
||
Type = PathPointType.EndPoint
|
||
};
|
||
|
||
LogManager.Info($"起点: ({_startPoint3D.X:F2}, {_startPoint3D.Y:F2}, {_startPoint3D.Z:F2})");
|
||
LogManager.Info($"终点: ({_endPoint3D.X:F2}, {_endPoint3D.Y:F2}, {_endPoint3D.Z:F2})");
|
||
|
||
// 计算车辆半径:基于长度和宽度的较大值的一半,高度暂时不参与半径计算
|
||
var vehicleRadius = Math.Max(VehicleLength, VehicleWidth) / 2.0;
|
||
|
||
LogManager.Info($"车辆参数: 长{VehicleLength:F1}m × 宽{VehicleWidth:F1}m × 高{VehicleHeight:F1}m");
|
||
LogManager.Info($"计算得出车辆半径: {vehicleRadius:F2}m (基于长宽较大值的一半)");
|
||
|
||
// 确定网格大小:如果启用手动设置则使用用户设置的值,否则使用-1(自动选择)
|
||
var gridSize = IsGridSizeManuallyEnabled ? GridSize : -1;
|
||
|
||
LogManager.Info($"网格大小设置: {(IsGridSizeManuallyEnabled ? $"手动设置 {GridSize:F1}米" : "自动选择")}");
|
||
|
||
// 在STA线程中执行Navisworks API调用
|
||
var pathRoute = await _pathPlanningManager?.AutoPlanPath(startPathPoint, endPathPoint, vehicleRadius, SafetyMargin, gridSize);
|
||
|
||
if (pathRoute != null && pathRoute.Points.Count > 0)
|
||
{
|
||
LogManager.Info($"路径规划成功,共 {pathRoute.Points.Count} 个点");
|
||
|
||
// 自动路径规划完成后,执行完整的事件订阅清理
|
||
LogManager.WriteLog("[自动路径规划] 规划完成后清理事件订阅状态");
|
||
CleanupAutoPathEventSubscriptions();
|
||
if (_pathPlanningManager != null)
|
||
{
|
||
_pathPlanningManager.StopClickTool();
|
||
}
|
||
|
||
// 清理临时的起点和终点标记
|
||
ClearTemporaryAutoPathMarkers();
|
||
|
||
// 不在这里直接操作UI,让RouteGenerated事件处理UI更新
|
||
// 这避免了重复的路径添加和UI更新冲突
|
||
|
||
// 获取实际使用的网格大小信息
|
||
var actualGridSize = IsGridSizeManuallyEnabled ? GridSize : -1;
|
||
var gridInfo = actualGridSize > 0 ? $"网格{actualGridSize:F1}m" : "自动网格";
|
||
AutoPathStatus = $"✅ 路径规划完成!共 {pathRoute.Points.Count} 个路径点,长度 {pathRoute.TotalLength:F2}米({gridInfo})";
|
||
LogManager.Info($"✅ 自动路径规划完成,RouteGenerated事件将处理UI更新");
|
||
}
|
||
else
|
||
{
|
||
// 检查是否已经通过ErrorOccurred事件设置了详细错误信息
|
||
if (!AutoPathStatus.Contains("❌ 自动路径规划失败"))
|
||
{
|
||
// 如果没有详细错误信息,则设置通用错误提示
|
||
AutoPathStatus = "❌ 路径规划失败,未找到可行路径";
|
||
LogManager.Warning("自动路径规划失败:未找到可行路径");
|
||
}
|
||
else
|
||
{
|
||
// 如果已有详细错误信息,保留不覆盖
|
||
LogManager.Info("保留ErrorOccurred事件设置的详细错误信息,不覆盖AutoPathStatus");
|
||
}
|
||
|
||
// 失败情况下也要清理事件订阅和临时标记
|
||
LogManager.WriteLog("[自动路径规划] 规划失败后清理所有状态");
|
||
CleanupAutoPathEventSubscriptions();
|
||
if (_pathPlanningManager != null)
|
||
{
|
||
_pathPlanningManager.StopClickTool();
|
||
}
|
||
|
||
// 清理临时的起点和终点标记
|
||
ClearTemporaryAutoPathMarkers();
|
||
|
||
// 确保失败后也清理所有可能的残留路径对象
|
||
if (PathPointRenderPlugin.Instance != null)
|
||
{
|
||
PathPointRenderPlugin.Instance.ClearAllPaths();
|
||
LogManager.WriteLog("[自动路径规划] 规划失败后已清理所有渲染对象");
|
||
}
|
||
}
|
||
}, "自动路径规划");
|
||
}
|
||
|
||
private async Task ExecuteClearAutoPathAsync()
|
||
{
|
||
// 使用基类的SafeExecuteAsync方法,确保在STA线程上执行
|
||
await SafeExecuteAsync(() =>
|
||
{
|
||
LogManager.Info("=== 开始重置自动路径规划状态 ===");
|
||
|
||
// 停止任何正在进行的点选择
|
||
if (_pathPlanningManager != null)
|
||
{
|
||
_pathPlanningManager.StopClickTool();
|
||
PathClickToolPlugin.MouseClicked -= OnAutoPathMouseClicked;
|
||
}
|
||
|
||
// 清除所有路径的可视化显示(与新建路径按钮保持一致)
|
||
if (PathPointRenderPlugin.Instance != null)
|
||
{
|
||
try
|
||
{
|
||
// 清除所有路径可视化,包括自动路径和手动路径
|
||
PathPointRenderPlugin.Instance.ClearAllPaths();
|
||
LogManager.Info("重置参数:已清除所有路径可视化显示");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"重置参数:清除路径可视化失败: {ex.Message}", ex);
|
||
// 不抛出异常,继续执行状态重置
|
||
}
|
||
}
|
||
|
||
// 强制重新初始化ToolPlugin以重新获得鼠标焦点,重置时不需要订阅事件
|
||
if (_pathPlanningManager != null)
|
||
{
|
||
ForceReinitializeToolPlugin(subscribeToEvents: false);
|
||
}
|
||
|
||
// 重置所有状态
|
||
_hasStartPoint = false;
|
||
_hasEndPoint = false;
|
||
_startPoint3D = new Point3D();
|
||
_endPoint3D = new Point3D();
|
||
AutoPathStartPoint = "未选择";
|
||
AutoPathEndPoint = "未选择";
|
||
AutoPathStatus = "就绪";
|
||
IsSelectingStartPoint = false;
|
||
IsSelectingEndPoint = false;
|
||
|
||
// 清除路径引用
|
||
_autoPathStartPointRoute = null;
|
||
_autoPathEndPointRoute = null;
|
||
|
||
// 通知Can Execute属性更改
|
||
OnPropertyChanged(nameof(CanExecuteAutoPlanPath));
|
||
|
||
LogManager.Info("自动路径规划参数和起终点标记已完全重置,ToolPlugin已重新初始化");
|
||
}, "重置自动路径规划");
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 路径编辑命令 - 待实现
|
||
|
||
private async Task ExecuteRenamePathAsync()
|
||
{
|
||
if (SelectedPathRoute == null) return;
|
||
|
||
await SafeExecuteAsync(async () =>
|
||
{
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
// TODO: 实现重命名对话框
|
||
var newName = $"路径 {PathRoutes.Count}";
|
||
var oldName = SelectedPathRoute.Name;
|
||
SelectedPathRoute.Name = newName;
|
||
AutoPathStatus = $"路径已重命名: {oldName} -> {newName}";
|
||
PathFileStatus = "未保存";
|
||
});
|
||
}, "重命名路径");
|
||
}
|
||
|
||
private async Task ExecuteAddPathPointAsync()
|
||
{
|
||
if (!CanExecuteStartEdit) return;
|
||
|
||
await SafeExecuteAsync(async () =>
|
||
{
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
IsPathEditMode = true;
|
||
if (SelectedPathRoute != null)
|
||
{
|
||
SelectedPathRoute.IsActive = true;
|
||
AutoPathStatus = $"正在激活3D路径编辑模式: {SelectedPathRoute.Name}...";
|
||
|
||
// 启动PathPlanningManager的点击工具,这会设置正确的编辑状态
|
||
if (_pathPlanningManager != null)
|
||
{
|
||
try
|
||
{
|
||
_pathPlanningManager.StartClickTool(PathPointType.WayPoint);
|
||
LogManager.Info($"已启动PathPlanningManager点击工具: {SelectedPathRoute.Name}");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"启动点击工具失败: {ex.Message}");
|
||
AutoPathStatus = "启动3D编辑工具失败,请重试";
|
||
IsPathEditMode = false;
|
||
return;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
AutoPathStatus = "路径规划管理器未初始化";
|
||
LogManager.Error("添加路径点:PathPlanningManager未初始化");
|
||
IsPathEditMode = false;
|
||
return;
|
||
}
|
||
|
||
AutoPathStatus = $"已进入3D路径编辑模式: {SelectedPathRoute.Name}";
|
||
PathEditTipText = "✓ 预览模式已激活 - 在3D视图中点击设置灰色预览点,满意后点击[确认]添加到路径,继续编辑或点击[结束]完成";
|
||
LogManager.Info($"开始添加路径点: {SelectedPathRoute.Name},已启动点击工具");
|
||
|
||
// 手动触发按钮状态更新
|
||
OnPropertyChanged(nameof(CanExecuteStartEdit));
|
||
OnPropertyChanged(nameof(CanExecuteEndEdit));
|
||
}
|
||
});
|
||
}, "添加路径点");
|
||
}
|
||
|
||
private async Task ExecuteEndEditAsync()
|
||
{
|
||
if (!CanExecuteEndEdit) return;
|
||
|
||
await SafeExecuteAsync(async () =>
|
||
{
|
||
bool success = false;
|
||
string operationMessage = "";
|
||
bool wasInPreviewMode = false;
|
||
|
||
if (_pathPlanningManager != null)
|
||
{
|
||
// 检查是否在修改路径点模式
|
||
bool isEditingPoint = _pathPlanningManager.PathEditState == PathEditState.EditingPoint;
|
||
|
||
if (isEditingPoint)
|
||
{
|
||
// 修改路径点模式 - 确认修改
|
||
success = _pathPlanningManager.ConfirmEditPoint();
|
||
operationMessage = success ? "路径点修改已确认" : "路径点修改确认失败";
|
||
LogManager.Info($"[UI-修改路径点] {operationMessage}");
|
||
}
|
||
else
|
||
{
|
||
// 记录是否在预览模式(因为确认预览点后会退出预览模式)
|
||
wasInPreviewMode = _pathPlanningManager.IsPreviewMode;
|
||
|
||
if (wasInPreviewMode)
|
||
{
|
||
// 预览模式 - 先确认预览点,然后立即完成编辑
|
||
var confirmedPoint = _pathPlanningManager.ConfirmPreviewPoint();
|
||
if (confirmedPoint != null)
|
||
{
|
||
LogManager.Info($"[UI-预览模式] 预览点已确认: {confirmedPoint.Name},现在完成编辑");
|
||
|
||
// 立即完成编辑,不让用户继续添加点
|
||
success = _pathPlanningManager.FinishEditing();
|
||
operationMessage = success ? $"预览点已确认并完成编辑: {confirmedPoint.Name}" : "预览点确认后完成编辑失败";
|
||
}
|
||
else
|
||
{
|
||
LogManager.Error("[UI-预览模式] 预览点确认失败");
|
||
operationMessage = "预览点确认失败";
|
||
success = false;
|
||
}
|
||
|
||
LogManager.Info($"[UI-预览模式] {operationMessage}");
|
||
}
|
||
else
|
||
{
|
||
// 普通模式 - 直接完成编辑
|
||
success = _pathPlanningManager.FinishEditing();
|
||
operationMessage = success ? "路径编辑完成" : "路径编辑完成失败";
|
||
|
||
LogManager.Info($"[UI-编辑模式] {operationMessage}");
|
||
}
|
||
}
|
||
}
|
||
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
if (success)
|
||
{
|
||
// 编辑完成成功
|
||
if (SelectedPathRoute != null)
|
||
{
|
||
if (_pathPlanningManager?.PathEditState == PathEditState.EditingPoint || operationMessage.Contains("路径点修改"))
|
||
{
|
||
AutoPathStatus = $"✅ 路径点修改完成: {SelectedPathRoute.Name}";
|
||
PathEditTipText = "路径点修改已完成";
|
||
}
|
||
else if (wasInPreviewMode)
|
||
{
|
||
AutoPathStatus = $"✅ 预览点已确认,路径编辑完成: {SelectedPathRoute.Name}";
|
||
}
|
||
else
|
||
{
|
||
AutoPathStatus = $"✅ 路径编辑完成: {SelectedPathRoute.Name} - 最后一个点已自动设置为终点";
|
||
}
|
||
PathFileStatus = "已完成";
|
||
}
|
||
|
||
LogManager.Info("[UI状态] 路径编辑完成,等待状态变更事件更新按钮");
|
||
}
|
||
else
|
||
{
|
||
// 编辑完成失败
|
||
if (SelectedPathRoute != null)
|
||
{
|
||
AutoPathStatus = $"❌ 路径编辑失败: {SelectedPathRoute.Name}";
|
||
PathFileStatus = "编辑失败";
|
||
}
|
||
|
||
LogManager.Error("[UI状态] 路径编辑失败");
|
||
}
|
||
|
||
// 手动触发按钮状态更新 - 确保UI及时响应
|
||
OnPropertyChanged(nameof(CanExecuteStartEdit));
|
||
OnPropertyChanged(nameof(CanExecuteEndEdit));
|
||
OnPropertyChanged(nameof(CanExecuteCancelModifyPoint));
|
||
});
|
||
}, "结束路径编辑");
|
||
}
|
||
|
||
private async Task ExecuteClearPathAsync()
|
||
{
|
||
if (!CanExecuteClearPath) return;
|
||
|
||
await SafeExecuteAsync(async () =>
|
||
{
|
||
var pointCount = SelectedPathRoute.Points.Count;
|
||
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
SelectedPathRoute.Points.Clear();
|
||
AutoPathStatus = $"已清空路径 {SelectedPathRoute.Name} 的 {pointCount} 个点";
|
||
PathFileStatus = "未保存";
|
||
});
|
||
}, "清空路径");
|
||
}
|
||
|
||
private async Task ExecuteDeletePointAsync(PathPointViewModel point)
|
||
{
|
||
if (SelectedPathRoute == null || point == null) return;
|
||
|
||
await SafeExecuteAsync(async () =>
|
||
{
|
||
// 1. 查找对应的Core数据
|
||
var coreRoute = _pathPlanningManager?.Routes?.FirstOrDefault(r => r.Name == SelectedPathRoute.Name);
|
||
if (coreRoute == null)
|
||
{
|
||
LogManager.Warning($"未找到对应的Core路径: {SelectedPathRoute.Name}");
|
||
return;
|
||
}
|
||
|
||
// 2. 在Core路径中查找对应的点(通过位置和名称匹配)
|
||
var corePoint = coreRoute.Points.FirstOrDefault(p =>
|
||
p.Name == point.Name &&
|
||
Math.Abs(p.Position.X - point.X) < 0.001 &&
|
||
Math.Abs(p.Position.Y - point.Y) < 0.001 &&
|
||
Math.Abs(p.Position.Z - point.Z) < 0.001);
|
||
|
||
if (corePoint == null)
|
||
{
|
||
LogManager.Warning($"未找到对应的Core路径点: {point.Name}");
|
||
// 即使Core数据不匹配,也要更新UI数据保持一致性
|
||
}
|
||
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
// 3. 同时删除UI和Core数据
|
||
SelectedPathRoute.Points.Remove(point);
|
||
LogManager.Info($"已从UI删除路径点: {point.Name}");
|
||
|
||
if (corePoint != null)
|
||
{
|
||
// 删除Core数据
|
||
coreRoute.Points.Remove(corePoint);
|
||
LogManager.Info($"已从Core数据删除路径点: {corePoint.Name}");
|
||
|
||
// 调用PathPlanningManager的3D删除方法
|
||
_pathPlanningManager.RemovePathPointFrom3D(corePoint);
|
||
}
|
||
|
||
// 4. 更新UI状态
|
||
AutoPathStatus = $"已删除路径点: {point.Name}";
|
||
PathFileStatus = "未保存";
|
||
|
||
LogManager.Info($"路径点删除完成,当前UI点数: {SelectedPathRoute.Points.Count}, Core点数: {coreRoute?.Points.Count ?? 0}");
|
||
});
|
||
|
||
// 5. 刷新3D可视化显示
|
||
UpdatePathVisualization();
|
||
LogManager.Info("已触发路径可视化更新");
|
||
|
||
}, "删除路径点");
|
||
}
|
||
|
||
private async Task ExecuteModifyPointAsync()
|
||
{
|
||
if (!CanExecuteModifyPoint) return;
|
||
|
||
await SafeExecuteAsync(async () =>
|
||
{
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
if (SelectedPathPoint != null && _pathPlanningManager != null)
|
||
{
|
||
// 获取选中路径点的索引
|
||
var currentRoute = _pathPlanningManager.CurrentRoute;
|
||
if (currentRoute != null && currentRoute.Points != null)
|
||
{
|
||
int pointIndex = -1;
|
||
|
||
// 详细调试信息
|
||
LogManager.Info($"开始查找路径点索引 - 选中点Id: {SelectedPathPoint.Id}, 选中点名称: {SelectedPathPoint.Name}");
|
||
LogManager.Info($"当前路径包含 {currentRoute.Points.Count} 个路径点");
|
||
|
||
// 遍历查找匹配的Id
|
||
for (int i = 0; i < currentRoute.Points.Count; i++)
|
||
{
|
||
var currentPoint = currentRoute.Points[i];
|
||
LogManager.Info($"检查路径点 [{i}] - Id: {currentPoint.Id}, 名称: {currentPoint.Name}");
|
||
|
||
if (currentPoint.Id == SelectedPathPoint.Id)
|
||
{
|
||
pointIndex = i;
|
||
LogManager.Info($"找到匹配的路径点,索引: {pointIndex}");
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (pointIndex >= 0)
|
||
{
|
||
// 调用PathPlanningManager开始修改路径点
|
||
bool success = _pathPlanningManager.StartEditingPoint(pointIndex);
|
||
|
||
if (success)
|
||
{
|
||
AutoPathStatus = $"正在修改路径点: {SelectedPathPoint.Name} - 请在3D视图中点击新位置";
|
||
PathEditTipText = "修改路径点模式:在3D视图中点击选择新位置,然后点击'确认'按钮完成修改";
|
||
LogManager.Info($"已开始修改路径点: {SelectedPathPoint.Name}, 索引: {pointIndex}");
|
||
|
||
// 更新UI状态
|
||
OnPropertyChanged(nameof(AutoPathStatus));
|
||
OnPropertyChanged(nameof(PathEditTipText));
|
||
|
||
// 刷新命令状态
|
||
OnPropertyChanged(nameof(CanExecuteStartEdit));
|
||
OnPropertyChanged(nameof(CanExecuteEndEdit));
|
||
OnPropertyChanged(nameof(CanExecuteModifyPoint));
|
||
OnPropertyChanged(nameof(CanExecuteCancelModifyPoint));
|
||
}
|
||
else
|
||
{
|
||
AutoPathStatus = "修改路径点失败,请检查路径状态";
|
||
LogManager.Error($"开始修改路径点失败: {SelectedPathPoint.Name}, 索引: {pointIndex}");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
AutoPathStatus = "找不到选中的路径点";
|
||
LogManager.Error($"找不到路径点索引 - 选中点Id: {SelectedPathPoint.Id}, 选中点名称: {SelectedPathPoint.Name}");
|
||
LogManager.Error("可能原因:1)选中的路径点与当前路径不匹配 2)路径点数据不同步 3)Id不匹配");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (currentRoute == null)
|
||
{
|
||
AutoPathStatus = "当前没有活动路径";
|
||
LogManager.Error("尝试修改路径点时没有当前路径");
|
||
}
|
||
else
|
||
{
|
||
AutoPathStatus = "当前路径没有路径点数据";
|
||
LogManager.Error("当前路径的Points集合为空");
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (SelectedPathPoint == null)
|
||
{
|
||
AutoPathStatus = "请先选中要修改的路径点";
|
||
LogManager.Warning("修改路径点时没有选中路径点");
|
||
}
|
||
else
|
||
{
|
||
AutoPathStatus = "路径规划管理器未初始化";
|
||
LogManager.Error("修改路径点时PathPlanningManager为空");
|
||
}
|
||
}
|
||
});
|
||
}, "修改路径点");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 取消修改路径点
|
||
/// </summary>
|
||
private async Task ExecuteCancelModifyPointAsync()
|
||
{
|
||
if (!CanExecuteCancelModifyPoint) return;
|
||
|
||
await SafeExecuteAsync(async () =>
|
||
{
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
if (_pathPlanningManager != null)
|
||
{
|
||
// 调用PathPlanningManager取消修改路径点
|
||
bool success = _pathPlanningManager.CancelEditPoint();
|
||
|
||
if (success)
|
||
{
|
||
AutoPathStatus = "已取消修改路径点";
|
||
PathEditTipText = "修改路径点操作已取消";
|
||
LogManager.Info("用户取消了修改路径点操作");
|
||
}
|
||
else
|
||
{
|
||
AutoPathStatus = "取消修改路径点失败";
|
||
LogManager.Error("取消修改路径点失败");
|
||
}
|
||
|
||
// 更新UI状态
|
||
OnPropertyChanged(nameof(AutoPathStatus));
|
||
OnPropertyChanged(nameof(PathEditTipText));
|
||
|
||
// 刷新命令状态
|
||
OnPropertyChanged(nameof(CanExecuteStartEdit));
|
||
OnPropertyChanged(nameof(CanExecuteEndEdit));
|
||
OnPropertyChanged(nameof(CanExecuteModifyPoint));
|
||
OnPropertyChanged(nameof(CanExecuteCancelModifyPoint));
|
||
}
|
||
});
|
||
}, "取消修改路径点");
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 文件管理命令
|
||
|
||
private async Task ExecuteImportPathAsync()
|
||
{
|
||
await SafeExecuteAsync(async () =>
|
||
{
|
||
try
|
||
{
|
||
// 打开文件选择对话框
|
||
var openFileDialog = new OpenFileDialog
|
||
{
|
||
Title = "导入路径文件",
|
||
Filter = "路径文件 (*.xml;*.json)|*.xml;*.json|XML文件 (*.xml)|*.xml|JSON文件 (*.json)|*.json|所有文件 (*.*)|*.*",
|
||
FilterIndex = 1,
|
||
CheckFileExists = true,
|
||
CheckPathExists = true
|
||
};
|
||
|
||
if (openFileDialog.ShowDialog() == true)
|
||
{
|
||
var filePath = openFileDialog.FileName;
|
||
var extension = Path.GetExtension(filePath).ToLower();
|
||
|
||
// 确定导入格式
|
||
ExportFormat importFormat;
|
||
switch (extension)
|
||
{
|
||
case ".json":
|
||
importFormat = ExportFormat.Json;
|
||
break;
|
||
case ".csv":
|
||
importFormat = ExportFormat.Csv;
|
||
break;
|
||
default:
|
||
importFormat = ExportFormat.Xml;
|
||
break;
|
||
}
|
||
|
||
// 创建导入参数
|
||
var importParameters = new ImportPathParameters
|
||
{
|
||
FilePath = filePath,
|
||
ImportFormat = importFormat,
|
||
MergeWithExisting = true,
|
||
CreateBackup = true,
|
||
DuplicateHandling = DuplicateNameHandling.Rename,
|
||
ValidateImportedData = true,
|
||
AutoSelectFirstRoute = true,
|
||
Description = $"导入路径文件: {Path.GetFileName(filePath)}"
|
||
};
|
||
|
||
// 创建并执行导入命令
|
||
var importCommand = new ImportPathCommand(importParameters, _pathPlanningManager);
|
||
var result = await importCommand.ExecuteAsync();
|
||
|
||
if (result.IsSuccess)
|
||
{
|
||
// 更新UI状态
|
||
RefreshPathRoutes();
|
||
|
||
// 如果需要自动选择第一个导入的路径,通过专门的选择逻辑处理
|
||
if (importParameters.AutoSelectFirstRoute && PathRoutes.Count > 0)
|
||
{
|
||
var firstRoute = PathRoutes.FirstOrDefault();
|
||
if (firstRoute != null)
|
||
{
|
||
SelectedPathRoute = firstRoute;
|
||
LogManager.Info($"导入完成后自动选择路径: {firstRoute.Name}");
|
||
}
|
||
}
|
||
|
||
var importResult = (result as PathPlanningResult<ImportPathResult>)?.Data;
|
||
if (importResult != null)
|
||
{
|
||
AutoPathStatus = $"✅ 导入成功: {importResult.ImportedCount}/{importResult.TotalInFile} 个路径";
|
||
if (importResult.FailedCount > 0)
|
||
{
|
||
AutoPathStatus += $" (失败 {importResult.FailedCount} 个)";
|
||
}
|
||
if (!string.IsNullOrEmpty(importResult.BackupFilePath))
|
||
{
|
||
LogManager.Info($"导入前备份已创建: {importResult.BackupFilePath}");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
AutoPathStatus = $"✅ 路径导入完成: {Path.GetFileName(filePath)}";
|
||
}
|
||
|
||
PathFileStatus = "已导入";
|
||
}
|
||
else
|
||
{
|
||
AutoPathStatus = $"❌ 导入失败: {result.ErrorMessage}";
|
||
PathFileStatus = "导入失败";
|
||
LogManager.Error($"路径导入失败: {result.ErrorMessage}");
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"导入路径异常: {ex.Message}", ex);
|
||
AutoPathStatus = $"❌ 导入异常: {ex.Message}";
|
||
PathFileStatus = "导入异常";
|
||
}
|
||
}, "导入路径");
|
||
}
|
||
|
||
private async Task ExecuteExportPathAsync()
|
||
{
|
||
if (!CanExecuteExportPath) return;
|
||
|
||
await SafeExecuteAsync(async () =>
|
||
{
|
||
try
|
||
{
|
||
// 打开文件保存对话框
|
||
var saveFileDialog = new SaveFileDialog
|
||
{
|
||
Title = "导出全部路径",
|
||
Filter = "XML文件 (*.xml)|*.xml|JSON文件 (*.json)|*.json|CSV文件 (*.csv)|*.csv|所有文件 (*.*)|*.*",
|
||
FilterIndex = 1,
|
||
FileName = $"路径导出_{DateTime.Now:yyyyMMdd_HHmmss}",
|
||
DefaultExt = "xml",
|
||
AddExtension = true
|
||
};
|
||
|
||
if (saveFileDialog.ShowDialog() == true)
|
||
{
|
||
var filePath = saveFileDialog.FileName;
|
||
var extension = Path.GetExtension(filePath).ToLower();
|
||
|
||
// 确定导出格式
|
||
ExportFormat exportFormat;
|
||
switch (extension)
|
||
{
|
||
case ".json":
|
||
exportFormat = ExportFormat.Json;
|
||
break;
|
||
case ".csv":
|
||
exportFormat = ExportFormat.Csv;
|
||
break;
|
||
default:
|
||
exportFormat = ExportFormat.Xml;
|
||
break;
|
||
}
|
||
|
||
// 创建导出参数 - 导出所有路径
|
||
var exportParameters = new ExportPathParameters
|
||
{
|
||
ExportAll = true,
|
||
OutputFilePath = filePath,
|
||
ExportFormat = exportFormat,
|
||
OverwriteExisting = true,
|
||
ValidateExportData = true,
|
||
GenerateReport = false,
|
||
Description = $"导出全部路径到: {Path.GetFileName(filePath)}"
|
||
};
|
||
|
||
// 创建并执行导出命令
|
||
var exportCommand = new ExportPathCommand(exportParameters, _pathPlanningManager);
|
||
var result = await exportCommand.ExecuteAsync();
|
||
|
||
if (result.IsSuccess)
|
||
{
|
||
var exportResult = (result as PathPlanningResult<ExportPathResult>)?.Data;
|
||
if (exportResult != null)
|
||
{
|
||
AutoPathStatus = $"✅ 导出全部成功: {exportResult.ExportedCount} 个路径到 {Path.GetFileName(filePath)}";
|
||
if (exportResult.FailedCount > 0)
|
||
{
|
||
AutoPathStatus += $" (失败 {exportResult.FailedCount} 个)";
|
||
}
|
||
LogManager.Info($"导出完成,文件大小: {exportResult.FileSizeMB:F2} MB");
|
||
}
|
||
else
|
||
{
|
||
AutoPathStatus = $"✅ 全部路径导出完成: {Path.GetFileName(filePath)}";
|
||
}
|
||
|
||
PathFileStatus = "已导出";
|
||
}
|
||
else
|
||
{
|
||
AutoPathStatus = $"❌ 导出全部失败: {result.ErrorMessage}";
|
||
PathFileStatus = "导出全部失败";
|
||
LogManager.Error($"全部路径导出失败: {result.ErrorMessage}");
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"导出全部路径异常: {ex.Message}", ex);
|
||
AutoPathStatus = $"❌ 导出全部异常: {ex.Message}";
|
||
PathFileStatus = "导出全部异常";
|
||
}
|
||
}, "导出全部路径");
|
||
}
|
||
|
||
private async Task ExecuteSaveAsPathAsync()
|
||
{
|
||
if (!CanExecuteSaveAsPath) return;
|
||
|
||
await SafeExecuteAsync(async () =>
|
||
{
|
||
try
|
||
{
|
||
// 打开文件保存对话框
|
||
var saveFileDialog = new SaveFileDialog
|
||
{
|
||
Title = $"导出选中路径: {SelectedPathRoute?.Name}",
|
||
Filter = "XML文件 (*.xml)|*.xml|JSON文件 (*.json)|*.json|CSV文件 (*.csv)|*.csv|所有文件 (*.*)|*.*",
|
||
FilterIndex = 1,
|
||
FileName = SelectedPathRoute?.Name?.Replace(" ", "_") ?? "路径",
|
||
DefaultExt = "xml",
|
||
AddExtension = true
|
||
};
|
||
|
||
if (saveFileDialog.ShowDialog() == true)
|
||
{
|
||
var filePath = saveFileDialog.FileName;
|
||
var extension = Path.GetExtension(filePath).ToLower();
|
||
|
||
// 确定导出格式
|
||
ExportFormat exportFormat;
|
||
switch (extension)
|
||
{
|
||
case ".json":
|
||
exportFormat = ExportFormat.Json;
|
||
break;
|
||
case ".csv":
|
||
exportFormat = ExportFormat.Csv;
|
||
break;
|
||
default:
|
||
exportFormat = ExportFormat.Xml;
|
||
break;
|
||
}
|
||
|
||
// 查找当前选中路径对应的Core路径
|
||
var coreRoute = _pathPlanningManager.Routes.FirstOrDefault(r => r.Name == SelectedPathRoute.Name);
|
||
if (coreRoute != null)
|
||
{
|
||
// 创建导出参数 - 只导出当前选中的路径
|
||
var exportParameters = new ExportPathParameters
|
||
{
|
||
PathsToExport = new System.Collections.Generic.List<PathRoute> { coreRoute },
|
||
OutputFilePath = filePath,
|
||
ExportFormat = exportFormat,
|
||
OverwriteExisting = true,
|
||
ValidateExportData = true,
|
||
GenerateReport = false,
|
||
Description = $"导出选中路径: {SelectedPathRoute.Name}"
|
||
};
|
||
|
||
// 创建并执行导出命令
|
||
var exportCommand = new ExportPathCommand(exportParameters, _pathPlanningManager);
|
||
var result = await exportCommand.ExecuteAsync();
|
||
|
||
if (result.IsSuccess)
|
||
{
|
||
var exportResult = (result as PathPlanningResult<ExportPathResult>)?.Data;
|
||
if (exportResult != null)
|
||
{
|
||
AutoPathStatus = $"✅ 导出选中路径成功: {SelectedPathRoute.Name} -> {Path.GetFileName(filePath)}";
|
||
LogManager.Info($"导出选中路径完成,文件大小: {exportResult.FileSizeMB:F2} MB");
|
||
}
|
||
else
|
||
{
|
||
AutoPathStatus = $"✅ 导出选中路径完成: {Path.GetFileName(filePath)}";
|
||
}
|
||
|
||
PathFileStatus = "已导出选中路径";
|
||
}
|
||
else
|
||
{
|
||
AutoPathStatus = $"❌ 导出选中路径失败: {result.ErrorMessage}";
|
||
PathFileStatus = "导出选中路径失败";
|
||
LogManager.Error($"导出选中路径失败: {result.ErrorMessage}");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
AutoPathStatus = $"❌ 导出选中路径失败:找不到对应的Core路径: {SelectedPathRoute.Name}";
|
||
PathFileStatus = "导出选中路径失败";
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"导出选中路径异常: {ex.Message}", ex);
|
||
AutoPathStatus = $"❌ 导出选中路径异常: {ex.Message}";
|
||
PathFileStatus = "导出选中路径异常";
|
||
}
|
||
}, "导出选中路径");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 刷新路径集合显示
|
||
/// </summary>
|
||
private void RefreshPathRoutes()
|
||
{
|
||
try
|
||
{
|
||
if (_pathPlanningManager?.Routes != null)
|
||
{
|
||
// 清除现有的UI路径
|
||
PathRoutes.Clear();
|
||
|
||
// 重新加载所有路径
|
||
foreach (var coreRoute in _pathPlanningManager.Routes)
|
||
{
|
||
var pathViewModel = new PathRouteViewModel
|
||
{
|
||
Name = coreRoute.Name,
|
||
Description = coreRoute.Description,
|
||
IsActive = false
|
||
};
|
||
|
||
// 转换路径点
|
||
foreach (var corePoint in coreRoute.Points)
|
||
{
|
||
var wpfPoint = new PathPointViewModel
|
||
{
|
||
Id = corePoint.Id,
|
||
Name = corePoint.Name,
|
||
X = corePoint.X,
|
||
Y = corePoint.Y,
|
||
Z = corePoint.Z,
|
||
Type = corePoint.Type
|
||
};
|
||
pathViewModel.Points.Add(wpfPoint);
|
||
}
|
||
|
||
PathRoutes.Add(pathViewModel);
|
||
LogManager.Info($"RefreshPathRoutes: 添加路径ViewModel {coreRoute.Name} (Hash: {pathViewModel.GetHashCode()})");
|
||
}
|
||
|
||
LogManager.Info($"已刷新路径集合显示,共 {PathRoutes.Count} 个路径");
|
||
|
||
// 调试:列出所有PathRoutes中的路径名称和哈希码
|
||
for (int i = 0; i < PathRoutes.Count; i++)
|
||
{
|
||
var route = PathRoutes[i];
|
||
LogManager.Info($" PathRoutes[{i}]: {route.Name} (Hash: {route.GetHashCode()}, Points: {route.Points.Count})");
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"刷新路径集合失败: {ex.Message}", ex);
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#endregion
|
||
|
||
#region 事件处理
|
||
|
||
/// <summary>
|
||
/// 处理自动路径规划的鼠标点击事件
|
||
/// </summary>
|
||
private async void OnAutoPathMouseClicked(object sender, PickItemResult pickResult)
|
||
{
|
||
try
|
||
{
|
||
LogManager.WriteLog("[自动路径事件] ===== OnAutoPathMouseClicked被调用 =====");
|
||
|
||
if (pickResult == null)
|
||
{
|
||
LogManager.WriteLog("[自动路径事件] pickResult为null,退出处理");
|
||
return;
|
||
}
|
||
|
||
var point3D = pickResult.Point;
|
||
LogManager.WriteLog($"[自动路径事件] 接收到点击事件: ({point3D.X:F2}, {point3D.Y:F2}, {point3D.Z:F2})");
|
||
LogManager.WriteLog($"[自动路径事件] IsSelectingStartPoint: {IsSelectingStartPoint}, IsSelectingEndPoint: {IsSelectingEndPoint}");
|
||
|
||
if (IsSelectingStartPoint)
|
||
{
|
||
LogManager.WriteLog("[自动路径事件] 设置起点并停止点击工具");
|
||
SetAutoPathStartPoint(point3D);
|
||
await SafeExecuteAsync(() =>
|
||
{
|
||
// 退出自动路径规划模式 - 恢复事件订阅
|
||
LogManager.Info("起点选择完成,恢复正常事件订阅状态");
|
||
CleanupAutoPathEventSubscriptions();
|
||
_pathPlanningManager?.EnableMouseHandling(); // 恢复PathPlanningManager的鼠标处理
|
||
_pathPlanningManager?.StopClickTool();
|
||
}, "停止起点选择工具");
|
||
}
|
||
else if (IsSelectingEndPoint)
|
||
{
|
||
LogManager.WriteLog("[自动路径事件] 设置终点并停止点击工具");
|
||
SetAutoPathEndPoint(point3D);
|
||
await SafeExecuteAsync(() =>
|
||
{
|
||
// 退出自动路径规划模式 - 恢复事件订阅
|
||
LogManager.Info("终点选择完成,恢复正常事件订阅状态");
|
||
CleanupAutoPathEventSubscriptions();
|
||
_pathPlanningManager?.EnableMouseHandling(); // 恢复PathPlanningManager的鼠标处理
|
||
_pathPlanningManager?.StopClickTool();
|
||
}, "停止终点选择工具");
|
||
}
|
||
else
|
||
{
|
||
LogManager.WriteLog("[自动路径事件] 非选择状态,直接清理事件订阅");
|
||
CleanupAutoPathEventSubscriptions();
|
||
_pathPlanningManager?.EnableMouseHandling(); // 恢复PathPlanningManager的鼠标处理
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[自动路径事件] 处理点击事件异常: {ex.Message}");
|
||
AutoPathStatus = $"获取点击位置失败: {ex.Message}";
|
||
|
||
IsSelectingStartPoint = false;
|
||
IsSelectingEndPoint = false;
|
||
|
||
// 异常情况下也要清理事件订阅并恢复状态
|
||
try
|
||
{
|
||
CleanupAutoPathEventSubscriptions();
|
||
_pathPlanningManager?.EnableMouseHandling(); // 恢复PathPlanningManager的鼠标处理
|
||
_pathPlanningManager?.StopClickTool();
|
||
}
|
||
catch (Exception cleanupEx)
|
||
{
|
||
LogManager.Error($"[自动路径事件] 清理异常: {cleanupEx.Message}");
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置自动路径规划的起点
|
||
/// </summary>
|
||
public async void SetAutoPathStartPoint(Point3D point)
|
||
{
|
||
await SafeExecuteAsync(() =>
|
||
{
|
||
// 直接设置新的起点(清理逻辑已在ExecuteSelectStartPointAsync中处理)
|
||
_startPoint3D = point;
|
||
_hasStartPoint = true;
|
||
AutoPathStartPoint = $"({point.X:F2}, {point.Y:F2}, {point.Z:F2})";
|
||
IsSelectingStartPoint = false;
|
||
|
||
// 通知Can Execute属性更改
|
||
OnPropertyChanged(nameof(CanExecuteAutoPlanPath));
|
||
|
||
// 渲染新的起点标记
|
||
if (PathPointRenderPlugin.Instance != null)
|
||
{
|
||
// 创建临时路径只包含起点
|
||
_autoPathStartPointRoute = new PathRoute("AutoPathStartPoint")
|
||
{
|
||
Description = "自动路径规划起点"
|
||
};
|
||
var startPathPoint = new PathPoint
|
||
{
|
||
Name = "起点",
|
||
Position = point,
|
||
Type = PathPointType.StartPoint
|
||
};
|
||
_autoPathStartPointRoute.AddPoint(startPathPoint);
|
||
|
||
// 使用新的只渲染点的API,不绘制连线
|
||
PathPointRenderPlugin.Instance.RenderPointOnly(_autoPathStartPointRoute);
|
||
LogManager.Info($"起点路径已创建,ID: {_autoPathStartPointRoute.Id}");
|
||
}
|
||
|
||
if (_hasEndPoint)
|
||
{
|
||
AutoPathStatus = "起点和终点已设置,可以开始路径规划";
|
||
}
|
||
else
|
||
{
|
||
AutoPathStatus = "起点已设置,请选择终点";
|
||
}
|
||
|
||
LogManager.Info($"起点已设置: ({point.X:F2}, {point.Y:F2}, {point.Z:F2})");
|
||
}, "设置起点");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置自动路径规划的终点
|
||
/// </summary>
|
||
public async void SetAutoPathEndPoint(Point3D point)
|
||
{
|
||
await SafeExecuteAsync(() =>
|
||
{
|
||
// 直接设置新的终点(清理逻辑已在ExecuteSelectEndPointAsync中处理)
|
||
_endPoint3D = point;
|
||
_hasEndPoint = true;
|
||
AutoPathEndPoint = $"({point.X:F2}, {point.Y:F2}, {point.Z:F2})";
|
||
IsSelectingEndPoint = false;
|
||
|
||
// 通知Can Execute属性更改
|
||
OnPropertyChanged(nameof(CanExecuteAutoPlanPath));
|
||
|
||
// 渲染新的终点标记
|
||
if (PathPointRenderPlugin.Instance != null)
|
||
{
|
||
// 创建临时路径只包含终点
|
||
_autoPathEndPointRoute = new PathRoute("AutoPathEndPoint")
|
||
{
|
||
Description = "自动路径规划终点"
|
||
};
|
||
var endPathPoint = new PathPoint
|
||
{
|
||
Name = "终点",
|
||
Position = point,
|
||
Type = PathPointType.EndPoint
|
||
};
|
||
_autoPathEndPointRoute.AddPoint(endPathPoint);
|
||
|
||
// 使用新的只渲染点的API,不绘制连线
|
||
PathPointRenderPlugin.Instance.RenderPointOnly(_autoPathEndPointRoute);
|
||
LogManager.Info($"终点路径已创建,ID: {_autoPathEndPointRoute.Id}");
|
||
}
|
||
|
||
if (_hasStartPoint)
|
||
{
|
||
AutoPathStatus = "起点和终点已设置,可以开始路径规划";
|
||
}
|
||
else
|
||
{
|
||
AutoPathStatus = "终点已设置,请选择起点";
|
||
}
|
||
|
||
LogManager.Info($"终点已设置: ({point.X:F2}, {point.Y:F2}, {point.Z:F2})");
|
||
}, "设置终点");
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 辅助方法
|
||
|
||
/// <summary>
|
||
/// 安全执行异步操作
|
||
/// </summary>
|
||
private async Task SafeExecuteAsync(Func<Task> action, string operationName = "未知操作")
|
||
{
|
||
try
|
||
{
|
||
await action();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"{operationName}发生异常: {ex.Message}", ex);
|
||
AutoPathStatus = $"{operationName}失败: {ex.Message}";
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 清理自动路径相关的事件订阅
|
||
/// </summary>
|
||
private void CleanupAutoPathEventSubscriptions()
|
||
{
|
||
try
|
||
{
|
||
LogManager.WriteLog("[事件清理] 开始清理自动路径事件订阅");
|
||
|
||
// 移除所有可能的OnAutoPathMouseClicked订阅
|
||
PathClickToolPlugin.MouseClicked -= OnAutoPathMouseClicked;
|
||
|
||
// 使用反射检查并清理重复订阅
|
||
var mouseClickedEvent = typeof(PathClickToolPlugin).GetEvent("MouseClicked");
|
||
if (mouseClickedEvent != null)
|
||
{
|
||
var eventField = typeof(PathClickToolPlugin).GetField("MouseClicked",
|
||
System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
|
||
if (eventField != null)
|
||
{
|
||
var currentDelegate = (MulticastDelegate)eventField.GetValue(null);
|
||
if (currentDelegate != null)
|
||
{
|
||
var invocationList = currentDelegate.GetInvocationList();
|
||
LogManager.WriteLog($"[事件清理] 发现 {invocationList.Length} 个事件订阅者");
|
||
|
||
foreach (var handler in invocationList)
|
||
{
|
||
if (handler.Target == this && handler.Method.Name == "OnAutoPathMouseClicked")
|
||
{
|
||
PathClickToolPlugin.MouseClicked -= (EventHandler<PickItemResult>)handler;
|
||
LogManager.WriteLog($"[事件清理] 移除了PathEditingViewModel.OnAutoPathMouseClicked订阅");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
LogManager.WriteLog("[事件清理] 自动路径事件订阅清理完成");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[事件清理] 清理自动路径事件订阅失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 清理临时的自动路径起点和终点标记
|
||
/// </summary>
|
||
private void ClearTemporaryAutoPathMarkers()
|
||
{
|
||
try
|
||
{
|
||
LogManager.WriteLog("[临时标记清理] 开始清理自动路径临时标记");
|
||
|
||
if (PathPointRenderPlugin.Instance != null)
|
||
{
|
||
// 清除临时的起点标记(使用正确的路径ID)
|
||
if (_autoPathStartPointRoute != null)
|
||
{
|
||
PathPointRenderPlugin.Instance.RemovePath(_autoPathStartPointRoute.Id);
|
||
LogManager.WriteLog($"[临时标记清理] 已清除起点标记,ID: {_autoPathStartPointRoute.Id}");
|
||
_autoPathStartPointRoute = null;
|
||
}
|
||
|
||
// 清除临时的终点标记(使用正确的路径ID)
|
||
if (_autoPathEndPointRoute != null)
|
||
{
|
||
PathPointRenderPlugin.Instance.RemovePath(_autoPathEndPointRoute.Id);
|
||
LogManager.WriteLog($"[临时标记清理] 已清除终点标记,ID: {_autoPathEndPointRoute.Id}");
|
||
_autoPathEndPointRoute = null;
|
||
}
|
||
|
||
LogManager.WriteLog("[临时标记清理] 自动路径临时标记清理完成");
|
||
}
|
||
else
|
||
{
|
||
LogManager.WriteLog("[临时标记清理] PathPointRenderPlugin实例为null,无法清理标记");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[临时标记清理] 清理临时标记失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region PathPlanningManager事件处理
|
||
|
||
/// <summary>
|
||
/// 订阅PathPlanningManager事件
|
||
/// </summary>
|
||
private void SubscribeToPathPlanningManager()
|
||
{
|
||
if (_pathPlanningManager == null)
|
||
{
|
||
LogManager.Info("PathPlanningManager为null,跳过事件订阅");
|
||
return;
|
||
}
|
||
|
||
try
|
||
{
|
||
LogManager.Info($"*** 开始订阅PathPlanningManager事件,Manager实例: {_pathPlanningManager.GetType().Name} ***");
|
||
|
||
// 订阅路径点操作事件(用于手工路径编辑)
|
||
_pathPlanningManager.PathPointOperation += OnPathPointOperation;
|
||
|
||
// 订阅路径点列表更新事件
|
||
_pathPlanningManager.PathPointsListUpdated += OnPathPointsListUpdated;
|
||
|
||
// 订阅当前路径变更事件
|
||
_pathPlanningManager.CurrentRouteChanged += OnCurrentRouteChanged;
|
||
|
||
// 订阅编辑状态变更事件
|
||
_pathPlanningManager.EditStateChanged += OnEditStateChanged;
|
||
|
||
// 订阅路径生成事件(用于处理自动路径)
|
||
_pathPlanningManager.RouteGenerated += OnRouteGenerated;
|
||
|
||
// 添加错误事件订阅 - 用于处理自动路径规划失败等错误
|
||
_pathPlanningManager.ErrorOccurred += OnPathPlanningError;
|
||
|
||
LogManager.Info("*** PathEditingViewModel已成功订阅PathPlanningManager事件,包括RouteGenerated和ErrorOccurred ***");
|
||
|
||
// 验证事件订阅是否成功
|
||
var routeGeneratedField = _pathPlanningManager.GetType().GetEvent("RouteGenerated");
|
||
if (routeGeneratedField != null)
|
||
{
|
||
LogManager.Info($"*** RouteGenerated事件存在,事件类型: {routeGeneratedField.EventHandlerType?.Name} ***");
|
||
}
|
||
else
|
||
{
|
||
LogManager.Error("*** RouteGenerated事件不存在!!! ***");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"*** 订阅PathPlanningManager事件失败: {ex.Message} ***", ex);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 取消订阅PathPlanningManager事件
|
||
/// </summary>
|
||
private void UnsubscribeFromPathPlanningManager()
|
||
{
|
||
if (_pathPlanningManager == null) return;
|
||
|
||
try
|
||
{
|
||
_pathPlanningManager.PathPointOperation -= OnPathPointOperation;
|
||
_pathPlanningManager.PathPointsListUpdated -= OnPathPointsListUpdated;
|
||
_pathPlanningManager.CurrentRouteChanged -= OnCurrentRouteChanged;
|
||
_pathPlanningManager.EditStateChanged -= OnEditStateChanged;
|
||
_pathPlanningManager.RouteGenerated -= OnRouteGenerated;
|
||
_pathPlanningManager.ErrorOccurred -= OnPathPlanningError;
|
||
|
||
LogManager.Info("PathEditingViewModel已取消订阅PathPlanningManager事件,包括ErrorOccurred");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"取消订阅PathPlanningManager事件失败: {ex.Message}", ex);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 处理路径点操作事件(手工路径编辑的核心)
|
||
/// </summary>
|
||
private async void OnPathPointOperation(object sender, PathPointOperationEventArgs e)
|
||
{
|
||
if (e == null || e.PathPoint == null) return;
|
||
|
||
try
|
||
{
|
||
await SafeExecuteAsync(() =>
|
||
{
|
||
LogManager.Info($"PathEditingViewModel收到路径点操作事件: {e.OperationType}, 点: {e.PathPoint.Name}");
|
||
|
||
// 只处理手动编辑的路径点,跳过自动路径点
|
||
// 自动路径通过RouteGenerated事件处理
|
||
if (e.PathPoint.Name.Contains("自动"))
|
||
{
|
||
LogManager.Info($"跳过自动路径点,将通过RouteGenerated事件处理: {e.PathPoint.Name}");
|
||
return;
|
||
}
|
||
|
||
// 注意:不在这里直接添加/移除单个路径点到UI
|
||
// 因为PathPointsListUpdated事件会处理完整的路径同步
|
||
// 这里只做状态更新和选择管理
|
||
|
||
if (e.OperationType == PathPointOperationType.Added)
|
||
{
|
||
// 确保对应的路径在UI中存在并选中
|
||
var pathViewModel = PathRoutes.FirstOrDefault(p => p.Name == e.Route.Name);
|
||
if (pathViewModel != null)
|
||
{
|
||
// 设置为选中路径
|
||
SelectedPathRoute = pathViewModel;
|
||
}
|
||
|
||
AutoPathStatus = $"已添加路径点: {e.PathPoint.Name} 到 {e.Route.Name}";
|
||
LogManager.Info($"路径点操作完成: {e.PathPoint.Name} 已添加到路径 {e.Route.Name}");
|
||
}
|
||
else if (e.OperationType == PathPointOperationType.Removed)
|
||
{
|
||
AutoPathStatus = $"已移除路径点: {e.PathPoint.Name} 从 {e.Route.Name}";
|
||
LogManager.Info($"路径点操作完成: {e.PathPoint.Name} 已从路径 {e.Route.Name} 移除");
|
||
}
|
||
}, "处理路径点操作事件");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"处理路径点操作事件失败: {ex.Message}", ex);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 添加路径点到UI
|
||
/// </summary>
|
||
// 此方法已删除 - UI同步现在通过OnPathPointsListUpdated事件处理
|
||
|
||
/// <summary>
|
||
/// 从UI移除路径点
|
||
/// </summary>
|
||
// 此方法已删除 - UI同步现在通过OnPathPointsListUpdated事件处理
|
||
|
||
/// <summary>
|
||
/// 处理路径点列表更新事件
|
||
/// </summary>
|
||
private async void OnPathPointsListUpdated(object sender, PathPointsListUpdatedEventArgs e)
|
||
{
|
||
if (e?.Route == null) return;
|
||
|
||
try
|
||
{
|
||
await SafeExecuteAsync(() =>
|
||
{
|
||
LogManager.Info($"路径点列表更新: {e.Route.Name}, 原因: {e.UpdateReason}");
|
||
|
||
// 刷新对应路径的显示
|
||
var pathViewModel = PathRoutes.FirstOrDefault(p => p.Name == e.Route.Name);
|
||
if (pathViewModel != null)
|
||
{
|
||
// 检查是否需要更新:数量变化或名称/类型/坐标变化
|
||
bool needsUpdate = pathViewModel.Points.Count != e.Route.Points.Count;
|
||
|
||
if (!needsUpdate && pathViewModel.Points.Count == e.Route.Points.Count)
|
||
{
|
||
// 数量相同时,检查名称、类型和坐标是否有变化
|
||
for (int i = 0; i < pathViewModel.Points.Count; i++)
|
||
{
|
||
var uiPoint = pathViewModel.Points[i];
|
||
var corePoint = e.Route.Points[i];
|
||
|
||
// 检查名称和类型变化
|
||
if (uiPoint.Name != corePoint.Name || uiPoint.Type != corePoint.Type)
|
||
{
|
||
needsUpdate = true;
|
||
LogManager.Info($"检测到路径点属性变化: {uiPoint.Name}({uiPoint.Type}) -> {corePoint.Name}({corePoint.Type})");
|
||
break;
|
||
}
|
||
|
||
// 检查坐标变化(使用小数位精度比较)
|
||
const double tolerance = 1e-6; // 1微米精度
|
||
if (Math.Abs(uiPoint.X - corePoint.Position.X) > tolerance ||
|
||
Math.Abs(uiPoint.Y - corePoint.Position.Y) > tolerance ||
|
||
Math.Abs(uiPoint.Z - corePoint.Position.Z) > tolerance)
|
||
{
|
||
needsUpdate = true;
|
||
LogManager.Info($"检测到路径点坐标变化: {uiPoint.Name} " +
|
||
$"({uiPoint.X:F3}, {uiPoint.Y:F3}, {uiPoint.Z:F3}) -> " +
|
||
$"({corePoint.Position.X:F3}, {corePoint.Position.Y:F3}, {corePoint.Position.Z:F3})");
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (!needsUpdate)
|
||
{
|
||
LogManager.Info($"路径点数据无变化({pathViewModel.Points.Count}),跳过更新");
|
||
return;
|
||
}
|
||
|
||
// 同步路径点数据
|
||
pathViewModel.Points.Clear();
|
||
foreach (var point in e.Route.Points)
|
||
{
|
||
var pointViewModel = new PathPointViewModel
|
||
{
|
||
Id = point.Id,
|
||
Name = point.Name,
|
||
X = point.Position.X,
|
||
Y = point.Position.Y,
|
||
Z = point.Position.Z,
|
||
Type = point.Type
|
||
};
|
||
pathViewModel.Points.Add(pointViewModel);
|
||
}
|
||
LogManager.Info($"已更新路径点列表,当前点数: {pathViewModel.Points.Count}");
|
||
}
|
||
else
|
||
{
|
||
LogManager.Warning($"未找到对应的PathViewModel: {e.Route.Name}");
|
||
}
|
||
}, "处理路径点列表更新事件");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"处理路径点列表更新事件失败: {ex.Message}", ex);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 处理当前路径变更事件
|
||
/// </summary>
|
||
private async void OnCurrentRouteChanged(object sender, CurrentRouteChangedEventArgs e)
|
||
{
|
||
try
|
||
{
|
||
await SafeExecuteAsync(() =>
|
||
{
|
||
LogManager.Info($"当前路径变更: {e.PreviousRoute?.Name} -> {e.NewRoute?.Name}");
|
||
|
||
if (e.NewRoute != null)
|
||
{
|
||
// 查找并选中对应的路径ViewModel
|
||
var pathViewModel = PathRoutes.FirstOrDefault(p => p.Name == e.NewRoute.Name);
|
||
if (pathViewModel != null)
|
||
{
|
||
// 只在真的不同时才设置,避免重复触发
|
||
if (SelectedPathRoute != pathViewModel)
|
||
{
|
||
SelectedPathRoute = pathViewModel;
|
||
LogManager.Info($"UI已同步选择路径: {e.NewRoute.Name}");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
LogManager.Warning($"OnCurrentRouteChanged: UI中未找到路径 {e.NewRoute.Name},可能UI还未刷新");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 当前路径被设为null,清除UI选择
|
||
if (SelectedPathRoute != null)
|
||
{
|
||
SelectedPathRoute = null;
|
||
LogManager.Info("UI已清除路径选择");
|
||
}
|
||
}
|
||
}, "处理当前路径变更事件");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"处理当前路径变更事件失败: {ex.Message}", ex);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 处理编辑状态变更事件
|
||
/// </summary>
|
||
private async void OnEditStateChanged(object sender, PathEditStateChangedEventArgs e)
|
||
{
|
||
try
|
||
{
|
||
await SafeExecuteAsync(() =>
|
||
{
|
||
LogManager.Info($"路径编辑状态变更: {e.PreviousState} -> {e.NewState}");
|
||
|
||
// 修复:包含 AddingPoints 和 EditingPoint 状态
|
||
IsPathEditMode = (e.NewState == PathEditState.Creating ||
|
||
e.NewState == PathEditState.Editing ||
|
||
e.NewState == PathEditState.AddingPoints ||
|
||
e.NewState == PathEditState.EditingPoint);
|
||
|
||
LogManager.Info($"[UI状态更新] IsPathEditMode 设为: {IsPathEditMode}");
|
||
|
||
// 更新状态提示
|
||
switch (e.NewState)
|
||
{
|
||
case PathEditState.Creating:
|
||
AutoPathStatus = $"正在新建路径: {e.EditingRoute?.Name} - 请在3D视图中点击设置路径点";
|
||
break;
|
||
case PathEditState.Editing:
|
||
AutoPathStatus = $"正在编辑路径: {e.EditingRoute?.Name} - 请在3D视图中点击设置路径点";
|
||
break;
|
||
case PathEditState.AddingPoints:
|
||
AutoPathStatus = $"正在添加路径点到: {e.EditingRoute?.Name} - 请在3D视图中点击设置路径点";
|
||
break;
|
||
case PathEditState.EditingPoint:
|
||
AutoPathStatus = $"正在修改路径点: {e.EditingRoute?.Name} - 请在3D视图中点击新位置,然后点击'结束'确认";
|
||
break;
|
||
case PathEditState.Viewing:
|
||
AutoPathStatus = "路径编辑已完成";
|
||
break;
|
||
default:
|
||
AutoPathStatus = "就绪";
|
||
break;
|
||
}
|
||
|
||
// 通知命令状态更新
|
||
OnPropertyChanged(nameof(CanExecuteStartEdit));
|
||
OnPropertyChanged(nameof(CanExecuteEndEdit));
|
||
OnPropertyChanged(nameof(CanExecuteModifyPoint));
|
||
OnPropertyChanged(nameof(CanExecuteCancelModifyPoint));
|
||
|
||
LogManager.Info($"[UI状态更新] 按钮状态已通知更新 - CanExecuteStartEdit: {CanExecuteStartEdit}, CanExecuteEndEdit: {CanExecuteEndEdit}, CanExecuteModifyPoint: {CanExecuteModifyPoint}, CanExecuteCancelModifyPoint: {CanExecuteCancelModifyPoint}");
|
||
}, "处理编辑状态变更事件");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"处理编辑状态变更事件失败: {ex.Message}", ex);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 处理路径生成事件(用于自动路径)
|
||
/// </summary>
|
||
private async void OnRouteGenerated(object sender, RouteGeneratedEventArgs e)
|
||
{
|
||
// 添加调试日志确认事件是否被调用
|
||
LogManager.Info($"*** OnRouteGenerated被调用!Sender: {sender?.GetType().Name}, Route: {e?.Route?.Name}, Method: {e?.GenerationMethod}, GridSize: {e?.GridSize} ***");
|
||
|
||
if (e?.Route == null)
|
||
{
|
||
LogManager.Warning("*** OnRouteGenerated: RouteGeneratedEventArgs或Route为null,退出处理 ***");
|
||
return;
|
||
}
|
||
|
||
try
|
||
{
|
||
await SafeExecuteAsync(() =>
|
||
{
|
||
LogManager.Info($"PathEditingViewModel收到路径生成事件: {e.Route.Name}, 生成方式: {e.GenerationMethod}");
|
||
|
||
// 处理自动路径生成
|
||
if (e.GenerationMethod == RouteGenerationMethod.AutoPlanning)
|
||
{
|
||
LogManager.Info($"*** 开始处理自动路径生成: {e.Route.Name} ***");
|
||
|
||
// 创建对应的 WPF ViewModel
|
||
var autoPathViewModel = new PathRouteViewModel
|
||
{
|
||
Name = e.Route.Name,
|
||
Description = e.Route.Description,
|
||
IsActive = true
|
||
};
|
||
|
||
// 转换路径点
|
||
foreach (var corePoint in e.Route.Points)
|
||
{
|
||
var wpfPoint = new PathPointViewModel
|
||
{
|
||
Id = corePoint.Id,
|
||
Name = corePoint.Name,
|
||
X = corePoint.X,
|
||
Y = corePoint.Y,
|
||
Z = corePoint.Z,
|
||
Type = corePoint.Type
|
||
};
|
||
autoPathViewModel.Points.Add(wpfPoint);
|
||
}
|
||
|
||
LogManager.Info($"*** 已创建PathRouteViewModel,包含 {autoPathViewModel.Points.Count} 个点 ***");
|
||
|
||
// 检查是否已存在同名路径,避免重复添加
|
||
var existingPath = PathRoutes.FirstOrDefault(p => p.Name == e.Route.Name);
|
||
if (existingPath == null)
|
||
{
|
||
// 添加到 UI 列表并选中
|
||
PathRoutes.Add(autoPathViewModel);
|
||
SelectedPathRoute = autoPathViewModel;
|
||
|
||
LogManager.Info($"*** 自动路径已添加到UI列表: {e.Route.Name},当前PathRoutes.Count = {PathRoutes.Count} ***");
|
||
}
|
||
else
|
||
{
|
||
// 更新现有路径
|
||
existingPath.Points.Clear();
|
||
foreach (var wpfPoint in autoPathViewModel.Points)
|
||
{
|
||
existingPath.Points.Add(wpfPoint);
|
||
}
|
||
SelectedPathRoute = existingPath;
|
||
|
||
LogManager.Info($"*** 自动路径已更新UI列表: {e.Route.Name} ***");
|
||
}
|
||
|
||
// 构建包含网格信息的完整状态消息,使用事件参数中的网格大小信息
|
||
var gridInfo = e.GridSize > 0 ? $"网格{e.GridSize:F1}m" : "自动网格";
|
||
AutoPathStatus = $"✅ 自动路径规划完成: {e.Route.Name},共 {e.Route.Points.Count} 个路径点,长度 {e.Route.TotalLength:F2}米({gridInfo})";
|
||
LogManager.Info($"*** AutoPathStatus已更新: {AutoPathStatus} ***");
|
||
}
|
||
else if (e.GenerationMethod == RouteGenerationMethod.Manual)
|
||
{
|
||
// 手工路径生成(如果需要特殊处理)
|
||
LogManager.Info($"手工路径生成完成: {e.Route.Name}");
|
||
}
|
||
}, "处理路径生成事件");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"*** OnRouteGenerated异常: {ex.Message} ***", ex);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 处理路径规划错误事件
|
||
/// </summary>
|
||
/// <summary>
|
||
/// 处理路径规划错误事件
|
||
/// </summary>
|
||
/// <summary>
|
||
/// 处理路径规划错误事件
|
||
/// </summary>
|
||
private async void OnPathPlanningError(object sender, PathPlanningErrorOccurredEventArgs e)
|
||
{
|
||
LogManager.Info($"*** OnPathPlanningError被调用!Sender: {sender?.GetType().Name}, Error: {e?.ErrorMessage} ***");
|
||
|
||
try
|
||
{
|
||
await SafeExecuteAsync(async () =>
|
||
{
|
||
LogManager.Error($"自动路径规划失败: {e.ErrorMessage}");
|
||
|
||
// 使用UIStateManager确保UI更新在主线程执行
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
// 更新自动路径状态显示错误信息
|
||
AutoPathStatus = $"❌ 自动路径规划失败: {e.ErrorMessage}";
|
||
LogManager.Info($"*** AutoPathStatus已更新为: {AutoPathStatus} ***");
|
||
});
|
||
|
||
}, "处理路径规划错误事件");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"*** OnPathPlanningError异常: {ex.Message} ***", ex);
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 清理资源
|
||
|
||
/// <summary>
|
||
/// 强制重新初始化ToolPlugin以确保获得鼠标焦点
|
||
/// </summary>
|
||
/// <param name="subscribeToEvents">是否订阅鼠标事件,新建路径需要true,自动路径选点需要false</param>
|
||
/// <returns>初始化是否成功</returns>
|
||
private bool ForceReinitializeToolPlugin(bool subscribeToEvents = false)
|
||
{
|
||
LogManager.Info($"开始强制重新初始化ToolPlugin以确保获得鼠标焦点,订阅事件: {subscribeToEvents}");
|
||
try
|
||
{
|
||
// 使用反射调用私有方法强制重新激活ToolPlugin
|
||
var deactivateMethod = _pathPlanningManager.GetType().GetMethod("DeactivateToolPlugin",
|
||
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||
if (deactivateMethod != null)
|
||
{
|
||
deactivateMethod.Invoke(_pathPlanningManager, null);
|
||
LogManager.Info("已停用ToolPlugin");
|
||
}
|
||
|
||
// 重置激活状态标志
|
||
var isActiveField = _pathPlanningManager.GetType().GetField("_isToolPluginActive",
|
||
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||
if (isActiveField != null)
|
||
{
|
||
isActiveField.SetValue(_pathPlanningManager, false);
|
||
LogManager.Info("已重置ToolPlugin激活状态标志");
|
||
}
|
||
|
||
// 重新激活ToolPlugin,根据参数决定是否订阅事件
|
||
var activateMethod = _pathPlanningManager.GetType().GetMethod("ActivateToolPlugin",
|
||
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||
if (activateMethod != null)
|
||
{
|
||
// 调用带参数的方法,传递subscribeToEvents参数
|
||
var result = activateMethod.Invoke(_pathPlanningManager, new object[] { subscribeToEvents });
|
||
LogManager.Info($"ToolPlugin重新激活结果(事件订阅: {subscribeToEvents}): {result}");
|
||
if (!(bool)result)
|
||
{
|
||
LogManager.Error("ToolPlugin激活失败");
|
||
return false;
|
||
}
|
||
}
|
||
|
||
LogManager.Info("ToolPlugin重新初始化成功,已获得鼠标焦点");
|
||
return true;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"重新初始化ToolPlugin失败: {ex.Message}");
|
||
return false;
|
||
}
|
||
}
|
||
|
||
public void Cleanup()
|
||
{
|
||
try
|
||
{
|
||
LogManager.Info("开始清理PathEditingViewModel资源");
|
||
|
||
// 取消事件订阅
|
||
UnsubscribeFromPathPlanningManager();
|
||
|
||
// 完整清理自动路径规划相关的事件订阅
|
||
try
|
||
{
|
||
CleanupAutoPathEventSubscriptions();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Warning($"清理自动路径事件订阅时发生异常: {ex.Message}");
|
||
}
|
||
|
||
// 确保停止任何活动的点击工具
|
||
try
|
||
{
|
||
if (_pathPlanningManager != null)
|
||
{
|
||
_pathPlanningManager.StopClickTool();
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Warning($"停止点击工具时发生异常: {ex.Message}");
|
||
}
|
||
|
||
LogManager.Info("PathEditingViewModel资源清理完成");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"PathEditingViewModel清理失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
}
|
||
|
||
/// <summary>
|
||
/// 车辆参数类 - 用于传递车辆尺寸信息给路径规划算法
|
||
/// </summary>
|
||
public class VehicleParameters
|
||
{
|
||
public double Length { get; set; }
|
||
public double Width { get; set; }
|
||
public double Height { get; set; }
|
||
public double SafetyMargin { get; set; }
|
||
}
|
||
} |