NavisworksTransport/src/Core/PathAnalysisEngine.cs

812 lines
31 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 Autodesk.Navisworks.Api;
using NavisworksTransport.Core.Models;
using NavisworksTransport.Utils;
namespace NavisworksTransport.Core
{
/// <summary>
/// 路径分析引擎
/// 负责计算路径的各项指标和评分
/// </summary>
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
/// <summary>
/// 获取终点分组阈值(转换为模型单位用于距离比较)
/// </summary>
private double EndpointThreshold => NavisworksTransport.Utils.UnitsConverter.ConvertFromMeters(ENDPOINT_THRESHOLD_METERS);
/// <summary>
/// 构造函数
/// </summary>
public PathAnalysisEngine(PathDatabase database)
{
_database = database ?? throw new ArgumentNullException(nameof(database));
}
#region
/// <summary>
/// 分析单条路径的所有指标
/// </summary>
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;
}
/// <summary>
/// 对多条路径进行分组(按终点)
/// </summary>
public List<EndpointGroup> GroupPathsByEndpoint(List<PathRoute> routes)
{
if (routes == null || routes.Count == 0)
return new List<EndpointGroup>();
var groups = new List<EndpointGroup>();
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;
}
/// <summary>
/// 分析多条路径并分组,包含完整的上下文计算
/// </summary>
public List<EndpointGroup> AnalyzeAndGroupPaths(List<PathRoute> routes, string strategy)
{
if (routes == null || routes.Count == 0)
return new List<EndpointGroup>();
// 第一步:初步分析所有路径(获取基础数据)
var analyses = new List<PathDetailedAnalysis>();
foreach (var route in routes)
{
var analysis = AnalyzePath(route, new AnalysisContext { Strategy = strategy });
analyses.Add(analysis);
}
// 第二步:按终点分组
var groups = new List<EndpointGroup>();
var processed = new HashSet<string>();
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<PathDetailedAnalysis>();
// 首先添加当前路径
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;
}
/// <summary>
/// 检测碰撞热点
/// </summary>
public List<CollisionHotspot> DetectHotspots(List<CollisionResult> collisions, double radius = HOTSPOT_RADIUS)
{
var hotspots = new List<CollisionHotspot>();
if (collisions == null || collisions.Count < 2)
return hotspots;
var processed = new HashSet<int>();
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<CollisionResult> { 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();
}
/// <summary>
/// 计算加权综合评分
/// </summary>
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);
}
/// <summary>
/// 查找组内最佳路径
/// </summary>
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];
}
/// <summary>
/// 生成优化建议
/// </summary>
public List<CategorizedSuggestion> GenerateSuggestions(PathDetailedAnalysis analysis, EndpointGroup group)
{
var suggestions = new List<CategorizedSuggestion>();
// 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
/// <summary>
/// 计算安全性分数
/// </summary>
private double CalculateSafetyScore(int collisionCount, int hotspotCount)
{
// 简单扣分制每个碰撞扣1分每个热点扣1分
double penalty = collisionCount * 1.0 + hotspotCount * 1.0;
return Math.Max(0, 100 - penalty);
}
/// <summary>
/// 计算效率分数 - 基于路径长度(越短越高效)
/// </summary>
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);
}
/// <summary>
/// 计算转弯难度分数
/// </summary>
private double CalculateTurnDifficultyScore(List<PathEdge> 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);
}
/// <summary>
/// 计算路径曲折度分数
/// </summary>
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;
}
/// <summary>
/// 计算冗余度分数
/// </summary>
private double CalculateRedundancyScore(PathRoute route, List<ChannelInfo> channels)
{
// 简单扣分制:基于通道窄宽缩减量扣分
// 每缩减10%扣1分
// 如果路径没有物体参数,使用配置默认值
double objectWidth = route.MaxObjectWidth > 0 ? route.MaxObjectWidth : 1.0;
double safetyMargin = route.SafetyMargin > 0 ? route.SafetyMargin : 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.WidthLimitMeters > 0 ? config.WidthLimitMeters : 3.0;
}
else
{
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
/// <summary>
/// 获取路径的碰撞数据
/// </summary>
private List<CollisionResult> GetCollisionsForRoute(string routeId)
{
try
{
// 获取最新的ClashDetective结果
var clashResult = _database.GetLatestClashDetectiveResultByRouteId(routeId);
if (clashResult == null)
return new List<CollisionResult>();
// 获取碰撞对象
var collisionObjects = _database.GetClashDetectiveCollisionObjects(clashResult.Id);
// 转换为CollisionResult列表
var collisions = new List<CollisionResult>();
foreach (var obj in collisionObjects)
{
if (obj.Item1PosX.HasValue && obj.Item1PosY.HasValue && obj.Item1PosZ.HasValue)
{
collisions.Add(new CollisionResult
{
Center = new Point3D(
obj.Item1PosX.Value,
obj.Item1PosY.Value,
obj.Item1PosZ.Value),
Item2 = ModelItemProxyHelper.CreateProxy(obj.DisplayName)
});
}
}
return collisions;
}
catch (Exception ex)
{
LogManager.Error($"[获取碰撞数据] 路径 {routeId} 失败: {ex.Message}");
return new List<CollisionResult>();
}
}
/// <summary>
/// 计算两点距离
/// </summary>
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);
}
/// <summary>
/// 计算碰撞中心点
/// </summary>
private Point3D CalculateCenter(List<CollisionResult> 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);
}
/// <summary>
/// 计算组中心点(取所有路径终点的平均值)
/// </summary>
private Point3D CalculateGroupCenter(List<PathDetailedAnalysis> analyses, List<PathRoute> 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);
}
/// <summary>
/// 更新组内统计信息
/// </summary>
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}");
}
/// <summary>
/// 设置默认分数(错误恢复)
/// </summary>
private void SetDefaultScores(PathDetailedAnalysis analysis)
{
analysis.SafetyScore = 50;
analysis.EfficiencyScore = 50;
analysis.TurnDifficultyScore = 50;
analysis.TortuosityScore = 50;
analysis.RedundancyScore = 50;
analysis.WeightedScore = 50;
}
/// <summary>
/// 获取安全的显示名称
/// </summary>
private string GetSafeDisplayName(ModelItem item)
{
if (item == null)
return "未知对象";
try
{
return item.DisplayName ?? "未命名对象";
}
catch
{
return "未知对象";
}
}
#endregion
}
/// <summary>
/// ModelItem代理类用于当没有实际ModelItem时
/// </summary>
internal static class ModelItemProxyHelper
{
public static ModelItem CreateProxy(string displayName)
{
// 这是一个简化实现,实际可能需要更复杂的处理
// 这里仅用于存储显示名称
return null;
}
}
}