先过滤楼层,再进行规划

This commit is contained in:
tian 2025-08-15 20:34:04 +08:00
parent b261efcaae
commit 720727a370
9 changed files with 1840 additions and 411 deletions

View File

@ -7,6 +7,7 @@ using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Forms.Integration;
using NavisApplication = Autodesk.Navisworks.Api.Application;
@ -48,7 +49,24 @@ namespace NavisworksTransport
{
// 捕获未处理的异常
AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
System.Windows.Forms.Application.ThreadException += OnThreadException;
// 捕获任务调度器未观察到的异常
System.Threading.Tasks.TaskScheduler.UnobservedTaskException += OnUnobservedTaskException;
// 尝试设置线程异常处理,但如果失败就跳过(可能控件已创建)
try
{
System.Windows.Forms.Application.ThreadException += OnThreadException;
System.Windows.Forms.Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
LogManager.Info("[全局异常] WinForms异常处理已设置");
}
catch (InvalidOperationException ex)
{
LogManager.Warning($"[全局异常] 无法设置WinForms异常处理模式控件已创建: {ex.Message}");
// 仍然可以添加事件处理器
System.Windows.Forms.Application.ThreadException += OnThreadException;
}
_isInitialized = true;
LogManager.Info("[全局异常] 全局异常处理器已初始化");
@ -70,16 +88,76 @@ namespace NavisworksTransport
string message = ex?.Message ?? "未知异常";
string stackTrace = ex?.StackTrace ?? "无堆栈信息";
LogManager.Error($"[全局异常] 未处理异常: {message}");
LogManager.Error($"[全局异常] 堆栈信息: {stackTrace}");
LogManager.Error($"[全局异常] 异常类型: {ex?.GetType().Name ?? "Unknown"}");
LogManager.Error($"[全局异常] 是否终止: {e.IsTerminating}");
LogManager.Error($"[全局异常] ===== 未处理异常发生 =====");
LogManager.Error($"[全局异常] 异常类型: {ex?.GetType().FullName ?? "Unknown"}");
LogManager.Error($"[全局异常] 异常消息: {message}");
LogManager.Error($"[全局异常] 是否终止进程: {e.IsTerminating}");
LogManager.Error($"[全局异常] 当前线程ID: {System.Threading.Thread.CurrentThread.ManagedThreadId}");
LogManager.Error($"[全局异常] 当前线程名称: {System.Threading.Thread.CurrentThread.Name ?? ""}");
// 检查是否为特定类型的异常
if (ex is OutOfMemoryException)
{
LogManager.Error($"[全局异常] ⚠️ 内存不足异常");
var memoryUsage = GC.GetTotalMemory(false);
LogManager.Error($"[全局异常] 当前内存使用: {memoryUsage / 1024 / 1024:F1} MB");
}
else if (ex is StackOverflowException)
{
LogManager.Error($"[全局异常] ⚠️ 堆栈溢出异常");
}
else if (ex is AccessViolationException)
{
LogManager.Error($"[全局异常] ⚠️ 访问违规异常");
}
else if (ex is System.Runtime.InteropServices.SEHException)
{
LogManager.Error($"[全局异常] ⚠️ 结构化异常处理异常");
}
LogManager.Error($"[全局异常] 堆栈跟踪:");
LogManager.Error(stackTrace);
if (ex?.InnerException != null)
{
LogManager.Error($"[全局异常] 内部异常: {ex.InnerException.GetType().Name}: {ex.InnerException.Message}");
LogManager.Error($"[全局异常] 内部异常堆栈: {ex.InnerException.StackTrace}");
}
// 显示用户友好的错误信息
ShowErrorDialog("程序发生未预期的错误", message, ex);
// 尝试恢复关键组件
TryRecoverComponents();
LogManager.Error($"[全局异常] ===== 异常处理完成 =====");
}
catch
{
// 异常处理中的异常,保持静默
}
}
/// <summary>
/// 处理未观察到的任务异常
/// </summary>
private static void OnUnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
{
try
{
Exception ex = e.Exception;
LogManager.Error($"[全局异常] 未观察任务异常: {ex.Message}");
LogManager.Error($"[全局异常] 堆栈信息: {ex.StackTrace}");
LogManager.Error($"[全局异常] 异常类型: {ex.GetType().Name}");
// 标记异常已被观察,防止应用程序终止
e.SetObserved();
// 显示用户友好的错误信息
ShowErrorDialog("程序发生后台任务错误", ex.Message, ex);
// 尝试恢复关键组件
TryRecoverComponents();
}
catch
{
@ -246,6 +324,23 @@ namespace NavisworksTransport
[DockPanePlugin(420, 700, FixedSize = false, AutoScroll = true)]
public class Main : DockPanePlugin
{
/// <summary>
/// 静态构造函数:在任何实例创建之前初始化全局异常处理器
/// </summary>
static Main()
{
try
{
// 在最早的时机初始化全局异常处理器
GlobalExceptionHandler.Initialize();
}
catch (Exception ex)
{
// 如果静态构造函数中的异常处理器初始化失败,记录到日志
LogManager.Error($"[静态构造] 静态构造函数中初始化异常处理器失败: {ex.Message}");
}
}
/// <summary>
/// 会话初始化标志用于确保在同一Navisworks会话中只清空一次日志
/// </summary>
@ -349,10 +444,7 @@ namespace NavisworksTransport
{
LogManager.Info("=== 物流路径规划插件初始化开始 ===");
// 初始化全局异常处理
GlobalExceptionHandler.Initialize();
// 初始化路径规划管理器
// 初始化路径规划管理器(全局异常处理已在静态构造函数中初始化)
InitializePathPlanningManager();
LogManager.Info("插件会话初始化完成");
@ -1516,16 +1608,23 @@ namespace NavisworksTransport
int totalModels = 0;
int logisticsModels = 0;
// 统计所有模型和物流模型
// 使用Search API高效查找物流模型
using (var search = new Search())
{
var searchConditions = search.SearchConditions;
searchConditions.Clear();
var hasLogisticsCategoryCondition = SearchCondition.HasCategoryByDisplayName(CategoryAttributeManager.LogisticsCategories.LOGISTICS);
searchConditions.Add(hasLogisticsCategoryCondition);
ModelItemCollection searchResults = search.FindAll(document, false);
logisticsModels = searchResults.Count;
}
// 统计总模型数(仍需要遍历,但这是必要的)
foreach (ModelItem rootItem in document.Models.RootItems)
{
foreach (ModelItem item in rootItem.DescendantsAndSelf)
{
totalModels++;
if (CategoryAttributeManager.HasLogisticsAttributes(item))
{
logisticsModels++;
}
}
}
@ -1546,21 +1645,25 @@ namespace NavisworksTransport
var document = NavisApplication.ActiveDocument;
if (document?.Models == null) return;
foreach (ModelItem rootItem in document.Models.RootItems)
// 使用Search API高效查找物流模型
using (var search = new Search())
{
foreach (ModelItem item in rootItem.DescendantsAndSelf)
var searchConditions = search.SearchConditions;
searchConditions.Clear();
var hasLogisticsCategoryCondition = SearchCondition.HasCategoryByDisplayName(CategoryAttributeManager.LogisticsCategories.LOGISTICS);
searchConditions.Add(hasLogisticsCategoryCondition);
ModelItemCollection searchResults = search.FindAll(document, false);
foreach (ModelItem item in searchResults)
{
if (CategoryAttributeManager.HasLogisticsAttributes(item))
var category = CategoryAttributeManager.GetLogisticsPropertyValue(item, CategoryAttributeManager.LogisticsProperties.TYPE);
var listItem = new ListViewItem(new string[]
{
var category = CategoryAttributeManager.GetLogisticsPropertyValue(item, CategoryAttributeManager.LogisticsProperties.TYPE);
var listItem = new ListViewItem(new string[]
{
item.DisplayName.Length > 25 ? item.DisplayName.Substring(0, 25) + "..." : item.DisplayName,
string.IsNullOrEmpty(category) ? "未知" : category
});
listItem.Tag = item; // 保存ModelItem引用
listView.Items.Add(listItem);
}
item.DisplayName.Length > 25 ? item.DisplayName.Substring(0, 25) + "..." : item.DisplayName,
string.IsNullOrEmpty(category) ? "未知" : category
});
listItem.Tag = item; // 保存ModelItem引用
listView.Items.Add(listItem);
}
}
@ -2888,22 +2991,26 @@ namespace NavisworksTransport
var document = NavisApplication.ActiveDocument;
if (document?.Models == null) return;
foreach (ModelItem rootItem in document.Models.RootItems)
// 使用Search API高效查找物流模型
using (var search = new Search())
{
foreach (ModelItem item in rootItem.DescendantsAndSelf)
var searchConditions = search.SearchConditions;
searchConditions.Clear();
var hasLogisticsCategoryCondition = SearchCondition.HasCategoryByDisplayName(CategoryAttributeManager.LogisticsCategories.LOGISTICS);
searchConditions.Add(hasLogisticsCategoryCondition);
ModelItemCollection searchResults = search.FindAll(document, false);
foreach (ModelItem item in searchResults)
{
if (CategoryAttributeManager.HasLogisticsAttributes(item))
// 使用COM API读取属性以确保获取最新值
var category = CategoryAttributeManager.GetLogisticsPropertyValueViaCom(item, CategoryAttributeManager.LogisticsProperties.TYPE);
var listItem = new ListViewItem(new string[]
{
// 使用COM API读取属性以确保获取最新值
var category = CategoryAttributeManager.GetLogisticsPropertyValueViaCom(item, CategoryAttributeManager.LogisticsProperties.TYPE);
var listItem = new ListViewItem(new string[]
{
item.DisplayName.Length > 25 ? item.DisplayName.Substring(0, 25) + "..." : item.DisplayName,
string.IsNullOrEmpty(category) ? "未知" : category
});
listItem.Tag = item; // 保存ModelItem引用
listView.Items.Add(listItem);
}
item.DisplayName.Length > 25 ? item.DisplayName.Substring(0, 25) + "..." : item.DisplayName,
string.IsNullOrEmpty(category) ? "未知" : category
});
listItem.Tag = item; // 保存ModelItem引用
listView.Items.Add(listItem);
}
}
@ -2931,16 +3038,23 @@ namespace NavisworksTransport
int totalModels = 0;
int logisticsModels = 0;
// 统计所有模型和物流模型
// 使用Search API高效查找物流模型
using (var search = new Search())
{
var searchConditions = search.SearchConditions;
searchConditions.Clear();
var hasLogisticsCategoryCondition = SearchCondition.HasCategoryByDisplayName(CategoryAttributeManager.LogisticsCategories.LOGISTICS);
searchConditions.Add(hasLogisticsCategoryCondition);
ModelItemCollection searchResults = search.FindAll(document, false);
logisticsModels = searchResults.Count;
}
// 统计总模型数(仍需要遍历,但这是必要的)
foreach (ModelItem rootItem in document.Models.RootItems)
{
foreach (ModelItem item in rootItem.DescendantsAndSelf)
{
totalModels++;
if (CategoryAttributeManager.HasLogisticsAttributes(item))
{
logisticsModels++;
}
}
}

View File

@ -4,6 +4,7 @@ using System.Linq;
using Autodesk.Navisworks.Api;
using Autodesk.Navisworks.Api.Plugins;
using NavisworksTransport.PathPlanning;
using NavisworksTransport.Utils;
namespace NavisworksTransport
{
@ -480,12 +481,8 @@ namespace NavisworksTransport
try
{
// 获取所有模型项
var allItems = new List<ModelItem>();
foreach (ModelItem rootItem in Application.ActiveDocument.Models.RootItems)
{
CollectAllItems(rootItem, allItems);
}
// 使用Search API获取具有物流属性的模型项
var allItems = SearchLogisticsItemsUsingSearchAPI();
foreach (var item in allItems)
{
@ -744,11 +741,7 @@ namespace NavisworksTransport
}
// 获取所有非通道元素
var allItems = new List<ModelItem>();
foreach (ModelItem rootItem in Application.ActiveDocument.Models.RootItems)
{
CollectAllItems(rootItem, allItems);
}
var allItems = SearchLogisticsItemsUsingSearchAPI();
var nonChannelItems = allItems.Except(_selectedChannels).ToList();
// 使用可见性管理器隐藏非通道元素
@ -886,13 +879,9 @@ namespace NavisworksTransport
try
{
// 获取所有模型项
var allItems = new List<ModelItem>();
foreach (ModelItem rootItem in Application.ActiveDocument.Models.RootItems)
{
CollectAllItems(rootItem, allItems);
}
var filteredItems = allItems.Where(item => item.HasGeometry).ToArray();
// 使用Search API获取具有物流属性的模型项
var logisticsItems = SearchLogisticsItemsUsingSearchAPI();
var filteredItems = logisticsItems.Where(item => item.HasGeometry).ToArray();
result.TotalModelItems = filteredItems.Length;
@ -1076,12 +1065,8 @@ namespace NavisworksTransport
try
{
// 获取所有已标记的物流项
var allItems = new List<ModelItem>();
foreach (ModelItem rootItem in Application.ActiveDocument.Models.RootItems)
{
CollectAllItems(rootItem, allItems);
}
// 使用Search API获取具有物流属性的模型项
var allItems = SearchLogisticsItemsUsingSearchAPI();
var filteredItems = allItems.Where(item => item.HasGeometry && CategoryAttributeManager.HasLogisticsAttributes(item)).ToArray();
var logisticsItems = new ModelItemCollection();
@ -1744,25 +1729,6 @@ namespace NavisworksTransport
#region
/// <summary>
/// 递归收集所有ModelItem
/// </summary>
/// <param name="item">当前ModelItem</param>
/// <param name="collection">收集列表</param>
private void CollectAllItems(ModelItem item, List<ModelItem> collection)
{
if (item == null) return;
collection.Add(item);
if (item.Children != null && item.Children.Count() > 0)
{
foreach (ModelItem child in item.Children)
{
CollectAllItems(child, collection);
}
}
}
/// <summary>
/// 计算两个3D点之间的距离
@ -1814,12 +1780,8 @@ namespace NavisworksTransport
try
{
// 获取所有标记为障碍物的模型项
var allItems = new List<ModelItem>();
foreach (ModelItem rootItem in Application.ActiveDocument.Models.RootItems)
{
CollectAllItems(rootItem, allItems);
}
// 使用Search API获取具有物流属性的模型项
var allItems = SearchLogisticsItemsUsingSearchAPI();
var filteredItems = allItems.Where(item => item.HasGeometry && CategoryAttributeManager.HasLogisticsAttributes(item)).ToArray();
var logisticsItems = new ModelItemCollection();
@ -3630,9 +3592,53 @@ namespace NavisworksTransport
{
try
{
LogManager.WriteLog("[ToolPlugin事件] ===== 收到精确点击坐标 =====");
LogManager.WriteLog($"[ToolPlugin事件] 精确坐标: ({pickResult.Point.X:F3}, {pickResult.Point.Y:F3}, {pickResult.Point.Z:F3})");
LogManager.WriteLog($"[ToolPlugin事件] 选中对象: {pickResult.ModelItem?.DisplayName ?? "NULL"}");
LogManager.WriteLog("[ToolPlugin事件-V2] ===== 收到精确点击坐标 =====");
LogManager.WriteLog($"[ToolPlugin事件-V2] 精确坐标: ({pickResult.Point.X:F3}, {pickResult.Point.Y:F3}, {pickResult.Point.Z:F3})");
LogManager.WriteLog($"[ToolPlugin事件-V2] 选中对象: {pickResult.ModelItem?.DisplayName ?? "NULL"}");
LogManager.WriteLog($"[ToolPlugin事件-V2] 🔍 代码版本检查: 新版本代码正在运行");
// 检查当前选中的通道状态
LogManager.WriteLog($"[ToolPlugin事件-V2] 当前_selectedChannels状态: {(_selectedChannels == null ? "NULL" : $"{_selectedChannels.Count}")}");
// 如果没有选中的通道,尝试实时搜索
if (_selectedChannels == null || _selectedChannels.Count == 0)
{
LogManager.WriteLog("[ToolPlugin事件] 没有预选通道,开始实时搜索可通行的物流模型");
try
{
var document = Application.ActiveDocument;
if (document?.Models != null)
{
// 使用Search API实时搜索
var logisticsItems = SearchLogisticsItemsUsingSearchAPI();
LogManager.WriteLog($"[ToolPlugin事件] 实时搜索找到 {logisticsItems.Count} 个物流模型");
if (logisticsItems.Count > 0)
{
// 筛选出可通行的物流模型
var modelCollection = new ModelItemCollection();
modelCollection.AddRange(logisticsItems);
var traversableItems = CategoryAttributeManager.FilterTraversableItems(modelCollection);
LogManager.WriteLog($"[ToolPlugin事件] 筛选出 {traversableItems.Count} 个可通行的物流模型");
// 临时设置为选中通道
if (_selectedChannels == null) _selectedChannels = new List<ModelItem>();
_selectedChannels.Clear();
foreach (ModelItem item in traversableItems)
{
_selectedChannels.Add(item);
LogManager.WriteLog($"[ToolPlugin事件] 添加可通行模型: '{item.DisplayName}'");
}
}
}
}
catch (Exception searchEx)
{
LogManager.WriteLog($"[ToolPlugin事件] 实时搜索失败: {searchEx.Message}");
}
}
// 检查是否在可通行的物流模型内
if (_selectedChannels != null && _selectedChannels.Any())
@ -3771,26 +3777,39 @@ namespace NavisworksTransport
_selectedChannels.Clear();
// 收集所有物流标记的模型
var allLogisticsItems = new ModelItemCollection();
foreach (ModelItem rootItem in document.Models.RootItems)
LogManager.Info("[通道自动选择] 开始搜索可通行的物流模型");
// 使用Search API代替手动遍历提高效率
LogManager.Info("[通道自动选择] 使用Search API搜索物流属性");
var allLogisticsItems = SearchLogisticsItemsUsingSearchAPI();
LogManager.Info($"[通道自动选择] Search API找到 {allLogisticsItems.Count} 个具有物流属性的模型项");
// 如果Search API没找到尝试使用GridMapGenerator的SearchChannelModels方法
if (allLogisticsItems.Count == 0)
{
foreach (ModelItem item in rootItem.DescendantsAndSelf)
LogManager.Warning("[通道自动选择] Search API未找到物流模型尝试使用GridMapGenerator搜索方法");
var gridGenerator = new PathPlanning.GridMapGenerator();
var channelModels = SearchChannelModelsUsingGridGenerator(document);
LogManager.Info($"[通道自动选择] GridGenerator找到 {channelModels.Count} 个通道模型");
// 将通道模型添加到结果中
foreach (var channel in channelModels)
{
if (CategoryAttributeManager.HasLogisticsAttributes(item))
{
allLogisticsItems.Add(item);
}
allLogisticsItems.Add(channel);
}
}
// 筛选出可通行的物流模型
var traversableItems = CategoryAttributeManager.FilterTraversableItems(allLogisticsItems);
var modelCollection = new ModelItemCollection();
modelCollection.AddRange(allLogisticsItems);
var traversableItems = CategoryAttributeManager.FilterTraversableItems(modelCollection);
LogManager.Info($"[通道自动选择] 筛选出 {traversableItems.Count} 个可通行的物流模型");
// 将可通行的物流模型添加到_selectedChannels
foreach (ModelItem item in traversableItems)
{
_selectedChannels.Add(item);
LogManager.Info($"[通道自动选择] 添加可通行模型: '{item.DisplayName}'");
}
if (_selectedChannels.Count > 0)
@ -3801,16 +3820,71 @@ namespace NavisworksTransport
// 触发事件
ChannelsSelected?.Invoke(this, _selectedChannels);
LogManager.Info($"自动选择了 {_selectedChannels.Count} 个可通行的物流模型");
LogManager.Info($"[通道自动选择] ✅ 自动选择了 {_selectedChannels.Count} 个可通行的物流模型");
}
else
{
LogManager.Warning("未找到任何可通行的物流模型");
LogManager.Warning("[通道自动选择] ❌ 未找到任何可通行的物流模型");
}
}
catch (Exception ex)
{
LogManager.WriteLog($"自动选择可通行物流模型失败: {ex.Message}");
LogManager.Error($"[通道自动选择] 自动选择可通行物流模型失败: {ex.Message}");
LogManager.Error($"[通道自动选择] 异常堆栈: {ex.StackTrace}");
}
}
/// <summary>
/// 使用GridMapGenerator的方法搜索通道模型作为备用方案
/// </summary>
private List<ModelItem> SearchChannelModelsUsingGridGenerator(Document document)
{
var channelModels = new List<ModelItem>();
try
{
LogManager.Info("[GridGenerator搜索] 开始使用GridMapGenerator搜索通道");
// 使用Search API查找所有有物流属性的模型项
using (var search = new Search())
{
var searchConditions = search.SearchConditions;
searchConditions.Clear();
var hasLogisticsCondition = SearchCondition.HasCategoryByDisplayName(CategoryAttributeManager.LogisticsCategories.LOGISTICS);
searchConditions.Add(hasLogisticsCondition);
ModelItemCollection logisticsResults = search.FindAll(document, false);
LogManager.Info($"[GridGenerator搜索] Search API找到 {logisticsResults.Count} 个具有物流属性的模型项");
// 筛选出通道类型的模型项
foreach (ModelItem item in logisticsResults)
{
try
{
var logisticsType = CategoryAttributeManager.GetLogisticsElementType(item);
LogManager.Info($"[GridGenerator搜索] 检查模型项: DisplayName='{item.DisplayName}', 物流类型={logisticsType}");
if (logisticsType == CategoryAttributeManager.LogisticsElementType.)
{
channelModels.Add(item);
LogManager.Info($"[GridGenerator搜索] ✅ 找到通道模型: '{item.DisplayName}'");
}
}
catch (Exception ex)
{
LogManager.Warning($"[GridGenerator搜索] 检查模型项时出错: {ex.Message}");
}
}
}
LogManager.Info($"[GridGenerator搜索] 搜索完成,共找到 {channelModels.Count} 个通道模型");
return channelModels;
}
catch (Exception ex)
{
LogManager.Error($"[GridGenerator搜索] 搜索通道模型失败: {ex.Message}");
return channelModels;
}
}
@ -3858,9 +3932,10 @@ namespace NavisworksTransport
/// <param name="startPoint">起点(世界坐标)</param>
/// <param name="endPoint">终点(世界坐标)</param>
/// <param name="vehicleSize">车辆尺寸(米)</param>
/// <param name="safetyMargin">安全间隙(车辆膨胀间隙,米)</param>
/// <param name="gridSize">网格精度(米)</param>
/// <returns>自动生成的路径</returns>
public PathRoute AutoPlanPath(Point3D startPoint, Point3D endPoint, double vehicleSize = 1.0, double gridSize = 0.5)
public PathRoute AutoPlanPath(Point3D startPoint, Point3D endPoint, double vehicleSize = 1.0, double safetyMargin = 0.5, double gridSize = -1)
{
try
{
@ -3876,20 +3951,120 @@ namespace NavisworksTransport
LogManager.Info($"模型边界: Min({bounds.Min.X:F2}, {bounds.Min.Y:F2}), Max({bounds.Max.X:F2}, {bounds.Max.Y:F2})");
// 智能选择网格大小
if (gridSize <= 0)
{
gridSize = CalculateOptimalGridSize(bounds);
LogManager.Info($"自动选择网格大小: {gridSize}米");
}
// 2. 生成网格地图
OnStatusChanged("正在生成网格地图...");
var gridMapGenerator = new GridMapGenerator();
var gridMap = gridMapGenerator.GenerateFromBIM(Application.ActiveDocument, bounds, gridSize, vehicleSize);
GridMap gridMap = null;
try
{
LogManager.Info($"开始调用GridMapGenerator.GenerateFromBIM起点: {startPoint}, 终点: {endPoint}");
gridMap = gridMapGenerator.GenerateFromBIM(
Application.ActiveDocument,
bounds,
gridSize,
vehicleSize,
safetyMargin,
startPoint,
endPoint
);
LogManager.Info("GridMapGenerator.GenerateFromBIM 调用完成");
}
catch (Exception ex)
{
LogManager.Error($"网格地图生成过程中发生严重错误: {ex.Message}");
LogManager.Error($"异常类型: {ex.GetType().Name}");
LogManager.Error($"堆栈跟踪: {ex.StackTrace}");
if (ex.InnerException != null)
{
LogManager.Error($"内部异常: {ex.InnerException.Message}");
LogManager.Error($"内部异常堆栈: {ex.InnerException.StackTrace}");
}
throw new AutoPathPlanningException($"网格地图生成失败: {ex.Message}", ex);
}
LogManager.Info($"网格地图生成完成: {gridMap.GetStatistics()}");
// 3. 获取通道数据用于精确高度计算
LogManager.Info("开始获取通道数据用于精确高度计算...");
var channelItems = GetChannelItemsForHeightCalculation();
LogManager.Info($"获取通道数据完成: {channelItems?.Count() ?? 0} 个通道项");
// 4. 执行A*路径查找(启用精确高度计算)
OnStatusChanged("正在计算最优路径...");
var pathFinder = new AutoPathFinder();
var pathPoints = pathFinder.FindPath(startPoint, endPoint, gridMap, channelItems);
// 4. 执行A*路径查找
LogManager.Info("=== 关键点:准备更新状态为'正在计算最优路径...' ===");
try
{
OnStatusChanged("正在计算最优路径...");
LogManager.Info("✅ 状态更新成功");
}
catch (Exception ex)
{
LogManager.Error($"❌ 状态更新失败: {ex.Message}");
LogManager.Error($"堆栈跟踪: {ex.StackTrace}");
throw;
}
LogManager.Info("=== 准备执行A*路径查找 ===");
LogManager.Info($"当前线程ID: {System.Threading.Thread.CurrentThread.ManagedThreadId}");
LogManager.Info($"当前线程名称: {System.Threading.Thread.CurrentThread.Name ?? ""}");
LogManager.Info($"是否为UI线程: {System.Threading.Thread.CurrentThread.GetApartmentState()}");
LogManager.Info($"GridMap统计: {gridMap?.GetStatistics() ?? "null"}");
LogManager.Info($"ChannelItems数量: {channelItems?.Count() ?? 0}");
// 检查内存状态
GC.Collect();
var memoryBefore = GC.GetTotalMemory(false);
LogManager.Info($"当前内存使用: {memoryBefore / 1024 / 1024:F1} MB");
LogManager.Info("开始创建AutoPathFinder实例...");
AutoPathFinder pathFinder = null;
List<Point3D> pathPoints = null;
try
{
// 添加内存和状态检查
GC.Collect(); // 强制垃圾回收,释放内存
LogManager.Info("已执行垃圾回收");
pathFinder = new AutoPathFinder();
LogManager.Info("AutoPathFinder实例创建成功");
LogManager.Info("开始调用FindPath方法...");
pathPoints = pathFinder.FindPath(startPoint, endPoint, gridMap, channelItems);
LogManager.Info("FindPath方法调用完成");
}
catch (OutOfMemoryException memEx)
{
LogManager.Error($"内存不足异常: {memEx.Message}");
throw new AutoPathPlanningException($"内存不足,无法完成路径规划: {memEx.Message}", memEx);
}
catch (StackOverflowException stackEx)
{
LogManager.Error($"堆栈溢出异常: {stackEx.Message}");
throw new AutoPathPlanningException($"堆栈溢出,路径规划算法过于复杂: {stackEx.Message}", stackEx);
}
catch (AccessViolationException accessEx)
{
LogManager.Error($"访问违规异常: {accessEx.Message}");
throw new AutoPathPlanningException($"访问违规,可能是内存损坏: {accessEx.Message}", accessEx);
}
catch (Exception ex)
{
LogManager.Error($"路径查找过程中发生未知异常: {ex.GetType().Name}: {ex.Message}");
LogManager.Error($"异常堆栈: {ex.StackTrace}");
if (ex.InnerException != null)
{
LogManager.Error($"内部异常: {ex.InnerException.Message}");
}
throw new AutoPathPlanningException($"路径查找失败: {ex.Message}", ex);
}
if (pathPoints == null || pathPoints.Count < 2)
{
@ -3919,14 +4094,26 @@ namespace NavisworksTransport
LogManager.Info($"自动路径规划成功完成: 路径长度 {autoRoute.TotalLength:F2}米,包含 {autoRoute.Points.Count} 个点");
return autoRoute;
}
catch (AutoPathPlanningException)
catch (AutoPathPlanningException ex)
{
LogManager.Error($"路径规划异常: {ex.Message}");
LogManager.Error($"异常位置: {ex.StackTrace}");
OnErrorOccurred(ex.Message);
throw; // 重新抛出已知的路径规划异常
}
catch (Exception ex)
{
var errorMsg = $"自动路径规划失败: {ex.Message}";
LogManager.Error(errorMsg);
LogManager.Error($"异常类型: {ex.GetType().FullName}");
LogManager.Error($"异常位置: {ex.StackTrace}");
if (ex.InnerException != null)
{
LogManager.Error($"内部异常: {ex.InnerException.Message}");
LogManager.Error($"内部异常位置: {ex.InnerException.StackTrace}");
}
OnErrorOccurred(errorMsg);
throw new AutoPathPlanningException(errorMsg, ex);
}
@ -4335,74 +4522,327 @@ namespace NavisworksTransport
}
}
/// <summary>
/// 根据模型大小智能计算最优网格大小
/// </summary>
/// <param name="bounds">模型边界</param>
/// <returns>最优网格大小(米)</returns>
private double CalculateOptimalGridSize(BoundingBox3D bounds)
{
try
{
// 计算模型的长度和宽度(转换为米)
double widthInModelUnits = bounds.Max.X - bounds.Min.X;
double heightInModelUnits = bounds.Max.Y - bounds.Min.Y;
// 转换为米
double metersToModelUnitsConversion = GetMetersToModelUnitsConversionFactor();
double widthInMeters = widthInModelUnits / metersToModelUnitsConversion;
double heightInMeters = heightInModelUnits / metersToModelUnitsConversion;
double maxDimension = Math.Max(widthInMeters, heightInMeters);
LogManager.Info($"模型尺寸: {widthInMeters:F1}m x {heightInMeters:F1}m, 最大维度: {maxDimension:F1}m");
// 根据建筑规模选择网格大小
double gridSize;
if (maxDimension < 50) // 小型建筑 (< 50米)
{
gridSize = 1.5; // 1.5米网格
LogManager.Info("检测到小型建筑使用1.5米网格");
}
else if (maxDimension < 150) // 中型建筑 (50-150米)
{
gridSize = 1.0; // 1米网格
LogManager.Info("检测到中型建筑使用1米网格");
}
else if (maxDimension < 300) // 大型建筑 (150-300米)
{
gridSize = 0.8; // 0.8米网格
LogManager.Info("检测到大型建筑使用0.8米网格");
}
else // 超大型建筑 (> 300米)
{
gridSize = 0.5; // 0.5米网格
LogManager.Info("检测到超大型建筑使用0.5米网格");
}
return gridSize;
}
catch (Exception ex)
{
LogManager.Warning($"网格大小计算失败: {ex.Message}使用默认值1.0米");
return 1.0;
}
}
/// <summary>
/// 获取米到模型单位的转换因子
/// </summary>
private double GetMetersToModelUnitsConversionFactor()
{
return UnitsConverter.GetMetersToUnitsConversionFactor();
}
/// <summary>
/// 获取通道模型项用于精确高度计算
/// 优先使用用户选择的通道如果没有则使用Navisworks Search API搜索
/// </summary>
/// <returns>通道模型项集合</returns>
private IEnumerable<ModelItem> GetChannelItemsForHeightCalculation()
{
var channelItems = new List<ModelItem>();
try
{
var channelItems = new List<ModelItem>();
var document = Application.ActiveDocument;
LogManager.Info("[通道数据] 开始GetChannelItemsForHeightCalculation方法 - 使用Navisworks Search API");
if (document?.Models == null)
// 方案1如果用户已选择通道优先使用用户选择的通道
if (_selectedChannels != null && _selectedChannels.Count > 0)
{
LogManager.Warning("[路径规划] 文档或模型为空,无法获取通道数据");
LogManager.Info($"[通道数据] 使用用户选择的 {_selectedChannels.Count} 个通道");
foreach (var selectedChannel in _selectedChannels)
{
try
{
if (selectedChannel != null)
{
channelItems.Add(selectedChannel);
LogManager.Info($"[通道数据] 添加用户选择的通道: {selectedChannel.DisplayName ?? "unnamed"}");
}
}
catch (Exception ex)
{
LogManager.Warning($"[通道数据] 处理用户选择的通道时出错: {ex.Message},跳过该通道");
}
}
LogManager.Info($"[通道数据] 完成,使用用户选择的 {channelItems.Count} 个通道用于高度计算");
return channelItems;
}
// 方案2使用Navisworks Search API高效搜索通道类型的物流模型项
LogManager.Info("[通道数据] 用户未选择通道使用Search API搜索物流通道类型的模型项");
var foundChannels = SearchChannelItemsUsingSearchAPI();
channelItems.AddRange(foundChannels);
// 收集所有模型项
var allItems = new List<ModelItem>();
foreach (var model in document.Models)
{
if (model.RootItem != null)
{
CollectAllItems(model.RootItem, allItems);
}
}
LogManager.Debug($"[路径规划] 扫描了 {allItems.Count} 个模型项");
// 过滤出有物流属性的模型项
var logisticsItems = allItems.Where(item =>
item.HasGeometry && CategoryAttributeManager.HasLogisticsAttributes(item)).ToArray();
LogManager.Debug($"[路径规划] 找到 {logisticsItems.Length} 个有物流属性的模型项");
// 过滤出通道类型的模型项
var channelTypes = new[] {
CategoryAttributeManager.LogisticsElementType.,
CategoryAttributeManager.LogisticsElementType.,
CategoryAttributeManager.LogisticsElementType.,
CategoryAttributeManager.LogisticsElementType.
};
// 将数组转换为ModelItemCollection
var logisticsItemCollection = new ModelItemCollection();
foreach (var item in logisticsItems)
{
logisticsItemCollection.Add(item);
}
foreach (var channelType in channelTypes)
{
var items = CategoryAttributeManager.FilterByLogisticsType(logisticsItemCollection, channelType);
channelItems.AddRange(items);
LogManager.Debug($"[路径规划] 找到 {items.Count()} 个 {channelType} 类型的模型项");
}
LogManager.Info($"[路径规划] 找到 {channelItems.Count} 个通道模型项用于精确高度计算");
LogManager.Info($"[通道数据] GetChannelItemsForHeightCalculation完成找到 {channelItems.Count} 个通道模型项");
return channelItems;
}
catch (Exception ex)
{
LogManager.Error($"[路径规划] 获取通道数据时发生错误: {ex.Message}");
return new List<ModelItem>();
LogManager.Error($"[通道数据] GetChannelItemsForHeightCalculation方法发生严重错误: {ex.Message}");
LogManager.Error($"[通道数据] 异常类型: {ex.GetType().Name}");
LogManager.Error($"[通道数据] 堆栈跟踪: {ex.StackTrace}");
if (ex.InnerException != null)
{
LogManager.Error($"[通道数据] 内部异常: {ex.InnerException.Message}");
}
LogManager.Warning($"[通道数据] 返回空列表,路径规划将继续进行但无精确高度计算");
return channelItems; // 返回空列表而不是抛出异常
}
}
/// <summary>
/// 使用Navisworks Search API高效搜索通道类型的模型项
/// </summary>
/// <returns>通道类型的模型项列表</returns>
private List<ModelItem> SearchChannelItemsUsingSearchAPI()
{
var channelItems = new List<ModelItem>();
try
{
var document = Application.ActiveDocument;
if (document?.Models == null)
{
LogManager.Warning("[Search API] 活动文档或模型为空");
return channelItems;
}
LogManager.Info("[Search API] 开始使用Navisworks Search API搜索通道类型模型项");
// 创建搜索对象
using (var search = new Search())
{
// 创建搜索条件:查找具有物流属性的项
var searchConditions = search.SearchConditions;
searchConditions.Clear();
// 搜索条件1有物流类别的项目
var hasLogisticsCategoryCondition = SearchCondition.HasCategoryByDisplayName(CategoryAttributeManager.LogisticsCategories.LOGISTICS);
searchConditions.Add(hasLogisticsCategoryCondition);
LogManager.Info("[Search API] 设置搜索条件:具有物流类型属性的模型项");
// 执行搜索
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
ModelItemCollection searchResults = null;
try
{
searchResults = search.FindAll(document, false);
stopwatch.Stop();
LogManager.Info($"[Search API] 搜索完成,找到 {searchResults?.Count ?? 0} 个具有物流属性的项,耗时: {stopwatch.ElapsedMilliseconds}ms");
}
catch (Exception searchEx)
{
LogManager.Error($"[Search API] 执行搜索时发生错误: {searchEx.Message}");
return channelItems;
}
if (searchResults == null || searchResults.Count == 0)
{
LogManager.Info("[Search API] 未找到具有物流属性的模型项");
return channelItems;
}
// 进一步筛选通道类型的项(包含门、楼梯、电梯等可通行区域)
LogManager.Info("[Search API] 开始筛选通道类型的物流项...");
int processedCount = 0;
int totalCount = searchResults.Count;
// 通道类型包括:通道、门、楼梯、电梯等可通行区域
var channelTypes = new[] {
CategoryAttributeManager.LogisticsElementType.,
CategoryAttributeManager.LogisticsElementType.,
CategoryAttributeManager.LogisticsElementType.,
CategoryAttributeManager.LogisticsElementType.
};
foreach (ModelItem item in searchResults)
{
try
{
processedCount++;
// 每处理100个项输出一次进度
if (processedCount % 100 == 0)
{
LogManager.Info($"[Search API] 通道类型筛选进度: {processedCount}/{totalCount} ({(double)processedCount/totalCount*100:F1}%)");
}
var logisticsType = CategoryAttributeManager.GetLogisticsElementType(item);
if (channelTypes.Contains(logisticsType))
{
channelItems.Add(item);
LogManager.Info($"[Search API] 找到{logisticsType}: {item.DisplayName ?? "unnamed"}");
}
}
catch (Exception ex)
{
LogManager.Warning($"[Search API] 检查项 {processedCount} 的通道类型时出错: {ex.Message}");
}
}
LogManager.Info($"[Search API] 通道类型筛选完成: {totalCount} -> {channelItems.Count}");
}
return channelItems;
}
catch (Exception ex)
{
LogManager.Error($"[Search API] SearchChannelItemsUsingSearchAPI方法发生错误: {ex.Message}");
LogManager.Error($"[Search API] 异常类型: {ex.GetType().Name}");
return channelItems;
}
}
/// <summary>
/// 使用Navisworks Search API高效搜索具有物流属性的模型项
/// </summary>
/// <returns>具有物流属性的模型项列表</returns>
private List<ModelItem> SearchLogisticsItemsUsingSearchAPI()
{
var logisticsItems = new List<ModelItem>();
try
{
var document = Application.ActiveDocument;
if (document?.Models == null)
{
LogManager.Warning("[Search API] 活动文档或模型为空");
return logisticsItems;
}
LogManager.Info("[Search API] 开始使用Search API搜索具有物流属性的模型项");
LogManager.Info($"[Search API] 文档状态: {document?.FileName ?? "NULL"}, 模型数量: {document?.Models?.Count ?? 0}");
LogManager.Info($"[Search API] 搜索目标类别: '{CategoryAttributeManager.LogisticsCategories.LOGISTICS}'");
// 强制文档更新,确保属性变更被索引
try
{
LogManager.Info("[Search API] 强制刷新文档状态以确保属性索引更新");
// 尝试强制刷新文档
if (document.Models.Count > 0)
{
var firstModel = document.Models.First();
LogManager.Info($"[Search API] 第一个模型: {firstModel?.FileName ?? "NULL"}");
}
}
catch (Exception refreshEx)
{
LogManager.Warning($"[Search API] 文档刷新警告: {refreshEx.Message}");
}
// 创建搜索对象
using (var search = new Search())
{
// 🔥 关键修复:选择所有项目作为搜索范围
search.Selection.SelectAll();
LogManager.Info("[Search API] ✅ 已设置搜索范围为全选 (SelectAll)");
// 创建搜索条件:查找具有物流属性的项
var searchConditions = search.SearchConditions;
searchConditions.Clear();
// 搜索条件:有物流类别的项目
var hasLogisticsCategoryCondition = SearchCondition.HasCategoryByDisplayName(CategoryAttributeManager.LogisticsCategories.LOGISTICS);
searchConditions.Add(hasLogisticsCategoryCondition);
LogManager.Info($"[Search API] 设置搜索条件HasCategoryByDisplayName('{CategoryAttributeManager.LogisticsCategories.LOGISTICS}')");
LogManager.Info($"[Search API] 搜索条件数量: {searchConditions.Count}");
// 执行搜索
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
ModelItemCollection searchResults = null;
try
{
LogManager.Info("[Search API] 开始执行 search.FindAll()");
searchResults = search.FindAll(document, false);
stopwatch.Stop();
LogManager.Info($"[Search API] 搜索完成,找到 {searchResults?.Count ?? 0} 个具有物流属性的项,耗时: {stopwatch.ElapsedMilliseconds}ms");
}
catch (Exception searchEx)
{
LogManager.Error($"[Search API] 执行搜索时发生错误: {searchEx.Message}");
return logisticsItems;
}
if (searchResults != null)
{
foreach (ModelItem item in searchResults)
{
logisticsItems.Add(item);
}
}
}
return logisticsItems;
}
catch (Exception ex)
{
LogManager.Error($"[Search API] SearchLogisticsItemsUsingSearchAPI方法发生错误: {ex.Message}");
LogManager.Error($"[Search API] 异常类型: {ex.GetType().Name}");
return logisticsItems;
}
}
}
}

View File

@ -25,11 +25,13 @@ namespace NavisworksTransport.PathPlanning
/// <returns>路径点列表(世界坐标)</returns>
public List<Point3D> FindPath(Point3D start, Point3D end, GridMap gridMap, IEnumerable<ModelItem> channelItems = null)
{
LogManager.Info($"开始A*路径查找: 起点({start?.X:F2}, {start?.Y:F2}), 终点({end?.X:F2}, {end?.Y:F2})");
// 如果提供了通道数据,设置到网格地图中启用精确高度计算
if (channelItems != null)
{
gridMap.SetChannelItems(channelItems);
LogManager.Info($"[路径查找] 已设置通道数据,启用精确高度计算: {channelItems.Count()} 个通道");
LogManager.Info($"已设置 {channelItems.Count()} 个通道用于精确高度计算");
}
return FindPathCore(start, end, gridMap);
@ -46,12 +48,10 @@ namespace NavisworksTransport.PathPlanning
{
try
{
LogManager.Info($"开始A*路径查找: 起点({start.X:F2}, {start.Y:F2}), 终点({end.X:F2}, {end.Y:F2})");
// 验证输入参数
ValidateInputs(start, end, gridMap);
// ⚠️ 设置路径规划的起点和终点用于Z坐标插值
// 设置路径规划的起点和终点用于Z坐标插值
gridMap.PlanningStartPoint = start;
gridMap.PlanningEndPoint = end;
gridMap.HasPlanningPoints = true;
@ -60,14 +60,6 @@ namespace NavisworksTransport.PathPlanning
var startGrid = gridMap.WorldToGrid(start);
var endGrid = gridMap.WorldToGrid(end);
LogManager.Info($"网格坐标: 起点({startGrid.X}, {startGrid.Y}), 终点({endGrid.X}, {endGrid.Y})");
// 验证反向转换
var verifyStart = gridMap.GridToWorld(startGrid);
var verifyEnd = gridMap.GridToWorld(endGrid);
LogManager.Info($"验证反向转换: 起点网格{startGrid.X},{startGrid.Y} -> 世界({verifyStart.X:F2}, {verifyStart.Y:F2}, {verifyStart.Z:F2})");
LogManager.Info($"验证反向转换: 终点网格{endGrid.X},{endGrid.Y} -> 世界({verifyEnd.X:F2}, {verifyEnd.Y:F2}, {verifyEnd.Z:F2})");
// 验证起点和终点是否在有效范围内
if (!gridMap.IsValidGridPosition(startGrid))
{
@ -90,14 +82,12 @@ namespace NavisworksTransport.PathPlanning
throw new AutoPathPlanningException($"终点({end.X:F2}, {end.Y:F2})位于障碍物上");
}
// 转换为RoyT.AStar网格格式
// 转换为RoyT.AStar网格格式并执行A*算法
LogManager.Info("执行A*算法查找路径...");
var astarGrid = ConvertToAStarGrid(gridMap);
// 执行A*路径查找
var startPos = new GridPosition(startGrid.X, startGrid.Y);
var endPos = new GridPosition(endGrid.X, endGrid.Y);
LogManager.Info("执行A*算法路径查找...");
var pathFinder = new PathFinder();
var astarPath = pathFinder.FindPath(startPos, endPos, astarGrid);
@ -108,14 +98,13 @@ namespace NavisworksTransport.PathPlanning
LogManager.Info($"A*算法找到路径,包含 {astarPath.Edges.Count + 1} 个网格点");
// 转换为世界坐标
// 转换为世界坐标并优化
var worldPath = ConvertPathToWorldCoordinates(astarPath, gridMap);
// 应用路径优化
var optimizedPath = OptimizePath(worldPath, gridMap);
var heightCorrectedPath = ApplyPreciseHeightCorrection(optimizedPath, gridMap);
LogManager.Info($"路径查找完成,优化后包含 {optimizedPath.Count} 个点");
return optimizedPath;
LogManager.Info($"路径查找完成,最终包含 {heightCorrectedPath.Count} 个点");
return heightCorrectedPath;
}
catch (AutoPathPlanningException)
{
@ -165,19 +154,24 @@ namespace NavisworksTransport.PathPlanning
{
try
{
LogManager.Info($"转换网格格式: {gridMap.Width}x{gridMap.Height}");
LogManager.Info($"[A*转换] 开始转换网格格式: {gridMap.Width}x{gridMap.Height}");
// ⚠️ 关键修复gridMap.CellSize 是模型单位,需要转换为米
// RoyT.AStar 的 cellSize 必须使用米为单位,因为它的 Position 就是米坐标
LogManager.Info($"[A*转换] 开始单位转换...");
double cellSizeInMeters = UnitsConverter.ConvertToMeters(gridMap.CellSize);
LogManager.Info($"[A*转换] 单位转换完成: {gridMap.CellSize:F2}模型单位 -> {cellSizeInMeters:F2}米");
LogManager.Info($"[A*转换] 创建网格参数...");
var gridSize = new GridSize(gridMap.Width, gridMap.Height);
var cellSize = new Size(Distance.FromMeters((float)cellSizeInMeters), Distance.FromMeters((float)cellSizeInMeters));
var traversalVelocity = Velocity.FromKilometersPerHour(5); // 5 km/h 的遍历速度
LogManager.Info($"A*网格参数: 尺寸={gridMap.Width}x{gridMap.Height}, 模型单位={gridMap.CellSize:F2}, 米单位={cellSizeInMeters:F2}, 速度=5km/h");
LogManager.Info($"[A*转换] A*网格参数: 尺寸={gridMap.Width}x{gridMap.Height}, 模型单位={gridMap.CellSize:F2}, 米单位={cellSizeInMeters:F2}, 速度=5km/h");
LogManager.Info($"[A*转换] 调用Grid.CreateGridWithLateralConnections...");
var grid = Grid.CreateGridWithLateralConnections(gridSize, cellSize, traversalVelocity);
LogManager.Info($"[A*转换] A*网格创建完成");
int blockedCells = 0;
@ -564,5 +558,78 @@ namespace NavisworksTransport.PathPlanning
return turns;
}
/// <summary>
/// 对最终路径点应用精确高度校正,使路径点贴合通道表面
/// </summary>
/// <param name="path">原始路径点</param>
/// <param name="gridMap">网格地图(包含通道信息)</param>
/// <returns>高度校正后的路径点</returns>
private List<Point3D> ApplyPreciseHeightCorrection(List<Point3D> path, GridMap gridMap)
{
try
{
LogManager.Info($"[高度校正] 开始对 {path.Count} 个路径点进行精确高度校正");
// 如果没有通道数据,直接返回原路径
if (gridMap.ChannelItems == null || !gridMap.ChannelItems.Any())
{
LogManager.Info("[高度校正] 无通道数据,跳过高度校正");
return path;
}
// 确保高度检测器可用
if (gridMap.HeightDetector == null)
{
LogManager.Warning("[高度校正] 高度检测器不可用,跳过高度校正");
return path;
}
var correctedPath = new List<Point3D>();
int correctedCount = 0;
for (int i = 0; i < path.Count; i++)
{
var originalPoint = path[i];
try
{
// 使用高度检测器获取该点的精确表面高度
var preciseHeight = gridMap.HeightDetector.GetChannelFloorHeight(originalPoint, gridMap.ChannelItems);
// 检查高度是否需要校正容差1cm
const double heightTolerance = 0.01; // 1cm
if (Math.Abs(originalPoint.Z - preciseHeight) > heightTolerance)
{
var correctedPoint = new Point3D(originalPoint.X, originalPoint.Y, preciseHeight);
correctedPath.Add(correctedPoint);
correctedCount++;
LogManager.Info($"[高度校正] 点{i}: ({originalPoint.X:F2}, {originalPoint.Y:F2}) 高度校正: {originalPoint.Z:F2} -> {preciseHeight:F2}");
}
else
{
// 高度已经正确,无需校正
correctedPath.Add(originalPoint);
LogManager.Debug($"[高度校正] 点{i}: ({originalPoint.X:F2}, {originalPoint.Y:F2}) 高度无需校正: {originalPoint.Z:F2}");
}
}
catch (Exception ex)
{
// 如果某个点的高度检测失败,使用原始点
LogManager.Warning($"[高度校正] 点{i}高度校正失败: {ex.Message},使用原始高度");
correctedPath.Add(originalPoint);
}
}
LogManager.Info($"[高度校正] 高度校正完成,共校正了 {correctedCount}/{path.Count} 个点");
return correctedPath;
}
catch (Exception ex)
{
LogManager.Error($"[高度校正] 路径高度校正失败: {ex.Message},返回原始路径");
return path;
}
}
}
}

View File

@ -68,9 +68,9 @@ namespace NavisworksTransport.PathPlanning
public SlopeAnalyzer SlopeAnalyzer { get; set; }
/// <summary>
/// 是否启用精确高度计算
/// 是否启用精确高度计算(网格构建时禁用以提升性能)
/// </summary>
public bool EnablePreciseHeightCalculation { get; set; } = true;
public bool EnablePreciseHeightCalculation { get; set; } = false;
/// <summary>
/// 网格单元格数组 [x, y]
@ -190,41 +190,16 @@ namespace NavisworksTransport.PathPlanning
}
/// <summary>
/// 根据起点和终点计算插值Z坐标支持精确高度计算
/// 根据起点和终点计算插值Z坐标网格构建时使用传统插值
/// </summary>
/// <param name="worldX">世界坐标X</param>
/// <param name="worldY">世界坐标Y</param>
/// <returns>插值后的Z坐标</returns>
private double CalculateInterpolatedZ(double worldX, double worldY)
{
try
{
LogManager.Info($"[网格地图] 开始计算高度: 位置({worldX:F2}, {worldY:F2}), 精确计算={EnablePreciseHeightCalculation}, 检测器={HeightDetector != null}, 通道数={ChannelItems?.Count() ?? 0}");
// 如果启用精确高度计算且有必要的组件
if (EnablePreciseHeightCalculation && HeightDetector != null && ChannelItems != null)
{
LogManager.Info($"[网格地图] ✅ 条件满足,使用精确高度计算");
var position = new Point3D(worldX, worldY, 0); // Z坐标先设为0由高度检测器确定
var preciseHeight = HeightDetector.GetChannelFloorHeight(position, ChannelItems);
LogManager.Info($"[网格地图] 精确高度计算结果: 位置({worldX:F2}, {worldY:F2}) -> 高度{preciseHeight:F2}");
return preciseHeight;
}
LogManager.Info($"[网格地图] ❌ 条件不满足,使用传统线性插值");
// 使用传统的线性插值方法(稳定且高效)
var legacyHeight = CalculateLegacyInterpolatedZ(worldX, worldY);
LogManager.Info($"[网格地图] 传统插值结果: 位置({worldX:F2}, {worldY:F2}) -> 高度{legacyHeight:F2}");
return legacyHeight;
}
catch (Exception ex)
{
LogManager.Error($"[网格地图] 高度计算失败,使用传统插值: {ex.Message}");
var fallbackHeight = CalculateLegacyInterpolatedZ(worldX, worldY);
LogManager.Info($"[网格地图] 异常后回退结果: 位置({worldX:F2}, {worldY:F2}) -> 高度{fallbackHeight:F2}");
return fallbackHeight;
}
// 网格构建时只使用传统线性插值,提升性能
// 精确高度计算将在最终路径校正阶段进行
return CalculateLegacyInterpolatedZ(worldX, worldY);
}
/// <summary>
@ -317,17 +292,8 @@ namespace NavisworksTransport.PathPlanning
ChannelItems = channelItems?.ToList() ?? new List<ModelItem>();
LogManager.Info($"[网格地图] 设置通道模型项: {(ChannelItems?.Count() ?? 0)} 个通道");
// 如果有通道数据,启用精确高度计算
if (ChannelItems?.Any() == true)
{
EnablePreciseHeightCalculation = true;
LogManager.Info("[网格地图] 精确高度计算已启用");
}
else
{
EnablePreciseHeightCalculation = false;
LogManager.Info("[网格地图] 精确高度计算已禁用(无通道数据)");
}
// 网格构建阶段不启用精确高度计算,仅存储通道数据供后续使用
LogManager.Info("[网格地图] 精确高度计算在网格构建时已禁用,将在路径校正时使用");
}
catch (Exception ex)
{

View File

@ -2,7 +2,8 @@ using System;
using System.Collections.Generic;
using System.Linq;
using Autodesk.Navisworks.Api;
using NavisworksTransport.PathPlanning;
using NavisworksTransport.Utils;
using NavisworksTransport.Core;
namespace NavisworksTransport.PathPlanning
{
@ -29,8 +30,11 @@ namespace NavisworksTransport.PathPlanning
/// <param name="bounds">规划区域边界</param>
/// <param name="cellSize">网格单元格大小(米)</param>
/// <param name="vehicleRadius">车辆半径(用于障碍物膨胀)</param>
/// <param name="safetyMargin">安全间隙(额外的膨胀间距,米)</param>
/// <param name="planningStartPoint">规划起点(用于确定楼层)</param>
/// <param name="planningEndPoint">规划终点(用于确定楼层)</param>
/// <returns>生成的网格地图</returns>
public GridMap GenerateFromBIM(Document document, BoundingBox3D bounds, double cellSize = 2.0, double vehicleRadius = 1.0)
public GridMap GenerateFromBIM(Document document, BoundingBox3D bounds, double cellSize = 2.0, double vehicleRadius = 1.0, double safetyMargin = 0.5, Point3D planningStartPoint = default(Point3D), Point3D planningEndPoint = default(Point3D))
{
try
{
@ -38,10 +42,14 @@ namespace NavisworksTransport.PathPlanning
double metersToModelUnits = GetMetersToModelUnitsConversionFactor();
double cellSizeInModelUnits = cellSize * metersToModelUnits;
double vehicleRadiusInModelUnits = vehicleRadius * metersToModelUnits;
double safetyMarginInModelUnits = safetyMargin * metersToModelUnits;
double totalInflationRadius = vehicleRadiusInModelUnits + safetyMarginInModelUnits;
LogManager.Info($"开始生成网格地图: 边界={bounds}");
LogManager.Info($"单元格大小: {cellSize}m = {cellSizeInModelUnits:F2}模型单位");
LogManager.Info($"车辆半径: {vehicleRadius}m = {vehicleRadiusInModelUnits:F2}模型单位");
LogManager.Info($"安全间隙: {safetyMargin}m = {safetyMarginInModelUnits:F2}模型单位");
LogManager.Info($"总膨胀半径: {totalInflationRadius:F2}模型单位");
LogManager.Info($"模型单位: {Application.ActiveDocument.Units}, 转换系数: {metersToModelUnits}");
// 计算网格尺寸
@ -69,26 +77,49 @@ namespace NavisworksTransport.PathPlanning
LogManager.Info($"调整后网格尺寸: {gridWidth}x{gridHeight} = {totalCells}个单元格");
}
// 创建基础网格地图(使用模型单位)
var gridMap = new GridMap(bounds, cellSizeInModelUnits);
LogManager.Info($"创建网格地图: {gridMap.Width}x{gridMap.Height} = {gridMap.Width * gridMap.Height}个单元格");
// 步骤1先进行楼层过滤获取目标楼层的所有模型项
LogManager.Info("[步骤1] 开始楼层过滤...");
var floorFilteredItems = GetFloorFilteredModelItems(document, planningStartPoint, planningEndPoint);
LogManager.Info($"[步骤1] 楼层过滤完成,共 {floorFilteredItems.Count} 个模型项");
// 获取规划区域内的所有模型项
var modelItems = GetModelItemsInBounds(document, bounds);
LogManager.Info($"在规划区域内找到 {modelItems.Count} 个模型项");
// 步骤2基于楼层过滤结果重新计算工作区域边界
LogManager.Info("[步骤2] 重新计算路径规划工作区域...");
var workingBounds = CalculateWorkingBounds(floorFilteredItems, bounds, planningStartPoint, planningEndPoint);
LogManager.Info($"[步骤2] 工作区域边界: {workingBounds}");
// 步骤3基于工作区域创建网格地图
LogManager.Info("[步骤3] 创建网格地图...");
var gridMap = new GridMap(workingBounds, cellSizeInModelUnits);
LogManager.Info($"[步骤3] 创建网格地图: {gridMap.Width}x{gridMap.Height} = {gridMap.Width * gridMap.Height}个单元格");
// 步骤4只使用楼层过滤后的模型项所有项目都已经在楼层内
var modelItems = floorFilteredItems;
LogManager.Info($"[步骤4] 使用楼层过滤后的 {modelItems.Count} 个模型项进行网格化");
// 识别障碍物
var obstacles = GetObstacleItems(modelItems);
LogManager.Info($"识别出 {obstacles.Count} 个障碍物");
LogManager.Info("开始识别障碍物...");
List<ModelItem> obstacles = null;
try
{
obstacles = GetObstacleItems(modelItems);
LogManager.Info($"识别出 {obstacles.Count} 个障碍物");
}
catch (Exception ex)
{
LogManager.Error($"识别障碍物时发生严重错误: {ex.Message}");
LogManager.Error($"异常类型: {ex.GetType().Name}");
LogManager.Error($"堆栈跟踪: {ex.StackTrace}");
throw new AutoPathPlanningException($"障碍物识别失败: {ex.Message}", ex);
}
// 标记障碍物单元格
MarkObstacleCells(gridMap, obstacles);
// 应用车辆尺寸膨胀(使用模型单位)
if (vehicleRadiusInModelUnits > 0)
// 应用车辆尺寸膨胀(使用模型单位,包含安全间隙
if (totalInflationRadius > 0)
{
ApplyVehicleInflation(gridMap, vehicleRadiusInModelUnits);
LogManager.Info($"已应用车辆膨胀,半径={vehicleRadius}m = {vehicleRadiusInModelUnits:F2}模型单位");
ApplyVehicleInflation(gridMap, totalInflationRadius);
LogManager.Info($"已应用车辆膨胀,车辆半径={vehicleRadius}m + 安全间隙={safetyMargin}m = 总膨胀半径{totalInflationRadius:F2}模型单位");
}
// 识别和标记特殊区域(门、通道等)
@ -105,14 +136,181 @@ namespace NavisworksTransport.PathPlanning
}
/// <summary>
/// 获取指定边界内的所有模型项
/// 进行楼层过滤,获取起终点所在楼层的所有模型项
/// </summary>
/// <param name="document">Navisworks文档</param>
/// <param name="planningStartPoint">规划起点</param>
/// <param name="planningEndPoint">规划终点</param>
/// <returns>楼层过滤后的模型项列表</returns>
private List<ModelItem> GetFloorFilteredModelItems(Document document, Point3D planningStartPoint, Point3D planningEndPoint)
{
try
{
LogManager.Info("[楼层过滤] 开始获取文档中的所有模型项...");
// 获取文档中的所有模型项
var allModelItems = new List<ModelItem>();
foreach (var model in document.Models)
{
if (model.RootItem != null)
{
CollectAllItemsRecursively(model.RootItem, allModelItems);
}
}
LogManager.Info($"[楼层过滤] 文档中共找到 {allModelItems.Count} 个模型项");
// 如果没有规划点,返回所有项目
if (planningStartPoint.Equals(default(Point3D)) || planningEndPoint.Equals(default(Point3D)))
{
LogManager.Info("[楼层过滤] 未提供规划起终点,跳过楼层过滤");
return allModelItems;
}
// 进行楼层感知过滤
var floorFilteredItems = ApplyFloorAwareFilteringWithSearchAPI(allModelItems, planningStartPoint, planningEndPoint, document);
LogManager.Info($"[楼层过滤] 楼层过滤后剩余 {floorFilteredItems.Count} 个模型项");
return floorFilteredItems;
}
catch (Exception ex)
{
LogManager.Error($"[楼层过滤] 楼层过滤失败: {ex.Message}");
return new List<ModelItem>();
}
}
/// <summary>
/// 基于楼层过滤结果计算路径规划工作区域边界
/// </summary>
/// <param name="floorFilteredItems">楼层过滤后的模型项</param>
/// <param name="originalBounds">原始边界(通道边界或模型边界)</param>
/// <param name="planningStartPoint">规划起点</param>
/// <param name="planningEndPoint">规划终点</param>
/// <returns>工作区域边界</returns>
private BoundingBox3D CalculateWorkingBounds(List<ModelItem> floorFilteredItems, BoundingBox3D originalBounds, Point3D planningStartPoint, Point3D planningEndPoint)
{
try
{
if (floorFilteredItems.Count == 0)
{
LogManager.Warning("[工作区域] 没有楼层过滤项,使用原始边界");
return originalBounds;
}
// 计算楼层模型项的实际边界
double minX = double.MaxValue, minY = double.MaxValue, minZ = double.MaxValue;
double maxX = double.MinValue, maxY = double.MinValue, maxZ = double.MinValue;
int validBoundsCount = 0;
foreach (var item in floorFilteredItems)
{
try
{
var itemBounds = item.BoundingBox();
if (itemBounds != null)
{
validBoundsCount++;
minX = Math.Min(minX, itemBounds.Min.X);
minY = Math.Min(minY, itemBounds.Min.Y);
minZ = Math.Min(minZ, itemBounds.Min.Z);
maxX = Math.Max(maxX, itemBounds.Max.X);
maxY = Math.Max(maxY, itemBounds.Max.Y);
maxZ = Math.Max(maxZ, itemBounds.Max.Z);
}
}
catch
{
// 忽略单个项目的边界计算错误
}
}
if (validBoundsCount == 0)
{
LogManager.Warning("[工作区域] 无有效边界的模型项,使用原始边界");
return originalBounds;
}
// 确保起终点都在工作区域内
if (!planningStartPoint.Equals(default(Point3D)))
{
minX = Math.Min(minX, planningStartPoint.X);
minY = Math.Min(minY, planningStartPoint.Y);
minZ = Math.Min(minZ, planningStartPoint.Z);
maxX = Math.Max(maxX, planningStartPoint.X);
maxY = Math.Max(maxY, planningStartPoint.Y);
maxZ = Math.Max(maxZ, planningStartPoint.Z);
}
if (!planningEndPoint.Equals(default(Point3D)))
{
minX = Math.Min(minX, planningEndPoint.X);
minY = Math.Min(minY, planningEndPoint.Y);
minZ = Math.Min(minZ, planningEndPoint.Z);
maxX = Math.Max(maxX, planningEndPoint.X);
maxY = Math.Max(maxY, planningEndPoint.Y);
maxZ = Math.Max(maxZ, planningEndPoint.Z);
}
// 添加一些缓冲区10%的扩展)
double bufferX = (maxX - minX) * 0.1;
double bufferY = (maxY - minY) * 0.1;
double bufferZ = (maxZ - minZ) * 0.1;
var workingBounds = new BoundingBox3D(
new Point3D(minX - bufferX, minY - bufferY, minZ - bufferZ),
new Point3D(maxX + bufferX, maxY + bufferY, maxZ + bufferZ)
);
LogManager.Info($"[工作区域] 原始边界: {originalBounds}");
LogManager.Info($"[工作区域] 楼层边界: Min({minX:F1}, {minY:F1}, {minZ:F1}), Max({maxX:F1}, {maxY:F1}, {maxZ:F1})");
LogManager.Info($"[工作区域] 工作区域: {workingBounds}");
return workingBounds;
}
catch (Exception ex)
{
LogManager.Error($"[工作区域] 计算工作区域失败: {ex.Message},使用原始边界");
return originalBounds;
}
}
/// <summary>
/// 递归收集所有模型项
/// </summary>
/// <param name="item">当前模型项</param>
/// <param name="result">结果列表</param>
private void CollectAllItemsRecursively(ModelItem item, List<ModelItem> result)
{
try
{
if (item.HasGeometry)
{
result.Add(item);
}
foreach (var child in item.Children)
{
CollectAllItemsRecursively(child, result);
}
}
catch (Exception ex)
{
LogManager.Warning($"[递归收集] 处理模型项 {item.DisplayName} 时发生错误: {ex.Message}");
}
}
/// <summary>
/// 获取指定边界内的所有模型项(带楼层过滤)
/// </summary>
/// <param name="document">Navisworks文档</param>
/// <param name="bounds">边界框</param>
/// <returns>模型项列表</returns>
private List<ModelItem> GetModelItemsInBounds(Document document, BoundingBox3D bounds)
/// <param name="planningStartPoint">规划起点</param>
/// <param name="planningEndPoint">规划终点</param>
/// <returns>过滤后的模型项列表</returns>
private List<ModelItem> GetModelItemsInBoundsWithFloorFiltering(Document document, BoundingBox3D bounds, Point3D planningStartPoint, Point3D planningEndPoint)
{
var modelItems = new List<ModelItem>();
var allModelItems = new List<ModelItem>();
try
{
@ -122,16 +320,30 @@ namespace NavisworksTransport.PathPlanning
if (model.RootItem != null)
{
// 递归查找边界内的模型项
CollectItemsInBounds(model.RootItem, bounds, modelItems);
CollectItemsInBounds(model.RootItem, bounds, allModelItems);
}
}
LogManager.Info($"边界内共找到 {allModelItems.Count} 个模型项,开始楼层过滤...");
// 如果没有规划点,则不进行楼层过滤
if (planningStartPoint.Equals(default(Point3D)) || planningEndPoint.Equals(default(Point3D)))
{
LogManager.Info("[楼层过滤] 未提供规划起终点,跳过楼层过滤");
return allModelItems;
}
// 进行楼层感知过滤使用Search API优化
var filteredItems = ApplyFloorAwareFilteringWithSearchAPI(allModelItems, planningStartPoint, planningEndPoint, document);
LogManager.Info($"[楼层过滤] 过滤后剩余 {filteredItems.Count} 个模型项");
return filteredItems;
}
catch (Exception ex)
{
LogManager.Warning($"获取模型项时发生错误: {ex.Message}");
return allModelItems;
}
return modelItems;
}
/// <summary>
@ -193,14 +405,16 @@ namespace NavisworksTransport.PathPlanning
{
try
{
if (IsObstacle(item))
bool isObstacle = IsObstacle(item);
if (isObstacle)
{
obstacles.Add(item);
}
}
catch (Exception ex)
catch (Exception)
{
LogManager.Warning($"检查模型项 {item.DisplayName} 是否为障碍物时发生错误: {ex.Message}");
// 静默处理异常,继续处理下一个项目
}
}
@ -254,7 +468,7 @@ namespace NavisworksTransport.PathPlanning
/// <summary>
/// 判断模型项是否为障碍物
/// 逻辑:只有明确标记为可通行的才是可通行,其他都是障碍物
/// 逻辑:只标记明确的结构障碍物(墙、柱等),默认空间为可通行
/// </summary>
/// <param name="item">模型项</param>
/// <returns>是否为障碍物</returns>
@ -281,29 +495,31 @@ namespace NavisworksTransport.PathPlanning
return false; // 明确的可通行物流类型
}
// 其他所有物流类型(包括障碍物、检查点等)都是障碍物
return true;
// 只有明确标记为障碍物的才是障碍物
return logisticsType == CategoryAttributeManager.LogisticsElementType.;
}
else
{
// 2. 对没有物流属性的项目,只有明确的可通行关键词才不是障碍物
// 2. 对没有物流属性的项目,使用更智能的判断策略
var displayName = item.DisplayName?.ToLower() ?? "";
var className = item.ClassName?.ToLower() ?? "";
// 明确的可通行区域关键词
string[] traversableKeywords = {
"asphalt", "沥青", "road", "道路", "path", "路径",
"floor", "地面", "ground", "grass", "草地",
"通道", "channel", "走道", "corridor"
// 明确的障碍物关键词(只标记真正阻挡路径的结构)
string[] obstacleKeywords = {
"wall", "墙", "column", "柱", "beam", "梁", "pillar", "支柱",
"barrier", "栅栏", "fence", "围栏", "pole", "杆",
"equipment", "设备", "machine", "机器", "tank", "罐",
"stair", "楼梯", "elevator", "电梯", // 未标记物流属性的楼梯和电梯当作障碍物
"solid", "revitsolid", "lcrevitsolid" // Revit实体类型
};
if (traversableKeywords.Any(keyword => displayName.Contains(keyword) || className.Contains(keyword)))
if (obstacleKeywords.Any(keyword => displayName.Contains(keyword) || className.Contains(keyword)))
{
return false; // 明确标识为可通行区域
return true; // 明确标识为障碍物
}
// 默认为障碍物(安全优先
return true;
// 默认为可通行(开放策略,让空间保持开放
return false;
}
}
catch (Exception ex)
@ -311,7 +527,7 @@ namespace NavisworksTransport.PathPlanning
LogManager.Warning($"判断障碍物时发生错误: {ex.Message}");
}
return true; // 出错时默认为障碍物(安全优先
return false; // 出错时默认为可通行(开放策略
}
/// <summary>
@ -737,5 +953,605 @@ namespace NavisworksTransport.PathPlanning
return overriddenCells;
}
/// <summary>
/// 应用楼层感知过滤只保留起终点所在楼层的模型项使用Search API优化
/// </summary>
/// <param name="allItems">所有模型项</param>
/// <param name="startPoint">起点</param>
/// <param name="endPoint">终点</param>
/// <param name="document">Navisworks文档</param>
/// <returns>过滤后的模型项列表</returns>
private List<ModelItem> ApplyFloorAwareFilteringWithSearchAPI(List<ModelItem> allItems, Point3D startPoint, Point3D endPoint, Document document)
{
try
{
// 获取通道所在的楼层值
string targetFloorValue = GetChannelFloorValue(document);
if (string.IsNullOrEmpty(targetFloorValue))
{
LogManager.Info("[楼层过滤] 无法确定目标楼层,使用传统方法");
return ApplyFloorAwareFiltering(allItems, startPoint, endPoint);
}
LogManager.Info($"[楼层过滤] 确定目标楼层: '{targetFloorValue}'");
// 使用Search API直接查找该楼层的所有模型项
var targetFloorItems = GetItemsInTargetFloorUsingSearchAPI(document, targetFloorValue);
if (targetFloorItems.Count == 0)
{
LogManager.Warning($"[楼层过滤] 未找到楼层 '{targetFloorValue}' 的模型项,使用传统方法");
return ApplyFloorAwareFiltering(allItems, startPoint, endPoint);
}
// 直接使用Search API找到的第一层子项目
LogManager.Info($"[楼层过滤] 直接使用Search API结果找到 {targetFloorItems.Count} 个第一层子项目");
// 检查项目边界有效性
var validFloorItems = new List<ModelItem>();
foreach (ModelItem floorItem in targetFloorItems)
{
try
{
var boundingBox = floorItem.BoundingBox();
if (boundingBox != null)
{
validFloorItems.Add(floorItem);
}
}
catch (Exception ex)
{
LogManager.Warning($"[楼层过滤] 检查项目边界失败: {ex.Message}");
}
}
LogManager.Info($"[楼层过滤] Search API过滤完成: {allItems.Count} -> {validFloorItems.Count}");
return validFloorItems;
}
catch (Exception ex)
{
LogManager.Error($"[楼层过滤] Search API过滤失败: {ex.Message},回退到传统方法");
return ApplyFloorAwareFiltering(allItems, startPoint, endPoint);
}
}
/// <summary>
/// 搜索所有物流通道模型
/// </summary>
/// <param name="document">Navisworks文档</param>
/// <returns>通道模型列表</returns>
private List<ModelItem> SearchChannelModels(Document document)
{
var channelModels = new List<ModelItem>();
try
{
LogManager.Info("[通道搜索] 开始搜索物流通道模型");
// 使用Search API查找所有有物流属性的模型项
using (var search = new Search())
{
// 🔥 关键修复:选择所有项目作为搜索范围
search.Selection.SelectAll();
LogManager.Info("[通道搜索] ✅ 已设置搜索范围为全选 (SelectAll)");
var searchConditions = search.SearchConditions;
searchConditions.Clear();
var hasLogisticsCondition = SearchCondition.HasCategoryByDisplayName(CategoryAttributeManager.LogisticsCategories.LOGISTICS);
searchConditions.Add(hasLogisticsCondition);
ModelItemCollection logisticsResults = search.FindAll(document, false);
LogManager.Info($"[通道搜索] Search API找到 {logisticsResults.Count} 个具有物流属性的模型项");
// 筛选出通道类型的模型项
foreach (ModelItem item in logisticsResults)
{
try
{
var logisticsType = CategoryAttributeManager.GetLogisticsElementType(item);
LogManager.Info($"[通道搜索] 检查模型项: DisplayName='{item.DisplayName}', 物流类型={logisticsType}");
if (logisticsType == CategoryAttributeManager.LogisticsElementType.)
{
channelModels.Add(item);
LogManager.Info($"[通道搜索] ✅ 找到通道模型: '{item.DisplayName}'");
}
}
catch (Exception ex)
{
LogManager.Warning($"[通道搜索] 检查模型项时出错: {ex.Message}");
}
}
}
LogManager.Info($"[通道搜索] 搜索完成,共找到 {channelModels.Count} 个通道模型");
return channelModels;
}
catch (Exception ex)
{
LogManager.Error($"[通道搜索] 搜索通道模型失败: {ex.Message}");
return channelModels;
}
}
/// <summary>
/// 获取通道所在的楼层值
/// </summary>
/// <param name="document">Navisworks文档</param>
/// <returns>通道所在楼层的值</returns>
private string GetChannelFloorValue(Document document)
{
try
{
LogManager.Info("[楼层属性] 开始获取通道楼层属性");
// 使用新的搜索方法获取通道模型
var channelModels = SearchChannelModels(document);
if (channelModels.Count == 0)
{
LogManager.Warning("[楼层属性] 未找到任何通道模型");
return null;
}
// 遍历所有通道,获取第一个有楼层属性的通道
foreach (var channelItem in channelModels)
{
LogManager.Info($"[楼层属性] 分析通道 '{channelItem.DisplayName}' 的楼层属性");
foreach (PropertyCategory category in channelItem.PropertyCategories)
{
LogManager.Info($"[楼层属性] 检查属性类别: '{category.DisplayName}'");
foreach (DataProperty property in category.Properties)
{
LogManager.Info($"[楼层属性] 检查属性: '{property.DisplayName}' = '{property.Value}'");
if (property.DisplayName == "层")
{
string floorValue = property.Value?.ToDisplayString()?.Trim();
if (!string.IsNullOrEmpty(floorValue))
{
LogManager.Info($"[楼层属性] ✅ 通道 '{channelItem.DisplayName}' 楼层属性: '{floorValue}'");
return floorValue;
}
}
}
}
LogManager.Warning($"[楼层属性] 通道 '{channelItem.DisplayName}' 没有'层'属性");
}
LogManager.Warning("[楼层属性] 所有通道都没有楼层属性");
return null;
}
catch (Exception ex)
{
LogManager.Error($"[楼层属性] 获取通道楼层值失败: {ex.Message}");
return null;
}
}
/// <summary>
/// 使用Search API获取具有楼层属性的项目
/// </summary>
/// <param name="document">Navisworks文档</param>
/// <param name="targetFloorValue">目标楼层值</param>
/// <returns>属于目标楼层的项目列表</returns>
private List<ModelItem> GetItemsInTargetFloorUsingSearchAPI(Document document, string targetFloorValue)
{
var floorItems = new List<ModelItem>();
try
{
LogManager.Info($"[Search API] 使用正确的类别名称搜索楼层 '{targetFloorValue}'");
// 使用Search API楼层属性"层"位于"项目"类别中
using (var search = new Search())
{
// 🔥 关键修复:设置搜索范围为全选
search.Selection.SelectAll();
LogManager.Info("[Search API] ✅ 已设置搜索范围为全选 (SelectAll)");
var searchConditions = search.SearchConditions;
searchConditions.Clear();
// 正确的搜索条件:在"项目"类别中查找"层"属性
var hasFloorProperty = SearchCondition.HasPropertyByDisplayName("项目", "层");
var floorValueCondition = hasFloorProperty.EqualValue(new VariantData(targetFloorValue));
searchConditions.Add(floorValueCondition);
LogManager.Info($"[Search API] 搜索条件:在'项目'类别中查找'层'属性等于'{targetFloorValue}'");
ModelItemCollection searchResults = search.FindAll(document, false);
LogManager.Info($"[Search API] 找到 {searchResults.Count} 个具有楼层 '{targetFloorValue}' 属性的层级节点");
// 从每个层级节点收集第一层子项目
foreach (ModelItem layerNode in searchResults)
{
// 只收集第一层子项目,不递归到所有后代
LogManager.Info($"[Search API] 开始收集层级节点 '{layerNode.DisplayName}' 的第一层子项目");
int childCount = 0;
foreach (ModelItem child in layerNode.Children)
{
try
{
// 检查是否为通道
bool isChannel = false;
try
{
if (HasLogisticsProperty(child))
{
var logisticsType = CategoryAttributeManager.GetLogisticsElementType(child);
if (logisticsType == CategoryAttributeManager.LogisticsElementType.)
{
isChannel = true;
}
}
}
catch { }
// 对于通道,无论是否有几何体都要包含
if (child.HasGeometry || isChannel)
{
floorItems.Add(child);
childCount++;
}
else
{
// 如果第一层子项目没有几何体继续查找其子项目限制深度为1
foreach (ModelItem grandchild in child.Children)
{
if (grandchild.HasGeometry)
{
floorItems.Add(grandchild);
childCount++;
}
}
}
}
catch (Exception childEx)
{
LogManager.Warning($"[Search API] 处理第一层子项目 '{child?.DisplayName}' 时出错: {childEx.Message}");
}
}
LogManager.Info($"[Search API] 层级节点 '{layerNode.DisplayName}' 第一层子项目收集完成,共收集 {childCount} 个");
}
}
LogManager.Info($"[Search API] Search API找到 {floorItems.Count} 个属于楼层 '{targetFloorValue}' 的项目");
}
catch (Exception ex)
{
LogManager.Warning($"[Search API] Search API搜索楼层 '{targetFloorValue}' 失败: {ex.Message},回退到手动遍历");
// 如果Search API失败回退到手动遍历
var allModelItems = new List<ModelItem>();
foreach (var model in document.Models)
{
if (model.RootItem != null)
{
CollectAllItemsRecursively(model.RootItem, allModelItems);
}
}
LogManager.Info($"[Search API] 开始在 {allModelItems.Count} 个模型项中手动查找楼层 '{targetFloorValue}'");
// 遍历所有模型项,查找具有目标楼层属性的项目
int foundCount = 0;
foreach (var item in allModelItems)
{
try
{
// 检查该项目是否属于目标楼层
if (IsItemInTargetFloor(item, targetFloorValue))
{
floorItems.Add(item);
foundCount++;
}
}
catch (Exception itemEx)
{
LogManager.Warning($"[Search API] 检查模型项楼层属性时出错: {itemEx.Message}");
}
}
LogManager.Info($"[Search API] 手动遍历找到 {foundCount} 个属于楼层 '{targetFloorValue}' 的项目");
}
return floorItems;
}
/// <summary>
/// 检查模型项是否属于目标楼层
/// </summary>
/// <param name="item">模型项</param>
/// <param name="targetFloorValue">目标楼层值</param>
/// <returns>是否属于目标楼层</returns>
private bool IsItemInTargetFloor(ModelItem item, string targetFloorValue)
{
try
{
foreach (PropertyCategory category in item.PropertyCategories)
{
foreach (DataProperty property in category.Properties)
{
if (property.DisplayName == "层")
{
string floorValue = property.Value?.ToDisplayString()?.Trim();
if (!string.IsNullOrEmpty(floorValue) && floorValue == targetFloorValue)
{
return true;
}
}
}
}
}
catch (Exception ex)
{
LogManager.Warning($"[Search API] 检查楼层属性时出错: {ex.Message}");
}
return false;
}
/// <summary>
/// 递归收集节点的所有后代
/// </summary>
/// <param name="node">根节点</param>
/// <param name="result">结果列表</param>
private void CollectAllDescendants(ModelItem node, List<ModelItem> result)
{
// 添加当前节点
result.Add(node);
// 递归添加所有子节点
foreach (ModelItem child in node.Children)
{
CollectAllDescendants(child, result);
}
}
/// <summary>
/// 应用简化的楼层过滤(基于高程)
/// </summary>
/// <param name="allItems">所有模型项</param>
/// <param name="startPoint">起点</param>
/// <param name="endPoint">终点</param>
/// <returns>过滤后的模型项</returns>
private List<ModelItem> ApplySimplifiedFloorFiltering(List<ModelItem> allItems, Point3D startPoint, Point3D endPoint)
{
try
{
// 计算起终点的平均高程
double avgElevation = (startPoint.Z + endPoint.Z) / 2.0;
const double elevationTolerance = 10.0; // 10米高程容差
LogManager.Info($"[简化楼层过滤] 使用平均高程 {avgElevation:F1},容差±{elevationTolerance}m");
var filteredItems = new List<ModelItem>();
foreach (var item in allItems)
{
try
{
var boundingBox = item.BoundingBox();
if (boundingBox != null)
{
// 检查项目的高程范围是否与起终点重叠
double itemMinZ = boundingBox.Min.Z;
double itemMaxZ = boundingBox.Max.Z;
// 如果项目的高程范围与平均高程有重叠,则保留
if (itemMaxZ >= avgElevation - elevationTolerance && itemMinZ <= avgElevation + elevationTolerance)
{
filteredItems.Add(item);
}
}
else
{
// 无包围盒的项目默认保留
filteredItems.Add(item);
}
}
catch
{
// 出错时保留项目
filteredItems.Add(item);
}
}
LogManager.Info($"[简化楼层过滤] 过滤结果: {allItems.Count} -> {filteredItems.Count}");
return filteredItems;
}
catch (Exception ex)
{
LogManager.Error($"[简化楼层过滤] 过滤失败: {ex.Message}");
return allItems;
}
}
/// <summary>
/// 应用楼层感知过滤,只保留起终点所在楼层的模型项(原有方法)
/// </summary>
/// <param name="allItems">所有模型项</param>
/// <param name="startPoint">起点</param>
/// <param name="endPoint">终点</param>
/// <returns>过滤后的模型项列表</returns>
private List<ModelItem> ApplyFloorAwareFiltering(List<ModelItem> allItems, Point3D startPoint, Point3D endPoint)
{
try
{
// 初始化楼层检测器
var floorDetector = new FloorDetector();
var modelCollection = new ModelItemCollection();
modelCollection.AddRange(allItems);
LogManager.Info($"[楼层过滤] 开始检测楼层,元素数量: {allItems.Count}");
// 检测所有楼层
var detectedFloors = floorDetector.DetectFloors(modelCollection);
if (detectedFloors == null || detectedFloors.Count == 0)
{
LogManager.Warning("[楼层过滤] 未检测到楼层信息,跳过过滤");
return allItems;
}
if (detectedFloors.Count == 1)
{
LogManager.Info($"[楼层过滤] 检测到单层建筑({detectedFloors[0].FloorName}),跳过过滤");
return allItems;
}
LogManager.Info($"[楼层过滤] 检测到 {detectedFloors.Count} 个楼层");
// 找到起终点所在的楼层
var startFloor = GetFloorAtPoint(detectedFloors, startPoint);
var endFloor = GetFloorAtPoint(detectedFloors, endPoint);
LogManager.Info($"[楼层过滤] 起点楼层: {startFloor?.FloorName ?? ""}, 终点楼层: {endFloor?.FloorName ?? ""}");
// 如果起终点在同一楼层,只保留该楼层的模型
if (startFloor != null && endFloor != null && startFloor.FloorName == endFloor.FloorName)
{
LogManager.Info($"[楼层过滤] 起终点在同一楼层 '{startFloor.FloorName}',只保留该楼层模型");
return FilterItemsByFloor(allItems, startFloor);
}
// 如果起终点在不同楼层,保留相关楼层的模型
if (startFloor != null && endFloor != null && startFloor.FloorName != endFloor.FloorName)
{
LogManager.Info($"[楼层过滤] 起终点在不同楼层('{startFloor.FloorName}' vs '{endFloor.FloorName}'),保留两个楼层模型");
return FilterItemsByFloors(allItems, new[] { startFloor, endFloor });
}
// 如果只找到一个楼层,保留该楼层模型
var targetFloor = startFloor ?? endFloor;
if (targetFloor != null)
{
LogManager.Info($"[楼层过滤] 只找到一个楼层 '{targetFloor.FloorName}',保留该楼层模型");
return FilterItemsByFloor(allItems, targetFloor);
}
// 如果未找到任何楼层,返回所有项目
LogManager.Warning("[楼层过滤] 未能确定起终点所在楼层,保留所有模型");
return allItems;
}
catch (Exception ex)
{
LogManager.Error($"[楼层过滤] 过滤过程中发生错误: {ex.Message}");
return allItems; // 出错时返回所有项目
}
}
/// <summary>
/// 根据点的位置找到对应的楼层
/// </summary>
/// <param name="floors">楼层列表</param>
/// <param name="point">点位置</param>
/// <returns>对应的楼层信息</returns>
private ModelSplitterManager.FloorInfo GetFloorAtPoint(List<ModelSplitterManager.FloorInfo> floors, Point3D point)
{
try
{
const double elevationTolerance = 5.0; // 5米高程容差
// 根据高程找到最接近的楼层
ModelSplitterManager.FloorInfo closestFloor = null;
double minDistance = double.MaxValue;
foreach (var floor in floors)
{
double elevationDifference = Math.Abs(point.Z - floor.Elevation);
if (elevationDifference <= elevationTolerance && elevationDifference < minDistance)
{
minDistance = elevationDifference;
closestFloor = floor;
}
}
if (closestFloor != null)
{
LogManager.Info($"[楼层匹配] 点({point.X:F1}, {point.Y:F1}, {point.Z:F1})匹配到楼层 '{closestFloor.FloorName}' (高程{closestFloor.Elevation:F1}, 距离{minDistance:F1})");
}
else
{
LogManager.Warning($"[楼层匹配] 点({point.X:F1}, {point.Y:F1}, {point.Z:F1})未找到匹配的楼层(容差{elevationTolerance}m");
}
return closestFloor;
}
catch (Exception ex)
{
LogManager.Error($"[楼层匹配] 匹配楼层时发生错误: {ex.Message}");
return null;
}
}
/// <summary>
/// 按单个楼层过滤模型项
/// </summary>
/// <param name="allItems">所有模型项</param>
/// <param name="targetFloor">目标楼层</param>
/// <returns>过滤后的模型项</returns>
private List<ModelItem> FilterItemsByFloor(List<ModelItem> allItems, ModelSplitterManager.FloorInfo targetFloor)
{
var filteredItems = new List<ModelItem>();
// 将楼层中的项转为哈希集合以加快查找
var floorItemSet = new HashSet<ModelItem>();
foreach (ModelItem item in targetFloor.Items)
{
floorItemSet.Add(item);
}
foreach (var item in allItems)
{
if (floorItemSet.Contains(item))
{
filteredItems.Add(item);
}
}
LogManager.Info($"[楼层过滤] 楼层 '{targetFloor.FloorName}' 过滤结果: {allItems.Count} -> {filteredItems.Count}");
return filteredItems;
}
/// <summary>
/// 按多个楼层过滤模型项
/// </summary>
/// <param name="allItems">所有模型项</param>
/// <param name="targetFloors">目标楼层列表</param>
/// <returns>过滤后的模型项</returns>
private List<ModelItem> FilterItemsByFloors(List<ModelItem> allItems, ModelSplitterManager.FloorInfo[] targetFloors)
{
var filteredItems = new List<ModelItem>();
var floorItemSet = new HashSet<ModelItem>();
// 合并所有目标楼层的项
foreach (var floor in targetFloors)
{
foreach (ModelItem item in floor.Items)
{
floorItemSet.Add(item);
}
}
foreach (var item in allItems)
{
if (floorItemSet.Contains(item))
{
filteredItems.Add(item);
}
}
var floorNames = string.Join(", ", targetFloors.Select(f => $"'{f.FloorName}'"));
LogManager.Info($"[楼层过滤] 楼层 {floorNames} 过滤结果: {allItems.Count} -> {filteredItems.Count}");
return filteredItems;
}
}
}

