修改碰撞检测报告到“查看碰撞报告”时,修复异步生成报告产生的崩溃风险。

This commit is contained in:
tian 2025-09-18 22:49:06 +08:00
parent 6e20628bd2
commit 1b95c37b80
3 changed files with 277 additions and 137 deletions

View File

@ -91,6 +91,20 @@ namespace NavisworksTransport.Commands
/// </summary>
public class ViewCollisionReportCommand : CommandBase
{
#region
/// <summary>
/// 碰撞报告结果缓存 - 使用测试名称作为键
/// </summary>
private static readonly Dictionary<string, CollisionReportResult> _reportCache = new Dictionary<string, CollisionReportResult>();
/// <summary>
/// 缓存操作锁对象
/// </summary>
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<CollisionReportResult>.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<CollisionReportResult>.Success(result, message);
}
#region
/// <summary>
/// 获取当前最新的物流碰撞检测测试名称作为缓存键
/// </summary>
/// <returns>测试名称,如果未找到则返回 null</returns>
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<ClashTest>()
.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;
}
}
/// <summary>
/// 清理过期缓存(可选功能)
/// </summary>
public static void ClearReportCache()
{
lock (_cacheLock)
{
var count = _reportCache.Count;
_reportCache.Clear();
LogManager.Info($"已清理碰撞报告缓存,共清理 {count} 个缓存项");
}
}
#endregion
/// <summary>
/// 收集所有碰撞数据及统计信息
/// </summary>

View File

@ -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
/// <summary>
/// 查看碰撞报告命令
/// </summary>
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
/// <summary>
/// 处理碰撞检测事件
/// </summary>
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);
}
/// <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)
{

View File

@ -18,6 +18,16 @@ namespace NavisworksTransport.UI.WPF.Views
/// </summary>
private CollisionReportViewModel _viewModel;
/// <summary>
/// 当前显示的碰撞报告窗口实例(单例模式)
/// </summary>
private static CollisionReportDialog _currentInstance = null;
/// <summary>
/// 用于线程同步的锁对象
/// </summary>
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;
}
}
/// <summary>
/// 激活并显示已存在的窗口
/// </summary>
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
/// <summary>
/// 创建并显示碰撞报告对话框
/// 创建并显示碰撞报告对话框(单例模式)
/// </summary>
/// <param name="reportResult">报告数据</param>
/// <param name="owner">父窗口</param>
@ -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;
}