Files
DP44/DataPRO/Modules/RegionOfInterest/RegionOfInterestChannels/ViewModel/RegionOfInterestChannelsViewModel.cs
2026-04-17 14:55:32 -04:00

1345 lines
59 KiB
C#

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
{
/// <summary>
/// Creates a new instance of the HardwareListViewModel
/// </summary>
/// <param name="view"></param>
/// <param name="regionManager">The logical placeholder defined within the application's UI (in the shell or within views) into which views are displayed.</param>
/// <param name="eventAggregator">The EventAggregator which allows different components to publish/subscribe to events without being coupled to each other.</param>
/// <param name="unityContainer">The unityContainer.</param>
public RegionOfInterestChannelsViewModel(IRegionOfInterestChannelsView view,Prism.Regions.IRegionManager regionManager,
Prism.Events.IEventAggregator eventAggregator, Unity.IUnityContainer unityContainer)
{
View = view;
View.DataContext = this;
NotificationRequest = new InteractionRequest<Notification>();
ConfirmationRequest = new InteractionRequest<Confirmation>();
_eventAggregator = eventAggregator;
_regionManager = regionManager;
_eventAggregator.GetEvent<RaiseNotification>().Subscribe(OnRaiseNotification);
_eventAggregator.GetEvent<BusyIndicatorChangeNotification>()
.Subscribe(OnBusyIndicatorNotification, Prism.Events.ThreadOption.PublisherThread, true);
RegionsOfInterest = new BindingList<IRegionOfInterest>();
}
#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<Fields, string> _filterByField = new Dictionary<Fields, string>();
private Prism.Events.IEventAggregator _eventAggregator { get; }
private Prism.Regions.IRegionManager _regionManager;
public InteractionRequest<Notification> NotificationRequest { get; }
public InteractionRequest<Confirmation> ConfirmationRequest { get; }
private ITestSummary _testSummary;
private BindingList<IRegionOfInterest> _regionsOfInterest;
public BindingList<IRegionOfInterest> 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<string>();
var channelIdList = new List<long>();
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<string>();
var idsNotFound = new List<long>();
var allChannelHash = new HashSet<string>();
var allChannelIdHash = new HashSet<long>();
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();
}
}
/// <summary>
/// If there is one and only one entry in the list with a given serial number
/// update it with different hardware.
/// </summary>
/// <param name="serialNumber"></param>
/// <param name="hashName"></param>
/// <param name="channelList"></param>
private void UpdateChannelList(string serialNumber, string hashName, List<string> 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;
/// <Summary>
/// holds a term (should be in lower case) to filter in/search for
/// </Summary>
private string SearchTerm { get; set; } = string.Empty;
public List<DTS.Common.Classes.Groups.GroupChannel> AllChannelsUnfiltered { get; set; } = new List<DTS.Common.Classes.Groups.GroupChannel>();
/// <summary>
/// this is all the channels displayed in the UI (but not any channels filtered out)
/// </summary>
public ObservableCollection<GroupChannel> AllChannels { get; set; }
private ObservableCollection<ITestChannel> AllTestChannels { get; set; }
public string[] AllChannelSSNs
{
get
{
var ssns = new List<string>();
//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<ChannelEnabler> _channelList = new BindingList<ChannelEnabler>();
public BindingList<ChannelEnabler> ChannelList
{
get => _channelList;
set
{
if (null != _channelList)
{
_channelList.ListChanged -= CheckboxesOnListChanged;
}
_channelList = value;
if (null != _channelList)
{
_channelList.ListChanged += CheckboxesOnListChanged;
}
OnPropertyChanged("ChannelList");
}
}
private ObservableCollection<ColumnDescriptor> _columns;
public ObservableCollection<ColumnDescriptor> 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<ChannelEnabler>)sender)[chChangedIndex].LastIndexChanged;
var checkedIds = new List<long>();
var checkedGuidStringList = new List<string>();
foreach (var channel in (BindingList<ChannelEnabler>)sender)
{
if (channel.ROIIncludes[roiChangedIndex].Checked)
{
checkedIds.Add(channel.ChannelId);
checkedGuidStringList.Add(channel.GuidString);
}
}
var checkedGuidStringIdDict = new Dictionary<string, long>();
var channelNameList = new List<string>();
var checkedChannelIdList = new List<long>();
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<ChannelEnabler>)sender).Where(ch => ch.GuidString == guidStringIdPair.Key).Select(ch => ch.Descriptor).FirstOrDefault<string>();
}
else
{
channelName = ((BindingList<ChannelEnabler>)sender).Where(ch => ch.ChannelId == guidStringIdPair.Value).Select(ch => ch.Descriptor).FirstOrDefault<string>();
}
channelNameList.Add(channelName);
}
_regionsOfInterest[roiChangedIndex].ChannelNames = channelNameList.ToArray();
_regionsOfInterest[roiChangedIndex].ChannelIds = checkedChannelIdList.ToArray();
_eventAggregator.GetEvent<RegionOfInterestChannelsSelectedEvent>()
.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<ColumnDescriptor>();
//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<ChannelEnabler>();
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;
}
/// <summary>
/// 44068 Older .dts files have a ChannelId consisting of: <Guid or channel name>_<number>_<unique number>,
/// 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.
/// </summary>
/// <param name="channelIdString"></param>
/// <returns></returns>
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<string, DTS.SensorDB.SensorCalibration> cals = null;
var channelList = new BindingList<ChannelEnabler>();
var roiChannelNames = new Dictionary<IRegionOfInterest, HashSet<string>>();
var roiChannelIds = new Dictionary<IRegionOfInterest, HashSet<long>>();
foreach (var roi in _regionsOfInterest)
{
roiChannelNames[roi] = new HashSet<string>();
roiChannelIds[roi] = new HashSet<long>();
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<GroupChannel>())
{
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<ITestChannel>();
}
Filter();
ResetDataView();
}
private void ProcessChannels(IGroupChannel[] allChannels, Dictionary<string, IDASHardware> hardwareLookup, IGroup[] groups, Dictionary<IGroupChannel, IGroup> groupLookup)
{
AllChannelsUnfiltered.Clear();
var channelLookup = new Dictionary<string, IHardwareChannel>();
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<string, DTS.SensorDB.SensorCalibration> calLookup = null)
{
var fields = Enum.GetValues(typeof(Fields)).Cast<Fields>().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;
}
/// <summary>
/// this method will return true as long as there's one field in the channel that contains the searchterm
/// </summary>
/// <param name="ch"></param>
/// <returns></returns>
private bool ChannelSearch(ITestChannel ch)
{
if (string.IsNullOrWhiteSpace(SearchTerm)) { return true; }
var fields = Enum.GetValues(typeof(Fields)).Cast<Fields>().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;
}
/// <summary>
/// this method will return true as long as there's one field in the channel that contains the searchterm
/// </summary>
/// <param name="ch"></param>
/// <returns></returns>
private bool ChannelSearch(IGroupChannel ch)
{
if (string.IsNullOrWhiteSpace(SearchTerm)) { return true; }
var fields = Enum.GetValues(typeof(Fields)).Cast<Fields>().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<Fields>().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<ChannelEnabler>(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<string> errors)
{
return true;
}
public void SetGroups(ITestSetup testSetup, Dictionary<string, IDASHardware> serialNumberToHardware,
IsoViewMode viewMode)
{
ISOViewMode = viewMode;
_filterByField.Clear();
var groupLookup = new Dictionary<IGroupChannel, IGroup>();
var allChannels = new List<IGroupChannel>();
if (null == AllChannels)
{
AllChannels = new ObservableCollection<GroupChannel>();
}
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<PageErrorEvent>().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<State>();
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<State> 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<ChannelEnabler>
{
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; }
}
}