View File

@ -71,6 +71,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
private string _autoPathStartPoint = "未选择";
private string _autoPathEndPoint = "未选择";
private double _autoPathVehicleSize = 1.0;
private double _autoPathSafetyMargin = 0.5;
private string _autoPathStatus = "就绪";
private Point3D _startPoint3D;
private Point3D _endPoint3D;
@ -518,6 +519,15 @@ namespace NavisworksTransport.UI.WPF.ViewModels
set => SetProperty(ref _autoPathVehicleSize, value);
}
/// <summary>
/// 自动路径规划安全间隙(车辆膨胀间隙)
/// </summary>
public double AutoPathSafetyMargin
{
get => _autoPathSafetyMargin;
set => SetProperty(ref _autoPathSafetyMargin, value);
}
/// <summary>
/// 自动路径规划状态
/// </summary>
@ -1648,7 +1658,8 @@ namespace NavisworksTransport.UI.WPF.ViewModels
var pathRoute = _pathPlanningManager?.AutoPlanPath(
_startPoint3D,
_endPoint3D,
AutoPathVehicleSize);
AutoPathVehicleSize,
AutoPathSafetyMargin);
// 在UI线程上更新结果
System.Windows.Application.Current.Dispatcher.Invoke(() =>
@ -1700,7 +1711,17 @@ namespace NavisworksTransport.UI.WPF.ViewModels
{
AutoPathStatus = $"路径规划出错: {ex.Message}";
StatusText = $"自动路径规划出错: {ex.Message}";
// 详细记录异常信息
LogManager.Error($"自动路径规划出错: {ex.Message}");
LogManager.Error($"异常类型: {ex.GetType().FullName}");
LogManager.Error($"堆栈跟踪: {ex.StackTrace}");
if (ex.InnerException != null)
{
LogManager.Error($"内部异常: {ex.InnerException.Message}");
LogManager.Error($"内部异常堆栈: {ex.InnerException.StackTrace}");
}
});
}
});
@ -1713,7 +1734,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
/// </summary>
private bool CanExecuteAutoPlanPath()
{
return _hasStartPoint && _hasEndPoint && AutoPathVehicleSize > 0;
return _hasStartPoint && _hasEndPoint && AutoPathVehicleSize > 0 && AutoPathSafetyMargin >= 0;
}
/// <summary>
@ -1788,6 +1809,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
AutoPathStartPoint = "未选择";
AutoPathEndPoint = "未选择";
AutoPathVehicleSize = 1.0;
AutoPathSafetyMargin = 0.5;
AutoPathStatus = "就绪";
IsSelectingStartPoint = false;
IsSelectingEndPoint = false;
@ -1984,24 +2006,54 @@ namespace NavisworksTransport.UI.WPF.ViewModels
/// </summary>
private void OnPathEditStateChanged(object sender, PathEditState newState)
{
// 使用 Dispatcher 确保在 UI 线程上更新
System.Windows.Application.Current.Dispatcher.Invoke(() =>
try
{
SafeExecute(() =>
// 安全地在UI线程上更新
if (System.Windows.Application.Current?.Dispatcher != null)
{
// 更新 ViewModel 的 IsPathEditMode 状态
// Creating 或 Editing 状态都视为编辑模式
IsPathEditMode = (newState == PathEditState.Creating || newState == PathEditState.Editing);
// 如果有选中的路径,也更新其 IsActive 状态
if (SelectedPathRoute != null)
if (System.Windows.Application.Current.Dispatcher.CheckAccess())
{
SelectedPathRoute.IsActive = IsPathEditMode;
// 已在UI线程上
UpdatePathEditState(newState);
}
LogManager.Info($"UI已更新 - 路径编辑状态已变更为: {newState}, IsPathEditMode: {IsPathEditMode}");
}, "处理路径编辑状态变更事件");
});
else
{
// 需要切换到UI线程
System.Windows.Application.Current.Dispatcher.Invoke(() =>
{
UpdatePathEditState(newState);
});
}
}
else
{
// Dispatcher不可用直接更新
UpdatePathEditState(newState);
}
}
catch (Exception ex)
{
LogManager.Error($"处理路径编辑状态变更失败: {ex.Message}");
LogManager.Error($"堆栈跟踪: {ex.StackTrace}");
}
}
private void UpdatePathEditState(PathEditState newState)
{
SafeExecute(() =>
{
// 更新 ViewModel 的 IsPathEditMode 状态
// Creating 或 Editing 状态都视为编辑模式
IsPathEditMode = (newState == PathEditState.Creating || newState == PathEditState.Editing);
// 如果有选中的路径,也更新其 IsActive 状态
if (SelectedPathRoute != null)
{
SelectedPathRoute.IsActive = IsPathEditMode;
}
LogManager.Info($"UI已更新 - 路径编辑状态已变更为: {newState}, IsPathEditMode: {IsPathEditMode}");
}, "处理路径编辑状态变更事件");
}
/// <summary>
@ -2128,12 +2180,39 @@ namespace NavisworksTransport.UI.WPF.ViewModels
/// </summary>
private void OnPathManagerStatusChanged(object sender, string statusMessage)
{
// 使用 Dispatcher 确保在 UI 线程上更新
System.Windows.Application.Current.Dispatcher.Invoke(() =>
try
{
StatusText = statusMessage;
LogManager.Info($"PathManager状态更新: {statusMessage}");
});
// 安全地更新状态文本
if (System.Windows.Application.Current?.Dispatcher != null)
{
if (System.Windows.Application.Current.Dispatcher.CheckAccess())
{
// 已在UI线程上
StatusText = statusMessage;
LogManager.Info($"PathManager状态更新: {statusMessage}");
}
else
{
// 需要切换到UI线程
System.Windows.Application.Current.Dispatcher.Invoke(() =>
{
StatusText = statusMessage;
LogManager.Info($"PathManager状态更新: {statusMessage}");
});
}
}
else
{
// Dispatcher不可用直接更新可能在非WPF环境中
StatusText = statusMessage;
LogManager.Info($"PathManager状态更新无Dispatcher: {statusMessage}");
}
}
catch (Exception ex)
{
LogManager.Error($"PathManager状态更新失败: {ex.Message}");
LogManager.Error($"堆栈跟踪: {ex.StackTrace}");
}
}
/// <summary>

