Calibrate.cs 20 KB

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