- 修复编辑角度窗口空输入反复确认崩溃 (UpdateSourceTrigger LostFocus→Explicit) - 修复Y轴角度修正在动画播放时双重叠加导致丢失 (删除 ApplyPlanarTrackedPose 中重复的 ResolvePlanarHostUpCorrectionRadians) - 修复先开文档后加载插件时路径数据库未连接 (InitializePathPlanningManager 中加入数据库连接) - 重构: TryConnectPathDatabase 辅助方法; TryUpdateNumberTextBox 拆分验证逻辑 - 更新 AGENTS.md 精简版; 更新 CHANGELOG.md 0.15.1; 更新 todo_features.md 5/18 条目
930 lines
36 KiB
C#
930 lines
36 KiB
C#
using Autodesk.Navisworks.Api;
|
||
using Autodesk.Navisworks.Api.Plugins;
|
||
using System;
|
||
using System.Threading.Tasks;
|
||
using System.Windows.Forms;
|
||
using System.Windows.Forms.Integration;
|
||
using NavisworksTransport.Core;
|
||
using NavisworksTransport.Core.Animation;
|
||
using NavisworksTransport.Core.Config;
|
||
using NavisworksTransport.Core.Services;
|
||
using NavisworksTransport.Utils;
|
||
using NavisworksTransport.Utils.CoordinateSystem;
|
||
using NavisApplication = Autodesk.Navisworks.Api.Application;
|
||
|
||
namespace NavisworksTransport
|
||
{
|
||
/// <summary>
|
||
/// 物流属性变更事件参数
|
||
/// </summary>
|
||
public class LogisticsAttributeChangedEventArgs : EventArgs
|
||
{
|
||
public string OperationType { get; set; } // "修改" 或 "删除"
|
||
public int AffectedCount { get; set; } // 受影响的模型数量
|
||
public string Message { get; set; } // 操作描述信息
|
||
|
||
public LogisticsAttributeChangedEventArgs(string operationType, int affectedCount, string message = "")
|
||
{
|
||
OperationType = operationType;
|
||
AffectedCount = affectedCount;
|
||
Message = message;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 全局异常处理工具类
|
||
/// </summary>
|
||
public static class GlobalExceptionHandler
|
||
{
|
||
private static bool _isInitialized = false;
|
||
|
||
/// <summary>
|
||
/// 初始化全局异常处理器
|
||
/// </summary>
|
||
public static void Initialize()
|
||
{
|
||
if (_isInitialized) return;
|
||
|
||
try
|
||
{
|
||
// 捕获未处理的异常
|
||
AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
|
||
|
||
// 捕获任务调度器未观察到的异常
|
||
System.Threading.Tasks.TaskScheduler.UnobservedTaskException += OnUnobservedTaskException;
|
||
|
||
// 直接注册WinForms线程异常处理器(不调用SetUnhandledExceptionMode,避免插件环境下的警告)
|
||
System.Windows.Forms.Application.ThreadException += OnThreadException;
|
||
|
||
_isInitialized = true;
|
||
|
||
LogManager.Info("[全局异常] 全局异常处理器已初始化");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[全局异常] 初始化异常处理器失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 处理未捕获的异常
|
||
/// </summary>
|
||
private static void OnUnhandledException(object sender, UnhandledExceptionEventArgs e)
|
||
{
|
||
try
|
||
{
|
||
Exception ex = e.ExceptionObject as Exception;
|
||
string message = ex?.Message ?? "未知异常";
|
||
string stackTrace = ex?.StackTrace ?? "无堆栈信息";
|
||
|
||
LogManager.Error($"[全局异常] ===== 未处理异常发生 =====");
|
||
LogManager.Error($"[全局异常] 异常类型: {ex?.GetType().FullName ?? "Unknown"}");
|
||
LogManager.Error($"[全局异常] 异常消息: {message}");
|
||
LogManager.Error($"[全局异常] 是否终止进程: {e.IsTerminating}");
|
||
LogManager.Error($"[全局异常] 当前线程ID: {System.Threading.Thread.CurrentThread.ManagedThreadId}");
|
||
LogManager.Error($"[全局异常] 当前线程名称: {System.Threading.Thread.CurrentThread.Name ?? "未命名"}");
|
||
|
||
// 检查是否为特定类型的异常
|
||
if (ex is OutOfMemoryException)
|
||
{
|
||
LogManager.Error($"[全局异常] ⚠️ 内存不足异常");
|
||
var memoryUsage = GC.GetTotalMemory(false);
|
||
LogManager.Error($"[全局异常] 当前内存使用: {memoryUsage / 1024 / 1024:F1} MB");
|
||
}
|
||
else if (ex is StackOverflowException)
|
||
{
|
||
LogManager.Error($"[全局异常] ⚠️ 堆栈溢出异常");
|
||
}
|
||
else if (ex is AccessViolationException)
|
||
{
|
||
LogManager.Error($"[全局异常] ⚠️ 访问违规异常");
|
||
}
|
||
else if (ex is System.Runtime.InteropServices.SEHException)
|
||
{
|
||
LogManager.Error($"[全局异常] ⚠️ 结构化异常处理异常");
|
||
}
|
||
|
||
LogManager.Error($"[全局异常] 堆栈跟踪:");
|
||
LogManager.Error(stackTrace);
|
||
|
||
if (ex?.InnerException != null)
|
||
{
|
||
LogManager.Error($"[全局异常] 内部异常: {ex.InnerException.GetType().Name}: {ex.InnerException.Message}");
|
||
LogManager.Error($"[全局异常] 内部异常堆栈: {ex.InnerException.StackTrace}");
|
||
}
|
||
|
||
// 显示用户友好的错误信息
|
||
ShowErrorDialog("程序发生未预期的错误", message, ex);
|
||
|
||
// 尝试恢复关键组件
|
||
TryRecoverComponents();
|
||
|
||
LogManager.Error($"[全局异常] ===== 异常处理完成 =====");
|
||
}
|
||
catch
|
||
{
|
||
// 异常处理中的异常,保持静默
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 处理未观察到的任务异常
|
||
/// </summary>
|
||
private static void OnUnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
|
||
{
|
||
try
|
||
{
|
||
Exception ex = e.Exception;
|
||
LogManager.Error($"[全局异常] 未观察任务异常: {ex.Message}");
|
||
LogManager.Error($"[全局异常] 堆栈信息: {ex.StackTrace}");
|
||
LogManager.Error($"[全局异常] 异常类型: {ex.GetType().Name}");
|
||
|
||
// 标记异常已被观察,防止应用程序终止
|
||
e.SetObserved();
|
||
|
||
// 显示用户友好的错误信息
|
||
ShowErrorDialog("程序发生后台任务错误", ex.Message, ex);
|
||
|
||
// 尝试恢复关键组件
|
||
TryRecoverComponents();
|
||
}
|
||
catch
|
||
{
|
||
// 异常处理中的异常,保持静默
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 处理线程异常
|
||
/// </summary>
|
||
private static void OnThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
|
||
{
|
||
try
|
||
{
|
||
Exception ex = e.Exception;
|
||
LogManager.Error($"[全局异常] 线程异常: {ex.Message}");
|
||
LogManager.Error($"[全局异常] 堆栈信息: {ex.StackTrace}");
|
||
LogManager.Error($"[全局异常] 异常类型: {ex.GetType().Name}");
|
||
|
||
// 显示用户友好的错误信息
|
||
ShowErrorDialog("程序发生线程错误", ex.Message, ex);
|
||
|
||
// 尝试恢复关键组件
|
||
TryRecoverComponents();
|
||
}
|
||
catch
|
||
{
|
||
// 异常处理中的异常,保持静默
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 安全执行方法,捕获所有异常
|
||
/// </summary>
|
||
public static T SafeExecute<T>(Func<T> action, T defaultValue = default, string operationName = "操作")
|
||
{
|
||
try
|
||
{
|
||
return action();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[安全执行] {operationName}失败: {ex.Message}");
|
||
LogManager.Error($"[安全执行] 堆栈信息: {ex.StackTrace}");
|
||
LogManager.Error($"[安全执行] 异常类型: {ex.GetType().Name}");
|
||
|
||
// 对于用户操作,显示简短的错误提示
|
||
if (!string.IsNullOrEmpty(operationName) && operationName != "操作")
|
||
{
|
||
ShowErrorToast($"{operationName}失败: {ex.Message}");
|
||
}
|
||
|
||
return defaultValue;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 安全执行方法(无返回值)
|
||
/// </summary>
|
||
public static void SafeExecute(Action action, string operationName = "操作")
|
||
{
|
||
SafeExecute(() => { action(); return true; }, false, operationName);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 显示错误对话框
|
||
/// </summary>
|
||
private static void ShowErrorDialog(string title, string message, Exception ex = null)
|
||
{
|
||
try
|
||
{
|
||
string detailMessage = message;
|
||
if (ex != null)
|
||
{
|
||
detailMessage += $"\n\n技术详情: {ex.GetType().Name}";
|
||
if (!string.IsNullOrEmpty(ex.Message))
|
||
{
|
||
detailMessage += $"\n错误信息: {ex.Message}";
|
||
}
|
||
}
|
||
|
||
MessageBox.Show(detailMessage, title, MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||
}
|
||
catch
|
||
{
|
||
// 如果连错误对话框都显示不了,就保持静默
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 显示简短的错误提示
|
||
/// </summary>
|
||
private static void ShowErrorToast(string message)
|
||
{
|
||
try
|
||
{
|
||
MessageBox.Show(message, "错误提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||
}
|
||
catch
|
||
{
|
||
// 保持静默
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 清理 Clash Detective 集成相关资源
|
||
/// </summary>
|
||
public static void CleanupClashDetectiveIntegration()
|
||
{
|
||
try
|
||
{
|
||
LogManager.Info("[清理] 开始清理 Clash Detective 集成...");
|
||
|
||
// 清理 Clash Detective 集成
|
||
ClashDetectiveIntegration.Instance.Cleanup();
|
||
|
||
LogManager.Info("[清理] Clash Detective 集成清理完成");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[清理] Clash Detective 集成清理失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 尝试恢复关键组件
|
||
/// </summary>
|
||
private static void TryRecoverComponents()
|
||
{
|
||
try
|
||
{
|
||
LogManager.Info("[全局异常] 尝试恢复关键组件...");
|
||
|
||
// 尝试重置路径编辑状态
|
||
var activeManager = PathPlanningManager.GetActivePathManager();
|
||
if (activeManager != null)
|
||
{
|
||
activeManager.ResetPathEditState();
|
||
LogManager.Info("[全局异常] 已重置路径编辑状态");
|
||
}
|
||
|
||
// 尝试清除临时高亮
|
||
if (NavisApplication.ActiveDocument?.Models != null)
|
||
{
|
||
ModelHighlightHelper.ClearAllHighlights();
|
||
LogManager.Info("[全局异常] 已清除临时材质");
|
||
}
|
||
|
||
// 清理 Clash Detective 集成
|
||
CleanupClashDetectiveIntegration();
|
||
|
||
LogManager.Info("[全局异常] 组件恢复完成");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[全局异常] 组件恢复失败: {ex.Message}");
|
||
}
|
||
}
|
||
}
|
||
|
||
[Plugin("NavisworksTransport.Main", "Tian",
|
||
DisplayName = "物流路径规划",
|
||
ToolTip = "物流路径规划和动画仿真插件")]
|
||
[DockPanePlugin(420, 700, FixedSize = false, AutoScroll = true)]
|
||
public class Main : DockPanePlugin
|
||
{
|
||
/// <summary>
|
||
/// 构造函数,提前初始化全局异常处理器
|
||
/// </summary>
|
||
// 状态跟踪变量(用于观察Models.CollectionChanged事件)
|
||
private bool _hasModels = false;
|
||
private int _eventCount = 0;
|
||
private DateTime _pluginStartTime = DateTime.Now;
|
||
|
||
public Main()
|
||
{
|
||
// 在任何控件创建之前初始化全局异常处理器
|
||
GlobalExceptionHandler.Initialize();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 插件加载后调用(Navisworks插件生命周期)
|
||
/// </summary>
|
||
protected override void OnLoaded()
|
||
{
|
||
base.OnLoaded();
|
||
|
||
_pluginStartTime = DateTime.Now; // 记录插件启动时间
|
||
|
||
LogManager.Info("[文档管理] 插件OnLoaded - 开始初始化");
|
||
|
||
//显式初始化配置管理器,确保配置加载完成
|
||
try
|
||
{
|
||
var config = ConfigManager.Instance.Current;
|
||
LogManager.Info($"[配置管理] 配置已加载");
|
||
|
||
// 检查配置兼容性
|
||
if (ConfigManager.Instance.HasMissingConfigItems)
|
||
{
|
||
var message = ConfigManager.Instance.GetConfigCompatibilityMessage();
|
||
LogManager.Warning($"[配置管理] {message}");
|
||
|
||
// 延迟显示配置兼容性提示(避免在插件加载时阻塞)
|
||
Task.Delay(3000).ContinueWith(_ =>
|
||
{
|
||
try
|
||
{
|
||
// 使用 Navisworks 的 API 显示消息框
|
||
System.Windows.Application.Current.Dispatcher.Invoke(() =>
|
||
{
|
||
System.Windows.MessageBox.Show(
|
||
message,
|
||
"配置文件需要升级",
|
||
System.Windows.MessageBoxButton.OK,
|
||
System.Windows.MessageBoxImage.Warning);
|
||
});
|
||
}
|
||
catch (Exception msgEx)
|
||
{
|
||
LogManager.Error($"[配置管理] 显示兼容性提示失败: {msgEx.Message}");
|
||
}
|
||
});
|
||
}
|
||
|
||
// 注意:坐标系初始化推迟到 InitializeManagers() 中进行
|
||
// 因为此时文档可能尚未加载,无法正确检测坐标系
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[配置管理] 配置加载失败: {ex.Message}");
|
||
}
|
||
|
||
// 订阅文档事件
|
||
SubscribeToDocumentEvents();
|
||
|
||
// 启动本地测试自动化 HTTP 控制面
|
||
TestAutomationHttpService.Instance.Start();
|
||
|
||
// 检查是否已有活动文档且包含模型
|
||
var activeDoc = NavisApplication.ActiveDocument;
|
||
if (activeDoc != null && activeDoc.Models != null && activeDoc.Models.Count > 0)
|
||
{
|
||
LogManager.Info("[文档管理] 插件加载时发现包含模型的活动文档,立即初始化管理器");
|
||
// 🔥 插件加载时不是虚拟物体变化,传入 false
|
||
InitializeManagers(isVirtualObjectChange: false);
|
||
}
|
||
else if (activeDoc != null)
|
||
{
|
||
LogManager.Info("[文档管理] 插件加载时发现空文档(无模型),等待文档加载完成");
|
||
}
|
||
else
|
||
{
|
||
LogManager.Info("[文档管理] 插件加载时无活动文档");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 插件卸载前调用(Navisworks插件生命周期)
|
||
/// </summary>
|
||
protected override void OnUnloading()
|
||
{
|
||
LogManager.Info("[文档管理] 插件OnUnloading - 开始清理");
|
||
|
||
// 取消订阅文档事件
|
||
UnsubscribeFromDocumentEvents();
|
||
|
||
// 清理管理器
|
||
CleanupManagers();
|
||
|
||
// 停止本地测试自动化 HTTP 控制面
|
||
TestAutomationHttpService.Instance.Stop();
|
||
|
||
base.OnUnloading();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 订阅文档相关事件
|
||
/// </summary>
|
||
private void SubscribeToDocumentEvents()
|
||
{
|
||
try
|
||
{
|
||
// 订阅文档切换前事件(保留用于未来兼容性)
|
||
NavisApplication.ActiveDocumentChanging += OnActiveDocumentChanging;
|
||
// 订阅文档切换后事件(保留用于未来兼容性)
|
||
NavisApplication.ActiveDocumentChanged += OnActiveDocumentChanged;
|
||
|
||
// 订阅模型集合变化事件(SDI架构下的真正文档变化事件)
|
||
if (NavisApplication.ActiveDocument != null)
|
||
{
|
||
NavisApplication.ActiveDocument.Models.CollectionChanged += OnModelsCollectionChanged;
|
||
LogManager.Info("[文档管理] 已订阅模型集合变化事件");
|
||
}
|
||
|
||
LogManager.Info("[文档管理] 已订阅文档相关事件(Changing + Changed + Models.CollectionChanged)");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[文档管理] 订阅文档事件失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 活动文档切换前事件处理
|
||
/// </summary>
|
||
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();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 活动文档切换事件处理
|
||
/// </summary>
|
||
private void OnActiveDocumentChanged(object sender, EventArgs e)
|
||
{
|
||
LogManager.Info($"[文档管理] 文档已切换到: {NavisApplication.ActiveDocument?.FileName ?? "无活动文档"}");
|
||
|
||
// 清除单位转换缓存(文档切换后单位可能不同)
|
||
Utils.UnitsConverter.ClearCache();
|
||
|
||
GlobalExceptionHandler.SafeExecute(() =>
|
||
{
|
||
var activeDoc = NavisApplication.ActiveDocument;
|
||
|
||
// 如果有新的活动文档,订阅其Models.CollectionChanged事件
|
||
if (activeDoc != null)
|
||
{
|
||
try
|
||
{
|
||
activeDoc.Models.CollectionChanged += OnModelsCollectionChanged;
|
||
LogManager.Info("[文档管理] 已订阅新文档的模型集合变化事件");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Warning($"[文档管理] 订阅新文档事件时出现警告: {ex.Message}");
|
||
}
|
||
|
||
// 🔥 优化:检测是否是虚拟物体引起的文档变化
|
||
bool isVirtualObjectChange = IsVirtualObjectChange(activeDoc);
|
||
if (isVirtualObjectChange)
|
||
{
|
||
LogManager.Info("[文档管理] 检测到虚拟物体文档变化,使用快速初始化模式");
|
||
}
|
||
|
||
InitializeManagers(isVirtualObjectChange);
|
||
}
|
||
else
|
||
{
|
||
LogManager.Info("[文档管理] 当前无活动文档");
|
||
}
|
||
|
||
}, "处理活动文档切换事件");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检测是否是虚拟物体引起的文档变化
|
||
/// 🔥 优化:通过 VirtualObjectManager 的标志精确检测,避免误触发缓存重建
|
||
/// </summary>
|
||
private bool IsVirtualObjectChange(Document doc)
|
||
{
|
||
try
|
||
{
|
||
// 🔥 优先检查 VirtualObjectManager 的更新标志(最准确)
|
||
if (VirtualObjectManager.Instance.IsUpdatingVirtualObject)
|
||
{
|
||
LogManager.Debug("[文档管理] VirtualObjectManager 正在更新,判定为虚拟物体变化");
|
||
return true;
|
||
}
|
||
|
||
if (doc?.Models == null) return false;
|
||
|
||
// 备选:检查是否包含虚拟物体模型
|
||
foreach (var model in doc.Models)
|
||
{
|
||
var modelName = model.RootItem?.DisplayName ?? "";
|
||
if (modelName.Contains("unit_cube") || modelName.Contains("VirtualObject"))
|
||
{
|
||
LogManager.Debug($"[文档管理] 检测到虚拟物体模型: {modelName}");
|
||
return true;
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Debug($"[文档管理] 检测虚拟物体变化时出错: {ex.Message}");
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 模型集合变化事件处理
|
||
/// </summary>
|
||
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($"[文档管理] *** 状态分析: 从无到有(可能是打开文档)***");
|
||
|
||
// 文档加载完成,仅连接已存在的数据库,不为未使用插件的文档自动创建 .db
|
||
TryConnectPathDatabase(activeDoc);
|
||
}
|
||
else if (_hasModels && !currentHasModels)
|
||
{
|
||
LogManager.Info($"[文档管理] *** 状态分析: 从有到无(可能是关闭文档)***");
|
||
}
|
||
else if (!_hasModels && !currentHasModels)
|
||
{
|
||
LogManager.Info($"[文档管理] *** 状态分析: 保持无模型状态 ***");
|
||
}
|
||
else if (_hasModels && currentHasModels)
|
||
{
|
||
LogManager.Info($"[文档管理] *** 状态分析: 保持有模型状态(模型数量变化)***");
|
||
}
|
||
|
||
LogManager.Info("========================================");
|
||
|
||
// 更新状态
|
||
_hasModels = currentHasModels;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 为当前文档初始化坐标系
|
||
/// 在文档加载完成后调用,确保可以正确检测 Document.UpVector
|
||
/// </summary>
|
||
private void InitializeCoordinateSystemForDocument()
|
||
{
|
||
try
|
||
{
|
||
var config = ConfigManager.Instance.Current;
|
||
if (config?.CoordinateSystem == null)
|
||
{
|
||
LogManager.Warning("[坐标系] 配置中缺少坐标系设置,使用默认自动检测");
|
||
CoordinateSystemManager.Instance.Initialize(CoordinateSystemType.AutoDetect);
|
||
return;
|
||
}
|
||
|
||
// 如果已经初始化且不是 AutoDetect 模式,则跳过
|
||
// 这样用户手动设置的坐标系不会在文档切换时被覆盖
|
||
if (CoordinateSystemManager.Instance.Current != null &&
|
||
CoordinateSystemManager.Instance.ConfiguredType != CoordinateSystemType.AutoDetect)
|
||
{
|
||
LogManager.Debug("[坐标系] 使用手动指定的坐标系,跳过自动检测");
|
||
return;
|
||
}
|
||
|
||
// 解析坐标系类型配置
|
||
CoordinateSystemType coordType;
|
||
var typeString = config.CoordinateSystem.Type?.Trim() ?? "AutoDetect";
|
||
|
||
switch (typeString.ToLowerInvariant())
|
||
{
|
||
case "zup":
|
||
case "z-up":
|
||
coordType = CoordinateSystemType.ZUp;
|
||
break;
|
||
case "yup":
|
||
case "y-up":
|
||
coordType = CoordinateSystemType.YUp;
|
||
break;
|
||
case "autodetect":
|
||
case "auto":
|
||
default:
|
||
coordType = CoordinateSystemType.AutoDetect;
|
||
break;
|
||
}
|
||
|
||
// 初始化坐标系
|
||
CoordinateSystemManager.Instance.Initialize(coordType);
|
||
|
||
var currentCS = CoordinateSystemManager.Instance.Current;
|
||
LogManager.Info($"[坐标系] 初始化完成 - {currentCS}");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[坐标系] 初始化失败: {ex.Message},使用默认 Z-Up");
|
||
CoordinateSystemManager.Instance.Initialize(CoordinateSystemType.ZUp);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 初始化各个管理器
|
||
/// </summary>
|
||
/// <param name="isVirtualObjectChange">是否由虚拟物体变化引起</param>
|
||
private void InitializeManagers(bool isVirtualObjectChange = false)
|
||
{
|
||
try
|
||
{
|
||
var activeDoc = NavisApplication.ActiveDocument;
|
||
if (activeDoc == null)
|
||
{
|
||
LogManager.Warning("[文档管理] 无活动文档,跳过初始化");
|
||
return;
|
||
}
|
||
|
||
LogManager.Info($"[文档管理] 开始为文档 '{activeDoc.FileName ?? "未保存"}' 初始化管理器...");
|
||
|
||
// 初始化坐标系(文档已加载,可以正确检测)
|
||
InitializeCoordinateSystemForDocument();
|
||
|
||
// 连接文档对应的路径数据库
|
||
TryConnectPathDatabase(activeDoc);
|
||
|
||
// 通知DocumentStateManager文档已就绪
|
||
DocumentStateManager.Instance.OnDocumentReady();
|
||
|
||
// 🔥 优化:虚拟物体变化时不清除缓存,避免80秒重建
|
||
if (isVirtualObjectChange)
|
||
{
|
||
LogManager.Info("[文档管理] 检测到虚拟物体变化,保留现有缓存,仅更新运动物体引用");
|
||
// 只清除运动物体引用,不清除基础缓存
|
||
ClashDetectiveIntegration.ClearAnimatedObject();
|
||
}
|
||
else
|
||
{
|
||
// 清理并重新初始化各种缓存
|
||
ClashDetectiveIntegration.ClearAllCaches();
|
||
}
|
||
|
||
LogManager.Info("[文档管理] 管理器初始化完成");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[文档管理] 初始化管理器失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 清理所有管理器状态
|
||
/// </summary>
|
||
private void CleanupManagers()
|
||
{
|
||
try
|
||
{
|
||
LogManager.Info("[文档管理] 开始清理管理器...");
|
||
|
||
// 通知DocumentStateManager文档已失效
|
||
DocumentStateManager.Instance.OnDocumentInvalidated();
|
||
|
||
// 停止所有动画(程序关闭专用)
|
||
try
|
||
{
|
||
var animationManager = PathAnimationManager.GetInstance();
|
||
if (animationManager != null)
|
||
{
|
||
animationManager.ShutdownAnimation(); // 使用关闭专用方法,不执行UI操作
|
||
animationManager.ClearReferences();
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Warning($"[文档管理] 清理动画管理器时出现警告: {ex.Message}");
|
||
}
|
||
|
||
// 清理路径规划管理器
|
||
try
|
||
{
|
||
var pathManager = PathPlanningManager.GetActivePathManager();
|
||
if (pathManager != null)
|
||
{
|
||
pathManager.ResetPathEditState();
|
||
pathManager.ClearInvalidReferences();
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Warning($"[文档管理] 清理路径管理器时出现警告: {ex.Message}");
|
||
}
|
||
|
||
// 清理路径渲染插件的可视化
|
||
try
|
||
{
|
||
var renderPlugin = PathPointRenderPlugin.Instance;
|
||
if (renderPlugin != null)
|
||
{
|
||
renderPlugin.ClearAllPaths();
|
||
LogManager.Info("[文档管理] 已清理路径可视化");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Warning($"[文档管理] 清理路径可视化时出现警告: {ex.Message}");
|
||
}
|
||
|
||
// 清理碰撞检测缓存
|
||
try
|
||
{
|
||
ClashDetectiveIntegration.Instance?.ClearCollisionCache();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Warning($"[文档管理] 清理碰撞检测缓存时出现警告: {ex.Message}");
|
||
}
|
||
|
||
// 清理IdleEventManager任务
|
||
try
|
||
{
|
||
IdleEventManager.Instance?.Dispose();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Warning($"[文档管理] 清理Idle事件管理器时出现警告: {ex.Message}");
|
||
}
|
||
|
||
// 清理虚拟物体
|
||
try
|
||
{
|
||
VirtualObjectManager.Instance.Cleanup();
|
||
LogManager.Info("[文档管理] 已清理虚拟物体");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Warning($"[文档管理] 清理虚拟物体时出现警告: {ex.Message}");
|
||
}
|
||
|
||
// 清除临时材质
|
||
try
|
||
{
|
||
var activeDoc = NavisApplication.ActiveDocument;
|
||
if (activeDoc?.Models != null)
|
||
{
|
||
ModelHighlightHelper.ClearAllHighlights();
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Warning($"[文档管理] 清除临时材质时出现警告: {ex.Message}");
|
||
}
|
||
|
||
LogManager.Info("[文档管理] 管理器清理完成");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[文档管理] 清理管理器失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
|
||
public override Control CreateControlPane()
|
||
{
|
||
try
|
||
{
|
||
LogManager.Info("=== 开始创建DockPane控制面板 ===");
|
||
|
||
// 使用官方示例的最简单实现
|
||
LogManager.Info("创建ElementHost...");
|
||
ElementHost eh = new ElementHost
|
||
{
|
||
AutoSize = true
|
||
};
|
||
|
||
LogManager.Info("创建LogisticsControlPanel...");
|
||
eh.Child = new UI.WPF.LogisticsControlPanel();
|
||
|
||
LogManager.Info("调用CreateControl()...");
|
||
eh.CreateControl();
|
||
|
||
LogManager.Info("=== DockPane控制面板创建成功 ===");
|
||
return eh;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"=== CreateControlPane失败: {ex.Message} ===");
|
||
LogManager.Error($"异常类型: {ex.GetType().FullName}");
|
||
LogManager.Error($"堆栈跟踪: {ex.StackTrace}");
|
||
|
||
// 返回null会导致Navisworks立即调用DestroyControlPane
|
||
throw; // 重新抛出异常让Navisworks知道创建失败
|
||
}
|
||
}
|
||
|
||
public override void DestroyControlPane(Control pane)
|
||
{
|
||
GlobalExceptionHandler.SafeExecute(() =>
|
||
{
|
||
LogManager.Info("开始销毁DockPane控制面板");
|
||
|
||
// 清理WPF控件资源
|
||
if (pane is ElementHost elementHost && elementHost.Child is UI.WPF.LogisticsControlPanel wpfControl)
|
||
{
|
||
// 清理WPF控件资源
|
||
wpfControl.Cleanup();
|
||
elementHost.Child = null;
|
||
}
|
||
|
||
// 取消订阅文档事件
|
||
UnsubscribeFromDocumentEvents();
|
||
|
||
// 释放控件资源
|
||
pane?.Dispose();
|
||
|
||
LogManager.Info("DockPane控制面板销毁完成");
|
||
|
||
}, "销毁控制面板");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 取消订阅文档相关事件
|
||
/// </summary>
|
||
private void UnsubscribeFromDocumentEvents()
|
||
{
|
||
try
|
||
{
|
||
// 取消订阅文档切换事件
|
||
NavisApplication.ActiveDocumentChanging -= OnActiveDocumentChanging;
|
||
NavisApplication.ActiveDocumentChanged -= OnActiveDocumentChanged;
|
||
|
||
// 取消订阅当前文档的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)
|
||
{
|
||
LogManager.Error($"[文档管理] 取消订阅文档事件失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 连接活动文档对应的路径数据库(仅连接已存在的 .db,不新建)。
|
||
/// </summary>
|
||
private static void TryConnectPathDatabase(Document activeDoc)
|
||
{
|
||
if (activeDoc == null || string.IsNullOrEmpty(activeDoc.FileName))
|
||
{
|
||
return;
|
||
}
|
||
|
||
var pathManager = PathPlanningManager.GetActivePathManager();
|
||
pathManager?.DatabaseInitialize(createIfMissing: false);
|
||
}
|
||
}
|
||
|
||
}
|