using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Data; using System.IO; using System.Linq; using System.Runtime.CompilerServices; using System.Threading.Tasks; using DTS.Common.Classes.Groups; using DTS.Common.Enums; using DTS.Common.Enums.Sensors; using DTS.Common.Events; using DTS.Common.Events.RegionOfInterest.RegionOfInterestChannels; using DTS.Common.Interface; using DTS.Common.Interface.Channels; using DTS.Common.Interface.DataRecorders; using DTS.Common.Interface.Groups.GroupList; using DTS.Common.Interface.RegionOfInterest; using DTS.Common.Interface.RegionOfInterest.RegionOfInterestChannels; using DTS.Common.Interface.TestDefinition; using DTS.Common.Interface.TestSetups.TestSetupsList; using DTS.Common.Utils; using RegionOfInterestChannels.Resources; using DTS.Common.Classes.TestSetups; using DTS.Common.Classes.Sensors; using DTS.Common.Enums.Hardware; using DTS.Common.Interactivity; namespace RegionOfInterestChannels { public class RegionOfInterestChannelsViewModel : IRegionOfInterestChannelsViewModel { /// /// Creates a new instance of the HardwareListViewModel /// /// /// 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 RegionOfInterestChannelsViewModel(IRegionOfInterestChannelsView view,Prism.Regions.IRegionManager regionManager, Prism.Events.IEventAggregator eventAggregator, Unity.IUnityContainer unityContainer) { View = view; View.DataContext = this; NotificationRequest = new InteractionRequest(); ConfirmationRequest = new InteractionRequest(); _eventAggregator = eventAggregator; _regionManager = regionManager; _eventAggregator.GetEvent().Subscribe(OnRaiseNotification); _eventAggregator.GetEvent() .Subscribe(OnBusyIndicatorNotification, Prism.Events.ThreadOption.PublisherThread, true); RegionsOfInterest = new BindingList(); } #region Properties #region base interface 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 IRegionOfInterestChannelsView View { get; set; } #endregion private const string VoltageString = " (Voltage)"; private const string CurrentString = " (Current)"; private object Parent; private string _currentFilter = String.Empty; private Dictionary _filterByField = new Dictionary(); private Prism.Events.IEventAggregator _eventAggregator { get; } private Prism.Regions.IRegionManager _regionManager; public InteractionRequest NotificationRequest { get; } public InteractionRequest ConfirmationRequest { get; } private ITestSummary _testSummary; private BindingList _regionsOfInterest; public BindingList RegionsOfInterest { get => _regionsOfInterest; set { _regionsOfInterest = value; if (null != _regionsOfInterest) { //FB16120: scrub list of channel names not found in case of bad data foreach (var roi in _regionsOfInterest) { if (null == roi.ChannelNames) { continue; } var channelList = new List(); var channelIdList = new List(); foreach (var channelName in roi.ChannelNames) { var noParentChannel = RegionOfInterest.RemoveParentDASName(channelName); channelList.Add(noParentChannel); } foreach (var channelId in roi.ChannelIds) { channelIdList.Add(channelId); } var notFound = new List(); var idsNotFound = new List(); var allChannelHash = new HashSet(); var allChannelIdHash = new HashSet(); var allChannelsUnfilteredIndex = 0; //18349: Cannot perform download ROI using download data tab. //in the download data route there is no SensorData, trying to access it will throw an exception ... foreach (var ach in AllChannelsUnfiltered) { if (null != ach.SensorData) { var achHardware = ach.SensorData.IsTestSpecificEmbedded ? DTS.Common.Classes.TestSetups.TestTemplateBase.GetEmbeddedChannelHardware(ach) : ach.Hardware; //Check for ROI channels that did not have a and now have a hardware channel via ID or manual assignment //If there is an EID, we want to strip off the leading "Assigned by ID" achHardware = RegionOfInterest.RemoveAssignedByIDFromHardwareString(achHardware); //33237 Don't unassign multiple ROI channels if Compact is selected in Hardware step var embedded = ach.SensorData?.IsTestSpecificEmbedded ?? false; var thermo = ach.SensorData?.IsTestSpecificThermo ?? false; var serialNumber = ChannelSerialNumber.SerialNumberFromChannel(embedded || thermo, ach.Sensor, ach.SensorData?.SerialNumber ?? ach.Sensor); var hashName = RegionOfInterest.GetAnalogChanName(serialNumber, achHardware, HardwareConstants.TSR_AIR_PREPEND, ach.Sensor); allChannelHash.Add(hashName); //The channelList entry may have no hardware channel or one that's //different than the one assigned later by ID. Additionally, //there may be more than one channel with the same sensor. //If we find an exact match, we're don't need to add/replace the hardware channel if (!channelList.Contains(hashName)) { //An exact match was not found, so add/replace the hardware channel if there is //one and only one with this sensor serial number. UpdateChannelList(ach.SensorData.SerialNumber, hashName, channelList, allChannelsUnfilteredIndex); } var hashId = ach.Id; allChannelIdHash.Add(hashId); allChannelsUnfilteredIndex++; } } foreach (var ch in channelList) { if (allChannelHash.Count > 0 && !allChannelHash.Contains(ch)) { notFound.Add(ch); } } foreach (var id in channelIdList) { if (allChannelIdHash.Count > 0 && !allChannelIdHash.Contains(id)) { idsNotFound.Add(id); } } foreach (var name in notFound) { channelList.RemoveAll(str => str == name); } foreach (var id in idsNotFound) { channelIdList.RemoveAll(lng => lng == id); } roi.SetChannelNamesNoNotify(channelList.ToArray()); roi.SetChannelIdsNoNotify(channelIdList.ToArray()); } } ResetDataView(); } } /// /// If there is one and only one entry in the list with a given serial number /// update it with different hardware. /// /// /// /// private void UpdateChannelList(string serialNumber, string hashName, List channelList, int startingIndex) { var index = 0; foreach (var channel in channelList) { if (index >= startingIndex) { var channelSerialNumber = channel.Substring(channel.LastIndexOf('\\') + 1); if (channelSerialNumber == serialNumber) { channelList[index] = hashName; return; } } index++; } } private PossibleFilters BridgeFilter { get; set; } = PossibleFilters.All; /// /// holds a term (should be in lower case) to filter in/search for /// private string SearchTerm { get; set; } = string.Empty; public List AllChannelsUnfiltered { get; set; } = new List(); /// /// this is all the channels displayed in the UI (but not any channels filtered out) /// public ObservableCollection AllChannels { get; set; } private ObservableCollection AllTestChannels { get; set; } public string[] AllChannelSSNs { get { var ssns = new List(); //13477 Crash when clicking + to add second ROI for CSV export //when coming from the export tile, AllChannels is null if (null != AllChannels && AllChannels.Any()) { foreach (var ch in AllChannels) { if (ch.IsDisabled) { continue; } if (ch.SensorId < 0) { continue; } var chHardware = ch.SensorData.IsTestSpecificEmbedded ? DTS.Common.Classes.TestSetups.TestTemplateBase.GetEmbeddedChannelHardware(ch) : ch.Hardware; var serialNumber = ChannelSerialNumber.SerialNumberFromChannel(ch.SensorData.IsTestSpecificEmbedded || ch.SensorData.IsTestSpecificThermo, ch.Sensor, ch.SensorData.SerialNumber); ssns.Add(chHardware + "\\" + serialNumber); } } else if (null != AllTestChannels && AllTestChannels.Any()) { foreach (var ch in AllTestChannels) { ssns.Add(ch.HardwareChannelName + "\\" + (ch.Bridge.ToUpperInvariant() == "SQUIB" || ch.Bridge.ToUpperInvariant() == "TOMDIGITAL" ? ch.Description.Replace(VoltageString, string.Empty).Replace(CurrentString, string.Empty) : ch.SerialNumber)); } } return ssns.ToArray(); } } private BindingList _channelList = new BindingList(); public BindingList ChannelList { get => _channelList; set { if (null != _channelList) { _channelList.ListChanged -= CheckboxesOnListChanged; } _channelList = value; if (null != _channelList) { _channelList.ListChanged += CheckboxesOnListChanged; } OnPropertyChanged("ChannelList"); } } private ObservableCollection _columns; public ObservableCollection Columns { get => _columns; set { _columns = value; OnPropertyChanged("Columns"); } } private void CheckboxesOnListChanged(object sender, ListChangedEventArgs listChangedEventArgs) { if (listChangedEventArgs.PropertyDescriptor.Name.Equals("LastIndexChanged")) { var chChangedIndex = listChangedEventArgs.NewIndex; var roiChangedIndex = ((BindingList)sender)[chChangedIndex].LastIndexChanged; var checkedIds = new List(); var checkedGuidStringList = new List(); foreach (var channel in (BindingList)sender) { if (channel.ROIIncludes[roiChangedIndex].Checked) { checkedIds.Add(channel.ChannelId); checkedGuidStringList.Add(channel.GuidString); } } var checkedGuidStringIdDict = new Dictionary(); var channelNameList = new List(); var checkedChannelIdList = new List(); foreach (var checkedId in checkedIds) { checkedChannelIdList.Add(checkedId); } var checkedIndex = 0; foreach (var guidString in checkedGuidStringList) { checkedGuidStringIdDict[guidString] = checkedChannelIdList[checkedIndex]; checkedIndex++; } foreach (var guidStringIdPair in checkedGuidStringIdDict) { var channelName = string.Empty; if (guidStringIdPair.Value == -1) { channelName = ((BindingList)sender).Where(ch => ch.GuidString == guidStringIdPair.Key).Select(ch => ch.Descriptor).FirstOrDefault(); } else { channelName = ((BindingList)sender).Where(ch => ch.ChannelId == guidStringIdPair.Value).Select(ch => ch.Descriptor).FirstOrDefault(); } channelNameList.Add(channelName); } _regionsOfInterest[roiChangedIndex].ChannelNames = channelNameList.ToArray(); _regionsOfInterest[roiChangedIndex].ChannelIds = checkedChannelIdList.ToArray(); _eventAggregator.GetEvent() .Publish(new RegionOfInterestChannelsSelectedEventArgs(_regionsOfInterest[roiChangedIndex].Suffix, _regionsOfInterest[roiChangedIndex].ChannelNames, _regionsOfInterest[roiChangedIndex].ChannelIds, Parent)); } } #endregion #region Methods public void SetParent(object o) { Parent = o; } private void ResetDataView() { try { var columnsList = new ObservableCollection(); //column order columnsList.Add(new ColumnDescriptor() { HeaderText = "Group Name", DisplayMember = "GroupName", MemberType = typeof(string) }); if (ISOViewMode == IsoViewMode.ISOAndUserCode || ISOViewMode == IsoViewMode.UserCodeOnly) { columnsList.Add(new ColumnDescriptor() { HeaderText = "User Code", DisplayMember = "UserCode", MemberType = typeof(string) }); columnsList.Add(new ColumnDescriptor() { HeaderText = "User Channel Name", DisplayMember = "UserChannelName", MemberType = typeof(string) }); } else if (ISOViewMode == IsoViewMode.ISOOnly || ISOViewMode == IsoViewMode.ISOAndUserCode) { columnsList.Add(new ColumnDescriptor() { HeaderText = "ISO Code", DisplayMember = "ISOCode", MemberType = typeof(string) }); columnsList.Add(new ColumnDescriptor() { HeaderText = "ISO Channel Name", DisplayMember = "ISOChannelName", MemberType = typeof(string) }); } else if (ISOViewMode == IsoViewMode.ChannelNameOnly) { columnsList.Add(new ColumnDescriptor() { HeaderText = StringResources.ChannelName, DisplayMember = "ChannelName", MemberType = typeof(string) }); } columnsList.Add(new ColumnDescriptor() { HeaderText = "Hardware", DisplayMember = "Hardware", MemberType = typeof(string) }); columnsList.Add(new ColumnDescriptor() { HeaderText = "Serial Number", DisplayMember = "SerialNumber", MemberType = typeof(string) }); columnsList.Add(new ColumnDescriptor() { HeaderText = "Sensor Name", DisplayMember = "SensorName", MemberType = typeof(string) }); columnsList.Add(new ColumnDescriptor() { HeaderText = "DAS Serial Number", DisplayMember = "DASSerialNumber", MemberType = typeof(string) }); columnsList.Add(new ColumnDescriptor() { HeaderText = "Sample Rate", DisplayMember = "SampleRate", MemberType = typeof(string) }); columnsList.Add(new ColumnDescriptor() { HeaderText = "Display Units", DisplayMember = "DisplayUnits", MemberType = typeof(string) }); for (var i = 0; i < _regionsOfInterest.Count; i++) { columnsList.Add(new ColumnDescriptor() { HeaderText = _regionsOfInterest[i].Suffix, DisplayMember = "ROIIncludes[" + i.ToString() + "]", MemberType = typeof(bool) }); } if (_testSummary != null) { //export tile use BuildChannelListFromSummary(); } else { //everywhere else BuildChannelListFromGroupChannels(); } Columns = columnsList; } catch (Exception ex) { DTS.Common.Utilities.Logging.APILogger.Log("ResetDataView exception: ", ex); throw; } } private string GetChannelCode(ITestChannel testChannel) { switch (ISOViewMode) { case IsoViewMode.ISOOnly: return testChannel.IsoCode; case IsoViewMode.ISOAndUserCode: return $"{testChannel.UserCode}/{testChannel.IsoCode}"; case IsoViewMode.UserCodeOnly: return testChannel.UserCode; case IsoViewMode.ChannelNameOnly: return string.Empty; } return ""; } private void BuildChannelListFromSummary() { var channelList = new BindingList(); foreach (var ch in AllTestChannels) { var serialNumber = ChannelSerialNumber.SerialNumberFromChannel(ch.SerialNumber == "TSA_Embedded", ch.UserChannelName, ch.SerialNumber); var newEnabler = new ChannelEnabler() { UserCode = ch.UserCode, UserChannelName = ch.UserChannelName, ISOCode = ch.IsoCode, ISOChannelName = ch.IsoChannelName, Hardware = ch.HardwareChannelName, SerialNumber = ch.SerialNumber, SensorName = ch.Description, DASSerialNumber = ch.HardwareChannelName ?? "N/A", //fix this SampleRate = ch.SampleRateHz.ToString(), //test this Code = GetChannelCode(ch), DisplayUnits = ch.Eu, GroupName = ch.ChannelGroupName ?? string.Empty, Descriptor = ch.HardwareChannelName + "\\" + serialNumber, GuidString = Guid.NewGuid().ToString(), ChannelId = ParseChannelId(ch.ChannelId) }; foreach (var roi in _regionsOfInterest) { if (null == roi.ChannelIds) { newEnabler.ROIIncludes.Add(new State(false)); } else { newEnabler.ROIIncludes.Add(new State(roi.ChannelIds.Contains(newEnabler.ChannelId))); } } channelList.Add(newEnabler); } ChannelList = channelList; } /// /// 44068 Older .dts files have a ChannelId consisting of: __, /// for example, "H3-3ch_0_2" or "f9f0bfe8-afc4-4730-8045-8f1e45340573_0_8533". The ChannelId in /// current .dts files is simply the unique number. /// /// /// private static long ParseChannelId(string channelIdString) { if (channelIdString.EndsWith(DTS.Common.Constants.CURRENT_SUFFIX)) { channelIdString = channelIdString.Substring(0, channelIdString.Length - DTS.Common.Constants.CURRENT_SUFFIX.Length); } var parsedChannelId = string.Empty; var lastUnderscore = channelIdString.LastIndexOf('_'); if (lastUnderscore != -1) { //Must be an older .dts file, so parse out the unique number var channelIdLen = channelIdString.Length - (lastUnderscore + 1); parsedChannelId = channelIdString.Substring(lastUnderscore + 1, channelIdLen); } else { parsedChannelId = channelIdString; } //the parse here would throw an exception with this dataset: // http://manuscript.dts.local/f/cases/45091/Add-ability-to-export-HDF-using-SLICEWare-data if (long.TryParse(parsedChannelId, out var l)) { return l; } return -1; } public IsoViewMode ISOViewMode { get; set; } private void BuildChannelListFromGroupChannels() { try { //FB 18875 cache sensor calibration //we don't need calibrations for ROI view! IReadOnlyDictionary cals = null; var channelList = new BindingList(); var roiChannelNames = new Dictionary>(); var roiChannelIds = new Dictionary>(); foreach (var roi in _regionsOfInterest) { roiChannelNames[roi] = new HashSet(); roiChannelIds[roi] = new HashSet(); foreach (var name in roi.ChannelNames) { roiChannelNames[roi].Add(name); } foreach (var channelId in roi.ChannelIds) { roiChannelIds[roi].Add(channelId); } } foreach (var ch in AllChannels ?? new ObservableCollection()) { if (ch.IsDisabled || ch.SensorId <= 0) { continue; } DTS.SensorDB.SensorData sd = null; if (null != ch.SensorData) { sd = ch.SensorData as DTS.SensorDB.SensorData; } if (null == sd) { if (null == cals) { cals = DTS.SensorDB.SensorsCollection.GetLatestCalibrations(); } sd = DTS.SensorDB.SensorsCollection.SensorsList.GetSensorById(ch.SensorId, true, cals); } if (null == sd) { DTS.Common.Utilities.Logging.APILogger.Log( "BuildChannelListFromGroupChannels: failed to find sensor: ", ch.SensorId); continue; } if (sd.IsDigitalOutput() || sd.IsTestSpecificDigitalOutput) { //FB14896: scrub digital outs from ROI DTS.Common.Utilities.Logging.APILogger.Log( "BuildChannelListFromGroupChannels: roi sensor is digital out: ", ch.SensorId); continue; } // FB16382: if sensor data missing from groupchannel, we already searched for it above so assign it while we're here if (null == ch.SensorData) { ch.SensorData = sd; } var chHardware = ch.SensorData.IsTestSpecificEmbedded ? DTS.Common.Classes.TestSetups.TestTemplateBase.GetEmbeddedChannelHardware(ch) : ch.Hardware; //If there is an EID, we want to strip off the leading "Assigned by ID" chHardware = RegionOfInterest.RemoveAssignedByIDFromHardwareString(chHardware); chHardware = RegionOfInterest.RemoveParentDASName(chHardware); var serialNumber = ChannelSerialNumber.SerialNumberFromChannel(sd.IsTestSpecificEmbedded || sd.IsTestSpecificThermo, ch.Sensor, sd.SerialNumber); var newEnabler = new ChannelEnabler { UserCode = ch.UserCode, UserChannelName = ch.UserChannelName, ISOCode = ch.IsoCode, ISOChannelName = ch.IsoChannelName, Hardware = ch.Hardware, SerialNumber = sd?.SerialNumber ?? "???", SensorName = sd?.Comment ?? "???", DASSerialNumber = ch.HardwareChannel?.GetParentDAS().SerialNumber ?? "N/A", SampleRate = ch.TestSampleRate == 0 ? "N/A" : ch.TestSampleRate.ToString(), Code = ch.GetChannelCode(ISOViewMode), DisplayUnits = sd.Calibration?.Records?.Records[0]?.EngineeringUnits ?? "N/A",//cal?.Records?.Records.First()?.EngineeringUnits ?? "N/A", GroupName = ch.GroupName, Descriptor = chHardware + "\\" + serialNumber, GuidString = Guid.NewGuid().ToString(), ChannelId = Convert.ToInt64(ch.Id) }; foreach (var roi in _regionsOfInterest) { if (null == roi.ChannelNames || null == roi.ChannelIds) { newEnabler.ROIIncludes.Add(new State(false)); } else { newEnabler.ROIIncludes.Add(new State(roiChannelIds[roi].Contains(newEnabler.ChannelId))); } } channelList.Add(newEnabler); } ChannelList = channelList; } catch (Exception ex) { DTS.Common.Utilities.Logging.APILogger.Log("Exception in BuildChannelListFromGroupChannels", ex); throw; } } public void SetTest(string path, IsoViewMode viewMode) { ISOViewMode = viewMode; _filterByField.Clear(); var tml = new DTS.Common.Classes.Viewer.TestMetadata.TestMetadataList(); var tsl = tml.GetTestSummaryList(this, path); foreach (var test in tsl) { Utils.SetChannelInfo(test.TestMetadata, path, DTS.Serialization.SliceRaw.File.PersistentChannel.GetIsoCode); test.Channels = test.TestMetadata.TestRun.Channels; test.Graphs = test.TestMetadata.TestSetup.TestGraphs; test.CalculatedChannels = test.TestMetadata.TestRun.CalculatedChannels; } _testSummary = tsl[0]; if (null == AllTestChannels) { AllTestChannels = new ObservableCollection(); } Filter(); ResetDataView(); } private void ProcessChannels(IGroupChannel[] allChannels, Dictionary hardwareLookup, IGroup[] groups, Dictionary groupLookup) { AllChannelsUnfiltered.Clear(); var channelLookup = new Dictionary(); foreach (var hl in hardwareLookup) { var channels = hl.Value.GetIHardwareChannels(); foreach (var ch in channels) { channelLookup[$"{hl.Value.DASId}_{ch.ChannelNumber}"] = ch; } } foreach (var ch in allChannels) { if (ch.IsDisabled || ch.SensorId < 0) { continue; } AllChannelsUnfiltered.Add((GroupChannel)ch); } Filter(); } public void Filter(string term) { SearchTerm = term.ToLower(); Filter(); } //sets the persistent filter to a given type and performs the filter public void SetFilter(PossibleFilters bridgeFilter) { BridgeFilter = bridgeFilter; Filter(); } public void Filter(object tag, string term) { if (Enum.TryParse((string)tag, out Fields field)) { _filterByField[field] = term; Filter(); } } private void Filter() { if (null != AllChannels) { AllChannels.Clear(); AllChannelsUnfiltered?.Sort(); //FB 18875 cache sensor calibration var latestCalibrations = DTS.SensorDB.SensorsCollection.GetLatestCalibrations(); foreach (var ch in AllChannelsUnfiltered) { if (ChannelFilter(ch, latestCalibrations) && ChannelSearch(ch)) { AllChannels.Add(ch); } } OnPropertyChanged("AllChannels"); BuildChannelListFromGroupChannels(); } else if (null != _testSummary) { AllTestChannels.Clear(); foreach (var ch in _testSummary.Channels) { if (ChannelFilter(ch) && ChannelSearch(ch)) { AllTestChannels.Add(ch); } } OnPropertyChanged("AllTestChannels"); BuildChannelListFromSummary(); } } public enum Fields { ChannelName, DisplayName, SerialNumber, SensorName, DASSerialNumber, SampleRate, ISOCode, DisplayUnits, GroupName, ROIIncludes } private bool ChannelFilter(GroupChannel ch, IReadOnlyDictionary calLookup = null) { var fields = Enum.GetValues(typeof(Fields)).Cast().ToArray(); //FB 18875 cache sensor calibration var sd = DTS.SensorDB.SensorsCollection.SensorsList.GetSensorById(ch.SensorId, true, calLookup); var channelName = ch.GetChannelName(ISOViewMode); foreach (var field in fields) { if (!_filterByField.ContainsKey(field)) { continue; } var term = _filterByField[field]; if (string.IsNullOrWhiteSpace(term)) { continue; } switch (field) { case Fields.ChannelName: if (!(System.Globalization.CultureInfo.CurrentCulture.CompareInfo.IndexOf(channelName, term, System.Globalization.CompareOptions.OrdinalIgnoreCase) >= 0)) { return false; } //@TODO break; case Fields.DisplayName: if (!(System.Globalization.CultureInfo.CurrentCulture.CompareInfo.IndexOf(channelName, term, System.Globalization.CompareOptions.OrdinalIgnoreCase) >= 0)) { return false; } //@TODO break; case Fields.SerialNumber: if (!(System.Globalization.CultureInfo.CurrentCulture.CompareInfo.IndexOf(sd.SerialNumber, term, System.Globalization.CompareOptions.OrdinalIgnoreCase) >= 0)) { return false; } break; case Fields.SensorName: if (!(System.Globalization.CultureInfo.CurrentCulture.CompareInfo.IndexOf(sd.Comment, term, System.Globalization.CompareOptions.OrdinalIgnoreCase) >= 0)) { return false; } break; case Fields.DASSerialNumber: if (!(System.Globalization.CultureInfo.CurrentCulture.CompareInfo.IndexOf(ch.HardwareChannel?.GetParentDAS().SerialNumber ?? "N/A", term, System.Globalization.CompareOptions.OrdinalIgnoreCase) >= 0)) { return false; } break; case Fields.SampleRate: if (!(System.Globalization.CultureInfo.CurrentCulture.CompareInfo.IndexOf(ch.TestSampleRate.ToString(), term, System.Globalization.CompareOptions.OrdinalIgnoreCase) >= 0)) { return false; } break; case Fields.ISOCode: if (!(System.Globalization.CultureInfo.CurrentCulture.CompareInfo.IndexOf(ch.IsoCode, term, System.Globalization.CompareOptions.OrdinalIgnoreCase) >= 0)) { return false; } break; case Fields.DisplayUnits: var cal = DTS.SensorDB.SensorCalibrationList.GetLatestCalibrationBySerialNumber(sd); if (!(System.Globalization.CultureInfo.CurrentCulture.CompareInfo.IndexOf(cal?.Records?.Records?.First()?.EngineeringUnits ?? string.Empty, term, System.Globalization.CompareOptions.OrdinalIgnoreCase) >= 0)) { return false; } break; case Fields.GroupName: if (!(System.Globalization.CultureInfo.CurrentCulture.CompareInfo.IndexOf(ch.GroupName, term, System.Globalization.CompareOptions.OrdinalIgnoreCase) >= 0)) { return false; } break; default: throw new ArgumentOutOfRangeException(); } } return true; } /// /// this method will return true as long as there's one field in the channel that contains the searchterm /// /// /// private bool ChannelSearch(ITestChannel ch) { if (string.IsNullOrWhiteSpace(SearchTerm)) { return true; } var fields = Enum.GetValues(typeof(Fields)).Cast().ToArray(); foreach (var field in fields) { switch (field) { case Fields.ChannelName: if (ch.ChannelName2.ToLower().Contains(SearchTerm)) { return true; } break; case Fields.DisplayName: if (ch.ChannelDisplayName.ToLower().Contains(SearchTerm)) { return true; } break; case Fields.SerialNumber: if (ch.SerialNumber.ToLower().Contains(SearchTerm)) { return true; } break; case Fields.SensorName: if (ch.Description.ToLower().Contains(SearchTerm)) { return true; } break; case Fields.DASSerialNumber: if (ch.HardwareChannelName.ToLower().Contains(SearchTerm)) { return true; } break; case Fields.SampleRate: { if (ch.SampleRateHz.ToString().Contains(SearchTerm)) { return true; } if (ch.SampleRateHz.ToString("N").Contains(SearchTerm)) { return true; } } break; case Fields.ISOCode: if (ch.IsoCode.ToLower().Contains(SearchTerm)) { return true; } break; case Fields.DisplayUnits: if (ch.Eu.ToLower().Contains(SearchTerm)) { return true; } break; case Fields.GroupName: if (ch.ChannelGroupName.ToLower().Contains(SearchTerm)) { return true; } break; case Fields.ROIIncludes: //I'm not sure it makes sense to search for this field, so just ignore it break; } } return false; } /// /// this method will return true as long as there's one field in the channel that contains the searchterm /// /// /// private bool ChannelSearch(IGroupChannel ch) { if (string.IsNullOrWhiteSpace(SearchTerm)) { return true; } var fields = Enum.GetValues(typeof(Fields)).Cast().ToArray(); var sd = DTS.SensorDB.SensorsCollection.SensorsList.GetSensorById(ch.SensorId); var channelName = ch.GetChannelName(ISOViewMode); foreach (var field in fields) { switch (field) { case Fields.ChannelName: if (channelName.ToLower().Contains(SearchTerm)) { return true; } break; case Fields.DisplayName: //in Filter this appears to use channelName again, this is redundant, so skip for now break; case Fields.SerialNumber: if (sd.SerialNumber.ToLower().Contains(SearchTerm)) { return true; } break; case Fields.SensorName: if (sd.Comment.ToLower().Contains(SearchTerm)) { return true; } break; case Fields.DASSerialNumber: { if (null == ch.HardwareChannel) { continue; } if (null == ch.HardwareChannel.GetParentDAS()) { continue; } if (ch.HardwareChannel.GetParentDAS().SerialNumber.ToLower().Contains(SearchTerm)) { return true; } } break; case Fields.SampleRate: { if (ch.TestSampleRate.ToString().Contains(SearchTerm)) { return true; } if (ch.TestSampleRate.ToString("N").Contains(SearchTerm)) { return true; } } break; case Fields.ISOCode: if (ch.IsoCode.ToLower().Contains(SearchTerm)) { return true; } break; case Fields.DisplayUnits: var cal = DTS.SensorDB.SensorCalibrationList.GetLatestCalibrationBySerialNumber(sd); if (null == cal) { continue; } if (null == cal.Records) { continue; } if (null == cal.Records.Records || 0 == cal.Records.Records.Length) { continue; } if (cal.Records.Records[0].EngineeringUnits.ToLower().Contains(SearchTerm)) { return true; } break; case Fields.GroupName: if (ch.GroupName.ToLower().Contains(SearchTerm)) { return true; } break; } } return false; } private bool ChannelFilter(ITestChannel ch) { var fields = Enum.GetValues(typeof(Fields)).Cast().ToArray(); foreach (var field in fields) { if (!_filterByField.ContainsKey(field)) { continue; } var term = _filterByField[field]; if (string.IsNullOrWhiteSpace(term)) { continue; } switch (field) { case Fields.ChannelName: if (!(System.Globalization.CultureInfo.CurrentCulture.CompareInfo.IndexOf(ch.ChannelName2, term, System.Globalization.CompareOptions.OrdinalIgnoreCase) >= 0)) { return false; } break; case Fields.DisplayName: if (!(System.Globalization.CultureInfo.CurrentCulture.CompareInfo.IndexOf(ch.ChannelDisplayName, term, System.Globalization.CompareOptions.OrdinalIgnoreCase) >= 0)) { return false; } break; case Fields.SerialNumber: if (!(System.Globalization.CultureInfo.CurrentCulture.CompareInfo.IndexOf(ch.SerialNumber, term, System.Globalization.CompareOptions.OrdinalIgnoreCase) >= 0)) { return false; } break; case Fields.SensorName: if (!(System.Globalization.CultureInfo.CurrentCulture.CompareInfo.IndexOf(ch.Description, term, System.Globalization.CompareOptions.OrdinalIgnoreCase) >= 0)) { return false; } break; case Fields.DASSerialNumber: if (!(System.Globalization.CultureInfo.CurrentCulture.CompareInfo.IndexOf(ch.HardwareChannelName, term, System.Globalization.CompareOptions.OrdinalIgnoreCase) >= 0)) { return false; } //fix this break; case Fields.SampleRate: if (!(System.Globalization.CultureInfo.CurrentCulture.CompareInfo.IndexOf(ch.SampleRateHz.ToString(), term, System.Globalization.CompareOptions.OrdinalIgnoreCase) >= 0)) { return false; } break; case Fields.ISOCode: if (!(System.Globalization.CultureInfo.CurrentCulture.CompareInfo.IndexOf(ch.IsoCode, term, System.Globalization.CompareOptions.OrdinalIgnoreCase) >= 0)) { return false; } break; case Fields.DisplayUnits: if (!(System.Globalization.CultureInfo.CurrentCulture.CompareInfo.IndexOf(ch.Eu, term, System.Globalization.CompareOptions.OrdinalIgnoreCase) >= 0)) { return false; } break; case Fields.GroupName: if (!(System.Globalization.CultureInfo.CurrentCulture.CompareInfo.IndexOf(ch.ChannelGroupName, term, System.Globalization.CompareOptions.OrdinalIgnoreCase) >= 0)) { return false; } break; default: throw new ArgumentOutOfRangeException(); } } return true; } public void ClearAllFilters() { _filterByField.Clear(); } private bool _sortAscending { get; set; } = true; private ChannelEnablerComparer _comparer = new ChannelEnablerComparer(); private Fields _sortField { get; set; } = Fields.GroupName; public void Sort(object o, bool bColumnClick) { if (!(o is string s)) { return; } if (s.Contains("[")) { if (int.TryParse(s.Split("[]".ToCharArray())[1], out var sortIndex)) { _comparer.SortFieldIndex = sortIndex; } s = s.Substring(0, s.IndexOf("[")); } if (!Enum.TryParse(s, out Fields field)) { return; } if (bColumnClick) { if (field != _sortField) { _sortField = field; _sortAscending = true; } else { _sortAscending = !_sortAscending; } } _comparer.SortAscending = _sortAscending; _comparer.SortField = _sortField; var channelList = ChannelList.ToList(); channelList.Sort(_comparer); if (ChannelList.SequenceEqual(channelList) && ChannelList.Count > 1) { //list was already in order. flip it for the user _sortAscending = !_sortAscending; _comparer.SortAscending = _sortAscending; channelList.Sort(_comparer); } ChannelList = new BindingList(channelList); //OnPropertyChanged("Channels"); } public void SelectAll(int roiIndex, bool selection) { if (roiIndex < 0 || roiIndex > RegionsOfInterest.Count - 1) { return; } foreach (var ch in ChannelList) { ch.ROIIncludes[roiIndex].Checked = selection; } } 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 void OnBusyIndicatorNotification(bool eventArg) { IsBusy = eventArg; } #region interfaces public void Activated() { } 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 event PropertyChangedEventHandler PropertyChanged; public void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } public bool Validate(ref List errors) { return true; } public void SetGroups(ITestSetup testSetup, Dictionary serialNumberToHardware, IsoViewMode viewMode) { ISOViewMode = viewMode; _filterByField.Clear(); var groupLookup = new Dictionary(); var allChannels = new List(); if (null == AllChannels) { AllChannels = new ObservableCollection(); } bool bHasPhantomDASAssignment = false; foreach (var group in testSetup.Groups) { var channels = testSetup.ChannelsForGroup[group]; allChannels.AddRange(channels); foreach (var ch in channels) { groupLookup[ch] = group; if (ch.DASId > 0) { if (ch.DASChannelIndex < 0) { bHasPhantomDASAssignment = true; continue; } var serialNumber = string.Empty; foreach (var h in serialNumberToHardware) { if (h.Value.DASId == ch.DASId) { serialNumber = h.Key; break; } } if (!string.IsNullOrWhiteSpace(serialNumber)) { ch.TestSampleRate = testSetup.DASSampleRateList[serialNumber]; } } else { ch.TestSampleRate = 0; } } } if (bHasPhantomDASAssignment) { _eventAggregator.GetEvent().Publish(new PageErrorArg(new[] { StringResources.PhantomDASChannelAssignment }, null)); } allChannels.Sort((a, b) => a.TestSetupOrder.CompareTo(b.TestSetupOrder)); ProcessChannels(allChannels.ToArray(), serialNumberToHardware, testSetup.Groups.ToArray(), groupLookup); ResetDataView(); } #endregion #endregion } public sealed class ChannelEnabler : INotifyPropertyChanged { public ChannelEnabler() { ROIIncludes = new BindingList(); ROIIncludes.ListChanged += RoiIncludesOnListChanged; } public int LastIndexChanged { get; set; } private void RoiIncludesOnListChanged(object sender, ListChangedEventArgs listChangedEventArgs) { LastIndexChanged = listChangedEventArgs.NewIndex; OnPropertyChanged("LastIndexChanged"); //OnPropertyChanged("ROIIncludes"/* + listChangedEventArgs.NewIndex.ToString()*/); } public string GetChannelName(IsoViewMode isoViewMode) { switch (isoViewMode) { case IsoViewMode.ISOOnly: return !string.IsNullOrWhiteSpace(ISOChannelName) ? ISOChannelName : SerialNumber; case IsoViewMode.ChannelNameOnly: case IsoViewMode.UserCodeOnly: return !string.IsNullOrWhiteSpace(UserChannelName) ? UserChannelName : SerialNumber; case IsoViewMode.ISOAndUserCode: default: if (string.IsNullOrWhiteSpace(ISOChannelName)) { return UserChannelName; } if (string.IsNullOrWhiteSpace(UserChannelName)) { return ISOChannelName; } if (string.IsNullOrWhiteSpace(ISOChannelName) && string.IsNullOrWhiteSpace(UserChannelName)) { return SerialNumber; } return $"{UserChannelName}\\{ISOChannelName}"; } } public BindingList ROIIncludes { get; private set; } public string UserCode { get; set; } public string UserChannelName { get; set; } public string ChannelName => UserChannelName; public string GuidString { get; set; } public long ChannelId { get; set; } public string ISOCode { get; set; } public string ISOChannelName { get; set; } public string Hardware { get; set; } public string SerialNumber { get; set; } public string SensorName { get; set; } public string DASSerialNumber { get; set; } public string SampleRate { get; set; } public string Code { get; set; } public string DisplayUnits { get; set; } public string GroupName { get; set; } internal string Descriptor { get; set; } public event PropertyChangedEventHandler PropertyChanged; public void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } sealed class ChannelEnablerComparer : IComparer { public RegionOfInterestChannelsViewModel.Fields SortField { get; set; } public bool SortAscending { get; set; } public int SortFieldIndex { get; set; } = -1; public int Compare(ChannelEnabler left, ChannelEnabler right) { if (left == right) { return 0; } var a = left; var b = right; if (!SortAscending) { a = right; b = left; } if (null == a) { return -1; } if (null == b) { return 1; } var aValue = a.GetType().GetProperty(SortField.ToString())?.GetValue(a, null); var bValue = b.GetType().GetProperty(SortField.ToString())?.GetValue(b, null); if (aValue is System.Collections.IList aList && SortFieldIndex >= 0 && SortFieldIndex < aList.Count) { aValue = aList[SortFieldIndex]; } if (bValue is System.Collections.IList bList && SortFieldIndex >= 0 && SortFieldIndex < bList.Count) { bValue = bList[SortFieldIndex]; } return string.Compare(aValue?.ToString(), bValue?.ToString(), StringComparison.InvariantCultureIgnoreCase); } } public sealed class State : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private bool _checked; public bool Checked { get => _checked; set { _checked = value; OnPropertyChanged(); } } public void Toggle() { Checked = !Checked; } public State(bool c) { Checked = c; } private void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } public override string ToString() { return Checked.ToString(); } } public sealed class ROIChannelEnabler : INotifyPropertyChanged { public ROIChannelEnabler(string suffix, string channel, bool enabled) { ROISuffix = suffix; ChannelName = channel; IsEnabled = enabled; } private string _roiSuffix = String.Empty; public string ROISuffix { get => _roiSuffix; set { _roiSuffix = value; OnPropertyChanged("ROISuffix"); } } private string _channelName = String.Empty; public string ChannelName { get => _channelName; set { _channelName = value; OnPropertyChanged("ChannelName"); } } private bool _isEnabled = false; public bool IsEnabled { get => _isEnabled; set { _isEnabled = value; OnPropertyChanged("IsEnabled"); } } public event PropertyChangedEventHandler PropertyChanged; public void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } public sealed class ColumnDescriptor { public string HeaderText { get; set; } public string DisplayMember { get; set; } public Type MemberType { get; set; } } }