Calibrate.xaml.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541
  1. namespace OINA.Extender.WPF.Testharness
  2. {
  3. using System;
  4. using System.Collections.ObjectModel;
  5. using System.ComponentModel;
  6. using System.Globalization;
  7. using System.Text;
  8. using System.Windows;
  9. using System.Windows.Controls;
  10. using System.Windows.Media;
  11. using System.Windows.Threading;
  12. using OINA.Extender.Acquisition.Quant;
  13. using OINA.Extender.Controls.Spectrum;
  14. /// <summary>
  15. /// Interaction logic for Calibrate.xaml
  16. /// </summary>
  17. public partial class Calibrate : Window, INotifyPropertyChanged
  18. {
  19. /// <summary>
  20. /// The quant calibration controller
  21. /// </summary>
  22. private readonly IQuantCalibrationController controller;
  23. /// <summary>
  24. /// The quant calibration settings
  25. /// </summary>
  26. private readonly IQuantCalibrationSettings settings;
  27. /// <summary>
  28. /// The current calibration element
  29. /// </summary>
  30. private int currentCalibrationElement;
  31. /// <summary>
  32. /// Flag to show if the use has been warned about insignificant calibration peak for a particular run
  33. /// </summary>
  34. private bool userWarnedOnCalibrationPeakInsignificant;
  35. /// <summary>
  36. /// Gets or sets the current calibration element
  37. /// </summary>
  38. public int CurrentCalibrationElement
  39. {
  40. get
  41. {
  42. return this.currentCalibrationElement;
  43. }
  44. set
  45. {
  46. if (value == this.currentCalibrationElement)
  47. {
  48. return;
  49. }
  50. this.currentCalibrationElement = value;
  51. this.OnPropertyChanged(nameof(this.CurrentCalibrationElement));
  52. }
  53. }
  54. /// <summary>
  55. /// The list of allowed calibration elements
  56. /// </summary>
  57. private ReadOnlyCollection<int> allowedCalibrationElements;
  58. /// <summary>
  59. /// Gets or sets the list of allowed calibration elements
  60. /// </summary>
  61. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "By design.")]
  62. public ReadOnlyCollection<int> AllowedCalibrationElements
  63. {
  64. get
  65. {
  66. return this.allowedCalibrationElements;
  67. }
  68. set
  69. {
  70. if (value == this.allowedCalibrationElements)
  71. {
  72. return;
  73. }
  74. this.allowedCalibrationElements = value;
  75. this.OnPropertyChanged(nameof(this.AllowedCalibrationElements));
  76. }
  77. }
  78. /// <summary>
  79. /// Calibrate constructor
  80. /// </summary>
  81. public Calibrate()
  82. {
  83. this.InitializeComponent();
  84. this.CalibrateElementCombo.DataContext = this;
  85. this.settings = OIHelper.QuantCalibrationSettings;
  86. // use the same Ed settings as spectrum acquistion
  87. var spectrumSettings = OIHelper.EdSpectrumSettings;
  88. this.settings.EdSettings.CopyFrom(spectrumSettings.EdSettings);
  89. this.AllowedCalibrationElements = this.settings.Capabilities.AllowedCalibrationElements;
  90. this.CurrentCalibrationElement = this.settings.CalibrationElementAtomicNumber;
  91. if (this.settings.CalibrationMode == QuantCalibrationMode.EnergyCalibration)
  92. {
  93. this.RoutineCombobox.SelectedIndex = 0;
  94. this.SettingsButton.Visibility = Visibility.Hidden;
  95. }
  96. else if (this.settings.CalibrationMode == QuantCalibrationMode.BeamMeasurement)
  97. {
  98. this.RoutineCombobox.SelectedIndex = 1;
  99. this.SettingsButton.Visibility = Visibility.Visible;
  100. }
  101. this.settings.PropertyChanged += this.OnSettingsPropertyChanged;
  102. this.settings.Capabilities.PropertyChanged += this.OnCapabilitiesPropertyChanged;
  103. this.CurrentStatusTextbox.Text = string.Empty;
  104. this.controller = OIHelper.QuantCalibrationController;
  105. // beam measurement
  106. this.controller.BeamMeasurementAcquisitionStarted += this.OnBeamMeasurementAcquisitionStarted;
  107. this.controller.BeamMeasurementAcquisitionFinished += this.OnBeamMeasurementAcquisitionFinished;
  108. this.controller.BeamMeasurementResult += this.OnBeamMeasurementResult;
  109. this.controller.BeamMeasurementFinished += this.OnBeamMeasurementFinished;
  110. // common
  111. this.controller.CalibrationPeakInsignificant += this.OnCalibrationPeakInsignificant;
  112. // energy calibration
  113. this.controller.EnergyCalibrationAcquisitionStarted += this.OnEnergyCalibrationAcquisitionStarted;
  114. this.controller.EnergyCalibrationAcquisitionFinished += this.OnEnergyCalibrationAcquisitionFinished;
  115. this.controller.EnergyCalibrationFinished += this.OnEnergyCalibrationFinished;
  116. this.controller.PropertyChanged += this.OnControllerPropertyChanged;
  117. }
  118. /// <summary>
  119. /// The calibration routine has changed, update the settings
  120. /// </summary>
  121. /// <param name="sender">The sender</param>
  122. /// <param name="e">The event args</param>
  123. private void RoutineCombobox_SelectionChanged(object sender, SelectionChangedEventArgs e)
  124. {
  125. if (this.RoutineCombobox.SelectedIndex == 0)
  126. {
  127. this.SettingsButton.Visibility = Visibility.Hidden;
  128. this.settings.CalibrationMode = QuantCalibrationMode.EnergyCalibration;
  129. }
  130. else
  131. {
  132. this.SettingsButton.Visibility = Visibility.Visible;
  133. this.settings.CalibrationMode = QuantCalibrationMode.BeamMeasurement;
  134. }
  135. }
  136. /// <summary>
  137. /// Handle quant calibration settings property changed notifications
  138. /// </summary>
  139. /// <param name="sender">The sender</param>
  140. /// <param name="e">The event args</param>
  141. private void OnSettingsPropertyChanged(object sender, PropertyChangedEventArgs e)
  142. {
  143. if (e.PropertyName == nameof(this.settings.CalibrationElementAtomicNumber))
  144. {
  145. this.CurrentCalibrationElement = this.settings.CalibrationElementAtomicNumber;
  146. }
  147. else if (e.PropertyName == nameof(this.settings.CalibrationMode))
  148. {
  149. if (this.settings.CalibrationMode == QuantCalibrationMode.EnergyCalibration)
  150. {
  151. this.RoutineCombobox.SelectedIndex = 0;
  152. }
  153. else if (this.settings.CalibrationMode == QuantCalibrationMode.BeamMeasurement)
  154. {
  155. this.RoutineCombobox.SelectedIndex = 1;
  156. }
  157. }
  158. }
  159. /// <summary>
  160. /// Handle quant calibration capabilites property changed notifications
  161. /// </summary>
  162. /// <param name="sender">The sender</param>
  163. /// <param name="e">The event args</param>
  164. private void OnCapabilitiesPropertyChanged(object sender, PropertyChangedEventArgs e)
  165. {
  166. if (e.PropertyName == nameof(this.settings.Capabilities.AllowedCalibrationElements))
  167. {
  168. this.AllowedCalibrationElements = this.settings.Capabilities.AllowedCalibrationElements;
  169. this.CurrentCalibrationElement = this.settings.CalibrationElementAtomicNumber;
  170. }
  171. }
  172. /// <summary>
  173. /// Handle property changed notifications from the controller
  174. /// </summary>
  175. /// <param name="sender">sender</param>
  176. /// <param name="e">PropertyChangedEventArgs</param>
  177. private void OnControllerPropertyChanged(object sender, PropertyChangedEventArgs e)
  178. {
  179. switch (e.PropertyName)
  180. {
  181. case nameof(this.controller.CurrentCalibrationSpectrum):
  182. // execute on UI thread
  183. this.Dispatcher.BeginInvoke(() =>
  184. {
  185. this.spectrumViewer.Spectrum = this.controller.CurrentCalibrationSpectrum;
  186. });
  187. break;
  188. case nameof(this.controller.PercentageComplete):
  189. // execute on UI thread
  190. this.Dispatcher.BeginInvoke(() =>
  191. {
  192. this.CalibrateProgressBar.Value = this.controller.PercentageComplete;
  193. this.EstimateTimeLabel.Content = this.controller.EstimatedTimeToCompletion.Ticks == 0 ? string.Empty : this.controller.EstimatedTimeToCompletion.ToString(@"hh\:mm\:ss", CultureInfo.CurrentCulture);
  194. });
  195. break;
  196. case nameof(this.controller.CurrentEnergyWindow):
  197. var window = this.controller.CurrentEnergyWindow;
  198. if (window != null)
  199. {
  200. var windowColor = Color.FromArgb(255, 255, 0, 119);
  201. var windows = new EnergyWindow[]
  202. {
  203. new EnergyWindow(window.LowerEnergy, window.UpperEnergy, windowColor),
  204. };
  205. // execute on UI thread
  206. this.Dispatcher.BeginInvoke(() =>
  207. {
  208. this.spectrumViewer.EnergyWindowsSource = windows;
  209. });
  210. }
  211. break;
  212. case nameof(this.controller.IsIdle):
  213. // execute on UI thread
  214. this.Dispatcher.BeginInvoke(() =>
  215. {
  216. if (this.controller.IsIdle)
  217. {
  218. this.CurrentStatusTextbox.Text = @"Data acquisition is Idle.";
  219. }
  220. else
  221. {
  222. this.CurrentStatusTextbox.Text = @"Data is currently acquiring.";
  223. }
  224. });
  225. break;
  226. }
  227. }
  228. /// <summary>
  229. /// Validate the settings and start the calibration routine
  230. /// </summary>
  231. /// <param name="sender">The sender</param>
  232. /// <param name="e">The event args</param>
  233. private void StartCalibrateButton_Click(object sender, EventArgs e)
  234. {
  235. this.settings.CalibrationElementAtomicNumber = this.CurrentCalibrationElement;
  236. var results = this.settings.Validate();
  237. if (!results.IsValid)
  238. {
  239. string message = string.Format(CultureInfo.CurrentCulture, @"The settings are not valid{0}", Environment.NewLine);
  240. message += string.Join(Environment.NewLine, results.ValidationErrors);
  241. MessageBox.Show(message, @"Invalid settings");
  242. return;
  243. }
  244. this.CalibrateProgressBar.Value = 0;
  245. this.controller.StartCalibration(this.settings);
  246. this.userWarnedOnCalibrationPeakInsignificant = false;
  247. }
  248. /// <summary>
  249. /// Stops the calibration routine
  250. /// </summary>
  251. /// <param name="sender">The sender</param>
  252. /// <param name="e">The event args</param>
  253. private void StopCalibrateButton_Click(object sender, EventArgs e)
  254. {
  255. this.controller.StopCalibration();
  256. }
  257. /// <summary>
  258. /// The beam measurement acquisition has started
  259. /// </summary>
  260. /// <param name="sender">The sender</param>
  261. /// <param name="e">The event args</param>
  262. private void OnBeamMeasurementAcquisitionStarted(object sender, EventArgs e)
  263. {
  264. this.EnableCalibrationControls(false);
  265. }
  266. /// <summary>
  267. /// The beam measurement acquisition has finished
  268. /// </summary>
  269. /// <param name="sender">The sender</param>
  270. /// <param name="e">The event args</param>
  271. private void OnBeamMeasurementAcquisitionFinished(object sender, CalibrationAcquisitionFinishedEventArgs e)
  272. {
  273. if (!e.Success)
  274. {
  275. // Acquisition was stopped by the user, or an error occured
  276. if (e.Error != null)
  277. {
  278. string message = string.Format(CultureInfo.CurrentCulture, @"Beam measurement acquisition did not complete successfully {0} {1}", Environment.NewLine, e.Error.Message);
  279. MessageBox.Show(message, @"Beam measurement");
  280. }
  281. e.Cancel = true; // should already be set to true
  282. this.EnableCalibrationControls(true);
  283. }
  284. }
  285. /// <summary>
  286. /// The beam measurement has been compared to the previous measurement
  287. /// </summary>
  288. /// <param name="sender">The sender</param>
  289. /// <param name="e">The event args</param>
  290. private void OnBeamMeasurementResult(object sender, BeamMeasurementResultEventArgs e)
  291. {
  292. string content = string.Empty;
  293. if (e.Status == BeamMeasurementResultStatus.MeasurementDifference)
  294. {
  295. content = string.Format(CultureInfo.CurrentCulture, @"The beam current is {0:0.00}% of the last value", e.Percent);
  296. }
  297. else if (e.Status == BeamMeasurementResultStatus.MeasurementOK)
  298. {
  299. content = @"Measurement OK";
  300. }
  301. else
  302. {
  303. throw new InvalidOperationException("Unexpected result status");
  304. }
  305. string message = string.Format(CultureInfo.CurrentCulture, @"Do you want to save the beam measurement? {0}{0}{1}", Environment.NewLine, content);
  306. if (MessageBox.Show(message, @"Beam measurement", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.No)
  307. {
  308. e.Cancel = true;
  309. this.EnableCalibrationControls(true);
  310. }
  311. }
  312. /// <summary>
  313. /// The beam measurement has been saved
  314. /// </summary>
  315. /// <param name="sender">The sender</param>
  316. /// <param name="e">The event args</param>
  317. private void OnBeamMeasurementFinished(object sender, CalibrationFinishedEventArgs e)
  318. {
  319. if (e.Error != null)
  320. {
  321. 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);
  322. }
  323. this.EnableCalibrationControls(true);
  324. }
  325. /// <summary>
  326. /// The calibration peak is not significant.
  327. /// Can occur during both Beam measurement and Energy calibration
  328. /// </summary>
  329. /// <param name="sender">The sender</param>
  330. /// <param name="e">The event args</param>
  331. private void OnCalibrationPeakInsignificant(object sender, CalibrationPeakInsignificantEventArgs e)
  332. {
  333. if (this.userWarnedOnCalibrationPeakInsignificant)
  334. {
  335. return;
  336. }
  337. var message = new StringBuilder();
  338. message.AppendFormat(
  339. CultureInfo.CurrentCulture,
  340. @"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}",
  341. Environment.NewLine,
  342. e.PeakCounts,
  343. e.LowEnergyBackgroundCounts,
  344. e.HighEnergyBackgroundCounts);
  345. message.AppendFormat(CultureInfo.CurrentCulture, @"The calibration process has been running for {0:hh\:mm\:ss}", e.ElapsedTime);
  346. if (e.EstimatedTimeToCompletion.TotalSeconds > 0)
  347. {
  348. message.AppendFormat(CultureInfo.CurrentCulture, @" and an estimated time to completion is {0:hh\:mm\:ss}. ", e.EstimatedTimeToCompletion);
  349. }
  350. message.AppendFormat(CultureInfo.CurrentCulture, @"{0}{0}The wrong element may have been selected. Would you like to continue?", Environment.NewLine);
  351. if (MessageBox.Show(message.ToString(), @"Quant optimization", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.No)
  352. {
  353. this.controller.StopCalibration();
  354. }
  355. this.userWarnedOnCalibrationPeakInsignificant = true;
  356. }
  357. /// <summary>
  358. /// The energy calibration acquisition has started
  359. /// </summary>
  360. /// <param name="sender">The sender</param>
  361. /// <param name="e">The event args</param>
  362. private void OnEnergyCalibrationAcquisitionStarted(object sender, EventArgs e)
  363. {
  364. this.EnableCalibrationControls(false);
  365. }
  366. /// <summary>
  367. /// The energy calibration acquisition has finished
  368. /// </summary>
  369. /// <param name="sender">The sender</param>
  370. /// <param name="e">The event args</param>
  371. private void OnEnergyCalibrationAcquisitionFinished(object sender, CalibrationAcquisitionFinishedEventArgs e)
  372. {
  373. if (e.Success)
  374. {
  375. string message = @"Perform Energy calibration using the acquired spectra?";
  376. if (MessageBox.Show(message, @"Energy calibration", MessageBoxButton.YesNo) == MessageBoxResult.No)
  377. {
  378. e.Cancel = true;
  379. this.EnableCalibrationControls(true);
  380. }
  381. }
  382. else
  383. {
  384. // Acquisition was stopped by the user, or an error occured
  385. if (e.Error != null)
  386. {
  387. string message = string.Format(CultureInfo.CurrentCulture, @"Energy calibration acquisition did not complete successfully {0} {1}", Environment.NewLine, e.Error.Message);
  388. MessageBox.Show(message, @"Energy calibration");
  389. }
  390. e.Cancel = true; // should already be set to true
  391. this.EnableCalibrationControls(true);
  392. }
  393. }
  394. /// <summary>
  395. /// The Energy calibration has been saved
  396. /// </summary>
  397. /// <param name="sender">The sender</param>
  398. /// <param name="e">The event args</param>
  399. private void OnEnergyCalibrationFinished(object sender, CalibrationFinishedEventArgs e)
  400. {
  401. if (e.Error != null)
  402. {
  403. 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);
  404. }
  405. this.EnableCalibrationControls(true);
  406. }
  407. /// <summary>
  408. /// Display the settings for Beam measurement
  409. /// </summary>
  410. /// <param name="sender">The sender</param>
  411. /// <param name="e">The event args</param>
  412. private void SettingsButton_Click(object sender, RoutedEventArgs e)
  413. {
  414. new BeamMeasurementSettings { WindowStartupLocation = WindowStartupLocation.CenterScreen }.Show();
  415. }
  416. /// <summary>
  417. /// Unsubscribe from the events
  418. /// </summary>
  419. /// <param name="e">The event args</param>
  420. protected override void OnClosing(CancelEventArgs e)
  421. {
  422. this.settings.PropertyChanged -= this.OnSettingsPropertyChanged;
  423. this.settings.Capabilities.PropertyChanged -= this.OnCapabilitiesPropertyChanged;
  424. this.controller.BeamMeasurementAcquisitionStarted -= this.OnBeamMeasurementAcquisitionStarted;
  425. this.controller.BeamMeasurementAcquisitionFinished -= this.OnBeamMeasurementAcquisitionFinished;
  426. this.controller.BeamMeasurementResult -= this.OnBeamMeasurementResult;
  427. this.controller.BeamMeasurementFinished -= this.OnBeamMeasurementFinished;
  428. this.controller.CalibrationPeakInsignificant -= this.OnCalibrationPeakInsignificant;
  429. this.controller.EnergyCalibrationAcquisitionStarted -= this.OnEnergyCalibrationAcquisitionStarted;
  430. this.controller.EnergyCalibrationAcquisitionFinished -= this.OnEnergyCalibrationAcquisitionFinished;
  431. this.controller.EnergyCalibrationFinished -= this.OnEnergyCalibrationFinished;
  432. this.controller.PropertyChanged -= this.OnControllerPropertyChanged;
  433. base.OnClosing(e);
  434. }
  435. /// <summary>
  436. /// Enable/disable the controls for starting the acquisition
  437. /// </summary>
  438. /// <param name="enable">Whether to enable or not</param>
  439. private void EnableCalibrationControls(bool enable)
  440. {
  441. this.Dispatcher.BeginInvoke(
  442. () =>
  443. {
  444. this.StartCalibrateButton.IsEnabled = enable;
  445. this.RoutineCombobox.IsEnabled = enable;
  446. this.SettingsButton.IsEnabled = enable;
  447. this.CalibrateElementCombo.IsEnabled = enable;
  448. this.StopCalibrateButton.IsEnabled = !enable;
  449. if (enable)
  450. {
  451. this.EstimateTimeLabel.Content = string.Empty;
  452. }
  453. });
  454. }
  455. #region INotifyPropertyChanged Members
  456. /// <summary>
  457. /// PropertyChanged event
  458. /// </summary>
  459. public event PropertyChangedEventHandler PropertyChanged;
  460. private void OnPropertyChanged(string propertyName)
  461. {
  462. this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
  463. }
  464. #endregion
  465. }
  466. }