using System; using System.Collections.Generic; using System.Linq; using Autodesk.Navisworks.Api; using NavisworksTransport.Core.Models; using NavisworksTransport.Utils; namespace NavisworksTransport.Core { /// /// 路径分析引擎 /// 负责计算路径的各项指标和评分 /// public class PathAnalysisEngine { private readonly PathDatabase _database; private const double HOTSPOT_RADIUS = 3.0; // 热点半径:3米 private const double ENDPOINT_THRESHOLD_METERS = 2.0; // 终点分组阈值:2米 private const double IDEAL_SPEED = 1.0; // 理想速度:1.0 m/s /// /// 获取终点分组阈值(转换为模型单位用于距离比较) /// private double EndpointThreshold => NavisworksTransport.Utils.UnitsConverter.ConvertFromMeters(ENDPOINT_THRESHOLD_METERS); /// /// 构造函数 /// public PathAnalysisEngine(PathDatabase database) { _database = database ?? throw new ArgumentNullException(nameof(database)); } #region 公共方法 /// /// 分析单条路径的所有指标 /// public PathDetailedAnalysis AnalyzePath(PathRoute route, AnalysisContext context) { if (route == null) throw new ArgumentNullException(nameof(route)); var analysis = new PathDetailedAnalysis { RouteId = route.Id, RouteName = route.Name, TotalLength = route.TotalLength, EstimatedTime = route.EstimatedTime, Strategy = context?.Strategy ?? AnalysisStrategies.Balanced, AnalysisTime = DateTime.Now }; try { // 1. 获取碰撞数据 var collisions = GetCollisionsForRoute(route.Id); analysis.CollisionCount = collisions?.Count ?? 0; // 2. 检测碰撞热点 analysis.Hotspots = DetectHotspots(collisions, HOTSPOT_RADIUS); analysis.HotspotCount = analysis.Hotspots.Count; // 3. 计算各维度分数(四维度:安全、效率、转弯、直达) analysis.SafetyScore = CalculateSafetyScore(analysis.CollisionCount, analysis.HotspotCount); analysis.EfficiencyScore = CalculateEfficiencyScore(route, context?.GroupMinLength ?? route.TotalLength); analysis.TurnDifficultyScore = CalculateTurnDifficultyScore(route.Edges); analysis.TortuosityScore = CalculateTortuosityScore(route); analysis.RedundancyScore = 0; // 已移除,保留字段兼容 // 4. 计算综合加权评分 analysis.WeightedScore = CalculateWeightedScore(analysis, analysis.Strategy); LogManager.Info($"[路径分析] {route.Name}: 安全{analysis.SafetyScore:F0}, 效率{analysis.EfficiencyScore:F0}, " + $"转弯{analysis.TurnDifficultyScore:F0}, 直达{analysis.TortuosityScore:F0}, 综合{analysis.WeightedScore:F0}"); } catch (Exception ex) { LogManager.Error($"[路径分析] 分析路径 {route.Name} 失败: {ex.Message}", ex); // 设置默认值,避免崩溃 SetDefaultScores(analysis); } return analysis; } /// /// 对多条路径进行分组(按终点) /// public List GroupPathsByEndpoint(List routes) { if (routes == null || routes.Count == 0) return new List(); var groups = new List(); foreach (var route in routes) { var endPoint = route.GetEndPoint()?.Position; if (endPoint == null) { LogManager.Warning($"[路径分组] 路径 {route.Name} 没有终点,跳过"); continue; } // 查找是否已有匹配的组(以终点为主,2米阈值) var matchingGroup = groups.FirstOrDefault(g => Distance(g.EndPoint, endPoint) < EndpointThreshold); if (matchingGroup != null) { // 添加到现有组 var analysis = AnalyzePath(route, new AnalysisContext { GroupMinTime = 0 }); matchingGroup.PathAnalyses.Add(analysis); // 更新组中心点 matchingGroup.EndPoint = CalculateGroupCenter(matchingGroup.PathAnalyses, routes); LogManager.Debug($"[路径分组] 路径 {route.Name} 加入现有组 {matchingGroup.GroupId}"); } else { // 创建新组 var newGroup = new EndpointGroup { GroupId = Guid.NewGuid().ToString("N").Substring(0, 8), GroupName = $"终点组-{groups.Count + 1}", EndPoint = endPoint, EndPointDescription = route.GetEndPoint()?.Name ?? "未知终点" }; var analysis = AnalyzePath(route, new AnalysisContext { GroupMinTime = 0 }); newGroup.PathAnalyses.Add(analysis); groups.Add(newGroup); LogManager.Info($"[路径分组] 创建新组 {newGroup.GroupId},终点: {newGroup.EndPointDescription}"); } } // 更新各组的组内统计数据 foreach (var group in groups) { UpdateGroupStatistics(group); } LogManager.Info($"[路径分组] 完成,共 {groups.Count} 个组,{routes.Count} 条路径"); return groups; } /// /// 分析多条路径并分组,包含完整的上下文计算 /// public List AnalyzeAndGroupPaths(List routes, string strategy) { if (routes == null || routes.Count == 0) return new List(); // 第一步:初步分析所有路径(获取基础数据) var analyses = new List(); foreach (var route in routes) { var analysis = AnalyzePath(route, new AnalysisContext { Strategy = strategy }); analyses.Add(analysis); } // 第二步:按终点分组 var groups = new List(); var processed = new HashSet(); LogManager.Info($"[路径分组] 开始对 {analyses.Count} 条分析结果进行分组"); foreach (var analysis in analyses) { if (processed.Contains(analysis.RouteId)) { LogManager.Debug($"[路径分组] 跳过已处理: {analysis.RouteId}"); continue; } var route = routes.FirstOrDefault(r => r.Id == analysis.RouteId); if (route == null) { LogManager.Warning($"[路径分组] 找不到路径: {analysis.RouteId}"); continue; } var endPointObj = route.GetEndPoint(); var endPoint = endPointObj?.Position; if (endPoint == null) { LogManager.Warning($"[路径分组] 路径 {route.Name} 没有终点"); continue; } LogManager.Info($"[路径分组] 处理路径 {route.Name}, 终点: ({endPoint.X:F2}, {endPoint.Y:F2}, {endPoint.Z:F2})"); // 找到同组的所有路径(包括当前路径) var groupAnalyses = new List(); // 首先添加当前路径 groupAnalyses.Add(analysis); processed.Add(analysis.RouteId); foreach (var otherAnalysis in analyses) { if (processed.Contains(otherAnalysis.RouteId)) continue; var otherRoute = routes.FirstOrDefault(r => r.Id == otherAnalysis.RouteId); if (otherRoute == null) continue; var otherEndPoint = otherRoute.GetEndPoint()?.Position; if (otherEndPoint == null) continue; if (Distance(endPoint, otherEndPoint) < EndpointThreshold) { groupAnalyses.Add(otherAnalysis); processed.Add(otherAnalysis.RouteId); } } if (groupAnalyses.Count > 0) { LogManager.Info($"[路径分组] 创建组,包含 {groupAnalyses.Count} 条路径"); try { var group = new EndpointGroup { GroupId = Guid.NewGuid().ToString("N").Substring(0, 8), EndPoint = CalculateGroupCenter(groupAnalyses, routes), PathAnalyses = groupAnalyses }; // 设置组名称和终点描述 var firstRoute = routes.FirstOrDefault(r => r.Id == groupAnalyses.First().RouteId); if (firstRoute != null) { var firstEndPoint = firstRoute.GetEndPoint(); group.GroupName = $"终点: {firstEndPoint?.Name ?? "未知"}"; group.EndPointDescription = $"坐标: ({group.EndPoint.X:F1}, {group.EndPoint.Y:F1}, {group.EndPoint.Z:F1})"; } groups.Add(group); LogManager.Info($"[路径分组] 组创建成功: {group.GroupName}"); } catch (Exception ex) { LogManager.Error($"[路径分组] 创建组失败: {ex.Message}", ex); } } else { LogManager.Warning($"[路径分组] 组内没有路径"); } } // 第三步:更新效率分数(基于组内最短路径)和排名 foreach (var group in groups) { UpdateGroupStatistics(group); // 只更新效率分数和综合评分,保留其他固有属性 foreach (var analysis in group.PathAnalyses) { var route = routes.FirstOrDefault(r => r.Id == analysis.RouteId); if (route != null) { // 只重新计算效率(基于组内最短路径) analysis.EfficiencyScore = CalculateEfficiencyScore(route, group.MinLength); // 重新计算综合加权评分 analysis.WeightedScore = CalculateWeightedScore(analysis, strategy); } analysis.GroupId = group.GroupId; } // 排序并更新排名 var sortedAnalyses = group.PathAnalyses .OrderByDescending(a => a.WeightedScore) .ToList(); for (int i = 0; i < sortedAnalyses.Count; i++) { sortedAnalyses[i].GroupRanking = i + 1; } group.PathAnalyses = sortedAnalyses; } return groups; } /// /// 检测碰撞热点 /// public List DetectHotspots(List collisions, double radius = HOTSPOT_RADIUS) { var hotspots = new List(); if (collisions == null || collisions.Count < 2) return hotspots; var processed = new HashSet(); for (int i = 0; i < collisions.Count; i++) { if (processed.Contains(i)) continue; var center = collisions[i].Center; if (center == null) continue; var nearbyCollisions = new List { collisions[i] }; // 查找范围内的其他碰撞 for (int j = i + 1; j < collisions.Count; j++) { if (processed.Contains(j)) continue; var otherCenter = collisions[j].Center; if (otherCenter == null) continue; if (Distance(center, otherCenter) <= radius) { nearbyCollisions.Add(collisions[j]); processed.Add(j); } } // 如果达到阈值(≥2次),创建热点 if (nearbyCollisions.Count >= 2) { var hotspot = new CollisionHotspot { Center = CalculateCenter(nearbyCollisions), Radius = radius, CollisionCount = nearbyCollisions.Count, CollidedObjectNames = nearbyCollisions .Select(c => GetSafeDisplayName(c.Item2)) .Distinct() .ToList() }; hotspots.Add(hotspot); } processed.Add(i); } LogManager.Debug($"[热点检测] 发现 {hotspots.Count} 个热点"); return hotspots.OrderByDescending(h => h.CollisionCount).ToList(); } /// /// 计算加权综合评分 /// public double CalculateWeightedScore(PathDetailedAnalysis analysis, string strategy) { var weights = AnalysisStrategies.GetWeights(strategy); // 四维度加权计算:安全、效率、转弯、直达 double weightedScore = analysis.SafetyScore * weights[0] + analysis.EfficiencyScore * weights[1] + analysis.TurnDifficultyScore * weights[2] + analysis.TortuosityScore * weights[3]; return Math.Round(weightedScore, 1); } /// /// 查找组内最佳路径 /// public PathDetailedAnalysis FindBestPathInGroup(EndpointGroup group, string strategy) { if (group?.PathAnalyses == null || group.PathAnalyses.Count == 0) return null; // 如果只有一条路径,直接返回 if (group.PathAnalyses.Count == 1) return group.PathAnalyses[0]; // 重新计算加权评分 foreach (var analysis in group.PathAnalyses) { analysis.WeightedScore = CalculateWeightedScore(analysis, strategy); } // 按评分排序 var sortedPaths = group.PathAnalyses.OrderByDescending(a => a.WeightedScore).ToList(); // 如果前两名评分接近(差距<5分),优先选碰撞少的 if (sortedPaths.Count >= 2) { var first = sortedPaths[0]; var second = sortedPaths[1]; if (Math.Abs(first.WeightedScore - second.WeightedScore) < 5) { if (second.CollisionCount < first.CollisionCount) { LogManager.Info($"[最佳路径] 评分接近,选择碰撞更少的路径: {second.RouteName}"); return second; } } } return sortedPaths[0]; } /// /// 生成优化建议 /// public List GenerateSuggestions(PathDetailedAnalysis analysis, EndpointGroup group) { var suggestions = new List(); // 1. 安全建议 if (analysis.CollisionCount > 0) { if (analysis.HotspotCount > 0) { suggestions.Add(new CategorizedSuggestion { Category = SuggestionCategory.Safety, Title = $"发现 {analysis.CollisionCount} 次碰撞({analysis.HotspotCount} 个热点)", Description = $"路径 {analysis.RouteName} 存在 {analysis.HotspotCount} 个碰撞热点区域," + $"建议重点检查热点位置并优化路径避让", RelatedRouteId = analysis.RouteId, RelatedRouteName = analysis.RouteName, Priority = analysis.HotspotCount >= 2 ? 5 : 4 }); } else { suggestions.Add(new CategorizedSuggestion { Category = SuggestionCategory.Safety, Title = $"发现 {analysis.CollisionCount} 次分散碰撞", Description = $"路径 {analysis.RouteName} 有 {analysis.CollisionCount} 次碰撞," + $"建议优化路径避让", RelatedRouteId = analysis.RouteId, RelatedRouteName = analysis.RouteName, Priority = 3 }); } } else { suggestions.Add(new CategorizedSuggestion { Category = SuggestionCategory.Safety, Title = "无碰撞,安全性良好", Description = $"路径 {analysis.RouteName} 未检测到碰撞", RelatedRouteId = analysis.RouteId, RelatedRouteName = analysis.RouteName, Priority = 1 }); } // 2. 组内对比建议 if (group?.PathAnalyses?.Count > 1) { var lengthDiff = group.GetLengthDiffPercent(analysis.RouteId); var timeDiff = group.GetTimeDiffPercent(analysis.RouteId); if (lengthDiff > 30) { suggestions.Add(new CategorizedSuggestion { Category = SuggestionCategory.Efficiency, Title = $"路径偏长(+{lengthDiff:F1}%)", Description = $"该路径比组内最短路径长 {lengthDiff:F1}%," + $"建议考虑更短路线", RelatedRouteId = analysis.RouteId, RelatedRouteName = analysis.RouteName, Priority = 3 }); } // 推荐最佳路径 if (analysis.IsBestInGroup) { suggestions.Add(new CategorizedSuggestion { Category = SuggestionCategory.GroupComparison, Title = $"🏆 本组最佳路径推荐", Description = $"路径 {analysis.RouteName} 是到达『{group.EndPointDescription}』的最佳选择," + $"综合评分 {analysis.WeightedScore:F1} 分", RelatedRouteId = analysis.RouteId, RelatedRouteName = analysis.RouteName, Priority = 5 }); } } // 3. 转弯建议 if (analysis.TurnDifficultyScore < 60) { suggestions.Add(new CategorizedSuggestion { Category = SuggestionCategory.TurnComplexity, Title = "转弯难度较高", Description = $"路径 {analysis.RouteName} 转弯难度分数较低," + $"建议检查急转弯位置", RelatedRouteId = analysis.RouteId, RelatedRouteName = analysis.RouteName, Priority = 3 }); } return suggestions.OrderByDescending(s => s.Priority).ToList(); } #endregion #region 私有计算方法 /// /// 计算安全性分数 /// private double CalculateSafetyScore(int collisionCount, int hotspotCount) { // 简单扣分制:每个碰撞扣1分,每个热点扣1分 double penalty = collisionCount * 1.0 + hotspotCount * 1.0; return Math.Max(0, 100 - penalty); } /// /// 计算效率分数 - 基于路径长度(越短越高效) /// public double CalculateEfficiencyScore(PathRoute route, double groupMinLength) { // 如果没有组内最短路径作为参考,使用当前路径长度 double referenceLength = groupMinLength > 0 ? groupMinLength : route.TotalLength; if (referenceLength <= 0) return 50; // 默认中等分数 // 计算与最短路径的差异率 double lengthDiffPercent = (route.TotalLength - referenceLength) / referenceLength; // 长度效率分:相同为100分,每多10%扣10分 double efficiencyScore = Math.Max(0, 100 - lengthDiffPercent * 100); return Math.Round(Math.Min(100, efficiencyScore), 1); } /// /// 计算转弯难度分数 /// private double CalculateTurnDifficultyScore(List edges) { if (edges == null || edges.Count == 0) return 100; // 无转弯,满分 // 简单扣分制:每个圆弧转弯扣1分 int arcEdgeCount = edges.Count(e => e.SegmentType == PathSegmentType.Arc); return Math.Max(0, 100 - arcEdgeCount * 1.0); } /// /// 计算路径曲折度分数 /// private double CalculateTortuosityScore(PathRoute route) { var sortedPoints = route.GetSortedPoints(); if (sortedPoints.Count < 2) return 100; var startPoint = sortedPoints.First().Position; var endPoint = sortedPoints.Last().Position; // 计算直线距离(模型单位) double straightDistanceModelUnits = Distance(startPoint, endPoint); // 转换为米(与 TotalLength 单位一致) double straightDistanceMeters = NavisworksTransport.Utils.UnitsConverter.ConvertToMeters(straightDistanceModelUnits); if (straightDistanceMeters <= 0) return 100; // 直达性 = 直线距离 / 实际路径长度 × 100(越接近100越好) // 实际路径长度由 TotalLength 计算属性提供(已转换为米) if (route.TotalLength <= 0.001) return 100; double directness = straightDistanceMeters / route.TotalLength * 100; double score = Math.Min(100, Math.Max(0, Math.Round(directness, 1))); LogManager.Info($"[直达性计算] {route.Name}: 直线距离={straightDistanceMeters:F2}m, 实际长度={route.TotalLength:F2}m, 直达性={score:F1}"); return score; } /// /// 计算冗余度分数 /// private double CalculateRedundancyScore(PathRoute route, List channels) { // 简单扣分制:基于通道窄宽缩减量扣分 // 每缩减10%扣1分 // 如果路径没有物体参数,使用配置默认值(转换为模型单位) double objectWidth = route.MaxObjectWidth > 0 ? route.MaxObjectWidth : NavisworksTransport.Utils.UnitsConverter.ConvertFromMeters(1.0); // 默认1米 double safetyMargin = route.SafetyMargin > 0 ? route.SafetyMargin : NavisworksTransport.Utils.UnitsConverter.ConvertFromMeters(0.1); // 默认0.1米 double effectiveObjectWidth = objectWidth + safetyMargin * 2; double channelWidth; if (channels == null || channels.Count == 0) { // 使用配置中的宽度限制(模型单位) var config = NavisworksTransport.Core.Config.ConfigManager.Instance.Current.Logistics; channelWidth = config.WidthLimit > 0 ? config.WidthLimit : NavisworksTransport.Utils.UnitsConverter.ConvertFromMeters(3.0); // 默认3米 } else { // ChannelInfo.WidthLimit 应该是模型单位 channelWidth = channels.Min(c => c.WidthLimit); } // 计算宽度缩减率(正值表示有冗余,负值表示不足) double widthMargin = (channelWidth - effectiveObjectWidth) / effectiveObjectWidth; // 每缩减10%扣1分(反向计算:越宽越高分) double score = 100 - widthMargin * 100 * 0.1; return Math.Min(100, Math.Max(0, score)); } #endregion #region 辅助方法 /// /// 获取路径的碰撞数据 /// private List GetCollisionsForRoute(string routeId) { try { // 获取最新的碰撞检测记录 var clashResult = _database.GetLatestDetectionResultByRouteId(routeId); if (clashResult == null) return new List(); // 获取碰撞对象 var collisionObjects = _database.GetClashDetectiveCollisionObjects(clashResult.Id); // 转换为CollisionResult列表 var collisions = new List(); foreach (var obj in collisionObjects) { if (obj.AnimatedObjectTrackedPosX.HasValue && obj.AnimatedObjectTrackedPosY.HasValue && obj.AnimatedObjectTrackedPosZ.HasValue) { collisions.Add(new CollisionResult { Center = new Point3D( obj.AnimatedObjectTrackedPosX.Value, obj.AnimatedObjectTrackedPosY.Value, obj.AnimatedObjectTrackedPosZ.Value), Item2 = ModelItemProxyHelper.CreateProxy(obj.DisplayName) }); } } return collisions; } catch (Exception ex) { LogManager.Error($"[获取碰撞数据] 路径 {routeId} 失败: {ex.Message}"); return new List(); } } /// /// 计算两点距离 /// private double Distance(Point3D p1, Point3D p2) { if (p1 == null || p2 == null) return double.MaxValue; double dx = p1.X - p2.X; double dy = p1.Y - p2.Y; double dz = p1.Z - p2.Z; return Math.Sqrt(dx * dx + dy * dy + dz * dz); } /// /// 计算碰撞中心点 /// private Point3D CalculateCenter(List collisions) { if (collisions == null || collisions.Count == 0) return new Point3D(0, 0, 0); double sumX = 0, sumY = 0, sumZ = 0; int count = 0; foreach (var collision in collisions) { if (collision.Center != null) { sumX += collision.Center.X; sumY += collision.Center.Y; sumZ += collision.Center.Z; count++; } } if (count == 0) return new Point3D(0, 0, 0); return new Point3D(sumX / count, sumY / count, sumZ / count); } /// /// 计算组中心点(取所有路径终点的平均值) /// private Point3D CalculateGroupCenter(List analyses, List routes) { if (analyses == null || analyses.Count == 0 || routes == null) return new Point3D(0, 0, 0); double sumX = 0, sumY = 0, sumZ = 0; int count = 0; foreach (var analysis in analyses) { var route = routes.FirstOrDefault(r => r.Id == analysis.RouteId); var endPoint = route?.GetEndPoint()?.Position; if (endPoint != null) { sumX += endPoint.X; sumY += endPoint.Y; sumZ += endPoint.Z; count++; } } if (count == 0) return new Point3D(0, 0, 0); return new Point3D(sumX / count, sumY / count, sumZ / count); } /// /// 更新组内统计信息 /// private void UpdateGroupStatistics(EndpointGroup group) { if (group?.PathAnalyses == null || group.PathAnalyses.Count == 0) return; // 更新排名 var sorted = group.PathAnalyses.OrderByDescending(a => a.WeightedScore).ToList(); for (int i = 0; i < sorted.Count; i++) { sorted[i].GroupRanking = i + 1; } LogManager.Debug($"[组统计] 组 {group.GroupId} 共 {group.RouteCount} 条路径," + $"最佳: {group.BestPath?.RouteName}"); } /// /// 设置默认分数(错误恢复) /// private void SetDefaultScores(PathDetailedAnalysis analysis) { analysis.SafetyScore = 50; analysis.EfficiencyScore = 50; analysis.TurnDifficultyScore = 50; analysis.TortuosityScore = 50; analysis.RedundancyScore = 50; analysis.WeightedScore = 50; } /// /// 获取安全的显示名称 /// private string GetSafeDisplayName(ModelItem item) { if (item == null) return "未知对象"; try { return item.DisplayName ?? "未命名对象"; } catch { return "未知对象"; } } #endregion } /// /// ModelItem代理类(用于当没有实际ModelItem时) /// internal static class ModelItemProxyHelper { public static ModelItem CreateProxy(string displayName) { // 这是一个简化实现,实际可能需要更复杂的处理 // 这里仅用于存储显示名称 return null; } } }