using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Data; using System.ComponentModel; using System.Data.SqlClient; using System.Windows; namespace DatabaseImport { public class TestTemplate : TagAwareBase, IComparable, ICachedContainer { /// /// returns true whenever the test template is a quick test setup, false otherwise /// public bool QuickSensorCheck { get; set; } // #region var and const public const string NON_ISO_INTERNAL_GROUP_NAME = "[NONE]"; private const int MAX_REALTIME_CHANNELS = 6; private const int MAX_ERRORS_TO_DISPLAY = 5; internal List CalculatedChannels = new List(); private string _errorMessage = ""; /// /// dictionary of harware overrides for the test /// key is hardware id /// if present, then value is whether the hardware is included or removed from the test. /// internal Dictionary HardwareOverrides = new Dictionary(); /// /// cache of sensors for the test setup /// this keeps us from having to lookup sensors and apply changes every time we need to use them /// public Dictionary>> SensorLookup = new Dictionary>>(); public string TestSetupXml { get; set; } private readonly Dictionary _dasSettings = new Dictionary(); public DASSettings[] DASSettings { get => _dasSettings.Count < 1 ? new DASSettings[0] : _dasSettings.Values.ToArray(); set { _dasSettings.Clear(); foreach (var setting in value) { _dasSettings[setting.DASSerialNumber] = setting; } } } /// /// whether to upload data after data collection is completed /// this lets you separate out the time consumptive network data tasks from the ones you want to keep fast (export/view) /// private bool _uploadData; public bool UploadData { get => _uploadData; set => SetProperty(ref _uploadData, value, TestTemplateTags.UploadData.ToString()); } /// /// whether to upload data after data collection is completed /// this lets you separate out the time consumptive network data tasks from the ones you want to keep fast (export/view) /// private bool _uploadExportsOnly; public bool UploadExportsOnly { get => _uploadExportsOnly; set => SetProperty(ref _uploadExportsOnly, value, TestTemplateTags.UploadExportsOnly.ToString()); } /// /// folder to upload to /// private string _uploadFolder = ""; public string UploadFolder { get => _uploadFolder; set => SetProperty(ref _uploadFolder, value, TestTemplateTags.UploadFolder.ToString()); } /// /// whether all units in the test share a common line /// this should be set to false if DAS in the test don't share a common line /// the default is true /// if units share a common status line and there are multiple units in the test, then each unit will monitor the status line /// if false or there's only one unit, the status line should not be monitored /// private bool _bCommonLine; public bool CommonLine { get => _bCommonLine; set => SetProperty(ref _bCommonLine, value, TestTemplateTags.CommonLine.ToString()); } /// /// true if the test objects and other fields have already been loaded from the database, false otherwise /// internal bool _bIsLoaded; public bool IsLoaded => _bIsLoaded; /// /// when true the expectation is that any unit that can have a battery will have a battery, and the user should be warned /// whenever a unit that should have a battery does not have an acceptable battery voltage /// private bool _warnOnFailedBattery; public bool WarnOnFailedBattery { get => _warnOnFailedBattery; set => SetProperty(ref _warnOnFailedBattery, value, TestTemplateTags.WarnOnFailedBattery.ToString()); } public string CompletionErrorMessage => _testTemplateLite.CompletionErrorMessage; /// /// returns whether the test setup is dirty or not (whether the is complete is calculated already or not) /// private bool _bIsDirty = true; public bool IsDirty { get => _bIsDirty; set => _bIsDirty = value; } private List _testGraphs = new List(); private TestTemplateLite _testTemplateLite; public string Name { get => _testTemplateLite.Name; set => _testTemplateLite.Name = value; } private bool _bAllowMissingSensors; public bool AllowMissingSensors { get => _bAllowMissingSensors; set => SetProperty(ref _bAllowMissingSensors, value, TestTemplateTags.AllowMissingSensors.ToString()); } private bool _bAllowSensorIdToBlankChannel; public bool AllowSensorIdToBlankChannel { get => _bAllowSensorIdToBlankChannel; set => SetProperty(ref _bAllowSensorIdToBlankChannel, value, TestTemplateTags.AllowSensorIdToBlankChannel.ToString()); } private bool _bAutomaticProgression; public bool AutomaticProgression { get => _bAutomaticProgression; set => SetProperty(ref _bAutomaticProgression, value, TestTemplateTags.AutomaticProgression.ToString()); } private int _automaticProgressionDelayMs; public int AutomaticProgressionDelayMS { get => _automaticProgressionDelayMs; set => SetProperty(ref _automaticProgressionDelayMs, value, TestTemplateTags.AutomaticProgressionDelayMS.ToString()); } private bool _invertTriggerCompletion; public bool InvertTriggerCompletion { get => _invertTriggerCompletion; set => SetProperty(ref _invertTriggerCompletion, value, TestTemplateTags.InvertTriggerCompletion.ToString()); } private bool _triggerCheckStep; public bool TriggerCheckStep { get => _triggerCheckStep; set => SetProperty(ref _triggerCheckStep, value, TestTemplateTags.TriggerCheckStep.ToString()); } private int _postTestDiagnosticsLevel; public bool PostTestDiagnosticsLevel { get => _postTestDiagnosticsLevel != 0; set { if (value) { SetProperty(ref _postTestDiagnosticsLevel, 255, TestTemplateTags.PostTestDiagnostics.ToString()); } else { SetProperty(ref _postTestDiagnosticsLevel, 0, TestTemplateTags.PostTestDiagnostics.ToString()); } } } private bool _triggerCheckRealtime; public bool TriggerCheckRealtime { get => _triggerCheckRealtime; set => SetProperty(ref _triggerCheckRealtime, value, TestTemplateTags.TriggerCheckRealtime.ToString()); } private bool _invertStartRecordCompletion; public bool InvertStartRecordCompletion { get => _invertStartRecordCompletion; set => SetProperty(ref _invertStartRecordCompletion, value, TestTemplateTags.InvertStartRecordCompletion.ToString()); } private bool _bViewDiagnostics = true; public bool ViewDiagnostics { get => _bViewDiagnostics; set => SetProperty(ref _bViewDiagnostics, value, TestTemplateTags.ViewDiagnostics.ToString()); } private bool _bVerify = true; public bool VerifyChannels { get => _bVerify; set => SetProperty(ref _bVerify, value, TestTemplateTags.VerifyChannels.ToString()); } private bool _bAutoVerifyChannels = true; public bool AutoVerifyChannels { get => _bAutoVerifyChannels; set => SetProperty(ref _bAutoVerifyChannels, value, TestTemplateTags.AutoVerifyProgress.ToString()); } private double _autoVerifyDelay = 2D; public double AutoVerifyDelaySeconds { get => _autoVerifyDelay; set => SetProperty(ref _autoVerifyDelay, value, TestTemplateTags.AutoVerifyDelaySeconds.ToString()); } private bool _bUseCustomerDetails; public bool UseCustomerDetails { get => _bUseCustomerDetails; set => SetProperty(ref _bUseCustomerDetails, value, TestTemplateTags.UseCustomerDetails.ToString()); } private bool _bUseTestEngineerDetails; public bool UseTestEngineerDetails { get => _bUseTestEngineerDetails; set => SetProperty(ref _bUseTestEngineerDetails, value, TestTemplateTags.UseTestEngineerDetails.ToString()); } private bool _bTurnOffExcitation = true; public bool TurnOffExcitation { get => _bTurnOffExcitation; set => SetProperty(ref _bTurnOffExcitation, value, TestTemplateTags.TurnOffExcitation.ToString()); } private bool _bUseLabratoryDetails; public bool UseLabratoryDetails { get => _bUseLabratoryDetails; set => SetProperty(ref _bUseLabratoryDetails, value, TestTemplateTags.UseLabratoryDetails.ToString()); } private bool _localOnly; public bool LocalOnly { get => _localOnly; set => SetProperty(ref _localOnly, value, TestTemplateTags.LocalOnly.ToString()); } public DateTime LastModified { get => _testTemplateLite.LastModified; set { _testTemplateLite.LastModified = value; OnPropertyChanged(TestTemplateTags.LastModified.ToString()); } } public string LastModifiedBy { get => _testTemplateLite.LastModifiedBy; set { _testTemplateLite.LastModifiedBy = value; OnPropertyChanged(TestTemplateTags.LastMmodifiedBy.ToString()); } } public string Description { get => _testTemplateLite.Description; set { _testTemplateLite.Description = value; OnPropertyChanged(TestTemplateTags.Description.ToString()); } } private double _samplesPerSecond; public double SamplesPerSecond { get => _samplesPerSecond; set { SetProperty(ref _samplesPerSecond, value, TestTemplateTags.SamplesPerSecond.ToString()); OnPropertyChanged(TestTemplateTags.SampleRateText.ToString()); } } public double PreTriggerSeconds { get => _testTemplateLite.PreTriggerSeconds; set { _testTemplateLite.PreTriggerSeconds = value; foreach (var roi in RegionsOfInterest) { roi.PreTrigger = PreTriggerSeconds * -1D; } OnPropertyChanged(TestTemplateTags.PreTriggerSeconds.ToString()); } } public double PostTriggerSeconds { get => _testTemplateLite.PostTriggerSeconds; set { _testTemplateLite.PostTriggerSeconds = value; foreach (var roi in RegionsOfInterest) { roi.PostTrigger = PostTriggerSeconds; } OnPropertyChanged(TestTemplateTags.PostTriggerSeconds.ToString()); } } private string _isfFile = ""; public string ISFFile { get => _isfFile; set => SetProperty(ref _isfFile, value, TestTemplateTags.ISFFile.ToString()); } private bool _doAutoArm; public bool DoAutoArm { get => _doAutoArm; set => SetProperty(ref _doAutoArm, value, TestTemplateTags.DoAutoArm.ToString()); } /// /// CheckoutMode means a non destructive data collection /// squib fires will only occur internally /// private bool _checkoutMode; public bool CheckoutMode { get => _checkoutMode; set => SetProperty(ref _checkoutMode, value, TestTemplateTags.CheckoutMode.ToString()); } /// /// QuitTestWithoutWarning flag will suppress warnings on exit /// see: 7509 add option to Quit Test Without Warning /// private bool _quitTestWithoutWarning; public bool QuitTestWithoutWarning { get => _quitTestWithoutWarning; set => SetProperty(ref _quitTestWithoutWarning, value, TestTemplateTags.QuitTestWithoutWarning.ToString()); } /// /// SuppressMissingSensorsWarning flag will suppress missing sensors warnings /// see: 8877 Suppress modal warning in checkout mode for missing sensors. /// private bool _suppressMissingSensorsWarning; public bool SuppressMissingSensorsWarning { get => _suppressMissingSensorsWarning; set => SetProperty(ref _suppressMissingSensorsWarning, value, TestTemplateTags.SuppressMissingSensorsWarning.ToString()); } /// /// suppress warnings on exit /// see: 7642 add two test setup option to suppress "not all channels have been viewed" warnings in both real-time and viewer. /// private bool _notAllChannelsRealTime; public bool NotAllChannelsRealTime { get => _notAllChannelsRealTime; set => SetProperty(ref _notAllChannelsRealTime, value, TestTemplateTags.NotAllChannelsRealTime.ToString()); } /// /// suppress warnings on exit /// see: 7642 add two test setup option to suppress "not all channels have been viewed" warnings in both real-time and viewer. /// private bool _notAllChannelsViewer; public bool NotAllChannelsViewer { get => _notAllChannelsViewer; set => SetProperty(ref _notAllChannelsViewer, value, TestTemplateTags.NotAllChannelsViewer.ToString()); } public RecordingModes RecordingMode { get => _testTemplateLite.RecordingMode; set { if (value != _testTemplateLite.RecordingMode) { _testTemplateLite.RecordingMode = value; OnPropertyChanged(TestTemplateTags.RecordingMode.ToString()); OnPropertyChanged(TestTemplateTags.RecordingModeText.ToString()); } } } private bool _bStrictDiagnostics; public bool StrictDiagnostics { get => _bStrictDiagnostics; set => SetProperty(ref _bStrictDiagnostics, value, TestTemplateTags.StrictDiagnostics.ToString()); } public TestTestObject[] TestObjectsWithChannels { get { var list = new List(_testObjects); for (var i = list.Count - 1; i >= 0; i--) { var isoTestObject = list[i].GetISOTestObject(); if (0 == isoTestObject.AllChannels.Length) { list.RemoveAt(i); } } return list.ToArray(); } } public List TestObjectsAndAddedGroupsList = new List(); // public TestObject[] TestObjectsAndAddedGroups => TestObjectsAndAddedGroupsList.ToArray(); private List _testObjects = new List(); public TestTestObject[] TestObjects { get => _testObjects.ToArray(); } private List _addedGroups = new List(); public TestTestObject[] AddedGroups { get => _addedGroups.ToArray(); set { //MarkIsCompleteUnchecked(); Removed to fix FB 10241 with the assumption that simply Setting AddedGroups should not cause a database write. SetProperty(ref _addedGroups, new List(value), TestTemplateTags.SysBuiltTestObjectTypes.ToString()); OnPropertyChanged(TestTemplateTags.AllTestObjects.ToString());//check this OnPropertyChanged("TestObjectsAndAddedGroups"); OnPropertyChanged(TestTemplateTags.TestObjectsAndAddedGroups.ToString()); } } private bool _bRequireUserConfirmationOnErrors; public bool RequireUserConfirmationOnErrors { get => _bRequireUserConfirmationOnErrors; set => SetProperty(ref _bRequireUserConfirmationOnErrors, value, TestTemplateTags.RequireUserConfirmationOnErrors.ToString()); } private bool _roiDownload; public bool DoROIDownload { get => _roiDownload; set { SetProperty(ref _roiDownload, value, TestTemplateTags.DoROIDownload.ToString()); OnPropertyChanged(TestTemplateTags.ROIButtonVisibility.ToString()); } } private bool _roiDownloadView; public bool ViewROIDownload { get => _roiDownloadView; set { SetProperty(ref _roiDownloadView, value, TestTemplateTags.ViewROIDownload.ToString()); OnPropertyChanged(TestTemplateTags.ViewROIDownloadButtonVisibility.ToString()); } } private bool _downloadAll; public bool DownloadAll { get => _downloadAll; set { SetProperty(ref _downloadAll, value, TestTemplateTags.DownloadAll.ToString()); OnPropertyChanged(TestTemplateTags.DownloadAllButtonVisibility.ToString()); } } private bool _viewRealtime; public bool ViewRealtime { get => 0 != GetNumberOfRealtimeSupportedChannels() && _viewRealtime; set { SetProperty(ref _viewRealtime, value, TestTemplateTags.ViewRealtime.ToString()); OnPropertyChanged(TestTemplateTags.ViewRealtime.ToString()); } } private BindingList _regionsOfInterest = new BindingList(); public BindingList RegionsOfInterest { get => _regionsOfInterest; set { if (null != value) { _regionsOfInterest.ListChanged -= _roi_ListChanged; foreach (var roi in value) { roi.PreTrigger = PreTriggerSeconds * -1D; roi.PostTrigger = PostTriggerSeconds; } SetProperty(ref _regionsOfInterest, value, TestTemplateTags.RegionsOfInterest.ToString()); _regionsOfInterest.ListChanged += _roi_ListChanged; } } } private double _roiStart; public double ROIStart { get => _roiStart; set => SetProperty(ref _roiStart, value, TestTemplateTags.ROIStart.ToString()); } private double _roiEnd; public double ROIEnd { get => _roiEnd; set => SetProperty(ref _roiEnd, value, TestTemplateTags.ROIEnd.ToString()); } private bool _viewDownloadAll; public bool ViewDownloadAll { get => _viewDownloadAll; set { SetProperty(ref _viewDownloadAll, value, TestTemplateTags.ViewDownloadAll.ToString()); OnPropertyChanged(TestTemplateTags.ViewDownloadAllButtonVisibility.ToString()); } } private bool _viewExport; public bool ViewExport { get => _viewExport; set { SetProperty(ref _viewExport, value, TestTemplateTags.ViewExport.ToString()); OnPropertyChanged(TestTemplateTags.ViewExportButtonVisibility.ToString()); } } private SupportedExportFormatBitFlags _exportFormats; public SupportedExportFormatBitFlags ExportFormats { get => _exportFormats; set => SetProperty(ref _exportFormats, value, TestTemplateTags.ExportFormats.ToString()); } private string _downloadFolder = Properties.Settings.Default.DownloadFolder; public string DownloadFolder { get => _downloadFolder; set => SetProperty(ref _downloadFolder, value, TestTemplateTags.DownloadFolder.ToString()); } private string _exportFolder = Properties.Settings.Default.DownloadFolder; public string ExportFolder { get => _exportFolder; set => SetProperty(ref _exportFolder, value, TestTemplateTags.ExportFolder.ToString()); } private bool _sameAsDownloadFolder = true; public bool SameAsDownloadFolder { get => _sameAsDownloadFolder; set => SetProperty(ref _sameAsDownloadFolder, value, TestTemplateTags.SameAsDownloadFolder.ToString()); } private LabratoryDetails _labDetails; public LabratoryDetails LabDetails { get => _labDetails; set => SetProperty(ref _labDetails, value, TestTemplateTags.LabDetails.ToString()); } private CustomerDetails _customerDetails; public CustomerDetails CustomerDetails { get => _customerDetails; set => SetProperty(ref _customerDetails, value, TestTemplateTags.CustomerDetails.ToString()); } private TestEngineerDetails _testEngineerDetails; public TestEngineerDetails TestEngineerDetails { get => _testEngineerDetails; set => SetProperty(ref _testEngineerDetails, value, TestTemplateTags.TestEngineerDetails.ToString()); } private int _defaultNumberRealtimeGraphs; public int DefaultNumberRealtimeGraphs { get { switch (_defaultNumberRealtimeGraphs) { case 6: return 6; case 3: return 3; case 1: default: return 1; } } set { switch (value) { case 6: _defaultNumberRealtimeGraphs = value; break; case 3: _defaultNumberRealtimeGraphs = value; break; case 1: default: _defaultNumberRealtimeGraphs = value; break; } OnPropertyChanged(TestTemplateTags.DefaultNumberRealtimeGraphs.ToString()); } } /// /// this is a brave new world, this is the hardware for this test. /// now groups in this test setup may have hardware assigned, or no hardware assigned /// we'll take that if we find it, but then we'll also check a list we have for the test itself and intersect the two /// we then modify the groups IN MEMORY and change their hardware to match all hardware officially in the test /// /// public void ApplyHardwareOverrides() { //for now don't do anything if the test setup is non iso var hardwareInTest = new Dictionary(); foreach (var group in TestObjects) { foreach (var h in group.Hardware) { var hiso = h.GetHardware(); if (!hardwareInTest.ContainsKey(hiso.GetId())) { hardwareInTest.Add(hiso.GetId(), h); } } } foreach (var group in AddedGroups) { foreach (var h in group.Hardware) { var hiso = h.GetHardware(); if (!hardwareInTest.ContainsKey(hiso.GetId())) { hardwareInTest.Add(hiso.GetId(), h); } } } using (var e = HardwareOverrides.GetEnumerator()) { while (e.MoveNext()) { switch (e.Current.Value.Action) { case HardwareInclusionInstruction.Actions.Add: if (!hardwareInTest.ContainsKey(e.Current.Value.HardwareId)) { DASHardware h = null; try { h = DASHardwareList.GetList().GetHardware(e.Current.Value.HardwareId); } catch (DASHardwareList.HardwareTypeChangedException) { //APILogger.Log("test: " + Name + " refers to hardware: " + // e.Current.Value.HardwareId + " which has changed types. " + // ex.Message); } if (null != h) { hardwareInTest.Add(h.GetHardware().GetId(), h); } } break; case HardwareInclusionInstruction.Actions.Remove: if (hardwareInTest.ContainsKey(e.Current.Value.HardwareId)) { hardwareInTest.Remove(e.Current.Value.HardwareId); } break; } } } GetNonISOTestObject(); foreach (var group in TestObjects) { var hardware = new List(); using (var e2 = hardwareInTest.GetEnumerator()) { while (e2.MoveNext()) { var newH = new DASHardware(e2.Current.Value.GetHardware()); newH.SetTimeStampMemory(e2.Current.Value.GetTimeStampMemory()); hardware.Add(newH); } } group.SetHardware(hardware.ToArray()); } foreach (var ag in AddedGroups) { var hardware = new List(); using (var e2 = hardwareInTest.GetEnumerator()) { while (e2.MoveNext()) { var newH = new DASHardware(e2.Current.Value.GetHardware()); newH.SetTimeStampMemory(e2.Current.Value.GetTimeStampMemory()); hardware.Add(newH); } } ag.SetHardware(hardware.ToArray()); } } internal Dictionary LevelTriggerChannels = new Dictionary(); public string GetLTKey(LevelTriggerChannel ch) { return $"{ch.HardwareChannelId}x{ch.SensorSerialNumber}"; } public void SetLevelTrigger(LevelTriggerChannel channel) { var key = GetLTKey(channel); LevelTriggerChannels[key] = new LevelTriggerChannel(channel);//do a copy just for safety } /// /// sets a channel to be disabled /// disabled channels don't appear in run test /// /// /// /// public void SetDisabled(string groupName, string channelName, bool disabled) { var matches = from g in TestObjects where g.SerialNumber == groupName select g; if (!matches.Any()) return; var iso = matches.First().GetISOTestObject(); var allChannels = iso.AllChannels; foreach (var ch in allChannels) { if (ch.Name == channelName) { ch.Disabled = disabled; break; } } } public SensorData GetSensor(string serialNumber, string testObjectSerial, string channelName) { lock (SensorLock) { var tto = GetTestTestObject(testObjectSerial); if (!SensorLookup.ContainsKey(testObjectSerial) || !SensorLookup[testObjectSerial].ContainsKey(channelName) || !SensorLookup[testObjectSerial][channelName].ContainsKey(serialNumber)) { var matches = from s in _sensorsFromBinary where s.SerialNumber == serialNumber select s; if (matches.Any()) { var s = matches.First(); var position = tto.Position.Position; if (position != TestTestObject.UserSetKey && position != TestTestObject.ChannelDefaultsKey && position != s.Position) { s.Position = position; } return s; } } if (SensorLookup.ContainsKey(testObjectSerial)) { if (SensorLookup[testObjectSerial].ContainsKey(channelName)) { if (SensorLookup[testObjectSerial][channelName].ContainsKey(serialNumber)) { if (SensorLookup[testObjectSerial][channelName][serialNumber].FilterClassIso == "?") { SensorLookup[testObjectSerial][channelName][serialNumber].FilterClassIso = "P"; // "Prefiltered > CFC 1000" (Unfiltered) } //for some reason the Filter isn't always set, setting filterclassiso will ensure it gets set, even though it should have already _been_ set SensorLookup[testObjectSerial][channelName][serialNumber].FilterClassIso = SensorLookup[testObjectSerial][channelName][serialNumber].FilterClassIso; if (null == tto) { return SensorLookup[testObjectSerial][channelName][serialNumber]; } var position = tto.Position.Position; if (position != TestTestObject.UserSetKey && position != TestTestObject.ChannelDefaultsKey && position != SensorLookup[testObjectSerial][channelName][serialNumber].Position) { SensorLookup[testObjectSerial][channelName][serialNumber].Position = position; } return SensorLookup[testObjectSerial][channelName][serialNumber]; } } } if (null == tto) { return SensorData.IsTestSpecificDigitalOutSN(serialNumber) ? new DigitalOutputSetting { SerialNumber = serialNumber } : SensorsCollection.SensorsList.GetSensorBySerialNumber(serialNumber); } var ttosd = tto.GetSensor(channelName, serialNumber); if (ttosd != null) { ttosd.TestObject = tto.TestObject.Test_Object; var position = tto.Position.Position; if (position != TestTestObject.UserSetKey && position != TestTestObject.ChannelDefaultsKey && position != ttosd.Position) { ttosd.Position = position; } } else { return SensorData.IsTestSpecificDigitalOutSN(serialNumber) ? new DigitalOutputSetting { SerialNumber = serialNumber } : null; } return ttosd; } } public void SetSensor(SensorData sd, string testobjectserial, string channelname) { SetSensor(sd, testobjectserial, channelname, false); } private static readonly object SensorLock = new object(); /// /// sets the sensor (and custom values like range/CFC/polarity) for a channel in the test /// /// /// /// /// public void SetSensor(SensorData sd, string testobjectserial, string channelname, bool bSetPositionFromSensor) { lock (SensorLock) { if (!SensorLookup.ContainsKey(testobjectserial)) { SensorLookup.Add(testobjectserial, new Dictionary>()); } if (!SensorLookup[testobjectserial].ContainsKey(channelname)) { SensorLookup[testobjectserial].Add(channelname, new Dictionary()); } //Build out the ISO fields we know about // FB 5423 var tto = GetTestTestObject(testobjectserial) ?? new TestTestObject(new TestObject()); sd.TestObject = tto.TestObject.Test_Object; // ReSharper disable once InconsistentNaming var isoTO = tto.GetISOTestObject(); var channel = isoTO.GetChannel(channelname); if (bSetPositionFromSensor) { if (tto.Position.Position != sd.Position && tto.Position.Position != TestTestObject.UserSetKey) { tto.SetPosition(TestTestObject.UserSetKey); } } else { if ((tto.Position.Position != TestTestObject.ChannelDefaultsKey) && //Remove this? (tto.Position.Position != TestTestObject.UserSetKey)) { sd.Position = tto.Position.Position; } } if (null != channel) { sd.FineLocation1 = channel.Channel.Fine_Loc_1; sd.FineLocation2 = channel.Channel.Fine_Loc_2; sd.FineLocation3 = channel.Channel.Fine_Loc_3; sd.MainLocation = channel.Channel.Trans_Main_Loc; sd.PhysicalDimension = channel.Channel.Physical_Dimension; sd.Direction = channel.Channel.Direction; } SensorLookup[testobjectserial][channelname][sd.SerialNumber] = sd; } } /// /// sets the dirty flag /// does NOT set the flag in the database, so this should only be called when the flag needs to be set in memory /// (for instance during initialization) /// /// public void SetIsDirty(bool bDirty) { _bIsDirty = bDirty; } /// /// sets the complete flag /// does not set the flag in the database, so this should only be called when the flag needs to be set in memory /// (for instance during initialization) /// /// public void SetIsComplete(bool bComplete) { _testTemplateLite.IsComplete = bComplete; } /// /// sets the completion error message. /// does not set the field in the db, so this should only be called when the information nets to be set in memory and not the db /// (for instance during initialization) /// /// public void SetCompletionErrorMessage(string msg) { _errorMessage = msg; } public int CompareTo(TestTemplate right) { if (null == right) { return 1; } var r = string.Compare(Name, right.Name, StringComparison.Ordinal); return 0 == r ? LastModified.CompareTo(right.LastModified) : r; } public TestTestObject GetTestTestObject(string testObjectSerial) { foreach (var tto in TestObjects) { if (tto.SerialNumber == testObjectSerial) { return tto; } } return Array.Find(AddedGroups, addedGroup => addedGroup.SerialNumber == testObjectSerial); } /// /// a list of hardware as read from the XML/binary /// this is used as the record of hardware that was present when test was committed /// private List _hardwareFromBinary = new List(); /// /// a list of sensors as read from the XML/binary /// this is used as the record of hardware that was present when test was committed /// private List _sensorsFromBinary = new List(); /// /// a list of sensor calibrations as read from the XML/binary /// this is used as the record of calibrations that was present when test was committed /// private List _calibrationsFromBinary = new List(); public IISOHardware[] GetAllCachedHardware() { var list = from h in _hardwareFromBinary select h.GetHardware(); if (list.Any()) { return list.ToArray(); } return new IISOHardware[0]; } public DASHardware GetCachedHardware(string serialNumber) { var matches = from h in _hardwareFromBinary where h.SerialNumber == serialNumber select h; if (matches.Any()) { return matches.First(); } return null; } // #region Validate public static bool ValidateTestSetupName(ref List errors, TestTemplate CurrentTestSetup) { if (string.IsNullOrWhiteSpace(CurrentTestSetup.Name)) { errors.Add("EditTestSetup_NameRequired"); return false; } if (CurrentTestSetup.Name == "QuickSensorCheck_DefaultTestName" && !CurrentTestSetup.QuickSensorCheck) { errors.Add("QuickSensorCheck_DefaultTestNameInvalid"); return false; } if (DiskUtility.ValidateFileAndPathNameChars(CurrentTestSetup.Name)) { var nameToCheck = CurrentTestSetup.Name.ToLower(); if (nameToCheck.Equals("iso") || nameToCheck.Equals("csv") || nameToCheck.Equals("realtime")) { errors.Add("EditTestSetup_NameCannotBeISOCSVOrRealtime"); return false; } var encoding = Encoding.GetEncoding("UTF-8"); var nameInBytes = encoding.GetBytes(nameToCheck); if (nameInBytes.Length == nameToCheck.Length) return true; errors.Add("EditTestSetup_NameCannotContainDoubleByteCharacters"); return false; } errors.Add("EditTestSetup_NameContainsInvalidCharacters"); //fatal return false; } public static bool Validate(ref List errors, ref List warnings, bool displayWindow, TestTemplate CurrentTestSetup, bool bValid, StrictLevel strictness, bool bWarnOnNotFullyAssigned, bool bRefreshHardware = true) { if (!Validate(ref errors, ref warnings, displayWindow, CurrentTestSetup, bValid, strictness, true, bWarnOnNotFullyAssigned, bRefreshHardware)) { CurrentTestSetup.SetIsComplete(false); return false; } if (errors.Any()) { CurrentTestSetup.SetIsComplete(false); return false; } CurrentTestSetup.SetIsComplete(true); return true; } private SensorCalibration GetSensorCalibration(SensorData sd, ExcitationVoltageOptions.ExcitationVoltageOption preferredExcitation) { var matches = from sc in _calibrationsFromBinary where sc.SerialNumber == sd.SerialNumber select sc; if (matches.Any()) { SensorCalibration sc = null; foreach (var cal in matches) { if (cal.IsProportional) { var bIsOk = Array.Exists(cal.Records.Records, record => record.Excitation == preferredExcitation); if (!bIsOk) { continue; } } if (null == sc) { sc = cal; } else if (sc.CalibrationDate < cal.CalibrationDate) { sc = cal; } else if (sc.CalibrationDate == cal.CalibrationDate && sc.ModifyDate < cal.ModifyDate) { sc = cal; } } if (null != sc) { return sc; } } return SensorCalibration.GetLatestCalibrationBySerialNumberAndExcitation(sd, preferredExcitation); } public static bool Validate(ref List errors, ref List warnings, bool displayWindow, TestTemplate CurrentTestSetup, bool bValid, StrictLevel strictness, bool markIsCompleteUnchecked, bool bWarnOnNotFullyAssigned, bool bRefreshHardware) { //I'm not sure of all the entry points into this function //it seems like there are many //however there are definite times we _don't_ want to just blindly change the iscompleteunchecked flag //just because we want to do some validation on the test plan ... //for now I just fixed the ones I know shouldn't be changing the flag blindly //and preserved current behavior for the rest ... //if (markIsCompleteUnchecked) { CurrentTestSetup.MarkIsCompleteUnchecked(); } var bHasHardware = false; var bHasChannels = false; var testobjects = new List(); testobjects.AddRange(CurrentTestSetup.TestObjects); testobjects.AddRange(CurrentTestSetup.AddedGroups); var serialNumbersProcessed = new Dictionary(); var hardwareChannelIdsProcessed = new Dictionary(); var channelLookup = new Dictionary(); var usedISOCodesLookup = new Dictionary(); var availableAnalogChannels = 0; var availableDigitalInputChannels = 0; var availableDigitalOutputChannels = 0; var availableSquibChannels = 0; if (testobjects.Any()) { //this is using the assumption that all testobjects now have the same list of hardware foreach (var lastTo in testobjects) { if (bRefreshHardware) { lastTo.RefreshHardware(); } foreach (var h in lastTo.Hardware) { if (null == h) { continue; } var hType = h.GetHardwareTypeEnum(); if (hType == HardwareTypes.SLICE_EthernetController || hType == HardwareTypes.SLICE_Distributor || hType == HardwareTypes.SLICE_LabEthernet || hType == HardwareTypes.SLICE6DB) { continue; } bHasHardware = true; if (CurrentTestSetup.DoAutoArm) { switch (hType) { case HardwareTypes.TDAS_Pro_Rack: case HardwareTypes.TDAS_LabRack: case HardwareTypes.G5INDUMMY: case HardwareTypes.G5VDS: errors.Add("EditTestSetupPage_HardwareDoesNotSupportAutoArm"); break; } } for (var i = 0; i < h.Channels.Length; i++) { var ch = h.Channels[i]; if (channelLookup.ContainsKey(ch.GetId())) continue; channelLookup.Add(ch.GetId(), ch); if (ch.IsSupportedBridgeType(Test.Module.Channel.Sensor.BridgeType.SQUIB)) { availableSquibChannels++; i++; /*bypass next channel as it's also a squib*/ } else if (ch.IsSupportedBridgeType(Test.Module.Channel.Sensor.BridgeType.DigitalInput)) { availableDigitalInputChannels++; } else if (ch.IsSupportedBridgeType(Test.Module.Channel.Sensor.BridgeType.TOMDigital)) { availableDigitalOutputChannels++; } else { availableAnalogChannels++; } } } } if (!bHasHardware && bWarnOnNotFullyAssigned) { errors.Add("EditTestSetupPage_NoHardwareInTest"); } } else { errors.Add("EditTestSetupPage_NoGroupsInTest"); } foreach (var to in testobjects) { var isoTo = to.GetISOTestObject(); foreach (var ch in isoTo.AllChannels) { if (!ch.Required) { continue; } bHasChannels = true; if (string.IsNullOrWhiteSpace(ch.SensorSerialNumber)) { if (!errors.Contains("EditTestSetupPage_NoSensorOnChannel") && bWarnOnNotFullyAssigned) { errors.Add("EditTestSetupPage_NoSensorOnChannel"); } } else { if (serialNumbersProcessed.ContainsKey(ch.SensorSerialNumber)) { var err = string.Format("EditTestSetupPage_SerialNumberAppearsTwice", ch.SensorSerialNumber); if (!errors.Contains(err)) { errors.Add(err); } return false; } serialNumbersProcessed.Add(ch.SensorSerialNumber, true); var sd = CurrentTestSetup.GetSensor(ch.SensorSerialNumber, to.SerialNumber, ch.Name); if (null == sd) { var err = string.Format("EditTestSetupPage_CouldNotFindSensor", ch.SensorSerialNumber, ch.Name, to.DisplaySerialNumber); if (!errors.Contains(err)) { errors.Add(err); } } else { if (sd.IsSquib()) { availableSquibChannels--; if (sd.SquibFireDelayMS + sd.SquibFireDurationMS > CurrentTestSetup.PostTriggerSeconds * 1000) { errors.Add(string.Format( "TestTemplate_InsufficientPostTriggerLength", sd.SerialNumber, ch.Name)); } } else if (sd.IsDigitalInput()) { availableDigitalInputChannels--; } else if (sd.IsDigitalOutput()) { availableDigitalOutputChannels--; if (sd.DigitalOutputDelayMS + sd.DigitalOutputDurationMS > CurrentTestSetup.PostTriggerSeconds * 1000) { errors.Add(string.Format( "TestTemplate_InsufficientPostTriggerLength", sd.SerialNumber, ch.Name)); } } else { availableAnalogChannels--; } var sensor = SensorsCollection.SensorsList.GetSensorBySerialNumber(ch.SensorSerialNumber); if (sensor != null) { var sensorDimension = sensor.PhysicalDimension; if ((((App)Application.Current).IsoDb.GetPhysicalDimensionByIso(sensorDimension) != null) && (((App)Application.Current).IsoDb.GetPhysicalDimensionByIso(ch.Channel .Physical_Dimension) != null) && (sd.IncompatibleSensorAssignment(sensorDimension, ch.Channel.Physical_Dimension))) { var err = string.Format( "EditTestSetupPage_IncompatibleSensorNotSupported", ((App)Application.Current).IsoDb.GetPhysicalDimensionByIso(sensorDimension) .Text_L1, ch.SensorSerialNumber, ((App)Application.Current).IsoDb .GetPhysicalDimensionByIso(ch.Channel.Physical_Dimension).Text_L1, ch.Name, to.DisplaySerialNumber); switch (SerializedSettings.IsoChannelSensorCompatibilityLevel) { case IsoChannelSensorCompatibilityLevels.DontAllow: if (!errors.Contains(err)) { errors.Add(err); } bValid = false; break; case IsoChannelSensorCompatibilityLevels.Warn: if (!warnings.Contains( "EditObjectSensorsControl_Warning" + ": " + err)) { warnings.Add("EditObjectSensorsControl_Warning" + ": " + err); } break; } } } if (!sd.IsDigitalOutput() && (CurrentTestSetup.GetISOTestSetupISOSupportLevel() == SerializedSettings.ISOSupportLevels.ISO_ONLY)) { if (sd.MainLocation.Contains('?') || sd.FineLocation1.Contains('?') || sd.FineLocation2.Contains('?') || sd.FineLocation3.Contains('?') || sd.Direction.Contains('?') || sd.PhysicalDimension.Contains('?')) { var isoto = to.GetISOTestObject(); foreach (var isotoChannel in isoto.AllChannels) { //reapplying channel iso settings here with the exception of filter class, position, and test object if (ch.Name != isotoChannel.Name) continue; sd.MainLocation = ch.Channel.Trans_Main_Loc; sd.FineLocation1 = ch.Channel.Fine_Loc_1; sd.FineLocation2 = ch.Channel.Fine_Loc_2; sd.FineLocation3 = ch.Channel.Fine_Loc_3; sd.Direction = ch.Channel.Direction; sd.PhysicalDimension = ch.Channel.Physical_Dimension; } } if (usedISOCodesLookup.ContainsKey(sd.ISOCode)) { var err = string.Format("TestTemplate_DuplicateISOCode", sd.ISOCode); if (!errors.Contains(err)) { errors.Add(err); } } else { usedISOCodesLookup[sd.ISOCode] = true; } } SensorCalibration sc = null; var preferredExcitation = sd.SupportedExcitation[0]; sc = CurrentTestSetup.GetSensorCalibration(sd, preferredExcitation); //sc = SensorCalibration.GetLatestCalibrationBySerialNumberAndExcitation(sd, preferredExcitation); //se); if (null == sc) { bValid = false; var err = string.Format("EditTestSEtupPage_MissingCalibration", ch.Name, sd.SerialNumber); if (!errors.Contains(err)) { errors.Add(err); } } else { var now = DateTime.Now; var days = sc.CalibrationDate.AddDays(sd.CalInterval).Subtract(now).TotalDays; if (SerializedSettings.TestSetupDefaultDontAllowOutOfCalSensors && ((days < 0) || (days < Properties.Settings.Default.CalWarningPeriodDays))) { bValid = false; var err = string.Format( "OverdueSensors_CalibrationDateOverdueOrNear", ch.Name, sd.SerialNumber); if (!errors.Contains(err)) { errors.Add(err); } } } if (string.IsNullOrWhiteSpace(ch.HardwareId)) continue; if (!channelLookup.ContainsKey(ch.HardwareId)) { var err = string.Format("EditTestSetupPage_InvalidHardware", ch.Name, to.DisplaySerialNumber); if (!errors.Contains(err)) { errors.Add(err); } } else { if (hardwareChannelIdsProcessed.ContainsKey(ch.HardwareId)) { bValid = false; var err = string.Format( "EditTestSetupPage_HardwareAlreadyInUse", ch.Name, to.DisplaySerialNumber); if (!errors.Contains(err)) { errors.Add(err); } } else { hardwareChannelIdsProcessed.Add(ch.HardwareId, true); var hch = channelLookup[ch.HardwareId]; if (sd.ByPassFilter && ((hch.Hardware.GetHardwareTypeEnum() != HardwareTypes.TDAS_Pro_Rack) && (hch.Hardware.GetHardwareTypeEnum() != HardwareTypes.TDAS_LabRack))) { var err = string.Format( "EditTestSetupPage_BypassAAFilterNotSupported", ch.Name, to.DisplaySerialNumber); if (!errors.Contains(err)) { errors.Add(err); } } else { if (hch.IsSupportedBridgeType(sd.Bridge)) continue; var err = string.Format( "EditTestSetupPage_BridgeNotSupported", sd.Bridge.ToString(), ch.Name, to.DisplaySerialNumber); if (!errors.Contains(err)) { errors.Add(err); } } } } } } } } if (!bHasChannels) { errors.Add("EditTestSetupPage_NoChannelsInTest"); } if (availableAnalogChannels < 0 && !CurrentTestSetup.AllowMissingSensors && bWarnOnNotFullyAssigned) { if (!errors.Contains("EditTestSetupPage_NeedMoreAnalogChannels") && !errors.Contains("EditTestSetupPage_NoHardwareInTest")) { errors.Add("EditTestSetupPage_NeedMoreAnalogChannels"); } } if (availableDigitalInputChannels < 0 && !CurrentTestSetup.AllowMissingSensors && bWarnOnNotFullyAssigned) { if (!errors.Contains("EditTestSetupPage_NeedMoreDigitalInputChannels") && !errors.Contains("EditTestSetupPage_NoHardwareInTest")) { errors.Add("EditTestSetupPage_NeedMoreDigitalInputChannels"); } } if (availableDigitalOutputChannels < 0 && !CurrentTestSetup.AllowMissingSensors && bWarnOnNotFullyAssigned) { if (!errors.Contains("EditTestSetupPage_NeedMoreDigitalOutputChannels") && !errors.Contains("EditTestSetupPage_NoHardwareInTest")) { errors.Add("EditTestSetupPage_NeedMoreDigitalOutputChannels"); } } if (availableSquibChannels < 0 && !CurrentTestSetup.AllowMissingSensors && bWarnOnNotFullyAssigned) { if (!errors.Contains("EditTestSetupPage_NeedMoreSquibChannels") && !errors.Contains("EditTestSetupPage_NoHardwareInTest")) { errors.Add("EditTestSetupPage_NeedMoreSquibChannels"); } } if (!ValidateTestSetupName(ref errors, CurrentTestSetup)) { bValid = false; } ValidateStorageSpace(ref errors, CurrentTestSetup);//not fatal ValidateUploadFolder(ref errors, CurrentTestSetup);//not fatal ValidateAutoArmAllowability(ref errors, CurrentTestSetup); return bValid; } public static bool ValidateUploadFolder(ref List errors, TestTemplate currentTest) { //if we have a export ini file, we still allow a blank upload folder if (!currentTest.UploadData || !string.IsNullOrWhiteSpace(currentTest.UploadFolder)) return true; if (SerializedSettings.ExportINIFile.Length > 0) { return true; } errors.Add("TestTestTemplate_InvalidUploadPath"); return false; } private static bool ValidateAutoArmAllowability(ref List errors, TestTemplate currentTest) { if (!currentTest.DoAutoArm || Properties.Settings.Default.AllowAutoArm) return true; errors.Add("EditTestSetupInfo_AutoArmNotAllowed"); return false; } public static bool ValidateStorageSpace(ref List errors, TestTemplate currentTest) { var bStorageSpaceValid = true; var preTriggerSeconds = currentTest.PreTriggerSeconds; var postTriggerSeconds = currentTest.PostTriggerSeconds; var sampleRate = currentTest.SamplesPerSecond; var secondsToRecord = preTriggerSeconds + postTriggerSeconds; var sampleClocksToRecord = (ulong)(secondsToRecord * sampleRate + 1); var das = new List(); foreach (var to in currentTest.TestObjects) { foreach (var h in to.Hardware) { if (!das.Contains(h)) { das.Add(h); } } } foreach (var to in currentTest.AddedGroups) { foreach (var h in to.Hardware) { if (!das.Contains(h)) { das.Add(h); } } } foreach (var d in das) { double maxSampleClockTicks = d.GetMaxMemoryLong(); if (!(maxSampleClockTicks > 0)) continue; if (!(sampleClocksToRecord > maxSampleClockTicks)) continue; bStorageSpaceValid = false; errors.Add(string.Format("TestTemplate_InsufficientMemory", d.SerialNumber, maxSampleClockTicks)); } return bStorageSpaceValid; } private void _roi_ListChanged(object sender, ListChangedEventArgs e) { OnPropertyChanged(TestTemplateTags.RegionsOfInterest.ToString()); } public void AddTestGraph(TestGraph tg) { _testGraphs.Add(tg); } /// /// holds test settings that don't have individual db columns /// //private ISO.TestSettingDictionary _settings = CreateSettingsDictionary(); private TestSettingDictionary _settings; /// /// loads values from a serialized string into the settings table /// /// public void LoadSettings(string s) { _settings.LoadSettings(s); } /// /// returns the iso support level of object (as determined by it's groups, or the current application support level) /// /// public SerializedSettings.ISOSupportLevels GetISOTestSetupISOSupportLevel() { var nonIsoTestObject = GetNonISOTestObject(); foreach (var to in TestObjects) { if (to == nonIsoTestObject) { //if it only has digital outputs, it could be either a non iso or an iso group, so ignore it var isoto = to.GetISOTestObject(); if (isoto.AllChannels.Where(ch => ch.Required && !string.IsNullOrWhiteSpace(ch.SensorSerialNumber)).Any(ch => !SensorData.IsTestSpecificDigitalOutSN(ch.SensorSerialNumber))) { return SerializedSettings.ISOSupportLevels.NO_ISO; } } else { return to.GetObjectISOLevel(); } } if (AddedGroups.Any()) { return SerializedSettings.ISOSupportLevels.ISO_ONLY; } return SerializedSettings.ISOSupportLevel; } private TestTestObject CreateNonISOGroupIfNecessary() { foreach (var group in _testObjects) { if (group.SerialNumberOrOriginalSerialNumber == NON_ISO_INTERNAL_GROUP_NAME) { return group; } } //if you got here it wasn't found var guid = Guid.NewGuid(); var template = new TestObjectTemplate(); var isoTestObjectTemplate = template.ToISOTestObjectTemplate(); isoTestObjectTemplate.Embedded = true; isoTestObjectTemplate.TemplateName = guid.ToString(); if (Application.Current is App app) { var db = app.IsoDb; template = new TestObjectTemplate(isoTestObjectTemplate, ref db); } var to = new TestObject { Template = template }; to.GetISOTestObject().Embedded = true; to.GetISOTestObject().OriginalSerialNumber = NON_ISO_INTERNAL_GROUP_NAME; to.SerialNumber = guid.ToString(); var g = new TestTestObject(to); _testObjects.Add(g); TestObjectsAndAddedGroupsList.Add(g); return g; } public TestTestObject GetNonISOTestObject() { return CreateNonISOGroupIfNecessary(); } public void AddTestObject(TestObject to, int excitationWarmupMs, double targetSampleRate, string testobject, string position) { if (null != to) { var tto = new TestTestObject(to) { ExcitationWarmupTimeMS = excitationWarmupMs, TargetSampleRate = targetSampleRate }; tto.SetPosition(position); tto.SetTestObject(testobject); //if this is the NON ISO group (which is created by default), the replace the original NONE group ... if (tto.SerialNumberOrOriginalSerialNumber == NON_ISO_INTERNAL_GROUP_NAME) { var index = -1; for (var i = 0; i < TestObjectsAndAddedGroupsList.Count; i++) { if (TestObjectsAndAddedGroupsList[i].SerialNumberOrOriginalSerialNumber != NON_ISO_INTERNAL_GROUP_NAME) continue; index = i; break; } if (index >= 0) { TestObjectsAndAddedGroupsList.RemoveAt(index); } index = -1; for (var i = 0; i < _testObjects.Count; i++) { if (_testObjects[i].SerialNumberOrOriginalSerialNumber != NON_ISO_INTERNAL_GROUP_NAME) continue; index = i; break; } if (index >= 0) { _testObjects.RemoveAt(index); } } _testObjects.Add(tto); TestObjectsAndAddedGroupsList.Add(tto); OnPropertyChanged(TestTemplateTags.TestObjects.ToString()); OnPropertyChanged(TestTemplateTags.DefaultNumberRealtimeGraphs.ToString()); } } public void AddTestObject(string serialNumber, int excitationWarmupMs, double targetSampleRate, string testobject, string position) { var to = TestObjectList.TestObjectsList.GetTestObject(serialNumber); AddTestObject(to, excitationWarmupMs, targetSampleRate, testobject, position); OnPropertyChanged(TestTemplateTags.DefaultNumberRealtimeGraphs.ToString()); } public void AddAddedGroup(TestObject to, string testobject, string position) { if (null == to) return; var tto = new TestTestObject(to); tto.SetPosition(position); tto.SetTestObject(testobject); tto.SerialNumberConverted = tto.SerialNumber.Remove(0, Name.Length + 1); tto.TestSetupName = Name; _addedGroups.Add(tto); TestObjectsAndAddedGroupsList.Add(tto); OnPropertyChanged(TestTemplateTags.DefaultNumberRealtimeGraphs.ToString()); } public void AddAddedGroup(string serialNumber, string testobject, string position) { var to = TestObjectList.AddedGroupsList.GetAddedGroup(serialNumber); AddAddedGroup(to, testobject, position); OnPropertyChanged(TestTemplateTags.DefaultNumberRealtimeGraphs.ToString()); } public int GetNumberOfRealtimeSupportedChannels() { var count = 0; foreach (var to in TestObjects) { var isoto = to.GetISOTestObject(); count += isoto.AllChannels.Where(ch => ch.Required && !string.IsNullOrWhiteSpace(ch.SensorSerialNumber)).Select(ch => GetSensor(ch.SensorSerialNumber, to.SerialNumber, ch.Name)).Where(sd => null != sd).Count(sd => !sd.IsDigitalInput() && !sd.IsDigitalOutput() && !sd.IsSquib()); } foreach (var to in AddedGroups) { var isoto = to.GetISOTestObject(); count += isoto.AllChannels.Where(ch => ch.Required && !string.IsNullOrWhiteSpace(ch.SensorSerialNumber)).Select(ch => GetSensor(ch.SensorSerialNumber, to.SerialNumber, ch.Name)).Where(sd => null != sd).Count(sd => !sd.IsDigitalInput() && !sd.IsDigitalOutput() && !sd.IsSquib()); } if (0 == count) { count = MAX_REALTIME_CHANNELS; } return count; } public void AddCalculatedChannel(CalculatedValueClass ch) { CalculatedChannels.Add(ch); } private readonly Dictionary _metaDataLookup = new Dictionary(); private TestSetupMetaData _testSetupMeta = new TestSetupMetaData(Properties.Settings.Default.RequireXCrashCompatibilityForISOExports); public void SetTestSetupMetaData(TestSetupMetaData meta) { _testSetupMeta = meta; } public void SetMetaData(char testobject, TestObjectMetaData meta) { _metaDataLookup[testobject] = meta; } } }