using Prism.Commands; using Prism.Events; using Prism.Mvvm; using System; using System.Threading.Tasks; using System.Windows; using XP.Common.Helpers; 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 Operate ViewModel /// 负责射线源开关控制、电压电流调节、状态监控 | Responsible for X-ray on/off control, voltage/current adjustment, status monitoring /// public class RaySourceOperateViewModel : 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 bool _isDisposed = false; private bool _isOperating = false; private bool _isVariablesConnected = false; private bool _autoInitialized = false; private Views.RaySourceConfigWindow _configWindow; /// /// 标记当前是否正在从设备反馈更新值(防止 code-behind 误触发写入) /// Flag indicating whether values are being updated from device feedback (prevents code-behind from triggering writes) /// public bool IsUpdatingFromDevice { get; private set; } = false; #endregion #region 状态属性 | Status Properties private RaySourceStatus _raySourceStatus = RaySourceStatus.Unavailable; /// /// 射线源状态(不可用/关闭/开启)| X-ray source status (Unavailable/Closed/Opened) /// public RaySourceStatus RaySourceStatus { get => _raySourceStatus; set { if (SetProperty(ref _raySourceStatus, value)) { RaisePropertyChanged(nameof(StatusText)); RaisePropertyChanged(nameof(IsSlidersEnabled)); TurnOnCommand?.RaiseCanExecuteChanged(); TurnOffCommand?.RaiseCanExecuteChanged(); } } } /// /// 状态文本(绑定到腰圆内的文字)| Status text (bound to capsule text) /// public string StatusText { get { return RaySourceStatus switch { RaySourceStatus.Unavailable => $"{_localizationService.GetString("RaySource_StatusUnavailable")}", RaySourceStatus.Closed => _localizationService.GetString("RaySource_StatusClosed"), RaySourceStatus.Opened => _localizationService.GetString("RaySource_StatusOpened"), _ => _localizationService.GetString("RaySource_StatusUnavailable") }; } } /// /// 滑块是否启用(仅服务已初始化时可调节)| Sliders enabled (only when service is initialized) /// public bool IsSlidersEnabled => _raySourceService.IsInitialized && _isVariablesConnected; /// /// PVI 变量是否已连接 | Whether PVI variables are connected /// public bool IsVariablesConnected { get => _isVariablesConnected; private set { if (SetProperty(ref _isVariablesConnected, value)) { RaisePropertyChanged(nameof(IsSlidersEnabled)); TurnOnCommand?.RaiseCanExecuteChanged(); TurnOffCommand?.RaiseCanExecuteChanged(); } } } private bool _isInterlockActive; /// /// 连锁是否激活 | Whether interlock is active /// public bool IsInterlockActive { get => _isInterlockActive; private set { if (SetProperty(ref _isInterlockActive, value)) { TurnOnCommand?.RaiseCanExecuteChanged(); } } } #endregion #region 电压属性 | Voltage Properties private double _voltageMin; /// /// 电压最小值(kV)| Voltage minimum (kV) /// public double VoltageMin { get => _voltageMin; set => SetProperty(ref _voltageMin, value); } private double _voltageMax; /// /// 电压最大值(kV)| Voltage maximum (kV) /// public double VoltageMax { get => _voltageMax; set => SetProperty(ref _voltageMax, value); } private double _voltageValue; /// /// 电压滑块值(kV),仅更新UI显示,不自动写入硬件 | Voltage slider value (kV), UI display only, no auto write to hardware /// public double VoltageValue { get => _voltageValue; set => SetProperty(ref _voltageValue, Math.Round(value, 0)); } private double _voltageActual; /// /// 电压实际值(kV)| Voltage actual value (kV) /// public double VoltageActual { get => _voltageActual; set => SetProperty(ref _voltageActual, value); } /// /// 电压实际值显示文本 | Voltage actual value display text /// public string VoltageActualText => string.Format(_localizationService.GetString("RaySource_ActualValueLabel"), VoltageActual.ToString("F1")); #endregion #region 电流属性 | Current Properties private double _currentMin; /// /// 电流最小值(μA)| Current minimum (μA) /// public double CurrentMin { get => _currentMin; set => SetProperty(ref _currentMin, value); } private double _currentMax; /// /// 电流最大值(μA)| Current maximum (μA) /// public double CurrentMax { get => _currentMax; set => SetProperty(ref _currentMax, value); } private double _currentValue; /// /// 电流滑块值(μA),仅更新UI显示,不自动写入硬件 | Current slider value (μA), UI display only, no auto write to hardware /// public double CurrentValue { get => _currentValue; set => SetProperty(ref _currentValue, Math.Round(value, 0)); } private double _currentActual; /// /// 电流实际值(μA)| Current actual value (μA) /// public double CurrentActual { get => _currentActual; set => SetProperty(ref _currentActual, value); } /// /// 电流实际值显示文本 | Current actual value display text /// public string CurrentActualText => string.Format(_localizationService.GetString("RaySource_ActualValueLabel"), CurrentActual.ToString("F1")); #endregion #region 命令 | Commands /// /// 开启射线源命令 | Turn on X-ray command /// public DelegateCommand TurnOnCommand { get; } /// /// 关闭射线源命令 | Turn off X-ray command /// public DelegateCommand TurnOffCommand { get; } /// /// 提交电压值命令(滑块松手/输入框失焦时触发)| Apply voltage command (triggered on slider drag complete / input lost focus) /// public DelegateCommand ApplyVoltageCommand { get; } /// /// 提交电流值命令(滑块松手/输入框失焦时触发)| Apply current command (triggered on slider drag complete / input lost focus) /// public DelegateCommand ApplyCurrentCommand { get; } /// /// 设置命令(预留接口)| Settings command (reserved interface) /// public DelegateCommand SettingsCommand { get; } /// /// 配置命令(打开射线源配置窗口)| Config command (open ray source config window) /// public DelegateCommand ConfigCommand { get; } #endregion #region 构造函数 | Constructor /// /// 构造函数 | Constructor /// public RaySourceOperateViewModel( IRaySourceService raySourceService, IEventAggregator eventAggregator, RaySourceConfig config, ILoggerService logger, ILocalizationService localizationService) { _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.ViewModel"); _localizationService = localizationService ?? throw new ArgumentNullException(nameof(localizationService)); _logger.Info("射线源操作视图模型已创建 | X-ray source operate view model created"); // 初始化极值 | Initialize min/max values VoltageMin = _config.MinVoltage; VoltageMax = _config.MaxVoltage; CurrentMin = _config.MinCurrent; CurrentMax = _config.MaxCurrent; // 初始化滑块默认值 | Initialize slider default values VoltageValue = _config.MinVoltage; //中间值:(_config.MinVoltage + _config.MaxVoltage) / 2; CurrentValue = _config.MinCurrent; //中间值:(_config.MinCurrent + _config.MaxCurrent) / 2; // 初始化命令 | Initialize commands TurnOnCommand = new DelegateCommand(ExecuteTurnOn, CanExecuteTurnOn); TurnOffCommand = new DelegateCommand(ExecuteTurnOff, CanExecuteTurnOff); ApplyVoltageCommand = new DelegateCommand(ApplyVoltage); ApplyCurrentCommand = new DelegateCommand(ApplyCurrent); SettingsCommand = new DelegateCommand(ExecuteSettings, CanExecuteSettings); ConfigCommand = new DelegateCommand(ExecuteConfig); // 订阅事件 | Subscribe to events SubscribeEvents(); // 初始化状态 | Initialize status UpdateStatusFromService(); } #endregion #region 事件订阅 | Event Subscription /// /// 订阅事件 | Subscribe to events /// private void SubscribeEvents() { // 订阅射线源状态变更事件(三态)| Subscribe to ray source status changed event (tri-state) _eventAggregator.GetEvent().Subscribe(OnRaySourceStatusChanged, ThreadOption.UIThread); // 订阅状态更新事件 | Subscribe to status updated event _eventAggregator.GetEvent().Subscribe(OnStatusUpdated, ThreadOption.UIThread); // 订阅错误事件 | Subscribe to error event _eventAggregator.GetEvent().Subscribe(OnErrorOccurred, ThreadOption.UIThread); // 订阅操作结果事件 | Subscribe to operation result event _eventAggregator.GetEvent().Subscribe(OnOperationResult, ThreadOption.UIThread); // 订阅变量连接状态变更事件 | Subscribe to variables connected event _eventAggregator.GetEvent().Subscribe(OnVariablesConnectedChanged, ThreadOption.UIThread); } /// /// 射线源状态变更事件处理(三态)| Ray source status changed event handler (tri-state) /// private void OnRaySourceStatusChanged(RaySourceStatus newStatus) { var previousStatus = RaySourceStatus; RaySourceStatus = newStatus; RaisePropertyChanged(nameof(IsSlidersEnabled)); // 连接恢复处理:从不可用变为已连接,主动读取最新状态刷新UI | Connection restored: from unavailable to connected, actively read latest status to refresh UI if (previousStatus == RaySourceStatus.Unavailable && (newStatus == RaySourceStatus.Opened || newStatus == RaySourceStatus.Closed)) { _logger.Info("射线源连接已建立 | Ray Source Connected"); } // 异常断联处理:从已连接状态变为不可用 | Handle unexpected disconnection: from connected to unavailable if (newStatus == RaySourceStatus.Unavailable && (previousStatus == RaySourceStatus.Opened || previousStatus == RaySourceStatus.Closed)) { // 断联时重置变量连接状态 | Reset variable connection state on disconnection IsVariablesConnected = false; _logger.Warn("ViewModel 检测到连接丢失(状态变为 Unavailable)| ViewModel detected connection lost (status changed to Unavailable)"); //MessageBox.Show("射线源连接丢失,请检查设备连接后重新初始化。", "连接丢失", MessageBoxButton.OK, MessageBoxImage.Error); } } /// /// 状态更新事件处理 | Status updated event handler /// 仅在实际值真正变化时更新滑块/输入框,避免无意义赋值触发事件链 /// Only update slider/input when actual value truly changed, avoiding unnecessary assignment triggering event chain /// private void OnStatusUpdated(SystemStatusData statusData) { if (statusData == null) return; IsUpdatingFromDevice = true; try { // 电压实际值变化时,仅更新实际值标签显示 | When actual voltage changes, only update actual value label if (Math.Abs(statusData.ActualVoltage - VoltageActual) > 0.01) { VoltageActual = statusData.ActualVoltage; RaisePropertyChanged(nameof(VoltageActualText)); } // 电流实际值变化时,仅更新实际值标签显示 | When actual current changes, only update actual value label if (Math.Abs(statusData.ActualCurrent - CurrentActual) > 0.01) { CurrentActual = statusData.ActualCurrent; RaisePropertyChanged(nameof(CurrentActualText)); } // 设定电压反馈同步到滑块(仅设备反馈时更新,人工操作时 IsUpdatingFromDevice=false 不会进入此处) // Sync set voltage feedback to slider (only during device feedback, not during manual operation) if (statusData.SetVoltage >= _config.MinVoltage && Math.Abs(statusData.SetVoltage - VoltageValue) > 0.5) { VoltageValue = Math.Round(statusData.SetVoltage, 0); } // 设定电流反馈同步到滑块 | Sync set current feedback to slider if (statusData.SetCurrent >= _config.MinCurrent && Math.Abs(statusData.SetCurrent - CurrentValue) > 0.5) { CurrentValue = Math.Round(statusData.SetCurrent, 0); } // 使用服务层的权威状态 | Use authoritative status from service layer RaySourceStatus = _raySourceService.CurrentStatus; // 同步连锁状态 | Sync interlock status IsInterlockActive = statusData.IsInterlockActive; } finally { IsUpdatingFromDevice = false; } } /// /// 错误事件处理 | Error event handler /// private void OnErrorOccurred(string errorMessage) { // 显示错误消息 | Show error message MessageBox.Show(errorMessage, "错误", MessageBoxButton.OK, MessageBoxImage.Error); } /// /// 操作结果事件处理 | Operation result event handler /// private void OnOperationResult(OperationResultData resultData) { if (resultData == null) return; if (!resultData.IsSuccess) { // 操作失败时显示消息 | Show message when operation fails MessageBox.Show(resultData.Message, "操作失败", MessageBoxButton.OK, MessageBoxImage.Warning); } } /// /// 变量连接状态变更事件处理 | Variables connected state changed event handler /// private void OnVariablesConnectedChanged(bool isConnected) { IsVariablesConnected = isConnected; _logger.Info("收到变量连接状态变更: {IsConnected} | Received variables connection state change: {IsConnected}", isConnected); if (isConnected) { _logger.Info("变量已连接,主动读取最新状态 | Variables Connected, actively reading latest status"); RefreshStatusFromDevice(); } } #endregion #region 命令执行逻辑 | Command Execution Logic /// /// 开启射线源执行逻辑 | Turn on X-ray execution logic /// private void ExecuteTurnOn() { _isOperating = true; TurnOnCommand.RaiseCanExecuteChanged(); TurnOffCommand.RaiseCanExecuteChanged(); try { _logger.Info("用户请求开启射线 | User requested to turn on X-ray"); var result = _raySourceService.TurnOn(); if (result.Success) { _logger.Info("射线开启指令发送成功 | X-ray turned on successfully"); } else { _logger.Warn("射线开启指令发送失败: {Error} | X-ray turn on failed: {Error}", result.ErrorMessage); MessageBox.Show(result.ErrorMessage, "开启失败", MessageBoxButton.OK, MessageBoxImage.Error); } } catch (Exception ex) { _logger.Error(ex, "开启射线源异常: {Message} | Exception turning on X-ray: {Message}", ex.Message); MessageBox.Show($"开启射线源异常: {ex.Message}", "异常", MessageBoxButton.OK, MessageBoxImage.Error); } finally { _isOperating = false; TurnOnCommand.RaiseCanExecuteChanged(); TurnOffCommand.RaiseCanExecuteChanged(); } } /// /// 开启命令是否可执行(仅关闭状态且连锁激活时可执行)| Can execute turn on command (only when closed and interlock active) /// private bool CanExecuteTurnOn() { return !_isOperating && _isVariablesConnected && IsInterlockActive && RaySourceStatus == RaySourceStatus.Closed && _raySourceService.IsInitialized; } /// /// 关闭射线源执行逻辑 | Turn off X-ray execution logic /// private void ExecuteTurnOff() { _isOperating = true; TurnOnCommand.RaiseCanExecuteChanged(); TurnOffCommand.RaiseCanExecuteChanged(); try { _logger.Info("用户请求关闭射线 | User requested to turn off X-ray"); var result = _raySourceService.TurnOff(); if (result.Success) { _logger.Info("射线关闭指令发送成功 | X-ray turned off successfully"); } else { _logger.Warn("射线关闭指令发送失败: {Error} | X-ray turn off failed: {Error}", result.ErrorMessage); MessageBox.Show(result.ErrorMessage, "关闭失败", MessageBoxButton.OK, MessageBoxImage.Error); } } catch (Exception ex) { _logger.Error(ex, "关闭射线源异常: {Message} | Exception turning off X-ray: {Message}", ex.Message); MessageBox.Show($"关闭射线源异常: {ex.Message}", "异常", MessageBoxButton.OK, MessageBoxImage.Error); } finally { _isOperating = false; TurnOnCommand.RaiseCanExecuteChanged(); TurnOffCommand.RaiseCanExecuteChanged(); } } /// /// 关闭命令是否可执行(仅开启状态可执行)| Can execute turn off command (only when opened) /// private bool CanExecuteTurnOff() { return !_isOperating && _isVariablesConnected && RaySourceStatus == RaySourceStatus.Opened && _raySourceService.IsInitialized; } /// /// 设置命令执行逻辑:打开外部高级设置程序 | Settings command: launch external advance settings program /// 如果程序已运行则将窗口置前 | If program is already running, bring its window to front /// private void ExecuteSettings() { var exePath = _config.AdvanceExePath; _logger.Info("用户点击高级按钮,目标程序: {ExePath} | User clicked advance button, target program: {ExePath}", exePath); var result = ProcessHelper.StartOrActivate(exePath, true); if (result.Success) { var action = result.IsNewlyStarted ? "已启动 | started" : "已激活 | activated"; _logger.Info("外部程序{Action} | External program {Action}", action, action); } else { _logger.Warn("外部程序操作失败: {Error} | External program operation failed: {Error}", result.ErrorMessage); MessageBox.Show(result.ErrorMessage, _localizationService.GetString("RaySource_Warning") ?? "警告", MessageBoxButton.OK, MessageBoxImage.Warning); } } /// /// 设置命令是否可执行 | Can execute settings command /// private bool CanExecuteSettings() { return true; // 始终可用 | Always available } /// /// 配置命令执行逻辑:打开射线源配置窗口 | Config command: open ray source config window /// private void ExecuteConfig() { _logger.Info("用户点击配置按钮 | User clicked config button"); // 如果窗口已存在且未关闭,激活它而不是重复创建 if (_configWindow != null && _configWindow.IsLoaded) { _configWindow.Activate(); return; } _configWindow = new Views.RaySourceConfigWindow { Owner = Application.Current.MainWindow, ShowInTaskbar = false }; _configWindow.Closed += (s, e) => _configWindow = null; _configWindow.Show(); } /// /// 提交电压值到硬件(滑块松手/输入框失焦时调用)| Apply voltage to hardware (called on slider drag complete / input lost focus) /// private void ApplyVoltage() { var voltage = (float)VoltageValue; if (!IsSlidersEnabled) return; // 范围校验 | Range validation if (voltage < _config.MinVoltage || voltage > _config.MaxVoltage) { _logger.Warn("电压值超出范围: {Voltage}kV,忽略提交 | Voltage out of range: {Voltage}kV, ignoring apply", voltage); return; } try { _logger.Debug("用户确认电压值: {Voltage}kV | User confirmed voltage: {Voltage}kV", voltage); var result = _raySourceService.SetVoltage(voltage); if (!result.Success) { _logger.Warn("电压设置失败: {Error} | Voltage setting failed: {Error}", result.ErrorMessage); // 设置失败时恢复滑块值(仅当实际值在有效范围内时)| Restore slider value when setting fails (only when actual value is in valid range) if (VoltageActual >= _config.MinVoltage) VoltageValue = VoltageActual; } } catch (Exception ex) { _logger.Error(ex, "设置电压异常: {Message} | Exception setting voltage: {Message}", ex.Message); MessageBox.Show($"设置电压异常: {ex.Message}", "异常", MessageBoxButton.OK, MessageBoxImage.Error); if (VoltageActual >= _config.MinVoltage) VoltageValue = VoltageActual; } } /// /// 提交电流值到硬件(滑块松手/输入框失焦时调用)| Apply current to hardware (called on slider drag complete / input lost focus) /// private void ApplyCurrent() { var current = (float)CurrentValue; if (!IsSlidersEnabled) return; // 范围校验 | Range validation if (current < _config.MinCurrent || current > _config.MaxCurrent) { _logger.Warn("电流值超出范围: {Current}μA,忽略提交 | Current out of range: {Current}μA, ignoring apply", current); return; } try { _logger.Debug("用户确认电流值: {Current}μA | User confirmed current: {Current}μA", current); var result = _raySourceService.SetCurrent(current); if (!result.Success) { _logger.Warn("电流设置失败: {Error} | Current setting failed: {Error}", result.ErrorMessage); // 设置失败时恢复滑块值(仅当实际值在有效范围内时)| Restore slider value when setting fails (only when actual value is in valid range) if (CurrentActual >= _config.MinCurrent) CurrentValue = CurrentActual; } } catch (Exception ex) { _logger.Error(ex, "设置电流异常: {Message} | Exception setting current: {Message}", ex.Message); MessageBox.Show($"设置电流异常: {ex.Message}", "异常", MessageBoxButton.OK, MessageBoxImage.Error); if (CurrentActual >= _config.MinCurrent) CurrentValue = CurrentActual; } } /// /// 从服务更新状态 | Update status from service /// private void UpdateStatusFromService() { RaySourceStatus = _raySourceService.CurrentStatus; } /// /// 从设备主动读取最新状态并刷新UI | Actively read latest status from device and refresh UI /// private void RefreshStatusFromDevice() { try { var result = _raySourceService.ReadSystemStatus(); if (result.Success && result.Data is SystemStatusData statusData) { OnStatusUpdated(statusData); _logger.Info("已从设备刷新最新状态 | Refreshed latest status from device"); } else { _logger.Warn("读取设备状态失败: {Error} | Failed to read device status: {Error}", result.ErrorMessage); } } catch (Exception ex) { _logger.Error(ex, "刷新设备状态异常: {Message} | Exception refreshing device status: {Message}", ex.Message); } } /// /// 界面加载时自动执行初始化和连接变量流程(仅执行一次) /// Auto-execute Initialize and ConnectVariables on view loaded (runs only once) /// /// /// 自动初始化:异步执行初始化 + 连接变量 | Auto-initialize: async Initialize + ConnectVariables /// 由 View 的 Loaded 事件触发,仅执行一次 | Triggered by View's Loaded event, runs only once /// public async Task AutoInitializeAsync() { // 防止重复执行 | Prevent duplicate execution if (_autoInitialized) return; _autoInitialized = true; // 如果已经初始化并连接,跳过 | Skip if already initialized and connected if (_raySourceService.IsInitialized && _isVariablesConnected) { _logger.Info("射线源已初始化且变量已连接,跳过自动初始化 | Ray source already initialized and variables connected, skipping auto-init"); return; } _logger.Info("开始自动初始化流程 | Starting auto-initialization sequence"); var result = await _raySourceService.InitializeAndConnectAsync(); if (result.Success) { _logger.Info("自动初始化命令已发送,等待设备确认 | Auto-initialization command sent, waiting for device confirmation"); } else { _logger.Warn("自动初始化流程失败: {Error} | Auto-initialization sequence failed: {Error}", result.ErrorMessage); } } #endregion #region IDisposable Implementation /// /// 释放资源 | Dispose resources /// public void Dispose() { if (!_isDisposed) { // 断开射线源连接 | Disconnect X-ray source if (_raySourceService.IsInitialized) { _logger.Info("界面关闭,执行断开操作 | View closing, executing disconnect"); try { Task.Run(() => _raySourceService.Disconnect()).Wait(5000); } catch (Exception ex) { _logger.Warn("断开射线源异常: {Message} | Disconnect exception: {Message}", ex.Message); } } // 关闭配置窗口 | Close config window if (_configWindow != null && _configWindow.IsLoaded) { _configWindow.Close(); _configWindow = null; } // 取消订阅事件 | Unsubscribe events _eventAggregator.GetEvent().Unsubscribe(OnRaySourceStatusChanged); _eventAggregator.GetEvent().Unsubscribe(OnStatusUpdated); _eventAggregator.GetEvent().Unsubscribe(OnErrorOccurred); _eventAggregator.GetEvent().Unsubscribe(OnOperationResult); _eventAggregator.GetEvent().Unsubscribe(OnVariablesConnectedChanged); _isDisposed = true; } } #endregion } }