From ac2de23e32ff43f57cec6cb6ef7da681a45ee805 Mon Sep 17 00:00:00 2001 From: tian <11429339@qq.com> Date: Wed, 3 Jun 2026 21:06:32 +0800 Subject: [PATCH] feat: support multi-select and batch export in path list - DataGrid: SelectionMode Extended with RowStyle IsSelected binding - PathRouteViewModel: add IsSelected property - Export: iterates selected paths, saves each to separate file - Ctrl/Shift+click to multi-select, then Export button batches them --- src/UI/WPF/Models/PathRouteViewModel.cs | 10 + src/UI/WPF/ViewModels/PathEditingViewModel.cs | 951 +++++++++--------- src/UI/WPF/Views/PathEditingView.xaml | 8 +- 3 files changed, 502 insertions(+), 467 deletions(-) diff --git a/src/UI/WPF/Models/PathRouteViewModel.cs b/src/UI/WPF/Models/PathRouteViewModel.cs index 9937926..89b7e2b 100644 --- a/src/UI/WPF/Models/PathRouteViewModel.cs +++ b/src/UI/WPF/Models/PathRouteViewModel.cs @@ -183,6 +183,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels private string _name; private ObservableCollection _points; private bool _isActive; + private bool _isSelected; private string _description; private DateTime _createdTime; private DateTime _lastModifiedTime; @@ -313,6 +314,15 @@ namespace NavisworksTransport.UI.WPF.ViewModels } } + /// + /// 是否被选中(DataGrid多选绑定) + /// + public bool IsSelected + { + get => _isSelected; + set => SetProperty(ref _isSelected, value); + } + /// /// 路径描述 /// diff --git a/src/UI/WPF/ViewModels/PathEditingViewModel.cs b/src/UI/WPF/ViewModels/PathEditingViewModel.cs index ce7eba1..5cc881d 100644 --- a/src/UI/WPF/ViewModels/PathEditingViewModel.cs +++ b/src/UI/WPF/ViewModels/PathEditingViewModel.cs @@ -94,15 +94,15 @@ namespace NavisworksTransport.UI.WPF.ViewModels private bool _isSelectingStartPoint = false; private bool _isSelectingEndPoint = false; - // 物体参数 - 改为三个独立参数(从配置初始化) - private double _objectLength; // 物体长度(米) - private double _objectWidth; // 物体宽度(米) - private double _objectHeight; // 物体高度(米) - private double _safetyMargin; // 安全间隙(米) + // 物体参数 - 改为三个独立参数(从配置初始化) + private double _objectLength; // 物体长度(米) + private double _objectWidth; // 物体宽度(米) + private double _objectHeight; // 物体高度(米) + private double _safetyMargin; // 安全间隙(米) - // 网格大小参数(从配置初始化) + // 网格大小参数(从配置初始化) private bool _isGridSizeManuallyEnabled = false; // 是否启用手动网格大小设置 - private double _gridSize; // 网格大小(米) + private double _gridSize; // 网格大小(米) // 可视化参数 private bool _showWalkableGrid = false; @@ -112,9 +112,9 @@ namespace NavisworksTransport.UI.WPF.ViewModels private GridPointType _gridPointType = GridPointType.Rectangle; // 三种渲染模式独立开关 private bool _showControlVisualization = true; // 默认显示控制点连线 - private bool _showPathLines = true; // 默认显示路径线(地面连线) + private bool _showPathLines = true; // 默认显示路径线(地面连线) private bool _showObjectSpace = false; // 默认不显示通行空间 - // 渲染模式已改为独立开关(_showControlVisualization, _showPathLines, _showObjectSpace) + // 渲染模式已改为独立开关(_showControlVisualization, _showPathLines, _showObjectSpace) // 下面的互斥属性已废弃并删除 // 路径策略参数 @@ -322,7 +322,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels private List _assemblyEndFaceSeedPoints => _railAssemblyContext.EndFaceSeedPoints; - // 自动路径起点和终点的路径对象引用(用于正确的ID管理) + // 自动路径起点和终点的路径对象引用(用于正确的ID管理) private PathRoute _autoPathStartPointRoute = null; private PathRoute _autoPathEndPointRoute = null; @@ -341,7 +341,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels private string _multiLevelStartPointText = "未选择"; private string _multiLevelEndPointText = "未选择"; private string _multiLevelStatsText = "层级数: 0 | 预估路径点数: 0"; - private string _hoistingLayerEditHintText = "选择一条吊装路径后,可在这里按层编辑相对起点的绝对高度。"; + private string _hoistingLayerEditHintText = "选择一条吊装路径后,可在这里按层编辑相对起点的绝对高度。"; private bool _isSelectingMultiLevelStartPoint = false; private bool _isSelectingMultiLevelEndPoint = false; private HoistingLevelItem _currentSelectingLevelTarget; @@ -367,7 +367,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels SelectedPathPoint = null; ResetRailAssemblyWorkflowState(announce: false); - // 🔧 修复:同步PathPlanningManager的CurrentRoute + // 🔧 修复:同步PathPlanningManager的CurrentRoute if (_pathPlanningManager != null && value != null) { // 查找对应的Core路径对象 @@ -384,45 +384,45 @@ namespace NavisworksTransport.UI.WPF.ViewModels ApplyStoredAutoPlanningParameters(coreRoute); - // 🔥 先设置可视化模式(在 SetCurrentRoute 之前,确保事件触发时状态已就绪) + // 🔥 先设置可视化模式(在 SetCurrentRoute 之前,确保事件触发时状态已就绪) UpdatePassageSpaceVisualizationForPathType(coreRoute.PathType); - - _pathPlanningManager.SetCurrentRoute(coreRoute); - LogManager.Info($"UI路径切换:已同步PathPlanningManager的CurrentRoute到 {value.Name}"); - // 新建路径流程里禁止自动改视角,避免打断建路径操作。 + _pathPlanningManager.SetCurrentRoute(coreRoute); + LogManager.Info($"UI路径切换:已同步PathPlanningManager的CurrentRoute到 {value.Name}"); + + // 新建路径流程里禁止自动改视角,避免打断建路径操作。 if (_pathPlanningManager.PathEditState == PathEditState.Creating) { - LogManager.Info($"UI路径切换:当前处于新建路径流程,跳过自动视角调整: {value.Name}"); + LogManager.Info($"UI路径切换:当前处于新建路径流程,跳过自动视角调整: {value.Name}"); } - // 自动调整视角到路径中心(只有路径有点时才调整) + // 自动调整视角到路径中心(只有路径有点时才调整) else if (coreRoute.Points.Count > 0) { try { ViewpointHelper.AdjustViewpointForPath(coreRoute); - LogManager.Info($"UI路径切换:已自动调整视角: {value.Name}, 类型={coreRoute.PathType}"); + LogManager.Info($"UI路径切换:已自动调整视角: {value.Name}, 类型={coreRoute.PathType}"); } catch (Exception ex) { - LogManager.Error($"UI路径切换:调整视角失败: {ex.Message}"); + LogManager.Error($"UI路径切换:调整视角失败: {ex.Message}"); } } else { - LogManager.Info($"UI路径切换:路径还没有点,跳过视角调整: {value.Name}"); + LogManager.Info($"UI路径切换:路径还没有点,跳过视角调整: {value.Name}"); } } else { - LogManager.Warning($"UI路径切换:未找到对应的Core路径 {value.Name}"); + LogManager.Warning($"UI路径切换:未找到对应的Core路径 {value.Name}"); } } else if (_pathPlanningManager != null && value == null) { - // 如果选择了null,也要同步清除CurrentRoute + // 如果选择了null,也要同步清除CurrentRoute _pathPlanningManager.SetCurrentRoute(null); - LogManager.Info("UI路径切换:已清除PathPlanningManager的CurrentRoute"); + LogManager.Info("UI路径切换:已清除PathPlanningManager的CurrentRoute"); } // 更新命令状态 @@ -433,7 +433,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels OnPropertyChanged(nameof(CanExecuteSaveAsPath)); OnPropertyChanged(nameof(CanExecuteModifyPoint)); - // 🔥 路径类型改变时,检查是否可以使用路径线 + // 🔥 路径类型改变时,检查是否可以使用路径线 OnPropertyChanged(nameof(CanUsePathLines)); OnPropertyChanged(nameof(IsRailRouteSelected)); OnPropertyChanged(nameof(IsHoistingRouteSelected)); @@ -444,9 +444,9 @@ namespace NavisworksTransport.UI.WPF.ViewModels NotifyRailAssemblyCommandStateChanged(); if (!CanUsePathLines && ShowPathLines) { - // 吊装和空轨路径不能使用路径线,自动关闭 + // 吊装和空轨路径不能使用路径线,自动关闭 ShowPathLines = false; - LogManager.Info("[路径可视化] 切换到吊装/空轨路径,自动关闭路径线"); + LogManager.Info("[路径可视化] 切换到吊装/空轨路径,自动关闭路径线"); } RefreshHoistingLayerEditItemsFromSelectedRoute(); @@ -467,7 +467,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels // 更新修改路径点按钮的启用状态 OnPropertyChanged(nameof(CanExecuteModifyPoint)); - // 路径点选中可视化:将选中的路径点显示为预览样式 + // 路径点选中可视化:将选中的路径点显示为预览样式 try { if (value != null) @@ -797,7 +797,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels } } /// - /// 更新路径可视化显示:清理其他路径,只显示当前选中的路径 + /// 更新路径可视化显示:清理其他路径,只显示当前选中的路径 /// private async void UpdatePathVisualization() { @@ -807,7 +807,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels { if (PathPointRenderPlugin.Instance == null) { - LogManager.Warning("PathPointRenderPlugin实例为空,无法更新路径可视化"); + LogManager.Warning("PathPointRenderPlugin实例为空,无法更新路径可视化"); return; } @@ -816,20 +816,20 @@ namespace NavisworksTransport.UI.WPF.ViewModels // 检查是否在编辑状态 bool isInEditMode = _pathPlanningManager?.IsInEditableState ?? false; - - // 1. 清理现有的路径可视化,但保留网格可视化和正在编辑的路径 + + // 1. 清理现有的路径可视化,但保留网格可视化和正在编辑的路径 if (isInEditMode) { - // 在编辑状态,不清空路径,避免清空正在编辑的路径 - LogManager.Debug("[路径可视化] 在编辑状态,跳过清空路径可视化"); + // 在编辑状态,不清空路径,避免清空正在编辑的路径 + LogManager.Debug("[路径可视化] 在编辑状态,跳过清空路径可视化"); } else { - // 不在编辑状态,清空所有路径可视化,但保留网格可视化 + // 不在编辑状态,清空所有路径可视化,但保留网格可视化 PathPointRenderPlugin.Instance.ClearPathsExcept("grid_visualization_all", "grid_visualization_channel", "grid_visualization_unknown", "grid_visualization_obstacle", "grid_visualization_door"); } - // 2. 如果有选中的路径,则显示该路径 + // 2. 如果有选中的路径,则显示该路径 if (_selectedPathRoute != null && _selectedPathRoute.Points.Count > 0) { // 查找对应的Core路径对象 @@ -839,7 +839,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels { // 使用PathPlanningManager的绘制方法来保持一致性 _pathPlanningManager.DrawRouteVisualization(coreRoute, isAutoPath: false); - LogManager.Info($"[路径可视化] 已显示选中路径: {_selectedPathRoute.Name},包含 {_selectedPathRoute.Points.Count} 个点"); + LogManager.Info($"[路径可视化] 已显示选中路径: {_selectedPathRoute.Name},包含 {_selectedPathRoute.Points.Count} 个点"); } else { @@ -849,7 +849,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels RefreshSelectedRailInstallationReferenceVisuals(); - // 3. 恢复网格可视化(如果网格可视化已启用且当前路径有关联的GridMap) + // 3. 恢复网格可视化(如果网格可视化已启用且当前路径有关联的GridMap) if (_pathPlanningManager?.IsAnyGridVisualizationEnabled == true) { // 检查当前路径是否有关联的GridMap @@ -871,7 +871,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels } else { - LogManager.Debug("[路径可视化] 当前路径没有关联的GridMap,跳过网格恢复"); + LogManager.Debug("[路径可视化] 当前路径没有关联的GridMap,跳过网格恢复"); } } } @@ -1049,7 +1049,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels if (!hasAnyStoredValue) { - LogManager.Debug($"[自动路径参数同步] 路径 {route.Name} 没有保存自动规划参数,保持当前输入值"); + LogManager.Debug($"[自动路径参数同步] 路径 {route.Name} 没有保存自动规划参数,保持当前输入值"); return; } @@ -1214,7 +1214,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels } /// - /// 是否显示控制点连线(独立开关) + /// 是否显示控制点连线(独立开关) /// public bool ShowControlVisualization { @@ -1229,7 +1229,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels } /// - /// 是否显示路径线/地面连线(独立开关) + /// 是否显示路径线/地面连线(独立开关) /// public bool ShowPathLines { @@ -1250,7 +1250,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels } /// - /// 是否显示通行空间(独立开关) + /// 是否显示通行空间(独立开关) /// public bool ShowObjectSpace { @@ -1266,7 +1266,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels /// /// 当前路径是否可以使用路径线 - /// 地面路径可以使用路径线,吊装和空轨路径不能使用路径线 + /// 地面路径可以使用路径线,吊装和空轨路径不能使用路径线 /// public bool CanUsePathLines { @@ -1279,7 +1279,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels } /// - /// 是否使用标准连线模式(向后兼容,根据独立开关计算) + /// 是否使用标准连线模式(向后兼容,根据独立开关计算) /// public bool IsStandardLineMode { @@ -1295,7 +1295,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels } /// - /// 是否使用物体通行空间模式(向后兼容,等同于 ShowObjectSpace) + /// 是否使用物体通行空间模式(向后兼容,等同于 ShowObjectSpace) /// public bool IsObjectSpaceMode { @@ -1304,7 +1304,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels } /// - /// 是否使用带状连线模式(向后兼容,等同于 ShowPathLines) + /// 是否使用带状连线模式(向后兼容,等同于 ShowPathLines) /// public bool IsRibbonLineMode { @@ -1413,7 +1413,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels public ICommand AnalyzeAssemblyTerminalFaceForEditCommand { get; private set; } public ICommand DecreaseSelectedRailNormalOffsetCommand { get; private set; } public ICommand IncreaseSelectedRailNormalOffsetCommand { get; private set; } - + // 多层吊装命令 public ICommand SelectMultiLevelStartPointCommand { get; private set; } public ICommand SelectMultiLevelEndPointCommand { get; private set; } @@ -1449,7 +1449,8 @@ namespace NavisworksTransport.UI.WPF.ViewModels public bool CanExecuteExportPath => _pathPlanningManager?.Routes?.Count > 0 || SelectedPathRoute != null; - public bool CanExecuteSaveAsPath => SelectedPathRoute != null && SelectedPathRoute.Points.Count > 0; + public bool CanExecuteSaveAsPath => PathRoutes?.Any(r => r.IsSelected) == true + || (SelectedPathRoute != null && SelectedPathRoute.Points.Count > 0); public bool CanSelectAssemblyStartPointForCreate => HasAssemblyTerminalObject && _pathPlanningManager != null && AssemblyReferencePathManager.Instance.HasReferenceLine && @@ -1540,9 +1541,9 @@ namespace NavisworksTransport.UI.WPF.ViewModels #region 构造函数 /// - /// 构造函数,需要传入主ViewModel以支持统一状态栏 + /// 构造函数,需要传入主ViewModel以支持统一状态栏 /// - /// 主ViewModel,用于统一状态栏 + /// 主ViewModel,用于统一状态栏 public PathEditingViewModel(LogisticsControlViewModel mainViewModel) : base() { try @@ -1552,7 +1553,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels _uiStateManager = UIStateManager.Instance; _pathDataManager = new PathDataManager(); - // 不在构造函数中创建PathPlanningManager,由外部设置 + // 不在构造函数中创建PathPlanningManager,由外部设置 _pathPlanningManager = null; if (_uiStateManager == null) @@ -1560,7 +1561,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels throw new InvalidOperationException("UIStateManager初始化失败"); } - // PathPlanningManager将在外部设置,这里不需要检查 + // PathPlanningManager将在外部设置,这里不需要检查 // 初始化集合 PathRoutes = new ObservableCollection(); @@ -1578,19 +1579,19 @@ namespace NavisworksTransport.UI.WPF.ViewModels // 初始化命令 InitializeCommands(); - // 初始化物体参数同步(在PathPointRenderPlugin实例不为空时才同步) - // 如果PathPointRenderPlugin尚未初始化,将在属性变更时自动同步 + // 初始化物体参数同步(在PathPointRenderPlugin实例不为空时才同步) + // 如果PathPointRenderPlugin尚未初始化,将在属性变更时自动同步 SyncObjectParametersToRenderPlugin(); - // 注意:不在这里订阅PathPlanningManager事件, - // 因为PathPlanningManager还没有设置,事件订阅在PathPlanningManager属性setter中处理 + // 注意:不在这里订阅PathPlanningManager事件, + // 因为PathPlanningManager还没有设置,事件订阅在PathPlanningManager属性setter中处理 LogManager.Info("PathEditingViewModel构造函数执行完成 - 支持统一状态栏"); } catch (Exception ex) { LogManager.Error($"PathEditingViewModel构造函数异常: {ex.Message}", ex); - UpdateMainStatus("初始化失败,请检查日志"); + UpdateMainStatus("初始化失败,请检查日志"); throw; } } @@ -1610,23 +1611,23 @@ namespace NavisworksTransport.UI.WPF.ViewModels { Value = PathStrategy.Shortest, DisplayName = "最短路径", - Description = "标准A*算法,选择路径总长度最短的路径" + Description = "标准A*算法,选择路径总长度最短的路径" }, new PathStrategyOption { Value = PathStrategy.Straightest, DisplayName = "直线优先", - Description = "优先选择主方向直线路径,减少转弯次数" + Description = "优先选择主方向直线路径,减少转弯次数" }, new PathStrategyOption { Value = PathStrategy.SafestCenter, DisplayName = "安全优先", - Description = "基于障碍物距离选择安全路径,适合大型物体居中行驶" + Description = "基于障碍物距离选择安全路径,适合大型物体居中行驶" } }; - // 默认选择最短路径策略(向后兼容) + // 默认选择最短路径策略(向后兼容) _selectedPathStrategy = _pathStrategyOptions.FirstOrDefault(x => x.Value == PathStrategy.Shortest); } @@ -1641,13 +1642,13 @@ namespace NavisworksTransport.UI.WPF.ViewModels { Value = RailMountMode.UnderRail, DisplayName = "轨下安装", - Description = "安装头位于双轨下方,构件从轨道平面下侧连接" + Description = "安装头位于双轨下方,构件从轨道平面下侧连接" }, new RailConfigOption { Value = RailMountMode.OverRail, DisplayName = "轨上安装", - Description = "安装头位于双轨上方,构件从轨道平面上侧连接" + Description = "安装头位于双轨上方,构件从轨道平面上侧连接" } }; @@ -1792,7 +1793,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels if (IsRailEditingWorkflowActive) { - // 编辑状态:使用默认杆长度构建参考线,方便选择更远的起点 + // 编辑状态:使用默认杆长度构建参考线,方便选择更远的起点 try { referenceLine = BuildAssemblyReferenceLine(); @@ -1805,7 +1806,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels } else { - // 非编辑状态:使用路径实际长度 + // 非编辑状态:使用路径实际长度 var coreRoute = GetSelectedCoreRoute(); if (!TryBuildRailRouteReferenceLine(coreRoute, out referenceLine)) { @@ -1859,12 +1860,12 @@ namespace NavisworksTransport.UI.WPF.ViewModels throw new InvalidOperationException("当前 Rail 路径没有可更新的起点。"); } - // 计算原路径长度(起点到终点的距离) + // 计算原路径长度(起点到终点的距离) Point3D currentStartPoint = route.Points[startPointIndex].Position; Point3D currentEndPoint = route.Points[installationPointIndex].Position; double pathLength = GeometryHelper.CalculatePointDistance(currentStartPoint, currentEndPoint); - // 更新终点(安装点) + // 更新终点(安装点) if (!_pathPlanningManager.UpdatePathPointWithConstraints(route, installationPointIndex, _assemblyInstallationAnchorPoint)) { throw new InvalidOperationException("更新 Rail 路径安装点失败。"); @@ -1896,7 +1897,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels RefreshSelectedRailReferenceLineVisuals(); } - LogManager.Info($"[Rail构型] {route.Name}: {logMessage},起点已同步更新为 ({newStartPoint.X:F3}, {newStartPoint.Y:F3}, {newStartPoint.Z:F3})"); + LogManager.Info($"[Rail构型] {route.Name}: {logMessage},起点已同步更新为 ({newStartPoint.X:F3}, {newStartPoint.Y:F3}, {newStartPoint.Z:F3})"); } private void RefreshSelectedRailInstallationReferenceVisuals() @@ -1936,7 +1937,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels { var config = ConfigManager.Instance.Current; - // 从 PathEditing 配置加载所有参数(使用米单位接口) + // 从 PathEditing 配置加载所有参数(使用米单位接口) _gridSize = config.PathEditing.CellSizeMeters; _objectLength = config.PathEditing.ObjectLengthMeters; _objectWidth = config.PathEditing.ObjectWidthMeters; @@ -1948,7 +1949,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels catch (Exception ex) { LogManager.Error($"加载配置参数失败: {ex.Message}", ex); - // 使用默认值,不抛出异常 + // 使用默认值,不抛出异常 } } @@ -2000,12 +2001,12 @@ namespace NavisworksTransport.UI.WPF.ViewModels // 只处理 PathEditing 相关的配置变更 if (category == ConfigCategory.PathEditing || category == ConfigCategory.All) { - LogManager.Info($"收到 PathEditing 配置变更通知,正在更新参数..."); + LogManager.Info($"收到 PathEditing 配置变更通知,正在更新参数..."); // 从配置重新加载参数 LoadParametersFromConfig(); - // 触发属性变更通知,更新UI + // 触发属性变更通知,更新UI OnPropertiesChanged( nameof(ObjectLength), nameof(ObjectWidth), @@ -2098,7 +2099,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels if (!ModelItemAnalysisHelper.IsModelItemValid(selectedItem)) { - throw new InvalidOperationException("当前选择对象无效,请重新选择终点箱体"); + throw new InvalidOperationException("当前选择对象无效,请重新选择终点箱体"); } SetRailAssemblyWorkflowMode(mode); @@ -2126,9 +2127,9 @@ namespace NavisworksTransport.UI.WPF.ViewModels if (isEditMode) { - // 编辑态重选箱体后,只恢复可由当前 Rail 路径和终点箱体稳定重建的可视化。 - // 安装参考面依赖安装拾取点、安装面 span 等会话态数据,这些数据当前未持久化到 PathRoute, - // 因此这里不尝试恢复安装参考面,避免后续误以为“少了一次刷新调用”是 bug。 + // 编辑态重选箱体后,只恢复可由当前 Rail 路径和终点箱体稳定重建的可视化。 + // 安装参考面依赖安装拾取点、安装面 span 等会话态数据,这些数据当前未持久化到 PathRoute, + // 因此这里不尝试恢复安装参考面,避免后续误以为"少了一次刷新调用"是 bug。 RefreshSelectedPathVisualizationForAssemblyEdit(); RefreshAssemblyReferenceRodIfNeeded(); } @@ -2175,11 +2176,11 @@ namespace NavisworksTransport.UI.WPF.ViewModels "grid_visualization_unknown", "grid_visualization_obstacle", "grid_visualization_door"); - LogManager.Info($"{context}:已清除现有路径可视化显示(保留网格可视化)"); + LogManager.Info($"{context}:已清除现有路径可视化显示(保留网格可视化)"); } catch (Exception ex) { - LogManager.Error($"{context}:清除现有路径可视化失败: {ex.Message}", ex); + LogManager.Error($"{context}:清除现有路径可视化失败: {ex.Message}", ex); throw; } } @@ -2190,24 +2191,24 @@ namespace NavisworksTransport.UI.WPF.ViewModels { if (_pathPlanningManager == null) { - throw new InvalidOperationException("路径规划管理器未初始化,无法选择装配起点"); + throw new InvalidOperationException("路径规划管理器未初始化,无法选择装配起点"); } if (_assemblyTerminalObject == null || !ModelItemAnalysisHelper.IsModelItemValid(_assemblyTerminalObject)) { - throw new InvalidOperationException("终点箱体未设置或已失效,请重新捕获终点箱体"); + throw new InvalidOperationException("终点箱体未设置或已失效,请重新捕获终点箱体"); } if (!AssemblyReferencePathManager.Instance.HasReferenceLine) { - throw new InvalidOperationException("请先选择安装点,系统生成终端安装辅助线后才能取起点"); + throw new InvalidOperationException("请先选择安装点,系统生成终端安装辅助线后才能取起点"); } ClearAssemblyInstallationReferenceVisuals(); BeginAssemblyStartPointSelection( - statusMessage: "请在辅助线附近点击一个起点,系统会自动吸附到辅助线上并生成直线路径", + statusMessage: "请在辅助线附近点击一个起点,系统会自动吸附到辅助线上并生成直线路径", logMessage: "[直线装配] 已进入起点拾取模式", - toolPluginFailureMessage: "ToolPlugin 初始化失败,请重试"); + toolPluginFailureMessage: "ToolPlugin 初始化失败,请重试"); }, "选择终端安装起点"); } @@ -2217,12 +2218,12 @@ namespace NavisworksTransport.UI.WPF.ViewModels { if (_pathPlanningManager == null) { - throw new InvalidOperationException("路径规划管理器未初始化,无法重选起点。"); + throw new InvalidOperationException("路径规划管理器未初始化,无法重选起点。"); } if (_assemblyTerminalObject == null || !ModelItemAnalysisHelper.IsModelItemValid(_assemblyTerminalObject)) { - throw new InvalidOperationException("终点箱体未设置或已失效,请先捕获终点箱体。"); + throw new InvalidOperationException("终点箱体未设置或已失效,请先捕获终点箱体。"); } var selectedRailRoute = GetSelectedCoreRoute(); @@ -2235,14 +2236,14 @@ namespace NavisworksTransport.UI.WPF.ViewModels RefreshSelectedRailReferenceLineVisuals(); if (!AssemblyReferencePathManager.Instance.HasReferenceLine) { - throw new InvalidOperationException("当前 Rail 路径无法生成辅助线,请先检查路径起点和终点。"); + throw new InvalidOperationException("当前 Rail 路径无法生成辅助线,请先检查路径起点和终点。"); } ClearAssemblyInstallationReferenceVisuals(); BeginAssemblyStartPointSelection( - statusMessage: "请在辅助线附近点击新的起点位置,系统会自动吸附到辅助线上并更新当前 Rail 路径。", + statusMessage: "请在辅助线附近点击新的起点位置,系统会自动吸附到辅助线上并更新当前 Rail 路径。", logMessage: "[Rail构型] 已进入起点重选模式", - toolPluginFailureMessage: "ToolPlugin 初始化失败,请重试。"); + toolPluginFailureMessage: "ToolPlugin 初始化失败,请重试。"); }, "重选 Rail 路径起点"); } @@ -2268,12 +2269,12 @@ namespace NavisworksTransport.UI.WPF.ViewModels { if (_pathPlanningManager == null) { - throw new InvalidOperationException("路径规划管理器未初始化,无法分析端面。"); + throw new InvalidOperationException("路径规划管理器未初始化,无法分析端面。"); } if (_assemblyTerminalObject == null || !ModelItemAnalysisHelper.IsModelItemValid(_assemblyTerminalObject)) { - throw new InvalidOperationException("终点箱体未设置或已失效,请先捕获终点箱体。"); + throw new InvalidOperationException("终点箱体未设置或已失效,请先捕获终点箱体。"); } PrepareRailAssemblyWorkflowForMode( @@ -2291,12 +2292,12 @@ namespace NavisworksTransport.UI.WPF.ViewModels { if (_pathPlanningManager == null) { - throw new InvalidOperationException("路径规划管理器未初始化,无法选择安装点。"); + throw new InvalidOperationException("路径规划管理器未初始化,无法选择安装点。"); } if (_assemblyTerminalObject == null || !ModelItemAnalysisHelper.IsModelItemValid(_assemblyTerminalObject)) { - throw new InvalidOperationException("终点箱体未设置或已失效,请先捕获终点箱体。"); + throw new InvalidOperationException("终点箱体未设置或已失效,请先捕获终点箱体。"); } PrepareRailAssemblyWorkflowForMode( @@ -2321,7 +2322,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels _assemblyStartPoint = projectedStartPoint; AssemblyStartPointText = $"({projectedStartPoint.X:F2}, {projectedStartPoint.Y:F2}, {projectedStartPoint.Z:F2})"; LogManager.Info( - $"[直线装配] 点击点=({pickResult.Point.X:F2}, {pickResult.Point.Y:F2}, {pickResult.Point.Z:F2})," + + $"[直线装配] 点击点=({pickResult.Point.X:F2}, {pickResult.Point.Y:F2}, {pickResult.Point.Z:F2})," + $"投影起点=({projectedStartPoint.X:F2}, {projectedStartPoint.Y:F2}, {projectedStartPoint.Z:F2})"); await SafeExecuteAsync(() => @@ -2352,7 +2353,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels { if (!IsPickOnAssemblyTerminalObject(pickResult)) { - UpdateMainStatus("请点击当前终点箱体的同一个端面,不要点到其他对象。"); + UpdateMainStatus("请点击当前终点箱体的同一个端面,不要点到其他对象。"); return; } @@ -2364,7 +2365,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels if (pickedCount < 3) { - UpdateMainStatus($"已记录端面点 {pickedCount}/3,请继续在同一端面平面上点击。"); + UpdateMainStatus($"已记录端面点 {pickedCount}/3,请继续在同一端面平面上点击。"); return; } @@ -2393,7 +2394,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels { if (!IsPickOnAssemblyTerminalObject(pickResult)) { - UpdateMainStatus("请点击当前终点箱体表面,不要点到其他对象。"); + UpdateMainStatus("请点击当前终点箱体表面,不要点到其他对象。"); return; } @@ -2405,7 +2406,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels if (pickedCount < 2) { - UpdateMainStatus("已记录安装面点 1/2,请继续在同一安装面上点击第二个点。"); + UpdateMainStatus("已记录安装面点 1/2,请继续在同一安装面上点击第二个点。"); return; } @@ -2497,11 +2498,11 @@ namespace NavisworksTransport.UI.WPF.ViewModels if (!ForceReinitializeToolPlugin(subscribeToEvents: false)) { CleanupAssemblyEndFaceSelection(clearVisuals: false); - throw new InvalidOperationException("ToolPlugin 初始化失败,请重试。"); + throw new InvalidOperationException("ToolPlugin 初始化失败,请重试。"); } - UpdateMainStatus("请在同一个端面平面上连续点击 3 个点,系统将分析端面中心。"); - LogManager.Info($"[直线装配] 已进入端面三点分析模式,模式={GetRailAssemblyWorkflowLabel(workflowMode)}"); + UpdateMainStatus("请在同一个端面平面上连续点击 3 个点,系统将分析端面中心。"); + LogManager.Info($"[直线装配] 已进入端面三点分析模式,模式={GetRailAssemblyWorkflowLabel(workflowMode)}"); } private void BeginAssemblyInstallationSelection() @@ -2521,12 +2522,12 @@ namespace NavisworksTransport.UI.WPF.ViewModels if (!ForceReinitializeToolPlugin(subscribeToEvents: false)) { CleanupAssemblyInstallationSelection(); - throw new InvalidOperationException("ToolPlugin 初始化失败,请重试。"); + throw new InvalidOperationException("ToolPlugin 初始化失败,请重试。"); } _assemblyInstallationSeedPoints.Clear(); - UpdateMainStatus("请在终点箱体表面连续点击两个安装面点,系统将按两点和光轴计算安装参考面与安装点。"); - LogManager.Info($"[直线装配] 已进入安装点拾取模式,模式={GetRailAssemblyWorkflowLabel(_railAssemblyWorkflowMode)}"); + UpdateMainStatus("请在终点箱体表面连续点击两个安装面点,系统将按两点和光轴计算安装参考面与安装点。"); + LogManager.Info($"[直线装配] 已进入安装点拾取模式,模式={GetRailAssemblyWorkflowLabel(_railAssemblyWorkflowMode)}"); } private void ApplyAssemblyStartPointForCurrentWorkflow(Point3D projectedStartPoint) @@ -2554,7 +2555,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels return; } - LogManager.Info("[直线装配] 安装点已确定,请继续选择起点以生成新路径"); + LogManager.Info("[直线装配] 安装点已确定,请继续选择起点以生成新路径"); } private void CreateAssemblyLinearRoute(Point3D startPoint) @@ -2608,7 +2609,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels logMessage: "[直线装配] 路径生成完成后已隐藏辅助线可视化"); AssemblyTerminalObjectInfo = string.Format( - "对接终点=({0:F2}, {1:F2}, {2:F2}),装配起点={3},安装方式={4},对接基准={5}", + "对接终点=({0:F2}, {1:F2}, {2:F2}),装配起点={3},安装方式={4},对接基准={5}", endPoint.X, endPoint.Y, endPoint.Z, @@ -2618,8 +2619,8 @@ namespace NavisworksTransport.UI.WPF.ViewModels OnPropertyChanged(nameof(PathRoutes)); LogManager.Info( - $"[直线装配] 已生成并完成路径: {route.Name},起点=({startPoint.X:F2}, {startPoint.Y:F2}, {startPoint.Z:F2}),终点=({endPoint.X:F2}, {endPoint.Y:F2}, {endPoint.Z:F2})" + - $"{(route.RailPreferredNormal != null ? $",安装法向=({route.RailPreferredNormal.X:F3}, {route.RailPreferredNormal.Y:F3}, {route.RailPreferredNormal.Z:F3})" : string.Empty)}"); + $"[直线装配] 已生成并完成路径: {route.Name},起点=({startPoint.X:F2}, {startPoint.Y:F2}, {startPoint.Z:F2}),终点=({endPoint.X:F2}, {endPoint.Y:F2}, {endPoint.Z:F2})" + + $"{(route.RailPreferredNormal != null ? $",安装法向=({route.RailPreferredNormal.X:F3}, {route.RailPreferredNormal.Y:F3}, {route.RailPreferredNormal.Z:F3})" : string.Empty)}"); } private void UpdateSelectedRailRouteStartPoint(Point3D startPoint) @@ -2728,7 +2729,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels { if (_assemblyTerminalObject == null || !ModelItemAnalysisHelper.IsModelItemValid(_assemblyTerminalObject)) { - throw new InvalidOperationException("终点箱体未设置或已失效,无法计算对接点"); + throw new InvalidOperationException("终点箱体未设置或已失效,无法计算对接点"); } if (_hasAssemblyInstallationReference && _assemblyInstallationAnchorPoint != null) @@ -2758,7 +2759,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels /// /// 根据 Rail 路径终点和路径长度计算起点位置。 - /// 起点位于从终点沿光轴方向(远离球心方向)延伸路径长度的位置。 + /// 起点位于从终点沿光轴方向(远离球心方向)延伸路径长度的位置。 /// private Point3D CalculateRailStartPointFromEndPoint(Point3D endPoint, double pathLength) { @@ -2769,7 +2770,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels Point3D canonicalEndPoint = adapter.ToCanonicalPoint(endPoint); Point3D canonicalOpticalAxisRef = adapter.ToCanonicalPoint(opticalAxisReferencePoint); - // 计算从球心到光轴参考点的方向(作为参考线方向) + // 计算从球心到光轴参考点的方向(作为参考线方向) Vector3D direction = new Vector3D( canonicalOpticalAxisRef.X - projectFrame.SphereCenterInCanonical.X, canonicalOpticalAxisRef.Y - projectFrame.SphereCenterInCanonical.Y, @@ -2850,7 +2851,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels if (_assemblyTerminalObject == null || !ModelItemAnalysisHelper.IsModelItemValid(_assemblyTerminalObject)) { - throw new InvalidOperationException("终点箱体未设置或已失效,无法计算光轴参考点"); + throw new InvalidOperationException("终点箱体未设置或已失效,无法计算光轴参考点"); } return _assemblyTerminalObject.BoundingBox().Center; @@ -2860,7 +2861,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels { if (_assemblyTerminalObject == null || !ModelItemAnalysisHelper.IsModelItemValid(_assemblyTerminalObject)) { - throw new InvalidOperationException("终点箱体未设置或已失效,无法生成装配参考线"); + throw new InvalidOperationException("终点箱体未设置或已失效,无法生成装配参考线"); } HostCoordinateAdapter adapter = CoordinateSystemManager.Instance.CreateHostAdapter(); @@ -2878,7 +2879,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels double directionLengthSquared = direction.X * direction.X + direction.Y * direction.Y + direction.Z * direction.Z; if (directionLengthSquared < 1e-9) { - throw new InvalidOperationException("光轴参考点与项目球心重合,无法生成装配参考线方向"); + throw new InvalidOperationException("光轴参考点与项目球心重合,无法生成装配参考线方向"); } direction = direction.Normalize(); @@ -2890,7 +2891,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels Point3D startPoint = adapter.FromCanonicalPoint(canonicalStartPoint); LogManager.Info( - $"[直线装配] 参考线已计算,终点锚点=({endPoint.X:F2}, {endPoint.Y:F2}, {endPoint.Z:F2}), " + + $"[直线装配] 参考线已计算,终点锚点=({endPoint.X:F2}, {endPoint.Y:F2}, {endPoint.Z:F2}), " + $"光轴参考点=({opticalAxisReferencePoint.X:F2}, {opticalAxisReferencePoint.Y:F2}, {opticalAxisReferencePoint.Z:F2}), " + $"参考线外端=({startPoint.X:F2}, {startPoint.Y:F2}, {startPoint.Z:F2}), " + $"球心到光轴参考点方向(内部坐标)=({direction.X:F3}, {direction.Y:F3}, {direction.Z:F3})"); @@ -2921,7 +2922,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels : string.Format("{0}点=未选择", anchorText); string installationText = _hasAssemblyInstallationReference ? string.Format( - "安装面点中点=({0:F2}, {1:F2}, {2:F2}),安装点=({3:F2}, {4:F2}, {5:F2}),偏距={6:F3}m", + "安装面点中点=({0:F2}, {1:F2}, {2:F2}),安装点=({3:F2}, {4:F2}, {5:F2}),偏距={6:F3}m", _assemblyInstallationPickPoint.X, _assemblyInstallationPickPoint.Y, _assemblyInstallationPickPoint.Z, @@ -2934,7 +2935,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels if (referenceStartPoint != null && referenceEndPoint != null) { AssemblyTerminalObjectInfo = string.Format( - "中心=({0:F2}, {1:F2}, {2:F2}),尺寸=({3:F2}, {4:F2}, {5:F2}),{6},{7},{8},{9},垂直偏移={10:F3}m,辅助线外端=({11:F2}, {12:F2}, {13:F2})", + "中心=({0:F2}, {1:F2}, {2:F2}),尺寸=({3:F2}, {4:F2}, {5:F2}),{6},{7},{8},{9},垂直偏移={10:F3}m,辅助线外端=({11:F2}, {12:F2}, {13:F2})", bounds.Center.X, bounds.Center.Y, bounds.Center.Z, @@ -2953,7 +2954,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels } AssemblyTerminalObjectInfo = string.Format( - "中心=({0:F2}, {1:F2}, {2:F2}),尺寸=({3:F2}, {4:F2}, {5:F2}),{6},{7},{8},{9},垂直偏移={10:F3}m", + "中心=({0:F2}, {1:F2}, {2:F2}),尺寸=({3:F2}, {4:F2}, {5:F2}),{6},{7},{8},{9},垂直偏移={10:F3}m", bounds.Center.X, bounds.Center.Y, bounds.Center.Z, @@ -3255,7 +3256,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels RefreshAssemblyTerminalObjectInfo(); RefreshAssemblyReferenceRodIfNeeded(); UpdateMainStatus( - $"安装参考已计算:安装点=({_assemblyInstallationAnchorPoint.X:F2}, {_assemblyInstallationAnchorPoint.Y:F2}, {_assemblyInstallationAnchorPoint.Z:F2}),偏距={_assemblyInstallationOffsetDistanceInMeters:F3}m,可继续取起点"); + $"安装参考已计算:安装点=({_assemblyInstallationAnchorPoint.X:F2}, {_assemblyInstallationAnchorPoint.Y:F2}, {_assemblyInstallationAnchorPoint.Z:F2}),偏距={_assemblyInstallationOffsetDistanceInMeters:F3}m,可继续取起点"); LogManager.Info( $"[直线装配] 安装参考已计算: 安装面点1=({firstPickPoint.X:F3}, {firstPickPoint.Y:F3}, {firstPickPoint.Z:F3}), " + $"安装面点2=({secondPickPoint.X:F3}, {secondPickPoint.Y:F3}, {secondPickPoint.Z:F3}), " + @@ -3603,7 +3604,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels RenderAssemblyEndFaceNormal(centerPoint, orientedNormal); RefreshAssemblyTerminalObjectInfo(); RefreshAssemblyReferenceRodIfNeeded(); - UpdateMainStatus($"端面分析完成:中心=({centerPoint.X:F2}, {centerPoint.Y:F2}, {centerPoint.Z:F2}),候选三角形={result.CandidateTriangleCount}"); + UpdateMainStatus($"端面分析完成:中心=({centerPoint.X:F2}, {centerPoint.Y:F2}, {centerPoint.Z:F2}),候选三角形={result.CandidateTriangleCount}"); LogManager.Info( $"[直线装配] 端面分析完成: 中心=({centerPoint.X:F3}, {centerPoint.Y:F3}, {centerPoint.Z:F3}), " + $"法向=({orientedNormal.X:F4}, {orientedNormal.Y:F4}, {orientedNormal.Z:F4}), " + @@ -3622,12 +3623,12 @@ namespace NavisworksTransport.UI.WPF.ViewModels try { PathPointRenderPlugin.Instance.ClearPathsExcept("grid_visualization_all", "grid_visualization_channel", "grid_visualization_unknown", "grid_visualization_obstacle", "grid_visualization_door"); - LogManager.Info("新建路径:已清除现有路径可视化显示(保留网格可视化)"); + LogManager.Info("新建路径:已清除现有路径可视化显示(保留网格可视化)"); } catch (Exception ex) { - LogManager.Error($"新建路径:清除现有路径可视化失败: {ex.Message}", ex); - throw; // 重新抛出异常,让上层处理 + LogManager.Error($"新建路径:清除现有路径可视化失败: {ex.Message}", ex); + throw; // 重新抛出异常,让上层处理 } } @@ -3655,24 +3656,24 @@ namespace NavisworksTransport.UI.WPF.ViewModels PathRoutes.Add(newPathViewModel); SelectedPathRoute = newPathViewModel; - // 强制重新初始化ToolPlugin以确保获得鼠标焦点,新建路径需要订阅事件 + // 强制重新初始化ToolPlugin以确保获得鼠标焦点,新建路径需要订阅事件 if (!ForceReinitializeToolPlugin(subscribeToEvents: true)) { - UpdateMainStatus("ToolPlugin初始化失败,请重试"); - LogManager.Error("新建路径:ToolPlugin初始化失败"); + UpdateMainStatus("ToolPlugin初始化失败,请重试"); + LogManager.Error("新建路径:ToolPlugin初始化失败"); return; } UpdateMainStatus($"已进入新建路径模式: {newRoute.Name} - 请在3D视图中点击设置路径点"); - LogManager.Info($"开始新建路径: {newRoute.Name},已强制重新初始化ToolPlugin获取鼠标焦点"); + LogManager.Info($"开始新建路径: {newRoute.Name},已强制重新初始化ToolPlugin获取鼠标焦点"); - // 不再显示对话框,状态文本已提供足够的反馈 + // 不再显示对话框,状态文本已提供足够的反馈 } else { - UpdateMainStatus("创建新路径失败:没有可通行的物流模型"); - LogManager.Error("创建新路径失败:没有可通行的物流模型"); - MessageBox.Show("创建新路径失败:没有找到任何可通行的物流模型。\n请先为模型设置可通行的物流属性,然后再尝试创建路径。", "错误", + UpdateMainStatus("创建新路径失败:没有可通行的物流模型"); + LogManager.Error("创建新路径失败:没有可通行的物流模型"); + MessageBox.Show("创建新路径失败:没有找到任何可通行的物流模型。\n请先为模型设置可通行的物流属性,然后再尝试创建路径。", "错误", MessageBoxButton.OK, MessageBoxImage.Warning); } } @@ -3697,11 +3698,11 @@ namespace NavisworksTransport.UI.WPF.ViewModels { // 清除所有现有路径的可视化显示 PathPointRenderPlugin.Instance.ClearPathsExcept("grid_visualization_all", "grid_visualization_channel", "grid_visualization_unknown", "grid_visualization_obstacle", "grid_visualization_door"); - LogManager.Info("新建传统空轨路径:已清除现有路径可视化显示(保留网格可视化)"); + LogManager.Info("新建传统空轨路径:已清除现有路径可视化显示(保留网格可视化)"); } catch (Exception ex) { - LogManager.Error($"新建传统空轨路径:清除现有路径可视化失败: {ex.Message}", ex); + LogManager.Error($"新建传统空轨路径:清除现有路径可视化失败: {ex.Message}", ex); throw; } } @@ -3713,7 +3714,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels { // 创建传统空轨路径 var newRoute = _pathPlanningManager.StartCreatingNewRoute( - isRailPath: true, + isRailPath: true, pathType: PathType.Rail); if (newRoute != null) @@ -3740,23 +3741,23 @@ namespace NavisworksTransport.UI.WPF.ViewModels // 强制重新初始化ToolPlugin以确保获得鼠标焦点 if (!ForceReinitializeToolPlugin(subscribeToEvents: true)) { - UpdateMainStatus("ToolPlugin初始化失败,请重试"); - LogManager.Error("新建传统空轨路径:ToolPlugin初始化失败"); + UpdateMainStatus("ToolPlugin初始化失败,请重试"); + LogManager.Error("新建传统空轨路径:ToolPlugin初始化失败"); return; } - // 启动点击工具,设置空轨吸附模式 + // 启动点击工具,设置空轨吸附模式 _pathPlanningManager.StartClickTool(PathPointType.WayPoint, enableRailSnapping: true); - LogManager.Info($"已启动传统空轨路径点击工具(吸附模式): {newRoute.Name}"); + LogManager.Info($"已启动传统空轨路径点击工具(吸附模式): {newRoute.Name}"); - UpdateMainStatus($"已进入传统空轨路径模式: {newRoute.Name} - 请在3D视图中点击空轨模型设置路径点(将自动吸附到基准路径)"); - LogManager.Info($"开始新建传统空轨路径: {newRoute.Name},已强制重新初始化ToolPlugin获取鼠标焦点"); + UpdateMainStatus($"已进入传统空轨路径模式: {newRoute.Name} - 请在3D视图中点击空轨模型设置路径点(将自动吸附到基准路径)"); + LogManager.Info($"开始新建传统空轨路径: {newRoute.Name},已强制重新初始化ToolPlugin获取鼠标焦点"); } else { - UpdateMainStatus("创建传统空轨路径失败:没有可通行的物流模型"); - LogManager.Error("创建传统空轨路径失败:没有可通行的物流模型"); - MessageBox.Show("创建传统空轨路径失败:没有找到任何可通行的物流模型。\n请先为模型设置可通行的物流属性,然后再尝试创建路径。", "错误", + UpdateMainStatus("创建传统空轨路径失败:没有可通行的物流模型"); + LogManager.Error("创建传统空轨路径失败:没有可通行的物流模型"); + MessageBox.Show("创建传统空轨路径失败:没有找到任何可通行的物流模型。\n请先为模型设置可通行的物流属性,然后再尝试创建路径。", "错误", MessageBoxButton.OK, MessageBoxImage.Warning); } } @@ -3767,7 +3768,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels } }, "新建传统空轨路径"); } - + /// /// 执行新建吊装路径命令 /// @@ -3776,29 +3777,29 @@ namespace NavisworksTransport.UI.WPF.ViewModels 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("新建吊装路径:已清除现有路径可视化显示(保留网格可视化)"); + LogManager.Info("新建吊装路径:已清除现有路径可视化显示(保留网格可视化)"); } catch (Exception ex) { - LogManager.Error($"新建吊装路径:清除现有路径可视化失败: {ex.Message}", ex); + LogManager.Error($"新建吊装路径:清除现有路径可视化失败: {ex.Message}", ex); throw; } } - + if (_pathPlanningManager != null) { // 创建吊装路径 var newRoute = _pathPlanningManager.StartCreatingNewRoute( - isRailPath: false, + isRailPath: false, pathType: PathType.Hoisting); - + if (newRoute != null) { // 创建对应的 WPF ViewModel @@ -3815,31 +3816,31 @@ namespace NavisworksTransport.UI.WPF.ViewModels { newPathViewModel.Points.Add(point); } - + // 添加到 UI 列表并选中 PathRoutes.Add(newPathViewModel); SelectedPathRoute = newPathViewModel; - + // 强制重新初始化ToolPlugin以确保获得鼠标焦点 if (!ForceReinitializeToolPlugin(subscribeToEvents: true)) { - UpdateMainStatus("ToolPlugin初始化失败,请重试"); - LogManager.Error("新建吊装路径:ToolPlugin初始化失败"); + UpdateMainStatus("ToolPlugin初始化失败,请重试"); + LogManager.Error("新建吊装路径:ToolPlugin初始化失败"); return; } - - // 启动点击工具,启用吊装模式 + + // 启动点击工具,启用吊装模式 _pathPlanningManager.StartClickTool(PathPointType.WayPoint, enableRailSnapping: false, enableHoistingMode: true); LogManager.Info($"已启动吊装路径点击工具: {newRoute.Name}"); - - UpdateMainStatus($"已进入新建吊装路径模式: {newRoute.Name} - 请在3D视图中点击吊装起点(地面位置)"); - LogManager.Info($"开始新建吊装路径: {newRoute.Name},已强制重新初始化ToolPlugin获取鼠标焦点"); + + UpdateMainStatus($"已进入新建吊装路径模式: {newRoute.Name} - 请在3D视图中点击吊装起点(地面位置)"); + LogManager.Info($"开始新建吊装路径: {newRoute.Name},已强制重新初始化ToolPlugin获取鼠标焦点"); } else { - UpdateMainStatus("创建新吊装路径失败:没有可通行的物流模型"); - LogManager.Error("创建新吊装路径失败:没有可通行的物流模型"); - MessageBox.Show("创建新吊装路径失败:没有找到任何可通行的物流模型。\n请先为模型设置可通行的物流属性,然后再尝试创建路径。", "错误", + UpdateMainStatus("创建新吊装路径失败:没有可通行的物流模型"); + LogManager.Error("创建新吊装路径失败:没有可通行的物流模型"); + MessageBox.Show("创建新吊装路径失败:没有找到任何可通行的物流模型。\n请先为模型设置可通行的物流属性,然后再尝试创建路径。", "错误", MessageBoxButton.OK, MessageBoxImage.Warning); } } @@ -3868,11 +3869,11 @@ namespace NavisworksTransport.UI.WPF.ViewModels try { PathPointRenderPlugin.Instance.ClearPathsExcept("grid_visualization_all", "grid_visualization_channel", "grid_visualization_unknown", "grid_visualization_obstacle", "grid_visualization_door"); - LogManager.Info("新建多层吊装路径:已清除现有路径可视化显示(保留网格可视化)"); + LogManager.Info("新建多层吊装路径:已清除现有路径可视化显示(保留网格可视化)"); } catch (Exception ex) { - LogManager.Error($"新建多层吊装路径:清除现有路径可视化失败: {ex.Message}", ex); + LogManager.Error($"新建多层吊装路径:清除现有路径可视化失败: {ex.Message}", ex); throw; } } @@ -3886,7 +3887,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels // 启用多层吊装页签模式 IsMultiLevelHoistingMode = true; - + // 清空集合和重置状态 MultiLevelItems.Clear(); _multiLevelStartPoint = null; @@ -3896,15 +3897,15 @@ namespace NavisworksTransport.UI.WPF.ViewModels MultiLevelStartPointText = "未选择"; MultiLevelEndPointText = "未选择"; UpdateMultiLevelStats(); - + // 重置选择状态 _isSelectingMultiLevelStartPoint = false; _isSelectingMultiLevelEndPoint = false; _currentSelectingLevelTarget = null; - + // 清理之前的点击事件订阅 PathClickToolPlugin.MouseClicked -= OnMultiLevelHoistingMouseClicked; - + UpdateMainStatus("多层吊装模式已启用 - 请设置起点、终点并添加层级"); LogManager.Info("[多层吊装] 页签模式已启用"); @@ -3932,20 +3933,20 @@ namespace NavisworksTransport.UI.WPF.ViewModels // 使用自动路径规划相同的模式 _pathPlanningManager.DisableMouseHandling(); - + // 设置状态 _isSelectingMultiLevelStartPoint = true; _isSelectingMultiLevelEndPoint = false; _currentSelectingLevelTarget = null; - + // 订阅点击事件 PathClickToolPlugin.MouseClicked -= OnMultiLevelHoistingMouseClicked; PathClickToolPlugin.MouseClicked += OnMultiLevelHoistingMouseClicked; - + // 强制重新初始化ToolPlugin以获取鼠标焦点 ForceReinitializeToolPlugin(subscribeToEvents: false); - - UpdateMainStatus("请在3D视图中点击选择起点(地面起吊位置)..."); + + UpdateMainStatus("请在3D视图中点击选择起点(地面起吊位置)..."); LogManager.Info("[多层吊装] 开始选择起点"); }, "选择多层吊装起点"); } @@ -3971,20 +3972,20 @@ namespace NavisworksTransport.UI.WPF.ViewModels // 使用自动路径规划相同的模式 _pathPlanningManager.DisableMouseHandling(); - + // 设置状态 _isSelectingMultiLevelStartPoint = false; _isSelectingMultiLevelEndPoint = true; _currentSelectingLevelTarget = null; - + // 订阅点击事件 PathClickToolPlugin.MouseClicked -= OnMultiLevelHoistingMouseClicked; PathClickToolPlugin.MouseClicked += OnMultiLevelHoistingMouseClicked; - + // 强制重新初始化ToolPlugin以获取鼠标焦点 ForceReinitializeToolPlugin(subscribeToEvents: false); - - UpdateMainStatus("请在3D视图中点击选择终点(地面落地位置)..."); + + UpdateMainStatus("请在3D视图中点击选择终点(地面落地位置)..."); LogManager.Info("[多层吊装] 开始选择终点"); }, "选择多层吊装终点"); } @@ -4002,9 +4003,9 @@ namespace NavisworksTransport.UI.WPF.ViewModels }; MultiLevelItems.Add(level); UpdateMultiLevelStats(); - + LogManager.Info($"[多层吊装] 添加层级{level.Index + 1}, 高度={level.HeightInMeters}m"); - UpdateMainStatus($"已添加层级{level.Index + 1},请设置目标位置"); + UpdateMainStatus($"已添加层级{level.Index + 1},请设置目标位置"); } /// @@ -4013,7 +4014,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels private void ExecuteDeleteMultiLevel(HoistingLevelItem item) { if (item == null) return; - + // 清除该层级的可视化标记 if (PathPointRenderPlugin.Instance != null) { @@ -4023,17 +4024,17 @@ namespace NavisworksTransport.UI.WPF.ViewModels PathPointRenderPlugin.Instance.RemovePath(existingRoute.Route.Id); } } - + MultiLevelItems.Remove(item); - + // 重新编号 for (int i = 0; i < MultiLevelItems.Count; i++) { MultiLevelItems[i].Index = i; } - + UpdateMultiLevelStats(); - LogManager.Info($"[多层吊装] 删除层级,剩余{MultiLevelItems.Count}个层级"); + LogManager.Info($"[多层吊装] 删除层级,剩余{MultiLevelItems.Count}个层级"); } /// @@ -4042,7 +4043,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels private void ExecutePickMultiLevelTarget(HoistingLevelItem item) { if (item == null || _pathPlanningManager == null) return; - + // 清除之前的标记 if (PathPointRenderPlugin.Instance != null) { @@ -4052,22 +4053,22 @@ namespace NavisworksTransport.UI.WPF.ViewModels PathPointRenderPlugin.Instance.RemovePath(existingRoute.Route.Id); } } - + // 使用自动路径规划相同的模式 _pathPlanningManager.DisableMouseHandling(); - + // 设置状态 _isSelectingMultiLevelStartPoint = false; _isSelectingMultiLevelEndPoint = false; _currentSelectingLevelTarget = item; - + // 订阅点击事件 PathClickToolPlugin.MouseClicked -= OnMultiLevelHoistingMouseClicked; PathClickToolPlugin.MouseClicked += OnMultiLevelHoistingMouseClicked; - + // 强制重新初始化ToolPlugin以获取鼠标焦点 ForceReinitializeToolPlugin(subscribeToEvents: false); - + UpdateMainStatus($"请在3D视图中点击层级{item.Index + 1}的目标位置..."); LogManager.Info($"[多层吊装] 开始选择层级{item.Index + 1}的目标位置"); } @@ -4107,7 +4108,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels MessageBoxButton.OK, MessageBoxImage.Warning); return; } - + if (level.HorizontalTarget == null) { MessageBox.Show($"请为层级{level.Index + 1}设置目标位置", "验证失败", @@ -4130,7 +4131,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels }); } - LogManager.Info($"[多层吊装] 创建路径:起点({_multiLevelStartPoint.X:F2},{_multiLevelStartPoint.Y:F2},{_multiLevelStartPoint.Z:F2}), " + + LogManager.Info($"[多层吊装] 创建路径:起点({_multiLevelStartPoint.X:F2},{_multiLevelStartPoint.Y:F2},{_multiLevelStartPoint.Z:F2}), " + $"终点({_multiLevelEndPoint.X:F2},{_multiLevelEndPoint.Y:F2},{_multiLevelEndPoint.Z:F2}), 层级数={levels.Count}"); // 使用命令创建路径 @@ -4170,7 +4171,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels UpdateMainStatus($"✓ 多层吊装路径创建成功: {route.Name} - {levels.Count}个层级, {route.Points.Count}个路径点"); OnPropertyChanged(nameof(PathRoutes)); - + // 退出多层吊装模式 ExecuteCancelMultiLevelHoisting(); } @@ -4189,7 +4190,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels { // 清理事件订阅 PathClickToolPlugin.MouseClicked -= OnMultiLevelHoistingMouseClicked; - + // 清理可视化标记 if (PathPointRenderPlugin.Instance != null) { @@ -4199,14 +4200,14 @@ namespace NavisworksTransport.UI.WPF.ViewModels { PathPointRenderPlugin.Instance.RemovePath(startRoute.Route.Id); } - + // 清理终点标记 var endRoute = PathRoutes.FirstOrDefault(r => r.Route.Name == "MultiLevelEndPoint"); if (endRoute != null) { PathPointRenderPlugin.Instance.RemovePath(endRoute.Route.Id); } - + // 清理层级标记 for (int i = 0; i < MultiLevelItems.Count; i++) { @@ -4217,7 +4218,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels } } } - + // 重置状态 IsMultiLevelHoistingMode = false; MultiLevelItems.Clear(); @@ -4230,11 +4231,11 @@ namespace NavisworksTransport.UI.WPF.ViewModels _isSelectingMultiLevelStartPoint = false; _isSelectingMultiLevelEndPoint = false; _currentSelectingLevelTarget = null; - + // 恢复PathPlanningManager鼠标处理 _pathPlanningManager?.EnableMouseHandling(); _pathPlanningManager?.StopClickTool(); - + UpdateMultiLevelStats(); UpdateMainStatus("多层吊装模式已取消"); LogManager.Info("[多层吊装] 页签模式已取消"); @@ -4248,10 +4249,10 @@ namespace NavisworksTransport.UI.WPF.ViewModels int levelCount = MultiLevelItems.Count; int pointCount = levelCount > 0 ? 2 + levelCount * 2 + 2 : 0; MultiLevelStatsText = $"层级数: {levelCount} | 预估路径点数: {pointCount}"; - + // 通知CanCreateMultiLevelPath变更 OnPropertyChanged(nameof(CanCreateMultiLevelPath)); - + // 通知命令可执行状态变更 System.Windows.Input.CommandManager.InvalidateRequerySuggested(); } @@ -4264,26 +4265,26 @@ namespace NavisworksTransport.UI.WPF.ViewModels if (e?.Point == null) return; var point = e.Point; - + if (_isSelectingMultiLevelStartPoint) { // 设置起点 _multiLevelStartPoint = point; _hasMultiLevelStartPoint = true; MultiLevelStartPointText = $"({point.X:F2}, {point.Y:F2}, {point.Z:F2})"; - + // 渲染起点标记 RenderMultiLevelPointMarker(point, isStart: true); - + LogManager.Info($"[多层吊装] 起点已设置: ({point.X:F2}, {point.Y:F2}, {point.Z:F2})"); - UpdateMainStatus($"起点已设置,请选择终点或添加层级"); - + UpdateMainStatus($"起点已设置,请选择终点或添加层级"); + // 清理事件订阅并停用ToolPlugin PathClickToolPlugin.MouseClicked -= OnMultiLevelHoistingMouseClicked; _pathPlanningManager?.EnableMouseHandling(); _pathPlanningManager?.StopClickTool(); _isSelectingMultiLevelStartPoint = false; - + // 更新统计 UpdateMultiLevelStats(); } @@ -4293,19 +4294,19 @@ namespace NavisworksTransport.UI.WPF.ViewModels _multiLevelEndPoint = point; _hasMultiLevelEndPoint = true; MultiLevelEndPointText = $"({point.X:F2}, {point.Y:F2}, {point.Z:F2})"; - + // 渲染终点标记 RenderMultiLevelPointMarker(point, isStart: false); - + LogManager.Info($"[多层吊装] 终点已设置: ({point.X:F2}, {point.Y:F2}, {point.Z:F2})"); - UpdateMainStatus($"终点已设置,可以添加层级创建路径"); - + UpdateMainStatus($"终点已设置,可以添加层级创建路径"); + // 清理事件订阅并停用ToolPlugin PathClickToolPlugin.MouseClicked -= OnMultiLevelHoistingMouseClicked; _pathPlanningManager?.EnableMouseHandling(); _pathPlanningManager?.StopClickTool(); _isSelectingMultiLevelEndPoint = false; - + // 更新统计 UpdateMultiLevelStats(); } @@ -4314,13 +4315,13 @@ namespace NavisworksTransport.UI.WPF.ViewModels // 设置层级目标位置 var targetPoint = new Point3D(point.X, point.Y, point.Z); _currentSelectingLevelTarget.HorizontalTarget = targetPoint; - + // 渲染层级目标标记 RenderMultiLevelLevelMarker(targetPoint, _currentSelectingLevelTarget.Index); - + LogManager.Info($"[多层吊装] 层级{_currentSelectingLevelTarget.Index + 1}目标位置已设置: ({point.X:F2}, {point.Y:F2})"); - UpdateMainStatus($"层级{_currentSelectingLevelTarget.Index + 1}目标位置已设置,可以继续添加层级或点击创建路径"); - + UpdateMainStatus($"层级{_currentSelectingLevelTarget.Index + 1}目标位置已设置,可以继续添加层级或点击创建路径"); + // 清理事件订阅并停用ToolPlugin PathClickToolPlugin.MouseClicked -= OnMultiLevelHoistingMouseClicked; _pathPlanningManager?.EnableMouseHandling(); @@ -4337,7 +4338,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels if (PathPointRenderPlugin.Instance == null) return; var routeName = $"MultiLevelLevel{levelIndex}"; - + // 移除之前的标记 var existingRoute = PathRoutes.FirstOrDefault(r => r.Route.Name == routeName); if (existingRoute != null) @@ -4358,7 +4359,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels }; route.AddPoint(pathPoint); - // 渲染点(使用不同颜色区分层级) + // 渲染点(使用不同颜色区分层级) PathPointRenderPlugin.Instance.RenderPointOnly(route); LogManager.Info($"[多层吊装] 层级{levelIndex + 1}目标标记已渲染"); } @@ -4373,7 +4374,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels var routeName = isStart ? "MultiLevelStartPoint" : "MultiLevelEndPoint"; var displayName = isStart ? "多层吊装起点" : "多层吊装终点"; var pointType = isStart ? PathPointType.StartPoint : PathPointType.EndPoint; - + // 移除之前的标记 var existingRoute = PathRoutes.FirstOrDefault(r => r.Route.Name == routeName); if (existingRoute != null) @@ -4409,7 +4410,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels { LogManager.Info("[空轨路径] 开始提取所有空轨模型的基准路径"); int successCount = PathPlanning.RailGeometryHelper.ExtractAndRenderAllRailBaselines(); - LogManager.Info($"[空轨路径] 空轨基准路径提取完成,成功 {successCount} 个"); + LogManager.Info($"[空轨路径] 空轨基准路径提取完成,成功 {successCount} 个"); } catch (Exception ex) { @@ -4426,12 +4427,12 @@ namespace NavisworksTransport.UI.WPF.ViewModels var pathName = SelectedPathRoute.Name; UpdateMainStatus($"正在删除路径: {pathName}..."); - // 清理当前路径的3D可视化显示(使用新的统一API) + // 清理当前路径的3D可视化显示(使用新的统一API) if (PathPointRenderPlugin.Instance != null) { try { - // 使用路径名找到对应的Core路径,获取其ID进行精确清理 + // 使用路径名找到对应的Core路径,获取其ID进行精确清理 string pathIdToRemove = null; if (_pathPlanningManager != null) { @@ -4439,40 +4440,40 @@ namespace NavisworksTransport.UI.WPF.ViewModels if (coreRoute != null) { pathIdToRemove = coreRoute.Id; - LogManager.Info($"删除路径:找到Core路径ID: {pathIdToRemove}"); + LogManager.Info($"删除路径:找到Core路径ID: {pathIdToRemove}"); // 使用新的API精确删除该路径 PathPointRenderPlugin.Instance.RemovePath(pathIdToRemove); - LogManager.Info($"删除路径:已清除路径 {pathName} (ID: {pathIdToRemove}) 的可视化显示"); + LogManager.Info($"删除路径:已清除路径 {pathName} (ID: {pathIdToRemove}) 的可视化显示"); } else { - LogManager.Warning($"删除路径:未找到路径 {pathName} 对应的Core路径,无法清理可视化"); + LogManager.Warning($"删除路径:未找到路径 {pathName} 对应的Core路径,无法清理可视化"); } } } catch (Exception ex) { - LogManager.Error($"删除路径:清除路径可视化失败: {ex.Message}", ex); - throw; // 重新抛出异常,让上层处理 + LogManager.Error($"删除路径:清除路径可视化失败: {ex.Message}", ex); + throw; // 重新抛出异常,让上层处理 } } - // 通知PathPlanningManager删除对应的路径(包括数据库删除) + // 通知PathPlanningManager删除对应的路径(包括数据库删除) if (_pathPlanningManager != null) { var coreRoute = _pathPlanningManager.Routes.FirstOrDefault(r => r.Name == pathName); if (coreRoute != null) { - // 使用PathPlanningManager的DeleteRoute方法,确保数据库也被删除 + // 使用PathPlanningManager的DeleteRoute方法,确保数据库也被删除 bool deleteSuccess = _pathPlanningManager.DeleteRoute(coreRoute); if (deleteSuccess) { - LogManager.Info($"删除路径:已从PathPlanningManager和数据库删除路径: {pathName}"); + LogManager.Info($"删除路径:已从PathPlanningManager和数据库删除路径: {pathName}"); } else { - LogManager.Warning($"删除路径:PathPlanningManager删除失败: {pathName}"); + LogManager.Warning($"删除路径:PathPlanningManager删除失败: {pathName}"); } } } @@ -4540,7 +4541,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels try { // 创建并显示对话框 - // 注意:在Navisworks插件的ViewModel中,不设置Owner + // 注意:在Navisworks插件的ViewModel中,不设置Owner // 因为Application.Current.MainWindow在Navisworks环境中不可用 var dialog = new EditCoordinatesWindow(point.X, point.Y, point.Z, point.Name, point.Type.ToString()); @@ -4552,7 +4553,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels var coreRoute = _pathPlanningManager.Routes.FirstOrDefault(r => r.Id == SelectedPathRoute.Id); if (coreRoute != null) { - // 🔥 通过Id查找路径点索引(而不是使用可能过时的point.Index) + // 🔥 通过Id查找路径点索引(而不是使用可能过时的point.Index) int pointIndex = -1; for (int i = 0; i < coreRoute.Points.Count; i++) { @@ -4566,8 +4567,8 @@ namespace NavisworksTransport.UI.WPF.ViewModels // 验证索引是否有效 if (pointIndex < 0 || pointIndex >= coreRoute.Points.Count) { - LogManager.Error($"[坐标编辑] 索引错误: 通过Id查找失败,point.Id={point.Id}, coreRoute.Points.Count={coreRoute.Points.Count}"); - System.Windows.MessageBox.Show($"路径点索引错误:无法找到对应的路径点,请刷新路径列表后重试", "错误", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error); + LogManager.Error($"[坐标编辑] 索引错误: 通过Id查找失败,point.Id={point.Id}, coreRoute.Points.Count={coreRoute.Points.Count}"); + System.Windows.MessageBox.Show($"路径点索引错误:无法找到对应的路径点,请刷新路径列表后重试", "错误", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error); return; } @@ -4576,13 +4577,13 @@ namespace NavisworksTransport.UI.WPF.ViewModels var corePoint = coreRoute.Points[pointIndex]; LogManager.Info($"[坐标编辑] Core路径点: {corePoint.Name}, 索引: {pointIndex}, 位置: ({corePoint.Position.X:F3}, {corePoint.Position.Y:F3}, {corePoint.Position.Z:F3})"); - // 使用公共方法更新路径点(自动处理吊装路径约束) + // 使用公共方法更新路径点(自动处理吊装路径约束) var newPosition = new Point3D(dialog.X, dialog.Y, dialog.Z); bool success = _pathPlanningManager.UpdatePathPointWithConstraints(coreRoute, pointIndex, newPosition); if (success) { - // 更新ViewModel中的坐标(从Core数据同步) + // 更新ViewModel中的坐标(从Core数据同步) corePoint = coreRoute.Points[pointIndex]; point.X = corePoint.Position.X; point.Y = corePoint.Position.Y; @@ -4610,7 +4611,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels } /// - /// 从Core路径创建PathPointViewModel列表(统一处理吊装路径属性) + /// 从Core路径创建PathPointViewModel列表(统一处理吊装路径属性) /// private List CreatePathPointViewModelsFromCoreRoute(PathRoute coreRoute) { @@ -4715,7 +4716,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels var coreRoute = GetSelectedCoreRoute(); if (coreRoute == null || coreRoute.PathType != PathType.Hoisting || coreRoute.Points == null || coreRoute.Points.Count < 2) { - HoistingLayerEditHintText = "选择一条吊装路径后,可在这里按层编辑相对起点的绝对高度。"; + HoistingLayerEditHintText = "选择一条吊装路径后,可在这里按层编辑相对起点的绝对高度。"; OnPropertyChanged(nameof(CanApplyHoistingLayerHeights)); return; } @@ -4769,7 +4770,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels } HoistingLayerEditHintText = HoistingLayerEditItems.Count > 0 - ? "层高按高到低显示;每层高度相对起吊点,且必须严格低于上一层、高于下一层。" + ? "层高按高到低显示;每层高度相对起吊点,且必须严格低于上一层、高于下一层。" : "当前吊装路径未解析出可编辑的空中层。"; OnPropertyChanged(nameof(CanApplyHoistingLayerHeights)); } @@ -4858,7 +4859,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels private void HandleInvalidHoistingLayerHeightInput(string message) { - const string validationMessage = "每层高度必须低于上一层、高于下一层,也不能相等。"; + const string validationMessage = "每层高度必须低于上一层、高于下一层,也不能相等。"; LogManager.Warning($"[吊装层高] 用户输入无效: {message}"); RefreshHoistingLayerEditItemsFromSelectedRoute(); UpdateMainStatus(validationMessage); @@ -4866,7 +4867,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels } /// - /// 更新吊装路径的关联点(UI层) + /// 更新吊装路径的关联点(UI层) /// /// 路径 /// 修改的点的索引 @@ -4880,7 +4881,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels var points = pathRoute.Points; - // 修改起点(索引0):更新提升点(索引1)的X,Y坐标 + // 修改起点(索引0):更新提升点(索引1)的X,Y坐标 if (modifiedPointIndex == 0) { var startPoint = points[0]; @@ -4888,7 +4889,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels liftPoint.X = startPoint.X; liftPoint.Y = startPoint.Y; } - // 修改终点(索引3):更新平移终点(索引2)的X,Y坐标 + // 修改终点(索引3):更新平移终点(索引2)的X,Y坐标 else if (modifiedPointIndex == 3) { var endPoint = points[3]; @@ -4899,7 +4900,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels } /// - /// 更新吊装路径的关联点(Core层) + /// 更新吊装路径的关联点(Core层) /// /// Core路径 /// 修改的点的索引 @@ -4913,7 +4914,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels var points = coreRoute.Points; - // 修改起点(索引0):更新提升点(索引1)的X,Y坐标 + // 修改起点(索引0):更新提升点(索引1)的X,Y坐标 if (modifiedPointIndex == 0) { var startPoint = points[0]; @@ -4921,7 +4922,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels liftPoint.X = startPoint.X; liftPoint.Y = startPoint.Y; } - // 修改终点(索引3):更新平移终点(索引2)的X,Y坐标 + // 修改终点(索引3):更新平移终点(索引2)的X,Y坐标 else if (modifiedPointIndex == 3) { var endPoint = points[3]; @@ -4943,12 +4944,12 @@ namespace NavisworksTransport.UI.WPF.ViewModels if (_pathPlanningManager == null) { - LogManager.Error("PathPlanningManager为null,无法选择起点"); + LogManager.Error("PathPlanningManager为null,无法选择起点"); UpdateMainStatus("路径规划管理器未初始化"); return; } - // 立即清理现有的起点标记(使用正确的路径ID) + // 立即清理现有的起点标记(使用正确的路径ID) if (PathPointRenderPlugin.Instance != null && _autoPathStartPointRoute != null) { PathPointRenderPlugin.Instance.RemovePath(_autoPathStartPointRoute.Id); @@ -4959,17 +4960,17 @@ namespace NavisworksTransport.UI.WPF.ViewModels _hasStartPoint = false; AutoPathStartPoint = "未选择"; - LogManager.Info("PathPlanningManager已初始化,开始设置自动路径规划状态"); + LogManager.Info("PathPlanningManager已初始化,开始设置自动路径规划状态"); IsSelectingStartPoint = true; IsSelectingEndPoint = false; - // 通知按钮状态更新,禁用"手动创建"按钮 + // 通知按钮状态更新,禁用"手动创建"按钮 OnPropertyChanged(nameof(CanExecuteNewPath)); UpdateMainStatus("请在3D视图中点击选择起点..."); // 进入自动路径规划模式 - 动态事件订阅 - LogManager.Info("进入自动路径规划模式,启用动态事件订阅"); + LogManager.Info("进入自动路径规划模式,启用动态事件订阅"); // 1. 禁用PathPlanningManager的鼠标处理 _pathPlanningManager.DisableMouseHandling(); @@ -4979,16 +4980,16 @@ namespace NavisworksTransport.UI.WPF.ViewModels PathClickToolPlugin.MouseClicked += OnAutoPathMouseClicked; LogManager.Info("已启用PathEditingViewModel的鼠标事件订阅"); - // 强制重新初始化ToolPlugin以确保获得鼠标焦点,自动路径不需要PathPlanningManager订阅事件 + // 强制重新初始化ToolPlugin以确保获得鼠标焦点,自动路径不需要PathPlanningManager订阅事件 if (!ForceReinitializeToolPlugin(subscribeToEvents: false)) { - UpdateMainStatus("ToolPlugin初始化失败,请重试"); + UpdateMainStatus("ToolPlugin初始化失败,请重试"); return; } // 在STA线程中调用Navisworks API _pathPlanningManager.StartClickTool(PathPointType.StartPoint); - LogManager.Info("=== 选择起点命令设置完成,已启用动态事件订阅,等待用户点击 ==="); + LogManager.Info("=== 选择起点命令设置完成,已启用动态事件订阅,等待用户点击 ==="); }, "选择起点"); } @@ -5000,16 +5001,16 @@ namespace NavisworksTransport.UI.WPF.ViewModels if (_pathPlanningManager == null) { - LogManager.Error("PathPlanningManager为null,无法选择终点"); + LogManager.Error("PathPlanningManager为null,无法选择终点"); UpdateMainStatus("路径规划管理器未初始化"); return; } - // 立即清理现有的终点标记(使用正确的路径ID) + // 立即清理现有的终点标记(使用正确的路径ID) if (PathPointRenderPlugin.Instance != null && _autoPathEndPointRoute != null) { PathPointRenderPlugin.Instance.RemovePath(_autoPathEndPointRoute.Id); - LogManager.Debug($"[选择终点] 已清除之前的终点标记,ID: {_autoPathEndPointRoute.Id}"); + LogManager.Debug($"[选择终点] 已清除之前的终点标记,ID: {_autoPathEndPointRoute.Id}"); _autoPathEndPointRoute = null; // 清除引用 } @@ -5017,17 +5018,17 @@ namespace NavisworksTransport.UI.WPF.ViewModels _hasEndPoint = false; AutoPathEndPoint = "未选择"; - LogManager.Info("PathPlanningManager已初始化,开始设置自动路径规划状态"); + LogManager.Info("PathPlanningManager已初始化,开始设置自动路径规划状态"); IsSelectingStartPoint = false; IsSelectingEndPoint = true; - // 通知按钮状态更新,禁用"手动创建"按钮 + // 通知按钮状态更新,禁用"手动创建"按钮 OnPropertyChanged(nameof(CanExecuteNewPath)); UpdateMainStatus("请在3D视图中点击选择终点..."); // 进入自动路径规划模式 - 动态事件订阅 - LogManager.Info("进入自动路径规划模式,启用动态事件订阅"); + LogManager.Info("进入自动路径规划模式,启用动态事件订阅"); // 1. 禁用PathPlanningManager的鼠标处理 _pathPlanningManager.DisableMouseHandling(); @@ -5037,16 +5038,16 @@ namespace NavisworksTransport.UI.WPF.ViewModels PathClickToolPlugin.MouseClicked += OnAutoPathMouseClicked; LogManager.Info("已启用PathEditingViewModel的鼠标事件订阅"); - // 强制重新初始化ToolPlugin以确保获得鼠标焦点,自动路径不需要PathPlanningManager订阅事件 + // 强制重新初始化ToolPlugin以确保获得鼠标焦点,自动路径不需要PathPlanningManager订阅事件 if (!ForceReinitializeToolPlugin(subscribeToEvents: false)) { - UpdateMainStatus("ToolPlugin初始化失败,请重试"); + UpdateMainStatus("ToolPlugin初始化失败,请重试"); return; } // 在STA线程中调用Navisworks API _pathPlanningManager.StartClickTool(PathPointType.EndPoint); - LogManager.Info("=== 选择终点命令设置完成,已启用动态事件订阅,等待用户点击 ==="); + LogManager.Info("=== 选择终点命令设置完成,已启用动态事件订阅,等待用户点击 ==="); }, "选择终点"); } @@ -5078,18 +5079,18 @@ namespace NavisworksTransport.UI.WPF.ViewModels _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(); // 清理所有路径,不保留任何内容 + PathPointRenderPlugin.Instance.ClearPathsExcept(); // 清理所有路径,不保留任何内容 } } @@ -5110,16 +5111,16 @@ namespace NavisworksTransport.UI.WPF.ViewModels LogManager.Info($"起点: ({_startPoint3D.X:F2}, {_startPoint3D.Y:F2}, {_startPoint3D.Z:F2})"); LogManager.Info($"终点: ({_endPoint3D.X:F2}, {_endPoint3D.Y:F2}, {_endPoint3D.Z:F2})"); - // 计算物体半径:基于长度和宽度的较大值的一半,高度暂时不参与半径计算 + // 计算物体半径:基于长度和宽度的较大值的一半,高度暂时不参与半径计算 var objectRadius = Math.Max(ObjectLength, ObjectWidth) / 2.0; LogManager.Info($"物体参数: 长{ObjectLength:F1}m × 宽{ObjectWidth:F1}m × 高{ObjectHeight:F1}m"); LogManager.Info($"计算得出物体半径: {objectRadius:F2}m (基于长宽较大值的一半)"); - // 确定网格大小:如果启用手动设置则使用用户设置的值,否则使用-1(自动选择) + // 确定网格大小:如果启用手动设置则使用用户设置的值,否则使用-1(自动选择) var gridSize = IsGridSizeManuallyEnabled ? GridSize : -1; - // 获取选择的路径策略,如果未选择则默认使用最短路径 + // 获取选择的路径策略,如果未选择则默认使用最短路径 var selectedStrategy = SelectedPathStrategy?.Value ?? PathStrategy.Shortest; LogManager.Info($"网格大小设置: {(IsGridSizeManuallyEnabled ? $"手动设置 {GridSize:F1}米" : "自动选择")}"); @@ -5130,9 +5131,9 @@ namespace NavisworksTransport.UI.WPF.ViewModels if (pathRoute != null && pathRoute.Points.Count > 0) { - LogManager.Info($"路径规划成功,共 {pathRoute.Points.Count} 个点"); + LogManager.Info($"路径规划成功,共 {pathRoute.Points.Count} 个点"); - // 自动路径规划完成后,执行完整的事件订阅清理 + // 自动路径规划完成后,执行完整的事件订阅清理 CleanupAutoPathEventSubscriptions(); if (_pathPlanningManager != null) { @@ -5142,11 +5143,11 @@ namespace NavisworksTransport.UI.WPF.ViewModels // 清理临时的起点和终点标记 ClearTemporaryAutoPathMarkers(); - // 不在这里直接操作UI,让RouteGenerated事件处理UI更新 + // 不在这里直接操作UI,让RouteGenerated事件处理UI更新 // 这避免了重复的路径添加和UI更新冲突 - // 不在这里设置状态,让RouteGenerated事件处理UI更新 - LogManager.Info($"✅ 自动路径规划完成,RouteGenerated事件将处理UI更新"); + // 不在这里设置状态,让RouteGenerated事件处理UI更新 + LogManager.Info($"✅ 自动路径规划完成,RouteGenerated事件将处理UI更新"); } else { @@ -5165,14 +5166,14 @@ namespace NavisworksTransport.UI.WPF.ViewModels // 确保失败后也清理所有可能的残留路径对象 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(); // 清理所有路径,不保留任何内容 + PathPointRenderPlugin.Instance.ClearPathsExcept(); // 清理所有路径,不保留任何内容 } } } @@ -5187,7 +5188,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels private async Task ExecuteClearAutoPathAsync() { - // 使用基类的SafeExecuteAsync方法,确保在STA线程上执行 + // 使用基类的SafeExecuteAsync方法,确保在STA线程上执行 await SafeExecuteAsync(() => { LogManager.Info("=== 开始重置自动路径规划状态 ==="); @@ -5199,23 +5200,23 @@ namespace NavisworksTransport.UI.WPF.ViewModels 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("重置参数:已清除路径可视化显示(保留网格可视化)"); + LogManager.Info("重置参数:已清除路径可视化显示(保留网格可视化)"); } catch (Exception ex) { - LogManager.Error($"重置参数:清除路径可视化失败: {ex.Message}", ex); - // 不抛出异常,继续执行状态重置 + LogManager.Error($"重置参数:清除路径可视化失败: {ex.Message}", ex); + // 不抛出异常,继续执行状态重置 } } - // 强制重新初始化ToolPlugin以重新获得鼠标焦点,重置时不需要订阅事件 + // 强制重新初始化ToolPlugin以重新获得鼠标焦点,重置时不需要订阅事件 if (_pathPlanningManager != null) { ForceReinitializeToolPlugin(subscribeToEvents: false); @@ -5240,7 +5241,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels OnPropertyChanged(nameof(CanExecuteAutoPlanPath)); OnPropertyChanged(nameof(CanExecuteNewPath)); - LogManager.Info("自动路径规划参数和起终点标记已完全重置,ToolPlugin已重新初始化"); + LogManager.Info("自动路径规划参数和起终点标记已完全重置,ToolPlugin已重新初始化"); }, "重置自动路径规划"); } @@ -5282,7 +5283,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels // 检查是否是空轨路径 bool isRailPath = SelectedPathRoute.PathType == PathType.Rail; - // 启动PathPlanningManager的点击工具,这会设置正确的编辑状态 + // 启动PathPlanningManager的点击工具,这会设置正确的编辑状态 if (_pathPlanningManager != null) { try @@ -5290,7 +5291,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels if (isRailPath) { _pathPlanningManager.StartClickTool(PathPointType.WayPoint, enableRailSnapping: true); - LogManager.Info($"已启动PathPlanningManager点击工具(空轨吸附模式): {SelectedPathRoute.Name}"); + LogManager.Info($"已启动PathPlanningManager点击工具(空轨吸附模式): {SelectedPathRoute.Name}"); } else { @@ -5301,7 +5302,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels catch (Exception ex) { LogManager.Error($"启动点击工具失败: {ex.Message}"); - UpdateMainStatus("启动3D编辑工具失败,请重试"); + UpdateMainStatus("启动3D编辑工具失败,请重试"); IsPathEditMode = false; return; } @@ -5309,14 +5310,14 @@ namespace NavisworksTransport.UI.WPF.ViewModels else { UpdateMainStatus("路径规划管理器未初始化"); - LogManager.Error("添加路径点:PathPlanningManager未初始化"); + LogManager.Error("添加路径点:PathPlanningManager未初始化"); IsPathEditMode = false; return; } - string modeText = isRailPath ? "(空轨吸附模式)" : ""; + string modeText = isRailPath ? "(空轨吸附模式)" : ""; UpdateMainStatus($"已进入3D路径编辑模式: {SelectedPathRoute.Name} {modeText} - 在3D视图中点击设置路径点"); - LogManager.Info($"开始添加路径点: {SelectedPathRoute.Name},已启动点击工具"); + LogManager.Info($"开始添加路径点: {SelectedPathRoute.Name},已启动点击工具"); // 手动触发按钮状态更新 OnPropertyChanged(nameof(CanExecuteStartEdit)); @@ -5358,7 +5359,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels } else { - // 确认失败(可能没有预览点),执行取消操作以安全退出 + // 确认失败(可能没有预览点),执行取消操作以安全退出 success = _pathPlanningManager.CancelEditPoint(); operationMessage = success ? "已取消路径点修改" : "取消路径点修改失败"; } @@ -5366,18 +5367,18 @@ namespace NavisworksTransport.UI.WPF.ViewModels } else { - // 记录是否在预览模式(因为确认预览点后会退出预览模式) + // 记录是否在预览模式(因为确认预览点后会退出预览模式) wasInPreviewMode = _pathPlanningManager.IsPreviewMode; if (wasInPreviewMode) { - // 预览模式 - 先确认预览点,然后立即完成编辑 + // 预览模式 - 先确认预览点,然后立即完成编辑 var confirmedPoint = _pathPlanningManager.ConvertPreviewToPathPoint(); if (confirmedPoint != null) { - LogManager.Info($"[UI-预览模式] 预览点已确认: {confirmedPoint.Name},现在完成编辑"); + LogManager.Info($"[UI-预览模式] 预览点已确认: {confirmedPoint.Name},现在完成编辑"); - // 立即完成编辑,不让用户继续添加点 + // 立即完成编辑,不让用户继续添加点 success = _pathPlanningManager.FinishEditing(); operationMessage = success ? $"预览点已确认并完成编辑: {confirmedPoint.Name}" : "预览点确认后完成编辑失败"; } @@ -5414,7 +5415,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels } else if (wasInPreviewMode) { - UpdateMainStatus($"✅ 预览点已确认,路径编辑完成: {SelectedPathRoute.Name}"); + UpdateMainStatus($"✅ 预览点已确认,路径编辑完成: {SelectedPathRoute.Name}"); } else { @@ -5422,7 +5423,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels } } - LogManager.Info("[UI状态] 路径编辑完成,等待状态变更事件更新按钮"); + LogManager.Info("[UI状态] 路径编辑完成,等待状态变更事件更新按钮"); } else { @@ -5483,15 +5484,15 @@ namespace NavisworksTransport.UI.WPF.ViewModels return; } - // 2. 路径完整性验证:检查删除后是否至少还有2个点 + // 2. 路径完整性验证:检查删除后是否至少还有2个点 if (coreRoute.Points.Count <= 2) { - LogManager.Warning($"路径点不足,无法删除。当前路径只有{coreRoute.Points.Count}个点,至少需要保留2个点(起点和终点)"); - UpdateMainStatus($"❌ 无法删除路径点:路径至少需要保留2个点(起点和终点),当前只有{coreRoute.Points.Count}个点"); + LogManager.Warning($"路径点不足,无法删除。当前路径只有{coreRoute.Points.Count}个点,至少需要保留2个点(起点和终点)"); + UpdateMainStatus($"❌ 无法删除路径点:路径至少需要保留2个点(起点和终点),当前只有{coreRoute.Points.Count}个点"); return; } - // 3. 在Core路径中查找对应的点(通过位置和名称匹配) + // 3. 在Core路径中查找对应的点(通过位置和名称匹配) var corePoint = coreRoute.Points.FirstOrDefault(p => p.Name == point.Name && Math.Abs(p.Position.X - point.X) < 0.001 && @@ -5501,7 +5502,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels if (corePoint == null) { LogManager.Warning($"未找到对应的Core路径点: {point.Name}"); - // 即使Core数据不匹配,也要更新UI数据保持一致性 + // 即使Core数据不匹配,也要更新UI数据保持一致性 } await _uiStateManager.ExecuteUIUpdateAsync(() => @@ -5514,23 +5515,23 @@ namespace NavisworksTransport.UI.WPF.ViewModels if (corePoint.Type == PathPointType.StartPoint) { LogManager.Warning($"不能删除路径的起点"); - UpdateMainStatus($"❌ 无法删除:{corePoint.Name} 是路径的起点"); + UpdateMainStatus($"❌ 无法删除:{corePoint.Name} 是路径的起点"); return; } if (corePoint.Type == PathPointType.EndPoint) { LogManager.Warning($"不能删除路径的终点"); - UpdateMainStatus($"❌ 无法删除:{corePoint.Name} 是路径的终点"); + UpdateMainStatus($"❌ 无法删除:{corePoint.Name} 是路径的终点"); return; } - // 吊装路径:禁止删除关键点(提升点、下降点) + // 吊装路径:禁止删除关键点(提升点、下降点) if (coreRoute.PathType == PathType.Hoisting && (corePoint.Name == "提升点" || corePoint.Name == "下降点")) { LogManager.Warning($"不能删除吊装路径的关键点: {corePoint.Name}"); - UpdateMainStatus($"❌ 无法删除:{corePoint.Name} 是吊装路径的关键点"); + UpdateMainStatus($"❌ 无法删除:{corePoint.Name} 是吊装路径的关键点"); return; } @@ -5538,12 +5539,12 @@ namespace NavisworksTransport.UI.WPF.ViewModels coreRoute.Points.Remove(corePoint); LogManager.Info($"已从Core数据删除路径点: {corePoint.Name}"); - // 吊装路径:正交化路径(处理斜线和清除多余点) + // 吊装路径:正交化路径(处理斜线和清除多余点) if (coreRoute.PathType == PathType.Hoisting) { _pathPlanningManager?.OrthogonalizePath(coreRoute); - - // 吊装路径:检测并移除矩形环路 + + // 吊装路径:检测并移除矩形环路 LogManager.Info($"[吊装路径] 删除路径点后检测矩形环路"); _pathPlanningManager?.RemoveRectangularLoops(coreRoute); } @@ -5553,27 +5554,27 @@ namespace NavisworksTransport.UI.WPF.ViewModels // 触发UI同步更新事件 _pathPlanningManager.RaisePathPointsListUpdated(coreRoute, "删除路径点并重新分配类型"); - LogManager.Info("已触发PathPointsListUpdated事件,UI将自动同步"); + LogManager.Info("已触发PathPointsListUpdated事件,UI将自动同步"); } - // 5. 删除UI数据(事件驱动会自动处理,这里只作为备份) + // 5. 删除UI数据(事件驱动会自动处理,这里只作为备份) SelectedPathRoute.Points.Remove(point); LogManager.Info($"已从UI删除路径点: {point.Name}"); - // 🔥 如果被删除的点是当前选中的点,清空选中状态 + // 🔥 如果被删除的点是当前选中的点,清空选中状态 if (SelectedPathPoint != null && SelectedPathPoint.Id == point.Id) { SelectedPathPoint = null; - LogManager.Info("已清空选中状态(删除的是当前选中的点)"); + LogManager.Info("已清空选中状态(删除的是当前选中的点)"); } // 6. 更新UI状态 UpdateMainStatus($"✅ 已删除路径点: {point.Name}"); - LogManager.Info($"路径点删除完成,当前UI点数: {SelectedPathRoute.Points.Count}, Core点数: {coreRoute?.Points.Count ?? 0}"); + LogManager.Info($"路径点删除完成,当前UI点数: {SelectedPathRoute.Points.Count}, Core点数: {coreRoute?.Points.Count ?? 0}"); }); - // 7. 刷新3D可视化显示(路径变更会自动更新可视化) + // 7. 刷新3D可视化显示(路径变更会自动更新可视化) UpdatePathVisualization(); LogManager.Info("已触发路径可视化更新"); @@ -5609,7 +5610,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels if (currentPoint.Id == SelectedPathPoint.Id) { pointIndex = i; - LogManager.Info($"找到匹配的路径点,索引: {pointIndex}"); + LogManager.Info($"找到匹配的路径点,索引: {pointIndex}"); break; } } @@ -5632,7 +5633,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels } else { - UpdateMainStatus("修改路径点失败,请检查路径状态"); + UpdateMainStatus("修改路径点失败,请检查路径状态"); LogManager.Error($"开始修改路径点失败: {SelectedPathPoint.Name}, 索引: {pointIndex}"); } } @@ -5640,7 +5641,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels { UpdateMainStatus("找不到选中的路径点"); LogManager.Error($"找不到路径点索引 - 选中点Id: {SelectedPathPoint.Id}, 选中点名称: {SelectedPathPoint.Name}"); - LogManager.Error("可能原因:1)选中的路径点与当前路径不匹配 2)路径点数据不同步 3)Id不匹配"); + LogManager.Error("可能原因:1)选中的路径点与当前路径不匹配 2)路径点数据不同步 3)Id不匹配"); } } else @@ -5858,8 +5859,8 @@ namespace NavisworksTransport.UI.WPF.ViewModels if (saveFileDialog.ShowDialog() == true) { var filePath = saveFileDialog.FileName; - - // 确定导出格式(通过FilterIndex判断,因为DELMIA Apriso和普通XML都是.xml扩展名) + + // 确定导出格式(通过FilterIndex判断,因为DELMIA Apriso和普通XML都是.xml扩展名) ExportFormat exportFormat; switch (saveFileDialog.FilterIndex) { @@ -5904,7 +5905,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels statusMessage += $" (失败 {exportResult.FailedCount} 个)"; } UpdateMainStatus(statusMessage); - LogManager.Info($"导出完成,文件大小: {exportResult.FileSizeMB:F2} MB"); + LogManager.Info($"导出完成,文件大小: {exportResult.FileSizeMB:F2} MB"); } else { @@ -5934,90 +5935,108 @@ namespace NavisworksTransport.UI.WPF.ViewModels { try { - // 打开文件保存对话框 + // 收集选中的路径 + var selectedRoutes = PathRoutes + .Where(r => r.IsSelected && r.Points.Count > 0) + .Select(r => _pathPlanningManager.Routes.FirstOrDefault(c => c.Name == r.Name)) + .Where(c => c != null) + .ToList(); + + // 如果没有多选,使用当前选中路径 + if (selectedRoutes.Count == 0 && SelectedPathRoute != null && SelectedPathRoute.Points.Count > 0) + { + var coreRoute = _pathPlanningManager.Routes.FirstOrDefault(r => r.Name == SelectedPathRoute.Name); + if (coreRoute != null) + selectedRoutes.Add(coreRoute); + } + + if (selectedRoutes.Count == 0) + { + UpdateMainStatus("没有可导出的路径"); + return; + } + + // 打开文件保存对话框(确定格式和目录) + var firstRoute = selectedRoutes[0]; var saveFileDialog = new SaveFileDialog { - Title = $"导出选中路径: {SelectedPathRoute?.Name}", + Title = selectedRoutes.Count == 1 + ? $"导出路径: {firstRoute.Name}" + : $"导出 {selectedRoutes.Count} 个路径", Filter = "XML文件 (*.xml)|*.xml|JSON文件 (*.json)|*.json|CSV文件 (*.csv)|*.csv|DELMIA Apriso XML (*.aprxml)|*.aprxml|所有文件 (*.*)|*.*", FilterIndex = 1, - FileName = SelectedPathRoute?.Name?.Replace(" ", "_") ?? "路径", + FileName = selectedRoutes.Count == 1 ? firstRoute.Name?.Replace(" ", "_") ?? "路径" : "路径", DefaultExt = "xml", AddExtension = true }; if (saveFileDialog.ShowDialog() == true) { - var filePath = saveFileDialog.FileName; - - // 确定导出格式(通过FilterIndex判断,因为DELMIA Apriso和普通XML都是.xml扩展名) ExportFormat exportFormat; switch (saveFileDialog.FilterIndex) { - case 2: // JSON文件 - exportFormat = ExportFormat.Json; - break; - case 3: // CSV文件 - exportFormat = ExportFormat.Csv; - break; - case 4: // DELMIA Apriso XML - exportFormat = ExportFormat.DelmiaApriso; - break; - default: // XML文件或其他 - exportFormat = ExportFormat.Xml; - break; + case 2: exportFormat = ExportFormat.Json; break; + case 3: exportFormat = ExportFormat.Csv; break; + case 4: exportFormat = ExportFormat.DelmiaApriso; break; + default: exportFormat = ExportFormat.Xml; break; } - // 查找当前选中路径对应的Core路径 - var coreRoute = _pathPlanningManager.Routes.FirstOrDefault(r => r.Name == SelectedPathRoute.Name); - if (coreRoute != null) + var outputDir = Path.GetDirectoryName(saveFileDialog.FileName); + int exportedCount = 0; + int failedCount = 0; + + foreach (var route in selectedRoutes) { - // 创建导出参数 - 只导出当前选中的路径 + var exportFilePath = Path.Combine(outputDir, $"{route.Name.Replace(" ", "_")}{Path.GetExtension(saveFileDialog.FileName)}"); + var exportParameters = new ExportPathParameters { - PathsToExport = new System.Collections.Generic.List { coreRoute }, - OutputFilePath = filePath, + PathsToExport = new List { route }, + OutputFilePath = exportFilePath, ExportFormat = exportFormat, OverwriteExisting = true, ValidateExportData = true, GenerateReport = false, - Description = $"导出选中路径: {SelectedPathRoute.Name}" + Description = $"导出路径: {route.Name}" }; - // 创建并执行导出命令 var exportCommand = new ExportPathCommand(exportParameters, _pathPlanningManager); var result = await exportCommand.ExecuteAsync(); if (result.IsSuccess) - { - var exportResult = (result as PathPlanningResult)?.Data; - if (exportResult != null) - { - UpdateMainStatus($"✅ 导出选中路径成功: {SelectedPathRoute.Name} -> {Path.GetFileName(filePath)}"); - LogManager.Info($"导出选中路径完成,文件大小: {exportResult.FileSizeMB:F2} MB"); - } - else - { - UpdateMainStatus($"✅ 导出选中路径完成: {Path.GetFileName(filePath)}"); - } - } + exportedCount++; else { - UpdateMainStatus($"❌ 导出选中路径失败: {result.ErrorMessage}"); - LogManager.Error($"导出选中路径失败: {result.ErrorMessage}"); + failedCount++; + LogManager.Error($"导出路径失败 [{route.Name}]: {result.ErrorMessage}"); } } + + if (exportedCount > 0) + { + var msg = $"✅ 导出成功: {exportedCount} 个路径"; + if (failedCount > 0) + msg += $" (失败 {failedCount} 个)"; + UpdateMainStatus(msg); + DialogHelper.ShowAutoClosingMessageBox( + $"成功导出 {exportedCount} 个路径到: {outputDir}" + + (failedCount > 0 ? $"\n失败: {failedCount} 个路径" : ""), + "导出完成", + failedCount == 0 ? System.Windows.MessageBoxImage.Information : System.Windows.MessageBoxImage.Warning, + autoCloseMilliseconds: 4000); + } else { - UpdateMainStatus($"❌ 导出选中路径失败:找不到对应的Core路径: {SelectedPathRoute.Name}"); + UpdateMainStatus("❌ 导出失败"); } } } catch (Exception ex) { - LogManager.Error($"导出选中路径异常: {ex.Message}", ex); - UpdateMainStatus($"❌ 导出选中路径异常: {ex.Message}"); + LogManager.Error($"导出路径异常: {ex.Message}", ex); + UpdateMainStatus($"❌ 导出异常: {ex.Message}"); } - }, "导出选中路径"); + }, "导出路径"); } /// @@ -6086,9 +6105,9 @@ namespace NavisworksTransport.UI.WPF.ViewModels LogManager.Info($"RefreshPathRoutes: 添加路径ViewModel {coreRoute.Name} (Hash: {pathViewModel.GetHashCode()})"); } - LogManager.Info($"已刷新路径集合显示,共 {PathRoutes.Count} 个路径"); + LogManager.Info($"已刷新路径集合显示,共 {PathRoutes.Count} 个路径"); - // 调试:列出所有PathRoutes中的路径名称和哈希码 + // 调试:列出所有PathRoutes中的路径名称和哈希码 for (int i = 0; i < PathRoutes.Count; i++) { var route = PathRoutes[i]; @@ -6128,7 +6147,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels await SafeExecuteAsync(() => { // 退出自动路径规划模式 - 恢复事件订阅 - LogManager.Info("起点选择完成,恢复正常事件订阅状态"); + LogManager.Info("起点选择完成,恢复正常事件订阅状态"); CleanupAutoPathEventSubscriptions(); _pathPlanningManager?.EnableMouseHandling(); // 恢复PathPlanningManager的鼠标处理 _pathPlanningManager?.StopClickTool(); @@ -6140,7 +6159,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels await SafeExecuteAsync(() => { // 退出自动路径规划模式 - 恢复事件订阅 - LogManager.Info("终点选择完成,恢复正常事件订阅状态"); + LogManager.Info("终点选择完成,恢复正常事件订阅状态"); CleanupAutoPathEventSubscriptions(); _pathPlanningManager?.EnableMouseHandling(); // 恢复PathPlanningManager的鼠标处理 _pathPlanningManager?.StopClickTool(); @@ -6181,7 +6200,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels { await SafeExecuteAsync(() => { - // 直接设置新的起点(清理逻辑已在ExecuteSelectStartPointAsync中处理) + // 直接设置新的起点(清理逻辑已在ExecuteSelectStartPointAsync中处理) _startPoint3D = point; _hasStartPoint = true; AutoPathStartPoint = $"({point.X:F2}, {point.Y:F2}, {point.Z:F2})"; @@ -6207,18 +6226,18 @@ namespace NavisworksTransport.UI.WPF.ViewModels }; _autoPathStartPointRoute.AddPoint(startPathPoint); - // 使用新的只渲染点的API,不绘制连线 + // 使用新的只渲染点的API,不绘制连线 PathPointRenderPlugin.Instance.RenderPointOnly(_autoPathStartPointRoute); - LogManager.Info($"起点路径已创建,ID: {_autoPathStartPointRoute.Id}"); + LogManager.Info($"起点路径已创建,ID: {_autoPathStartPointRoute.Id}"); } if (_hasEndPoint) { - UpdateMainStatus("起点和终点已设置,可以开始路径规划"); + UpdateMainStatus("起点和终点已设置,可以开始路径规划"); } else { - UpdateMainStatus("起点已设置,请选择终点"); + UpdateMainStatus("起点已设置,请选择终点"); } LogManager.Info($"起点已设置: ({point.X:F2}, {point.Y:F2}, {point.Z:F2})"); @@ -6232,7 +6251,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels { await SafeExecuteAsync(() => { - // 直接设置新的终点(清理逻辑已在ExecuteSelectEndPointAsync中处理) + // 直接设置新的终点(清理逻辑已在ExecuteSelectEndPointAsync中处理) _endPoint3D = point; _hasEndPoint = true; AutoPathEndPoint = $"({point.X:F2}, {point.Y:F2}, {point.Z:F2})"; @@ -6258,18 +6277,18 @@ namespace NavisworksTransport.UI.WPF.ViewModels }; _autoPathEndPointRoute.AddPoint(endPathPoint); - // 使用新的只渲染点的API,不绘制连线 + // 使用新的只渲染点的API,不绘制连线 PathPointRenderPlugin.Instance.RenderPointOnly(_autoPathEndPointRoute); - LogManager.Info($"终点路径已创建,ID: {_autoPathEndPointRoute.Id}"); + LogManager.Info($"终点路径已创建,ID: {_autoPathEndPointRoute.Id}"); } if (_hasStartPoint) { - UpdateMainStatus("起点和终点已设置,可以开始路径规划"); + UpdateMainStatus("起点和终点已设置,可以开始路径规划"); } else { - UpdateMainStatus("终点已设置,请选择起点"); + UpdateMainStatus("终点已设置,请选择起点"); } LogManager.Info($"终点已设置: ({point.X:F2}, {point.Y:F2}, {point.Z:F2})"); @@ -6290,17 +6309,17 @@ namespace NavisworksTransport.UI.WPF.ViewModels if (PathPointRenderPlugin.Instance != null) { // 检查检测动画页签是否选择了物体或使用了虚拟物体 - // 如果有,使用动画页签的物体参数来更新通行空间(而不是跳过) + // 如果有,使用动画页签的物体参数来更新通行空间(而不是跳过) var animationVm = AnimationControlViewModel.Instance; if (animationVm != null && animationVm.HasSelectedAnimatedObject) { - // 动画页签已选择物体,使用动画页签的物体尺寸和当前路径的类型 - LogManager.Debug("[物体参数同步] 检测动画页签已选择动画对象,使用动画页签的物体尺寸"); + // 动画页签已选择物体,使用动画页签的物体尺寸和当前路径的类型 + LogManager.Debug("[物体参数同步] 检测动画页签已选择动画对象,使用动画页签的物体尺寸"); SyncAnimationViewObjectParameters(animationVm); return; } - // 将米单位转换为模型单位(SetPassageSpaceParameters需要模型单位) + // 将米单位转换为模型单位(SetPassageSpaceParameters需要模型单位) var metersToUnits = UnitsConverter.GetMetersToUnitsConversionFactor(); double objectLengthModel = ObjectLength * metersToUnits; double objectWidthModel = ObjectWidth * metersToUnits; @@ -6313,29 +6332,29 @@ namespace NavisworksTransport.UI.WPF.ViewModels if (pathType == PathType.Rail) { - // 空轨路径:物体水平悬挂,垂直于路径的截面包含物体的宽度和高度 - passageAcrossPath = objectWidthModel + 2 * safetyMarginModel; // Y方向(宽度)+ 2*间隙(垂直于路径方向) - passageNormalToPath = objectHeightModel + 2 * safetyMarginModel; // Z方向(高度)+ 2*间隙(法线方向) - passageAlongPath = objectLengthModel; // X方向(长度)(沿路径方向) + // 空轨路径:物体水平悬挂,垂直于路径的截面包含物体的宽度和高度 + passageAcrossPath = objectWidthModel + 2 * safetyMarginModel; // Y方向(宽度)+ 2*间隙(垂直于路径方向) + passageNormalToPath = objectHeightModel + 2 * safetyMarginModel; // Z方向(高度)+ 2*间隙(法线方向) + passageAlongPath = objectLengthModel; // X方向(长度)(沿路径方向) } else if (pathType == PathType.Hoisting) { - // 吊装路径:物体垂直悬挂,垂直于路径的截面包含物体的长度和宽度 - passageAcrossPath = objectWidthModel + 2 * safetyMarginModel; // Y方向(宽度)+ 2*间隙(垂直于路径方向) - passageNormalToPath = objectLengthModel + 2 * safetyMarginModel; // X方向(长度)+ 2*间隙(法线方向) - passageAlongPath = objectHeightModel; // Z方向(高度)(沿路径方向) + // 吊装路径:物体垂直悬挂,垂直于路径的截面包含物体的长度和宽度 + passageAcrossPath = objectWidthModel + 2 * safetyMarginModel; // Y方向(宽度)+ 2*间隙(垂直于路径方向) + passageNormalToPath = objectLengthModel + 2 * safetyMarginModel; // X方向(长度)+ 2*间隙(法线方向) + passageAlongPath = objectHeightModel; // Z方向(高度)(沿路径方向) } else // Ground { - // 地面路径:高度只有上方加间隙 - passageAcrossPath = objectWidthModel + 2 * safetyMarginModel; // Y方向(宽度)+ 2*间隙(垂直于路径方向) - passageNormalToPath = objectHeightModel + safetyMarginModel; // Z方向(高度)+ 间隙(法线方向,只有上方) - passageAlongPath = objectLengthModel; // X方向(长度)(沿路径方向) + // 地面路径:高度只有上方加间隙 + passageAcrossPath = objectWidthModel + 2 * safetyMarginModel; // Y方向(宽度)+ 2*间隙(垂直于路径方向) + passageNormalToPath = objectHeightModel + safetyMarginModel; // Z方向(高度)+ 间隙(法线方向,只有上方) + passageAlongPath = objectLengthModel; // X方向(长度)(沿路径方向) } // 设置通行空间参数到渲染插件 - // passageNormalToPathVertical: 垂直段的高度(物体长度 + 2×安全间隙) - // passageNormalToPathHorizontal: 水平段的高度(物体高度 + 2×安全间隙) + // passageNormalToPathVertical: 垂直段的高度(物体长度 + 2×安全间隙) + // passageNormalToPathHorizontal: 水平段的高度(物体高度 + 2×安全间隙) double passageNormalToPathVertical = objectLengthModel + 2 * safetyMarginModel; double passageNormalToPathHorizontal = objectHeightModel + 2 * safetyMarginModel; PathPointRenderPlugin.Instance.SetPassageSpaceParameters( @@ -6348,7 +6367,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels } else { - LogManager.Warning("[物体参数同步] PathPointRenderPlugin实例为空,无法同步物体参数"); + LogManager.Warning("[物体参数同步] PathPointRenderPlugin实例为空,无法同步物体参数"); } } catch (Exception ex) @@ -6364,9 +6383,9 @@ namespace NavisworksTransport.UI.WPF.ViewModels { try { - // 获取当前路径类型(使用路径编辑页签的当前选择) + // 获取当前路径类型(使用路径编辑页签的当前选择) PathType pathType = SelectedPathRoute?.PathType ?? PathType.Ground; - + // 获取单位转换因子 var metersToUnits = UnitsConverter.GetMetersToUnitsConversionFactor(); double safetyMarginModel = SafetyMargin * metersToUnits; @@ -6418,7 +6437,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels } else { - // 回退:使用选择物体时保存的原始尺寸(米) + // 回退:使用选择物体时保存的原始尺寸(米) objectLengthModel = animationVm.ObjectOriginalLength * metersToUnits; objectWidthModel = animationVm.ObjectOriginalWidth * metersToUnits; objectHeightModel = animationVm.ObjectOriginalHeight * metersToUnits; @@ -6429,8 +6448,8 @@ namespace NavisworksTransport.UI.WPF.ViewModels } else { - // 没有物体,使用默认参数 - LogManager.Warning("[物体参数同步] 动画页签已选择物体但实际无物体,使用默认参数"); + // 没有物体,使用默认参数 + LogManager.Warning("[物体参数同步] 动画页签已选择物体但实际无物体,使用默认参数"); objectLengthModel = ObjectLength * metersToUnits; objectWidthModel = ObjectWidth * metersToUnits; objectHeightModel = ObjectHeight * metersToUnits; @@ -6517,7 +6536,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels } else { - LogManager.Warning("无法获取路径规划管理器,网格可视化设置可能不会立即生效"); + LogManager.Warning("无法获取路径规划管理器,网格可视化设置可能不会立即生效"); UpdateMainStatus("网格可视化设置已保存"); } } @@ -6546,7 +6565,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels } else { - LogManager.Warning("无法获取PathPointRenderPlugin实例,网格点类型可能不会立即生效"); + LogManager.Warning("无法获取PathPointRenderPlugin实例,网格点类型可能不会立即生效"); UpdateMainStatus("网格点类型已保存"); } } @@ -6559,8 +6578,8 @@ namespace NavisworksTransport.UI.WPF.ViewModels /// /// 根据路径类型自动设置可视化模式 - /// - 地面路径:控制点连线 + 路径线 - /// - 吊装和空轨:控制点连线 + 通行空间(不能使用路径线) + /// - 地面路径:控制点连线 + 路径线 + /// - 吊装和空轨:控制点连线 + 通行空间(不能使用路径线) /// private void UpdatePassageSpaceVisualizationForPathType(PathType pathType) { @@ -6573,17 +6592,17 @@ namespace NavisworksTransport.UI.WPF.ViewModels { case PathType.Rail: case PathType.Hoisting: - // 空中路径:通行空间 + 控制点,不能使用路径线 + // 空中路径:通行空间 + 控制点,不能使用路径线 ShowPathLines = false; ShowObjectSpace = true; - LogManager.Debug($"[路径编辑] 路径类型={pathType}(空中路径),默认模式:控制点+通行空间"); + LogManager.Debug($"[路径编辑] 路径类型={pathType}(空中路径),默认模式:控制点+通行空间"); break; case PathType.Ground: - // 地面路径:路径线 + 控制点,不使用通行空间 + // 地面路径:路径线 + 控制点,不使用通行空间 ShowPathLines = true; ShowObjectSpace = false; - LogManager.Debug($"[路径编辑] 路径类型={pathType}(地面路径),默认模式:控制点+路径线"); + LogManager.Debug($"[路径编辑] 路径类型={pathType}(地面路径),默认模式:控制点+路径线"); break; default: @@ -6591,9 +6610,9 @@ namespace NavisworksTransport.UI.WPF.ViewModels return; } - LogManager.Info($"[路径编辑] 已根据路径类型自动设置可视化模式:控制点={ShowControlVisualization}, 路径线={ShowPathLines}, 通行空间={ShowObjectSpace}"); - - // 🔥 触发可视化状态变更事件,通知状态栏刷新按钮 + LogManager.Info($"[路径编辑] 已根据路径类型自动设置可视化模式:控制点={ShowControlVisualization}, 路径线={ShowPathLines}, 通行空间={ShowObjectSpace}"); + + // 🔥 触发可视化状态变更事件,通知状态栏刷新按钮 _pathPlanningManager?.RaiseVisualizationStateChanged(); } catch (Exception ex) @@ -6696,14 +6715,14 @@ namespace NavisworksTransport.UI.WPF.ViewModels try { // 简单安全地移除事件订阅 - // 由于C#事件处理机制,多次取消订阅同一个处理程序是安全的 - // 即使处理程序未订阅,取消操作也不会抛出异常 + // 由于C#事件处理机制,多次取消订阅同一个处理程序是安全的 + // 即使处理程序未订阅,取消操作也不会抛出异常 PathClickToolPlugin.MouseClicked -= OnAutoPathMouseClicked; } catch (Exception ex) { LogManager.Error($"[事件清理] 清理自动路径事件订阅失败: {ex.Message}", ex); - // 记录异常但不抛出,避免影响主流程 + // 记录异常但不抛出,避免影响主流程 } } @@ -6716,14 +6735,14 @@ namespace NavisworksTransport.UI.WPF.ViewModels { if (PathPointRenderPlugin.Instance != null) { - // 清除临时的起点标记(使用正确的路径ID) + // 清除临时的起点标记(使用正确的路径ID) if (_autoPathStartPointRoute != null) { PathPointRenderPlugin.Instance.RemovePath(_autoPathStartPointRoute.Id); _autoPathStartPointRoute = null; } - // 清除临时的终点标记(使用正确的路径ID) + // 清除临时的终点标记(使用正确的路径ID) if (_autoPathEndPointRoute != null) { PathPointRenderPlugin.Instance.RemovePath(_autoPathEndPointRoute.Id); @@ -6733,7 +6752,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels } else { - LogManager.Warning("[临时标记清理] PathPointRenderPlugin实例为null,无法清理标记"); + LogManager.Warning("[临时标记清理] PathPointRenderPlugin实例为null,无法清理标记"); } } catch (Exception ex) @@ -6753,13 +6772,13 @@ namespace NavisworksTransport.UI.WPF.ViewModels { if (_pathPlanningManager == null) { - LogManager.Debug("PathPlanningManager尚未设置,跳过事件订阅"); + LogManager.Debug("PathPlanningManager尚未设置,跳过事件订阅"); return; } try { - // 订阅路径点操作事件(用于手工路径编辑) + // 订阅路径点操作事件(用于手工路径编辑) _pathPlanningManager.PathPointOperation += OnPathPointOperation; // 订阅路径点列表更新事件 @@ -6771,10 +6790,10 @@ namespace NavisworksTransport.UI.WPF.ViewModels // 订阅编辑状态变更事件 _pathPlanningManager.EditStateChanged += OnEditStateChanged; - // 订阅路径生成事件(用于处理自动路径) + // 订阅路径生成事件(用于处理自动路径) _pathPlanningManager.RouteGenerated += OnRouteGenerated; - // 订阅路径加载完成事件(用于处理数据库加载的路径) + // 订阅路径加载完成事件(用于处理数据库加载的路径) _pathPlanningManager.RoutesLoaded += OnRoutesLoaded; // 添加错误事件订阅 - 用于处理自动路径规划失败等错误 @@ -6805,7 +6824,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels _pathPlanningManager.RoutesLoaded -= OnRoutesLoaded; _pathPlanningManager.ErrorOccurred -= OnPathPlanningError; - LogManager.Info("PathEditingViewModel已取消订阅PathPlanningManager事件,包括ErrorOccurred"); + LogManager.Info("PathEditingViewModel已取消订阅PathPlanningManager事件,包括ErrorOccurred"); } catch (Exception ex) { @@ -6814,7 +6833,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels } /// - /// 处理路径点操作事件(手工路径编辑的核心) + /// 处理路径点操作事件(手工路径编辑的核心) /// private async void OnPathPointOperation(object sender, PathPointOperationEventArgs e) { @@ -6826,15 +6845,15 @@ namespace NavisworksTransport.UI.WPF.ViewModels { LogManager.Info($"PathEditingViewModel收到路径点操作事件: {e.OperationType}, 点: {e.PathPoint.Name}"); - // 只处理手动编辑的路径点,跳过自动路径点 + // 只处理手动编辑的路径点,跳过自动路径点 // 自动路径通过RouteGenerated事件处理 if (e.PathPoint.Name.Contains("自动")) { - LogManager.Info($"跳过自动路径点,将通过RouteGenerated事件处理: {e.PathPoint.Name}"); + LogManager.Info($"跳过自动路径点,将通过RouteGenerated事件处理: {e.PathPoint.Name}"); return; } - // 注意:不在这里直接添加/移除单个路径点到UI + // 注意:不在这里直接添加/移除单个路径点到UI // 因为PathPointsListUpdated事件会处理完整的路径同步 // 这里只做状态更新和选择管理 @@ -6884,11 +6903,11 @@ namespace NavisworksTransport.UI.WPF.ViewModels bool updated = SyncPathViewModelFromCoreRoute(pathViewModel, e.Route, preserveSelection: true); if (!updated) { - LogManager.Info($"路径点数据无变化({pathViewModel.Points.Count}),跳过更新"); + LogManager.Info($"路径点数据无变化({pathViewModel.Points.Count}),跳过更新"); return; } - LogManager.Info($"已更新路径点列表,当前点数: {pathViewModel.Points.Count}"); + LogManager.Info($"已更新路径点列表,当前点数: {pathViewModel.Points.Count}"); if (SelectedPathRoute == pathViewModel) { @@ -6935,7 +6954,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels ApplyStoredAutoPlanningParameters(e.NewRoute); - // 只在真的不同时才设置,避免重复触发 + // 只在真的不同时才设置,避免重复触发 if (SelectedPathRoute != pathViewModel) { SelectedPathRoute = pathViewModel; @@ -6944,12 +6963,12 @@ namespace NavisworksTransport.UI.WPF.ViewModels } else { - LogManager.Warning($"OnCurrentRouteChanged: UI中未找到路径 {e.NewRoute.Name},可能UI还未刷新"); + LogManager.Warning($"OnCurrentRouteChanged: UI中未找到路径 {e.NewRoute.Name},可能UI还未刷新"); } } else { - // 当前路径被设为null,清除UI选择 + // 当前路径被设为null,清除UI选择 if (SelectedPathRoute != null) { SelectedPathRoute = null; @@ -6975,7 +6994,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels { LogManager.Info($"路径编辑状态变更: {e.PreviousState} -> {e.NewState}"); - // 修复:包含 AddingPoints 和 EditingPoint 状态 + // 修复:包含 AddingPoints 和 EditingPoint 状态 IsPathEditMode = (e.NewState == PathEditState.Creating || e.NewState == PathEditState.Editing || e.NewState == PathEditState.AddingPoints || @@ -6996,10 +7015,10 @@ namespace NavisworksTransport.UI.WPF.ViewModels UpdateMainStatus($"正在添加路径点到: {e.EditingRoute?.Name} - 请在3D视图中点击设置路径点"); break; case PathEditState.EditingPoint: - UpdateMainStatus($"正在修改路径点: {e.EditingRoute?.Name} - 请在3D视图中点击新位置,然后点击'结束'确认"); + UpdateMainStatus($"正在修改路径点: {e.EditingRoute?.Name} - 请在3D视图中点击新位置,然后点击'结束'确认"); break; case PathEditState.Viewing: - // 如果当前状态包含路径规划完成信息(包括完成度百分比),则保留不覆盖 + // 如果当前状态包含路径规划完成信息(包括完成度百分比),则保留不覆盖 var currentStatus = _mainViewModel?.StatusText ?? ""; if (!currentStatus.Contains("路径规划完成") && !currentStatus.Contains("路径规划部分完成") && !currentStatus.Contains("✅") && !currentStatus.Contains("🔶")) @@ -7029,16 +7048,16 @@ namespace NavisworksTransport.UI.WPF.ViewModels } /// - /// 处理路径生成事件(用于自动路径) + /// 处理路径生成事件(用于自动路径) /// 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} ***"); + 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,退出处理 ***"); + LogManager.Warning("*** OnRouteGenerated: RouteGeneratedEventArgs或Route为null,退出处理 ***"); return; } @@ -7070,9 +7089,9 @@ namespace NavisworksTransport.UI.WPF.ViewModels autoPathViewModel.Points.Add(point); } - LogManager.Info($"*** 已创建PathRouteViewModel,包含 {autoPathViewModel.Points.Count} 个点 ***"); + LogManager.Info($"*** 已创建PathRouteViewModel,包含 {autoPathViewModel.Points.Count} 个点 ***"); - // 检查是否已存在同名路径,避免重复添加 + // 检查是否已存在同名路径,避免重复添加 var existingPath = PathRoutes.FirstOrDefault(p => p.Name == e.Route.Name); if (existingPath == null) { @@ -7080,7 +7099,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels PathRoutes.Add(autoPathViewModel); SelectedPathRoute = autoPathViewModel; - LogManager.Info($"*** 自动路径已添加到UI列表: {e.Route.Name},当前PathRoutes.Count = {PathRoutes.Count} ***"); + LogManager.Info($"*** 自动路径已添加到UI列表: {e.Route.Name},当前PathRoutes.Count = {PathRoutes.Count} ***"); } else { @@ -7095,19 +7114,19 @@ namespace NavisworksTransport.UI.WPF.ViewModels 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}米"; + 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}%)"; + statusMessage = $"🔶 自动路径规划部分完成: {e.Route.Name},共 {e.Route.Points.Count} 个路径点,长度 {e.Route.TotalLength:F2}米,(完成度: {e.Route.CompletionPercentage:F1}%)"; } UpdateMainStatus(statusMessage); @@ -7115,7 +7134,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels } else if (e.GenerationMethod == RouteGenerationMethod.Manual) { - LogManager.Info($"手工路径生成完成: {e.Route.Name},开始刷新路径列表"); + LogManager.Info($"手工路径生成完成: {e.Route.Name},开始刷新路径列表"); var existingPath = PathRoutes.FirstOrDefault(p => p.Name == e.Route.Name); if (existingPath == null) { @@ -7130,7 +7149,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels SyncPathViewModelFromCoreRoute(existingPath, e.Route, preserveSelection: true); SelectedPathRoute = existingPath; - LogManager.Info($"手工路径已同步并重新选中: {e.Route.Name},点数: {existingPath.Points.Count}"); + LogManager.Info($"手工路径已同步并重新选中: {e.Route.Name},点数: {existingPath.Points.Count}"); OnPropertyChanged(nameof(PathRoutes)); OnPropertyChanged(nameof(CanExecuteStartEdit)); @@ -7138,8 +7157,8 @@ namespace NavisworksTransport.UI.WPF.ViewModels } else if (e.GenerationMethod == RouteGenerationMethod.DatabaseLoad) { - // 注意:DatabaseLoad现在由OnRoutesLoaded事件处理,这里不再处理 - LogManager.Warning($"*** OnRouteGenerated收到DatabaseLoad方法,这不应该发生!应该由OnRoutesLoaded处理 ***"); + // 注意:DatabaseLoad现在由OnRoutesLoaded事件处理,这里不再处理 + LogManager.Warning($"*** OnRouteGenerated收到DatabaseLoad方法,这不应该发生!应该由OnRoutesLoaded处理 ***"); } }, "处理路径生成事件"); } @@ -7150,15 +7169,15 @@ namespace NavisworksTransport.UI.WPF.ViewModels } /// - /// 处理路径加载完成事件(用于数据库加载) + /// 处理路径加载完成事件(用于数据库加载) /// private async void OnRoutesLoaded(object sender, RoutesLoadedEventArgs e) { - LogManager.Info($"*** OnRoutesLoaded被调用!Sender: {sender?.GetType().Name}, RouteCount: {e?.RouteCount}, LoadSource: {e?.LoadSource} ***"); + LogManager.Info($"*** OnRoutesLoaded被调用!Sender: {sender?.GetType().Name}, RouteCount: {e?.RouteCount}, LoadSource: {e?.LoadSource} ***"); if (e?.Routes == null) { - LogManager.Warning("*** OnRoutesLoaded: RoutesLoadedEventArgs或Routes为null,退出处理 ***"); + LogManager.Warning("*** OnRoutesLoaded: RoutesLoadedEventArgs或Routes为null,退出处理 ***"); return; } @@ -7166,7 +7185,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels { await SafeExecuteAsync(() => { - LogManager.Info($"PathEditingViewModel收到路径加载完成事件: 加载了 {e.RouteCount} 条路径,来源: {e.LoadSource}"); + LogManager.Info($"PathEditingViewModel收到路径加载完成事件: 加载了 {e.RouteCount} 条路径,来源: {e.LoadSource}"); // 处理从数据库加载的历史路径 foreach (var coreRoute in e.Routes) @@ -7189,7 +7208,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels dbPathViewModel.Points.Add(point); } - // 检查是否已存在同名路径,避免重复添加 + // 检查是否已存在同名路径,避免重复添加 var existingPath = PathRoutes.FirstOrDefault(p => p.Name == coreRoute.Name); if (existingPath == null) { @@ -7198,7 +7217,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels } else { - LogManager.Debug($"*** 数据库路径已存在,跳过: {coreRoute.Name} ***"); + LogManager.Debug($"*** 数据库路径已存在,跳过: {coreRoute.Name} ***"); } } @@ -7234,7 +7253,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels /// private async void OnPathPlanningError(object sender, PathPlanningErrorOccurredEventArgs e) { - LogManager.Info($"*** OnPathPlanningError被调用!Sender: {sender?.GetType().Name}, Error: {e?.ErrorMessage}, Level: {e?.ErrorLevel} ***"); + LogManager.Info($"*** OnPathPlanningError被调用!Sender: {sender?.GetType().Name}, Error: {e?.ErrorMessage}, Level: {e?.ErrorLevel} ***"); try { @@ -7274,23 +7293,23 @@ namespace NavisworksTransport.UI.WPF.ViewModels /// /// 强制重新初始化ToolPlugin以确保获得鼠标焦点 /// - /// 是否订阅鼠标事件,新建路径需要true,自动路径选点需要false + /// 是否订阅鼠标事件,新建路径需要true,自动路径选点需要false /// 初始化是否成功 private bool ForceReinitializeToolPlugin(bool subscribeToEvents = false) { try { - LogManager.Info($"开始强制重新初始化ToolPlugin以确保获得鼠标焦点,订阅事件: {subscribeToEvents}"); + LogManager.Info($"开始强制重新初始化ToolPlugin以确保获得鼠标焦点,订阅事件: {subscribeToEvents}"); - // 使用PathPlanningManager的公共方法,避免反射调用 + // 使用PathPlanningManager的公共方法,避免反射调用 if (_pathPlanningManager != null) { bool result = _pathPlanningManager.ForceReinitializeToolPlugin(subscribeToEvents); - LogManager.Info($"ToolPlugin重新初始化结果(事件订阅: {subscribeToEvents}): {result}"); + LogManager.Info($"ToolPlugin重新初始化结果(事件订阅: {subscribeToEvents}): {result}"); if (result) { - LogManager.Info("ToolPlugin重新初始化成功,已获得鼠标焦点"); + LogManager.Info("ToolPlugin重新初始化成功,已获得鼠标焦点"); } else { @@ -7301,7 +7320,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels } else { - LogManager.Error("PathPlanningManager为null,无法重新初始化ToolPlugin"); + LogManager.Error("PathPlanningManager为null,无法重新初始化ToolPlugin"); return false; } } diff --git a/src/UI/WPF/Views/PathEditingView.xaml b/src/UI/WPF/Views/PathEditingView.xaml index d5826ef..0b08031 100644 --- a/src/UI/WPF/Views/PathEditingView.xaml +++ b/src/UI/WPF/Views/PathEditingView.xaml @@ -42,6 +42,11 @@ NavisworksTransport 路径编辑页签视图 - 采用与动画控制和分层管 + + + @@ -241,7 +246,8 @@ NavisworksTransport 路径编辑页签视图 - 采用与动画控制和分层管 AutoGenerateColumns="False" CanUserAddRows="False" CanUserDeleteRows="False" - SelectionMode="Single" + SelectionMode="Extended" + RowStyle="{StaticResource SelectableRowStyle}" HeadersVisibility="Column" GridLinesVisibility="Horizontal" Height="340"