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}"/>
+
diff --git a/src/UI/WPF/Views/SystemManagementView.xaml b/src/UI/WPF/Views/SystemManagementView.xaml
index e3bfd42..3f974d5 100644
--- a/src/UI/WPF/Views/SystemManagementView.xaml
+++ b/src/UI/WPF/Views/SystemManagementView.xaml
@@ -170,11 +170,11 @@ NavisworksTransport 系统管理页签视图 - 采用与其他页签一致的Nav
Margin="0,0,20,0"
VerticalAlignment="Center"
ToolTip="显示标准圆柱形连线"/>
-
+ ToolTip="显示通行空间(矩形通道)"/>