3080 lines
130 KiB
C#
3080 lines
130 KiB
C#
using System;
|
||
using System.Collections.ObjectModel;
|
||
using System.Drawing;
|
||
using System.Drawing.Imaging;
|
||
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.Core.Config;
|
||
using NavisworksTransport.Commands;
|
||
using NavisworksTransport.UI.WPF.Views;
|
||
|
||
namespace NavisworksTransport.UI.WPF.ViewModels
|
||
{
|
||
/// <summary>
|
||
/// 路径策略选项辅助类
|
||
/// </summary>
|
||
public class PathStrategyOption
|
||
{
|
||
/// <summary>
|
||
/// 策略值
|
||
/// </summary>
|
||
public PathStrategy Value { get; set; }
|
||
|
||
/// <summary>
|
||
/// 显示名称
|
||
/// </summary>
|
||
public string DisplayName { get; set; }
|
||
|
||
/// <summary>
|
||
/// 详细描述
|
||
/// </summary>
|
||
public string Description { get; set; }
|
||
}
|
||
|
||
/// <summary>
|
||
/// 路径编辑页面专用ViewModel
|
||
/// </summary>
|
||
public class PathEditingViewModel : ViewModelBase, IDisposable
|
||
{
|
||
#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 Point3D _startPoint3D;
|
||
private Point3D _endPoint3D;
|
||
private bool _hasStartPoint = false;
|
||
private bool _hasEndPoint = false;
|
||
private bool _isSelectingStartPoint = false;
|
||
private bool _isSelectingEndPoint = false;
|
||
|
||
// 车辆参数 - 改为三个独立参数(从配置初始化)
|
||
private double _vehicleLength; // 车辆长度(米)
|
||
private double _vehicleWidth; // 车辆宽度(米)
|
||
private double _vehicleHeight; // 车辆高度(米)
|
||
private double _safetyMargin; // 安全间隙(米)
|
||
|
||
// 网格大小参数(从配置初始化)
|
||
private bool _isGridSizeManuallyEnabled = false; // 是否启用手动网格大小设置
|
||
private double _gridSize; // 网格大小(米)
|
||
|
||
// 可视化参数
|
||
private bool _showWalkableGrid = false;
|
||
private bool _showObstacleGrid = false;
|
||
private bool _showUnknownGrid = false;
|
||
private bool _showDoorGrid = false;
|
||
private GridPointType _gridPointType = GridPointType.Rectangle;
|
||
private bool _isStandardLineMode = true;
|
||
private bool _isVehicleSpaceMode = false;
|
||
|
||
// 路径策略参数
|
||
private PathStrategyOption _selectedPathStrategy;
|
||
private ObservableCollection<PathStrategyOption> _pathStrategyOptions;
|
||
|
||
// 自动路径起点和终点的路径对象引用(用于正确的ID管理)
|
||
private PathRoute _autoPathStartPointRoute = null;
|
||
private PathRoute _autoPathEndPointRoute = null;
|
||
|
||
|
||
// 资源清理管理
|
||
private bool _disposed = false;
|
||
|
||
#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));
|
||
|
||
// 路径点选中可视化:将选中的路径点显示为预览样式
|
||
try
|
||
{
|
||
if (value != null)
|
||
{
|
||
// 从PathPointViewModel创建PathPoint对象
|
||
var pathPoint = new PathPoint(
|
||
new Point3D(value.X, value.Y, value.Z),
|
||
value.Name,
|
||
value.Type
|
||
)
|
||
{
|
||
Id = value.Id
|
||
};
|
||
|
||
// 将选中的路径点设为预览样式
|
||
PathPointRenderPlugin.Instance.RenderPreviewPoint(pathPoint);
|
||
LogManager.Info($"路径点已设为预览样式: {value.Name}");
|
||
}
|
||
else
|
||
{
|
||
// 清除预览样式
|
||
PathPointRenderPlugin.Instance.ClearPreviewPoint();
|
||
LogManager.Info("已清除路径点预览样式");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"路径点选中可视化失败: {ex.Message}");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
/// <summary>
|
||
/// 更新路径可视化显示:清理其他路径,只显示当前选中的路径
|
||
/// </summary>
|
||
private async void UpdatePathVisualization()
|
||
{
|
||
await SafeExecuteAsync(() =>
|
||
{
|
||
try
|
||
{
|
||
if (PathPointRenderPlugin.Instance == null)
|
||
{
|
||
LogManager.Warning("PathPointRenderPlugin实例为空,无法更新路径可视化");
|
||
return;
|
||
}
|
||
|
||
// 0. 先同步车辆参数到渲染插件
|
||
SyncVehicleParametersToRenderPlugin();
|
||
|
||
// 1. 清理现有的路径可视化,但保留网格可视化
|
||
PathPointRenderPlugin.Instance.ClearPathsExcept("grid_visualization_all", "grid_visualization_channel", "grid_visualization_unknown", "grid_visualization_obstacle", "grid_visualization_door");
|
||
|
||
// 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.Debug("[路径可视化] 没有选中的路径,仅清理显示");
|
||
}
|
||
|
||
// 3. 恢复网格可视化(如果网格可视化已启用且当前路径有关联的GridMap)
|
||
if (_pathPlanningManager?.IsAnyGridVisualizationEnabled == true)
|
||
{
|
||
// 检查当前路径是否有关联的GridMap
|
||
var currentRoute = _pathPlanningManager.CurrentRoute;
|
||
if (currentRoute?.AssociatedGridMap != null)
|
||
{
|
||
// 通过PathPlanningManager的RefreshGridVisualization方法恢复网格
|
||
// 这会使用当前路径的AssociatedGridMap来重新渲染网格
|
||
var refreshMethod = typeof(PathPlanningManager).GetMethod("RefreshGridVisualization",
|
||
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||
if (refreshMethod != null)
|
||
{
|
||
refreshMethod.Invoke(_pathPlanningManager, null);
|
||
}
|
||
else
|
||
{
|
||
LogManager.Warning("[路径可视化] 无法找到RefreshGridVisualization方法");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
LogManager.Debug("[路径可视化] 当前路径没有关联的GridMap,跳过网格恢复");
|
||
}
|
||
}
|
||
}
|
||
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 double VehicleLength
|
||
{
|
||
get => _vehicleLength;
|
||
set
|
||
{
|
||
if (SetProperty(ref _vehicleLength, value))
|
||
{
|
||
SyncVehicleParametersToRenderPlugin();
|
||
OnPropertyChanged(nameof(CanExecuteAutoPlanPath));
|
||
}
|
||
}
|
||
}
|
||
|
||
public double VehicleWidth
|
||
{
|
||
get => _vehicleWidth;
|
||
set
|
||
{
|
||
if (SetProperty(ref _vehicleWidth, value))
|
||
{
|
||
SyncVehicleParametersToRenderPlugin();
|
||
OnPropertyChanged(nameof(CanExecuteAutoPlanPath));
|
||
}
|
||
}
|
||
}
|
||
|
||
public double VehicleHeight
|
||
{
|
||
get => _vehicleHeight;
|
||
set
|
||
{
|
||
if (SetProperty(ref _vehicleHeight, value))
|
||
{
|
||
SyncVehicleParametersToRenderPlugin();
|
||
OnPropertyChanged(nameof(CanExecuteAutoPlanPath));
|
||
}
|
||
}
|
||
}
|
||
|
||
public double SafetyMargin
|
||
{
|
||
get => _safetyMargin;
|
||
set
|
||
{
|
||
// 限制精度到2位小数
|
||
var roundedValue = Math.Round(value, 2);
|
||
if (SetProperty(ref _safetyMargin, roundedValue))
|
||
{
|
||
SyncVehicleParametersToRenderPlugin();
|
||
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));
|
||
}
|
||
}
|
||
}
|
||
|
||
#region 可视化参数属性
|
||
|
||
/// <summary>
|
||
/// 是否显示可通行网格点
|
||
/// </summary>
|
||
public bool ShowWalkableGrid
|
||
{
|
||
get => _showWalkableGrid;
|
||
set
|
||
{
|
||
if (SetProperty(ref _showWalkableGrid, value))
|
||
{
|
||
OnGridVisualizationChanged();
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 是否显示障碍物网格点
|
||
/// </summary>
|
||
public bool ShowObstacleGrid
|
||
{
|
||
get => _showObstacleGrid;
|
||
set
|
||
{
|
||
if (SetProperty(ref _showObstacleGrid, value))
|
||
{
|
||
OnGridVisualizationChanged();
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 是否显示未知区域网格点
|
||
/// </summary>
|
||
public bool ShowUnknownGrid
|
||
{
|
||
get => _showUnknownGrid;
|
||
set
|
||
{
|
||
if (SetProperty(ref _showUnknownGrid, value))
|
||
{
|
||
OnGridVisualizationChanged();
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 是否显示门网格点
|
||
/// </summary>
|
||
public bool ShowDoorGrid
|
||
{
|
||
get => _showDoorGrid;
|
||
set
|
||
{
|
||
if (SetProperty(ref _showDoorGrid, value))
|
||
{
|
||
OnGridVisualizationChanged();
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 网格点类型
|
||
/// </summary>
|
||
public GridPointType GridPointType
|
||
{
|
||
get => _gridPointType;
|
||
set
|
||
{
|
||
if (SetProperty(ref _gridPointType, value))
|
||
{
|
||
OnPropertyChanged(nameof(IsRectanglePointType));
|
||
OnPropertyChanged(nameof(IsCirclePointType));
|
||
OnGridPointTypeChanged();
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 是否使用立方体点类型
|
||
/// </summary>
|
||
public bool IsRectanglePointType
|
||
{
|
||
get => _gridPointType == GridPointType.Rectangle;
|
||
set
|
||
{
|
||
if (value)
|
||
{
|
||
GridPointType = GridPointType.Rectangle;
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 是否使用圆形点类型
|
||
/// </summary>
|
||
public bool IsCirclePointType
|
||
{
|
||
get => _gridPointType == GridPointType.Circle;
|
||
set
|
||
{
|
||
if (value)
|
||
{
|
||
GridPointType = GridPointType.Circle;
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 是否使用标准连线模式
|
||
/// </summary>
|
||
public bool IsStandardLineMode
|
||
{
|
||
get => _isStandardLineMode;
|
||
set
|
||
{
|
||
if (SetProperty(ref _isStandardLineMode, value))
|
||
{
|
||
if (value)
|
||
{
|
||
IsVehicleSpaceMode = false;
|
||
OnPathVisualizationModeChanged();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 是否使用车辆通行空间模式
|
||
/// </summary>
|
||
public bool IsVehicleSpaceMode
|
||
{
|
||
get => _isVehicleSpaceMode;
|
||
set
|
||
{
|
||
if (SetProperty(ref _isVehicleSpaceMode, value))
|
||
{
|
||
if (value)
|
||
{
|
||
IsStandardLineMode = false;
|
||
OnPathVisualizationModeChanged();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
/// <summary>
|
||
/// 路径策略选项集合
|
||
/// </summary>
|
||
public ObservableCollection<PathStrategyOption> PathStrategyOptions
|
||
{
|
||
get => _pathStrategyOptions;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 当前选择的路径策略
|
||
/// </summary>
|
||
public PathStrategyOption SelectedPathStrategy
|
||
{
|
||
get => _selectedPathStrategy;
|
||
set
|
||
{
|
||
if (SetProperty(ref _selectedPathStrategy, value))
|
||
{
|
||
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 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 EditPointCoordinatesCommand { 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 CanExecuteNewPath => !IsSelectingStartPoint && !IsSelectingEndPoint;
|
||
|
||
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 || SelectedPathRoute != null;
|
||
|
||
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 构造函数
|
||
|
||
/// <summary>
|
||
/// 构造函数,需要传入主ViewModel以支持统一状态栏
|
||
/// </summary>
|
||
/// <param name="mainViewModel">主ViewModel,用于统一状态栏</param>
|
||
public PathEditingViewModel(LogisticsControlViewModel mainViewModel) : base()
|
||
{
|
||
try
|
||
{
|
||
// 设置主ViewModel引用到基类
|
||
SetMainViewModel(mainViewModel);
|
||
|
||
_uiStateManager = UIStateManager.Instance;
|
||
_pathDataManager = new PathDataManager();
|
||
// 不在构造函数中创建PathPlanningManager,由外部设置
|
||
_pathPlanningManager = null;
|
||
|
||
if (_uiStateManager == null)
|
||
{
|
||
throw new InvalidOperationException("UIStateManager初始化失败");
|
||
}
|
||
|
||
// PathPlanningManager将在外部设置,这里不需要检查
|
||
|
||
// 初始化集合
|
||
PathRoutes = new ObservableCollection<PathRouteViewModel>();
|
||
|
||
// 初始化路径策略选项
|
||
InitializePathStrategyOptions();
|
||
|
||
// 从配置加载参数
|
||
LoadParametersFromConfig();
|
||
|
||
// 初始化命令
|
||
InitializeCommands();
|
||
|
||
// 初始化车辆参数同步(在PathPointRenderPlugin实例不为空时才同步)
|
||
// 如果PathPointRenderPlugin尚未初始化,将在属性变更时自动同步
|
||
SyncVehicleParametersToRenderPlugin();
|
||
|
||
// 注意:不在这里订阅PathPlanningManager事件,
|
||
// 因为PathPlanningManager还没有设置,事件订阅在PathPlanningManager属性setter中处理
|
||
|
||
LogManager.Info("PathEditingViewModel构造函数执行完成 - 支持统一状态栏");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"PathEditingViewModel构造函数异常: {ex.Message}", ex);
|
||
UpdateMainStatus("初始化失败,请检查日志");
|
||
throw;
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 初始化方法
|
||
|
||
/// <summary>
|
||
/// 初始化路径策略选项
|
||
/// </summary>
|
||
private void InitializePathStrategyOptions()
|
||
{
|
||
_pathStrategyOptions = new ObservableCollection<PathStrategyOption>
|
||
{
|
||
new PathStrategyOption
|
||
{
|
||
Value = PathStrategy.Shortest,
|
||
DisplayName = "最短路径",
|
||
Description = "标准A*算法,选择路径总长度最短的路径"
|
||
},
|
||
new PathStrategyOption
|
||
{
|
||
Value = PathStrategy.Straightest,
|
||
DisplayName = "直线优先",
|
||
Description = "优先选择主方向直线路径,减少转弯次数"
|
||
},
|
||
new PathStrategyOption
|
||
{
|
||
Value = PathStrategy.SafestCenter,
|
||
DisplayName = "安全优先",
|
||
Description = "基于障碍物距离选择安全路径,适合大型车辆居中行驶"
|
||
}
|
||
};
|
||
|
||
// 默认选择最短路径策略(向后兼容)
|
||
_selectedPathStrategy = _pathStrategyOptions.FirstOrDefault(x => x.Value == PathStrategy.Shortest);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 从配置文件加载参数
|
||
/// </summary>
|
||
private void LoadParametersFromConfig()
|
||
{
|
||
try
|
||
{
|
||
var config = ConfigManager.Instance.Current;
|
||
|
||
// 从 PathEditing 配置加载所有参数
|
||
_gridSize = config.PathEditing.CellSizeMeters;
|
||
_vehicleLength = config.PathEditing.VehicleLengthMeters;
|
||
_vehicleWidth = config.PathEditing.VehicleWidthMeters;
|
||
_vehicleHeight = config.PathEditing.VehicleHeightMeters;
|
||
_safetyMargin = config.PathEditing.SafetyMarginMeters;
|
||
|
||
LogManager.Info($"从配置加载参数 - 车辆: {_vehicleLength:F1}x{_vehicleWidth:F1}x{_vehicleHeight:F1}米, 安全间隙: {_safetyMargin:F2}米, 网格: {_gridSize:F1}米");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"加载配置参数失败: {ex.Message}", ex);
|
||
// 使用默认值,不抛出异常
|
||
}
|
||
}
|
||
|
||
private void InitializeCommands()
|
||
{
|
||
NewPathCommand = new RelayCommand(async () => await ExecuteNewPathAsync(), () => CanExecuteNewPath);
|
||
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());
|
||
EditPointCoordinatesCommand = new RelayCommand<PathPointViewModel>(ExecuteEditPointCoordinates);
|
||
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(() =>
|
||
{
|
||
UpdateMainStatus("正在创建新路径...");
|
||
|
||
// 清除所有现有路径的可视化显示
|
||
if (PathPointRenderPlugin.Instance != null)
|
||
{
|
||
try
|
||
{
|
||
PathPointRenderPlugin.Instance.ClearPathsExcept("grid_visualization_all", "grid_visualization_channel", "grid_visualization_unknown", "grid_visualization_obstacle", "grid_visualization_door");
|
||
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
|
||
{
|
||
Id = newRoute.Id, // 添加Id属性设置
|
||
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))
|
||
{
|
||
UpdateMainStatus("ToolPlugin初始化失败,请重试");
|
||
LogManager.Error("新建路径:ToolPlugin初始化失败");
|
||
return;
|
||
}
|
||
|
||
UpdateMainStatus($"已进入新建路径模式: {newRoute.Name} - 请在3D视图中点击设置路径点");
|
||
LogManager.Info($"开始新建路径: {newRoute.Name},已强制重新初始化ToolPlugin获取鼠标焦点");
|
||
|
||
// 不再显示对话框,状态文本已提供足够的反馈
|
||
}
|
||
else
|
||
{
|
||
UpdateMainStatus("创建新路径失败:没有可通行的物流模型");
|
||
LogManager.Error("创建新路径失败:没有可通行的物流模型");
|
||
MessageBox.Show("创建新路径失败:没有找到任何可通行的物流模型。\n请先为模型设置可通行的物流属性,然后再尝试创建路径。", "错误",
|
||
MessageBoxButton.OK, MessageBoxImage.Warning);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
UpdateMainStatus("路径规划管理器未初始化");
|
||
LogManager.Error("路径规划管理器未初始化");
|
||
}
|
||
}, "新建路径");
|
||
}
|
||
|
||
private async Task ExecuteDeletePathAsync()
|
||
{
|
||
if (SelectedPathRoute == null) return;
|
||
|
||
await SafeExecuteAsync(() =>
|
||
{
|
||
var pathName = SelectedPathRoute.Name;
|
||
UpdateMainStatus($"正在删除路径: {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的DeleteRoute方法,确保数据库也被删除
|
||
bool deleteSuccess = _pathPlanningManager.DeleteRoute(coreRoute);
|
||
if (deleteSuccess)
|
||
{
|
||
LogManager.Info($"删除路径:已从PathPlanningManager和数据库删除路径: {pathName}");
|
||
}
|
||
else
|
||
{
|
||
LogManager.Warning($"删除路径:PathPlanningManager删除失败: {pathName}");
|
||
}
|
||
}
|
||
}
|
||
|
||
// 更新UI
|
||
PathRoutes.Remove(SelectedPathRoute);
|
||
SelectedPathRoute = null;
|
||
UpdateMainStatus($"已完全删除路径: {pathName}");
|
||
}, "删除路径");
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 坐标编辑命令
|
||
|
||
private void ExecuteEditPointCoordinates(PathPointViewModel point)
|
||
{
|
||
if (point == null) return;
|
||
|
||
try
|
||
{
|
||
// 创建并显示对话框
|
||
// 注意:在Navisworks插件的ViewModel中,不设置Owner
|
||
// 因为Application.Current.MainWindow在Navisworks环境中不可用
|
||
var dialog = new EditCoordinatesWindow(point.X, point.Y, point.Z);
|
||
|
||
|
||
if (dialog.ShowDialog() == true)
|
||
{
|
||
// 更新ViewModel中的坐标
|
||
point.X = dialog.X;
|
||
point.Y = dialog.Y;
|
||
point.Z = dialog.Z;
|
||
|
||
// 同步更新到底层数据模型
|
||
if (_pathPlanningManager != null)
|
||
{
|
||
var coreRoute = _pathPlanningManager.Routes.FirstOrDefault(r => r.Id == SelectedPathRoute.Id);
|
||
if (coreRoute != null)
|
||
{
|
||
var corePoint = coreRoute.Points.FirstOrDefault(p => p.Id == point.Id);
|
||
if (corePoint != null)
|
||
{
|
||
corePoint.X = point.X;
|
||
corePoint.Y = point.Y;
|
||
corePoint.Z = point.Z;
|
||
|
||
// 保存更改到数据库
|
||
_pathPlanningManager.UpdateRoute(coreRoute);
|
||
|
||
// 刷新视图
|
||
UpdatePathVisualization();
|
||
|
||
LogManager.Info($"已更新路径点 {point.Name} 的坐标为 ({point.X:F3}, {point.Y:F3}, {point.Z:F3})");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"编辑坐标对话框操作失败: {ex.Message}", ex);
|
||
System.Windows.MessageBox.Show($"无法打开编辑对话框: {ex.Message}", "错误", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 自动路径规划命令
|
||
|
||
private async Task ExecuteSelectStartPointAsync()
|
||
{
|
||
await SafeExecuteAsync(() =>
|
||
{
|
||
LogManager.Info("=== 开始执行选择起点命令 ===");
|
||
|
||
if (_pathPlanningManager == null)
|
||
{
|
||
LogManager.Error("PathPlanningManager为null,无法选择起点");
|
||
UpdateMainStatus("路径规划管理器未初始化");
|
||
return;
|
||
}
|
||
|
||
// 立即清理现有的起点标记(使用正确的路径ID)
|
||
if (PathPointRenderPlugin.Instance != null && _autoPathStartPointRoute != null)
|
||
{
|
||
PathPointRenderPlugin.Instance.RemovePath(_autoPathStartPointRoute.Id);
|
||
_autoPathStartPointRoute = null; // 清除引用
|
||
}
|
||
|
||
// 重置状态
|
||
_hasStartPoint = false;
|
||
AutoPathStartPoint = "未选择";
|
||
|
||
LogManager.Info("PathPlanningManager已初始化,开始设置自动路径规划状态");
|
||
IsSelectingStartPoint = true;
|
||
IsSelectingEndPoint = false;
|
||
|
||
// 通知按钮状态更新,禁用"手动创建"按钮
|
||
OnPropertyChanged(nameof(CanExecuteNewPath));
|
||
|
||
UpdateMainStatus("请在3D视图中点击选择起点...");
|
||
|
||
// 进入自动路径规划模式 - 动态事件订阅
|
||
LogManager.Info("进入自动路径规划模式,启用动态事件订阅");
|
||
|
||
// 1. 禁用PathPlanningManager的鼠标处理
|
||
_pathPlanningManager.DisableMouseHandling();
|
||
|
||
// 2. 启用PathEditingViewModel的事件订阅
|
||
PathClickToolPlugin.MouseClicked -= OnAutoPathMouseClicked; // 先取消避免重复
|
||
PathClickToolPlugin.MouseClicked += OnAutoPathMouseClicked;
|
||
LogManager.Info("已启用PathEditingViewModel的鼠标事件订阅");
|
||
|
||
// 强制重新初始化ToolPlugin以确保获得鼠标焦点,自动路径不需要PathPlanningManager订阅事件
|
||
if (!ForceReinitializeToolPlugin(subscribeToEvents: false))
|
||
{
|
||
UpdateMainStatus("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,无法选择终点");
|
||
UpdateMainStatus("路径规划管理器未初始化");
|
||
return;
|
||
}
|
||
|
||
// 立即清理现有的终点标记(使用正确的路径ID)
|
||
if (PathPointRenderPlugin.Instance != null && _autoPathEndPointRoute != null)
|
||
{
|
||
PathPointRenderPlugin.Instance.RemovePath(_autoPathEndPointRoute.Id);
|
||
LogManager.Debug($"[选择终点] 已清除之前的终点标记,ID: {_autoPathEndPointRoute.Id}");
|
||
_autoPathEndPointRoute = null; // 清除引用
|
||
}
|
||
|
||
// 重置状态
|
||
_hasEndPoint = false;
|
||
AutoPathEndPoint = "未选择";
|
||
|
||
LogManager.Info("PathPlanningManager已初始化,开始设置自动路径规划状态");
|
||
IsSelectingStartPoint = false;
|
||
IsSelectingEndPoint = true;
|
||
|
||
// 通知按钮状态更新,禁用"手动创建"按钮
|
||
OnPropertyChanged(nameof(CanExecuteNewPath));
|
||
|
||
UpdateMainStatus("请在3D视图中点击选择终点...");
|
||
|
||
// 进入自动路径规划模式 - 动态事件订阅
|
||
LogManager.Info("进入自动路径规划模式,启用动态事件订阅");
|
||
|
||
// 1. 禁用PathPlanningManager的鼠标处理
|
||
_pathPlanningManager.DisableMouseHandling();
|
||
|
||
// 2. 启用PathEditingViewModel的事件订阅
|
||
PathClickToolPlugin.MouseClicked -= OnAutoPathMouseClicked; // 先取消避免重复
|
||
PathClickToolPlugin.MouseClicked += OnAutoPathMouseClicked;
|
||
LogManager.Info("已启用PathEditingViewModel的鼠标事件订阅");
|
||
|
||
// 强制重新初始化ToolPlugin以确保获得鼠标焦点,自动路径不需要PathPlanningManager订阅事件
|
||
if (!ForceReinitializeToolPlugin(subscribeToEvents: false))
|
||
{
|
||
UpdateMainStatus("ToolPlugin初始化失败,请重试");
|
||
return;
|
||
}
|
||
|
||
// 在STA线程中调用Navisworks API
|
||
_pathPlanningManager.StartClickTool(PathPointType.EndPoint);
|
||
LogManager.Info("=== 选择终点命令设置完成,已启用动态事件订阅,等待用户点击 ===");
|
||
}, "选择终点");
|
||
}
|
||
|
||
private async Task ExecuteAutoPlanPathAsync()
|
||
{
|
||
await SafeExecuteAsync(async () =>
|
||
{
|
||
if (!_hasStartPoint || !_hasEndPoint)
|
||
{
|
||
UpdateMainStatus("请先选择起点和终点");
|
||
return;
|
||
}
|
||
|
||
UpdateMainStatus("正在计算最优路径...", -1, true);
|
||
LogManager.Info("=== 开始执行自动路径规划 ===");
|
||
|
||
// 确保在开始自动路径规划前清理任何残留的事件订阅
|
||
CleanupAutoPathEventSubscriptions();
|
||
if (_pathPlanningManager != null)
|
||
{
|
||
_pathPlanningManager.StopClickTool();
|
||
}
|
||
|
||
// 清理所有临时路径,确保干净的起始状态
|
||
ClearTemporaryAutoPathMarkers();
|
||
if (PathPointRenderPlugin.Instance != null)
|
||
{
|
||
// 检查是否有网格可视化启用,只在启用时保留网格可视化路径
|
||
if (_pathPlanningManager?.IsAnyGridVisualizationEnabled == true)
|
||
{
|
||
PathPointRenderPlugin.Instance.ClearPathsExcept("grid_visualization_all", "grid_visualization_channel", "grid_visualization_unknown", "grid_visualization_obstacle", "grid_visualization_door");
|
||
}
|
||
else
|
||
{
|
||
PathPointRenderPlugin.Instance.ClearPathsExcept(); // 清理所有路径,不保留任何内容
|
||
}
|
||
}
|
||
|
||
// 调用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;
|
||
|
||
// 获取选择的路径策略,如果未选择则默认使用最短路径
|
||
var selectedStrategy = SelectedPathStrategy?.Value ?? PathStrategy.Shortest;
|
||
|
||
LogManager.Info($"网格大小设置: {(IsGridSizeManuallyEnabled ? $"手动设置 {GridSize:F1}米" : "自动选择")}");
|
||
LogManager.Info($"路径策略: {SelectedPathStrategy?.DisplayName ?? "最短路径"}");
|
||
|
||
// 在STA线程中执行Navisworks API调用
|
||
var pathRoute = await _pathPlanningManager?.AutoPlanPath(startPathPoint, endPathPoint, vehicleRadius, SafetyMargin, gridSize, VehicleHeight, selectedStrategy);
|
||
|
||
if (pathRoute != null && pathRoute.Points.Count > 0)
|
||
{
|
||
LogManager.Info($"路径规划成功,共 {pathRoute.Points.Count} 个点");
|
||
|
||
// 自动路径规划完成后,执行完整的事件订阅清理
|
||
CleanupAutoPathEventSubscriptions();
|
||
if (_pathPlanningManager != null)
|
||
{
|
||
_pathPlanningManager.StopClickTool();
|
||
}
|
||
|
||
// 清理临时的起点和终点标记
|
||
ClearTemporaryAutoPathMarkers();
|
||
|
||
// 不在这里直接操作UI,让RouteGenerated事件处理UI更新
|
||
// 这避免了重复的路径添加和UI更新冲突
|
||
|
||
// 不在这里设置状态,让RouteGenerated事件处理UI更新
|
||
LogManager.Info($"✅ 自动路径规划完成,RouteGenerated事件将处理UI更新");
|
||
}
|
||
else
|
||
{
|
||
// 这里的逻辑已经在上方处理过了
|
||
|
||
// 失败情况下也要清理事件订阅和临时标记
|
||
CleanupAutoPathEventSubscriptions();
|
||
if (_pathPlanningManager != null)
|
||
{
|
||
_pathPlanningManager.StopClickTool();
|
||
}
|
||
|
||
// 清理临时的起点和终点标记
|
||
ClearTemporaryAutoPathMarkers();
|
||
|
||
// 确保失败后也清理所有可能的残留路径对象
|
||
if (PathPointRenderPlugin.Instance != null)
|
||
{
|
||
// 检查是否有网格可视化启用,只在启用时保留网格可视化路径
|
||
if (_pathPlanningManager?.IsAnyGridVisualizationEnabled == true)
|
||
{
|
||
PathPointRenderPlugin.Instance.ClearPathsExcept("grid_visualization_all", "grid_visualization_channel", "grid_visualization_unknown", "grid_visualization_obstacle", "grid_visualization_door");
|
||
}
|
||
else
|
||
{
|
||
PathPointRenderPlugin.Instance.ClearPathsExcept(); // 清理所有路径,不保留任何内容
|
||
}
|
||
}
|
||
}
|
||
}, "自动路径规划");
|
||
}
|
||
|
||
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.ClearPathsExcept("grid_visualization_all", "grid_visualization_channel", "grid_visualization_unknown", "grid_visualization_obstacle", "grid_visualization_door");
|
||
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 = "未选择";
|
||
UpdateMainStatus("就绪");
|
||
IsSelectingStartPoint = false;
|
||
IsSelectingEndPoint = false;
|
||
|
||
// 清除路径引用
|
||
_autoPathStartPointRoute = null;
|
||
_autoPathEndPointRoute = null;
|
||
|
||
// 通知Can Execute属性更改
|
||
OnPropertyChanged(nameof(CanExecuteAutoPlanPath));
|
||
OnPropertyChanged(nameof(CanExecuteNewPath));
|
||
|
||
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;
|
||
UpdateMainStatus($"路径已重命名: {oldName} -> {newName}");
|
||
});
|
||
}, "重命名路径");
|
||
}
|
||
|
||
private async Task ExecuteAddPathPointAsync()
|
||
{
|
||
if (!CanExecuteStartEdit) return;
|
||
|
||
await SafeExecuteAsync(async () =>
|
||
{
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
IsPathEditMode = true;
|
||
if (SelectedPathRoute != null)
|
||
{
|
||
SelectedPathRoute.IsActive = true;
|
||
UpdateMainStatus($"正在激活3D路径编辑模式: {SelectedPathRoute.Name}...");
|
||
|
||
// 启动PathPlanningManager的点击工具,这会设置正确的编辑状态
|
||
if (_pathPlanningManager != null)
|
||
{
|
||
try
|
||
{
|
||
_pathPlanningManager.StartClickTool(PathPointType.WayPoint);
|
||
LogManager.Info($"已启动PathPlanningManager点击工具: {SelectedPathRoute.Name}");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"启动点击工具失败: {ex.Message}");
|
||
UpdateMainStatus("启动3D编辑工具失败,请重试");
|
||
IsPathEditMode = false;
|
||
return;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
UpdateMainStatus("路径规划管理器未初始化");
|
||
LogManager.Error("添加路径点:PathPlanningManager未初始化");
|
||
IsPathEditMode = false;
|
||
return;
|
||
}
|
||
|
||
UpdateMainStatus($"已进入3D路径编辑模式: {SelectedPathRoute.Name} - 在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.UpdatePointPosition();
|
||
if (success)
|
||
{
|
||
operationMessage = "路径点修改已确认";
|
||
}
|
||
else
|
||
{
|
||
// 确认失败(可能没有预览点),执行取消操作以安全退出
|
||
success = _pathPlanningManager.CancelEditPoint();
|
||
operationMessage = success ? "已取消路径点修改" : "取消路径点修改失败";
|
||
}
|
||
LogManager.Info($"[UI-修改路径点] {operationMessage}");
|
||
}
|
||
else
|
||
{
|
||
// 记录是否在预览模式(因为确认预览点后会退出预览模式)
|
||
wasInPreviewMode = _pathPlanningManager.IsPreviewMode;
|
||
|
||
if (wasInPreviewMode)
|
||
{
|
||
// 预览模式 - 先确认预览点,然后立即完成编辑
|
||
var confirmedPoint = _pathPlanningManager.ConvertPreviewToPathPoint();
|
||
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("路径点修改"))
|
||
{
|
||
UpdateMainStatus($"✅ 路径点修改完成: {SelectedPathRoute.Name}");
|
||
}
|
||
else if (wasInPreviewMode)
|
||
{
|
||
UpdateMainStatus($"✅ 预览点已确认,路径编辑完成: {SelectedPathRoute.Name}");
|
||
}
|
||
else
|
||
{
|
||
UpdateMainStatus($"✅ 路径编辑完成: {SelectedPathRoute.Name} - 最后一个点已自动设置为终点");
|
||
}
|
||
}
|
||
|
||
LogManager.Info("[UI状态] 路径编辑完成,等待状态变更事件更新按钮");
|
||
}
|
||
else
|
||
{
|
||
// 编辑完成失败
|
||
if (SelectedPathRoute != null)
|
||
{
|
||
UpdateMainStatus($"❌ 路径编辑失败: {SelectedPathRoute.Name}");
|
||
}
|
||
|
||
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();
|
||
UpdateMainStatus($"已清空路径 {SelectedPathRoute.Name} 的 {pointCount} 个点");
|
||
});
|
||
}, "清空路径");
|
||
}
|
||
|
||
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. 路径完整性验证:检查删除后是否至少还有2个点
|
||
if (coreRoute.Points.Count <= 2)
|
||
{
|
||
LogManager.Warning($"路径点不足,无法删除。当前路径只有{coreRoute.Points.Count}个点,至少需要保留2个点(起点和终点)");
|
||
UpdateMainStatus($"❌ 无法删除路径点:路径至少需要保留2个点(起点和终点),当前只有{coreRoute.Points.Count}个点");
|
||
return;
|
||
}
|
||
|
||
// 3. 在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(() =>
|
||
{
|
||
// 4. 起点和终点的特殊处理逻辑
|
||
if (corePoint != null)
|
||
{
|
||
int pointIndex = coreRoute.Points.IndexOf(corePoint);
|
||
|
||
if (corePoint.Type == PathPointType.StartPoint && coreRoute.Points.Count > 1)
|
||
{
|
||
// 删除起点:下一个点变为起点
|
||
var nextPoint = coreRoute.Points[pointIndex + 1];
|
||
nextPoint.Type = PathPointType.StartPoint;
|
||
LogManager.Info($"起点被删除,下一个路径点 {nextPoint.Name} 已设为新的起点");
|
||
}
|
||
else if (corePoint.Type == PathPointType.EndPoint && coreRoute.Points.Count > 1)
|
||
{
|
||
// 删除终点:上一个点变为终点
|
||
var prevPoint = coreRoute.Points[pointIndex - 1];
|
||
prevPoint.Type = PathPointType.EndPoint;
|
||
LogManager.Info($"终点被删除,上一个路径点 {prevPoint.Name} 已设为新的终点");
|
||
}
|
||
|
||
// 删除Core数据
|
||
coreRoute.Points.Remove(corePoint);
|
||
LogManager.Info($"已从Core数据删除路径点: {corePoint.Name}");
|
||
|
||
// 调用PathPlanningManager的3D删除方法
|
||
_pathPlanningManager.RemovePathPoint(corePoint);
|
||
|
||
// 触发UI同步更新事件
|
||
_pathPlanningManager.RaisePathPointsListUpdated(coreRoute, "删除路径点并重新分配类型");
|
||
LogManager.Info("已触发PathPointsListUpdated事件,UI将自动同步");
|
||
}
|
||
|
||
// 5. 删除UI数据(事件驱动会自动处理,这里只作为备份)
|
||
SelectedPathRoute.Points.Remove(point);
|
||
LogManager.Info($"已从UI删除路径点: {point.Name}");
|
||
|
||
// 6. 更新UI状态
|
||
UpdateMainStatus($"✅ 已删除路径点: {point.Name}");
|
||
|
||
LogManager.Info($"路径点删除完成,当前UI点数: {SelectedPathRoute.Points.Count}, Core点数: {coreRoute?.Points.Count ?? 0}");
|
||
});
|
||
|
||
// 7. 刷新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)
|
||
{
|
||
UpdateMainStatus($"正在修改路径点: {SelectedPathPoint.Name} - 请在3D视图中点击新位置");
|
||
LogManager.Info($"已开始修改路径点: {SelectedPathPoint.Name}, 索引: {pointIndex}");
|
||
|
||
// 刷新命令状态
|
||
OnPropertyChanged(nameof(CanExecuteStartEdit));
|
||
OnPropertyChanged(nameof(CanExecuteEndEdit));
|
||
OnPropertyChanged(nameof(CanExecuteModifyPoint));
|
||
OnPropertyChanged(nameof(CanExecuteCancelModifyPoint));
|
||
}
|
||
else
|
||
{
|
||
UpdateMainStatus("修改路径点失败,请检查路径状态");
|
||
LogManager.Error($"开始修改路径点失败: {SelectedPathPoint.Name}, 索引: {pointIndex}");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
UpdateMainStatus("找不到选中的路径点");
|
||
LogManager.Error($"找不到路径点索引 - 选中点Id: {SelectedPathPoint.Id}, 选中点名称: {SelectedPathPoint.Name}");
|
||
LogManager.Error("可能原因:1)选中的路径点与当前路径不匹配 2)路径点数据不同步 3)Id不匹配");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (currentRoute == null)
|
||
{
|
||
UpdateMainStatus("当前没有活动路径");
|
||
LogManager.Error("尝试修改路径点时没有当前路径");
|
||
}
|
||
else
|
||
{
|
||
UpdateMainStatus("当前路径没有路径点数据");
|
||
LogManager.Error("当前路径的Points集合为空");
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (SelectedPathPoint == null)
|
||
{
|
||
UpdateMainStatus("请先选中要修改的路径点");
|
||
LogManager.Warning("修改路径点时没有选中路径点");
|
||
}
|
||
else
|
||
{
|
||
UpdateMainStatus("路径规划管理器未初始化");
|
||
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)
|
||
{
|
||
UpdateMainStatus("已取消修改路径点");
|
||
LogManager.Info("用户取消了修改路径点操作");
|
||
}
|
||
else
|
||
{
|
||
UpdateMainStatus("取消修改路径点失败");
|
||
LogManager.Error("取消修改路径点失败");
|
||
}
|
||
|
||
// 刷新命令状态
|
||
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)
|
||
{
|
||
var statusMessage = $"✅ 导入成功: {importResult.ImportedCount}/{importResult.TotalInFile} 个路径";
|
||
if (importResult.FailedCount > 0)
|
||
{
|
||
statusMessage += $" (失败 {importResult.FailedCount} 个)";
|
||
}
|
||
UpdateMainStatus(statusMessage);
|
||
if (!string.IsNullOrEmpty(importResult.BackupFilePath))
|
||
{
|
||
LogManager.Info($"导入前备份已创建: {importResult.BackupFilePath}");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
UpdateMainStatus($"✅ 路径导入完成: {Path.GetFileName(filePath)}");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
UpdateMainStatus($"❌ 导入失败: {result.ErrorMessage}");
|
||
LogManager.Error($"路径导入失败: {result.ErrorMessage}");
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"导入路径异常: {ex.Message}", ex);
|
||
UpdateMainStatus($"❌ 导入异常: {ex.Message}");
|
||
}
|
||
}, "导入路径");
|
||
}
|
||
|
||
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)
|
||
{
|
||
var statusMessage = $"✅ 导出全部成功: {exportResult.ExportedCount} 个路径到 {Path.GetFileName(filePath)}";
|
||
if (exportResult.FailedCount > 0)
|
||
{
|
||
statusMessage += $" (失败 {exportResult.FailedCount} 个)";
|
||
}
|
||
UpdateMainStatus(statusMessage);
|
||
LogManager.Info($"导出完成,文件大小: {exportResult.FileSizeMB:F2} MB");
|
||
}
|
||
else
|
||
{
|
||
UpdateMainStatus($"✅ 全部路径导出完成: {Path.GetFileName(filePath)}");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
UpdateMainStatus($"❌ 导出全部失败: {result.ErrorMessage}");
|
||
LogManager.Error($"全部路径导出失败: {result.ErrorMessage}");
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"导出全部路径异常: {ex.Message}", ex);
|
||
UpdateMainStatus($"❌ 导出全部异常: {ex.Message}");
|
||
}
|
||
}, "导出全部路径");
|
||
}
|
||
|
||
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)
|
||
{
|
||
UpdateMainStatus($"✅ 导出选中路径成功: {SelectedPathRoute.Name} -> {Path.GetFileName(filePath)}");
|
||
LogManager.Info($"导出选中路径完成,文件大小: {exportResult.FileSizeMB:F2} MB");
|
||
}
|
||
else
|
||
{
|
||
UpdateMainStatus($"✅ 导出选中路径完成: {Path.GetFileName(filePath)}");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
UpdateMainStatus($"❌ 导出选中路径失败: {result.ErrorMessage}");
|
||
LogManager.Error($"导出选中路径失败: {result.ErrorMessage}");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
UpdateMainStatus($"❌ 导出选中路径失败:找不到对应的Core路径: {SelectedPathRoute.Name}");
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"导出选中路径异常: {ex.Message}", ex);
|
||
UpdateMainStatus($"❌ 导出选中路径异常: {ex.Message}");
|
||
}
|
||
}, "导出选中路径");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 生成导航地图的纯业务逻辑方法
|
||
/// </summary>
|
||
/// <param name="renderStyle">渲染样式</param>
|
||
/// <param name="width">图像宽度</param>
|
||
/// <param name="height">图像高度</param>
|
||
/// <param name="format">图像格式</param>
|
||
/// <returns>生成的图像数据</returns>
|
||
public Bitmap GenerateNavigationMap(ImageGenerationStyle renderStyle, int width, int height, ImageFormat format)
|
||
{
|
||
try
|
||
{
|
||
LogManager.Info($"开始生成导航地图: 样式={renderStyle}, 尺寸={width}x{height}, 格式={format}");
|
||
|
||
// 创建导航地图生成器
|
||
var generator = new NavigationMapGenerator();
|
||
|
||
// 生成图像
|
||
var bitmap = generator.GenerateNavigationMapImage(renderStyle, width, height);
|
||
|
||
LogManager.Info($"导航地图生成成功: {width}x{height}像素");
|
||
return bitmap;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"生成导航地图失败: {ex.Message}", ex);
|
||
throw;
|
||
}
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 刷新路径集合显示
|
||
/// </summary>
|
||
private void RefreshPathRoutes()
|
||
{
|
||
try
|
||
{
|
||
if (_pathPlanningManager?.Routes != null)
|
||
{
|
||
// 清除现有的UI路径
|
||
PathRoutes.Clear();
|
||
|
||
// 重新加载所有路径
|
||
foreach (var coreRoute in _pathPlanningManager.Routes)
|
||
{
|
||
var pathViewModel = new PathRouteViewModel
|
||
{
|
||
Id = coreRoute.Id, // 添加Id属性设置
|
||
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
|
||
{
|
||
if (pickResult == null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
var point3D = pickResult.Point;
|
||
|
||
if (IsSelectingStartPoint)
|
||
{
|
||
SetAutoPathStartPoint(point3D);
|
||
await SafeExecuteAsync(() =>
|
||
{
|
||
// 退出自动路径规划模式 - 恢复事件订阅
|
||
LogManager.Info("起点选择完成,恢复正常事件订阅状态");
|
||
CleanupAutoPathEventSubscriptions();
|
||
_pathPlanningManager?.EnableMouseHandling(); // 恢复PathPlanningManager的鼠标处理
|
||
_pathPlanningManager?.StopClickTool();
|
||
}, "停止起点选择工具");
|
||
}
|
||
else if (IsSelectingEndPoint)
|
||
{
|
||
SetAutoPathEndPoint(point3D);
|
||
await SafeExecuteAsync(() =>
|
||
{
|
||
// 退出自动路径规划模式 - 恢复事件订阅
|
||
LogManager.Info("终点选择完成,恢复正常事件订阅状态");
|
||
CleanupAutoPathEventSubscriptions();
|
||
_pathPlanningManager?.EnableMouseHandling(); // 恢复PathPlanningManager的鼠标处理
|
||
_pathPlanningManager?.StopClickTool();
|
||
}, "停止终点选择工具");
|
||
}
|
||
else
|
||
{
|
||
CleanupAutoPathEventSubscriptions();
|
||
_pathPlanningManager?.EnableMouseHandling(); // 恢复PathPlanningManager的鼠标处理
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[自动路径事件] 处理点击事件异常: {ex.Message}");
|
||
UpdateMainStatus($"获取点击位置失败: {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));
|
||
OnPropertyChanged(nameof(CanExecuteNewPath));
|
||
|
||
// 渲染新的起点标记
|
||
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)
|
||
{
|
||
UpdateMainStatus("起点和终点已设置,可以开始路径规划");
|
||
}
|
||
else
|
||
{
|
||
UpdateMainStatus("起点已设置,请选择终点");
|
||
}
|
||
|
||
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));
|
||
OnPropertyChanged(nameof(CanExecuteNewPath));
|
||
|
||
// 渲染新的终点标记
|
||
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)
|
||
{
|
||
UpdateMainStatus("起点和终点已设置,可以开始路径规划");
|
||
}
|
||
else
|
||
{
|
||
UpdateMainStatus("终点已设置,请选择起点");
|
||
}
|
||
|
||
LogManager.Info($"终点已设置: ({point.X:F2}, {point.Y:F2}, {point.Z:F2})");
|
||
}, "设置终点");
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 车辆参数同步
|
||
|
||
/// <summary>
|
||
/// 同步车辆参数到渲染插件
|
||
/// </summary>
|
||
private void SyncVehicleParametersToRenderPlugin()
|
||
{
|
||
try
|
||
{
|
||
if (PathPointRenderPlugin.Instance != null)
|
||
{
|
||
PathPointRenderPlugin.Instance.SetVehicleParameters(
|
||
VehicleLength,
|
||
VehicleWidth,
|
||
VehicleHeight,
|
||
SafetyMargin);
|
||
}
|
||
else
|
||
{
|
||
LogManager.Warning("[车辆参数同步] PathPointRenderPlugin实例为空,无法同步车辆参数");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[车辆参数同步] 同步车辆参数失败: {ex.Message}", ex);
|
||
}
|
||
}
|
||
|
||
#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);
|
||
UpdateMainStatus($"{operationName}失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 网格可视化设置变更事件处理
|
||
/// </summary>
|
||
private void OnGridVisualizationChanged()
|
||
{
|
||
try
|
||
{
|
||
LogManager.Info($"网格可视化设置已更改: 通行={ShowWalkableGrid}, 障碍物={ShowObstacleGrid}, 未知={ShowUnknownGrid}, 门={ShowDoorGrid}");
|
||
|
||
// 通知路径规划管理器更新网格可视化
|
||
if (_pathPlanningManager != null)
|
||
{
|
||
_pathPlanningManager.UpdateGridVisualizationSettings(
|
||
showWalkable: ShowWalkableGrid,
|
||
showObstacle: ShowObstacleGrid,
|
||
showUnknown: ShowUnknownGrid,
|
||
showDoor: ShowDoorGrid);
|
||
|
||
UpdateMainStatus("网格可视化设置已更新");
|
||
}
|
||
else
|
||
{
|
||
LogManager.Warning("无法获取路径规划管理器,网格可视化设置可能不会立即生效");
|
||
UpdateMainStatus("网格可视化设置已保存");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"应用网格可视化设置失败: {ex.Message}", ex);
|
||
UpdateMainStatus("网格可视化设置应用失败");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 网格点类型变更事件处理
|
||
/// </summary>
|
||
private void OnGridPointTypeChanged()
|
||
{
|
||
try
|
||
{
|
||
LogManager.Info($"网格点类型已更改: {GridPointType}");
|
||
|
||
// 通知路径点渲染插件更新网格点类型
|
||
var renderPlugin = PathPointRenderPlugin.Instance;
|
||
if (renderPlugin != null)
|
||
{
|
||
renderPlugin.GridPointType = GridPointType;
|
||
UpdateMainStatus("网格点类型已更新");
|
||
}
|
||
else
|
||
{
|
||
LogManager.Warning("无法获取PathPointRenderPlugin实例,网格点类型可能不会立即生效");
|
||
UpdateMainStatus("网格点类型已保存");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"应用网格点类型失败: {ex.Message}", ex);
|
||
UpdateMainStatus("网格点类型应用失败");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 路径可视化模式变更事件处理
|
||
/// </summary>
|
||
private void OnPathVisualizationModeChanged()
|
||
{
|
||
try
|
||
{
|
||
var renderPlugin = PathPointRenderPlugin.Instance;
|
||
if (renderPlugin != null)
|
||
{
|
||
var mode = IsVehicleSpaceMode ? PathVisualizationMode.VehicleSpace : PathVisualizationMode.StandardLine;
|
||
renderPlugin.VisualizationMode = mode;
|
||
|
||
LogManager.Info($"路径可视化模式已更改: {(IsVehicleSpaceMode ? "车辆通行空间" : "标准连线")}");
|
||
UpdateMainStatus("路径可视化模式已更新");
|
||
}
|
||
else
|
||
{
|
||
LogManager.Warning("无法获取PathPointRenderPlugin实例");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"应用路径可视化模式失败: {ex.Message}", ex);
|
||
UpdateMainStatus("路径可视化模式更新失败");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 清理自动路径相关的事件订阅
|
||
/// </summary>
|
||
private void CleanupAutoPathEventSubscriptions()
|
||
{
|
||
try
|
||
{
|
||
// 简单安全地移除事件订阅
|
||
// 由于C#事件处理机制,多次取消订阅同一个处理程序是安全的
|
||
// 即使处理程序未订阅,取消操作也不会抛出异常
|
||
PathClickToolPlugin.MouseClicked -= OnAutoPathMouseClicked;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[事件清理] 清理自动路径事件订阅失败: {ex.Message}", ex);
|
||
// 记录异常但不抛出,避免影响主流程
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 清理临时的自动路径起点和终点标记
|
||
/// </summary>
|
||
private void ClearTemporaryAutoPathMarkers()
|
||
{
|
||
try
|
||
{
|
||
if (PathPointRenderPlugin.Instance != null)
|
||
{
|
||
// 清除临时的起点标记(使用正确的路径ID)
|
||
if (_autoPathStartPointRoute != null)
|
||
{
|
||
PathPointRenderPlugin.Instance.RemovePath(_autoPathStartPointRoute.Id);
|
||
_autoPathStartPointRoute = null;
|
||
}
|
||
|
||
// 清除临时的终点标记(使用正确的路径ID)
|
||
if (_autoPathEndPointRoute != null)
|
||
{
|
||
PathPointRenderPlugin.Instance.RemovePath(_autoPathEndPointRoute.Id);
|
||
_autoPathEndPointRoute = null;
|
||
}
|
||
|
||
}
|
||
else
|
||
{
|
||
LogManager.Warning("[临时标记清理] PathPointRenderPlugin实例为null,无法清理标记");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[临时标记清理] 清理临时标记失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region PathPlanningManager事件处理
|
||
|
||
/// <summary>
|
||
/// 订阅PathPlanningManager事件
|
||
/// </summary>
|
||
private void SubscribeToPathPlanningManager()
|
||
{
|
||
if (_pathPlanningManager == null)
|
||
{
|
||
LogManager.Debug("PathPlanningManager尚未设置,跳过事件订阅");
|
||
return;
|
||
}
|
||
|
||
try
|
||
{
|
||
// 订阅路径点操作事件(用于手工路径编辑)
|
||
_pathPlanningManager.PathPointOperation += OnPathPointOperation;
|
||
|
||
// 订阅路径点列表更新事件
|
||
_pathPlanningManager.PathPointsListUpdated += OnPathPointsListUpdated;
|
||
|
||
// 订阅当前路径变更事件
|
||
_pathPlanningManager.CurrentRouteChanged += OnCurrentRouteChanged;
|
||
|
||
// 订阅编辑状态变更事件
|
||
_pathPlanningManager.EditStateChanged += OnEditStateChanged;
|
||
|
||
// 订阅路径生成事件(用于处理自动路径)
|
||
_pathPlanningManager.RouteGenerated += OnRouteGenerated;
|
||
|
||
// 添加错误事件订阅 - 用于处理自动路径规划失败等错误
|
||
_pathPlanningManager.ErrorOccurred += OnPathPlanningError;
|
||
|
||
LogManager.Debug("PathEditingViewModel已成功订阅PathPlanningManager事件");
|
||
}
|
||
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;
|
||
}
|
||
|
||
UpdateMainStatus($"已添加路径点: {e.PathPoint.Name} 到 {e.Route.Name}");
|
||
LogManager.Info($"路径点操作完成: {e.PathPoint.Name} 已添加到路径 {e.Route.Name}");
|
||
}
|
||
else if (e.OperationType == PathPointOperationType.Removed)
|
||
{
|
||
UpdateMainStatus($"已移除路径点: {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:
|
||
UpdateMainStatus($"正在新建路径: {e.EditingRoute?.Name} - 请在3D视图中点击设置路径点");
|
||
break;
|
||
case PathEditState.Editing:
|
||
UpdateMainStatus($"正在编辑路径: {e.EditingRoute?.Name} - 请在3D视图中点击设置路径点");
|
||
break;
|
||
case PathEditState.AddingPoints:
|
||
UpdateMainStatus($"正在添加路径点到: {e.EditingRoute?.Name} - 请在3D视图中点击设置路径点");
|
||
break;
|
||
case PathEditState.EditingPoint:
|
||
UpdateMainStatus($"正在修改路径点: {e.EditingRoute?.Name} - 请在3D视图中点击新位置,然后点击'结束'确认");
|
||
break;
|
||
case PathEditState.Viewing:
|
||
// 如果当前状态包含路径规划完成信息(包括完成度百分比),则保留不覆盖
|
||
var currentStatus = _mainViewModel?.StatusText ?? "";
|
||
if (!currentStatus.Contains("路径规划完成") && !currentStatus.Contains("路径规划部分完成") &&
|
||
!currentStatus.Contains("✅") && !currentStatus.Contains("🔶"))
|
||
{
|
||
UpdateMainStatus("路径编辑已完成");
|
||
}
|
||
break;
|
||
default:
|
||
UpdateMainStatus("就绪");
|
||
break;
|
||
}
|
||
|
||
// 通知命令状态更新
|
||
OnPropertyChanged(nameof(CanExecuteNewPath));
|
||
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
|
||
{
|
||
Id = e.Route.Id,
|
||
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" : "自动网格";
|
||
string statusMessage;
|
||
|
||
if (e.Route.IsComplete)
|
||
{
|
||
// 完全到达终点
|
||
statusMessage = $"✅ 自动路径规划完成: {e.Route.Name},共 {e.Route.Points.Count} 个路径点,长度 {e.Route.TotalLength:F2}米";
|
||
}
|
||
else
|
||
{
|
||
// 部分到达终点
|
||
statusMessage = $"🔶 自动路径规划部分完成: {e.Route.Name},共 {e.Route.Points.Count} 个路径点,长度 {e.Route.TotalLength:F2}米,(完成度: {e.Route.CompletionPercentage:F1}%)";
|
||
}
|
||
|
||
UpdateMainStatus(statusMessage);
|
||
LogManager.Info($"*** 状态已更新: {statusMessage} ***");
|
||
}
|
||
else if (e.GenerationMethod == RouteGenerationMethod.Manual)
|
||
{
|
||
// 手工路径生成(如果需要特殊处理)
|
||
LogManager.Info($"手工路径生成完成: {e.Route.Name}");
|
||
}
|
||
else if (e.GenerationMethod == RouteGenerationMethod.DatabaseLoad)
|
||
{
|
||
// 从数据库加载的历史路径
|
||
LogManager.Info($"*** 开始处理数据库加载路径: {e.Route.Name} ***");
|
||
|
||
// 创建对应的 WPF ViewModel
|
||
var dbPathViewModel = new PathRouteViewModel
|
||
{
|
||
Id = e.Route.Id,
|
||
Name = e.Route.Name,
|
||
Description = e.Route.Description,
|
||
IsActive = false // 历史路径默认不激活
|
||
};
|
||
|
||
// 转换路径点
|
||
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
|
||
};
|
||
dbPathViewModel.Points.Add(wpfPoint);
|
||
}
|
||
|
||
LogManager.Info($"*** 已创建PathRouteViewModel(数据库路径),包含 {dbPathViewModel.Points.Count} 个点 ***");
|
||
|
||
// 检查是否已存在同名路径,避免重复添加
|
||
var existingPath = PathRoutes.FirstOrDefault(p => p.Name == e.Route.Name);
|
||
if (existingPath == null)
|
||
{
|
||
// 添加到 UI 列表
|
||
PathRoutes.Add(dbPathViewModel);
|
||
LogManager.Info($"*** 数据库路径已添加到UI列表: {e.Route.Name},当前PathRoutes.Count = {PathRoutes.Count} ***");
|
||
}
|
||
else
|
||
{
|
||
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(() =>
|
||
{
|
||
// 更新自动路径状态显示错误信息
|
||
UpdateMainStatus($"❌ 自动路径规划失败: {e.ErrorMessage}");
|
||
LogManager.Info($"*** 状态已更新为: ❌ 自动路径规划失败: {e.ErrorMessage} ***");
|
||
});
|
||
|
||
}, "处理路径规划错误事件");
|
||
}
|
||
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)
|
||
{
|
||
try
|
||
{
|
||
LogManager.Info($"开始强制重新初始化ToolPlugin以确保获得鼠标焦点,订阅事件: {subscribeToEvents}");
|
||
|
||
// 使用PathPlanningManager的公共方法,避免反射调用
|
||
if (_pathPlanningManager != null)
|
||
{
|
||
bool result = _pathPlanningManager.ForceReinitializeToolPlugin(subscribeToEvents);
|
||
LogManager.Info($"ToolPlugin重新初始化结果(事件订阅: {subscribeToEvents}): {result}");
|
||
|
||
if (result)
|
||
{
|
||
LogManager.Info("ToolPlugin重新初始化成功,已获得鼠标焦点");
|
||
}
|
||
else
|
||
{
|
||
LogManager.Error("ToolPlugin初始化失败");
|
||
}
|
||
|
||
return result;
|
||
}
|
||
else
|
||
{
|
||
LogManager.Error("PathPlanningManager为null,无法重新初始化ToolPlugin");
|
||
return false;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"重新初始化ToolPlugin失败: {ex.Message}", ex);
|
||
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}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 释放资源 (IDisposable implementation)
|
||
/// </summary>
|
||
public void Dispose()
|
||
{
|
||
Dispose(true);
|
||
GC.SuppressFinalize(this);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 释放托管和非托管资源
|
||
/// </summary>
|
||
/// <param name="disposing">是否释放托管资源</param>
|
||
protected virtual void Dispose(bool disposing)
|
||
{
|
||
if (!_disposed)
|
||
{
|
||
if (disposing)
|
||
{
|
||
try
|
||
{
|
||
// 调用现有的清理逻辑
|
||
Cleanup();
|
||
LogManager.Info("PathEditingViewModel已正确释放资源 (IDisposable)");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"PathEditingViewModel释放资源时发生异常: {ex.Message}", ex);
|
||
}
|
||
}
|
||
_disposed = true;
|
||
}
|
||
}
|
||
|
||
#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; }
|
||
}
|
||
} |