View File

@ -28,7 +28,7 @@
</StackPanel>
</GroupBox>
<GroupBox Header="自动路径规划" Margin="0,10" Height="180">
<GroupBox Header="自动路径规划" Margin="0,10" Height="210">
<StackPanel Margin="10">
<Grid>
<Grid.ColumnDefinitions>
@ -41,6 +41,7 @@
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content="起点:" VerticalAlignment="Center"/>
@ -55,7 +56,11 @@
<TextBox Grid.Row="2" Grid.Column="1" Text="{Binding AutoPathVehicleSize}" Margin="5,2"/>
<Label Grid.Row="2" Grid.Column="2" Content="米" VerticalAlignment="Center" Margin="5,2"/>
<StackPanel Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="3" Orientation="Horizontal" Margin="0,10,0,0">
<Label Grid.Row="3" Grid.Column="0" Content="安全间隙:" VerticalAlignment="Center"/>
<TextBox Grid.Row="3" Grid.Column="1" Text="{Binding AutoPathSafetyMargin}" Margin="5,2"/>
<Label Grid.Row="3" Grid.Column="2" Content="米" VerticalAlignment="Center" Margin="5,2"/>
<StackPanel Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="3" Orientation="Horizontal" Margin="0,10,0,0">
<Button Content="自动规划路径" Command="{Binding AutoPlanPathCommand}" Margin="0,0,10,0" Padding="10,5" Background="LightBlue"/>
<Button Content="清除规划" Command="{Binding ClearAutoPathCommand}" Margin="0,0,10,0" Padding="10,5"/>
<TextBlock Text="{Binding AutoPathStatus}" VerticalAlignment="Center" Foreground="Blue" FontSize="10"/>

