NavisworksTransport/src/UI/WPF/ViewModels/PathEditingViewModel.cs

3080 lines
130 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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; }
}
}