Files

920 lines
39 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using 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
{
/// <summary>
/// 射线源配置视图模型 | X-Ray Source Config ViewModel
/// 负责射线源初始化、连接、断开及设备状态监控 | Responsible for X-ray source init, connect, disconnect and device status monitoring
/// </summary>
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;
/// <summary>
/// 灯丝寿命定时刷新定时器(60 秒间隔)| Filament lifetime refresh timer (60-second interval)
/// </summary>
private DispatcherTimer _lifetimeRefreshTimer;
#endregion
#region | Connection Properties
/// <summary>
/// 是否已初始化(代理到服务层)| Whether initialized (delegated to service layer)
/// </summary>
public bool IsInitialized => _raySourceService.IsInitialized;
/// <summary>
/// 通知 IsInitialized 变更并刷新相关命令状态 | Notify IsInitialized changed and refresh related command states
/// </summary>
private void NotifyInitializedChanged()
{
RaisePropertyChanged(nameof(IsInitialized));
InitializeCommand?.RaiseCanExecuteChanged();
ConnectVariablesCommand?.RaiseCanExecuteChanged();
DisconnectCommand?.RaiseCanExecuteChanged();
RaisePropertyChanged(nameof(ConnectionStatusText));
}
/// <summary>
/// 射线源是否已建立完整连接(代理到服务层)| Whether fully connected (delegated to service layer)
/// </summary>
public bool IsConnected => _raySourceService.IsConnected;
/// <summary>
/// 通知 IsConnected 变更并刷新相关命令状态 | Notify IsConnected changed and refresh related command states
/// </summary>
private void NotifyConnectedChanged()
{
RaisePropertyChanged(nameof(IsConnected));
ConnectVariablesCommand?.RaiseCanExecuteChanged();
DisconnectCommand?.RaiseCanExecuteChanged();
RaisePropertyChanged(nameof(ConnectionStatusText));
}
/// <summary>
/// 连接状态文本 | Connection status text
/// </summary>
public string ConnectionStatusText
{
get
{
if (IsConnected)
return _localizationService.GetString("RaySource_VariablesConnected") ?? "变量已连接";
if (IsInitialized)
return _localizationService.GetString("RaySource_Connected") ?? "已连接";
return _localizationService.GetString("RaySource_Disconnected") ?? "未连接";
}
}
/// <summary>
/// 射线源类型显示 | Ray source type display
/// </summary>
public string SourceTypeText => _config.SourceType;
#endregion
#region | Device Status Properties
private string _warmUpStatus = "--";
/// <summary>
/// 暖机状态 | Warm-up status
/// </summary>
public string WarmUpStatus
{
get => _warmUpStatus;
private set => SetProperty(ref _warmUpStatus, value);
}
private string _vacuumStatus = "--";
/// <summary>
/// 真空状态 | Vacuum status
/// </summary>
public string VacuumStatus
{
get => _vacuumStatus;
private set => SetProperty(ref _vacuumStatus, value);
}
private string _startUpStatus = "--";
/// <summary>
/// 启动状态 | Startup status
/// </summary>
public string StartUpStatus
{
get => _startUpStatus;
private set => SetProperty(ref _startUpStatus, value);
}
private string _autoCenterStatus = "--";
/// <summary>
/// 自动定心状态 | Auto-center status
/// </summary>
public string AutoCenterStatus
{
get => _autoCenterStatus;
private set => SetProperty(ref _autoCenterStatus, value);
}
private string _filamentAdjustStatus = "--";
/// <summary>
/// 灯丝校准状态 | Filament adjust status
/// </summary>
public string FilamentAdjustStatus
{
get => _filamentAdjustStatus;
private set => SetProperty(ref _filamentAdjustStatus, value);
}
private string _powerMode = "--";
/// <summary>
/// 功率模式 | Power mode
/// </summary>
public string PowerMode
{
get => _powerMode;
private set => SetProperty(ref _powerMode, value);
}
private bool _isInterlockActive;
/// <summary>
/// 连锁是否激活 | Whether interlock is active
/// </summary>
public bool IsInterlockActive
{
get => _isInterlockActive;
private set
{
if (SetProperty(ref _isInterlockActive, value))
{
RaisePropertyChanged(nameof(InterlockStatusText));
RefreshCommandStates();
}
}
}
/// <summary>
/// 连锁状态文本 | Interlock status text
/// </summary>
public string InterlockStatusText => IsInterlockActive
? (_localizationService.GetString("RaySource_InterlockActive") ?? "激活")
: (_localizationService.GetString("RaySource_InterlockNormal") ?? "未激活");
private string _watchdogStatus = "--";
/// <summary>
/// 看门狗状态 | Watchdog status
/// </summary>
public string WatchdogStatus
{
get => _watchdogStatus;
private set => SetProperty(ref _watchdogStatus, value);
}
private bool _isXRayOn;
/// <summary>
/// 射线开启状态 | X-ray on status
/// </summary>
public bool IsXRayOn
{
get => _isXRayOn;
private set
{
if (SetProperty(ref _isXRayOn, value))
RaisePropertyChanged(nameof(XRayOnStatusText));
}
}
/// <summary>
/// 射线开启状态文本 | X-ray on status text
/// </summary>
public string XRayOnStatusText => IsXRayOn
? (_localizationService.GetString("RaySource_XRayOn") ?? "开启")
: (_localizationService.GetString("RaySource_XRayOff") ?? "关闭");
private string _txiStatus = "--";
/// <summary>
/// TXI状态 | TXI status
/// </summary>
public string TxiStatus
{
get => _txiStatus;
private set => SetProperty(ref _txiStatus, value);
}
private double _filamentLifetime;
/// <summary>
/// 灯丝寿命百分比(0-100| Filament lifetime percentage (0-100)
/// </summary>
public double FilamentLifetime
{
get => _filamentLifetime;
private set => SetProperty(ref _filamentLifetime, value);
}
private string _filamentLifetimeText = "";
/// <summary>
/// 灯丝寿命显示文本(如 "123.4h / 1000.0h (12.3%)"
/// Filament lifetime display text (e.g. "123.4h / 1000.0h (12.3%)")
/// </summary>
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<RaySourceStatusChangedEvent>().Subscribe(OnRaySourceStatusChanged, ThreadOption.UIThread);
_eventAggregator.GetEvent<StatusUpdatedEvent>().Subscribe(OnStatusUpdated, ThreadOption.UIThread);
_eventAggregator.GetEvent<VariablesConnectedEvent>().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();
}
}
/// <summary>
/// 状态更新事件处理:刷新设备状态面板 | Status updated event handler: refresh device status panel
/// </summary>
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);
}
/// <summary>
/// 重置设备状态为默认值 | Reset device status to defaults
/// </summary>
private void ResetDeviceStatus()
{
WarmUpStatus = "--";
VacuumStatus = "--";
StartUpStatus = "--";
AutoCenterStatus = "--";
FilamentAdjustStatus = "--";
PowerMode = "--";
IsInterlockActive = false;
WatchdogStatus = "--";
IsXRayOn = false;
TxiStatus = "--";
FilamentLifetime = 0;
FilamentLifetimeText = "";
}
/// <summary>
/// 变量连接状态变更事件处理 | Variables connected state changed event handler
/// </summary>
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
/// <summary>
/// 初始化命令执行逻辑 | Initialize command execution logic
/// </summary>
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();
}
}
/// <summary>
/// 初始化命令是否可执行 | Can execute initialize command
/// </summary>
private bool CanExecuteInitialize() => !_isOperating && !IsInitialized;
/// <summary>
/// 连接变量命令执行逻辑 | Connect variables command execution logic
/// </summary>
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();
}
}
/// <summary>
/// 连接变量命令是否可执行 | Can execute connect variables command
/// </summary>
private bool CanExecuteConnectVariables() => !_isOperating && IsInitialized && !IsConnected;
/// <summary>
/// 断开命令执行逻辑 | Disconnect command execution logic
/// </summary>
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<VariablesConnectedEvent>().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();
}
}
/// <summary>
/// 断开命令是否可执行 | Can execute disconnect command
/// </summary>
private bool CanExecuteDisconnect() => !_isOperating && IsInitialized;
/// <summary>
/// 刷新所有命令的可执行状态 | Refresh all command CanExecute states
/// </summary>
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();
}
/// <summary>
/// 设备命令是否可执行(需已连接变量)| Can execute device command (requires variables connected)
/// </summary>
private bool CanExecuteDeviceCommand() => !_isOperating && IsConnected && IsInitialized && IsInterlockActive;
/// <summary>
/// TXI 开启 | TXI On
/// </summary>
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);
}
}
/// <summary>
/// TXI 关闭 | TXI Off
/// </summary>
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);
}
}
/// <summary>
/// 切换到高功率模式 | Switch to High Power mode
/// </summary>
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);
}
}
/// <summary>
/// 切换到微焦点模式 | Switch to Micro Focus mode
/// </summary>
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);
}
}
/// <summary>
/// 暖机设置 | Warm-up setting
/// 参照旧代码:确认对话框 → 显示进度条 → 发送暖机命令
/// </summary>
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);
}
/// <summary>
/// 训机设置 | Training setting
/// 参照旧代码:确认对话框 → 显示进度条 → 发送训机命令
/// </summary>
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);
}
/// <summary>
/// 灯丝校准 | Filament calibration
/// 参照旧代码:确认对话框 → 显示进度条 → 发送灯丝校准命令
/// </summary>
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);
}
/// <summary>
/// 自动定心设置 | Auto-center setting
/// 参照旧代码:确认对话框 → 显示进度条 → 发送全部电压自动定心命令
/// </summary>
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);
}
/// <summary>
/// 执行长时间运行操作的通用方法(显示进度条窗口,实时跟踪设备进度)
/// 发送指令后订阅 StatusUpdatedEvent,根据实时电压/电流/状态字符串计算进度
/// </summary>
/// <param name="title">进度窗口标题</param>
/// <param name="message">进度窗口提示信息</param>
/// <param name="operation">要执行的操作(发送指令)</param>
/// <param name="operationType">操作类型(用于选择进度计算策略)</param>
/// <param name="timeoutMs">超时时间(毫秒)</param>
private async Task ExecuteLongRunningOperation(
string title, string message, Func<XRayResult> 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<bool>();
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<StatusUpdatedEvent>().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<StatusUpdatedEvent>().Unsubscribe(OnProgressStatusUpdated);
cts.Dispose();
_isOperating = false;
RefreshCommandStates();
progressWindow.Close();
}
}
/// <summary>
/// 从灯丝寿命服务获取最新数据并刷新 UI 属性(纯展示,不含计算逻辑)
/// Refresh filament lifetime UI properties from service (display only, no calculation logic)
/// </summary>
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);
}
}
/// <summary>
/// 启动灯丝寿命定时刷新定时器 | Start filament lifetime periodic refresh timer
/// </summary>
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)");
}
/// <summary>
/// 停止灯丝寿命定时刷新定时器 | Stop filament lifetime periodic refresh timer
/// </summary>
private void StopLifetimeRefreshTimer()
{
if (_lifetimeRefreshTimer != null)
{
_lifetimeRefreshTimer.Stop();
_lifetimeRefreshTimer.Tick -= OnLifetimeRefreshTimerTick;
_lifetimeRefreshTimer = null;
_logger.Debug("灯丝寿命定时刷新已停止 | Filament lifetime refresh timer stopped");
}
}
/// <summary>
/// 定时器 Tick 事件处理:刷新灯丝寿命数据 | Timer tick handler: refresh filament lifetime data
/// </summary>
private void OnLifetimeRefreshTimerTick(object sender, EventArgs e)
{
RefreshFilamentLifetime();
}
#endregion
#region IDisposable | IDisposable Implementation
/// <summary>
/// 释放资源:停止定时器、取消事件订阅 | Dispose resources: stop timer, unsubscribe events
/// </summary>
public void Dispose()
{
if (!_isDisposed)
{
// 停止灯丝寿命定时刷新 | Stop filament lifetime refresh timer
StopLifetimeRefreshTimer();
// 取消事件订阅 | Unsubscribe events
_eventAggregator.GetEvent<RaySourceStatusChangedEvent>().Unsubscribe(OnRaySourceStatusChanged);
_eventAggregator.GetEvent<StatusUpdatedEvent>().Unsubscribe(OnStatusUpdated);
_eventAggregator.GetEvent<VariablesConnectedEvent>().Unsubscribe(OnVariablesConnectedChanged);
_isDisposed = true;
_logger.Info("射线源配置视图模型已释放 | X-ray source config view model disposed");
}
}
#endregion
}
}