Files
XplorePlane/XP.Hardware.Detector/ViewModels/DetectorConfigViewModel.cs
T
QI Mingxuan 2d7cf17a3b 探测器XP.Hardware.Detector类库为了更好集成新的探测器,统一接口方法,DetectorService重构为通过统一接口;
新增暗场校正和亮场校正帧数配置属性(默认 64,范围 1-128),config 加载校正帧数;
修正探测器IsConnected连接状态的判断逻辑。
2026-05-21 13:19:30 +08:00

774 lines
32 KiB
C#
Raw 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 System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using Prism.Commands;
using Prism.Events;
using Prism.Mvvm;
using XP.Common.GeneralForm.Views;
using XP.Common.Localization;
using XP.Common.Logging.Interfaces;
using XP.Hardware.Detector.Abstractions;
using XP.Hardware.Detector.Abstractions.Events;
using XP.Hardware.Detector.Abstractions.Enums;
using XP.Hardware.Detector.Config;
using XP.Hardware.Detector.Services;
namespace XP.Hardware.Detector.ViewModels
{
/// <summary>
/// 面阵探测器配置 ViewModel | Area detector configuration ViewModel
/// 根据探测器类型动态加载参数选项,包含校正参数一致性校验和扫描期间 UI 锁定
/// </summary>
public class DetectorConfigViewModel : BindableBase
{
private readonly IDetectorService _detectorService;
private readonly IEventAggregator _eventAggregator;
private readonly ILoggerService _logger;
private DetectorConfig _config;
#region | Connection status
private bool _isConnected;
/// <summary>
/// 探测器是否已连接 | Whether detector is connected
/// </summary>
public bool IsConnected
{
get => _isConnected;
private set
{
if (SetProperty(ref _isConnected, value))
RaiseAllCommandsCanExecuteChanged();
}
}
#endregion
#region | Parameter snapshot at dark correction time
private int _darkCorrectionBinningIndex = -1;
private int _darkCorrectionPga = -1;
private decimal _darkCorrectionFrameRate = -1m;
#endregion
#region PGA| Sensitivity (PGA)
/// <summary>
/// PGA 可选项列表 | PGA selectable items
/// </summary>
public ObservableCollection<int> PgaItems { get; } = new ObservableCollection<int>();
private int _selectedPga;
/// <summary>
/// 当前选中的 PGA 值 | Currently selected PGA value
/// </summary>
public int SelectedPga
{
get => _selectedPga;
set => SetProperty(ref _selectedPga, value);
}
#endregion
#region Binning| Pixel binning
/// <summary>
/// Binning 可选项列表 | Binning selectable items
/// </summary>
public ObservableCollection<BinningOption> BinningItems { get; } = new ObservableCollection<BinningOption>();
private int _selectedBinningIndex;
/// <summary>
/// 当前选中的 Binning 索引 | Currently selected binning index
/// </summary>
public int SelectedBinningIndex
{
get => _selectedBinningIndex;
set
{
if (SetProperty(ref _selectedBinningIndex, value))
{
ClampFrameRate();
UpdateImageSpec();
}
}
}
#endregion
#region | Frame rate
private decimal _frameRate = 5m;
/// <summary>
/// 帧率 | Frame rate
/// </summary>
public decimal FrameRate
{
get => _frameRate;
set
{
if (value < FrameRateMinimum)
value = FrameRateMinimum;
var max = GetMaxFrameRate(_selectedBinningIndex);
if (value > max)
{
_logger?.Warn("帧率超出当前 Binning 模式上限 {Max},已自动修正 | Frame rate exceeds max {Max} for current binning, auto corrected", max);
value = max;
}
SetProperty(ref _frameRate, value);
}
}
/// <summary>
/// 帧率最小值 | Frame rate minimum
/// </summary>
public decimal FrameRateMinimum => 0.1m;
private decimal _frameRateMaximum = 15m;
/// <summary>
/// 帧率最大值(随 Binning 变化)| Frame rate maximum (varies with binning)
/// </summary>
public decimal FrameRateMaximum
{
get => _frameRateMaximum;
private set => SetProperty(ref _frameRateMaximum, value);
}
#endregion
#region | Average frames
private int _avgFrames = 1;
/// <summary>
/// 帧合并数 | Average frame count
/// </summary>
public int AvgFrames
{
get => _avgFrames;
set
{
if (value < 1) value = 1;
SetProperty(ref _avgFrames, value);
}
}
#endregion
#region Binning | Image spec (read-only, varies with binning)
private string _imageSpecText = "";
/// <summary>
/// 当前 Binning 模式下的图像规格文本 | Image spec text for current binning mode
/// </summary>
public string ImageSpecText
{
get => _imageSpecText;
private set => SetProperty(ref _imageSpecText, value);
}
#endregion
#region | Commands
public DelegateCommand DarkCorrectionCommand { get; }
public DelegateCommand LightCorrectionCommand { get; }
public DelegateCommand BadPixelCorrectionCommand { get; }
public DelegateCommand ApplyParametersCommand { get; }
#endregion
#region | Status
private bool _isBusy;
/// <summary>
/// 是否正在执行校正操作 | Whether a correction operation is in progress
/// </summary>
public bool IsBusy
{
get => _isBusy;
private set
{
SetProperty(ref _isBusy, value);
RaiseAllCommandsCanExecuteChanged();
}
}
private bool _darkCorrectionDone;
/// <summary>
/// 暗场校正是否已完成 | Whether dark correction is done
/// </summary>
public bool DarkCorrectionDone
{
get => _darkCorrectionDone;
private set
{
SetProperty(ref _darkCorrectionDone, value);
LightCorrectionCommand.RaiseCanExecuteChanged();
}
}
private bool _isParametersLocked;
/// <summary>
/// 参数是否被锁定(扫描采集期间禁止修改)| Whether parameters are locked (during acquisition)
/// </summary>
public bool IsParametersLocked
{
get => _isParametersLocked;
private set
{
SetProperty(ref _isParametersLocked, value);
RaisePropertyChanged(nameof(IsParametersEditable));
RaiseAllCommandsCanExecuteChanged();
}
}
/// <summary>
/// 参数是否可编辑(已连接、未锁定且不忙)| Whether parameters are editable
/// </summary>
public bool IsParametersEditable => _isConnected && !_isParametersLocked && !_isBusy;
#endregion
#region | Constructor
public DetectorConfigViewModel(
IDetectorService detectorService,
IEventAggregator eventAggregator,
ILoggerService logger)
{
_detectorService = detectorService ?? throw new ArgumentNullException(nameof(detectorService));
_eventAggregator = eventAggregator ?? throw new ArgumentNullException(nameof(eventAggregator));
_logger = logger?.ForModule<DetectorConfigViewModel>() ?? throw new ArgumentNullException(nameof(logger));
DarkCorrectionCommand = new DelegateCommand(ExecuteDarkCorrectionAsync, CanExecuteCorrection);
LightCorrectionCommand = new DelegateCommand(ExecuteLightCorrectionAsync, CanExecuteLightCorrection);
BadPixelCorrectionCommand = new DelegateCommand(ExecuteBadPixelCorrectionAsync, CanExecuteCorrection);
ApplyParametersCommand = new DelegateCommand(ExecuteApplyParametersAsync, CanExecuteCorrection);
// 订阅探测器状态变更事件,用于扫描期间锁定参数 | Subscribe to status changed event for parameter locking
_eventAggregator.GetEvent<StatusChangedEvent>()
.Subscribe(OnDetectorStatusChanged, ThreadOption.UIThread);
// 从配置加载 UI 选项 | Load UI options from config
LoadOptionsFromConfig();
// 初始化连接状态(ViewModel 可能在探测器已连接后才创建)| Initialize connection status (ViewModel may be created after detector is already connected)
IsConnected = _detectorService.IsConnected;
}
#endregion
#region | Public methods
/// <summary>
/// 锁定参数(外部调用,如扫描开始时)| Lock parameters (called externally, e.g. when scan starts)
/// </summary>
public void LockParameters()
{
IsParametersLocked = true;
_logger?.Info("探测器参数已锁定 | Detector parameters locked");
}
/// <summary>
/// 解锁参数(外部调用,如扫描结束时)| Unlock parameters (called externally, e.g. when scan ends)
/// </summary>
public void UnlockParameters()
{
IsParametersLocked = false;
_logger?.Info("探测器参数已解锁 | Detector parameters unlocked");
}
/// <summary>
/// 获取当前参数的图像规格(供扫描配置导出)| Get current image spec for scan config export
/// </summary>
public BinningImageSpec GetCurrentImageSpec()
{
return _config?.GetImageSpec(_selectedBinningIndex);
}
/// <summary>
/// 导出当前探测器参数为字典(供扫描配置保存)| Export current parameters as dictionary
/// </summary>
public System.Collections.Generic.Dictionary<string, string> ExportParameters()
{
var dict = new System.Collections.Generic.Dictionary<string, string>();
var spec = GetCurrentImageSpec();
var binningName = _selectedBinningIndex < BinningItems.Count
? BinningItems[_selectedBinningIndex].DisplayName : "1×1";
dict["Det_Binning"] = binningName;
dict["Det_PGA"] = _selectedPga.ToString();
dict["Det_Frame_rate"] = _frameRate.ToString(System.Globalization.CultureInfo.InvariantCulture);
dict["Det_Avg_Frames"] = _avgFrames.ToString();
if (spec != null)
{
dict["Pixel_X"] = spec.PixelX.ToString(System.Globalization.CultureInfo.InvariantCulture);
dict["Pixel_Y"] = spec.PixelY.ToString(System.Globalization.CultureInfo.InvariantCulture);
dict["Image_Size_Width"] = spec.ImageWidth.ToString();
dict["Image_Size_Height"] = spec.ImageHeight.ToString();
}
return dict;
}
#endregion
#region | Private methods
/// <summary>
/// 从探测器配置加载下拉框选项 | Load combo box options from detector config
/// </summary>
private void LoadOptionsFromConfig()
{
_config = _detectorService.GetCurrentConfig();
// 加载 Binning 选项 | Load binning options
BinningItems.Clear();
var binnings = _config?.GetSupportedBinnings();
if (binnings != null && binnings.Count > 0)
{
foreach (var b in binnings) BinningItems.Add(b);
}
else
{
BinningItems.Add(new BinningOption("1×1", 0));
BinningItems.Add(new BinningOption("2×2", 1));
}
// 加载 PGA 选项 | Load PGA options
PgaItems.Clear();
var pgas = _config?.GetSupportedPgaValues();
if (pgas != null && pgas.Count > 0)
{
foreach (var p in pgas) PgaItems.Add(p);
}
else
{
foreach (var p in new[] { 2, 3, 4, 5, 6, 7 }) PgaItems.Add(p);
}
// 尝试从持久化配置恢复参数 | Try to restore parameters from saved config
var saved = ConfigLoader.LoadSavedParameters();
if (saved.HasValue)
{
var (binIdx, pga, fr, avg) = saved.Value;
SelectedBinningIndex = binIdx < BinningItems.Count ? binIdx : 0;
SelectedPga = PgaItems.Contains(pga) ? pga : PgaItems.FirstOrDefault();
FrameRate = fr > 0 ? fr : 5m;
AvgFrames = avg > 0 ? avg : 1;
_logger?.Info("从持久化配置恢复参数,Binning={Binning}PGA={PGA},帧率={FrameRate},帧合并={AvgFrames} | Restored parameters from saved config",
binIdx, pga, fr, avg);
}
else
{
SelectedBinningIndex = 0;
SelectedPga = PgaItems.FirstOrDefault();
}
// 初始化帧率上限 | Initialize frame rate maximum
FrameRateMaximum = GetMaxFrameRate(_selectedBinningIndex);
UpdateImageSpec();
_logger?.Info("探测器配置选项已加载,类型={Type}Binning选项数={BinCount}PGA选项数={PgaCount} | Detector config options loaded",
_config?.Type.ToString() ?? "未知", BinningItems.Count, PgaItems.Count);
}
/// <summary>
/// 获取最大帧率(优先从配置获取)| Get max frame rate (prefer from config)
/// </summary>
private decimal GetMaxFrameRate(int binningIndex)
{
return _config?.GetMaxFrameRate(binningIndex) ?? 15m;
}
private void ClampFrameRate()
{
var max = GetMaxFrameRate(_selectedBinningIndex);
FrameRateMaximum = max;
if (_frameRate > max)
{
_logger?.Warn("Binning 变更,帧率已从 {Old} 调整为上限 {Max} | Binning changed, frame rate adjusted from {Old} to max {Max}", _frameRate, max);
FrameRate = max;
}
}
/// <summary>
/// 更新图像规格显示文本 | Update image spec display text
/// </summary>
private void UpdateImageSpec()
{
var spec = _config?.GetImageSpec(_selectedBinningIndex);
if (spec != null)
{
ImageSpecText = $"{spec.ImageWidth}×{spec.ImageHeight} 像素尺寸 {spec.PixelX}×{spec.PixelY} mm";
}
else
{
ImageSpecText = "";
}
}
/// <summary>
/// 探测器状态变更回调,用于同步连接状态 | Detector status changed callback for connection status sync
/// 注意:参数锁定由外部扫描流程通过 LockParameters()/UnlockParameters() 显式控制,
/// 普通预览采集不应锁定配置页面按钮
/// </summary>
private void OnDetectorStatusChanged(DetectorStatus status)
{
// 同步连接状态:只有 Ready、Acquiring、Correcting 视为已连接 | Only Ready, Acquiring, Correcting are considered connected
IsConnected = status == DetectorStatus.Ready
|| status == DetectorStatus.Acquiring
|| status == DetectorStatus.Correcting;
}
/// <summary>
/// 校验亮场校正前参数是否与暗场校正时一致 | Validate parameters consistency before light correction
/// </summary>
/// <returns>参数是否一致 | Whether parameters are consistent</returns>
private bool ValidateCorrectionParametersConsistency()
{
if (_darkCorrectionBinningIndex < 0)
{
_logger?.Warn("未执行暗场校正,无法进行亮场校正 | Dark correction not done, cannot perform light correction");
return false;
}
if (_selectedBinningIndex != _darkCorrectionBinningIndex ||
_selectedPga != _darkCorrectionPga ||
_frameRate != _darkCorrectionFrameRate)
{
_logger?.Warn("暗场校正与亮场校正参数不一致:暗场 Binning={DarkBin}PGA={DarkPga},帧率={DarkFr};当前 Binning={CurBin}PGA={CurPga},帧率={CurFr} | Parameter mismatch between dark and light correction",
_darkCorrectionBinningIndex, _darkCorrectionPga, _darkCorrectionFrameRate,
_selectedBinningIndex, _selectedPga, _frameRate);
return false;
}
return true;
}
/// <summary>
/// 记录暗场校正时的参数快照 | Record parameter snapshot at dark correction time
/// </summary>
private void RecordDarkCorrectionParameters()
{
_darkCorrectionBinningIndex = _selectedBinningIndex;
_darkCorrectionPga = _selectedPga;
_darkCorrectionFrameRate = _frameRate;
}
#endregion
#region | Command execution
private bool CanExecuteCorrection() => _isConnected && !_isBusy && !_isParametersLocked;
private bool CanExecuteLightCorrection() => _isConnected && !_isBusy && !_isParametersLocked && _darkCorrectionDone;
private void RaiseAllCommandsCanExecuteChanged()
{
DarkCorrectionCommand.RaiseCanExecuteChanged();
LightCorrectionCommand.RaiseCanExecuteChanged();
BadPixelCorrectionCommand.RaiseCanExecuteChanged();
ApplyParametersCommand.RaiseCanExecuteChanged();
RaisePropertyChanged(nameof(IsParametersEditable));
}
/// <summary>
/// 获取校正帧数(从配置加载)| Get correction frame count (loaded from config)
/// </summary>
private CorrectionCapabilities GetCorrectionCaps() => _detectorService.GetCorrectionCapabilities();
/// <summary>
/// 执行暗场校正 | Execute dark correction
/// </summary>
private async void ExecuteDarkCorrectionAsync()
{
// 弹出用户确认对话框 | Show user confirmation dialog
var confirmResult = MessageBox.Show(
LocalizationHelper.Get("Detector_DarkCorrection_ConfirmMessage"),
LocalizationHelper.Get("Detector_DarkCorrection_ConfirmTitle"),
MessageBoxButton.OKCancel,
MessageBoxImage.Question);
if (confirmResult != MessageBoxResult.OK)
{
_logger?.Info("用户取消暗场校正 | User cancelled dark correction");
return;
}
var binningName = _selectedBinningIndex < BinningItems.Count ? BinningItems[_selectedBinningIndex].DisplayName : "?";
_logger?.Info("开始暗场校正,Binning={Binning}PGA={PGA},帧率={FrameRate},校正帧数={FrameCount} | Starting dark correction",
binningName, _selectedPga, _frameRate, GetCorrectionCaps().DarkFrameCount);
// 显示进度条窗口 | Show progress window
var progressWindow = new ProgressWindow(
title: LocalizationHelper.Get("Detector_DarkCorrection_Title"),
message: LocalizationHelper.Get("Detector_Progress_ApplyingParameters"),
isCancelable: false,
logger: _logger);
progressWindow.Show();
IsBusy = true;
bool wasAcquiring = false;
try
{
// 1. 如果正在采集,先停止(后续应用参数和校正都需要探测器空闲)| Stop acquisition if running
wasAcquiring = _detectorService.Status == DetectorStatus.Acquiring;
if (wasAcquiring)
{
progressWindow.UpdateProgress(LocalizationHelper.Get("Detector_Progress_StoppingAcquisition"), 5);
var stopResult = await _detectorService.StopAcquisitionAsync();
if (!stopResult.IsSuccess)
{
_logger?.Error(stopResult.Exception, "停止采集失败,暗场校正中止:{Message} | Stop acquisition failed, dark correction aborted: {Message}", stopResult.ErrorMessage);
return;
}
}
// 2. 应用参数到硬件 | Apply parameters to hardware
progressWindow.UpdateProgress(LocalizationHelper.Get("Detector_Progress_ApplyingParameters"), 10);
var applyResult = await _detectorService.ApplyParametersAsync(_selectedBinningIndex, _selectedPga, _frameRate);
if (!applyResult.IsSuccess)
{
_logger?.Error(applyResult.Exception, "应用参数失败,暗场校正中止:{Message} | Apply parameters failed, dark correction aborted: {Message}", applyResult.ErrorMessage);
return;
}
// 3. 执行暗场校正 | Execute dark correction
progressWindow.UpdateProgress(LocalizationHelper.Get("Detector_Progress_AcquiringDarkData"), 30);
var result = await _detectorService.DarkCorrectionAsync(GetCorrectionCaps().DarkFrameCount);
if (result.IsSuccess)
{
progressWindow.UpdateProgress(LocalizationHelper.Get("Detector_Progress_DarkCorrectionDone"), 100);
RecordDarkCorrectionParameters();
DarkCorrectionDone = true;
_logger?.Info("暗场校正完成 | Dark correction completed");
_detectorService.SaveParameters(_selectedBinningIndex, _selectedPga, _frameRate, _avgFrames);
}
else
{
_logger?.Error(result.Exception, "暗场校正失败:{Message} | Dark correction failed: {Message}", result.ErrorMessage);
}
}
catch (Exception ex)
{
_logger?.Error(ex, "暗场校正异常:{Message} | Dark correction exception: {Message}", ex.Message);
}
finally
{
// 如果之前在采集,恢复连续采集 | Resume acquisition if it was running before
if (wasAcquiring)
{
_logger?.Info("暗场校正流程结束,恢复连续采集 | Dark correction flow done, resuming continuous acquisition");
await _detectorService.StartAcquisitionAsync();
}
IsBusy = false;
progressWindow.Close();
}
}
/// <summary>
/// 执行亮场校正(含参数一致性校验)| Execute light correction (with parameter consistency check)
/// </summary>
private async void ExecuteLightCorrectionAsync()
{
// 校验参数一致性 | Validate parameter consistency
if (!ValidateCorrectionParametersConsistency())
{
_logger?.Warn("暗场校正与亮场校正参数不一致,请重新进行暗场校正 | Parameter mismatch, please redo dark correction");
MessageBox.Show(
LocalizationHelper.Get("Detector_ParameterMismatch_Message"),
LocalizationHelper.Get("Detector_ParameterMismatch_Title"),
MessageBoxButton.OK,
MessageBoxImage.Warning);
DarkCorrectionDone = false;
return;
}
// 弹出确认对话框:物体移出视野 | Confirm object removed from field of view
var confirmObjectResult = MessageBox.Show(
LocalizationHelper.Get("Detector_LightCorrection_ConfirmObjectMessage"),
LocalizationHelper.Get("Detector_LightCorrection_ConfirmObjectTitle"),
MessageBoxButton.OKCancel,
MessageBoxImage.Question);
if (confirmObjectResult != MessageBoxResult.OK)
{
_logger?.Info("用户取消亮场校正(物体确认)| User cancelled light correction (object confirmation)");
return;
}
// 弹出确认对话框:射线源已开启 | Confirm X-ray source is ON
var confirmRayResult = MessageBox.Show(
LocalizationHelper.Get("Detector_LightCorrection_ConfirmRayMessage"),
LocalizationHelper.Get("Detector_LightCorrection_ConfirmObjectTitle"),
MessageBoxButton.OKCancel,
MessageBoxImage.Question);
if (confirmRayResult != MessageBoxResult.OK)
{
_logger?.Info("用户取消亮场校正(射线源确认)| User cancelled light correction (ray source confirmation)");
return;
}
var binningName = _selectedBinningIndex < BinningItems.Count ? BinningItems[_selectedBinningIndex].DisplayName : "?";
_logger?.Info("开始亮场校正,Binning={Binning}PGA={PGA},帧率={FrameRate},校正帧数={FrameCount} | Starting light correction",
binningName, _selectedPga, _frameRate, GetCorrectionCaps().GainFrameCount);
// 显示进度条窗口 | Show progress window
var progressWindow = new ProgressWindow(
title: LocalizationHelper.Get("Detector_LightCorrection_Title"),
message: LocalizationHelper.Get("Detector_Progress_AcquiringLightData"),
isCancelable: false,
logger: _logger);
progressWindow.Show();
IsBusy = true;
bool wasAcquiring = false;
try
{
// 0. 如果正在采集,先停止 | Stop acquisition if running
wasAcquiring = _detectorService.Status == DetectorStatus.Acquiring;
if (wasAcquiring)
{
progressWindow.UpdateProgress(LocalizationHelper.Get("Detector_Progress_StoppingAcquisition"), 5);
var stopResult = await _detectorService.StopAcquisitionAsync();
if (!stopResult.IsSuccess)
{
_logger?.Error(stopResult.Exception, "停止采集失败,亮场校正中止:{Message} | Stop acquisition failed, light correction aborted: {Message}", stopResult.ErrorMessage);
return;
}
}
// 1. 执行亮场校正 | Execute light correction
progressWindow.UpdateProgress(LocalizationHelper.Get("Detector_Progress_AcquiringLightData"), 20);
var result = await _detectorService.GainCorrectionAsync(GetCorrectionCaps().GainFrameCount);
if (result.IsSuccess)
{
_logger?.Info("亮场校正完成,开始执行坏像素校正 | Light correction completed, starting bad pixel correction");
// 2. 亮场校正完成后自动执行坏像素校正 | Auto execute bad pixel correction after light correction
progressWindow.UpdateProgress(LocalizationHelper.Get("Detector_Progress_BadPixelCorrecting"), 70);
var badPixelResult = await _detectorService.BadPixelCorrectionAsync();
if (badPixelResult.IsSuccess)
{
progressWindow.UpdateProgress(LocalizationHelper.Get("Detector_Progress_LightAndBadPixelDone"), 100);
_logger?.Info("亮场校正及坏像素校正全部完成 | Light correction and bad pixel correction all completed");
}
else
{
progressWindow.UpdateProgress(LocalizationHelper.Get("Detector_Progress_LightDoneBadPixelFailed"), 90);
_logger?.Error(badPixelResult.Exception, "坏像素校正失败:{Message} | Bad pixel correction failed: {Message}", badPixelResult.ErrorMessage);
}
}
else
{
_logger?.Error(result.Exception, "亮场校正失败:{Message} | Light correction failed: {Message}", result.ErrorMessage);
}
}
catch (Exception ex)
{
_logger?.Error(ex, "亮场校正异常:{Message} | Light correction exception: {Message}", ex.Message);
}
finally
{
// 如果之前在采集,恢复连续采集 | Resume acquisition if it was running before
if (wasAcquiring)
{
_logger?.Info("亮场校正流程结束,恢复连续采集 | Light correction flow done, resuming continuous acquisition");
await _detectorService.StartAcquisitionAsync();
}
IsBusy = false;
progressWindow.Close();
}
}
/// <summary>
/// 执行坏像素校正 | Execute bad pixel correction
/// </summary>
private async void ExecuteBadPixelCorrectionAsync()
{
_logger?.Info("开始坏像素校正 | Starting bad pixel correction");
// 显示进度条窗口 | Show progress window
var progressWindow = new ProgressWindow(
title: LocalizationHelper.Get("Detector_BadPixelCorrection_Title"),
message: LocalizationHelper.Get("Detector_Progress_DetectingBadPixels"),
isCancelable: false,
logger: _logger);
progressWindow.Show();
IsBusy = true;
try
{
progressWindow.UpdateProgress(LocalizationHelper.Get("Detector_Progress_DetectingBadPixels"), 30);
var result = await _detectorService.BadPixelCorrectionAsync();
if (result.IsSuccess)
{
progressWindow.UpdateProgress(LocalizationHelper.Get("Detector_Progress_BadPixelDone"), 100);
_logger?.Info("坏像素校正完成 | Bad pixel correction completed");
}
else
{
_logger?.Error(result.Exception, "坏像素校正失败:{Message} | Bad pixel correction failed: {Message}", result.ErrorMessage);
}
}
catch (Exception ex)
{
_logger?.Error(ex, "坏像素校正异常:{Message} | Bad pixel correction exception: {Message}", ex.Message);
}
finally
{
IsBusy = false;
progressWindow.Close();
}
}
/// <summary>
/// 应用参数到探测器硬件 | Apply parameters to detector hardware
/// </summary>
private async void ExecuteApplyParametersAsync()
{
var binningName = _selectedBinningIndex < BinningItems.Count ? BinningItems[_selectedBinningIndex].DisplayName : "?";
_logger?.Info("应用探测器参数,Binning={Binning}PGA={PGA},帧率={FrameRate} | Applying detector parameters",
binningName, _selectedPga, _frameRate);
IsBusy = true;
try
{
var result = await _detectorService.ApplyParametersAsync(_selectedBinningIndex, _selectedPga, _frameRate);
if (result.IsSuccess)
{
_logger?.Info("参数应用成功 | Parameters applied successfully");
// 持久化参数 | Persist parameters
_detectorService.SaveParameters(_selectedBinningIndex, _selectedPga, _frameRate, _avgFrames);
}
else
{
_logger?.Error(result.Exception, "参数应用失败:{Message} | Parameters apply failed: {Message}", result.ErrorMessage);
}
}
catch (Exception ex)
{
_logger?.Error(ex, "参数应用异常:{Message} | Parameters apply exception: {Message}", ex.Message);
}
finally
{
IsBusy = false;
}
}
#endregion
}
}