修改碰撞检测报告到“查看碰撞报告”时,修复异步生成报告产生的崩溃风险。
This commit is contained in:
parent
6e20628bd2
commit
1b95c37b80
@ -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>
|
||||
|
||||
@ -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)
|
||||
{
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user