using System; using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.Composition; using System.Linq; using System.Threading.Tasks; using System.Windows.Data; using DTS.Common.DAS.Concepts; using DTS.Common.Enums; using DTS.Common.Events; using DTS.Common.Events.TTSImport; using DTS.Common.Interface; using DTS.Common.Interface.DataRecorders; using DTS.Common.Interface.TestSetups.Imports.TTS.LevelTrigger; using DTS.Common.Interface.TestSetups.Imports.TTS.ReadFile; using Prism.Events; using Unity; using DTS.Common.Interactivity; using Prism.Regions; using Prism.Commands; using TTSImport.Model; using TTSImport.Resources; // 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 LevelTriggerViewModel : ILevelTriggerViewModel { /// /// The Hardware Scan view /// public ILevelTriggerView View { get; set; } private IEventAggregator _eventAggregator { get; } private IRegionManager _regionManager; private IUnityContainer UnityContainer { get; } public InteractionRequest NotificationRequest { get; } public InteractionRequest ConfirmationRequest { get; } /// /// Creates a new instance of the TechnologyDomainEditViewModel. /// /// The Level Trigger View. /// 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 LevelTriggerViewModel(ILevelTriggerView levelTriggerView, IRegionManager regionManager, IEventAggregator eventAggregator, IUnityContainer unityContainer) { View = levelTriggerView; 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(OnEIDMapping, ThreadOption.PublisherThread, true); _eventAggregator.GetEvent().Subscribe(OnHardwareScanFinished, ThreadOption.PublisherThread, true); _eventAggregator.GetEvent().Subscribe(OnImportSavedChanges, ThreadOption.PublisherThread, true); } #region Methods private void OnImportSavedChanges(bool bSaved) { if (bSaved) { UpdateLevelTriggers(); } } private void OnHardwareScanFinished(List hardware) { _hardware = hardware; } private void OnReadFileFinished(ReadFileStatusArg statusArg) { if (statusArg.Status) { _setup = statusArg.TTSSetup; } } private IDictionary _sensorIdToChannelId = new Dictionary(); private void OnEIDMapping(IDictionary sensorIdToChannelId) { _sensorIdToChannelId = sensorIdToChannelId; UpdateLevelTriggers(); } private void UpdateLevelTriggers() { if (null == _setup) { return; } if (null == _hardware || !_hardware.Any()) { return; } var channelIdToDASChannel = new Dictionary(); var channelIdToIChannel = new Dictionary(); var availableSquibChannels = new Queue(); var availableDigitalInputChannels = new Queue(); foreach (var h in _hardware) { var channels = h.GetIHardwareChannels(); foreach (var ch in channels) { //Create a DASChannel so that voltage can be validated below channelIdToDASChannel[ch.GetId()] = new DASChannel(ch); channelIdToIChannel[ch.GetId()] = ch; if (ch.IsDigitalIn) { availableDigitalInputChannels.Enqueue(ch); } else if (ch.IsSquib) { availableSquibChannels.Enqueue(ch); } } } //handle pre-assigned channels if any //these are channels which we know the sensor should be assigned to a given hardware channel ahead of time //we do this before the EID check so that EID will override it if (_setup.PreAssignedSensorIdAndHwId.Any()) { var remainingPreassigned = new List>(); foreach (var tuple in _setup.PreAssignedSensorIdAndHwId) { var sensorId = tuple.Item1; var hwId = tuple.Item2; if (!channelIdToIChannel.ContainsKey(hwId)) { remainingPreassigned.Add(tuple); continue; } var matches = from ch in _setup.Channels where ch.SensorSerialNumber == sensorId select ch; if (matches.Any()) { var ittsChannel = matches.First(); //do not make the assignment if the user has already assigned the channel to a different hardware channel //this would happen if the user scanned, didn't find the hardware, went to analog channels, assigned the sensor //then finally went to hardware scan and then DID find the hardware. in this case just ignore the preassignment if (null == ittsChannel.HardwareChannel) { var hardwareChannel = channelIdToIChannel[hwId]; var excitation = Test.Module.Channel.Sensor.GetExcitationVoltageEnumFromMagnitude(ittsChannel.SensorExcitationVolts); //if we have a hardware assignment, but the excitation is no longer valid, then don't make the assignment if (hardwareChannel.IsAnalog && !hardwareChannel.IsSupportedExcitation(excitation)) { continue; } ittsChannel.HardwareChannel = hardwareChannel; } } } //if there are any remaining channels, these are hardware assignments that we didn't find hardware for, and we still need //to look for assignment _setup.PreAssignedSensorIdAndHwId = remainingPreassigned.ToArray(); } foreach (var channel in _setup.Channels) { if (null != channel.HardwareChannel && !channelIdToIChannel.ContainsKey(channel.HardwareChannel.GetId())) { //hardware no longer present, unassign channel.HardwareChannel = null; } if (string.IsNullOrWhiteSpace(channel.SensorEID)) { //11245 TOM and DI channels in TTS imports without IDs should have sensors assigned automatically if (channel.IsSquib) { if (availableSquibChannels.Any()) { var first = availableSquibChannels.Dequeue(); channel.HardwareChannel = first; //first channel voltage/initiation, second current, etc var second = availableSquibChannels.Dequeue(); } } else if (channel.IsDigitalInput) { if (availableDigitalInputChannels.Any()) { var first = availableDigitalInputChannels.Dequeue(); channel.HardwareChannel = first; } } continue; } if (!_sensorIdToChannelId.ContainsKey(channel.SensorEID) || !channelIdToDASChannel.ContainsKey(_sensorIdToChannelId[channel.SensorEID]) || !VoltageIsValid(channel, channelIdToDASChannel[_sensorIdToChannelId[channel.SensorEID]]) //the below condition I think was missing a not, it appears to be looking for channels that don't have a valid channel code //but DO have a valid match for sensor id ... this is probably a reserve channel where the sensor was still found that it's looking for //15643 Sensors with EID do not remain assigned after import of XML produced from TTS import //changed it again ... this is inhibiting sensors from being assigned, additionally tested it with a sensor without a valid channel code but //a valid eid, and it worked fine || (_sensorIdToChannelId.ContainsKey(channel.SensorEID) && !channel.IsChannelCodeValid) ) { //unassign hardware if one is assigned, sensor id wasn't found or the sensor has an invalid excitation voltage for this hardware //or the channel does have a jcode channel.HardwareChannel = null; continue; } var channelId = _sensorIdToChannelId[channel.SensorEID]; if (!channelIdToIChannel.ContainsKey(channelId)) continue; channel.HardwareChannel = channelIdToIChannel[channelId]; } foreach (var lt in _setup.LevelTriggers) { lt.Refresh(); } OnPropertyChanged("LevelTriggers"); _eventAggregator.GetEvent().Publish(_setup); CollectionViewSource.GetDefaultView(LevelTriggers)?.Refresh(); } /// /// returns True if the sensor's voltage is supported by the hardware channel, False if not /// /// /// /// private bool VoltageIsValid(ITTSChannelRecord selectedRemainingChannel, DASChannel selectedDASChannel) { 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 return selectedDASChannel.HardwareChannel.IsSupportedExcitation(voltageEnum); } 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 IList _hardware; private ITTSSetup _setup; public ILevelTrigger[] LevelTriggers => _setup?.LevelTriggers; 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"); } } #endregion Properties #region Commands #endregion /// ///Occurs when a property value changes. /// public event PropertyChangedEventHandler PropertyChanged; public void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } }