using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.ComponentModel.Composition; using System.Threading.Tasks; using System.Windows; using System.Windows.Data; using DTS.Common.Events; using DTS.Common.Events.TTSImport; using DTS.Common.Interface.DataRecorders; using DTS.Common.Interface.TestSetups.Imports.TTS; using DTS.Common.Interface.TestSetups.Imports.TTS.ReadFile; using DTS.Common.Utilities.Logging; using TTSImport.Resources; using Application = System.Windows.Application; using DASChannel = TTSImport.Model.DASChannel; using MessageBox = System.Windows.MessageBox; using DTS.Common.DAS.Concepts; using DTS.Common.Enums; using TTSImport.Model; using Prism.Events; using Unity; using DTS.Common.Interactivity; using Prism.Regions; using Prism.Commands; // ReSharper disable CheckNamespace // ReSharper disable MemberCanBePrivate.Global // ReSharper disable InconsistentNaming namespace TTSImport { /// /// this class handles Level Trigger edit/create functionality /// [PartCreationPolicy(CreationPolicy.Shared)] public class AnalogChannelsViewModel : IAnalogChannelsViewModel { /// /// The Hardware Scan view /// public IAnalogChannelsView View { get; set; } private IEventAggregator _eventAggregator { get; } private IRegionManager _regionManager; private IUnityContainer UnityContainer { get; } public InteractionRequest NotificationRequest { get; } public InteractionRequest ConfirmationRequest { get; } /// /// /// Occurs when a property value changes. /// public event PropertyChangedEventHandler PropertyChanged; public void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } #region constructors and initializers /// /// 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 AnalogChannelsViewModel(IAnalogChannelsView 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(OnAssignedChannelsChangedEvent, ThreadOption.PublisherThread, true); _eventAggregator.GetEvent() .Subscribe(OnHardwareScanComplete, ThreadOption.PublisherThread, true); _eventAggregator.GetEvent() .Subscribe(OnEIDComplete, ThreadOption.PublisherThread, true); } #endregion #region Methods private void OnEIDComplete(IDictionary sensorIdToChannelId) { var channelIdToSensorId = new Dictionary(); using (var e = sensorIdToChannelId.GetEnumerator()) { while (e.MoveNext()) { channelIdToSensorId[e.Current.Value] = e.Current.Key; } } _hardwareChannelIdToSensorId = channelIdToSensorId; } public string Validate() { var bEmptyChannelCodes = false; foreach (var channel in DASChannels) { if (null == channel.Channel) { continue; } if (string.IsNullOrWhiteSpace(channel.ToyotaCode)) { bEmptyChannelCodes = true; } } return bEmptyChannelCodes ? StringResources.EmptyChannelCodeWarning : string.Empty; } private void OnHardwareScanComplete(List hardware) { _hardware = hardware; } private void OnAssignedChannelsChangedEvent(ITTSSetup setup) { if (!Application.Current.Dispatcher.CheckAccess()) { Application.Current.Dispatcher.BeginInvoke(new Action(() => { OnAssignedChannelsChangedEvent(setup); })); return; } _setup = setup; if (null == _hardware || null == _setup) { return; } var channels = new ObservableCollection(); var remainingChannels = new ObservableCollection(); var channelIdToDASChannel = new Dictionary(); foreach (var das in _hardware) { if (das.IsSLICEEthernetController) { continue; } var ichannels = das.GetIHardwareChannels(); foreach (var ch in ichannels) { if (!ch.IsAnalog) { continue; } var newChannel = new DASChannel(ch); if (_hardwareChannelIdToSensorId.ContainsKey(ch.GetId())) { newChannel.EID = _hardwareChannelIdToSensorId[ch.GetId()]; } channels.Add(newChannel); channelIdToDASChannel[newChannel.HardwareChannel.GetId()] = newChannel; } } foreach (var channelRecord in _setup.Channels) { if (channelRecord.IsEmptyRecord) { continue; } if (channelRecord.IsDigitalInput) { continue; } if (channelRecord.IsDigitalOutput) { continue; } if (channelRecord.IsSquib) { continue; } if (!channelRecord.IsChannelCodeValid) { continue; } if (channelRecord.ChannelCode == TTSChannelRecord.NONE) { continue; } if (null != channelRecord.HardwareChannel) { var key = channelRecord.HardwareChannel.GetId(); if (channelIdToDASChannel.ContainsKey(key)) { channelIdToDASChannel[key].SetITTSChannelRecord(channelRecord); } } else { remainingChannels.Add(channelRecord); } } DASChannels = channels; RemainingChannels = remainingChannels; OnPropertyChanged("DASChannels"); OnPropertyChanged("RemainingChannels"); } 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() { 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 }); } #endregion #region Properties private ITTSSetup _setup; private IList _hardware; private IDictionary _hardwareChannelIdToSensorId = new Dictionary(); public bool IsDirty { get; private set; } private bool _isBusy; public bool IsBusy { get => _isBusy; set { _isBusy = value; OnPropertyChanged("IsBusy"); } } private bool _isMenuIncluded; public bool IsMenuIncluded { get => _isMenuIncluded; set { _isMenuIncluded = value; OnPropertyChanged("IsMenuIncluded"); } } private bool _isNavigationIncluded; public bool IsNavigationIncluded { get => _isNavigationIncluded; set { _isNavigationIncluded = value; OnPropertyChanged("IsNavigationIncluded"); } } public bool AssignEnabled { get; set; } public bool RemoveEnabled { get; set; } public bool EnableOrDisableEnabled { get; set; } public ObservableCollection DASChannels { get; set; } = new ObservableCollection(); public ObservableCollection RemainingChannels { get; set; } = new ObservableCollection(); private ITTSChannelRecord _selectedRemainingChannel; public ITTSChannelRecord SelectedRemainingChannel { get => _selectedRemainingChannel; set { _selectedRemainingChannel = value; if (null == _selectedRemainingChannel || null == SelectedDASChannel) return; AssignEnabled = true; OnPropertyChanged("AssignEnabled"); } } private DASChannel _selectedDASChannel; public DASChannel SelectedDASChannel { get => _selectedDASChannel; set { _selectedDASChannel = value; DetermineRemoveEnableStatus(); } } public void DetermineRemoveEnableStatus() { if (_selectedDASChannel?.Channel != null) { if (!string.IsNullOrWhiteSpace(_selectedDASChannel.EID) && _selectedDASChannel.EID == _selectedDASChannel.Channel.SensorEID) { //can only be replaced, can't be removed RemoveEnabled = false; } RemoveEnabled = true; } else { RemoveEnabled = false; } OnPropertyChanged("RemoveEnabled"); AssignEnabled = null != _selectedDASChannel && null != _selectedRemainingChannel; OnPropertyChanged("AssignEnabled"); EnableOrDisableEnabled = null != _selectedDASChannel && null != _selectedDASChannel.Channel; OnPropertyChanged("EnableOrDisableEnabled"); OnPropertyChanged("EnableOrDisableText"); } public string EnableOrDisableText { get { if (SelectedDASChannel?.Channel == null) { return StringResources.Analog_Enable; } return SelectedDASChannel.Channel.Disabled ? StringResources.Analog_Enable : StringResources.Analog_Disable; } } #endregion Properties #region Commands #region assign /// /// Assign a channel code to a channel /// private DelegateCommand _assignCommand; public DelegateCommand AssignCommand => _assignCommand ?? (_assignCommand = new DelegateCommand(Assign)); private void Assign() { if (SelectedRemainingChannel == null) { return; } var error = string.Empty; if (!VoltageIsValid(SelectedRemainingChannel, SelectedDASChannel, out error)) { var window = Application.Current.MainWindow; if (null == window) { return; } MessageBox.Show(window, error, StringResources.AssignSensorExcitationError, MessageBoxButton.OK); return; } _eventAggregator.GetEvent().Publish(_setup); //BEFORE we go any further, check the state of sensor ids //if the channel has a sensor id AND there's a sensor on the channel with the same id //then prompt on replacing the id //IF the channel has a sensor id and there's no sensor on the channel BUT the new sensor has a different id //then prompt on replacing the id var bReplacingID = false; if (!string.IsNullOrWhiteSpace(SelectedDASChannel.EID)) { if (null != SelectedRemainingChannel && SelectedRemainingChannel.SensorEID != SelectedDASChannel.EID) { bReplacingID = true; } //if existing channel has this ID, we will need to clear out the old id and assign a new one... if (null != SelectedDASChannel.Channel && SelectedDASChannel.Channel.SensorEID == SelectedDASChannel.EID) { bReplacingID = true; } } else if (_setup.RequireEIDFound && !string.IsNullOrWhiteSpace(SelectedRemainingChannel.SensorEID)) { var window = Application.Current.MainWindow; if (null == window) { return; } var msg = string.Format(StringResources.AllowEIDToBlankChannelChallenge, SelectedRemainingChannel.SensorSerialNumber); msg = msg.Replace("\\r\\n", "\r\n"); var dialogResult = MessageBox.Show(window, msg, StringResources.UserFeedbackRequired, MessageBoxButton.OKCancel); APILogger.Log(msg, $"User pressed {dialogResult.ToString()}"); if (dialogResult == MessageBoxResult.OK) { SelectedRemainingChannel.SensorEID = string.Empty; AssignWork(); } return; } if (bReplacingID) { var window = Application.Current.MainWindow; if (null == window) { return; } var dialogResult = MessageBox.Show(window, StringResources.AssignSensorPrompt, StringResources.UserFeedbackRequired, MessageBoxButton.YesNo); APILogger.Log(StringResources.AssignSensorPrompt, $"User pressed {dialogResult.ToString()}"); if (dialogResult == MessageBoxResult.Yes) { AssignWork(); } return; } AssignWork(); } /// /// returns True if the sensor's voltage is supported by the hardware channel, or an error message and False if not /// /// /// /// /// private bool VoltageIsValid(ITTSChannelRecord selectedRemainingChannel, DASChannel selectedDASChannel, out string error) { error = string.Empty; var voltageEnum = ExcitationVoltageOptions.ExcitationVoltageOption.Undefined; try { voltageEnum = Test.Module.Channel.Sensor.GetExcitationVoltageEnumFromMagnitude(selectedRemainingChannel.SensorExcitationVolts); } catch { } //GetExcitationVoltageEnumFromMagnitude will throw an exception if an invalid voltage is passed to it if (selectedDASChannel.HardwareChannel.IsSupportedExcitation(voltageEnum)) return true; error = string.Format(StringResources.InvalidExcitationAssignment, selectedRemainingChannel.SensorExcitationVolts); return false; } /// /// removes the channel as a selected channel for any level triggers /// removes the channel as a possible channel for any level triggers /// /// private void RemoveFromLevelTriggers(ITTSChannelRecord channel) { if (null != _setup) { foreach (var lt in _setup.LevelTriggers) { lt.Remove(channel); } } } /// /// adds the channel as a possible channel to any level triggers /// /// private void AddToLevelTriggers(ITTSChannelRecord channel) { if (null != _setup) { foreach (var lt in _setup.LevelTriggers) { lt.Add(channel); } } } /// /// assigns a channel record to a physical channel /// private void AssignWork() { if (null == SelectedDASChannel || null == SelectedRemainingChannel) { return; } var excitation = Test.Module.Channel.Sensor.GetExcitationVoltageEnumFromMagnitude(SelectedRemainingChannel .SensorExcitationVolts); if (!SelectedDASChannel.HardwareChannel.IsSupportedExcitation(excitation)) { var window = Application.Current.MainWindow; if (null == window) { return; } var msg = string.Format(StringResources.ExcitationNotSupportedByChannel, excitation.ToString()); var result = MessageBox.Show(window, msg, StringResources.Warning, MessageBoxButton.OK); APILogger.Log(msg, $"user pressed {result.ToString()}"); return; } if (null != SelectedDASChannel.Channel) { if (!string.IsNullOrWhiteSpace(SelectedDASChannel.EID)) { SelectedDASChannel.Channel.SensorEID = ""; } RemainingChannels.Add(SelectedDASChannel.Channel); RemoveFromLevelTriggers(SelectedDASChannel.Channel); } if (!string.IsNullOrWhiteSpace(SelectedDASChannel.EID)) { SelectedRemainingChannel.SensorEID = SelectedDASChannel.EID; } SelectedDASChannel.SetITTSChannelRecord(SelectedRemainingChannel); AddToLevelTriggers(SelectedDASChannel.Channel); var channel = SelectedRemainingChannel; var index = RemainingChannels.IndexOf(channel); SelectedRemainingChannel = null; RemainingChannels.Remove(channel); CollectionViewSource.GetDefaultView(DASChannels)?.Refresh(); if (index < RemainingChannels.Count) { SelectedRemainingChannel = RemainingChannels[index]; OnPropertyChanged("SelectedRemainingChannel"); } else if (RemainingChannels.Count > 0) { SelectedRemainingChannel = RemainingChannels[index - 1]; OnPropertyChanged("SelectedRemainingChannel"); } index = DASChannels.IndexOf(SelectedDASChannel); for (var i = index; i < DASChannels.Count; i++) { var dasChannel = DASChannels[i]; if (null != dasChannel.Channel) { continue; } SelectedDASChannel = dasChannel; OnPropertyChanged("SelectedDASChannel"); return; } //didn't find a match, start from the beginning? for (var i = 0; i < index; i++) { var dasChannel = DASChannels[i]; if (null != dasChannel.Channel) { continue; } SelectedDASChannel = dasChannel; OnPropertyChanged("SelectedDASChannel"); return; } //if we get here there's no new channel to go to, change remove/enable/disable button status OnPropertyChanged("SelectedDASChannel"); DetermineRemoveEnableStatus(); } #endregion #region remove private DelegateCommand _removeCommand; public DelegateCommand RemoveCommand => _removeCommand ?? (_removeCommand = new DelegateCommand(Remove)); /// /// remove a hardware channel assignment (does not remove the channel from the test setup though?) /// private void Remove() { if (null == SelectedDASChannel || null == SelectedDASChannel.Channel) { return; } _eventAggregator.GetEvent().Publish(_setup); RemainingChannels.Add(SelectedDASChannel.Channel); SelectedDASChannel.SetITTSChannelRecord(null); CollectionViewSource.GetDefaultView(DASChannels)?.Refresh(); var index = DASChannels.IndexOf(SelectedDASChannel); for (var i = index; i < DASChannels.Count; i++) { if (null == DASChannels[i].Channel) { continue; } SelectedDASChannel = DASChannels[i]; OnPropertyChanged("SelectedDASChannel"); return; } for (var i = 0; i < index; i++) { if (null == DASChannels[i].Channel) { continue; } SelectedDASChannel = DASChannels[i]; OnPropertyChanged("SelectedDASChannel"); return; } //if we get here there's no new channel to go to, but we need to set the remove/enable/disable button status OnPropertyChanged("SelectedDASChannel"); DetermineRemoveEnableStatus(); } #endregion #region enableordisable private DelegateCommand _enableOrDisableCommand; public DelegateCommand EnableOrDisableCommand => _enableOrDisableCommand ?? (_enableOrDisableCommand = new DelegateCommand(EnableOrDisable)); /// /// enables or disables a channel in the test. /// private void EnableOrDisable() { if (SelectedDASChannel?.Channel == null) { return; } SelectedDASChannel.Channel.Disabled = !SelectedDASChannel.Channel.Disabled; SelectedDASChannel.Disabled = SelectedDASChannel.Channel.Disabled; if (SelectedDASChannel.Channel.Disabled) { RemoveFromLevelTriggers(SelectedDASChannel.Channel); } else { AddToLevelTriggers(SelectedDASChannel.Channel); } OnPropertyChanged("EnableOrDisableText"); _eventAggregator.GetEvent().Publish(_setup); } #endregion #endregion } }