NavisworksTransport/src/UI/WPF/ViewModels/SystemManagementViewModel.cs

1929 lines
79 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

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

using System;
using System.Collections.ObjectModel;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using System.Windows.Threading;
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 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 ExportSectionBoxCommand { get; private set; }
// 坐标系设置
public ObservableCollection<string> CoordinateSystemOptions { 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);
}
#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());
ExportSectionBoxCommand = new RelayCommand(() => ExecuteExportSectionBox());
// 初始化坐标系选项
CoordinateSystemOptions = new ObservableCollection<string> { "AutoDetect", "ZUp", "YUp" };
// 从配置加载当前坐标系设置
var configType = ConfigManager.Instance.Current.CoordinateSystem?.Type ?? "AutoDetect";
_selectedCoordinateSystem = configType;
// 订阅文档事件(用于自动检测坐标系)
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();
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}";
}
#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}");
}
});
}
}
#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
{
// 使用单例模式显示配置编辑器(非模态,避免独占焦点)
// 不传递 owner让 ShowEditor 自己通过 DialogHelper 处理
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 = "错误";
}
}
/// <summary>
/// 执行数据备份
/// </summary>
private void ExecuteBackupData()
{
SafeExecute(() =>
{
try
{
var pathDatabase = PathPlanningManager.Instance?.GetPathDatabase();
if (pathDatabase == null)
{
System.Windows.MessageBox.Show(
"未找到数据库连接,请确保已加载模型",
"备份数据",
System.Windows.MessageBoxButton.OK,
System.Windows.MessageBoxImage.Warning);
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 = PathPlanningManager.Instance?.GetPathDatabase();
if (pathDatabase == null)
{
System.Windows.MessageBox.Show(
"未找到数据库连接,请确保已加载模型",
"恢复数据",
System.Windows.MessageBoxButton.OK,
System.Windows.MessageBoxImage.Warning);
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 = PathPlanningManager.Instance?.GetPathDatabase();
if (pathDatabase == null)
{
System.Windows.MessageBox.Show(
"未找到数据库连接",
"修复数据库",
System.Windows.MessageBoxButton.OK,
System.Windows.MessageBoxImage.Warning);
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 = PathPlanningManager.Instance?.GetPathDatabase();
if (pathDatabase == null)
{
System.Windows.MessageBox.Show(
"未找到数据库连接",
"清空数据",
System.Windows.MessageBoxButton.OK,
System.Windows.MessageBoxImage.Warning);
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>
/// 读取选中对象的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
)
);
// 应用overridefalse=增量模式)
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}");
}
}, "坐标系检测");
}
/// <summary>
/// 执行导出剖面盒功能
/// </summary>
private void ExecuteExportSectionBox()
{
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 exporter = new NavisworksTransport.Core.SectionBoxExporter();
string filePath = exporter.ExportToJson(doc);
if (!string.IsNullOrEmpty(filePath))
{
var result = System.Windows.MessageBox.Show(
$"剖面盒信息已导出到:\n{filePath}\n\n是否打开文件所在目录",
"导出成功",
System.Windows.MessageBoxButton.YesNo,
System.Windows.MessageBoxImage.Information);
if (result == System.Windows.MessageBoxResult.Yes)
{
System.Diagnostics.Process.Start("explorer.exe", $"/select,\"{filePath}\"");
}
UpdateMainStatus($"剖面盒信息已导出: {filePath}");
LogManager.Info($"剖面盒信息导出成功: {filePath}");
}
else
{
UpdateMainStatus("导出剖面盒被取消或失败");
}
}
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
}
}