2577 lines
108 KiB
C#
2577 lines
108 KiB
C#
using System;
|
||
using System.Collections.ObjectModel;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using System.Numerics;
|
||
using System.Runtime.InteropServices;
|
||
using System.Text;
|
||
using System.Threading.Tasks;
|
||
using System.Windows.Input;
|
||
using System.Windows.Threading;
|
||
using Autodesk.Navisworks.Api;
|
||
using ComApiBridge = Autodesk.Navisworks.Api.ComApi.ComApiBridge;
|
||
using NavisworksTransport.UI.WPF.Collections;
|
||
using NavisworksTransport.Core;
|
||
using NavisworksTransport.Core.Config;
|
||
using NavisworksTransport.Utils;
|
||
using NavisworksTransport.Utils.CoordinateSystem;
|
||
using NavisworksTransport.Commands;
|
||
|
||
namespace NavisworksTransport.UI.WPF.ViewModels
|
||
{
|
||
/// <summary>
|
||
/// 系统管理ViewModel - 处理插件系统管理功能
|
||
/// </summary>
|
||
public class SystemManagementViewModel : ViewModelBase, IDisposable
|
||
{
|
||
#region 私有字段
|
||
|
||
private readonly UIStateManager _uiStateManager;
|
||
private readonly HashSet<string> _documentRotationHintShown = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||
private readonly HashSet<string> _documentRotationHintPending = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||
|
||
// 日志管理字段
|
||
private ObservableCollection<string> _logLevels;
|
||
private string _selectedLogLevel = "Info";
|
||
|
||
private string _pluginVersion = "v1.0";
|
||
private string _navisworksVersion = "2026";
|
||
private string _memoryUsage = "0 MB";
|
||
private string _runningTime = "00:00:00";
|
||
|
||
// 数据管理字段
|
||
private string _databaseVersion = "--";
|
||
private string _pathCount = "--";
|
||
private string _collisionRecordCount = "--";
|
||
private bool _autoBackupEnabled = true;
|
||
private int _selectedBackupKeepCount = 10;
|
||
private string _lastBackupInfo = "暂无备份记录";
|
||
|
||
// 🔧 修复:添加释放状态标志
|
||
private bool _disposed = false;
|
||
|
||
// 系统信息刷新定时器
|
||
private DispatcherTimer _systemInfoTimer;
|
||
|
||
#endregion
|
||
|
||
#region 公共属性
|
||
|
||
/// <summary>
|
||
/// 日志级别集合
|
||
/// </summary>
|
||
public ObservableCollection<string> LogLevels
|
||
{
|
||
get => _logLevels;
|
||
set => SetProperty(ref _logLevels, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 选中的日志级别
|
||
/// </summary>
|
||
public string SelectedLogLevel
|
||
{
|
||
get => _selectedLogLevel;
|
||
set
|
||
{
|
||
if (SetProperty(ref _selectedLogLevel, value))
|
||
{
|
||
// 将字符串转换为LogLevel枚举并设置
|
||
if (Enum.TryParse<LogLevel>(value, out var level))
|
||
{
|
||
LogManager.SetLogLevel(level);
|
||
LogManager.Info($"日志级别已更改为: {value}");
|
||
UpdateMainStatus($"日志级别已设置为: {value}");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 插件版本
|
||
/// </summary>
|
||
public string PluginVersion
|
||
{
|
||
get => _pluginVersion;
|
||
set => SetProperty(ref _pluginVersion, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Navisworks版本
|
||
/// </summary>
|
||
public string NavisworksVersion
|
||
{
|
||
get => _navisworksVersion;
|
||
set => SetProperty(ref _navisworksVersion, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 内存使用情况
|
||
/// </summary>
|
||
public string MemoryUsage
|
||
{
|
||
get => _memoryUsage;
|
||
set => SetProperty(ref _memoryUsage, value);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 运行时间
|
||
/// </summary>
|
||
public string RunningTime
|
||
{
|
||
get => _runningTime;
|
||
set => SetProperty(ref _runningTime, value);
|
||
}
|
||
|
||
// 数据管理属性
|
||
public string DatabaseVersion
|
||
{
|
||
get => _databaseVersion;
|
||
set => SetProperty(ref _databaseVersion, value);
|
||
}
|
||
|
||
public string PathCount
|
||
{
|
||
get => _pathCount;
|
||
set => SetProperty(ref _pathCount, value);
|
||
}
|
||
|
||
public string DetectionRecordCount
|
||
{
|
||
get => _collisionRecordCount;
|
||
set => SetProperty(ref _collisionRecordCount, value);
|
||
}
|
||
|
||
public bool AutoBackupEnabled
|
||
{
|
||
get => _autoBackupEnabled;
|
||
set => SetProperty(ref _autoBackupEnabled, value);
|
||
}
|
||
|
||
public ObservableCollection<int> BackupKeepCountOptions { get; } = new ObservableCollection<int> { 5, 10, 20, 50 };
|
||
|
||
public int SelectedBackupKeepCount
|
||
{
|
||
get => _selectedBackupKeepCount;
|
||
set => SetProperty(ref _selectedBackupKeepCount, value);
|
||
}
|
||
|
||
public string LastBackupInfo
|
||
{
|
||
get => _lastBackupInfo;
|
||
set => SetProperty(ref _lastBackupInfo, value);
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 命令
|
||
|
||
public ICommand ViewLogCommand { get; private set; }
|
||
public ICommand OpenSettingsCommand { get; private set; }
|
||
public ICommand GeneratePerformanceReportCommand { get; private set; }
|
||
public ICommand DiagnosticCommand { get; private set; }
|
||
|
||
// 数据管理命令
|
||
public ICommand BackupDataCommand { get; private set; }
|
||
public ICommand RestoreDataCommand { get; private set; }
|
||
public ICommand RepairDatabaseCommand { get; private set; }
|
||
public ICommand ClearAllDataCommand { get; private set; }
|
||
|
||
// 功能测试命令
|
||
public ICommand TestVoxelGridSDFCommand { get; private set; }
|
||
public ICommand TestVoxelPathFindingCommand { get; private set; }
|
||
public ICommand ReadTransformTestCommand { get; private set; }
|
||
public ICommand CoordinateSystemExplorerCommand { get; private set; }
|
||
public ICommand CaptureRealObjectTransformCommand { get; private set; }
|
||
public ICommand DetectFragmentDefaultUpCommand { get; private set; }
|
||
|
||
|
||
// 坐标系设置
|
||
public ObservableCollection<string> CoordinateSystemOptions { get; private set; }
|
||
public ObservableCollection<string> FragmentDefaultUpAxisOptions { get; private set; }
|
||
|
||
private string _selectedCoordinateSystem = "AutoDetect";
|
||
public string SelectedCoordinateSystem
|
||
{
|
||
get => _selectedCoordinateSystem;
|
||
set
|
||
{
|
||
if (SetProperty(ref _selectedCoordinateSystem, value))
|
||
{
|
||
OnCoordinateSystemChanged(value);
|
||
}
|
||
}
|
||
}
|
||
|
||
private string _currentCoordinateSystemInfo = "当前坐标系: 未初始化";
|
||
public string CurrentCoordinateSystemInfo
|
||
{
|
||
get => _currentCoordinateSystemInfo;
|
||
set => SetProperty(ref _currentCoordinateSystemInfo, value);
|
||
}
|
||
|
||
private string _selectedFragmentDefaultUpAxis = "Y";
|
||
public string SelectedFragmentDefaultUpAxis
|
||
{
|
||
get => _selectedFragmentDefaultUpAxis;
|
||
set
|
||
{
|
||
if (SetProperty(ref _selectedFragmentDefaultUpAxis, value))
|
||
{
|
||
SaveCurrentDocumentFragmentDefaultUpAxis(value);
|
||
UpdateMainStatus($"当前文档 fragment 默认Up已设置为: {value}");
|
||
LogManager.Info($"[fragment默认Up] 当前文档设置为: {value}");
|
||
}
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 构造函数
|
||
|
||
public SystemManagementViewModel() : base()
|
||
{
|
||
try
|
||
{
|
||
// 获取UI状态管理器实例
|
||
_uiStateManager = UIStateManager.Instance;
|
||
|
||
// 验证关键组件是否正常初始化
|
||
if (_uiStateManager == null)
|
||
{
|
||
LogManager.Error("UIStateManager初始化失败");
|
||
throw new InvalidOperationException("UIStateManager初始化失败");
|
||
}
|
||
|
||
// 初始化日志级别集合
|
||
LogLevels = new ThreadSafeObservableCollection<string>();
|
||
|
||
// 初始化命令
|
||
InitializeCommands();
|
||
|
||
// 初始化系统管理设置
|
||
_ = InitializeSystemManagementSettingsAsync();
|
||
|
||
// 订阅文档切换事件,以便重新检测坐标系
|
||
SubscribeToDocumentEvents();
|
||
|
||
LogManager.Info("SystemManagementViewModel构造函数执行完成");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"SystemManagementViewModel构造函数异常: {ex.Message}", ex);
|
||
|
||
// 在构造函数中尽量保证对象处于可用状态
|
||
UpdateMainStatus("初始化失败,请检查日志");
|
||
throw;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 带主ViewModel参数的构造函数,支持统一状态栏
|
||
/// </summary>
|
||
/// <param name="mainViewModel">主ViewModel,用于统一状态栏</param>
|
||
public SystemManagementViewModel(LogisticsControlViewModel mainViewModel) : base()
|
||
{
|
||
try
|
||
{
|
||
// 设置主ViewModel引用到基类
|
||
SetMainViewModel(mainViewModel);
|
||
|
||
// 获取UI状态管理器实例
|
||
_uiStateManager = UIStateManager.Instance;
|
||
|
||
// 验证关键组件是否正常初始化
|
||
if (_uiStateManager == null)
|
||
{
|
||
LogManager.Error("UIStateManager初始化失败");
|
||
throw new InvalidOperationException("UIStateManager初始化失败");
|
||
}
|
||
|
||
// 初始化日志级别集合
|
||
LogLevels = new ThreadSafeObservableCollection<string>();
|
||
|
||
// 初始化命令
|
||
InitializeCommands();
|
||
|
||
// 初始化系统管理设置
|
||
_ = InitializeSystemManagementSettingsAsync();
|
||
|
||
LogManager.Info("SystemManagementViewModel构造函数执行完成 - 支持统一状态栏");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"SystemManagementViewModel构造函数异常: {ex.Message}", ex);
|
||
|
||
// 在构造函数中尽量保证对象处于可用状态
|
||
UpdateMainStatus("初始化失败,请检查日志");
|
||
throw;
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 初始化
|
||
|
||
/// <summary>
|
||
/// 初始化命令
|
||
/// </summary>
|
||
private void InitializeCommands()
|
||
{
|
||
try
|
||
{
|
||
// 系统管理命令
|
||
ViewLogCommand = new RelayCommand(() => ExecuteViewLog());
|
||
OpenSettingsCommand = new RelayCommand(() => ExecuteOpenSettings());
|
||
GeneratePerformanceReportCommand = new RelayCommand(() => ExecuteGeneratePerformanceReport());
|
||
DiagnosticCommand = new RelayCommand(() => ExecuteDiagnostic());
|
||
|
||
// 数据管理命令
|
||
BackupDataCommand = new RelayCommand(() => ExecuteBackupData());
|
||
RestoreDataCommand = new RelayCommand(() => ExecuteRestoreData());
|
||
RepairDatabaseCommand = new RelayCommand(() => ExecuteRepairDatabase());
|
||
ClearAllDataCommand = new RelayCommand(() => ExecuteClearAllData());
|
||
|
||
// 功能测试命令
|
||
TestVoxelGridSDFCommand = new RelayCommand(() => ExecuteTestVoxelGridSDF());
|
||
TestVoxelPathFindingCommand = new RelayCommand(() => ExecuteTestVoxelPathFinding());
|
||
ReadTransformTestCommand = new RelayCommand(() => ExecuteReadTransformTest());
|
||
CoordinateSystemExplorerCommand = new RelayCommand(() => ExecuteCoordinateSystemExplorer());
|
||
CaptureRealObjectTransformCommand = new RelayCommand(() => ExecuteCaptureRealObjectTransform());
|
||
DetectFragmentDefaultUpCommand = new RelayCommand(() => ExecuteDetectFragmentDefaultUp());
|
||
|
||
|
||
// 初始化坐标系选项
|
||
CoordinateSystemOptions = new ObservableCollection<string> { "AutoDetect", "ZUp", "YUp" };
|
||
FragmentDefaultUpAxisOptions = new ObservableCollection<string> { "Y", "Z" };
|
||
|
||
// 从配置加载当前坐标系设置
|
||
var configType = ConfigManager.Instance.Current.CoordinateSystem?.Type ?? "AutoDetect";
|
||
_selectedCoordinateSystem = configType;
|
||
_selectedFragmentDefaultUpAxis = GetCurrentDocumentFragmentDefaultUpAxis();
|
||
|
||
// 订阅文档事件(用于自动检测坐标系)
|
||
SubscribeToDocumentEvents();
|
||
|
||
// 检查当前是否已有活动文档,如果有立即检测
|
||
if (Autodesk.Navisworks.Api.Application.ActiveDocument != null)
|
||
{
|
||
InitializeCoordinateSystem();
|
||
}
|
||
else
|
||
{
|
||
// 没有活动文档,显示等待状态
|
||
CurrentCoordinateSystemInfo = "当前坐标系: 等待文档加载...";
|
||
}
|
||
|
||
LogManager.Info("系统管理命令初始化完成");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"初始化命令失败: {ex.Message}", ex);
|
||
throw;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取路径规划管理器实例
|
||
/// </summary>
|
||
/// <returns>路径规划管理器或null</returns>
|
||
private PathPlanningManager GetPathPlanningManager()
|
||
{
|
||
try
|
||
{
|
||
// 使用PathPlanningManager的静态方法获取活动实例
|
||
var pathManager = PathPlanningManager.GetActivePathManager();
|
||
if (pathManager != null)
|
||
{
|
||
LogManager.Info("成功获取到活动的PathPlanningManager实例");
|
||
return pathManager;
|
||
}
|
||
else
|
||
{
|
||
LogManager.Warning("当前没有活动的PathPlanningManager实例");
|
||
return null;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"获取PathPlanningManager失败: {ex.Message}", ex);
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 初始化坐标系
|
||
/// </summary>
|
||
private void InitializeCoordinateSystem()
|
||
{
|
||
try
|
||
{
|
||
// 解析配置类型
|
||
if (!Enum.TryParse<CoordinateSystemType>(_selectedCoordinateSystem, out var configType))
|
||
{
|
||
configType = CoordinateSystemType.AutoDetect;
|
||
}
|
||
|
||
// 初始化坐标系管理器
|
||
CoordinateSystemManager.Instance.Initialize(configType);
|
||
|
||
// 更新显示信息
|
||
UpdateCoordinateSystemInfo();
|
||
ShowRootModelRotationHintIfNeeded();
|
||
|
||
LogManager.Info($"坐标系初始化完成: {configType}");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"坐标系初始化失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 坐标系选择改变时调用
|
||
/// </summary>
|
||
private void OnCoordinateSystemChanged(string newValue)
|
||
{
|
||
try
|
||
{
|
||
if (!Enum.TryParse<CoordinateSystemType>(newValue, out var type))
|
||
{
|
||
type = CoordinateSystemType.AutoDetect;
|
||
}
|
||
|
||
// 设置新坐标系
|
||
CoordinateSystemManager.Instance.SetCoordinateSystem(type);
|
||
|
||
// 更新显示信息
|
||
UpdateCoordinateSystemInfo();
|
||
|
||
// 更新配置
|
||
ConfigManager.Instance.Current.CoordinateSystem.Type = newValue;
|
||
|
||
UpdateMainStatus($"坐标系已切换为: {newValue}");
|
||
LogManager.Info($"坐标系已手动切换为: {newValue}");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"切换坐标系失败: {ex.Message}");
|
||
UpdateMainStatus($"切换坐标系失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新坐标系显示信息
|
||
/// </summary>
|
||
private void UpdateCoordinateSystemInfo()
|
||
{
|
||
var info = CoordinateSystemManager.Instance.GetCurrentInfo();
|
||
CurrentCoordinateSystemInfo = $"当前坐标系: {info}";
|
||
RefreshFragmentDefaultUpAxisSelectionFromCurrentDocument();
|
||
}
|
||
|
||
private void RefreshFragmentDefaultUpAxisSelectionFromCurrentDocument()
|
||
{
|
||
string resolvedAxis = FragmentDefaultUpContext.GetCurrentDocumentFragmentDefaultUpAxis();
|
||
if (_selectedFragmentDefaultUpAxis != resolvedAxis)
|
||
{
|
||
SetProperty(ref _selectedFragmentDefaultUpAxis, resolvedAxis, nameof(SelectedFragmentDefaultUpAxis));
|
||
}
|
||
}
|
||
|
||
private string GetCurrentDocumentFragmentDefaultUpAxis()
|
||
{
|
||
return FragmentDefaultUpContext.GetCurrentDocumentFragmentDefaultUpAxis();
|
||
}
|
||
|
||
private void SaveCurrentDocumentFragmentDefaultUpAxis(string axisName)
|
||
{
|
||
FragmentDefaultUpContext.SetCurrentDocumentFragmentDefaultUpAxis(axisName);
|
||
}
|
||
|
||
private string GetCurrentDocumentKey()
|
||
{
|
||
return FragmentDefaultUpContext.GetCurrentDocumentKey();
|
||
}
|
||
|
||
private string GetCurrentHostUpAxis()
|
||
{
|
||
return FragmentDefaultUpContext.GetCurrentHostUpAxis();
|
||
}
|
||
|
||
private static bool IsSupportedFragmentDefaultUpAxis(string axisName)
|
||
{
|
||
return FragmentDefaultUpContext.IsSupportedAxis(axisName);
|
||
}
|
||
|
||
private void ShowRootModelRotationHintIfNeeded()
|
||
{
|
||
string documentKey = GetCurrentDocumentKey();
|
||
if (string.IsNullOrWhiteSpace(documentKey) ||
|
||
_documentRotationHintShown.Contains(documentKey) ||
|
||
_documentRotationHintPending.Contains(documentKey))
|
||
{
|
||
return;
|
||
}
|
||
|
||
var doc = Autodesk.Navisworks.Api.Application.ActiveDocument;
|
||
if (doc == null || doc.IsClear || doc.Models == null || doc.Models.Count == 0)
|
||
{
|
||
return;
|
||
}
|
||
|
||
if (!TryGetRootModelRotationHint(doc, out string hintMessage))
|
||
{
|
||
return;
|
||
}
|
||
|
||
_documentRotationHintPending.Add(documentKey);
|
||
_ = Task.Run(async () =>
|
||
{
|
||
try
|
||
{
|
||
await Task.Delay(1500).ConfigureAwait(false);
|
||
_uiStateManager?.QueueUIUpdate(() =>
|
||
{
|
||
try
|
||
{
|
||
string currentDocumentKey = GetCurrentDocumentKey();
|
||
if (!string.Equals(documentKey, currentDocumentKey, StringComparison.OrdinalIgnoreCase))
|
||
{
|
||
_documentRotationHintPending.Remove(documentKey);
|
||
return;
|
||
}
|
||
|
||
if (_documentRotationHintShown.Contains(documentKey))
|
||
{
|
||
_documentRotationHintPending.Remove(documentKey);
|
||
return;
|
||
}
|
||
|
||
_documentRotationHintShown.Add(documentKey);
|
||
_documentRotationHintPending.Remove(documentKey);
|
||
LogManager.Info($"[模型整体旋转提示] {hintMessage}");
|
||
DialogHelper.ShowAutoClosingMessageBox(
|
||
hintMessage,
|
||
"坐标系提示",
|
||
System.Windows.MessageBoxImage.Information,
|
||
autoCloseMilliseconds: 4000);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_documentRotationHintPending.Remove(documentKey);
|
||
LogManager.Warning($"[模型整体旋转提示] 延后弹出失败: {ex.Message}");
|
||
}
|
||
});
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_documentRotationHintPending.Remove(documentKey);
|
||
LogManager.Warning($"[模型整体旋转提示] 延后任务失败: {ex.Message}");
|
||
}
|
||
});
|
||
}
|
||
|
||
private static bool TryGetRootModelRotationHint(Document doc, out string hintMessage)
|
||
{
|
||
hintMessage = null;
|
||
if (doc == null || doc.Models == null || doc.Models.Count == 0)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
const double minAngleDegrees = 1.0;
|
||
|
||
for (int i = 0; i < doc.Models.Count; i++)
|
||
{
|
||
Model model = doc.Models[i];
|
||
ModelItem rootItem = model?.RootItem;
|
||
if (rootItem == null)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
Transform3DComponents components = rootItem.Transform.Factor();
|
||
var axisAngle = components.Rotation.ToAxisAndAngle();
|
||
double angleDegrees = Math.Abs(axisAngle.Angle) * 180.0 / Math.PI;
|
||
if (angleDegrees < minAngleDegrees)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
hintMessage =
|
||
$"检测到根模型存在整体旋转(约 {angleDegrees:F1}°),模型可能经过坐标系转换;如后续真实物体姿态异常,请检查 Fragment默认Up 设置。";
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
#region 文档事件处理
|
||
|
||
/// <summary>
|
||
/// 订阅文档切换事件
|
||
/// </summary>
|
||
private void SubscribeToDocumentEvents()
|
||
{
|
||
try
|
||
{
|
||
// 订阅文档切换后事件(文档已完全加载)
|
||
Autodesk.Navisworks.Api.Application.ActiveDocumentChanged += OnActiveDocumentChanged;
|
||
|
||
// 同时订阅模型集合变化事件(SDI架构下的真正文档变化)
|
||
if (Autodesk.Navisworks.Api.Application.ActiveDocument != null)
|
||
{
|
||
Autodesk.Navisworks.Api.Application.ActiveDocument.Models.CollectionChanged += OnModelsCollectionChanged;
|
||
}
|
||
|
||
LogManager.Info("[系统管理] 已订阅文档相关事件");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[系统管理] 订阅文档事件失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 取消订阅文档事件
|
||
/// </summary>
|
||
private void UnsubscribeFromDocumentEvents()
|
||
{
|
||
try
|
||
{
|
||
// 取消订阅文档切换事件
|
||
Autodesk.Navisworks.Api.Application.ActiveDocumentChanged -= OnActiveDocumentChanged;
|
||
|
||
// 取消订阅模型集合变化事件
|
||
if (Autodesk.Navisworks.Api.Application.ActiveDocument != null)
|
||
{
|
||
Autodesk.Navisworks.Api.Application.ActiveDocument.Models.CollectionChanged -= OnModelsCollectionChanged;
|
||
}
|
||
|
||
LogManager.Info("[系统管理] 已取消订阅文档相关事件");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Warning($"[系统管理] 取消订阅文档事件时出现警告: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 文档切换事件处理
|
||
/// </summary>
|
||
private void OnActiveDocumentChanged(object sender, EventArgs e)
|
||
{
|
||
try
|
||
{
|
||
LogManager.Info("[系统管理] 检测到文档切换");
|
||
|
||
// 订阅新文档的 Models.CollectionChanged
|
||
if (Autodesk.Navisworks.Api.Application.ActiveDocument != null)
|
||
{
|
||
try
|
||
{
|
||
Autodesk.Navisworks.Api.Application.ActiveDocument.Models.CollectionChanged += OnModelsCollectionChanged;
|
||
LogManager.Info("[系统管理] 已订阅新文档的模型集合变化事件");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Warning($"[系统管理] 订阅模型集合变化事件失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
// 执行坐标系检测
|
||
HandleDocumentChange();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[系统管理] 文档切换处理失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 模型集合变化事件处理(SDI架构下的真正文档变化)
|
||
/// </summary>
|
||
private void OnModelsCollectionChanged(object sender, EventArgs e)
|
||
{
|
||
try
|
||
{
|
||
LogManager.Info("[系统管理] 检测到模型集合变化");
|
||
HandleDocumentChange();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[系统管理] 模型集合变化处理失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 处理文档变化,重新检测坐标系
|
||
/// </summary>
|
||
private void HandleDocumentChange()
|
||
{
|
||
// 检查文档是否已完全加载
|
||
var doc = Autodesk.Navisworks.Api.Application.ActiveDocument;
|
||
if (doc == null || string.IsNullOrEmpty(doc.FileName))
|
||
{
|
||
LogManager.Debug("[系统管理] 文档尚未完全加载,跳过坐标系检测");
|
||
return;
|
||
}
|
||
|
||
// 如果配置为自动检测,重新检测坐标系
|
||
if (_selectedCoordinateSystem == "AutoDetect")
|
||
{
|
||
// 在 UI 线程执行
|
||
_ = _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
try
|
||
{
|
||
InitializeCoordinateSystem();
|
||
UpdateMainStatus("坐标系已根据新文档重新检测");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[系统管理] 坐标系检测失败: {ex.Message}");
|
||
}
|
||
});
|
||
}
|
||
|
||
_ = _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
RefreshFragmentDefaultUpAxisSelectionFromCurrentDocument();
|
||
});
|
||
}
|
||
|
||
#endregion
|
||
|
||
/// <summary>
|
||
/// 初始化系统管理设置
|
||
/// </summary>
|
||
private async Task InitializeSystemManagementSettingsAsync()
|
||
{
|
||
await SafeExecuteAsync(async () =>
|
||
{
|
||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||
{
|
||
// 初始化日志级别
|
||
LogLevels.Clear();
|
||
LogLevels.Add("Debug");
|
||
LogLevels.Add("Info");
|
||
LogLevels.Add("Warning");
|
||
LogLevels.Add("Error");
|
||
|
||
// 获取当前日志级别
|
||
var currentLevel = LogManager.GetLogLevel();
|
||
SelectedLogLevel = currentLevel.ToString();
|
||
|
||
// 初始化系统信息 - 使用动态获取
|
||
var systemInfo = VersionInfo.GetSystemInfo();
|
||
PluginVersion = systemInfo.PluginVersion;
|
||
NavisworksVersion = systemInfo.NavisworksVersion;
|
||
MemoryUsage = systemInfo.MemoryUsage;
|
||
RunningTime = systemInfo.RunningTime;
|
||
|
||
// 初始化数据库状态(如果数据库已连接)
|
||
RefreshDatabaseStatus();
|
||
|
||
// 启动系统信息定时刷新
|
||
StartSystemInfoTimer();
|
||
|
||
UpdateMainStatus("系统管理初始化完成");
|
||
});
|
||
|
||
LogManager.Info("系统管理设置初始化完成");
|
||
}, "初始化系统管理设置");
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 命令实现
|
||
|
||
|
||
/// <summary>
|
||
/// 查看日志
|
||
/// </summary>
|
||
private void ExecuteViewLog()
|
||
{
|
||
SafeExecute(() =>
|
||
{
|
||
try
|
||
{
|
||
// 创建并显示日志查看器对话框
|
||
var logViewerDialog = new NavisworksTransport.UI.WPF.Views.LogViewerDialog();
|
||
|
||
// 设置窗口所有者
|
||
DialogHelper.SetOwnerSafely(logViewerDialog);
|
||
|
||
// 显示对话框(使用Show而不是ShowDialog以避免阻塞)
|
||
logViewerDialog.Show();
|
||
|
||
UpdateMainStatus("日志查看器已打开");
|
||
LogManager.Info("通过系统管理打开日志查看器");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
UpdateMainStatus("打开日志查看器失败");
|
||
LogManager.Error($"打开日志查看器失败: {ex.Message}", ex);
|
||
|
||
// 显示错误消息给用户
|
||
System.Windows.MessageBox.Show($"打开日志查看器失败: {ex.Message}", "错误",
|
||
System.Windows.MessageBoxButton.OK,
|
||
System.Windows.MessageBoxImage.Error);
|
||
}
|
||
}, "查看日志");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 打开设置
|
||
/// </summary>
|
||
private void ExecuteOpenSettings()
|
||
{
|
||
SafeExecute(() =>
|
||
{
|
||
try
|
||
{
|
||
// 使用模态配置编辑器,确保键盘输入不会被 Navisworks 主窗口截获
|
||
var dialog = NavisworksTransport.UI.WPF.Views.ConfigEditorDialog.ShowEditor();
|
||
|
||
if (dialog != null)
|
||
{
|
||
UpdateMainStatus("配置编辑器已关闭");
|
||
LogManager.Info("配置编辑器:已打开并关闭(模态)");
|
||
}
|
||
else
|
||
{
|
||
UpdateMainStatus("配置编辑器已在运行");
|
||
LogManager.Info("配置编辑器:窗口已存在并已激活");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
UpdateMainStatus("打开配置编辑器失败");
|
||
LogManager.Error($"打开配置编辑器失败: {ex.Message}", ex);
|
||
|
||
System.Windows.MessageBox.Show($"打开配置编辑器失败: {ex.Message}", "错误",
|
||
System.Windows.MessageBoxButton.OK,
|
||
System.Windows.MessageBoxImage.Error);
|
||
}
|
||
}, "打开设置");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 生成性能报告 (已优化为Idle事件监听)
|
||
/// </summary>
|
||
private void ExecuteGeneratePerformanceReport()
|
||
{
|
||
SafeExecute(() =>
|
||
{
|
||
UpdateMainStatus("正在生成性能报告...");
|
||
|
||
// 使用Idle事件监听替代Thread.Sleep(1500)
|
||
const string taskId = "SystemManagement_GenerateReport";
|
||
var startTime = DateTime.Now;
|
||
|
||
IdleEventManager.Instance.RegisterOnceTask(
|
||
taskId,
|
||
() => (DateTime.Now - startTime).TotalMilliseconds >= 1500, // 1.5秒后条件满足
|
||
() =>
|
||
{
|
||
try
|
||
{
|
||
// 检查释放状态,避免在释放后更新UI
|
||
if (_disposed)
|
||
{
|
||
LogManager.Info("SystemManagementViewModel已释放,跳过性能报告UI更新");
|
||
return;
|
||
}
|
||
|
||
UpdateMainStatus("性能报告生成完成");
|
||
// 性能信息更新已合并到统一状态栏
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"性能报告UI更新失败: {ex.Message}");
|
||
}
|
||
},
|
||
8 // 高优先级
|
||
);
|
||
|
||
LogManager.Info("生成性能报告 (使用Idle事件监听)");
|
||
}, "生成性能报告");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 执行环境诊断
|
||
/// </summary>
|
||
private void ExecuteDiagnostic()
|
||
{
|
||
SafeExecute(() =>
|
||
{
|
||
try
|
||
{
|
||
LogManager.Info("开始环境诊断");
|
||
|
||
// 直接调用本地的环境诊断方法
|
||
string diagnosticReport = DiagnoseEnvironment();
|
||
|
||
// 显示诊断结果对话框
|
||
System.Windows.MessageBox.Show(
|
||
diagnosticReport,
|
||
"环境诊断结果",
|
||
System.Windows.MessageBoxButton.OK,
|
||
System.Windows.MessageBoxImage.Information);
|
||
|
||
LogManager.Info("环境诊断完成");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"环境诊断失败: {ex.Message}", ex);
|
||
System.Windows.MessageBox.Show(
|
||
$"环境诊断失败: {ex.Message}",
|
||
"错误",
|
||
System.Windows.MessageBoxButton.OK,
|
||
System.Windows.MessageBoxImage.Error);
|
||
}
|
||
}, "环境诊断");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 环境诊断方法 - 快速检查API是否可用
|
||
/// </summary>
|
||
/// <returns>诊断报告</returns>
|
||
private string DiagnoseEnvironment()
|
||
{
|
||
var report = new System.Text.StringBuilder();
|
||
|
||
try
|
||
{
|
||
// 1. 线程状态
|
||
var apartmentState = System.Threading.Thread.CurrentThread.GetApartmentState();
|
||
report.AppendLine($"线程状态: {apartmentState} {(apartmentState == System.Threading.ApartmentState.STA ? "✓" : "✗")}");
|
||
|
||
// 2. API可用性
|
||
try
|
||
{
|
||
var app = Autodesk.Navisworks.Api.Application.ActiveDocument;
|
||
report.AppendLine("API可用性: 可用 ✓");
|
||
}
|
||
catch (Exception apiEx)
|
||
{
|
||
report.AppendLine($"API可用性: 异常 - {apiEx.Message} ✗");
|
||
}
|
||
|
||
// 3. 文档状态
|
||
try
|
||
{
|
||
var document = Autodesk.Navisworks.Api.Application.ActiveDocument;
|
||
if (document != null)
|
||
{
|
||
report.AppendLine($"活动文档: {document.FileName ?? "未命名"} ✓");
|
||
report.AppendLine($"模型数量: {document.Models?.Count ?? 0}");
|
||
}
|
||
else
|
||
{
|
||
report.AppendLine("活动文档: 无 ✗");
|
||
}
|
||
}
|
||
catch (Exception docEx)
|
||
{
|
||
report.AppendLine($"活动文档: 异常 - {docEx.Message} ✗");
|
||
}
|
||
|
||
// 4. 内存状态
|
||
long memoryMB = GC.GetTotalMemory(false) / 1024 / 1024;
|
||
report.AppendLine($"当前内存: {memoryMB} MB");
|
||
|
||
// 5. 系统信息
|
||
report.AppendLine($"插件版本: {PluginVersion}");
|
||
report.AppendLine($"Navisworks版本: {NavisworksVersion}");
|
||
report.AppendLine("系统状态: 正常");
|
||
|
||
string result = report.ToString();
|
||
LogManager.Info($"[SystemManagement] 环境诊断报告:\n{result}");
|
||
return result;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
string error = $"诊断过程出错: {ex.Message}";
|
||
report.AppendLine(error);
|
||
LogManager.Error($"[SystemManagement] {error}");
|
||
return report.ToString();
|
||
}
|
||
}
|
||
|
||
#region 数据管理方法
|
||
|
||
/// <summary>
|
||
/// 刷新数据库状态信息
|
||
/// </summary>
|
||
private void RefreshDatabaseStatus()
|
||
{
|
||
try
|
||
{
|
||
var pathDatabase = PathPlanningManager.Instance?.GetPathDatabase();
|
||
if (pathDatabase != null)
|
||
{
|
||
// 获取数据库信息
|
||
var dbInfo = pathDatabase.Backup.GetDatabaseInfo();
|
||
DatabaseVersion = dbInfo.VersionFormatted;
|
||
PathCount = dbInfo.PathRoutesCount.ToString();
|
||
DetectionRecordCount = dbInfo.DetectionRecordsCount.ToString();
|
||
LogManager.Debug($"数据库状态: 版本{dbInfo.VersionFormatted}, 路径{dbInfo.PathRoutesCount}, 点{dbInfo.PathPointsCount}, 边{dbInfo.PathEdgesCount}, 碰撞{dbInfo.DetectionRecordsCount}");
|
||
}
|
||
else
|
||
{
|
||
DatabaseVersion = "未创建";
|
||
PathCount = "--";
|
||
DetectionRecordCount = "--";
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"刷新数据库状态失败: {ex.Message}");
|
||
DatabaseVersion = "错误";
|
||
PathCount = "--";
|
||
DetectionRecordCount = "--";
|
||
}
|
||
}
|
||
|
||
private PathDatabase GetDatabaseForSystemManagement(string operationName)
|
||
{
|
||
var pathDatabase = PathPlanningManager.Instance?.GetPathDatabase();
|
||
if (pathDatabase != null)
|
||
{
|
||
return pathDatabase;
|
||
}
|
||
|
||
System.Windows.MessageBox.Show(
|
||
"当前文档还没有数据库文件。\n\n请先执行会产生持久化数据的操作(例如保存路径),或先恢复一个数据库备份,再使用这个功能。",
|
||
operationName,
|
||
System.Windows.MessageBoxButton.OK,
|
||
System.Windows.MessageBoxImage.Information);
|
||
UpdateMainStatus($"{operationName}未执行:当前文档没有数据库");
|
||
LogManager.Info($"[{operationName}] 当前文档没有数据库,已阻止操作");
|
||
RefreshDatabaseStatus();
|
||
return null;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 执行数据备份
|
||
/// </summary>
|
||
private void ExecuteBackupData()
|
||
{
|
||
SafeExecute(() =>
|
||
{
|
||
try
|
||
{
|
||
var pathDatabase = GetDatabaseForSystemManagement("备份数据");
|
||
if (pathDatabase == null) return;
|
||
|
||
UpdateMainStatus("正在备份数据库...");
|
||
LogManager.Info("开始数据库文件备份");
|
||
|
||
string backupPath = pathDatabase.Backup.BackupDatabase();
|
||
|
||
if (!string.IsNullOrEmpty(backupPath))
|
||
{
|
||
var info = pathDatabase.Backup.GetDatabaseInfo();
|
||
LastBackupInfo = $"上次备份: {DateTime.Now:yyyy-MM-dd HH:mm:ss}, 版本{info.VersionFormatted}, {info.FileSizeFormatted}";
|
||
System.Windows.MessageBox.Show(
|
||
$"数据库备份完成!\n\n" +
|
||
$"版本: {info.VersionFormatted}\n" +
|
||
$"文件: {backupPath}\n\n" +
|
||
$"包含数据:\n" +
|
||
$"- 路径: {info.PathRoutesCount} 条\n" +
|
||
$"- 路径点: {info.PathPointsCount} 个\n" +
|
||
$"- 检测记录: {info.DetectionRecordsCount} 条\n" +
|
||
$"- 截图: {info.ScreenshotsCount} 张",
|
||
"备份成功",
|
||
System.Windows.MessageBoxButton.OK,
|
||
System.Windows.MessageBoxImage.Information);
|
||
UpdateMainStatus("数据库备份完成");
|
||
LogManager.Info($"数据库备份完成: {backupPath}, 版本{info.VersionFormatted}");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"数据备份失败: {ex.Message}");
|
||
System.Windows.MessageBox.Show(
|
||
$"数据备份失败: {ex.Message}",
|
||
"备份失败",
|
||
System.Windows.MessageBoxButton.OK,
|
||
System.Windows.MessageBoxImage.Error);
|
||
UpdateMainStatus("数据备份失败");
|
||
}
|
||
}, "数据备份");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 执行数据恢复
|
||
/// </summary>
|
||
private void ExecuteRestoreData()
|
||
{
|
||
SafeExecute(() =>
|
||
{
|
||
try
|
||
{
|
||
var pathDatabase = GetDatabaseForSystemManagement("恢复数据");
|
||
if (pathDatabase == null) return;
|
||
|
||
// 选择备份文件
|
||
var openFileDialog = new Microsoft.Win32.OpenFileDialog
|
||
{
|
||
Title = "选择数据库备份文件",
|
||
Filter = "数据库备份文件 (*.db)|*.db",
|
||
InitialDirectory = System.IO.Path.Combine(
|
||
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
||
"NavisworksTransport", "Backups")
|
||
};
|
||
|
||
if (openFileDialog.ShowDialog() != true)
|
||
{
|
||
return;
|
||
}
|
||
|
||
// 确认恢复
|
||
var result = System.Windows.MessageBox.Show(
|
||
"恢复数据将覆盖现有数据,建议先备份当前数据。\n\n是否继续?",
|
||
"确认恢复",
|
||
System.Windows.MessageBoxButton.YesNo,
|
||
System.Windows.MessageBoxImage.Warning);
|
||
|
||
if (result != System.Windows.MessageBoxResult.Yes)
|
||
{
|
||
return;
|
||
}
|
||
|
||
UpdateMainStatus("正在恢复数据库...");
|
||
LogManager.Info($"开始数据库恢复: {openFileDialog.FileName}");
|
||
|
||
pathDatabase.Backup.RestoreDatabase(openFileDialog.FileName);
|
||
|
||
// 恢复成功
|
||
bool restoreSuccess = true;
|
||
|
||
if (restoreSuccess)
|
||
{
|
||
// 刷新数据库状态
|
||
RefreshDatabaseStatus();
|
||
|
||
var info = pathDatabase.Backup.GetDatabaseInfo();
|
||
System.Windows.MessageBox.Show(
|
||
$"数据库恢复完成!\n\n" +
|
||
$"当前数据:\n" +
|
||
$"- 路径: {info.PathRoutesCount} 条\n" +
|
||
$"- 路径点: {info.PathPointsCount} 个\n" +
|
||
$"- 检测记录: {info.DetectionRecordsCount} 条\n" +
|
||
$"- 截图: {info.ScreenshotsCount} 张\n\n" +
|
||
$"请重新启动 Navisworks 以加载恢复的数据。",
|
||
"恢复成功",
|
||
System.Windows.MessageBoxButton.OK,
|
||
System.Windows.MessageBoxImage.Information);
|
||
UpdateMainStatus("数据恢复完成");
|
||
LogManager.Info("数据恢复完成");
|
||
|
||
// 刷新显示
|
||
RefreshDatabaseStatus();
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"数据恢复失败: {ex.Message}");
|
||
System.Windows.MessageBox.Show(
|
||
$"数据恢复失败: {ex.Message}",
|
||
"恢复失败",
|
||
System.Windows.MessageBoxButton.OK,
|
||
System.Windows.MessageBoxImage.Error);
|
||
UpdateMainStatus("数据恢复失败");
|
||
}
|
||
}, "数据恢复");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 执行数据库修复
|
||
/// </summary>
|
||
private void ExecuteRepairDatabase()
|
||
{
|
||
SafeExecute(() =>
|
||
{
|
||
try
|
||
{
|
||
var pathDatabase = GetDatabaseForSystemManagement("修复数据库");
|
||
if (pathDatabase == null) return;
|
||
|
||
UpdateMainStatus("正在检查数据库...");
|
||
LogManager.Info("开始数据库修复检查");
|
||
|
||
// 获取数据库信息
|
||
var info = pathDatabase.Backup.GetDatabaseInfo();
|
||
|
||
System.Windows.MessageBox.Show(
|
||
$"数据库检查结果:\n\n" +
|
||
$"版本: {info.VersionFormatted}\n" +
|
||
$"文件大小: {info.FileSizeFormatted}\n" +
|
||
$"路径数量: {info.PathRoutesCount} 条\n" +
|
||
$"路径点: {info.PathPointsCount} 个\n" +
|
||
$"路径边: {info.PathEdgesCount} 条\n" +
|
||
$"检测记录: {info.DetectionRecordsCount} 条\n" +
|
||
$"截图: {info.ScreenshotsCount} 张",
|
||
"检查结果",
|
||
System.Windows.MessageBoxButton.OK,
|
||
System.Windows.MessageBoxImage.Information);
|
||
|
||
// 刷新状态
|
||
RefreshDatabaseStatus();
|
||
UpdateMainStatus("数据库检查完成");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"数据库修复失败: {ex.Message}");
|
||
System.Windows.MessageBox.Show(
|
||
$"数据库修复失败: {ex.Message}",
|
||
"修复失败",
|
||
System.Windows.MessageBoxButton.OK,
|
||
System.Windows.MessageBoxImage.Error);
|
||
UpdateMainStatus("数据库修复失败");
|
||
}
|
||
}, "数据库修复");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 执行清空所有数据
|
||
/// </summary>
|
||
private void ExecuteClearAllData()
|
||
{
|
||
SafeExecute(() =>
|
||
{
|
||
try
|
||
{
|
||
var pathDatabase = GetDatabaseForSystemManagement("清空数据");
|
||
if (pathDatabase == null) return;
|
||
|
||
// 双重确认
|
||
var result1 = System.Windows.MessageBox.Show(
|
||
"⚠️ 警告:此操作将永久删除所有路径和检测记录!\n\n" +
|
||
"建议先备份数据。\n\n" +
|
||
"是否继续?",
|
||
"确认清空数据",
|
||
System.Windows.MessageBoxButton.YesNo,
|
||
System.Windows.MessageBoxImage.Warning);
|
||
|
||
if (result1 != System.Windows.MessageBoxResult.Yes)
|
||
{
|
||
return;
|
||
}
|
||
|
||
var result2 = System.Windows.MessageBox.Show(
|
||
"请再次确认:是否永久删除所有数据?\n\n" +
|
||
"此操作不可恢复!",
|
||
"最终确认",
|
||
System.Windows.MessageBoxButton.YesNo,
|
||
System.Windows.MessageBoxImage.Warning);
|
||
|
||
if (result2 != System.Windows.MessageBoxResult.Yes)
|
||
{
|
||
return;
|
||
}
|
||
|
||
UpdateMainStatus("正在清空数据...");
|
||
LogManager.Info("开始清空所有数据");
|
||
|
||
// 清空所有表
|
||
pathDatabase.ClearAllData();
|
||
|
||
System.Windows.MessageBox.Show(
|
||
"所有数据已清空!",
|
||
"清空完成",
|
||
System.Windows.MessageBoxButton.OK,
|
||
System.Windows.MessageBoxImage.Information);
|
||
UpdateMainStatus("数据已清空");
|
||
LogManager.Info("所有数据已清空");
|
||
|
||
// 刷新状态
|
||
RefreshDatabaseStatus();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"清空数据失败: {ex.Message}");
|
||
System.Windows.MessageBox.Show(
|
||
$"清空数据失败: {ex.Message}",
|
||
"操作失败",
|
||
System.Windows.MessageBoxButton.OK,
|
||
System.Windows.MessageBoxImage.Error);
|
||
UpdateMainStatus("清空数据失败");
|
||
}
|
||
}, "清空数据");
|
||
}
|
||
|
||
#endregion
|
||
|
||
/// <summary>
|
||
/// 执行体素网格 SDF 测试(使用 MeshSignedDistanceGrid)
|
||
/// </summary>
|
||
private async void ExecuteTestVoxelGridSDF()
|
||
{
|
||
await SafeExecuteAsync(async () =>
|
||
{
|
||
try
|
||
{
|
||
UpdateMainStatus("正在执行体素网格 SDF 测试...");
|
||
LogManager.Info("开始体素网格 SDF 测试(使用 MeshSignedDistanceGrid)");
|
||
|
||
// 获取当前文档
|
||
var document = Autodesk.Navisworks.Api.Application.ActiveDocument;
|
||
if (document == null)
|
||
{
|
||
System.Windows.MessageBox.Show(
|
||
"未找到活动文档,请先打开一个 Navisworks 模型",
|
||
"体素网格 SDF 测试",
|
||
System.Windows.MessageBoxButton.OK,
|
||
System.Windows.MessageBoxImage.Warning);
|
||
UpdateMainStatus("体素网格 SDF 测试取消:无活动文档");
|
||
return;
|
||
}
|
||
|
||
// 创建并执行体素网格 SDF 测试命令
|
||
// 使用系统配置的网格大小参数
|
||
double cellSizeMeters = ConfigManager.Instance.Current.PathEditing.CellSizeMeters;
|
||
LogManager.Info($"使用系统配置的体素大小: {cellSizeMeters}米");
|
||
|
||
var testCommand = new VoxelGridSDFTestCommand(
|
||
document,
|
||
cellSize: cellSizeMeters, // 使用系统配置的体素大小
|
||
samplingRate: 1, // 采样率 1(显示所有体素)
|
||
objectRadius: 0.6, // 物体半径 0.6米(必须 >= 体素大小才能看到膨胀效果)
|
||
objectHeight: 1.8 // 物体高度 1.8米
|
||
);
|
||
|
||
var result = await testCommand.ExecuteAsync();
|
||
|
||
if (result.IsSuccess)
|
||
{
|
||
// 显示测试结果
|
||
System.Windows.MessageBox.Show(
|
||
$"体素网格 SDF 测试成功!\n\n{result.Message}\n\n详细信息请查看日志。",
|
||
"体素网格 SDF 测试结果",
|
||
System.Windows.MessageBoxButton.OK,
|
||
System.Windows.MessageBoxImage.Information);
|
||
|
||
UpdateMainStatus("体素网格 SDF 测试完成");
|
||
LogManager.Info($"体素网格 SDF 测试成功: {result.Message}");
|
||
}
|
||
else
|
||
{
|
||
System.Windows.MessageBox.Show(
|
||
$"体素网格 SDF 测试失败:\n{result.Message}",
|
||
"体素网格 SDF 测试",
|
||
System.Windows.MessageBoxButton.OK,
|
||
System.Windows.MessageBoxImage.Error);
|
||
|
||
UpdateMainStatus($"体素网格 SDF 测试失败: {result.Message}");
|
||
LogManager.Error($"体素网格 SDF 测试失败: {result.Message}");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"体素网格 SDF 测试异常: {ex.Message}", ex);
|
||
System.Windows.MessageBox.Show(
|
||
$"体素网格 SDF 测试出现异常:\n{ex.Message}",
|
||
"错误",
|
||
System.Windows.MessageBoxButton.OK,
|
||
System.Windows.MessageBoxImage.Error);
|
||
UpdateMainStatus($"体素网格 SDF 测试异常: {ex.Message}");
|
||
}
|
||
}, "体素网格 SDF 测试");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 执行体素路径规划测试(3D A*)
|
||
/// </summary>
|
||
private async void ExecuteTestVoxelPathFinding()
|
||
{
|
||
await SafeExecuteAsync(async () =>
|
||
{
|
||
try
|
||
{
|
||
UpdateMainStatus("正在执行体素路径规划测试...");
|
||
LogManager.Info("开始体素路径规划测试(3D A*)");
|
||
|
||
// 获取当前文档
|
||
var document = Autodesk.Navisworks.Api.Application.ActiveDocument;
|
||
if (document == null)
|
||
{
|
||
System.Windows.MessageBox.Show(
|
||
"未找到活动文档,请先打开一个 Navisworks 模型",
|
||
"体素路径规划测试",
|
||
System.Windows.MessageBoxButton.OK,
|
||
System.Windows.MessageBoxImage.Warning);
|
||
UpdateMainStatus("体素路径规划测试取消:无活动文档");
|
||
return;
|
||
}
|
||
|
||
// 使用系统配置的网格大小参数
|
||
double cellSizeMeters = ConfigManager.Instance.Current.PathEditing.CellSizeMeters;
|
||
LogManager.Info($"使用系统配置的体素大小: {cellSizeMeters}米");
|
||
|
||
// 根据可通行体素样本选择测试起点和终点
|
||
// 起点:体素(77, 41, 9) -> 世界(-116.35, 4.94, 50.16) - 模型中心、最高层
|
||
// 终点:体素(107, 11, 8) -> 世界(-67.13, -44.27, 48.52) - 偏右下、低一层
|
||
// 注意:VoxelToWorld返回的是体素左下角坐标,需要加上半个体素尺寸偏移到中心点
|
||
double voxelSizeInModelUnits = cellSizeMeters * UnitsConverter.GetMetersToUnitsConversionFactor(document.Units);
|
||
double halfVoxel = voxelSizeInModelUnits / 2.0;
|
||
|
||
// 起点:模型中心、最高层Z=9
|
||
var startPoint = new Autodesk.Navisworks.Api.Point3D(-200 + halfVoxel, -24.27 + halfVoxel, 42 + halfVoxel);
|
||
// 终点:偏右下、低一层Z=8
|
||
var endPoint = new Autodesk.Navisworks.Api.Point3D(-116.35 + halfVoxel, 0 + halfVoxel, 45 + halfVoxel);
|
||
|
||
LogManager.Info($"测试起点: ({startPoint.X:F2}, {startPoint.Y:F2}, {startPoint.Z:F2})");
|
||
LogManager.Info($"测试终点: ({endPoint.X:F2}, {endPoint.Y:F2}, {endPoint.Z:F2})");
|
||
|
||
// 创建并执行体素路径规划测试命令
|
||
var testCommand = new VoxelPathFindingTestCommand(
|
||
document,
|
||
cellSize: cellSizeMeters, // 体素大小(米)
|
||
objectRadius: 0.6, // 物体半径 0.6米
|
||
objectHeight: 1.8, // 物体高度 1.8米
|
||
startPoint: startPoint, // 起点
|
||
endPoint: endPoint // 终点
|
||
);
|
||
|
||
var result = await testCommand.ExecuteAsync();
|
||
|
||
if (result.IsSuccess)
|
||
{
|
||
// 显示测试结果
|
||
System.Windows.MessageBox.Show(
|
||
$"体素路径规划测试成功!\n\n{result.Message}\n\n详细信息请查看日志和3D视图中的路径可视化。",
|
||
"体素路径规划测试结果",
|
||
System.Windows.MessageBoxButton.OK,
|
||
System.Windows.MessageBoxImage.Information);
|
||
|
||
UpdateMainStatus("体素路径规划测试完成");
|
||
LogManager.Info($"体素路径规划测试成功: {result.Message}");
|
||
}
|
||
else
|
||
{
|
||
System.Windows.MessageBox.Show(
|
||
$"体素路径规划测试失败:\n{result.Message}",
|
||
"体素路径规划测试",
|
||
System.Windows.MessageBoxButton.OK,
|
||
System.Windows.MessageBoxImage.Error);
|
||
|
||
UpdateMainStatus($"体素路径规划测试失败: {result.Message}");
|
||
LogManager.Error($"体素路径规划测试失败: {result.Message}");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"体素路径规划测试异常: {ex.Message}", ex);
|
||
System.Windows.MessageBox.Show(
|
||
$"体素路径规划测试出现异常:\n{ex.Message}",
|
||
"错误",
|
||
System.Windows.MessageBoxButton.OK,
|
||
System.Windows.MessageBoxImage.Error);
|
||
UpdateMainStatus($"体素路径规划测试异常: {ex.Message}");
|
||
}
|
||
}, "体素路径规划测试");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 捕获选中的真实物体,显示原始变换信息并可视化三轴。
|
||
/// </summary>
|
||
private void ExecuteCaptureRealObjectTransform()
|
||
{
|
||
SafeExecute(() =>
|
||
{
|
||
var doc = Autodesk.Navisworks.Api.Application.ActiveDocument;
|
||
if (doc == null || doc.IsClear)
|
||
{
|
||
System.Windows.MessageBox.Show(
|
||
"没有活动的文档!请先打开一个模型。",
|
||
"错误",
|
||
System.Windows.MessageBoxButton.OK,
|
||
System.Windows.MessageBoxImage.Error);
|
||
return;
|
||
}
|
||
|
||
var selection = doc.CurrentSelection.SelectedItems;
|
||
if (selection.Count == 0)
|
||
{
|
||
ClearCapturedRealObjectTransformVisualization();
|
||
System.Windows.MessageBox.Show(
|
||
"请先选择一个真实物体!",
|
||
"提示",
|
||
System.Windows.MessageBoxButton.OK,
|
||
System.Windows.MessageBoxImage.Information);
|
||
return;
|
||
}
|
||
|
||
ModelItem item = selection[0];
|
||
BoundingBox3D bounds = item.BoundingBox();
|
||
Point3D boundsCenter = bounds.Center;
|
||
TransformDiagnosticInfo selectedInfo = CreateTransformDiagnosticInfo(item, "选中节点");
|
||
List<ModelItem> geometryDescendants = CollectGeometryDescendants(item);
|
||
TransformDiagnosticInfo geometryInfo = geometryDescendants.Count > 0
|
||
? CreateTransformDiagnosticInfo(geometryDescendants[0], "首个几何后代")
|
||
: null;
|
||
FragmentOrientationAnalysis fragmentAnalysis = AnalyzeFragmentOrientations(item);
|
||
|
||
Quaternion visualizationQuaternion = fragmentAnalysis != null && fragmentAnalysis.HasRepresentativeOrientation
|
||
? fragmentAnalysis.RepresentativeRotation
|
||
: geometryInfo?.RotationQuaternion ?? selectedInfo.RotationQuaternion;
|
||
|
||
double axisLength = GetTransformAxisVisualizationLength(bounds);
|
||
RenderCapturedRealObjectTransformAxes(boundsCenter, visualizationQuaternion, axisLength);
|
||
|
||
var info = new StringBuilder();
|
||
info.AppendLine("=== 真实物体原始变换信息 ===");
|
||
info.AppendLine();
|
||
info.AppendLine($"对象: {item.DisplayName}");
|
||
info.AppendLine($"ClassName: {item.ClassName}");
|
||
info.AppendLine($"HasGeometry: {item.HasGeometry}");
|
||
info.AppendLine();
|
||
info.AppendLine("【包围盒】");
|
||
info.AppendLine($"Min = ({bounds.Min.X:F3}, {bounds.Min.Y:F3}, {bounds.Min.Z:F3})");
|
||
info.AppendLine($"Max = ({bounds.Max.X:F3}, {bounds.Max.Y:F3}, {bounds.Max.Z:F3})");
|
||
info.AppendLine($"Center = ({boundsCenter.X:F3}, {boundsCenter.Y:F3}, {boundsCenter.Z:F3})");
|
||
info.AppendLine();
|
||
|
||
AppendTransformDiagnosticInfo(info, selectedInfo);
|
||
|
||
info.AppendLine("【几何后代】");
|
||
info.AppendLine($"几何后代数量 = {geometryDescendants.Count}");
|
||
if (geometryInfo != null)
|
||
{
|
||
AppendTransformDiagnosticInfo(info, geometryInfo);
|
||
}
|
||
else
|
||
{
|
||
info.AppendLine("未找到 HasGeometry=True 的后代节点");
|
||
info.AppendLine();
|
||
}
|
||
|
||
AppendFragmentAnalysis(info, fragmentAnalysis);
|
||
info.AppendLine();
|
||
info.AppendLine("【可视化说明】");
|
||
info.AppendLine($"三轴原点使用包围盒中心: ({boundsCenter.X:F3}, {boundsCenter.Y:F3}, {boundsCenter.Z:F3})");
|
||
info.AppendLine($"轴长度 = {axisLength:F3}");
|
||
info.AppendLine($"三轴方向来源 = {(fragmentAnalysis != null && fragmentAnalysis.HasRepresentativeOrientation ? "fragment代表姿态" : geometryInfo != null ? "首个几何后代Transform" : "选中节点Transform")}");
|
||
info.AppendLine("红色=X轴,绿色=Y轴,蓝色=Z轴");
|
||
|
||
string message = info.ToString();
|
||
LogManager.Info("[真实物体原始位姿]\n" + message);
|
||
|
||
var resultDialog = new NavisworksTransport.UI.WPF.Views.CoordinateSystemResultDialog
|
||
{
|
||
Title = "真实物体原始变换信息",
|
||
ResultText = message
|
||
};
|
||
DialogHelper.SetOwnerSafely(resultDialog);
|
||
resultDialog.ShowDialog();
|
||
|
||
UpdateMainStatus($"已捕获真实物体原始位姿:{item.DisplayName}");
|
||
}, "捕获真实物体原始位姿");
|
||
}
|
||
|
||
private void ExecuteDetectFragmentDefaultUp()
|
||
{
|
||
SafeExecute(() =>
|
||
{
|
||
var doc = Autodesk.Navisworks.Api.Application.ActiveDocument;
|
||
if (doc == null || doc.IsClear)
|
||
{
|
||
System.Windows.MessageBox.Show(
|
||
"没有活动的文档!请先打开一个模型。",
|
||
"错误",
|
||
System.Windows.MessageBoxButton.OK,
|
||
System.Windows.MessageBoxImage.Error);
|
||
return;
|
||
}
|
||
|
||
var selection = doc.CurrentSelection.SelectedItems;
|
||
if (selection.Count == 0)
|
||
{
|
||
System.Windows.MessageBox.Show(
|
||
"请先选择一个明显水平放置的真实物体,再执行 fragment 默认Up检测。",
|
||
"提示",
|
||
System.Windows.MessageBoxButton.OK,
|
||
System.Windows.MessageBoxImage.Information);
|
||
return;
|
||
}
|
||
|
||
ModelItem item = selection[0];
|
||
FragmentOrientationAnalysis fragmentAnalysis = AnalyzeFragmentOrientations(item);
|
||
if (fragmentAnalysis == null || !fragmentAnalysis.HasRepresentativeOrientation)
|
||
{
|
||
System.Windows.MessageBox.Show(
|
||
"未能读取选中物体的 fragment 代表姿态,无法检测默认Up。",
|
||
"提示",
|
||
System.Windows.MessageBoxButton.OK,
|
||
System.Windows.MessageBoxImage.Warning);
|
||
return;
|
||
}
|
||
|
||
string hostUpAxis = GetCurrentHostUpAxis();
|
||
if (!RealObjectReferencePoseResolver.TryDetectDefaultUpAxis(
|
||
fragmentAnalysis.ValidMatrices,
|
||
hostUpAxis,
|
||
out string detectedAxis,
|
||
out float yAlignment,
|
||
out float zAlignment))
|
||
{
|
||
System.Windows.MessageBox.Show(
|
||
"未能从 fragment 姿态中检测默认Up。",
|
||
"提示",
|
||
System.Windows.MessageBoxButton.OK,
|
||
System.Windows.MessageBoxImage.Warning);
|
||
return;
|
||
}
|
||
string currentAxis = SelectedFragmentDefaultUpAxis;
|
||
LogManager.Info(
|
||
$"[fragment默认Up检测] 对象={item.DisplayName}, 宿主Up={hostUpAxis}, 当前值={currentAxis}, 检测值={detectedAxis}, " +
|
||
$"Y对齐度={yAlignment:F4}, Z对齐度={zAlignment:F4}");
|
||
|
||
if (string.Equals(detectedAxis, currentAxis, StringComparison.OrdinalIgnoreCase))
|
||
{
|
||
System.Windows.MessageBox.Show(
|
||
$"检测完成:当前文档 Fragment默认Up 已是 {currentAxis}。",
|
||
"Fragment默认Up检测",
|
||
System.Windows.MessageBoxButton.OK,
|
||
System.Windows.MessageBoxImage.Information);
|
||
UpdateMainStatus($"Fragment默认Up检测完成:当前值 {currentAxis} 已匹配");
|
||
return;
|
||
}
|
||
|
||
SetProperty(ref _selectedFragmentDefaultUpAxis, detectedAxis, nameof(SelectedFragmentDefaultUpAxis));
|
||
SaveCurrentDocumentFragmentDefaultUpAxis(detectedAxis);
|
||
|
||
System.Windows.MessageBox.Show(
|
||
$"检测完成:当前文档 Fragment默认Up 已自动改为 {detectedAxis}。",
|
||
"Fragment默认Up检测",
|
||
System.Windows.MessageBoxButton.OK,
|
||
System.Windows.MessageBoxImage.Information);
|
||
|
||
UpdateMainStatus($"当前文档 fragment 默认Up已自动更新为: {detectedAxis}");
|
||
LogManager.Info($"[fragment默认Up] 检测后自动更新为: {detectedAxis}");
|
||
}, "检测Fragment默认Up");
|
||
}
|
||
|
||
private void ClearCapturedRealObjectTransformVisualization()
|
||
{
|
||
var renderPlugin = PathPointRenderPlugin.Instance;
|
||
if (renderPlugin == null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
renderPlugin.ClearRailBaseline("system_transform_axis_x");
|
||
renderPlugin.ClearRailBaseline("system_transform_axis_y");
|
||
renderPlugin.ClearRailBaseline("system_transform_axis_z");
|
||
}
|
||
|
||
private void RenderCapturedRealObjectTransformAxes(Point3D origin, Quaternion rotationQuaternion, double axisLength)
|
||
{
|
||
var renderPlugin = PathPointRenderPlugin.Instance;
|
||
if (renderPlugin == null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
ClearCapturedRealObjectTransformVisualization();
|
||
|
||
RenderTransformAxis(renderPlugin, "system_transform_axis_x", origin, Vector3.Transform(Vector3.UnitX, rotationQuaternion), axisLength, RenderStyleName.TransformAxisX);
|
||
RenderTransformAxis(renderPlugin, "system_transform_axis_y", origin, Vector3.Transform(Vector3.UnitY, rotationQuaternion), axisLength, RenderStyleName.TransformAxisY);
|
||
RenderTransformAxis(renderPlugin, "system_transform_axis_z", origin, Vector3.Transform(Vector3.UnitZ, rotationQuaternion), axisLength, RenderStyleName.TransformAxisZ);
|
||
}
|
||
|
||
private static void RenderTransformAxis(
|
||
PathPointRenderPlugin renderPlugin,
|
||
string pathId,
|
||
Point3D origin,
|
||
Vector3 direction,
|
||
double axisLength,
|
||
RenderStyleName renderStyleName)
|
||
{
|
||
if (direction.LengthSquared() < 1e-8f)
|
||
{
|
||
return;
|
||
}
|
||
|
||
Vector3 normalizedDirection = Vector3.Normalize(direction);
|
||
Point3D endPoint = new Point3D(
|
||
origin.X + normalizedDirection.X * (float)axisLength,
|
||
origin.Y + normalizedDirection.Y * (float)axisLength,
|
||
origin.Z + normalizedDirection.Z * (float)axisLength);
|
||
|
||
renderPlugin.RenderRailBaseline(
|
||
pathId,
|
||
new System.Collections.Generic.List<Point3D> { origin, endPoint },
|
||
renderStyleName);
|
||
}
|
||
|
||
private static double GetTransformAxisVisualizationLength(BoundingBox3D bounds)
|
||
{
|
||
double xSpan = bounds.Max.X - bounds.Min.X;
|
||
double ySpan = bounds.Max.Y - bounds.Min.Y;
|
||
double zSpan = bounds.Max.Z - bounds.Min.Z;
|
||
double maxSpan = Math.Max(xSpan, Math.Max(ySpan, zSpan));
|
||
double minLength = UnitsConverter.ConvertFromMeters(0.5);
|
||
return Math.Max(minLength, maxSpan * 0.3);
|
||
}
|
||
|
||
private static Quaternion ToQuaternion(Rotation3D rotation)
|
||
{
|
||
var axisAngle = rotation.ToAxisAndAngle();
|
||
if (axisAngle.Axis.IsZero || Math.Abs(axisAngle.Angle) < 1e-9)
|
||
{
|
||
return Quaternion.Identity;
|
||
}
|
||
|
||
return Quaternion.Normalize(Quaternion.CreateFromAxisAngle(
|
||
new Vector3((float)axisAngle.Axis.X, (float)axisAngle.Axis.Y, (float)axisAngle.Axis.Z),
|
||
(float)axisAngle.Angle));
|
||
}
|
||
|
||
private static TransformDiagnosticInfo CreateTransformDiagnosticInfo(ModelItem item, string label)
|
||
{
|
||
Transform3DComponents components = item.Transform.Factor();
|
||
var axisAngle = components.Rotation.ToAxisAndAngle();
|
||
Quaternion rotationQuaternion = ToQuaternion(components.Rotation);
|
||
|
||
return new TransformDiagnosticInfo
|
||
{
|
||
Label = label,
|
||
DisplayName = item.DisplayName,
|
||
ClassName = item.ClassName,
|
||
HasGeometry = item.HasGeometry,
|
||
Translation = components.Translation,
|
||
Scale = components.Scale,
|
||
RotationAxis = axisAngle.Axis,
|
||
RotationAngleRadians = axisAngle.Angle,
|
||
RotationQuaternion = rotationQuaternion,
|
||
HostXAxis = Vector3.Normalize(Vector3.Transform(Vector3.UnitX, rotationQuaternion)),
|
||
HostYAxis = Vector3.Normalize(Vector3.Transform(Vector3.UnitY, rotationQuaternion)),
|
||
HostZAxis = Vector3.Normalize(Vector3.Transform(Vector3.UnitZ, rotationQuaternion))
|
||
};
|
||
}
|
||
|
||
private static void AppendTransformDiagnosticInfo(StringBuilder info, TransformDiagnosticInfo diagnostic)
|
||
{
|
||
info.AppendLine($"【{diagnostic.Label}】");
|
||
info.AppendLine($"对象 = {diagnostic.DisplayName}");
|
||
info.AppendLine($"ClassName = {diagnostic.ClassName}");
|
||
info.AppendLine($"HasGeometry = {diagnostic.HasGeometry}");
|
||
info.AppendLine($"Translation = ({diagnostic.Translation.X:F3}, {diagnostic.Translation.Y:F3}, {diagnostic.Translation.Z:F3})");
|
||
info.AppendLine($"Scale = ({diagnostic.Scale.X:F3}, {diagnostic.Scale.Y:F3}, {diagnostic.Scale.Z:F3})");
|
||
info.AppendLine($"Rotation.Axis = ({diagnostic.RotationAxis.X:F4}, {diagnostic.RotationAxis.Y:F4}, {diagnostic.RotationAxis.Z:F4})");
|
||
info.AppendLine($"Rotation.Angle = {diagnostic.RotationAngleRadians:F6} rad ({diagnostic.RotationAngleRadians * 180.0 / Math.PI:F2}°)");
|
||
info.AppendLine($"X轴 = ({diagnostic.HostXAxis.X:F4}, {diagnostic.HostXAxis.Y:F4}, {diagnostic.HostXAxis.Z:F4})");
|
||
info.AppendLine($"Y轴 = ({diagnostic.HostYAxis.X:F4}, {diagnostic.HostYAxis.Y:F4}, {diagnostic.HostYAxis.Z:F4})");
|
||
info.AppendLine($"Z轴 = ({diagnostic.HostZAxis.X:F4}, {diagnostic.HostZAxis.Y:F4}, {diagnostic.HostZAxis.Z:F4})");
|
||
info.AppendLine();
|
||
}
|
||
|
||
private static List<ModelItem> CollectGeometryDescendants(ModelItem root)
|
||
{
|
||
var result = new List<ModelItem>();
|
||
if (root == null)
|
||
{
|
||
return result;
|
||
}
|
||
|
||
CollectGeometryDescendantsRecursive(root, result);
|
||
return result;
|
||
}
|
||
|
||
private static void CollectGeometryDescendantsRecursive(ModelItem item, List<ModelItem> result)
|
||
{
|
||
if (item == null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
if (item.HasGeometry)
|
||
{
|
||
result.Add(item);
|
||
}
|
||
|
||
foreach (ModelItem child in item.Children)
|
||
{
|
||
CollectGeometryDescendantsRecursive(child, result);
|
||
}
|
||
}
|
||
|
||
private static FragmentOrientationAnalysis AnalyzeFragmentOrientations(ModelItem item)
|
||
{
|
||
if (item == null)
|
||
{
|
||
return null;
|
||
}
|
||
|
||
Autodesk.Navisworks.Api.ModelItemCollection collection = new Autodesk.Navisworks.Api.ModelItemCollection { item };
|
||
var comSelection = ComApiBridge.ToInwOpSelection(collection);
|
||
List<FragmentInfo> fragments = null;
|
||
try
|
||
{
|
||
fragments = GeometryHelper.GetAllFragments(comSelection);
|
||
var validMatrices = fragments
|
||
.Where(f => f.TransformMatrix != null && f.TransformMatrix.Length == 16)
|
||
.Select(f => f.TransformMatrix)
|
||
.ToList();
|
||
|
||
var analysis = new FragmentOrientationAnalysis
|
||
{
|
||
FragmentCount = fragments.Count,
|
||
ValidTransformCount = validMatrices.Count,
|
||
ValidMatrices = validMatrices
|
||
};
|
||
|
||
if (validMatrices.Count == 0)
|
||
{
|
||
return analysis;
|
||
}
|
||
|
||
Vector3 referenceX = NormalizeOrUnitX(GetMatrixAxis(validMatrices[0], 0));
|
||
Vector3 referenceY = NormalizeOrUnitY(GetMatrixAxis(validMatrices[0], 1));
|
||
Vector3 referenceZ = NormalizeOrUnitZ(GetMatrixAxis(validMatrices[0], 2));
|
||
|
||
Vector3 sumX = referenceX;
|
||
Vector3 sumY = referenceY;
|
||
Vector3 sumZ = referenceZ;
|
||
double xDotSum = 1.0;
|
||
double yDotSum = 1.0;
|
||
double zDotSum = 1.0;
|
||
|
||
for (int i = 1; i < validMatrices.Count; i++)
|
||
{
|
||
Vector3 axisX = NormalizeWithReference(GetMatrixAxis(validMatrices[i], 0), referenceX);
|
||
Vector3 axisY = NormalizeWithReference(GetMatrixAxis(validMatrices[i], 1), referenceY);
|
||
Vector3 axisZ = NormalizeWithReference(GetMatrixAxis(validMatrices[i], 2), referenceZ);
|
||
|
||
xDotSum += Math.Abs(Vector3.Dot(axisX, referenceX));
|
||
yDotSum += Math.Abs(Vector3.Dot(axisY, referenceY));
|
||
zDotSum += Math.Abs(Vector3.Dot(axisZ, referenceZ));
|
||
|
||
sumX += axisX;
|
||
sumY += axisY;
|
||
sumZ += axisZ;
|
||
}
|
||
|
||
Vector3 representativeX = NormalizeOrUnitX(sumX);
|
||
Vector3 representativeY = sumY - Vector3.Dot(sumY, representativeX) * representativeX;
|
||
representativeY = NormalizeOrUnitY(representativeY);
|
||
Vector3 representativeZ = Vector3.Normalize(Vector3.Cross(representativeX, representativeY));
|
||
if (Vector3.Dot(representativeZ, NormalizeOrUnitZ(sumZ)) < 0)
|
||
{
|
||
representativeZ = -representativeZ;
|
||
}
|
||
representativeY = Vector3.Normalize(Vector3.Cross(representativeZ, representativeX));
|
||
|
||
Matrix4x4 linear = new Matrix4x4(
|
||
representativeX.X, representativeY.X, representativeZ.X, 0f,
|
||
representativeX.Y, representativeY.Y, representativeZ.Y, 0f,
|
||
representativeX.Z, representativeY.Z, representativeZ.Z, 0f,
|
||
0f, 0f, 0f, 1f);
|
||
|
||
analysis.HasRepresentativeOrientation = true;
|
||
analysis.RepresentativeRotation = Quaternion.Normalize(Quaternion.CreateFromRotationMatrix(linear));
|
||
analysis.RepresentativeXAxis = representativeX;
|
||
analysis.RepresentativeYAxis = representativeY;
|
||
analysis.RepresentativeZAxis = representativeZ;
|
||
analysis.AverageXAxisConsistency = xDotSum / validMatrices.Count;
|
||
analysis.AverageYAxisConsistency = yDotSum / validMatrices.Count;
|
||
analysis.AverageZAxisConsistency = zDotSum / validMatrices.Count;
|
||
analysis.SampleTranslation = GetMatrixTranslation(validMatrices[0]);
|
||
return analysis;
|
||
}
|
||
finally
|
||
{
|
||
if (fragments != null)
|
||
{
|
||
foreach (var fragment in fragments)
|
||
{
|
||
if (fragment?.Fragment != null)
|
||
{
|
||
try
|
||
{
|
||
Marshal.ReleaseComObject(fragment.Fragment);
|
||
}
|
||
catch
|
||
{
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if (comSelection != null)
|
||
{
|
||
try
|
||
{
|
||
Marshal.ReleaseComObject(comSelection);
|
||
}
|
||
catch
|
||
{
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
private static void AppendFragmentAnalysis(StringBuilder info, FragmentOrientationAnalysis analysis)
|
||
{
|
||
info.AppendLine("【Fragment 姿态分析】");
|
||
if (analysis == null)
|
||
{
|
||
info.AppendLine("未能执行 fragment 分析");
|
||
info.AppendLine();
|
||
return;
|
||
}
|
||
|
||
info.AppendLine($"Fragment 总数 = {analysis.FragmentCount}");
|
||
info.AppendLine($"有效变换矩阵数 = {analysis.ValidTransformCount}");
|
||
if (!analysis.HasRepresentativeOrientation)
|
||
{
|
||
info.AppendLine("未得到可用的 fragment 代表姿态");
|
||
info.AppendLine();
|
||
return;
|
||
}
|
||
|
||
info.AppendLine($"示例平移 = ({analysis.SampleTranslation.X:F3}, {analysis.SampleTranslation.Y:F3}, {analysis.SampleTranslation.Z:F3})");
|
||
info.AppendLine($"代表X轴 = ({analysis.RepresentativeXAxis.X:F4}, {analysis.RepresentativeXAxis.Y:F4}, {analysis.RepresentativeXAxis.Z:F4})");
|
||
info.AppendLine($"代表Y轴 = ({analysis.RepresentativeYAxis.X:F4}, {analysis.RepresentativeYAxis.Y:F4}, {analysis.RepresentativeYAxis.Z:F4})");
|
||
info.AppendLine($"代表Z轴 = ({analysis.RepresentativeZAxis.X:F4}, {analysis.RepresentativeZAxis.Y:F4}, {analysis.RepresentativeZAxis.Z:F4})");
|
||
info.AppendLine($"X轴一致性 = {analysis.AverageXAxisConsistency:F4}");
|
||
info.AppendLine($"Y轴一致性 = {analysis.AverageYAxisConsistency:F4}");
|
||
info.AppendLine($"Z轴一致性 = {analysis.AverageZAxisConsistency:F4}");
|
||
info.AppendLine();
|
||
}
|
||
|
||
private static Vector3 GetMatrixAxis(double[] matrix, int axisIndex)
|
||
{
|
||
switch (axisIndex)
|
||
{
|
||
case 0:
|
||
return new Vector3((float)matrix[0], (float)matrix[1], (float)matrix[2]);
|
||
case 1:
|
||
return new Vector3((float)matrix[4], (float)matrix[5], (float)matrix[6]);
|
||
case 2:
|
||
return new Vector3((float)matrix[8], (float)matrix[9], (float)matrix[10]);
|
||
default:
|
||
throw new ArgumentOutOfRangeException(nameof(axisIndex));
|
||
}
|
||
}
|
||
|
||
private static Vector3 GetMatrixTranslation(double[] matrix)
|
||
{
|
||
return new Vector3((float)matrix[12], (float)matrix[13], (float)matrix[14]);
|
||
}
|
||
|
||
private static Vector3 NormalizeWithReference(Vector3 axis, Vector3 reference)
|
||
{
|
||
Vector3 normalized = NormalizeOrFallback(axis, reference);
|
||
return Vector3.Dot(normalized, reference) < 0 ? -normalized : normalized;
|
||
}
|
||
|
||
private static Vector3 NormalizeOrUnitX(Vector3 value)
|
||
{
|
||
return NormalizeOrFallback(value, Vector3.UnitX);
|
||
}
|
||
|
||
private static Vector3 NormalizeOrUnitY(Vector3 value)
|
||
{
|
||
return NormalizeOrFallback(value, Vector3.UnitY);
|
||
}
|
||
|
||
private static Vector3 NormalizeOrUnitZ(Vector3 value)
|
||
{
|
||
return NormalizeOrFallback(value, Vector3.UnitZ);
|
||
}
|
||
|
||
private static Vector3 NormalizeOrFallback(Vector3 value, Vector3 fallback)
|
||
{
|
||
return value.LengthSquared() < 1e-8f ? fallback : Vector3.Normalize(value);
|
||
}
|
||
|
||
private sealed class TransformDiagnosticInfo
|
||
{
|
||
public string Label { get; set; }
|
||
public string DisplayName { get; set; }
|
||
public string ClassName { get; set; }
|
||
public bool HasGeometry { get; set; }
|
||
public Vector3D Translation { get; set; }
|
||
public Vector3D Scale { get; set; }
|
||
public Vector3D RotationAxis { get; set; }
|
||
public double RotationAngleRadians { get; set; }
|
||
public Quaternion RotationQuaternion { get; set; }
|
||
public Vector3 HostXAxis { get; set; }
|
||
public Vector3 HostYAxis { get; set; }
|
||
public Vector3 HostZAxis { get; set; }
|
||
}
|
||
|
||
private sealed class FragmentOrientationAnalysis
|
||
{
|
||
public int FragmentCount { get; set; }
|
||
public int ValidTransformCount { get; set; }
|
||
public List<double[]> ValidMatrices { get; set; }
|
||
public bool HasRepresentativeOrientation { get; set; }
|
||
public Quaternion RepresentativeRotation { get; set; }
|
||
public Vector3 RepresentativeXAxis { get; set; }
|
||
public Vector3 RepresentativeYAxis { get; set; }
|
||
public Vector3 RepresentativeZAxis { get; set; }
|
||
public double AverageXAxisConsistency { get; set; }
|
||
public double AverageYAxisConsistency { get; set; }
|
||
public double AverageZAxisConsistency { get; set; }
|
||
public Vector3 SampleTranslation { get; set; }
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取选中对象的Transform信息,并测试旋转
|
||
/// </summary>
|
||
private void ExecuteReadTransformTest()
|
||
{
|
||
try
|
||
{
|
||
var doc = Autodesk.Navisworks.Api.Application.ActiveDocument;
|
||
var selection = doc.CurrentSelection.SelectedItems;
|
||
|
||
if (selection.Count == 0)
|
||
{
|
||
System.Windows.MessageBox.Show("请先选择一个对象!", "提示",
|
||
System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Information);
|
||
return;
|
||
}
|
||
|
||
var item = selection[0];
|
||
var modelItems = new Autodesk.Navisworks.Api.ModelItemCollection { item };
|
||
|
||
// === 读取初始状态 ===
|
||
var transform1 = item.Transform;
|
||
var components1 = transform1.Factor();
|
||
var bbox1 = item.BoundingBox();
|
||
var center1 = bbox1.Center;
|
||
|
||
var info = $"=== 初始状态 ===\n";
|
||
info += $"对象: {item.DisplayName}\n";
|
||
info += $"包围盒中心: ({center1.X:F3}, {center1.Y:F3}, {center1.Z:F3})\n";
|
||
info += $"Transform.Translation: ({components1.Translation.X:F3}, {components1.Translation.Y:F3}, {components1.Translation.Z:F3})\n";
|
||
info += $"Transform.Rotation: {components1.Rotation}\n\n";
|
||
|
||
// === 应用旋转override ===
|
||
// 绕Z轴旋转45度(π/4弧度)
|
||
double angle = Math.PI / 4; // 45度
|
||
var rotationTransform = new Autodesk.Navisworks.Api.Transform3D(
|
||
new Autodesk.Navisworks.Api.Rotation3D(
|
||
new Autodesk.Navisworks.Api.UnitVector3D(0, 0, 1),
|
||
angle
|
||
)
|
||
);
|
||
|
||
// 应用override(false=增量模式)
|
||
doc.Models.OverridePermanentTransform(modelItems, rotationTransform, false);
|
||
|
||
info += $"=== 应用旋转后(绕Z轴45度)===\n";
|
||
|
||
// === 重新读取状态 ===
|
||
var transform2 = item.Transform;
|
||
var components2 = transform2.Factor();
|
||
var bbox2 = item.BoundingBox();
|
||
var center2 = bbox2.Center;
|
||
|
||
info += $"包围盒中心: ({center2.X:F3}, {center2.Y:F3}, {center2.Z:F3})\n";
|
||
info += $"Transform.Translation: ({components2.Translation.X:F3}, {components2.Translation.Y:F3}, {components2.Translation.Z:F3})\n";
|
||
info += $"Transform.Rotation: {components2.Rotation}\n\n";
|
||
|
||
info += $"=== 对比 ===\n";
|
||
info += $"包围盒中心变化: ({center2.X - center1.X:F3}, {center2.Y - center1.Y:F3}, {center2.Z - center1.Z:F3})\n";
|
||
info += $"item.Transform是否变化: {!transform1.Equals(transform2)}\n";
|
||
|
||
LogManager.Info(info);
|
||
System.Windows.MessageBox.Show(info, "Transform测试",
|
||
System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Information);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
System.Windows.MessageBox.Show($"错误: {ex.Message}", "错误",
|
||
System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);
|
||
LogManager.Error($"读取Transform失败: {ex.Message}\n{ex.StackTrace}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 执行坐标系检测命令
|
||
/// </summary>
|
||
private void ExecuteCoordinateSystemExplorer()
|
||
{
|
||
SafeExecute(() =>
|
||
{
|
||
try
|
||
{
|
||
UpdateMainStatus("正在执行坐标系检测...");
|
||
LogManager.Info("开始坐标系检测");
|
||
|
||
var doc = Autodesk.Navisworks.Api.Application.ActiveDocument;
|
||
if (doc == null || doc.IsClear)
|
||
{
|
||
System.Windows.MessageBox.Show(
|
||
"没有活动的文档!请先打开一个模型。",
|
||
"错误",
|
||
System.Windows.MessageBoxButton.OK,
|
||
System.Windows.MessageBoxImage.Error);
|
||
UpdateMainStatus("坐标系检测失败:无活动文档");
|
||
return;
|
||
}
|
||
|
||
var sb = new StringBuilder();
|
||
sb.AppendLine("=== Navisworks 坐标系检测 ===\n");
|
||
|
||
// 1. Document 级别信息
|
||
sb.AppendLine("【1. Document 级别信息】");
|
||
sb.AppendLine($"文档名称: {doc.FileName}");
|
||
sb.AppendLine($"文档标题: {doc.Title}");
|
||
sb.AppendLine($"模型数量: {doc.Models.Count}");
|
||
sb.AppendLine();
|
||
|
||
// 2. Model 级别信息
|
||
sb.AppendLine("【2. Model 级别信息】");
|
||
int modelIndex = 0;
|
||
foreach (var model in doc.Models)
|
||
{
|
||
sb.AppendLine($"模型 [{modelIndex}]:");
|
||
|
||
var rootItem = model.RootItem;
|
||
if (rootItem != null)
|
||
{
|
||
sb.AppendLine($" - RootItem.DisplayName: {rootItem.DisplayName}");
|
||
|
||
var components = rootItem.Transform.Factor();
|
||
sb.AppendLine($" - Transform.Translation: ({components.Translation.X:F4}, {components.Translation.Y:F4}, {components.Translation.Z:F4})");
|
||
var rotation = components.Rotation;
|
||
// Rotation3D 使用 ToAxisAndAngle() 方法获取轴和角度
|
||
var axisAngle = rotation.ToAxisAndAngle();
|
||
sb.AppendLine($" - Transform.Rotation.Axis: ({axisAngle.Axis.X:F4}, {axisAngle.Axis.Y:F4}, {axisAngle.Axis.Z:F4})");
|
||
sb.AppendLine($" - Transform.Rotation.Angle: {axisAngle.Angle:F6} rad ({axisAngle.Angle * 180 / Math.PI:F2}°)");
|
||
sb.AppendLine($" - Transform.Scale: ({components.Scale.X:F4}, {components.Scale.Y:F4}, {components.Scale.Z:F4})");
|
||
|
||
var bbox = rootItem.BoundingBox();
|
||
sb.AppendLine($" - 包围盒 Min: ({bbox.Min.X:F4}, {bbox.Min.Y:F4}, {bbox.Min.Z:F4})");
|
||
sb.AppendLine($" - 包围盒 Max: ({bbox.Max.X:F4}, {bbox.Max.Y:F4}, {bbox.Max.Z:F4})");
|
||
|
||
double xSpan = bbox.Max.X - bbox.Min.X;
|
||
double ySpan = bbox.Max.Y - bbox.Min.Y;
|
||
double zSpan = bbox.Max.Z - bbox.Min.Z;
|
||
sb.AppendLine($" - 跨度: X={xSpan:F2}, Y={ySpan:F2}, Z={zSpan:F2}");
|
||
sb.AppendLine($" - 坐标系推测: {(ySpan > zSpan * 2 ? "可能是 Y-Up" : "可能是 Z-Up")}");
|
||
}
|
||
sb.AppendLine();
|
||
modelIndex++;
|
||
}
|
||
|
||
// 3. Document 坐标系向量 (直接来自模型)
|
||
sb.AppendLine("【3. Document 坐标系向量 (模型原始)】");
|
||
var docUp = doc.UpVector;
|
||
var docRight = doc.RightVector;
|
||
var docFront = doc.FrontVector;
|
||
|
||
if (docUp.IsZero)
|
||
{
|
||
sb.AppendLine("UpVector: (0, 0, 0) - 未定义");
|
||
}
|
||
else
|
||
{
|
||
sb.AppendLine($"UpVector: ({docUp.X:F4}, {docUp.Y:F4}, {docUp.Z:F4})");
|
||
}
|
||
|
||
if (docRight.IsZero)
|
||
{
|
||
sb.AppendLine("RightVector: (0, 0, 0) - 未定义");
|
||
}
|
||
else
|
||
{
|
||
sb.AppendLine($"RightVector: ({docRight.X:F4}, {docRight.Y:F4}, {docRight.Z:F4})");
|
||
}
|
||
|
||
if (docFront.IsZero)
|
||
{
|
||
sb.AppendLine("FrontVector: (0, 0, 0) - 未定义");
|
||
}
|
||
else
|
||
{
|
||
sb.AppendLine($"FrontVector: ({docFront.X:F4}, {docFront.Y:F4}, {docFront.Z:F4})");
|
||
}
|
||
sb.AppendLine();
|
||
|
||
// 4. Viewpoint 信息 (Navisworks 处理后的)
|
||
sb.AppendLine("【4. Viewpoint 信息 (Navisworks 处理后)】");
|
||
var vp = doc.CurrentViewpoint.Value;
|
||
sb.AppendLine($"WorldUpVector: ({vp.WorldUpVector.X:F4}, {vp.WorldUpVector.Y:F4}, {vp.WorldUpVector.Z:F4})");
|
||
sb.AppendLine($"Position: ({vp.Position.X:F4}, {vp.Position.Y:F4}, {vp.Position.Z:F4})");
|
||
var vpRotation = vp.Rotation.ToAxisAndAngle();
|
||
sb.AppendLine($"Rotation.Axis: ({vpRotation.Axis.X:F4}, {vpRotation.Axis.Y:F4}, {vpRotation.Axis.Z:F4})");
|
||
sb.AppendLine($"Rotation.Angle: {vpRotation.Angle:F6} rad");
|
||
sb.AppendLine();
|
||
|
||
// 5. 坐标系推测总结
|
||
sb.AppendLine("【5. 坐标系推测总结】");
|
||
|
||
// 方法1: 优先使用 Document.UpVector 判断(模型原始定义)
|
||
sb.AppendLine($"方法1 - Document.UpVector (原始):");
|
||
if (!docUp.IsZero)
|
||
{
|
||
sb.AppendLine($" 值: ({docUp.X:F4}, {docUp.Y:F4}, {docUp.Z:F4})");
|
||
|
||
if (Math.Abs(docUp.Z) > 0.9)
|
||
{
|
||
sb.AppendLine($" ✅ 判定: Z-Up 坐标系 (原始)");
|
||
}
|
||
else if (Math.Abs(docUp.Y) > 0.9)
|
||
{
|
||
sb.AppendLine($" ✅ 判定: Y-Up 坐标系 (原始)");
|
||
}
|
||
else if (Math.Abs(docUp.X) > 0.9)
|
||
{
|
||
sb.AppendLine($" ⚠️ 判定: X-Up 坐标系 (罕见)");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
sb.AppendLine($" 未定义 (Zero),需使用 Viewpoint.WorldUpVector");
|
||
}
|
||
sb.AppendLine();
|
||
|
||
// 方法2: 使用 WorldUpVector 判断(Navisworks 处理后的,最可靠)
|
||
var worldUp = doc.CurrentViewpoint.Value.WorldUpVector;
|
||
sb.AppendLine($"方法2 - Viewpoint.WorldUpVector (推荐):");
|
||
sb.AppendLine($" 值: ({worldUp.X:F4}, {worldUp.Y:F4}, {worldUp.Z:F4})");
|
||
|
||
string detectedCoordinateSystem = "Unknown";
|
||
if (Math.Abs(worldUp.Z) > 0.9)
|
||
{
|
||
sb.AppendLine($" ✅ 判定: Z-Up 坐标系 (Z轴向上)");
|
||
sb.AppendLine($" 说明: 这是 Navisworks 默认坐标系,插件无需特殊配置");
|
||
detectedCoordinateSystem = "ZUp";
|
||
}
|
||
else if (Math.Abs(worldUp.Y) > 0.9)
|
||
{
|
||
sb.AppendLine($" ✅ 判定: Y-Up 坐标系 (Y轴向上)");
|
||
sb.AppendLine($" 说明: 通常是 Revit 等软件导出的模型");
|
||
sb.AppendLine($" 建议: 在 default_config.toml 中设置坐标系为 YUp");
|
||
detectedCoordinateSystem = "YUp";
|
||
}
|
||
else if (Math.Abs(worldUp.X) > 0.9)
|
||
{
|
||
sb.AppendLine($" ⚠️ 判定: X-Up 坐标系 (罕见)");
|
||
sb.AppendLine($" 说明: 非标准坐标系,需要手动适配");
|
||
detectedCoordinateSystem = "XUp";
|
||
}
|
||
else
|
||
{
|
||
sb.AppendLine($" ❓ 判定: 无法确定(非标准向上向量)");
|
||
}
|
||
sb.AppendLine();
|
||
|
||
// 方法3: 通过模型 Transform 旋转判断(辅助验证)
|
||
sb.AppendLine($"方法3 - 模型 Transform 旋转:");
|
||
if (doc.Models.Count > 0)
|
||
{
|
||
var rootItem = doc.Models[0].RootItem;
|
||
if (rootItem != null)
|
||
{
|
||
var components = rootItem.Transform.Factor();
|
||
var rotation = components.Rotation.ToAxisAndAngle();
|
||
double angleDegrees = rotation.Angle * 180 / Math.PI;
|
||
|
||
sb.AppendLine($" 旋转轴: ({rotation.Axis.X:F4}, {rotation.Axis.Y:F4}, {rotation.Axis.Z:F4})");
|
||
sb.AppendLine($" 旋转角: {angleDegrees:F2}°");
|
||
|
||
// 检查是否有 90° 或 270° 绕 X 轴旋转(Y-Up 模型的典型特征)
|
||
if (Math.Abs(rotation.Axis.X) > 0.9 &&
|
||
(Math.Abs(angleDegrees - 90) < 5 || Math.Abs(angleDegrees - 270) < 5))
|
||
{
|
||
sb.AppendLine($" ✅ 发现 X 轴旋转 ~{angleDegrees:F0}°,这是 Y-Up 导入的典型特征");
|
||
}
|
||
else if (Math.Abs(angleDegrees) < 1)
|
||
{
|
||
sb.AppendLine($" 无显著旋转,符合 Z-Up 模型特征");
|
||
}
|
||
}
|
||
}
|
||
sb.AppendLine();
|
||
|
||
// 方法4: 包围盒跨度分析(启发式)
|
||
sb.AppendLine($"方法4 - 包围盒跨度分析(启发式):");
|
||
if (doc.Models.Count > 0)
|
||
{
|
||
var rootBounds = doc.Models[0].RootItem.BoundingBox();
|
||
double xSpan = rootBounds.Max.X - rootBounds.Min.X;
|
||
double ySpan = rootBounds.Max.Y - rootBounds.Min.Y;
|
||
double zSpan = rootBounds.Max.Z - rootBounds.Min.Z;
|
||
|
||
sb.AppendLine($" X 跨度: {xSpan:F2} 单位");
|
||
sb.AppendLine($" Y 跨度: {ySpan:F2} 单位");
|
||
sb.AppendLine($" Z 跨度: {zSpan:F2} 单位");
|
||
|
||
// 注意:经过旋转后,Y-Up 模型在 Navisworks 中显示为 Z-Up
|
||
// 所以这里看到的是转换后的坐标
|
||
if (zSpan > ySpan * 1.5)
|
||
{
|
||
sb.AppendLine($" Z 跨度大于 Y 跨度,显示为 Z-Up 方向");
|
||
}
|
||
else if (ySpan > zSpan * 1.5)
|
||
{
|
||
sb.AppendLine($" Y 跨度大于 Z 跨度,可能未完全转换");
|
||
}
|
||
}
|
||
sb.AppendLine();
|
||
|
||
// 最终建议
|
||
sb.AppendLine($"【最终建议】");
|
||
if (detectedCoordinateSystem == "YUp")
|
||
{
|
||
sb.AppendLine($"⚠️ 检测到 Y-Up 坐标系!");
|
||
sb.AppendLine($"当前插件使用 Z-Up 假设,在此模型上可能出现问题。");
|
||
sb.AppendLine($"建议:实施坐标系动态适配功能。");
|
||
}
|
||
else if (detectedCoordinateSystem == "ZUp")
|
||
{
|
||
sb.AppendLine($"✅ 标准 Z-Up 坐标系,插件应正常工作。");
|
||
}
|
||
|
||
// 显示和记录结果
|
||
string result = sb.ToString();
|
||
LogManager.Info(result);
|
||
|
||
// 使用可复制的对话框显示结果
|
||
var resultDialog = new NavisworksTransport.UI.WPF.Views.CoordinateSystemResultDialog
|
||
{
|
||
Title = "坐标系检测结果",
|
||
ResultText = result
|
||
};
|
||
|
||
// 设置对话框所有者并显示
|
||
DialogHelper.SetOwnerSafely(resultDialog);
|
||
resultDialog.ShowDialog();
|
||
|
||
UpdateMainStatus("坐标系检测完成");
|
||
LogManager.Info("坐标系检测成功完成");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"坐标系检测异常: {ex.Message}", ex);
|
||
System.Windows.MessageBox.Show(
|
||
$"坐标系检测出现异常:\n{ex.Message}",
|
||
"错误",
|
||
System.Windows.MessageBoxButton.OK,
|
||
System.Windows.MessageBoxImage.Error);
|
||
UpdateMainStatus($"坐标系检测异常: {ex.Message}");
|
||
}
|
||
}, "坐标系检测");
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 辅助方法
|
||
|
||
/// <summary>
|
||
/// 安全执行异步操作
|
||
/// </summary>
|
||
private async Task SafeExecuteAsync(Func<Task> action, string operationName = "未知操作")
|
||
{
|
||
try
|
||
{
|
||
await action();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"{operationName}发生异常: {ex.Message}", ex);
|
||
UpdateMainStatus($"{operationName}失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 安全执行同步操作
|
||
/// </summary>
|
||
private void SafeExecute(Action action, string operationName = "未知操作")
|
||
{
|
||
try
|
||
{
|
||
action();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"{operationName}发生异常: {ex.Message}", ex);
|
||
UpdateMainStatus($"{operationName}失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 验证ViewModel状态是否正常
|
||
/// </summary>
|
||
public bool IsValidState()
|
||
{
|
||
return _uiStateManager != null && LogLevels != null;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取ViewModel状态信息
|
||
/// </summary>
|
||
public string GetStateInfo()
|
||
{
|
||
return $"UIStateManager: {(_uiStateManager != null ? "已初始化" : "未初始化")}, " +
|
||
$"日志级别数量: {LogLevels?.Count ?? 0}";
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 清理资源和IDisposable实现
|
||
|
||
/// <summary>
|
||
/// 释放资源
|
||
/// </summary>
|
||
public void Dispose()
|
||
{
|
||
Dispose(true);
|
||
GC.SuppressFinalize(this);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 释放资源的具体实现
|
||
/// </summary>
|
||
/// <param name="disposing">是否正在释放托管资源</param>
|
||
protected virtual void Dispose(bool disposing)
|
||
{
|
||
if (!_disposed)
|
||
{
|
||
if (disposing)
|
||
{
|
||
try
|
||
{
|
||
// 调用现有的清理逻辑
|
||
Cleanup();
|
||
LogManager.Info("SystemManagementViewModel已正确释放资源 (IDisposable)");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"SystemManagementViewModel释放资源时发生异常: {ex.Message}", ex);
|
||
}
|
||
}
|
||
_disposed = true;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 启动系统信息定时刷新
|
||
/// </summary>
|
||
private void StartSystemInfoTimer()
|
||
{
|
||
try
|
||
{
|
||
if (_systemInfoTimer == null)
|
||
{
|
||
_systemInfoTimer = new DispatcherTimer
|
||
{
|
||
Interval = TimeSpan.FromSeconds(5) // 每5秒刷新一次
|
||
};
|
||
_systemInfoTimer.Tick += (s, e) => RefreshSystemInfo();
|
||
}
|
||
|
||
_systemInfoTimer.Start();
|
||
LogManager.Debug("系统信息定时器已启动 (5秒间隔)");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"启动系统信息定时器失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 停止系统信息定时刷新
|
||
/// </summary>
|
||
private void StopSystemInfoTimer()
|
||
{
|
||
try
|
||
{
|
||
if (_systemInfoTimer != null)
|
||
{
|
||
_systemInfoTimer.Stop();
|
||
_systemInfoTimer.Tick -= (s, e) => { };
|
||
_systemInfoTimer = null;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Warning($"停止系统信息定时器时出现警告: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 刷新系统信息 (内存、运行时间)
|
||
/// </summary>
|
||
private void RefreshSystemInfo()
|
||
{
|
||
try
|
||
{
|
||
// 使用 VersionInfo 获取最新的内存和运行时间
|
||
MemoryUsage = VersionInfo.GetMemoryUsage();
|
||
RunningTime = VersionInfo.GetRunningTime();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Debug($"刷新系统信息时出错: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
public void Cleanup()
|
||
{
|
||
try
|
||
{
|
||
LogManager.Info("开始清理SystemManagementViewModel资源");
|
||
|
||
// 取消订阅文档相关事件
|
||
UnsubscribeFromDocumentEvents();
|
||
|
||
// 停止性能监控Idle任务,替代DispatcherTimer清理
|
||
try
|
||
{
|
||
IdleEventManager.Instance.UnregisterTask("SystemManagement_PerformanceMonitor");
|
||
LogManager.Info("SystemManagementViewModel性能监控Idle任务已停止");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Warning($"停止性能监控Idle任务时出现警告: {ex.Message}");
|
||
}
|
||
|
||
// 清理可能的一次性任务
|
||
try
|
||
{
|
||
IdleEventManager.Instance.UnregisterTask("SystemManagement_CheckUpdate");
|
||
IdleEventManager.Instance.UnregisterTask("SystemManagement_GenerateReport");
|
||
LogManager.Debug("SystemManagementViewModel一次性Idle任务已清理");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Debug($"清理一次性Idle任务时出现提示: {ex.Message}");
|
||
}
|
||
|
||
// 停止系统信息定时器
|
||
try
|
||
{
|
||
StopSystemInfoTimer();
|
||
LogManager.Debug("SystemManagementViewModel系统信息定时器已停止");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Debug($"停止系统信息定时器时出现提示: {ex.Message}");
|
||
}
|
||
|
||
LogManager.Info("SystemManagementViewModel资源清理完成");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"SystemManagementViewModel清理失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
}
|
||
}
|