Calibrate.xaml.cs 21 KB

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