将Feature/XP.Common和Feature/XP.Hardware分支合并至Develop/XP.forHardwareAndCommon,完善XPapp注册和相关硬件类库通用类库功能。
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,771 @@
|
||||
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
|
||||
{
|
||||
/// <summary>
|
||||
/// 射线源操作视图模型 | X-Ray Source Operate ViewModel
|
||||
/// 负责射线源开关控制、电压电流调节、状态监控 | Responsible for X-ray on/off control, voltage/current adjustment, status monitoring
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// 标记当前是否正在从设备反馈更新值(防止 code-behind 误触发写入)
|
||||
/// Flag indicating whether values are being updated from device feedback (prevents code-behind from triggering writes)
|
||||
/// </summary>
|
||||
public bool IsUpdatingFromDevice { get; private set; } = false;
|
||||
|
||||
#endregion
|
||||
|
||||
#region 状态属性 | Status Properties
|
||||
|
||||
private RaySourceStatus _raySourceStatus = RaySourceStatus.Unavailable;
|
||||
/// <summary>
|
||||
/// 射线源状态(不可用/关闭/开启)| X-ray source status (Unavailable/Closed/Opened)
|
||||
/// </summary>
|
||||
public RaySourceStatus RaySourceStatus
|
||||
{
|
||||
get => _raySourceStatus;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _raySourceStatus, value))
|
||||
{
|
||||
RaisePropertyChanged(nameof(StatusText));
|
||||
RaisePropertyChanged(nameof(IsSlidersEnabled));
|
||||
TurnOnCommand?.RaiseCanExecuteChanged();
|
||||
TurnOffCommand?.RaiseCanExecuteChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 状态文本(绑定到腰圆内的文字)| Status text (bound to capsule text)
|
||||
/// </summary>
|
||||
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")
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 滑块是否启用(仅服务已初始化时可调节)| Sliders enabled (only when service is initialized)
|
||||
/// </summary>
|
||||
public bool IsSlidersEnabled => _raySourceService.IsInitialized && _isVariablesConnected;
|
||||
|
||||
/// <summary>
|
||||
/// PVI 变量是否已连接 | Whether PVI variables are connected
|
||||
/// </summary>
|
||||
public bool IsVariablesConnected
|
||||
{
|
||||
get => _isVariablesConnected;
|
||||
private set
|
||||
{
|
||||
if (SetProperty(ref _isVariablesConnected, value))
|
||||
{
|
||||
RaisePropertyChanged(nameof(IsSlidersEnabled));
|
||||
TurnOnCommand?.RaiseCanExecuteChanged();
|
||||
TurnOffCommand?.RaiseCanExecuteChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool _isInterlockActive;
|
||||
/// <summary>
|
||||
/// 连锁是否激活 | Whether interlock is active
|
||||
/// </summary>
|
||||
public bool IsInterlockActive
|
||||
{
|
||||
get => _isInterlockActive;
|
||||
private set
|
||||
{
|
||||
if (SetProperty(ref _isInterlockActive, value))
|
||||
{
|
||||
TurnOnCommand?.RaiseCanExecuteChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 电压属性 | Voltage Properties
|
||||
|
||||
private double _voltageMin;
|
||||
/// <summary>
|
||||
/// 电压最小值(kV)| Voltage minimum (kV)
|
||||
/// </summary>
|
||||
public double VoltageMin
|
||||
{
|
||||
get => _voltageMin;
|
||||
set => SetProperty(ref _voltageMin, value);
|
||||
}
|
||||
|
||||
private double _voltageMax;
|
||||
/// <summary>
|
||||
/// 电压最大值(kV)| Voltage maximum (kV)
|
||||
/// </summary>
|
||||
public double VoltageMax
|
||||
{
|
||||
get => _voltageMax;
|
||||
set => SetProperty(ref _voltageMax, value);
|
||||
}
|
||||
|
||||
private double _voltageValue;
|
||||
/// <summary>
|
||||
/// 电压滑块值(kV),仅更新UI显示,不自动写入硬件 | Voltage slider value (kV), UI display only, no auto write to hardware
|
||||
/// </summary>
|
||||
public double VoltageValue
|
||||
{
|
||||
get => _voltageValue;
|
||||
set => SetProperty(ref _voltageValue, Math.Round(value, 0));
|
||||
}
|
||||
|
||||
private double _voltageActual;
|
||||
/// <summary>
|
||||
/// 电压实际值(kV)| Voltage actual value (kV)
|
||||
/// </summary>
|
||||
public double VoltageActual
|
||||
{
|
||||
get => _voltageActual;
|
||||
set => SetProperty(ref _voltageActual, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 电压实际值显示文本 | Voltage actual value display text
|
||||
/// </summary>
|
||||
public string VoltageActualText => string.Format(_localizationService.GetString("RaySource_ActualValueLabel"), VoltageActual.ToString("F1"));
|
||||
|
||||
#endregion
|
||||
|
||||
#region 电流属性 | Current Properties
|
||||
|
||||
private double _currentMin;
|
||||
/// <summary>
|
||||
/// 电流最小值(μA)| Current minimum (μA)
|
||||
/// </summary>
|
||||
public double CurrentMin
|
||||
{
|
||||
get => _currentMin;
|
||||
set => SetProperty(ref _currentMin, value);
|
||||
}
|
||||
|
||||
private double _currentMax;
|
||||
/// <summary>
|
||||
/// 电流最大值(μA)| Current maximum (μA)
|
||||
/// </summary>
|
||||
public double CurrentMax
|
||||
{
|
||||
get => _currentMax;
|
||||
set => SetProperty(ref _currentMax, value);
|
||||
}
|
||||
|
||||
private double _currentValue;
|
||||
/// <summary>
|
||||
/// 电流滑块值(μA),仅更新UI显示,不自动写入硬件 | Current slider value (μA), UI display only, no auto write to hardware
|
||||
/// </summary>
|
||||
public double CurrentValue
|
||||
{
|
||||
get => _currentValue;
|
||||
set => SetProperty(ref _currentValue, Math.Round(value, 0));
|
||||
}
|
||||
|
||||
private double _currentActual;
|
||||
/// <summary>
|
||||
/// 电流实际值(μA)| Current actual value (μA)
|
||||
/// </summary>
|
||||
public double CurrentActual
|
||||
{
|
||||
get => _currentActual;
|
||||
set => SetProperty(ref _currentActual, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 电流实际值显示文本 | Current actual value display text
|
||||
/// </summary>
|
||||
public string CurrentActualText => string.Format(_localizationService.GetString("RaySource_ActualValueLabel"), CurrentActual.ToString("F1"));
|
||||
|
||||
#endregion
|
||||
|
||||
#region 命令 | Commands
|
||||
|
||||
/// <summary>
|
||||
/// 开启射线源命令 | Turn on X-ray command
|
||||
/// </summary>
|
||||
public DelegateCommand TurnOnCommand { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 关闭射线源命令 | Turn off X-ray command
|
||||
/// </summary>
|
||||
public DelegateCommand TurnOffCommand { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 提交电压值命令(滑块松手/输入框失焦时触发)| Apply voltage command (triggered on slider drag complete / input lost focus)
|
||||
/// </summary>
|
||||
public DelegateCommand ApplyVoltageCommand { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 提交电流值命令(滑块松手/输入框失焦时触发)| Apply current command (triggered on slider drag complete / input lost focus)
|
||||
/// </summary>
|
||||
public DelegateCommand ApplyCurrentCommand { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 设置命令(预留接口)| Settings command (reserved interface)
|
||||
/// </summary>
|
||||
public DelegateCommand SettingsCommand { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 配置命令(打开射线源配置窗口)| Config command (open ray source config window)
|
||||
/// </summary>
|
||||
public DelegateCommand ConfigCommand { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region 构造函数 | Constructor
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数 | Constructor
|
||||
/// </summary>
|
||||
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
|
||||
|
||||
/// <summary>
|
||||
/// 订阅事件 | Subscribe to events
|
||||
/// </summary>
|
||||
private void SubscribeEvents()
|
||||
{
|
||||
// 订阅射线源状态变更事件(三态)| Subscribe to ray source status changed event (tri-state)
|
||||
_eventAggregator.GetEvent<RaySourceStatusChangedEvent>().Subscribe(OnRaySourceStatusChanged, ThreadOption.UIThread);
|
||||
|
||||
// 订阅状态更新事件 | Subscribe to status updated event
|
||||
_eventAggregator.GetEvent<StatusUpdatedEvent>().Subscribe(OnStatusUpdated, ThreadOption.UIThread);
|
||||
|
||||
// 订阅错误事件 | Subscribe to error event
|
||||
_eventAggregator.GetEvent<ErrorOccurredEvent>().Subscribe(OnErrorOccurred, ThreadOption.UIThread);
|
||||
|
||||
// 订阅操作结果事件 | Subscribe to operation result event
|
||||
_eventAggregator.GetEvent<OperationResultEvent>().Subscribe(OnOperationResult, ThreadOption.UIThread);
|
||||
|
||||
// 订阅变量连接状态变更事件 | Subscribe to variables connected event
|
||||
_eventAggregator.GetEvent<VariablesConnectedEvent>().Subscribe(OnVariablesConnectedChanged, ThreadOption.UIThread);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 射线源状态变更事件处理(三态)| Ray source status changed event handler (tri-state)
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 状态更新事件处理 | Status updated event handler
|
||||
/// 仅在实际值真正变化时更新滑块/输入框,避免无意义赋值触发事件链
|
||||
/// Only update slider/input when actual value truly changed, avoiding unnecessary assignment triggering event chain
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 错误事件处理 | Error event handler
|
||||
/// </summary>
|
||||
private void OnErrorOccurred(string errorMessage)
|
||||
{
|
||||
// 显示错误消息 | Show error message
|
||||
MessageBox.Show(errorMessage, "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 操作结果事件处理 | Operation result event handler
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 变量连接状态变更事件处理 | Variables connected state changed event handler
|
||||
/// </summary>
|
||||
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
|
||||
|
||||
/// <summary>
|
||||
/// 开启射线源执行逻辑 | Turn on X-ray execution logic
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 开启命令是否可执行(仅关闭状态且连锁激活时可执行)| Can execute turn on command (only when closed and interlock active)
|
||||
/// </summary>
|
||||
private bool CanExecuteTurnOn()
|
||||
{
|
||||
return !_isOperating && _isVariablesConnected && IsInterlockActive && RaySourceStatus == RaySourceStatus.Closed && _raySourceService.IsInitialized;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 关闭射线源执行逻辑 | Turn off X-ray execution logic
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 关闭命令是否可执行(仅开启状态可执行)| Can execute turn off command (only when opened)
|
||||
/// </summary>
|
||||
private bool CanExecuteTurnOff()
|
||||
{
|
||||
return !_isOperating && _isVariablesConnected && RaySourceStatus == RaySourceStatus.Opened && _raySourceService.IsInitialized;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置命令执行逻辑:打开外部高级设置程序 | Settings command: launch external advance settings program
|
||||
/// 如果程序已运行则将窗口置前 | If program is already running, bring its window to front
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置命令是否可执行 | Can execute settings command
|
||||
/// </summary>
|
||||
private bool CanExecuteSettings()
|
||||
{
|
||||
return true; // 始终可用 | Always available
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 配置命令执行逻辑:打开射线源配置窗口 | Config command: open ray source config window
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 提交电压值到硬件(滑块松手/输入框失焦时调用)| Apply voltage to hardware (called on slider drag complete / input lost focus)
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 提交电流值到硬件(滑块松手/输入框失焦时调用)| Apply current to hardware (called on slider drag complete / input lost focus)
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从服务更新状态 | Update status from service
|
||||
/// </summary>
|
||||
private void UpdateStatusFromService()
|
||||
{
|
||||
RaySourceStatus = _raySourceService.CurrentStatus;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从设备主动读取最新状态并刷新UI | Actively read latest status from device and refresh UI
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 界面加载时自动执行初始化和连接变量流程(仅执行一次)
|
||||
/// Auto-execute Initialize and ConnectVariables on view loaded (runs only once)
|
||||
/// </summary>
|
||||
/// <summary>
|
||||
/// 自动初始化:异步执行初始化 + 连接变量 | Auto-initialize: async Initialize + ConnectVariables
|
||||
/// 由 View 的 Loaded 事件触发,仅执行一次 | Triggered by View's Loaded event, runs only once
|
||||
/// </summary>
|
||||
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
|
||||
|
||||
/// <summary>
|
||||
/// 释放资源 | Dispose resources
|
||||
/// </summary>
|
||||
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<RaySourceStatusChangedEvent>().Unsubscribe(OnRaySourceStatusChanged);
|
||||
_eventAggregator.GetEvent<StatusUpdatedEvent>().Unsubscribe(OnStatusUpdated);
|
||||
_eventAggregator.GetEvent<ErrorOccurredEvent>().Unsubscribe(OnErrorOccurred);
|
||||
_eventAggregator.GetEvent<OperationResultEvent>().Unsubscribe(OnOperationResult);
|
||||
_eventAggregator.GetEvent<VariablesConnectedEvent>().Unsubscribe(OnVariablesConnectedChanged);
|
||||
|
||||
_isDisposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user