namespace OINA.Extender.WPF.Testharness { using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Threading; using OINA.Extender.Acquisition; using OINA.Extender.Acquisition.Ed; using OINA.Extender.Acquisition.XSync; using OINA.Extender.Data.Ed; using OINA.Extender.Data.Image; using OINA.Extender.Processing; using OINA.Extender.Processing.Ed; /// /// Interaction logic for XSyncMapAcquisition.xaml /// public partial class XSyncMapAcquisition : UserControl, INotifyPropertyChanged { private readonly List enabledDetectors; private EdHardwareId primaryHardwareId; private List secondaryHardwareIds; private IXSyncEdMapAcquisitionController controller; private IEdSpectrum spectrum; private bool isPaused; private string pauseResumeState; private int completedFrames; private ObservableCollection acquiredMaps; private IEdMap selectedMap; private IReadOnlyList elementMaps; private double progressPercentage; private IEdMapAcquisitionData mapData; private IEdMapProcessor mapProcessor; private IAutoIdSettings autoIdSettings; private IEdSpectrumProcessing spectrumProcessing; public XSyncMapAcquisition() { this.InitializeComponent(); this.DataContext = this; this.autoIdSettings = ProcessingFactory.CreateAutoIdSettings(); this.spectrumProcessing = ProcessingFactory.CreateSpectrumProcessing(); this.Settings = AcquireFactory.CreateXSyncEdMapSettings(); this.Settings.XSyncSettings.SynchronizationMode = XSyncMode.CASLineSync; // pixel size is the number of data pixels in x double pixelSize = 1.0 / 120; // assume that there are also 120 data pixels in y Size size = new Size(1.0, 1.0); this.Settings.XSyncSettings.AcquisitionRegion.CreateRectangleRegion(new Rect(size), pixelSize); this.Settings.XSyncSettings.FrameCount = 1; // assume that there are 2040 lines scanned, this gives a binning factor of 17 this.Settings.XSyncSettings.LineSync.BinFactorY = 17; this.Settings.XSyncSettings.LineSync.LinesPerFrame = 2040; // = (120 * 17) // set the calibrated field width to set the image viewer scale bar this.Settings.XSyncSettings.CalibratedFieldWidthmm = 180; this.enabledDetectors = this.Settings.EdSettings.IsHardwareEnabled .Select(i => new DetectorEnableModel(i.Key, i.Value)).ToList(); this.PrimaryHardwareId = this.Settings.EdSettings.PrimaryHardwareId; this.controller = AcquireFactory.CreateXSyncEdMapServer(); this.controller.ExperimentStarted += this.OnExperimentStarted; this.controller.ExperimentFinished += this.OnExperimentFinished; this.controller.PauseRequested += this.OnPauseRequested; this.controller.ResumeRequested += this.OnResumeRequested; this.controller.FrameCompleted += this.OnFrameCompleted; this.acquiredMaps = new ObservableCollection(); } /// /// Event that notifies that the processing progress has changed /// public event PropertyChangedEventHandler PropertyChanged; private void RaisePropertyChanged(string propertyName) { this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } private void StartButton_Click(object sender, System.Windows.RoutedEventArgs e) { this.PauseResumeState = string.Empty; this.IsPaused = false; this.CompletedFrames = 0; this.ElementMaps = null; this.ProgressPercentage = 0; var edSettings = this.Settings.EdSettings; var xsyncSettings = this.Settings.XSyncSettings; foreach (var detectorEnabled in this.secondaryHardwareIds) { edSettings.EnableDevice(detectorEnabled.EdHardwareId, detectorEnabled.IsDetectorEnabled); } edSettings.PrimaryHardwareId = this.primaryHardwareId; if (xsyncSettings.SynchronizationMode == XSyncMode.CASLineSync) { edSettings.AcquisitionMode = EdAcquireMode.RealTime; edSettings.AcquisitionTime = TimeSpan.FromTicks((long)(xsyncSettings.LineSync.DwellTimeMicroSeconds * TimeSpan.TicksPerMillisecond / 1000)); edSettings.TimeMode = EdTimeMode.Pixel; } var results = this.Settings.Validate(); if (!results.IsValid) { string errors = string.Join(Environment.NewLine, results.ValidationErrors); MessageBox.Show(errors, @"Invalid acquisition settings"); return; } this.controller.StartAcquisition(this.Settings); } private void StopButton_Click(object sender, System.Windows.RoutedEventArgs e) { this.controller.StopAcquisition(); } private void OnExperimentStarted(object sender, AcquisitionStartedEventArgs e) { this.Spectrum = e.Value.EdSpectrum; this.mapData = e.Value; } private void OnExperimentFinished(object sender, AcquisitionFinishedEventArgs e) { var edMap = e.Value?.EdMap; if (edMap != null) { this.Dispatcher.BeginInvoke(() => { this.acquiredMaps.Add(edMap); this.SelectedMap = edMap; }); } } public IEdSpectrum Spectrum { get { return this.spectrum; } set { this.spectrum = value; this.RaisePropertyChanged(nameof(this.Spectrum)); } } public IXSyncEdMapSettings Settings { get; } public EdHardwareId PrimaryHardwareId { get { return this.primaryHardwareId; } set { if (this.primaryHardwareId == value) { return; } this.primaryHardwareId = value; this.RaisePropertyChanged(nameof(this.PrimaryHardwareId)); this.SecondaryHardwareIds = this.enabledDetectors.Where(i => i.EdHardwareId != value).ToList(); } } public IReadOnlyList SecondaryHardwareIds { get { return this.secondaryHardwareIds; } set { this.secondaryHardwareIds = value.ToList(); this.RaisePropertyChanged(nameof(this.SecondaryHardwareIds)); } } #region Pause / Resume private void OnPauseRequested(object sender, PauseScanEventArgs e) { this.PauseResumeState = @"Pause requested"; } private void OnResumeRequested(object sender, ResumeScanEventArgs e) { this.PauseResumeState = @"Resume requested"; } /// /// Gets or sets a value indicating whether the external scan hardware is paused /// public bool IsPaused { get { return this.isPaused; } set { if (this.isPaused == value) { return; } this.isPaused = value; this.RaisePropertyChanged(nameof(this.IsPaused)); this.controller.NotifyPauseResume(value); } } /// /// Gets or sets a value indicating whether a pause or resume is requested /// public string PauseResumeState { get { return this.pauseResumeState; } set { if (this.pauseResumeState == value) { return; } this.pauseResumeState = value; this.RaisePropertyChanged(nameof(this.PauseResumeState)); } } #endregion private void OnFrameCompleted(object sender, FrameCompletedEventArgs e) { this.CompletedFrames = e.FrameNumber; } /// /// Gets or sets the number of completed frames /// public int CompletedFrames { get { return this.completedFrames; } set { if (this.completedFrames == value) { return; } this.completedFrames = value; this.RaisePropertyChanged(nameof(this.CompletedFrames)); } } /// /// Gets the acquired maps /// public ObservableCollection AcquiredMaps { get { return this.acquiredMaps; } } /// /// Gets or sets the selected map /// public IEdMap SelectedMap { get { return this.selectedMap; } set { this.selectedMap = value; this.RaisePropertyChanged(nameof(this.SelectedMap)); } } /// /// Deletes the selected map /// private void DeleteMapButton_Click(object sender, RoutedEventArgs e) { // Maps should be deleted when they are no longer required as the data is saved in the // AppData\Local\Oxford Instruments NanoAnalysis\Extender\TempProject folder of the current user // Note these projects cannot be used with AZtec. var edMap = this.selectedMap; if (edMap != null) { this.controller.Delete(edMap); edMap.Dispose(); this.acquiredMaps.Remove(edMap); this.SelectedMap = this.acquiredMaps.LastOrDefault(); } } #region FLS Processing /// /// Starts FLS map processing /// /// The map data private void StartFLSProcessing(IEdMapAcquisitionData mapData) { this.PerformAutoId(mapData); var processor = ProcessingFactory.CreateEdMapFLSProcessor(); var settings = ProcessingFactory.CreateEdMapFLSSettings(); try { this.MapProcessor = processor; this.ElementMaps = processor.StartProcessing(mapData, settings); } catch (Exception ex) { MessageBox.Show(ex.Message, @"Error starting FLS processing"); } } #endregion #region window integral processing /// /// Starts window integral map processing /// /// The map data private void StartWindowIntegralProcessing(IEdMapAcquisitionData mapData) { this.PerformAutoId(mapData); var processor = ProcessingFactory.CreateEdMapWindowIntegralProcessor(); var settings = ProcessingFactory.CreateEdMapWindowIntegralSettings(); try { this.MapProcessor = processor; this.ElementMaps = processor.StartProcessing(mapData, settings); } catch (Exception ex) { MessageBox.Show(ex.Message, @"Error starting window integral processing"); } } #endregion #region common map processing /// /// Gets or sets the Ed Map processor /// private IEdMapProcessor MapProcessor { get { return this.mapProcessor; } set { if (this.mapProcessor == value) { return; } var processor = this.mapProcessor; if (processor != null) { processor.CancelProcessing(); processor.ProgressChanged -= this.OnProcessingProgressChanged; processor.Dispose(); } this.mapProcessor = value; if (value != null) { value.ProgressChanged += this.OnProcessingProgressChanged; } } } private void OnProcessingProgressChanged(object sender, ProcessingProgressEventArgs e) { this.ProgressPercentage = e.ProgressPercentage; if (e.State == ProcessingProgressState.Completed) { this.elementMaps.ToList().ForEach(i => i.AutoBrightness()); // calculate total counts in the first element map var elementMap = this.elementMaps.OfType().FirstOrDefault(); if (elementMap != null) { int pixels = elementMap.Height * elementMap.Width; float[] countsPerSecond = new float[pixels]; elementMap.GetData(countsPerSecond); float[] liveTimes = new float[pixels]; this.mapData.EdMap.GetLiveTimes(elementMap.BinFactor, liveTimes); double totalCounts = Enumerable.Range(0, pixels).Sum(i => countsPerSecond[i] * liveTimes[i]); } } } /// /// Gets or sets the processing percentage (0 - 100) /// public double ProgressPercentage { get { return this.progressPercentage; } set { this.progressPercentage = value; this.RaisePropertyChanged(nameof(this.ProgressPercentage)); } } /// /// Gets or sets the element maps /// public IReadOnlyList ElementMaps { get { return this.elementMaps; } set { if (this.elementMaps == value) { return; } var elementMaps = this.elementMaps; elementMaps?.ToList().ForEach(i => i.Dispose()); this.elementMaps = value; this.RaisePropertyChanged(nameof(this.ElementMaps)); } } private void PerformAutoId(IEdMapAcquisitionData mapData) { try { // Identify the elements in the sum spectrum var elements = this.spectrumProcessing.IdentifyElements(mapData.EdSpectrum, this.autoIdSettings); if (elements.Any()) { // Update the identified elements in the map. Used to create the element maps. mapData.EdMap.ClearIdentifiedElements(); mapData.EdMap.SetIdentifiedElements(elements, true); // Update the peak labels this.spectrumProcessing.UpdatePeakLabels(mapData.EdSpectrum, null); } } catch (Exception ex) { MessageBox.Show(ex.Message, @"Error processing spectrum"); } } private void CancelButton_Click(object sender, RoutedEventArgs e) { this.MapProcessor?.CancelProcessing(); } #endregion private void WindowIntegral_Click(object sender, RoutedEventArgs e) { if (this.mapData != null) { this.StartWindowIntegralProcessing(this.mapData); } } private void FLS_Click(object sender, RoutedEventArgs e) { if (this.mapData != null) { this.StartFLSProcessing(this.mapData); } } } }