View File

@ -139,14 +139,21 @@ namespace NavisworksTransport
foreach (ModelItem item in items)
{
string floorValue = GetAttributeValue(item, attributeName);
if (!string.IsNullOrEmpty(floorValue))
try
{
if (!floorGroups.ContainsKey(floorValue))
string floorValue = GetAttributeValue(item, attributeName);
if (!string.IsNullOrEmpty(floorValue))
{
floorGroups[floorValue] = new List<ModelItem>();
if (!floorGroups.ContainsKey(floorValue))
{
floorGroups[floorValue] = new List<ModelItem>();
}
floorGroups[floorValue].Add(item);
}
floorGroups[floorValue].Add(item);
}
catch (Exception ex)
{
LogManager.Warning($"[FloorDetector] 处理模型项楼层属性时出错: {ex.Message}");
}
}
@ -285,30 +292,55 @@ namespace NavisworksTransport
private string FindBestFloorAttribute(ModelItemCollection items)
{
var attributeScores = new Dictionary<string, int>();
int sampleSize = Math.Min(200, items.Count); // 增加采样数量以提高准确性
int sampleSize = Math.Min(50, items.Count); // 减少采样数量以提高稳定性
int processedCount = 0;
foreach (ModelItem item in items.Take(sampleSize))
foreach (ModelItem item in items)
{
foreach (PropertyCategory category in item.PropertyCategories)
if (processedCount >= sampleSize) break; // 限制处理数量
try
{
foreach (DataProperty property in category.Properties)
foreach (PropertyCategory category in item.PropertyCategories)
{
string propName = property.DisplayName;
if (IsFloorRelatedAttribute(propName))
try
{
string value = property.Value?.ToString()?.Trim();
if (!string.IsNullOrEmpty(value) && IsValidFloorValue(value))
foreach (DataProperty property in category.Properties)
{
// 根据属性名称的匹配度给分
int score = CalculateAttributeScore(propName);
if (attributeScores.ContainsKey(propName))
attributeScores[propName] = attributeScores[propName] + score;
else
attributeScores[propName] = score;
try
{
string propName = property.DisplayName;
if (IsFloorRelatedAttribute(propName))
{
string value = property.Value?.ToString()?.Trim();
if (!string.IsNullOrEmpty(value) && IsValidFloorValue(value))
{
// 根据属性名称的匹配度给分
int score = CalculateAttributeScore(propName);
if (attributeScores.ContainsKey(propName))
attributeScores[propName] = attributeScores[propName] + score;
else
attributeScores[propName] = score;
}
}
}
catch (Exception ex)
{
LogManager.Warning($"[FloorDetector] 处理属性时出错: {ex.Message}");
}
}
}
catch (Exception ex)
{
LogManager.Warning($"[FloorDetector] 处理属性类别时出错: {ex.Message}");
}
}
processedCount++;
}
catch (Exception ex)
{
LogManager.Warning($"[FloorDetector] 处理模型项时出错: {ex.Message}");
}
}
@ -502,14 +534,22 @@ namespace NavisworksTransport
try
{
var itemBounds = item.BoundingBox();
if (itemBounds.HasVolume)
if (itemBounds != null && itemBounds.HasVolume)
{
combinedBounds = combinedBounds.Extend(itemBounds);
if (combinedBounds == null)
{
combinedBounds = itemBounds;
}
else
{
combinedBounds = combinedBounds.Extend(itemBounds);
}
}
}
catch
catch (Exception ex)
{
// 忽略无法获取边界框的元素
// 记录错误但继续处理其他元素
LogManager.Warning($"[FloorDetector] 计算边界框时出错: {ex.Message}");
}
}

