diff --git a/CLAUDE.md b/CLAUDE.md index 02d0d3d..7223d14 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -186,3 +186,4 @@ public class PathClickToolPlugin : ToolPlugin { } - **Hot reload**: Restart Navisworks required after compilation to load new plugin version - **Debugging**: Use LogManager for centralized logging and the built-in log viewer dialog for log analysis - **2026 Features**: Test advanced animation capabilities, enhanced collision detection, and improved model handling +- 在编码中,不要用回退或向后兼容的思路和步骤 \ No newline at end of file diff --git a/src/PathPlanning/GridMapGenerator.cs b/src/PathPlanning/GridMapGenerator.cs index 2912486..a6b9337 100644 --- a/src/PathPlanning/GridMapGenerator.cs +++ b/src/PathPlanning/GridMapGenerator.cs @@ -236,10 +236,10 @@ namespace NavisworksTransport.PathPlanning var channelRelatedItems = CollectChannelRelatedItems(allChannelItems.ToList()); LogManager.Info($"[包围盒2.5D模式] 智能收集到 {channelRelatedItems.Count} 个通道相关节点"); - // 2. 直接遍历处理障碍物(包围盒检测) - LogManager.Info("[包围盒2.5D模式] 步骤2: 包围盒遍历处理障碍物"); + // 2. 直接遍历处理障碍物(包围盒检测) - 使用高性能优化版本 + LogManager.Info("[包围盒2.5D模式] 步骤2: 高性能包围盒遍历处理障碍物"); double scanHeight = vehicleHeight + safetyMargin; // 扫描高度 = 车辆高度 + 安全间隙 - ProcessObstaclesWithBoundingBox(document, channelCoverage.GridMap, channelRelatedItems, scanHeight); + ProcessObstaclesWithBoundingBoxOptimized(document, channelCoverage.GridMap, channelRelatedItems, scanHeight); LogManager.Info($"[阶段2完成] 障碍物处理后网格统计: {channelCoverage.GridMap.GetStatistics()}"); @@ -1087,7 +1087,361 @@ namespace NavisworksTransport.PathPlanning /// - /// 使用包围盒遍历方式处理障碍物(优化版) + /// 数据预处理:提取所有模型项的属性到缓存(单线程API访问) + /// + /// 所有模型项 + /// 通道项集合 + /// 网格地图 + /// 扫描高度 + /// 模型项属性缓存 + private Dictionary PreprocessItemProperties( + List allItems, + HashSet channelItemsSet, + GridMap gridMap, + double scanHeight) + { + LogManager.Info("[数据预处理] 开始提取模型项属性"); + var startTime = DateTime.Now; + var itemCache = new Dictionary(); + + var apiCalls = 0; + foreach (var item in allItems) + { + try + { + var properties = new ItemProperties(); + + // API调用1: HasGeometry + properties.HasGeometry = item.HasGeometry; + apiCalls++; + + // API调用2: Children.Any() + properties.IsContainer = item.Children?.Any() ?? false; + apiCalls++; + + // API调用3: BoundingBox() + properties.BoundingBox = item.BoundingBox(); + apiCalls++; + + // 通道关系检查(HashSet查找,相对较快) + properties.IsChannelItem = channelItemsSet.Contains(item); + properties.IsChildOfChannel = !properties.IsChannelItem && IsChildOfChannelItems(item, channelItemsSet); + + // 高度范围检查(纯数值计算) + if (properties.BoundingBox != null) + { + properties.IsInScanHeightRange = IsInScanHeightRange(properties.BoundingBox, gridMap, scanHeight); + } + + itemCache[item] = properties; + } + catch (Exception ex) + { + LogManager.Debug($"[数据预处理] 处理模型项失败: {item?.DisplayName ?? "NULL"}, {ex.Message}"); + } + } + + var elapsed = (DateTime.Now - startTime).TotalMilliseconds; + LogManager.Info($"[数据预处理] 完成,处理 {allItems.Count} 个模型项,执行 {apiCalls} 次API调用,耗时: {elapsed:F1}ms"); + + return itemCache; + } + + /// + /// 使用Search API一次性获取所有有几何体的模型项 + /// 这比逐项调用HasGeometry快得多 + /// + /// + /// 使用Search API + LINQ混合优化获取目标模型项 + /// Search API预筛选 + LINQ精细过滤,最大化性能提升 + /// + /// + /// 使用Search API + LINQ混合优化获取目标模型项 + /// Search API预筛选 + LINQ精细过滤,最大化性能提升 + /// + /// + /// 使用Search API + LINQ混合优化获取目标模型项 + /// Search API预筛选 + LINQ精细过滤,最大化性能提升 + /// + /// + /// 使用Search API + LINQ混合优化获取目标模型项 + /// Search API预筛选 + LINQ精细过滤,最大化性能提升 + /// + private List GetGeometryItemsUsingSearchAPI(Document document) + { + try + { + LogManager.Info("[Search API优化] 开始批量获取几何体项目"); + var startTime = DateTime.Now; + + // 统计模型总数 + var totalModelItems = document.Models.RootItemDescendantsAndSelf.Count(); + LogManager.Info($"[Search API优化] 模型总数统计: {totalModelItems} 个模型项"); + + var search = new Search(); + + // Search API阶段:批量筛选基础条件 + search.SearchConditions.Add( + SearchCondition.HasCategoryByName(PropertyCategoryNames.Geometry)); + + search.Selection.SelectAll(); + search.Locations = SearchLocations.DescendantsAndSelf; + + // 一次性获取所有有几何体的项目 + var geometryItems = search.FindAll(document, false); + + var searchElapsed = (DateTime.Now - startTime).TotalMilliseconds; + var filteredByGeometry = totalModelItems - geometryItems.Count(); + var geometryFilterRatio = (double)filteredByGeometry / totalModelItems; + + LogManager.Info($"[Search API优化] Search API阶段完成: 获取 {geometryItems.Count()} 个几何体项目,耗时: {searchElapsed:F1}ms"); + LogManager.Info($"[Search API优化] 几何体筛选效果: 过滤掉 {filteredByGeometry} 个无几何体项目,筛选率: {geometryFilterRatio:P1}"); + + // 🔥 关键优化:直接返回Search API结果,移除所有昂贵的LINQ统计调用 + // 基于多模型测试验证,LINQ筛选条件(IsCollection, IsComposite, Children.Any)都返回0项 + var filteredItems = geometryItems.ToList(); + + var totalElapsed = (DateTime.Now - startTime).TotalMilliseconds; + var totalFilteredCount = totalModelItems - filteredItems.Count; + var totalFilterRatio = (double)totalFilteredCount / totalModelItems; + + LogManager.Info($"[Search API优化] 优化完成,总耗时: {totalElapsed:F1}ms"); + LogManager.Info($"[Search API优化] 优化效果: 总模型项 {totalModelItems} → 几何体项目 {filteredItems.Count}"); + LogManager.Info($"[Search API优化] 总优化率: 减少 {totalFilteredCount} 项 ({totalFilterRatio:P1})"); + + return filteredItems; + } + catch (Exception ex) + { + LogManager.Error($"[Search API优化] 筛选失败: {ex.Message}"); + throw; + } + } + + /// + /// 轻量级后处理:只对Search API预筛选的几何体项目进行必要的属性提取 + /// 大幅减少API调用次数 + /// + /// + /// 轻量级后处理:只对Search API预筛选的几何体项目进行必要的属性提取 + /// 大幅减少API调用次数 + /// + private Dictionary PostProcessGeometryItems( + List geometryItems, + HashSet channelItemsSet, + GridMap gridMap, + double scanHeight) + { + LogManager.Info("[轻量级后处理] 开始处理Search API筛选结果"); + var startTime = DateTime.Now; + var itemCache = new Dictionary(); + + var totalItems = geometryItems.Count; + var apiCalls = 0; + var skippedByChannel = 0; + var skippedByBoundingBox = 0; + var skippedByHeight = 0; + + foreach (var item in geometryItems) + { + try + { + var properties = new ItemProperties(); + + // 已知条件:HasGeometry = true(Search API保证) + properties.HasGeometry = true; + + // 快速筛除1:通道相关项目(HashSet查找,极快) + properties.IsChannelItem = channelItemsSet.Contains(item); + if (properties.IsChannelItem) + { + skippedByChannel++; + continue; // 跳过通道项目,不进行昂贵的API调用 + } + + properties.IsChildOfChannel = IsChildOfChannelItems(item, channelItemsSet); + if (properties.IsChildOfChannel) + { + skippedByChannel++; + continue; // 跳过通道子项目 + } + + // 移除容器检查:基于4个模型测试验证,所有几何体项目的容器检查结果都是0 + // 注释:原有的容器检查代码 + // API调用1: Children.Any() - 检查是否为容器 + // properties.IsContainer = item.Children?.Any() ?? false; + // apiCalls++; + // if (properties.IsContainer) { + // skippedByContainer++; + // continue; // 跳过容器项目 + // } + + // 设为已知值(避免后续筛选中的判断错误) + properties.IsContainer = false; // 基于测试验证,几何体项目都不是容器 + + // API调用1: BoundingBox() - 获取边界框(唯一保留的API调用) + properties.BoundingBox = item.BoundingBox(); + apiCalls++; + + if (properties.BoundingBox == null) + { + skippedByBoundingBox++; + continue; // 跳过无边界框项目 + } + + // 快速计算:高度范围检查(纯数值计算,无API调用) + properties.IsInScanHeightRange = IsInScanHeightRange(properties.BoundingBox, gridMap, scanHeight); + if (!properties.IsInScanHeightRange) + { + skippedByHeight++; + continue; // 跳过高度范围外项目 + } + + // 只有通过所有筛选的项目才加入结果 + itemCache[item] = properties; + } + catch (Exception ex) + { + LogManager.Debug($"[轻量级后处理] 处理模型项失败: {item?.DisplayName ?? "NULL"}, {ex.Message}"); + } + } + + var elapsed = (DateTime.Now - startTime).TotalMilliseconds; + LogManager.Info($"[轻量级后处理] 完成,输入 {totalItems} 项,有效结果 {itemCache.Count} 项,API调用 {apiCalls} 次,耗时: {elapsed:F1}ms"); + LogManager.Info($"[轻量级后处理] 筛除统计 - 通道相关: {skippedByChannel}, 容器: 0 (已优化移除), 无边界框: {skippedByBoundingBox}, 高度范围外: {skippedByHeight}"); + LogManager.Info($"[性能优化] 移除无效容器检查,每个几何体项目节省1次API调用"); + + return itemCache; + } + + /// + /// 使用包围盒遍历方式处理障碍物(高性能优化版) + /// 分离API访问和数值计算,利用并行处理加速纯计算部分 + /// + /// Navisworks文档 + /// 网格地图 + /// 通道模型项列表 + /// 扫描高度范围 + private void ProcessObstaclesWithBoundingBoxOptimized(Document document, GridMap gridMap, HashSet channelItems, double scanHeight) + { + try + { + LogManager.Info("[高性能障碍物处理] 开始处理障碍物 - Search API优化版本"); + var startTime = DateTime.Now; + + var channelItemsSet = channelItems ?? new HashSet(); + + // 阶段1:Search API预筛选(一次性批量获取几何体项目) + var geometryItems = GetGeometryItemsUsingSearchAPI(document); + + LogManager.Info($"[高性能障碍物处理] 输入统计 - 几何体项目: {geometryItems.Count}, 通道元素: {channelItemsSet.Count}"); + + // 阶段2:轻量级后处理(只对预筛选结果进行必要的API调用) + var itemCache = PostProcessGeometryItems(geometryItems, channelItemsSet, gridMap, scanHeight); + + // 阶段3:条件过滤(并行数值计算)- 使用50%CPU核心优化 + LogManager.Info("[高性能障碍物处理] 阶段3: 并行条件过滤"); + var filterStart = DateTime.Now; + + // 🔥 性能优化:限制并行度为CPU核心数的50%,避免过度并行化 + var optimalParallelism = Math.Max(1, Environment.ProcessorCount / 2); + LogManager.Info($"[并行优化] CPU核心: {Environment.ProcessorCount}, 使用并行度: {optimalParallelism}"); + + var validItems = itemCache.AsParallel() + .WithDegreeOfParallelism(optimalParallelism) + .Where(kvp => kvp.Value.HasGeometry && + !kvp.Value.IsContainer && + !kvp.Value.IsChannelItem && + !kvp.Value.IsChildOfChannel && + kvp.Value.BoundingBox != null && + kvp.Value.IsInScanHeightRange) + .ToList(); + + var filterElapsed = (DateTime.Now - filterStart).TotalMilliseconds; + LogManager.Info($"[高性能障碍物处理] 阶段3完成: 从缓存中筛选出 {validItems.Count} 个有效项,耗时: {filterElapsed:F1}ms"); + + // 阶段4:网格覆盖计算(并行几何计算)- 使用50%CPU核心优化 + LogManager.Info("[高性能障碍物处理] 阶段4: 并行网格覆盖计算"); + var gridCalcStart = DateTime.Now; + + var gridUpdates = validItems.AsParallel() + .WithDegreeOfParallelism(optimalParallelism) + .SelectMany(kvp => + { + var coveredCells = CalculateBoundingBoxGridCoverage(kvp.Value.BoundingBox, gridMap); + return coveredCells.Select(cell => new GridUpdate + { + Item = kvp.Key, + X = cell.x, + Y = cell.y, + BoundingBox = kvp.Value.BoundingBox + }); + }) + .ToList(); + + var gridCalcElapsed = (DateTime.Now - gridCalcStart).TotalMilliseconds; + LogManager.Info($"[高性能障碍物处理] 阶段4完成: 生成 {gridUpdates.Count} 个网格更新操作,耗时: {gridCalcElapsed:F1}ms"); + + // 阶段5:网格状态更新(单线程写入) + LogManager.Info("[高性能障碍物处理] 阶段5: 单线程网格状态更新"); + var updateStart = DateTime.Now; + + var updatedCells = 0; + var obstacleItems = 0; + + foreach (var update in gridUpdates) + { + var cell = gridMap.Cells[update.X, update.Y]; + + // 只更新通道网格,跳过已是障碍物的网格 + if (cell.CellType == CategoryAttributeManager.LogisticsElementType.通道 && cell.IsInChannel) + { + // 标记为障碍物 + cell.IsWalkable = false; + cell.CellType = CategoryAttributeManager.LogisticsElementType.障碍物; + cell.Cost = double.MaxValue; + cell.IsInChannel = false; + cell.RelatedModelItem = update.Item; + + // 记录高度信息 + if (cell.PassableHeights == null) + cell.PassableHeights = new List(); + + var groundHeight = cell.WorldPosition.Z; + cell.PassableHeights.Add(new HeightInterval( + update.BoundingBox.Min.Z - groundHeight, + update.BoundingBox.Max.Z - groundHeight + )); + + gridMap.Cells[update.X, update.Y] = cell; + updatedCells++; + } + } + + obstacleItems = gridUpdates.GroupBy(u => u.Item).Count(); + var updateElapsed = (DateTime.Now - updateStart).TotalMilliseconds; + LogManager.Info($"[高性能障碍物处理] 阶段5完成: 更新 {updatedCells} 个网格单元,涉及 {obstacleItems} 个障碍物项,耗时: {updateElapsed:F1}ms"); + + // 输出总体统计 + var totalElapsed = (DateTime.Now - startTime).TotalMilliseconds; + LogManager.Info($"[高性能障碍物处理] 处理完成,总耗时: {totalElapsed:F1}ms"); + + var searchTime = (DateTime.Now - startTime).TotalMilliseconds - filterElapsed - gridCalcElapsed - updateElapsed; + LogManager.Info($"[高性能障碍物处理] 性能分解 - Search API预筛选+后处理: {searchTime:F1}ms, 过滤: {filterElapsed:F1}ms, 计算: {gridCalcElapsed:F1}ms, 更新: {updateElapsed:F1}ms"); + + // 性能对比统计 + LogManager.Info($"[性能优化统计] 处理几何体项目: {geometryItems.Count}, 最终有效项目: {validItems.Count}, 优化比率: {(1.0 - (double)validItems.Count / geometryItems.Count):P1}"); + LogManager.Info($"[并行优化统计] 使用 {optimalParallelism}/{Environment.ProcessorCount} CPU核心,每核心处理 {Math.Ceiling((double)validItems.Count / optimalParallelism)} 个项目"); + } + catch (Exception ex) + { + LogManager.Error($"[高性能障碍物处理] 处理失败: {ex.Message}"); + throw; + } + } + + /// + /// 使用包围盒遍历方式处理障碍物(原版本-单线程) /// 直接遍历所有模型项,使用包围盒检测替代空间索引+垂直扫描 /// /// Navisworks文档 @@ -1332,4 +1686,32 @@ namespace NavisworksTransport.PathPlanning } } } + + #region 性能优化数据结构 + + /// + /// 模型项缓存属性,存储昂贵API调用的结果 + /// + internal class ItemProperties + { + public bool HasGeometry { get; set; } + public bool IsContainer { get; set; } + public BoundingBox3D BoundingBox { get; set; } + public bool IsChannelItem { get; set; } + public bool IsChildOfChannel { get; set; } + public bool IsInScanHeightRange { get; set; } + } + + /// + /// 网格更新操作,用于并行计算后的批量更新 + /// + internal class GridUpdate + { + public ModelItem Item { get; set; } + public int X { get; set; } + public int Y { get; set; } + public BoundingBox3D BoundingBox { get; set; } + } + + #endregion } \ No newline at end of file