namespace OINA.Extender.WPF.Testharness { using System; using System.Collections.ObjectModel; using System.ComponentModel; using System.Globalization; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Threading; using OINA.Extender.Acquisition.Quant; using OINA.Extender.Controls.Spectrum; /// /// Interaction logic for Calibrate.xaml /// public partial class Calibrate : Window, INotifyPropertyChanged { /// /// The quant calibration controller /// private readonly IQuantCalibrationController controller; /// /// The quant calibration settings /// private readonly IQuantCalibrationSettings settings; /// /// The current calibration element /// private int currentCalibrationElement; /// /// Flag to show if the use has been warned about insignificant calibration peak for a particular run /// private bool userWarnedOnCalibrationPeakInsignificant; /// /// Gets or sets the current calibration element /// public int CurrentCalibrationElement { get { return this.currentCalibrationElement; } set { if (value == this.currentCalibrationElement) { return; } this.currentCalibrationElement = value; this.OnPropertyChanged(nameof(this.CurrentCalibrationElement)); } } /// /// The list of allowed calibration elements /// private ReadOnlyCollection allowedCalibrationElements; /// /// Gets or sets the list of allowed calibration elements /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "By design.")] public ReadOnlyCollection AllowedCalibrationElements { get { return this.allowedCalibrationElements; } set { if (value == this.allowedCalibrationElements) { return; } this.allowedCalibrationElements = value; this.OnPropertyChanged(nameof(this.AllowedCalibrationElements)); } } /// /// Calibrate constructor /// public Calibrate() { this.InitializeComponent(); this.CalibrateElementCombo.DataContext = this; this.settings = OIHelper.QuantCalibrationSettings; // use the same Ed settings as spectrum acquistion var spectrumSettings = OIHelper.EdSpectrumSettings; this.settings.EdSettings.CopyFrom(spectrumSettings.EdSettings); this.AllowedCalibrationElements = this.settings.Capabilities.AllowedCalibrationElements; this.CurrentCalibrationElement = this.settings.CalibrationElementAtomicNumber; if (this.settings.CalibrationMode == QuantCalibrationMode.EnergyCalibration) { this.RoutineCombobox.SelectedIndex = 0; this.SettingsButton.Visibility = Visibility.Hidden; } else if (this.settings.CalibrationMode == QuantCalibrationMode.BeamMeasurement) { this.RoutineCombobox.SelectedIndex = 1; this.SettingsButton.Visibility = Visibility.Visible; } this.settings.PropertyChanged += this.OnSettingsPropertyChanged; this.settings.Capabilities.PropertyChanged += this.OnCapabilitiesPropertyChanged; this.CurrentStatusTextbox.Text = string.Empty; this.controller = OIHelper.QuantCalibrationController; // beam measurement this.controller.BeamMeasurementAcquisitionStarted += this.OnBeamMeasurementAcquisitionStarted; this.controller.BeamMeasurementAcquisitionFinished += this.OnBeamMeasurementAcquisitionFinished; this.controller.BeamMeasurementResult += this.OnBeamMeasurementResult; this.controller.BeamMeasurementFinished += this.OnBeamMeasurementFinished; // common this.controller.CalibrationPeakInsignificant += this.OnCalibrationPeakInsignificant; // energy calibration this.controller.EnergyCalibrationAcquisitionStarted += this.OnEnergyCalibrationAcquisitionStarted; this.controller.EnergyCalibrationAcquisitionFinished += this.OnEnergyCalibrationAcquisitionFinished; this.controller.EnergyCalibrationFinished += this.OnEnergyCalibrationFinished; this.controller.PropertyChanged += this.OnControllerPropertyChanged; } /// /// The calibration routine has changed, update the settings /// /// The sender /// The event args private void RoutineCombobox_SelectionChanged(object sender, SelectionChangedEventArgs e) { if (this.RoutineCombobox.SelectedIndex == 0) { this.SettingsButton.Visibility = Visibility.Hidden; this.settings.CalibrationMode = QuantCalibrationMode.EnergyCalibration; } else { this.SettingsButton.Visibility = Visibility.Visible; this.settings.CalibrationMode = QuantCalibrationMode.BeamMeasurement; } } /// /// Handle quant calibration settings property changed notifications /// /// The sender /// The event args private void OnSettingsPropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == nameof(this.settings.CalibrationElementAtomicNumber)) { this.CurrentCalibrationElement = this.settings.CalibrationElementAtomicNumber; } else if (e.PropertyName == nameof(this.settings.CalibrationMode)) { if (this.settings.CalibrationMode == QuantCalibrationMode.EnergyCalibration) { this.RoutineCombobox.SelectedIndex = 0; } else if (this.settings.CalibrationMode == QuantCalibrationMode.BeamMeasurement) { this.RoutineCombobox.SelectedIndex = 1; } } } /// /// Handle quant calibration capabilites property changed notifications /// /// The sender /// The event args private void OnCapabilitiesPropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == nameof(this.settings.Capabilities.AllowedCalibrationElements)) { this.AllowedCalibrationElements = this.settings.Capabilities.AllowedCalibrationElements; this.CurrentCalibrationElement = this.settings.CalibrationElementAtomicNumber; } } /// /// Handle property changed notifications from the controller /// /// sender /// PropertyChangedEventArgs private void OnControllerPropertyChanged(object sender, PropertyChangedEventArgs e) { switch (e.PropertyName) { case nameof(this.controller.CurrentCalibrationSpectrum): // execute on UI thread this.Dispatcher.BeginInvoke(() => { this.spectrumViewer.Spectrum = this.controller.CurrentCalibrationSpectrum; }); break; case nameof(this.controller.PercentageComplete): // execute on UI thread this.Dispatcher.BeginInvoke(() => { this.CalibrateProgressBar.Value = this.controller.PercentageComplete; this.EstimateTimeLabel.Content = this.controller.EstimatedTimeToCompletion.Ticks == 0 ? string.Empty : this.controller.EstimatedTimeToCompletion.ToString(@"hh\:mm\:ss", CultureInfo.CurrentCulture); }); break; case nameof(this.controller.CurrentEnergyWindow): var window = this.controller.CurrentEnergyWindow; if (window != null) { var windowColor = Color.FromArgb(255, 255, 0, 119); var windows = new EnergyWindow[] { new EnergyWindow(window.LowerEnergy, window.UpperEnergy, windowColor), }; // execute on UI thread this.Dispatcher.BeginInvoke(() => { this.spectrumViewer.EnergyWindowsSource = windows; }); } break; case nameof(this.controller.IsIdle): // execute on UI thread this.Dispatcher.BeginInvoke(() => { if (this.controller.IsIdle) { this.CurrentStatusTextbox.Text = @"Data acquisition is Idle."; } else { this.CurrentStatusTextbox.Text = @"Data is currently acquiring."; } }); break; } } /// /// Validate the settings and start the calibration routine /// /// The sender /// The event args private void StartCalibrateButton_Click(object sender, EventArgs e) { this.settings.CalibrationElementAtomicNumber = this.CurrentCalibrationElement; var results = this.settings.Validate(); if (!results.IsValid) { string message = string.Format(CultureInfo.CurrentCulture, @"The settings are not valid{0}", Environment.NewLine); message += string.Join(Environment.NewLine, results.ValidationErrors); MessageBox.Show(message, @"Invalid settings"); return; } this.CalibrateProgressBar.Value = 0; this.controller.StartCalibration(this.settings); this.userWarnedOnCalibrationPeakInsignificant = false; } /// /// Stops the calibration routine /// /// The sender /// The event args private void StopCalibrateButton_Click(object sender, EventArgs e) { this.controller.StopCalibration(); } /// /// The beam measurement acquisition has started /// /// The sender /// The event args private void OnBeamMeasurementAcquisitionStarted(object sender, EventArgs e) { this.EnableCalibrationControls(false); } /// /// The beam measurement acquisition has finished /// /// The sender /// The event args private void OnBeamMeasurementAcquisitionFinished(object sender, CalibrationAcquisitionFinishedEventArgs e) { if (!e.Success) { // Acquisition was stopped by the user, or an error occured if (e.Error != null) { string message = string.Format(CultureInfo.CurrentCulture, @"Beam measurement acquisition did not complete successfully {0} {1}", Environment.NewLine, e.Error.Message); MessageBox.Show(message, @"Beam measurement"); } e.Cancel = true; // should already be set to true this.EnableCalibrationControls(true); } } /// /// The beam measurement has been compared to the previous measurement /// /// The sender /// The event args private void OnBeamMeasurementResult(object sender, BeamMeasurementResultEventArgs e) { string content = string.Empty; if (e.Status == BeamMeasurementResultStatus.MeasurementDifference) { content = string.Format(CultureInfo.CurrentCulture, @"The beam current is {0:0.00}% of the last value", e.Percent); } else if (e.Status == BeamMeasurementResultStatus.MeasurementOK) { content = @"Measurement OK"; } else { throw new InvalidOperationException("Unexpected result status"); } string message = string.Format(CultureInfo.CurrentCulture, @"Do you want to save the beam measurement? {0}{0}{1}", Environment.NewLine, content); if (MessageBox.Show(message, @"Beam measurement", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.No) { e.Cancel = true; this.EnableCalibrationControls(true); } } /// /// The beam measurement has been saved /// /// The sender /// The event args private void OnBeamMeasurementFinished(object sender, CalibrationFinishedEventArgs e) { if (e.Error != null) { MessageBox.Show(string.Format(CultureInfo.CurrentCulture, @"Failed to perform beam measurement:{0}{0}{1}", Environment.NewLine, e.Error.Message), @"Beam measurement", MessageBoxButton.OK, MessageBoxImage.Exclamation); } this.EnableCalibrationControls(true); } /// /// The calibration peak is not significant. /// Can occur during both Beam measurement and Energy calibration /// /// The sender /// The event args private void OnCalibrationPeakInsignificant(object sender, CalibrationPeakInsignificantEventArgs e) { if (this.userWarnedOnCalibrationPeakInsignificant) { return; } var message = new StringBuilder(); message.AppendFormat( CultureInfo.CurrentCulture, @"The calibration peak is not significantly greater than the background:{0}Peak counts: {1}{0}Low energy background: {2}{0}high energy background: {3}{0}{0}", Environment.NewLine, e.PeakCounts, e.LowEnergyBackgroundCounts, e.HighEnergyBackgroundCounts); message.AppendFormat(CultureInfo.CurrentCulture, @"The calibration process has been running for {0:hh\:mm\:ss}", e.ElapsedTime); if (e.EstimatedTimeToCompletion.TotalSeconds > 0) { message.AppendFormat(CultureInfo.CurrentCulture, @" and an estimated time to completion is {0:hh\:mm\:ss}. ", e.EstimatedTimeToCompletion); } message.AppendFormat(CultureInfo.CurrentCulture, @"{0}{0}The wrong element may have been selected. Would you like to continue?", Environment.NewLine); if (MessageBox.Show(message.ToString(), @"Quant optimization", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.No) { this.controller.StopCalibration(); } this.userWarnedOnCalibrationPeakInsignificant = true; } /// /// The energy calibration acquisition has started /// /// The sender /// The event args private void OnEnergyCalibrationAcquisitionStarted(object sender, EventArgs e) { this.EnableCalibrationControls(false); } /// /// The energy calibration acquisition has finished /// /// The sender /// The event args private void OnEnergyCalibrationAcquisitionFinished(object sender, CalibrationAcquisitionFinishedEventArgs e) { if (e.Success) { string message = @"Perform Energy calibration using the acquired spectra?"; if (MessageBox.Show(message, @"Energy calibration", MessageBoxButton.YesNo) == MessageBoxResult.No) { e.Cancel = true; this.EnableCalibrationControls(true); } } else { // Acquisition was stopped by the user, or an error occured if (e.Error != null) { string message = string.Format(CultureInfo.CurrentCulture, @"Energy calibration acquisition did not complete successfully {0} {1}", Environment.NewLine, e.Error.Message); MessageBox.Show(message, @"Energy calibration"); } e.Cancel = true; // should already be set to true this.EnableCalibrationControls(true); } } /// /// The Energy calibration has been saved /// /// The sender /// The event args private void OnEnergyCalibrationFinished(object sender, CalibrationFinishedEventArgs e) { if (e.Error != null) { MessageBox.Show(string.Format(CultureInfo.CurrentCulture, @"Failed to perform energy calibration:{0}{0}{1}", Environment.NewLine, e.Error.Message), @"Energy calibration", MessageBoxButton.OK, MessageBoxImage.Exclamation); } this.EnableCalibrationControls(true); } /// /// Display the settings for Beam measurement /// /// The sender /// The event args private void SettingsButton_Click(object sender, RoutedEventArgs e) { new BeamMeasurementSettings { WindowStartupLocation = WindowStartupLocation.CenterScreen }.Show(); } /// /// Unsubscribe from the events /// /// The event args protected override void OnClosing(CancelEventArgs e) { this.settings.PropertyChanged -= this.OnSettingsPropertyChanged; this.settings.Capabilities.PropertyChanged -= this.OnCapabilitiesPropertyChanged; this.controller.BeamMeasurementAcquisitionStarted -= this.OnBeamMeasurementAcquisitionStarted; this.controller.BeamMeasurementAcquisitionFinished -= this.OnBeamMeasurementAcquisitionFinished; this.controller.BeamMeasurementResult -= this.OnBeamMeasurementResult; this.controller.BeamMeasurementFinished -= this.OnBeamMeasurementFinished; this.controller.CalibrationPeakInsignificant -= this.OnCalibrationPeakInsignificant; this.controller.EnergyCalibrationAcquisitionStarted -= this.OnEnergyCalibrationAcquisitionStarted; this.controller.EnergyCalibrationAcquisitionFinished -= this.OnEnergyCalibrationAcquisitionFinished; this.controller.EnergyCalibrationFinished -= this.OnEnergyCalibrationFinished; this.controller.PropertyChanged -= this.OnControllerPropertyChanged; base.OnClosing(e); } /// /// Enable/disable the controls for starting the acquisition /// /// Whether to enable or not private void EnableCalibrationControls(bool enable) { this.Dispatcher.BeginInvoke( () => { this.StartCalibrateButton.IsEnabled = enable; this.RoutineCombobox.IsEnabled = enable; this.SettingsButton.IsEnabled = enable; this.CalibrateElementCombo.IsEnabled = enable; this.StopCalibrateButton.IsEnabled = !enable; if (enable) { this.EstimateTimeLabel.Content = string.Empty; } }); } #region INotifyPropertyChanged Members /// /// PropertyChanged event /// public event PropertyChangedEventHandler PropertyChanged; private void OnPropertyChanged(string propertyName) { this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } #endregion } }