using System; using System.ComponentModel; using System.ComponentModel.Composition; using System.Threading.Tasks; using System.Windows; using DTS.Common.Events; using DTS.Common.Interface; using DTS.Common.Interface.TestSetups.Imports.TTS.ReadFile; using Prism.Events; using Unity; using DTS.Common.Interactivity; using Prism.Regions; using Prism.Commands; using System.Collections.Generic; using System.Linq; using System.Threading; using DTS.Common; using DTS.Common.Interface.DataRecorders; using DTS.Common.Base; using DTS.Common.Classes; using DTS.Common.Enums; using DTS.Common.Events.TTSImport; using TTSImport.Model; using TTSImport.Resources; // ReSharper disable CheckNamespace // ReSharper disable MemberCanBePrivate.Global // ReSharper disable InconsistentNaming namespace TTSImport { /// /// this class handles Summary functionality /// [PartCreationPolicy(CreationPolicy.Shared)] public class SummaryViewModel : ISummaryViewModel { /// /// The Status and Progress bars /// public IStatusAndProgressBarView StatusAndProgressBarView { get; private set; } /// /// The Summary view /// public ISummaryView View { get; set; } private IEventAggregator _eventAggregator { get; set; } private IRegionManager _regionManager; private IUnityContainer UnityContainer { get; set; } public InteractionRequest NotificationRequest { get; private set; } public InteractionRequest ConfirmationRequest { get; private set; } /// /// Creates a new instance of the TechnologyDomainEditViewModel. /// /// /// The logical placeholder defined within the application's UI (in the shell or within views) into which views are displayed. /// The EventAggregator which allows different components to publish/subscribe to events without being coupled to each other. /// The unityContainer. public SummaryViewModel(ISummaryView view, IRegionManager regionManager, IEventAggregator eventAggregator, IUnityContainer unityContainer) { View = view; View.DataContext = this; NotificationRequest = new InteractionRequest(); ConfirmationRequest = new InteractionRequest(); _eventAggregator = eventAggregator; UnityContainer = unityContainer; _regionManager = regionManager; _eventAggregator.GetEvent().Subscribe(OnRaiseNotification); _eventAggregator.GetEvent().Subscribe(OnBusyIndicatorNotification, ThreadOption.PublisherThread, true); _eventAggregator.GetEvent().Subscribe(OnReadFileFinished, ThreadOption.PublisherThread, true); _eventAggregator.GetEvent().Subscribe(OnTestSetupChanged, ThreadOption.PublisherThread, true); } #region Methods public void Cleanup() { } public Task CleanupAsync() { return Task.CompletedTask; } public void Initialize() { } public void Initialize(object parameter) { } public void Initialize(object parameter, object model) { } public Task InitializeAsync() { StatusAndProgressBarView = GetStatusAndProgressBarView(this); return Task.CompletedTask; } public Task InitializeAsync(object parameter) { return Task.CompletedTask; } public void Activated() { } /// /// Private Event handler for RaiseNotification event. /// private void OnBusyIndicatorNotification(bool eventArg) { IsBusy = eventArg; } /// /// Private Event handler for RaiseNotification event. /// private void OnRaiseNotification(NotificationContentEventArgs eventArgsWithTitle) { // The NotificationRequest.Raise triggers the Invoke() method of the PopupWindowAction object to show the NotificationWindow window // Notification object expects a NotificationContentEventArgsWithoutTitle object and a Title string. var eventArgsWithoutTitle = new NotificationContentEventArgs(eventArgsWithTitle.Message, "", eventArgsWithTitle.Image, string.Empty); NotificationRequest.Raise(new Notification { Content = eventArgsWithoutTitle, Title = eventArgsWithTitle.Title }); } public string GetTestId() { return null == View ? "" : View.GetTestId(); } private void OnReadFileFinished(ReadFileStatusArg statusArg) { TestSetupImported = false; if (!statusArg.Status) return; _setup = statusArg.TTSSetup; View?.SetTestName(_setup.TestId); OnPropertyChanged(PropertyNames.ImportFileName.ToString()); OnPropertyChanged(PropertyNames.TestSetupName.ToString()); OnPropertyChanged(PropertyNames.SampleRate.ToString()); OnPropertyChanged(PropertyNames.RecordingMode.ToString()); OnPropertyChanged(PropertyNames.TestLength.ToString()); OnPropertyChanged(PropertyNames.PreTrigger.ToString()); OnPropertyChanged(PropertyNames.PostTrigger.ToString()); OnPropertyChanged(PropertyNames.ROIStart.ToString()); OnPropertyChanged(PropertyNames.ROIEnd.ToString()); OnPropertyChanged(PropertyNames.AAF_TDAS.ToString()); OnPropertyChanged(PropertyNames.AAF_SLICE.ToString()); } private void OnTestSetupChanged(ITTSSetup setup) { TestSetupImported = false; } public void UpdateUI() { SampleRate = _setup.SampleRate; SetAvailableRecordingModes(); RecordingMode = _setup.Mode; SetChannelList(); TestSetupComplete = !SummaryChannelList.Exists(channelType => (channelType.Unassigned != "0") && (channelType.Unassigned != Resources.StringResources.Table_NA)); if (!TestSetupComplete || Array.Exists(_setup.Channels, channel => channel.IsModified) || Array.Exists(_setup.LevelTriggers, levelTrigger => levelTrigger.IsModified)) { TestSetupImported = false; } } private List _availableSampleRates = new List(); public List AvailableSampleRates { get => _availableSampleRates; set { _availableSampleRates = value; OnPropertyChanged("AvailableSampleRates"); } } public void SetAvailableSampleRates(int[] values) { AvailableSampleRates.Clear(); var minSampleRate = (double)values.Min(); var maxSampleRate = (double)values.Max(); foreach (var channel in _setup.Channels) { if (channel.HardwareChannel == null) continue; minSampleRate = Math.Max(channel.HardwareChannel.GetParentDAS().GetMinSampleRateDouble(), minSampleRate); maxSampleRate = Math.Min(channel.HardwareChannel.GetParentDAS().GetMaxSampleRateDouble(), maxSampleRate); } foreach (var sampleRate in values) { if (sampleRate >= minSampleRate && sampleRate <= maxSampleRate) { AvailableSampleRates.Add(sampleRate); } } } public string AAF_TDAS => string.Format(StringResources.AAF_TDAS, !_aafExceptions.ContainsKey(SampleRate) ? (SampleRate / 5) : _aafExceptions[SampleRate][0]); public string AAF_SLICE => string.Format(StringResources.AAF_SLICE, !_aafExceptions.ContainsKey(SampleRate) ? (SampleRate / 5) : _aafExceptions[SampleRate][1]); private Dictionary> _aafExceptions = new Dictionary>(); public void SetAAFExceptions(Dictionary> values) { _aafExceptions.Clear(); _aafExceptions = values; } public List AvailableRecordingModes { get; } = new List(); private void SetAvailableRecordingModes() { AvailableRecordingModes.Clear(); AvailableRecordingModes.Add(RecordingModes.CircularBuffer); AvailableRecordingModes.Add(RecordingModes.Recorder); if (_setup.AllowAdvancedRecordingModes) { AvailableRecordingModes.Add(RecordingModes.HybridRecorder); } } public void SetSerializedTestIdValues(string[] values) { View?.UpdateTestIds(values); } /// /// Fill in the table of channels, including those added after the file was read /// public void SetChannelList() { const int Assigned = 0; const int Unassigned = 1; var analogChannels = new[] { 0, 0 }; var tomChannels = new[] { 0, 0 }; var digitalInChannels = new[] { 0, 0 }; var levelTriggers = new[] { 0, 0 }; foreach (var channelRecord in _setup.Channels) { if (string.Equals(channelRecord.ChannelCode, TTSChannelRecord.NONE, StringComparison.CurrentCultureIgnoreCase) || channelRecord.Disabled) continue; if (channelRecord.IsSquib) { if (channelRecord.HardwareChannel != null && channelRecord.HardwareChannel.IsSquib) { tomChannels[Assigned] += 1; } else { tomChannels[Unassigned] += 1; } } else if (channelRecord.IsDigitalInput) { if (channelRecord.HardwareChannel != null && channelRecord.HardwareChannel.IsDigitalIn) { digitalInChannels[Assigned] += 1; } else { digitalInChannels[Unassigned] += 1; } } else if (!channelRecord.IsDigitalOutput) { if (channelRecord.HardwareChannel != null && channelRecord.HardwareChannel.IsAnalog) { analogChannels[Assigned] += 1; } else { analogChannels[Unassigned] += 1; } } } foreach (var levelTrigger in _setup.LevelTriggers) { if (!string.IsNullOrWhiteSpace(levelTrigger.Code)) { levelTriggers[Assigned] += 1; } } var temp = new List(); var channel = new SummaryChannel { ChannelType = "Analog", //internationalize these Assigned = analogChannels[Assigned], Unassigned = analogChannels[Unassigned].ToString() }; temp.Add(channel); channel = new SummaryChannel { ChannelType = "TOM", Assigned = tomChannels[Assigned], Unassigned = tomChannels[Unassigned].ToString() }; temp.Add(channel); channel = new SummaryChannel { ChannelType = "Digital In", Assigned = digitalInChannels[Assigned], Unassigned = digitalInChannels[Unassigned].ToString() }; temp.Add(channel); channel = new SummaryChannel { ChannelType = "Level Trigger", Assigned = levelTriggers[Assigned], Unassigned = Resources.StringResources.Table_NA }; temp.Add(channel); SummaryChannelList = temp; } private readonly StatusAndProgressBarEventArgs statusAndProgressBarEventArgs = new StatusAndProgressBarEventArgs(); /// /// Sets the text, background color, progress value, and/or progress visibility /// /// /// public void SetStatus(string status, string error = default(string)) //default(string) is null { switch (status) { case "Waiting": //change this to string resources but use the same one in DataPRO and here??? statusAndProgressBarEventArgs.StatusColor = BrushesAndColors.Brush_ApplicationStatus_Waiting.Color; statusAndProgressBarEventArgs.ProgressBarVisibility = Visibility.Collapsed; break; case "Working": statusAndProgressBarEventArgs.StatusColor = BrushesAndColors.Brush_ApplicationStatus_Busy.Color; statusAndProgressBarEventArgs.ProgressValue = 0; statusAndProgressBarEventArgs.ProgressBarVisibility = Visibility.Visible; break; case "Failed": TestSetupImported = false; statusAndProgressBarEventArgs.StatusColor = BrushesAndColors.Brush_ApplicationStatus_Failed.Color; statusAndProgressBarEventArgs.ProgressBarVisibility = Visibility.Collapsed; break; case "Done": TestSetupImported = true; foreach (var ch in _setup.Channels) { ch.IsModified = false; } foreach (var lt in _setup.LevelTriggers) { lt.IsModified = false; } statusAndProgressBarEventArgs.StatusColor = BrushesAndColors.Brush_ApplicationStatus_Complete.Color; statusAndProgressBarEventArgs.ProgressBarVisibility = Visibility.Collapsed; break; } if (!string.IsNullOrEmpty(error)) { statusAndProgressBarEventArgs.StatusText = status + " - " + error; } else { statusAndProgressBarEventArgs.StatusText = status; } statusAndProgressBarEventArgs.Requester = this; _eventAggregator.GetEvent().Publish(statusAndProgressBarEventArgs); } /// /// Sets the progress value on the Status and Progress bar /// /// public void SetProgress(double progress) { statusAndProgressBarEventArgs.ProgressValue = (int)progress; statusAndProgressBarEventArgs.Requester = this; _eventAggregator.GetEvent().Publish(statusAndProgressBarEventArgs); } private IStatusAndProgressBarView GetStatusAndProgressBarView(IBaseViewModel parent) { var view = UnityContainer.Resolve(); var viewModel = UnityContainer.Resolve(); view.DataContext = viewModel; viewModel.Initialize(parent); return view; } #endregion #region Properties private ITTSSetup _setup; public bool IsDirty { get; private set; } private bool _isBusy = false; public bool IsBusy { get => _isBusy; set { _isBusy = value; OnPropertyChanged("IsBusy"); } } private bool _isMenuIncluded = false; public bool IsMenuIncluded { get => _isMenuIncluded; set { _isMenuIncluded = value; OnPropertyChanged("IsMenuIncluded"); } } private bool _isNavigationIncluded = false; public bool IsNavigationIncluded { get => _isNavigationIncluded; set { _isNavigationIncluded = value; OnPropertyChanged("IsNavigationIncluded"); } } private List _summaryChannelList = new List(); public List SummaryChannelList { get => _summaryChannelList; set { _summaryChannelList = value; OnPropertyChanged("SummaryChannelList"); } } private bool _testSetupComplete; public bool TestSetupComplete { get => _testSetupComplete; set { _testSetupComplete = value; OnPropertyChanged("ImportEnabled"); } } private bool _testSetupImported; public bool TestSetupImported { get => _testSetupImported; set { _testSetupImported = value; OnPropertyChanged("RunTestVisible"); } } public bool ImportEnabled => TestSetupComplete & IsROIValid; public Visibility RunTestVisible => TestSetupImported ? Visibility.Visible : Visibility.Hidden; public Visibility SummaryPreTriggerVisibility => RecordingMode == RecordingModes.CircularBuffer ? Visibility.Visible : Visibility.Collapsed; public string PostTriggerOrTestLength => RecordingMode == RecordingModes.CircularBuffer ? StringResources.PostTrigger : StringResources.TestLength; private bool _isROIStartValid = true; public bool IsROIStartValid { get => _isROIStartValid; set { _isROIStartValid = value; OnPropertyChanged("IsROIStartValid"); } } private bool _isROIEndValid = true; public bool IsROIEndValid { get => _isROIEndValid; set { _isROIEndValid = value; OnPropertyChanged("IsROIEndValid"); } } private void CalculateROIValid() { if (null == _setup) { IsROIEndValid = true; IsROIStartValid = true; return; } if (_setup.ROIEnd < _setup.ROIStart) { IsROIStartValid = false; IsROIEndValid = false; return; } if (RecordingMode == RecordingModes.Recorder) { IsROIStartValid = true; IsROIEndValid = !(_setup.ROIEnd - _setup.ROIStart > _setup.PostTrigger); } else { IsROIEndValid = !(_setup.ROIEnd > _setup.PostTrigger); IsROIStartValid = !(Math.Abs(_setup.ROIStart) > _setup.PreTrigger); } } public bool IsROIValid { get { CalculateROIValid(); return IsROIStartValid && IsROIEndValid; } } //=> RecordingMode != RecordingModes.Recorder || !(_setup.ROIEnd - _setup.ROIStart > _setup.PostTrigger); #endregion Properties #region Commands /// /// browse to a file to import, should be xml, maybe needs a few other criteria /// private DelegateCommand _importCommand; public DelegateCommand ImportClicked => _importCommand ?? (_importCommand = new DelegateCommand(ImportMethod)); private void ImportMethod() { _eventAggregator.GetEvent().Publish(_setup); } /// /// Run the test since the Run Test button was clicked /// private DelegateCommand _runTestCommand; public DelegateCommand RunTestClicked => _runTestCommand ?? (_runTestCommand = new DelegateCommand(RunTestMethod)); private void RunTestMethod() { SetStatus(Resources.StringResources.ImportTestSetup_PossibleStatus_Working); //use string resource (same both here and where passed) ThreadPool.QueueUserWorkItem(RunTestWorkThread, null); } void RunTestWorkThread(object obj) { _eventAggregator.GetEvent().Publish(_setup); } #endregion #region properties /// /// all properties that are exposed /// public enum PropertyNames { ImportFileName, TestSetupName, SampleRate, RecordingMode, TestLength, PreTrigger, PostTrigger, ROIStart, ROIEnd, AAF_TDAS, AAF_SLICE, AllowAdvancedRecordingModes, AllowTSRAIRRecordingModes } /// /// full path to import file /// public string ImportFileName => _setup?.Filename ?? ""; public string TestSetupName { get => _setup?.TestId ?? ""; set { TestSetupImported = false; _setup.TestId = value; View?.SetTestName(_setup.TestId); } } public double SampleRate { get => _setup?.SampleRate ?? 10000; set { if (_setup.SampleRate != value) { TestSetupImported = false; _setup.SampleRate = value; } OnPropertyChanged(PropertyNames.SampleRate.ToString()); OnPropertyChanged(PropertyNames.AAF_TDAS.ToString()); OnPropertyChanged(PropertyNames.AAF_SLICE.ToString()); } } private void OnPropertyChanged(PropertyNames sampleRate) { throw new NotImplementedException(); } public RecordingModes RecordingMode { get => _setup?.Mode ?? RecordingModes.CircularBuffer; set { if (_setup.Mode != value) { TestSetupImported = false; switch (value) { case RecordingModes.CircularBuffer: case RecordingModes.Recorder: case RecordingModes.HybridRecorder: _setup.Mode = value; break; default: _setup.Mode = RecordingModes.CircularBuffer; break; } } OnPropertyChanged("RecordingMode"); OnPropertyChanged("SummaryPreTriggerVisibility"); OnPropertyChanged("PostTriggerOrTestLength"); OnPropertyChanged("IsROIValid"); OnPropertyChanged("ImportEnabled"); } } public string PreTrigger { get => _setup?.PreTrigger.ToString("0.00") ?? ""; set { if (!double.TryParse(value, out double preTrigger)) return; //if (preTrigger * -1 > _setup.ROIStart) ROIStart = (preTrigger * -1).ToString("0.00"); //if (preTrigger * -1 > _setup.ROIEnd) ROIEnd = (preTrigger * -1).ToString("0.00"); TestSetupImported = false; _setup.PreTrigger = preTrigger; OnPropertyChanged("PreTrigger"); OnPropertyChanged("IsROIValid"); OnPropertyChanged("ImportEnabled"); } } public string PostTrigger { get => _setup?.PostTrigger.ToString("0.00") ?? ""; set { if (!double.TryParse(value, out double postTrigger)) return; //if (postTrigger < _setup.ROIStart) ROIStart = postTrigger.ToString("0.00"); //if (postTrigger < _setup.ROIEnd) ROIEnd = postTrigger.ToString("0.00"); TestSetupImported = false; _setup.PostTrigger = postTrigger; OnPropertyChanged("PostTrigger"); OnPropertyChanged("IsROIValid"); OnPropertyChanged("ImportEnabled"); } } public string ROIStart { get => _setup?.ROIStart.ToString("0.00") ?? ""; set { if (!double.TryParse(value, out double roiStart)) return; if (roiStart < _setup.PreTrigger * -1 || roiStart > _setup.PostTrigger || roiStart > _setup.ROIEnd) return; TestSetupImported = false; _setup.ROIStart = roiStart; OnPropertyChanged("ROIStart"); OnPropertyChanged("IsROIValid"); OnPropertyChanged("ImportEnabled"); } } public string ROIEnd { get => _setup?.ROIEnd.ToString("0.00") ?? ""; set { if (!double.TryParse(value, out double roiEnd)) return; if (roiEnd < _setup.PreTrigger * -1 || roiEnd > _setup.PostTrigger || roiEnd < _setup.ROIStart) return; TestSetupImported = false; _setup.ROIEnd = roiEnd; OnPropertyChanged("ROIEnd"); OnPropertyChanged("IsROIValid"); OnPropertyChanged("ImportEnabled"); } } #endregion /// ///Occurs when a property value changes. /// public event PropertyChangedEventHandler PropertyChanged; public void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } }