using System; using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Windows; using DTS.Common.Base; using DTS.Common.Classes; 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.HardwareScan; using DTS.Common.Interface.TestSetups.Imports.TTS.ReadFile; using DTS.Common.Utilities.Logging; using Prism.Events; using Unity; using DTS.Common.Interactivity; using Prism.Regions; using Prism.Commands; using TTSImport.Model; // ReSharper disable CheckNamespace // ReSharper disable MemberCanBePrivate.Global // ReSharper disable InconsistentNaming namespace TTSImport { /// /// this class handles Hardware Scan functionality /// [PartCreationPolicy(CreationPolicy.Shared)] public class HardwareScanViewModel : IHardwareScanViewModel { /// /// The Status and Progress bars /// public IStatusAndProgressBarView StatusAndProgressBarView { get; private set; } /// /// The Hardware Scan view /// public IHardwareScanView 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 Hardware Scan 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 HardwareScanViewModel(IHardwareScanView hardwareScanView, IRegionManager regionManager, IEventAggregator eventAggregator, IUnityContainer unityContainer) { View = hardwareScanView; 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(OnHardwareScanFinished, ThreadOption.PublisherThread, true); _eventAggregator.GetEvent().Subscribe(OnAssignedChannelsChangedEvent, 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 }); } private readonly StatusAndProgressBarEventArgs statusAndProgressBarEventArgs = new StatusAndProgressBarEventArgs(); /// /// Sets the text, background color, progress value, and/or progress visibility /// /// public void SetStatus(string status) { switch (status) { case "Waiting": //change this to string resources but use the same one in DataPRO and here??? statusAndProgressBarEventArgs.StatusColor = DTS.Common.BrushesAndColors.Brush_ApplicationStatus_Waiting.Color; statusAndProgressBarEventArgs.ProgressBarVisibility = Visibility.Collapsed; break; case "Working": statusAndProgressBarEventArgs.StatusColor = DTS.Common.BrushesAndColors.Brush_ApplicationStatus_Busy.Color; statusAndProgressBarEventArgs.ProgressValue = 0; statusAndProgressBarEventArgs.ProgressBarVisibility = Visibility.Visible; break; case "Failed": statusAndProgressBarEventArgs.StatusColor = DTS.Common.BrushesAndColors.Brush_ApplicationStatus_Failed.Color; statusAndProgressBarEventArgs.ProgressBarVisibility = Visibility.Collapsed; break; case "Done": statusAndProgressBarEventArgs.StatusColor = DTS.Common.BrushesAndColors.Brush_ApplicationStatus_Complete.Color; statusAndProgressBarEventArgs.ProgressBarVisibility = Visibility.Collapsed; break; } 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; } private void OnReadFileFinished(ReadFileStatusArg statusArg) { if (statusArg.Status) { _setup = statusArg.TTSSetup; } } public void HardwareScan() { SetStatus(Resources.StringResources .ImportTestSetup_PossibleStatus_Working); //use string resource (same both here and where passed) var data = new WorkFunctionThreadData(); ThreadPool.QueueUserWorkItem(HardwareScanWorkThread, data); } void HardwareScanWorkThread(object obj) { ITTSSetup temp = null; //Blank out the tables before re-scanning HardwareRecords[0].Update(0, 0, 0, 0, 0, 0, 0, 0, 0, 0); DasSummaryList = new List(); ChannelSummaryList = new List(); _eventAggregator.GetEvent().Publish(temp); //temp not needed? } private void UpdateUnitCount(IDASHardware hardwareRecord, ref uint sps, ref uint spd, ref uint spt, ref uint ecm, ref uint g5, ref uint rack) { if (hardwareRecord.SerialNumber.Length < 3) { return; } var firstThree = hardwareRecord.SerialNumber.Substring(0, 3).ToLower(); switch (firstThree) { case "spt": spt++; break; case "spd": spd++; break; case "sps": sps++; break; case "spe": ecm++; break; case "slt": spt++; break; case "sld": spd++; break; case "sle": ecm++; break; case "sls": sps++; break; case "sg5": g5++; break; } var firstTwo = hardwareRecord.SerialNumber.Substring(0, 2).ToLower(); switch (firstTwo) { case "dr": rack++; break; case "lr": rack++; break; case "5m": g5++; break; } } /// /// returns IP addresses for which SLICE2 modules have been found without a corresponding ECM/SLE /// /// /// private static IEnumerable GetMissingECMIPAddresses(List hardwareRecords) { var foundECMIPAddresses = new HashSet(); var missingECMIPAddresses = new List(); foreach (var hardwareRecord in hardwareRecords) { if (!hardwareRecord.IsSLICEEthernetController) continue; var ipAddress = hardwareRecord.Connection; var index = ipAddress.IndexOf(':'); if (index > 0) { ipAddress = ipAddress.Substring(0, index); } foundECMIPAddresses.Add(ipAddress); } foreach (var hardwareRecord in hardwareRecords) { if (hardwareRecord.SerialNumber.Length < 3) { continue; } var ipAddress = hardwareRecord.Connection.ToLower(); var index = ipAddress.IndexOf(':'); if (index > 0) { ipAddress = ipAddress.Substring(0, index); } if (foundECMIPAddresses.Contains(ipAddress)) { continue; } if (missingECMIPAddresses.Contains(ipAddress)) { continue; } var firstThree = hardwareRecord.SerialNumber.Substring(0, 3).ToLower(); switch (firstThree) { case "spt": case "spd": case "sps": case "slt": case "sld": case "sls": if (ipAddress.Contains("usb") || ipAddress.Length < 6) { continue; } missingECMIPAddresses.Add(ipAddress); break; } } return missingECMIPAddresses; } /// /// Fill in the table of DAS that are in the database and connected /// /// private void OnHardwareScanFinished(List hardwareRecords) { //Create mapping from IP address to ECM/SDB/Rack var connectionToParent = hardwareRecords.Where(hardwareRecord => hardwareRecord.IsSLICEEthernetController || hardwareRecord.IsTDASRack()).ToDictionary(hardwareRecord => hardwareRecord.Connection, hardwareRecord => hardwareRecord.SerialNumber); var moduleRecords = new List(); uint analog = 0; uint din = 0; uint dout = 0; uint squib = 0; uint sps = 0; uint spd = 0; uint spt = 0; uint ecm = 0; uint g5 = 0; uint rack = 0; var missingECMIPS = GetMissingECMIPAddresses(hardwareRecords); if (missingECMIPS.Any()) { var prompt = $"{Resources.StringResources.MissingECMSWarning}\r\n"; prompt += string.Join("\r\n", missingECMIPS.ToArray()); var mreLocal = new ManualResetEvent(false); APILogger.Log("MessageBox", prompt); Application.Current.Dispatcher.BeginInvoke(new Action(() => { var window = Application.Current.MainWindow; if (null == window) { return; } MessageBox.Show(window, prompt, Resources.StringResources.Warning, MessageBoxButton.OK, MessageBoxImage.Warning); mreLocal.Set(); })); mreLocal.WaitOne(); } foreach (var hardwareRecord in hardwareRecords) { //We don't want to list bridges, but we do want to list modules in a TDAS Rack, and DAS connected via ECM, SDB, and USB. //Modules in a TDAS Rack have IsModule() == false. //DAS connected via ECM or SDB have a hardwareRecord.Connection that's in the connectionToParent dictionary above. //DAS connected via USB have a hardwareRecord.Connection == "USB". if (hardwareRecord.IsModule() && !connectionToParent.ContainsKey(hardwareRecord.Connection) && hardwareRecord.Connection != "USB") continue; UpdateUnitCount(hardwareRecord, ref sps, ref spd, ref spt, ref ecm, ref g5, ref rack); //We want to display TDAS Racks, ECM, and SDBs in a separate row for their battery and //voltage status but also as a parent of their connected DAS. if (!hardwareRecord.IsSLICEEthernetController && !hardwareRecord.IsTDASRack()) { //Display the DAS with its associated parent (TDAS Rack/ECM/SDB) var parentDAS = connectionToParent.ContainsKey(hardwareRecord.Connection) ? connectionToParent[hardwareRecord.Connection] : hardwareRecord.ParentDAS; if (!string.IsNullOrWhiteSpace(parentDAS)) { //DAS connected via USB have a blank ParentDAS //When the hardwareRecord.ToString() override is implemented for all DAS types, the following //can be modified to use it. Currently, only Slice DAS connected via ECM return ":". hardwareRecord.SerialNumberFamily = "[" + parentDAS + ":" + hardwareRecord.SerialNumber + "]"; } else { hardwareRecord.SerialNumberFamily = hardwareRecord.SerialNumber; } UpdateChannelCount(hardwareRecord, ref analog, ref squib, ref din, ref dout); } else { hardwareRecord.SerialNumberFamily = hardwareRecord.SerialNumber; if (hardwareRecord.IsTDASRack()) { UpdateChannelCount(hardwareRecord, ref analog, ref squib, ref din, ref dout); } } moduleRecords.Add(hardwareRecord); } HardwareRecords[0].Update(analog, squib, din, dout, ecm, sps, spt, spd, g5, rack); DasSummaryList = moduleRecords; SetStatus(hardwareRecords.Any() ? Resources.StringResources.ImportTestSetup_PossibleStatus_Done : Resources.StringResources.ImportTestSetup_PossibleStatus_Failed); } /// /// counts the number of analog/squib/digitalin/digitalout on given hardware and updates count /// /// /// /// /// /// private static void UpdateChannelCount(IDASHardware hardwareRecord, ref uint analog, ref uint squib, ref uint din, ref uint dout) { var channels = hardwareRecord.GetIHardwareChannels(); for (var i = 0; i < channels.Length; i++) { var ch = channels[i]; if (ch.IsAnalog) { analog++; } else if (ch.IsDigitalIn) { din++; } else if (ch.IsDigitalOut) { dout++; } else if (ch.IsSquib) { squib++; i++;//skip the next squib } } } /// /// Sets a global dictionary to be used by SetChannelSummaryList /// private void OnAssignedChannelsChangedEvent(ITTSSetup setup) { SetChannelSummaryList(_setup.Channels); } /// /// Fill in the table of channels that were found in the Read File step /// /// public void SetChannelSummaryList(ITTSChannelRecord[] channelRecords) { const int Requested = 0; const int Assigned = 1; const int Unassigned = 2; var tomChannels = new[] { 0, 0, 0 }; var digitalInChannels = new[] { 0, 0, 0 }; var analogChannels = new[] { 0, 0, 0 }; foreach (var channelRecord in channelRecords) { if (string.Equals(channelRecord.ChannelCode, TTSChannelRecord.NONE, StringComparison.CurrentCultureIgnoreCase)) continue; if (channelRecord.IsSquib) { tomChannels[Requested] += 1; if (channelRecord.HardwareChannel != null && channelRecord.HardwareChannel.IsSquib) { tomChannels[Assigned] += 1; } else { tomChannels[Unassigned] += 1; } } else if (channelRecord.IsDigitalInput) { digitalInChannels[Requested] += 1; if (channelRecord.HardwareChannel != null && channelRecord.HardwareChannel.IsDigitalIn) { digitalInChannels[Assigned] += 1; } else { digitalInChannels[Unassigned] += 1; } } else if (!channelRecord.IsDigitalOutput) { //Must be analog analogChannels[Requested] += 1; if (channelRecord.HardwareChannel != null && channelRecord.HardwareChannel.IsAnalog) { analogChannels[Assigned] += 1; } else { analogChannels[Unassigned] += 1; } } } var temp = new List(); var channel = new ChannelSummary { ChannelType = Resources.StringResources.Analog, Requested = analogChannels[Requested], Assigned = analogChannels[Assigned], Unassigned = analogChannels[Unassigned], }; temp.Add(channel); channel = new ChannelSummary { ChannelType = Resources.StringResources.TOM, Requested = tomChannels[Requested], Assigned = tomChannels[Assigned], Unassigned = tomChannels[Unassigned], }; temp.Add(channel); channel = new ChannelSummary { ChannelType = Resources.StringResources.DigitalIn, Requested = digitalInChannels[Requested], Assigned = digitalInChannels[Assigned], Unassigned = digitalInChannels[Unassigned], }; temp.Add(channel); ChannelSummaryList = temp; } #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 _dasSummaryList = new List(); public List DasSummaryList { get => _dasSummaryList; set { _dasSummaryList = value; OnPropertyChanged("DasSummaryList"); } } private readonly IHardwareSummaryRecord[] _hardwareRecords = { new HardwareSummaryRecord() }; public IHardwareSummaryRecord[] HardwareRecords => _hardwareRecords; private List _channelSummaryList = new List(); public List ChannelSummaryList { get => _channelSummaryList; set { _channelSummaryList = value; OnPropertyChanged("ChannelSummaryList"); } } #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)); } } }