清理了一些过期代码
This commit is contained in:
parent
508c3e8e79
commit
3c1458245c
@ -1,17 +0,0 @@
|
||||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: true
|
||||
---
|
||||
本项目中设计方案和开发任何代码,都要先参考Navisworks2017的API文档;
|
||||
每次完成一个开发任务,更新 [VERSION.md](mdc:NavisworksTransport/NavisworksTransport/NavisworksTransport/NavisworksTransport/VERSION.md) 和 [change_log.md](mdc:NavisworksTransport/NavisworksTransport/NavisworksTransport/NavisworksTransport/change_log.md);
|
||||
生成的任务清单文件和其他临时文件,放在 doc/working目录下;
|
||||
每次分析错误,要看日志文件[NavisworksTransport_Debug.log](mdc:NavisworksTransport/NavisworksTransport/NavisworksTransport/Desktop/NavisworksTransport_Debug.log);
|
||||
每次增加新的代码文件,要把文件增加到 [NavisworksTransportPlugin.csproj](mdc:NavisworksTransport/NavisworksTransport/NavisworksTransport/NavisworksTransport/NavisworksTransportPlugin.csproj)中;
|
||||
这个项目的开发环境是windows,生成命令时要注意;
|
||||
在对代码进行修改时,不能随意删掉代码中原有的和此次修改无关的代码
|
||||
|
||||
编译使用命令:
|
||||
```sh
|
||||
.\compile.bat
|
||||
```
|
||||
@ -102,7 +102,6 @@
|
||||
|
||||
<!-- Core - Business Logic -->
|
||||
<Compile Include="src\Core\ModelSplitterManager.cs" />
|
||||
<Compile Include="src\Core\SimplifiedModelSplitterManager.cs" />
|
||||
<Compile Include="src\Core\PathDataManager.cs" />
|
||||
<Compile Include="src\Core\PathPlanningManager.cs" />
|
||||
<Compile Include="src\Core\PathPlanningModels.cs" />
|
||||
@ -128,7 +127,6 @@
|
||||
<Compile Include="src\Commands\DeletePathCommand.cs" />
|
||||
<Compile Include="src\Commands\ExportPathCommand.cs" />
|
||||
<Compile Include="src\Commands\ImportPathCommand.cs" />
|
||||
<Compile Include="src\Commands\ModelSplitterCommand.cs" />
|
||||
<Compile Include="src\Commands\SetLogisticsAttributeCommand.cs" />
|
||||
<Compile Include="src\Commands\StartAnimationCommand.cs" />
|
||||
<Compile Include="src\Commands\ViewCollisionReportCommand.cs" />
|
||||
@ -162,9 +160,6 @@
|
||||
<Compile Include="src\UI\Forms\LogisticsPropertyEditDialog.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="src\UI\Forms\ModelSplitterDialog.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="src\UI\Forms\SimpleDockPaneControl.cs">
|
||||
<SubType>UserControl</SubType>
|
||||
</Compile>
|
||||
|
||||
@ -1,897 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Threading;
|
||||
|
||||
namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
{
|
||||
/// <summary>
|
||||
/// 系统管理ViewModel - 参照PathEditingViewModel的设计模式
|
||||
/// 专门负责系统管理相关功能,包括:日志管理、设置管理、性能监控、版本控制、配置管理等
|
||||
/// </summary>
|
||||
public class SystemManagementViewModel : ViewModelBase
|
||||
{
|
||||
#region 私有字段
|
||||
|
||||
// 依赖注入的核心服务
|
||||
private readonly UIStateManager _uiStateManager;
|
||||
|
||||
// 系统管理相关的管理器实例(通过依赖注入)
|
||||
private ISystemLogManager _logManager;
|
||||
private ISystemConfigManager _configManager;
|
||||
private IPerformanceMonitor _performanceMonitor;
|
||||
private IModelSplitterManager _modelSplitterManager;
|
||||
|
||||
// 日志管理相关字段
|
||||
private ObservableCollection<string> _logLevels;
|
||||
private string _selectedLogLevel = "Info";
|
||||
private string _logStatus = "日志系统正常";
|
||||
private int _logEntryCount = 0;
|
||||
private string _logFilePath = "未设置";
|
||||
|
||||
// 系统设置相关字段
|
||||
private bool _isAutoSaveEnabled = true;
|
||||
private bool _isDebugModeEnabled = false;
|
||||
private string _settingsStatus = "设置已加载";
|
||||
private int _autoSaveInterval = 300; // 秒
|
||||
private string _workingDirectory = "";
|
||||
|
||||
// 版本和系统信息相关字段
|
||||
private string _pluginVersion = "v1.0";
|
||||
private string _navisworksVersion = "2026";
|
||||
private string _systemStatus = "正常";
|
||||
private string _systemStatusColor = "Green";
|
||||
private string _lastUpdateCheck = "未检查";
|
||||
|
||||
// 性能监控相关字段
|
||||
private string _memoryUsage = "0 MB";
|
||||
private string _runningTime = "00:00:00";
|
||||
private string _performanceInfo = "性能监控就绪";
|
||||
private double _cpuUsage = 0.0;
|
||||
private int _threadsCount = 0;
|
||||
private DispatcherTimer _performanceTimer;
|
||||
|
||||
// 模型分割器相关字段
|
||||
private string _modelSplitterStatus = "就绪";
|
||||
private bool _canRunModelSplitter = true;
|
||||
private string _lastSplitResult = "未执行";
|
||||
|
||||
// 配置管理相关字段
|
||||
private string _currentConfigProfile = "默认配置";
|
||||
private ObservableCollection<string> _availableProfiles;
|
||||
private bool _hasUnsavedChanges = false;
|
||||
|
||||
#endregion
|
||||
|
||||
#region 公共属性
|
||||
|
||||
#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))
|
||||
{
|
||||
OnLogLevelChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 日志系统状态
|
||||
/// </summary>
|
||||
public string LogStatus
|
||||
{
|
||||
get => _logStatus;
|
||||
set => SetProperty(ref _logStatus, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 日志条目数量
|
||||
/// </summary>
|
||||
public int LogEntryCount
|
||||
{
|
||||
get => _logEntryCount;
|
||||
set => SetProperty(ref _logEntryCount, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 日志文件路径
|
||||
/// </summary>
|
||||
public string LogFilePath
|
||||
{
|
||||
get => _logFilePath;
|
||||
set => SetProperty(ref _logFilePath, value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 系统设置属性
|
||||
|
||||
/// <summary>
|
||||
/// 是否启用自动保存
|
||||
/// </summary>
|
||||
public bool IsAutoSaveEnabled
|
||||
{
|
||||
get => _isAutoSaveEnabled;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _isAutoSaveEnabled, value))
|
||||
{
|
||||
OnAutoSaveSettingChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否启用调试模式
|
||||
/// </summary>
|
||||
public bool IsDebugModeEnabled
|
||||
{
|
||||
get => _isDebugModeEnabled;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _isDebugModeEnabled, value))
|
||||
{
|
||||
OnDebugModeChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置状态
|
||||
/// </summary>
|
||||
public string SettingsStatus
|
||||
{
|
||||
get => _settingsStatus;
|
||||
set => SetProperty(ref _settingsStatus, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 自动保存间隔(秒)
|
||||
/// </summary>
|
||||
public int AutoSaveInterval
|
||||
{
|
||||
get => _autoSaveInterval;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _autoSaveInterval, value))
|
||||
{
|
||||
OnAutoSaveIntervalChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 工作目录
|
||||
/// </summary>
|
||||
public string WorkingDirectory
|
||||
{
|
||||
get => _workingDirectory;
|
||||
set => SetProperty(ref _workingDirectory, value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 版本和系统信息属性
|
||||
|
||||
/// <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 SystemStatus
|
||||
{
|
||||
get => _systemStatus;
|
||||
set => SetProperty(ref _systemStatus, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 系统状态颜色
|
||||
/// </summary>
|
||||
public string SystemStatusColor
|
||||
{
|
||||
get => _systemStatusColor;
|
||||
set => SetProperty(ref _systemStatusColor, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 最后更新检查时间
|
||||
/// </summary>
|
||||
public string LastUpdateCheck
|
||||
{
|
||||
get => _lastUpdateCheck;
|
||||
set => SetProperty(ref _lastUpdateCheck, value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 性能监控属性
|
||||
|
||||
/// <summary>
|
||||
/// 内存使用情况
|
||||
/// </summary>
|
||||
public string MemoryUsage
|
||||
{
|
||||
get => _memoryUsage;
|
||||
set => SetProperty(ref _memoryUsage, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 运行时间
|
||||
/// </summary>
|
||||
public string RunningTime
|
||||
{
|
||||
get => _runningTime;
|
||||
set => SetProperty(ref _runningTime, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 性能信息
|
||||
/// </summary>
|
||||
public string PerformanceInfo
|
||||
{
|
||||
get => _performanceInfo;
|
||||
set => SetProperty(ref _performanceInfo, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// CPU使用率
|
||||
/// </summary>
|
||||
public double CpuUsage
|
||||
{
|
||||
get => _cpuUsage;
|
||||
set => SetProperty(ref _cpuUsage, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 线程数量
|
||||
/// </summary>
|
||||
public int ThreadsCount
|
||||
{
|
||||
get => _threadsCount;
|
||||
set => SetProperty(ref _threadsCount, value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 模型分割器属性
|
||||
|
||||
/// <summary>
|
||||
/// 模型分层拆分状态
|
||||
/// </summary>
|
||||
public string ModelSplitterStatus
|
||||
{
|
||||
get => _modelSplitterStatus;
|
||||
set => SetProperty(ref _modelSplitterStatus, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否可以运行模型分割器
|
||||
/// </summary>
|
||||
public bool CanRunModelSplitter
|
||||
{
|
||||
get => _canRunModelSplitter;
|
||||
set => SetProperty(ref _canRunModelSplitter, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 最后分割结果
|
||||
/// </summary>
|
||||
public string LastSplitResult
|
||||
{
|
||||
get => _lastSplitResult;
|
||||
set => SetProperty(ref _lastSplitResult, value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 配置管理属性
|
||||
|
||||
/// <summary>
|
||||
/// 当前配置档案
|
||||
/// </summary>
|
||||
public string CurrentConfigProfile
|
||||
{
|
||||
get => _currentConfigProfile;
|
||||
set => SetProperty(ref _currentConfigProfile, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 可用配置档案
|
||||
/// </summary>
|
||||
public ObservableCollection<string> AvailableProfiles
|
||||
{
|
||||
get => _availableProfiles;
|
||||
set => SetProperty(ref _availableProfiles, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否有未保存的更改
|
||||
/// </summary>
|
||||
public bool HasUnsavedChanges
|
||||
{
|
||||
get => _hasUnsavedChanges;
|
||||
set => SetProperty(ref _hasUnsavedChanges, value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#region 命令定义
|
||||
|
||||
// 日志管理命令
|
||||
public ICommand ViewLogCommand { get; private set; }
|
||||
public ICommand ClearLogCommand { get; private set; }
|
||||
public ICommand ExportLogCommand { get; private set; }
|
||||
public ICommand RefreshLogStatusCommand { get; private set; }
|
||||
|
||||
// 设置管理命令
|
||||
public ICommand OpenSettingsCommand { get; private set; }
|
||||
public ICommand ResetSettingsCommand { get; private set; }
|
||||
public ICommand SaveSettingsCommand { get; private set; }
|
||||
public ICommand LoadSettingsCommand { get; private set; }
|
||||
|
||||
// 配置管理命令
|
||||
public ICommand ImportConfigCommand { get; private set; }
|
||||
public ICommand ExportConfigCommand { get; private set; }
|
||||
public ICommand CreateProfileCommand { get; private set; }
|
||||
public ICommand DeleteProfileCommand { get; private set; }
|
||||
public ICommand SwitchProfileCommand { get; private set; }
|
||||
|
||||
// 系统管理命令
|
||||
public ICommand CheckUpdateCommand { get; private set; }
|
||||
public ICommand RestartSystemCommand { get; private set; }
|
||||
public ICommand SystemDiagnosticsCommand { get; private set; }
|
||||
|
||||
// 性能管理命令
|
||||
public ICommand GeneratePerformanceReportCommand { get; private set; }
|
||||
public ICommand StartPerformanceMonitoringCommand { get; private set; }
|
||||
public ICommand StopPerformanceMonitoringCommand { get; private set; }
|
||||
public ICommand ClearPerformanceDataCommand { get; private set; }
|
||||
|
||||
// 模型分割器命令
|
||||
public ICommand RunModelSplitterCommand { get; private set; }
|
||||
public ICommand ConfigureModelSplitterCommand { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region 构造函数
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数 - 参照PathEditingViewModel的设计模式
|
||||
/// </summary>
|
||||
/// <param name="logManager">日志管理器(可选,用于依赖注入)</param>
|
||||
/// <param name="configManager">配置管理器(可选,用于依赖注入)</param>
|
||||
/// <param name="performanceMonitor">性能监控器(可选,用于依赖注入)</param>
|
||||
/// <param name="modelSplitterManager">模型分割器管理器(可选,用于依赖注入)</param>
|
||||
public SystemManagementViewModel(
|
||||
ISystemLogManager logManager = null,
|
||||
ISystemConfigManager configManager = null,
|
||||
IPerformanceMonitor performanceMonitor = null,
|
||||
IModelSplitterManager modelSplitterManager = null) : base()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 获取UI状态管理器
|
||||
_uiStateManager = UIStateManager.Instance;
|
||||
if (_uiStateManager == null)
|
||||
{
|
||||
throw new InvalidOperationException("UIStateManager初始化失败");
|
||||
}
|
||||
|
||||
// 设置依赖(如果为null则使用默认实现)
|
||||
_logManager = logManager ?? CreateDefaultLogManager();
|
||||
_configManager = configManager ?? CreateDefaultConfigManager();
|
||||
_performanceMonitor = performanceMonitor ?? CreateDefaultPerformanceMonitor();
|
||||
_modelSplitterManager = modelSplitterManager ?? CreateDefaultModelSplitterManager();
|
||||
|
||||
// 初始化集合
|
||||
LogLevels = new ThreadSafeObservableCollection<string>();
|
||||
AvailableProfiles = new ThreadSafeObservableCollection<string>();
|
||||
|
||||
// 初始化命令
|
||||
InitializeCommands();
|
||||
|
||||
// 初始化系统管理设置(异步)
|
||||
InitializeSystemManagementAsync();
|
||||
|
||||
LogManager.Info("SystemManagementViewModel构造函数执行完成");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"SystemManagementViewModel构造函数异常: {ex.Message}", ex);
|
||||
SystemStatus = "初始化失败,请检查日志";
|
||||
SystemStatusColor = "Red";
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 初始化方法
|
||||
|
||||
/// <summary>
|
||||
/// 初始化命令
|
||||
/// </summary>
|
||||
private void InitializeCommands()
|
||||
{
|
||||
// 日志管理命令
|
||||
ViewLogCommand = new RelayCommand(async () => await ExecuteViewLogAsync());
|
||||
ClearLogCommand = new RelayCommand(async () => await ExecuteClearLogAsync());
|
||||
ExportLogCommand = new RelayCommand(async () => await ExecuteExportLogAsync());
|
||||
RefreshLogStatusCommand = new RelayCommand(async () => await ExecuteRefreshLogStatusAsync());
|
||||
|
||||
// 设置管理命令
|
||||
OpenSettingsCommand = new RelayCommand(async () => await ExecuteOpenSettingsAsync());
|
||||
ResetSettingsCommand = new RelayCommand(async () => await ExecuteResetSettingsAsync());
|
||||
SaveSettingsCommand = new RelayCommand(async () => await ExecuteSaveSettingsAsync());
|
||||
LoadSettingsCommand = new RelayCommand(async () => await ExecuteLoadSettingsAsync());
|
||||
|
||||
// 配置管理命令
|
||||
ImportConfigCommand = new RelayCommand(async () => await ExecuteImportConfigAsync());
|
||||
ExportConfigCommand = new RelayCommand(async () => await ExecuteExportConfigAsync());
|
||||
CreateProfileCommand = new RelayCommand(async () => await ExecuteCreateProfileAsync());
|
||||
DeleteProfileCommand = new RelayCommand(async () => await ExecuteDeleteProfileAsync());
|
||||
SwitchProfileCommand = new RelayCommand<string>(async (profile) => await ExecuteSwitchProfileAsync(profile));
|
||||
|
||||
// 系统管理命令
|
||||
CheckUpdateCommand = new RelayCommand(async () => await ExecuteCheckUpdateAsync());
|
||||
RestartSystemCommand = new RelayCommand(async () => await ExecuteRestartSystemAsync());
|
||||
SystemDiagnosticsCommand = new RelayCommand(async () => await ExecuteSystemDiagnosticsAsync());
|
||||
|
||||
// 性能管理命令
|
||||
GeneratePerformanceReportCommand = new RelayCommand(async () => await ExecuteGeneratePerformanceReportAsync());
|
||||
StartPerformanceMonitoringCommand = new RelayCommand(async () => await ExecuteStartPerformanceMonitoringAsync());
|
||||
StopPerformanceMonitoringCommand = new RelayCommand(async () => await ExecuteStopPerformanceMonitoringAsync());
|
||||
ClearPerformanceDataCommand = new RelayCommand(async () => await ExecuteClearPerformanceDataAsync());
|
||||
|
||||
// 模型分割器命令
|
||||
RunModelSplitterCommand = new RelayCommand(async () => await ExecuteRunModelSplitterAsync(), () => CanRunModelSplitter);
|
||||
ConfigureModelSplitterCommand = new RelayCommand(async () => await ExecuteConfigureModelSplitterAsync());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步初始化系统管理设置
|
||||
/// </summary>
|
||||
private async void InitializeSystemManagementAsync()
|
||||
{
|
||||
await SafeExecuteAsync(async () =>
|
||||
{
|
||||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||||
{
|
||||
// 初始化日志级别
|
||||
LogLevels.Clear();
|
||||
var logLevels = new[] { "Debug", "Info", "Warning", "Error" };
|
||||
foreach (var level in logLevels)
|
||||
{
|
||||
LogLevels.Add(level);
|
||||
}
|
||||
SelectedLogLevel = "Info";
|
||||
|
||||
// 初始化配置档案
|
||||
AvailableProfiles.Clear();
|
||||
var profiles = new[] { "默认配置", "开发配置", "生产配置", "测试配置" };
|
||||
foreach (var profile in profiles)
|
||||
{
|
||||
AvailableProfiles.Add(profile);
|
||||
}
|
||||
CurrentConfigProfile = "默认配置";
|
||||
|
||||
// 初始化系统信息
|
||||
PluginVersion = GetPluginVersion();
|
||||
NavisworksVersion = GetNavisworksVersion();
|
||||
SystemStatus = "正常";
|
||||
SystemStatusColor = "Green";
|
||||
WorkingDirectory = GetWorkingDirectory();
|
||||
LogFilePath = GetLogFilePath();
|
||||
});
|
||||
|
||||
// 启动性能监控
|
||||
await StartPerformanceMonitoring();
|
||||
|
||||
LogManager.Info("系统管理设置初始化完成");
|
||||
}, "初始化系统管理设置");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 命令实现
|
||||
|
||||
// 这里只展示架构框架,具体实现方法将在后续详细说明
|
||||
|
||||
#region 日志管理命令实现
|
||||
|
||||
private async Task ExecuteViewLogAsync()
|
||||
{
|
||||
await SafeExecuteAsync(async () =>
|
||||
{
|
||||
// TODO: 实现日志查看功能
|
||||
await _logManager.OpenLogViewerAsync();
|
||||
LogStatus = "日志查看器已打开";
|
||||
}, "查看日志");
|
||||
}
|
||||
|
||||
private async Task ExecuteClearLogAsync()
|
||||
{
|
||||
await SafeExecuteAsync(async () =>
|
||||
{
|
||||
// TODO: 实现日志清空功能
|
||||
await _logManager.ClearLogsAsync();
|
||||
LogEntryCount = 0;
|
||||
LogStatus = "日志已清空";
|
||||
}, "清空日志");
|
||||
}
|
||||
|
||||
private async Task ExecuteExportLogAsync()
|
||||
{
|
||||
await SafeExecuteAsync(async () =>
|
||||
{
|
||||
// TODO: 实现日志导出功能
|
||||
var exportPath = await _logManager.ExportLogsAsync();
|
||||
LogStatus = $"日志已导出到: {exportPath}";
|
||||
}, "导出日志");
|
||||
}
|
||||
|
||||
private async Task ExecuteRefreshLogStatusAsync()
|
||||
{
|
||||
await SafeExecuteAsync(async () =>
|
||||
{
|
||||
// TODO: 实现日志状态刷新功能
|
||||
var status = await _logManager.GetLogStatusAsync();
|
||||
LogStatus = status.Message;
|
||||
LogEntryCount = status.EntryCount;
|
||||
LogFilePath = status.FilePath;
|
||||
}, "刷新日志状态");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// 其他命令实现方法...
|
||||
// (这里省略具体实现,在实际代码中会完整实现)
|
||||
|
||||
#endregion
|
||||
|
||||
#region 事件处理
|
||||
|
||||
/// <summary>
|
||||
/// 处理日志级别改变事件
|
||||
/// </summary>
|
||||
private void OnLogLevelChanged()
|
||||
{
|
||||
SafeExecute(() =>
|
||||
{
|
||||
_logManager?.SetLogLevel(SelectedLogLevel);
|
||||
LogStatus = $"日志级别已设置为: {SelectedLogLevel}";
|
||||
}, "设置日志级别");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 处理自动保存设置改变事件
|
||||
/// </summary>
|
||||
private void OnAutoSaveSettingChanged()
|
||||
{
|
||||
SafeExecute(() =>
|
||||
{
|
||||
_configManager?.SetAutoSaveEnabled(IsAutoSaveEnabled);
|
||||
SettingsStatus = $"自动保存: {(IsAutoSaveEnabled ? "已启用" : "已禁用")}";
|
||||
HasUnsavedChanges = true;
|
||||
}, "设置自动保存");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 处理调试模式改变事件
|
||||
/// </summary>
|
||||
private void OnDebugModeChanged()
|
||||
{
|
||||
SafeExecute(() =>
|
||||
{
|
||||
_configManager?.SetDebugMode(IsDebugModeEnabled);
|
||||
SettingsStatus = $"调试模式: {(IsDebugModeEnabled ? "已启用" : "已禁用")}";
|
||||
HasUnsavedChanges = true;
|
||||
}, "设置调试模式");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 处理自动保存间隔改变事件
|
||||
/// </summary>
|
||||
private void OnAutoSaveIntervalChanged()
|
||||
{
|
||||
SafeExecute(() =>
|
||||
{
|
||||
_configManager?.SetAutoSaveInterval(AutoSaveInterval);
|
||||
SettingsStatus = $"自动保存间隔: {AutoSaveInterval}秒";
|
||||
HasUnsavedChanges = true;
|
||||
}, "设置自动保存间隔");
|
||||
}
|
||||
|
||||
#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);
|
||||
SystemStatus = $"{operationName}失败: {ex.Message}";
|
||||
SystemStatusColor = "Red";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 安全执行同步操作
|
||||
/// </summary>
|
||||
private void SafeExecute(Action action, string operationName = "未知操作")
|
||||
{
|
||||
try
|
||||
{
|
||||
action();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"{operationName}发生异常: {ex.Message}", ex);
|
||||
SystemStatus = $"{operationName}失败: {ex.Message}";
|
||||
SystemStatusColor = "Red";
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 性能监控
|
||||
|
||||
/// <summary>
|
||||
/// 启动性能监控
|
||||
/// </summary>
|
||||
private async Task StartPerformanceMonitoring()
|
||||
{
|
||||
await SafeExecuteAsync(async () =>
|
||||
{
|
||||
var startTime = DateTime.Now;
|
||||
|
||||
// 使用定时器更新性能信息
|
||||
_performanceTimer = new DispatcherTimer();
|
||||
_performanceTimer.Interval = TimeSpan.FromSeconds(5);
|
||||
_performanceTimer.Tick += async (s, e) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await UpdatePerformanceInfo(startTime);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"性能监控更新失败: {ex.Message}");
|
||||
}
|
||||
};
|
||||
_performanceTimer.Start();
|
||||
|
||||
PerformanceInfo = "性能监控已启动";
|
||||
}, "启动性能监控");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新性能信息
|
||||
/// </summary>
|
||||
private async Task UpdatePerformanceInfo(DateTime startTime)
|
||||
{
|
||||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||||
{
|
||||
// 更新内存使用
|
||||
var process = Process.GetCurrentProcess();
|
||||
var memoryMB = process.WorkingSet64 / (1024 * 1024);
|
||||
MemoryUsage = $"{memoryMB} MB";
|
||||
|
||||
// 更新运行时间
|
||||
var runTime = DateTime.Now - startTime;
|
||||
RunningTime = $"{runTime.Hours:D2}:{runTime.Minutes:D2}:{runTime.Seconds:D2}";
|
||||
|
||||
// 更新线程数
|
||||
ThreadsCount = process.Threads.Count;
|
||||
|
||||
// 更新性能信息
|
||||
PerformanceInfo = $"CPU: 正常, 内存: {memoryMB}MB, 线程: {ThreadsCount}, 运行时间: {RunningTime}";
|
||||
});
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 默认实现创建方法
|
||||
|
||||
private ISystemLogManager CreateDefaultLogManager()
|
||||
{
|
||||
// TODO: 创建默认日志管理器实现
|
||||
return new DefaultSystemLogManager();
|
||||
}
|
||||
|
||||
private ISystemConfigManager CreateDefaultConfigManager()
|
||||
{
|
||||
// TODO: 创建默认配置管理器实现
|
||||
return new DefaultSystemConfigManager();
|
||||
}
|
||||
|
||||
private IPerformanceMonitor CreateDefaultPerformanceMonitor()
|
||||
{
|
||||
// TODO: 创建默认性能监控器实现
|
||||
return new DefaultPerformanceMonitor();
|
||||
}
|
||||
|
||||
private IModelSplitterManager CreateDefaultModelSplitterManager()
|
||||
{
|
||||
// TODO: 创建默认模型分割器管理器实现
|
||||
return new DefaultModelSplitterManager();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 系统信息获取方法
|
||||
|
||||
private string GetPluginVersion()
|
||||
{
|
||||
// TODO: 从程序集获取插件版本
|
||||
return "v1.0";
|
||||
}
|
||||
|
||||
private string GetNavisworksVersion()
|
||||
{
|
||||
// TODO: 从Navisworks API获取版本信息
|
||||
return "2026";
|
||||
}
|
||||
|
||||
private string GetWorkingDirectory()
|
||||
{
|
||||
// TODO: 获取当前工作目录
|
||||
return Environment.CurrentDirectory;
|
||||
}
|
||||
|
||||
private string GetLogFilePath()
|
||||
{
|
||||
// TODO: 获取日志文件路径
|
||||
return Path.Combine(GetWorkingDirectory(), "Logs", "NavisworksTransport.log");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 资源清理
|
||||
|
||||
/// <summary>
|
||||
/// 清理资源
|
||||
/// </summary>
|
||||
public void Cleanup()
|
||||
{
|
||||
try
|
||||
{
|
||||
LogManager.Info("开始清理SystemManagementViewModel资源");
|
||||
|
||||
// 停止性能监控定时器
|
||||
if (_performanceTimer != null)
|
||||
{
|
||||
_performanceTimer.Stop();
|
||||
_performanceTimer = null;
|
||||
}
|
||||
|
||||
// 清理管理器资源
|
||||
_logManager?.Dispose();
|
||||
_configManager?.Dispose();
|
||||
_performanceMonitor?.Dispose();
|
||||
_modelSplitterManager?.Dispose();
|
||||
|
||||
LogManager.Info("SystemManagementViewModel资源清理完成");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"SystemManagementViewModel清理失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
// 接口定义(这些接口将在单独的文件中定义)
|
||||
|
||||
/// <summary>
|
||||
/// 系统日志管理器接口
|
||||
/// </summary>
|
||||
public interface ISystemLogManager : IDisposable
|
||||
{
|
||||
Task OpenLogViewerAsync();
|
||||
Task ClearLogsAsync();
|
||||
Task<string> ExportLogsAsync();
|
||||
Task<LogStatus> GetLogStatusAsync();
|
||||
void SetLogLevel(string level);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 系统配置管理器接口
|
||||
/// </summary>
|
||||
public interface ISystemConfigManager : IDisposable
|
||||
{
|
||||
void SetAutoSaveEnabled(bool enabled);
|
||||
void SetDebugMode(bool enabled);
|
||||
void SetAutoSaveInterval(int seconds);
|
||||
Task SaveConfigAsync();
|
||||
Task LoadConfigAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 性能监控器接口
|
||||
/// </summary>
|
||||
public interface IPerformanceMonitor : IDisposable
|
||||
{
|
||||
Task<PerformanceData> GetCurrentPerformanceAsync();
|
||||
Task StartMonitoringAsync();
|
||||
Task StopMonitoringAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 模型分割器管理器接口
|
||||
/// </summary>
|
||||
public interface IModelSplitterManager : IDisposable
|
||||
{
|
||||
Task<bool> RunModelSplitterAsync();
|
||||
Task ConfigureAsync();
|
||||
bool CanRun { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 日志状态数据结构
|
||||
/// </summary>
|
||||
public class LogStatus
|
||||
{
|
||||
public string Message { get; set; }
|
||||
public int EntryCount { get; set; }
|
||||
public string FilePath { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 性能数据结构
|
||||
/// </summary>
|
||||
public class PerformanceData
|
||||
{
|
||||
public double CpuUsage { get; set; }
|
||||
public long MemoryUsage { get; set; }
|
||||
public int ThreadCount { get; set; }
|
||||
public TimeSpan RunningTime { get; set; }
|
||||
}
|
||||
@ -1,457 +0,0 @@
|
||||
# SystemManagementViewModel 集成方案
|
||||
|
||||
## 1. 与LogisticsControlViewModel的解耦策略
|
||||
|
||||
### 1.1 依赖关系处理
|
||||
|
||||
**原始依赖关系问题:**
|
||||
- LogisticsControlViewModel 直接包含所有系统管理代码
|
||||
- UI更新、状态管理、业务逻辑混合在一起
|
||||
- 违反单一职责原则
|
||||
|
||||
**解耦方案:**
|
||||
|
||||
```csharp
|
||||
// 在LogisticsControlViewModel中
|
||||
public class LogisticsControlViewModel : ViewModelBase
|
||||
{
|
||||
// 移除所有系统管理相关的字段和属性
|
||||
// private string _modelSplitterStatus; // 删除
|
||||
// private ObservableCollection<string> _logLevels; // 删除
|
||||
// ... 其他系统管理相关代码
|
||||
|
||||
// 添加SystemManagementViewModel的引用
|
||||
private SystemManagementViewModel _systemManagementViewModel;
|
||||
|
||||
public SystemManagementViewModel SystemManagement
|
||||
{
|
||||
get => _systemManagementViewModel;
|
||||
private set => SetProperty(ref _systemManagementViewModel, value);
|
||||
}
|
||||
|
||||
public LogisticsControlViewModel() : base()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 原有初始化代码...
|
||||
|
||||
// 初始化系统管理ViewModel
|
||||
SystemManagement = new SystemManagementViewModel();
|
||||
|
||||
// 订阅系统管理事件(如果需要)
|
||||
SubscribeToSystemManagementEvents();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 错误处理
|
||||
}
|
||||
}
|
||||
|
||||
// 移除所有系统管理相关的方法
|
||||
// private async Task InitializeSystemManagementSettingsAsync() // 删除
|
||||
// private void ExecuteClearLog() // 删除
|
||||
// private void ExecuteExportLog() // 删除
|
||||
// ... 其他系统管理方法
|
||||
}
|
||||
```
|
||||
|
||||
### 1.2 事件通信机制
|
||||
|
||||
```csharp
|
||||
// 定义系统管理事件接口
|
||||
public interface ISystemManagementEvents
|
||||
{
|
||||
event EventHandler<SystemStatusChangedEventArgs> SystemStatusChanged;
|
||||
event EventHandler<PerformanceAlertEventArgs> PerformanceAlert;
|
||||
event EventHandler<ConfigurationChangedEventArgs> ConfigurationChanged;
|
||||
}
|
||||
|
||||
// 在SystemManagementViewModel中实现事件发布
|
||||
public class SystemManagementViewModel : ViewModelBase, ISystemManagementEvents
|
||||
{
|
||||
public event EventHandler<SystemStatusChangedEventArgs> SystemStatusChanged;
|
||||
public event EventHandler<PerformanceAlertEventArgs> PerformanceAlert;
|
||||
public event EventHandler<ConfigurationChangedEventArgs> ConfigurationChanged;
|
||||
|
||||
// 触发系统状态变更事件
|
||||
private void OnSystemStatusChanged(string newStatus, string color)
|
||||
{
|
||||
SystemStatusChanged?.Invoke(this, new SystemStatusChangedEventArgs
|
||||
{
|
||||
Status = newStatus,
|
||||
StatusColor = color,
|
||||
Timestamp = DateTime.Now
|
||||
});
|
||||
}
|
||||
|
||||
// 触发性能警告事件
|
||||
private void OnPerformanceAlert(PerformanceAlertType alertType, string message)
|
||||
{
|
||||
PerformanceAlert?.Invoke(this, new PerformanceAlertEventArgs
|
||||
{
|
||||
AlertType = alertType,
|
||||
Message = message,
|
||||
Timestamp = DateTime.Now
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 在LogisticsControlViewModel中订阅事件
|
||||
private void SubscribeToSystemManagementEvents()
|
||||
{
|
||||
if (SystemManagement != null)
|
||||
{
|
||||
SystemManagement.SystemStatusChanged += OnSystemStatusChanged;
|
||||
SystemManagement.PerformanceAlert += OnPerformanceAlert;
|
||||
SystemManagement.ConfigurationChanged += OnConfigurationChanged;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSystemStatusChanged(object sender, SystemStatusChangedEventArgs e)
|
||||
{
|
||||
// 处理系统状态变更(如果主ViewModel需要知道)
|
||||
// 例如:更新主界面的状态指示器
|
||||
StatusText = $"系统状态: {e.Status}";
|
||||
}
|
||||
```
|
||||
|
||||
## 2. UI集成策略
|
||||
|
||||
### 2.1 XAML绑定方案
|
||||
|
||||
```xml
|
||||
<!-- 原始的SystemManagementView.xaml -->
|
||||
<!-- 通过DataContext绑定到LogisticsControlViewModel.SystemManagement -->
|
||||
<UserControl x:Class="NavisworksTransport.UI.WPF.Views.SystemManagementView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
|
||||
<!-- 原有的UI控件绑定路径需要调整 -->
|
||||
|
||||
<!-- 原来: -->
|
||||
<!-- <ComboBox ItemsSource="{Binding LogLevels}" SelectedItem="{Binding SelectedLogLevel}" /> -->
|
||||
|
||||
<!-- 现在: -->
|
||||
<!-- 通过父级LogisticsControlViewModel的SystemManagement属性访问 -->
|
||||
<ComboBox ItemsSource="{Binding SystemManagement.LogLevels}"
|
||||
SelectedItem="{Binding SystemManagement.SelectedLogLevel}" />
|
||||
|
||||
<Button Content="查看日志" Command="{Binding SystemManagement.ViewLogCommand}" />
|
||||
<Button Content="清空日志" Command="{Binding SystemManagement.ClearLogCommand}" />
|
||||
<Button Content="导出日志" Command="{Binding SystemManagement.ExportLogCommand}" />
|
||||
|
||||
<!-- 性能监控控件 -->
|
||||
<TextBlock Text="{Binding SystemManagement.MemoryUsage}" />
|
||||
<TextBlock Text="{Binding SystemManagement.RunningTime}" />
|
||||
<TextBlock Text="{Binding SystemManagement.PerformanceInfo}" />
|
||||
|
||||
<!-- 系统状态控件 -->
|
||||
<TextBlock Text="{Binding SystemManagement.SystemStatus}"
|
||||
Foreground="{Binding SystemManagement.SystemStatusColor}" />
|
||||
</UserControl>
|
||||
```
|
||||
|
||||
### 2.2 ViewModelLocator调整
|
||||
|
||||
```csharp
|
||||
// 在ViewModelLocator中注册SystemManagementViewModel
|
||||
public class ViewModelLocator
|
||||
{
|
||||
public LogisticsControlViewModel LogisticsControl => ServiceLocator.Current.GetInstance<LogisticsControlViewModel>();
|
||||
|
||||
// 可以选择直接注册SystemManagementViewModel
|
||||
public SystemManagementViewModel SystemManagement => ServiceLocator.Current.GetInstance<SystemManagementViewModel>();
|
||||
|
||||
static ViewModelLocator()
|
||||
{
|
||||
// 注册服务
|
||||
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
|
||||
|
||||
// 注册SystemManagementViewModel及其依赖
|
||||
SimpleIoc.Default.Register<ISystemLogManager, DefaultSystemLogManager>();
|
||||
SimpleIoc.Default.Register<ISystemConfigManager, DefaultSystemConfigManager>();
|
||||
SimpleIoc.Default.Register<IPerformanceMonitor, DefaultPerformanceMonitor>();
|
||||
SimpleIoc.Default.Register<IModelSplitterManager, DefaultModelSplitterManager>();
|
||||
|
||||
SimpleIoc.Default.Register<SystemManagementViewModel>();
|
||||
SimpleIoc.Default.Register<LogisticsControlViewModel>();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 3. 初始化和生命周期管理
|
||||
|
||||
### 3.1 初始化顺序
|
||||
|
||||
```csharp
|
||||
public class LogisticsControlViewModel : ViewModelBase
|
||||
{
|
||||
private async void InitializeViewModelAsync()
|
||||
{
|
||||
await SafeExecuteAsync(async () =>
|
||||
{
|
||||
// 1. 先初始化核心组件
|
||||
await UpdateInstructionTextAsync();
|
||||
await UpdateSelectionDisplayAsync();
|
||||
await InitializeCategoriesAsync();
|
||||
|
||||
// 2. 初始化系统管理ViewModel
|
||||
await InitializeSystemManagementAsync();
|
||||
|
||||
// 3. 最后更新UI状态
|
||||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||||
{
|
||||
StatusText = "插件已就绪";
|
||||
AnimationStatus = "动画状态: 就绪";
|
||||
AnimationProgress = 0;
|
||||
WidthLimit = 3.0;
|
||||
});
|
||||
|
||||
// 4. 初始化动画设置
|
||||
await InitializeAnimationSettingsAsync();
|
||||
|
||||
LogManager.Info("LogisticsControlViewModel 初始化完成");
|
||||
}, "初始化ViewModel");
|
||||
}
|
||||
|
||||
private async Task InitializeSystemManagementAsync()
|
||||
{
|
||||
await SafeExecuteAsync(async () =>
|
||||
{
|
||||
// 创建SystemManagementViewModel实例
|
||||
SystemManagement = new SystemManagementViewModel(
|
||||
logManager: ServiceProvider.GetService<ISystemLogManager>(),
|
||||
configManager: ServiceProvider.GetService<ISystemConfigManager>(),
|
||||
performanceMonitor: ServiceProvider.GetService<IPerformanceMonitor>(),
|
||||
modelSplitterManager: ServiceProvider.GetService<IModelSplitterManager>()
|
||||
);
|
||||
|
||||
// 订阅系统管理事件
|
||||
SubscribeToSystemManagementEvents();
|
||||
|
||||
LogManager.Info("系统管理ViewModel初始化完成");
|
||||
}, "初始化系统管理");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 清理机制
|
||||
|
||||
```csharp
|
||||
public class LogisticsControlViewModel : ViewModelBase
|
||||
{
|
||||
// 添加清理方法
|
||||
public void Cleanup()
|
||||
{
|
||||
try
|
||||
{
|
||||
LogManager.Info("开始清理LogisticsControlViewModel资源");
|
||||
|
||||
// 清理系统管理ViewModel
|
||||
if (SystemManagement != null)
|
||||
{
|
||||
UnsubscribeFromSystemManagementEvents();
|
||||
SystemManagement.Cleanup();
|
||||
SystemManagement = null;
|
||||
}
|
||||
|
||||
// 原有的清理代码...
|
||||
|
||||
LogManager.Info("LogisticsControlViewModel资源清理完成");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"LogisticsControlViewModel清理失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void UnsubscribeFromSystemManagementEvents()
|
||||
{
|
||||
if (SystemManagement != null)
|
||||
{
|
||||
SystemManagement.SystemStatusChanged -= OnSystemStatusChanged;
|
||||
SystemManagement.PerformanceAlert -= OnPerformanceAlert;
|
||||
SystemManagement.ConfigurationChanged -= OnConfigurationChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 4. 命令重定向机制
|
||||
|
||||
### 4.1 保持向后兼容性
|
||||
|
||||
```csharp
|
||||
public class LogisticsControlViewModel : ViewModelBase
|
||||
{
|
||||
// 保持原有命令接口,但重定向到SystemManagementViewModel
|
||||
public ICommand ViewLogCommand => SystemManagement?.ViewLogCommand;
|
||||
public ICommand ClearLogCommand => SystemManagement?.ClearLogCommand;
|
||||
public ICommand ExportLogCommand => SystemManagement?.ExportLogCommand;
|
||||
public ICommand OpenSettingsCommand => SystemManagement?.OpenSettingsCommand;
|
||||
public ICommand ResetSettingsCommand => SystemManagement?.ResetSettingsCommand;
|
||||
public ICommand ImportConfigCommand => SystemManagement?.ImportConfigCommand;
|
||||
public ICommand CheckUpdateCommand => SystemManagement?.CheckUpdateCommand;
|
||||
public ICommand GeneratePerformanceReportCommand => SystemManagement?.GeneratePerformanceReportCommand;
|
||||
|
||||
// 或者使用代理模式
|
||||
private void InitializeCommandProxies()
|
||||
{
|
||||
ViewLogCommand = new RelayCommand(async () =>
|
||||
{
|
||||
if (SystemManagement != null)
|
||||
await SystemManagement.ViewLogCommand.ExecuteAsync(null);
|
||||
});
|
||||
|
||||
ClearLogCommand = new RelayCommand(async () =>
|
||||
{
|
||||
if (SystemManagement != null)
|
||||
await SystemManagement.ClearLogCommand.ExecuteAsync(null);
|
||||
});
|
||||
|
||||
// ... 其他命令代理
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 5. 测试策略
|
||||
|
||||
### 5.1 单元测试
|
||||
|
||||
```csharp
|
||||
[TestFixture]
|
||||
public class SystemManagementViewModelTests
|
||||
{
|
||||
private SystemManagementViewModel _viewModel;
|
||||
private Mock<ISystemLogManager> _mockLogManager;
|
||||
private Mock<ISystemConfigManager> _mockConfigManager;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_mockLogManager = new Mock<ISystemLogManager>();
|
||||
_mockConfigManager = new Mock<ISystemConfigManager>();
|
||||
|
||||
_viewModel = new SystemManagementViewModel(
|
||||
_mockLogManager.Object,
|
||||
_mockConfigManager.Object
|
||||
);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task ExecuteViewLogAsync_ShouldCallLogManager()
|
||||
{
|
||||
// Arrange
|
||||
_mockLogManager.Setup(x => x.OpenLogViewerAsync()).Returns(Task.CompletedTask);
|
||||
|
||||
// Act
|
||||
await _viewModel.ViewLogCommand.ExecuteAsync(null);
|
||||
|
||||
// Assert
|
||||
_mockLogManager.Verify(x => x.OpenLogViewerAsync(), Times.Once);
|
||||
Assert.AreEqual("日志查看器已打开", _viewModel.LogStatus);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5.2 集成测试
|
||||
|
||||
```csharp
|
||||
[TestFixture]
|
||||
public class SystemManagementIntegrationTests
|
||||
{
|
||||
[Test]
|
||||
public void LogisticsControlViewModel_ShouldInitializeSystemManagement()
|
||||
{
|
||||
// Arrange & Act
|
||||
var logisticsVM = new LogisticsControlViewModel();
|
||||
|
||||
// Assert
|
||||
Assert.IsNotNull(logisticsVM.SystemManagement);
|
||||
Assert.IsInstanceOf<SystemManagementViewModel>(logisticsVM.SystemManagement);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SystemManagementEvents_ShouldPropagateToMainViewModel()
|
||||
{
|
||||
// Arrange
|
||||
var logisticsVM = new LogisticsControlViewModel();
|
||||
var systemManagementVM = logisticsVM.SystemManagement;
|
||||
string receivedStatus = null;
|
||||
|
||||
logisticsVM.PropertyChanged += (s, e) =>
|
||||
{
|
||||
if (e.PropertyName == nameof(LogisticsControlViewModel.StatusText))
|
||||
receivedStatus = logisticsVM.StatusText;
|
||||
};
|
||||
|
||||
// Act
|
||||
systemManagementVM.SystemStatus = "测试状态";
|
||||
// 触发SystemStatusChanged事件
|
||||
|
||||
// Assert
|
||||
Assert.IsTrue(receivedStatus?.Contains("测试状态"));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 6. 迁移计划
|
||||
|
||||
### 6.1 分阶段迁移
|
||||
|
||||
**阶段1:创建基础架构**
|
||||
1. 创建SystemManagementViewModel类
|
||||
2. 定义所需接口
|
||||
3. 创建默认实现类
|
||||
4. 设置依赖注入
|
||||
|
||||
**阶段2:迁移核心功能**
|
||||
1. 迁移日志管理功能
|
||||
2. 迁移设置管理功能
|
||||
3. 迁移性能监控功能
|
||||
4. 测试基本功能
|
||||
|
||||
**阶段3:集成和优化**
|
||||
1. 集成到LogisticsControlViewModel
|
||||
2. 调整UI绑定
|
||||
3. 实现事件通信
|
||||
4. 性能优化
|
||||
|
||||
**阶段4:测试和部署**
|
||||
1. 全面测试
|
||||
2. 修复问题
|
||||
3. 文档更新
|
||||
4. 部署验证
|
||||
|
||||
### 6.2 兼容性保证
|
||||
|
||||
```csharp
|
||||
// 在迁移期间,保持旧接口可用
|
||||
public class LogisticsControlViewModel : ViewModelBase
|
||||
{
|
||||
// 新的系统管理ViewModel
|
||||
private SystemManagementViewModel _systemManagement;
|
||||
|
||||
// 保持旧属性兼容性(标记为过时)
|
||||
[Obsolete("请使用SystemManagement.LogLevels", false)]
|
||||
public ObservableCollection<string> LogLevels => SystemManagement?.LogLevels;
|
||||
|
||||
[Obsolete("请使用SystemManagement.SelectedLogLevel", false)]
|
||||
public string SelectedLogLevel
|
||||
{
|
||||
get => SystemManagement?.SelectedLogLevel;
|
||||
set { if (SystemManagement != null) SystemManagement.SelectedLogLevel = value; }
|
||||
}
|
||||
|
||||
// 保持旧命令兼容性
|
||||
[Obsolete("请使用SystemManagement.ViewLogCommand", false)]
|
||||
public ICommand ViewLogCommand => SystemManagement?.ViewLogCommand;
|
||||
}
|
||||
```
|
||||
|
||||
这个解耦方案确保了:
|
||||
1. **清晰的职责分离**:系统管理功能完全独立
|
||||
2. **向后兼容性**:现有UI和代码不会立即失效
|
||||
3. **渐进式迁移**:可以分步骤进行迁移
|
||||
4. **良好的测试性**:每个组件都可以独立测试
|
||||
5. **事件驱动通信**:组件间通过事件解耦
|
||||
@ -1,744 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
|
||||
namespace NavisworksTransport.Core.SystemManagement
|
||||
{
|
||||
#region 事件参数定义
|
||||
|
||||
/// <summary>
|
||||
/// 系统状态变更事件参数
|
||||
/// </summary>
|
||||
public class SystemStatusChangedEventArgs : EventArgs
|
||||
{
|
||||
public string Status { get; set; }
|
||||
public string StatusColor { get; set; }
|
||||
public DateTime Timestamp { get; set; }
|
||||
public string Details { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 性能警告事件参数
|
||||
/// </summary>
|
||||
public class PerformanceAlertEventArgs : EventArgs
|
||||
{
|
||||
public PerformanceAlertType AlertType { get; set; }
|
||||
public string Message { get; set; }
|
||||
public DateTime Timestamp { get; set; }
|
||||
public double Value { get; set; }
|
||||
public double Threshold { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 配置变更事件参数
|
||||
/// </summary>
|
||||
public class ConfigurationChangedEventArgs : EventArgs
|
||||
{
|
||||
public string ConfigKey { get; set; }
|
||||
public object OldValue { get; set; }
|
||||
public object NewValue { get; set; }
|
||||
public string ProfileName { get; set; }
|
||||
public DateTime Timestamp { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 性能警告类型枚举
|
||||
/// </summary>
|
||||
public enum PerformanceAlertType
|
||||
{
|
||||
MemoryHigh,
|
||||
CpuHigh,
|
||||
ThreadCountHigh,
|
||||
ResponseTimeLow
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 数据模型
|
||||
|
||||
/// <summary>
|
||||
/// 日志状态数据模型
|
||||
/// </summary>
|
||||
public class LogStatus
|
||||
{
|
||||
public string Message { get; set; }
|
||||
public int EntryCount { get; set; }
|
||||
public string FilePath { get; set; }
|
||||
public DateTime LastUpdate { get; set; }
|
||||
public long FileSizeBytes { get; set; }
|
||||
public string CurrentLevel { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 性能数据模型
|
||||
/// </summary>
|
||||
public class PerformanceData
|
||||
{
|
||||
public double CpuUsage { get; set; }
|
||||
public long MemoryUsageMB { get; set; }
|
||||
public int ThreadCount { get; set; }
|
||||
public TimeSpan RunningTime { get; set; }
|
||||
public int HandleCount { get; set; }
|
||||
public double ResponseTimeMs { get; set; }
|
||||
public DateTime Timestamp { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 配置档案数据模型
|
||||
/// </summary>
|
||||
public class ConfigProfile
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public Dictionary<string, object> Settings { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public DateTime LastModified { get; set; }
|
||||
public bool IsDefault { get; set; }
|
||||
public string Description { get; set; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 系统日志管理器实现
|
||||
|
||||
/// <summary>
|
||||
/// 默认系统日志管理器实现
|
||||
/// </summary>
|
||||
public class DefaultSystemLogManager : ISystemLogManager
|
||||
{
|
||||
private readonly string _logDirectory;
|
||||
private readonly string _logFilePath;
|
||||
private string _currentLogLevel = "Info";
|
||||
|
||||
public DefaultSystemLogManager()
|
||||
{
|
||||
_logDirectory = Path.Combine(Environment.CurrentDirectory, "Logs");
|
||||
_logFilePath = Path.Combine(_logDirectory, "NavisworksTransport.log");
|
||||
|
||||
// 确保日志目录存在
|
||||
if (!Directory.Exists(_logDirectory))
|
||||
{
|
||||
Directory.CreateDirectory(_logDirectory);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task OpenLogViewerAsync()
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
// 检查是否存在自定义日志查看器
|
||||
var logViewerPath = Path.Combine(Environment.CurrentDirectory, "tools", "LogViewer.exe");
|
||||
|
||||
if (File.Exists(logViewerPath))
|
||||
{
|
||||
// 使用自定义日志查看器
|
||||
Process.Start(logViewerPath, $"\"{_logFilePath}\"");
|
||||
}
|
||||
else if (File.Exists(_logFilePath))
|
||||
{
|
||||
// 使用默认文本编辑器打开
|
||||
Process.Start("notepad.exe", _logFilePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox.Show("日志文件不存在", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"打开日志查看器失败: {ex.Message}", ex);
|
||||
MessageBox.Show($"打开日志查看器失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public async Task ClearLogsAsync()
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (File.Exists(_logFilePath))
|
||||
{
|
||||
// 备份当前日志文件
|
||||
var backupPath = Path.Combine(_logDirectory, $"backup_{DateTime.Now:yyyyMMdd_HHmmss}.log");
|
||||
File.Move(_logFilePath, backupPath);
|
||||
|
||||
LogManager.Info("日志已清空,备份文件已创建");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"清空日志失败: {ex.Message}", ex);
|
||||
throw;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public async Task<string> ExportLogsAsync()
|
||||
{
|
||||
return await Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var exportDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "NavisworksTransport_Logs");
|
||||
if (!Directory.Exists(exportDirectory))
|
||||
{
|
||||
Directory.CreateDirectory(exportDirectory);
|
||||
}
|
||||
|
||||
var exportFileName = $"NavisworksTransport_Log_{DateTime.Now:yyyyMMdd_HHmmss}.zip";
|
||||
var exportPath = Path.Combine(exportDirectory, exportFileName);
|
||||
|
||||
// 这里可以使用System.IO.Compression.ZipFile来创建压缩包
|
||||
// 包含主日志文件和任何相关的日志文件
|
||||
if (File.Exists(_logFilePath))
|
||||
{
|
||||
var singleLogExportPath = Path.Combine(exportDirectory, $"NavisworksTransport_Log_{DateTime.Now:yyyyMMdd_HHmmss}.log");
|
||||
File.Copy(_logFilePath, singleLogExportPath);
|
||||
return singleLogExportPath;
|
||||
}
|
||||
|
||||
return exportPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"导出日志失败: {ex.Message}", ex);
|
||||
throw;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public async Task<LogStatus> GetLogStatusAsync()
|
||||
{
|
||||
return await Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var status = new LogStatus
|
||||
{
|
||||
FilePath = _logFilePath,
|
||||
CurrentLevel = _currentLogLevel,
|
||||
LastUpdate = DateTime.Now
|
||||
};
|
||||
|
||||
if (File.Exists(_logFilePath))
|
||||
{
|
||||
var fileInfo = new FileInfo(_logFilePath);
|
||||
status.FileSizeBytes = fileInfo.Length;
|
||||
status.LastUpdate = fileInfo.LastWriteTime;
|
||||
|
||||
// 估算日志条目数量(简单实现)
|
||||
var lines = File.ReadAllLines(_logFilePath);
|
||||
status.EntryCount = lines.Length;
|
||||
status.Message = $"日志文件正常,共 {status.EntryCount} 条记录";
|
||||
}
|
||||
else
|
||||
{
|
||||
status.EntryCount = 0;
|
||||
status.FileSizeBytes = 0;
|
||||
status.Message = "日志文件不存在";
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"获取日志状态失败: {ex.Message}", ex);
|
||||
return new LogStatus
|
||||
{
|
||||
Message = $"获取日志状态失败: {ex.Message}",
|
||||
EntryCount = 0,
|
||||
FilePath = _logFilePath,
|
||||
CurrentLevel = _currentLogLevel
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void SetLogLevel(string level)
|
||||
{
|
||||
try
|
||||
{
|
||||
_currentLogLevel = level;
|
||||
// 这里需要与实际的日志系统集成
|
||||
LogManager.SetLogLevel(level);
|
||||
LogManager.Info($"日志级别已设置为: {level}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"设置日志级别失败: {ex.Message}", ex);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// 清理资源
|
||||
LogManager.Info("SystemLogManager已清理");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 系统配置管理器实现
|
||||
|
||||
/// <summary>
|
||||
/// 默认系统配置管理器实现
|
||||
/// </summary>
|
||||
public class DefaultSystemConfigManager : ISystemConfigManager
|
||||
{
|
||||
private readonly string _configDirectory;
|
||||
private readonly string _configFilePath;
|
||||
private Dictionary<string, object> _currentSettings;
|
||||
private ConfigProfile _currentProfile;
|
||||
|
||||
public event EventHandler<ConfigurationChangedEventArgs> ConfigurationChanged;
|
||||
|
||||
public DefaultSystemConfigManager()
|
||||
{
|
||||
_configDirectory = Path.Combine(Environment.CurrentDirectory, "Config");
|
||||
_configFilePath = Path.Combine(_configDirectory, "settings.json");
|
||||
_currentSettings = new Dictionary<string, object>();
|
||||
|
||||
// 确保配置目录存在
|
||||
if (!Directory.Exists(_configDirectory))
|
||||
{
|
||||
Directory.CreateDirectory(_configDirectory);
|
||||
}
|
||||
|
||||
// 加载默认配置
|
||||
LoadDefaultSettings();
|
||||
}
|
||||
|
||||
private void LoadDefaultSettings()
|
||||
{
|
||||
_currentSettings = new Dictionary<string, object>
|
||||
{
|
||||
{ "AutoSaveEnabled", true },
|
||||
{ "AutoSaveInterval", 300 },
|
||||
{ "DebugModeEnabled", false },
|
||||
{ "LogLevel", "Info" },
|
||||
{ "WorkingDirectory", Environment.CurrentDirectory },
|
||||
{ "MaxMemoryUsageMB", 1024 },
|
||||
{ "PerformanceMonitoringEnabled", true }
|
||||
};
|
||||
|
||||
_currentProfile = new ConfigProfile
|
||||
{
|
||||
Name = "默认配置",
|
||||
Settings = new Dictionary<string, object>(_currentSettings),
|
||||
CreatedAt = DateTime.Now,
|
||||
LastModified = DateTime.Now,
|
||||
IsDefault = true,
|
||||
Description = "系统默认配置档案"
|
||||
};
|
||||
}
|
||||
|
||||
public void SetAutoSaveEnabled(bool enabled)
|
||||
{
|
||||
SetSetting("AutoSaveEnabled", enabled);
|
||||
}
|
||||
|
||||
public void SetDebugMode(bool enabled)
|
||||
{
|
||||
SetSetting("DebugModeEnabled", enabled);
|
||||
}
|
||||
|
||||
public void SetAutoSaveInterval(int seconds)
|
||||
{
|
||||
SetSetting("AutoSaveInterval", seconds);
|
||||
}
|
||||
|
||||
private void SetSetting(string key, object value)
|
||||
{
|
||||
try
|
||||
{
|
||||
var oldValue = _currentSettings.ContainsKey(key) ? _currentSettings[key] : null;
|
||||
_currentSettings[key] = value;
|
||||
|
||||
// 触发配置变更事件
|
||||
ConfigurationChanged?.Invoke(this, new ConfigurationChangedEventArgs
|
||||
{
|
||||
ConfigKey = key,
|
||||
OldValue = oldValue,
|
||||
NewValue = value,
|
||||
ProfileName = _currentProfile?.Name,
|
||||
Timestamp = DateTime.Now
|
||||
});
|
||||
|
||||
LogManager.Info($"配置项已更新: {key} = {value}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"设置配置项失败 {key}: {ex.Message}", ex);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task SaveConfigAsync()
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
// 这里应该实现JSON序列化保存
|
||||
// var json = JsonConvert.SerializeObject(_currentSettings, Formatting.Indented);
|
||||
// File.WriteAllText(_configFilePath, json);
|
||||
|
||||
LogManager.Info("配置已保存到文件");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"保存配置失败: {ex.Message}", ex);
|
||||
throw;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public async Task LoadConfigAsync()
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (File.Exists(_configFilePath))
|
||||
{
|
||||
// var json = File.ReadAllText(_configFilePath);
|
||||
// _currentSettings = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
|
||||
LogManager.Info("配置已从文件加载");
|
||||
}
|
||||
else
|
||||
{
|
||||
LoadDefaultSettings();
|
||||
LogManager.Info("使用默认配置");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"加载配置失败: {ex.Message}", ex);
|
||||
LoadDefaultSettings();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public T GetSetting<T>(string key, T defaultValue = default(T))
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_currentSettings.ContainsKey(key))
|
||||
{
|
||||
return (T)Convert.ChangeType(_currentSettings[key], typeof(T));
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"获取配置项失败 {key}: {ex.Message}", ex);
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 自动保存配置
|
||||
SaveConfigAsync().Wait(5000); // 等待最多5秒
|
||||
LogManager.Info("SystemConfigManager已清理");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"SystemConfigManager清理失败: {ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 性能监控器实现
|
||||
|
||||
/// <summary>
|
||||
/// 默认性能监控器实现
|
||||
/// </summary>
|
||||
public class DefaultPerformanceMonitor : IPerformanceMonitor
|
||||
{
|
||||
private readonly Process _currentProcess;
|
||||
private bool _isMonitoring;
|
||||
private readonly List<PerformanceData> _performanceHistory;
|
||||
private readonly object _lockObject = new object();
|
||||
|
||||
public event EventHandler<PerformanceAlertEventArgs> PerformanceAlert;
|
||||
|
||||
public DefaultPerformanceMonitor()
|
||||
{
|
||||
_currentProcess = Process.GetCurrentProcess();
|
||||
_performanceHistory = new List<PerformanceData>();
|
||||
_isMonitoring = false;
|
||||
}
|
||||
|
||||
public async Task<PerformanceData> GetCurrentPerformanceAsync()
|
||||
{
|
||||
return await Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
_currentProcess.Refresh();
|
||||
|
||||
var performanceData = new PerformanceData
|
||||
{
|
||||
MemoryUsageMB = _currentProcess.WorkingSet64 / (1024 * 1024),
|
||||
ThreadCount = _currentProcess.Threads.Count,
|
||||
HandleCount = _currentProcess.HandleCount,
|
||||
RunningTime = DateTime.Now - _currentProcess.StartTime,
|
||||
Timestamp = DateTime.Now,
|
||||
CpuUsage = GetCpuUsage(), // 需要实现CPU使用率获取
|
||||
ResponseTimeMs = GetResponseTime() // 需要实现响应时间测量
|
||||
};
|
||||
|
||||
// 检查性能阈值
|
||||
CheckPerformanceThresholds(performanceData);
|
||||
|
||||
lock (_lockObject)
|
||||
{
|
||||
_performanceHistory.Add(performanceData);
|
||||
|
||||
// 保持历史记录在合理范围内(最多保留1000条记录)
|
||||
if (_performanceHistory.Count > 1000)
|
||||
{
|
||||
_performanceHistory.RemoveAt(0);
|
||||
}
|
||||
}
|
||||
|
||||
return performanceData;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"获取性能数据失败: {ex.Message}", ex);
|
||||
throw;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private double GetCpuUsage()
|
||||
{
|
||||
// 简单实现,实际应该使用PerformanceCounter或更准确的方法
|
||||
try
|
||||
{
|
||||
return _currentProcess.TotalProcessorTime.TotalMilliseconds / Environment.TickCount * 100;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
private double GetResponseTime()
|
||||
{
|
||||
// 这里应该实现响应时间测量逻辑
|
||||
// 可以通过测试关键操作的执行时间来获得
|
||||
return 50.0; // 模拟值
|
||||
}
|
||||
|
||||
private void CheckPerformanceThresholds(PerformanceData data)
|
||||
{
|
||||
// 内存使用率检查(超过512MB警告)
|
||||
if (data.MemoryUsageMB > 512)
|
||||
{
|
||||
PerformanceAlert?.Invoke(this, new PerformanceAlertEventArgs
|
||||
{
|
||||
AlertType = PerformanceAlertType.MemoryHigh,
|
||||
Message = $"内存使用量较高: {data.MemoryUsageMB}MB",
|
||||
Value = data.MemoryUsageMB,
|
||||
Threshold = 512,
|
||||
Timestamp = DateTime.Now
|
||||
});
|
||||
}
|
||||
|
||||
// 线程数量检查(超过50个线程警告)
|
||||
if (data.ThreadCount > 50)
|
||||
{
|
||||
PerformanceAlert?.Invoke(this, new PerformanceAlertEventArgs
|
||||
{
|
||||
AlertType = PerformanceAlertType.ThreadCountHigh,
|
||||
Message = $"线程数量较多: {data.ThreadCount}个",
|
||||
Value = data.ThreadCount,
|
||||
Threshold = 50,
|
||||
Timestamp = DateTime.Now
|
||||
});
|
||||
}
|
||||
|
||||
// CPU使用率检查(超过80%警告)
|
||||
if (data.CpuUsage > 80)
|
||||
{
|
||||
PerformanceAlert?.Invoke(this, new PerformanceAlertEventArgs
|
||||
{
|
||||
AlertType = PerformanceAlertType.CpuHigh,
|
||||
Message = $"CPU使用率较高: {data.CpuUsage:F1}%",
|
||||
Value = data.CpuUsage,
|
||||
Threshold = 80,
|
||||
Timestamp = DateTime.Now
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public async Task StartMonitoringAsync()
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
_isMonitoring = true;
|
||||
LogManager.Info("性能监控已启动");
|
||||
});
|
||||
}
|
||||
|
||||
public async Task StopMonitoringAsync()
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
_isMonitoring = false;
|
||||
LogManager.Info("性能监控已停止");
|
||||
});
|
||||
}
|
||||
|
||||
public List<PerformanceData> GetPerformanceHistory(TimeSpan? timeRange = null)
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
if (timeRange.HasValue)
|
||||
{
|
||||
var cutoffTime = DateTime.Now - timeRange.Value;
|
||||
return _performanceHistory.Where(p => p.Timestamp >= cutoffTime).ToList();
|
||||
}
|
||||
return new List<PerformanceData>(_performanceHistory);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
try
|
||||
{
|
||||
_isMonitoring = false;
|
||||
_currentProcess?.Dispose();
|
||||
LogManager.Info("PerformanceMonitor已清理");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"PerformanceMonitor清理失败: {ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 模型分割器管理器实现
|
||||
|
||||
/// <summary>
|
||||
/// 默认模型分割器管理器实现
|
||||
/// </summary>
|
||||
public class DefaultModelSplitterManager : IModelSplitterManager
|
||||
{
|
||||
private bool _isRunning;
|
||||
private string _outputDirectory;
|
||||
|
||||
public bool CanRun => !_isRunning && HasValidModel();
|
||||
|
||||
public DefaultModelSplitterManager()
|
||||
{
|
||||
_outputDirectory = Path.Combine(Environment.CurrentDirectory, "ModelSplitter", "Output");
|
||||
if (!Directory.Exists(_outputDirectory))
|
||||
{
|
||||
Directory.CreateDirectory(_outputDirectory);
|
||||
}
|
||||
}
|
||||
|
||||
private bool HasValidModel()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 检查Navisworks中是否有已加载的模型
|
||||
var document = Autodesk.Navisworks.Api.Application.ActiveDocument;
|
||||
return document != null && document.Models != null && document.Models.Count > 0;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> RunModelSplitterAsync()
|
||||
{
|
||||
if (!CanRun)
|
||||
{
|
||||
LogManager.Warning("模型分割器无法运行:条件不满足");
|
||||
return false;
|
||||
}
|
||||
|
||||
return await Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
_isRunning = true;
|
||||
LogManager.Info("开始模型分层拆分");
|
||||
|
||||
// 这里应该实现实际的模型分割逻辑
|
||||
// 1. 获取当前Navisworks文档
|
||||
// 2. 按照楼层、区域或其他标准进行分割
|
||||
// 3. 导出各个分割后的模型
|
||||
|
||||
var document = Autodesk.Navisworks.Api.Application.ActiveDocument;
|
||||
if (document?.Models != null)
|
||||
{
|
||||
// 模拟分割过程
|
||||
System.Threading.Thread.Sleep(2000);
|
||||
|
||||
// 实际实现中,这里会调用ModelSplitterManager的分割逻辑
|
||||
// var splitterResult = ModelSplitterManager.SplitByFloors(document);
|
||||
|
||||
LogManager.Info("模型分层拆分完成");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"模型分割器运行失败: {ex.Message}", ex);
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isRunning = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public async Task ConfigureAsync()
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
// 这里可以打开模型分割器的配置对话框
|
||||
// 或者加载配置设置
|
||||
LogManager.Info("模型分割器配置界面已打开");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"配置模型分割器失败: {ex.Message}", ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_isRunning = false;
|
||||
LogManager.Info("ModelSplitterManager已清理");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@ -38,12 +38,14 @@
|
||||
### 功能保留情况
|
||||
|
||||
✅ **保留的核心功能:**
|
||||
|
||||
- 楼层分析功能:检测模型是否具备层属性
|
||||
- 设置层属性功能:手动设置层属性
|
||||
- 分层保存功能:按楼层/属性/类别/高程分层保存
|
||||
- 选中保存功能:保存选择树中的选中内容
|
||||
|
||||
❌ **移除的复杂功能:**
|
||||
|
||||
- 复杂的配置参数(保留智能默认值)
|
||||
- 按选择集分层等高级策略
|
||||
- 复杂的报告生成和元数据管理
|
||||
@ -58,6 +60,7 @@
|
||||
### UI集成验证
|
||||
|
||||
✅ **验证项目:**
|
||||
|
||||
- [x] 编译通过(./compile.bat成功)
|
||||
- [x] LayerManagementView.xaml绑定正确
|
||||
- [x] LayerManagementViewModel命令定义完整
|
||||
@ -106,11 +109,13 @@ NavisworksFileExporter (复用现有)
|
||||
### 关键类说明
|
||||
|
||||
**SimplifiedModelSplitterManager**
|
||||
|
||||
- 位置:`src/Core/SimplifiedModelSplitterManager.cs`
|
||||
- 职责:提供简化的分层管理API
|
||||
- 特点:无状态设计,线程安全,事件驱动
|
||||
|
||||
**LayerManagementViewModel**
|
||||
|
||||
- 位置:`src/UI/WPF/ViewModels/LayerManagementViewModel.cs`
|
||||
- 修改:使用SimplifiedModelSplitterManager替代原ModelSplitterManager
|
||||
- 特点:保持原有UI绑定,MVVM模式完整
|
||||
@ -126,8 +131,9 @@ NavisworksFileExporter (复用现有)
|
||||
此次重构成功实现了分层管理功能的简化,在保证4个核心功能完整性的前提下,将代码复杂度降低了33%。新的架构更加清晰,易于维护,同时完全适配了新的UI设计。
|
||||
|
||||
**重构成功标志:**
|
||||
|
||||
- ✅ 编译通过
|
||||
- ✅ 核心功能保留
|
||||
- ✅ UI集成完成
|
||||
- ✅ 代码简化达标
|
||||
- ✅ 性能优化实现
|
||||
- ✅ 性能优化实现
|
||||
|
||||
@ -316,28 +316,6 @@ namespace NavisworksTransport.Commands
|
||||
return new StartAnimationCommand(parameters);
|
||||
});
|
||||
|
||||
// 7. 注册模型分层命令
|
||||
RegisterCommand("ModelSplitter", (object[] args) =>
|
||||
{
|
||||
if (args.Length < 1)
|
||||
throw new ArgumentException("ModelSplitter 需要输出目录参数");
|
||||
|
||||
var outputDirectory = args[0]?.ToString();
|
||||
if (string.IsNullOrEmpty(outputDirectory))
|
||||
throw new ArgumentException("输出目录不能为空");
|
||||
|
||||
var config = new ModelSplitterManager.SplitConfiguration
|
||||
{
|
||||
OutputDirectory = outputDirectory,
|
||||
Strategy = args.Length > 1 && args[1] is ModelSplitterManager.SplitStrategy strategy ?
|
||||
strategy : ModelSplitterManager.SplitStrategy.ByFloor,
|
||||
AttributeName = args.Length > 2 ? args[2]?.ToString() : "Level",
|
||||
GenerateReport = args.Length <= 3 || !(args[3] is bool v) || v
|
||||
};
|
||||
|
||||
return new ModelSplitterCommand(config);
|
||||
});
|
||||
|
||||
LogManager.Info("新的业务Commands注册完成");
|
||||
}
|
||||
|
||||
|
||||
@ -1,83 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using NavisworksTransport.Core;
|
||||
using NavisworksTransport.Utils;
|
||||
|
||||
namespace NavisworksTransport.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// 模型分层Command
|
||||
/// </summary>
|
||||
public class ModelSplitterCommand : CommandBase
|
||||
{
|
||||
private readonly ModelSplitterManager.SplitConfiguration _configuration;
|
||||
private readonly ModelSplitterManager _modelSplitterManager;
|
||||
|
||||
public ModelSplitterCommand(ModelSplitterManager.SplitConfiguration configuration, ModelSplitterManager modelSplitterManager = null)
|
||||
: base("ModelSplitter", "模型分层", "执行模型分层拆分操作")
|
||||
{
|
||||
_configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
|
||||
_modelSplitterManager = modelSplitterManager ?? new ModelSplitterManager();
|
||||
}
|
||||
|
||||
protected override PathPlanningResult ValidateParameters()
|
||||
{
|
||||
if (_configuration == null)
|
||||
return PathPlanningResult.ValidationFailure("分层配置不能为空");
|
||||
|
||||
if (string.IsNullOrEmpty(_configuration.OutputDirectory))
|
||||
return PathPlanningResult.ValidationFailure("输出目录不能为空");
|
||||
|
||||
return PathPlanningResult.Success("参数验证通过");
|
||||
}
|
||||
|
||||
protected override async Task<PathPlanningResult> ExecuteInternalAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
UpdateProgress(10, "初始化模型分层...");
|
||||
|
||||
try
|
||||
{
|
||||
UpdateProgress(30, "执行分层拆分...");
|
||||
|
||||
var results = await _modelSplitterManager.ExecuteSplitAsync(_configuration);
|
||||
|
||||
UpdateProgress(100, "模型分层完成");
|
||||
|
||||
return PathPlanningResult<List<ModelSplitterManager.SplitResult>>.Success(results,
|
||||
$"模型分层完成:成功处理 {results.Count} 个分层");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogError("模型分层执行失败", ex);
|
||||
return PathPlanningResult.Failure($"模型分层失败: {ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static ModelSplitterCommand CreateByFloor(string outputDirectory, string attributeName = "Level")
|
||||
{
|
||||
var config = new ModelSplitterManager.SplitConfiguration
|
||||
{
|
||||
Strategy = ModelSplitterManager.SplitStrategy.ByFloor,
|
||||
OutputDirectory = outputDirectory,
|
||||
AttributeName = attributeName,
|
||||
GenerateReport = true
|
||||
};
|
||||
|
||||
return new ModelSplitterCommand(config);
|
||||
}
|
||||
|
||||
public static ModelSplitterCommand CreateByCategory(string outputDirectory)
|
||||
{
|
||||
var config = new ModelSplitterManager.SplitConfiguration
|
||||
{
|
||||
Strategy = ModelSplitterManager.SplitStrategy.ByCategory,
|
||||
OutputDirectory = outputDirectory,
|
||||
GenerateReport = true
|
||||
};
|
||||
|
||||
return new ModelSplitterCommand(config);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -136,7 +136,9 @@ namespace NavisworksTransport.Core.Animation
|
||||
// 检查对象是否相同
|
||||
if (!_currentCachedAnimationObject.Equals(animationObject))
|
||||
{
|
||||
LogManager.Debug($"[缓存验证] 动画对象已变更: '{_currentCachedAnimationObject.DisplayName}' -> '{animationObject.DisplayName}'");
|
||||
string cachedObjectName = GetSafeDisplayName(_currentCachedAnimationObject);
|
||||
string currentObjectName = GetSafeDisplayName(animationObject);
|
||||
LogManager.Debug($"[缓存验证] 动画对象已变更: '{cachedObjectName}' -> '{currentObjectName}'");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -165,7 +167,16 @@ namespace NavisworksTransport.Core.Animation
|
||||
{
|
||||
if (_currentCachedAnimationObject != null)
|
||||
{
|
||||
LogManager.Debug($"[缓存管理] 清除缓存: {_currentCachedAnimationObject.DisplayName}");
|
||||
try
|
||||
{
|
||||
// 安全访问DisplayName,防止WeakRef已释放的警告
|
||||
string objectName = GetSafeDisplayName(_currentCachedAnimationObject);
|
||||
LogManager.Debug($"[缓存管理] 清除缓存: {objectName}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Debug($"[缓存管理] 清除缓存时访问对象名称失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
_currentCachedAnimationObject = null;
|
||||
@ -192,7 +203,7 @@ namespace NavisworksTransport.Core.Animation
|
||||
}
|
||||
|
||||
var age = DateTime.Now - _cacheCreatedTime;
|
||||
var objectName = _currentCachedAnimationObject.DisplayName ?? "";
|
||||
string objectName = GetSafeDisplayName(_currentCachedAnimationObject);
|
||||
var count = _cachedExclusionList?.Count ?? 0;
|
||||
|
||||
return $"缓存状态: 对象='{objectName}', 排除数={count}, 年龄={age.TotalSeconds:F1}秒";
|
||||
@ -202,6 +213,38 @@ namespace NavisworksTransport.Core.Animation
|
||||
return $"缓存状态获取失败: {ex.Message}";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 安全获取ModelItem的DisplayName,避免访问已释放对象的警告
|
||||
/// </summary>
|
||||
/// <param name="item">ModelItem对象</param>
|
||||
/// <returns>安全的显示名称</returns>
|
||||
private string GetSafeDisplayName(ModelItem item)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
return "NULL";
|
||||
}
|
||||
|
||||
// 尝试访问DisplayName,如果对象已被释放会抛出异常
|
||||
return item.DisplayName ?? "未命名对象";
|
||||
}
|
||||
catch (System.ObjectDisposedException)
|
||||
{
|
||||
return "已释放对象";
|
||||
}
|
||||
catch (System.Runtime.InteropServices.COMException)
|
||||
{
|
||||
return "COM对象已释放";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return $"访问失败({ex.GetType().Name})";
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
@ -314,6 +314,13 @@ namespace NavisworksTransport.Core.Animation
|
||||
{
|
||||
try
|
||||
{
|
||||
// 检查动画状态,避免重复停止
|
||||
if (_currentState == AnimationState.Stopped || _currentState == AnimationState.Idle)
|
||||
{
|
||||
LogManager.Debug($"动画已处于{_currentState}状态,跳过重复停止操作");
|
||||
return;
|
||||
}
|
||||
|
||||
// 清理定时器资源
|
||||
CleanupTimers();
|
||||
|
||||
@ -480,23 +487,87 @@ namespace NavisworksTransport.Core.Animation
|
||||
{
|
||||
try
|
||||
{
|
||||
StopAnimation(); // 停止当前动画
|
||||
|
||||
// 恢复对象的原始变换
|
||||
if (_animatedObject != null)
|
||||
// 只在需要时停止动画,避免重复调用
|
||||
if (_currentState == AnimationState.Playing || _currentState == AnimationState.Paused)
|
||||
{
|
||||
var modelItems = new ModelItemCollection { _animatedObject };
|
||||
NavisApplication.ActiveDocument.Models.OverridePermanentTransform(modelItems, _originalTransform, false);
|
||||
LogManager.Info($"部件 {_animatedObject.DisplayName} 已重置到原始位置");
|
||||
StopAnimation(); // 停止当前动画
|
||||
}
|
||||
|
||||
ProgressChanged?.Invoke(this, 0); // 重置进度条
|
||||
SetState(AnimationState.Ready); // 重置后回到就绪状态
|
||||
// 恢复对象的原始变换(安全检查Navisworks对象可用性)
|
||||
if (_animatedObject != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 检查Navisworks应用程序和文档是否仍然可用
|
||||
var activeDoc = NavisApplication.ActiveDocument;
|
||||
if (activeDoc != null && activeDoc.Models != null)
|
||||
{
|
||||
// 先安全获取对象名称,避免访问已释放对象的属性
|
||||
string objectName = GetSafeObjectName(_animatedObject);
|
||||
|
||||
var modelItems = new ModelItemCollection { _animatedObject };
|
||||
activeDoc.Models.OverridePermanentTransform(modelItems, _originalTransform, false);
|
||||
LogManager.Info($"部件 {objectName} 已重置到原始位置");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.Info("Navisworks文档已不可用,跳过对象重置操作");
|
||||
}
|
||||
}
|
||||
catch (Exception resetEx)
|
||||
{
|
||||
LogManager.Warning($"重置对象位置时出现警告(可能因为Navisworks对象已释放): {resetEx.Message}");
|
||||
// 不再抛出异常,因为在资源清理阶段这是正常的
|
||||
}
|
||||
}
|
||||
|
||||
// 重置UI状态(这些操作不依赖Navisworks对象)
|
||||
try
|
||||
{
|
||||
ProgressChanged?.Invoke(this, 0); // 重置进度条
|
||||
SetState(AnimationState.Ready); // 重置后回到就绪状态
|
||||
}
|
||||
catch (Exception uiEx)
|
||||
{
|
||||
LogManager.Warning($"重置UI状态时出现警告: {uiEx.Message}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"重置动画失败: {ex.Message}");
|
||||
throw;
|
||||
LogManager.Error($"重置动画过程中发生异常: {ex.Message}");
|
||||
// 在资源清理阶段不再抛出异常,避免影响程序正常关闭
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 安全获取ModelItem的DisplayName,避免访问已释放对象的警告
|
||||
/// </summary>
|
||||
/// <param name="item">ModelItem对象</param>
|
||||
/// <returns>安全的显示名称</returns>
|
||||
private string GetSafeObjectName(ModelItem item)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
return "NULL";
|
||||
}
|
||||
|
||||
// 尝试访问DisplayName,如果对象已被释放会抛出异常
|
||||
return item.DisplayName ?? "未命名对象";
|
||||
}
|
||||
catch (System.ObjectDisposedException)
|
||||
{
|
||||
return "已释放对象";
|
||||
}
|
||||
catch (System.Runtime.InteropServices.COMException)
|
||||
{
|
||||
return "COM对象已释放";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return $"访问失败({ex.GetType().Name})";
|
||||
}
|
||||
}
|
||||
|
||||
@ -938,22 +1009,69 @@ namespace NavisworksTransport.Core.Animation
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
StopAnimation();
|
||||
ResetAnimation();
|
||||
|
||||
// 清理 TimeLiner 资源
|
||||
if (_timeLinerManager != null)
|
||||
try
|
||||
{
|
||||
LogManager.Info("开始清理PathAnimationManager资源");
|
||||
|
||||
// 1. 优先停止动画(最小化的操作,不触发复杂的重置逻辑)
|
||||
// 只有在动画运行时才停止,避免重复调用
|
||||
try
|
||||
{
|
||||
_timeLinerManager.Dispose();
|
||||
_timeLinerManager = null;
|
||||
LogManager.Info("TimeLiner 集成资源已清理");
|
||||
if (_currentState == AnimationState.Playing || _currentState == AnimationState.Paused)
|
||||
{
|
||||
StopAnimation();
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.Debug($"动画已处于{_currentState}状态,跳过停止操作");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
catch (Exception stopEx)
|
||||
{
|
||||
LogManager.Warning($"清理 TimeLiner 资源时出现警告: {ex.Message}");
|
||||
LogManager.Warning($"停止动画时出现警告: {stopEx.Message},继续清理其他资源");
|
||||
}
|
||||
|
||||
// 2. 尝试重置动画(如果Navisworks对象仍然可用)
|
||||
// ResetAnimation内部已经有状态检查,不会重复停止动画
|
||||
try
|
||||
{
|
||||
// 检查Navisworks应用程序是否仍然可用,只有在可用时才进行重置
|
||||
var activeDoc = NavisApplication.ActiveDocument;
|
||||
if (activeDoc != null && activeDoc.Models != null)
|
||||
{
|
||||
ResetAnimation();
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.Info("Navisworks文档已不可用,跳过动画重置操作");
|
||||
}
|
||||
}
|
||||
catch (Exception resetEx)
|
||||
{
|
||||
LogManager.Warning($"重置动画时出现警告: {resetEx.Message},继续清理其他资源");
|
||||
}
|
||||
|
||||
// 3. 清理 TimeLiner 资源
|
||||
if (_timeLinerManager != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
_timeLinerManager.Dispose();
|
||||
_timeLinerManager = null;
|
||||
LogManager.Info("TimeLiner 集成资源已清理");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Warning($"清理 TimeLiner 资源时出现警告: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
LogManager.Info("PathAnimationManager资源清理完成");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"PathAnimationManager资源清理过程中发生异常: {ex.Message}");
|
||||
// 不抛出异常,避免影响应用程序正常关闭
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -2257,28 +2257,8 @@ namespace NavisworksTransport
|
||||
{
|
||||
LogManager.Warning($"检查单个对象碰撞时出错 {item.DisplayName}: {itemEx.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
// 输出完整的性能分析结果
|
||||
var totalTime = exclusionStopwatch.ElapsedMilliseconds + getAllItemsStopwatch.ElapsedMilliseconds +
|
||||
excludeLogicStopwatch.ElapsedMilliseconds + boundingBoxStopwatch.ElapsedMilliseconds +
|
||||
intersectionStopwatch.ElapsedMilliseconds + distanceStopwatch.ElapsedMilliseconds;
|
||||
|
||||
LogManager.Info($"纯碰撞检测完整性能分析结果 (使用缓存):");
|
||||
LogManager.Info($" - 构建排除列表: {exclusionStopwatch.ElapsedMilliseconds}ms ({(exclusionStopwatch.ElapsedMilliseconds * 100.0 / Math.Max(totalTime, 1)):F1}%)");
|
||||
LogManager.Info($" - 获取对象列表: {getAllItemsStopwatch.ElapsedMilliseconds}ms ({(getAllItemsStopwatch.ElapsedMilliseconds * 100.0 / Math.Max(totalTime, 1)):F1}%) - 使用缓存");
|
||||
LogManager.Info($" - 排除逻辑检查: {excludeLogicStopwatch.ElapsedMilliseconds}ms ({(excludeLogicStopwatch.ElapsedMilliseconds * 100.0 / Math.Max(totalTime, 1)):F1}%) - {checkedCount + excludedCount} 次调用,使用缓存");
|
||||
LogManager.Info($" - 包围盒获取: {boundingBoxStopwatch.ElapsedMilliseconds}ms ({(boundingBoxStopwatch.ElapsedMilliseconds * 100.0 / Math.Max(totalTime, 1)):F1}%) - {boundingBoxCallCount} 次调用,平均: {(boundingBoxCallCount > 0 ? boundingBoxStopwatch.ElapsedMilliseconds / (double)boundingBoxCallCount : 0):F3}ms/次");
|
||||
LogManager.Info($" - 相交检测: {intersectionStopwatch.ElapsedMilliseconds}ms ({(intersectionStopwatch.ElapsedMilliseconds * 100.0 / Math.Max(totalTime, 1)):F1}%) - {intersectionCallCount} 次调用,平均: {(intersectionCallCount > 0 ? intersectionStopwatch.ElapsedMilliseconds / (double)intersectionCallCount : 0):F4}ms/次");
|
||||
LogManager.Info($" - 距离计算: {distanceStopwatch.ElapsedMilliseconds}ms ({(distanceStopwatch.ElapsedMilliseconds * 100.0 / Math.Max(totalTime, 1)):F1}%) - {distanceCallCount} 次调用,平均: {(distanceCallCount > 0 ? distanceStopwatch.ElapsedMilliseconds / (double)distanceCallCount : 0):F4}ms/次");
|
||||
LogManager.Info($" - 总计测量时间: {totalTime}ms");
|
||||
|
||||
LogManager.Info($"纯碰撞检测完成统计:");
|
||||
LogManager.Info($" - 检查对象: {checkedCount} 个");
|
||||
LogManager.Info($" - 排除对象: {excludedCount} 个");
|
||||
LogManager.Info($" - 发现碰撞: {results.Count} 个");
|
||||
LogManager.Info($" - 排除效率: {(excludedCount * 100.0 / (checkedCount + excludedCount)):F1}%");
|
||||
|
||||
}
|
||||
|
||||
if (results.Count == 0)
|
||||
{
|
||||
LogManager.Info($"未发现碰撞");
|
||||
|
||||
@ -247,7 +247,7 @@ namespace NavisworksTransport.Core
|
||||
// 属性修改成功,清除分层缓存确保下次预览使用最新数据
|
||||
try
|
||||
{
|
||||
SimplifiedModelSplitterManager.Instance?.ClearCache();
|
||||
ModelSplitterManager.Instance?.ClearCache();
|
||||
LogManager.Info("[FloorAttributeManager] 属性修改成功,已清除分层缓存");
|
||||
}
|
||||
catch (Exception cacheEx)
|
||||
@ -584,7 +584,7 @@ namespace NavisworksTransport.Core
|
||||
// 属性清除成功,清除分层缓存确保下次预览使用最新数据
|
||||
try
|
||||
{
|
||||
SimplifiedModelSplitterManager.Instance?.ClearCache();
|
||||
ModelSplitterManager.Instance?.ClearCache();
|
||||
LogManager.Info("[FloorAttributeManager] 属性清除成功,已清除分层缓存");
|
||||
}
|
||||
catch (Exception cacheEx)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,543 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using NavisworksTransport.Core.UIUpdate;
|
||||
using NavisworksTransport.Utils;
|
||||
|
||||
namespace NavisworksTransport.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// MainPlugin UI重构示例
|
||||
/// 展示如何将现有的BeginInvoke调用替换为新的UI更新流程
|
||||
/// </summary>
|
||||
public partial class MainPluginUIRefactorExample
|
||||
{
|
||||
#region 字段
|
||||
|
||||
private MainPluginUICoordinator _uiCoordinator;
|
||||
private bool _isUICoordinatorInitialized = false;
|
||||
|
||||
// 示例控件(这些在实际的MainPlugin中已存在)
|
||||
private ListView _pathListView;
|
||||
private Label _memoryLabel;
|
||||
private Label _vehicleStatusLabel;
|
||||
private Button _createAnimationButton;
|
||||
private Label _pathInfoLabel;
|
||||
private ComboBox _pathComboBox;
|
||||
private ProgressBar _animationProgressBar;
|
||||
private Label _animationStatusLabel;
|
||||
private Button _startAnimationButton;
|
||||
private Button _stopAnimationButton;
|
||||
|
||||
#endregion
|
||||
|
||||
#region UI协调器初始化
|
||||
|
||||
/// <summary>
|
||||
/// 初始化UI协调器
|
||||
/// 这个方法应该在MainPlugin的LoadPlugin方法中调用
|
||||
/// </summary>
|
||||
private async Task InitializeUICoordinatorAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
LogManager.Info("[MainPlugin] 开始初始化UI协调器");
|
||||
|
||||
// 创建UI协调器
|
||||
_uiCoordinator = new MainPluginUICoordinator();
|
||||
|
||||
// 初始化协调器
|
||||
await _uiCoordinator.InitializeAsync();
|
||||
|
||||
// 注册所有控件
|
||||
RegisterControlsToCoordinator();
|
||||
|
||||
_isUICoordinatorInitialized = true;
|
||||
LogManager.Info("[MainPlugin] UI协调器初始化完成");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error("[MainPlugin] 初始化UI协调器失败", ex);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 注册控件到UI协调器
|
||||
/// </summary>
|
||||
private void RegisterControlsToCoordinator()
|
||||
{
|
||||
var controls = new Dictionary<string, Control>
|
||||
{
|
||||
["PathListView"] = _pathListView,
|
||||
["MemoryLabel"] = _memoryLabel,
|
||||
["VehicleStatusLabel"] = _vehicleStatusLabel,
|
||||
["CreateAnimationButton"] = _createAnimationButton,
|
||||
["PathInfoLabel"] = _pathInfoLabel,
|
||||
["PathComboBox"] = _pathComboBox,
|
||||
["AnimationProgressBar"] = _animationProgressBar,
|
||||
["AnimationStatusLabel"] = _animationStatusLabel,
|
||||
["StartAnimationButton"] = _startAnimationButton,
|
||||
["StopAnimationButton"] = _stopAnimationButton
|
||||
};
|
||||
|
||||
_uiCoordinator.RegisterControls(controls);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 重构前后对比示例
|
||||
|
||||
/// <summary>
|
||||
/// 重构前:直接使用BeginInvoke更新内存标签
|
||||
/// 这是MainPlugin中的原始代码模式
|
||||
/// </summary>
|
||||
private void UpdateMemoryLabelOldWay(string memoryInfo)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_memoryLabel.InvokeRequired)
|
||||
{
|
||||
_memoryLabel.BeginInvoke(new Action(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
_memoryLabel.Text = memoryInfo;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"更新内存标签失败: {ex.Message}");
|
||||
}
|
||||
}));
|
||||
}
|
||||
else
|
||||
{
|
||||
_memoryLabel.Text = memoryInfo;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"更新内存标签时发生异常: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重构后:使用UI协调器更新内存标签
|
||||
/// 新的统一UI更新模式
|
||||
/// </summary>
|
||||
private async Task UpdateMemoryLabelNewWay(string memoryInfo)
|
||||
{
|
||||
if (!_isUICoordinatorInitialized)
|
||||
{
|
||||
LogManager.Warning("[MainPlugin] UI协调器未初始化,跳过UI更新");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var result = await _uiCoordinator.UpdateControlTextAsync("MemoryLabel", memoryInfo, UIUpdatePriority.High);
|
||||
|
||||
if (!result.IsSuccess)
|
||||
{
|
||||
LogManager.Error($"更新内存标签失败: {result.Message}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error("[MainPlugin] 更新内存标签时发生异常", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重构前:直接使用BeginInvoke更新动画状态
|
||||
/// </summary>
|
||||
private void UpdateAnimationStatusOldWay(string status, bool animationRunning)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_animationStatusLabel.InvokeRequired)
|
||||
{
|
||||
_animationStatusLabel.BeginInvoke(new Action(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
_animationStatusLabel.Text = status;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"更新动画状态标签失败: {ex.Message}");
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
if (_startAnimationButton.InvokeRequired)
|
||||
{
|
||||
_startAnimationButton.BeginInvoke(new Action(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
_startAnimationButton.Enabled = !animationRunning;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"更新开始按钮失败: {ex.Message}");
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
if (_stopAnimationButton.InvokeRequired)
|
||||
{
|
||||
_stopAnimationButton.BeginInvoke(new Action(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
_stopAnimationButton.Enabled = animationRunning;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"更新停止按钮失败: {ex.Message}");
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"更新动画状态时发生异常: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重构后:使用UI协调器批量更新动画状态
|
||||
/// </summary>
|
||||
private async Task UpdateAnimationStatusNewWay(string status, bool animationRunning)
|
||||
{
|
||||
if (!_isUICoordinatorInitialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// 使用批量更新,原子性地更新所有相关控件
|
||||
var updates = new Dictionary<string, Dictionary<string, object>>
|
||||
{
|
||||
["AnimationStatusLabel"] = new Dictionary<string, object> { ["Text"] = status },
|
||||
["StartAnimationButton"] = new Dictionary<string, object> { ["Enabled"] = !animationRunning },
|
||||
["StopAnimationButton"] = new Dictionary<string, object> { ["Enabled"] = animationRunning }
|
||||
};
|
||||
|
||||
var result = await _uiCoordinator.UpdateControlsAsync(updates, UIUpdatePriority.High);
|
||||
|
||||
if (!result.IsSuccess)
|
||||
{
|
||||
LogManager.Error($"批量更新动画状态失败: {result.Message}");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.Info($"成功更新动画状态,成功{result.SuccessCount}个,失败{result.FailureCount}个");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error("[MainPlugin] 更新动画状态时发生异常", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重构前:直接更新ListView
|
||||
/// </summary>
|
||||
private void UpdatePathListViewOldWay(List<string> pathPoints)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_pathListView.InvokeRequired)
|
||||
{
|
||||
_pathListView.BeginInvoke(new Action(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
_pathListView.Items.Clear();
|
||||
foreach (var point in pathPoints)
|
||||
{
|
||||
var item = new ListViewItem(point);
|
||||
_pathListView.Items.Add(item);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"更新路径列表失败: {ex.Message}");
|
||||
}
|
||||
}));
|
||||
}
|
||||
else
|
||||
{
|
||||
_pathListView.Items.Clear();
|
||||
foreach (var point in pathPoints)
|
||||
{
|
||||
var item = new ListViewItem(point);
|
||||
_pathListView.Items.Add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"更新路径列表时发生异常: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重构后:使用UI协调器更新ListView
|
||||
/// </summary>
|
||||
private async Task UpdatePathListViewNewWay(List<string> pathPoints)
|
||||
{
|
||||
if (!_isUICoordinatorInitialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var listViewItems = pathPoints.Select(point => new ListViewItem(point)).ToArray();
|
||||
|
||||
var result = await _uiCoordinator.RefreshListViewAsync("PathListView", listViewItems, UIUpdatePriority.Normal);
|
||||
|
||||
if (!result.IsSuccess)
|
||||
{
|
||||
LogManager.Error($"刷新路径列表失败: {result.Message}");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.Info($"成功刷新路径列表,共{listViewItems.Length}项");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error("[MainPlugin] 更新路径列表时发生异常", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重构后:使用事务管理复杂的UI状态更新
|
||||
/// </summary>
|
||||
private async Task UpdateComplexUIStateWithTransaction(string operationName, Dictionary<string, object> newState)
|
||||
{
|
||||
if (!_isUICoordinatorInitialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
using (var transaction = _uiCoordinator.UIUpdateService.BeginTransaction($"UpdateComplexUIState_{operationName}"))
|
||||
{
|
||||
var updates = new List<IUIUpdate>();
|
||||
|
||||
// 根据新状态构建更新操作
|
||||
foreach (var state in newState)
|
||||
{
|
||||
var controlName = state.Key;
|
||||
var stateData = state.Value;
|
||||
|
||||
// 假设stateData是一个包含属性的匿名对象
|
||||
if (_uiCoordinator.RegisteredControls.TryGetValue(controlName, out var control))
|
||||
{
|
||||
updates.AddRange(UIUpdateFactory.UpdateProperties(control, stateData, UIUpdatePriority.High));
|
||||
}
|
||||
}
|
||||
|
||||
var result = await _uiCoordinator.UIUpdateService.ExecuteInTransactionAsync(transaction, updates);
|
||||
|
||||
if (result.IsSuccess)
|
||||
{
|
||||
LogManager.Info($"成功完成复杂UI状态更新事务: {operationName}");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.Error($"复杂UI状态更新事务失败: {operationName} - {result.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"[MainPlugin] 执行复杂UI状态更新事务{operationName}时发生异常", ex);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 高级UI更新模式
|
||||
|
||||
/// <summary>
|
||||
/// 条件性UI更新
|
||||
/// 根据条件动态更新UI状态
|
||||
/// </summary>
|
||||
private async Task ConditionalUIUpdate(bool condition, string statusText)
|
||||
{
|
||||
if (!_isUICoordinatorInitialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// 创建条件更新操作
|
||||
var trueOperation = UIUpdateFactory.UpdateProperty(_vehicleStatusLabel, "Text", statusText, UIUpdatePriority.High);
|
||||
var falseOperation = UIUpdateFactory.UpdateProperty(_vehicleStatusLabel, "Text", "条件不满足", UIUpdatePriority.High);
|
||||
|
||||
var conditionalUpdate = UIUpdateFactory.CreateConditionalUpdate(() => condition, trueOperation, falseOperation);
|
||||
|
||||
var result = await _uiCoordinator.UIUpdateService.ExecuteUpdateAsync(conditionalUpdate);
|
||||
|
||||
if (!result.IsSuccess)
|
||||
{
|
||||
LogManager.Error($"条件性UI更新失败: {result.Message}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error("[MainPlugin] 条件性UI更新时发生异常", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 延迟UI更新
|
||||
/// 延迟执行UI更新操作
|
||||
/// </summary>
|
||||
private async Task DelayedUIUpdate(string message, int delayMilliseconds)
|
||||
{
|
||||
if (!_isUICoordinatorInitialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var updateOperation = UIUpdateFactory.UpdateProperty(_pathInfoLabel, "Text", message, UIUpdatePriority.Normal);
|
||||
var delayedUpdate = UIUpdateFactory.CreateDelayedUpdate(delayMilliseconds, updateOperation);
|
||||
|
||||
var result = await _uiCoordinator.UIUpdateService.ExecuteUpdateAsync(delayedUpdate);
|
||||
|
||||
if (!result.IsSuccess)
|
||||
{
|
||||
LogManager.Error($"延迟UI更新失败: {result.Message}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error("[MainPlugin] 延迟UI更新时发生异常", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 自定义UI操作
|
||||
/// 执行复杂的自定义UI逻辑
|
||||
/// </summary>
|
||||
private async Task CustomUIOperation()
|
||||
{
|
||||
if (!_isUICoordinatorInitialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// 创建自定义操作
|
||||
var result = await _uiCoordinator.ExecuteCustomActionAsync("CustomUIOperation", () =>
|
||||
{
|
||||
// 这里可以执行任何复杂的UI逻辑
|
||||
_pathInfoLabel.Text = $"自定义操作执行于: {DateTime.Now:HH:mm:ss}";
|
||||
_pathInfoLabel.BackColor = System.Drawing.Color.LightGreen;
|
||||
|
||||
// 模拟一些处理时间
|
||||
System.Threading.Thread.Sleep(100);
|
||||
|
||||
_pathInfoLabel.BackColor = System.Drawing.SystemColors.Control;
|
||||
}, UIUpdatePriority.Normal);
|
||||
|
||||
if (!result.IsSuccess)
|
||||
{
|
||||
LogManager.Error($"自定义UI操作失败: {result.Message}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error("[MainPlugin] 自定义UI操作时发生异常", ex);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 清理资源
|
||||
|
||||
/// <summary>
|
||||
/// 清理UI协调器资源
|
||||
/// 这个方法应该在MainPlugin的UnloadPlugin方法中调用
|
||||
/// </summary>
|
||||
private async Task CleanupUICoordinatorAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_uiCoordinator != null)
|
||||
{
|
||||
LogManager.Info("[MainPlugin] 开始清理UI协调器");
|
||||
|
||||
// 关闭UI更新服务
|
||||
await _uiCoordinator.UIUpdateService.ShutdownAsync();
|
||||
|
||||
// 释放协调器
|
||||
_uiCoordinator.Dispose();
|
||||
_uiCoordinator = null;
|
||||
|
||||
_isUICoordinatorInitialized = false;
|
||||
LogManager.Info("[MainPlugin] UI协调器清理完成");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error("[MainPlugin] 清理UI协调器时发生异常", ex);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 使用指南注释
|
||||
|
||||
/*
|
||||
* MainPlugin UI重构指南:
|
||||
*
|
||||
* 1. 初始化阶段(在LoadPlugin中):
|
||||
* - 调用 InitializeUICoordinatorAsync()
|
||||
*
|
||||
* 2. UI更新替换:
|
||||
* - 将所有 if (control.InvokeRequired) { control.BeginInvoke(...) } 模式
|
||||
* - 替换为 await _uiCoordinator.UpdateControlXxxAsync(...)
|
||||
*
|
||||
* 3. 批量更新:
|
||||
* - 多个相关的UI更新可以合并为批量操作
|
||||
* - 使用 UpdateControlsAsync() 或事务操作
|
||||
*
|
||||
* 4. 错误处理:
|
||||
* - 新的UI更新系统提供统一的错误处理
|
||||
* - 检查 UIUpdateResult.IsSuccess 和 Message
|
||||
*
|
||||
* 5. 性能优化:
|
||||
* - 使用适当的优先级(Critical, High, Normal, Low)
|
||||
* - 批量操作比单个操作更高效
|
||||
* - 条件和延迟更新可以减少不必要的UI操作
|
||||
*
|
||||
* 6. 线程安全:
|
||||
* - 新系统自动处理线程安全问题
|
||||
* - 不再需要手动检查 InvokeRequired
|
||||
*
|
||||
* 7. 清理阶段(在UnloadPlugin中):
|
||||
* - 调用 CleanupUICoordinatorAsync()
|
||||
*
|
||||
* 8. 调试和监控:
|
||||
* - 启用详细日志记录可以监控UI更新性能
|
||||
* - UI更新事件提供实时状态反馈
|
||||
*/
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -1,52 +0,0 @@
|
||||
using Autodesk.Navisworks.Api.Plugins;
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace NavisworksTransport
|
||||
{
|
||||
[Plugin("NavisworksTransport.MinimalTest", "Tian",
|
||||
DisplayName = "最小测试插件",
|
||||
ToolTip = "用于测试基本插件加载的最小插件")]
|
||||
[DockPanePlugin(300, 200, FixedSize = false)]
|
||||
public class MinimalTestPlugin : DockPanePlugin
|
||||
{
|
||||
public override Control CreateControlPane()
|
||||
{
|
||||
try
|
||||
{
|
||||
var panel = new Panel
|
||||
{
|
||||
BackColor = System.Drawing.Color.LightBlue
|
||||
};
|
||||
|
||||
var label = new Label
|
||||
{
|
||||
Text = "最小测试插件已加载\n" + DateTime.Now.ToString(),
|
||||
Dock = DockStyle.Fill,
|
||||
TextAlign = ContentAlignment.MiddleCenter
|
||||
};
|
||||
|
||||
panel.Controls.Add(label);
|
||||
|
||||
return panel;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var errorPanel = new Panel { BackColor = System.Drawing.Color.Red };
|
||||
var errorLabel = new Label
|
||||
{
|
||||
Text = $"错误: {ex.Message}",
|
||||
Dock = DockStyle.Fill,
|
||||
ForeColor = System.Drawing.Color.White
|
||||
};
|
||||
errorPanel.Controls.Add(errorLabel);
|
||||
return errorPanel;
|
||||
}
|
||||
}
|
||||
|
||||
public override void DestroyControlPane(Control pane)
|
||||
{
|
||||
pane?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,916 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
using Autodesk.Navisworks.Api;
|
||||
using NavisApplication = Autodesk.Navisworks.Api.Application;
|
||||
|
||||
namespace NavisworksTransport
|
||||
{
|
||||
/// <summary>
|
||||
/// 定义动画播放的状态
|
||||
/// </summary>
|
||||
public enum AnimationState
|
||||
{
|
||||
Idle, // 空闲,未生成动画
|
||||
Ready, // 已就绪,动画已生成但未播放
|
||||
Playing, // 播放中
|
||||
Paused, // 暂停
|
||||
Stopped, // 已停止
|
||||
Finished // 已完成
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 路径动画管理器 - 基于TimeLiner和动态变换实现沿路径的动画效果
|
||||
/// 注意:由于Navisworks API限制,无法直接使用Animator API,因此使用OverridePermanentTransform实现动画
|
||||
/// 已集成 TimeLiner 功能,支持在 TimeLiner 中显示和管理动画任务
|
||||
/// </summary>
|
||||
public class PathAnimationManager
|
||||
{
|
||||
private ModelItem _animatedObject;
|
||||
private List<Point3D> _pathPoints;
|
||||
private Timer _animationTimer;
|
||||
private double _animationDuration = 10.0; // 动画总时长(秒)
|
||||
private DateTime _animationStartTime;
|
||||
private Transform3D _originalTransform;
|
||||
private Point3D _originalCenter; // 存储部件的原始中心位置
|
||||
private Point3D _currentPosition; // 存储部件的当前位置
|
||||
private AnimationState _currentState = AnimationState.Idle;
|
||||
|
||||
// TimeLiner 集成
|
||||
private TimeLinerIntegrationManager _timeLinerManager;
|
||||
private string _currentTaskId;
|
||||
|
||||
// --- 新增事件 ---
|
||||
/// <summary>
|
||||
/// 当动画状态发生改变时触发
|
||||
/// </summary>
|
||||
public event EventHandler<AnimationState> StateChanged;
|
||||
|
||||
/// <summary>
|
||||
/// 当动画进度更新时触发 (0-100)
|
||||
/// </summary>
|
||||
public event EventHandler<int> ProgressChanged;
|
||||
|
||||
// 动画完成事件 (旧版,保留兼容性)
|
||||
public event EventHandler AnimationCompleted;
|
||||
|
||||
/// <summary>
|
||||
/// 当检测到碰撞时触发
|
||||
/// </summary>
|
||||
public event EventHandler<CollisionDetectedEventArgs> CollisionDetected;
|
||||
|
||||
public PathAnimationManager()
|
||||
{
|
||||
_pathPoints = new List<Point3D>();
|
||||
|
||||
// 初始化 TimeLiner 集成
|
||||
try
|
||||
{
|
||||
_timeLinerManager = new TimeLinerIntegrationManager();
|
||||
LogManager.Info("PathAnimationManager 已集成 TimeLiner 功能");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Warning($"TimeLiner 集成初始化失败,将使用基础动画功能: {ex.Message}");
|
||||
_timeLinerManager = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置动画参数
|
||||
/// </summary>
|
||||
/// <param name="animatedObject">要动画化的模型对象</param>
|
||||
/// <param name="pathPoints">路径点列表</param>
|
||||
/// <param name="durationSeconds">动画持续时间(秒)</param>
|
||||
public void SetupAnimation(ModelItem animatedObject, List<Point3D> pathPoints, double durationSeconds = 10.0)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (pathPoints == null || pathPoints.Count < 2)
|
||||
throw new ArgumentException("路径点数量必须至少为2个", nameof(pathPoints));
|
||||
|
||||
// 添加路径点坐标有效性验证
|
||||
LogManager.Info("=== 动画管理器坐标验证 ===");
|
||||
bool hasInvalidCoordinates = false;
|
||||
for (int i = 0; i < pathPoints.Count; i++)
|
||||
{
|
||||
var point = pathPoints[i];
|
||||
bool isValid = !double.IsNaN(point.X) && !double.IsNaN(point.Y) && !double.IsNaN(point.Z) &&
|
||||
!double.IsInfinity(point.X) && !double.IsInfinity(point.Y) && !double.IsInfinity(point.Z);
|
||||
|
||||
LogManager.Info($"路径点[{i}]坐标验证: ({point.X:F6},{point.Y:F6},{point.Z:F6}) - {(isValid ? "有效" : "无效")}");
|
||||
|
||||
if (!isValid)
|
||||
{
|
||||
hasInvalidCoordinates = true;
|
||||
LogManager.Error($"检测到无效坐标: 路径点[{i}] = ({point.X},{point.Y},{point.Z})");
|
||||
}
|
||||
|
||||
// 检查是否为零坐标(可能表示数据丢失)
|
||||
if (isValid && point.X == 0.0 && point.Y == 0.0 && point.Z == 0.0)
|
||||
{
|
||||
LogManager.Warning($"路径点[{i}]坐标为零,可能存在数据丢失问题");
|
||||
}
|
||||
}
|
||||
|
||||
if (hasInvalidCoordinates)
|
||||
{
|
||||
throw new ArgumentException("路径点包含无效坐标(NaN或无穷大),无法创建动画");
|
||||
}
|
||||
|
||||
LogManager.Info("=== 坐标验证完成 ===");
|
||||
|
||||
_animatedObject = animatedObject ?? throw new ArgumentNullException(nameof(animatedObject));
|
||||
_pathPoints = new List<Point3D>(pathPoints);
|
||||
_animationDuration = durationSeconds;
|
||||
|
||||
// 保存原始变换以便重置
|
||||
_originalTransform = GetCurrentTransform(_animatedObject);
|
||||
|
||||
// 保存车辆的原始中心位置
|
||||
var originalBoundingBox = animatedObject.BoundingBox();
|
||||
_originalCenter = originalBoundingBox.Center;
|
||||
|
||||
// 关键修复:将车辆立即移动到路径起点
|
||||
// 这样动画就从路径起点开始,而不是从车辆当前位置开始
|
||||
MoveVehicleToPathStart();
|
||||
|
||||
// 记录文档单位信息
|
||||
var documentUnits = GetDocumentUnitsInfo();
|
||||
|
||||
// 添加详细的调试信息
|
||||
LogManager.Info($"动画设置完成:对象={_animatedObject.DisplayName}, 路径点数={_pathPoints.Count}, 时长={_animationDuration}秒");
|
||||
LogManager.Info($"文档单位: {documentUnits}");
|
||||
LogManager.Info($"路径起点: ({_pathPoints[0].X:F2},{_pathPoints[0].Y:F2},{_pathPoints[0].Z:F2})");
|
||||
LogManager.Info($"路径终点: ({_pathPoints[_pathPoints.Count-1].X:F2},{_pathPoints[_pathPoints.Count-1].Y:F2},{_pathPoints[_pathPoints.Count-1].Z:F2})");
|
||||
|
||||
var totalDist = CalculateTotalPathDistance();
|
||||
LogManager.Info($"路径总长度: {totalDist:F2}");
|
||||
LogManager.Info($"车辆已移动到路径起点,动画将从起点开始");
|
||||
LogManager.Info($"=== 调试信息结束 ===");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"设置动画失败: {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将车辆移动到路径起点
|
||||
/// </summary>
|
||||
private void MoveVehicleToPathStart()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_pathPoints.Count == 0) return;
|
||||
|
||||
var doc = NavisApplication.ActiveDocument;
|
||||
var modelItems = new ModelItemCollection { _animatedObject };
|
||||
|
||||
// 计算从部件原始中心到路径起点的偏移
|
||||
var startOffset = new Vector3D(
|
||||
_pathPoints[0].X - _originalCenter.X,
|
||||
_pathPoints[0].Y - _originalCenter.Y,
|
||||
_pathPoints[0].Z - _originalCenter.Z
|
||||
);
|
||||
|
||||
// 创建变换并应用
|
||||
var startTransform = Transform3D.CreateTranslation(startOffset);
|
||||
doc.Models.OverridePermanentTransform(modelItems, startTransform, false);
|
||||
|
||||
// 更新当前位置为路径起点
|
||||
_currentPosition = _pathPoints[0];
|
||||
|
||||
LogManager.Info($"部件已移动到路径起点,偏移: ({startOffset.X:F2},{startOffset.Y:F2},{startOffset.Z:F2})");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"移动部件到路径起点失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 开始播放动画
|
||||
/// </summary>
|
||||
public void StartAnimation()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_animatedObject == null || _pathPoints.Count < 2)
|
||||
{
|
||||
throw new InvalidOperationException("请先调用SetupAnimation设置动画参数");
|
||||
}
|
||||
|
||||
// 停止之前的动画
|
||||
StopAnimation();
|
||||
|
||||
// 创建 TimeLiner 任务(如果可用)
|
||||
if (_timeLinerManager != null && _timeLinerManager.IsTimeLinerAvailable)
|
||||
{
|
||||
var taskName = $"{_animatedObject.DisplayName}_运输_{DateTime.Now:HHmmss}";
|
||||
var duration = TimeSpan.FromSeconds(_animationDuration);
|
||||
|
||||
LogManager.Info($"创建 TimeLiner 任务: {taskName}");
|
||||
|
||||
_currentTaskId = _timeLinerManager.CreateTransportTask(
|
||||
taskName,
|
||||
_pathPoints,
|
||||
duration,
|
||||
_animatedObject);
|
||||
|
||||
if (!string.IsNullOrEmpty(_currentTaskId))
|
||||
{
|
||||
LogManager.Info($"✓ TimeLiner 任务创建成功: {taskName} (ID: {_currentTaskId})");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.Warning("TimeLiner 任务创建失败,继续使用基础动画功能");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.Info("TimeLiner 不可用,使用基础动画功能");
|
||||
}
|
||||
|
||||
// 设置动态碰撞检测(简化版本)
|
||||
SetupDynamicClashDetection();
|
||||
|
||||
// 初始化动画状态
|
||||
_animationStartTime = DateTime.Now;
|
||||
|
||||
// 创建并启动定时器(每50ms更新一次,实现流畅动画)
|
||||
_animationTimer = new Timer
|
||||
{
|
||||
Interval = 50 // 20 FPS
|
||||
};
|
||||
_animationTimer.Tick += AnimationTimer_Tick;
|
||||
_animationTimer.Start();
|
||||
|
||||
SetState(AnimationState.Playing);
|
||||
LogManager.Info("动画开始播放");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"启动动画失败: {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 停止动画
|
||||
/// </summary>
|
||||
public void StopAnimation()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_animationTimer != null)
|
||||
{
|
||||
_animationTimer.Stop();
|
||||
_animationTimer.Dispose();
|
||||
_animationTimer = null;
|
||||
|
||||
SetState(AnimationState.Stopped);
|
||||
LogManager.Info("动画已停止");
|
||||
}
|
||||
|
||||
// 动画停止时不创建碰撞测试汇总,由动画完成事件统一处理
|
||||
LogManager.Info("动画停止,等待动画完成事件统一处理碰撞测试...");
|
||||
|
||||
// 更新 TimeLiner 任务状态
|
||||
if (_timeLinerManager != null && !string.IsNullOrEmpty(_currentTaskId))
|
||||
{
|
||||
_timeLinerManager.UpdateTaskProgress(_currentTaskId, 0.0, AnimationState.Stopped);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"停止动画失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重置动画对象到原始位置
|
||||
/// </summary>
|
||||
public void ResetAnimation()
|
||||
{
|
||||
try
|
||||
{
|
||||
StopAnimation(); // 停止当前动画
|
||||
|
||||
// 恢复对象的原始变换
|
||||
if (_animatedObject != null)
|
||||
{
|
||||
var modelItems = new ModelItemCollection { _animatedObject };
|
||||
NavisApplication.ActiveDocument.Models.OverridePermanentTransform(modelItems, _originalTransform, false);
|
||||
LogManager.Info($"部件 {_animatedObject.DisplayName} 已重置到原始位置");
|
||||
}
|
||||
|
||||
ProgressChanged?.Invoke(this, 0); // 重置进度条
|
||||
SetState(AnimationState.Ready); // 重置后回到就绪状态
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"重置动画失败: {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 动画定时器事件处理
|
||||
/// </summary>
|
||||
private void AnimationTimer_Tick(object sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
double elapsedSeconds = (DateTime.Now - _animationStartTime).TotalSeconds;
|
||||
double progress = Math.Min(elapsedSeconds / _animationDuration, 1.0);
|
||||
|
||||
// 更新UI进度条
|
||||
ProgressChanged?.Invoke(this, (int)(progress * 100));
|
||||
|
||||
// 同步进度到 TimeLiner(如果可用)
|
||||
if (_timeLinerManager != null && !string.IsNullOrEmpty(_currentTaskId))
|
||||
{
|
||||
_timeLinerManager.UpdateTaskProgress(_currentTaskId, progress, _currentState);
|
||||
}
|
||||
|
||||
Point3D newPosition = InterpolatePosition(progress);
|
||||
UpdateObjectPosition(newPosition);
|
||||
|
||||
// 使用 Clash Detective 集成进行碰撞检测
|
||||
CheckAndHighlightCollisionsWithClashDetective();
|
||||
|
||||
if (progress >= 1.0)
|
||||
{
|
||||
StopAnimation();
|
||||
SetState(AnimationState.Finished); // 标记为完成
|
||||
AnimationCompleted?.Invoke(this, EventArgs.Empty); // 触发旧版完成事件
|
||||
LogManager.Info("动画播放完成");
|
||||
|
||||
// 动画结束后统一创建所有碰撞测试(基于官方示例的批量处理)
|
||||
LogManager.Info("动画播放完成,开始创建最终的碰撞测试汇总...");
|
||||
ClashDetectiveIntegration.Instance.CreateAllAnimationCollisionTests();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"动画帧更新失败: {ex.Message}");
|
||||
StopAnimation(); // 发生错误时停止动画
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据进度插值计算当前位置
|
||||
/// </summary>
|
||||
private Point3D InterpolatePosition(double progress)
|
||||
{
|
||||
if (_pathPoints.Count < 2)
|
||||
return _pathPoints[0];
|
||||
|
||||
// 确保进度在0-1范围内
|
||||
progress = Math.Max(0.0, Math.Min(1.0, progress));
|
||||
|
||||
// 如果进度达到100%,直接返回终点
|
||||
if (progress >= 1.0)
|
||||
{
|
||||
return _pathPoints[_pathPoints.Count - 1];
|
||||
}
|
||||
|
||||
// 计算总路径长度
|
||||
var totalDistance = CalculateTotalPathDistance();
|
||||
|
||||
// 检查总距离是否有效
|
||||
if (totalDistance <= 0.0 || double.IsNaN(totalDistance) || double.IsInfinity(totalDistance))
|
||||
{
|
||||
LogManager.Error($"路径总长度无效: {totalDistance},返回起点坐标");
|
||||
return _pathPoints[0];
|
||||
}
|
||||
|
||||
var targetDistance = totalDistance * progress;
|
||||
|
||||
// 找到当前应该在哪两个点之间
|
||||
var accumulatedDistance = 0.0;
|
||||
for (int i = 0; i < _pathPoints.Count - 1; i++)
|
||||
{
|
||||
var segmentDistance = CalculateDistance(_pathPoints[i], _pathPoints[i + 1]);
|
||||
|
||||
// 检查段距离是否有效
|
||||
if (double.IsNaN(segmentDistance) || double.IsInfinity(segmentDistance))
|
||||
{
|
||||
LogManager.Error($"路径段[{i}-{i+1}]距离无效: {segmentDistance},跳过此段");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (accumulatedDistance + segmentDistance >= targetDistance)
|
||||
{
|
||||
// 在这个线段内
|
||||
var segmentProgress = segmentDistance > 0 ? (targetDistance - accumulatedDistance) / segmentDistance : 0.0;
|
||||
// 确保段内进度也在0-1范围内
|
||||
segmentProgress = Math.Max(0.0, Math.Min(1.0, segmentProgress));
|
||||
|
||||
var interpolatedPoint = InterpolatePoints(_pathPoints[i], _pathPoints[i + 1], segmentProgress);
|
||||
|
||||
// 验证插值结果
|
||||
if (double.IsNaN(interpolatedPoint.X) || double.IsNaN(interpolatedPoint.Y) || double.IsNaN(interpolatedPoint.Z))
|
||||
{
|
||||
LogManager.Error($"插值计算结果无效: ({interpolatedPoint.X},{interpolatedPoint.Y},{interpolatedPoint.Z}),返回起点");
|
||||
return _pathPoints[0];
|
||||
}
|
||||
|
||||
return interpolatedPoint;
|
||||
}
|
||||
|
||||
accumulatedDistance += segmentDistance;
|
||||
}
|
||||
|
||||
// 如果到达这里,返回最后一个点
|
||||
return _pathPoints[_pathPoints.Count - 1];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在两点间插值
|
||||
/// </summary>
|
||||
private Point3D InterpolatePoints(Point3D point1, Point3D point2, double t)
|
||||
{
|
||||
return new Point3D(
|
||||
point1.X + (point2.X - point1.X) * t,
|
||||
point1.Y + (point2.Y - point1.Y) * t,
|
||||
point1.Z + (point2.Z - point1.Z) * t
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算路径总长度
|
||||
/// </summary>
|
||||
private double CalculateTotalPathDistance()
|
||||
{
|
||||
var totalDistance = 0.0;
|
||||
for (int i = 0; i < _pathPoints.Count - 1; i++)
|
||||
{
|
||||
totalDistance += CalculateDistance(_pathPoints[i], _pathPoints[i + 1]);
|
||||
}
|
||||
return totalDistance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取对象当前位置
|
||||
/// </summary>
|
||||
private Point3D GetObjectPosition(ModelItem item)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (item == null) return new Point3D(0, 0, 0);
|
||||
|
||||
var bounds = item.BoundingBox();
|
||||
if (bounds != null)
|
||||
{
|
||||
return new Point3D(
|
||||
(bounds.Min.X + bounds.Max.X) / 2,
|
||||
(bounds.Min.Y + bounds.Max.Y) / 2,
|
||||
(bounds.Min.Z + bounds.Max.Z) / 2
|
||||
);
|
||||
}
|
||||
return new Point3D(0, 0, 0);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"获取对象位置失败: {ex.Message}");
|
||||
return new Point3D(0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算两点间距离
|
||||
/// </summary>
|
||||
private double CalculateDistance(Point3D point1, Point3D point2)
|
||||
{
|
||||
var dx = point2.X - point1.X;
|
||||
var dy = point2.Y - point1.Y;
|
||||
var dz = point2.Z - point1.Z;
|
||||
return Math.Sqrt(dx * dx + dy * dy + dz * dz);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新对象位置
|
||||
/// </summary>
|
||||
private void UpdateObjectPosition(Point3D newPosition)
|
||||
{
|
||||
try
|
||||
{
|
||||
var doc = NavisApplication.ActiveDocument;
|
||||
var modelItems = new ModelItemCollection { _animatedObject };
|
||||
|
||||
// 正确的增量变换:计算从当前位置到新位置的偏移
|
||||
var incrementalOffset = new Vector3D(
|
||||
newPosition.X - _currentPosition.X,
|
||||
newPosition.Y - _currentPosition.Y,
|
||||
newPosition.Z - _currentPosition.Z
|
||||
);
|
||||
|
||||
// 创建增量变换
|
||||
var incrementalTransform = Transform3D.CreateTranslation(incrementalOffset);
|
||||
|
||||
// 应用增量变换(不重置之前的变换)
|
||||
doc.Models.OverridePermanentTransform(modelItems, incrementalTransform, false);
|
||||
|
||||
LogManager.Debug($"部件位置更新:从({_currentPosition.X:F2},{_currentPosition.Y:F2},{_currentPosition.Z:F2})到({newPosition.X:F2},{newPosition.Y:F2},{newPosition.Z:F2}),增量偏移({incrementalOffset.X:F2},{incrementalOffset.Y:F2},{incrementalOffset.Z:F2})");
|
||||
|
||||
// 更新当前位置
|
||||
_currentPosition = newPosition;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"更新部件位置失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从车辆变换矩阵中获取真实的缩放系数
|
||||
/// 注意:此方法已废弃但保留,用于车辆模型的缩放处理。
|
||||
/// 现在使用场馆部件进行动画,通常不需要复杂的缩放计算。
|
||||
/// </summary>
|
||||
private double GetVehicleScaleFactor()
|
||||
{
|
||||
try
|
||||
{
|
||||
var transform = _animatedObject.Transform;
|
||||
var linear = transform.Linear;
|
||||
|
||||
// 获取变换矩阵的第一行的长度作为缩放系数
|
||||
// 变换矩阵: (a, b, c)
|
||||
// (d, e, f)
|
||||
// (g, h, i)
|
||||
// 缩放系数 ≈ sqrt(a² + b² + c²)
|
||||
var scaleX = Math.Sqrt(
|
||||
linear.Get(0, 0) * linear.Get(0, 0) +
|
||||
linear.Get(0, 1) * linear.Get(0, 1) +
|
||||
linear.Get(0, 2) * linear.Get(0, 2)
|
||||
);
|
||||
|
||||
LogManager.Debug($"车辆缩放系数计算: {scaleX:F2} (变换矩阵第一行模长)");
|
||||
|
||||
// 如果缩放系数接近1550(39.37²),说明是米到英寸的平方缩放
|
||||
if (Math.Abs(scaleX - 1550.0) < 10.0)
|
||||
{
|
||||
var linearScale = Math.Sqrt(scaleX);
|
||||
LogManager.Info($"检测到车辆使用平方缩放系数: {scaleX:F2},已修正为线性缩放系数: {linearScale:F2}");
|
||||
return linearScale;
|
||||
}
|
||||
// 如果缩放系数接近39.37,说明是简单的米到英寸缩放
|
||||
else if (Math.Abs(scaleX - 39.37) < 1.0)
|
||||
{
|
||||
LogManager.Info($"检测到车辆使用线性缩放系数: {scaleX:F2}");
|
||||
return scaleX;
|
||||
}
|
||||
// 如果接近1,说明没有缩放
|
||||
else if (Math.Abs(scaleX - 1.0) < 0.1)
|
||||
{
|
||||
LogManager.Info($"车辆无缩放: {scaleX:F2}");
|
||||
return 1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.Warning($"未知的车辆缩放系数: {scaleX:F2},使用原值");
|
||||
return scaleX;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"获取车辆缩放系数失败: {ex.Message},使用默认值1.0");
|
||||
return 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取文档单位信息(用于日志记录)
|
||||
/// </summary>
|
||||
private string GetDocumentUnitsInfo()
|
||||
{
|
||||
try
|
||||
{
|
||||
var doc = NavisApplication.ActiveDocument;
|
||||
var units = doc.Units;
|
||||
LogManager.Debug($"[单位检测] 文档单位: {units}");
|
||||
return units.ToString();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"获取文档单位信息失败: {ex.Message}");
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取模型当前变换
|
||||
/// </summary>
|
||||
private Transform3D GetCurrentTransform(ModelItem item)
|
||||
{
|
||||
// 获取包围盒中心作为参考点
|
||||
var boundingBox = item.BoundingBox();
|
||||
var center = boundingBox.Center;
|
||||
|
||||
// 创建基于中心点的单位变换
|
||||
return Transform3D.CreateTranslation(new Vector3D(center.X, center.Y, center.Z));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用 Clash Detective 集成进行碰撞检测并使用缓存的碰撞结果
|
||||
/// </summary>
|
||||
private void CheckAndHighlightCollisionsWithClashDetective()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_animatedObject == null)
|
||||
return;
|
||||
|
||||
// 使用 Clash Detective 集成进行碰撞检测
|
||||
var collisionResults = ClashDetectiveIntegration.Instance.DetectCollisions(_animatedObject);
|
||||
|
||||
// 高亮显示碰撞对象
|
||||
ClashDetectiveIntegration.Instance.HighlightCollisions(collisionResults);
|
||||
|
||||
// 缓存碰撞结果,动画结束后统一处理
|
||||
if (collisionResults.Count > 0)
|
||||
{
|
||||
LogManager.Info($"=== [动画运行中] 检测到 {collisionResults.Count} 个碰撞,开始记录详细位置 ===");
|
||||
|
||||
// 缓存所有碰撞结果,包含位置信息
|
||||
var animatedObjectPosition = GetObjectPosition(_animatedObject);
|
||||
LogManager.Info($"[动画位置] 动画对象 {_animatedObject.DisplayName}: ({animatedObjectPosition.X:F2},{animatedObjectPosition.Y:F2},{animatedObjectPosition.Z:F2})");
|
||||
|
||||
int collisionIndex = 0;
|
||||
foreach (var collision in collisionResults)
|
||||
{
|
||||
collisionIndex++;
|
||||
var collisionObjectPosition = GetObjectPosition(collision.Item2);
|
||||
LogManager.Info($"[碰撞位置{collisionIndex}] 动画对象 vs {collision.Item2.DisplayName}:");
|
||||
LogManager.Info($" 动画物体位置: ({animatedObjectPosition.X:F2},{animatedObjectPosition.Y:F2},{animatedObjectPosition.Z:F2})");
|
||||
LogManager.Info($" 碰撞物体位置: ({collisionObjectPosition.X:F2},{collisionObjectPosition.Y:F2},{collisionObjectPosition.Z:F2})");
|
||||
LogManager.Info($" 两物体距离: {CalculateDistance(animatedObjectPosition, collisionObjectPosition):F2}");
|
||||
LogManager.Info($" 碰撞状态: 已检测到碰撞");
|
||||
|
||||
ClashDetectiveIntegration.Instance.CacheCollisionDuringAnimation(_animatedObject, animatedObjectPosition, collision.Item2, collisionObjectPosition);
|
||||
}
|
||||
|
||||
LogManager.Info($"=== [动画运行中] 位置记录完成 ===");
|
||||
}
|
||||
|
||||
LogManager.Debug($"碰撞检测完成: {collisionResults.Count} 个碰撞 (已缓存)");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"碰撞检测失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置动态碰撞检测(实时模式,集成 Clash Detective)
|
||||
/// </summary>
|
||||
private void SetupDynamicClashDetection()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 初始化 Clash Detective 集成
|
||||
ClashDetectiveIntegration.Instance.Initialize();
|
||||
|
||||
// 订阅碰撞检测事件
|
||||
ClashDetectiveIntegration.Instance.CollisionDetected += OnClashDetectiveCollisionDetected;
|
||||
|
||||
LogManager.Info("动态碰撞检测设置完成(实时模式)");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"设置动态碰撞检测失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 处理 Clash Detective 碰撞检测事件
|
||||
/// </summary>
|
||||
private void OnClashDetectiveCollisionDetected(object sender, CollisionDetectedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 将碰撞结果转发到动画管理器的事件
|
||||
OnCollisionDetected(e);
|
||||
|
||||
LogManager.Debug($"Clash Detective 检测到 {e.CollisionCount} 个碰撞");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"处理 Clash Detective 碰撞事件失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 触发碰撞检测事件
|
||||
/// </summary>
|
||||
protected virtual void OnCollisionDetected(CollisionDetectedEventArgs e)
|
||||
{
|
||||
CollisionDetected?.Invoke(this, e);
|
||||
}
|
||||
|
||||
// 已删除:CheckAndHighlightCollisions - 使用Clash Detective集成代替
|
||||
|
||||
/// <summary>
|
||||
/// 检查两个包围盒是否相交
|
||||
/// </summary>
|
||||
private bool BoundingBoxesIntersect(BoundingBox3D box1, BoundingBox3D box2)
|
||||
{
|
||||
return !(box1.Max.X < box2.Min.X || box2.Max.X < box1.Min.X ||
|
||||
box1.Max.Y < box2.Min.Y || box2.Max.Y < box1.Min.Y ||
|
||||
box1.Max.Z < box2.Min.Z || box2.Max.Z < box1.Min.Z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置动画持续时间
|
||||
/// </summary>
|
||||
public void SetAnimationDuration(double durationSeconds)
|
||||
{
|
||||
_animationDuration = Math.Max(1.0, durationSeconds);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取动画状态
|
||||
/// </summary>
|
||||
public bool IsAnimating => _animationTimer != null && _animationTimer.Enabled;
|
||||
|
||||
/// <summary>
|
||||
/// 获取 TimeLiner 是否可用
|
||||
/// </summary>
|
||||
public bool IsTimeLinerAvailable => _timeLinerManager?.IsTimeLinerAvailable ?? false;
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前动画状态
|
||||
/// </summary>
|
||||
public AnimationState CurrentState => _currentState;
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前 TimeLiner 任务ID
|
||||
/// </summary>
|
||||
public string CurrentTaskId => _currentTaskId;
|
||||
|
||||
/// <summary>
|
||||
/// 获取 TimeLiner 集成管理器
|
||||
/// </summary>
|
||||
public TimeLinerIntegrationManager TimeLinerManager => _timeLinerManager;
|
||||
|
||||
/// <summary>
|
||||
/// 资源清理
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
StopAnimation();
|
||||
ResetAnimation();
|
||||
|
||||
// 清理 TimeLiner 资源
|
||||
if (_timeLinerManager != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
_timeLinerManager.Dispose();
|
||||
_timeLinerManager = null;
|
||||
LogManager.Info("TimeLiner 集成资源已清理");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Warning($"清理 TimeLiner 资源时出现警告: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 快速测试TimeLiner API的可用性
|
||||
/// </summary>
|
||||
public bool TestTimeLinerAPI()
|
||||
{
|
||||
try
|
||||
{
|
||||
var doc = NavisApplication.ActiveDocument;
|
||||
|
||||
// 尝试访问TimeLiner
|
||||
var timeliner = doc.Timeliner;
|
||||
if (timeliner != null)
|
||||
{
|
||||
LogManager.Info("TimeLiner API基本可用");
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.Info("TimeLiner API不可用");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Info($"TimeLiner API测试失败: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 简化的动画设置方法,支持直接传入部件和路径
|
||||
/// </summary>
|
||||
/// <param name="component">部件模型</param>
|
||||
/// <param name="pathPoints">路径点列表</param>
|
||||
/// <param name="durationSeconds">动画持续时间(秒)</param>
|
||||
public bool SetupSimpleAnimation(ModelItem component, List<Point3D> pathPoints, double durationSeconds = 10.0)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (component == null)
|
||||
{
|
||||
LogManager.Error("部件模型不能为空");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pathPoints == null || pathPoints.Count < 2)
|
||||
{
|
||||
LogManager.Error("路径点数量必须至少为2个");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 使用现有的SetupAnimation方法
|
||||
SetupAnimation(component, pathPoints, durationSeconds);
|
||||
|
||||
LogManager.Info($"简化动画设置成功:部件={component.DisplayName}, 路径点数={pathPoints.Count}, 时长={durationSeconds}秒");
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"简化动画设置失败: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前选中的部件模型(简化版本)
|
||||
/// </summary>
|
||||
/// <returns>选中的部件模型,如果没有选中或选中多个则返回null</returns>
|
||||
public static ModelItem GetSelectedComponent()
|
||||
{
|
||||
try
|
||||
{
|
||||
var doc = NavisApplication.ActiveDocument;
|
||||
var selectedItems = doc.CurrentSelection.SelectedItems;
|
||||
|
||||
if (selectedItems.Count == 1)
|
||||
{
|
||||
var item = selectedItems.First;
|
||||
if (item.HasGeometry)
|
||||
{
|
||||
LogManager.Info($"已获取选中部件: {item.DisplayName}");
|
||||
return item;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.Warning("选中的项目没有几何体,可能不适合用于动画");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else if (selectedItems.Count == 0)
|
||||
{
|
||||
LogManager.Info("没有选中任何项目");
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.Info($"选中了{selectedItems.Count}个项目,请只选择一个部件");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"获取选中部件失败: {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置并触发状态变更事件
|
||||
/// </summary>
|
||||
/// <param name="newState">新的动画状态</param>
|
||||
private void SetState(AnimationState newState)
|
||||
{
|
||||
if (_currentState != newState)
|
||||
{
|
||||
_currentState = newState;
|
||||
StateChanged?.Invoke(this, _currentState);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建动画
|
||||
/// </summary>
|
||||
/// <param name="animatedObject">要动画化的模型对象</param>
|
||||
/// <param name="pathPoints">路径点列表</param>
|
||||
/// <param name="durationSeconds">动画持续时间(秒)</param>
|
||||
public void CreateAnimation(ModelItem animatedObject, List<Point3D> pathPoints, double durationSeconds = 10.0)
|
||||
{
|
||||
SetupAnimation(animatedObject, pathPoints, durationSeconds);
|
||||
SetState(AnimationState.Ready);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,897 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using Autodesk.Navisworks.Api;
|
||||
using NavisApplication = Autodesk.Navisworks.Api.Application;
|
||||
|
||||
namespace NavisworksTransport
|
||||
{
|
||||
/// <summary>
|
||||
/// 模型分层拆分对话框
|
||||
/// </summary>
|
||||
public partial class ModelSplitterDialog : Form
|
||||
{
|
||||
#region 私有字段
|
||||
|
||||
private ModelSplitterManager _splitterManager;
|
||||
private FloorDetector _floorDetector;
|
||||
private AttributeGrouper _attributeGrouper;
|
||||
private List<ModelSplitterManager.SplitResult> _previewResults;
|
||||
private bool _isProcessing = false;
|
||||
|
||||
// UI控件
|
||||
private ComboBox _strategyComboBox;
|
||||
private ComboBox _attributeComboBox;
|
||||
private TextBox _outputDirectoryTextBox;
|
||||
private Button _browseDirectoryButton;
|
||||
private TextBox _fileNamePatternTextBox;
|
||||
private CheckBox _includeEmptyLayersCheckBox;
|
||||
private CheckBox _createSubDirectoriesCheckBox;
|
||||
private CheckBox _generateReportCheckBox;
|
||||
private ListView _previewListView;
|
||||
private ProgressBar _progressBar;
|
||||
private Label _statusLabel;
|
||||
private Button _previewButton;
|
||||
private Button _executeButton;
|
||||
private Button _cancelButton;
|
||||
private Button _helpButton;
|
||||
|
||||
// 高级设置控件
|
||||
private NumericUpDown _elevationToleranceNumeric;
|
||||
private NumericUpDown _minFloorHeightNumeric;
|
||||
private ComboBox _fileFormatComboBox;
|
||||
|
||||
#endregion
|
||||
|
||||
#region 构造函数
|
||||
|
||||
public ModelSplitterDialog()
|
||||
{
|
||||
InitializeComponent();
|
||||
InitializeManagers();
|
||||
LoadInitialData();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 初始化方法
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.Text = "模型分层拆分工具";
|
||||
this.Size = new Size(800, 720);
|
||||
this.StartPosition = FormStartPosition.CenterParent;
|
||||
this.FormBorderStyle = FormBorderStyle.FixedDialog;
|
||||
this.MaximizeBox = false;
|
||||
this.MinimizeBox = false;
|
||||
this.ShowInTaskbar = false;
|
||||
|
||||
CreateControls();
|
||||
LayoutControls();
|
||||
AttachEventHandlers();
|
||||
}
|
||||
|
||||
private void CreateControls()
|
||||
{
|
||||
// 分层策略
|
||||
var strategyLabel = new Label
|
||||
{
|
||||
Text = "分层策略:",
|
||||
AutoSize = true,
|
||||
Font = new Font("微软雅黑", 8, FontStyle.Bold)
|
||||
};
|
||||
|
||||
_strategyComboBox = new ComboBox
|
||||
{
|
||||
DropDownStyle = ComboBoxStyle.DropDownList,
|
||||
Font = new Font("微软雅黑", 8)
|
||||
};
|
||||
_strategyComboBox.Items.AddRange(new object[]
|
||||
{
|
||||
"按楼层分层",
|
||||
"按自定义属性分层",
|
||||
"按类别分层",
|
||||
"按高程范围分层"
|
||||
});
|
||||
_strategyComboBox.SelectedIndex = 0;
|
||||
|
||||
// 属性选择
|
||||
var attributeLabel = new Label
|
||||
{
|
||||
Text = "分层属性:",
|
||||
AutoSize = true,
|
||||
Font = new Font("微软雅黑", 8, FontStyle.Bold)
|
||||
};
|
||||
|
||||
_attributeComboBox = new ComboBox
|
||||
{
|
||||
DropDownStyle = ComboBoxStyle.DropDownList,
|
||||
Font = new Font("微软雅黑", 8)
|
||||
};
|
||||
|
||||
// 输出目录
|
||||
var outputLabel = new Label
|
||||
{
|
||||
Text = "输出目录:",
|
||||
AutoSize = true,
|
||||
Font = new Font("微软雅黑", 8, FontStyle.Bold)
|
||||
};
|
||||
|
||||
_outputDirectoryTextBox = new TextBox
|
||||
{
|
||||
Font = new Font("微软雅黑", 8),
|
||||
Text = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "NavisworksSplit")
|
||||
};
|
||||
|
||||
_browseDirectoryButton = new Button
|
||||
{
|
||||
Text = "浏览...",
|
||||
Font = new Font("微软雅黑", 8),
|
||||
Size = new Size(80, 30)
|
||||
};
|
||||
|
||||
// 文件命名模式
|
||||
var patternLabel = new Label
|
||||
{
|
||||
Text = "文件命名:",
|
||||
AutoSize = true,
|
||||
Font = new Font("微软雅黑", 8, FontStyle.Bold)
|
||||
};
|
||||
|
||||
_fileNamePatternTextBox = new TextBox
|
||||
{
|
||||
Font = new Font("微软雅黑", 8),
|
||||
Text = "{ProjectName}_{LayerName}"
|
||||
};
|
||||
|
||||
// 选项设置
|
||||
_includeEmptyLayersCheckBox = new CheckBox
|
||||
{
|
||||
Text = "包含空分层",
|
||||
Font = new Font("微软雅黑", 8),
|
||||
AutoSize = true
|
||||
};
|
||||
|
||||
_createSubDirectoriesCheckBox = new CheckBox
|
||||
{
|
||||
Text = "创建子目录",
|
||||
Font = new Font("微软雅黑", 8),
|
||||
AutoSize = true,
|
||||
Checked = true
|
||||
};
|
||||
|
||||
_generateReportCheckBox = new CheckBox
|
||||
{
|
||||
Text = "生成报告",
|
||||
Font = new Font("微软雅黑", 8),
|
||||
AutoSize = true,
|
||||
Checked = true
|
||||
};
|
||||
|
||||
// 高级设置
|
||||
var toleranceLabel = new Label
|
||||
{
|
||||
Text = "高程容差(m):",
|
||||
AutoSize = true,
|
||||
Font = new Font("微软雅黑", 8)
|
||||
};
|
||||
|
||||
_elevationToleranceNumeric = new NumericUpDown
|
||||
{
|
||||
Font = new Font("微软雅黑", 8),
|
||||
DecimalPlaces = 1,
|
||||
Minimum = 0.1m,
|
||||
Maximum = 10.0m,
|
||||
Value = 0.5m,
|
||||
Increment = 0.1m
|
||||
};
|
||||
|
||||
var floorHeightLabel = new Label
|
||||
{
|
||||
Text = "最小楼层高度(m):",
|
||||
AutoSize = true,
|
||||
Font = new Font("微软雅黑", 8)
|
||||
};
|
||||
|
||||
_minFloorHeightNumeric = new NumericUpDown
|
||||
{
|
||||
Font = new Font("微软雅黑", 8),
|
||||
DecimalPlaces = 1,
|
||||
Minimum = 1.0m,
|
||||
Maximum = 20.0m,
|
||||
Value = 2.5m,
|
||||
Increment = 0.5m
|
||||
};
|
||||
|
||||
var formatLabel = new Label
|
||||
{
|
||||
Text = "文件格式:",
|
||||
AutoSize = true,
|
||||
Font = new Font("微软雅黑", 8)
|
||||
};
|
||||
|
||||
_fileFormatComboBox = new ComboBox
|
||||
{
|
||||
DropDownStyle = ComboBoxStyle.DropDownList,
|
||||
Font = new Font("微软雅黑", 8)
|
||||
};
|
||||
_fileFormatComboBox.Items.AddRange(new object[] { "nwd", "nwf", "nwc" });
|
||||
_fileFormatComboBox.SelectedIndex = 0;
|
||||
|
||||
// 预览列表
|
||||
var previewLabel = new Label
|
||||
{
|
||||
Text = "分层预览:",
|
||||
AutoSize = true,
|
||||
Font = new Font("微软雅黑", 8, FontStyle.Bold)
|
||||
};
|
||||
|
||||
_previewListView = new ListView
|
||||
{
|
||||
View = System.Windows.Forms.View.Details,
|
||||
FullRowSelect = true,
|
||||
GridLines = true,
|
||||
Font = new Font("微软雅黑", 8)
|
||||
};
|
||||
_previewListView.Columns.Add("分层名称", 150);
|
||||
_previewListView.Columns.Add("元素数量", 80);
|
||||
_previewListView.Columns.Add("输出文件", 200);
|
||||
_previewListView.Columns.Add("状态", 100);
|
||||
|
||||
// 进度和状态
|
||||
_progressBar = new ProgressBar
|
||||
{
|
||||
Visible = false
|
||||
};
|
||||
|
||||
_statusLabel = new Label
|
||||
{
|
||||
Text = "就绪",
|
||||
Font = new Font("微软雅黑", 8),
|
||||
ForeColor = System.Drawing.Color.DarkBlue,
|
||||
AutoSize = true
|
||||
};
|
||||
|
||||
// 按钮
|
||||
_previewButton = new Button
|
||||
{
|
||||
Text = "预览分层",
|
||||
Font = new Font("微软雅黑", 8),
|
||||
Size = new Size(100, 30)
|
||||
};
|
||||
|
||||
_executeButton = new Button
|
||||
{
|
||||
Text = "开始拆分",
|
||||
Font = new Font("微软雅黑", 8),
|
||||
Size = new Size(100, 30),
|
||||
Enabled = false
|
||||
};
|
||||
|
||||
_cancelButton = new Button
|
||||
{
|
||||
Text = "取消",
|
||||
Font = new Font("微软雅黑", 8),
|
||||
Size = new Size(80, 30),
|
||||
DialogResult = DialogResult.Cancel
|
||||
};
|
||||
|
||||
_helpButton = new Button
|
||||
{
|
||||
Text = "帮助",
|
||||
Font = new Font("微软雅黑", 8),
|
||||
Size = new Size(80, 30)
|
||||
};
|
||||
|
||||
// 添加所有控件到窗体
|
||||
this.Controls.AddRange(new Control[]
|
||||
{
|
||||
strategyLabel, _strategyComboBox,
|
||||
attributeLabel, _attributeComboBox,
|
||||
outputLabel, _outputDirectoryTextBox, _browseDirectoryButton,
|
||||
patternLabel, _fileNamePatternTextBox,
|
||||
_includeEmptyLayersCheckBox, _createSubDirectoriesCheckBox, _generateReportCheckBox,
|
||||
toleranceLabel, _elevationToleranceNumeric,
|
||||
floorHeightLabel, _minFloorHeightNumeric,
|
||||
formatLabel, _fileFormatComboBox,
|
||||
previewLabel, _previewListView,
|
||||
_progressBar, _statusLabel,
|
||||
_previewButton, _executeButton, _cancelButton, _helpButton
|
||||
});
|
||||
}
|
||||
|
||||
private void LayoutControls()
|
||||
{
|
||||
int margin = 15;
|
||||
int currentY = margin;
|
||||
int labelHeight = 20;
|
||||
int controlHeight = 25;
|
||||
int spacing = 8;
|
||||
|
||||
// 分层策略
|
||||
var strategyLabel = this.Controls.OfType<Label>().First(l => l.Text == "分层策略:");
|
||||
strategyLabel.Location = new Point(margin, currentY);
|
||||
currentY += labelHeight + 5;
|
||||
|
||||
_strategyComboBox.Location = new Point(margin, currentY);
|
||||
_strategyComboBox.Size = new Size(200, controlHeight);
|
||||
currentY += controlHeight + spacing;
|
||||
|
||||
// 属性选择
|
||||
var attributeLabel = this.Controls.OfType<Label>().First(l => l.Text == "分层属性:");
|
||||
attributeLabel.Location = new Point(margin, currentY);
|
||||
currentY += labelHeight + 5;
|
||||
|
||||
_attributeComboBox.Location = new Point(margin, currentY);
|
||||
_attributeComboBox.Size = new Size(200, controlHeight);
|
||||
currentY += controlHeight + spacing;
|
||||
|
||||
// 输出目录
|
||||
var outputLabel = this.Controls.OfType<Label>().First(l => l.Text == "输出目录:");
|
||||
outputLabel.Location = new Point(margin, currentY);
|
||||
currentY += labelHeight + 5;
|
||||
|
||||
_outputDirectoryTextBox.Location = new Point(margin, currentY);
|
||||
_outputDirectoryTextBox.Size = new Size(500, controlHeight);
|
||||
_browseDirectoryButton.Location = new Point(margin + 510, currentY);
|
||||
currentY += controlHeight + spacing;
|
||||
|
||||
// 文件命名
|
||||
var patternLabel = this.Controls.OfType<Label>().First(l => l.Text == "文件命名:");
|
||||
patternLabel.Location = new Point(margin, currentY);
|
||||
currentY += labelHeight + 5;
|
||||
|
||||
_fileNamePatternTextBox.Location = new Point(margin, currentY);
|
||||
_fileNamePatternTextBox.Size = new Size(300, controlHeight);
|
||||
currentY += controlHeight + spacing;
|
||||
|
||||
// 选项设置
|
||||
_includeEmptyLayersCheckBox.Location = new Point(margin, currentY);
|
||||
_createSubDirectoriesCheckBox.Location = new Point(margin + 120, currentY);
|
||||
_generateReportCheckBox.Location = new Point(margin + 240, currentY);
|
||||
currentY += controlHeight + spacing;
|
||||
|
||||
// 高级设置 - 第一行
|
||||
var toleranceLabel = this.Controls.OfType<Label>().First(l => l.Text == "高程容差(m):");
|
||||
toleranceLabel.Location = new Point(margin, currentY);
|
||||
_elevationToleranceNumeric.Location = new Point(margin + 120, currentY);
|
||||
_elevationToleranceNumeric.Size = new Size(80, controlHeight);
|
||||
|
||||
var floorHeightLabel = this.Controls.OfType<Label>().First(l => l.Text == "最小楼层高度(m):");
|
||||
floorHeightLabel.Location = new Point(margin + 220, currentY);
|
||||
_minFloorHeightNumeric.Location = new Point(margin + 370, currentY);
|
||||
_minFloorHeightNumeric.Size = new Size(80, controlHeight);
|
||||
currentY += controlHeight + spacing;
|
||||
|
||||
// 文件格式
|
||||
var formatLabel = this.Controls.OfType<Label>().First(l => l.Text == "文件格式:");
|
||||
formatLabel.Location = new Point(margin, currentY);
|
||||
_fileFormatComboBox.Location = new Point(margin + 100, currentY);
|
||||
_fileFormatComboBox.Size = new Size(100, controlHeight);
|
||||
currentY += controlHeight + spacing * 2;
|
||||
|
||||
// 预览列表
|
||||
var previewLabel = this.Controls.OfType<Label>().First(l => l.Text == "分层预览:");
|
||||
previewLabel.Location = new Point(margin, currentY);
|
||||
currentY += labelHeight + 5;
|
||||
|
||||
_previewListView.Location = new Point(margin, currentY);
|
||||
_previewListView.Size = new Size(750, 200);
|
||||
currentY += 200 + spacing;
|
||||
|
||||
// 进度条和状态
|
||||
_progressBar.Location = new Point(margin, currentY);
|
||||
_progressBar.Size = new Size(500, 20);
|
||||
_statusLabel.Location = new Point(margin + 500, currentY + 2);
|
||||
currentY += 25 + spacing;
|
||||
|
||||
// 按钮
|
||||
_helpButton.Location = new Point(margin, currentY);
|
||||
_previewButton.Location = new Point(this.Width - 330, currentY);
|
||||
_executeButton.Location = new Point(this.Width - 220, currentY);
|
||||
_cancelButton.Location = new Point(this.Width - 110, currentY);
|
||||
}
|
||||
|
||||
private void AttachEventHandlers()
|
||||
{
|
||||
_strategyComboBox.SelectedIndexChanged += OnStrategyChanged;
|
||||
_attributeComboBox.SelectedIndexChanged += OnAttributeChanged;
|
||||
_browseDirectoryButton.Click += OnBrowseDirectory;
|
||||
_previewButton.Click += OnPreviewClick;
|
||||
_executeButton.Click += OnExecuteClick;
|
||||
_helpButton.Click += OnHelpClick;
|
||||
this.FormClosing += OnFormClosing;
|
||||
}
|
||||
|
||||
private void InitializeManagers()
|
||||
{
|
||||
try
|
||||
{
|
||||
_splitterManager = new ModelSplitterManager();
|
||||
_floorDetector = new FloorDetector();
|
||||
_attributeGrouper = new AttributeGrouper();
|
||||
|
||||
// 订阅事件
|
||||
_splitterManager.ProgressChanged += OnSplitterProgressChanged;
|
||||
_splitterManager.StatusChanged += OnSplitterStatusChanged;
|
||||
_splitterManager.LayerProcessed += OnLayerProcessed;
|
||||
_splitterManager.SplitCompleted += OnSplitCompleted;
|
||||
_splitterManager.ErrorOccurred += OnSplitterError;
|
||||
|
||||
LogManager.Info("[ModelSplitterDialog] 管理器初始化完成");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"[ModelSplitterDialog] 管理器初始化失败: {ex.Message}");
|
||||
MessageBox.Show($"初始化失败: {ex.Message}", "错误",
|
||||
MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadInitialData()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 加载可用属性
|
||||
LoadAvailableAttributes();
|
||||
|
||||
// 设置默认值
|
||||
UpdateAttributeComboBox();
|
||||
|
||||
UpdateStatusLabel("就绪 - 请选择分层策略并点击预览");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"[ModelSplitterDialog] 加载初始数据失败: {ex.Message}");
|
||||
UpdateStatusLabel($"初始化错误: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 事件处理方法
|
||||
|
||||
private void OnStrategyChanged(object sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
UpdateAttributeComboBox();
|
||||
ClearPreview();
|
||||
_executeButton.Enabled = false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"[ModelSplitterDialog] 策略变更处理失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnAttributeChanged(object sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
ClearPreview();
|
||||
_executeButton.Enabled = false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"[ModelSplitterDialog] 属性变更处理失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnBrowseDirectory(object sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var dialog = new FolderBrowserDialog())
|
||||
{
|
||||
dialog.Description = "选择输出目录";
|
||||
dialog.SelectedPath = _outputDirectoryTextBox.Text;
|
||||
|
||||
if (dialog.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
_outputDirectoryTextBox.Text = dialog.SelectedPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"[ModelSplitterDialog] 浏览目录失败: {ex.Message}");
|
||||
MessageBox.Show($"浏览目录失败: {ex.Message}", "错误",
|
||||
MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPreviewClick(object sender, EventArgs e)
|
||||
{
|
||||
if (_isProcessing) return;
|
||||
|
||||
try
|
||||
{
|
||||
_isProcessing = true;
|
||||
_previewButton.Enabled = false;
|
||||
UpdateStatusLabel("正在生成预览...");
|
||||
|
||||
var config = GetCurrentConfiguration();
|
||||
if (!ValidateConfiguration(config))
|
||||
return;
|
||||
|
||||
// 使用同步方法,避免异步复杂性(.NET Framework 4.6兼容)
|
||||
_previewResults = _splitterManager.PreviewSplit(config);
|
||||
|
||||
UpdatePreviewList(_previewResults);
|
||||
_executeButton.Enabled = _previewResults.Count > 0;
|
||||
|
||||
UpdateStatusLabel($"预览完成 - 识别到 {_previewResults.Count} 个分层");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"[ModelSplitterDialog] 预览失败: {ex.Message}");
|
||||
MessageBox.Show($"预览失败: {ex.Message}", "错误",
|
||||
MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
UpdateStatusLabel("预览失败");
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isProcessing = false;
|
||||
_previewButton.Enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnExecuteClick(object sender, EventArgs e)
|
||||
{
|
||||
if (_isProcessing) return;
|
||||
|
||||
try
|
||||
{
|
||||
var result = MessageBox.Show(
|
||||
$"确定要开始分层拆分吗?\n\n将生成 {_previewResults.Count} 个文件到:\n{_outputDirectoryTextBox.Text}",
|
||||
"确认拆分", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
|
||||
|
||||
if (result != DialogResult.Yes)
|
||||
return;
|
||||
|
||||
_isProcessing = true;
|
||||
_executeButton.Enabled = false;
|
||||
_previewButton.Enabled = false;
|
||||
_progressBar.Visible = true;
|
||||
|
||||
var config = GetCurrentConfiguration();
|
||||
|
||||
// 使用同步方法,避免异步复杂性(.NET Framework 4.6兼容)
|
||||
// 在后台线程中执行拆分操作
|
||||
var backgroundWorker = new BackgroundWorker();
|
||||
backgroundWorker.DoWork += (s, args) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var task = _splitterManager.ExecuteSplitAsync(config);
|
||||
task.Wait(); // 等待异步任务完成
|
||||
args.Result = task.Result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
args.Result = ex;
|
||||
}
|
||||
};
|
||||
|
||||
backgroundWorker.RunWorkerCompleted += (s, args) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (args.Result is Exception ex)
|
||||
{
|
||||
throw ex;
|
||||
}
|
||||
|
||||
var results = args.Result as List<ModelSplitterManager.SplitResult>;
|
||||
if (results != null)
|
||||
{
|
||||
ShowCompletionDialog(results);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"[ModelSplitterDialog] 执行拆分失败: {ex.Message}");
|
||||
MessageBox.Show($"拆分失败: {ex.Message}", "错误",
|
||||
MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isProcessing = false;
|
||||
_executeButton.Enabled = true;
|
||||
_previewButton.Enabled = true;
|
||||
_progressBar.Visible = false;
|
||||
}
|
||||
};
|
||||
|
||||
backgroundWorker.RunWorkerAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"[ModelSplitterDialog] 执行拆分失败: {ex.Message}");
|
||||
MessageBox.Show($"拆分失败: {ex.Message}", "错误",
|
||||
MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
_isProcessing = false;
|
||||
_executeButton.Enabled = true;
|
||||
_previewButton.Enabled = true;
|
||||
_progressBar.Visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnHelpClick(object sender, EventArgs e)
|
||||
{
|
||||
string helpText = @"模型分层拆分工具使用说明:
|
||||
|
||||
1. 分层策略:
|
||||
- 按楼层:根据楼层属性自动分层
|
||||
- 按自定义属性:根据指定属性值分层
|
||||
- 按类别:根据模型元素类别分层
|
||||
- 按高程范围:根据Z坐标范围分层
|
||||
|
||||
2. 操作步骤:
|
||||
- 选择分层策略和相关参数
|
||||
- 设置输出目录和文件命名规则
|
||||
- 点击'预览分层'查看分层结果
|
||||
- 确认无误后点击'开始拆分'
|
||||
|
||||
3. 注意事项:
|
||||
- 确保有足够的磁盘空间
|
||||
- 拆分过程中请勿关闭Navisworks
|
||||
- 建议先在小模型上测试
|
||||
|
||||
4. 文件命名变量:
|
||||
- {ProjectName}: 项目名称
|
||||
- {LayerName}: 分层名称
|
||||
- {DateTime}: 当前时间";
|
||||
|
||||
MessageBox.Show(helpText, "帮助", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
}
|
||||
|
||||
private void OnFormClosing(object sender, FormClosingEventArgs e)
|
||||
{
|
||||
if (_isProcessing)
|
||||
{
|
||||
var result = MessageBox.Show("拆分正在进行中,确定要关闭吗?", "确认关闭",
|
||||
MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
|
||||
|
||||
if (result == DialogResult.No)
|
||||
{
|
||||
e.Cancel = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 管理器事件处理
|
||||
|
||||
private void OnSplitterProgressChanged(object sender, ProgressChangedEventArgs e)
|
||||
{
|
||||
if (InvokeRequired)
|
||||
{
|
||||
Invoke(new Action(() => OnSplitterProgressChanged(sender, e)));
|
||||
return;
|
||||
}
|
||||
|
||||
_progressBar.Value = Math.Min(100, Math.Max(0, e.ProgressPercentage));
|
||||
}
|
||||
|
||||
private void OnSplitterStatusChanged(object sender, string status)
|
||||
{
|
||||
if (InvokeRequired)
|
||||
{
|
||||
Invoke(new Action(() => OnSplitterStatusChanged(sender, status)));
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateStatusLabel(status);
|
||||
}
|
||||
|
||||
private void OnLayerProcessed(object sender, ModelSplitterManager.SplitResult result)
|
||||
{
|
||||
if (InvokeRequired)
|
||||
{
|
||||
Invoke(new Action(() => OnLayerProcessed(sender, result)));
|
||||
return;
|
||||
}
|
||||
|
||||
// 更新预览列表中对应项的状态
|
||||
UpdatePreviewItemStatus(result);
|
||||
}
|
||||
|
||||
private void OnSplitCompleted(object sender, List<ModelSplitterManager.SplitResult> results)
|
||||
{
|
||||
if (InvokeRequired)
|
||||
{
|
||||
Invoke(new Action(() => OnSplitCompleted(sender, results)));
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateStatusLabel($"拆分完成 - 成功: {results.Count(r => r.Success)}, 失败: {results.Count(r => !r.Success)}");
|
||||
}
|
||||
|
||||
private void OnSplitterError(object sender, Exception ex)
|
||||
{
|
||||
if (InvokeRequired)
|
||||
{
|
||||
Invoke(new Action(() => OnSplitterError(sender, ex)));
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateStatusLabel($"错误: {ex.Message}");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 辅助方法
|
||||
|
||||
private void LoadAvailableAttributes()
|
||||
{
|
||||
try
|
||||
{
|
||||
var document = NavisApplication.ActiveDocument;
|
||||
if (document?.Models?.Count > 0)
|
||||
{
|
||||
var allItems = document.Models.RootItemDescendantsAndSelf;
|
||||
|
||||
// 转换为ModelItemCollection
|
||||
var itemCollection = new ModelItemCollection();
|
||||
itemCollection.AddRange(allItems);
|
||||
|
||||
// 加载楼层属性
|
||||
var floorAttributes = _floorDetector.GetAvailableFloorAttributes(itemCollection);
|
||||
|
||||
// 加载所有属性
|
||||
var allAttributes = _attributeGrouper.GetAvailableAttributes(itemCollection);
|
||||
|
||||
// 合并并去重
|
||||
var combinedAttributes = floorAttributes.Union(allAttributes).OrderBy(a => a).ToList();
|
||||
|
||||
_attributeComboBox.Items.Clear();
|
||||
_attributeComboBox.Items.AddRange(combinedAttributes.ToArray());
|
||||
|
||||
if (combinedAttributes.Count > 0)
|
||||
{
|
||||
_attributeComboBox.SelectedIndex = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"[ModelSplitterDialog] 加载属性失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateAttributeComboBox()
|
||||
{
|
||||
var strategy = GetSelectedStrategy();
|
||||
_attributeComboBox.Enabled = (strategy == ModelSplitterManager.SplitStrategy.ByFloor ||
|
||||
strategy == ModelSplitterManager.SplitStrategy.ByAttribute);
|
||||
}
|
||||
|
||||
private ModelSplitterManager.SplitStrategy GetSelectedStrategy()
|
||||
{
|
||||
switch (_strategyComboBox.SelectedIndex)
|
||||
{
|
||||
case 0: return ModelSplitterManager.SplitStrategy.ByFloor;
|
||||
case 1: return ModelSplitterManager.SplitStrategy.ByAttribute;
|
||||
case 2: return ModelSplitterManager.SplitStrategy.ByCategory;
|
||||
case 3: return ModelSplitterManager.SplitStrategy.ByElevation;
|
||||
default: return ModelSplitterManager.SplitStrategy.ByFloor;
|
||||
}
|
||||
}
|
||||
|
||||
private ModelSplitterManager.SplitConfiguration GetCurrentConfiguration()
|
||||
{
|
||||
return new ModelSplitterManager.SplitConfiguration
|
||||
{
|
||||
Strategy = GetSelectedStrategy(),
|
||||
AttributeName = _attributeComboBox.Text,
|
||||
OutputDirectory = _outputDirectoryTextBox.Text,
|
||||
FileNamePattern = _fileNamePatternTextBox.Text,
|
||||
IncludeEmptyLayers = _includeEmptyLayersCheckBox.Checked,
|
||||
CreateSubDirectories = _createSubDirectoriesCheckBox.Checked,
|
||||
GenerateReport = _generateReportCheckBox.Checked,
|
||||
ElevationTolerance = (double)_elevationToleranceNumeric.Value,
|
||||
MinFloorHeight = (double)_minFloorHeightNumeric.Value
|
||||
};
|
||||
}
|
||||
|
||||
private bool ValidateConfiguration(ModelSplitterManager.SplitConfiguration config)
|
||||
{
|
||||
if (string.IsNullOrEmpty(config.OutputDirectory))
|
||||
{
|
||||
MessageBox.Show("请指定输出目录", "验证失败", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((config.Strategy == ModelSplitterManager.SplitStrategy.ByFloor ||
|
||||
config.Strategy == ModelSplitterManager.SplitStrategy.ByAttribute) &&
|
||||
string.IsNullOrEmpty(config.AttributeName))
|
||||
{
|
||||
MessageBox.Show("请选择分层属性", "验证失败", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(config.FileNamePattern))
|
||||
{
|
||||
MessageBox.Show("请指定文件命名模式", "验证失败", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void UpdatePreviewList(List<ModelSplitterManager.SplitResult> results)
|
||||
{
|
||||
_previewListView.Items.Clear();
|
||||
|
||||
foreach (var result in results)
|
||||
{
|
||||
var item = new ListViewItem(result.LayerName);
|
||||
item.SubItems.Add(result.ItemCount.ToString());
|
||||
item.SubItems.Add(Path.GetFileName(result.OutputFilePath));
|
||||
item.SubItems.Add("待处理");
|
||||
item.Tag = result;
|
||||
|
||||
_previewListView.Items.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdatePreviewItemStatus(ModelSplitterManager.SplitResult result)
|
||||
{
|
||||
foreach (ListViewItem item in _previewListView.Items)
|
||||
{
|
||||
if (item.Tag is ModelSplitterManager.SplitResult itemResult &&
|
||||
itemResult.LayerName == result.LayerName)
|
||||
{
|
||||
item.SubItems[3].Text = result.Success ? "完成" : "失败";
|
||||
item.ForeColor = result.Success ? System.Drawing.Color.Green : System.Drawing.Color.Red;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearPreview()
|
||||
{
|
||||
_previewListView.Items.Clear();
|
||||
_previewResults = null;
|
||||
}
|
||||
|
||||
private void UpdateStatusLabel(string status)
|
||||
{
|
||||
_statusLabel.Text = status;
|
||||
_statusLabel.Refresh();
|
||||
}
|
||||
|
||||
private void ShowCompletionDialog(List<ModelSplitterManager.SplitResult> results)
|
||||
{
|
||||
int successCount = results.Count(r => r.Success);
|
||||
int failCount = results.Count(r => !r.Success);
|
||||
|
||||
string message = $"分层拆分完成!\n\n成功: {successCount} 个文件\n失败: {failCount} 个文件\n\n";
|
||||
|
||||
if (successCount > 0)
|
||||
{
|
||||
message += $"文件已保存到: {_outputDirectoryTextBox.Text}\n\n";
|
||||
}
|
||||
|
||||
message += "是否打开输出目录?";
|
||||
|
||||
var result = MessageBox.Show(message, "拆分完成",
|
||||
MessageBoxButtons.YesNo, MessageBoxIcon.Information);
|
||||
|
||||
if (result == DialogResult.Yes && Directory.Exists(_outputDirectoryTextBox.Text))
|
||||
{
|
||||
System.Diagnostics.Process.Start("explorer.exe", _outputDirectoryTextBox.Text);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -403,10 +403,10 @@ namespace NavisworksTransport.UI.WPF.Commands
|
||||
/// </summary>
|
||||
public class PreviewSplitCommand : ILayerManagementCommand<PreviewSplitResult>
|
||||
{
|
||||
private readonly SimplifiedModelSplitterManager _modelSplitterManager;
|
||||
private readonly SimplifiedModelSplitterManager.SplitConfiguration _config;
|
||||
private readonly ModelSplitterManager _modelSplitterManager;
|
||||
private readonly ModelSplitterManager.SplitConfiguration _config;
|
||||
|
||||
public PreviewSplitCommand(SimplifiedModelSplitterManager modelSplitterManager, SimplifiedModelSplitterManager.SplitConfiguration config)
|
||||
public PreviewSplitCommand(ModelSplitterManager modelSplitterManager, ModelSplitterManager.SplitConfiguration config)
|
||||
{
|
||||
_modelSplitterManager = modelSplitterManager ?? throw new ArgumentNullException(nameof(modelSplitterManager));
|
||||
_config = config ?? throw new ArgumentNullException(nameof(config));
|
||||
@ -485,20 +485,20 @@ namespace NavisworksTransport.UI.WPF.Commands
|
||||
/// <summary>
|
||||
/// 根据分层策略和分层名称生成属性描述
|
||||
/// </summary>
|
||||
private string GetLayerAttributeDescription(SimplifiedModelSplitterManager.SplitStrategy strategy, string layerName)
|
||||
private string GetLayerAttributeDescription(ModelSplitterManager.SplitStrategy strategy, string layerName)
|
||||
{
|
||||
switch (strategy)
|
||||
{
|
||||
case SimplifiedModelSplitterManager.SplitStrategy.BySmartFloorDetection:
|
||||
case ModelSplitterManager.SplitStrategy.BySmartFloorDetection:
|
||||
return "智能楼层检测";
|
||||
|
||||
case SimplifiedModelSplitterManager.SplitStrategy.ByFloorAttribute:
|
||||
case ModelSplitterManager.SplitStrategy.ByFloorAttribute:
|
||||
return "按楼层属性";
|
||||
|
||||
case SimplifiedModelSplitterManager.SplitStrategy.ByZoneAttribute:
|
||||
case ModelSplitterManager.SplitStrategy.ByZoneAttribute:
|
||||
return "按区域属性";
|
||||
|
||||
case SimplifiedModelSplitterManager.SplitStrategy.BySubSystemAttribute:
|
||||
case ModelSplitterManager.SplitStrategy.BySubSystemAttribute:
|
||||
return "按子系统属性";
|
||||
|
||||
default:
|
||||
@ -512,10 +512,10 @@ namespace NavisworksTransport.UI.WPF.Commands
|
||||
/// </summary>
|
||||
public class ExecuteSplitCommand : ILayerManagementCommand<ExecuteSplitResult>
|
||||
{
|
||||
private readonly SimplifiedModelSplitterManager _modelSplitterManager;
|
||||
private readonly SimplifiedModelSplitterManager.SplitConfiguration _config;
|
||||
private readonly ModelSplitterManager _modelSplitterManager;
|
||||
private readonly ModelSplitterManager.SplitConfiguration _config;
|
||||
|
||||
public ExecuteSplitCommand(SimplifiedModelSplitterManager modelSplitterManager, SimplifiedModelSplitterManager.SplitConfiguration config)
|
||||
public ExecuteSplitCommand(ModelSplitterManager modelSplitterManager, ModelSplitterManager.SplitConfiguration config)
|
||||
{
|
||||
_modelSplitterManager = modelSplitterManager ?? throw new ArgumentNullException(nameof(modelSplitterManager));
|
||||
_config = config ?? throw new ArgumentNullException(nameof(config));
|
||||
@ -715,11 +715,11 @@ namespace NavisworksTransport.UI.WPF.Commands
|
||||
/// </summary>
|
||||
public class TestExportCommand : ILayerManagementCommand<TestExportResult>
|
||||
{
|
||||
private readonly SimplifiedModelSplitterManager _modelSplitterManager;
|
||||
private readonly ModelSplitterManager _modelSplitterManager;
|
||||
private readonly string _outputPath;
|
||||
private readonly int _maxItems;
|
||||
|
||||
public TestExportCommand(SimplifiedModelSplitterManager modelSplitterManager, string outputPath, int maxItems = 5)
|
||||
public TestExportCommand(ModelSplitterManager modelSplitterManager, string outputPath, int maxItems = 5)
|
||||
{
|
||||
_modelSplitterManager = modelSplitterManager ?? throw new ArgumentNullException(nameof(modelSplitterManager));
|
||||
_outputPath = outputPath ?? throw new ArgumentNullException(nameof(outputPath));
|
||||
|
||||
@ -1481,42 +1481,97 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
{
|
||||
try
|
||||
{
|
||||
// 取消PathAnimationManager事件订阅
|
||||
if (_pathAnimationManager != null)
|
||||
{
|
||||
_pathAnimationManager.ProgressChanged -= OnAnimationProgressChanged;
|
||||
_pathAnimationManager.StateChanged -= OnAnimationStateChanged;
|
||||
}
|
||||
LogManager.Info("开始清理AnimationControlViewModel资源");
|
||||
|
||||
// 停止当前动画
|
||||
_pathAnimationManager?.StopAnimation();
|
||||
|
||||
// 清理动画管理器
|
||||
_pathAnimationManager?.Dispose();
|
||||
|
||||
// 清理LogisticsAnimationManager缓存
|
||||
_logisticsAnimationManager?.ClearExclusionCache();
|
||||
_logisticsAnimationManager?.Dispose();
|
||||
|
||||
// 取消碰撞检测事件订阅
|
||||
if (_clashIntegration != null)
|
||||
{
|
||||
_clashIntegration.CollisionDetected -= OnCollisionDetected;
|
||||
}
|
||||
|
||||
// 清理防抖定时器
|
||||
// 1. 优先清理防抖定时器,避免在清理过程中触发更新
|
||||
if (_parameterUpdateTimer != null)
|
||||
{
|
||||
_parameterUpdateTimer.Stop();
|
||||
_parameterUpdateTimer.Tick -= OnParameterUpdateTimerTick;
|
||||
_parameterUpdateTimer = null;
|
||||
try
|
||||
{
|
||||
_parameterUpdateTimer.Stop();
|
||||
_parameterUpdateTimer.Tick -= OnParameterUpdateTimerTick;
|
||||
_parameterUpdateTimer = null;
|
||||
LogManager.Debug("防抖定时器清理完成");
|
||||
}
|
||||
catch (Exception timerEx)
|
||||
{
|
||||
LogManager.Warning($"清理防抖定时器时出现警告: {timerEx.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 取消事件订阅(在停止动画之前,避免事件处理中访问已释放的对象)
|
||||
if (_pathAnimationManager != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
_pathAnimationManager.ProgressChanged -= OnAnimationProgressChanged;
|
||||
_pathAnimationManager.StateChanged -= OnAnimationStateChanged;
|
||||
LogManager.Debug("PathAnimationManager事件订阅取消完成");
|
||||
}
|
||||
catch (Exception eventEx)
|
||||
{
|
||||
LogManager.Warning($"取消PathAnimationManager事件订阅时出现警告: {eventEx.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
if (_clashIntegration != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
_clashIntegration.CollisionDetected -= OnCollisionDetected;
|
||||
LogManager.Debug("碰撞检测事件订阅取消完成");
|
||||
}
|
||||
catch (Exception eventEx)
|
||||
{
|
||||
LogManager.Warning($"取消碰撞检测事件订阅时出现警告: {eventEx.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 直接清理动画管理器(PathAnimationManager.Dispose内部已经优化了重复调用)
|
||||
// 不需要在这里再次调用StopAnimation,让PathAnimationManager自己处理
|
||||
if (_pathAnimationManager != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 检查Navisworks应用程序是否仍然可用
|
||||
var app = Autodesk.Navisworks.Api.Application.ActiveDocument;
|
||||
if (app != null && app.Models != null)
|
||||
{
|
||||
_pathAnimationManager.Dispose();
|
||||
LogManager.Debug("PathAnimationManager清理完成");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.Info("Navisworks文档已不可用,跳过PathAnimationManager清理操作");
|
||||
}
|
||||
}
|
||||
catch (Exception disposeEx)
|
||||
{
|
||||
LogManager.Warning($"清理PathAnimationManager时出现警告: {disposeEx.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 清理LogisticsAnimationManager缓存
|
||||
if (_logisticsAnimationManager != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logisticsAnimationManager.ClearExclusionCache();
|
||||
_logisticsAnimationManager.Dispose();
|
||||
LogManager.Debug("LogisticsAnimationManager缓存清理完成");
|
||||
}
|
||||
catch (Exception logisticsEx)
|
||||
{
|
||||
LogManager.Warning($"清理LogisticsAnimationManager缓存时出现警告: {logisticsEx.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
LogManager.Info("AnimationControlViewModel资源清理完成");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"AnimationControlViewModel资源清理失败: {ex.Message}");
|
||||
LogManager.Error($"AnimationControlViewModel资源清理过程中发生异常: {ex.Message}");
|
||||
// 即使清理过程中发生异常,也不应该抛出,因为这会干扰应用程序的正常关闭
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -40,7 +40,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
#region 私有字段和依赖注入
|
||||
|
||||
private readonly FloorDetector _floorDetector;
|
||||
private readonly SimplifiedModelSplitterManager _modelSplitterManager;
|
||||
private readonly ModelSplitterManager _modelSplitterManager;
|
||||
private readonly AttributeGrouper _attributeGrouper;
|
||||
private readonly UIStateManager _uiStateManager;
|
||||
private CancellationTokenSource _cancellationTokenSource;
|
||||
@ -801,7 +801,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
|
||||
// 初始化业务逻辑组件
|
||||
_floorDetector = new FloorDetector();
|
||||
_modelSplitterManager = new SimplifiedModelSplitterManager();
|
||||
_modelSplitterManager = new ModelSplitterManager();
|
||||
_attributeGrouper = new AttributeGrouper();
|
||||
|
||||
// 初始化命令 - 使用异步Command Pattern
|
||||
@ -1209,12 +1209,12 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
{
|
||||
// 2. 纯业务逻辑执行(后台线程,不使用UIStateManager)
|
||||
var strategy = GetSplitStrategyFromString(SelectedSplitStrategy);
|
||||
var config = new SimplifiedModelSplitterManager.SplitConfiguration
|
||||
var config = new ModelSplitterManager.SplitConfiguration
|
||||
{
|
||||
Strategy = strategy,
|
||||
OutputDirectory = OutputDirectory,
|
||||
MaxDepth = CurrentDepthValue, // 传递深度参数
|
||||
ExportOptions = new SimplifiedModelSplitterManager.NwdExportUserOptions
|
||||
ExportOptions = new ModelSplitterManager.NwdExportUserOptions
|
||||
{
|
||||
EmbedXrefs = EmbedXrefs,
|
||||
PreventObjectPropertyExport = PreventObjectPropertyExport,
|
||||
@ -1345,12 +1345,12 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
|
||||
// 2. 纯业务逻辑执行(后台线程,不使用UIStateManager)
|
||||
var strategy = GetSplitStrategyFromString(SelectedSplitStrategy);
|
||||
var config = new SimplifiedModelSplitterManager.SplitConfiguration
|
||||
var config = new ModelSplitterManager.SplitConfiguration
|
||||
{
|
||||
Strategy = strategy,
|
||||
OutputDirectory = selectedDirectory,
|
||||
MaxDepth = CurrentDepthValue, // 传递深度参数
|
||||
ExportOptions = new SimplifiedModelSplitterManager.NwdExportUserOptions
|
||||
ExportOptions = new ModelSplitterManager.NwdExportUserOptions
|
||||
{
|
||||
EmbedXrefs = EmbedXrefs,
|
||||
PreventObjectPropertyExport = PreventObjectPropertyExport,
|
||||
@ -1358,8 +1358,8 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
}
|
||||
};
|
||||
|
||||
// 将选中的 SplitPreviewItem 转换为 SplitPreviewResult 以便传递给 SimplifiedModelSplitterManager
|
||||
var previewResultsForSave = layersToSave.Select(item => new SimplifiedModelSplitterManager.SplitPreviewResult
|
||||
// 将选中的 SplitPreviewItem 转换为 SplitPreviewResult 以便传递给 ModelSplitterManager
|
||||
var previewResultsForSave = layersToSave.Select(item => new ModelSplitterManager.SplitPreviewResult
|
||||
{
|
||||
LayerName = item.LayerName,
|
||||
LayerAttribute = item.LayerAttribute,
|
||||
@ -1369,7 +1369,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
|
||||
LogManager.Info($"[LayerManagementViewModel] 开始执行分层,输出目录: {selectedDirectory}");
|
||||
|
||||
// 直接调用 SimplifiedModelSplitterManager 的方法处理选中的分层
|
||||
// 直接调用 ModelSplitterManager 的方法处理选中的分层
|
||||
await ExecuteSelectedLayersAsync(_modelSplitterManager, config, previewResultsForSave, _cancellationTokenSource.Token);
|
||||
|
||||
LogManager.Info($"[LayerManagementViewModel] 分层执行完成");
|
||||
@ -1410,9 +1410,9 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
/// 执行选中的分层保存
|
||||
/// </summary>
|
||||
private async Task ExecuteSelectedLayersAsync(
|
||||
SimplifiedModelSplitterManager modelSplitterManager,
|
||||
SimplifiedModelSplitterManager.SplitConfiguration config,
|
||||
List<SimplifiedModelSplitterManager.SplitPreviewResult> previewResultsForSave,
|
||||
ModelSplitterManager modelSplitterManager,
|
||||
ModelSplitterManager.SplitConfiguration config,
|
||||
List<ModelSplitterManager.SplitPreviewResult> previewResultsForSave,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
@ -1501,7 +1501,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
/// <summary>
|
||||
/// 处理单个分层(异步版本)
|
||||
/// </summary>
|
||||
private async Task ProcessSingleLayerAsync(SimplifiedModelSplitterManager.SplitPreviewResult preview, SimplifiedModelSplitterManager.SplitConfiguration config, CancellationToken cancellationToken)
|
||||
private async Task ProcessSingleLayerAsync(ModelSplitterManager.SplitPreviewResult preview, ModelSplitterManager.SplitConfiguration config, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -1603,17 +1603,17 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
/// </summary>
|
||||
/// <param name="strategy">分层策略</param>
|
||||
/// <returns>属性类型名称</returns>
|
||||
private string GetAttributeTypeName(SimplifiedModelSplitterManager.SplitStrategy strategy)
|
||||
private string GetAttributeTypeName(ModelSplitterManager.SplitStrategy strategy)
|
||||
{
|
||||
switch (strategy)
|
||||
{
|
||||
case SimplifiedModelSplitterManager.SplitStrategy.BySmartFloorDetection:
|
||||
case ModelSplitterManager.SplitStrategy.BySmartFloorDetection:
|
||||
return "楼层";
|
||||
case SimplifiedModelSplitterManager.SplitStrategy.ByFloorAttribute:
|
||||
case ModelSplitterManager.SplitStrategy.ByFloorAttribute:
|
||||
return "楼层";
|
||||
case SimplifiedModelSplitterManager.SplitStrategy.ByZoneAttribute:
|
||||
case ModelSplitterManager.SplitStrategy.ByZoneAttribute:
|
||||
return "区域";
|
||||
case SimplifiedModelSplitterManager.SplitStrategy.BySubSystemAttribute:
|
||||
case ModelSplitterManager.SplitStrategy.BySubSystemAttribute:
|
||||
return "子系统";
|
||||
default:
|
||||
return "分层";
|
||||
@ -1664,7 +1664,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
/// <param name="strategy">分层策略</param>
|
||||
/// <param name="config">配置信息</param>
|
||||
/// <returns>文件名(不包括路径)</returns>
|
||||
private string GenerateFileName(string attributeValue, SimplifiedModelSplitterManager.SplitStrategy strategy, SimplifiedModelSplitterManager.SplitConfiguration config)
|
||||
private string GenerateFileName(string attributeValue, ModelSplitterManager.SplitStrategy strategy, ModelSplitterManager.SplitConfiguration config)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -1704,7 +1704,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
/// <param name="layerName">分层名称</param>
|
||||
/// <param name="config">配置信息</param>
|
||||
/// <returns>文件名(不包括路径)</returns>
|
||||
private string GenerateFileName(string layerName, SimplifiedModelSplitterManager.SplitConfiguration config)
|
||||
private string GenerateFileName(string layerName, ModelSplitterManager.SplitConfiguration config)
|
||||
{
|
||||
// 使用默认策略调用新版本方法
|
||||
return GenerateFileName(layerName, config.Strategy, config);
|
||||
@ -1713,7 +1713,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
/// <summary>
|
||||
/// 导出分层到NWD文件
|
||||
/// </summary>
|
||||
private bool ExportLayerToNwd(SimplifiedModelSplitterManager.SplitPreviewResult preview, string outputPath, SimplifiedModelSplitterManager.SplitConfiguration config)
|
||||
private bool ExportLayerToNwd(ModelSplitterManager.SplitPreviewResult preview, string outputPath, ModelSplitterManager.SplitConfiguration config)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -2633,27 +2633,27 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
FloorAnalysisResultColor = color;
|
||||
}
|
||||
|
||||
private SimplifiedModelSplitterManager.SplitStrategy GetSplitStrategyFromString(string strategy)
|
||||
private ModelSplitterManager.SplitStrategy GetSplitStrategyFromString(string strategy)
|
||||
{
|
||||
switch (strategy)
|
||||
{
|
||||
case "智能检测楼层":
|
||||
return SimplifiedModelSplitterManager.SplitStrategy.BySmartFloorDetection;
|
||||
return ModelSplitterManager.SplitStrategy.BySmartFloorDetection;
|
||||
case "按自定义分层":
|
||||
// 根据二级选项决定具体的自定义分层策略
|
||||
switch (SelectedCustomLayerOption)
|
||||
{
|
||||
case "按楼层":
|
||||
return SimplifiedModelSplitterManager.SplitStrategy.ByFloorAttribute;
|
||||
return ModelSplitterManager.SplitStrategy.ByFloorAttribute;
|
||||
case "按区域":
|
||||
return SimplifiedModelSplitterManager.SplitStrategy.ByZoneAttribute;
|
||||
return ModelSplitterManager.SplitStrategy.ByZoneAttribute;
|
||||
case "按子系统":
|
||||
return SimplifiedModelSplitterManager.SplitStrategy.BySubSystemAttribute;
|
||||
return ModelSplitterManager.SplitStrategy.BySubSystemAttribute;
|
||||
default:
|
||||
return SimplifiedModelSplitterManager.SplitStrategy.ByFloorAttribute;
|
||||
return ModelSplitterManager.SplitStrategy.ByFloorAttribute;
|
||||
}
|
||||
default:
|
||||
return SimplifiedModelSplitterManager.SplitStrategy.BySmartFloorDetection;
|
||||
return ModelSplitterManager.SplitStrategy.BySmartFloorDetection;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -32,7 +32,7 @@ namespace NavisworksTransport
|
||||
/// <param name="attributeName">指定的楼层属性名称,为空时自动检测</param>
|
||||
/// <param name="maxDepth">最大遍历深度,1=仅第一级,2=到第二级,3=到第三级,4=到第四级,5=到第五级,0=所有级别</param>
|
||||
/// <returns>检测到的楼层信息列表</returns>
|
||||
public List<ModelSplitterManager.FloorInfo> DetectFloors(ModelItemCollection items, string attributeName = null, int maxDepth = 1)
|
||||
public List<FloorInfo> DetectFloors(ModelItemCollection items, string attributeName = null, int maxDepth = 1)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -43,7 +43,7 @@ namespace NavisworksTransport
|
||||
if (items == null || items.Count == 0)
|
||||
{
|
||||
LogManager.Warning("[FloorDetector] 输入的模型元素集合为空");
|
||||
return new List<ModelSplitterManager.FloorInfo>();
|
||||
return new List<FloorInfo>();
|
||||
}
|
||||
|
||||
LogManager.Info($"[FloorDetector] 开始检测楼层,元素数量: {items.Count},遍历深度: {maxDepth}");
|
||||
@ -52,7 +52,7 @@ namespace NavisworksTransport
|
||||
var itemsToAnalyze = GetItemsByDepth(items, maxDepth);
|
||||
LogManager.Info($"[FloorDetector] 按深度筛选后的元素数量: {itemsToAnalyze.Count}");
|
||||
|
||||
List<ModelSplitterManager.FloorInfo> floors;
|
||||
List<FloorInfo> floors;
|
||||
|
||||
if (!string.IsNullOrEmpty(attributeName))
|
||||
{
|
||||
@ -79,7 +79,7 @@ namespace NavisworksTransport
|
||||
LogManager.Info($"[FloorDetector] 未找到合适的楼层属性,无法进行楼层检测");
|
||||
LogManager.Info($"[FloorDetector] 建议:请确保模型包含标准的楼层属性(如Level、Floor、楼层等)");
|
||||
// 不再使用高程检测,直接返回空结果
|
||||
floors = new List<ModelSplitterManager.FloorInfo>();
|
||||
floors = new List<FloorInfo>();
|
||||
}
|
||||
}
|
||||
|
||||
@ -199,7 +199,7 @@ namespace NavisworksTransport
|
||||
/// <param name="items">预先过滤的模型项集合</param>
|
||||
/// <param name="attributeName">指定的楼层属性名称,为空时自动检测</param>
|
||||
/// <returns>检测到的楼层信息列表</returns>
|
||||
public List<ModelSplitterManager.FloorInfo> DetectFloorsFromGivenItems(ModelItemCollection items, string attributeName = null)
|
||||
public List<FloorInfo> DetectFloorsFromGivenItems(ModelItemCollection items, string attributeName = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -209,12 +209,12 @@ namespace NavisworksTransport
|
||||
if (items == null || items.Count == 0)
|
||||
{
|
||||
LogManager.Warning("[FloorDetector] 输入的模型元素集合为空");
|
||||
return new List<ModelSplitterManager.FloorInfo>();
|
||||
return new List<FloorInfo>();
|
||||
}
|
||||
|
||||
LogManager.Info($"[FloorDetector] 直接使用传入的模型项集合进行楼层检测,元素数量: {items.Count}");
|
||||
|
||||
List<ModelSplitterManager.FloorInfo> floors;
|
||||
List<FloorInfo> floors;
|
||||
|
||||
if (!string.IsNullOrEmpty(attributeName))
|
||||
{
|
||||
@ -241,7 +241,7 @@ namespace NavisworksTransport
|
||||
LogManager.Info($"[FloorDetector] 未找到合适的楼层属性,无法进行楼层检测");
|
||||
LogManager.Info($"[FloorDetector] 建议:请确保模型包含标准的楼层属性(如Level、Floor、楼层等)");
|
||||
// 不再使用高程检测,直接返回空结果
|
||||
floors = new List<ModelSplitterManager.FloorInfo>();
|
||||
floors = new List<FloorInfo>();
|
||||
}
|
||||
}
|
||||
|
||||
@ -338,7 +338,7 @@ namespace NavisworksTransport
|
||||
}
|
||||
}
|
||||
|
||||
private List<ModelSplitterManager.FloorInfo> DetectFloorsByAttribute(ModelItemCollection items, string attributeName)
|
||||
private List<FloorInfo> DetectFloorsByAttribute(ModelItemCollection items, string attributeName)
|
||||
{
|
||||
var floorGroups = new Dictionary<string, List<ModelItem>>();
|
||||
|
||||
@ -362,13 +362,13 @@ namespace NavisworksTransport
|
||||
}
|
||||
}
|
||||
|
||||
var floors = new List<ModelSplitterManager.FloorInfo>();
|
||||
var floors = new List<FloorInfo>();
|
||||
foreach (var kvp in floorGroups)
|
||||
{
|
||||
var floorItems = new ModelItemCollection();
|
||||
floorItems.AddRange(kvp.Value);
|
||||
|
||||
var floorInfo = new ModelSplitterManager.FloorInfo
|
||||
var floorInfo = new FloorInfo
|
||||
{
|
||||
FloorName = SanitizeFloorName(kvp.Key),
|
||||
Items = floorItems,
|
||||
@ -595,12 +595,12 @@ namespace NavisworksTransport
|
||||
|
||||
#region 私有方法 - 结果验证和优化
|
||||
|
||||
private List<ModelSplitterManager.FloorInfo> ValidateAndOptimizeFloors(List<ModelSplitterManager.FloorInfo> floors)
|
||||
private List<FloorInfo> ValidateAndOptimizeFloors(List<FloorInfo> floors)
|
||||
{
|
||||
if (floors == null || floors.Count == 0)
|
||||
return floors;
|
||||
|
||||
var validatedFloors = new List<ModelSplitterManager.FloorInfo>();
|
||||
var validatedFloors = new List<FloorInfo>();
|
||||
|
||||
foreach (var floor in floors)
|
||||
{
|
||||
@ -626,7 +626,7 @@ namespace NavisworksTransport
|
||||
return validatedFloors;
|
||||
}
|
||||
|
||||
private bool IsValidFloor(ModelSplitterManager.FloorInfo floor)
|
||||
private bool IsValidFloor(FloorInfo floor)
|
||||
{
|
||||
// 检查楼层是否包含模型元素
|
||||
if (floor.Items == null || floor.Items.Count == 0)
|
||||
@ -659,7 +659,7 @@ namespace NavisworksTransport
|
||||
return string.IsNullOrEmpty(optimized) ? "Unknown_Floor" : optimized;
|
||||
}
|
||||
|
||||
private void RenumberFloorsIfNeeded(List<ModelSplitterManager.FloorInfo> floors)
|
||||
private void RenumberFloorsIfNeeded(List<FloorInfo> floors)
|
||||
{
|
||||
// 如果楼层名称都是自动生成的数字格式,重新编号
|
||||
bool needsRenumbering = floors.All(f =>
|
||||
@ -765,4 +765,50 @@ namespace NavisworksTransport
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 楼层信息类
|
||||
/// 独立的数据结构,用于表示楼层检测结果
|
||||
/// </summary>
|
||||
public class FloorInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// 楼层名称
|
||||
/// </summary>
|
||||
public string FloorName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 楼层标高
|
||||
/// </summary>
|
||||
public double Elevation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 楼层边界框
|
||||
/// </summary>
|
||||
public BoundingBox3D Bounds { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 楼层包含的模型元素
|
||||
/// </summary>
|
||||
public ModelItemCollection Items { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 楼层元素数量
|
||||
/// </summary>
|
||||
public int ItemCount => Items?.Count ?? 0;
|
||||
|
||||
/// <summary>
|
||||
/// 楼层附加属性
|
||||
/// </summary>
|
||||
public System.Collections.Generic.Dictionary<string, object> Properties { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
public FloorInfo()
|
||||
{
|
||||
Items = new ModelItemCollection();
|
||||
Properties = new System.Collections.Generic.Dictionary<string, object>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,46 +0,0 @@
|
||||
# 路径点插入位置修复测试
|
||||
|
||||
## 问题描述
|
||||
修复了预览连线功能工作正常,但确认添加路径点时,新的路径点被错误地添加到路径末尾,而不是插入到预览时计算出来的正确位置的问题。
|
||||
|
||||
## 修改内容
|
||||
|
||||
### 1. 在 PathPlanningManager 中添加字段保存预览插入的索引位置
|
||||
```csharp
|
||||
private int _previewInsertIndex = -1; // 保存预览点应该插入的索引位置
|
||||
```
|
||||
|
||||
### 2. 添加了查找最近线段的方法
|
||||
- `FindNearestLineSegmentWithIndex()` - 查找最近线段并返回插入索引
|
||||
- `CalculatePointToLineSegmentDistance()` - 计算点到线段距离
|
||||
- `CalculateDistance()` - 计算两点间距离
|
||||
|
||||
### 3. 修改 SetPreviewPoint 方法
|
||||
- 在设置预览点时,计算并保存最佳插入位置索引
|
||||
- 当路径中有至少2个点时,调用 `FindNearestLineSegmentWithIndex()` 计算插入位置
|
||||
|
||||
### 4. 修改 ConfirmPreviewPoint 方法
|
||||
- 使用保存的 `_previewInsertIndex` 进行 `Insert()` 操作,而不是 `Add()`
|
||||
- 如果没有有效插入索引,则回退到原来的添加到末尾的行为
|
||||
|
||||
### 5. 修改 ClearPreviewPoint 方法
|
||||
- 清除预览时也清除保存的插入索引
|
||||
|
||||
## 测试步骤
|
||||
1. 创建新路径,添加至少2个路径点
|
||||
2. 启用添加路径点模式
|
||||
3. 将鼠标移动到两个现有路径点之间的位置
|
||||
4. 观察预览连线是否正确显示插入位置
|
||||
5. 点击确认添加路径点
|
||||
6. 验证新路径点是否插入到预览显示的正确位置,而不是路径末尾
|
||||
|
||||
## 预期结果
|
||||
- 预览连线正确显示新路径点应该插入的位置
|
||||
- 确认添加时,新路径点插入到预览时计算的正确位置
|
||||
- 路径点顺序符合用户的期望
|
||||
|
||||
## 技术细节
|
||||
- 插入索引的计算基于点到线段的最短距离
|
||||
- 使用了几何算法计算点到线段的垂直距离
|
||||
- 插入索引是最近线段的第二个点的索引位置
|
||||
- 例如:如果最近线段是点1到点2,新点应该插入到索引2的位置
|
||||
Loading…
Reference in New Issue
Block a user