using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows;
using System.Windows.Input;
using System.Threading.Tasks;
using System.Linq;
using System.IO;
using System.Numerics;
using Microsoft.Win32;
using Autodesk.Navisworks.Api;
using NavisApplication = Autodesk.Navisworks.Api.Application;
using NavisworksTransport.Core;
using NavisworksTransport.Core.Config;
using NavisworksTransport.Commands;
using NavisworksTransport.UI.WPF.Views;
using NavisworksTransport.UI.WPF.Models;
using NavisworksTransport.Utils;
using NavisworksTransport.Utils.CoordinateSystem;
using NavisworksTransport.Utils.GeometryAnalysis;
namespace NavisworksTransport.UI.WPF.ViewModels
{
///
/// 路径策略选项辅助类
///
public class PathStrategyOption
{
///
/// 策略值
///
public PathStrategy Value { get; set; }
///
/// 显示名称
///
public string DisplayName { get; set; }
///
/// 详细描述
///
public string Description { get; set; }
}
///
/// Rail 构型选项辅助类
///
/// 枚举类型
public class RailConfigOption
{
///
/// 配置值
///
public T Value { get; set; }
///
/// 显示名称
///
public string DisplayName { get; set; }
///
/// 详细描述
///
public string Description { get; set; }
}
///
/// 路径编辑页面专用ViewModel
///
public class PathEditingViewModel : ViewModelBase, IDisposable
{
#region 私有字段
private PathPlanningManager _pathPlanningManager;
private readonly UIStateManager _uiStateManager;
private readonly PathDataManager _pathDataManager;
// 路径集合
private ObservableCollection _pathRoutes;
private PathRouteViewModel _selectedPathRoute;
private PathPointViewModel _selectedPathPoint;
private bool _isPathEditMode;
// 自动路径规划参数
private string _autoPathStartPoint = "未选择";
private string _autoPathEndPoint = "未选择";
private Point3D _startPoint3D;
private Point3D _endPoint3D;
private bool _hasStartPoint = false;
private bool _hasEndPoint = false;
private bool _isSelectingStartPoint = false;
private bool _isSelectingEndPoint = false;
// 物体参数 - 改为三个独立参数(从配置初始化)
private double _objectLength; // 物体长度(米)
private double _objectWidth; // 物体宽度(米)
private double _objectHeight; // 物体高度(米)
private double _safetyMargin; // 安全间隙(米)
// 网格大小参数(从配置初始化)
private bool _isGridSizeManuallyEnabled = false; // 是否启用手动网格大小设置
private double _gridSize; // 网格大小(米)
// 可视化参数
private bool _showWalkableGrid = false;
private bool _showObstacleGrid = false;
private bool _showUnknownGrid = false;
private bool _showDoorGrid = false;
private GridPointType _gridPointType = GridPointType.Rectangle;
// 三种渲染模式独立开关
private bool _showControlVisualization = true; // 默认显示控制点连线
private bool _showPathLines = true; // 默认显示路径线(地面连线)
private bool _showObjectSpace = false; // 默认不显示通行空间
// 渲染模式已改为独立开关(_showControlVisualization, _showPathLines, _showObjectSpace)
// 下面的互斥属性已废弃并删除
// 路径策略参数
private PathStrategyOption _selectedPathStrategy;
private ObservableCollection _pathStrategyOptions;
private ObservableCollection> _railMountModeOptions;
private const double DefaultAssemblyReferenceRodLengthInMeters = 20.0;
private static readonly double DefaultAssemblyReferenceRodDiameterInMeters = CalculateDefaultAssemblyReferenceRodDiameterInMeters();
private const double DefaultAssemblyAnchorVerticalOffsetInMeters = 0.0;
private const double AssemblyVisualizationPointDiameterInMeters = 0.05;
private const double RailNormalOffsetNudgeStepInMeters = 0.1;
private const double DefaultAssemblySphereCenterX = 0.0;
private const double DefaultAssemblySphereCenterY = 0.0;
private const double DefaultAssemblySphereCenterZ = 0.0;
private readonly RailAssemblyWorkflowContext _railAssemblyContext = new RailAssemblyWorkflowContext(
DefaultAssemblyReferenceRodLengthInMeters,
DefaultAssemblyReferenceRodDiameterInMeters,
DefaultAssemblyAnchorVerticalOffsetInMeters,
DefaultAssemblySphereCenterX,
DefaultAssemblySphereCenterY,
DefaultAssemblySphereCenterZ);
private const string AssemblyAnchorMarkerPathId = "assembly_anchor_marker";
private const string AssemblyCenterGuideLinePathId = "assembly_center_guide_line";
private const string AssemblyReferenceLinePathId = "assembly_reference_line";
private const string AssemblyEndFaceSeedPathId = "assembly_end_face_seed_points";
private const string AssemblyEndFaceCenterPathId = "assembly_end_face_center";
private const string AssemblyEndFaceNormalPathId = "assembly_end_face_normal";
private const string AssemblyInstallationPickPointPathId = "assembly_install_pick_point";
private const string AssemblyInstallationAnchorPointPathId = "assembly_install_anchor_point";
private const string AssemblyInstallationCenterLinePathId = "assembly_install_center_line";
private const string AssemblyInstallationOffsetLinePathId = "assembly_install_offset_line";
private const string AssemblyInstallationPlanePathId = "assembly_install_plane";
private const double AssemblyInstallationPlanePaddingInMeters = 0.5;
private const double AssemblyInstallationLineLengthScale = 1.2;
private string _assemblyTerminalObjectName
{
get => _railAssemblyContext.TerminalObjectName;
set => _railAssemblyContext.TerminalObjectName = value;
}
private string _assemblyTerminalObjectInfo
{
get => _railAssemblyContext.TerminalObjectInfo;
set => _railAssemblyContext.TerminalObjectInfo = value;
}
private double _assemblyReferenceRodLengthInMeters
{
get => _railAssemblyContext.ReferenceRodLengthInMeters;
set => _railAssemblyContext.ReferenceRodLengthInMeters = value;
}
private double _assemblyReferenceRodDiameterInMeters
{
get => _railAssemblyContext.ReferenceRodDiameterInMeters;
set => _railAssemblyContext.ReferenceRodDiameterInMeters = value;
}
private double _assemblyAnchorVerticalOffsetInMeters
{
get => _railAssemblyContext.AnchorVerticalOffsetInMeters;
set => _railAssemblyContext.AnchorVerticalOffsetInMeters = value;
}
private double _assemblySphereCenterX
{
get => _railAssemblyContext.SphereCenterX;
set => _railAssemblyContext.SphereCenterX = value;
}
private double _assemblySphereCenterY
{
get => _railAssemblyContext.SphereCenterY;
set => _railAssemblyContext.SphereCenterY = value;
}
private double _assemblySphereCenterZ
{
get => _railAssemblyContext.SphereCenterZ;
set => _railAssemblyContext.SphereCenterZ = value;
}
private string _assemblyStartPointText
{
get => _railAssemblyContext.StartPointText;
set => _railAssemblyContext.StartPointText = value;
}
private RailMountMode _assemblyMountMode
{
get => _railAssemblyContext.MountMode;
set => _railAssemblyContext.MountMode = value;
}
private bool _hasAssemblyTerminalObject
{
get => _railAssemblyContext.HasTerminalObject;
set => _railAssemblyContext.HasTerminalObject = value;
}
private bool _isSelectingAssemblyStartPoint
{
get => _railAssemblyContext.IsSelectingStartPoint;
set => _railAssemblyContext.IsSelectingStartPoint = value;
}
private bool _isSelectingAssemblyEndFacePoints
{
get => _railAssemblyContext.IsSelectingEndFacePoints;
set => _railAssemblyContext.IsSelectingEndFacePoints = value;
}
private bool _isSelectingAssemblyInstallationPoint
{
get => _railAssemblyContext.IsSelectingInstallationPoint;
set => _railAssemblyContext.IsSelectingInstallationPoint = value;
}
private RailAssemblyWorkflowMode _railAssemblyWorkflowMode
{
get => _railAssemblyContext.WorkflowMode;
set => _railAssemblyContext.WorkflowMode = value;
}
private bool _hasAssemblyEndFaceAnalysis
{
get => _railAssemblyContext.HasEndFaceAnalysis;
set => _railAssemblyContext.HasEndFaceAnalysis = value;
}
private bool _hasAssemblyInstallationReference
{
get => _railAssemblyContext.HasInstallationReference;
set => _railAssemblyContext.HasInstallationReference = value;
}
private ModelItem _assemblyTerminalObject
{
get => _railAssemblyContext.TerminalObject;
set => _railAssemblyContext.TerminalObject = value;
}
private Point3D _assemblyStartPoint
{
get => _railAssemblyContext.StartPoint;
set => _railAssemblyContext.StartPoint = value;
}
private Point3D _assemblyEndFaceCenterPoint
{
get => _railAssemblyContext.EndFaceCenterPoint;
set => _railAssemblyContext.EndFaceCenterPoint = value;
}
private Vector3 _assemblyEndFaceNormal
{
get => _railAssemblyContext.EndFaceNormal;
set => _railAssemblyContext.EndFaceNormal = value;
}
private Point3D _assemblyInstallationPickPoint
{
get => _railAssemblyContext.InstallationPickPoint;
set => _railAssemblyContext.InstallationPickPoint = value;
}
private Point3D _assemblyInstallationBaseAnchorPoint
{
get => _railAssemblyContext.InstallationBaseAnchorPoint;
set => _railAssemblyContext.InstallationBaseAnchorPoint = value;
}
private Point3D _assemblyInstallationAnchorPoint
{
get => _railAssemblyContext.InstallationAnchorPoint;
set => _railAssemblyContext.InstallationAnchorPoint = value;
}
private Vector3 _assemblyInstallationPlaneNormal
{
get => _railAssemblyContext.InstallationPlaneNormal;
set => _railAssemblyContext.InstallationPlaneNormal = value;
}
private Vector3 _assemblyInstallationPlaneSpanDirection
{
get => _railAssemblyContext.InstallationPlaneSpanDirection;
set => _railAssemblyContext.InstallationPlaneSpanDirection = value;
}
private double _assemblyInstallationOffsetDistanceInMeters
{
get => _railAssemblyContext.InstallationOffsetDistanceInMeters;
set => _railAssemblyContext.InstallationOffsetDistanceInMeters = value;
}
private string _assemblyInstallationReferenceRouteId
{
get => _railAssemblyContext.InstallationReferenceRouteId;
set => _railAssemblyContext.InstallationReferenceRouteId = value;
}
private List _assemblyInstallationSeedPoints => _railAssemblyContext.InstallationSeedPoints;
private List _assemblyEndFaceSeedPoints => _railAssemblyContext.EndFaceSeedPoints;
// 自动路径起点和终点的路径对象引用(用于正确的ID管理)
private PathRoute _autoPathStartPointRoute = null;
private PathRoute _autoPathEndPointRoute = null;
// 资源清理管理
private bool _disposed = false;
// 多层吊装模式
private bool _isMultiLevelHoistingMode = false;
private ObservableCollection _multiLevelItems = new ObservableCollection();
private Point3D _multiLevelStartPoint;
private Point3D _multiLevelEndPoint;
private bool _hasMultiLevelStartPoint = false;
private bool _hasMultiLevelEndPoint = false;
private string _multiLevelStartPointText = "未选择";
private string _multiLevelEndPointText = "未选择";
private string _multiLevelStatsText = "层级数: 0 | 预估路径点数: 0";
private bool _isSelectingMultiLevelStartPoint = false;
private bool _isSelectingMultiLevelEndPoint = false;
private HoistingLevelItem _currentSelectingLevelTarget;
#endregion
#region 公共属性
public ObservableCollection PathRoutes
{
get => _pathRoutes;
set => SetProperty(ref _pathRoutes, value);
}
public PathRouteViewModel SelectedPathRoute
{
get => _selectedPathRoute;
set
{
if (SetProperty(ref _selectedPathRoute, value))
{
// 路径变更时清除选中的路径点
SelectedPathPoint = null;
ResetRailAssemblyWorkflowState(announce: false);
// 🔧 修复:同步PathPlanningManager的CurrentRoute
if (_pathPlanningManager != null && value != null)
{
// 查找对应的Core路径对象
var coreRoute = _pathPlanningManager.Routes?.FirstOrDefault(r => r.Id == value.Id)
?? _pathPlanningManager.Routes?.FirstOrDefault(r => r.Name == value.Name);
if (coreRoute != null)
{
if (coreRoute.PathType == PathType.Rail)
{
AssemblyMountMode = coreRoute.RailMountMode;
_assemblyAnchorVerticalOffsetInMeters = UnitsConverter.ConvertToMeters(coreRoute.RailNormalOffset);
OnPropertyChanged(nameof(AssemblyAnchorVerticalOffsetInMeters));
}
// 🔥 先设置可视化模式(在 SetCurrentRoute 之前,确保事件触发时状态已就绪)
UpdatePassageSpaceVisualizationForPathType(coreRoute.PathType);
_pathPlanningManager.SetCurrentRoute(coreRoute);
LogManager.Info($"UI路径切换:已同步PathPlanningManager的CurrentRoute到 {value.Name}");
// 自动调整视角到路径中心(只有路径有点时才调整)
if (coreRoute.Points.Count > 0)
{
try
{
ViewpointHelper.AdjustViewpointForPath(coreRoute);
LogManager.Info($"UI路径切换:已自动调整视角: {value.Name}, 类型={coreRoute.PathType}");
}
catch (Exception ex)
{
LogManager.Error($"UI路径切换:调整视角失败: {ex.Message}");
}
}
else
{
LogManager.Info($"UI路径切换:路径还没有点,跳过视角调整: {value.Name}");
}
}
else
{
LogManager.Warning($"UI路径切换:未找到对应的Core路径 {value.Name}");
}
}
else if (_pathPlanningManager != null && value == null)
{
// 如果选择了null,也要同步清除CurrentRoute
_pathPlanningManager.SetCurrentRoute(null);
LogManager.Info("UI路径切换:已清除PathPlanningManager的CurrentRoute");
}
// 更新命令状态
OnPropertyChanged(nameof(CanExecuteStartEdit));
OnPropertyChanged(nameof(CanExecuteEndEdit));
OnPropertyChanged(nameof(CanExecuteClearPath));
OnPropertyChanged(nameof(CanExecuteExportPath));
OnPropertyChanged(nameof(CanExecuteSaveAsPath));
OnPropertyChanged(nameof(CanExecuteModifyPoint));
// 🔥 路径类型改变时,检查是否可以使用路径线
OnPropertyChanged(nameof(CanUsePathLines));
OnPropertyChanged(nameof(IsRailRouteSelected));
OnPropertyChanged(nameof(SelectedRailMountMode));
OnPropertyChanged(nameof(SelectedRailNormalOffsetInMeters));
OnPropertyChanged(nameof(CanRepositionRailStartPoint));
NotifyRailAssemblyCommandStateChanged();
if (!CanUsePathLines && ShowPathLines)
{
// 吊装和空轨路径不能使用路径线,自动关闭
ShowPathLines = false;
LogManager.Info("[路径可视化] 切换到吊装/空轨路径,自动关闭路径线");
}
// 实现路径选择时的可视化切换
UpdatePathVisualization();
}
}
}
public PathPointViewModel SelectedPathPoint
{
get => _selectedPathPoint;
set
{
if (SetProperty(ref _selectedPathPoint, value))
{
// 更新修改路径点按钮的启用状态
OnPropertyChanged(nameof(CanExecuteModifyPoint));
// 路径点选中可视化:将选中的路径点显示为预览样式
try
{
if (value != null)
{
// 从PathPointViewModel创建PathPoint对象
var pathPoint = new PathPoint(
new Point3D(value.X, value.Y, value.Z),
value.Name,
value.Type
)
{
Id = value.Id
};
// 将选中的路径点设为预览样式
PathPointRenderPlugin.Instance.RenderPreviewPoint(pathPoint);
LogManager.Info($"路径点已设为预览样式: {value.Name}");
}
else
{
// 清除预览样式
PathPointRenderPlugin.Instance.ClearPreviewPoint();
LogManager.Info("已清除路径点预览样式");
}
}
catch (Exception ex)
{
LogManager.Error($"路径点选中可视化失败: {ex.Message}");
}
}
}
}
public bool IsRailRouteSelected => SelectedPathRoute?.PathType == PathType.Rail;
public ObservableCollection> RailMountModeOptions
{
get => _railMountModeOptions;
}
public RailMountMode SelectedRailMountMode
{
get => GetSelectedCoreRoute()?.RailMountMode ?? RailMountMode.UnderRail;
set
{
var coreRoute = GetSelectedCoreRoute();
if (coreRoute == null || coreRoute.PathType != PathType.Rail || coreRoute.RailMountMode == value)
{
return;
}
coreRoute.RailMountMode = value;
AssemblyMountMode = value;
ApplyRailRouteConfigurationChange(coreRoute, $"安装方式已更新为 {value}");
OnPropertyChanged(nameof(SelectedRailMountMode));
}
}
public double SelectedRailNormalOffsetInMeters
{
get
{
var coreRoute = GetSelectedCoreRoute();
if (coreRoute == null || coreRoute.PathType != PathType.Rail)
{
return 0.0;
}
return UnitsConverter.ConvertToMeters(coreRoute.RailNormalOffset);
}
set
{
var coreRoute = GetSelectedCoreRoute();
if (coreRoute == null || coreRoute.PathType != PathType.Rail)
{
return;
}
double offsetInModelUnits = UnitsConverter.ConvertFromMeters(value);
if (Math.Abs(coreRoute.RailNormalOffset - offsetInModelUnits) < 1e-6)
{
return;
}
_assemblyAnchorVerticalOffsetInMeters = value;
OnPropertyChanged(nameof(AssemblyAnchorVerticalOffsetInMeters));
coreRoute.RailNormalOffset = offsetInModelUnits;
if (IsAssemblyInstallationReferenceActiveForRoute(coreRoute))
{
UpdateAssemblyInstallationAnchorFromVerticalOffset();
PersistAssemblyInstallationReferenceToRailRoute(coreRoute, $"安装法向偏移已更新为 {value:F3} 米");
}
else
{
ApplyRailRouteConfigurationChange(coreRoute, $"安装法向偏移已更新为 {value:F3} 米");
}
OnPropertyChanged(nameof(SelectedRailNormalOffsetInMeters));
}
}
public string AssemblyTerminalObjectName
{
get => _assemblyTerminalObjectName;
set
{
if (string.Equals(_assemblyTerminalObjectName, value, StringComparison.Ordinal))
{
return;
}
_assemblyTerminalObjectName = value;
OnPropertyChanged(nameof(AssemblyTerminalObjectName));
}
}
public string AssemblyTerminalObjectInfo
{
get => _assemblyTerminalObjectInfo;
set
{
if (string.Equals(_assemblyTerminalObjectInfo, value, StringComparison.Ordinal))
{
return;
}
_assemblyTerminalObjectInfo = value;
OnPropertyChanged(nameof(AssemblyTerminalObjectInfo));
}
}
public double AssemblyReferenceRodLengthInMeters
{
get => _assemblyReferenceRodLengthInMeters;
set
{
if (Math.Abs(_assemblyReferenceRodLengthInMeters - value) < 1e-9)
{
return;
}
_assemblyReferenceRodLengthInMeters = value;
OnPropertyChanged(nameof(AssemblyReferenceRodLengthInMeters));
}
}
public double AssemblyReferenceRodDiameterInMeters
{
get => _assemblyReferenceRodDiameterInMeters;
set
{
if (Math.Abs(_assemblyReferenceRodDiameterInMeters - value) < 1e-9)
{
return;
}
_assemblyReferenceRodDiameterInMeters = value;
OnPropertyChanged(nameof(AssemblyReferenceRodDiameterInMeters));
}
}
public double AssemblyAnchorVerticalOffsetInMeters
{
get => _assemblyAnchorVerticalOffsetInMeters;
set
{
if (Math.Abs(_assemblyAnchorVerticalOffsetInMeters - value) < 1e-9)
{
return;
}
_assemblyAnchorVerticalOffsetInMeters = value;
OnPropertyChanged(nameof(AssemblyAnchorVerticalOffsetInMeters));
if (HasAssemblyTerminalObject)
{
var coreRoute = GetSelectedCoreRoute();
if (_hasAssemblyInstallationReference)
{
UpdateAssemblyInstallationAnchorFromVerticalOffset();
if (IsAssemblyInstallationReferenceActiveForRoute(coreRoute))
{
PersistAssemblyInstallationReferenceToRailRoute(coreRoute, $"安装法向偏移已更新为 {value:F3} 米");
}
}
RefreshAssemblyTerminalObjectInfo();
if (coreRoute != null &&
coreRoute.PathType == PathType.Rail &&
AssemblyReferencePathManager.Instance.HasReferenceLine)
{
RefreshSelectedRailReferenceLineVisuals();
}
else
{
RefreshAssemblyReferenceRodIfNeeded();
}
}
}
}
public double AssemblySphereCenterX
{
get => _assemblySphereCenterX;
set
{
if (Math.Abs(_assemblySphereCenterX - value) < 1e-9)
{
return;
}
_assemblySphereCenterX = value;
OnPropertyChanged(nameof(AssemblySphereCenterX));
if (HasAssemblyTerminalObject)
{
RefreshAssemblyTerminalObjectInfo();
RefreshAssemblyReferenceRodIfNeeded();
}
}
}
public double AssemblySphereCenterY
{
get => _assemblySphereCenterY;
set
{
if (Math.Abs(_assemblySphereCenterY - value) < 1e-9)
{
return;
}
_assemblySphereCenterY = value;
OnPropertyChanged(nameof(AssemblySphereCenterY));
if (HasAssemblyTerminalObject)
{
RefreshAssemblyTerminalObjectInfo();
RefreshAssemblyReferenceRodIfNeeded();
}
}
}
public double AssemblySphereCenterZ
{
get => _assemblySphereCenterZ;
set
{
if (Math.Abs(_assemblySphereCenterZ - value) < 1e-9)
{
return;
}
_assemblySphereCenterZ = value;
OnPropertyChanged(nameof(AssemblySphereCenterZ));
if (HasAssemblyTerminalObject)
{
RefreshAssemblyTerminalObjectInfo();
RefreshAssemblyReferenceRodIfNeeded();
}
}
}
public bool HasAssemblyTerminalObject
{
get => _hasAssemblyTerminalObject;
set
{
if (_hasAssemblyTerminalObject == value)
{
return;
}
_hasAssemblyTerminalObject = value;
OnPropertyChanged(nameof(HasAssemblyTerminalObject));
}
}
public string AssemblyStartPointText
{
get => _assemblyStartPointText;
set
{
if (string.Equals(_assemblyStartPointText, value, StringComparison.Ordinal))
{
return;
}
_assemblyStartPointText = value;
OnPropertyChanged(nameof(AssemblyStartPointText));
}
}
public RailMountMode AssemblyMountMode
{
get => _assemblyMountMode;
set
{
if (_assemblyMountMode == value)
{
return;
}
_assemblyMountMode = value;
OnPropertyChanged(nameof(AssemblyMountMode));
if (HasAssemblyTerminalObject)
{
RefreshAssemblyTerminalObjectInfo();
RefreshAssemblyReferenceRodIfNeeded();
}
}
}
public bool IsSelectingAssemblyStartPoint
{
get => _isSelectingAssemblyStartPoint;
set
{
if (_isSelectingAssemblyStartPoint == value)
{
return;
}
_isSelectingAssemblyStartPoint = value;
OnPropertyChanged(nameof(IsSelectingAssemblyStartPoint));
}
}
///
/// 更新路径可视化显示:清理其他路径,只显示当前选中的路径
///
private async void UpdatePathVisualization()
{
await SafeExecuteAsync(() =>
{
try
{
if (PathPointRenderPlugin.Instance == null)
{
LogManager.Warning("PathPointRenderPlugin实例为空,无法更新路径可视化");
return;
}
// 0. 先同步物体参数到渲染插件
SyncObjectParametersToRenderPlugin();
// 检查是否在编辑状态
bool isInEditMode = _pathPlanningManager?.IsInEditableState ?? false;
// 1. 清理现有的路径可视化,但保留网格可视化和正在编辑的路径
if (isInEditMode)
{
// 在编辑状态,不清空路径,避免清空正在编辑的路径
LogManager.Debug("[路径可视化] 在编辑状态,跳过清空路径可视化");
}
else
{
// 不在编辑状态,清空所有路径可视化,但保留网格可视化
PathPointRenderPlugin.Instance.ClearPathsExcept("grid_visualization_all", "grid_visualization_channel", "grid_visualization_unknown", "grid_visualization_obstacle", "grid_visualization_door");
}
// 2. 如果有选中的路径,则显示该路径
if (_selectedPathRoute != null && _selectedPathRoute.Points.Count > 0)
{
// 查找对应的Core路径对象
var coreRoute = _pathPlanningManager?.Routes?.FirstOrDefault(r => r.Id == _selectedPathRoute.Id)
?? _pathPlanningManager?.Routes?.FirstOrDefault(r => r.Name == _selectedPathRoute.Name);
if (coreRoute != null)
{
// 使用PathPlanningManager的绘制方法来保持一致性
_pathPlanningManager.DrawRouteVisualization(coreRoute, isAutoPath: false);
LogManager.Info($"[路径可视化] 已显示选中路径: {_selectedPathRoute.Name},包含 {_selectedPathRoute.Points.Count} 个点");
}
else
{
LogManager.Warning($"[路径可视化] 未找到对应的Core路径: {_selectedPathRoute.Name}");
}
}
RefreshSelectedRailInstallationReferenceVisuals();
// 3. 恢复网格可视化(如果网格可视化已启用且当前路径有关联的GridMap)
if (_pathPlanningManager?.IsAnyGridVisualizationEnabled == true)
{
// 检查当前路径是否有关联的GridMap
var currentRoute = _pathPlanningManager.CurrentRoute;
if (currentRoute?.AssociatedGridMap != null)
{
// 通过PathPlanningManager的RefreshGridVisualization方法恢复网格
// 这会使用当前路径的AssociatedGridMap来重新渲染网格
var refreshMethod = typeof(PathPlanningManager).GetMethod("RefreshGridVisualization",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
if (refreshMethod != null)
{
refreshMethod.Invoke(_pathPlanningManager, null);
}
else
{
LogManager.Warning("[路径可视化] 无法找到RefreshGridVisualization方法");
}
}
else
{
LogManager.Debug("[路径可视化] 当前路径没有关联的GridMap,跳过网格恢复");
}
}
}
catch (Exception ex)
{
LogManager.Error($"[路径可视化] 更新路径可视化失败: {ex.Message}", ex);
}
}, "更新路径可视化");
}
public bool IsPathEditMode
{
get => _isPathEditMode;
set => SetProperty(ref _isPathEditMode, value);
}
#region 自动路径规划属性
public string AutoPathStartPoint
{
get => _autoPathStartPoint;
set => SetProperty(ref _autoPathStartPoint, value);
}
public string AutoPathEndPoint
{
get => _autoPathEndPoint;
set => SetProperty(ref _autoPathEndPoint, value);
}
#region 多层吊装属性
public bool IsMultiLevelHoistingMode
{
get => _isMultiLevelHoistingMode;
set => SetProperty(ref _isMultiLevelHoistingMode, value);
}
public ObservableCollection MultiLevelItems
{
get => _multiLevelItems;
set => SetProperty(ref _multiLevelItems, value);
}
public string MultiLevelStartPointText
{
get => _multiLevelStartPointText;
set => SetProperty(ref _multiLevelStartPointText, value);
}
public string MultiLevelEndPointText
{
get => _multiLevelEndPointText;
set => SetProperty(ref _multiLevelEndPointText, value);
}
public string MultiLevelStatsText
{
get => _multiLevelStatsText;
set => SetProperty(ref _multiLevelStatsText, value);
}
public bool CanCreateMultiLevelPath => _hasMultiLevelStartPoint && _hasMultiLevelEndPoint && _multiLevelItems.Count > 0;
#endregion
public double ObjectLength
{
get => _objectLength;
set
{
if (SetProperty(ref _objectLength, value))
{
SyncObjectParametersToRenderPlugin();
OnPropertyChanged(nameof(CanExecuteAutoPlanPath));
}
}
}
public double ObjectWidth
{
get => _objectWidth;
set
{
if (SetProperty(ref _objectWidth, value))
{
SyncObjectParametersToRenderPlugin();
OnPropertyChanged(nameof(CanExecuteAutoPlanPath));
}
}
}
public double ObjectHeight
{
get => _objectHeight;
set
{
if (SetProperty(ref _objectHeight, value))
{
SyncObjectParametersToRenderPlugin();
OnPropertyChanged(nameof(CanExecuteAutoPlanPath));
}
}
}
public double SafetyMargin
{
get => _safetyMargin;
set
{
// 限制精度到2位小数
var roundedValue = Math.Round(value, 2);
if (SetProperty(ref _safetyMargin, roundedValue))
{
SyncObjectParametersToRenderPlugin();
OnPropertyChanged(nameof(CanExecuteAutoPlanPath));
}
}
}
public bool IsGridSizeManuallyEnabled
{
get => _isGridSizeManuallyEnabled;
set
{
if (SetProperty(ref _isGridSizeManuallyEnabled, value))
{
OnPropertyChanged(nameof(CanExecuteAutoPlanPath));
}
}
}
public double GridSize
{
get => _gridSize;
set
{
// 限制精度到1位小数
var roundedValue = Math.Round(value, 1);
if (SetProperty(ref _gridSize, roundedValue))
{
OnPropertyChanged(nameof(CanExecuteAutoPlanPath));
}
}
}
#region 可视化参数属性
///
/// 是否显示可通行网格点
///
public bool ShowWalkableGrid
{
get => _showWalkableGrid;
set
{
if (SetProperty(ref _showWalkableGrid, value))
{
OnGridVisualizationChanged();
}
}
}
///
/// 是否显示障碍物网格点
///
public bool ShowObstacleGrid
{
get => _showObstacleGrid;
set
{
if (SetProperty(ref _showObstacleGrid, value))
{
OnGridVisualizationChanged();
}
}
}
///
/// 是否显示未知区域网格点
///
public bool ShowUnknownGrid
{
get => _showUnknownGrid;
set
{
if (SetProperty(ref _showUnknownGrid, value))
{
OnGridVisualizationChanged();
}
}
}
///
/// 是否显示门网格点
///
public bool ShowDoorGrid
{
get => _showDoorGrid;
set
{
if (SetProperty(ref _showDoorGrid, value))
{
OnGridVisualizationChanged();
}
}
}
///
/// 网格点类型
///
public GridPointType GridPointType
{
get => _gridPointType;
set
{
if (SetProperty(ref _gridPointType, value))
{
OnPropertyChanged(nameof(IsRectanglePointType));
OnPropertyChanged(nameof(IsCirclePointType));
OnGridPointTypeChanged();
}
}
}
///
/// 是否使用立方体点类型
///
public bool IsRectanglePointType
{
get => _gridPointType == GridPointType.Rectangle;
set
{
if (value)
{
GridPointType = GridPointType.Rectangle;
}
}
}
///
/// 是否使用圆形点类型
///
public bool IsCirclePointType
{
get => _gridPointType == GridPointType.Circle;
set
{
if (value)
{
GridPointType = GridPointType.Circle;
}
}
}
///
/// 是否显示控制点连线(独立开关)
///
public bool ShowControlVisualization
{
get => _showControlVisualization;
set
{
if (SetProperty(ref _showControlVisualization, value))
{
OnControlVisualizationChanged();
}
}
}
///
/// 是否显示路径线/地面连线(独立开关)
///
public bool ShowPathLines
{
get => _showPathLines;
set
{
// 吊装和空轨路径不能使用路径线
if (value && !CanUsePathLines)
{
LogManager.Warning("[路径可视化] 吊装和空轨路径不能使用路径线");
return;
}
if (SetProperty(ref _showPathLines, value))
{
OnPathVisualizationModeChanged();
}
}
}
///
/// 是否显示通行空间(独立开关)
///
public bool ShowObjectSpace
{
get => _showObjectSpace;
set
{
if (SetProperty(ref _showObjectSpace, value))
{
OnPathVisualizationModeChanged();
}
}
}
///
/// 当前路径是否可以使用路径线
/// 地面路径可以使用路径线,吊装和空轨路径不能使用路径线
///
public bool CanUsePathLines
{
get
{
if (SelectedPathRoute == null) return true;
// 只有地面路径可以使用路径线
return SelectedPathRoute.PathType == PathType.Ground;
}
}
///
/// 是否使用标准连线模式(向后兼容,根据独立开关计算)
///
public bool IsStandardLineMode
{
get => _showPathLines && !_showObjectSpace;
set
{
if (value)
{
ShowPathLines = true;
ShowObjectSpace = false;
}
}
}
///
/// 是否使用物体通行空间模式(向后兼容,等同于 ShowObjectSpace)
///
public bool IsObjectSpaceMode
{
get => _showObjectSpace;
set => ShowObjectSpace = value;
}
///
/// 是否使用带状连线模式(向后兼容,等同于 ShowPathLines)
///
public bool IsRibbonLineMode
{
get => _showPathLines;
set => ShowPathLines = value;
}
#endregion
///
/// 路径策略选项集合
///
public ObservableCollection PathStrategyOptions
{
get => _pathStrategyOptions;
}
///
/// 当前选择的路径策略
///
public PathStrategyOption SelectedPathStrategy
{
get => _selectedPathStrategy;
set
{
if (SetProperty(ref _selectedPathStrategy, value))
{
OnPropertyChanged(nameof(CanExecuteAutoPlanPath));
}
}
}
public bool IsSelectingStartPoint
{
get => _isSelectingStartPoint;
set => SetProperty(ref _isSelectingStartPoint, value);
}
public bool IsSelectingEndPoint
{
get => _isSelectingEndPoint;
set => SetProperty(ref _isSelectingEndPoint, value);
}
#endregion
public PathPlanningManager PathPlanningManager
{
get => _pathPlanningManager;
set
{
if (_pathPlanningManager != value)
{
// 取消旧的事件订阅
if (_pathPlanningManager != null)
{
UnsubscribeFromPathPlanningManager();
}
_pathPlanningManager = value;
// 订阅新的事件
if (_pathPlanningManager != null)
{
SubscribeToPathPlanningManager();
}
}
}
}
#endregion
#region 命令
public ICommand NewPathCommand { get; private set; }
public ICommand NewRailPathCommand { get; private set; }
public ICommand NewHoistingPathCommand { get; private set; }
public ICommand NewMultiLevelHoistingPathCommand { get; private set; }
public ICommand DuplicatePathCommand { get; private set; }
public ICommand DeletePathCommand { get; private set; }
public ICommand RenamePathCommand { get; private set; }
public ICommand StartEditCommand { get; private set; }
public ICommand EndEditCommand { get; private set; }
public ICommand ClearPathCommand { get; private set; }
public ICommand DeletePointCommand { get; private set; }
public ICommand ModifyPointCommand { get; private set; }
public ICommand CancelModifyPointCommand { get; private set; }
public ICommand SelectStartPointCommand { get; private set; }
public ICommand SelectEndPointCommand { get; private set; }
public ICommand EditPointCoordinatesCommand { get; private set; }
public ICommand AutoPlanPathCommand { get; private set; }
public ICommand ClearAutoPathCommand { get; private set; }
public ICommand ImportPathCommand { get; private set; }
public ICommand ExportPathCommand { get; private set; }
public ICommand SaveAsPathCommand { get; private set; }
public ICommand CaptureAssemblyTerminalObjectForCreateCommand { get; private set; }
public ICommand ReCaptureAssemblyTerminalObjectForEditCommand { get; private set; }
public ICommand SelectAssemblyStartPointForCreateCommand { get; private set; }
public ICommand RepositionRailStartPointCommand { get; private set; }
public ICommand SelectAssemblyInstallationPointForCreateCommand { get; private set; }
public ICommand SelectAssemblyInstallationPointForEditCommand { get; private set; }
public ICommand ClearAssemblyReferenceRodCommand { get; private set; }
public ICommand CancelRailAssemblyWorkflowCommand { get; private set; }
public ICommand AnalyzeAssemblyTerminalFaceForCreateCommand { get; private set; }
public ICommand AnalyzeAssemblyTerminalFaceForEditCommand { get; private set; }
public ICommand DecreaseSelectedRailNormalOffsetCommand { get; private set; }
public ICommand IncreaseSelectedRailNormalOffsetCommand { get; private set; }
// 多层吊装命令
public ICommand SelectMultiLevelStartPointCommand { get; private set; }
public ICommand SelectMultiLevelEndPointCommand { get; private set; }
public ICommand AddMultiLevelCommand { get; private set; }
public ICommand DeleteMultiLevelCommand { get; private set; }
public ICommand PickMultiLevelTargetCommand { get; private set; }
public ICommand CreateMultiLevelPathCommand { get; private set; }
public ICommand CancelMultiLevelHoistingCommand { get; private set; }
#endregion
#region Can Execute属性
public bool CanExecuteAutoPlanPath => _hasStartPoint && _hasEndPoint &&
ObjectLength > 0 && ObjectWidth > 0 && ObjectHeight > 0 &&
SafetyMargin >= 0;
public bool CanExecuteNewPath => !IsSelectingStartPoint && !IsSelectingEndPoint;
public bool CanExecuteNewRailPath => !IsSelectingStartPoint && !IsSelectingEndPoint;
public bool CanExecuteStartEdit => SelectedPathRoute != null &&
(_pathPlanningManager?.PathEditState == PathEditState.Viewing);
public bool CanExecuteEndEdit => (_pathPlanningManager?.PathEditState == PathEditState.Creating ||
_pathPlanningManager?.PathEditState == PathEditState.Editing ||
_pathPlanningManager?.PathEditState == PathEditState.AddingPoints ||
_pathPlanningManager?.PathEditState == PathEditState.EditingPoint) ||
CanCancelRailAssemblyWorkflow;
public bool CanExecuteClearPath => SelectedPathRoute != null && SelectedPathRoute.Points.Count > 0;
public bool CanExecuteExportPath => _pathPlanningManager?.Routes?.Count > 0 || SelectedPathRoute != null;
public bool CanExecuteSaveAsPath => SelectedPathRoute != null && SelectedPathRoute.Points.Count > 0;
public bool CanSelectAssemblyStartPointForCreate => HasAssemblyTerminalObject &&
_pathPlanningManager != null &&
AssemblyReferencePathManager.Instance.HasReferenceLine &&
!IsSelectingAssemblyStartPoint &&
!_isSelectingAssemblyEndFacePoints &&
!_isSelectingAssemblyInstallationPoint;
public bool CanRepositionRailStartPoint => HasAssemblyTerminalObject &&
IsRailRouteSelected &&
SelectedPathRoute != null &&
_pathPlanningManager != null &&
AssemblyReferencePathManager.Instance.HasReferenceLine &&
!IsSelectingAssemblyStartPoint &&
!_isSelectingAssemblyEndFacePoints &&
!_isSelectingAssemblyInstallationPoint;
public bool CanSelectAssemblyInstallationPointForCreate => HasAssemblyTerminalObject &&
_pathPlanningManager != null &&
!_isSelectingAssemblyStartPoint &&
!_isSelectingAssemblyEndFacePoints &&
!_isSelectingAssemblyInstallationPoint;
public bool CanSelectAssemblyInstallationPointForEdit => HasAssemblyTerminalObject &&
IsRailRouteSelected &&
SelectedPathRoute != null &&
_pathPlanningManager != null &&
!_isSelectingAssemblyStartPoint &&
!_isSelectingAssemblyEndFacePoints &&
!_isSelectingAssemblyInstallationPoint;
public bool CanAnalyzeAssemblyTerminalFaceForCreate => HasAssemblyTerminalObject &&
_pathPlanningManager != null &&
!IsSelectingAssemblyStartPoint &&
!IsSelectingAssemblyEndFacePoints &&
!_isSelectingAssemblyInstallationPoint;
public bool CanAnalyzeAssemblyTerminalFaceForEdit => HasAssemblyTerminalObject &&
IsRailRouteSelected &&
SelectedPathRoute != null &&
_pathPlanningManager != null &&
!IsSelectingAssemblyStartPoint &&
!IsSelectingAssemblyEndFacePoints &&
!_isSelectingAssemblyInstallationPoint;
public string CreateAssemblyInstallationPointButtonText => _hasAssemblyInstallationReference && !IsRailEditingWorkflowActive
? "重选安装点"
: "选安装点";
public string EditAssemblyInstallationPointButtonText => HasSelectedRailInstallationReference()
? "重选安装点"
: "选安装点";
public bool IsSelectingAssemblyEndFacePoints => _isSelectingAssemblyEndFacePoints;
public bool CanCancelRailAssemblyWorkflow => _railAssemblyWorkflowMode != RailAssemblyWorkflowMode.None ||
HasAssemblyTerminalObject ||
_isSelectingAssemblyStartPoint ||
_isSelectingAssemblyEndFacePoints ||
_isSelectingAssemblyInstallationPoint ||
_hasAssemblyEndFaceAnalysis ||
_hasAssemblyInstallationReference;
private bool IsRailCreateWorkflowActive => _railAssemblyWorkflowMode == RailAssemblyWorkflowMode.CreateNewRailPath;
private bool IsRailEditingWorkflowActive => _railAssemblyWorkflowMode == RailAssemblyWorkflowMode.EditSelectedRail;
private static bool IsRailAssemblyEditMode(RailAssemblyWorkflowMode workflowMode)
{
return workflowMode == RailAssemblyWorkflowMode.EditSelectedRail;
}
private static string GetRailAssemblyWorkflowLabel(RailAssemblyWorkflowMode workflowMode)
{
return IsRailAssemblyEditMode(workflowMode)
? "编辑现有Rail路径"
: "新建Rail路径";
}
private bool ShouldShowAssemblyReferenceLineVisuals()
{
if (_assemblyTerminalObject == null || !ModelItemAnalysisHelper.IsModelItemValid(_assemblyTerminalObject))
{
return false;
}
return IsRailEditingWorkflowActive || _hasAssemblyInstallationReference;
}
public bool CanExecuteModifyPoint => SelectedPathPoint != null &&
_pathPlanningManager?.PathEditState != PathEditState.EditingPoint;
public bool CanExecuteCancelModifyPoint => _pathPlanningManager?.PathEditState == PathEditState.EditingPoint;
#endregion
#region 构造函数
///
/// 构造函数,需要传入主ViewModel以支持统一状态栏
///
/// 主ViewModel,用于统一状态栏
public PathEditingViewModel(LogisticsControlViewModel mainViewModel) : base()
{
try
{
// 设置主ViewModel引用到基类
SetMainViewModel(mainViewModel);
_uiStateManager = UIStateManager.Instance;
_pathDataManager = new PathDataManager();
// 不在构造函数中创建PathPlanningManager,由外部设置
_pathPlanningManager = null;
if (_uiStateManager == null)
{
throw new InvalidOperationException("UIStateManager初始化失败");
}
// PathPlanningManager将在外部设置,这里不需要检查
// 初始化集合
PathRoutes = new ObservableCollection();
// 初始化路径策略选项
InitializePathStrategyOptions();
InitializeRailConfigOptions();
// 从配置加载参数
LoadParametersFromConfig();
// 订阅配置变更事件
SubscribeToConfigChanges();
// 初始化命令
InitializeCommands();
// 初始化物体参数同步(在PathPointRenderPlugin实例不为空时才同步)
// 如果PathPointRenderPlugin尚未初始化,将在属性变更时自动同步
SyncObjectParametersToRenderPlugin();
// 注意:不在这里订阅PathPlanningManager事件,
// 因为PathPlanningManager还没有设置,事件订阅在PathPlanningManager属性setter中处理
LogManager.Info("PathEditingViewModel构造函数执行完成 - 支持统一状态栏");
}
catch (Exception ex)
{
LogManager.Error($"PathEditingViewModel构造函数异常: {ex.Message}", ex);
UpdateMainStatus("初始化失败,请检查日志");
throw;
}
}
#endregion
#region 初始化方法
///
/// 初始化路径策略选项
///
private void InitializePathStrategyOptions()
{
_pathStrategyOptions = new ObservableCollection
{
new PathStrategyOption
{
Value = PathStrategy.Shortest,
DisplayName = "最短路径",
Description = "标准A*算法,选择路径总长度最短的路径"
},
new PathStrategyOption
{
Value = PathStrategy.Straightest,
DisplayName = "直线优先",
Description = "优先选择主方向直线路径,减少转弯次数"
},
new PathStrategyOption
{
Value = PathStrategy.SafestCenter,
DisplayName = "安全优先",
Description = "基于障碍物距离选择安全路径,适合大型物体居中行驶"
}
};
// 默认选择最短路径策略(向后兼容)
_selectedPathStrategy = _pathStrategyOptions.FirstOrDefault(x => x.Value == PathStrategy.Shortest);
}
///
/// 初始化 Rail 构型选项
///
private void InitializeRailConfigOptions()
{
_railMountModeOptions = new ObservableCollection>
{
new RailConfigOption
{
Value = RailMountMode.UnderRail,
DisplayName = "轨下安装",
Description = "安装头位于双轨下方,构件从轨道平面下侧连接"
},
new RailConfigOption
{
Value = RailMountMode.OverRail,
DisplayName = "轨上安装",
Description = "安装头位于双轨上方,构件从轨道平面上侧连接"
}
};
}
private PathRoute GetSelectedCoreRoute()
{
if (_pathPlanningManager == null || SelectedPathRoute == null)
{
return null;
}
return _pathPlanningManager.Routes?.FirstOrDefault(route => route.Id == SelectedPathRoute.Id)
?? _pathPlanningManager.Routes?.FirstOrDefault(route => route.Name == SelectedPathRoute.Name);
}
private void ApplyRailRouteConfigurationChange(PathRoute coreRoute, string logMessage)
{
coreRoute.LastModified = DateTime.Now;
SyncObjectParametersToRenderPlugin();
_pathPlanningManager.DrawRouteVisualization(coreRoute, isAutoPath: false);
_pathPlanningManager.SavePathToDatabase(coreRoute);
_pathPlanningManager.RaiseVisualizationStateChanged();
LogManager.Info($"[Rail构型] {coreRoute.Name}: {logMessage}");
}
private void SetRailAssemblyWorkflowMode(RailAssemblyWorkflowMode mode)
{
if (_railAssemblyWorkflowMode == mode)
{
return;
}
_railAssemblyWorkflowMode = mode;
NotifyRailAssemblyCommandStateChanged();
}
private void NotifyRailAssemblyCommandStateChanged()
{
OnPropertyChanged(nameof(CanAnalyzeAssemblyTerminalFaceForCreate));
OnPropertyChanged(nameof(CanAnalyzeAssemblyTerminalFaceForEdit));
OnPropertyChanged(nameof(CanSelectAssemblyStartPointForCreate));
OnPropertyChanged(nameof(CanRepositionRailStartPoint));
OnPropertyChanged(nameof(CanSelectAssemblyInstallationPointForCreate));
OnPropertyChanged(nameof(CanSelectAssemblyInstallationPointForEdit));
OnPropertyChanged(nameof(CreateAssemblyInstallationPointButtonText));
OnPropertyChanged(nameof(EditAssemblyInstallationPointButtonText));
OnPropertyChanged(nameof(CanCancelRailAssemblyWorkflow));
OnPropertyChanged(nameof(CanExecuteEndEdit));
}
private bool HasSelectedRailInstallationReference()
{
var coreRoute = GetSelectedCoreRoute();
return coreRoute != null &&
coreRoute.PathType == PathType.Rail &&
coreRoute.RailPreferredNormal != null;
}
private int GetRoutePointIndexByType(PathRoute route, PathPointType pointType, int fallbackIndex)
{
if (route?.Points == null || route.Points.Count == 0)
{
return -1;
}
for (int i = 0; i < route.Points.Count; i++)
{
if (route.Points[i].Type == pointType)
{
return i;
}
}
return fallbackIndex >= 0 && fallbackIndex < route.Points.Count
? fallbackIndex
: -1;
}
private bool IsAssemblyInstallationReferenceActiveForRoute(PathRoute route)
{
return route != null &&
route.PathType == PathType.Rail &&
_hasAssemblyInstallationReference &&
_assemblyInstallationAnchorPoint != null &&
!string.IsNullOrWhiteSpace(_assemblyInstallationReferenceRouteId) &&
string.Equals(_assemblyInstallationReferenceRouteId, route.Id, StringComparison.Ordinal);
}
private int GetRailRouteInstallationPointIndex(PathRoute route)
{
return GetRoutePointIndexByType(route, PathPointType.EndPoint, route?.Points?.Count - 1 ?? -1);
}
private int GetRailRouteStartPointIndex(PathRoute route)
{
return GetRoutePointIndexByType(route, PathPointType.StartPoint, 0);
}
private bool TryBuildRailRouteReferenceLine(PathRoute route, out AssemblyReferenceLine referenceLine)
{
referenceLine = null;
if (route == null || route.PathType != PathType.Rail || route.Points == null || route.Points.Count < 2)
{
return false;
}
int startIndex = GetRailRouteStartPointIndex(route);
int endIndex = GetRailRouteInstallationPointIndex(route);
if (startIndex < 0 || endIndex < 0 || startIndex == endIndex)
{
return false;
}
Point3D startPoint = route.Points[startIndex].Position;
Point3D endPoint = route.Points[endIndex].Position;
Vector3D direction = new Vector3D(
startPoint.X - endPoint.X,
startPoint.Y - endPoint.Y,
startPoint.Z - endPoint.Z);
double lengthSquared = direction.X * direction.X + direction.Y * direction.Y + direction.Z * direction.Z;
if (lengthSquared < 1e-9)
{
return false;
}
referenceLine = new AssemblyReferenceLine(startPoint, endPoint, direction.Normalize());
return true;
}
private void RefreshSelectedRailReferenceLineVisuals(string statusMessage = null)
{
if (_assemblyTerminalObject == null || !ModelItemAnalysisHelper.IsModelItemValid(_assemblyTerminalObject))
{
ClearAssemblyReferenceLineVisuals();
return;
}
AssemblyReferenceLine referenceLine;
if (IsRailEditingWorkflowActive)
{
// 编辑状态:使用默认杆长度构建参考线,方便选择更远的起点
try
{
referenceLine = BuildAssemblyReferenceLine();
}
catch
{
ClearAssemblyReferenceLineVisuals();
return;
}
}
else
{
// 非编辑状态:使用路径实际长度
var coreRoute = GetSelectedCoreRoute();
if (!TryBuildRailRouteReferenceLine(coreRoute, out referenceLine))
{
ClearAssemblyReferenceLineVisuals();
return;
}
}
AssemblyReferencePathManager.Instance.CreateOrUpdateReferenceRod(
referenceLine.StartPoint,
referenceLine.EndPoint,
AssemblyReferenceRodDiameterInMeters);
AssemblyStartPointText = $"({referenceLine.StartPoint.X:F2}, {referenceLine.StartPoint.Y:F2}, {referenceLine.StartPoint.Z:F2})";
RefreshAssemblyTerminalObjectInfo(referenceLine.StartPoint, referenceLine.EndPoint);
RenderAssemblyReferenceLine(referenceLine);
NotifyRailAssemblyCommandStateChanged();
if (!string.IsNullOrWhiteSpace(statusMessage))
{
UpdateMainStatus(statusMessage);
}
}
private void PersistAssemblyInstallationReferenceToRailRoute(PathRoute route, string logMessage)
{
if (route == null)
{
throw new ArgumentNullException(nameof(route));
}
if (route.PathType != PathType.Rail)
{
throw new InvalidOperationException("只有 Rail 路径支持安装点参数更新。");
}
if (!_hasAssemblyInstallationReference || _assemblyInstallationAnchorPoint == null)
{
throw new InvalidOperationException("当前没有可应用的安装点参考。");
}
int installationPointIndex = GetRailRouteInstallationPointIndex(route);
if (installationPointIndex < 0)
{
throw new InvalidOperationException("当前 Rail 路径没有可更新的终点。");
}
// 获取当前起点索引
int startPointIndex = GetRailRouteStartPointIndex(route);
if (startPointIndex < 0)
{
throw new InvalidOperationException("当前 Rail 路径没有可更新的起点。");
}
// 计算原路径长度(起点到终点的距离)
Point3D currentStartPoint = route.Points[startPointIndex].Position;
Point3D currentEndPoint = route.Points[installationPointIndex].Position;
double pathLength = GeometryHelper.CalculatePointDistance(currentStartPoint, currentEndPoint);
// 更新终点(安装点)
if (!_pathPlanningManager.UpdatePathPointWithConstraints(route, installationPointIndex, _assemblyInstallationAnchorPoint))
{
throw new InvalidOperationException("更新 Rail 路径安装点失败。");
}
// 根据新终点和原路径长度重新计算起点位置
Point3D newStartPoint = CalculateRailStartPointFromEndPoint(_assemblyInstallationAnchorPoint, pathLength);
if (!_pathPlanningManager.UpdatePathPointWithConstraints(route, startPointIndex, newStartPoint))
{
throw new InvalidOperationException("更新 Rail 路径起点失败。");
}
route.RailPreferredNormal = CreatePersistedAssemblyPreferredNormal();
route.RailNormalOffset = UnitsConverter.ConvertFromMeters(AssemblyAnchorVerticalOffsetInMeters);
route.LastModified = DateTime.Now;
_assemblyInstallationReferenceRouteId = route.Id;
_pathPlanningManager.SavePathToDatabase(route);
_pathPlanningManager.DrawRouteVisualization(route, isAutoPath: false);
_pathPlanningManager.RaiseVisualizationStateChanged();
if (SelectedPathRoute != null && SelectedPathRoute.Id == route.Id)
{
SyncPathViewModelFromCoreRoute(SelectedPathRoute, route, preserveSelection: true);
OnPropertyChanged(nameof(SelectedRailNormalOffsetInMeters));
NotifyRailAssemblyCommandStateChanged();
RefreshAssemblyTerminalObjectInfo();
RefreshSelectedRailInstallationReferenceVisuals();
RefreshSelectedRailReferenceLineVisuals();
}
LogManager.Info($"[Rail构型] {route.Name}: {logMessage},起点已同步更新为 ({newStartPoint.X:F3}, {newStartPoint.Y:F3}, {newStartPoint.Z:F3})");
}
private void RefreshSelectedRailInstallationReferenceVisuals()
{
var coreRoute = GetSelectedCoreRoute();
if (!IsAssemblyInstallationReferenceActiveForRoute(coreRoute))
{
ClearAssemblyInstallationReferenceVisuals();
return;
}
RenderAssemblyInstallationPickPoints(_assemblyInstallationSeedPoints);
RenderAssemblyInstallationAnchorPoint(_assemblyInstallationAnchorPoint);
RenderAssemblyInstallationCenterLine(_assemblyInstallationPickPoint, GetCurrentAssemblyOpticalAxisDirection());
RenderAssemblyInstallationPlane(_assemblyInstallationAnchorPoint, GetCurrentAssemblyOpticalAxisDirection(), _assemblyInstallationPlaneSpanDirection);
}
private void AdjustSelectedRailNormalOffset(double deltaInMeters)
{
var coreRoute = GetSelectedCoreRoute();
if (coreRoute == null || coreRoute.PathType != PathType.Rail)
{
return;
}
double currentOffsetInMeters = UnitsConverter.ConvertToMeters(coreRoute.RailNormalOffset);
double nextOffsetInMeters = Math.Round(currentOffsetInMeters + deltaInMeters, 3);
SelectedRailNormalOffsetInMeters = nextOffsetInMeters;
}
///
/// 从配置文件加载参数
///
private void LoadParametersFromConfig()
{
try
{
var config = ConfigManager.Instance.Current;
// 从 PathEditing 配置加载所有参数(使用米单位接口)
_gridSize = config.PathEditing.CellSizeMeters;
_objectLength = config.PathEditing.ObjectLengthMeters;
_objectWidth = config.PathEditing.ObjectWidthMeters;
_objectHeight = config.PathEditing.ObjectHeightMeters;
_safetyMargin = config.PathEditing.SafetyMarginMeters;
LogManager.Info($"从配置加载参数 - 物体: {_objectLength:F1}x{_objectWidth:F1}x{_objectHeight:F1}米, 安全间隙: {_safetyMargin:F2}米, 网格: {_gridSize:F1}米");
}
catch (Exception ex)
{
LogManager.Error($"加载配置参数失败: {ex.Message}", ex);
// 使用默认值,不抛出异常
}
}
#region 配置变更处理
///
/// 订阅配置变更事件
///
private void SubscribeToConfigChanges()
{
try
{
// 订阅特定类别的配置变更事件
ConfigManager.Instance.CategoryConfigurationChanged += OnCategoryConfigurationChanged;
LogManager.Info("PathEditingViewModel 已订阅配置变更事件");
}
catch (Exception ex)
{
LogManager.Error($"订阅配置变更事件失败: {ex.Message}", ex);
}
}
///
/// 取消订阅配置变更事件
///
private void UnsubscribeFromConfigChanges()
{
try
{
ConfigManager.Instance.CategoryConfigurationChanged -= OnCategoryConfigurationChanged;
LogManager.Info("PathEditingViewModel 已取消订阅配置变更事件");
}
catch (Exception ex)
{
LogManager.Warning($"取消订阅配置变更事件时发生异常: {ex.Message}");
}
}
///
/// 配置变更事件处理
///
private void OnCategoryConfigurationChanged(object sender, Tuple args)
{
try
{
var category = args.Item1;
var eventArgs = args.Item2;
// 只处理 PathEditing 相关的配置变更
if (category == ConfigCategory.PathEditing || category == ConfigCategory.All)
{
LogManager.Info($"收到 PathEditing 配置变更通知,正在更新参数...");
// 从配置重新加载参数
LoadParametersFromConfig();
// 触发属性变更通知,更新UI
OnPropertiesChanged(
nameof(ObjectLength),
nameof(ObjectWidth),
nameof(ObjectHeight),
nameof(SafetyMargin),
nameof(GridSize)
);
// 同步物体参数到渲染插件
SyncObjectParametersToRenderPlugin();
// 更新状态栏
string source = eventArgs.IsExternalChange ? "外部修改" : "内部更新";
UpdateMainStatus($"配置已更新 ({source}) - 物体参数: {ObjectLength:F1}x{ObjectWidth:F1}x{ObjectHeight:F1}米");
LogManager.Info($"PathEditing 配置参数已更新完成 - 来源: {source}");
}
}
catch (Exception ex)
{
LogManager.Error($"处理配置变更事件失败: {ex.Message}", ex);
}
}
#endregion
private void InitializeCommands()
{
NewPathCommand = new RelayCommand(async () => await ExecuteNewPathAsync(), () => CanExecuteNewPath);
NewRailPathCommand = new RelayCommand(async () => await ExecuteNewRailPathAsync(), () => CanExecuteNewRailPath);
NewHoistingPathCommand = new RelayCommand(async () => await ExecuteNewHoistingPathAsync());
NewMultiLevelHoistingPathCommand = new RelayCommand(async () => await ExecuteNewMultiLevelHoistingPathAsync());
SelectMultiLevelStartPointCommand = new RelayCommand(async () => await ExecuteSelectMultiLevelStartPointAsync());
SelectMultiLevelEndPointCommand = new RelayCommand(async () => await ExecuteSelectMultiLevelEndPointAsync());
AddMultiLevelCommand = new RelayCommand(() => ExecuteAddMultiLevel());
DeleteMultiLevelCommand = new RelayCommand((item) => ExecuteDeleteMultiLevel(item));
PickMultiLevelTargetCommand = new RelayCommand((item) => ExecutePickMultiLevelTarget(item));
CreateMultiLevelPathCommand = new RelayCommand(async () => await ExecuteCreateMultiLevelPathAsync(), () => CanCreateMultiLevelPath);
CancelMultiLevelHoistingCommand = new RelayCommand(() => ExecuteCancelMultiLevelHoisting());
DuplicatePathCommand = new RelayCommand(async () => await ExecuteDuplicatePathAsync());
DeletePathCommand = new RelayCommand(async () => await ExecuteDeletePathAsync());
RenamePathCommand = new RelayCommand(async () => await ExecuteRenamePathAsync());
StartEditCommand = new RelayCommand(async () => await ExecuteAddPathPointAsync(), () => CanExecuteStartEdit);
EndEditCommand = new RelayCommand(async () => await ExecuteEndEditAsync(), () => CanExecuteEndEdit);
ClearPathCommand = new RelayCommand(async () => await ExecuteClearPathAsync(), () => CanExecuteClearPath);
DeletePointCommand = new RelayCommand(async (point) => await ExecuteDeletePointAsync(point));
ModifyPointCommand = new RelayCommand(async () => await ExecuteModifyPointAsync(), () => CanExecuteModifyPoint);
CancelModifyPointCommand = new RelayCommand(async () => await ExecuteCancelModifyPointAsync(), () => CanExecuteCancelModifyPoint);
SelectStartPointCommand = new RelayCommand(async () => await ExecuteSelectStartPointAsync());
SelectEndPointCommand = new RelayCommand(async () => await ExecuteSelectEndPointAsync());
EditPointCoordinatesCommand = new RelayCommand(ExecuteEditPointCoordinates);
AutoPlanPathCommand = new RelayCommand(async () => await ExecuteAutoPlanPathAsync(), () => CanExecuteAutoPlanPath);
ClearAutoPathCommand = new RelayCommand(async () => await ExecuteClearAutoPathAsync());
ImportPathCommand = new RelayCommand(async () => await ExecuteImportPathAsync());
ExportPathCommand = new RelayCommand(async () => await ExecuteExportPathAsync(), () => CanExecuteExportPath);
SaveAsPathCommand = new RelayCommand(async () => await ExecuteSaveAsPathAsync(), () => CanExecuteSaveAsPath);
CaptureAssemblyTerminalObjectForCreateCommand = new RelayCommand(async () => await ExecuteCaptureAssemblyTerminalObjectAsync(RailAssemblyWorkflowMode.CreateNewRailPath));
ReCaptureAssemblyTerminalObjectForEditCommand = new RelayCommand(async () => await ExecuteCaptureAssemblyTerminalObjectAsync(RailAssemblyWorkflowMode.EditSelectedRail));
SelectAssemblyStartPointForCreateCommand = new RelayCommand(async () => await ExecuteSelectAssemblyStartPointAsync(), () => CanSelectAssemblyStartPointForCreate);
RepositionRailStartPointCommand = new RelayCommand(async () => await ExecuteRepositionRailStartPointAsync(), () => CanRepositionRailStartPoint);
SelectAssemblyInstallationPointForCreateCommand = new RelayCommand(async () => await ExecuteSelectAssemblyInstallationPointAsync(RailAssemblyWorkflowMode.CreateNewRailPath), () => CanSelectAssemblyInstallationPointForCreate);
SelectAssemblyInstallationPointForEditCommand = new RelayCommand(async () => await ExecuteSelectAssemblyInstallationPointAsync(RailAssemblyWorkflowMode.EditSelectedRail), () => CanSelectAssemblyInstallationPointForEdit);
ClearAssemblyReferenceRodCommand = new RelayCommand(() => ExecuteClearAssemblyReferenceRod());
CancelRailAssemblyWorkflowCommand = new RelayCommand(() => ExecuteCancelRailAssemblyWorkflow(), () => CanCancelRailAssemblyWorkflow);
AnalyzeAssemblyTerminalFaceForCreateCommand = new RelayCommand(async () => await ExecuteAnalyzeAssemblyTerminalFaceAsync(RailAssemblyWorkflowMode.CreateNewRailPath), () => CanAnalyzeAssemblyTerminalFaceForCreate);
AnalyzeAssemblyTerminalFaceForEditCommand = new RelayCommand(async () => await ExecuteAnalyzeAssemblyTerminalFaceAsync(RailAssemblyWorkflowMode.EditSelectedRail), () => CanAnalyzeAssemblyTerminalFaceForEdit);
DecreaseSelectedRailNormalOffsetCommand = new RelayCommand(() => AdjustSelectedRailNormalOffset(-RailNormalOffsetNudgeStepInMeters));
IncreaseSelectedRailNormalOffsetCommand = new RelayCommand(() => AdjustSelectedRailNormalOffset(RailNormalOffsetNudgeStepInMeters));
}
#endregion
#region 命令实现 - 从LogisticsControlViewModelcopy.cs迁移
#region 路径管理命令
private async Task ExecuteCaptureAssemblyTerminalObjectAsync(RailAssemblyWorkflowMode mode)
{
await SafeExecuteAsync(() =>
{
ClearNonGridPathVisualizations("[直线装配] 捕获箱体");
var document = NavisApplication.ActiveDocument;
var selectedItem = document?.CurrentSelection?.SelectedItems?.FirstOrDefault();
if (selectedItem == null)
{
throw new InvalidOperationException("请先在 Navisworks 中选择终点处已安装的箱体");
}
if (!ModelItemAnalysisHelper.IsModelItemValid(selectedItem))
{
throw new InvalidOperationException("当前选择对象无效,请重新选择终点箱体");
}
SetRailAssemblyWorkflowMode(mode);
_assemblyTerminalObject = selectedItem;
_assemblyStartPoint = Point3D.Origin;
HasAssemblyTerminalObject = true;
AssemblyStartPointText = "未选择";
AssemblyTerminalObjectName = ModelItemAnalysisHelper.GetSafeDisplayName(selectedItem);
ResetAssemblyEndFaceAnalysisState();
ResetAssemblyInstallationReferenceState();
RefreshAssemblyTerminalObjectInfo();
ClearAssemblyAnchorMarker();
ClearAssemblyEndFaceAnalysisVisuals();
ClearAssemblyInstallationReferenceVisuals();
NotifyRailAssemblyCommandStateChanged();
// 新建路径时隐藏其他路径的辅助线
ClearAssemblyReferenceLineVisuals();
UpdateMainStatus($"已捕获终点箱体: {AssemblyTerminalObjectName}");
LogManager.Info($"[直线装配] 已捕获终点箱体: {AssemblyTerminalObjectName}");
}, "捕获终点箱体");
}
private void ClearNonGridPathVisualizations(string context)
{
if (PathPointRenderPlugin.Instance == null)
{
return;
}
try
{
PathPointRenderPlugin.Instance.ClearPathsExcept(
"grid_visualization_all",
"grid_visualization_channel",
"grid_visualization_unknown",
"grid_visualization_obstacle",
"grid_visualization_door");
LogManager.Info($"{context}:已清除现有路径可视化显示(保留网格可视化)");
}
catch (Exception ex)
{
LogManager.Error($"{context}:清除现有路径可视化失败: {ex.Message}", ex);
throw;
}
}
private async Task ExecuteSelectAssemblyStartPointAsync()
{
await SafeExecuteAsync(() =>
{
if (_pathPlanningManager == null)
{
throw new InvalidOperationException("路径规划管理器未初始化,无法选择装配起点");
}
if (_assemblyTerminalObject == null || !ModelItemAnalysisHelper.IsModelItemValid(_assemblyTerminalObject))
{
throw new InvalidOperationException("终点箱体未设置或已失效,请重新捕获终点箱体");
}
if (!AssemblyReferencePathManager.Instance.HasReferenceLine)
{
throw new InvalidOperationException("请先选择安装点,系统生成终端安装辅助线后才能取起点");
}
BeginAssemblyStartPointSelection(
statusMessage: "请在辅助线附近点击一个起点,系统会自动吸附到辅助线上并生成直线路径",
logMessage: "[直线装配] 已进入起点拾取模式",
toolPluginFailureMessage: "ToolPlugin 初始化失败,请重试");
}, "选择终端安装起点");
}
private async Task ExecuteRepositionRailStartPointAsync()
{
await SafeExecuteAsync(() =>
{
if (_pathPlanningManager == null)
{
throw new InvalidOperationException("路径规划管理器未初始化,无法重选起点。");
}
if (_assemblyTerminalObject == null || !ModelItemAnalysisHelper.IsModelItemValid(_assemblyTerminalObject))
{
throw new InvalidOperationException("终点箱体未设置或已失效,请先捕获终点箱体。");
}
var selectedRailRoute = GetSelectedCoreRoute();
if (selectedRailRoute == null || selectedRailRoute.PathType != PathType.Rail)
{
throw new InvalidOperationException("请先选中一条需要编辑起点的 Rail 路径。");
}
SetRailAssemblyWorkflowMode(RailAssemblyWorkflowMode.EditSelectedRail);
RefreshSelectedRailReferenceLineVisuals();
if (!AssemblyReferencePathManager.Instance.HasReferenceLine)
{
throw new InvalidOperationException("当前 Rail 路径无法生成辅助线,请先检查路径起点和终点。");
}
BeginAssemblyStartPointSelection(
statusMessage: "请在辅助线附近点击新的起点位置,系统会自动吸附到辅助线上并更新当前 Rail 路径。",
logMessage: "[Rail构型] 已进入起点重选模式",
toolPluginFailureMessage: "ToolPlugin 初始化失败,请重试。");
}, "重选 Rail 路径起点");
}
private void ExecuteClearAssemblyReferenceRod()
{
try
{
ClearAssemblyReferenceLineVisuals();
ClearAssemblyEndFaceAnalysisVisuals();
ClearAssemblyInstallationReferenceVisuals();
UpdateMainStatus("已隐藏终端安装辅助线");
LogManager.Info("[直线装配] 已隐藏辅助线");
}
catch (Exception ex)
{
LogManager.Error($"[直线装配] 隐藏辅助线失败: {ex.Message}");
}
}
private async Task ExecuteAnalyzeAssemblyTerminalFaceAsync(RailAssemblyWorkflowMode workflowMode)
{
await SafeExecuteAsync(() =>
{
if (_pathPlanningManager == null)
{
throw new InvalidOperationException("路径规划管理器未初始化,无法分析端面。");
}
if (_assemblyTerminalObject == null || !ModelItemAnalysisHelper.IsModelItemValid(_assemblyTerminalObject))
{
throw new InvalidOperationException("终点箱体未设置或已失效,请先捕获终点箱体。");
}
PrepareRailAssemblyWorkflowForMode(
workflowMode,
editActionDescription: "请先选中一条需要编辑安装点的 Rail 路径。",
syncMountModeFromRoute: true);
BeginAssemblyEndFaceSelection(workflowMode);
}, "分析终端端面");
}
private async Task ExecuteSelectAssemblyInstallationPointAsync(RailAssemblyWorkflowMode workflowMode)
{
await SafeExecuteAsync(() =>
{
if (_pathPlanningManager == null)
{
throw new InvalidOperationException("路径规划管理器未初始化,无法选择安装点。");
}
if (_assemblyTerminalObject == null || !ModelItemAnalysisHelper.IsModelItemValid(_assemblyTerminalObject))
{
throw new InvalidOperationException("终点箱体未设置或已失效,请先捕获终点箱体。");
}
PrepareRailAssemblyWorkflowForMode(
workflowMode,
editActionDescription: "请先选中一条需要编辑安装点的 Rail 路径。",
syncMountModeFromRoute: false);
BeginAssemblyInstallationSelection();
}, "选择安装点");
}
private async void OnAssemblyReferenceMouseClicked(object sender, PickItemResult pickResult)
{
try
{
if (!IsSelectingAssemblyStartPoint || pickResult == null)
{
return;
}
Point3D projectedStartPoint = AssemblyReferencePathManager.Instance.ProjectPointToReferenceLine(pickResult.Point);
_assemblyStartPoint = projectedStartPoint;
AssemblyStartPointText = $"({projectedStartPoint.X:F2}, {projectedStartPoint.Y:F2}, {projectedStartPoint.Z:F2})";
LogManager.Info(
$"[直线装配] 点击点=({pickResult.Point.X:F2}, {pickResult.Point.Y:F2}, {pickResult.Point.Z:F2})," +
$"投影起点=({projectedStartPoint.X:F2}, {projectedStartPoint.Y:F2}, {projectedStartPoint.Z:F2})");
await SafeExecuteAsync(() =>
{
ApplyAssemblyStartPointForCurrentWorkflow(projectedStartPoint);
CleanupAssemblyReferenceSelection();
}, "生成终端安装路径");
}
catch (Exception ex)
{
LogManager.Error($"[直线装配] 处理起点拾取失败: {ex.Message}", ex);
UpdateMainStatus($"终端安装起点拾取失败: {ex.Message}");
CleanupAssemblyReferenceSelection();
}
}
private async void OnAssemblyEndFaceMouseClicked(object sender, PickItemResult pickResult)
{
try
{
if (!_isSelectingAssemblyEndFacePoints || pickResult == null)
{
return;
}
await SafeExecuteAsync(() =>
{
if (!IsPickOnAssemblyTerminalObject(pickResult))
{
UpdateMainStatus("请点击当前终点箱体的同一个端面,不要点到其他对象。");
return;
}
_assemblyEndFaceSeedPoints.Add(pickResult.Point);
RenderAssemblyEndFaceSeedPoints();
int pickedCount = _assemblyEndFaceSeedPoints.Count;
LogManager.Info($"[直线装配] 已记录端面种子点 {pickedCount}: ({pickResult.Point.X:F3}, {pickResult.Point.Y:F3}, {pickResult.Point.Z:F3})");
if (pickedCount < 3)
{
UpdateMainStatus($"已记录端面点 {pickedCount}/3,请继续在同一端面平面上点击。");
return;
}
AnalyzeCurrentAssemblyEndFace();
CleanupAssemblyEndFaceSelection(clearVisuals: false);
}, "处理端面三点拾取");
}
catch (Exception ex)
{
LogManager.Error($"[直线装配] 端面三点分析失败: {ex.Message}", ex);
UpdateMainStatus($"端面三点分析失败: {ex.Message}");
CleanupAssemblyEndFaceSelection(clearVisuals: false);
}
}
private async void OnAssemblyInstallationMouseClicked(object sender, PickItemResult pickResult)
{
try
{
if (!_isSelectingAssemblyInstallationPoint || pickResult == null)
{
return;
}
await SafeExecuteAsync(() =>
{
if (!IsPickOnAssemblyTerminalObject(pickResult))
{
UpdateMainStatus("请点击当前终点箱体表面,不要点到其他对象。");
return;
}
_assemblyInstallationSeedPoints.Add(pickResult.Point);
RenderAssemblyInstallationPickPoints(_assemblyInstallationSeedPoints);
int pickedCount = _assemblyInstallationSeedPoints.Count;
LogManager.Info($"[直线装配] 已记录安装面点 {pickedCount}: ({pickResult.Point.X:F3}, {pickResult.Point.Y:F3}, {pickResult.Point.Z:F3})");
if (pickedCount < 2)
{
UpdateMainStatus("已记录安装面点 1/2,请继续在同一安装面上点击第二个点。");
return;
}
BuildAndRenderAssemblyInstallationReference(
_assemblyInstallationSeedPoints[0],
_assemblyInstallationSeedPoints[1]);
ApplyAssemblyInstallationReferenceForCurrentWorkflow();
CleanupAssemblyInstallationSelection(clearVisuals: false);
}, "处理安装点拾取");
}
catch (Exception ex)
{
LogManager.Error($"[直线装配] 安装点计算失败: {ex.Message}", ex);
UpdateMainStatus($"安装点计算失败: {ex.Message}");
CleanupAssemblyInstallationSelection(clearVisuals: true);
}
}
private void PrepareRailAssemblyWorkflowForMode(
RailAssemblyWorkflowMode workflowMode,
string editActionDescription,
bool syncMountModeFromRoute)
{
PathRoute selectedRailRoute = null;
if (IsRailAssemblyEditMode(workflowMode))
{
selectedRailRoute = RequireSelectedRailRouteForAssemblyEdit(editActionDescription);
if (syncMountModeFromRoute)
{
AssemblyMountMode = selectedRailRoute.RailMountMode;
}
}
SetRailAssemblyWorkflowMode(workflowMode);
}
private PathRoute RequireSelectedRailRouteForAssemblyEdit(string errorMessage)
{
var selectedRailRoute = GetSelectedCoreRoute();
if (selectedRailRoute == null || selectedRailRoute.PathType != PathType.Rail)
{
throw new InvalidOperationException(errorMessage);
}
return selectedRailRoute;
}
private void BeginAssemblyStartPointSelection(string statusMessage, string logMessage, string toolPluginFailureMessage)
{
CleanupAssemblyReferenceSelection();
_pathPlanningManager.DisableMouseHandling();
IsSelectingAssemblyStartPoint = true;
NotifyRailAssemblyCommandStateChanged();
PathClickToolPlugin.MouseClicked -= OnAssemblyReferenceMouseClicked;
PathClickToolPlugin.MouseClicked += OnAssemblyReferenceMouseClicked;
if (!ForceReinitializeToolPlugin(subscribeToEvents: false))
{
CleanupAssemblyReferenceSelection();
throw new InvalidOperationException(toolPluginFailureMessage);
}
_pathPlanningManager.StartClickTool(PathPointType.StartPoint);
UpdateMainStatus(statusMessage);
LogManager.Info(logMessage);
}
private void BeginAssemblyEndFaceSelection(RailAssemblyWorkflowMode workflowMode)
{
CleanupAssemblyReferenceSelection();
CleanupAssemblyEndFaceSelection(clearVisuals: false);
CleanupAssemblyInstallationSelection();
ClearAssemblyEndFaceAnalysisVisuals();
ResetAssemblyEndFaceAnalysisState();
ClearAssemblyInstallationReferenceVisuals();
ResetAssemblyInstallationReferenceState();
_assemblyEndFaceSeedPoints.Clear();
_pathPlanningManager.DisableMouseHandling();
_isSelectingAssemblyEndFacePoints = true;
NotifyRailAssemblyCommandStateChanged();
PathClickToolPlugin.MouseClicked -= OnAssemblyEndFaceMouseClicked;
PathClickToolPlugin.MouseClicked += OnAssemblyEndFaceMouseClicked;
if (!ForceReinitializeToolPlugin(subscribeToEvents: false))
{
CleanupAssemblyEndFaceSelection(clearVisuals: false);
throw new InvalidOperationException("ToolPlugin 初始化失败,请重试。");
}
UpdateMainStatus("请在同一个端面平面上连续点击 3 个点,系统将分析端面中心。");
LogManager.Info($"[直线装配] 已进入端面三点分析模式,模式={GetRailAssemblyWorkflowLabel(workflowMode)}");
}
private void BeginAssemblyInstallationSelection()
{
CleanupAssemblyReferenceSelection();
CleanupAssemblyEndFaceSelection(clearVisuals: false);
CleanupAssemblyInstallationSelection();
ClearAssemblyInstallationReferenceVisuals();
_pathPlanningManager.DisableMouseHandling();
_isSelectingAssemblyInstallationPoint = true;
NotifyRailAssemblyCommandStateChanged();
PathClickToolPlugin.MouseClicked -= OnAssemblyInstallationMouseClicked;
PathClickToolPlugin.MouseClicked += OnAssemblyInstallationMouseClicked;
if (!ForceReinitializeToolPlugin(subscribeToEvents: false))
{
CleanupAssemblyInstallationSelection();
throw new InvalidOperationException("ToolPlugin 初始化失败,请重试。");
}
_assemblyInstallationSeedPoints.Clear();
UpdateMainStatus("请在终点箱体表面连续点击两个安装面点,系统将按两点和光轴计算安装参考面与安装点。");
LogManager.Info($"[直线装配] 已进入安装点拾取模式,模式={GetRailAssemblyWorkflowLabel(_railAssemblyWorkflowMode)}");
}
private void ApplyAssemblyStartPointForCurrentWorkflow(Point3D projectedStartPoint)
{
if (IsRailEditingWorkflowActive)
{
UpdateSelectedRailRouteStartPoint(projectedStartPoint);
ClearAssemblyReferenceLineVisuals();
UpdateMainStatus("已根据辅助线更新当前 Rail 路径起点");
return;
}
CreateAssemblyLinearRoute(projectedStartPoint);
UpdateMainStatus("已根据辅助线起点生成终端安装路径");
}
private void ApplyAssemblyInstallationReferenceForCurrentWorkflow()
{
if (IsRailEditingWorkflowActive)
{
var selectedRailRoute = RequireSelectedRailRouteForAssemblyEdit("当前没有可更新安装点的 Rail 路径。");
PersistAssemblyInstallationReferenceToRailRoute(
selectedRailRoute,
"已重选安装点并同步更新 Rail 路径安装参数");
return;
}
LogManager.Info("[直线装配] 安装点已确定,请继续选择起点以生成新路径");
}
private void CreateAssemblyLinearRoute(Point3D startPoint)
{
Point3D endPoint = AssemblyReferencePathManager.Instance.ReferenceLineEnd;
string routeName = $"人工_{DateTime.Now:MMdd_HHmmss}";
var route = new PathRoute(routeName)
{
Description = $"直线装配路径 - {AssemblyTerminalObjectName}",
PathType = PathType.Rail,
RailMountMode = AssemblyMountMode,
RailPathDefinitionMode = RailPathDefinitionMode.RailCenterLine,
RailNormalOffset = UnitsConverter.ConvertFromMeters(AssemblyAnchorVerticalOffsetInMeters)
};
if (_hasAssemblyInstallationReference)
{
route.RailPreferredNormal = CreatePersistedAssemblyPreferredNormal();
}
route.AddPoint(new PathPoint(startPoint, "起点", PathPointType.StartPoint));
route.AddPoint(new PathPoint(endPoint, "装配终点", PathPointType.EndPoint));
if (!_pathPlanningManager.AddRoute(route))
{
throw new InvalidOperationException("装配路径添加失败");
}
_pathPlanningManager.SetCurrentRoute(route);
if (!_pathPlanningManager.GeneratePath(route))
{
throw new InvalidOperationException("装配路径生成失败");
}
if (!_pathPlanningManager.FinishEditing())
{
throw new InvalidOperationException("装配路径完成编辑失败");
}
var generatedRouteViewModel = PathRoutes.FirstOrDefault(p => p.Name == route.Name);
if (generatedRouteViewModel != null)
{
SelectedPathRoute = generatedRouteViewModel;
UpdatePassageSpaceVisualizationForPathType(route.PathType);
}
HideAssemblyReferenceVisuals(
resetStartPointText: false,
statusMessage: null,
logMessage: "[直线装配] 路径生成完成后已隐藏辅助线可视化");
AssemblyTerminalObjectInfo = string.Format(
"对接终点=({0:F2}, {1:F2}, {2:F2}),装配起点={3},安装方式={4},对接基准={5}",
endPoint.X,
endPoint.Y,
endPoint.Z,
AssemblyStartPointText,
AssemblyMountMode == RailMountMode.OverRail ? "轨上安装" : "轨下安装",
GetAssemblyAnchorText());
OnPropertyChanged(nameof(PathRoutes));
LogManager.Info(
$"[直线装配] 已生成并完成路径: {route.Name},起点=({startPoint.X:F2}, {startPoint.Y:F2}, {startPoint.Z:F2}),终点=({endPoint.X:F2}, {endPoint.Y:F2}, {endPoint.Z:F2})" +
$"{(route.RailPreferredNormal != null ? $",安装法向=({route.RailPreferredNormal.X:F3}, {route.RailPreferredNormal.Y:F3}, {route.RailPreferredNormal.Z:F3})" : string.Empty)}");
}
private void UpdateSelectedRailRouteStartPoint(Point3D startPoint)
{
var route = GetSelectedCoreRoute();
if (route == null || route.PathType != PathType.Rail)
{
throw new InvalidOperationException("当前没有可更新起点的 Rail 路径。");
}
int startPointIndex = GetRailRouteStartPointIndex(route);
if (startPointIndex < 0)
{
throw new InvalidOperationException("当前 Rail 路径没有可更新的起点。");
}
if (!_pathPlanningManager.UpdatePathPointWithConstraints(route, startPointIndex, startPoint))
{
throw new InvalidOperationException("更新 Rail 路径起点失败。");
}
route.LastModified = DateTime.Now;
_pathPlanningManager.SavePathToDatabase(route);
_pathPlanningManager.DrawRouteVisualization(route, isAutoPath: false);
_pathPlanningManager.RaiseVisualizationStateChanged();
if (SelectedPathRoute != null && SelectedPathRoute.Id == route.Id)
{
SyncPathViewModelFromCoreRoute(SelectedPathRoute, route, preserveSelection: true);
RefreshSelectedRailReferenceLineVisuals();
}
LogManager.Info($"[Rail构型] {route.Name}: 已重选起点为 ({startPoint.X:F3}, {startPoint.Y:F3}, {startPoint.Z:F3})");
}
private void HideAssemblyReferenceVisuals(bool resetStartPointText, string statusMessage, string logMessage)
{
CleanupAssemblyReferenceSelection();
CleanupAssemblyEndFaceSelection(clearVisuals: false);
ClearAssemblyReferenceLineVisuals();
ClearAssemblyEndFaceAnalysisVisuals();
ClearAssemblyInstallationReferenceVisuals();
if (resetStartPointText)
{
AssemblyStartPointText = "未选择";
}
NotifyRailAssemblyCommandStateChanged();
if (!string.IsNullOrWhiteSpace(statusMessage))
{
UpdateMainStatus(statusMessage);
}
if (!string.IsNullOrWhiteSpace(logMessage))
{
LogManager.Info(logMessage);
}
}
private void ResetRailAssemblyWorkflowState(bool announce, string statusMessage = null, string logMessage = null)
{
CleanupAssemblyReferenceSelection();
CleanupAssemblyEndFaceSelection(clearVisuals: true);
CleanupAssemblyInstallationSelection(clearVisuals: true);
ClearAssemblyReferenceLineVisuals();
ClearAssemblyEndFaceAnalysisVisuals();
ClearAssemblyInstallationReferenceVisuals();
ResetAssemblyEndFaceAnalysisState();
ResetAssemblyInstallationReferenceState();
_railAssemblyContext.ResetSession();
HasAssemblyTerminalObject = false;
AssemblyTerminalObjectName = _assemblyTerminalObjectName;
AssemblyTerminalObjectInfo = _assemblyTerminalObjectInfo;
AssemblyStartPointText = _assemblyStartPointText;
SetRailAssemblyWorkflowMode(RailAssemblyWorkflowMode.None);
NotifyRailAssemblyCommandStateChanged();
if (announce && !string.IsNullOrWhiteSpace(statusMessage))
{
UpdateMainStatus(statusMessage);
}
if (!string.IsNullOrWhiteSpace(logMessage))
{
LogManager.Info(logMessage);
}
}
private void ExecuteCancelRailAssemblyWorkflow()
{
try
{
ResetRailAssemblyWorkflowState(
announce: true,
statusMessage: "已取消 Rail 装配流程",
logMessage: "[直线装配] 已取消 Rail 装配流程并清理状态");
}
catch (Exception ex)
{
LogManager.Error($"[直线装配] 取消 Rail 装配流程失败: {ex.Message}", ex);
UpdateMainStatus($"取消 Rail 装配流程失败: {ex.Message}");
}
}
private Point3D GetAssemblyTerminalAnchorPoint()
{
if (_assemblyTerminalObject == null || !ModelItemAnalysisHelper.IsModelItemValid(_assemblyTerminalObject))
{
throw new InvalidOperationException("终点箱体未设置或已失效,无法计算对接点");
}
if (_hasAssemblyInstallationReference && _assemblyInstallationAnchorPoint != null)
{
return _assemblyInstallationAnchorPoint;
}
HostCoordinateAdapter adapter = CoordinateSystemManager.Instance.CreateHostAdapter();
ProjectReferenceFrame projectFrame = CreateAssemblyProjectReferenceFrame(adapter);
var bounds = _assemblyTerminalObject.BoundingBox();
Point3D canonicalCenter = adapter.ToCanonicalPoint(bounds.Center);
Vector3D canonicalUp = adapter.ToCanonicalVector(
ModelItemTransformHelper.GetDirectionFromTransform(
_assemblyTerminalObject.Transform,
projectFrame.DefaultModelAxisConvention.UpAxis));
double direction = GetAssemblyAnchorDirection();
double verticalOffset = UnitsConverter.ConvertFromMeters(AssemblyAnchorVerticalOffsetInMeters);
Point3D canonicalAnchorPoint = new Point3D(
canonicalCenter.X + canonicalUp.X * verticalOffset * direction,
canonicalCenter.Y + canonicalUp.Y * verticalOffset * direction,
canonicalCenter.Z + canonicalUp.Z * verticalOffset * direction);
return adapter.FromCanonicalPoint(canonicalAnchorPoint);
}
///
/// 根据 Rail 路径终点和路径长度计算起点位置。
/// 起点位于从终点沿光轴方向(远离球心方向)延伸路径长度的位置。
///
private Point3D CalculateRailStartPointFromEndPoint(Point3D endPoint, double pathLength)
{
HostCoordinateAdapter adapter = CoordinateSystemManager.Instance.CreateHostAdapter();
ProjectReferenceFrame projectFrame = CreateAssemblyProjectReferenceFrame(adapter);
Point3D opticalAxisReferencePoint = GetAssemblyOpticalAxisReferencePoint();
Point3D canonicalEndPoint = adapter.ToCanonicalPoint(endPoint);
Point3D canonicalOpticalAxisRef = adapter.ToCanonicalPoint(opticalAxisReferencePoint);
// 计算从球心到光轴参考点的方向(作为参考线方向)
Vector3D direction = new Vector3D(
canonicalOpticalAxisRef.X - projectFrame.SphereCenterInCanonical.X,
canonicalOpticalAxisRef.Y - projectFrame.SphereCenterInCanonical.Y,
canonicalOpticalAxisRef.Z - projectFrame.SphereCenterInCanonical.Z);
direction = direction.Normalize();
// 起点 = 终点 + 方向 * 路径长度
Point3D canonicalStartPoint = new Point3D(
canonicalEndPoint.X + direction.X * pathLength,
canonicalEndPoint.Y + direction.Y * pathLength,
canonicalEndPoint.Z + direction.Z * pathLength);
return adapter.FromCanonicalPoint(canonicalStartPoint);
}
private void UpdateAssemblyInstallationAnchorFromVerticalOffset()
{
if (!_hasAssemblyInstallationReference ||
_assemblyInstallationBaseAnchorPoint == null)
{
return;
}
double offset = UnitsConverter.ConvertFromMeters(_assemblyAnchorVerticalOffsetInMeters);
_assemblyInstallationAnchorPoint = new Point3D(
_assemblyInstallationBaseAnchorPoint.X + _assemblyInstallationPlaneNormal.X * (float)offset,
_assemblyInstallationBaseAnchorPoint.Y + _assemblyInstallationPlaneNormal.Y * (float)offset,
_assemblyInstallationBaseAnchorPoint.Z + _assemblyInstallationPlaneNormal.Z * (float)offset);
if (_hasAssemblyInstallationReference)
{
RenderAssemblyInstallationPickPoints(_assemblyInstallationSeedPoints);
RenderAssemblyInstallationAnchorPoint(_assemblyInstallationAnchorPoint);
RenderAssemblyInstallationCenterLine(_assemblyInstallationPickPoint, GetCurrentAssemblyOpticalAxisDirection());
RenderAssemblyInstallationPlane(_assemblyInstallationAnchorPoint, GetCurrentAssemblyOpticalAxisDirection(), _assemblyInstallationPlaneSpanDirection);
}
else
{
ClearAssemblyInstallationReferenceVisuals();
}
}
private Point3D CreatePersistedAssemblyPreferredNormal()
{
HostCoordinateAdapter adapter = CoordinateSystemManager.Instance.CreateHostAdapter();
Vector3 normalizedPreferredNormal = RailPathPoseHelper.NormalizePreferredNormalToHostUpHemisphere(
_assemblyInstallationPlaneNormal,
adapter.HostUpVector3);
return new Point3D(
normalizedPreferredNormal.X,
normalizedPreferredNormal.Y,
normalizedPreferredNormal.Z);
}
private Vector3 GetCurrentAssemblyOpticalAxisDirection()
{
Point3D opticalAxisReferencePoint = GetAssemblyOpticalAxisReferencePoint();
Point3D sphereCenterPoint = new Point3D(
AssemblySphereCenterX,
AssemblySphereCenterY,
AssemblySphereCenterZ);
Vector3 opticalAxisDirection = new Vector3(
(float)(opticalAxisReferencePoint.X - sphereCenterPoint.X),
(float)(opticalAxisReferencePoint.Y - sphereCenterPoint.Y),
(float)(opticalAxisReferencePoint.Z - sphereCenterPoint.Z));
return Vector3.Normalize(opticalAxisDirection);
}
private Point3D GetAssemblyOpticalAxisReferencePoint()
{
if (_hasAssemblyEndFaceAnalysis && _assemblyEndFaceCenterPoint != null)
{
return _assemblyEndFaceCenterPoint;
}
if (_assemblyTerminalObject == null || !ModelItemAnalysisHelper.IsModelItemValid(_assemblyTerminalObject))
{
throw new InvalidOperationException("终点箱体未设置或已失效,无法计算光轴参考点");
}
return _assemblyTerminalObject.BoundingBox().Center;
}
private AssemblyReferenceLine BuildAssemblyReferenceLine()
{
if (_assemblyTerminalObject == null || !ModelItemAnalysisHelper.IsModelItemValid(_assemblyTerminalObject))
{
throw new InvalidOperationException("终点箱体未设置或已失效,无法生成装配参考线");
}
HostCoordinateAdapter adapter = CoordinateSystemManager.Instance.CreateHostAdapter();
ProjectReferenceFrame projectFrame = CreateAssemblyProjectReferenceFrame(adapter);
Point3D opticalAxisReferencePoint = GetAssemblyOpticalAxisReferencePoint();
Point3D endPoint = GetAssemblyTerminalAnchorPoint();
Point3D canonicalAxisReferencePoint = adapter.ToCanonicalPoint(opticalAxisReferencePoint);
Point3D canonicalEndPoint = adapter.ToCanonicalPoint(endPoint);
Vector3D direction = new Vector3D(
canonicalAxisReferencePoint.X - projectFrame.SphereCenterInCanonical.X,
canonicalAxisReferencePoint.Y - projectFrame.SphereCenterInCanonical.Y,
canonicalAxisReferencePoint.Z - projectFrame.SphereCenterInCanonical.Z);
double directionLengthSquared = direction.X * direction.X + direction.Y * direction.Y + direction.Z * direction.Z;
if (directionLengthSquared < 1e-9)
{
throw new InvalidOperationException("光轴参考点与项目球心重合,无法生成装配参考线方向");
}
direction = direction.Normalize();
double rodLength = UnitsConverter.ConvertFromMeters(AssemblyReferenceRodLengthInMeters);
Point3D canonicalStartPoint = new Point3D(
canonicalEndPoint.X + direction.X * rodLength,
canonicalEndPoint.Y + direction.Y * rodLength,
canonicalEndPoint.Z + direction.Z * rodLength);
Point3D startPoint = adapter.FromCanonicalPoint(canonicalStartPoint);
LogManager.Info(
$"[直线装配] 参考线已计算,终点锚点=({endPoint.X:F2}, {endPoint.Y:F2}, {endPoint.Z:F2}), " +
$"光轴参考点=({opticalAxisReferencePoint.X:F2}, {opticalAxisReferencePoint.Y:F2}, {opticalAxisReferencePoint.Z:F2}), " +
$"参考线外端=({startPoint.X:F2}, {startPoint.Y:F2}, {startPoint.Z:F2}), " +
$"球心到光轴参考点方向(内部坐标)=({direction.X:F3}, {direction.Y:F3}, {direction.Z:F3})");
return new AssemblyReferenceLine(startPoint, endPoint, direction);
}
private void RefreshAssemblyTerminalObjectInfo(Point3D referenceStartPoint = null, Point3D referenceEndPoint = null)
{
if (_assemblyTerminalObject == null || !ModelItemAnalysisHelper.IsModelItemValid(_assemblyTerminalObject))
{
return;
}
var bounds = _assemblyTerminalObject.BoundingBox();
string anchorText = GetAssemblyAnchorText();
string mountText = AssemblyMountMode == RailMountMode.OverRail ? "轨上安装" : "轨下安装";
Point3D opticalAxisReferencePoint = GetAssemblyOpticalAxisReferencePoint();
string opticalAxisText = _hasAssemblyEndFaceAnalysis
? string.Format("端面中心=({0:F2}, {1:F2}, {2:F2})", opticalAxisReferencePoint.X, opticalAxisReferencePoint.Y, opticalAxisReferencePoint.Z)
: string.Format("箱体中心=({0:F2}, {1:F2}, {2:F2})", opticalAxisReferencePoint.X, opticalAxisReferencePoint.Y, opticalAxisReferencePoint.Z);
string anchorPointText = _hasAssemblyInstallationReference
? string.Format("{0}点=({1:F2}, {2:F2}, {3:F2})",
anchorText,
_assemblyInstallationAnchorPoint.X,
_assemblyInstallationAnchorPoint.Y,
_assemblyInstallationAnchorPoint.Z)
: string.Format("{0}点=未选择", anchorText);
string installationText = _hasAssemblyInstallationReference
? string.Format(
"安装面点中点=({0:F2}, {1:F2}, {2:F2}),安装点=({3:F2}, {4:F2}, {5:F2}),偏距={6:F3}m",
_assemblyInstallationPickPoint.X,
_assemblyInstallationPickPoint.Y,
_assemblyInstallationPickPoint.Z,
_assemblyInstallationAnchorPoint.X,
_assemblyInstallationAnchorPoint.Y,
_assemblyInstallationAnchorPoint.Z,
_assemblyInstallationOffsetDistanceInMeters)
: "安装点=未选择";
if (referenceStartPoint != null && referenceEndPoint != null)
{
AssemblyTerminalObjectInfo = string.Format(
"中心=({0:F2}, {1:F2}, {2:F2}),尺寸=({3:F2}, {4:F2}, {5:F2}),{6},{7},{8},{9},垂直偏移={10:F3}m,辅助线外端=({11:F2}, {12:F2}, {13:F2})",
bounds.Center.X,
bounds.Center.Y,
bounds.Center.Z,
bounds.Max.X - bounds.Min.X,
bounds.Max.Y - bounds.Min.Y,
bounds.Max.Z - bounds.Min.Z,
mountText,
anchorPointText,
opticalAxisText,
installationText,
AssemblyAnchorVerticalOffsetInMeters,
referenceStartPoint.X,
referenceStartPoint.Y,
referenceStartPoint.Z);
return;
}
AssemblyTerminalObjectInfo = string.Format(
"中心=({0:F2}, {1:F2}, {2:F2}),尺寸=({3:F2}, {4:F2}, {5:F2}),{6},{7},{8},{9},垂直偏移={10:F3}m",
bounds.Center.X,
bounds.Center.Y,
bounds.Center.Z,
bounds.Max.X - bounds.Min.X,
bounds.Max.Y - bounds.Min.Y,
bounds.Max.Z - bounds.Min.Z,
mountText,
anchorPointText,
opticalAxisText,
installationText,
AssemblyAnchorVerticalOffsetInMeters);
}
private void RefreshAssemblyReferenceRodIfNeeded()
{
if (!ShouldShowAssemblyReferenceLineVisuals())
{
ClearAssemblyReferenceLineVisuals();
return;
}
if (_assemblyTerminalObject == null)
{
return;
}
try
{
AssemblyReferenceLine referenceLine = BuildAssemblyReferenceLine();
AssemblyReferencePathManager.Instance.CreateOrUpdateReferenceRod(
referenceLine.StartPoint,
referenceLine.EndPoint,
AssemblyReferenceRodDiameterInMeters);
RefreshAssemblyTerminalObjectInfo(referenceLine.StartPoint, referenceLine.EndPoint);
RenderAssemblyAnchorMarker();
RenderAssemblyCenterGuideLine();
RenderAssemblyReferenceLine(referenceLine);
NotifyRailAssemblyCommandStateChanged();
}
catch (Exception ex)
{
LogManager.Error($"[直线装配] 刷新参考杆失败: {ex.Message}", ex);
}
}
private void InitializeAssemblyAnchorVerticalOffsetFromTerminalObject()
{
if (_assemblyTerminalObject == null || !ModelItemAnalysisHelper.IsModelItemValid(_assemblyTerminalObject))
{
return;
}
HostCoordinateAdapter adapter = CoordinateSystemManager.Instance.CreateHostAdapter();
ProjectReferenceFrame projectFrame = CreateAssemblyProjectReferenceFrame(adapter);
var bounds = _assemblyTerminalObject.BoundingBox();
double suggestedOffset = UnitsConverter.ConvertToMeters(
Math.Max(0.0, projectFrame.DefaultModelAxisConvention.GetUpSize(bounds) / 2.0));
_assemblyAnchorVerticalOffsetInMeters = suggestedOffset;
OnPropertyChanged(nameof(AssemblyAnchorVerticalOffsetInMeters));
}
private double GetAssemblyAnchorDirection()
{
return PathRoute.IsTopPayloadAnchorForMountMode(AssemblyMountMode)
? 1.0
: -1.0;
}
private string GetAssemblyAnchorText()
{
return PathRoute.IsTopPayloadAnchorForMountMode(AssemblyMountMode)
? "顶面对接"
: "底面对接";
}
private sealed class AssemblyReferenceLine
{
public AssemblyReferenceLine(Point3D startPoint, Point3D endPoint, Vector3D direction)
{
StartPoint = startPoint;
EndPoint = endPoint;
Direction = direction;
}
public Point3D StartPoint { get; }
public Point3D EndPoint { get; }
public Vector3D Direction { get; }
}
private static double CalculateDefaultAssemblyReferenceRodDiameterInMeters()
{
double standardRadiusInMeters = ConfigManager.Instance.Current.PathEditing.CellSizeMeters;
standardRadiusInMeters = Math.Max(0.1, Math.Min(0.5, standardRadiusInMeters));
return standardRadiusInMeters * 0.8;
}
private void RenderAssemblyAnchorMarker()
{
if (_assemblyTerminalObject == null || !ModelItemAnalysisHelper.IsModelItemValid(_assemblyTerminalObject))
{
return;
}
var renderPlugin = PathPointRenderPlugin.Instance;
if (renderPlugin == null)
{
return;
}
Point3D anchorPoint = GetAssemblyTerminalAnchorPoint();
LogManager.Info(
$"[直线装配] 已渲染终点锚点标记: ({anchorPoint.X:F2}, {anchorPoint.Y:F2}, {anchorPoint.Z:F2})");
var markerRoute = new PathRoute("装配对接点")
{
Id = AssemblyAnchorMarkerPathId,
Description = "直线装配终点对接标记"
};
AddVisualizationPoint(markerRoute, anchorPoint, "对接点", PathPointType.EndPoint);
renderPlugin.RenderPointOnly(markerRoute);
}
private void RenderAssemblyReferenceLine(AssemblyReferenceLine referenceLine)
{
var renderPlugin = PathPointRenderPlugin.Instance;
if (renderPlugin == null || referenceLine == null)
{
return;
}
renderPlugin.RenderRailBaseline(
AssemblyReferenceLinePathId,
new List { referenceLine.EndPoint, referenceLine.StartPoint });
LogManager.Info(
$"[直线装配] 已渲染参考线: 终点锚点=({referenceLine.EndPoint.X:F2}, {referenceLine.EndPoint.Y:F2}, {referenceLine.EndPoint.Z:F2}), " +
$"参考线外端=({referenceLine.StartPoint.X:F2}, {referenceLine.StartPoint.Y:F2}, {referenceLine.StartPoint.Z:F2})");
}
private void RenderAssemblyCenterGuideLine()
{
if (_assemblyTerminalObject == null || !ModelItemAnalysisHelper.IsModelItemValid(_assemblyTerminalObject))
{
return;
}
var renderPlugin = PathPointRenderPlugin.Instance;
if (renderPlugin == null)
{
return;
}
HostCoordinateAdapter adapter = CoordinateSystemManager.Instance.CreateHostAdapter();
ProjectReferenceFrame projectFrame = CreateAssemblyProjectReferenceFrame(adapter);
Point3D centerPoint = GetAssemblyOpticalAxisReferencePoint();
Point3D sphereCenterPoint = adapter.FromCanonicalPoint(projectFrame.SphereCenterInCanonical);
renderPlugin.RenderRailBaseline(
AssemblyCenterGuideLinePathId,
new List { sphereCenterPoint, centerPoint },
RenderStyleName.AssemblyGuideLine);
LogManager.Info(
$"[直线装配] 已渲染球心到光轴参考点基准线: 球心=({sphereCenterPoint.X:F2}, {sphereCenterPoint.Y:F2}, {sphereCenterPoint.Z:F2}), " +
$"光轴参考点=({centerPoint.X:F2}, {centerPoint.Y:F2}, {centerPoint.Z:F2})");
}
private void RenderAssemblyEndFaceSeedPoints()
{
var renderPlugin = PathPointRenderPlugin.Instance;
if (renderPlugin == null)
{
return;
}
renderPlugin.RemovePath(AssemblyEndFaceSeedPathId);
if (_assemblyEndFaceSeedPoints.Count == 0)
{
return;
}
var markerRoute = new PathRoute("端面三点")
{
Id = AssemblyEndFaceSeedPathId,
Description = "终端安装端面三点"
};
for (int i = 0; i < _assemblyEndFaceSeedPoints.Count; i++)
{
AddVisualizationPoint(
markerRoute,
_assemblyEndFaceSeedPoints[i],
$"端面点{i + 1}",
PathPointType.WayPoint,
UnitsConverter.ConvertFromMeters(AssemblyVisualizationPointDiameterInMeters));
}
renderPlugin.RenderPointOnly(markerRoute);
}
private void RenderAssemblyEndFaceCenter(Point3D centerPoint)
{
var renderPlugin = PathPointRenderPlugin.Instance;
if (renderPlugin == null)
{
return;
}
renderPlugin.RemovePath(AssemblyEndFaceCenterPathId);
var markerRoute = new PathRoute("端面中心")
{
Id = AssemblyEndFaceCenterPathId,
Description = "终端安装端面中心"
};
AddVisualizationPoint(
markerRoute,
centerPoint,
"端面中心",
PathPointType.WayPoint,
UnitsConverter.ConvertFromMeters(AssemblyVisualizationPointDiameterInMeters));
renderPlugin.RenderPointOnly(markerRoute);
}
private static void AddVisualizationPoint(
PathRoute route,
Point3D position,
string name,
PathPointType type,
double? visualizationDiameter = null)
{
if (route == null)
{
throw new ArgumentNullException(nameof(route));
}
route.Points.Add(new PathPoint(position, name, type)
{
Index = route.Points.Count,
VisualizationDiameter = visualizationDiameter
});
}
private void RenderAssemblyEndFaceNormal(Point3D centerPoint, Vector3 normal)
{
var renderPlugin = PathPointRenderPlugin.Instance;
if (renderPlugin == null)
{
return;
}
renderPlugin.ClearRailBaseline(AssemblyEndFaceNormalPathId);
double lineLength = UnitsConverter.ConvertFromMeters(Math.Max(0.5, AssemblyReferenceRodDiameterInMeters * 4.0));
Point3D normalEndPoint = new Point3D(
centerPoint.X + normal.X * (float)lineLength,
centerPoint.Y + normal.Y * (float)lineLength,
centerPoint.Z + normal.Z * (float)lineLength);
renderPlugin.RenderRailBaseline(
AssemblyEndFaceNormalPathId,
new List { centerPoint, normalEndPoint },
RenderStyleName.AssemblyGuideLine);
}
private void BuildAndRenderAssemblyInstallationReference(Point3D firstPickPoint, Point3D secondPickPoint)
{
Point3D opticalAxisReferencePoint = GetAssemblyOpticalAxisReferencePoint();
Point3D sphereCenterPoint = new Point3D(
AssemblySphereCenterX,
AssemblySphereCenterY,
AssemblySphereCenterZ);
Vector3 opticalAxisDirection = new Vector3(
(float)(opticalAxisReferencePoint.X - sphereCenterPoint.X),
(float)(opticalAxisReferencePoint.Y - sphereCenterPoint.Y),
(float)(opticalAxisReferencePoint.Z - sphereCenterPoint.Z));
AssemblyInstallationReferenceResult result = AssemblyInstallationReferenceBuilder.Build(
new Vector3((float)opticalAxisReferencePoint.X, (float)opticalAxisReferencePoint.Y, (float)opticalAxisReferencePoint.Z),
opticalAxisDirection,
new Vector3((float)firstPickPoint.X, (float)firstPickPoint.Y, (float)firstPickPoint.Z),
new Vector3((float)secondPickPoint.X, (float)secondPickPoint.Y, (float)secondPickPoint.Z));
_hasAssemblyInstallationReference = true;
_assemblyInstallationPickPoint = AssemblyEndFaceAnalyzer.ToPoint3D((result.PickPoint + result.SecondaryPickPoint) * 0.5f);
_assemblyInstallationBaseAnchorPoint = AssemblyEndFaceAnalyzer.ToPoint3D(result.AnchorPoint);
_assemblyInstallationPlaneNormal = result.PlaneNormal;
_assemblyInstallationPlaneSpanDirection = result.PlaneSpanDirection;
_assemblyInstallationOffsetDistanceInMeters = UnitsConverter.ConvertToMeters(result.OffsetDistance);
_assemblyAnchorVerticalOffsetInMeters = DefaultAssemblyAnchorVerticalOffsetInMeters;
OnPropertyChanged(nameof(AssemblyAnchorVerticalOffsetInMeters));
UpdateAssemblyInstallationAnchorFromVerticalOffset();
RenderAssemblyInstallationPickPoints(_assemblyInstallationSeedPoints);
RenderAssemblyInstallationCenterLine(AssemblyEndFaceAnalyzer.ToPoint3D(result.InstallLineBasePoint), result.OpticalAxisDirection);
RefreshAssemblyTerminalObjectInfo();
RefreshAssemblyReferenceRodIfNeeded();
UpdateMainStatus(
$"安装参考已计算:安装点=({_assemblyInstallationAnchorPoint.X:F2}, {_assemblyInstallationAnchorPoint.Y:F2}, {_assemblyInstallationAnchorPoint.Z:F2}),偏距={_assemblyInstallationOffsetDistanceInMeters:F3}m,可继续取起点");
LogManager.Info(
$"[直线装配] 安装参考已计算: 安装面点1=({firstPickPoint.X:F3}, {firstPickPoint.Y:F3}, {firstPickPoint.Z:F3}), " +
$"安装面点2=({secondPickPoint.X:F3}, {secondPickPoint.Y:F3}, {secondPickPoint.Z:F3}), " +
$"安装点=({_assemblyInstallationAnchorPoint.X:F3}, {_assemblyInstallationAnchorPoint.Y:F3}, {_assemblyInstallationAnchorPoint.Z:F3}), " +
$"偏距={_assemblyInstallationOffsetDistanceInMeters:F3}m, 平面法向=({result.PlaneNormal.X:F4}, {result.PlaneNormal.Y:F4}, {result.PlaneNormal.Z:F4})");
NotifyRailAssemblyCommandStateChanged();
}
private void RenderAssemblyInstallationPickPoints(IReadOnlyList pickPoints)
{
var renderPlugin = PathPointRenderPlugin.Instance;
if (renderPlugin == null)
{
return;
}
renderPlugin.RemovePath(AssemblyInstallationPickPointPathId);
var markerRoute = new PathRoute("安装点")
{
Id = AssemblyInstallationPickPointPathId,
Description = "终端安装点"
};
for (int i = 0; i < pickPoints.Count; i++)
{
AddVisualizationPoint(
markerRoute,
pickPoints[i],
$"安装面点{i + 1}",
PathPointType.WayPoint,
UnitsConverter.ConvertFromMeters(AssemblyVisualizationPointDiameterInMeters));
}
renderPlugin.RenderPointOnly(markerRoute);
}
private void RenderAssemblyInstallationAnchorPoint(Point3D anchorPoint)
{
var renderPlugin = PathPointRenderPlugin.Instance;
if (renderPlugin == null)
{
return;
}
renderPlugin.RemovePath(AssemblyInstallationAnchorPointPathId);
var markerRoute = new PathRoute("安装点")
{
Id = AssemblyInstallationAnchorPointPathId,
Description = "终端安装点"
};
AddVisualizationPoint(
markerRoute,
anchorPoint,
"安装点",
PathPointType.WayPoint,
UnitsConverter.ConvertFromMeters(AssemblyVisualizationPointDiameterInMeters));
renderPlugin.RenderPointOnly(markerRoute);
}
private void RenderAssemblyInstallationCenterLine(Point3D lineBasePoint, Vector3 axisDirection)
{
var renderPlugin = PathPointRenderPlugin.Instance;
if (renderPlugin == null)
{
return;
}
renderPlugin.ClearRailBaseline(AssemblyInstallationCenterLinePathId);
double halfLength = GetAssemblyInstallationPlaneSideLength() * AssemblyInstallationLineLengthScale / 2.0;
Point3D startPoint = new Point3D(
lineBasePoint.X - axisDirection.X * (float)halfLength,
lineBasePoint.Y - axisDirection.Y * (float)halfLength,
lineBasePoint.Z - axisDirection.Z * (float)halfLength);
Point3D endPoint = new Point3D(
lineBasePoint.X + axisDirection.X * (float)halfLength,
lineBasePoint.Y + axisDirection.Y * (float)halfLength,
lineBasePoint.Z + axisDirection.Z * (float)halfLength);
renderPlugin.RenderRailBaseline(
AssemblyInstallationCenterLinePathId,
new List { startPoint, endPoint },
RenderStyleName.AssemblyGuideLine);
}
private void RenderAssemblyInstallationPlane(Point3D planeCenter, Vector3 axisDirection, Vector3 planeSpanDirection)
{
var renderPlugin = PathPointRenderPlugin.Instance;
if (renderPlugin == null)
{
return;
}
double sideLength = GetAssemblyInstallationPlaneSideLength();
double halfLength = sideLength / 2.0;
double thickness = UnitsConverter.ConvertFromMeters(0.01);
Vector3D xVector = new Vector3D(
axisDirection.X * (float)sideLength,
axisDirection.Y * (float)sideLength,
axisDirection.Z * (float)sideLength);
Vector3D yVector = new Vector3D(
planeSpanDirection.X * (float)sideLength,
planeSpanDirection.Y * (float)sideLength,
planeSpanDirection.Z * (float)sideLength);
Point3D origin = new Point3D(
planeCenter.X - axisDirection.X * (float)halfLength - planeSpanDirection.X * (float)halfLength + _assemblyInstallationPlaneNormal.X * thickness,
planeCenter.Y - axisDirection.Y * (float)halfLength - planeSpanDirection.Y * (float)halfLength + _assemblyInstallationPlaneNormal.Y * thickness,
planeCenter.Z - axisDirection.Z * (float)halfLength - planeSpanDirection.Z * (float)halfLength + _assemblyInstallationPlaneNormal.Z * thickness);
renderPlugin.RemovePath(AssemblyInstallationPlanePathId);
renderPlugin.RenderRectanglePlane(
AssemblyInstallationPlanePathId,
origin,
xVector,
yVector,
RenderStyleName.AssemblyInstallationPlane,
filled: true);
}
private double GetAssemblyInstallationPlaneSideLength()
{
if (_assemblyTerminalObject == null || !ModelItemAnalysisHelper.IsModelItemValid(_assemblyTerminalObject))
{
return UnitsConverter.ConvertFromMeters(1.0);
}
BoundingBox3D bounds = _assemblyTerminalObject.BoundingBox();
double maxSize = Math.Max(
bounds.Max.X - bounds.Min.X,
Math.Max(bounds.Max.Y - bounds.Min.Y, bounds.Max.Z - bounds.Min.Z));
return maxSize + UnitsConverter.ConvertFromMeters(AssemblyInstallationPlanePaddingInMeters) * 2.0;
}
private ProjectReferenceFrame CreateAssemblyProjectReferenceFrame(HostCoordinateAdapter adapter)
{
if (adapter == null)
{
throw new ArgumentNullException(nameof(adapter));
}
Point3D hostSphereCenter = new Point3D(
AssemblySphereCenterX,
AssemblySphereCenterY,
AssemblySphereCenterZ);
Point3D canonicalSphereCenter = adapter.ToCanonicalPoint(hostSphereCenter);
return new ProjectReferenceFrame(
canonicalSphereCenter,
HostCoordinateAdapter.CanonicalUp,
ModelAxisConvention.CreateDefaultForHost(adapter.HostType));
}
private void ClearAssemblyAnchorMarker()
{
var renderPlugin = PathPointRenderPlugin.Instance;
if (renderPlugin == null)
{
return;
}
renderPlugin.RemovePath(AssemblyAnchorMarkerPathId);
}
private void ClearAssemblyReferenceLine()
{
var renderPlugin = PathPointRenderPlugin.Instance;
if (renderPlugin == null)
{
return;
}
renderPlugin.ClearRailBaseline(AssemblyReferenceLinePathId);
}
private void ClearAssemblyCenterGuideLine()
{
var renderPlugin = PathPointRenderPlugin.Instance;
if (renderPlugin == null)
{
return;
}
renderPlugin.ClearRailBaseline(AssemblyCenterGuideLinePathId);
}
private void ClearAssemblyReferenceLineVisuals()
{
AssemblyReferencePathManager.Instance.HideReferenceRod();
ClearAssemblyAnchorMarker();
ClearAssemblyCenterGuideLine();
ClearAssemblyReferenceLine();
NotifyRailAssemblyCommandStateChanged();
OnPropertyChanged(nameof(CanRepositionRailStartPoint));
}
private void ClearAssemblyEndFaceAnalysisVisuals()
{
var renderPlugin = PathPointRenderPlugin.Instance;
if (renderPlugin == null)
{
return;
}
renderPlugin.RemovePath(AssemblyEndFaceSeedPathId);
renderPlugin.RemovePath(AssemblyEndFaceCenterPathId);
renderPlugin.ClearRailBaseline(AssemblyEndFaceNormalPathId);
}
private void ResetAssemblyEndFaceAnalysisState()
{
_railAssemblyContext.ResetEndFaceAnalysis();
}
private void ClearAssemblyInstallationReferenceVisuals()
{
var renderPlugin = PathPointRenderPlugin.Instance;
if (renderPlugin == null)
{
return;
}
renderPlugin.RemovePath(AssemblyInstallationPickPointPathId);
renderPlugin.RemovePath(AssemblyInstallationAnchorPointPathId);
renderPlugin.ClearRailBaseline(AssemblyInstallationCenterLinePathId);
renderPlugin.ClearRailBaseline(AssemblyInstallationOffsetLinePathId);
renderPlugin.RemovePath(AssemblyInstallationPlanePathId);
}
private void ResetAssemblyInstallationReferenceState()
{
_railAssemblyContext.ResetInstallationReference();
OnPropertyChanged(nameof(AssemblyAnchorVerticalOffsetInMeters));
NotifyRailAssemblyCommandStateChanged();
}
private void CleanupAssemblyReferenceSelection()
{
try
{
PathClickToolPlugin.MouseClicked -= OnAssemblyReferenceMouseClicked;
IsSelectingAssemblyStartPoint = false;
_pathPlanningManager?.EnableMouseHandling();
_pathPlanningManager?.StopClickTool();
NotifyRailAssemblyCommandStateChanged();
}
catch (Exception ex)
{
LogManager.Error($"[直线装配] 清理起点拾取状态失败: {ex.Message}", ex);
}
}
private void CleanupAssemblyInstallationSelection(bool clearVisuals = true)
{
try
{
PathClickToolPlugin.MouseClicked -= OnAssemblyInstallationMouseClicked;
_isSelectingAssemblyInstallationPoint = false;
_pathPlanningManager?.EnableMouseHandling();
_pathPlanningManager?.StopClickTool();
if (clearVisuals)
{
ClearAssemblyInstallationReferenceVisuals();
}
NotifyRailAssemblyCommandStateChanged();
}
catch (Exception ex)
{
LogManager.Error($"[直线装配] 清理参考安装点拾取状态失败: {ex.Message}", ex);
}
}
private void CleanupAssemblyEndFaceSelection(bool clearVisuals)
{
try
{
PathClickToolPlugin.MouseClicked -= OnAssemblyEndFaceMouseClicked;
_isSelectingAssemblyEndFacePoints = false;
_pathPlanningManager?.EnableMouseHandling();
_pathPlanningManager?.StopClickTool();
if (clearVisuals)
{
_assemblyEndFaceSeedPoints.Clear();
ClearAssemblyEndFaceAnalysisVisuals();
ResetAssemblyEndFaceAnalysisState();
}
NotifyRailAssemblyCommandStateChanged();
}
catch (Exception ex)
{
LogManager.Error($"[直线装配] 清理端面三点拾取状态失败: {ex.Message}", ex);
}
}
private bool IsPickOnAssemblyTerminalObject(PickItemResult pickResult)
{
if (pickResult?.ModelItem == null || _assemblyTerminalObject == null)
{
return false;
}
if (ModelItemAnalysisHelper.ModelItemEquals(pickResult.ModelItem, _assemblyTerminalObject))
{
return true;
}
return pickResult.ModelItem.AncestorsAndSelf.Any(ancestor => ModelItemAnalysisHelper.ModelItemEquals(ancestor, _assemblyTerminalObject));
}
private void AnalyzeCurrentAssemblyEndFace()
{
if (_assemblyEndFaceSeedPoints.Count != 3)
{
throw new InvalidOperationException("端面分析需要恰好 3 个种子点。");
}
var triangles = GeometryHelper.ExtractTriangles(new[] { _assemblyTerminalObject })
.Select(triangle => new AnalysisTriangle3(
new Vector3((float)triangle.Point1.X, (float)triangle.Point1.Y, (float)triangle.Point1.Z),
new Vector3((float)triangle.Point2.X, (float)triangle.Point2.Y, (float)triangle.Point2.Z),
new Vector3((float)triangle.Point3.X, (float)triangle.Point3.Y, (float)triangle.Point3.Z)))
.ToList();
EndFaceAnalysisResult result = AssemblyEndFaceAnalyzer.Analyze(
triangles,
new Vector3((float)_assemblyEndFaceSeedPoints[0].X, (float)_assemblyEndFaceSeedPoints[0].Y, (float)_assemblyEndFaceSeedPoints[0].Z),
new Vector3((float)_assemblyEndFaceSeedPoints[1].X, (float)_assemblyEndFaceSeedPoints[1].Y, (float)_assemblyEndFaceSeedPoints[1].Z),
new Vector3((float)_assemblyEndFaceSeedPoints[2].X, (float)_assemblyEndFaceSeedPoints[2].Y, (float)_assemblyEndFaceSeedPoints[2].Z));
if (!result.IsReliable)
{
throw new InvalidOperationException(result.DiagnosticMessage);
}
Point3D centerPoint = AssemblyEndFaceAnalyzer.ToPoint3D(result.Center);
Point3D sphereCenterPoint = new Point3D(
AssemblySphereCenterX,
AssemblySphereCenterY,
AssemblySphereCenterZ);
Vector3 orientedNormal = AssemblyEndFaceAnalyzer.OrientNormalTowardTarget(
result.Normal,
new Vector3((float)centerPoint.X, (float)centerPoint.Y, (float)centerPoint.Z),
new Vector3((float)sphereCenterPoint.X, (float)sphereCenterPoint.Y, (float)sphereCenterPoint.Z));
_hasAssemblyEndFaceAnalysis = true;
_assemblyEndFaceCenterPoint = centerPoint;
_assemblyEndFaceNormal = orientedNormal;
RenderAssemblyEndFaceSeedPoints();
RenderAssemblyEndFaceCenter(centerPoint);
RenderAssemblyEndFaceNormal(centerPoint, orientedNormal);
RefreshAssemblyTerminalObjectInfo();
RefreshAssemblyReferenceRodIfNeeded();
UpdateMainStatus($"端面分析完成:中心=({centerPoint.X:F2}, {centerPoint.Y:F2}, {centerPoint.Z:F2}),候选三角形={result.CandidateTriangleCount}");
LogManager.Info(
$"[直线装配] 端面分析完成: 中心=({centerPoint.X:F3}, {centerPoint.Y:F3}, {centerPoint.Z:F3}), " +
$"法向=({orientedNormal.X:F4}, {orientedNormal.Y:F4}, {orientedNormal.Z:F4}), " +
$"三角形={result.CandidateTriangleCount}, 顶点={result.CandidateVertexCount}, 偏差={result.MaxPlaneDeviation:F6}");
}
private async Task ExecuteNewPathAsync()
{
await SafeExecuteAsync(() =>
{
UpdateMainStatus("正在创建新路径...");
// 清除所有现有路径的可视化显示
if (PathPointRenderPlugin.Instance != null)
{
try
{
PathPointRenderPlugin.Instance.ClearPathsExcept("grid_visualization_all", "grid_visualization_channel", "grid_visualization_unknown", "grid_visualization_obstacle", "grid_visualization_door");
LogManager.Info("新建路径:已清除现有路径可视化显示(保留网格可视化)");
}
catch (Exception ex)
{
LogManager.Error($"新建路径:清除现有路径可视化失败: {ex.Message}", ex);
throw; // 重新抛出异常,让上层处理
}
}
if (_pathPlanningManager != null)
{
var newRoute = _pathPlanningManager.StartCreatingNewRoute();
if (newRoute != null)
{
// 创建对应的 WPF ViewModel
var newPathViewModel = new PathRouteViewModel
{
Route = newRoute,
IsActive = true
};
// 使用统一的辅助方法创建路径点
var pathPoints = CreatePathPointViewModelsFromCoreRoute(newRoute);
foreach (var point in pathPoints)
{
newPathViewModel.Points.Add(point);
}
// 添加到 UI 列表并选中
PathRoutes.Add(newPathViewModel);
SelectedPathRoute = newPathViewModel;
// 强制重新初始化ToolPlugin以确保获得鼠标焦点,新建路径需要订阅事件
if (!ForceReinitializeToolPlugin(subscribeToEvents: true))
{
UpdateMainStatus("ToolPlugin初始化失败,请重试");
LogManager.Error("新建路径:ToolPlugin初始化失败");
return;
}
UpdateMainStatus($"已进入新建路径模式: {newRoute.Name} - 请在3D视图中点击设置路径点");
LogManager.Info($"开始新建路径: {newRoute.Name},已强制重新初始化ToolPlugin获取鼠标焦点");
// 不再显示对话框,状态文本已提供足够的反馈
}
else
{
UpdateMainStatus("创建新路径失败:没有可通行的物流模型");
LogManager.Error("创建新路径失败:没有可通行的物流模型");
MessageBox.Show("创建新路径失败:没有找到任何可通行的物流模型。\n请先为模型设置可通行的物流属性,然后再尝试创建路径。", "错误",
MessageBoxButton.OK, MessageBoxImage.Warning);
}
}
else
{
UpdateMainStatus("路径规划管理器未初始化");
LogManager.Error("路径规划管理器未初始化");
}
}, "新建路径");
}
private async Task ExecuteNewRailPathAsync()
{
await SafeExecuteAsync(() =>
{
UpdateMainStatus("正在创建传统空轨路径...");
// 检查是否有空轨基准路径
if (PathPointRenderPlugin.Instance != null)
{
try
{
// 清除所有现有路径的可视化显示
PathPointRenderPlugin.Instance.ClearPathsExcept("grid_visualization_all", "grid_visualization_channel", "grid_visualization_unknown", "grid_visualization_obstacle", "grid_visualization_door");
LogManager.Info("新建传统空轨路径:已清除现有路径可视化显示(保留网格可视化)");
}
catch (Exception ex)
{
LogManager.Error($"新建传统空轨路径:清除现有路径可视化失败: {ex.Message}", ex);
throw;
}
}
// 自动提取并显示所有空轨模型的基准线
ExtractAndRenderRailBaselinePaths();
if (_pathPlanningManager != null)
{
// 创建传统空轨路径
var newRoute = _pathPlanningManager.StartCreatingNewRoute(
isRailPath: true,
pathType: PathType.Rail);
if (newRoute != null)
{
// 创建对应的 WPF ViewModel
var newPathViewModel = new PathRouteViewModel
{
Route = newRoute,
IsActive = true,
PathType = PathType.Rail
};
// 使用统一的辅助方法创建路径点
var pathPoints = CreatePathPointViewModelsFromCoreRoute(newRoute);
foreach (var point in pathPoints)
{
newPathViewModel.Points.Add(point);
}
// 添加到 UI 列表并选中
PathRoutes.Add(newPathViewModel);
SelectedPathRoute = newPathViewModel;
// 强制重新初始化ToolPlugin以确保获得鼠标焦点
if (!ForceReinitializeToolPlugin(subscribeToEvents: true))
{
UpdateMainStatus("ToolPlugin初始化失败,请重试");
LogManager.Error("新建传统空轨路径:ToolPlugin初始化失败");
return;
}
// 启动点击工具,设置空轨吸附模式
_pathPlanningManager.StartClickTool(PathPointType.WayPoint, enableRailSnapping: true);
LogManager.Info($"已启动传统空轨路径点击工具(吸附模式): {newRoute.Name}");
UpdateMainStatus($"已进入传统空轨路径模式: {newRoute.Name} - 请在3D视图中点击空轨模型设置路径点(将自动吸附到基准路径)");
LogManager.Info($"开始新建传统空轨路径: {newRoute.Name},已强制重新初始化ToolPlugin获取鼠标焦点");
}
else
{
UpdateMainStatus("创建传统空轨路径失败:没有可通行的物流模型");
LogManager.Error("创建传统空轨路径失败:没有可通行的物流模型");
MessageBox.Show("创建传统空轨路径失败:没有找到任何可通行的物流模型。\n请先为模型设置可通行的物流属性,然后再尝试创建路径。", "错误",
MessageBoxButton.OK, MessageBoxImage.Warning);
}
}
else
{
UpdateMainStatus("路径规划管理器未初始化");
LogManager.Error("路径规划管理器未初始化");
}
}, "新建传统空轨路径");
}
///
/// 执行新建吊装路径命令
///
private async Task ExecuteNewHoistingPathAsync()
{
await SafeExecuteAsync(() =>
{
UpdateMainStatus("正在创建新吊装路径...");
// 清除所有现有路径的可视化显示
if (PathPointRenderPlugin.Instance != null)
{
try
{
PathPointRenderPlugin.Instance.ClearPathsExcept("grid_visualization_all", "grid_visualization_channel", "grid_visualization_unknown", "grid_visualization_obstacle", "grid_visualization_door");
LogManager.Info("新建吊装路径:已清除现有路径可视化显示(保留网格可视化)");
}
catch (Exception ex)
{
LogManager.Error($"新建吊装路径:清除现有路径可视化失败: {ex.Message}", ex);
throw;
}
}
if (_pathPlanningManager != null)
{
// 创建吊装路径
var newRoute = _pathPlanningManager.StartCreatingNewRoute(
isRailPath: false,
pathType: PathType.Hoisting);
if (newRoute != null)
{
// 创建对应的 WPF ViewModel
var newPathViewModel = new PathRouteViewModel
{
Route = newRoute,
IsActive = true,
PathType = PathType.Hoisting
};
// 使用统一的辅助方法创建路径点
var pathPoints = CreatePathPointViewModelsFromCoreRoute(newRoute);
foreach (var point in pathPoints)
{
newPathViewModel.Points.Add(point);
}
// 添加到 UI 列表并选中
PathRoutes.Add(newPathViewModel);
SelectedPathRoute = newPathViewModel;
// 强制重新初始化ToolPlugin以确保获得鼠标焦点
if (!ForceReinitializeToolPlugin(subscribeToEvents: true))
{
UpdateMainStatus("ToolPlugin初始化失败,请重试");
LogManager.Error("新建吊装路径:ToolPlugin初始化失败");
return;
}
// 启动点击工具,启用吊装模式
_pathPlanningManager.StartClickTool(PathPointType.WayPoint, enableRailSnapping: false, enableHoistingMode: true);
LogManager.Info($"已启动吊装路径点击工具: {newRoute.Name}");
UpdateMainStatus($"已进入新建吊装路径模式: {newRoute.Name} - 请在3D视图中点击吊装起点(地面位置)");
LogManager.Info($"开始新建吊装路径: {newRoute.Name},已强制重新初始化ToolPlugin获取鼠标焦点");
}
else
{
UpdateMainStatus("创建新吊装路径失败:没有可通行的物流模型");
LogManager.Error("创建新吊装路径失败:没有可通行的物流模型");
MessageBox.Show("创建新吊装路径失败:没有找到任何可通行的物流模型。\n请先为模型设置可通行的物流属性,然后再尝试创建路径。", "错误",
MessageBoxButton.OK, MessageBoxImage.Warning);
}
}
else
{
UpdateMainStatus("路径规划管理器未初始化");
LogManager.Error("路径规划管理器未初始化");
}
}, "新建吊装路径");
}
#region 多层吊装页签模式实现
///
/// 执行新建多层阶梯式吊装路径命令 - 启用页签模式
///
private async Task ExecuteNewMultiLevelHoistingPathAsync()
{
await SafeExecuteAsync(() =>
{
UpdateMainStatus("正在创建多层阶梯式吊装路径...");
// 清除所有现有路径的可视化显示
if (PathPointRenderPlugin.Instance != null)
{
try
{
PathPointRenderPlugin.Instance.ClearPathsExcept("grid_visualization_all", "grid_visualization_channel", "grid_visualization_unknown", "grid_visualization_obstacle", "grid_visualization_door");
LogManager.Info("新建多层吊装路径:已清除现有路径可视化显示(保留网格可视化)");
}
catch (Exception ex)
{
LogManager.Error($"新建多层吊装路径:清除现有路径可视化失败: {ex.Message}", ex);
throw;
}
}
if (_pathPlanningManager == null)
{
UpdateMainStatus("路径规划管理器未初始化");
LogManager.Error("路径规划管理器未初始化");
return;
}
// 启用多层吊装页签模式
IsMultiLevelHoistingMode = true;
// 清空集合和重置状态
MultiLevelItems.Clear();
_multiLevelStartPoint = null;
_multiLevelEndPoint = null;
_hasMultiLevelStartPoint = false;
_hasMultiLevelEndPoint = false;
MultiLevelStartPointText = "未选择";
MultiLevelEndPointText = "未选择";
UpdateMultiLevelStats();
// 重置选择状态
_isSelectingMultiLevelStartPoint = false;
_isSelectingMultiLevelEndPoint = false;
_currentSelectingLevelTarget = null;
// 清理之前的点击事件订阅
PathClickToolPlugin.MouseClicked -= OnMultiLevelHoistingMouseClicked;
UpdateMainStatus("多层吊装模式已启用 - 请设置起点、终点并添加层级");
LogManager.Info("[多层吊装] 页签模式已启用");
}, "新建多层吊装路径");
}
///
/// 执行选择多层吊装起点命令
///
private async Task ExecuteSelectMultiLevelStartPointAsync()
{
await SafeExecuteAsync(() =>
{
if (_pathPlanningManager == null) return;
// 清除之前的起点标记
if (_hasMultiLevelStartPoint && PathPointRenderPlugin.Instance != null)
{
var existingRoute = PathRoutes.FirstOrDefault(r => r.Route.Name == "MultiLevelStartPoint");
if (existingRoute != null)
{
PathPointRenderPlugin.Instance.RemovePath(existingRoute.Route.Id);
}
}
// 使用自动路径规划相同的模式
_pathPlanningManager.DisableMouseHandling();
// 设置状态
_isSelectingMultiLevelStartPoint = true;
_isSelectingMultiLevelEndPoint = false;
_currentSelectingLevelTarget = null;
// 订阅点击事件
PathClickToolPlugin.MouseClicked -= OnMultiLevelHoistingMouseClicked;
PathClickToolPlugin.MouseClicked += OnMultiLevelHoistingMouseClicked;
// 强制重新初始化ToolPlugin以获取鼠标焦点
ForceReinitializeToolPlugin(subscribeToEvents: false);
UpdateMainStatus("请在3D视图中点击选择起点(地面起吊位置)...");
LogManager.Info("[多层吊装] 开始选择起点");
}, "选择多层吊装起点");
}
///
/// 执行选择多层吊装终点命令
///
private async Task ExecuteSelectMultiLevelEndPointAsync()
{
await SafeExecuteAsync(() =>
{
if (_pathPlanningManager == null) return;
// 清除之前的终点标记
if (_hasMultiLevelEndPoint && PathPointRenderPlugin.Instance != null)
{
var existingRoute = PathRoutes.FirstOrDefault(r => r.Route.Name == "MultiLevelEndPoint");
if (existingRoute != null)
{
PathPointRenderPlugin.Instance.RemovePath(existingRoute.Route.Id);
}
}
// 使用自动路径规划相同的模式
_pathPlanningManager.DisableMouseHandling();
// 设置状态
_isSelectingMultiLevelStartPoint = false;
_isSelectingMultiLevelEndPoint = true;
_currentSelectingLevelTarget = null;
// 订阅点击事件
PathClickToolPlugin.MouseClicked -= OnMultiLevelHoistingMouseClicked;
PathClickToolPlugin.MouseClicked += OnMultiLevelHoistingMouseClicked;
// 强制重新初始化ToolPlugin以获取鼠标焦点
ForceReinitializeToolPlugin(subscribeToEvents: false);
UpdateMainStatus("请在3D视图中点击选择终点(地面落地位置)...");
LogManager.Info("[多层吊装] 开始选择终点");
}, "选择多层吊装终点");
}
///
/// 执行添加层级命令
///
private void ExecuteAddMultiLevel()
{
var level = new HoistingLevelItem
{
Index = MultiLevelItems.Count,
HeightInMeters = 3.0,
Description = $"层级{MultiLevelItems.Count + 1}"
};
MultiLevelItems.Add(level);
UpdateMultiLevelStats();
LogManager.Info($"[多层吊装] 添加层级{level.Index + 1}, 高度={level.HeightInMeters}m");
UpdateMainStatus($"已添加层级{level.Index + 1},请设置目标位置");
}
///
/// 执行删除层级命令
///
private void ExecuteDeleteMultiLevel(HoistingLevelItem item)
{
if (item == null) return;
// 清除该层级的可视化标记
if (PathPointRenderPlugin.Instance != null)
{
var existingRoute = PathRoutes.FirstOrDefault(r => r.Route.Name == $"MultiLevelLevel{item.Index}");
if (existingRoute != null)
{
PathPointRenderPlugin.Instance.RemovePath(existingRoute.Route.Id);
}
}
MultiLevelItems.Remove(item);
// 重新编号
for (int i = 0; i < MultiLevelItems.Count; i++)
{
MultiLevelItems[i].Index = i;
}
UpdateMultiLevelStats();
LogManager.Info($"[多层吊装] 删除层级,剩余{MultiLevelItems.Count}个层级");
}
///
/// 执行拾取层级目标位置命令
///
private void ExecutePickMultiLevelTarget(HoistingLevelItem item)
{
if (item == null || _pathPlanningManager == null) return;
// 清除之前的标记
if (PathPointRenderPlugin.Instance != null)
{
var existingRoute = PathRoutes.FirstOrDefault(r => r.Route.Name == $"MultiLevelLevel{item.Index}");
if (existingRoute != null)
{
PathPointRenderPlugin.Instance.RemovePath(existingRoute.Route.Id);
}
}
// 使用自动路径规划相同的模式
_pathPlanningManager.DisableMouseHandling();
// 设置状态
_isSelectingMultiLevelStartPoint = false;
_isSelectingMultiLevelEndPoint = false;
_currentSelectingLevelTarget = item;
// 订阅点击事件
PathClickToolPlugin.MouseClicked -= OnMultiLevelHoistingMouseClicked;
PathClickToolPlugin.MouseClicked += OnMultiLevelHoistingMouseClicked;
// 强制重新初始化ToolPlugin以获取鼠标焦点
ForceReinitializeToolPlugin(subscribeToEvents: false);
UpdateMainStatus($"请在3D视图中点击层级{item.Index + 1}的目标位置...");
LogManager.Info($"[多层吊装] 开始选择层级{item.Index + 1}的目标位置");
}
///
/// 执行创建多层吊装路径命令
///
private async Task ExecuteCreateMultiLevelPathAsync()
{
await SafeExecuteAsync(() =>
{
// 验证输入
if (!_hasMultiLevelStartPoint)
{
MessageBox.Show("请先选择起点", "验证失败", MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
if (!_hasMultiLevelEndPoint)
{
MessageBox.Show("请先选择终点", "验证失败", MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
if (MultiLevelItems.Count == 0)
{
MessageBox.Show("请至少添加一个层级", "验证失败", MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
// 验证所有层级
foreach (var level in MultiLevelItems)
{
if (level.HeightInMeters <= 0)
{
MessageBox.Show($"层级{level.Index + 1}的高度必须大于0", "验证失败",
MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
if (level.HorizontalTarget == null)
{
MessageBox.Show($"请为层级{level.Index + 1}设置目标位置", "验证失败",
MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
}
// 创建层级列表
var levels = new List();
foreach (var item in MultiLevelItems)
{
levels.Add(new HoistingLevel
{
Index = item.Index,
HeightInMeters = item.HeightInMeters,
HorizontalTarget = item.HorizontalTarget,
IsRise = true,
Description = item.Description
});
}
LogManager.Info($"[多层吊装] 创建路径:起点({_multiLevelStartPoint.X:F2},{_multiLevelStartPoint.Y:F2},{_multiLevelStartPoint.Z:F2}), " +
$"终点({_multiLevelEndPoint.X:F2},{_multiLevelEndPoint.Y:F2},{_multiLevelEndPoint.Z:F2}), 层级数={levels.Count}");
// 使用命令创建路径
var parameters = new AerialPathParameters
{
PathType = PathType.Hoisting,
HoistingMode = HoistingMode.MultiLevel,
StartPoint = _multiLevelStartPoint,
EndPoint = _multiLevelEndPoint,
HoistingLevels = levels
};
var command = new CreateAerialPathCommand(parameters);
var result = command.ExecuteAsync().Result;
if (result.IsSuccess && result is PathPlanningResult typedResult)
{
var route = typedResult.Data;
LogManager.Info($"[多层吊装] 路径创建成功: {route.Name}, 点数: {route.Points.Count}");
// 创建 ViewModel
var newPathViewModel = new PathRouteViewModel
{
Route = route,
IsActive = true,
PathType = PathType.Hoisting
};
var pathPoints = CreatePathPointViewModelsFromCoreRoute(route);
foreach (var point in pathPoints)
{
newPathViewModel.Points.Add(point);
}
PathRoutes.Add(newPathViewModel);
SelectedPathRoute = newPathViewModel;
UpdateMainStatus($"✓ 多层吊装路径创建成功: {route.Name} - {levels.Count}个层级, {route.Points.Count}个路径点");
OnPropertyChanged(nameof(PathRoutes));
// 退出多层吊装模式
ExecuteCancelMultiLevelHoisting();
}
else
{
UpdateMainStatus($"创建失败: {result.Message}");
MessageBox.Show($"创建多层吊装路径失败: {result.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}, "创建多层吊装路径");
}
///
/// 执行取消多层吊装命令
///
private void ExecuteCancelMultiLevelHoisting()
{
// 清理事件订阅
PathClickToolPlugin.MouseClicked -= OnMultiLevelHoistingMouseClicked;
// 清理可视化标记
if (PathPointRenderPlugin.Instance != null)
{
// 清理起点标记
var startRoute = PathRoutes.FirstOrDefault(r => r.Route.Name == "MultiLevelStartPoint");
if (startRoute != null)
{
PathPointRenderPlugin.Instance.RemovePath(startRoute.Route.Id);
}
// 清理终点标记
var endRoute = PathRoutes.FirstOrDefault(r => r.Route.Name == "MultiLevelEndPoint");
if (endRoute != null)
{
PathPointRenderPlugin.Instance.RemovePath(endRoute.Route.Id);
}
// 清理层级标记
for (int i = 0; i < MultiLevelItems.Count; i++)
{
var levelRoute = PathRoutes.FirstOrDefault(r => r.Route.Name == $"MultiLevelLevel{i}");
if (levelRoute != null)
{
PathPointRenderPlugin.Instance.RemovePath(levelRoute.Route.Id);
}
}
}
// 重置状态
IsMultiLevelHoistingMode = false;
MultiLevelItems.Clear();
_multiLevelStartPoint = null;
_multiLevelEndPoint = null;
_hasMultiLevelStartPoint = false;
_hasMultiLevelEndPoint = false;
MultiLevelStartPointText = "未选择";
MultiLevelEndPointText = "未选择";
_isSelectingMultiLevelStartPoint = false;
_isSelectingMultiLevelEndPoint = false;
_currentSelectingLevelTarget = null;
// 恢复PathPlanningManager鼠标处理
_pathPlanningManager?.EnableMouseHandling();
_pathPlanningManager?.StopClickTool();
UpdateMultiLevelStats();
UpdateMainStatus("多层吊装模式已取消");
LogManager.Info("[多层吊装] 页签模式已取消");
}
///
/// 更新多层吊装统计信息
///
private void UpdateMultiLevelStats()
{
int levelCount = MultiLevelItems.Count;
int pointCount = levelCount > 0 ? 2 + levelCount * 2 + 2 : 0;
MultiLevelStatsText = $"层级数: {levelCount} | 预估路径点数: {pointCount}";
// 通知CanCreateMultiLevelPath变更
OnPropertyChanged(nameof(CanCreateMultiLevelPath));
// 通知命令可执行状态变更
System.Windows.Input.CommandManager.InvalidateRequerySuggested();
}
///
/// 多层吊装路径鼠标点击处理
///
private void OnMultiLevelHoistingMouseClicked(object sender, PickItemResult e)
{
if (e?.Point == null) return;
var point = e.Point;
if (_isSelectingMultiLevelStartPoint)
{
// 设置起点
_multiLevelStartPoint = point;
_hasMultiLevelStartPoint = true;
MultiLevelStartPointText = $"({point.X:F2}, {point.Y:F2}, {point.Z:F2})";
// 渲染起点标记
RenderMultiLevelPointMarker(point, isStart: true);
LogManager.Info($"[多层吊装] 起点已设置: ({point.X:F2}, {point.Y:F2}, {point.Z:F2})");
UpdateMainStatus($"起点已设置,请选择终点或添加层级");
// 清理事件订阅并停用ToolPlugin
PathClickToolPlugin.MouseClicked -= OnMultiLevelHoistingMouseClicked;
_pathPlanningManager?.EnableMouseHandling();
_pathPlanningManager?.StopClickTool();
_isSelectingMultiLevelStartPoint = false;
// 更新统计
UpdateMultiLevelStats();
}
else if (_isSelectingMultiLevelEndPoint)
{
// 设置终点
_multiLevelEndPoint = point;
_hasMultiLevelEndPoint = true;
MultiLevelEndPointText = $"({point.X:F2}, {point.Y:F2}, {point.Z:F2})";
// 渲染终点标记
RenderMultiLevelPointMarker(point, isStart: false);
LogManager.Info($"[多层吊装] 终点已设置: ({point.X:F2}, {point.Y:F2}, {point.Z:F2})");
UpdateMainStatus($"终点已设置,可以添加层级创建路径");
// 清理事件订阅并停用ToolPlugin
PathClickToolPlugin.MouseClicked -= OnMultiLevelHoistingMouseClicked;
_pathPlanningManager?.EnableMouseHandling();
_pathPlanningManager?.StopClickTool();
_isSelectingMultiLevelEndPoint = false;
// 更新统计
UpdateMultiLevelStats();
}
else if (_currentSelectingLevelTarget != null)
{
// 设置层级目标位置
var targetPoint = new Point3D(point.X, point.Y, point.Z);
_currentSelectingLevelTarget.HorizontalTarget = targetPoint;
// 渲染层级目标标记
RenderMultiLevelLevelMarker(targetPoint, _currentSelectingLevelTarget.Index);
LogManager.Info($"[多层吊装] 层级{_currentSelectingLevelTarget.Index + 1}目标位置已设置: ({point.X:F2}, {point.Y:F2})");
UpdateMainStatus($"层级{_currentSelectingLevelTarget.Index + 1}目标位置已设置,可以继续添加层级或点击创建路径");
// 清理事件订阅并停用ToolPlugin
PathClickToolPlugin.MouseClicked -= OnMultiLevelHoistingMouseClicked;
_pathPlanningManager?.EnableMouseHandling();
_pathPlanningManager?.StopClickTool();
_currentSelectingLevelTarget = null;
}
}
///
/// 渲染层级目标位置标记
///
private void RenderMultiLevelLevelMarker(Point3D point, int levelIndex)
{
if (PathPointRenderPlugin.Instance == null) return;
var routeName = $"MultiLevelLevel{levelIndex}";
// 移除之前的标记
var existingRoute = PathRoutes.FirstOrDefault(r => r.Route.Name == routeName);
if (existingRoute != null)
{
PathPointRenderPlugin.Instance.RemovePath(existingRoute.Route.Id);
}
// 创建临时路径
var route = new PathRoute(routeName)
{
Description = $"层级{levelIndex + 1}目标位置"
};
var pathPoint = new PathPoint
{
Name = $"层级{levelIndex + 1}",
Position = point,
Type = PathPointType.WayPoint
};
route.AddPoint(pathPoint);
// 渲染点(使用不同颜色区分层级)
PathPointRenderPlugin.Instance.RenderPointOnly(route);
LogManager.Info($"[多层吊装] 层级{levelIndex + 1}目标标记已渲染");
}
///
/// 渲染多层吊装路径的点标记
///
private void RenderMultiLevelPointMarker(Point3D point, bool isStart)
{
if (PathPointRenderPlugin.Instance == null) return;
var routeName = isStart ? "MultiLevelStartPoint" : "MultiLevelEndPoint";
var displayName = isStart ? "多层吊装起点" : "多层吊装终点";
var pointType = isStart ? PathPointType.StartPoint : PathPointType.EndPoint;
// 移除之前的标记
var existingRoute = PathRoutes.FirstOrDefault(r => r.Route.Name == routeName);
if (existingRoute != null)
{
PathPointRenderPlugin.Instance.RemovePath(existingRoute.Route.Id);
}
// 创建临时路径
var route = new PathRoute(routeName)
{
Description = displayName
};
var pathPoint = new PathPoint
{
Name = displayName,
Position = point,
Type = pointType
};
route.AddPoint(pathPoint);
// 渲染点
PathPointRenderPlugin.Instance.RenderPointOnly(route);
}
#endregion
///
/// 提取并渲染所有空轨模型的基准路径
///
private void ExtractAndRenderRailBaselinePaths()
{
try
{
LogManager.Info("[空轨路径] 开始提取所有空轨模型的基准路径");
int successCount = PathPlanning.RailGeometryHelper.ExtractAndRenderAllRailBaselines();
LogManager.Info($"[空轨路径] 空轨基准路径提取完成,成功 {successCount} 个");
}
catch (Exception ex)
{
LogManager.Error($"[空轨路径] 提取空轨基准路径失败: {ex.Message}");
}
}
private async Task ExecuteDeletePathAsync()
{
if (SelectedPathRoute == null) return;
await SafeExecuteAsync(() =>
{
var pathName = SelectedPathRoute.Name;
UpdateMainStatus($"正在删除路径: {pathName}...");
// 清理当前路径的3D可视化显示(使用新的统一API)
if (PathPointRenderPlugin.Instance != null)
{
try
{
// 使用路径名找到对应的Core路径,获取其ID进行精确清理
string pathIdToRemove = null;
if (_pathPlanningManager != null)
{
var coreRoute = _pathPlanningManager.Routes.FirstOrDefault(r => r.Name == pathName);
if (coreRoute != null)
{
pathIdToRemove = coreRoute.Id;
LogManager.Info($"删除路径:找到Core路径ID: {pathIdToRemove}");
// 使用新的API精确删除该路径
PathPointRenderPlugin.Instance.RemovePath(pathIdToRemove);
LogManager.Info($"删除路径:已清除路径 {pathName} (ID: {pathIdToRemove}) 的可视化显示");
}
else
{
LogManager.Warning($"删除路径:未找到路径 {pathName} 对应的Core路径,无法清理可视化");
}
}
}
catch (Exception ex)
{
LogManager.Error($"删除路径:清除路径可视化失败: {ex.Message}", ex);
throw; // 重新抛出异常,让上层处理
}
}
// 通知PathPlanningManager删除对应的路径(包括数据库删除)
if (_pathPlanningManager != null)
{
var coreRoute = _pathPlanningManager.Routes.FirstOrDefault(r => r.Name == pathName);
if (coreRoute != null)
{
// 使用PathPlanningManager的DeleteRoute方法,确保数据库也被删除
bool deleteSuccess = _pathPlanningManager.DeleteRoute(coreRoute);
if (deleteSuccess)
{
LogManager.Info($"删除路径:已从PathPlanningManager和数据库删除路径: {pathName}");
}
else
{
LogManager.Warning($"删除路径:PathPlanningManager删除失败: {pathName}");
}
}
}
// 更新UI
PathRoutes.Remove(SelectedPathRoute);
SelectedPathRoute = null;
UpdateMainStatus($"已完全删除路径: {pathName}");
}, "删除路径");
}
private async Task ExecuteDuplicatePathAsync()
{
if (SelectedPathRoute == null || _pathPlanningManager == null) return;
await SafeExecuteAsync(async () =>
{
var sourceRoute = SelectedPathRoute.Route ??
_pathPlanningManager.Routes.FirstOrDefault(r => r.Id == SelectedPathRoute.Id);
if (sourceRoute == null)
{
throw new InvalidOperationException("未找到要复制的路径数据");
}
UpdateMainStatus($"正在复制路径: {sourceRoute.Name}...");
var duplicatedRoute = sourceRoute.Clone();
duplicatedRoute.Name = PathHelper.BuildDuplicatedPathName(sourceRoute.Name);
duplicatedRoute.CreatedTime = DateTime.Now;
duplicatedRoute.LastModified = DateTime.Now;
if (!_pathPlanningManager.AddRoute(duplicatedRoute))
{
throw new InvalidOperationException($"复制路径失败: {sourceRoute.Name}");
}
var duplicatedPathViewModel = new PathRouteViewModel(isFromDatabase: true)
{
Route = duplicatedRoute,
IsActive = false
};
duplicatedPathViewModel.SetTimeInfo(duplicatedRoute.CreatedTime, duplicatedRoute.LastModified);
foreach (var point in CreatePathPointViewModelsFromCoreRoute(duplicatedRoute))
{
duplicatedPathViewModel.Points.Add(point);
}
PathRoutes.Add(duplicatedPathViewModel);
SelectedPathRoute = duplicatedPathViewModel;
UpdateMainStatus($"已复制路径: {sourceRoute.Name} -> {duplicatedPathViewModel.Name}");
await Task.CompletedTask;
}, "复制路径");
}
#endregion
#region 坐标编辑命令
private void ExecuteEditPointCoordinates(PathPointViewModel point)
{
if (point == null) return;
try
{
// 创建并显示对话框
// 注意:在Navisworks插件的ViewModel中,不设置Owner
// 因为Application.Current.MainWindow在Navisworks环境中不可用
var dialog = new EditCoordinatesWindow(point.X, point.Y, point.Z, point.Name, point.Type.ToString());
if (dialog.ShowDialog() == true)
{
// 同步更新到底层数据模型
if (_pathPlanningManager != null)
{
var coreRoute = _pathPlanningManager.Routes.FirstOrDefault(r => r.Id == SelectedPathRoute.Id);
if (coreRoute != null)
{
// 🔥 通过Id查找路径点索引(而不是使用可能过时的point.Index)
int pointIndex = -1;
for (int i = 0; i < coreRoute.Points.Count; i++)
{
if (coreRoute.Points[i].Id == point.Id)
{
pointIndex = i;
break;
}
}
// 验证索引是否有效
if (pointIndex < 0 || pointIndex >= coreRoute.Points.Count)
{
LogManager.Error($"[坐标编辑] 索引错误: 通过Id查找失败,point.Id={point.Id}, coreRoute.Points.Count={coreRoute.Points.Count}");
System.Windows.MessageBox.Show($"路径点索引错误:无法找到对应的路径点,请刷新路径列表后重试", "错误", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);
return;
}
LogManager.Info($"[坐标编辑] 修改点: {point.Name}, 类型: {point.Type}, 索引: {pointIndex}");
var corePoint = coreRoute.Points[pointIndex];
LogManager.Info($"[坐标编辑] Core路径点: {corePoint.Name}, 索引: {pointIndex}, 位置: ({corePoint.Position.X:F3}, {corePoint.Position.Y:F3}, {corePoint.Position.Z:F3})");
// 使用公共方法更新路径点(自动处理吊装路径约束)
var newPosition = new Point3D(dialog.X, dialog.Y, dialog.Z);
bool success = _pathPlanningManager.UpdatePathPointWithConstraints(coreRoute, pointIndex, newPosition);
if (success)
{
// 更新ViewModel中的坐标(从Core数据同步)
corePoint = coreRoute.Points[pointIndex];
point.X = corePoint.Position.X;
point.Y = corePoint.Position.Y;
point.Z = corePoint.Position.Z;
// 刷新视图
UpdatePathVisualization();
LogManager.Info($"已更新路径点 {point.Name} 的坐标为 ({point.X:F3}, {point.Y:F3}, {point.Z:F3})");
}
else
{
LogManager.Error($"更新路径点失败: {point.Name}");
System.Windows.MessageBox.Show($"更新路径点失败", "错误", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);
}
}
}
}
}
catch (Exception ex)
{
LogManager.Error($"编辑坐标对话框操作失败: {ex.Message}", ex);
System.Windows.MessageBox.Show($"无法打开编辑对话框: {ex.Message}", "错误", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);
}
}
///
/// 从Core路径创建PathPointViewModel列表(统一处理吊装路径属性)
///
private List CreatePathPointViewModelsFromCoreRoute(PathRoute coreRoute)
{
var points = new List();
for (int i = 0; i < coreRoute.Points.Count; i++)
{
var corePoint = coreRoute.Points[i];
var wpfPoint = new PathPointViewModel
{
Id = corePoint.Id,
Name = corePoint.Name,
X = corePoint.X,
Y = corePoint.Y,
Z = corePoint.Z,
Type = corePoint.Type,
Index = i // 🔥 设置正确的索引
};
points.Add(wpfPoint);
}
return points;
}
private bool SyncPathViewModelFromCoreRoute(PathRouteViewModel pathViewModel, PathRoute coreRoute, bool preserveSelection = true)
{
if (pathViewModel == null || coreRoute == null)
{
return false;
}
bool needsUpdate = !ReferenceEquals(pathViewModel.Route, coreRoute) ||
pathViewModel.Points.Count != coreRoute.Points.Count;
if (!needsUpdate && pathViewModel.Points.Count == coreRoute.Points.Count)
{
for (int i = 0; i < pathViewModel.Points.Count; i++)
{
var uiPoint = pathViewModel.Points[i];
var corePoint = coreRoute.Points[i];
if (uiPoint.Id != corePoint.Id ||
uiPoint.Name != corePoint.Name ||
uiPoint.Type != corePoint.Type)
{
needsUpdate = true;
break;
}
const double tolerance = 1e-6;
if (Math.Abs(uiPoint.X - corePoint.Position.X) > tolerance ||
Math.Abs(uiPoint.Y - corePoint.Position.Y) > tolerance ||
Math.Abs(uiPoint.Z - corePoint.Position.Z) > tolerance)
{
needsUpdate = true;
break;
}
}
}
pathViewModel.Route = coreRoute;
pathViewModel.SetTimeInfo(coreRoute.CreatedTime, coreRoute.LastModified);
if (!needsUpdate)
{
pathViewModel.NotifyPropertyChanged(nameof(PathRouteViewModel.TotalLength));
pathViewModel.NotifyPropertyChanged(nameof(PathRouteViewModel.LastModifiedTime));
return false;
}
string selectedPointId = preserveSelection ? SelectedPathPoint?.Id : null;
bool hadSelection = !string.IsNullOrEmpty(selectedPointId);
PathPointViewModel newSelectedPoint = null;
pathViewModel.Points.Clear();
foreach (var pointViewModel in CreatePathPointViewModelsFromCoreRoute(coreRoute))
{
pathViewModel.Points.Add(pointViewModel);
if (hadSelection && pointViewModel.Id == selectedPointId)
{
newSelectedPoint = pointViewModel;
}
}
pathViewModel.NotifyPropertyChanged(nameof(PathRouteViewModel.PointCount));
pathViewModel.NotifyPropertyChanged(nameof(PathRouteViewModel.TotalLength));
pathViewModel.NotifyPropertyChanged(nameof(PathRouteViewModel.SummaryInfo));
pathViewModel.NotifyPropertyChanged(nameof(PathRouteViewModel.LastModifiedTime));
if (hadSelection)
{
SelectedPathPoint = newSelectedPoint;
}
return true;
}
///
/// 更新吊装路径的关联点(UI层)
///
/// 路径
/// 修改的点的索引
private void UpdateAerialPathRelatedPoints(PathRouteViewModel pathRoute, int modifiedPointIndex)
{
if (pathRoute.PathType != NavisworksTransport.PathType.Hoisting ||
pathRoute.Points.Count != 4)
{
return;
}
var points = pathRoute.Points;
// 修改起点(索引0):更新提升点(索引1)的X,Y坐标
if (modifiedPointIndex == 0)
{
var startPoint = points[0];
var liftPoint = points[1];
liftPoint.X = startPoint.X;
liftPoint.Y = startPoint.Y;
}
// 修改终点(索引3):更新平移终点(索引2)的X,Y坐标
else if (modifiedPointIndex == 3)
{
var endPoint = points[3];
var moveEndPoint = points[2];
moveEndPoint.X = endPoint.X;
moveEndPoint.Y = endPoint.Y;
}
}
///
/// 更新吊装路径的关联点(Core层)
///
/// Core路径
/// 修改的点的索引
private void UpdateAerialPathRelatedPointsCore(PathRoute coreRoute, int modifiedPointIndex)
{
if (coreRoute.PathType != NavisworksTransport.PathType.Hoisting ||
coreRoute.Points.Count != 4)
{
return;
}
var points = coreRoute.Points;
// 修改起点(索引0):更新提升点(索引1)的X,Y坐标
if (modifiedPointIndex == 0)
{
var startPoint = points[0];
var liftPoint = points[1];
liftPoint.X = startPoint.X;
liftPoint.Y = startPoint.Y;
}
// 修改终点(索引3):更新平移终点(索引2)的X,Y坐标
else if (modifiedPointIndex == 3)
{
var endPoint = points[3];
var moveEndPoint = points[2];
moveEndPoint.X = endPoint.X;
moveEndPoint.Y = endPoint.Y;
}
}
#endregion
#region 自动路径规划命令
private async Task ExecuteSelectStartPointAsync()
{
await SafeExecuteAsync(() =>
{
LogManager.Info("=== 开始执行选择起点命令 ===");
if (_pathPlanningManager == null)
{
LogManager.Error("PathPlanningManager为null,无法选择起点");
UpdateMainStatus("路径规划管理器未初始化");
return;
}
// 立即清理现有的起点标记(使用正确的路径ID)
if (PathPointRenderPlugin.Instance != null && _autoPathStartPointRoute != null)
{
PathPointRenderPlugin.Instance.RemovePath(_autoPathStartPointRoute.Id);
_autoPathStartPointRoute = null; // 清除引用
}
// 重置状态
_hasStartPoint = false;
AutoPathStartPoint = "未选择";
LogManager.Info("PathPlanningManager已初始化,开始设置自动路径规划状态");
IsSelectingStartPoint = true;
IsSelectingEndPoint = false;
// 通知按钮状态更新,禁用"手动创建"按钮
OnPropertyChanged(nameof(CanExecuteNewPath));
UpdateMainStatus("请在3D视图中点击选择起点...");
// 进入自动路径规划模式 - 动态事件订阅
LogManager.Info("进入自动路径规划模式,启用动态事件订阅");
// 1. 禁用PathPlanningManager的鼠标处理
_pathPlanningManager.DisableMouseHandling();
// 2. 启用PathEditingViewModel的事件订阅
PathClickToolPlugin.MouseClicked -= OnAutoPathMouseClicked; // 先取消避免重复
PathClickToolPlugin.MouseClicked += OnAutoPathMouseClicked;
LogManager.Info("已启用PathEditingViewModel的鼠标事件订阅");
// 强制重新初始化ToolPlugin以确保获得鼠标焦点,自动路径不需要PathPlanningManager订阅事件
if (!ForceReinitializeToolPlugin(subscribeToEvents: false))
{
UpdateMainStatus("ToolPlugin初始化失败,请重试");
return;
}
// 在STA线程中调用Navisworks API
_pathPlanningManager.StartClickTool(PathPointType.StartPoint);
LogManager.Info("=== 选择起点命令设置完成,已启用动态事件订阅,等待用户点击 ===");
}, "选择起点");
}
private async Task ExecuteSelectEndPointAsync()
{
await SafeExecuteAsync(() =>
{
LogManager.Info("=== 开始执行选择终点命令 ===");
if (_pathPlanningManager == null)
{
LogManager.Error("PathPlanningManager为null,无法选择终点");
UpdateMainStatus("路径规划管理器未初始化");
return;
}
// 立即清理现有的终点标记(使用正确的路径ID)
if (PathPointRenderPlugin.Instance != null && _autoPathEndPointRoute != null)
{
PathPointRenderPlugin.Instance.RemovePath(_autoPathEndPointRoute.Id);
LogManager.Debug($"[选择终点] 已清除之前的终点标记,ID: {_autoPathEndPointRoute.Id}");
_autoPathEndPointRoute = null; // 清除引用
}
// 重置状态
_hasEndPoint = false;
AutoPathEndPoint = "未选择";
LogManager.Info("PathPlanningManager已初始化,开始设置自动路径规划状态");
IsSelectingStartPoint = false;
IsSelectingEndPoint = true;
// 通知按钮状态更新,禁用"手动创建"按钮
OnPropertyChanged(nameof(CanExecuteNewPath));
UpdateMainStatus("请在3D视图中点击选择终点...");
// 进入自动路径规划模式 - 动态事件订阅
LogManager.Info("进入自动路径规划模式,启用动态事件订阅");
// 1. 禁用PathPlanningManager的鼠标处理
_pathPlanningManager.DisableMouseHandling();
// 2. 启用PathEditingViewModel的事件订阅
PathClickToolPlugin.MouseClicked -= OnAutoPathMouseClicked; // 先取消避免重复
PathClickToolPlugin.MouseClicked += OnAutoPathMouseClicked;
LogManager.Info("已启用PathEditingViewModel的鼠标事件订阅");
// 强制重新初始化ToolPlugin以确保获得鼠标焦点,自动路径不需要PathPlanningManager订阅事件
if (!ForceReinitializeToolPlugin(subscribeToEvents: false))
{
UpdateMainStatus("ToolPlugin初始化失败,请重试");
return;
}
// 在STA线程中调用Navisworks API
_pathPlanningManager.StartClickTool(PathPointType.EndPoint);
LogManager.Info("=== 选择终点命令设置完成,已启用动态事件订阅,等待用户点击 ===");
}, "选择终点");
}
private async Task ExecuteAutoPlanPathAsync()
{
await SafeExecuteAsync(async () =>
{
// 保存当前光标
var oldCursor = Mouse.OverrideCursor;
try
{
if (!_hasStartPoint || !_hasEndPoint)
{
UpdateMainStatus("请先选择起点和终点");
return;
}
// 设置等待光标
Mouse.OverrideCursor = Cursors.Wait;
UpdateMainStatus("正在计算最优路径...", -1, true);
LogManager.Info("=== 开始执行自动路径规划 ===");
// 确保在开始自动路径规划前清理任何残留的事件订阅
CleanupAutoPathEventSubscriptions();
if (_pathPlanningManager != null)
{
_pathPlanningManager.StopClickTool();
}
// 清理所有临时路径,确保干净的起始状态
ClearTemporaryAutoPathMarkers();
if (PathPointRenderPlugin.Instance != null)
{
// 检查是否有网格可视化启用,只在启用时保留网格可视化路径
if (_pathPlanningManager?.IsAnyGridVisualizationEnabled == true)
{
PathPointRenderPlugin.Instance.ClearPathsExcept("grid_visualization_all", "grid_visualization_channel", "grid_visualization_unknown", "grid_visualization_obstacle", "grid_visualization_door");
}
else
{
PathPointRenderPlugin.Instance.ClearPathsExcept(); // 清理所有路径,不保留任何内容
}
}
// 调用PathPlanningManager的自动路径规划功能
var startPathPoint = new PathPoint
{
Name = "自动起点",
Position = _startPoint3D,
Type = PathPointType.StartPoint
};
var endPathPoint = new PathPoint
{
Name = "自动终点",
Position = _endPoint3D,
Type = PathPointType.EndPoint
};
LogManager.Info($"起点: ({_startPoint3D.X:F2}, {_startPoint3D.Y:F2}, {_startPoint3D.Z:F2})");
LogManager.Info($"终点: ({_endPoint3D.X:F2}, {_endPoint3D.Y:F2}, {_endPoint3D.Z:F2})");
// 计算物体半径:基于长度和宽度的较大值的一半,高度暂时不参与半径计算
var objectRadius = Math.Max(ObjectLength, ObjectWidth) / 2.0;
LogManager.Info($"物体参数: 长{ObjectLength:F1}m × 宽{ObjectWidth:F1}m × 高{ObjectHeight:F1}m");
LogManager.Info($"计算得出物体半径: {objectRadius:F2}m (基于长宽较大值的一半)");
// 确定网格大小:如果启用手动设置则使用用户设置的值,否则使用-1(自动选择)
var gridSize = IsGridSizeManuallyEnabled ? GridSize : -1;
// 获取选择的路径策略,如果未选择则默认使用最短路径
var selectedStrategy = SelectedPathStrategy?.Value ?? PathStrategy.Shortest;
LogManager.Info($"网格大小设置: {(IsGridSizeManuallyEnabled ? $"手动设置 {GridSize:F1}米" : "自动选择")}");
LogManager.Info($"路径策略: {SelectedPathStrategy?.DisplayName ?? "最短路径"}");
// 在STA线程中执行Navisworks API调用
var pathRoute = await _pathPlanningManager?.AutoPlanPath(startPathPoint, endPathPoint, objectRadius, SafetyMargin, gridSize, ObjectHeight, selectedStrategy);
if (pathRoute != null && pathRoute.Points.Count > 0)
{
LogManager.Info($"路径规划成功,共 {pathRoute.Points.Count} 个点");
// 自动路径规划完成后,执行完整的事件订阅清理
CleanupAutoPathEventSubscriptions();
if (_pathPlanningManager != null)
{
_pathPlanningManager.StopClickTool();
}
// 清理临时的起点和终点标记
ClearTemporaryAutoPathMarkers();
// 不在这里直接操作UI,让RouteGenerated事件处理UI更新
// 这避免了重复的路径添加和UI更新冲突
// 不在这里设置状态,让RouteGenerated事件处理UI更新
LogManager.Info($"✅ 自动路径规划完成,RouteGenerated事件将处理UI更新");
}
else
{
// 这里的逻辑已经在上方处理过了
// 失败情况下也要清理事件订阅和临时标记
CleanupAutoPathEventSubscriptions();
if (_pathPlanningManager != null)
{
_pathPlanningManager.StopClickTool();
}
// 清理临时的起点和终点标记
ClearTemporaryAutoPathMarkers();
// 确保失败后也清理所有可能的残留路径对象
if (PathPointRenderPlugin.Instance != null)
{
// 检查是否有网格可视化启用,只在启用时保留网格可视化路径
if (_pathPlanningManager?.IsAnyGridVisualizationEnabled == true)
{
PathPointRenderPlugin.Instance.ClearPathsExcept("grid_visualization_all", "grid_visualization_channel", "grid_visualization_unknown", "grid_visualization_obstacle", "grid_visualization_door");
}
else
{
PathPointRenderPlugin.Instance.ClearPathsExcept(); // 清理所有路径,不保留任何内容
}
}
}
}
finally
{
// 恢复光标
Mouse.OverrideCursor = oldCursor;
}
}, "自动路径规划");
}
private async Task ExecuteClearAutoPathAsync()
{
// 使用基类的SafeExecuteAsync方法,确保在STA线程上执行
await SafeExecuteAsync(() =>
{
LogManager.Info("=== 开始重置自动路径规划状态 ===");
// 停止任何正在进行的点选择
if (_pathPlanningManager != null)
{
_pathPlanningManager.StopClickTool();
PathClickToolPlugin.MouseClicked -= OnAutoPathMouseClicked;
}
// 清除所有路径的可视化显示(与新建路径按钮保持一致)
if (PathPointRenderPlugin.Instance != null)
{
try
{
// 清除路径可视化,但保留网格可视化
PathPointRenderPlugin.Instance.ClearPathsExcept("grid_visualization_all", "grid_visualization_channel", "grid_visualization_unknown", "grid_visualization_obstacle", "grid_visualization_door");
LogManager.Info("重置参数:已清除路径可视化显示(保留网格可视化)");
}
catch (Exception ex)
{
LogManager.Error($"重置参数:清除路径可视化失败: {ex.Message}", ex);
// 不抛出异常,继续执行状态重置
}
}
// 强制重新初始化ToolPlugin以重新获得鼠标焦点,重置时不需要订阅事件
if (_pathPlanningManager != null)
{
ForceReinitializeToolPlugin(subscribeToEvents: false);
}
// 重置所有状态
_hasStartPoint = false;
_hasEndPoint = false;
_startPoint3D = new Point3D();
_endPoint3D = new Point3D();
AutoPathStartPoint = "未选择";
AutoPathEndPoint = "未选择";
UpdateMainStatus("就绪");
IsSelectingStartPoint = false;
IsSelectingEndPoint = false;
// 清除路径引用
_autoPathStartPointRoute = null;
_autoPathEndPointRoute = null;
// 通知Can Execute属性更改
OnPropertyChanged(nameof(CanExecuteAutoPlanPath));
OnPropertyChanged(nameof(CanExecuteNewPath));
LogManager.Info("自动路径规划参数和起终点标记已完全重置,ToolPlugin已重新初始化");
}, "重置自动路径规划");
}
#endregion
#region 路径编辑命令 - 待实现
private async Task ExecuteRenamePathAsync()
{
if (SelectedPathRoute == null) return;
await SafeExecuteAsync(async () =>
{
await _uiStateManager.ExecuteUIUpdateAsync(() =>
{
// TODO: 实现重命名对话框
var newName = $"路径 {PathRoutes.Count}";
var oldName = SelectedPathRoute.Name;
SelectedPathRoute.Name = newName;
UpdateMainStatus($"路径已重命名: {oldName} -> {newName}");
});
}, "重命名路径");
}
private async Task ExecuteAddPathPointAsync()
{
if (!CanExecuteStartEdit) return;
await SafeExecuteAsync(async () =>
{
await _uiStateManager.ExecuteUIUpdateAsync(() =>
{
IsPathEditMode = true;
if (SelectedPathRoute != null)
{
SelectedPathRoute.IsActive = true;
UpdateMainStatus($"正在激活3D路径编辑模式: {SelectedPathRoute.Name}...");
// 检查是否是空轨路径
bool isRailPath = SelectedPathRoute.PathType == PathType.Rail;
// 启动PathPlanningManager的点击工具,这会设置正确的编辑状态
if (_pathPlanningManager != null)
{
try
{
if (isRailPath)
{
_pathPlanningManager.StartClickTool(PathPointType.WayPoint, enableRailSnapping: true);
LogManager.Info($"已启动PathPlanningManager点击工具(空轨吸附模式): {SelectedPathRoute.Name}");
}
else
{
_pathPlanningManager.StartClickTool(PathPointType.WayPoint);
LogManager.Info($"已启动PathPlanningManager点击工具: {SelectedPathRoute.Name}");
}
}
catch (Exception ex)
{
LogManager.Error($"启动点击工具失败: {ex.Message}");
UpdateMainStatus("启动3D编辑工具失败,请重试");
IsPathEditMode = false;
return;
}
}
else
{
UpdateMainStatus("路径规划管理器未初始化");
LogManager.Error("添加路径点:PathPlanningManager未初始化");
IsPathEditMode = false;
return;
}
string modeText = isRailPath ? "(空轨吸附模式)" : "";
UpdateMainStatus($"已进入3D路径编辑模式: {SelectedPathRoute.Name} {modeText} - 在3D视图中点击设置路径点");
LogManager.Info($"开始添加路径点: {SelectedPathRoute.Name},已启动点击工具");
// 手动触发按钮状态更新
OnPropertyChanged(nameof(CanExecuteStartEdit));
OnPropertyChanged(nameof(CanExecuteEndEdit));
}
});
}, "添加路径点");
}
private async Task ExecuteEndEditAsync()
{
if (!CanExecuteEndEdit) return;
if ((_pathPlanningManager == null || _pathPlanningManager.PathEditState == PathEditState.Viewing) &&
CanCancelRailAssemblyWorkflow)
{
ExecuteCancelRailAssemblyWorkflow();
return;
}
await SafeExecuteAsync(async () =>
{
bool success = false;
string operationMessage = "";
bool wasInPreviewMode = false;
if (_pathPlanningManager != null)
{
// 检查是否在修改路径点模式
bool isEditingPoint = _pathPlanningManager.PathEditState == PathEditState.EditingPoint;
if (isEditingPoint)
{
// 修改路径点模式 - 尝试确认修改
success = _pathPlanningManager.UpdatePointPosition();
if (success)
{
operationMessage = "路径点修改已确认";
}
else
{
// 确认失败(可能没有预览点),执行取消操作以安全退出
success = _pathPlanningManager.CancelEditPoint();
operationMessage = success ? "已取消路径点修改" : "取消路径点修改失败";
}
LogManager.Info($"[UI-修改路径点] {operationMessage}");
}
else
{
// 记录是否在预览模式(因为确认预览点后会退出预览模式)
wasInPreviewMode = _pathPlanningManager.IsPreviewMode;
if (wasInPreviewMode)
{
// 预览模式 - 先确认预览点,然后立即完成编辑
var confirmedPoint = _pathPlanningManager.ConvertPreviewToPathPoint();
if (confirmedPoint != null)
{
LogManager.Info($"[UI-预览模式] 预览点已确认: {confirmedPoint.Name},现在完成编辑");
// 立即完成编辑,不让用户继续添加点
success = _pathPlanningManager.FinishEditing();
operationMessage = success ? $"预览点已确认并完成编辑: {confirmedPoint.Name}" : "预览点确认后完成编辑失败";
}
else
{
LogManager.Error("[UI-预览模式] 预览点确认失败");
operationMessage = "预览点确认失败";
success = false;
}
LogManager.Info($"[UI-预览模式] {operationMessage}");
}
else
{
// 普通模式 - 直接完成编辑
success = _pathPlanningManager.FinishEditing();
operationMessage = success ? "路径编辑完成" : "路径编辑完成失败";
LogManager.Info($"[UI-编辑模式] {operationMessage}");
}
}
}
await _uiStateManager.ExecuteUIUpdateAsync(() =>
{
if (success)
{
// 编辑完成成功
if (SelectedPathRoute != null)
{
if (_pathPlanningManager?.PathEditState == PathEditState.EditingPoint || operationMessage.Contains("路径点修改"))
{
UpdateMainStatus($"✅ 路径点修改完成: {SelectedPathRoute.Name}");
}
else if (wasInPreviewMode)
{
UpdateMainStatus($"✅ 预览点已确认,路径编辑完成: {SelectedPathRoute.Name}");
}
else
{
UpdateMainStatus($"✅ 路径编辑完成: {SelectedPathRoute.Name} - 最后一个点已自动设置为终点");
}
}
LogManager.Info("[UI状态] 路径编辑完成,等待状态变更事件更新按钮");
}
else
{
// 编辑完成失败
if (SelectedPathRoute != null)
{
UpdateMainStatus($"❌ 路径编辑失败: {SelectedPathRoute.Name}");
}
LogManager.Error("[UI状态] 路径编辑失败");
}
// 手动触发按钮状态更新 - 确保UI及时响应
OnPropertyChanged(nameof(CanExecuteStartEdit));
OnPropertyChanged(nameof(CanExecuteEndEdit));
OnPropertyChanged(nameof(CanExecuteCancelModifyPoint));
});
}, "结束路径编辑");
}
private async Task ExecuteClearPathAsync()
{
if (!CanExecuteClearPath) return;
await SafeExecuteAsync(async () =>
{
var pointCount = SelectedPathRoute.Points.Count;
// 清空 Core 层的路径点
var coreRoute = _pathPlanningManager?.Routes?.FirstOrDefault(r => r.Name == SelectedPathRoute.Name);
if (coreRoute != null)
{
coreRoute.Points.Clear();
coreRoute.RecalculateAndSaveRoute("清空路径");
LogManager.Info($"已清空Core路径: {coreRoute.Name}");
}
// 清空 UI 层的路径点
await _uiStateManager.ExecuteUIUpdateAsync(() =>
{
SelectedPathRoute.Points.Clear();
UpdateMainStatus($"已清空路径 {SelectedPathRoute.Name} 的 {pointCount} 个点");
});
}, "清空路径");
}
private async Task ExecuteDeletePointAsync(PathPointViewModel point)
{
if (SelectedPathRoute == null || point == null) return;
await SafeExecuteAsync(async () =>
{
// 1. 查找对应的Core数据
var coreRoute = _pathPlanningManager?.Routes?.FirstOrDefault(r => r.Name == SelectedPathRoute.Name);
if (coreRoute == null)
{
LogManager.Warning($"未找到对应的Core路径: {SelectedPathRoute.Name}");
return;
}
// 2. 路径完整性验证:检查删除后是否至少还有2个点
if (coreRoute.Points.Count <= 2)
{
LogManager.Warning($"路径点不足,无法删除。当前路径只有{coreRoute.Points.Count}个点,至少需要保留2个点(起点和终点)");
UpdateMainStatus($"❌ 无法删除路径点:路径至少需要保留2个点(起点和终点),当前只有{coreRoute.Points.Count}个点");
return;
}
// 3. 在Core路径中查找对应的点(通过位置和名称匹配)
var corePoint = coreRoute.Points.FirstOrDefault(p =>
p.Name == point.Name &&
Math.Abs(p.Position.X - point.X) < 0.001 &&
Math.Abs(p.Position.Y - point.Y) < 0.001 &&
Math.Abs(p.Position.Z - point.Z) < 0.001);
if (corePoint == null)
{
LogManager.Warning($"未找到对应的Core路径点: {point.Name}");
// 即使Core数据不匹配,也要更新UI数据保持一致性
}
await _uiStateManager.ExecuteUIUpdateAsync(() =>
{
// 4. 禁止删除起点和终点
if (corePoint != null)
{
int pointIndex = coreRoute.Points.IndexOf(corePoint);
if (corePoint.Type == PathPointType.StartPoint)
{
LogManager.Warning($"不能删除路径的起点");
UpdateMainStatus($"❌ 无法删除:{corePoint.Name} 是路径的起点");
return;
}
if (corePoint.Type == PathPointType.EndPoint)
{
LogManager.Warning($"不能删除路径的终点");
UpdateMainStatus($"❌ 无法删除:{corePoint.Name} 是路径的终点");
return;
}
// 吊装路径:禁止删除关键点(提升点、下降点)
if (coreRoute.PathType == PathType.Hoisting &&
(corePoint.Name == "提升点" || corePoint.Name == "下降点"))
{
LogManager.Warning($"不能删除吊装路径的关键点: {corePoint.Name}");
UpdateMainStatus($"❌ 无法删除:{corePoint.Name} 是吊装路径的关键点");
return;
}
// 删除Core数据
coreRoute.Points.Remove(corePoint);
LogManager.Info($"已从Core数据删除路径点: {corePoint.Name}");
// 吊装路径:正交化路径(处理斜线和清除多余点)
if (coreRoute.PathType == PathType.Hoisting)
{
_pathPlanningManager?.OrthogonalizePath(coreRoute);
// 吊装路径:检测并移除矩形环路
LogManager.Info($"[吊装路径] 删除路径点后检测矩形环路");
_pathPlanningManager?.RemoveRectangularLoops(coreRoute);
}
// 调用PathPlanningManager的3D删除方法
_pathPlanningManager.RemovePathPoint(corePoint);
// 触发UI同步更新事件
_pathPlanningManager.RaisePathPointsListUpdated(coreRoute, "删除路径点并重新分配类型");
LogManager.Info("已触发PathPointsListUpdated事件,UI将自动同步");
}
// 5. 删除UI数据(事件驱动会自动处理,这里只作为备份)
SelectedPathRoute.Points.Remove(point);
LogManager.Info($"已从UI删除路径点: {point.Name}");
// 🔥 如果被删除的点是当前选中的点,清空选中状态
if (SelectedPathPoint != null && SelectedPathPoint.Id == point.Id)
{
SelectedPathPoint = null;
LogManager.Info("已清空选中状态(删除的是当前选中的点)");
}
// 6. 更新UI状态
UpdateMainStatus($"✅ 已删除路径点: {point.Name}");
LogManager.Info($"路径点删除完成,当前UI点数: {SelectedPathRoute.Points.Count}, Core点数: {coreRoute?.Points.Count ?? 0}");
});
// 7. 刷新3D可视化显示(路径变更会自动更新可视化)
UpdatePathVisualization();
LogManager.Info("已触发路径可视化更新");
}, "删除路径点");
}
private async Task ExecuteModifyPointAsync()
{
if (!CanExecuteModifyPoint) return;
await SafeExecuteAsync(async () =>
{
await _uiStateManager.ExecuteUIUpdateAsync(() =>
{
if (SelectedPathPoint != null && _pathPlanningManager != null)
{
// 获取选中路径点的索引
var currentRoute = _pathPlanningManager.CurrentRoute;
if (currentRoute != null && currentRoute.Points != null)
{
int pointIndex = -1;
// 详细调试信息
LogManager.Info($"开始查找路径点索引 - 选中点Id: {SelectedPathPoint.Id}, 选中点名称: {SelectedPathPoint.Name}");
LogManager.Info($"当前路径包含 {currentRoute.Points.Count} 个路径点");
// 遍历查找匹配的Id
for (int i = 0; i < currentRoute.Points.Count; i++)
{
var currentPoint = currentRoute.Points[i];
//LogManager.Info($"检查路径点 [{i}] - Id: {currentPoint.Id}, 名称: {currentPoint.Name}");
if (currentPoint.Id == SelectedPathPoint.Id)
{
pointIndex = i;
LogManager.Info($"找到匹配的路径点,索引: {pointIndex}");
break;
}
}
if (pointIndex >= 0)
{
// 调用PathPlanningManager开始修改路径点
bool success = _pathPlanningManager.StartEditingPoint(pointIndex);
if (success)
{
UpdateMainStatus($"正在修改路径点: {SelectedPathPoint.Name} - 请在3D视图中点击新位置");
LogManager.Info($"已开始修改路径点: {SelectedPathPoint.Name}, 索引: {pointIndex}");
// 刷新命令状态
OnPropertyChanged(nameof(CanExecuteStartEdit));
OnPropertyChanged(nameof(CanExecuteEndEdit));
OnPropertyChanged(nameof(CanExecuteModifyPoint));
OnPropertyChanged(nameof(CanExecuteCancelModifyPoint));
}
else
{
UpdateMainStatus("修改路径点失败,请检查路径状态");
LogManager.Error($"开始修改路径点失败: {SelectedPathPoint.Name}, 索引: {pointIndex}");
}
}
else
{
UpdateMainStatus("找不到选中的路径点");
LogManager.Error($"找不到路径点索引 - 选中点Id: {SelectedPathPoint.Id}, 选中点名称: {SelectedPathPoint.Name}");
LogManager.Error("可能原因:1)选中的路径点与当前路径不匹配 2)路径点数据不同步 3)Id不匹配");
}
}
else
{
if (currentRoute == null)
{
UpdateMainStatus("当前没有活动路径");
LogManager.Error("尝试修改路径点时没有当前路径");
}
else
{
UpdateMainStatus("当前路径没有路径点数据");
LogManager.Error("当前路径的Points集合为空");
}
}
}
else
{
if (SelectedPathPoint == null)
{
UpdateMainStatus("请先选中要修改的路径点");
LogManager.Warning("修改路径点时没有选中路径点");
}
else
{
UpdateMainStatus("路径规划管理器未初始化");
LogManager.Error("修改路径点时PathPlanningManager为空");
}
}
});
}, "修改路径点");
}
///
/// 取消修改路径点
///
private async Task ExecuteCancelModifyPointAsync()
{
if (!CanExecuteCancelModifyPoint) return;
await SafeExecuteAsync(async () =>
{
await _uiStateManager.ExecuteUIUpdateAsync(() =>
{
if (_pathPlanningManager != null)
{
// 调用PathPlanningManager取消修改路径点
bool success = _pathPlanningManager.CancelEditPoint();
if (success)
{
UpdateMainStatus("已取消修改路径点");
LogManager.Info("用户取消了修改路径点操作");
}
else
{
UpdateMainStatus("取消修改路径点失败");
LogManager.Error("取消修改路径点失败");
}
// 刷新命令状态
OnPropertyChanged(nameof(CanExecuteStartEdit));
OnPropertyChanged(nameof(CanExecuteEndEdit));
OnPropertyChanged(nameof(CanExecuteModifyPoint));
OnPropertyChanged(nameof(CanExecuteCancelModifyPoint));
}
});
}, "取消修改路径点");
}
#endregion
#region 文件管理命令
private async Task ExecuteImportPathAsync()
{
await SafeExecuteAsync(async () =>
{
try
{
// 打开文件选择对话框
var openFileDialog = new OpenFileDialog
{
Title = "导入路径文件",
Filter = "路径文件 (*.xml;*.json)|*.xml;*.json|XML文件 (*.xml)|*.xml|JSON文件 (*.json)|*.json|所有文件 (*.*)|*.*",
FilterIndex = 1,
CheckFileExists = true,
CheckPathExists = true
};
if (openFileDialog.ShowDialog() == true)
{
var filePath = openFileDialog.FileName;
var extension = Path.GetExtension(filePath).ToLower();
// 确定导入格式
ExportFormat importFormat;
switch (extension)
{
case ".json":
importFormat = ExportFormat.Json;
break;
case ".csv":
importFormat = ExportFormat.Csv;
break;
default:
importFormat = ExportFormat.Xml;
break;
}
// 创建导入参数
var importParameters = new ImportPathParameters
{
FilePath = filePath,
ImportFormat = importFormat,
MergeWithExisting = true,
CreateBackup = true,
DuplicateHandling = DuplicateNameHandling.Rename,
ValidateImportedData = true,
AutoSelectFirstRoute = true,
Description = $"导入路径文件: {Path.GetFileName(filePath)}"
};
// 创建并执行导入命令
var importCommand = new ImportPathCommand(importParameters, _pathPlanningManager);
var result = await importCommand.ExecuteAsync();
if (result.IsSuccess)
{
// 更新UI状态
RefreshPathRoutes();
// 如果需要自动选择第一个导入的路径,通过专门的选择逻辑处理
if (importParameters.AutoSelectFirstRoute && PathRoutes.Count > 0)
{
var firstRoute = PathRoutes.FirstOrDefault();
if (firstRoute != null)
{
SelectedPathRoute = firstRoute;
LogManager.Info($"导入完成后自动选择路径: {firstRoute.Name}");
}
}
var importResult = (result as PathPlanningResult)?.Data;
if (importResult != null)
{
var statusMessage = $"✅ 导入成功: {importResult.ImportedCount}/{importResult.TotalInFile} 个路径";
if (importResult.FailedCount > 0)
{
statusMessage += $" (失败 {importResult.FailedCount} 个)";
}
UpdateMainStatus(statusMessage);
if (!string.IsNullOrEmpty(importResult.BackupFilePath))
{
LogManager.Info($"导入前备份已创建: {importResult.BackupFilePath}");
}
}
else
{
UpdateMainStatus($"✅ 路径导入完成: {Path.GetFileName(filePath)}");
}
}
else
{
UpdateMainStatus($"❌ 导入失败: {result.ErrorMessage}");
LogManager.Error($"路径导入失败: {result.ErrorMessage}");
}
}
}
catch (Exception ex)
{
LogManager.Error($"导入路径异常: {ex.Message}", ex);
UpdateMainStatus($"❌ 导入异常: {ex.Message}");
}
}, "导入路径");
}
private async Task ExecuteExportPathAsync()
{
if (!CanExecuteExportPath) return;
await SafeExecuteAsync(async () =>
{
try
{
// 打开文件保存对话框
var saveFileDialog = new SaveFileDialog
{
Title = "导出全部路径",
Filter = "XML文件 (*.xml)|*.xml|JSON文件 (*.json)|*.json|CSV文件 (*.csv)|*.csv|DELMIA Apriso XML (*.aprxml)|*.aprxml|所有文件 (*.*)|*.*",
FilterIndex = 1,
FileName = $"路径导出_{DateTime.Now:yyyyMMdd_HHmmss}",
DefaultExt = "xml",
AddExtension = true
};
if (saveFileDialog.ShowDialog() == true)
{
var filePath = saveFileDialog.FileName;
// 确定导出格式(通过FilterIndex判断,因为DELMIA Apriso和普通XML都是.xml扩展名)
ExportFormat exportFormat;
switch (saveFileDialog.FilterIndex)
{
case 2: // JSON文件
exportFormat = ExportFormat.Json;
break;
case 3: // CSV文件
exportFormat = ExportFormat.Csv;
break;
case 4: // DELMIA Apriso XML
exportFormat = ExportFormat.DelmiaApriso;
break;
default: // XML文件或其他
exportFormat = ExportFormat.Xml;
break;
}
// 创建导出参数 - 导出所有路径
var exportParameters = new ExportPathParameters
{
ExportAll = true,
OutputFilePath = filePath,
ExportFormat = exportFormat,
OverwriteExisting = true,
ValidateExportData = true,
GenerateReport = false,
Description = $"导出全部路径到: {Path.GetFileName(filePath)}"
};
// 创建并执行导出命令
var exportCommand = new ExportPathCommand(exportParameters, _pathPlanningManager);
var result = await exportCommand.ExecuteAsync();
if (result.IsSuccess)
{
var exportResult = (result as PathPlanningResult)?.Data;
if (exportResult != null)
{
var statusMessage = $"✅ 导出全部成功: {exportResult.ExportedCount} 个路径到 {Path.GetFileName(filePath)}";
if (exportResult.FailedCount > 0)
{
statusMessage += $" (失败 {exportResult.FailedCount} 个)";
}
UpdateMainStatus(statusMessage);
LogManager.Info($"导出完成,文件大小: {exportResult.FileSizeMB:F2} MB");
}
else
{
UpdateMainStatus($"✅ 全部路径导出完成: {Path.GetFileName(filePath)}");
}
}
else
{
UpdateMainStatus($"❌ 导出全部失败: {result.ErrorMessage}");
LogManager.Error($"全部路径导出失败: {result.ErrorMessage}");
}
}
}
catch (Exception ex)
{
LogManager.Error($"导出全部路径异常: {ex.Message}", ex);
UpdateMainStatus($"❌ 导出全部异常: {ex.Message}");
}
}, "导出全部路径");
}
private async Task ExecuteSaveAsPathAsync()
{
if (!CanExecuteSaveAsPath) return;
await SafeExecuteAsync(async () =>
{
try
{
// 打开文件保存对话框
var saveFileDialog = new SaveFileDialog
{
Title = $"导出选中路径: {SelectedPathRoute?.Name}",
Filter = "XML文件 (*.xml)|*.xml|JSON文件 (*.json)|*.json|CSV文件 (*.csv)|*.csv|DELMIA Apriso XML (*.aprxml)|*.aprxml|所有文件 (*.*)|*.*",
FilterIndex = 1,
FileName = SelectedPathRoute?.Name?.Replace(" ", "_") ?? "路径",
DefaultExt = "xml",
AddExtension = true
};
if (saveFileDialog.ShowDialog() == true)
{
var filePath = saveFileDialog.FileName;
// 确定导出格式(通过FilterIndex判断,因为DELMIA Apriso和普通XML都是.xml扩展名)
ExportFormat exportFormat;
switch (saveFileDialog.FilterIndex)
{
case 2: // JSON文件
exportFormat = ExportFormat.Json;
break;
case 3: // CSV文件
exportFormat = ExportFormat.Csv;
break;
case 4: // DELMIA Apriso XML
exportFormat = ExportFormat.DelmiaApriso;
break;
default: // XML文件或其他
exportFormat = ExportFormat.Xml;
break;
}
// 查找当前选中路径对应的Core路径
var coreRoute = _pathPlanningManager.Routes.FirstOrDefault(r => r.Name == SelectedPathRoute.Name);
if (coreRoute != null)
{
// 创建导出参数 - 只导出当前选中的路径
var exportParameters = new ExportPathParameters
{
PathsToExport = new System.Collections.Generic.List { coreRoute },
OutputFilePath = filePath,
ExportFormat = exportFormat,
OverwriteExisting = true,
ValidateExportData = true,
GenerateReport = false,
Description = $"导出选中路径: {SelectedPathRoute.Name}"
};
// 创建并执行导出命令
var exportCommand = new ExportPathCommand(exportParameters, _pathPlanningManager);
var result = await exportCommand.ExecuteAsync();
if (result.IsSuccess)
{
var exportResult = (result as PathPlanningResult)?.Data;
if (exportResult != null)
{
UpdateMainStatus($"✅ 导出选中路径成功: {SelectedPathRoute.Name} -> {Path.GetFileName(filePath)}");
LogManager.Info($"导出选中路径完成,文件大小: {exportResult.FileSizeMB:F2} MB");
}
else
{
UpdateMainStatus($"✅ 导出选中路径完成: {Path.GetFileName(filePath)}");
}
}
else
{
UpdateMainStatus($"❌ 导出选中路径失败: {result.ErrorMessage}");
LogManager.Error($"导出选中路径失败: {result.ErrorMessage}");
}
}
else
{
UpdateMainStatus($"❌ 导出选中路径失败:找不到对应的Core路径: {SelectedPathRoute.Name}");
}
}
}
catch (Exception ex)
{
LogManager.Error($"导出选中路径异常: {ex.Message}", ex);
UpdateMainStatus($"❌ 导出选中路径异常: {ex.Message}");
}
}, "导出选中路径");
}
///
/// 生成导航地图的纯业务逻辑方法
///
/// 渲染样式
/// 图像宽度
/// 图像高度
/// 图像格式
/// 生成的图像数据
public Bitmap GenerateNavigationMap(ImageGenerationStyle renderStyle, int width, int height, ImageFormat format)
{
try
{
LogManager.Info($"开始生成导航地图: 样式={renderStyle}, 尺寸={width}x{height}, 格式={format}");
// 创建导航地图生成器
var generator = new NavigationMapGenerator();
// 生成图像
var bitmap = generator.GenerateNavigationMapImage(renderStyle, width, height);
LogManager.Info($"导航地图生成成功: {width}x{height}像素");
return bitmap;
}
catch (Exception ex)
{
LogManager.Error($"生成导航地图失败: {ex.Message}", ex);
throw;
}
}
///
/// 刷新路径集合显示
///
private void RefreshPathRoutes()
{
try
{
if (_pathPlanningManager?.Routes != null)
{
// 清除现有的UI路径
PathRoutes.Clear();
// 重新加载所有路径
foreach (var coreRoute in _pathPlanningManager.Routes)
{
var pathViewModel = new PathRouteViewModel(isFromDatabase: true)
{
Route = coreRoute,
IsActive = false
};
// 设置时间信息
pathViewModel.SetTimeInfo(coreRoute.CreatedTime, coreRoute.LastModified);
// 使用统一的辅助方法创建路径点
var pathPoints = CreatePathPointViewModelsFromCoreRoute(coreRoute);
foreach (var point in pathPoints)
{
pathViewModel.Points.Add(point);
}
PathRoutes.Add(pathViewModel);
LogManager.Info($"RefreshPathRoutes: 添加路径ViewModel {coreRoute.Name} (Hash: {pathViewModel.GetHashCode()})");
}
LogManager.Info($"已刷新路径集合显示,共 {PathRoutes.Count} 个路径");
// 调试:列出所有PathRoutes中的路径名称和哈希码
for (int i = 0; i < PathRoutes.Count; i++)
{
var route = PathRoutes[i];
LogManager.Info($" PathRoutes[{i}]: {route.Name} (Hash: {route.GetHashCode()}, Points: {route.Points.Count})");
}
}
}
catch (Exception ex)
{
LogManager.Error($"刷新路径集合失败: {ex.Message}", ex);
}
}
#endregion
#endregion
#region 事件处理
///
/// 处理自动路径规划的鼠标点击事件
///
private async void OnAutoPathMouseClicked(object sender, PickItemResult pickResult)
{
try
{
if (pickResult == null)
{
return;
}
var point3D = pickResult.Point;
if (IsSelectingStartPoint)
{
SetAutoPathStartPoint(point3D);
await SafeExecuteAsync(() =>
{
// 退出自动路径规划模式 - 恢复事件订阅
LogManager.Info("起点选择完成,恢复正常事件订阅状态");
CleanupAutoPathEventSubscriptions();
_pathPlanningManager?.EnableMouseHandling(); // 恢复PathPlanningManager的鼠标处理
_pathPlanningManager?.StopClickTool();
}, "停止起点选择工具");
}
else if (IsSelectingEndPoint)
{
SetAutoPathEndPoint(point3D);
await SafeExecuteAsync(() =>
{
// 退出自动路径规划模式 - 恢复事件订阅
LogManager.Info("终点选择完成,恢复正常事件订阅状态");
CleanupAutoPathEventSubscriptions();
_pathPlanningManager?.EnableMouseHandling(); // 恢复PathPlanningManager的鼠标处理
_pathPlanningManager?.StopClickTool();
}, "停止终点选择工具");
}
else
{
CleanupAutoPathEventSubscriptions();
_pathPlanningManager?.EnableMouseHandling(); // 恢复PathPlanningManager的鼠标处理
}
}
catch (Exception ex)
{
LogManager.Error($"[自动路径事件] 处理点击事件异常: {ex.Message}");
UpdateMainStatus($"获取点击位置失败: {ex.Message}");
IsSelectingStartPoint = false;
IsSelectingEndPoint = false;
// 异常情况下也要清理事件订阅并恢复状态
try
{
CleanupAutoPathEventSubscriptions();
_pathPlanningManager?.EnableMouseHandling(); // 恢复PathPlanningManager的鼠标处理
_pathPlanningManager?.StopClickTool();
}
catch (Exception cleanupEx)
{
LogManager.Error($"[自动路径事件] 清理异常: {cleanupEx.Message}");
}
}
}
///
/// 设置自动路径规划的起点
///
public async void SetAutoPathStartPoint(Point3D point)
{
await SafeExecuteAsync(() =>
{
// 直接设置新的起点(清理逻辑已在ExecuteSelectStartPointAsync中处理)
_startPoint3D = point;
_hasStartPoint = true;
AutoPathStartPoint = $"({point.X:F2}, {point.Y:F2}, {point.Z:F2})";
IsSelectingStartPoint = false;
// 通知Can Execute属性更改
OnPropertyChanged(nameof(CanExecuteAutoPlanPath));
OnPropertyChanged(nameof(CanExecuteNewPath));
// 渲染新的起点标记
if (PathPointRenderPlugin.Instance != null)
{
// 创建临时路径只包含起点
_autoPathStartPointRoute = new PathRoute("AutoPathStartPoint")
{
Description = "自动路径规划起点"
};
var startPathPoint = new PathPoint
{
Name = "起点",
Position = point,
Type = PathPointType.StartPoint
};
_autoPathStartPointRoute.AddPoint(startPathPoint);
// 使用新的只渲染点的API,不绘制连线
PathPointRenderPlugin.Instance.RenderPointOnly(_autoPathStartPointRoute);
LogManager.Info($"起点路径已创建,ID: {_autoPathStartPointRoute.Id}");
}
if (_hasEndPoint)
{
UpdateMainStatus("起点和终点已设置,可以开始路径规划");
}
else
{
UpdateMainStatus("起点已设置,请选择终点");
}
LogManager.Info($"起点已设置: ({point.X:F2}, {point.Y:F2}, {point.Z:F2})");
}, "设置起点");
}
///
/// 设置自动路径规划的终点
///
public async void SetAutoPathEndPoint(Point3D point)
{
await SafeExecuteAsync(() =>
{
// 直接设置新的终点(清理逻辑已在ExecuteSelectEndPointAsync中处理)
_endPoint3D = point;
_hasEndPoint = true;
AutoPathEndPoint = $"({point.X:F2}, {point.Y:F2}, {point.Z:F2})";
IsSelectingEndPoint = false;
// 通知Can Execute属性更改
OnPropertyChanged(nameof(CanExecuteAutoPlanPath));
OnPropertyChanged(nameof(CanExecuteNewPath));
// 渲染新的终点标记
if (PathPointRenderPlugin.Instance != null)
{
// 创建临时路径只包含终点
_autoPathEndPointRoute = new PathRoute("AutoPathEndPoint")
{
Description = "自动路径规划终点"
};
var endPathPoint = new PathPoint
{
Name = "终点",
Position = point,
Type = PathPointType.EndPoint
};
_autoPathEndPointRoute.AddPoint(endPathPoint);
// 使用新的只渲染点的API,不绘制连线
PathPointRenderPlugin.Instance.RenderPointOnly(_autoPathEndPointRoute);
LogManager.Info($"终点路径已创建,ID: {_autoPathEndPointRoute.Id}");
}
if (_hasStartPoint)
{
UpdateMainStatus("起点和终点已设置,可以开始路径规划");
}
else
{
UpdateMainStatus("终点已设置,请选择起点");
}
LogManager.Info($"终点已设置: ({point.X:F2}, {point.Y:F2}, {point.Z:F2})");
}, "设置终点");
}
#endregion
#region 物体参数同步
///
/// 同步物体参数到渲染插件
///
private void SyncObjectParametersToRenderPlugin()
{
try
{
if (PathPointRenderPlugin.Instance != null)
{
// 检查检测动画页签是否选择了物体或使用了虚拟物体
// 如果有,使用动画页签的物体参数来更新通行空间(而不是跳过)
var animationVm = AnimationControlViewModel.Instance;
if (animationVm != null && animationVm.HasSelectedAnimatedObject)
{
// 动画页签已选择物体,使用动画页签的物体尺寸和当前路径的类型
LogManager.Debug("[物体参数同步] 检测动画页签已选择动画对象,使用动画页签的物体尺寸");
SyncAnimationViewObjectParameters(animationVm);
return;
}
// 将米单位转换为模型单位(SetPassageSpaceParameters需要模型单位)
var metersToUnits = UnitsConverter.GetMetersToUnitsConversionFactor();
double objectLengthModel = ObjectLength * metersToUnits;
double objectWidthModel = ObjectWidth * metersToUnits;
double objectHeightModel = ObjectHeight * metersToUnits;
double safetyMarginModel = SafetyMargin * metersToUnits;
// 根据路径类型计算通行空间尺寸
double passageAcrossPath, passageNormalToPath, passageAlongPath;
PathType pathType = SelectedPathRoute?.PathType ?? PathType.Ground;
if (pathType == PathType.Rail)
{
// 空轨路径:物体水平悬挂,垂直于路径的截面包含物体的宽度和高度
passageAcrossPath = objectWidthModel + 2 * safetyMarginModel; // Y方向(宽度)+ 2*间隙(垂直于路径方向)
passageNormalToPath = objectHeightModel + 2 * safetyMarginModel; // Z方向(高度)+ 2*间隙(法线方向)
passageAlongPath = objectLengthModel; // X方向(长度)(沿路径方向)
}
else if (pathType == PathType.Hoisting)
{
// 吊装路径:物体垂直悬挂,垂直于路径的截面包含物体的长度和宽度
passageAcrossPath = objectWidthModel + 2 * safetyMarginModel; // Y方向(宽度)+ 2*间隙(垂直于路径方向)
passageNormalToPath = objectLengthModel + 2 * safetyMarginModel; // X方向(长度)+ 2*间隙(法线方向)
passageAlongPath = objectHeightModel; // Z方向(高度)(沿路径方向)
}
else // Ground
{
// 地面路径:高度只有上方加间隙
passageAcrossPath = objectWidthModel + 2 * safetyMarginModel; // Y方向(宽度)+ 2*间隙(垂直于路径方向)
passageNormalToPath = objectHeightModel + safetyMarginModel; // Z方向(高度)+ 间隙(法线方向,只有上方)
passageAlongPath = objectLengthModel; // X方向(长度)(沿路径方向)
}
// 设置通行空间参数到渲染插件
// passageNormalToPathVertical: 垂直段的高度(物体长度 + 2×安全间隙)
// passageNormalToPathHorizontal: 水平段的高度(物体高度 + 2×安全间隙)
double passageNormalToPathVertical = objectLengthModel + 2 * safetyMarginModel;
double passageNormalToPathHorizontal = objectHeightModel + 2 * safetyMarginModel;
PathPointRenderPlugin.Instance.SetPassageSpaceParameters(
passageAcrossPath,
passageNormalToPath,
passageAlongPath,
passageNormalToPathVertical,
passageNormalToPathHorizontal);
LogManager.Debug($"[物体参数同步] 已设置通行空间参数: 垂直路径={passageAcrossPath / metersToUnits:F2}m, 法线={passageNormalToPath / metersToUnits:F2}m, 沿路径={passageAlongPath / metersToUnits:F2}m, 间隙={SafetyMargin:F2}m");
}
else
{
LogManager.Warning("[物体参数同步] PathPointRenderPlugin实例为空,无法同步物体参数");
}
}
catch (Exception ex)
{
LogManager.Error($"[物体参数同步] 同步物体参数失败: {ex.Message}", ex);
}
}
///
/// 使用动画页签的物体参数同步到渲染插件
///
private void SyncAnimationViewObjectParameters(AnimationControlViewModel animationVm)
{
try
{
// 获取当前路径类型(使用路径编辑页签的当前选择)
PathType pathType = SelectedPathRoute?.PathType ?? PathType.Ground;
// 获取单位转换因子
var metersToUnits = UnitsConverter.GetMetersToUnitsConversionFactor();
double safetyMarginModel = SafetyMargin * metersToUnits;
double objectLengthModel, objectWidthModel, objectHeightModel;
if (animationVm.UseVirtualObject)
{
// 使用虚拟物体尺寸
objectLengthModel = animationVm.VirtualObjectLengthInMeters * metersToUnits;
objectWidthModel = animationVm.VirtualObjectWidthInMeters * metersToUnits;
objectHeightModel = animationVm.VirtualObjectHeightInMeters * metersToUnits;
LogManager.Debug($"[物体参数同步] 使用虚拟物体尺寸: {animationVm.VirtualObjectLengthInMeters:F2}m x {animationVm.VirtualObjectWidthInMeters:F2}m x {animationVm.VirtualObjectHeightInMeters:F2}m");
}
else if (animationVm.SelectedAnimatedObject != null)
{
var animationManager = NavisworksTransport.Core.Animation.PathAnimationManager.GetInstance();
if ((pathType == PathType.Ground || pathType == PathType.Hoisting) &&
animationManager != null &&
animationManager.TryGetCurrentRouteRealObjectPlanarProjectedExtents(
out double resolvedPlanarForward,
out double resolvedPlanarSide,
out double resolvedPlanarUp))
{
objectLengthModel = resolvedPlanarForward;
objectWidthModel = resolvedPlanarSide;
objectHeightModel = resolvedPlanarUp;
LogManager.Debug(
$"[物体参数同步] 使用真实物体平面路径最终姿态尺寸: " +
$"沿路径={resolvedPlanarForward / metersToUnits:F2}m, " +
$"垂直路径={resolvedPlanarSide / metersToUnits:F2}m, " +
$"法线={resolvedPlanarUp / metersToUnits:F2}m");
}
else if (pathType == PathType.Rail &&
animationManager != null &&
animationManager.TryGetCurrentRouteRealObjectRailProjectedExtents(
out double resolvedRailForward,
out double resolvedRailSide,
out double resolvedRailUp))
{
objectLengthModel = resolvedRailForward;
objectWidthModel = resolvedRailSide;
objectHeightModel = resolvedRailUp;
LogManager.Debug(
$"[物体参数同步] 使用真实物体 Rail 最终姿态尺寸: " +
$"沿路径={resolvedRailForward / metersToUnits:F2}m, " +
$"垂直路径={resolvedRailSide / metersToUnits:F2}m, " +
$"法线={resolvedRailUp / metersToUnits:F2}m");
}
else
{
// 回退:使用选择物体时保存的原始尺寸(米)
objectLengthModel = animationVm.ObjectOriginalLength * metersToUnits;
objectWidthModel = animationVm.ObjectOriginalWidth * metersToUnits;
objectHeightModel = animationVm.ObjectOriginalHeight * metersToUnits;
LogManager.Debug(
$"[物体参数同步] 回退使用选择物体保存的原始尺寸: " +
$"{animationVm.ObjectOriginalLength:F2}m x {animationVm.ObjectOriginalWidth:F2}m x {animationVm.ObjectOriginalHeight:F2}m");
}
}
else
{
// 没有物体,使用默认参数
LogManager.Warning("[物体参数同步] 动画页签已选择物体但实际无物体,使用默认参数");
objectLengthModel = ObjectLength * metersToUnits;
objectWidthModel = ObjectWidth * metersToUnits;
objectHeightModel = ObjectHeight * metersToUnits;
}
// 根据路径类型计算通行空间尺寸
double passageAcrossPath, passageNormalToPath, passageAlongPath;
if (pathType == PathType.Rail)
{
passageAcrossPath = objectWidthModel + 2 * safetyMarginModel;
passageNormalToPath = objectHeightModel + 2 * safetyMarginModel;
passageAlongPath = objectLengthModel;
}
else if (pathType == PathType.Hoisting)
{
passageAcrossPath = objectWidthModel + 2 * safetyMarginModel;
passageNormalToPath = objectLengthModel + 2 * safetyMarginModel;
passageAlongPath = objectHeightModel;
}
else // Ground
{
passageAcrossPath = objectWidthModel + 2 * safetyMarginModel;
passageNormalToPath = objectHeightModel + safetyMarginModel;
passageAlongPath = objectLengthModel;
}
// 设置通行空间参数到渲染插件
double passageNormalToPathVertical = objectLengthModel + 2 * safetyMarginModel;
double passageNormalToPathHorizontal = objectHeightModel + 2 * safetyMarginModel;
PathPointRenderPlugin.Instance.SetPassageSpaceParameters(
passageAcrossPath,
passageNormalToPath,
passageAlongPath,
passageNormalToPathVertical,
passageNormalToPathHorizontal);
LogManager.Debug($"[物体参数同步] 已使用动画页签物体参数设置通行空间: 类型={pathType}, 沿路径={passageAlongPath / metersToUnits:F2}m, 垂直路径={passageAcrossPath / metersToUnits:F2}m");
}
catch (Exception ex)
{
LogManager.Error($"[物体参数同步] 同步动画页签物体参数失败: {ex.Message}", ex);
}
}
#endregion
#region 辅助方法
///
/// 安全执行异步操作
///
private async Task SafeExecuteAsync(Func action, string operationName = "未知操作")
{
try
{
await action();
}
catch (Exception ex)
{
LogManager.Error($"{operationName}发生异常: {ex.Message}", ex);
UpdateMainStatus($"{operationName}失败: {ex.Message}");
}
}
///
/// 网格可视化设置变更事件处理
///
private void OnGridVisualizationChanged()
{
try
{
LogManager.Info($"网格可视化设置已更改: 通行={ShowWalkableGrid}, 障碍物={ShowObstacleGrid}, 未知={ShowUnknownGrid}, 门={ShowDoorGrid}");
// 通知路径规划管理器更新网格可视化
if (_pathPlanningManager != null)
{
_pathPlanningManager.UpdateGridVisualizationSettings(
showWalkable: ShowWalkableGrid,
showObstacle: ShowObstacleGrid,
showUnknown: ShowUnknownGrid,
showDoor: ShowDoorGrid);
UpdateMainStatus("网格可视化设置已更新");
}
else
{
LogManager.Warning("无法获取路径规划管理器,网格可视化设置可能不会立即生效");
UpdateMainStatus("网格可视化设置已保存");
}
}
catch (Exception ex)
{
LogManager.Error($"应用网格可视化设置失败: {ex.Message}", ex);
UpdateMainStatus("网格可视化设置应用失败");
}
}
///
/// 网格点类型变更事件处理
///
private void OnGridPointTypeChanged()
{
try
{
LogManager.Info($"网格点类型已更改: {GridPointType}");
// 通知路径点渲染插件更新网格点类型
var renderPlugin = PathPointRenderPlugin.Instance;
if (renderPlugin != null)
{
renderPlugin.GridPointType = GridPointType;
UpdateMainStatus("网格点类型已更新");
}
else
{
LogManager.Warning("无法获取PathPointRenderPlugin实例,网格点类型可能不会立即生效");
UpdateMainStatus("网格点类型已保存");
}
}
catch (Exception ex)
{
LogManager.Error($"应用网格点类型失败: {ex.Message}", ex);
UpdateMainStatus("网格点类型应用失败");
}
}
///
/// 根据路径类型自动设置可视化模式
/// - 地面路径:控制点连线 + 路径线
/// - 吊装和空轨:控制点连线 + 通行空间(不能使用路径线)
///
private void UpdatePassageSpaceVisualizationForPathType(PathType pathType)
{
try
{
// 所有路径类型默认都打开控制点连线
ShowControlVisualization = true;
switch (pathType)
{
case PathType.Rail:
case PathType.Hoisting:
// 空中路径:通行空间 + 控制点,不能使用路径线
ShowPathLines = false;
ShowObjectSpace = true;
LogManager.Debug($"[路径编辑] 路径类型={pathType}(空中路径),默认模式:控制点+通行空间");
break;
case PathType.Ground:
// 地面路径:路径线 + 控制点,不使用通行空间
ShowPathLines = true;
ShowObjectSpace = false;
LogManager.Debug($"[路径编辑] 路径类型={pathType}(地面路径),默认模式:控制点+路径线");
break;
default:
LogManager.Warning($"[路径编辑] 未知的路径类型: {pathType}");
return;
}
LogManager.Info($"[路径编辑] 已根据路径类型自动设置可视化模式:控制点={ShowControlVisualization}, 路径线={ShowPathLines}, 通行空间={ShowObjectSpace}");
// 🔥 触发可视化状态变更事件,通知状态栏刷新按钮
_pathPlanningManager?.RaiseVisualizationStateChanged();
}
catch (Exception ex)
{
LogManager.Error($"[路径编辑] 设置可视化模式失败: {ex.Message}", ex);
}
}
///
/// 路径可视化模式变更事件处理
///
private void OnPathVisualizationModeChanged()
{
try
{
var renderPlugin = PathPointRenderPlugin.Instance;
if (renderPlugin != null)
{
// 使用新的独立开关设置渲染插件
renderPlugin.ShowPathLines = ShowPathLines;
renderPlugin.ShowObjectSpace = ShowObjectSpace;
string modeName = "";
if (ShowPathLines && ShowObjectSpace)
modeName = "路径线 + 通行空间";
else if (ShowPathLines)
modeName = "路径线";
else if (ShowObjectSpace)
modeName = "通行空间";
else
modeName = "无路径可视化";
// 🔥 触发视图刷新
if (NavisApplication.ActiveDocument?.ActiveView != null)
{
NavisApplication.ActiveDocument.ActiveView.RequestDelayedRedraw(ViewRedrawRequests.Render);
}
LogManager.Info($"路径可视化模式已更改: {modeName}");
UpdateMainStatus($"路径可视化: {modeName}");
}
else
{
LogManager.Warning("无法获取PathPointRenderPlugin实例");
}
}
catch (Exception ex)
{
LogManager.Error($"应用路径可视化模式失败: {ex.Message}", ex);
UpdateMainStatus("路径可视化模式更新失败");
}
}
///
/// 控制点可视化变更事件处理
///
private void OnControlVisualizationChanged()
{
try
{
var renderPlugin = PathPointRenderPlugin.Instance;
if (renderPlugin != null)
{
// 更新所有路径的 ShowControlVisualization 属性
lock (typeof(PathPointRenderPlugin))
{
var allVisualizations = renderPlugin.GetAllPathVisualizations();
foreach (var visualization in allVisualizations)
{
visualization.ShowControlVisualization = ShowControlVisualization;
}
}
// 触发视图刷新
if (NavisApplication.ActiveDocument?.ActiveView != null)
{
NavisApplication.ActiveDocument.ActiveView.RequestDelayedRedraw(ViewRedrawRequests.Render);
}
LogManager.Info($"控制点可视化已更改: {(ShowControlVisualization ? "显示" : "隐藏")}");
UpdateMainStatus("控制点可视化已更新");
}
else
{
LogManager.Warning("无法获取PathPointRenderPlugin实例");
}
}
catch (Exception ex)
{
LogManager.Error($"应用控制点可视化设置失败: {ex.Message}", ex);
UpdateMainStatus("控制点可视化设置失败");
}
}
///
/// 清理自动路径相关的事件订阅
///
private void CleanupAutoPathEventSubscriptions()
{
try
{
// 简单安全地移除事件订阅
// 由于C#事件处理机制,多次取消订阅同一个处理程序是安全的
// 即使处理程序未订阅,取消操作也不会抛出异常
PathClickToolPlugin.MouseClicked -= OnAutoPathMouseClicked;
}
catch (Exception ex)
{
LogManager.Error($"[事件清理] 清理自动路径事件订阅失败: {ex.Message}", ex);
// 记录异常但不抛出,避免影响主流程
}
}
///
/// 清理临时的自动路径起点和终点标记
///
private void ClearTemporaryAutoPathMarkers()
{
try
{
if (PathPointRenderPlugin.Instance != null)
{
// 清除临时的起点标记(使用正确的路径ID)
if (_autoPathStartPointRoute != null)
{
PathPointRenderPlugin.Instance.RemovePath(_autoPathStartPointRoute.Id);
_autoPathStartPointRoute = null;
}
// 清除临时的终点标记(使用正确的路径ID)
if (_autoPathEndPointRoute != null)
{
PathPointRenderPlugin.Instance.RemovePath(_autoPathEndPointRoute.Id);
_autoPathEndPointRoute = null;
}
}
else
{
LogManager.Warning("[临时标记清理] PathPointRenderPlugin实例为null,无法清理标记");
}
}
catch (Exception ex)
{
LogManager.Error($"[临时标记清理] 清理临时标记失败: {ex.Message}");
}
}
#endregion
#region PathPlanningManager事件处理
///
/// 订阅PathPlanningManager事件
///
private void SubscribeToPathPlanningManager()
{
if (_pathPlanningManager == null)
{
LogManager.Debug("PathPlanningManager尚未设置,跳过事件订阅");
return;
}
try
{
// 订阅路径点操作事件(用于手工路径编辑)
_pathPlanningManager.PathPointOperation += OnPathPointOperation;
// 订阅路径点列表更新事件
_pathPlanningManager.PathPointsListUpdated += OnPathPointsListUpdated;
// 订阅当前路径变更事件
_pathPlanningManager.CurrentRouteChanged += OnCurrentRouteChanged;
// 订阅编辑状态变更事件
_pathPlanningManager.EditStateChanged += OnEditStateChanged;
// 订阅路径生成事件(用于处理自动路径)
_pathPlanningManager.RouteGenerated += OnRouteGenerated;
// 订阅路径加载完成事件(用于处理数据库加载的路径)
_pathPlanningManager.RoutesLoaded += OnRoutesLoaded;
// 添加错误事件订阅 - 用于处理自动路径规划失败等错误
_pathPlanningManager.ErrorOccurred += OnPathPlanningError;
LogManager.Debug("PathEditingViewModel已成功订阅PathPlanningManager事件");
}
catch (Exception ex)
{
LogManager.Error($"*** 订阅PathPlanningManager事件失败: {ex.Message} ***", ex);
}
}
///
/// 取消订阅PathPlanningManager事件
///
private void UnsubscribeFromPathPlanningManager()
{
if (_pathPlanningManager == null) return;
try
{
_pathPlanningManager.PathPointOperation -= OnPathPointOperation;
_pathPlanningManager.PathPointsListUpdated -= OnPathPointsListUpdated;
_pathPlanningManager.CurrentRouteChanged -= OnCurrentRouteChanged;
_pathPlanningManager.EditStateChanged -= OnEditStateChanged;
_pathPlanningManager.RouteGenerated -= OnRouteGenerated;
_pathPlanningManager.RoutesLoaded -= OnRoutesLoaded;
_pathPlanningManager.ErrorOccurred -= OnPathPlanningError;
LogManager.Info("PathEditingViewModel已取消订阅PathPlanningManager事件,包括ErrorOccurred");
}
catch (Exception ex)
{
LogManager.Error($"取消订阅PathPlanningManager事件失败: {ex.Message}", ex);
}
}
///
/// 处理路径点操作事件(手工路径编辑的核心)
///
private async void OnPathPointOperation(object sender, PathPointOperationEventArgs e)
{
if (e == null || e.PathPoint == null) return;
try
{
await SafeExecuteAsync(() =>
{
LogManager.Info($"PathEditingViewModel收到路径点操作事件: {e.OperationType}, 点: {e.PathPoint.Name}");
// 只处理手动编辑的路径点,跳过自动路径点
// 自动路径通过RouteGenerated事件处理
if (e.PathPoint.Name.Contains("自动"))
{
LogManager.Info($"跳过自动路径点,将通过RouteGenerated事件处理: {e.PathPoint.Name}");
return;
}
// 注意:不在这里直接添加/移除单个路径点到UI
// 因为PathPointsListUpdated事件会处理完整的路径同步
// 这里只做状态更新和选择管理
if (e.OperationType == PathPointOperationType.Added)
{
// 确保对应的路径在UI中存在并选中
var pathViewModel = PathRoutes.FirstOrDefault(p => p.Name == e.Route.Name);
if (pathViewModel != null)
{
// 设置为选中路径
SelectedPathRoute = pathViewModel;
}
UpdateMainStatus($"已添加路径点: {e.PathPoint.Name} 到 {e.Route.Name}");
LogManager.Info($"路径点操作完成: {e.PathPoint.Name} 已添加到路径 {e.Route.Name}");
}
else if (e.OperationType == PathPointOperationType.Removed)
{
UpdateMainStatus($"已移除路径点: {e.PathPoint.Name} 从 {e.Route.Name}");
LogManager.Info($"路径点操作完成: {e.PathPoint.Name} 已从路径 {e.Route.Name} 移除");
}
}, "处理路径点操作事件");
}
catch (Exception ex)
{
LogManager.Error($"处理路径点操作事件失败: {ex.Message}", ex);
}
}
///
/// 处理路径点列表更新事件
///
private async void OnPathPointsListUpdated(object sender, PathPointsListUpdatedEventArgs e)
{
if (e?.Route == null) return;
try
{
await SafeExecuteAsync(() =>
{
LogManager.Info($"路径点列表更新: {e.Route.Name}, 原因: {e.UpdateReason}");
// 刷新对应路径的显示
var pathViewModel = PathRoutes.FirstOrDefault(p => p.Name == e.Route.Name);
if (pathViewModel != null)
{
bool updated = SyncPathViewModelFromCoreRoute(pathViewModel, e.Route, preserveSelection: true);
if (!updated)
{
LogManager.Info($"路径点数据无变化({pathViewModel.Points.Count}),跳过更新");
return;
}
LogManager.Info($"已更新路径点列表,当前点数: {pathViewModel.Points.Count}");
// 更新真实路径可视化
UpdatePathVisualization();
}
else
{
LogManager.Warning($"未找到对应的PathViewModel: {e.Route.Name}");
}
}, "处理路径点列表更新事件");
}
catch (Exception ex)
{
LogManager.Error($"处理路径点列表更新事件失败: {ex.Message}", ex);
}
}
///
/// 处理当前路径变更事件
///
private async void OnCurrentRouteChanged(object sender, CurrentRouteChangedEventArgs e)
{
try
{
await SafeExecuteAsync(() =>
{
LogManager.Info($"当前路径变更: {e.PreviousRoute?.Name} -> {e.NewRoute?.Name}");
if (e.NewRoute != null)
{
// 查找并选中对应的路径ViewModel
var pathViewModel = PathRoutes.FirstOrDefault(p => p.Name == e.NewRoute.Name);
if (pathViewModel != null)
{
bool synced = SyncPathViewModelFromCoreRoute(pathViewModel, e.NewRoute, preserveSelection: true);
if (synced)
{
LogManager.Info($"当前路径切换时已同步路径点列表: {e.NewRoute.Name}, 点数: {pathViewModel.Points.Count}");
}
// 只在真的不同时才设置,避免重复触发
if (SelectedPathRoute != pathViewModel)
{
SelectedPathRoute = pathViewModel;
LogManager.Info($"UI已同步选择路径: {e.NewRoute.Name}");
}
}
else
{
LogManager.Warning($"OnCurrentRouteChanged: UI中未找到路径 {e.NewRoute.Name},可能UI还未刷新");
}
}
else
{
// 当前路径被设为null,清除UI选择
if (SelectedPathRoute != null)
{
SelectedPathRoute = null;
LogManager.Info("UI已清除路径选择");
}
}
}, "处理当前路径变更事件");
}
catch (Exception ex)
{
LogManager.Error($"处理当前路径变更事件失败: {ex.Message}", ex);
}
}
///
/// 处理编辑状态变更事件
///
private async void OnEditStateChanged(object sender, PathEditStateChangedEventArgs e)
{
try
{
await SafeExecuteAsync(() =>
{
LogManager.Info($"路径编辑状态变更: {e.PreviousState} -> {e.NewState}");
// 修复:包含 AddingPoints 和 EditingPoint 状态
IsPathEditMode = (e.NewState == PathEditState.Creating ||
e.NewState == PathEditState.Editing ||
e.NewState == PathEditState.AddingPoints ||
e.NewState == PathEditState.EditingPoint);
LogManager.Info($"[UI状态更新] IsPathEditMode 设为: {IsPathEditMode}");
// 更新状态提示
switch (e.NewState)
{
case PathEditState.Creating:
UpdateMainStatus($"正在新建路径: {e.EditingRoute?.Name} - 请在3D视图中点击设置路径点");
break;
case PathEditState.Editing:
UpdateMainStatus($"正在编辑路径: {e.EditingRoute?.Name} - 请在3D视图中点击设置路径点");
break;
case PathEditState.AddingPoints:
UpdateMainStatus($"正在添加路径点到: {e.EditingRoute?.Name} - 请在3D视图中点击设置路径点");
break;
case PathEditState.EditingPoint:
UpdateMainStatus($"正在修改路径点: {e.EditingRoute?.Name} - 请在3D视图中点击新位置,然后点击'结束'确认");
break;
case PathEditState.Viewing:
// 如果当前状态包含路径规划完成信息(包括完成度百分比),则保留不覆盖
var currentStatus = _mainViewModel?.StatusText ?? "";
if (!currentStatus.Contains("路径规划完成") && !currentStatus.Contains("路径规划部分完成") &&
!currentStatus.Contains("✅") && !currentStatus.Contains("🔶"))
{
UpdateMainStatus("路径编辑已完成");
}
break;
default:
UpdateMainStatus("就绪");
break;
}
// 通知命令状态更新
OnPropertyChanged(nameof(CanExecuteNewPath));
OnPropertyChanged(nameof(CanExecuteStartEdit));
OnPropertyChanged(nameof(CanExecuteEndEdit));
OnPropertyChanged(nameof(CanExecuteModifyPoint));
OnPropertyChanged(nameof(CanExecuteCancelModifyPoint));
LogManager.Info($"[UI状态更新] 按钮状态已通知更新 - CanExecuteStartEdit: {CanExecuteStartEdit}, CanExecuteEndEdit: {CanExecuteEndEdit}, CanExecuteModifyPoint: {CanExecuteModifyPoint}, CanExecuteCancelModifyPoint: {CanExecuteCancelModifyPoint}");
}, "处理编辑状态变更事件");
}
catch (Exception ex)
{
LogManager.Error($"处理编辑状态变更事件失败: {ex.Message}", ex);
}
}
///
/// 处理路径生成事件(用于自动路径)
///
private async void OnRouteGenerated(object sender, RouteGeneratedEventArgs e)
{
// 添加调试日志确认事件是否被调用
LogManager.Info($"*** OnRouteGenerated被调用!Sender: {sender?.GetType().Name}, Route: {e?.Route?.Name}, Method: {e?.GenerationMethod}, GridSize: {e?.GridSize} ***");
if (e?.Route == null)
{
LogManager.Warning("*** OnRouteGenerated: RouteGeneratedEventArgs或Route为null,退出处理 ***");
return;
}
try
{
await SafeExecuteAsync(() =>
{
LogManager.Info($"PathEditingViewModel收到路径生成事件: {e.Route.Name}, 生成方式: {e.GenerationMethod}");
// 处理自动路径生成
if (e.GenerationMethod == RouteGenerationMethod.AutoPlanning)
{
LogManager.Info($"*** 开始处理自动路径生成: {e.Route.Name} ***");
// 创建对应的 WPF ViewModel
var autoPathViewModel = new PathRouteViewModel(isFromDatabase: true)
{
Route = e.Route,
IsActive = true
};
// 设置时间信息
autoPathViewModel.SetTimeInfo(e.Route.CreatedTime, e.Route.LastModified);
// 使用统一的辅助方法创建路径点
var pathPoints = CreatePathPointViewModelsFromCoreRoute(e.Route);
foreach (var point in pathPoints)
{
autoPathViewModel.Points.Add(point);
}
LogManager.Info($"*** 已创建PathRouteViewModel,包含 {autoPathViewModel.Points.Count} 个点 ***");
// 检查是否已存在同名路径,避免重复添加
var existingPath = PathRoutes.FirstOrDefault(p => p.Name == e.Route.Name);
if (existingPath == null)
{
// 添加到 UI 列表并选中
PathRoutes.Add(autoPathViewModel);
SelectedPathRoute = autoPathViewModel;
LogManager.Info($"*** 自动路径已添加到UI列表: {e.Route.Name},当前PathRoutes.Count = {PathRoutes.Count} ***");
}
else
{
// 更新现有路径
existingPath.Points.Clear();
foreach (var wpfPoint in autoPathViewModel.Points)
{
existingPath.Points.Add(wpfPoint);
}
SelectedPathRoute = existingPath;
LogManager.Info($"*** 自动路径已更新UI列表: {e.Route.Name} ***");
}
// 构建包含网格信息和完成度的完整状态消息,使用事件参数中的网格大小信息
var gridInfo = e.GridSize > 0 ? $"网格{e.GridSize:F1}m" : "自动网格";
string statusMessage;
if (e.Route.IsComplete)
{
// 完全到达终点
statusMessage = $"✅ 自动路径规划完成: {e.Route.Name},共 {e.Route.Points.Count} 个路径点,长度 {e.Route.TotalLength:F2}米";
}
else
{
// 部分到达终点
statusMessage = $"🔶 自动路径规划部分完成: {e.Route.Name},共 {e.Route.Points.Count} 个路径点,长度 {e.Route.TotalLength:F2}米,(完成度: {e.Route.CompletionPercentage:F1}%)";
}
UpdateMainStatus(statusMessage);
LogManager.Info($"*** 状态已更新: {statusMessage} ***");
}
else if (e.GenerationMethod == RouteGenerationMethod.Manual)
{
LogManager.Info($"手工路径生成完成: {e.Route.Name},开始刷新路径列表");
var existingPath = PathRoutes.FirstOrDefault(p => p.Name == e.Route.Name);
if (existingPath == null)
{
existingPath = new PathRouteViewModel(isFromDatabase: true)
{
Route = e.Route,
IsActive = true
};
PathRoutes.Add(existingPath);
LogManager.Info($"手工路径生成后补建UI路径壳: {e.Route.Name}");
}
SyncPathViewModelFromCoreRoute(existingPath, e.Route, preserveSelection: true);
SelectedPathRoute = existingPath;
LogManager.Info($"手工路径已同步并重新选中: {e.Route.Name},点数: {existingPath.Points.Count}");
OnPropertyChanged(nameof(PathRoutes));
OnPropertyChanged(nameof(CanExecuteStartEdit));
OnPropertyChanged(nameof(CanExecuteEndEdit));
}
else if (e.GenerationMethod == RouteGenerationMethod.DatabaseLoad)
{
// 注意:DatabaseLoad现在由OnRoutesLoaded事件处理,这里不再处理
LogManager.Warning($"*** OnRouteGenerated收到DatabaseLoad方法,这不应该发生!应该由OnRoutesLoaded处理 ***");
}
}, "处理路径生成事件");
}
catch (Exception ex)
{
LogManager.Error($"*** OnRouteGenerated异常: {ex.Message} ***", ex);
}
}
///
/// 处理路径加载完成事件(用于数据库加载)
///
private async void OnRoutesLoaded(object sender, RoutesLoadedEventArgs e)
{
LogManager.Info($"*** OnRoutesLoaded被调用!Sender: {sender?.GetType().Name}, RouteCount: {e?.RouteCount}, LoadSource: {e?.LoadSource} ***");
if (e?.Routes == null)
{
LogManager.Warning("*** OnRoutesLoaded: RoutesLoadedEventArgs或Routes为null,退出处理 ***");
return;
}
try
{
await SafeExecuteAsync(() =>
{
LogManager.Info($"PathEditingViewModel收到路径加载完成事件: 加载了 {e.RouteCount} 条路径,来源: {e.LoadSource}");
// 处理从数据库加载的历史路径
foreach (var coreRoute in e.Routes)
{
// 创建对应的 WPF ViewModel
var dbPathViewModel = new PathRouteViewModel(isFromDatabase: true)
{
Route = coreRoute,
IsActive = false, // 历史路径默认不激活
PathType = coreRoute.PathType
};
// 设置时间信息
dbPathViewModel.SetTimeInfo(coreRoute.CreatedTime, coreRoute.LastModified);
// 使用统一的辅助方法创建路径点
var pathPoints = CreatePathPointViewModelsFromCoreRoute(coreRoute);
foreach (var point in pathPoints)
{
dbPathViewModel.Points.Add(point);
}
// 检查是否已存在同名路径,避免重复添加
var existingPath = PathRoutes.FirstOrDefault(p => p.Name == coreRoute.Name);
if (existingPath == null)
{
// 添加到 UI 列表
PathRoutes.Add(dbPathViewModel);
}
else
{
LogManager.Debug($"*** 数据库路径已存在,跳过: {coreRoute.Name} ***");
}
}
// 更新状态消息
string statusMessage = $"✅ 已从{e.LoadSource}加载 {e.RouteCount} 条历史路径";
UpdateMainStatus(statusMessage);
// 自动提取并显示所有空轨模型的基准线
ExtractAndRenderRailBaselinePaths();
}, "处理路径加载完成事件");
}
catch (Exception ex)
{
LogManager.Error($"*** OnRoutesLoaded异常: {ex.Message} ***", ex);
}
}
///
/// 处理路径规划错误事件
///
///
/// 处理路径规划错误事件
///
///
/// 处理路径规划错误事件
///
private async void OnPathPlanningError(object sender, PathPlanningErrorOccurredEventArgs e)
{
LogManager.Info($"*** OnPathPlanningError被调用!Sender: {sender?.GetType().Name}, Error: {e?.ErrorMessage}, Level: {e?.ErrorLevel} ***");
try
{
await SafeExecuteAsync(async () =>
{
// 根据错误级别决定日志记录方式
if (e.ErrorLevel == PathPlanningErrorLevel.Warning)
{
LogManager.Warning($"自动路径规划失败: {e.ErrorMessage}");
}
else
{
LogManager.Error($"自动路径规划失败: {e.ErrorMessage}");
}
// 使用UIStateManager确保UI更新在主线程执行
await _uiStateManager.ExecuteUIUpdateAsync(() =>
{
// 更新自动路径状态显示错误信息
UpdateMainStatus($"❌ 自动路径规划失败: {e.ErrorMessage}");
LogManager.Info($"*** 状态已更新为: ❌ 自动路径规划失败: {e.ErrorMessage} ***");
});
}, "处理路径规划错误事件");
}
catch (Exception ex)
{
LogManager.Error($"*** OnPathPlanningError异常: {ex.Message} ***", ex);
}
}
#endregion
#region 清理资源
///
/// 强制重新初始化ToolPlugin以确保获得鼠标焦点
///
/// 是否订阅鼠标事件,新建路径需要true,自动路径选点需要false
/// 初始化是否成功
private bool ForceReinitializeToolPlugin(bool subscribeToEvents = false)
{
try
{
LogManager.Info($"开始强制重新初始化ToolPlugin以确保获得鼠标焦点,订阅事件: {subscribeToEvents}");
// 使用PathPlanningManager的公共方法,避免反射调用
if (_pathPlanningManager != null)
{
bool result = _pathPlanningManager.ForceReinitializeToolPlugin(subscribeToEvents);
LogManager.Info($"ToolPlugin重新初始化结果(事件订阅: {subscribeToEvents}): {result}");
if (result)
{
LogManager.Info("ToolPlugin重新初始化成功,已获得鼠标焦点");
}
else
{
LogManager.Error("ToolPlugin初始化失败");
}
return result;
}
else
{
LogManager.Error("PathPlanningManager为null,无法重新初始化ToolPlugin");
return false;
}
}
catch (Exception ex)
{
LogManager.Error($"重新初始化ToolPlugin失败: {ex.Message}", ex);
return false;
}
}
public void Cleanup()
{
try
{
LogManager.Info("开始清理PathEditingViewModel资源");
// 取消配置变更事件订阅
UnsubscribeFromConfigChanges();
// 取消事件订阅
UnsubscribeFromPathPlanningManager();
// 完整清理自动路径规划相关的事件订阅
try
{
CleanupAutoPathEventSubscriptions();
}
catch (Exception ex)
{
LogManager.Warning($"清理自动路径事件订阅时发生异常: {ex.Message}");
}
try
{
CleanupAssemblyReferenceSelection();
}
catch (Exception ex)
{
LogManager.Warning($"清理直线装配事件订阅时发生异常: {ex.Message}");
}
try
{
CleanupAssemblyEndFaceSelection(clearVisuals: true);
}
catch (Exception ex)
{
LogManager.Warning($"清理端面三点分析事件订阅时发生异常: {ex.Message}");
}
try
{
ClearAssemblyAnchorMarker();
}
catch (Exception ex)
{
LogManager.Warning($"清理直线装配对接点标记时发生异常: {ex.Message}");
}
try
{
ClearAssemblyReferenceLine();
}
catch (Exception ex)
{
LogManager.Warning($"清理直线装配参考线时发生异常: {ex.Message}");
}
// 确保停止任何活动的点击工具
try
{
if (_pathPlanningManager != null)
{
_pathPlanningManager.StopClickTool();
}
}
catch (Exception ex)
{
LogManager.Warning($"停止点击工具时发生异常: {ex.Message}");
}
LogManager.Info("PathEditingViewModel资源清理完成");
}
catch (Exception ex)
{
LogManager.Error($"PathEditingViewModel清理失败: {ex.Message}");
}
}
///
/// 释放资源 (IDisposable implementation)
///
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
///
/// 释放托管和非托管资源
///
/// 是否释放托管资源
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
try
{
// 调用现有的清理逻辑
Cleanup();
LogManager.Info("PathEditingViewModel已正确释放资源 (IDisposable)");
}
catch (Exception ex)
{
LogManager.Error($"PathEditingViewModel释放资源时发生异常: {ex.Message}", ex);
}
}
_disposed = true;
}
}
#endregion
}
///
/// 物体参数类 - 用于传递物体尺寸信息给路径规划算法
///
public class ObjectParameters
{
public double Length { get; set; }
public double Width { get; set; }
public double Height { get; set; }
public double SafetyMargin { get; set; }
}
}