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 { /// /// 碰撞报告参数类 /// public class CollisionReportParameters { /// /// 报告类型 /// public enum ReportType { /// 动画碰撞报告 Animation, /// 独立碰撞检测报告 Independent, /// 综合报告(包含所有类型) Comprehensive } /// /// 要生成的报告类型 /// public ReportType Type { get; set; } = ReportType.Comprehensive; /// /// 是否包含详细信息 /// public bool IncludeDetails { get; set; } = true; /// /// 是否自动高亮报告中的碰撞对象 /// public bool AutoHighlight { get; set; } = false; public PathPlanningResult ValidateParameters() { return PathPlanningResult.Success("参数验证通过"); } } /// /// 碰撞数据收集结果 /// public class CollisionDataResult { public List AllCollisions { get; set; } = new List(); public int BoundingBoxTestCount { get; set; } // 包围盒测试数量 public int ClashDetectiveCollisionCount { get; set; } // Clash Detective结果数量 public string MovingObjectInfo { get; set; } = string.Empty; // 新增:被撞物体去重统计 public HashSet UniqueCollidedObjects { get; set; } = new HashSet(); } /// /// 碰撞报告结果类 /// public class CollisionReportResult { public string ReportContent { get; set; } public int TotalCollisions { get; set; } public int AnimationCollisions { get; set; } // 检测点数量 public List AllCollisions { get; set; } = new List(); public long GenerationTimeMs { get; set; } public string MovingObjectInfo { get; set; } = string.Empty; // 新增:被撞物体清单 public List CollidedObjectsList { get; set; } = new List(); public int UniqueCollidedObjectsCount { get; set; } // 新增:动画参数 public string PathName { get; set; } = "未知路径"; public int FrameRate { get; set; } = 30; public double Duration { get; set; } = 10.0; public double DetectionGap { get; set; } = 0.05; } /// /// 生成碰撞报告Command /// public class GenerateCollisionReportCommand : CommandBase { #region 静态缓存机制 /// /// 碰撞报告结果缓存 - 使用测试名称作为键 /// private static readonly Dictionary _reportCache = new Dictionary(); /// /// 缓存操作锁对象 /// private static readonly object _cacheLock = new object(); #endregion private readonly CollisionReportParameters _parameters; public GenerateCollisionReportCommand(CollisionReportParameters parameters = null) : base("GenerateCollisionReport", "生成碰撞报告", "生成并显示详细的碰撞检测报告") { _parameters = parameters ?? new CollisionReportParameters(); } protected override PathPlanningResult ValidateParameters() { return _parameters.ValidateParameters(); } protected override async Task ExecuteInternalAsync(CancellationToken cancellationToken) { var startTime = DateTime.UtcNow; 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 { 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; // 新增:从动画管理器获取动画参数 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) { var highlightColor = Color.Green; // 绿色 (Navisworks API中没有Orange) highlightIntegration.ManageHighlightsByCategory(ModelHighlightHelper.CollisionResultsCategory, collisionData.AllCollisions, highlightColor, true); LogManager.Info($"自动高亮显示报告中的 {collisionData.AllCollisions.Count} 个碰撞对象"); } } catch (Exception ex) { LogManager.Error($"自动高亮报告对象失败: {ex.Message}"); } } UpdateProgress(100, "报告生成完成"); // 显示报告(UI线程) ShowReport(result); // 将报告结果缓存起来 if (!string.IsNullOrEmpty(testNameKey)) { lock (_cacheLock) { _reportCache[testNameKey] = result; LogManager.Info($"碰撞报告已缓存: {testNameKey}, 缓存总数: {_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.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 animationManager = Core.Animation.PathAnimationManager.GetInstance(); var currentPathName = animationManager?.PathName; if (string.IsNullOrEmpty(currentPathName)) { throw new InvalidOperationException("无法获取当前路径名称"); } // 精确匹配当前路径的测试 var matchedTest = documentClash.TestsData.Tests.Cast() .Where(t => t.DisplayName.StartsWith("物流碰撞检测_") && t.DisplayName.Contains(currentPathName)) .OrderByDescending(t => t.DisplayName) // 同一路径可能有多个测试,取最新的 .FirstOrDefault(); if (matchedTest != null) { LogManager.Info($"找到当前路径的测试: {matchedTest.DisplayName}"); return matchedTest.DisplayName; } else { throw new InvalidOperationException($"未找到路径 '{currentPathName}' 的碰撞测试"); } } catch (Exception ex) { LogManager.Error($"获取测试名称作为缓存键失败: {ex.Message}"); throw; // 让错误暴露出来 } } /// /// 清理过期缓存(可选功能) /// public static void ClearReportCache() { lock (_cacheLock) { var count = _reportCache.Count; _reportCache.Clear(); LogManager.Info($"已清理碰撞报告缓存,共清理 {count} 个缓存项"); } } #endregion /// /// 收集所有碰撞数据及统计信息 /// private CollisionDataResult CollectAllCollisionData() { var result = new CollisionDataResult(); var allCollisions = new List(); try { LogManager.Info("开始收集所有碰撞数据及统计信息..."); var clashIntegration = ClashDetectiveIntegration.Instance; if (clashIntegration == null) { throw new InvalidOperationException("无法获取ClashDetectiveIntegration实例"); } // 从动画管理器获取当前路径名称 var animationManager = Core.Animation.PathAnimationManager.GetInstance(); var currentPathName = animationManager?.PathName; if (string.IsNullOrEmpty(currentPathName)) { throw new InvalidOperationException("无法获取当前路径名称"); } // 使用公共方法获取当前路径的测试 var matchedTest = clashIntegration.GetCurrentPathClashTest(currentPathName); if (matchedTest == null) { throw new InvalidOperationException($"未找到路径 '{currentPathName}' 的碰撞测试"); } LogManager.Info($"找到当前路径的测试: {matchedTest.DisplayName}"); // 使用ClashDetectiveIntegration的方法提取碰撞数据 var testCollisionsRaw = clashIntegration.ExtractClashResultsFromTest(matchedTest); // 转换为CollisionResult对象 foreach (var clash in testCollisionsRaw) { var collision = new CollisionResult { ClashGuid = clash.Guid, DisplayName = $"[{matchedTest.DisplayName}] {clash.DisplayName}", Status = clash.Status, Item1 = clash.Item1, Item2 = clash.Item2, Center = clash.Center, Distance = clash.Distance, CreatedTime = DateTime.Now }; allCollisions.Add(collision); } // 统计匹配测试的碰撞总数 var totalCollisionCount = testCollisionsRaw.Count; result.ClashDetectiveCollisionCount = totalCollisionCount; LogManager.Debug($"匹配测试 '{matchedTest.DisplayName}' 包含 {totalCollisionCount} 个碰撞"); // 去重处理 var uniqueCollisions = allCollisions .GroupBy(c => new { c.Item1, c.Item2, c.Center }) .Select(g => g.First()) .ToList(); // 新增:统计去重后的被撞物体 // 使用Dictionary,ModelItem作为键会自动调用Equals进行比较 var uniqueCollidedObjectsDict = new Dictionary(); foreach (var collision in uniqueCollisions) { if (collision.Item2 != null) { // ModelItem作为字典的键,会使用其Equals方法进行比较 if (!uniqueCollidedObjectsDict.ContainsKey(collision.Item2)) { var item2Name = NavisworksApiHelper.GetModelItemNameWithAncestorFallback(collision.Item2); uniqueCollidedObjectsDict[collision.Item2] = item2Name ?? "未知对象"; } } } // 修复:保留所有碰撞对象,为相同名称的对象添加编号区分 var nameCountDict = new Dictionary(); var uniqueCollidedObjectsList = new List(); foreach (var name in uniqueCollidedObjectsDict.Values) { if (nameCountDict.ContainsKey(name)) { nameCountDict[name]++; uniqueCollidedObjectsList.Add($"{name} #{nameCountDict[name]}"); } else { nameCountDict[name] = 1; uniqueCollidedObjectsList.Add(name); } } var uniqueCollidedObjectsSet = new HashSet(uniqueCollidedObjectsList); result.AllCollisions = uniqueCollisions; result.UniqueCollidedObjects = uniqueCollidedObjectsSet; result.BoundingBoxTestCount = 0; // 从碰撞结果的Item1中获取运动构件信息 if (uniqueCollisions.Count > 0 && uniqueCollisions[0].Item1 != null) { var movingItem = uniqueCollisions[0].Item1; var movingItemName = NavisworksApiHelper.GetModelItemNameWithAncestorFallback(movingItem) ?? "未知运动构件"; result.MovingObjectInfo = $"运动构件: {movingItemName}"; } else { result.MovingObjectInfo = "运动构件: 未知"; } LogManager.Info($"收集完成 - Clash Detective权威结果: {result.ClashDetectiveCollisionCount}个, 去重后碰撞对象: {uniqueCollisions.Count}个, 去重后被撞构件: {uniqueCollidedObjectsSet.Count}个"); return result; } catch (Exception ex) { LogManager.Error($"收集碰撞数据失败: {ex.Message}"); result.AllCollisions = allCollisions; return result; } } /// /// 生成报告内容 /// 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(); } /// /// 添加碰撞详细信息到报告中 /// 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(); } /// /// 获取报告类型名称 /// 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 "未知类型"; } } /// /// 获取碰撞状态名称 /// 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 "未知"; } } /// /// 显示报告 /// 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); } } /// /// 内部报告显示方法 /// 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}"); } } /// /// 创建综合报告命令 /// public static GenerateCollisionReportCommand CreateComprehensive(bool autoHighlight = false) { return new GenerateCollisionReportCommand(new CollisionReportParameters { Type = CollisionReportParameters.ReportType.Comprehensive, IncludeDetails = true, AutoHighlight = autoHighlight }); } /// /// 将距离转换为米 /// private double ConvertDistanceToMeters(double distance) { try { var conversionFactor = UnitsConverter.GetUnitsToMetersConversionFactor(); return distance * conversionFactor; } catch (Exception ex) { LogManager.Warning($"距离单位转换失败: {ex.Message},使用原始值"); return distance; } } /// /// 将3D点坐标转换为米 /// 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; } } } }