View File

@ -1,98 +0,0 @@
@echo off
echo ========================================
echo Navisworks Transport Plugin 日志查看器
echo ========================================
echo.
set LOGFILE=C:\ProgramData\Autodesk\Navisworks Manage 2026\NavisworksTransport\logs\debug.log
if not exist "%LOGFILE%" (
echo 日志文件不存在: %LOGFILE%
echo 请先运行分层拆分功能生成日志
pause
exit /b
)
echo 日志文件位置: %LOGFILE%
echo 文件大小:
for %%A in ("%LOGFILE%") do echo %%~zA 字节
echo.
:MENU
echo 请选择操作:
echo 1. 查看最新50行日志
echo 2. 查看最新100行日志
echo 3. 查看所有ERROR日志
echo 4. 查看最新会话日志
echo 5. 实时监控日志按Ctrl+C停止
echo 6. 清空日志文件
echo 7. 退出
echo.
set /p choice=请输入选择 (1-7):
if "%choice%"=="1" goto TAIL50
if "%choice%"=="2" goto TAIL100
if "%choice%"=="3" goto ERRORS
if "%choice%"=="4" goto SESSION
if "%choice%"=="5" goto MONITOR
if "%choice%"=="6" goto CLEAR
if "%choice%"=="7" goto EXIT
echo 无效选择,请重新输入
goto MENU
:TAIL50
echo.
echo ========== 最新50行日志 ==========
powershell -Command "Get-Content '%LOGFILE%' -Tail 50"
echo.
pause
goto MENU
:TAIL100
echo.
echo ========== 最新100行日志 ==========
powershell -Command "Get-Content '%LOGFILE%' -Tail 100"
echo.
pause
goto MENU
:ERRORS
echo.
echo ========== 所有ERROR日志 ==========
findstr /i "ERROR" "%LOGFILE%"
echo.
pause
goto MENU
:SESSION
echo.
echo ========== 最新会话日志 ==========
powershell -Command "$content = Get-Content '%LOGFILE%'; $lastSession = ($content | Select-String 'SESSION.*新会话开始' | Select-Object -Last 1).LineNumber - 1; if($lastSession -ge 0) { $content[$lastSession..($content.Length-1)] } else { '没有找到会话标记' }"
echo.
pause
goto MENU
:MONITOR
echo.
echo ========== 实时监控日志 (按Ctrl+C停止) ==========
echo 正在监控日志文件变化...
powershell -Command "Get-Content '%LOGFILE%' -Wait -Tail 10"
goto MENU
:CLEAR
echo.
set /p confirm=确定要清空日志文件吗?(y/N):
if /i "%confirm%"=="y" (
echo. > "%LOGFILE%"
echo 日志文件已清空
) else (
echo 操作已取消
)
echo.
pause
goto MENU
:EXIT
echo 再见!
pause