先过滤楼层,再进行规划
This commit is contained in:
parent
b261efcaae
commit
720727a370
@ -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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
{
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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>
|
||||
|
||||
@ -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"/>
|
||||
|
||||
@ -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}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
Loading…
Reference in New Issue
Block a user