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;
}
}
}
}