namespace OINA.Extender.WPF.Testharness
{
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
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.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 IEdMapFLSProcessor flsProcessor;
private double progressPercentage;
private IEdMapAcquisitionData processedMapData;
public XSyncMapAcquisition()
{
this.InitializeComponent();
this.DataContext = this;
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;
this.processedMapData = null;
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;
}
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;
});
}
if (e.Success)
{
// Get off the acquisition thread
Task.Run(() =>
{
this.StartFLSProcessing(e.Value);
});
}
}
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)
{
var autoIdSettings = ProcessingFactory.CreateAutoIdSettings();
var spectrumProcessing = ProcessingFactory.CreateSpectrumProcessing();
var elements = spectrumProcessing.IdentifyElements(mapData.EdSpectrum, autoIdSettings);
if (elements.Any())
{
mapData.EdMap.SetIdentifiedElements(elements, true);
this.FLSProcessor = ProcessingFactory.CreateEdMapFLSProcessor();
var settings = ProcessingFactory.CreateEdMapFLSSettings();
try
{
this.processedMapData = mapData;
this.ElementMaps = this.FLSProcessor.StartProcessing(mapData, settings);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, @"Error starting processing");
}
}
}
///
/// Gets or sets the Ed Map FLS processor
///
private IEdMapFLSProcessor FLSProcessor
{
get
{
return this.flsProcessor;
}
set
{
if (this.flsProcessor == value)
{
return;
}
var processor = this.flsProcessor;
if (processor != null)
{
processor.ProgressChanged -= this.OnProcessingProgressChanged;
processor.Dispose();
}
this.flsProcessor = 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.processedMapData.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 CancelButton_Click(object sender, RoutedEventArgs e)
{
this.FLSProcessor?.CancelProcessing();
}
///
/// Reprocess a previously processed map
///
///
///
private void ReprocessButton_Click(object sender, RoutedEventArgs e)
{
try
{
if (this.processedMapData != null &&
this.processedMapData.EdMap.IdentifiedElements.Any())
{
var settings = ProcessingFactory.CreateEdMapFLSSettings();
this.ElementMaps = this.FLSProcessor.StartProcessing(this.processedMapData, settings);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, @"Error reprocessing map");
}
}
#endregion
}
}