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.Utils;
using NavisworksTransport.Utils.CoordinateSystem;
using NavisApplication = Autodesk.Navisworks.Api.Application;
namespace NavisworksTransport
{
///
/// 物流属性变更事件参数
///
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;
}
}
///
/// 全局异常处理工具类
///
public static class GlobalExceptionHandler
{
private static bool _isInitialized = false;
///
/// 初始化全局异常处理器
///
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}");
}
}
///
/// 处理未捕获的异常
///
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
{
// 异常处理中的异常,保持静默
}
}
///
/// 处理未观察到的任务异常
///
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
{
// 异常处理中的异常,保持静默
}
}
///
/// 处理线程异常
///
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
{
// 异常处理中的异常,保持静默
}
}
///
/// 安全执行方法,捕获所有异常
///
public static T SafeExecute(Func 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;
}
}
///
/// 安全执行方法(无返回值)
///
public static void SafeExecute(Action action, string operationName = "操作")
{
SafeExecute(() => { action(); return true; }, false, operationName);
}
///
/// 显示错误对话框
///
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
{
// 如果连错误对话框都显示不了,就保持静默
}
}
///
/// 显示简短的错误提示
///
private static void ShowErrorToast(string message)
{
try
{
MessageBox.Show(message, "错误提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
catch
{
// 保持静默
}
}
///
/// 清理 Clash Detective 集成相关资源
///
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}");
}
}
///
/// 尝试恢复关键组件
///
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
{
///
/// 构造函数,提前初始化全局异常处理器
///
// 状态跟踪变量(用于观察Models.CollectionChanged事件)
private bool _hasModels = false;
private int _eventCount = 0;
private DateTime _pluginStartTime = DateTime.Now;
public Main()
{
// 在任何控件创建之前初始化全局异常处理器
GlobalExceptionHandler.Initialize();
}
///
/// 插件加载后调用(Navisworks插件生命周期)
///
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();
// 检查是否已有活动文档且包含模型
var activeDoc = NavisApplication.ActiveDocument;
if (activeDoc != null && activeDoc.Models != null && activeDoc.Models.Count > 0)
{
LogManager.Info("[文档管理] 插件加载时发现包含模型的活动文档,立即初始化管理器");
InitializeManagers();
}
else if (activeDoc != null)
{
LogManager.Info("[文档管理] 插件加载时发现空文档(无模型),等待文档加载完成");
}
else
{
LogManager.Info("[文档管理] 插件加载时无活动文档");
}
}
///
/// 插件卸载前调用(Navisworks插件生命周期)
///
protected override void OnUnloading()
{
LogManager.Info("[文档管理] 插件OnUnloading - 开始清理");
// 取消订阅文档事件
UnsubscribeFromDocumentEvents();
// 清理管理器
CleanupManagers();
base.OnUnloading();
}
///
/// 订阅文档相关事件
///
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}");
}
}
///
/// 活动文档切换前事件处理
///
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 ?? "无活动文档"}");
// 清除单位转换缓存(文档切换后单位可能不同)
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}");
}
InitializeManagers();
}
else
{
LogManager.Info("[文档管理] 当前无活动文档");
}
}, "处理活动文档切换事件");
}
///
/// 模型集合变化事件处理
///
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($"[文档管理] *** 状态分析: 从无到有(可能是打开文档)***");
// 文档加载完成,初始化PathDatabase
if (!string.IsNullOrEmpty(activeDoc?.FileName))
{
try
{
var pathManager = PathPlanningManager.GetActivePathManager();
if (pathManager != null)
{
pathManager.DatabaseInitialize();
}
}
catch (Exception ex)
{
// 数据库初始化失败是严重错误,需要明确提示用户
var errorMsg = $"数据库初始化失败:{ex.Message}\n\n";
errorMsg += "可能的原因:\n";
errorMsg += "1. 文件夹权限不足\n";
errorMsg += "2. 磁盘空间不足\n";
errorMsg += "3. 数据库文件被锁定\n";
errorMsg += "4. SQLite 组件损坏\n\n";
errorMsg += "详细信息请查看日志文件。";
LogManager.Error($"[文档管理] 数据库初始化失败: {ex.Message}", ex);
LogManager.Error($"[文档管理] 文档路径: {activeDoc.FileName}");
LogManager.Error($"[文档管理] 异常类型: {ex.GetType().Name}");
LogManager.Error($"[文档管理] 堆栈信息: {ex.StackTrace}");
System.Windows.MessageBox.Show(
errorMsg,
"数据库初始化失败",
System.Windows.MessageBoxButton.OK,
System.Windows.MessageBoxImage.Error
);
// 不重新抛出,避免插件崩溃,但批处理等功能将不可用
}
}
}
else if (_hasModels && !currentHasModels)
{
LogManager.Info($"[文档管理] *** 状态分析: 从有到无(可能是关闭文档)***");
}
else if (!_hasModels && !currentHasModels)
{
LogManager.Info($"[文档管理] *** 状态分析: 保持无模型状态 ***");
}
else if (_hasModels && currentHasModels)
{
LogManager.Info($"[文档管理] *** 状态分析: 保持有模型状态(模型数量变化)***");
}
LogManager.Info("========================================");
// 更新状态
_hasModels = currentHasModels;
}
///
/// 为当前文档初始化坐标系
/// 在文档加载完成后调用,确保可以正确检测 Document.UpVector
///
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);
}
}
///
/// 初始化各个管理器
///
private void InitializeManagers()
{
try
{
var activeDoc = NavisApplication.ActiveDocument;
if (activeDoc == null)
{
LogManager.Warning("[文档管理] 无活动文档,跳过初始化");
return;
}
LogManager.Info($"[文档管理] 开始为文档 '{activeDoc.FileName ?? "未保存"}' 初始化管理器...");
// 初始化坐标系(文档已加载,可以正确检测)
InitializeCoordinateSystemForDocument();
// 通知DocumentStateManager文档已就绪
DocumentStateManager.Instance.OnDocumentReady();
// 清理并重新初始化各种缓存
ClashDetectiveIntegration.ClearAllCaches();
LogManager.Info("[文档管理] 管理器初始化完成");
}
catch (Exception ex)
{
LogManager.Error($"[文档管理] 初始化管理器失败: {ex.Message}");
}
}
///
/// 清理所有管理器状态
///
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
{
VirtualVehicleManager.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控制面板销毁完成");
}, "销毁控制面板");
}
///
/// 取消订阅文档相关事件
///
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}");
}
}
}
}