NavisworksTransport/src/Core/PathPlanningManager.cs

3774 lines
148 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Autodesk.Navisworks.Api;
using Autodesk.Navisworks.Api.Plugins;
using NavisworksTransport.PathPlanning;
using NavisworksTransport.Utils;
using NavisworksTransport.Core;
using NavisworksTransport.Commands;
namespace NavisworksTransport
{
/// <summary>
/// 路径规划管理器(重构版)
/// 负责路径规划的核心业务逻辑与UI完全解耦
/// </summary>
public class PathPlanningManager : IPathPlanningManager, IDisposable
{
#region
private readonly string _managerId;
private readonly CategoryAttributeManager _categoryManager;
private readonly UIStateManager _uiStateManager;
private CoordinateConverter _coordinateConverter;
private PathPointRenderPlugin _renderPlugin;
// 路径分析相关
private PathDatabase _pathDatabase;
private PathAnalysisService _analysisService;
private List<ModelItem> _walkableAreas;
private List<PathRoute> _routes;
private PathRoute _currentRoute;
private ChannelBounds _combinedChannelBounds;
// 路径编辑状态管理
private PathEditState _pathEditState = PathEditState.Viewing;
private PathPointType _currentPointType = PathPointType.WayPoint;
private PathRoute _editingRoute = null;
private PathHistoryManager _historyManager;
// ToolPlugin 集成
private bool _isToolPluginActive = false;
// 自动路径规划模式标志
private bool _isInAutoPathMode = false;
// 路径点3D标记管理
private List<PathPointMarker> _pathPointMarkers;
// 预览点管理
private PathPoint _previewPoint = null;
// 网格可视化设置
private bool _showWalkableGrid = false;
private bool _showObstacleGrid = false;
private bool _showUnknownGrid = false;
private bool _showDoorGrid = false;
private GridMap _currentGridMap = null; // 保存当前网格地图用于刷新
private double _currentVehicleHeight = 2.0; // 保存当前车辆高度(米)用于网格刷新
private bool _isPreviewMode = false;
private int _previewInsertIndex = -1; // 保存预览点应该插入的索引位置
// 修改路径点管理
private int _editingPointIndex = -1; // 正在修改的路径点索引
private PathPoint _originalPoint = null; // 修改前的原始路径点
private PathPoint _editingPreviewPoint = null; // 修改时的预览路径点
/// <summary>
/// ToolPlugin是否处于激活状态供InputMonitor使用
/// </summary>
public bool IsToolPluginActive => _isToolPluginActive;
/// <summary>
/// 获取当前是否在预览模式
/// </summary>
public bool IsPreviewMode => _isPreviewMode;
// 静态引用用于处理ToolPlugin事件
private static PathPlanningManager _activePathManager;
#endregion
#region
/// <summary>
/// 构造函数
/// </summary>
/// <param name="categoryManager">类别属性管理器</param>
/// <param name="uiStateManager">UI状态管理器可选</param>
public PathPlanningManager(CategoryAttributeManager categoryManager, UIStateManager uiStateManager = null)
{
_managerId = Guid.NewGuid().ToString("N").Substring(0, 8); // 前8位作为ID
_categoryManager = categoryManager ?? throw new ArgumentNullException(nameof(categoryManager));
_uiStateManager = uiStateManager ?? UIStateManager.Instance;
InitializeManager();
}
/// <summary>
/// 无参构造函数(为向后兼容而保留)
/// </summary>
public PathPlanningManager()
{
_managerId = Guid.NewGuid().ToString("N").Substring(0, 8);
_categoryManager = new CategoryAttributeManager();
_uiStateManager = UIStateManager.Instance;
InitializeManager();
}
private void InitializeManager()
{
// 设置静态引用,确保所有地方都使用同一个实例
_activePathManager = this;
LogManager.Debug($"[路径管理] 设置_activePathManager静态引用, ManagerId: {_managerId}");
// 尝试初始化路径分析数据库
EnsureDatabaseInitialized();
}
/// <summary>
/// 确保路径分析数据库已初始化(支持延迟初始化)
/// </summary>
public void EnsureDatabaseInitialized()
{
if (_pathDatabase != null && _analysisService != null)
{
LogManager.Info("路径分析数据库已初始化,跳过");
return;
}
try
{
var documentPath = Autodesk.Navisworks.Api.Application.ActiveDocument?.FileName;
if (!string.IsNullOrEmpty(documentPath))
{
_pathDatabase = new PathDatabase(documentPath);
_analysisService = new PathAnalysisService(_pathDatabase);
LogManager.Info($"路径分析数据库初始化成功,文档路径: {documentPath}");
}
else
{
LogManager.Warning("文档路径为空,路径分析数据库初始化将延迟到文档加载后");
}
}
catch (Exception ex)
{
LogManager.Error($"初始化路径分析数据库失败: {ex.Message}", ex);
}
// 获取已注册的圆形渲染插件实例
try
{
_renderPlugin = PathPointRenderPlugin.Instance;
if (_renderPlugin != null)
{
LogManager.Info("[路径管理] ✅ PathPointRenderPlugin实例获取成功");
LogManager.Debug($"[路径管理] 渲染插件状态 - 启用: {_renderPlugin.IsEnabled}, 标记数量: {_renderPlugin.MarkerCount}");
// 推送默认的网格大小和车辆参数,确保渲染插件有合理的初始值
InitializeRenderPluginDefaults();
}
else
{
LogManager.Warning("[路径管理] ❌ PathPointRenderPlugin实例尚未就绪将在后续尝试获取");
}
}
catch (Exception ex)
{
LogManager.Error($"[路径管理] PathPointRenderPlugin实例获取失败: {ex.Message}");
LogManager.Error($"[路径管理] 异常堆栈: {ex.StackTrace}");
_renderPlugin = null;
}
_walkableAreas = new List<ModelItem>();
_routes = new List<PathRoute>();
_currentRoute = new PathRoute("默认路径");
_historyManager = new PathHistoryManager(50); // 最多保存50个历史记录
_coordinateConverter = null; // 将在需要时初始化
_pathPointMarkers = new List<PathPointMarker>();
// 从数据库加载历史路径到内存
LoadHistoricalRoutesFromDatabase();
LogManager.Info($"PathPlanningManager初始化完成ManagerId: {_managerId}");
}
/// <summary>
/// 初始化渲染插件的默认值
/// 推送合理的网格大小和车辆参数,确保渲染插件在任何情况下都有正确的可视化效果
/// </summary>
private void InitializeRenderPluginDefaults()
{
try
{
// 设置默认网格大小0.5米,适合大多数场景)
const double defaultGridSizeInMeters = 0.5;
_renderPlugin.SetGridSize(defaultGridSizeInMeters);
LogManager.Info($"[渲染插件初始化] 已设置默认网格大小: {defaultGridSizeInMeters}米");
// 设置默认车辆参数与PathEditingViewModel的默认值保持一致
const double defaultVehicleLength = 1.0; // 米
const double defaultVehicleWidth = 1.0; // 米
const double defaultVehicleHeight = 2.0; // 米
const double defaultSafetyMargin = 0.25; // 米
_renderPlugin.SetVehicleParameters(
defaultVehicleLength,
defaultVehicleWidth,
defaultVehicleHeight,
defaultSafetyMargin
);
LogManager.Info($"[渲染插件初始化] 已设置默认车辆参数: 长={defaultVehicleLength}m, 宽={defaultVehicleWidth}m, 高={defaultVehicleHeight}m, 安全间隙={defaultSafetyMargin}m");
}
catch (Exception ex)
{
LogManager.Error($"[渲染插件初始化] 设置默认值失败: {ex.Message}", ex);
// 即使失败也不抛出异常避免影响PathPlanningManager的初始化
}
}
#endregion
#region IPathPlanningManager接口实现
public string ManagerId => _managerId;
public PathHistoryManager HistoryManager => _historyManager;
public PathEditState PathEditState
{
get { return _pathEditState; }
private set
{
var previousState = _pathEditState;
_pathEditState = value;
// 触发状态变更事件
RaiseEditStateChanged(previousState, value, _editingRoute);
LogManager.Info($"路径编辑状态变更: {previousState} -> {value}");
}
}
public PathRoute EditingRoute
{
get { return _editingRoute; }
private set { _editingRoute = value; }
}
public bool IsInEditableState
{
get { return _pathEditState == PathEditState.Creating || _pathEditState == PathEditState.Editing || _pathEditState == PathEditState.AddingPoints; }
}
public PathRoute CurrentRoute
{
get { return _currentRoute; }
private set
{
var previousRoute = _currentRoute;
_currentRoute = value;
// 触发当前路径变更事件
RaiseCurrentRouteChanged(previousRoute, value);
}
}
public IReadOnlyList<PathRoute> Routes => _routes.AsReadOnly();
/// <summary>
/// 获取可修改的路线集合(内部使用)
/// </summary>
internal List<PathRoute> ModifiableRoutes => _routes;
public IReadOnlyList<ModelItem> SelectedChannels => _walkableAreas.AsReadOnly();
public PathPointType CurrentPointType
{
get { return _currentPointType; }
private set { _currentPointType = value; }
}
/// <summary>
/// 是否在自动路径规划模式
/// </summary>
public bool IsInAutoPathMode
{
get { return _isInAutoPathMode; }
private set { _isInAutoPathMode = value; }
}
public ChannelBounds CombinedChannelBounds => _combinedChannelBounds;
#endregion
#region
public event EventHandler<PathPlanningStatusChangedEventArgs> StatusChanged;
public event EventHandler<PathPlanningErrorOccurredEventArgs> ErrorOccurred;
public event EventHandler<PathEditStateChangedEventArgs> EditStateChanged;
public event EventHandler<ChannelSelectionChangedEventArgs> ChannelSelectionChanged;
public event EventHandler<PathPointOperationEventArgs> PathPointOperation;
public event EventHandler<RouteGeneratedEventArgs> RouteGenerated;
public event EventHandler<CurrentRouteChangedEventArgs> CurrentRouteChanged;
public event EventHandler<PathPointsListUpdatedEventArgs> PathPointsListUpdated;
// 保留原有事件以保持向后兼容
[Obsolete("请使用新的强类型事件接口")]
public event EventHandler<List<ModelItem>> ChannelsSelected;
[Obsolete("请使用新的强类型事件接口")]
public event EventHandler<PathRoute> CurrentRouteChanged_Legacy;
[Obsolete("请使用新的强类型事件接口")]
public event EventHandler<PathRoute> RouteGenerated_Legacy;
[Obsolete("请使用新的强类型事件接口")]
public event EventHandler<string> StatusChanged_Legacy;
[Obsolete("请使用新的强类型事件接口")]
public event EventHandler<string> ErrorOccurred_Legacy;
[Obsolete("请使用新的强类型事件接口")]
public event EventHandler<PathEditState> PathEditStateChanged;
[Obsolete("请使用新的强类型事件接口")]
public event EventHandler<PathPoint> PathPointAddedIn3D;
[Obsolete("请使用新的强类型事件接口")]
public event EventHandler<PathPoint> PathPointRemovedFrom3D;
[Obsolete("请使用新的强类型事件接口")]
public event EventHandler<PathRoute> PathPointsListUpdated_Legacy;
#endregion
#region
private void RaiseStatusChanged(string statusMessage, PathPlanningStatusType statusType = PathPlanningStatusType.Info, object additionalData = null)
{
try
{
var eventArgs = new PathPlanningStatusChangedEventArgs(statusMessage, statusType, additionalData, _managerId);
// 使用UIStateManager安全地触发事件
if (_uiStateManager != null)
{
_uiStateManager.QueueUIUpdate(() =>
{
StatusChanged?.Invoke(this, eventArgs);
// 向后兼容的事件
StatusChanged_Legacy?.Invoke(this, statusMessage);
});
}
else
{
StatusChanged?.Invoke(this, eventArgs);
StatusChanged_Legacy?.Invoke(this, statusMessage);
}
LogManager.Info($"[状态变更] {statusMessage}");
}
catch (Exception ex)
{
LogManager.Error($"[状态变更] 事件触发失败: {ex.Message}");
}
}
private void RaiseErrorOccurred(string errorMessage, Exception exception = null, PathPlanningErrorLevel errorLevel = PathPlanningErrorLevel.Error)
{
try
{
var eventArgs = new PathPlanningErrorOccurredEventArgs(errorMessage, exception, errorLevel, _managerId);
// 使用UIStateManager安全地触发事件
if (_uiStateManager != null)
{
_uiStateManager.QueueUIUpdate(() =>
{
ErrorOccurred?.Invoke(this, eventArgs);
// 向后兼容的事件
ErrorOccurred_Legacy?.Invoke(this, errorMessage);
});
}
else
{
ErrorOccurred?.Invoke(this, eventArgs);
ErrorOccurred_Legacy?.Invoke(this, errorMessage);
}
LogManager.Error($"[错误发生] {errorMessage}");
if (exception != null)
{
LogManager.Error($"[错误详情] {exception}");
}
}
catch (Exception ex)
{
LogManager.Error($"[错误事件] 触发失败: {ex.Message}");
}
}
private void RaiseEditStateChanged(PathEditState previousState, PathEditState newState, PathRoute editingRoute)
{
try
{
var eventArgs = new PathEditStateChangedEventArgs(previousState, newState, editingRoute, _managerId);
if (_uiStateManager != null)
{
_uiStateManager.QueueUIUpdate(() =>
{
EditStateChanged?.Invoke(this, eventArgs);
// 向后兼容的事件
PathEditStateChanged?.Invoke(this, newState);
});
}
else
{
EditStateChanged?.Invoke(this, eventArgs);
PathEditStateChanged?.Invoke(this, newState);
}
}
catch (Exception ex)
{
LogManager.Error($"[编辑状态变更] 事件触发失败: {ex.Message}");
}
}
private void RaiseChannelSelectionChanged(List<ModelItem> selectedChannels, string selectionMethod)
{
try
{
var eventArgs = new ChannelSelectionChangedEventArgs(selectedChannels, selectionMethod, _managerId);
if (_uiStateManager != null)
{
_uiStateManager.QueueUIUpdate(() =>
{
ChannelSelectionChanged?.Invoke(this, eventArgs);
// 向后兼容的事件
ChannelsSelected?.Invoke(this, selectedChannels);
});
}
else
{
ChannelSelectionChanged?.Invoke(this, eventArgs);
ChannelsSelected?.Invoke(this, selectedChannels);
}
}
catch (Exception ex)
{
LogManager.Error($"[通道选择变更] 事件触发失败: {ex.Message}");
}
}
private void RaisePathPointOperation(PathPointOperationType operationType, PathPoint pathPoint, PathRoute route)
{
try
{
var eventArgs = new PathPointOperationEventArgs(operationType, pathPoint, route, _managerId);
if (_uiStateManager != null)
{
_uiStateManager.QueueUIUpdate(() =>
{
PathPointOperation?.Invoke(this, eventArgs);
// 向后兼容的事件
if (operationType == PathPointOperationType.Added)
{
PathPointAddedIn3D?.Invoke(this, pathPoint);
}
else if (operationType == PathPointOperationType.Removed)
{
PathPointRemovedFrom3D?.Invoke(this, pathPoint);
}
});
}
else
{
PathPointOperation?.Invoke(this, eventArgs);
if (operationType == PathPointOperationType.Added)
{
PathPointAddedIn3D?.Invoke(this, pathPoint);
}
else if (operationType == PathPointOperationType.Removed)
{
PathPointRemovedFrom3D?.Invoke(this, pathPoint);
}
}
}
catch (Exception ex)
{
LogManager.Error($"[路径点操作] 事件触发失败: {ex.Message}");
}
}
private void RaiseRouteGenerated(PathRoute route, RouteGenerationMethod generationMethod, double gridSize = -1)
{
try
{
LogManager.Info($"*** RaiseRouteGenerated被调用: {route?.Name}, Method: {generationMethod}, GridSize: {gridSize} ***");
var eventArgs = new RouteGeneratedEventArgs(route, generationMethod, gridSize, _managerId);
// 检查是否有订阅者
if (RouteGenerated != null)
{
var invocationList = RouteGenerated.GetInvocationList();
LogManager.Info($"*** RouteGenerated事件有 {invocationList.Length} 个订阅者 ***");
for (int i = 0; i < invocationList.Length; i++)
{
var handler = invocationList[i];
LogManager.Info($"*** 订阅者 {i}: {handler.Target?.GetType().Name}.{handler.Method.Name} ***");
}
}
else
{
LogManager.Warning("*** RouteGenerated事件没有订阅者 ***");
}
// 测试强制同步处理使用高优先级QueueUIUpdate代替立即执行
if (_uiStateManager != null)
{
LogManager.Info("*** 使用高优先级强制同步处理RouteGenerated事件 ***");
_uiStateManager.QueueUIUpdateWithForcedSync(() =>
{
LogManager.Info($"*** 强制同步队列中执行RouteGenerated事件: {route?.Name} ***");
RouteGenerated?.Invoke(this, eventArgs);
// 向后兼容的事件
RouteGenerated_Legacy?.Invoke(this, route);
LogManager.Info($"*** RouteGenerated事件强制同步执行完成: {route?.Name} ***");
}, UIUpdatePriority.Critical, $"RouteGenerated强制同步事件({route?.Name})");
}
else
{
LogManager.Info("*** 直接触发RouteGenerated事件UIStateManager为null***");
RouteGenerated?.Invoke(this, eventArgs);
RouteGenerated_Legacy?.Invoke(this, route);
LogManager.Info($"*** RouteGenerated事件直接调用完成: {route?.Name} ***");
}
}
catch (Exception ex)
{
LogManager.Error($"*** [路径生成] 事件触发失败: {ex.Message} ***");
}
}
private void RaiseCurrentRouteChanged(PathRoute previousRoute, PathRoute newRoute)
{
try
{
var eventArgs = new CurrentRouteChangedEventArgs(previousRoute, newRoute, _managerId);
if (_uiStateManager != null)
{
_uiStateManager.QueueUIUpdate(() =>
{
CurrentRouteChanged?.Invoke(this, eventArgs);
// 向后兼容的事件
CurrentRouteChanged_Legacy?.Invoke(this, newRoute);
});
}
else
{
CurrentRouteChanged?.Invoke(this, eventArgs);
CurrentRouteChanged_Legacy?.Invoke(this, newRoute);
}
}
catch (Exception ex)
{
LogManager.Error($"[当前路径变更] 事件触发失败: {ex.Message}");
}
}
public void RaisePathPointsListUpdated(PathRoute route, string updateReason)
{
try
{
var eventArgs = new PathPointsListUpdatedEventArgs(route, updateReason, _managerId);
if (_uiStateManager != null)
{
_uiStateManager.QueueUIUpdate(() =>
{
PathPointsListUpdated?.Invoke(this, eventArgs);
// 向后兼容的事件
PathPointsListUpdated_Legacy?.Invoke(this, route);
});
}
else
{
PathPointsListUpdated?.Invoke(this, eventArgs);
PathPointsListUpdated_Legacy?.Invoke(this, route);
}
}
catch (Exception ex)
{
LogManager.Error($"[路径点列表更新] 事件触发失败: {ex.Message}");
}
}
#endregion
#region
/// <summary>
/// 切换到查看状态
/// </summary>
public void SwitchToViewingState()
{
PathEditState = PathEditState.Viewing;
EditingRoute = null;
// 智能管理ToolPlugin状态
ManageToolPluginForEditState();
RaiseStatusChanged("已切换到查看状态", PathPlanningStatusType.Info);
}
/// <summary>
/// 重置路径编辑状态
/// </summary>
public void ResetPathEditState()
{
try
{
PathEditState = PathEditState.None;
LogManager.Info("路径编辑状态已重置");
}
catch (Exception ex)
{
LogManager.Error("重置路径编辑状态失败", ex);
RaiseErrorOccurred(ex.Message, ex);
}
}
/// <summary>
/// 清理无效的对象引用
/// </summary>
public void ClearInvalidReferences()
{
try
{
LogManager.Info("[PathPlanningManager] 清理无效对象引用");
// 重置编辑状态
PathEditState = PathEditState.None;
// 清空当前路径
if (_currentRoute != null)
{
_currentRoute.Points.Clear();
}
// 清空通道引用
if (_walkableAreas != null)
{
_walkableAreas.Clear();
LogManager.Info("[PathPlanningManager] 已清空通道引用");
}
// 清空组合边界
_combinedChannelBounds = null;
LogManager.Info("[PathPlanningManager] 对象引用清理完成");
}
catch (Exception ex)
{
LogManager.Error($"[PathPlanningManager] 清理对象引用失败: {ex.Message}");
}
}
/// <summary>
/// 保存当前路线到历史记录
/// </summary>
/// <param name="description">保存描述</param>
public void SaveCurrentRouteToHistory(string description = "手动保存")
{
try
{
if (CurrentRoute != null)
{
// 创建历史记录条目
var entry = new PathHistoryEntry(CurrentRoute.Id, PathHistoryOperationType.ManualSave, CurrentRoute, description);
_historyManager?.AddHistoryEntry(entry);
LogManager.Info($"路线 '{CurrentRoute.Name}' 已保存到历史记录: {description}");
}
}
catch (Exception ex)
{
LogManager.Error("保存路线到历史记录失败", ex);
RaiseErrorOccurred(ex.Message, ex);
}
}
/// <summary>
/// 清除3D路径标记保留网格可视化
/// </summary>
public void Clear3DPathMarkers()
{
try
{
_pathPointMarkers?.Clear();
// 清除路径标记但保留网格可视化
_renderPlugin?.ClearPathsExcept("grid_visualization_all", "grid_visualization_channel", "grid_visualization_unknown", "grid_visualization_obstacle", "grid_visualization_door");
LogManager.Info("3D路径标记已清除保留网格可视化");
}
catch (Exception ex)
{
LogManager.Error("清除3D路径标记失败", ex);
RaiseErrorOccurred(ex.Message, ex);
}
}
/// <summary>
/// 删除路径点
/// </summary>
/// <param name="point">要删除的路径点</param>
public void RemovePathPoint(PathPoint point)
{
try
{
if (point != null)
{
if (_pathPointMarkers != null)
{
// 找到对应的PathPointMarker
var marker = _pathPointMarkers.FirstOrDefault(m => m.PathPoint?.Id == point.Id);
if (marker != null)
{
_pathPointMarkers.Remove(marker);
LogManager.Info($"已从3D中移除路径点标记: {point.Name}");
}
}
// 重新渲染当前路径以反映更改
if (_renderPlugin != null && CurrentRoute != null)
{
_renderPlugin.RenderPath(CurrentRoute);
LogManager.Info($"已更新路径可视化: {point.Name}");
}
// 触发路径点移除事件
RaisePathPointOperation(PathPointOperationType.Removed, point, CurrentRoute);
// 保存到数据库
if (CurrentRoute != null)
{
SavePathToDatabase(CurrentRoute);
}
}
}
catch (Exception ex)
{
LogManager.Error("从3D中移除路径点失败", ex);
RaiseErrorOccurred(ex.Message, ex);
}
}
/// <summary>
/// 启动点击工具
/// </summary>
/// <param name="pointType">点击类型</param>
public void StartClickTool(PathPointType pointType)
{
try
{
CurrentPointType = pointType;
// 检查是否在自动路径模式 - 如果是则不订阅PathPlanningManager的事件
bool shouldSubscribeToEvents = !IsInAutoPathMode;
LogManager.Info($"StartClickTool - 自动路径模式: {IsInAutoPathMode}, 订阅事件: {shouldSubscribeToEvents}");
ActivateToolPlugin(shouldSubscribeToEvents);
PathEditState = PathEditState.AddingPoints;
LogManager.Info($"点击工具已启动,类型: {pointType},事件订阅: {shouldSubscribeToEvents}");
}
catch (Exception ex)
{
LogManager.Error("启动点击工具失败", ex);
RaiseErrorOccurred(ex.Message, ex);
}
}
/// <summary>
/// 停止点击工具
/// </summary>
public void StopClickTool()
{
try
{
LogManager.Debug("[事件清理] ===== 开始执行StopClickTool - 完整事件订阅清理 =====");
// 1. 停用ToolPlugin并清理事件订阅
DeactivateToolPlugin();
// 2. 简化的事件订阅清理 - 移除危险的反射操作
LogManager.Debug("[事件清理] 执行安全的事件订阅清理");
try
{
// 安全地移除事件订阅,多次取消订阅同一处理程序是安全的
PathClickToolPlugin.MouseClicked -= OnToolPluginMouseClicked;
LogManager.Debug("[事件清理] 已安全移除PathPlanningManager.OnToolPluginMouseClicked订阅");
}
catch (Exception cleanupEx)
{
LogManager.Error($"[事件清理] 事件清理过程异常: {cleanupEx.Message}");
}
// 注意移除了PathEditState设置StopClickTool只管理工具插件状态不修改业务逻辑状态
LogManager.Debug("[事件清理] ===== StopClickTool执行完成所有事件订阅已清理 =====");
}
catch (Exception ex)
{
LogManager.Error("停止点击工具失败", ex);
RaiseErrorOccurred(ex.Message, ex);
}
}
/// <summary>
/// 禁用鼠标处理(用于自动路径规划模式)
/// </summary>
public void DisableMouseHandling()
{
try
{
LogManager.Info("禁用PathPlanningManager鼠标处理进入自动路径规划模式");
IsInAutoPathMode = true;
// 取消PathPlanningManager的鼠标事件订阅
PathClickToolPlugin.MouseClicked -= OnToolPluginMouseClicked;
LogManager.Info("已取消PathPlanningManager的鼠标事件订阅");
}
catch (Exception ex)
{
LogManager.Error($"禁用鼠标处理失败: {ex.Message}", ex);
}
}
/// <summary>
/// 启用鼠标处理(用于手动路径编辑模式)
/// </summary>
public void EnableMouseHandling()
{
try
{
LogManager.Info("启用PathPlanningManager鼠标处理退出自动路径规划模式");
IsInAutoPathMode = false;
// 重新订阅PathPlanningManager的鼠标事件
PathClickToolPlugin.MouseClicked -= OnToolPluginMouseClicked; // 先取消避免重复
PathClickToolPlugin.MouseClicked += OnToolPluginMouseClicked;
LogManager.Info("已重新订阅PathPlanningManager的鼠标事件");
}
catch (Exception ex)
{
LogManager.Error($"启用鼠标处理失败: {ex.Message}", ex);
}
}
/// <summary>
/// 自动路径规划(支持路径策略)
/// </summary>
/// <param name="startPoint">起点</param>
/// <param name="endPoint">终点</param>
/// <param name="vehicleRadius">车辆尺寸(米)</param>
/// <param name="safetyMargin">安全间隙(米)</param>
/// <param name="gridSize">网格精度(米)</param>
/// <param name="vehicleHeight">车辆高度(米)</param>
/// <param name="strategy">路径规划策略</param>
/// <returns>规划结果</returns>
public Task<PathRoute> AutoPlanPath(PathPoint startPoint, PathPoint endPoint, double vehicleRadius, double safetyMargin, double gridSize, double vehicleHeight, PathStrategy strategy)
{
try
{
if (startPoint == null || endPoint == null)
{
throw new ArgumentException("起点和终点不能为空");
}
LogManager.Info($"开始自动路径规划: {startPoint.Name} -> {endPoint.Name}");
LogManager.Info($"起点坐标: ({startPoint.Position.X:F2}, {startPoint.Position.Y:F2}, {startPoint.Position.Z:F2})");
LogManager.Info($"终点坐标: ({endPoint.Position.X:F2}, {endPoint.Position.Y:F2}, {endPoint.Position.Z:F2})");
LogManager.Info($"车辆半径: {vehicleRadius}m, 安全间隙: {safetyMargin}m, 车辆高度: {vehicleHeight}m");
RaiseStatusChanged("正在进行自动路径规划...", PathPlanningStatusType.Info);
// 1. 获取模型边界
var bounds = GetModelBounds() ?? throw new Exception("无法获取模型边界,请确保模型已加载");
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}米");
}
else
{
LogManager.Info($"使用用户设置的网格大小: {gridSize}米");
}
// 2. 生成网格地图
RaiseStatusChanged("正在生成网格地图...", PathPlanningStatusType.Info);
var gridMapGenerator = new GridMapGenerator();
GridMap gridMap = null;
try
{
var performanceStart = System.Diagnostics.Stopwatch.StartNew();
// 获取当前文档
var document = Application.ActiveDocument;
LogManager.Info($"网格生成参数 - 边界: {bounds.Min.X:F2},{bounds.Min.Y:F2} -> {bounds.Max.X:F2},{bounds.Max.Y:F2}");
LogManager.Info($"网格生成参数 - 网格大小: {gridSize}m, 车辆半径: {vehicleRadius}m, 安全边距: {safetyMargin}m");
LogManager.Info($"网格生成参数 - 起点: ({startPoint.Position.X:F2}, {startPoint.Position.Y:F2}, {startPoint.Position.Z:F2})");
LogManager.Info($"网格生成参数 - 终点: ({endPoint.Position.X:F2}, {endPoint.Position.Y:F2}, {endPoint.Position.Z:F2})");
try
{
gridMap = gridMapGenerator.GenerateFromBIM(
bounds,
gridSize,
vehicleRadius,
safetyMargin,
startPoint.Position,
endPoint.Position,
vehicleHeight
);
LogManager.Info("✅ 网格地图生成成功");
}
catch (Exception modeEx)
{
LogManager.Warning($"网格地图生成失败,{modeEx.Message}");
}
performanceStart.Stop();
LogManager.Info($"GridMapGenerator.GenerateFromBIM 调用完成,耗时: {performanceStart.ElapsedMilliseconds}ms");
LogManager.Info($"生成的网格统计: {gridMap?.GetStatistics() ?? "NULL"}");
}
catch (Exception ex)
{
LogManager.Error($"网格地图生成失败: {ex.Message}");
throw; // 直接抛出原始异常,不包装
}
// 根据用户设置决定是否进行网格可视化
if (_showWalkableGrid || _showObstacleGrid || _showUnknownGrid || _showDoorGrid)
{
LogManager.Info("开始网格可视化,显示所有可通行网格单元");
VisualizeGridCells(gridMap, vehicleHeight);
}
// 3. 获取通道高度数据
var channelItems = GetChannelItemsForHeightCalculation(gridMap);
LogManager.Info($"获取通道数据完成: {channelItems?.Count() ?? 0} 个通道项");
// 4. 创建ChannelCoverage对象关键修复
ChannelCoverage channelCoverage = null;
if (channelItems != null && channelItems.Any())
{
LogManager.Info($"创建ChannelCoverage对象包含 {channelItems.Count()} 个通道项");
channelCoverage = new ChannelCoverage
{
GridMap = gridMap,
ChannelItems = channelItems.ToList(),
TotalBounds = bounds
};
LogManager.Info($"ChannelCoverage创建完成: {channelCoverage.GetStatistics()}");
}
else
{
LogManager.Error("未找到通道数据,无法进行路径规划");
throw new Exception("路径规划需要通道数据,请先进行通道检测");
}
// 5. 执行A*路径查找
RaiseStatusChanged("正在计算最优路径...", PathPlanningStatusType.Info);
LogManager.Info("=== 开始执行A*路径查找 ===");
AutoPathFinder pathFinder = null;
PathFindingResult pathResult = null;
try
{
pathFinder = new AutoPathFinder();
double vehicleHeightInModelUnits = vehicleHeight * UnitsConverter.GetMetersToUnitsConversionFactor(Application.ActiveDocument.Units);
LogManager.Info($"使用2.5D模式进行路径查找,车辆高度: {vehicleHeight}m ({vehicleHeightInModelUnits:F2}模型单位),策略: {strategy}");
pathResult = pathFinder.FindPath(startPoint.Position, endPoint.Position, gridMap, channelCoverage, vehicleHeightInModelUnits, strategy);
LogManager.Info("FindPath方法调用完成");
}
catch (Exception ex)
{
LogManager.Error($"路径查找失败: {ex.Message}");
throw; // 直接抛出原始异常,不包装
}
// 1. 检查路径查找是否完全失败
if (pathResult == null)
{
throw new Exception("路径查找失败");
}
// 2. 检查是否找到了可用的路径点至少2个点才能构成路径
if (pathResult.PathPoints.Count < 2)
{
throw new Exception("未找到可行路径(起点或终点不可达)");
}
// 3. 部分路径也是有效路径,不再抛出异常,继续正常处理
LogManager.Info($"A*算法找到路径,包含 {pathResult.PathPoints.Count} 个点,完成度: {pathResult.CompletionPercentage:F1}%");
// 6. 创建PathRoute对象并保存GridMap
var routeName = $"自动路径_{DateTime.Now:HHmmss}";
// 🔥 关键修复在创建路径前设置_currentGridMap确保GetSpeedLimitAtPosition能正常工作
_currentGridMap = gridMap;
var autoRoute = CreateAutoPathRoute(pathResult, routeName);
// 保存GridMap和参数到PathRoute以便后续恢复网格可视化
autoRoute.AssociatedGridMap = gridMap;
autoRoute.GridSize = gridSize;
// 将vehicleRadius拆分为长宽暂时使用相同值后续可以传入更详细的参数
autoRoute.MaxVehicleLength = vehicleRadius * 2; // 车辆半径转换为长度
autoRoute.MaxVehicleWidth = vehicleRadius * 2; // 车辆半径转换为宽度
autoRoute.MaxVehicleHeight = vehicleHeight;
autoRoute.SafetyMargin = safetyMargin;
LogManager.Info($"已保存GridMap到路径: {routeName}, 网格大小: {gridSize}米");
// 7. 添加到路径集合
if (!_routes.Contains(autoRoute))
{
_routes.Add(autoRoute);
// 保存自动生成的路径到数据库
SavePathToDatabase(autoRoute);
}
CurrentRoute = autoRoute;
// 8. 确保路径可视化使用正确的网格大小(修复尺寸自适应问题)
var renderPlugin = PathPointRenderPlugin.Instance;
if (renderPlugin != null && gridMap != null)
{
double gridSizeInMeters = UnitsConverter.ConvertToMeters(gridMap.CellSize);
renderPlugin.SetGridSize(gridSizeInMeters);
LogManager.Debug($"[路径可视化] 设置网格大小为 {gridSizeInMeters:F3}米,确保路径点尺寸正确");
}
// 9. 自动绘制路径可视化
DrawRouteVisualization(autoRoute, isAutoPath: true);
// 10. 强制设置路径编辑状态为查看状态(修复状态问题)
// 在所有事件触发之前设置状态,确保不会被后续操作重置
LogManager.Info("自动路径规划完成强制设置状态为Viewing");
PathEditState = PathEditState.Viewing;
EditingRoute = null; // 确保没有编辑中的路径
// 11. 触发事件
RaiseRouteGenerated(autoRoute, RouteGenerationMethod.AutoPlanning, gridSize);
var statusMessage = $"自动路径规划完成(2.5D模式): {routeName},使用网格大小 {gridSize:F2}米";
// 添加完成度信息
if (!autoRoute.IsComplete)
{
statusMessage += $" (完成度: {autoRoute.CompletionPercentage:F1}%)";
}
RaiseStatusChanged(statusMessage, PathPlanningStatusType.Success);
LogManager.Info($"自动路径规划成功完成: 路径长度 {autoRoute.TotalLength:F2}米,包含 {autoRoute.Points.Count} 个点,使用网格大小 {gridSize:F2}米");
return Task.FromResult(autoRoute);
}
catch (Exception ex)
{
LogManager.Error($"自动路径规划失败: {ex.Message}", ex);
RaiseErrorOccurred(ex.Message, ex); // 使用原始异常消息,不再包装
return Task.FromResult<PathRoute>(null);
}
}
/// <summary>
/// 设置当前路线
/// </summary>
/// <param name="route">要设置的路线</param>
public void SetCurrentRoute(PathRoute route)
{
try
{
CurrentRoute = route;
// 如果路径有关联的GridMap加载它用于网格可视化
if (route?.AssociatedGridMap != null)
{
_currentGridMap = route.AssociatedGridMap;
LogManager.Info($"已加载路径 '{route.Name}' 的GridMap网格大小: {route.GridSize}米");
// 如果网格可视化开启,立即刷新显示
if (IsAnyGridVisualizationEnabled)
{
LogManager.Info("检测到网格可视化已启用,自动刷新网格显示");
RefreshGridVisualization();
}
}
else
{
// 如果路径没有GridMap清空当前缓存
_currentGridMap = null;
if (route != null)
{
LogManager.Info($"路径 '{route.Name}' 没有关联的GridMap");
}
}
LogManager.Info($"当前路线已设置: {route?.Name ?? "null"}");
}
catch (Exception ex)
{
LogManager.Error("设置当前路线失败", ex);
RaiseErrorOccurred(ex.Message, ex);
}
}
/// <summary>
/// 切换到编辑状态
/// </summary>
/// <param name="route">要编辑的路径</param>
public void SwitchToEditingState(PathRoute route)
{
PathEditState = PathEditState.Editing;
EditingRoute = route ?? throw new ArgumentNullException(nameof(route));
CurrentRoute = route;
// 智能管理ToolPlugin状态
ManageToolPluginForEditState();
RaiseStatusChanged($"已切换到编辑状态,正在编辑路径: {route.Name}", PathPlanningStatusType.Info);
}
/// <summary>
/// 选择通道模型
/// </summary>
/// <param name="useCurrentSelection">是否使用当前选择的模型</param>
/// <returns>选择的通道数量</returns>
public int SelectChannels(bool useCurrentSelection = true)
{
try
{
_walkableAreas.Clear();
if (useCurrentSelection)
{
// 使用当前选择的模型
var currentSelection = Application.ActiveDocument.CurrentSelection.SelectedItems;
if (currentSelection.Any())
{
_walkableAreas.AddRange(currentSelection);
RaiseStatusChanged($"已选择 {_walkableAreas.Count} 个模型作为通道", PathPlanningStatusType.Success);
}
else
{
RaiseStatusChanged("未选择任何模型,请先选择通道模型", PathPlanningStatusType.Warning);
return 0;
}
}
else
{
// 直接使用 CategoryAttributeManager 的方法筛选通道,无需额外的包装方法
var document = Application.ActiveDocument;
if (document != null)
{
var allLogisticsItems = CategoryAttributeManager.GetAllLogisticsItems();
var channelItems = CategoryAttributeManager.FilterByLogisticsType(allLogisticsItems, CategoryAttributeManager.LogisticsElementType.);
_walkableAreas.AddRange(channelItems);
LogManager.Info($"[SelectChannels] 通过CategoryAttributeManager直接筛选到 {channelItems.Count} 个通道");
}
RaiseStatusChanged($"通过类别筛选到 {_walkableAreas.Count} 个通道", PathPlanningStatusType.Success);
}
if (_walkableAreas.Any())
{
// 计算组合边界
CalculateCombinedBounds();
// 触发通道选择变更事件
RaiseChannelSelectionChanged(_walkableAreas, useCurrentSelection ? "手动选择" : "类别筛选");
}
return _walkableAreas.Count;
}
catch (Exception ex)
{
RaiseErrorOccurred($"选择通道时发生错误: {ex.Message}", ex);
return 0;
}
}
/// <summary>
/// 添加路径到管理器
/// </summary>
/// <param name="route">要添加的路径</param>
/// <returns>是否成功添加</returns>
public bool AddRoute(PathRoute route)
{
try
{
if (route == null)
{
RaiseErrorOccurred("无法添加空路径");
return false;
}
// 检查是否已存在同名路径
var existingRoute = _routes.FirstOrDefault(r => r.Name == route.Name);
if (existingRoute != null)
{
// 如果存在同名路径,生成唯一名称
int counter = 1;
string originalName = route.Name;
while (_routes.Any(r => r.Name == route.Name))
{
route.Name = $"{originalName}_{counter}";
counter++;
}
}
_routes.Add(route);
RaiseStatusChanged($"已添加路径: {route.Name}", PathPlanningStatusType.Success);
// 保存到数据库
SavePathToDatabase(route);
// AddRoute只负责添加路径到数据集合不自动设置当前路径
// 路径选择应该通过专门的选择逻辑处理,而不是添加操作的副作用
// if (_currentRoute == null || _currentRoute.Points.Count == 0)
// {
// CurrentRoute = route;
// }
// 注释掉多余的路径生成事件调用Manual类型路径通过UI层的RefreshPathRoutes()统一刷新
// RaiseRouteGenerated(route, RouteGenerationMethod.Manual);
return true;
}
catch (Exception ex)
{
RaiseErrorOccurred($"添加路径失败: {ex.Message}", ex);
return false;
}
}
/// <summary>
/// 创建新路径
/// </summary>
/// <param name="routeName">路径名称</param>
/// <returns>新创建的路径</returns>
public PathRoute CreateNewRoute(string routeName = null)
{
if (string.IsNullOrEmpty(routeName))
{
routeName = $"路径_{_routes.Count + 1}";
}
var newRoute = new PathRoute(routeName);
_routes.Add(newRoute);
CurrentRoute = newRoute;
// 保存到数据库
SavePathToDatabase(newRoute);
RaiseStatusChanged($"已创建新路径: {routeName}", PathPlanningStatusType.Success);
return newRoute;
}
/// <summary>
/// 删除路径
/// </summary>
/// <param name="route">要删除的路径</param>
/// <returns>是否成功删除</returns>
public bool DeleteRoute(PathRoute route)
{
if (route == null) return false;
try
{
bool removed = _routes.Remove(route);
if (removed)
{
// 从数据库删除
if (_pathDatabase != null)
{
try
{
_pathDatabase.DeletePathRoute(route.Id);
LogManager.Info($"已从数据库删除路径: {route.Name}");
}
catch (Exception dbEx)
{
LogManager.Error($"从数据库删除路径失败: {dbEx.Message}", dbEx);
// 继续执行,不影响内存操作
}
}
if (_currentRoute == route)
{
var newRoute = _routes.FirstOrDefault() ?? new PathRoute("默认路径");
CurrentRoute = newRoute;
}
RaiseStatusChanged($"已删除路径: {route.Name}", PathPlanningStatusType.Success);
}
return removed;
}
catch (Exception ex)
{
RaiseErrorOccurred($"删除路径时发生错误: {ex.Message}", ex);
return false;
}
}
/// <summary>
/// 生成路径
/// </summary>
/// <param name="route">要生成的路径为null时使用当前路径</param>
/// <returns>是否成功生成</returns>
public bool GeneratePath(PathRoute route = null)
{
route = route ?? _currentRoute;
if (route == null) return false;
try
{
// 验证路径有效性
if (!route.IsValid())
{
RaiseErrorOccurred("路径无效:必须包含至少一个起点和一个终点");
return false;
}
// 更新路径关联的通道ID
route.AssociatedChannelIds.Clear();
foreach (var channel in _walkableAreas)
{
route.AssociatedChannelIds.Add(channel.InstanceGuid.ToString());
}
// 计算预估时间(简单实现)
CalculateEstimatedTime(route);
RaiseStatusChanged($"路径生成成功: {route.Name}, 长度: {route.TotalLength:F2}米, 预估时间: {route.EstimatedTime:F1}秒", PathPlanningStatusType.Success);
RaiseRouteGenerated(route, RouteGenerationMethod.Manual);
return true;
}
catch (Exception ex)
{
RaiseErrorOccurred($"生成路径时发生错误: {ex.Message}", ex);
return false;
}
}
/// <summary>
/// 开始新建路径
/// </summary>
/// <param name="routeName">路径名称</param>
/// <returns>创建的新路径</returns>
public PathRoute StartCreatingNewRoute(string routeName = null)
{
try
{
// 设置为创建状态
PathEditState = PathEditState.Creating;
// 创建新路径
var newRoute = new PathRoute(routeName ?? $"路径{DateTime.Now:yyyyMMdd_HHmmss}");
_editingRoute = newRoute;
CurrentRoute = newRoute;
// 自动选择所有可通行的物流模型
AutoSelectLogisticsChannels();
// 检查是否有可通行的物流模型
if (_walkableAreas == null || _walkableAreas.Count == 0)
{
RaiseErrorOccurred("没有找到任何可通行的物流模型,请先为模型设置可通行的物流属性");
// 重置状态
SwitchToViewingState();
return null;
}
// 智能管理ToolPlugin状态
ManageToolPluginForEditState();
RaiseStatusChanged($"正在新建路径: {newRoute.Name} - 请在3D视图中可通行的物流模型上点击设置路径点", PathPlanningStatusType.Info);
return newRoute;
}
catch (Exception ex)
{
RaiseErrorOccurred($"开始新建路径失败: {ex.Message}", ex);
// 确保在出错时也重置状态
SwitchToViewingState();
return null;
}
}
/// <summary>
/// 完成当前编辑并保存
/// </summary>
/// <returns>是否成功完成编辑</returns>
public bool FinishEditing()
{
if (!IsInEditableState)
{
RaiseErrorOccurred("当前不在编辑状态,无法完成编辑");
return false;
}
try
{
// 自动设置最后一个点为终点
if (CurrentRoute != null && CurrentRoute.Points.Count > 1)
{
var lastPoint = CurrentRoute.Points.Last();
if (lastPoint.Type != PathPointType.StartPoint) // 起点不能是终点
{
lastPoint.Type = PathPointType.EndPoint;
lastPoint.Name = GeneratePointName(PathPointType.EndPoint);
// 更新3D路径可视化
// 重新渲染整个路径以反映类型变更
_renderPlugin?.RenderPath(CurrentRoute);
LogManager.Info($"已自动设置最后一个点为终点: {lastPoint.Name}");
}
}
// 如果是创建模式,将当前路径添加到路径集合
if (_pathEditState == PathEditState.Creating && CurrentRoute != null)
{
if (!_routes.Contains(CurrentRoute))
{
_routes.Add(CurrentRoute);
// 保存到数据库
SavePathToDatabase(CurrentRoute);
// 添加历史记录
var historyEntry = new PathHistoryEntry(
CurrentRoute.Id,
PathHistoryOperationType.Created,
CurrentRoute,
$"创建新路径: {CurrentRoute.Name}");
_historyManager.AddHistoryEntry(historyEntry);
}
}
// 如果是编辑模式,保存编辑历史
if (_pathEditState == PathEditState.Editing && EditingRoute != null)
{
// 更新数据库
SavePathToDatabase(EditingRoute);
var historyEntry = new PathHistoryEntry(
EditingRoute.Id,
PathHistoryOperationType.Edited,
EditingRoute,
$"编辑路径: {EditingRoute.Name}");
_historyManager.AddHistoryEntry(historyEntry);
}
// 切换回查看状态
SwitchToViewingState();
// 触发路径点列表更新事件
RaisePathPointsListUpdated(CurrentRoute, "编辑完成");
// 触发路径生成事件
RaiseRouteGenerated(CurrentRoute, RouteGenerationMethod.Manual);
RaiseStatusChanged("路径编辑已完成", PathPlanningStatusType.Success);
return true;
}
catch (Exception ex)
{
RaiseErrorOccurred($"完成编辑失败: {ex.Message}", ex);
return false;
}
}
#region
/// <summary>
/// 开始修改指定的路径点
/// </summary>
/// <param name="pointIndex">要修改的路径点索引</param>
/// <returns>是否成功开始修改</returns>
public bool StartEditingPoint(int pointIndex)
{
try
{
if (CurrentRoute == null || pointIndex < 0 || pointIndex >= CurrentRoute.Points.Count)
{
RaiseErrorOccurred("无效的路径点索引或当前没有路径");
return false;
}
// 保存原始点和索引
_editingPointIndex = pointIndex;
var originalPoint = CurrentRoute.Points[pointIndex];
_originalPoint = new PathPoint
{
Id = originalPoint.Id,
Name = originalPoint.Name,
Position = originalPoint.Position,
Type = originalPoint.Type
};
// 切换到修改路径点状态
PathEditState = PathEditState.EditingPoint;
// 激活ToolPlugin以接收3D点击
ActivateToolPlugin();
RaiseStatusChanged($"开始修改路径点: {_originalPoint.Name}", PathPlanningStatusType.Info);
LogManager.Info($"开始修改路径点,索引: {pointIndex}, 名称: {_originalPoint.Name}");
return true;
}
catch (Exception ex)
{
RaiseErrorOccurred($"开始修改路径点失败: {ex.Message}", ex);
return false;
}
}
/// <summary>
/// 设置修改路径点时的预览位置
/// </summary>
/// <param name="position">新的预览位置</param>
public void SetEditingPreviewPoint(Point3D position)
{
try
{
if (PathEditState != PathEditState.EditingPoint || _editingPointIndex == -1)
{
return;
}
// 创建预览路径点
_editingPreviewPoint = new PathPoint
{
Position = position,
Name = _originalPoint.Name + "_预览",
Type = _originalPoint.Type
};
// 更新预览可视化
UpdateEditingPreviewVisualization();
LogManager.Info($"设置修改预览点: ({position.X:F3}, {position.Y:F3}, {position.Z:F3})");
}
catch (Exception ex)
{
LogManager.Error($"设置修改预览点失败: {ex.Message}");
}
}
/// <summary>
/// 更新路径点位置(保存修改)
/// </summary>
/// <returns>是否成功更新</returns>
public bool UpdatePointPosition()
{
try
{
if (PathEditState != PathEditState.EditingPoint ||
_editingPointIndex == -1 ||
_editingPreviewPoint == null ||
CurrentRoute == null)
{
RaiseErrorOccurred("当前不在修改路径点状态或没有预览点");
return false;
}
// 更新路径点位置
var pointToUpdate = CurrentRoute.Points[_editingPointIndex];
pointToUpdate.Position = _editingPreviewPoint.Position;
// 添加历史记录
var historyEntry = new PathHistoryEntry(
CurrentRoute.Id,
PathHistoryOperationType.Edited,
CurrentRoute,
$"修改路径点 {pointToUpdate.Name} 位置");
_historyManager.AddHistoryEntry(historyEntry);
// 清理修改预览可视化(会自动清除预览并重新渲染正常路径)
ClearEditingPreviewVisualization();
// 清理修改状态
ClearEditingState();
// 切换回查看状态
PathEditState = PathEditState.Viewing;
// 停用ToolPlugin
DeactivateToolPlugin();
// 触发路径点列表更新事件
RaisePathPointsListUpdated(CurrentRoute, "路径点修改完成");
// 保存到数据库
SavePathToDatabase(CurrentRoute);
RaiseStatusChanged($"路径点 {pointToUpdate.Name} 修改完成", PathPlanningStatusType.Success);
LogManager.Info($"路径点修改完成: {pointToUpdate.Name}");
return true;
}
catch (Exception ex)
{
RaiseErrorOccurred($"确认修改路径点失败: {ex.Message}");
return false;
}
}
/// <summary>
/// 取消修改路径点
/// </summary>
/// <returns>是否成功取消修改</returns>
public bool CancelEditPoint()
{
try
{
if (PathEditState != PathEditState.EditingPoint)
{
return true; // 已经不在修改状态,认为成功
}
// 清理修改状态
ClearEditingState();
// 切换回查看状态
PathEditState = PathEditState.Viewing;
// 停用ToolPlugin
DeactivateToolPlugin();
// 清理预览可视化
ClearEditingPreviewVisualization();
RaiseStatusChanged("已取消路径点修改", PathPlanningStatusType.Info);
LogManager.Info("已取消路径点修改");
return true;
}
catch (Exception ex)
{
RaiseErrorOccurred($"取消修改路径点失败: {ex.Message}", ex);
return false;
}
}
/// <summary>
/// 清理修改路径点的状态
/// </summary>
private void ClearEditingState()
{
_editingPointIndex = -1;
_originalPoint = null;
_editingPreviewPoint = null;
}
/// <summary>
/// 更新修改路径点时的预览可视化
/// </summary>
private void UpdateEditingPreviewVisualization()
{
try
{
if (_renderPlugin == null || CurrentRoute == null || _editingPreviewPoint == null)
{
return;
}
// 创建预览路径,替换修改中的路径点
var previewRoute = CurrentRoute.Clone() as PathRoute;
previewRoute.Points[_editingPointIndex] = _editingPreviewPoint;
// 渲染预览路径(使用特殊的预览样式)
_renderPlugin.RenderPreviewPath(previewRoute);
}
catch (Exception ex)
{
LogManager.Error($"更新修改预览可视化失败: {ex.Message}");
}
}
/// <summary>
/// 清理修改路径点时的预览可视化
/// </summary>
private void ClearEditingPreviewVisualization()
{
try
{
if (_renderPlugin == null)
{
return;
}
// 清理预览渲染,恢复原始路径渲染
_renderPlugin.ClearPreview();
if (CurrentRoute != null)
{
_renderPlugin.RenderPath(CurrentRoute);
}
}
catch (Exception ex)
{
LogManager.Error($"清理修改预览可视化失败: {ex.Message}");
}
}
#endregion
/// <summary>
/// 取消当前编辑
/// </summary>
/// <returns>是否成功取消编辑</returns>
public bool CancelEditing()
{
if (!IsInEditableState)
{
RaiseErrorOccurred("当前不在编辑状态,无法取消编辑");
return false;
}
try
{
// 如果是创建模式,清理当前路径
if (_pathEditState == PathEditState.Creating && CurrentRoute != null)
{
CurrentRoute.Points.Clear();
LogManager.Info("已清理新建路径的临时数据");
}
// 切换回查看状态
SwitchToViewingState();
RaiseStatusChanged("路径编辑已取消", PathPlanningStatusType.Info);
return true;
}
catch (Exception ex)
{
RaiseErrorOccurred($"取消编辑失败: {ex.Message}", ex);
return false;
}
}
/// <summary>
/// 在3D视图中添加路径点
/// </summary>
/// <param name="worldPoint">3D世界坐标</param>
/// <param name="pointType">路径点类型为null时使用当前类型</param>
/// <returns>添加的路径点失败时返回null</returns>
public PathPoint AddPathPointIn3D(Point3D worldPoint, PathPointType? pointType = null)
{
// 确保在编辑状态下才能添加点
if (!IsInEditableState)
{
RaiseErrorOccurred("不在编辑状态,无法添加路径点");
return null;
}
// 如果没有当前路径,则无法添加
if (CurrentRoute == null)
{
RaiseErrorOccurred("内部错误:当前路径丢失");
return null;
}
try
{
// 确定路径点类型
PathPointType finalPointType;
if (pointType.HasValue)
{
finalPointType = pointType.Value;
}
else
{
// 自动判断类型:第一个点为起点,其余为路径点
finalPointType = (CurrentRoute.Points.Count == 0)
? PathPointType.StartPoint
: PathPointType.WayPoint;
}
// 创建路径点
var pathPoint = new PathPoint
{
Name = GeneratePointName(finalPointType),
Position = worldPoint,
Type = finalPointType
};
CurrentRoute.Points.Add(pathPoint);
LogManager.Info($"路径点已添加: {pathPoint.Name}, 位置: ({worldPoint.X:F2}, {worldPoint.Y:F2}, {worldPoint.Z:F2})");
// 绘制3D路径可视化使用与自动路径规划相同的方法
try
{
DrawRouteVisualization(CurrentRoute, isAutoPath: false);
LogManager.Info($"手工路径3D可视化已更新: {pathPoint.Name}");
}
catch (Exception renderEx)
{
LogManager.Error($"绘制手工路径3D可视化失败: {renderEx.Message}");
}
// 触发路径点添加事件
RaisePathPointOperation(PathPointOperationType.Added, pathPoint, CurrentRoute);
// 触发路径列表更新事件
RaisePathPointsListUpdated(CurrentRoute, "添加路径点");
return pathPoint;
}
catch (Exception ex)
{
RaiseErrorOccurred($"添加路径点失败: {ex.Message}", ex);
return null;
}
}
/// <summary>
/// 设置预览点位置(仅用于预览,不添加到路径中)
/// </summary>
/// <param name="worldPoint">3D世界坐标</param>
/// <param name="pointType">点类型</param>
/// <returns>预览点对象</returns>
public PathPoint SetPreviewPoint(Point3D worldPoint, PathPointType? pointType = null)
{
// 确保在编辑状态下才能设置预览点
if (!IsInEditableState)
{
RaiseErrorOccurred("不在编辑状态,无法设置预览点");
return null;
}
// 如果没有当前路径,则无法设置预览点
if (CurrentRoute == null)
{
RaiseErrorOccurred("内部错误:当前路径丢失");
return null;
}
try
{
// 确定路径点类型
PathPointType finalPointType;
if (pointType.HasValue)
{
finalPointType = pointType.Value;
}
else
{
// 自动判断类型:第一个点为起点,其余为路径点
finalPointType = (CurrentRoute.Points.Count == 0)
? PathPointType.StartPoint
: PathPointType.WayPoint;
}
// 计算预览点应该插入的位置
_previewInsertIndex = -1; // 重置插入索引
if (CurrentRoute.Points.Count >= 2)
{
// 如果路径中有至少2个点计算最佳插入位置
var nearestSegment = FindNearestLineSegmentWithIndex(worldPoint, CurrentRoute.Points.ToList());
if (nearestSegment.HasValue)
{
_previewInsertIndex = nearestSegment.Value.insertIndex;
LogManager.Info($"预览点计算出插入索引: {_previewInsertIndex},位于 {nearestSegment.Value.prevPoint.Name} 和 {nearestSegment.Value.nextPoint.Name} 之间");
}
}
// 创建预览点(不添加到路径中)
_previewPoint = new PathPoint
{
Name = $"预览-{GeneratePointName(finalPointType)}",
Position = worldPoint,
Type = finalPointType
};
_isPreviewMode = true;
LogManager.Info($"预览点已设置: {_previewPoint.Name}, 位置: ({worldPoint.X:F2}, {worldPoint.Y:F2}, {worldPoint.Z:F2}), 插入索引: {_previewInsertIndex}");
// 绘制预览点可视化(灰色)
try
{
DrawPreviewPointVisualization(_previewPoint);
LogManager.Info($"预览点3D可视化已更新: {_previewPoint.Name}");
// 如果路径中有足够的点,绘制预览连线
if (CurrentRoute.Points.Count >= 2)
{
DrawPreviewLinesVisualization(_previewPoint, CurrentRoute.Points.ToList());
LogManager.Info($"预览连线已绘制: {_previewPoint.Name}");
}
}
catch (Exception renderEx)
{
LogManager.Error($"绘制预览点3D可视化失败: {renderEx.Message}");
}
return _previewPoint;
}
catch (Exception ex)
{
RaiseErrorOccurred($"设置预览点失败: {ex.Message}", ex);
return null;
}
}
/// <summary>
/// 将预览点添加为正式路径点
/// </summary>
/// <returns>添加的路径点如果失败返回null</returns>
public PathPoint ConvertPreviewToPathPoint()
{
if (!_isPreviewMode || _previewPoint == null)
{
RaiseErrorOccurred("没有预览点可确认");
return null;
}
if (CurrentRoute == null)
{
RaiseErrorOccurred("内部错误:当前路径丢失");
return null;
}
try
{
// 创建正式的路径点
var confirmPoint = new PathPoint
{
Name = GeneratePointName(_previewPoint.Type),
Position = _previewPoint.Position,
Type = _previewPoint.Type
};
// 根据保存的插入索引决定添加方式
if (_previewInsertIndex >= 0 && _previewInsertIndex <= CurrentRoute.Points.Count)
{
// 在指定位置插入路径点
CurrentRoute.Points.Insert(_previewInsertIndex, confirmPoint);
LogManager.Info($"路径点已插入到索引 {_previewInsertIndex}: {confirmPoint.Name}, 位置: ({confirmPoint.Position.X:F2}, {confirmPoint.Position.Y:F2}, {confirmPoint.Position.Z:F2})");
}
else
{
// 如果没有有效的插入索引,添加到末尾(原来的行为)
CurrentRoute.Points.Add(confirmPoint);
LogManager.Info($"路径点已添加到末尾: {confirmPoint.Name}, 位置: ({confirmPoint.Position.X:F2}, {confirmPoint.Position.Y:F2}, {confirmPoint.Position.Z:F2})");
}
// 清除预览状态
ClearPreviewPoint();
// 绘制3D路径可视化
try
{
DrawRouteVisualization(CurrentRoute, isAutoPath: false);
LogManager.Info($"手工路径3D可视化已更新: {confirmPoint.Name}");
}
catch (Exception renderEx)
{
LogManager.Error($"绘制手工路径3D可视化失败: {renderEx.Message}");
}
// 触发路径点添加事件
RaisePathPointOperation(PathPointOperationType.Added, confirmPoint, CurrentRoute);
// 触发路径列表更新事件
RaisePathPointsListUpdated(CurrentRoute, "确认添加预览点");
// 保存到数据库
SavePathToDatabase(CurrentRoute);
return confirmPoint;
}
catch (Exception ex)
{
RaiseErrorOccurred($"确认预览点失败: {ex.Message}");
return null;
}
}
/// <summary>
/// 清除预览点
/// </summary>
public void ClearPreviewPoint()
{
if (_isPreviewMode && _previewPoint != null)
{
LogManager.Info($"清除预览点: {_previewPoint.Name}");
// 清除预览点可视化
try
{
ClearPreviewPointVisualization();
}
catch (Exception ex)
{
LogManager.Error($"清除预览点可视化失败: {ex.Message}");
}
}
_previewPoint = null;
_isPreviewMode = false;
_previewInsertIndex = -1; // 清除保存的插入索引
}
/// <summary>
/// 绘制预览点可视化(灰色)
/// </summary>
/// <param name="previewPoint">预览点</param>
private void DrawPreviewPointVisualization(PathPoint previewPoint)
{
if (_renderPlugin != null && previewPoint != null)
{
// 先清除之前的预览点
_renderPlugin.ClearPreviewPoint();
// 绘制新的灰色预览点
_renderPlugin.RenderPreviewPoint(previewPoint);
LogManager.Info($"预览点可视化已绘制: {previewPoint.Name}");
}
}
/// <summary>
/// 绘制预览连线的3D可视化灰色
/// </summary>
/// <param name="previewPoint">预览点</param>
/// <param name="pathPoints">当前路径点列表</param>
private void DrawPreviewLinesVisualization(PathPoint previewPoint, List<PathPoint> pathPoints)
{
if (_renderPlugin != null && previewPoint != null && pathPoints != null)
{
// 绘制预览连线
_renderPlugin.RenderPreviewLines(previewPoint, pathPoints);
LogManager.Info($"预览连线可视化已绘制: {previewPoint.Name}");
}
}
/// <summary>
/// 清除预览点可视化
/// </summary>
private void ClearPreviewPointVisualization()
{
if (_renderPlugin != null)
{
_renderPlugin.ClearPreviewPoint();
LogManager.Info("预览点可视化已清除");
}
}
/// <summary>
/// 获取路径统计信息
/// </summary>
/// <returns>统计信息字符串</returns>
public string GetStatistics()
{
var stats = $"通道数量: {_walkableAreas.Count}\n";
stats += $"路径数量: {_routes.Count}\n";
if (_currentRoute != null)
{
stats += $"当前路径: {_currentRoute.Name}\n";
stats += $"路径点数: {_currentRoute.Points.Count}\n";
stats += $"路径长度: {_currentRoute.TotalLength:F2}米\n";
stats += $"预估时间: {_currentRoute.EstimatedTime:F1}秒\n";
}
if (_combinedChannelBounds != null)
{
double scaleX = 0, scaleY = 0;
_coordinateConverter?.GetMapScale(out scaleX, out scaleY);
stats += $"通道范围: {_combinedChannelBounds.MinPoint.X:F2},{_combinedChannelBounds.MinPoint.Y:F2} - {_combinedChannelBounds.MaxPoint.X:F2},{_combinedChannelBounds.MaxPoint.Y:F2}\n";
stats += $"地图缩放: {scaleX:F4}, {scaleY:F4}";
}
return stats;
}
/// <summary>
/// 验证路径管理器状态
/// </summary>
/// <returns>验证结果</returns>
public PathPlanningResult ValidateManagerState()
{
try
{
var errors = new List<string>();
// 检查基础组件
if (_categoryManager == null)
errors.Add("类别属性管理器未初始化");
if (_uiStateManager == null)
errors.Add("UI状态管理器未初始化");
// 检查Navisworks环境
var document = Application.ActiveDocument;
if (document == null)
errors.Add("当前没有活动的Navisworks文档");
else if (document.Models == null || !document.Models.Any())
errors.Add("当前文档中没有加载的模型");
// 检查通道选择状态
if (_walkableAreas.Count == 0)
errors.Add("没有选择任何通道模型");
if (errors.Count > 0)
{
return PathPlanningResult.ValidationFailure($"路径管理器状态验证失败: {string.Join("; ", errors)}");
}
return PathPlanningResult.Success("路径管理器状态验证通过");
}
catch (Exception ex)
{
LogManager.Error("验证路径管理器状态时发生异常", ex);
return PathPlanningResult.ValidationFailure($"状态验证异常: {ex.Message}");
}
}
/// <summary>
/// 验证路径有效性
/// </summary>
/// <param name="route">要验证的路径</param>
/// <returns>验证结果</returns>
public PathPlanningResult ValidateRoute(PathRoute route)
{
try
{
if (route == null)
return PathPlanningResult.ValidationFailure("路径不能为空");
var errors = new List<string>();
var warnings = new List<string>();
// 基本验证
if (string.IsNullOrWhiteSpace(route.Name))
errors.Add("路径名称不能为空");
if (route.Points.Count < 2)
errors.Add("路径至少需要包含2个点");
// 点类型验证
var startPoints = route.Points.Where(p => p.Type == PathPointType.StartPoint).ToList();
var endPoints = route.Points.Where(p => p.Type == PathPointType.EndPoint).ToList();
if (startPoints.Count == 0)
errors.Add("路径必须包含至少一个起点");
else if (startPoints.Count > 1)
warnings.Add($"路径包含多个起点({startPoints.Count}个)");
if (endPoints.Count == 0)
errors.Add("路径必须包含至少一个终点");
else if (endPoints.Count > 1)
warnings.Add($"路径包含多个终点({endPoints.Count}个)");
// 坐标验证
foreach (var point in route.Points)
{
if (point.Position == null)
errors.Add($"路径点 '{point.Name}' 缺少位置信息");
}
// 路径长度验证
if (route.TotalLength < 0.1)
warnings.Add("路径长度过短,可能存在重复点");
else if (route.TotalLength > 10000)
warnings.Add("路径长度过长,可能影响性能");
// 构建结果
if (errors.Count > 0)
{
return PathPlanningResult.ValidationFailure($"路径验证失败: {string.Join("; ", errors)}");
}
var message = "路径验证通过";
if (warnings.Count > 0)
{
message += $" (警告: {string.Join("; ", warnings)})";
}
return PathPlanningResult.Success(message);
}
catch (Exception ex)
{
LogManager.Error($"验证路径 '{route?.Name}' 时发生异常", ex);
return PathPlanningResult.ValidationFailure($"路径验证异常: {ex.Message}");
}
}
#endregion
#region
private void CalculateCombinedBounds()
{
// 保留原有实现但移除UI相关调用
if (!_walkableAreas.Any())
{
_combinedChannelBounds = null;
return;
}
try
{
var conversionFactor = GetUnitsToMetersConversionFactor();
var units = Application.ActiveDocument.Units;
var allBounds = new List<BoundingBox3D>();
foreach (var channel in _walkableAreas)
{
try
{
var originalBoundingBox = channel.BoundingBox();
if (originalBoundingBox != null)
{
var boundingBoxInMeters = ConvertBoundingBoxToMeters(originalBoundingBox, conversionFactor);
allBounds.Add(boundingBoxInMeters);
}
}
catch
{
continue;
}
}
if (allBounds.Any())
{
var minX = allBounds.Min(b => b.Min.X);
var minY = allBounds.Min(b => b.Min.Y);
var minZ = allBounds.Min(b => b.Min.Z);
var maxX = allBounds.Max(b => b.Max.X);
var maxY = allBounds.Max(b => b.Max.Y);
var maxZ = allBounds.Max(b => b.Max.Z);
var combinedBoundingBox = new BoundingBox3D(
new Point3D(minX, minY, minZ),
new Point3D(maxX, maxY, maxZ)
);
_combinedChannelBounds = new ChannelBounds(combinedBoundingBox);
RaiseStatusChanged($"已计算通道边界: {_combinedChannelBounds.MinPoint.X:F2},{_combinedChannelBounds.MinPoint.Y:F2} - {_combinedChannelBounds.MaxPoint.X:F2},{_combinedChannelBounds.MaxPoint.Y:F2}", PathPlanningStatusType.Info);
}
}
catch (Exception ex)
{
RaiseErrorOccurred($"计算通道边界时发生错误: {ex.Message}", ex);
}
}
/// <summary>
/// 计算预估时间
/// </summary>
private void CalculateEstimatedTime(PathRoute route)
{
// 简单的时间估算假设平均速度1米/秒
const double averageSpeed = 1.0; // 米/秒
route.EstimatedTime = route.TotalLength / averageSpeed;
}
/// <summary>
/// 生成路径点名称
/// </summary>
private string GeneratePointName(PathPointType pointType)
{
var currentPoints = _currentRoute?.Points ?? new List<PathPoint>();
var typeCount = currentPoints.Count(p => p.Type == pointType) + 1;
switch (pointType)
{
case PathPointType.StartPoint:
// 起点固定显示为"起点",不带编号
return "起点";
case PathPointType.EndPoint:
// 终点固定显示为"终点",不带编号
return "终点";
case PathPointType.WayPoint:
return $"路径点{typeCount}";
default:
return $"点{typeCount}";
}
}
/// <summary>
/// 自动选择所有可通行的物流模型
/// </summary>
private void AutoSelectLogisticsChannels()
{
try
{
var document = Application.ActiveDocument;
if (document?.Models == null) return;
_walkableAreas.Clear();
LogManager.Info("[通道自动选择] 开始搜索可通行的物流模型");
// 使用CategoryAttributeManager统一接口获取物流项目
var allLogisticsItemsCollection = CategoryAttributeManager.GetAllLogisticsItems();
LogManager.Info($"[通道自动选择] CategoryAttributeManager找到 {allLogisticsItemsCollection.Count} 个具有物流属性的模型项");
// 筛选出可通行的物流模型
var traversableItems = CategoryAttributeManager.FilterTraversableItems(allLogisticsItemsCollection);
LogManager.Info($"[通道自动选择] 筛选出 {traversableItems.Count} 个可通行的物流模型");
// 将可通行的物流模型添加到_selectedChannels
foreach (ModelItem item in traversableItems)
{
_walkableAreas.Add(item);
LogManager.Info($"[通道自动选择] 添加可通行模型: '{item.DisplayName}'");
}
if (_walkableAreas.Count > 0)
{
// 计算组合边界
CalculateCombinedBounds();
// 触发通道选择变更事件
RaiseChannelSelectionChanged(_walkableAreas, "自动选择");
LogManager.Info($"[通道自动选择] ✅ 自动选择了 {_walkableAreas.Count} 个可通行的物流模型");
}
else
{
LogManager.Warning("[通道自动选择] ❌ 未找到任何可通行的物流模型");
}
}
catch (Exception ex)
{
LogManager.Error($"[通道自动选择] 自动选择可通行物流模型失败: {ex.Message}");
RaiseErrorOccurred($"自动选择可通行物流模型失败: {ex.Message}", ex);
}
}
/// <summary>
/// 智能ToolPlugin管理根据编辑状态自动激活或停用
/// </summary>
private void ManageToolPluginForEditState()
{
try
{
if (IsInEditableState)
{
// 编辑状态确保ToolPlugin已激活
if (!_isToolPluginActive)
{
ActivateToolPlugin();
}
}
else
{
// 查看状态确保ToolPlugin已停用
if (_isToolPluginActive)
{
DeactivateToolPlugin();
}
}
}
catch (Exception ex)
{
LogManager.Error($"管理ToolPlugin状态时发生错误: {ex.Message}");
RaiseErrorOccurred($"管理ToolPlugin状态失败: {ex.Message}", ex);
}
}
/// <summary>
/// 激活自定义ToolPlugin进行精确点击检测
/// </summary>
/// <param name="subscribeToEvents">是否订阅鼠标事件</param>
private bool ActivateToolPlugin(bool subscribeToEvents = true)
{
// 如果已经激活,检查是否需要订阅事件
if (_isToolPluginActive)
{
LogManager.Debug("[ToolPlugin] ToolPlugin已激活检查事件订阅状态");
if (subscribeToEvents)
{
// 确保事件订阅(避免重复订阅)
PathClickToolPlugin.MouseClicked -= OnToolPluginMouseClicked;
PathClickToolPlugin.MouseClicked += OnToolPluginMouseClicked;
LogManager.Debug("[ToolPlugin] 已确保PathPlanningManager事件订阅");
}
return true;
}
try
{
LogManager.Debug("[ToolPlugin] ===== 开始激活ToolPlugin =====");
LogManager.Debug($"[ToolPlugin] 订阅事件: {subscribeToEvents}");
LogManager.Debug($"[ToolPlugin] 当前应用程序状态: {Application.IsAutomated}");
LogManager.Debug($"[ToolPlugin] 当前文档状态: {Application.ActiveDocument?.Title ?? "NULL"}");
// 1. 加载插件程序集
LogManager.Debug("[ToolPlugin] 步骤1: 加载插件程序集");
var assemblyPath = PathClickToolPlugin.AssemblyPath;
LogManager.Debug($"[ToolPlugin] 程序集路径: {assemblyPath}");
LogManager.Debug($"[ToolPlugin] 程序集文件是否存在: {System.IO.File.Exists(assemblyPath)}");
Application.Plugins.AddPluginAssembly(assemblyPath);
LogManager.Debug("[ToolPlugin] ✓ 程序集加载完成");
// 2. 查找插件
LogManager.Debug("[ToolPlugin] 步骤2: 查找插件");
ToolPluginRecord toolPluginRecord = (ToolPluginRecord)Application.Plugins.FindPlugin("PathClickTool.NavisworksTransport");
if (toolPluginRecord == null)
{
LogManager.Error("[ToolPlugin] ✗ 错误: 无法找到PathClickTool插件");
return false;
}
LogManager.Debug("[ToolPlugin] ✓ 插件查找成功");
// 3. 加载插件
LogManager.Debug("[ToolPlugin] 步骤3: 加载插件");
var loadedPlugin = toolPluginRecord.LoadPlugin();
if (loadedPlugin == null)
{
LogManager.Error("[ToolPlugin] ✗ 错误: 插件加载失败");
return false;
}
LogManager.Debug("[ToolPlugin] ✓ 插件加载成功");
// 4. 设置为活动工具
LogManager.Debug("[ToolPlugin] 步骤4: 设置为活动工具");
// 先重置工具状态,清除可能的导航工具
// 这一步很重要确保从导航工具如Pan、Orbit切换回自定义工具
Application.MainDocument.Tool.Value = Tool.None;
LogManager.Debug("[ToolPlugin] 已重置工具状态为None");
// 然后设置自定义工具插件
Application.MainDocument.Tool.SetCustomToolPlugin(loadedPlugin);
LogManager.Debug("[ToolPlugin] ✓ 工具设置成功");
// 5. 根据参数决定是否订阅点击事件
if (subscribeToEvents)
{
LogManager.Debug("[ToolPlugin] 步骤5: 订阅点击事件");
PathClickToolPlugin.MouseClicked += OnToolPluginMouseClicked;
LogManager.Debug("[ToolPlugin] ✓ PathPlanningManager事件订阅成功");
}
else
{
LogManager.Debug("[ToolPlugin] 步骤5: 跳过事件订阅(由外部组件管理)");
}
_isToolPluginActive = true;
LogManager.Info("[ToolPlugin] ===== ToolPlugin激活完成 =====");
return true;
}
catch (Exception ex)
{
LogManager.Error($"[ToolPlugin] 激活异常: {ex.Message}");
LogManager.Error($"[ToolPlugin] 堆栈: {ex.StackTrace}");
return false;
}
}
/// <summary>
/// 重新激活ToolPlugin供InputMonitor调用
/// 用于在工具失活后快速恢复,不重复订阅事件
/// </summary>
public void ReactivateToolPlugin()
{
try
{
// 检查当前状态和实际工具值
if ((PathEditState == PathEditState.Creating ||
PathEditState == PathEditState.AddingPoints ||
PathEditState == PathEditState.EditingPoint))
{
// 检查当前工具是否为自定义工具,如果不是则需要重新激活
var currentTool = Application.MainDocument.Tool.Value;
if (currentTool != Tool.CustomToolPlugin)
{
LogManager.Debug($"[ReactivateToolPlugin] 当前工具为{currentTool}开始重新激活ToolPlugin");
// 重要先将激活标志设置为false强制执行完整的激活流程
_isToolPluginActive = false;
// 调用现有的激活方法,但不重复订阅事件
if (ActivateToolPlugin(false))
{
LogManager.Info("[ReactivateToolPlugin] ToolPlugin重新激活成功");
RaiseStatusChanged("工具已重新激活", PathPlanningStatusType.Info);
}
else
{
LogManager.Warning("[ReactivateToolPlugin] ToolPlugin重新激活失败");
RaiseErrorOccurred("无法重新激活编辑工具");
}
}
else
{
LogManager.Debug("[ReactivateToolPlugin] 当前已是CustomToolPlugin无需重新激活");
}
}
else
{
LogManager.Debug($"[ReactivateToolPlugin] 跳过重新激活 - PathEditState: {PathEditState}");
}
}
catch (Exception ex)
{
LogManager.Error($"[ReactivateToolPlugin] 重新激活失败: {ex.Message}");
RaiseErrorOccurred($"重新激活工具失败: {ex.Message}", ex);
}
}
/// <summary>
/// 处理ToolPlugin的鼠标点击事件
/// </summary>
private void OnToolPluginMouseClicked(object sender, PickItemResult pickResult)
{
try
{
// 如果在自动路径规划模式则跳过处理应该由PathEditingViewModel处理
if (IsInAutoPathMode)
{
LogManager.Debug("[ToolPlugin事件] 当前在自动路径规划模式跳过PathPlanningManager处理");
return;
}
// 手动路径编辑处理
ProcessManualPathEditing(pickResult);
}
catch (Exception ex)
{
LogManager.Error($"[ToolPlugin事件] 处理异常: {ex.Message}");
LogManager.Error($"[ToolPlugin事件] 堆栈: {ex.StackTrace}");
}
}
/// <summary>
/// 处理手动路径编辑的鼠标点击
/// </summary>
private void ProcessManualPathEditing(PickItemResult pickResult)
{
// 检查当前选中的通道状态
LogManager.Debug($"[手动编辑] 当前_selectedChannels状态: {(_walkableAreas == null ? "NULL" : $"{_walkableAreas.Count}")}");
// 如果没有选中的通道,尝试实时搜索
if (_walkableAreas == null || _walkableAreas.Count == 0)
{
LogManager.Debug("[手动编辑] 没有预选通道,开始实时搜索可通行的物流模型");
SearchAndSetTraversableChannels();
}
// 检查是否在可通行的物流模型内并处理点击
if (_walkableAreas != null && _walkableAreas.Any())
{
bool isInTraversableLogisticsModel = IsItemInSelectedChannels(pickResult.ModelItem) ||
IsItemChildOfSelectedChannels(pickResult.ModelItem);
LogManager.Debug($"[手动编辑] 在可通行的物流模型内: {isInTraversableLogisticsModel}");
if (isInTraversableLogisticsModel)
{
// 手动路径编辑 - 根据当前模式处理点击
if (PathEditState == PathEditState.AddingPoints)
{
// 添加路径点模式 - 使用预览点
LogManager.Debug("[手动编辑] 设置预览点位置");
var previewPoint = SetPreviewPoint(pickResult.Point);
if (previewPoint != null)
{
LogManager.Debug($"[手动编辑] ✓ 预览点已设置: {previewPoint.Name}");
}
else
{
LogManager.Warning("[手动编辑] ✗ 预览点设置失败");
}
}
else if (PathEditState == PathEditState.EditingPoint)
{
// 修改路径点模式 - 设置预览位置
LogManager.Debug("[手动编辑] 设置修改路径点预览位置");
SetEditingPreviewPoint(pickResult.Point);
LogManager.Debug($"[手动编辑] ✓ 修改路径点预览位置已设置: ({pickResult.Point.X:F3}, {pickResult.Point.Y:F3}, {pickResult.Point.Z:F3})");
}
else
{
// 其他编辑模式 - 保持原有逻辑
LogManager.Debug("[手动编辑] 调用AddPathPointIn3D添加路径点");
var pathPoint = AddPathPointIn3D(pickResult.Point);
if (pathPoint != null)
{
LogManager.Debug($"[手动编辑] ✓ 路径点添加成功: {pathPoint.Name}");
}
else
{
LogManager.Warning("[手动编辑] ✗ 路径点添加失败");
}
}
}
else
{
LogManager.Debug("[手动编辑] ✗ 点击位置不在可通行的物流模型内");
RaiseErrorOccurred("点击位置不在物流通道内,请选择有效的物流路径位置");
}
}
else
{
LogManager.Warning("[手动编辑] ✗ 未找到可通行的物流模型");
RaiseErrorOccurred("未找到可通行的物流通道,请先选择或配置物流通道");
}
}
/// <summary>
/// 搜索并设置可通行的通道
/// </summary>
private void SearchAndSetTraversableChannels()
{
try
{
var document = Application.ActiveDocument;
if (document?.Models != null)
{
// 使用CategoryAttributeManager统一接口获取物流项目
var logisticsItemsCollection = CategoryAttributeManager.GetAllLogisticsItems();
LogManager.Debug($"[搜索通道] CategoryAttributeManager找到 {logisticsItemsCollection.Count} 个物流模型");
if (logisticsItemsCollection.Count > 0)
{
// 筛选出可通行的物流模型
var traversableItems = CategoryAttributeManager.FilterTraversableItems(logisticsItemsCollection);
LogManager.Debug($"[搜索通道] 筛选出 {traversableItems.Count} 个可通行的物流模型");
// 临时设置为选中通道
if (_walkableAreas == null) _walkableAreas = new List<ModelItem>();
_walkableAreas.Clear();
foreach (ModelItem item in traversableItems)
{
_walkableAreas.Add(item);
LogManager.Debug($"[搜索通道] 添加可通行模型: '{item.DisplayName}'");
}
}
}
}
catch (Exception searchEx)
{
LogManager.Error($"[搜索通道] 实时搜索失败: {searchEx.Message}");
}
}
/// <summary>
/// 获取Navisworks文档单位并转换为米的系数
/// </summary>
private double GetUnitsToMetersConversionFactor()
{
try
{
var units = Application.ActiveDocument.Units;
switch (units)
{
case Units.Millimeters:
return 0.001;
case Units.Centimeters:
return 0.01;
case Units.Meters:
return 1.0;
case Units.Inches:
return 0.0254;
case Units.Feet:
return 0.3048;
case Units.Kilometers:
return 1000.0;
case Units.Micrometers:
return 0.000001;
case Units.Microinches:
return 0.0000000254;
case Units.Mils:
return 0.0000254;
case Units.Yards:
return 0.9144;
case Units.Miles:
return 1609.43;
default:
return 1.0;
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"获取文档单位失败: {ex.Message},默认为米");
return 1.0;
}
}
/// <summary>
/// 转换包围盒到米单位
/// </summary>
private BoundingBox3D ConvertBoundingBoxToMeters(BoundingBox3D boundingBox, double conversionFactor)
{
var minPoint = new Point3D(
boundingBox.Min.X * conversionFactor,
boundingBox.Min.Y * conversionFactor,
boundingBox.Min.Z * conversionFactor
);
var maxPoint = new Point3D(
boundingBox.Max.X * conversionFactor,
boundingBox.Max.Y * conversionFactor,
boundingBox.Max.Z * conversionFactor
);
return new BoundingBox3D(minPoint, maxPoint);
}
#endregion
#region IDisposable实现
public void Dispose()
{
try
{
// 清理资源
_renderPlugin?.ClearAllPaths();
_renderPlugin = null;
// 停用ToolPlugin
if (_isToolPluginActive)
{
DeactivateToolPlugin();
}
}
catch (Exception ex)
{
LogManager.Error($"PathPlanningManager释放时发生错误: {ex.Message}");
}
}
private bool DeactivateToolPlugin()
{
if (!_isToolPluginActive)
{
LogManager.Debug("[ToolPlugin] ToolPlugin未激活无需停用");
return true;
}
try
{
// 1. 取消事件订阅
PathClickToolPlugin.MouseClicked -= OnToolPluginMouseClicked;
// 2. 重置为无活动工具状态
try
{
// 使用Tool.None重置到无活动工具状态让Navisworks处理默认导航
Application.MainDocument.Tool.Value = Tool.None;
}
catch (Exception ex)
{
LogManager.Warning($"[ToolPlugin] 重置工具状态失败: {ex.Message}");
}
_isToolPluginActive = false;
LogManager.Debug("[ToolPlugin] ===== ToolPlugin停用完成 =====");
return true;
}
catch (Exception ex)
{
LogManager.Error($"[ToolPlugin] 停用异常: {ex.Message}");
LogManager.Error($"[ToolPlugin] 堆栈: {ex.StackTrace}");
return false;
}
}
/// <summary>
/// 强制重新初始化ToolPlugin公共接口避免反射调用
/// </summary>
/// <param name="subscribeToEvents">是否订阅事件</param>
/// <returns>初始化是否成功</returns>
public bool ForceReinitializeToolPlugin(bool subscribeToEvents = false)
{
try
{
LogManager.Debug($"[工具插件重初始化] 开始强制重新初始化ToolPlugin订阅事件: {subscribeToEvents}");
// 1. 停用当前ToolPlugin
bool deactivated = DeactivateToolPlugin();
LogManager.Debug($"[工具插件重初始化] 停用结果: {deactivated}");
// 2. 强制重置激活状态
_isToolPluginActive = false;
LogManager.Debug("[工具插件重初始化] 已重置激活状态标志");
// 3. 重新激活ToolPlugin
bool activated = ActivateToolPlugin(subscribeToEvents);
LogManager.Debug($"[工具插件重初始化] 激活结果(事件订阅: {subscribeToEvents}: {activated}");
if (activated)
{
LogManager.Info("[工具插件重初始化] ToolPlugin重新初始化成功已获得鼠标焦点");
}
else
{
LogManager.Error("[工具插件重初始化] ToolPlugin激活失败");
}
return activated;
}
catch (Exception ex)
{
LogManager.Error($"[工具插件重初始化] 重新初始化ToolPlugin失败: {ex.Message}", ex);
return false;
}
}
#endregion
#region 访
/// <summary>
/// 获取PathDatabase实例
/// </summary>
public PathDatabase GetPathDatabase()
{
return _pathDatabase;
}
#endregion
#region
/// <summary>
/// 获取当前模型的边界框
/// </summary>
/// <returns>模型边界框</returns>
private BoundingBox3D GetModelBounds()
{
try
{
// 如果没有选中的可通行区域,自动搜索
if (_walkableAreas == null || _walkableAreas.Count == 0)
{
LogManager.Info("未找到预定义的可通行区域,尝试自动搜索...");
AutoSelectLogisticsChannels();
}
// 检查是否找到可通行区域
if (_walkableAreas == null || _walkableAreas.Count == 0)
{
LogManager.Error("没有找到任何可通行区域(通道、门、电梯、楼梯等)");
throw new InvalidOperationException("无法进行路径规划:模型中没有定义可通行的物流区域。请先设置物流属性。");
}
// 使用可通行区域的边界作为路径规划范围
return CalculateChannelsBounds(_walkableAreas);
}
catch (Exception ex)
{
LogManager.Error($"获取模型边界失败: {ex.Message}");
throw;
}
}
/// <summary>
/// 计算选中通道的组合边界
/// </summary>
private BoundingBox3D CalculateChannelsBounds(List<ModelItem> channels)
{
var allBounds = new List<BoundingBox3D>();
foreach (var channel in channels)
{
var bounds = channel.BoundingBox();
if (bounds != null)
{
allBounds.Add(bounds);
}
}
if (!allBounds.Any()) return null;
var minX = allBounds.Min(b => b.Min.X);
var minY = allBounds.Min(b => b.Min.Y);
var minZ = allBounds.Min(b => b.Min.Z);
var maxX = allBounds.Max(b => b.Max.X);
var maxY = allBounds.Max(b => b.Max.Y);
var maxZ = allBounds.Max(b => b.Max.Z);
return new BoundingBox3D(
new Point3D(minX, minY, minZ),
new Point3D(maxX, maxY, maxZ)
);
}
/// <summary>
/// 计算最优网格大小
/// </summary>
private double CalculateOptimalGridSize(BoundingBox3D bounds)
{
try
{
var width = bounds.Max.X - bounds.Min.X;
var height = bounds.Max.Y - bounds.Min.Y;
var maxDimension = Math.Max(width, height);
// 基于模型大小智能选择网格大小
if (maxDimension > 1000) return 5.0; // 大型模型5米
if (maxDimension > 500) return 2.0; // 中型模型2米
return 0.5; // 小型模型0.5米
}
catch
{
return 0.5; // 默认值改为0.5米与UI默认设置保持一致
}
}
/// <summary>
/// 获取通道模型项用于高度计算
/// </summary>
private IEnumerable<ModelItem> GetChannelItemsForHeightCalculation(GridMap gridMap = null)
{
var channelItems = new List<ModelItem>();
try
{
// 🔥 关键修复:优先使用网格生成时已确定的通道数据
if (gridMap?.ChannelItems != null && gridMap.ChannelItems.Any())
{
LogManager.Info($"[通道数据] 使用网格生成时的通道数据,数量: {gridMap.ChannelItems.Count()}");
channelItems.AddRange(gridMap.ChannelItems);
return channelItems;
}
// 如果用户已选择通道,优先使用用户选择的通道
if (_walkableAreas != null && _walkableAreas.Count > 0)
{
LogManager.Info($"[通道数据] 使用用户选择的通道,数量: {_walkableAreas.Count}");
channelItems.AddRange(_walkableAreas);
return channelItems;
}
// 否则使用CategoryAttributeManager搜索
LogManager.Info("[通道数据] 用户未选择通道且网格无通道数据使用CategoryAttributeManager搜索");
var document = Application.ActiveDocument;
if (document != null)
{
var foundChannelsCollection = CategoryAttributeManager.GetAllLogisticsItems();
foreach (ModelItem item in foundChannelsCollection)
{
channelItems.Add(item);
}
}
LogManager.Info($"[通道数据] 找到 {channelItems.Count} 个通道模型项");
return channelItems;
}
catch (Exception ex)
{
LogManager.Error($"[通道数据] 获取通道项失败: {ex.Message}");
return channelItems;
}
}
/// <summary>
/// 从A*算法结果创建PathRoute对象
/// </summary>
private PathRoute CreateAutoPathRoute(PathFindingResult pathResult, string routeName)
{
try
{
var route = new PathRoute(routeName);
var pathPoints = pathResult.PathPoints;
// 设置路径完成状态信息
route.IsComplete = pathResult.IsComplete;
route.OriginalEndPoint = pathResult.OriginalEndPoint;
route.ActualEndPoint = pathResult.ActualEndPoint;
route.CompletionPercentage = pathResult.CompletionPercentage;
for (int i = 0; i < pathPoints.Count; i++)
{
var point = pathPoints[i];
var pathPoint = new PathPoint
{
Name = GenerateAutoPathPointName(i, pathPoints.Count),
Position = point,
Type = DeterminePointType(i, pathPoints.Count),
SpeedLimit = GetSpeedLimitAtPosition(point)
};
route.Points.Add(pathPoint);
}
// 计算路径长度
route.RecalculateLength();
LogManager.Info($"创建自动路径: {routeName}, 点数: {pathPoints.Count}, 长度: {route.TotalLength:F2}米");
return route;
}
catch (Exception ex)
{
LogManager.Error($"创建自动路径失败: {ex.Message}");
return null;
}
}
/// <summary>
/// 根据世界坐标获取该位置的限速
/// </summary>
/// <param name="worldPosition">世界坐标</param>
/// <returns>限速值(米/秒0表示未设置限速</returns>
private double GetSpeedLimitAtPosition(Point3D worldPosition)
{
try
{
// 如果没有当前网格地图返回0
if (_currentGridMap == null)
{
return 0;
}
// 将世界坐标转换为网格坐标
var gridPos = _currentGridMap.WorldToGrid(worldPosition);
// 检查网格坐标是否有效
if (!_currentGridMap.IsValidGridPosition(gridPos))
{
return 0;
}
// 获取网格单元格
var cell = _currentGridMap.GetCell(gridPos);
return cell?.SpeedLimit ?? 0;
}
catch (Exception ex)
{
LogManager.Error($"获取位置限速失败: {ex.Message}");
return 0;
}
}
/// <summary>
/// 生成自动路径点名称
/// </summary>
private string GenerateAutoPathPointName(int index, int totalCount)
{
if (index == 0) return "自动起点";
if (index == totalCount - 1) return "自动终点";
return $"自动点{index}";
}
/// <summary>
/// 确定路径点类型
/// </summary>
private PathPointType DeterminePointType(int index, int totalCount)
{
if (index == 0) return PathPointType.StartPoint;
if (index == totalCount - 1) return PathPointType.EndPoint;
return PathPointType.WayPoint;
}
/// <summary>
/// 绘制路径可视化
/// </summary>
public void DrawRouteVisualization(PathRoute route, bool isAutoPath = false)
{
if (route == null) return;
try
{
var renderPlugin = PathPointRenderPlugin.Instance;
if (renderPlugin == null)
{
LogManager.Warning("PathPointRenderPlugin实例为空无法绘制路径");
return;
}
// 使用新的统一路径渲染API
renderPlugin.RenderPath(route);
LogManager.Info($"已渲染路径: {route.Name},包含 {route.Points.Count} 个路径点");
}
catch (Exception ex)
{
LogManager.Warning($"路径可视化失败: {ex.Message}");
}
}
#endregion
#region
/// <summary>
/// 检查项目是否在选定通道中
/// </summary>
/// <param name="item">要检查的项目</param>
/// <returns>是否在选定通道中</returns>
private bool IsItemInSelectedChannels(ModelItem item)
{
return _walkableAreas.Contains(item) || IsItemChildOfSelectedChannels(item);
}
/// <summary>
/// 检查项目是否为选定通道的子项
/// </summary>
/// <param name="item">要检查的项目</param>
/// <returns>是否为子项</returns>
private bool IsItemChildOfSelectedChannels(ModelItem item)
{
foreach (var channel in _walkableAreas)
{
if (IsChildOf(item, channel))
{
return true;
}
}
return false;
}
/// <summary>
/// 递归检查是否为子项
/// </summary>
/// <param name="child">子项</param>
/// <param name="parent">父项</param>
/// <returns>是否为子项</returns>
private bool IsChildOf(ModelItem child, ModelItem parent)
{
var currentParent = child.Parent;
while (currentParent != null)
{
if (currentParent == parent)
{
return true;
}
currentParent = currentParent.Parent;
}
return false;
}
/// <summary>
/// 状态变更通知(向后兼容方法)
/// </summary>
/// <param name="status">状态消息</param>
private void OnStatusChanged(string status)
{
try
{
// 使用新的事件触发机制
RaiseStatusChanged(status, PathPlanningStatusType.Info);
}
catch (Exception ex)
{
LogManager.Error($"[PathManager] OnStatusChanged方法异常: {ex.Message}");
LogManager.Error($"[PathManager] 状态消息: {status}");
}
}
#endregion
#region
/// <summary>
/// 查找预览点应该插入的最近线段和插入索引
/// </summary>
/// <param name="previewPosition">预览点位置</param>
/// <param name="pathPoints">路径点列表</param>
/// <returns>最近线段的前后两点以及插入索引未找到时返回null</returns>
private (PathPoint prevPoint, PathPoint nextPoint, int insertIndex)? FindNearestLineSegmentWithIndex(Point3D previewPosition, List<PathPoint> pathPoints)
{
if (pathPoints == null || pathPoints.Count < 2)
{
return null;
}
// 对路径点进行排序(按添加顺序,即列表中的索引)
var sortedPoints = pathPoints.ToList();
double minDistance = double.MaxValue;
(PathPoint prevPoint, PathPoint nextPoint, int insertIndex)? nearestSegment = null;
// 遍历相邻的路径点对,找到距离预览点最近的线段
for (int i = 0; i < sortedPoints.Count - 1; i++)
{
var currentPoint = sortedPoints[i];
var nextPoint = sortedPoints[i + 1];
// 计算预览点到线段的距离
var distance = CalculatePointToLineSegmentDistance(previewPosition, currentPoint.Position, nextPoint.Position);
if (distance < minDistance)
{
minDistance = distance;
// 插入索引应该是nextPoint的索引位置这样新点会插入到currentPoint和nextPoint之间
nearestSegment = (currentPoint, nextPoint, i + 1);
}
}
return nearestSegment;
}
/// <summary>
/// 计算点到线段的最短距离
/// </summary>
/// <param name="point">目标点</param>
/// <param name="lineStart">线段起点</param>
/// <param name="lineEnd">线段终点</param>
/// <returns>最短距离</returns>
private double CalculatePointToLineSegmentDistance(Point3D point, Point3D lineStart, Point3D lineEnd)
{
// 线段向量
var lineVector = new Point3D(lineEnd.X - lineStart.X, lineEnd.Y - lineStart.Y, lineEnd.Z - lineStart.Z);
// 点到线段起点的向量
var pointVector = new Point3D(point.X - lineStart.X, point.Y - lineStart.Y, point.Z - lineStart.Z);
// 计算线段长度的平方
var lineLengthSquared = lineVector.X * lineVector.X + lineVector.Y * lineVector.Y + lineVector.Z * lineVector.Z;
if (lineLengthSquared == 0)
{
// 线段退化为点,返回点到点的距离
return CalculateDistance(point, lineStart);
}
// 计算投影参数t
var t = (pointVector.X * lineVector.X + pointVector.Y * lineVector.Y + pointVector.Z * lineVector.Z) / lineLengthSquared;
// 将t限制在[0,1]范围内
t = Math.Max(0, Math.Min(1, t));
// 计算线段上最近点
var closestPoint = new Point3D(
lineStart.X + t * lineVector.X,
lineStart.Y + t * lineVector.Y,
lineStart.Z + t * lineVector.Z
);
// 返回点到最近点的距离
return CalculateDistance(point, closestPoint);
}
/// <summary>
/// 计算两点间距离
/// </summary>
/// <param name="point1">第一个点</param>
/// <param name="point2">第二个点</param>
/// <returns>两点间距离</returns>
private double CalculateDistance(Point3D point1, Point3D point2)
{
var dx = point1.X - point2.X;
var dy = point1.Y - point2.Y;
var dz = point1.Z - point2.Z;
return Math.Sqrt(dx * dx + dy * dy + dz * dz);
}
#endregion
#region
/// <summary>
/// 更新网格可视化设置
/// </summary>
/// <param name="showWalkable">是否显示可通行网格点</param>
/// <param name="showObstacle">是否显示障碍物网格点</param>
/// <param name="showUnknown">是否显示未知区域网格点</param>
/// <param name="showDoor">是否显示门网格点</param>
public void UpdateGridVisualizationSettings(bool showWalkable, bool showObstacle, bool showUnknown, bool showDoor = false)
{
try
{
LogManager.Info($"[网格可视化设置] 更新设置: 通行={showWalkable}, 障碍物={showObstacle}, 未知={showUnknown}, 门={showDoor}");
_showWalkableGrid = showWalkable;
_showObstacleGrid = showObstacle;
_showUnknownGrid = showUnknown;
_showDoorGrid = showDoor;
// 如果当前有网格正在显示,重新应用可视化
RefreshGridVisualization();
LogManager.Info("[网格可视化设置] 设置更新完成");
}
catch (Exception ex)
{
LogManager.Error($"[网格可视化设置] 更新设置失败: {ex.Message}", ex);
}
}
/// <summary>
/// 检查是否有任何网格可视化已启用
/// </summary>
public bool IsAnyGridVisualizationEnabled
{
get { return _showWalkableGrid || _showObstacleGrid || _showUnknownGrid || _showDoorGrid; }
}
/// <summary>
/// 刷新网格可视化(根据当前设置重新显示)
/// </summary>
private void RefreshGridVisualization()
{
try
{
// 清除当前网格可视化
ClearGridVisualization();
// 如果存在缓存的网格地图且有网格可视化设置启用,重新进行可视化
if (_currentGridMap != null && IsAnyGridVisualizationEnabled)
{
LogManager.Info("[网格可视化] 使用缓存的网格地图重新渲染");
VisualizeGridCells(_currentGridMap, _currentVehicleHeight);
}
else if (_currentGridMap == null)
{
LogManager.Info("[网格可视化] 无缓存网格地图,需要重新进行路径规划以查看网格");
}
else
{
LogManager.Info("[网格可视化] 网格可视化设置已关闭,跳过渲染");
}
}
catch (Exception ex)
{
LogManager.Error($"[网格可视化] 刷新失败: {ex.Message}", ex);
}
}
/// <summary>
/// 在3D视图中可视化网格中的可通行单元格
/// 在每个可通行网格的中心绘制一个绿色小球
/// </summary>
/// <param name="gridMap">要可视化的网格地图</param>
/// <param name="vehicleHeight">车辆高度(米),用于判断层高是否足够</param>
public void VisualizeGridCells(GridMap gridMap, double vehicleHeight = 2.0)
{
if (gridMap == null)
{
LogManager.Warning("[网格可视化] 网格地图为null无法进行可视化");
return;
}
try
{
// 保存当前网格地图和车辆高度用于后续刷新
_currentGridMap = gridMap;
_currentVehicleHeight = vehicleHeight;
LogManager.Info($"[网格可视化] 开始可视化网格:{gridMap.Width}x{gridMap.Height}, 车辆高度:{vehicleHeight}m");
// 获取渲染插件
var renderPlugin = PathPointRenderPlugin.Instance;
if (renderPlugin == null)
{
LogManager.Warning("[网格可视化] PathPointRenderPlugin实例为空无法绘制网格");
return;
}
// 设置网格大小以实现自适应点大小
// gridMap.CellSize是模型单位需要转换为米
double gridSizeInMeters = UnitsConverter.ConvertToMeters(gridMap.CellSize);
renderPlugin.SetGridSize(gridSizeInMeters);
// 清除之前的网格可视化
ClearGridVisualization();
// 创建四种独立的网格可视化路径
var channelRoute = new PathRoute("GridVisualization_Channel")
{
Id = "grid_visualization_channel"
};
var unknownRoute = new PathRoute("GridVisualization_Unknown")
{
Id = "grid_visualization_unknown"
};
var obstacleRoute = new PathRoute("GridVisualization_Obstacle")
{
Id = "grid_visualization_obstacle"
};
var doorRoute = new PathRoute("GridVisualization_Door")
{
Id = "grid_visualization_door"
};
// 统计变量
int channelCells = 0;
int unknownCells = 0;
int obstacleCells = 0;
int doorCells = 0;
int totalVisualized = 0;
int multiLayerCells = 0;
// 遍历所有网格单元格,根据类型分别收集
for (int x = 0; x < gridMap.Width; x++)
{
for (int y = 0; y < gridMap.Height; y++)
{
var cell = gridMap.Cells[x, y];
// 计算网格单元格的XY中心位置Z坐标在多层逻辑中单独处理
var gridPos = new NavisworksTransport.PathPlanning.GridPoint2D(x, y);
var gridCorner = gridMap.GridToWorld3D(gridPos);
double centerX = gridCorner.X + gridMap.CellSize / 2;
double centerY = gridCorner.Y + gridMap.CellSize / 2;
// 所有网格都基于高度层,统一处理
if (cell.HeightLayers != null && cell.HeightLayers.Count > 0)
{
multiLayerCells++;
foreach (var layer in cell.HeightLayers)
{
double layerZ = layer.Z;
PathRoute targetRoute = null;
string gridTypeName = "";
// 仅根据 layer.IsWalkable 决定渲染
if (layer.IsWalkable)
{
// 可通行层 - 门特殊处理,其他统一样式
if (cell.CellType == CategoryAttributeManager.LogisticsElementType.)
{
if (_showDoorGrid)
{
targetRoute = doorRoute;
gridTypeName = "门";
doorCells++;
}
}
else
{
// 其他可通行类型统一为通道样式(绿色)
if (_showWalkableGrid)
{
targetRoute = channelRoute;
gridTypeName = "通道";
channelCells++;
}
}
}
else
{
// 不可通行层 - 渲染为障碍物(灰色)
if (_showObstacleGrid)
{
targetRoute = obstacleRoute;
gridTypeName = "障碍";
obstacleCells++;
}
}
// 创建该层的可视化点
if (targetRoute != null)
{
var gridCenter = new Point3D(centerX, centerY, layerZ);
var gridPoint = new PathPoint
{
Position = gridCenter,
Name = $"网格_{gridTypeName}({x},{y})_Z{layerZ:F2}",
Type = PathPointType.WayPoint,
Index = totalVisualized,
Notes = $"GridType:{cell.CellType}, LayerZ={layerZ:F2}, IsWalkable={layer.IsWalkable}"
};
targetRoute.Points.Add(gridPoint);
totalVisualized++;
}
}
}
else if (cell.CellType == CategoryAttributeManager.LogisticsElementType.Unknown)
{
// Unknown网格没有高度层用于调试
if (_showUnknownGrid)
{
var gridCenter = new Point3D(centerX, centerY, gridCorner.Z);
var gridPoint = new PathPoint
{
Position = gridCenter,
Name = $"网格_空洞({x},{y})",
Type = PathPointType.WayPoint,
Index = totalVisualized,
Notes = $"GridType:Unknown, NoLayers"
};
unknownRoute.Points.Add(gridPoint);
unknownCells++;
totalVisualized++;
}
}
}
}
// 分别渲染四种类型的网格点
if (channelRoute.Points.Count > 0)
{
renderPlugin.RenderPointOnly(channelRoute);
LogManager.Info($"[网格可视化] 渲染可通行网格层: {channelRoute.Points.Count} 个(绿色)");
}
if (doorRoute.Points.Count > 0)
{
renderPlugin.RenderPointOnly(doorRoute);
LogManager.Info($"[网格可视化] 渲染门网格层: {doorRoute.Points.Count} 个50%透明绿色)");
}
if (unknownRoute.Points.Count > 0)
{
renderPlugin.RenderPointOnly(unknownRoute);
LogManager.Info($"[网格可视化] 渲染Unknown网格: {unknownRoute.Points.Count} 个(红色,调试用)");
}
if (obstacleRoute.Points.Count > 0)
{
renderPlugin.RenderPointOnly(obstacleRoute);
LogManager.Info($"[网格可视化] 渲染障碍物网格层: {obstacleRoute.Points.Count} 个(灰色)");
}
// 输出统计信息
LogManager.Info($"[网格可视化] 可视化完成:");
LogManager.Info($" - 总网格单元格数:{gridMap.Width * gridMap.Height}");
LogManager.Info($" - 多层网格数:{multiLayerCells}");
LogManager.Info($" - 可通行层数:{channelCells}");
LogManager.Info($" - 门层数:{doorCells}");
LogManager.Info($" - Unknown单元格数{unknownCells}");
LogManager.Info($" - 障碍物层数:{obstacleCells}");
LogManager.Info($" - 已可视化点数:{totalVisualized}");
RaiseStatusChanged($"网格可视化完成:显示了 {totalVisualized} 个网格层 (多层网格:{multiLayerCells}, 可通行:{channelCells}, 门:{doorCells}, Unknown:{unknownCells}, 障碍:{obstacleCells})", PathPlanningStatusType.Success);
}
catch (Exception ex)
{
LogManager.Error($"[网格可视化] 可视化失败: {ex.Message}");
RaiseErrorOccurred($"网格可视化失败: {ex.Message}", ex);
}
}
/// <summary>
/// 清除网格可视化
/// </summary>
public void ClearGridVisualization()
{
try
{
LogManager.Info("[网格可视化] 开始清除网格可视化");
var renderPlugin = PathPointRenderPlugin.Instance;
if (renderPlugin != null)
{
// 清除所有网格可视化路径(包括旧的和新的)
renderPlugin.RemovePath("grid_visualization_all"); // 旧的单一路径
renderPlugin.RemovePath("grid_visualization_channel"); // 新的通道路径
renderPlugin.RemovePath("grid_visualization_unknown"); // 新的Unknown路径
renderPlugin.RemovePath("grid_visualization_obstacle"); // 新的障碍物路径
renderPlugin.RemovePath("grid_visualization_door"); // 新的门路径
LogManager.Info("[网格可视化] 网格可视化已清除");
}
else
{
LogManager.Warning("[网格可视化] PathPointRenderPlugin实例为空无法清除可视化");
}
}
catch (Exception ex)
{
LogManager.Error($"[网格可视化] 清除可视化失败: {ex.Message}");
}
}
#endregion
#region
/// <summary>
/// 获取所有路径(供分析使用)
/// </summary>
public List<PathRoute> GetAllRoutes()
{
try
{
// 直接返回内存中的路径列表
// 因为所有路径操作都已同步到数据库,内存列表就是当前的完整列表
return _routes ?? new List<PathRoute>();
}
catch (Exception ex)
{
LogManager.Error($"获取所有路径失败: {ex.Message}", ex);
return new List<PathRoute>();
}
}
/// <summary>
/// 分析多条路径并进行对比
/// </summary>
/// <param name="routeIds">要分析的路径ID列表</param>
/// <param name="strategy">分析策略</param>
/// <returns>对比结果</returns>
public PathComparisonResult AnalyzePathsAndCompare(List<string> routeIds, string strategy)
{
try
{
if (_analysisService == null)
{
LogManager.Error("路径分析服务未初始化");
return null;
}
// 获取要分析的路径
var routesToAnalyze = _routes?.Where(r => routeIds.Contains(r.Id)).ToList();
if (routesToAnalyze == null || routesToAnalyze.Count == 0)
{
LogManager.Warning("未找到要分析的路径");
return null;
}
// 执行分析和对比
var result = _analysisService.CompareRoutes(routesToAnalyze, strategy);
return result;
}
catch (Exception ex)
{
LogManager.Error($"路径分析失败: {ex.Message}", ex);
return null;
}
}
/// <summary>
/// 从数据库加载历史路径到内存
/// </summary>
private void LoadHistoricalRoutesFromDatabase()
{
try
{
if (_pathDatabase == null)
{
LogManager.Info("数据库未初始化,跳过加载历史路径");
return;
}
var historicalRoutes = _pathDatabase.GetAllPathRoutes();
if (historicalRoutes != null && historicalRoutes.Count > 0)
{
// 使用HashSet记录已存在的路径ID避免重复
var existingIds = new HashSet<string>(_routes.Select(r => r.Id));
int loadedCount = 0;
// 加载历史路径(跳过已存在的)
foreach (var route in historicalRoutes)
{
if (!existingIds.Contains(route.Id))
{
_routes.Add(route);
loadedCount++;
//LogManager.Info($"加载历史路径: {route.Name}, ID={route.Id}, 长度={route.TotalLength:F2}米");
}
}
if (loadedCount > 0)
{
LogManager.Info($"成功从数据库加载 {loadedCount} 条历史路径(总计 {historicalRoutes.Count} 条)");
}
else
{
LogManager.Info($"数据库中的 {historicalRoutes.Count} 条路径已全部在内存中");
}
// 如果当前没有选中的路径,且路径列表不为空,选择第一条
if ((_currentRoute == null || string.IsNullOrEmpty(_currentRoute.Name) || _currentRoute.Name == "默认路径")
&& _routes.Count > 0)
{
_currentRoute = _routes[0];
LogManager.Info($"设置当前路径为: {_currentRoute.Name}");
}
// 历史路径加载完成后触发RouteGenerated事件通知UI刷新
// 使用特殊的生成方法标记这是数据库加载
foreach (var route in _routes)
{
RaiseRouteGenerated(route, RouteGenerationMethod.DatabaseLoad);
}
LogManager.Info($"已触发{_routes.Count}个RouteGenerated事件通知UI刷新历史路径");
}
else
{
LogManager.Info("数据库中没有历史路径记录");
}
}
catch (Exception ex)
{
LogManager.Error($"加载历史路径失败: {ex.Message}", ex);
}
}
/// <summary>
/// 保存路径时同时保存到数据库
/// </summary>
private void SavePathToDatabase(PathRoute route)
{
try
{
if (_pathDatabase != null && route != null)
{
_pathDatabase.SavePathRoute(route);
LogManager.Info($"路径已保存到数据库: {route.Name}");
}
}
catch (Exception ex)
{
LogManager.Error($"保存路径到数据库失败: {ex.Message}", ex);
}
}
/// <summary>
/// 获取Instance单例为兼容现有代码
/// </summary>
public static PathPlanningManager Instance
{
get
{
if (_activePathManager == null)
{
LogManager.Warning("PathPlanningManager未初始化创建新实例");
_activePathManager = new PathPlanningManager();
}
return _activePathManager;
}
}
#endregion
#region
/// <summary>
/// 静态方法:获取当前活动的路径管理器
/// </summary>
/// <returns>当前活动的路径管理器</returns>
public static PathPlanningManager GetActivePathManager()
{
return _activePathManager;
}
#endregion
}
}