NavisworksTransport/src/Commands/GenerateCollisionReportCommand.cs

678 lines
26 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Autodesk.Navisworks.Api;
using Autodesk.Navisworks.Api.Clash;
using NavisworksTransport.Utils;
using NavisworksTransport.UI.WPF.Views;
namespace NavisworksTransport.Commands
{
/// <summary>
/// 碰撞报告参数类
/// </summary>
public class CollisionReportParameters
{
/// <summary>
/// 报告类型
/// </summary>
public enum ReportType
{
/// <summary>动画碰撞报告</summary>
Animation,
/// <summary>独立碰撞检测报告</summary>
Independent,
/// <summary>综合报告(包含所有类型)</summary>
Comprehensive
}
/// <summary>
/// 要生成的报告类型
/// </summary>
public ReportType Type { get; set; } = ReportType.Comprehensive;
/// <summary>
/// 是否包含详细信息
/// </summary>
public bool IncludeDetails { get; set; } = true;
/// <summary>
/// 是否自动高亮报告中的碰撞对象
/// </summary>
public bool AutoHighlight { get; set; } = false;
public PathPlanningResult ValidateParameters()
{
return PathPlanningResult.Success("参数验证通过");
}
}
/// <summary>
/// 碰撞数据收集结果
/// </summary>
public class CollisionDataResult
{
public List<CollisionResult> AllCollisions { get; set; } = new List<CollisionResult>();
public int BoundingBoxTestCount { get; set; } // 包围盒测试数量
public int ClashDetectiveCollisionCount { get; set; } // Clash Detective结果数量
public int AnimationCollisions { get; set; } // 动画过程碰撞数量
public string MovingObjectInfo { get; set; } = string.Empty;
// 新增:被撞物体去重统计
public HashSet<string> UniqueCollidedObjects { get; set; } = new HashSet<string>();
}
/// <summary>
/// 碰撞报告结果类
/// </summary>
public class CollisionReportResult
{
public string ReportContent { get; set; }
public int TotalCollisions { get; set; }
public int AnimationCollisions { get; set; } // 检测点数量
public List<CollisionResult> AllCollisions { get; set; } = new List<CollisionResult>();
public long GenerationTimeMs { get; set; }
public string MovingObjectInfo { get; set; } = string.Empty;
// 新增:被撞物体清单
public List<string> CollidedObjectsList { get; set; } = new List<string>();
public int UniqueCollidedObjectsCount { get; set; }
// 新增:动画参数
public string PathName { get; set; }
public int FrameRate { get; set; }
public double Duration { get; set; }
public double DetectionGap { get; set; }
}
/// <summary>
/// 生成碰撞报告Command
/// </summary>
public class GenerateCollisionReportCommand : 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;
private string _specificTestName = null; // 指定的测试名称(用于历史报告)
public GenerateCollisionReportCommand(CollisionReportParameters parameters = null)
: base("GenerateCollisionReport", "生成碰撞报告", "生成并显示详细的碰撞检测报告")
{
_parameters = parameters ?? new CollisionReportParameters();
}
/// <summary>
/// 设置要生成报告的测试名称
/// </summary>
public void SetTestName(string testName)
{
_specificTestName = testName;
}
protected override PathPlanningResult ValidateParameters()
{
return _parameters.ValidateParameters();
}
protected override async Task<PathPlanningResult> ExecuteInternalAsync(CancellationToken cancellationToken)
{
var startTime = DateTime.UtcNow;
UpdateProgress(10, "开始生成碰撞报告...");
// 获取ClashDetectiveIntegration实例
var clashIntegration = ClashDetectiveIntegration.Instance;
// 使用指定的测试名称(如果有),否则使用当前测试名称
var targetTestName = _specificTestName ?? clashIntegration?.CurrentTestName;
if (string.IsNullOrEmpty(targetTestName))
{
return PathPlanningResult.Failure("无法确定测试名称,请先运行碰撞检测或选择历史记录");
}
// 检查缓存
lock (_cacheLock)
{
if (_reportCache.ContainsKey(targetTestName))
{
var cachedResult = _reportCache[targetTestName];
LogManager.Info($"使用缓存的碰撞报告: {targetTestName}");
UpdateProgress(100, "使用缓存报告");
// 显示缓存的报告
ShowReport(cachedResult);
var cachedMessage = cachedResult.TotalCollisions > 0 ?
$"碰撞报告显示完成(缓存):发现 {cachedResult.TotalCollisions} 个碰撞" :
"碰撞报告显示完成(缓存):未发现碰撞";
return PathPlanningResult<CollisionReportResult>.Success(cachedResult, cachedMessage);
}
else
{
LogManager.Debug($"缓存中未找到测试: {targetTestName}");
}
}
var result = new CollisionReportResult();
try
{
UpdateProgress(30, "收集碰撞数据...");
// 收集所有碰撞数据及统计信息(同步执行,避免 COM 线程问题)
var collisionData = CollectAllCollisionData();
result.AllCollisions = collisionData.AllCollisions;
// 修改统计数据
result.AnimationCollisions = collisionData.AnimationCollisions;
result.TotalCollisions = collisionData.ClashDetectiveCollisionCount;
result.MovingObjectInfo = collisionData.MovingObjectInfo;
// 新增:被撞物体清单
result.CollidedObjectsList = collisionData.UniqueCollidedObjects.OrderBy(x => x).ToList();
result.UniqueCollidedObjectsCount = collisionData.UniqueCollidedObjects.Count;
// 新增:从动画管理器获取动画参数
var animationManager = Core.Animation.PathAnimationManager.GetInstance();
if (animationManager != null)
{
result.FrameRate = animationManager.AnimationFrameRate;
result.Duration = animationManager.AnimationDuration;
result.DetectionGap = animationManager.DetectionGap;
// PathName需要从动画管理器获取
result.PathName = animationManager.PathName ?? "未知路径";
}
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
{
var highlightIntegration = ClashDetectiveIntegration.Instance;
if (highlightIntegration != null)
{
ModelHighlightHelper.HighlightCollisionResults(collisionData.AllCollisions, clearPrevious: true);
LogManager.Info($"自动高亮显示报告中的 {collisionData.AllCollisions.Count} 个碰撞对象");
}
}
catch (Exception ex)
{
LogManager.Error($"自动高亮报告对象失败: {ex.Message}");
}
}
UpdateProgress(100, "报告生成完成");
// 显示报告UI线程
ShowReport(result);
// 将报告结果缓存起来
lock (_cacheLock)
{
_reportCache[targetTestName] = result;
LogManager.Info($"碰撞报告已缓存: {targetTestName}, 缓存总数: {_reportCache.Count}");
}
}
catch (OperationCanceledException)
{
return PathPlanningResult.Failure("报告生成被取消");
}
catch (Exception ex)
{
LogManager.Error($"生成碰撞报告失败: {ex.Message}");
return PathPlanningResult.Failure($"报告生成失败: {ex.Message}");
}
result.GenerationTimeMs = (long)(DateTime.UtcNow - startTime).TotalMilliseconds;
var message = result.TotalCollisions > 0 ?
$"碰撞报告生成完成:发现 {result.TotalCollisions} 个碰撞" :
"碰撞报告生成完成:未发现碰撞";
return PathPlanningResult<CollisionReportResult>.Success(result, message);
}
#region
/// <summary>
/// 从缓存获取报告
/// </summary>
public static CollisionReportResult GetReportFromCache(string testName)
{
if (string.IsNullOrEmpty(testName))
return null;
lock (_cacheLock)
{
if (_reportCache.ContainsKey(testName))
{
LogManager.Info($"从缓存获取报告: {testName}");
return _reportCache[testName];
}
}
return null;
}
/// <summary>
/// 缓存报告
/// </summary>
public static void CacheReport(string testName, CollisionReportResult reportResult)
{
if (string.IsNullOrEmpty(testName) || reportResult == null)
return;
lock (_cacheLock)
{
_reportCache[testName] = reportResult;
LogManager.Info($"报告已缓存: {testName}, 缓存总数: {_reportCache.Count}");
}
}
/// <summary>
/// 清理过期缓存(可选功能)
/// </summary>
public static void ClearReportCache()
{
lock (_cacheLock)
{
var count = _reportCache.Count;
_reportCache.Clear();
LogManager.Info($"已清理碰撞报告缓存,共清理 {count} 个缓存项");
}
}
#endregion
/// <summary>
/// 收集所有碰撞数据及统计信息
/// </summary>
private CollisionDataResult CollectAllCollisionData()
{
var result = new CollisionDataResult();
var allCollisions = new List<CollisionResult>();
try
{
LogManager.Info("开始收集所有碰撞数据及统计信息...");
var clashIntegration = ClashDetectiveIntegration.Instance;
if (clashIntegration == null)
{
throw new InvalidOperationException("无法获取ClashDetectiveIntegration实例");
}
// 从动画管理器获取当前路径名称
var animationManager = Core.Animation.PathAnimationManager.GetInstance();
var currentPathName = animationManager?.PathName;
// 使用指定的测试名称(如果有),否则使用当前测试名称
var targetTestName = _specificTestName ?? clashIntegration?.CurrentTestName;
if (string.IsNullOrEmpty(targetTestName))
{
throw new InvalidOperationException("无法确定测试名称,请先运行碰撞检测或选择历史记录");
}
// 获取碰撞结果(先检查缓存,没有则从数据库加载)
var testCollisionsRaw = clashIntegration.GetCurrentPathClashResults(targetTestName);
if (testCollisionsRaw == null || testCollisionsRaw.Count == 0)
{
throw new InvalidOperationException($"未找到测试 '{targetTestName}' 的ClashDetective碰撞结果");
}
LogManager.Info($"从缓存获取ClashDetective结果: {testCollisionsRaw.Count}个碰撞,测试名称:{targetTestName}");
// 直接使用缓存中的CollisionResult对象已经过复合对象处理和去重
allCollisions.AddRange(testCollisionsRaw);
// 统计碰撞总数
result.ClashDetectiveCollisionCount = testCollisionsRaw.Count;
LogManager.Debug($"测试 '{targetTestName}' 包含 {testCollisionsRaw.Count} 个碰撞");
// 统计被撞物体(缓存中已经是复合对象,不需要再去重)
var nameCountDict = new Dictionary<string, int>();
var uniqueCollidedObjectsList = new List<string>();
foreach (var collision in testCollisionsRaw)
{
if (collision.Item2 != null)
{
// Item2已经是容器对象在创建CollisionResult时已用FindNamedParentContainer处理
var item2Name = ModelItemAnalysisHelper.GetSafeDisplayName(collision.Item2) ?? "未知对象";
// 为相同名称的对象添加编号区分
if (nameCountDict.ContainsKey(item2Name))
{
nameCountDict[item2Name]++;
uniqueCollidedObjectsList.Add($"{item2Name} #{nameCountDict[item2Name]}");
}
else
{
nameCountDict[item2Name] = 1;
uniqueCollidedObjectsList.Add(item2Name);
}
}
}
result.AllCollisions = allCollisions;
result.UniqueCollidedObjects = new HashSet<string>(uniqueCollidedObjectsList);
result.BoundingBoxTestCount = 0;
// 设置动画碰撞数量
result.AnimationCollisions = clashIntegration?.AnimationCollisionCount ?? 0;
// 从碰撞结果的Item1中获取运动构件信息
if (allCollisions.Count > 0 && allCollisions[0].Item1 != null)
{
// Item1已经是容器对象在创建CollisionResult时已用FindNamedParentContainer处理
var movingItemName = ModelItemAnalysisHelper.GetSafeDisplayName(allCollisions[0].Item1) ?? "未知运动构件";
result.MovingObjectInfo = $"运动构件: {movingItemName}";
}
else
{
result.MovingObjectInfo = "运动构件: 未知";
}
LogManager.Info($"收集完成 - Clash Detective权威结果: {result.ClashDetectiveCollisionCount}个, 碰撞对象: {allCollisions.Count}个, 被撞构件: {uniqueCollidedObjectsList.Count}个");
return result;
}
catch (Exception ex)
{
LogManager.Error($"收集碰撞数据失败: {ex.Message}");
result.AllCollisions = allCollisions;
return result;
}
}
/// <summary>
/// 生成报告内容
/// </summary>
private string GenerateReportContent(CollisionDataResult collisionData,
CollisionReportParameters.ReportType reportType, bool includeDetails)
{
var report = new StringBuilder();
var now = DateTime.Now;
var collisions = collisionData.AllCollisions;
// 报告头部
report.AppendLine("========================================");
report.AppendLine(" NavisworksTransport 碰撞检测报告");
report.AppendLine("========================================");
report.AppendLine($"生成时间: {now:yyyy年MM月dd日 HH:mm:ss}");
report.AppendLine($"报告类型: {GetReportTypeName(reportType)}");
// 运动构件信息(原运动物体信息)
if (!string.IsNullOrEmpty(collisionData.MovingObjectInfo))
{
var movingInfo = collisionData.MovingObjectInfo.Replace("运动对象", "运动构件");
report.AppendLine($"{movingInfo}");
}
report.AppendLine();
// 总体统计
report.AppendLine("=== 总体统计 ===");
var clashIntegration = ClashDetectiveIntegration.Instance;
var animationCollisionCount = clashIntegration?.AnimationCollisionCount ?? 0;
report.AppendLine($"总碰撞数: {collisionData.ClashDetectiveCollisionCount}个");
report.AppendLine($"碰撞构件: {collisionData.UniqueCollidedObjects.Count}个");
report.AppendLine($"检测点: {animationCollisionCount}个");
report.AppendLine();
// 碰撞构件清单(新增)
if (collisionData.UniqueCollidedObjects?.Count > 0)
{
report.AppendLine("=== 碰撞构件清单 ===");
int index = 1;
foreach (var objName in collisionData.UniqueCollidedObjects.OrderBy(x => x).Take(20)) // 限制显示前20个
{
report.AppendLine($" {index}. {objName}");
index++;
}
if (collisionData.UniqueCollidedObjects.Count > 20)
{
report.AppendLine($" ... 还有 {collisionData.UniqueCollidedObjects.Count - 20} 个构件");
}
report.AppendLine();
}
// 详细信息
if (includeDetails && collisions.Count > 0)
{
report.AppendLine("=== 详细碰撞信息 ===");
report.AppendLine("--- 碰撞详情 ---");
foreach (var collision in collisions.Take(10)) // 限制显示数量
{
AppendCollisionDetail(report, collision);
}
if (collisions.Count > 10)
{
report.AppendLine($"... 还有 {collisions.Count - 10} 个碰撞未显示");
}
report.AppendLine();
}
// 建议和结论
report.AppendLine("=== 建议与结论 ===");
if (collisions.Count == 0)
{
report.AppendLine("✅ 未发现碰撞问题,路径规划良好。");
}
else
{
report.AppendLine($"⚠️ 发现 {collisions.Count} 个碰撞问题,建议采取以下措施:");
report.AppendLine(" 1. 调整路径规划,避开碰撞区域");
report.AppendLine(" 2. 检查车辆尺寸和安全边距设置");
report.AppendLine(" 3. 考虑增加临时障碍物标记");
report.AppendLine(" 4. 重新评估物流流程设计");
report.AppendLine(" 5. 建议进行更详细的碰撞检测分析");
}
report.AppendLine();
report.AppendLine("========================================");
report.AppendLine($"报告生成完成 - {now:HH:mm:ss}");
report.AppendLine("========================================");
return report.ToString();
}
/// <summary>
/// 添加碰撞详细信息到报告中
/// </summary>
private void AppendCollisionDetail(StringBuilder report, CollisionResult collision)
{
var item1Name = collision.Item1?.DisplayName ?? "未知对象";
var item2Name = collision.Item2?.DisplayName ?? "未知对象";
// 转换距离单位为米
var distanceInMeters = ConvertDistanceToMeters(collision.Distance);
report.AppendLine($"• {item1Name} ↔ {item2Name}");
report.AppendLine($" 状态: {GetStatusName(collision.Status)}, 距离: {distanceInMeters:F3}m");
// 转换位置坐标单位为米
var centerInMeters = ConvertPointToMeters(collision.Center);
report.AppendLine($" 位置: ({centerInMeters.X:F2}, {centerInMeters.Y:F2}, {centerInMeters.Z:F2})m");
report.AppendLine($" GUID: {collision.ClashGuid}");
report.AppendLine();
}
/// <summary>
/// 获取报告类型名称
/// </summary>
private string GetReportTypeName(CollisionReportParameters.ReportType reportType)
{
switch (reportType)
{
case CollisionReportParameters.ReportType.Animation:
return "动画碰撞报告";
case CollisionReportParameters.ReportType.Independent:
return "独立检测报告";
case CollisionReportParameters.ReportType.Comprehensive:
return "综合碰撞报告";
default:
return "未知类型";
}
}
/// <summary>
/// 获取碰撞状态名称
/// </summary>
private string GetStatusName(ClashResultStatus status)
{
switch (status)
{
case ClashResultStatus.New:
return "新发现";
case ClashResultStatus.Active:
return "活跃";
case ClashResultStatus.Reviewed:
return "已审阅";
case ClashResultStatus.Approved:
return "已批准";
case ClashResultStatus.Resolved:
return "已解决";
default:
return "未知";
}
}
/// <summary>
/// 显示报告
/// </summary>
private void ShowReport(CollisionReportResult reportResult)
{
try
{
// 确保在UI线程中执行
if (System.Windows.Application.Current != null)
{
if (System.Windows.Application.Current.Dispatcher.CheckAccess())
{
// 已在UI线程上
ShowReportInternal(reportResult);
}
else
{
// 切换到UI线程
System.Windows.Application.Current.Dispatcher.Invoke(() => ShowReportInternal(reportResult));
}
}
else
{
// 如果WPF Application不存在回退到原来的方式
ShowReportInternal(reportResult);
}
}
catch (Exception ex)
{
LogManager.Error($"显示报告失败: {ex.Message}");
System.Windows.MessageBox.Show($"显示报告失败: {ex.Message}", "错误",
System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);
}
}
/// <summary>
/// 内部报告显示方法
/// </summary>
private void ShowReportInternal(CollisionReportResult reportResult)
{
try
{
// 使用新的WPF碰撞报告对话框
var reportDialog = CollisionReportDialog.ShowReport(reportResult);
if (reportDialog != null)
{
LogManager.Info("WPF碰撞报告对话框显示成功");
}
}
catch (Exception ex)
{
LogManager.Error($"显示WPF报告对话框失败: {ex.Message}");
}
}
/// <summary>
/// 创建综合报告命令
/// </summary>
public static GenerateCollisionReportCommand CreateComprehensive(bool autoHighlight = false)
{
return new GenerateCollisionReportCommand(new CollisionReportParameters
{
Type = CollisionReportParameters.ReportType.Comprehensive,
IncludeDetails = true,
AutoHighlight = autoHighlight
});
}
/// <summary>
/// 将距离转换为米
/// </summary>
private double ConvertDistanceToMeters(double distance)
{
try
{
var conversionFactor = UnitsConverter.GetUnitsToMetersConversionFactor();
return distance * conversionFactor;
}
catch (Exception ex)
{
LogManager.Warning($"距离单位转换失败: {ex.Message},使用原始值");
return distance;
}
}
/// <summary>
/// 将3D点坐标转换为米
/// </summary>
private Point3D ConvertPointToMeters(Point3D point)
{
try
{
var conversionFactor = UnitsConverter.GetUnitsToMetersConversionFactor();
return new Point3D(
point.X * conversionFactor,
point.Y * conversionFactor,
point.Z * conversionFactor
);
}
catch (Exception ex)
{
LogManager.Warning($"坐标单位转换失败: {ex.Message},使用原始值");
return point;
}
}
}
}