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