diff --git a/src/Commands/ViewCollisionReportCommand.cs b/src/Commands/ViewCollisionReportCommand.cs index 0aa68cb..6ff06d9 100644 --- a/src/Commands/ViewCollisionReportCommand.cs +++ b/src/Commands/ViewCollisionReportCommand.cs @@ -91,6 +91,20 @@ namespace NavisworksTransport.Commands /// public class ViewCollisionReportCommand : CommandBase { + #region 静态缓存机制 + + /// + /// 碰撞报告结果缓存 - 使用测试名称作为键 + /// + private static readonly Dictionary _reportCache = new Dictionary(); + + /// + /// 缓存操作锁对象 + /// + private static readonly object _cacheLock = new object(); + + #endregion + private readonly CollisionReportParameters _parameters; public ViewCollisionReportCommand(CollisionReportParameters parameters = null) @@ -110,62 +124,101 @@ namespace NavisworksTransport.Commands UpdateProgress(10, "开始生成碰撞报告..."); + // 检查缓存 + var testNameKey = GetLatestTestNameForCache(); + if (!string.IsNullOrEmpty(testNameKey)) + { + lock (_cacheLock) + { + if (_reportCache.ContainsKey(testNameKey)) + { + var cachedResult = _reportCache[testNameKey]; + LogManager.Info($"使用缓存的碰撞报告: {testNameKey}"); + + UpdateProgress(100, "使用缓存报告"); + + // 显示缓存的报告 + ShowReport(cachedResult); + + var cachedMessage = cachedResult.TotalCollisions > 0 ? + $"碰撞报告显示完成(缓存):发现 {cachedResult.TotalCollisions} 个碰撞" : + "碰撞报告显示完成(缓存):未发现碰撞"; + + return PathPlanningResult.Success(cachedResult, cachedMessage); + } + else + { + LogManager.Info($"未找到缓存报告,开始生成新报告: {testNameKey}"); + } + } + } + else + { + LogManager.Warning("无法获取测试名称,跳过缓存检查"); + } + var result = new CollisionReportResult(); try { - await Task.Run(() => + UpdateProgress(30, "收集碰撞数据..."); + + // 收集所有碰撞数据及统计信息(同步执行,避免 COM 线程问题) + var collisionData = CollectAllCollisionData(); + result.AllCollisions = collisionData.AllCollisions; + + // 修改统计数据 + var clashIntegration = ClashDetectiveIntegration.Instance; + result.AnimationCollisions = clashIntegration?.AnimationCollisionCount ?? 0; // 检测点数量 + result.TotalCollisions = collisionData.ClashDetectiveCollisionCount; + result.MovingObjectInfo = collisionData.MovingObjectInfo; + + // 新增:被撞物体清单 + result.CollidedObjectsList = collisionData.UniqueCollidedObjects.OrderBy(x => x).ToList(); + result.UniqueCollidedObjectsCount = collisionData.UniqueCollidedObjects.Count; + + LogManager.Info($"碰撞报告计数 - 检查点: {result.AnimationCollisions}, Clash Detective: {result.TotalCollisions}"); + + UpdateProgress(70, "生成报告内容..."); + + // 生成报告内容 + result.ReportContent = GenerateReportContent(collisionData, _parameters.Type, _parameters.IncludeDetails); + + UpdateProgress(90, "处理报告显示..."); + + // 自动高亮(如果启用) + if (_parameters.AutoHighlight && collisionData.AllCollisions.Count > 0) { - UpdateProgress(30, "收集碰撞数据..."); - - // 收集所有碰撞数据及统计信息 - var collisionData = CollectAllCollisionData(); - result.AllCollisions = collisionData.AllCollisions; - - // 修改统计数据 - var clashIntegration = ClashDetectiveIntegration.Instance; - result.AnimationCollisions = clashIntegration?.AnimationCollisionCount ?? 0; // 检测点数量 - result.TotalCollisions = collisionData.ClashDetectiveCollisionCount; - result.MovingObjectInfo = collisionData.MovingObjectInfo; - - // 新增:被撞物体清单 - result.CollidedObjectsList = collisionData.UniqueCollidedObjects.OrderBy(x => x).ToList(); - result.UniqueCollidedObjectsCount = collisionData.UniqueCollidedObjects.Count; - - LogManager.Info($"碰撞报告计数 - 检查点: {result.AnimationCollisions}, Clash Detective: {result.TotalCollisions}"); - - UpdateProgress(70, "生成报告内容..."); - - // 生成报告内容 - result.ReportContent = GenerateReportContent(collisionData, _parameters.Type, _parameters.IncludeDetails); - - UpdateProgress(90, "处理报告显示..."); - - // 自动高亮(如果启用) - if (_parameters.AutoHighlight && collisionData.AllCollisions.Count > 0) + try { - try + var highlightIntegration = ClashDetectiveIntegration.Instance; + if (highlightIntegration != null) { - var highlightIntegration = ClashDetectiveIntegration.Instance; - if (highlightIntegration != null) - { - var highlightColor = Color.Green; // 绿色 (Navisworks API中没有Orange) - highlightIntegration.ManageHighlightsByCategory("report", collisionData.AllCollisions, highlightColor, true); - LogManager.Info($"自动高亮显示报告中的 {collisionData.AllCollisions.Count} 个碰撞对象"); - } - } - catch (Exception ex) - { - LogManager.Error($"自动高亮报告对象失败: {ex.Message}"); + var highlightColor = Color.Green; // 绿色 (Navisworks API中没有Orange) + highlightIntegration.ManageHighlightsByCategory("report", collisionData.AllCollisions, highlightColor, true); + LogManager.Info($"自动高亮显示报告中的 {collisionData.AllCollisions.Count} 个碰撞对象"); } } + catch (Exception ex) + { + LogManager.Error($"自动高亮报告对象失败: {ex.Message}"); + } + } - UpdateProgress(100, "报告生成完成"); + UpdateProgress(100, "报告生成完成"); - }, cancellationToken); - - // 显示报告(必须在UI线程中执行) + // 显示报告(UI线程) ShowReport(result); + + // 将报告结果缓存起来 + if (!string.IsNullOrEmpty(testNameKey)) + { + lock (_cacheLock) + { + _reportCache[testNameKey] = result; + LogManager.Info($"碰撞报告已缓存: {testNameKey}, 缓存总数: {_reportCache.Count}"); + } + } } catch (OperationCanceledException) { @@ -186,6 +239,64 @@ namespace NavisworksTransport.Commands return PathPlanningResult.Success(result, message); } + #region 缓存相关方法 + + /// + /// 获取当前最新的物流碰撞检测测试名称作为缓存键 + /// + /// 测试名称,如果未找到则返回 null + private string GetLatestTestNameForCache() + { + try + { + var doc = Autodesk.Navisworks.Api.Application.ActiveDocument; + var documentClash = doc.GetClash(); + + if (documentClash == null) + { + LogManager.Warning("无法获取 Clash Detective 文档,无法生成缓存键"); + return null; + } + + // 查找最新的物流碰撞检测测试 + var latestTest = documentClash.TestsData.Tests.Cast() + .Where(t => t.DisplayName.StartsWith("物流碰撞检测_")) + .OrderByDescending(t => t.DisplayName) + .FirstOrDefault(); + + if (latestTest != null) + { + LogManager.Debug($"缓存键用测试名称: {latestTest.DisplayName}"); + return latestTest.DisplayName; + } + else + { + LogManager.Info("未找到物流碰撞检测测试,无法生成缓存键"); + return null; + } + } + catch (Exception ex) + { + LogManager.Error($"获取测试名称作为缓存键失败: {ex.Message}"); + return null; + } + } + + /// + /// 清理过期缓存(可选功能) + /// + public static void ClearReportCache() + { + lock (_cacheLock) + { + var count = _reportCache.Count; + _reportCache.Clear(); + LogManager.Info($"已清理碰撞报告缓存,共清理 {count} 个缓存项"); + } + } + + #endregion + /// /// 收集所有碰撞数据及统计信息 /// diff --git a/src/UI/WPF/ViewModels/AnimationControlViewModel.cs b/src/UI/WPF/ViewModels/AnimationControlViewModel.cs index b08ef71..7f75ffb 100644 --- a/src/UI/WPF/ViewModels/AnimationControlViewModel.cs +++ b/src/UI/WPF/ViewModels/AnimationControlViewModel.cs @@ -8,6 +8,7 @@ using System.Windows.Threading; using Autodesk.Navisworks.Api; using Autodesk.Navisworks.Api.Clash; using NavisworksTransport.Core; +using NavisworksTransport.Commands; using NavisworksTransport.UI.WPF.Collections; namespace NavisworksTransport.UI.WPF.ViewModels @@ -541,7 +542,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels StartAnimationCommand = new RelayCommand(async () => await ExecuteStartAnimationAsync(), () => CanStartAnimation); PauseAnimationCommand = new RelayCommand(async () => await ExecutePauseAnimationAsync(), () => CanPauseAnimation); StopAnimationCommand = new RelayCommand(async () => await ExecuteStopAnimationAsync(), () => CanStopAnimation); - ViewCollisionReportCommand = new RelayCommand(async () => await ExecuteViewCollisionReportAsync(), () => HasCollisionResults); + ViewCollisionReportCommand = new RelayCommand(async () => await ExecuteViewCollisionReport(), () => HasCollisionResults); SelectAnimatedObjectCommand = new RelayCommand(ExecuteSelectAnimatedObject); ClearAnimatedObjectCommand = new RelayCommand(ExecuteClearAnimatedObject, () => HasSelectedAnimatedObject); GenerateAnimationCommand = new RelayCommand(ExecuteGenerateAnimation, () => CanGenerateAnimation); @@ -683,65 +684,37 @@ namespace NavisworksTransport.UI.WPF.ViewModels /// /// 查看碰撞报告命令 /// - private async Task ExecuteViewCollisionReportAsync() + private async Task ExecuteViewCollisionReport() { if (!HasCollisionResults) { - await _uiStateManager.ExecuteUIUpdateAsync(() => - { - UpdateMainStatus("尚无碰撞检测结果可查看"); - }); + UpdateMainStatus("尚无碰撞检测结果可查看"); return; } try { - await _uiStateManager.ExecuteUIUpdateAsync(() => - { - UpdateMainStatus("正在生成碰撞报告...", -1, true); - }); - + UpdateMainStatus("正在生成碰撞报告...", -1, true); LogManager.Info("开始生成碰撞检测详细报告"); - // 在后台线程生成报告 - var reportData = await Task.Run(() => - { - try - { - return GenerateCollisionReport(); - } - catch (Exception ex) - { - LogManager.Error($"生成碰撞报告失败: {ex.Message}"); - return null; - } - }); + // 直接调用完整的ViewCollisionReportCommand生成详细报告(在主线程上执行COM操作) + var viewReportCommand = NavisworksTransport.Commands.ViewCollisionReportCommand.CreateComprehensive(); + var result = await viewReportCommand.ExecuteAsync(); - if (reportData != null) + if (result.IsSuccess) { - // 显示报告 - 可以通过多种方式显示 - await ShowCollisionReport(reportData); - - await _uiStateManager.ExecuteUIUpdateAsync(() => - { - UpdateMainStatus("碰撞报告已生成"); - LogManager.Info("碰撞报告生成完成并已显示"); - }); + UpdateMainStatus("碰撞报告已生成"); + LogManager.Info("碰撞报告生成完成并已显示"); } else { - await _uiStateManager.ExecuteUIUpdateAsync(() => - { - UpdateMainStatus("生成碰撞报告失败"); - }); + UpdateMainStatus($"报告生成失败: {result.ErrorMessage}"); + LogManager.Error($"碰撞报告生成失败: {result.ErrorMessage}"); } } catch (Exception ex) { - await _uiStateManager.ExecuteUIUpdateAsync(() => - { - UpdateMainStatus("查看碰撞报告出错"); - }); + UpdateMainStatus("查看碰撞报告出错"); LogManager.Error($"查看碰撞报告异常: {ex.Message}", ex); } } @@ -1040,23 +1013,13 @@ namespace NavisworksTransport.UI.WPF.ViewModels /// /// 处理碰撞检测事件 /// - private async void OnCollisionDetected(object sender, CollisionDetectedEventArgs e) + private void OnCollisionDetected(object sender, CollisionDetectedEventArgs e) { - await _uiStateManager.ExecuteUIUpdateAsync(() => - { - HasCollisionResults = e.Results.Count > 0; - var summary = e.Results.Count > 0 - ? $"发现 {e.Results.Count} 个碰撞点" - : "未发现碰撞"; - UpdateMainStatus(summary); - }); - - // 自动生成并显示碰撞报告 - if (HasCollisionResults) - { - LogManager.Info("碰撞检测完成,自动生成报告"); - await ExecuteViewCollisionReportAsync(); - } + HasCollisionResults = e.Results.Count > 0; + var summary = e.Results.Count > 0 + ? $"发现 {e.Results.Count} 个碰撞点" + : "未发现碰撞"; + UpdateMainStatus(summary); } /// @@ -1658,31 +1621,10 @@ namespace NavisworksTransport.UI.WPF.ViewModels } }); - // 如果报告有效且有碰撞数据,显示详细报告窗口 + // 如果报告有效且有碰撞数据,用户可通过"查看碰撞报告"按钮查看详细信息 if (reportData.IsValid && reportData.TotalCollisions > 0) { - // 调用ViewCollisionReportCommand显示详细报告窗口 - try - { - // 创建综合碰撞报告命令(现在不需要传递参数了!) - var reportCommand = NavisworksTransport.Commands.ViewCollisionReportCommand.CreateComprehensive(autoHighlight: false); - - // 执行命令,UI显示会在正确的线程中处理 - var result = await reportCommand.ExecuteAsync(); - - if (result.IsSuccess) - { - LogManager.Info("碰撞报告窗口已成功显示"); - } - else - { - LogManager.Warning($"显示碰撞报告窗口失败: {result.ErrorMessage}"); - } - } - catch (Exception ex) - { - LogManager.Error($"调用碰撞报告命令失败: {ex.Message}"); - } + LogManager.Info($"碰撞检测完成,发现 {reportData.TotalCollisions} 个碰撞问题。用户可通过报告按钮查看详细信息"); } else if (reportData.IsValid && reportData.TotalCollisions == 0) { diff --git a/src/UI/WPF/Views/CollisionReportDialog.xaml.cs b/src/UI/WPF/Views/CollisionReportDialog.xaml.cs index cc9b0b5..5e04110 100644 --- a/src/UI/WPF/Views/CollisionReportDialog.xaml.cs +++ b/src/UI/WPF/Views/CollisionReportDialog.xaml.cs @@ -18,6 +18,16 @@ namespace NavisworksTransport.UI.WPF.Views /// private CollisionReportViewModel _viewModel; + /// + /// 当前显示的碰撞报告窗口实例(单例模式) + /// + private static CollisionReportDialog _currentInstance = null; + + /// + /// 用于线程同步的锁对象 + /// + private static readonly object _lock = new object(); + #endregion #region 构造函数 @@ -106,6 +116,16 @@ namespace NavisworksTransport.UI.WPF.Views _viewModel = null; } + // 清除静态实例引用 + lock (_lock) + { + if (_currentInstance == this) + { + _currentInstance = null; + LogManager.Info("碰撞报告对话框实例已从单例缓存中移除"); + } + } + LogManager.Debug("碰撞报告对话框资源已清理"); } catch (Exception ex) @@ -198,18 +218,44 @@ namespace NavisworksTransport.UI.WPF.Views catch (Exception ex) { LogManager.Error($"显示碰撞报告对话框失败: {ex.Message}"); - MessageBox.Show($"显示报告对话框失败: {ex.Message}", "错误", + MessageBox.Show($"显示报告对话框失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error); return false; } } + /// + /// 激活并显示已存在的窗口 + /// + private void ActivateWindow() + { + try + { + // 如果窗口被最小化,则恢复 + if (this.WindowState == WindowState.Minimized) + { + this.WindowState = WindowState.Normal; + } + + // 激活并前置窗口 + this.Activate(); + this.Topmost = true; // 暂时置顶 + this.Topmost = false; // 然后取消置顶,但保持前置 + + LogManager.Info("碰撞报告对话框已激活并前置显示"); + } + catch (Exception ex) + { + LogManager.Error($"激活碰撞报告对话框失败: {ex.Message}"); + } + } + #endregion #region 静态工厂方法 /// - /// 创建并显示碰撞报告对话框 + /// 创建并显示碰撞报告对话框(单例模式) /// /// 报告数据 /// 父窗口 @@ -218,23 +264,64 @@ namespace NavisworksTransport.UI.WPF.Views { try { - var dialog = new CollisionReportDialog(reportResult); - - if (owner != null) + lock (_lock) { - dialog.Owner = owner; - } + // 如果当前已有实例且仍然打开,则更新数据并激活窗口 + if (_currentInstance != null) + { + try + { + // 检查窗口是否仍然有效 + if (_currentInstance.IsLoaded && _currentInstance.IsVisible) + { + LogManager.Info("检测到已存在的碰撞报告窗口,更新数据并激活窗口"); - // 异步显示对话框,避免阻塞UI - dialog.Show(); - - LogManager.Info("碰撞报告对话框已显示"); - return dialog; + // 更新数据 + if (reportResult != null) + { + _currentInstance.UpdateReport(reportResult); + } + + // 激活窗口 + _currentInstance.ActivateWindow(); + + return _currentInstance; + } + else + { + // 窗口已关闭,清除引用 + _currentInstance = null; + } + } + catch (Exception ex) + { + LogManager.Warning($"检查现有窗口状态时出现异常: {ex.Message},将创建新窗口"); + _currentInstance = null; + } + } + + // 创建新的对话框实例 + var dialog = new CollisionReportDialog(reportResult); + + if (owner != null) + { + dialog.Owner = owner; + } + + // 设置为当前实例 + _currentInstance = dialog; + + // 异步显示对话框,避免阻塞UI + dialog.Show(); + + LogManager.Info("新的碰撞报告对话框已显示"); + return dialog; + } } catch (Exception ex) { LogManager.Error($"显示碰撞报告对话框失败: {ex.Message}"); - MessageBox.Show($"显示报告对话框失败: {ex.Message}", "错误", + MessageBox.Show($"显示报告对话框失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error); return null; }