From 8946873e32b6fa3bd95d3dbcf45d85363a813387 Mon Sep 17 00:00:00 2001 From: tian <11429339@qq.com> Date: Sun, 14 Sep 2025 21:24:51 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BD=91=E6=A0=BC=E7=82=B9=E5=9C=A8=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE=E6=97=B6=E5=90=8C=E6=AD=A5=E5=88=B7=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .claude/settings.local.json | 3 +- CLAUDE.md | 3 +- doc/design/2026/NavisworksAPI使用方法.md | 26 ++ doc/requirement/todo_features.md | 2 +- .../2025-09-14_文档切换三级处理机制设计.md | 331 ++++++++++++++++++ src/Core/MainPlugin.cs | 157 ++++++++- src/Core/PathPlanningManager.cs | 68 +++- src/Core/PathPlanningModels.cs | 37 +- src/Core/PathPointRenderPlugin.cs | 40 ++- src/UI/WPF/ViewModels/PathEditingViewModel.cs | 44 ++- src/UI/WPF/Views/HelpDialog.xaml | 2 + src/UI/WPF/Views/SystemManagementView.xaml | 4 +- 12 files changed, 673 insertions(+), 44 deletions(-) create mode 100644 doc/working/2025-09-14_文档切换三级处理机制设计.md diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 11c8d78..c1831cb 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -135,7 +135,8 @@ "Bash(\"./compile.bat\")", "Bash(xcopy:*)", "Read(//c/ProgramData/Autodesk/Navisworks Manage 2026/NavisworksTransport/logs/**)", - "Bash(sed:*)" + "Bash(sed:*)", + "Read(//c/ProgramData/Autodesk/**)" ], "deny": [], "additionalDirectories": [ diff --git a/CLAUDE.md b/CLAUDE.md index 7223d14..d685304 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -186,4 +186,5 @@ public class PathClickToolPlugin : ToolPlugin { } - **Hot reload**: Restart Navisworks required after compilation to load new plugin version - **Debugging**: Use LogManager for centralized logging and the built-in log viewer dialog for log analysis - **2026 Features**: Test advanced animation capabilities, enhanced collision detection, and improved model handling -- 在编码中,不要用回退或向后兼容的思路和步骤 \ No newline at end of file +- 在编码中,不要用回退或向后兼容的思路和步骤 +- 程序的日志在:C:\ProgramData\Autodesk\Navisworks Manage 2026\NavisworksTransport\logs\debug.log \ No newline at end of file diff --git a/doc/design/2026/NavisworksAPI使用方法.md b/doc/design/2026/NavisworksAPI使用方法.md index 5334278..679efc3 100644 --- a/doc/design/2026/NavisworksAPI使用方法.md +++ b/doc/design/2026/NavisworksAPI使用方法.md @@ -1871,3 +1871,29 @@ private static void SimpleProgressExample() Autodesk.Navisworks.Api.Application.EndProgress(); } ``` + +### Navisworks的Models.CollectionChanged事件模式 + + 事件模式总结 + + 场景1:第一次打开文件(2ND_FLOOR) + - 事件#1 (6.6秒):Count=0,状态保持无模型(文档清空准备加载) + - 事件#2 (6.7秒):Count=1,从无模型→有模型(文件加载完成) + + 场景2:重新打开新文件(4TH_FLOOR) + - 事件#3 (25.6秒):Count=0,从有模型→无模型(旧文件被清空) + - 事件#4 (25.6秒):Count=1,从无模型→有模型(新文件加载完成) + + 场景3:关闭程序 + - 事件#5 (39.8秒):Count=0,从有模型→无模型(程序关闭前清理) + + 关键发现 + + 1. 每次文档操作都有两个事件: + - 先清空Models(Count=0) + - 再加载新Models(Count>0) + 2. 状态转换清晰: + - 无模型→无模型:初始清理 + - 无模型→有模型:文档加载完成 ✅ 这是我们需要初始化的时机 + - 有模型→无模型:文档关闭/切换 ✅ 这是我们需要清理的时机 + 3. 时间间隔很短:事件成对出现,间隔仅0.1秒左右 \ No newline at end of file diff --git a/doc/requirement/todo_features.md b/doc/requirement/todo_features.md index 6dfd5e4..8842c14 100644 --- a/doc/requirement/todo_features.md +++ b/doc/requirement/todo_features.md @@ -4,7 +4,7 @@ ### [2025/09/14] -1. [x] (功能)增加两种路径连线方式:1、宽度路径,半透明,膨胀直径,10cm高立方体。2、和前面一样,高度为车高加安全距离 +1. [x] (功能)增加空间通道连线方式:1、宽度路径,半透明,膨胀直径高度为车高加安全距离 ### [2025/09/12] diff --git a/doc/working/2025-09-14_文档切换三级处理机制设计.md b/doc/working/2025-09-14_文档切换三级处理机制设计.md new file mode 100644 index 0000000..ca91c08 --- /dev/null +++ b/doc/working/2025-09-14_文档切换三级处理机制设计.md @@ -0,0 +1,331 @@ +# 文档切换三级处理机制设计方案 + +**日期**: 2025-09-14 +**作者**: Claude +**背景**: 解决Navisworks插件在文档切换时的崩溃问题 + +## 一、问题分析 + +### 发现的问题 + +1. Navisworks是SDI(单文档界面)应用,ActiveDocument在整个生命周期中保持不变 +2. 文档内容变化通过`Models.CollectionChanged`事件通知 +3. 原代码在Models清空时调用了完整的CleanupManagers,导致IdleEventManager被Dispose,引发崩溃 + +### 测试观察结果 + +通过观察版本测试,发现Models.CollectionChanged事件的触发模式: + +**场景1:第一次打开文件** + +- 事件#1:Count=0,状态保持无模型(文档清空准备加载) +- 事件#2:Count=1,从无模型→有模型(文件加载完成) + +**场景2:重新打开新文件** + +- 事件#3:Count=0,从有模型→无模型(旧文件被清空) +- 事件#4:Count=1,从无模型→有模型(新文件加载完成) + +**场景3:关闭程序** + +- 事件#5:Count=0,从有模型→无模型(程序关闭前清理) + +## 二、设计方案 + +### 整体架构 + +``` +插件生命周期: +├── OnLoaded(插件启动)- 重量级初始化 +├── Models.CollectionChanged(文档切换)- 轻量级状态管理 +└── OnUnloading/DestroyControlPane(插件关闭)- 重量级清理 +``` + +### 三种处理级别 + +1. **插件初始化**(OnLoaded) + - 创建所有管理器实例 + - 初始化UI组件 + - 订阅事件 + +2. **文档状态切换**(Models.CollectionChanged) + - 轻量级操作 + - 清理/刷新ModelItem引用 + - 重置可视化状态 + - 保持管理器实例存活 + +3. **插件关闭**(OnUnloading + DestroyControlPane) + - 重量级清理 + - 销毁所有管理器实例 + - 取消所有事件订阅 + - 释放所有资源 + +## 三、具体实现 + +### 1. 添加必要的成员变量 + +```csharp +public class Main : DockPanePlugin +{ + // 状态跟踪变量 + private bool _hasModels = false; // 跟踪模型状态 + private bool _isShuttingDown = false; // 插件关闭标志 + private int _eventCount = 0; // 事件计数(调试用) + private DateTime _pluginStartTime; // 插件启动时间 +} +``` + +### 2. OnLoaded - 插件初始化(一次性) + +```csharp +protected override void OnLoaded() +{ + base.OnLoaded(); + + _pluginStartTime = DateTime.Now; + _isShuttingDown = false; + + LogManager.Info("[文档管理] 插件OnLoaded - 开始初始化"); + + // 订阅文档事件 + SubscribeToDocumentEvents(); + + // 一次性初始化管理器(创建实例) + if (NavisApplication.ActiveDocument != null) + { + LogManager.Info("[文档管理] 插件加载时发现活动文档,初始化管理器"); + InitializeManagers(); // 创建管理器实例 + + // 检查文档是否有内容 + if (NavisApplication.ActiveDocument.Models?.Count > 0) + { + _hasModels = true; + RefreshDocumentStates(); // 刷新状态 + } + } +} +``` + +### 3. OnModelsCollectionChanged - 文档切换处理(频繁) + +```csharp +private void OnModelsCollectionChanged(object sender, EventArgs e) +{ + _eventCount++; + + // 如果插件正在关闭,跳过所有处理 + if (_isShuttingDown) + { + LogManager.Info("[文档管理] 插件正在关闭,跳过Models.CollectionChanged处理"); + return; + } + + var activeDoc = NavisApplication.ActiveDocument; + var modelCount = activeDoc?.Models?.Count ?? 0; + bool currentHasModels = modelCount > 0; + + LogManager.Info($"[文档管理] Models.CollectionChanged #{_eventCount}, Count={modelCount}, 状态: {(_hasModels ? "有" : "无")} -> {(currentHasModels ? "有" : "无")}"); + + // 状态转换:从有到无(文档被清空) + if (_hasModels && !currentHasModels) + { + LogManager.Info("[文档管理] 文档已清空,执行轻量级清理"); + ClearDocumentStates(); + } + // 状态转换:从无到有(文档加载完成) + else if (!_hasModels && currentHasModels) + { + LogManager.Info("[文档管理] 文档已加载,刷新状态"); + RefreshDocumentStates(); + } + // 状态未变化 + else + { + LogManager.Info("[文档管理] 模型集合更新但状态未变"); + } + + _hasModels = currentHasModels; +} +``` + +### 4. 新增轻量级方法 + +```csharp +/// +/// 轻量级清理 - 仅清理文档相关状态,不销毁管理器 +/// +private void ClearDocumentStates() +{ + try + { + LogManager.Info("[文档管理] 开始轻量级状态清理..."); + + // 1. 停止动画(如果正在播放) + var animationManager = PathAnimationManager.GetInstance(); + if (animationManager?.IsAnimating == true) + { + animationManager.StopAnimation(); + } + // 清理动画中的ModelItem引用 + animationManager?.ClearReferences(); + + // 2. 清理路径管理器状态 + var pathManager = PathPlanningManager.GetActivePathManager(); + if (pathManager != null) + { + pathManager.ResetPathEditState(); + pathManager.ClearInvalidReferences(); // 清理ModelItem引用 + } + + // 3. 清理路径可视化 + PathPointRenderPlugin.Instance?.ClearAllPaths(); + + // 4. 清理碰撞检测缓存 + ClashDetectiveIntegration.Instance?.ClearCollisionCache(); + ClashDetectiveIntegration.ClearAllCaches(); + + // 5. 清除临时材质 + if (NavisApplication.ActiveDocument?.Models != null) + { + NavisApplication.ActiveDocument.Models.ResetAllTemporaryMaterials(); + } + + // 6. 通知文档状态管理器 + DocumentStateManager.Instance.OnDocumentInvalidated(); + + LogManager.Info("[文档管理] 轻量级状态清理完成"); + } + catch (Exception ex) + { + LogManager.Warning($"[文档管理] 状态清理时出现警告: {ex.Message}"); + } +} + +/// +/// 轻量级刷新 - 仅刷新状态,不重新创建管理器 +/// +private void RefreshDocumentStates() +{ + try + { + LogManager.Info("[文档管理] 开始刷新文档状态..."); + + // 1. 通知文档状态管理器 + DocumentStateManager.Instance.OnDocumentReady(); + + // 2. 清理旧缓存 + ClashDetectiveIntegration.ClearAllCaches(); + + // 3. 刷新路径管理器(如果需要) + var pathManager = PathPlanningManager.GetActivePathManager(); + pathManager?.RefreshDocument(); + + LogManager.Info("[文档管理] 文档状态刷新完成"); + } + catch (Exception ex) + { + LogManager.Warning($"[文档管理] 状态刷新时出现警告: {ex.Message}"); + } +} +``` + +### 5. OnUnloading - 插件关闭(一次性) + +```csharp +protected override void OnUnloading() +{ + LogManager.Info("[文档管理] 插件OnUnloading - 开始关闭"); + + // 设置关闭标志,阻止后续的Models事件处理 + _isShuttingDown = true; + + // 取消订阅文档事件 + UnsubscribeFromDocumentEvents(); + + // 执行完整的管理器清理(销毁实例) + CleanupManagers(); + + base.OnUnloading(); +} +``` + +### 6. DestroyControlPane - 控件销毁 + +```csharp +public override void DestroyControlPane(Control pane) +{ + GlobalExceptionHandler.SafeExecute(() => + { + LogManager.Info("开始销毁DockPane控制面板"); + + // 设置关闭标志(双重保险) + _isShuttingDown = true; + + // 清理WPF控件资源 + if (pane is ElementHost elementHost && elementHost.Child is UI.WPF.LogisticsControlPanel wpfControl) + { + wpfControl.Cleanup(); + elementHost.Child = null; + } + + // 释放控件资源 + pane?.Dispose(); + + LogManager.Info("DockPane控制面板销毁完成"); + }, "销毁控制面板"); +} +``` + +### 7. 保持不变的方法 + +- **InitializeManagers()** - 保持原样,用于创建管理器实例 +- **CleanupManagers()** - 保持原样,用于销毁管理器实例 +- **SubscribeToDocumentEvents()** - 保持原样 +- **UnsubscribeFromDocumentEvents()** - 保持原样 + +## 四、关键设计原则 + +### 1. 分离关注点 + +- 插件生命周期管理(OnLoaded/OnUnloading) +- 文档状态管理(Models.CollectionChanged) +- UI生命周期管理(CreateControlPane/DestroyControlPane) + +### 2. 轻量级 vs 重量级 + +- **轻量级**:清理引用、重置状态、清空缓存 +- **重量级**:创建/销毁实例、订阅/取消事件 + +### 3. 防御性编程 + +- 使用`_isShuttingDown`标志防止关闭时的事件处理 +- 所有清理操作都包含try-catch +- 使用GlobalExceptionHandler.SafeExecute + +### 4. 性能优化 + +- 文档切换时不重新创建管理器 +- 只在必要时清理和刷新 +- 避免重复操作 + +## 五、预期效果 + +1. **启动时**:完整初始化,创建所有组件 +2. **文档切换时**:快速清理和刷新,保持响应性 +3. **关闭时**:干净退出,避免异常 + +## 六、实施计划 + +1. 先实现`_isShuttingDown`标志,防止关闭时的崩溃 +2. 实现`ClearDocumentStates()`和`RefreshDocumentStates()`方法 +3. 修改`OnModelsCollectionChanged`使用新的轻量级方法 +4. 测试各种场景确保稳定性 + +## 七、注意事项 + +1. PathPlanningManager可能需要添加`RefreshDocument()`方法 +2. 确保所有管理器都有适当的清理引用方法 +3. 考虑添加更多的日志来跟踪状态变化 +4. 可能需要在某些管理器中区分"清理状态"和"销毁实例" + +这个方案清晰地分离了不同场景的处理逻辑,避免了混淆,确保了稳定性。 diff --git a/src/Core/MainPlugin.cs b/src/Core/MainPlugin.cs index d88217f..4fa6014 100644 --- a/src/Core/MainPlugin.cs +++ b/src/Core/MainPlugin.cs @@ -312,6 +312,11 @@ namespace NavisworksTransport /// /// 构造函数,提前初始化全局异常处理器 /// + // 状态跟踪变量(用于观察Models.CollectionChanged事件) + private bool _hasModels = false; + private int _eventCount = 0; + private DateTime _pluginStartTime = DateTime.Now; + public Main() { // 在任何控件创建之前初始化全局异常处理器 @@ -325,6 +330,8 @@ namespace NavisworksTransport { base.OnLoaded(); + _pluginStartTime = DateTime.Now; // 记录插件启动时间 + LogManager.Info("[文档管理] 插件OnLoaded - 开始初始化"); // 订阅文档事件 @@ -365,10 +372,19 @@ namespace NavisworksTransport { try { - // 只订阅活动文档切换事件 + // 订阅文档切换前事件(保留用于未来兼容性) + NavisApplication.ActiveDocumentChanging += OnActiveDocumentChanging; + // 订阅文档切换后事件(保留用于未来兼容性) NavisApplication.ActiveDocumentChanged += OnActiveDocumentChanged; - - LogManager.Info("[文档管理] 已订阅活动文档切换事件"); + + // 订阅模型集合变化事件(SDI架构下的真正文档变化事件) + if (NavisApplication.ActiveDocument != null) + { + NavisApplication.ActiveDocument.Models.CollectionChanged += OnModelsCollectionChanged; + LogManager.Info("[文档管理] 已订阅模型集合变化事件"); + } + + LogManager.Info("[文档管理] 已订阅文档相关事件(Changing + Changed + Models.CollectionChanged)"); } catch (Exception ex) { @@ -376,34 +392,111 @@ namespace NavisworksTransport } } + /// + /// 活动文档切换前事件处理 + /// + private void OnActiveDocumentChanging(object sender, EventArgs e) + { + LogManager.Info($"[文档管理] 文档即将切换,当前文档: {NavisApplication.ActiveDocument?.FileName ?? "无"}"); + + // 取消订阅旧文档的Models.CollectionChanged事件 + if (NavisApplication.ActiveDocument != null) + { + try + { + NavisApplication.ActiveDocument.Models.CollectionChanged -= OnModelsCollectionChanged; + LogManager.Info("[文档管理] 已取消订阅旧文档的模型集合变化事件"); + } + catch (Exception ex) + { + LogManager.Warning($"[文档管理] 取消订阅旧文档事件时出现警告: {ex.Message}"); + } + } + + // 清理旧文档的状态 + CleanupManagers(); + } + /// /// 活动文档切换事件处理 /// private void OnActiveDocumentChanged(object sender, EventArgs e) { + LogManager.Info($"[文档管理] 文档已切换到: {NavisApplication.ActiveDocument?.FileName ?? "无活动文档"}"); + GlobalExceptionHandler.SafeExecute(() => { var activeDoc = NavisApplication.ActiveDocument; - var oldDocName = DocumentStateManager.Instance.CurrentDocumentPath ?? "无"; - var newDocName = activeDoc?.FileName ?? "无活动文档"; - - LogManager.Info($"[文档管理] 活动文档切换: {oldDocName} → {newDocName}"); - - // 先清理旧文档的状态 - CleanupManagers(); - - // 如果有新的活动文档,重新初始化 + + // 如果有新的活动文档,订阅其Models.CollectionChanged事件 if (activeDoc != null) { + try + { + activeDoc.Models.CollectionChanged += OnModelsCollectionChanged; + LogManager.Info("[文档管理] 已订阅新文档的模型集合变化事件"); + } + catch (Exception ex) + { + LogManager.Warning($"[文档管理] 订阅新文档事件时出现警告: {ex.Message}"); + } + InitializeManagers(); } else { LogManager.Info("[文档管理] 当前无活动文档"); } - + }, "处理活动文档切换事件"); } + + /// + /// 模型集合变化事件处理(SDI架构下的真正文档变化事件) + /// + private void OnModelsCollectionChanged(object sender, EventArgs e) + { + _eventCount++; + + var activeDoc = NavisApplication.ActiveDocument; + var modelCount = activeDoc?.Models?.Count ?? 0; + bool currentHasModels = modelCount > 0; + var timeSinceStart = (DateTime.Now - _pluginStartTime).TotalSeconds; + + LogManager.Info("========================================"); + LogManager.Info($"[文档管理] Models.CollectionChanged 事件 #{_eventCount}"); + LogManager.Info($"[文档管理] 触发时间: 插件启动后 {timeSinceStart:F1} 秒"); + LogManager.Info($"[文档管理] 当前Models.Count = {modelCount}"); + LogManager.Info($"[文档管理] 状态变化: {(_hasModels ? "有模型" : "无模型")} -> {(currentHasModels ? "有模型" : "无模型")}"); + LogManager.Info($"[文档管理] 文档文件名: {activeDoc?.FileName ?? "空"}"); + LogManager.Info($"[文档管理] 文档标题: {activeDoc?.Title ?? "空"}"); + + // 分析状态变化类型 + if (!_hasModels && currentHasModels) + { + LogManager.Info($"[文档管理] *** 状态分析: 从无到有(可能是打开文档)***"); + } + else if (_hasModels && !currentHasModels) + { + LogManager.Info($"[文档管理] *** 状态分析: 从有到无(可能是关闭文档)***"); + } + else if (!_hasModels && !currentHasModels) + { + LogManager.Info($"[文档管理] *** 状态分析: 保持无模型状态 ***"); + } + else if (_hasModels && currentHasModels) + { + LogManager.Info($"[文档管理] *** 状态分析: 保持有模型状态(模型数量变化)***"); + } + + LogManager.Info("========================================"); + + // 更新状态 + _hasModels = currentHasModels; + + // 暂时不执行任何清理或初始化操作 + LogManager.Info("[文档管理] 观察模式:不执行清理或初始化操作"); + } /// /// 初始化各个管理器 @@ -476,7 +569,22 @@ namespace NavisworksTransport { LogManager.Warning($"[文档管理] 清理路径管理器时出现警告: {ex.Message}"); } - + + // 清理路径渲染插件的可视化 + try + { + var renderPlugin = PathPointRenderPlugin.Instance; + if (renderPlugin != null) + { + renderPlugin.ClearAllPaths(); + LogManager.Info("[文档管理] 已清理路径可视化"); + } + } + catch (Exception ex) + { + LogManager.Warning($"[文档管理] 清理路径可视化时出现警告: {ex.Message}"); + } + // 清理碰撞检测缓存 try { @@ -585,10 +693,25 @@ namespace NavisworksTransport { try { - // 只取消订阅活动文档切换事件 + // 取消订阅文档切换事件 + NavisApplication.ActiveDocumentChanging -= OnActiveDocumentChanging; NavisApplication.ActiveDocumentChanged -= OnActiveDocumentChanged; - - LogManager.Info("[文档管理] 已取消订阅文档事件"); + + // 取消订阅当前文档的Models.CollectionChanged事件 + if (NavisApplication.ActiveDocument != null) + { + try + { + NavisApplication.ActiveDocument.Models.CollectionChanged -= OnModelsCollectionChanged; + LogManager.Info("[文档管理] 已取消订阅模型集合变化事件"); + } + catch (Exception ex) + { + LogManager.Warning($"[文档管理] 取消订阅模型集合事件时出现警告: {ex.Message}"); + } + } + + LogManager.Info("[文档管理] 已取消订阅所有文档相关事件"); } catch (Exception ex) { diff --git a/src/Core/PathPlanningManager.cs b/src/Core/PathPlanningManager.cs index 877d947..ee1d9b1 100644 --- a/src/Core/PathPlanningManager.cs +++ b/src/Core/PathPlanningManager.cs @@ -53,6 +53,7 @@ namespace NavisworksTransport private bool _showObstacleGrid = false; private bool _showUnknownGrid = false; private bool _showDoorGrid = false; + private GridMap _currentGridMap = null; // 保存当前网格地图用于刷新 private bool _isPreviewMode = false; private int _previewInsertIndex = -1; // 保存预览点应该插入的索引位置 @@ -578,7 +579,17 @@ namespace NavisworksTransport { _currentRoute.Points.Clear(); } - + + // 清空通道引用 + if (_walkableAreas != null) + { + _walkableAreas.Clear(); + LogManager.Info("[PathPlanningManager] 已清空通道引用"); + } + + // 清空组合边界 + _combinedChannelBounds = null; + LogManager.Info("[PathPlanningManager] 对象引用清理完成"); } catch (Exception ex) @@ -968,10 +979,18 @@ namespace NavisworksTransport LogManager.Info($"A*算法找到路径,包含 {pathResult.PathPoints.Count} 个点,完成度: {pathResult.CompletionPercentage:F1}%"); - // 6. 创建PathRoute对象 + // 6. 创建PathRoute对象并保存GridMap var routeName = $"自动路径_{DateTime.Now:HHmmss}"; var autoRoute = CreateAutoPathRoute(pathResult, routeName); + // 保存GridMap和参数到PathRoute以便后续恢复网格可视化 + autoRoute.AssociatedGridMap = gridMap; + autoRoute.GridSize = gridSize; + autoRoute.VehicleSize = vehicleSize; + autoRoute.SafetyMargin = safetyMargin; + autoRoute.VehicleHeight = vehicleHeight; + LogManager.Info($"已保存GridMap到路径: {routeName}, 网格大小: {gridSize}米"); + // 7. 添加到路径集合 if (!_routes.Contains(autoRoute)) { @@ -1049,6 +1068,30 @@ namespace NavisworksTransport try { CurrentRoute = route; + + // 如果路径有关联的GridMap,加载它用于网格可视化 + if (route?.AssociatedGridMap != null) + { + _currentGridMap = route.AssociatedGridMap; + LogManager.Info($"已加载路径 '{route.Name}' 的GridMap,网格大小: {route.GridSize}米"); + + // 如果网格可视化开启,立即刷新显示 + if (IsAnyGridVisualizationEnabled) + { + LogManager.Info("检测到网格可视化已启用,自动刷新网格显示"); + RefreshGridVisualization(); + } + } + else + { + // 如果路径没有GridMap,清空当前缓存 + _currentGridMap = null; + if (route != null) + { + LogManager.Info($"路径 '{route.Name}' 没有关联的GridMap"); + } + } + LogManager.Info($"当前路线已设置: {route?.Name ?? "null"}"); } catch (Exception ex) @@ -3177,10 +3220,21 @@ namespace NavisworksTransport { // 清除当前网格可视化 ClearGridVisualization(); - - // 如果存在当前网格地图,重新应用可视化 - // 这里需要保存对最后一个gridMap的引用,或者从当前路径规划状态中重新获取 - LogManager.Info("[网格可视化] 刷新完成 - 需要重新进行路径规划以查看网格"); + + // 如果存在缓存的网格地图且有网格可视化设置启用,重新进行可视化 + if (_currentGridMap != null && IsAnyGridVisualizationEnabled) + { + LogManager.Info("[网格可视化] 使用缓存的网格地图重新渲染"); + VisualizeGridCells(_currentGridMap); + } + else if (_currentGridMap == null) + { + LogManager.Info("[网格可视化] 无缓存网格地图,需要重新进行路径规划以查看网格"); + } + else + { + LogManager.Info("[网格可视化] 网格可视化设置已关闭,跳过渲染"); + } } catch (Exception ex) { @@ -3203,6 +3257,8 @@ namespace NavisworksTransport try { + // 保存当前网格地图用于后续刷新 + _currentGridMap = gridMap; LogManager.Info($"[网格可视化] 开始可视化网格:{gridMap.Width}x{gridMap.Height}"); // 获取渲染插件 diff --git a/src/Core/PathPlanningModels.cs b/src/Core/PathPlanningModels.cs index f97949b..5759845 100644 --- a/src/Core/PathPlanningModels.cs +++ b/src/Core/PathPlanningModels.cs @@ -376,6 +376,32 @@ namespace NavisworksTransport /// public double CompletionPercentage { get; set; } = 100.0; + /// + /// 关联的网格地图(用于网格可视化) + /// + [XmlIgnore] + public NavisworksTransport.PathPlanning.GridMap AssociatedGridMap { get; set; } + + /// + /// 网格大小(米) + /// + public double GridSize { get; set; } = 0.5; + + /// + /// 车辆尺寸(米) + /// + public double VehicleSize { get; set; } = 1.0; + + /// + /// 安全间隙(米) + /// + public double SafetyMargin { get; set; } = 0.5; + + /// + /// 车辆高度(米) + /// + public double VehicleHeight { get; set; } = 2.0; + /// /// 构造函数 /// @@ -557,7 +583,16 @@ namespace NavisworksTransport CreatedTime = CreatedTime, LastModified = DateTime.Now, Description = Description, - AssociatedChannelIds = new List(AssociatedChannelIds) + AssociatedChannelIds = new List(AssociatedChannelIds), + IsComplete = IsComplete, + OriginalEndPoint = OriginalEndPoint, + ActualEndPoint = ActualEndPoint, + CompletionPercentage = CompletionPercentage, + AssociatedGridMap = AssociatedGridMap, // 共享引用,不深拷贝 + GridSize = GridSize, + VehicleSize = VehicleSize, + SafetyMargin = SafetyMargin, + VehicleHeight = VehicleHeight }; // 克隆所有路径点 diff --git a/src/Core/PathPointRenderPlugin.cs b/src/Core/PathPointRenderPlugin.cs index d7e934a..cb41aa7 100644 --- a/src/Core/PathPointRenderPlugin.cs +++ b/src/Core/PathPointRenderPlugin.cs @@ -240,8 +240,8 @@ namespace NavisworksTransport set { _visualizationMode = value; - // 模式改变时刷新所有路径可视化 - RefreshAllPaths(); + // 模式改变时刷新所有普通路径(排除网格) + RefreshNormalPaths(); } } @@ -254,8 +254,11 @@ namespace NavisworksTransport set { _vehicleHeightMode = value; - // 高度模式改变时刷新所有路径可视化 - RefreshAllPaths(); + // 只有在车辆空间模式下才需要刷新 + if (_visualizationMode == PathVisualizationMode.VehicleSpace) + { + RefreshNormalPaths(); + } } } @@ -537,14 +540,27 @@ namespace NavisworksTransport } /// - /// 刷新所有路径可视化(用于可视化模式改变时) + /// 判断是否为网格可视化路径 /// - private void RefreshAllPaths() + private bool IsGridVisualizationPath(string pathId) + { + return pathId != null && ( + pathId.StartsWith("grid_visualization_") || + pathId == "grid_visualization_all"); + } + + /// + /// 刷新所有普通路径(排除网格可视化) + /// + private void RefreshNormalPaths() { lock (_lockObject) { - var pathIds = _pathVisualizations.Keys.ToList(); - foreach (var pathId in pathIds) + var normalPathIds = _pathVisualizations.Keys + .Where(id => !IsGridVisualizationPath(id)) + .ToList(); + + foreach (var pathId in normalPathIds) { if (_pathVisualizations.TryGetValue(pathId, out var visualization)) { @@ -552,9 +568,9 @@ namespace NavisworksTransport } } - if (pathIds.Count > 0) + if (normalPathIds.Count > 0) { - LogManager.WriteLog($"[路径刷新] 刷新所有路径: {pathIds.Count}个, 可视化模式: {_visualizationMode}"); + LogManager.WriteLog($"[路径刷新] 刷新普通路径: {normalPathIds.Count}个, 模式: {_visualizationMode}"); RequestViewRefresh(); } } @@ -873,10 +889,10 @@ namespace NavisworksTransport LogManager.WriteLog($"[车辆参数] 更新车辆参数: 长={vehicleLength:F2}m, 宽={vehicleWidth:F2}m, 高={vehicleHeight:F2}m, 安全间隙={safetyMargin:F2}m"); - // 车辆参数改变时刷新所有路径可视化 + // 车辆参数改变时刷新所有普通路径 if (_visualizationMode == PathVisualizationMode.VehicleSpace) { - RefreshAllPaths(); + RefreshNormalPaths(); } } diff --git a/src/UI/WPF/ViewModels/PathEditingViewModel.cs b/src/UI/WPF/ViewModels/PathEditingViewModel.cs index a9fffe2..04b383e 100644 --- a/src/UI/WPF/ViewModels/PathEditingViewModel.cs +++ b/src/UI/WPF/ViewModels/PathEditingViewModel.cs @@ -179,7 +179,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels // 1. 清理现有的路径可视化,但保留网格可视化 PathPointRenderPlugin.Instance.ClearPathsExcept("grid_visualization_all", "grid_visualization_channel", "grid_visualization_unknown", "grid_visualization_obstacle", "grid_visualization_door"); LogManager.WriteLog("[路径可视化] 已清理现有路径显示(保留网格可视化)"); - + // 2. 如果有选中的路径,则显示该路径 if (_selectedPathRoute != null && _selectedPathRoute.Points.Count > 0) { @@ -200,6 +200,35 @@ namespace NavisworksTransport.UI.WPF.ViewModels { LogManager.WriteLog("[路径可视化] 没有选中的路径,仅清理显示"); } + + // 3. 恢复网格可视化(如果网格可视化已启用且当前路径有关联的GridMap) + if (_pathPlanningManager?.IsAnyGridVisualizationEnabled == true) + { + // 检查当前路径是否有关联的GridMap + var currentRoute = _pathPlanningManager.CurrentRoute; + if (currentRoute?.AssociatedGridMap != null) + { + LogManager.WriteLog("[路径可视化] 检测到网格可视化已启用且当前路径有GridMap,恢复网格显示"); + + // 通过PathPlanningManager的RefreshGridVisualization方法恢复网格 + // 这会使用当前路径的AssociatedGridMap来重新渲染网格 + var refreshMethod = typeof(PathPlanningManager).GetMethod("RefreshGridVisualization", + System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + if (refreshMethod != null) + { + refreshMethod.Invoke(_pathPlanningManager, null); + LogManager.WriteLog("[路径可视化] 网格可视化已恢复"); + } + else + { + LogManager.Warning("[路径可视化] 无法找到RefreshGridVisualization方法"); + } + } + else + { + LogManager.WriteLog("[路径可视化] 当前路径没有关联的GridMap,跳过网格恢复"); + } + } } catch (Exception ex) { @@ -1112,9 +1141,18 @@ namespace NavisworksTransport.UI.WPF.ViewModels if (isEditingPoint) { - // 修改路径点模式 - 确认修改 + // 修改路径点模式 - 尝试确认修改 success = _pathPlanningManager.ConfirmEditPoint(); - operationMessage = success ? "路径点修改已确认" : "路径点修改确认失败"; + if (success) + { + operationMessage = "路径点修改已确认"; + } + else + { + // 确认失败(可能没有预览点),执行取消操作以安全退出 + success = _pathPlanningManager.CancelEditPoint(); + operationMessage = success ? "已取消路径点修改" : "取消路径点修改失败"; + } LogManager.Info($"[UI-修改路径点] {operationMessage}"); } else diff --git a/src/UI/WPF/Views/HelpDialog.xaml b/src/UI/WPF/Views/HelpDialog.xaml index 7974f21..71b3903 100644 --- a/src/UI/WPF/Views/HelpDialog.xaml +++ b/src/UI/WPF/Views/HelpDialog.xaml @@ -88,6 +88,8 @@ NavisworksTransport 帮助对话框 - 采用与主界面一致的Navisworks 2026 Style="{StaticResource DialogContentTextStyle}"/> +