using Prism.Commands; using Prism.Events; using Prism.Mvvm; using System; using System.Threading.Tasks; using System.Windows; using System.Windows.Threading; using System.Threading; using XP.Common.GeneralForm.Views; using XP.Common.Localization.Interfaces; using XP.Common.Logging.Interfaces; using XP.Hardware.RaySource.Abstractions; using XP.Hardware.RaySource.Abstractions.Enums; using XP.Hardware.RaySource.Abstractions.Events; using XP.Hardware.RaySource.Config; using XP.Hardware.RaySource.Services; namespace XP.Hardware.RaySource.ViewModels { /// /// 射线源配置视图模型 | X-Ray Source Config ViewModel /// 负责射线源初始化、连接、断开及设备状态监控 | Responsible for X-ray source init, connect, disconnect and device status monitoring /// public class RaySourceConfigViewModel : BindableBase, IDisposable { #region 字段 | Fields private readonly IRaySourceService _raySourceService; private readonly IEventAggregator _eventAggregator; private readonly RaySourceConfig _config; private readonly ILoggerService _logger; private readonly ILocalizationService _localizationService; private readonly IFilamentLifetimeService _filamentLifetimeService; private bool _isOperating = false; private bool _isDisposed = false; /// /// 灯丝寿命定时刷新定时器(60 秒间隔)| Filament lifetime refresh timer (60-second interval) /// private DispatcherTimer _lifetimeRefreshTimer; #endregion #region 连接属性 | Connection Properties /// /// 是否已初始化(代理到服务层)| Whether initialized (delegated to service layer) /// public bool IsInitialized => _raySourceService.IsInitialized; /// /// 通知 IsInitialized 变更并刷新相关命令状态 | Notify IsInitialized changed and refresh related command states /// private void NotifyInitializedChanged() { RaisePropertyChanged(nameof(IsInitialized)); InitializeCommand?.RaiseCanExecuteChanged(); ConnectVariablesCommand?.RaiseCanExecuteChanged(); DisconnectCommand?.RaiseCanExecuteChanged(); RaisePropertyChanged(nameof(ConnectionStatusText)); } /// /// 射线源是否已建立完整连接(代理到服务层)| Whether fully connected (delegated to service layer) /// public bool IsConnected => _raySourceService.IsConnected; /// /// 通知 IsConnected 变更并刷新相关命令状态 | Notify IsConnected changed and refresh related command states /// private void NotifyConnectedChanged() { RaisePropertyChanged(nameof(IsConnected)); ConnectVariablesCommand?.RaiseCanExecuteChanged(); DisconnectCommand?.RaiseCanExecuteChanged(); RaisePropertyChanged(nameof(ConnectionStatusText)); } /// /// 连接状态文本 | Connection status text /// public string ConnectionStatusText { get { if (IsConnected) return _localizationService.GetString("RaySource_VariablesConnected") ?? "变量已连接"; if (IsInitialized) return _localizationService.GetString("RaySource_Connected") ?? "已连接"; return _localizationService.GetString("RaySource_Disconnected") ?? "未连接"; } } /// /// 射线源类型显示 | Ray source type display /// public string SourceTypeText => _config.SourceType; #endregion #region 设备状态属性 | Device Status Properties private string _warmUpStatus = "--"; /// /// 暖机状态 | Warm-up status /// public string WarmUpStatus { get => _warmUpStatus; private set => SetProperty(ref _warmUpStatus, value); } private string _vacuumStatus = "--"; /// /// 真空状态 | Vacuum status /// public string VacuumStatus { get => _vacuumStatus; private set => SetProperty(ref _vacuumStatus, value); } private string _startUpStatus = "--"; /// /// 启动状态 | Startup status /// public string StartUpStatus { get => _startUpStatus; private set => SetProperty(ref _startUpStatus, value); } private string _autoCenterStatus = "--"; /// /// 自动定心状态 | Auto-center status /// public string AutoCenterStatus { get => _autoCenterStatus; private set => SetProperty(ref _autoCenterStatus, value); } private string _filamentAdjustStatus = "--"; /// /// 灯丝校准状态 | Filament adjust status /// public string FilamentAdjustStatus { get => _filamentAdjustStatus; private set => SetProperty(ref _filamentAdjustStatus, value); } private string _powerMode = "--"; /// /// 功率模式 | Power mode /// public string PowerMode { get => _powerMode; private set => SetProperty(ref _powerMode, value); } private bool _isInterlockActive; /// /// 连锁是否激活 | Whether interlock is active /// public bool IsInterlockActive { get => _isInterlockActive; private set { if (SetProperty(ref _isInterlockActive, value)) { RaisePropertyChanged(nameof(InterlockStatusText)); RefreshCommandStates(); } } } /// /// 连锁状态文本 | Interlock status text /// public string InterlockStatusText => IsInterlockActive ? (_localizationService.GetString("RaySource_InterlockActive") ?? "激活") : (_localizationService.GetString("RaySource_InterlockNormal") ?? "未激活"); private string _watchdogStatus = "--"; /// /// 看门狗状态 | Watchdog status /// public string WatchdogStatus { get => _watchdogStatus; private set => SetProperty(ref _watchdogStatus, value); } private bool _isXRayOn; /// /// 射线开启状态 | X-ray on status /// public bool IsXRayOn { get => _isXRayOn; private set { if (SetProperty(ref _isXRayOn, value)) RaisePropertyChanged(nameof(XRayOnStatusText)); } } /// /// 射线开启状态文本 | X-ray on status text /// public string XRayOnStatusText => IsXRayOn ? (_localizationService.GetString("RaySource_XRayOn") ?? "开启") : (_localizationService.GetString("RaySource_XRayOff") ?? "关闭"); private string _txiStatus = "--"; /// /// TXI状态 | TXI status /// public string TxiStatus { get => _txiStatus; private set => SetProperty(ref _txiStatus, value); } private double _filamentLifetime; /// /// 灯丝寿命百分比(0-100)| Filament lifetime percentage (0-100) /// public double FilamentLifetime { get => _filamentLifetime; private set => SetProperty(ref _filamentLifetime, value); } private string _filamentLifetimeText = ""; /// /// 灯丝寿命显示文本(如 "123.4h / 1000.0h (12.3%)") /// Filament lifetime display text (e.g. "123.4h / 1000.0h (12.3%)") /// public string FilamentLifetimeText { get => _filamentLifetimeText; private set => SetProperty(ref _filamentLifetimeText, value); } #endregion #region 命令 | Commands public DelegateCommand InitializeCommand { get; } public DelegateCommand ConnectVariablesCommand { get; } public DelegateCommand DisconnectCommand { get; } public DelegateCommand TxiOnCommand { get; } public DelegateCommand TxiOffCommand { get; } public DelegateCommand HighPowerCommand { get; } public DelegateCommand MicroFocusCommand { get; } public DelegateCommand WarmUpSettingCommand { get; } public DelegateCommand TrainingSettingCommand { get; } public DelegateCommand FilamentCalibrationCommand { get; } public DelegateCommand AutoCenterSettingCommand { get; } #endregion #region 构造函数 | Constructor public RaySourceConfigViewModel( IRaySourceService raySourceService, IEventAggregator eventAggregator, RaySourceConfig config, ILoggerService logger, ILocalizationService localizationService, IFilamentLifetimeService filamentLifetimeService) { _raySourceService = raySourceService ?? throw new ArgumentNullException(nameof(raySourceService)); _eventAggregator = eventAggregator ?? throw new ArgumentNullException(nameof(eventAggregator)); _config = config ?? throw new ArgumentNullException(nameof(config)); _logger = (logger ?? throw new ArgumentNullException(nameof(logger))).ForModule("RaySource.ConfigVM"); _localizationService = localizationService ?? throw new ArgumentNullException(nameof(localizationService)); _filamentLifetimeService = filamentLifetimeService ?? throw new ArgumentNullException(nameof(filamentLifetimeService)); InitializeCommand = new DelegateCommand(ExecuteInitialize, CanExecuteInitialize); ConnectVariablesCommand = new DelegateCommand(ExecuteConnectVariables, CanExecuteConnectVariables); DisconnectCommand = new DelegateCommand(ExecuteDisconnect, CanExecuteDisconnect); TxiOnCommand = new DelegateCommand(ExecuteTxiOn, CanExecuteDeviceCommand); TxiOffCommand = new DelegateCommand(ExecuteTxiOff, CanExecuteDeviceCommand); HighPowerCommand = new DelegateCommand(ExecuteHighPower, CanExecuteDeviceCommand); MicroFocusCommand = new DelegateCommand(ExecuteMicroFocus, CanExecuteDeviceCommand); WarmUpSettingCommand = new DelegateCommand(ExecuteWarmUpSetting, CanExecuteDeviceCommand); TrainingSettingCommand = new DelegateCommand(ExecuteTrainingSetting, CanExecuteDeviceCommand); FilamentCalibrationCommand = new DelegateCommand(ExecuteFilamentCalibration, CanExecuteDeviceCommand); AutoCenterSettingCommand = new DelegateCommand(ExecuteAutoCenterSetting, CanExecuteDeviceCommand); // 订阅事件 | Subscribe events _eventAggregator.GetEvent().Subscribe(OnRaySourceStatusChanged, ThreadOption.UIThread); _eventAggregator.GetEvent().Subscribe(OnStatusUpdated, ThreadOption.UIThread); _eventAggregator.GetEvent().Subscribe(OnVariablesConnectedChanged, ThreadOption.UIThread); // 同步当前状态 | Sync current state NotifyInitializedChanged(); NotifyConnectedChanged(); // 初始刷新灯丝寿命数据 | Initial refresh of filament lifetime data RefreshFilamentLifetime(); // 启动灯丝寿命定时刷新(60 秒间隔)| Start filament lifetime periodic refresh (60-second interval) StartLifetimeRefreshTimer(); _logger.Info("射线源配置视图模型已创建 | X-ray source config view model created"); } #endregion #region 事件处理 | Event Handlers private void OnRaySourceStatusChanged(RaySourceStatus newStatus) { NotifyInitializedChanged(); if (newStatus == RaySourceStatus.Unavailable && IsConnected) { NotifyConnectedChanged(); ResetDeviceStatus(); } } /// /// 状态更新事件处理:刷新设备状态面板 | Status updated event handler: refresh device status panel /// private void OnStatusUpdated(SystemStatusData data) { if (data == null) return; // 使用 StatusCodeMapper 将原始 code 映射为多语言文本 | Map raw codes to localized text WarmUpStatus = StatusCodeMapper.MapWarmUpStatus(data.WarmUpStatus, _localizationService); VacuumStatus = StatusCodeMapper.MapVacuumStatus(data.VacuumStatus, _localizationService); StartUpStatus = StatusCodeMapper.MapStartUpStatus(data.StartUpStatus, _localizationService); AutoCenterStatus = StatusCodeMapper.MapAutoCenterStatus(data.AutoCenterStatus, _localizationService); FilamentAdjustStatus = StatusCodeMapper.MapFilamentAdjustStatus(data.FilamentAdjustStatus, _localizationService); PowerMode = StatusCodeMapper.MapPowerMode(data.PowerMode, _localizationService); IsInterlockActive = data.IsInterlockActive; WatchdogStatus = data.WatchdogStatus ?? "--"; IsXRayOn = data.IsXRayOn; TxiStatus = StatusCodeMapper.MapTxiStatus(data.TxiStatus, _localizationService); } /// /// 重置设备状态为默认值 | Reset device status to defaults /// private void ResetDeviceStatus() { WarmUpStatus = "--"; VacuumStatus = "--"; StartUpStatus = "--"; AutoCenterStatus = "--"; FilamentAdjustStatus = "--"; PowerMode = "--"; IsInterlockActive = false; WatchdogStatus = "--"; IsXRayOn = false; TxiStatus = "--"; FilamentLifetime = 0; FilamentLifetimeText = ""; } /// /// 变量连接状态变更事件处理 | Variables connected state changed event handler /// private void OnVariablesConnectedChanged(bool isConnected) { NotifyConnectedChanged(); // 同步初始化状态(自动初始化后服务状态可能已变)| Sync init state (service state may have changed after auto-init) NotifyInitializedChanged(); _logger.Info("ConfigVM 收到变量连接状态变更: {IsConnected} | ConfigVM received variables connection state change: {IsConnected}", isConnected); } #endregion #region 命令执行逻辑 | Command Execution Logic /// /// 初始化命令执行逻辑 | Initialize command execution logic /// private async void ExecuteInitialize() { _isOperating = true; RefreshCommandStates(); try { _logger.Info("用户请求初始化射线源 | User requested to initialize X-ray source"); var result = await Task.Run(() => _raySourceService.Initialize()); if (result.Success) { NotifyInitializedChanged(); _logger.Info("射线源初始化成功 | X-ray source initialized successfully"); } else { _logger.Warn("射线源初始化失败: {Message} | X-ray source initialization failed: {Message}", result.ErrorMessage); MessageBox.Show($"初始化失败: {result.ErrorMessage}", "错误", MessageBoxButton.OK, MessageBoxImage.Warning); } } catch (Exception ex) { _logger.Error(ex, "初始化射线源异常: {Message} | Exception initializing X-ray source: {Message}", ex.Message); MessageBox.Show($"初始化异常: {ex.Message}", "异常", MessageBoxButton.OK, MessageBoxImage.Error); } finally { _isOperating = false; RefreshCommandStates(); } } /// /// 初始化命令是否可执行 | Can execute initialize command /// private bool CanExecuteInitialize() => !_isOperating && !IsInitialized; /// /// 连接变量命令执行逻辑 | Connect variables command execution logic /// private async void ExecuteConnectVariables() { _isOperating = true; RefreshCommandStates(); try { _logger.Info("用户请求连接 PVI 变量 | User requested to connect PVI variables"); var result = await Task.Run(() => _raySourceService.ConnectVariables()); if (result.Success) { // 不在此处设置 IsConnected,等待设备层 RaySourceConnected 推送通过 VariablesConnectedEvent 事件确认 // IsConnected will be set by VariablesConnectedEvent from device layer RaySourceConnected push _logger.Info("PVI 变量连接命令已发送,等待设备确认 | PVI variable connection command sent, waiting for device confirmation"); } else { _logger.Warn("PVI 变量连接失败: {Message} | PVI variables connection failed: {Message}", result.ErrorMessage); MessageBox.Show($"连接变量失败: {result.ErrorMessage}", "错误", MessageBoxButton.OK, MessageBoxImage.Warning); } } catch (Exception ex) { _logger.Error(ex, "连接变量异常: {Message} | Exception connecting variables: {Message}", ex.Message); MessageBox.Show($"连接变量异常: {ex.Message}", "异常", MessageBoxButton.OK, MessageBoxImage.Error); } finally { _isOperating = false; RefreshCommandStates(); } } /// /// 连接变量命令是否可执行 | Can execute connect variables command /// private bool CanExecuteConnectVariables() => !_isOperating && IsInitialized && !IsConnected; /// /// 断开命令执行逻辑 | Disconnect command execution logic /// private async void ExecuteDisconnect() { _isOperating = true; RefreshCommandStates(); try { _logger.Info("用户请求断开射线源 | User requested to disconnect X-ray source"); var result = await Task.Run(() => _raySourceService.Disconnect()); if (result.Success) { NotifyConnectedChanged(); NotifyInitializedChanged(); _eventAggregator.GetEvent().Publish(false); ResetDeviceStatus(); _logger.Info("射线源已断开 | X-ray source disconnected"); } else { _logger.Warn("断开射线源失败: {Error} | Disconnect X-ray source failed: {Error}", result.ErrorMessage); MessageBox.Show(result.ErrorMessage, _localizationService.GetString("Message_OperationFailed") ?? "操作失败", MessageBoxButton.OK, MessageBoxImage.Error); } } catch (Exception ex) { _logger.Error(ex, "断开射线源异常: {Message} | Exception disconnecting X-ray source: {Message}", ex.Message); MessageBox.Show($"断开异常: {ex.Message}", "异常", MessageBoxButton.OK, MessageBoxImage.Error); } finally { _isOperating = false; RefreshCommandStates(); } } /// /// 断开命令是否可执行 | Can execute disconnect command /// private bool CanExecuteDisconnect() => !_isOperating && IsInitialized; /// /// 刷新所有命令的可执行状态 | Refresh all command CanExecute states /// private void RefreshCommandStates() { InitializeCommand?.RaiseCanExecuteChanged(); ConnectVariablesCommand?.RaiseCanExecuteChanged(); DisconnectCommand?.RaiseCanExecuteChanged(); TxiOnCommand?.RaiseCanExecuteChanged(); TxiOffCommand?.RaiseCanExecuteChanged(); HighPowerCommand?.RaiseCanExecuteChanged(); MicroFocusCommand?.RaiseCanExecuteChanged(); WarmUpSettingCommand?.RaiseCanExecuteChanged(); TrainingSettingCommand?.RaiseCanExecuteChanged(); FilamentCalibrationCommand?.RaiseCanExecuteChanged(); AutoCenterSettingCommand?.RaiseCanExecuteChanged(); } /// /// 设备命令是否可执行(需已连接变量)| Can execute device command (requires variables connected) /// private bool CanExecuteDeviceCommand() => !_isOperating && IsConnected && IsInitialized && IsInterlockActive; /// /// TXI 开启 | TXI On /// private void ExecuteTxiOn() { _logger.Info("用户请求 TXI ON | User requested TXI ON"); try { var result = _raySourceService.TxiOn(); if (!result.Success) { _logger.Error(null, "TXI ON 失败: {ErrorMessage}", result.ErrorMessage); MessageBox.Show(result.ErrorMessage, _localizationService.GetString("RaySource_OperationFailed") ?? "操作失败", MessageBoxButton.OK, MessageBoxImage.Warning); } } catch (Exception ex) { _logger.Error(ex, "TXI ON 异常: {Message}", ex.Message); } } /// /// TXI 关闭 | TXI Off /// private void ExecuteTxiOff() { _logger.Info("用户请求 TXI OFF | User requested TXI OFF"); try { var result = _raySourceService.TxiOff(); if (!result.Success) { _logger.Error(null, "TXI OFF 失败: {ErrorMessage}", result.ErrorMessage); MessageBox.Show(result.ErrorMessage, _localizationService.GetString("RaySource_OperationFailed") ?? "操作失败", MessageBoxButton.OK, MessageBoxImage.Warning); } } catch (Exception ex) { _logger.Error(ex, "TXI OFF 异常: {Message}", ex.Message); } } /// /// 切换到高功率模式 | Switch to High Power mode /// private void ExecuteHighPower() { _logger.Info("用户请求切换到 High Power 模式 | User requested switch to High Power mode"); try { var result = _raySourceService.SetPowerMode(2); if (!result.Success) { _logger.Error(null, "切换 High Power 模式失败: {ErrorMessage}", result.ErrorMessage); MessageBox.Show(result.ErrorMessage, _localizationService.GetString("RaySource_OperationFailed") ?? "操作失败", MessageBoxButton.OK, MessageBoxImage.Warning); } } catch (Exception ex) { _logger.Error(ex, "切换 High Power 模式异常: {Message}", ex.Message); } } /// /// 切换到微焦点模式 | Switch to Micro Focus mode /// private void ExecuteMicroFocus() { _logger.Info("用户请求切换到 Micro Focus 模式 | User requested switch to Micro Focus mode"); try { var result = _raySourceService.SetPowerMode(1); if (!result.Success) { _logger.Error(null, "切换 Micro Focus 模式失败: {ErrorMessage}", result.ErrorMessage); MessageBox.Show(result.ErrorMessage, _localizationService.GetString("RaySource_OperationFailed") ?? "操作失败", MessageBoxButton.OK, MessageBoxImage.Warning); } } catch (Exception ex) { _logger.Error(ex, "切换 Micro Focus 模式异常: {Message}", ex.Message); } } /// /// 暖机设置 | Warm-up setting /// 参照旧代码:确认对话框 → 显示进度条 → 发送暖机命令 /// private async void ExecuteWarmUpSetting() { _logger.Info("用户打开暖机设置 | User opened warm-up setting"); var confirmResult = MessageBox.Show( _localizationService.GetString("RaySource_WarmUp_Confirm") ?? "是否确认执行暖机操作?", _localizationService.GetString("RaySource_Confirm_Title") ?? "确认", MessageBoxButton.YesNo, MessageBoxImage.Question); if (confirmResult != MessageBoxResult.Yes) { _logger.Info("用户取消暖机操作 | User cancelled warm-up operation"); return; } await ExecuteLongRunningOperation( _localizationService.GetString("RaySource_WarmUp_Title") ?? "暖机设置", _localizationService.GetString("RaySource_WarmUp_Message") ?? "正在执行暖机操作,请稍候...", () => _raySourceService.WarmUp(), OperationType.WarmUp, _config.WarmUpTimeout); } /// /// 训机设置 | Training setting /// 参照旧代码:确认对话框 → 显示进度条 → 发送训机命令 /// private async void ExecuteTrainingSetting() { _logger.Info("用户打开训机设置 | User opened training setting"); var confirmResult = MessageBox.Show( _localizationService.GetString("RaySource_Training_Confirm") ?? "是否确认执行训机操作?", _localizationService.GetString("RaySource_Confirm_Title") ?? "确认", MessageBoxButton.YesNo, MessageBoxImage.Question); if (confirmResult != MessageBoxResult.Yes) { _logger.Info("用户取消训机操作 | User cancelled training operation"); return; } await ExecuteLongRunningOperation( _localizationService.GetString("RaySource_Training_Title") ?? "训机设置", _localizationService.GetString("RaySource_Training_Message") ?? "正在执行训机操作,请稍候...", () => _raySourceService.Training(), OperationType.Training, _config.StartUpTimeout); } /// /// 灯丝校准 | Filament calibration /// 参照旧代码:确认对话框 → 显示进度条 → 发送灯丝校准命令 /// private async void ExecuteFilamentCalibration() { _logger.Info("用户请求灯丝校准 | User requested filament calibration"); var confirmResult = MessageBox.Show( _localizationService.GetString("RaySource_FilamentCalibration_Confirm") ?? "是否确认执行灯丝校准操作?", _localizationService.GetString("RaySource_Confirm_Title") ?? "确认", MessageBoxButton.YesNo, MessageBoxImage.Question); if (confirmResult != MessageBoxResult.Yes) { _logger.Info("用户取消灯丝校准操作 | User cancelled filament calibration operation"); return; } await ExecuteLongRunningOperation( _localizationService.GetString("RaySource_FilamentCalibration_Title") ?? "灯丝校准", _localizationService.GetString("RaySource_FilamentCalibration_Message") ?? "正在执行灯丝校准操作,请稍候...", () => _raySourceService.FilamentCalibration(), OperationType.FilamentCalibration, _config.FilamentAdjustTimeout); } /// /// 自动定心设置 | Auto-center setting /// 参照旧代码:确认对话框 → 显示进度条 → 发送全部电压自动定心命令 /// private async void ExecuteAutoCenterSetting() { _logger.Info("用户请求自动定心 | User requested auto-center"); var confirmResult = MessageBox.Show( _localizationService.GetString("RaySource_AutoCenter_Confirm") ?? "是否确认执行全部电压自动定心操作?", _localizationService.GetString("RaySource_Confirm_Title") ?? "确认", MessageBoxButton.YesNo, MessageBoxImage.Question); if (confirmResult != MessageBoxResult.Yes) { _logger.Info("用户取消自动定心操作 | User cancelled auto-center operation"); return; } await ExecuteLongRunningOperation( _localizationService.GetString("RaySource_AutoCenter_Title") ?? "自动定心", _localizationService.GetString("RaySource_AutoCenter_Message") ?? "正在执行全部电压自动定心操作,请稍候...", () => _raySourceService.AutoCenter(), OperationType.AutoCenter, _config.AutoCenterTimeout); } /// /// 执行长时间运行操作的通用方法(显示进度条窗口,实时跟踪设备进度) /// 发送指令后订阅 StatusUpdatedEvent,根据实时电压/电流/状态字符串计算进度 /// /// 进度窗口标题 /// 进度窗口提示信息 /// 要执行的操作(发送指令) /// 操作类型(用于选择进度计算策略) /// 超时时间(毫秒) private async Task ExecuteLongRunningOperation( string title, string message, Func operation, OperationType operationType, int timeoutMs) { var progressWindow = new ProgressWindow( title: title, message: message, isCancelable: false, logger: _logger); var calculator = new OperationProgressCalculator(operationType, _config); var completionSource = new TaskCompletionSource(); var cts = new CancellationTokenSource(timeoutMs); // 状态更新事件处理:计算进度并更新窗口 | Status update handler: calculate progress and update window void OnProgressStatusUpdated(SystemStatusData data) { if (data == null) return; var result = calculator.Calculate(data); progressWindow.UpdateProgress(message, result.Progress); if (result.IsCompleted && !completionSource.Task.IsCompleted) { completionSource.TrySetResult(true); } } try { _isOperating = true; RefreshCommandStates(); progressWindow.Show(); progressWindow.UpdateProgress(message, 0); // 订阅状态更新事件以跟踪进度 | Subscribe to status update event for progress tracking _eventAggregator.GetEvent().Subscribe(OnProgressStatusUpdated, ThreadOption.UIThread); // 在后台线程发送操作指令 | Send operation command on background thread var cmdResult = await Task.Run(() => operation()); if (!cmdResult.Success) { _logger.Error(null, "{Title} 操作失败: {ErrorMessage}", title, cmdResult.ErrorMessage); MessageBox.Show(cmdResult.ErrorMessage, _localizationService.GetString("RaySource_OperationFailed") ?? "操作失败", MessageBoxButton.OK, MessageBoxImage.Warning); return; } _logger.Info("{Title} 操作指令已发送,等待设备完成 | {Title} command sent, waiting for device completion", title); // 等待完成或超时 | Wait for completion or timeout using (cts.Token.Register(() => completionSource.TrySetResult(false))) { var completed = await completionSource.Task; if (completed) { progressWindow.UpdateProgress( _localizationService.GetString("RaySource_Operation_Completed") ?? "操作已完成", 100); _logger.Info("{Title} 操作已完成 | {Title} operation completed", title); await Task.Delay(1500); // 短暂停留让用户看到完成状态 | Brief pause for user to see completion } else { _logger.Warn("{Title} 操作超时({Timeout}ms)| {Title} operation timed out ({Timeout}ms)", title, timeoutMs); MessageBox.Show( _localizationService.GetString("RaySource_Operation_Timeout") ?? "操作超时,请检查设备状态。", _localizationService.GetString("RaySource_Warning") ?? "警告", MessageBoxButton.OK, MessageBoxImage.Warning); } } } catch (Exception ex) { _logger.Error(ex, "{Title} 操作异常: {Message}", title, ex.Message); } finally { // 取消订阅状态更新事件 | Unsubscribe from status update event _eventAggregator.GetEvent().Unsubscribe(OnProgressStatusUpdated); cts.Dispose(); _isOperating = false; RefreshCommandStates(); progressWindow.Close(); } } /// /// 从灯丝寿命服务获取最新数据并刷新 UI 属性(纯展示,不含计算逻辑) /// Refresh filament lifetime UI properties from service (display only, no calculation logic) /// private void RefreshFilamentLifetime() { try { if (!_filamentLifetimeService.IsInitialized) return; // 从服务获取数据 | Get data from service var percentage = _filamentLifetimeService.GetLifetimePercentage(); var totalSeconds = _filamentLifetimeService.GetCurrentTotalLifeSeconds(); var thresholdSeconds = _filamentLifetimeService.GetThresholdSeconds(); // 转换为小时(保留一位小数)| Convert to hours (one decimal place) var totalHours = totalSeconds / 3600.0; var thresholdHours = thresholdSeconds / 3600.0; // 更新 UI 属性 | Update UI properties FilamentLifetime = percentage; FilamentLifetimeText = $"{totalHours:F1}h / {thresholdHours:F1}h ({percentage:F1}%)"; } catch (Exception ex) { _logger.Error(ex, "刷新灯丝寿命数据失败 | Failed to refresh filament lifetime data: {Message}", ex.Message); } } /// /// 启动灯丝寿命定时刷新定时器 | Start filament lifetime periodic refresh timer /// private void StartLifetimeRefreshTimer() { _lifetimeRefreshTimer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(60) }; _lifetimeRefreshTimer.Tick += OnLifetimeRefreshTimerTick; _lifetimeRefreshTimer.Start(); _logger.Debug("灯丝寿命定时刷新已启动(间隔 60 秒)| Filament lifetime refresh timer started (60s interval)"); } /// /// 停止灯丝寿命定时刷新定时器 | Stop filament lifetime periodic refresh timer /// private void StopLifetimeRefreshTimer() { if (_lifetimeRefreshTimer != null) { _lifetimeRefreshTimer.Stop(); _lifetimeRefreshTimer.Tick -= OnLifetimeRefreshTimerTick; _lifetimeRefreshTimer = null; _logger.Debug("灯丝寿命定时刷新已停止 | Filament lifetime refresh timer stopped"); } } /// /// 定时器 Tick 事件处理:刷新灯丝寿命数据 | Timer tick handler: refresh filament lifetime data /// private void OnLifetimeRefreshTimerTick(object sender, EventArgs e) { RefreshFilamentLifetime(); } #endregion #region IDisposable 实现 | IDisposable Implementation /// /// 释放资源:停止定时器、取消事件订阅 | Dispose resources: stop timer, unsubscribe events /// public void Dispose() { if (!_isDisposed) { // 停止灯丝寿命定时刷新 | Stop filament lifetime refresh timer StopLifetimeRefreshTimer(); // 取消事件订阅 | Unsubscribe events _eventAggregator.GetEvent().Unsubscribe(OnRaySourceStatusChanged); _eventAggregator.GetEvent().Unsubscribe(OnStatusUpdated); _eventAggregator.GetEvent().Unsubscribe(OnVariablesConnectedChanged); _isDisposed = true; _logger.Info("射线源配置视图模型已释放 | X-ray source config view model disposed"); } } #endregion } }