将Feature/XP.Common和Feature/XP.Hardware分支合并至Develop/XP.forHardwareAndCommon,完善XPapp注册和相关硬件类库通用类库功能。

This commit is contained in:
QI Mingxuan
2026-04-16 17:31:13 +08:00
parent 6ec4c3ddaa
commit 2bd6e566c3
581 changed files with 74600 additions and 222 deletions
@@ -0,0 +1,919 @@
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
}
}