//FB 32926 Old location of this file is \DataPRO\DataPRO\DataModel\Classes\TestTemplate\TestTemplate.cs using DataPROWin7.Common; using DataPROWin7.DataModel.Classes.Hardware; using DataPROWin7.DataModel.Classes.TestTemplate; using DTS.Common; using DTS.Common.Classes.CustomerDetails; using DTS.Common.Classes.Groups; using DTS.Common.Classes.Groups.ChannelSettings; using DTS.Common.Classes.Hardware; using DTS.Common.Classes.LabratoryDetails; using DTS.Common.Classes.Sensors; using DTS.Common.Classes.TestEngineerDetails; using DTS.Common.Classes.TestSetups; using DTS.Common.Converters; using DTS.Common.Enums; using DTS.Common.Enums.DASFactory; using DTS.Common.Enums.Hardware; using DTS.Common.Enums.Sensors; using DTS.Common.Enums.TestSetups; using DTS.Common.Events; using DTS.Common.Interface.Channels; using DTS.Common.Interface.DASFactory; using DTS.Common.Interface.DASFactory.Diagnostics; using DTS.Common.Interface.DataRecorders; using DTS.Common.Interface.Groups; using DTS.Common.Interface.Groups.GroupList; using DTS.Common.Interface.ISO.ExtraProperties; using DTS.Common.Interface.RegionOfInterest; using DTS.Common.Interface.Sensors; using DTS.Common.Interface.TestSetups; using DTS.Common.Interface.TestSetups.TestSetupsList; using DTS.Common.ISO; using DTS.Common.Storage; using DTS.Common.Utilities; using DTS.Common.Utilities.Logging; using DTS.Common.Utils; using DTS.SensorDB; using DTS.Slice.Users.UserSettings; using ISO; using Prism.Ioc; using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Data.SqlClient; using System.Globalization; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Windows; using IGroupChannel = DTS.Common.Interface.Channels.IGroupChannel; using DTS.Common.SharedResource.Strings; using DTS.Common.DataModel; using DTS.Common.Licensing; using DTS.Common.DataModel.Common.DbWrappers; using DTS.Common.DataModel.Common; using DTS.Common.Classes.DSP; using Prism.Events; using Unity; using DTS.Common.Classes.Sensors.StreamOut; using DTS.Common.Constant.DASSpecific; namespace DataPROWin7.DataModel { public class TestTemplate : TestTemplateBase, IComparable, ICachedContainer, ITestTemplate { /// /// returns true if device is a S6A or S6A-BR /// /// /// private static bool IsASlice6AirType(HardwareTypes hType) { return hType == HardwareTypes.SLICE6_AIR || hType == HardwareTypes.SLICE6_AIR_BR; } /// /// returns true if the test setup has meaningful pre trigger time /// public bool HasPreTrigger { get { if (RecordingMode == RecordingModes.Active && StartWithEvent) { return false; } if (DoStreaming) { return false; } return RecordingModeExtensions.IsACircularBufferMode(RecordingMode); } } /// /// returns true if level triggers are disabled because of the recording mode for test /// http://manuscript.dts.local/f/cases/edit/26963/Incorrect-TSR-AIR-Arm-step-displays-when-Start-recording-with-event-line-is-checked /// public bool LevelTriggersDisabled { get { if (RecordingMode == RecordingModes.Scheduled) { return true; } if (RecordingMode == RecordingModes.Interval) { return true; } if (StartWithEvent) { return true; } return false; } } /// /// adjusts the channels available in test for any TSR AIR based on recording rate /// public void FixTSRAIRChannelsForSpeed(DASHardware h, bool forceChanges = false) { if (null == h) { return; } if (!h.IsTSRAIR()) { return; } var rate = GetSampleRateForHardware(h); if (rate <= DFConstantsAndEnums.TSR_AIR_HIGH_G_CUTOFF_RATE_SPS) { RemoveHighGChannels(h); } else { AddHighGChannels(h, forceChanges); } } private readonly List previousHiGChannels = new List(); /// /// removes any High g channels for hardware from test (if present) /// private void RemoveHighGChannels(DASHardware h) { var channels = GetChannels(); var channelsToRemove = new List(); foreach (var ch in channels) { if (ch.DASId != h.DASId) { continue; } if (null == ch.HardwareChannel) { continue; } if (SensorConstants.IsTSRAirHighGChannel(ch.HardwareChannel.ModuleSerialNumber)) { //30144 3) High G channels should not appear in the Channel List table after changing // the Sample Rate from <= 500 to > 500 if they were not part of the Test Setup previously. previousHiGChannels.Add(ch.HardwareChannel.ToString()); channelsToRemove.Add(ch); } } if (channelsToRemove.Any()) { var group = channelsToRemove.Select(ch => ch.Group).FirstOrDefault(); var groupChannels = ChannelsForGroup[group].ToList(); foreach (var ch in channelsToRemove) { groupChannels.Remove(ch); } ChannelsForGroup[group] = groupChannels.ToArray(); } var allChannels = GetChannels(); } /// /// adds High g channels for hardware to test (if not present) /// private void AddHighGChannels(DASHardware h, bool forceChanges = false) { var channels = GetChannels(); var dontNeedToAdd = channels.Exists(ch => ch.DASId == h.DASId && null != ch.HardwareChannel && SensorConstants.IsTSRAirHighGChannel(ch.HardwareChannel.ModuleSerialNumber)); if (dontNeedToAdd) { return; } var group = channels.Find(ch => ch.DASId == h.DASId).Group; var channelsForGroup = ChannelsForGroup[group].ToList(); var insertPoint = 3; var lowGForDAS = from ch in channelsForGroup where ch.DASId == h.DASId && null != ch.HardwareChannel && SensorConstants.IsTSRAirLowGChannel(ch.HardwareChannel.ModuleSerialNumber) select ch; if (null != lowGForDAS && lowGForDAS.Any()) { insertPoint = 1 + lowGForDAS.Max(ch => channelsForGroup.IndexOf(ch)); } var defaults = DbOperations.GetChannelSettingDefaults(); var analog = DTS.SensorDB.SensorsCollection.SensorsList.GetSensorBySerialNumber(SensorConstants.TEST_SPECIFIC_ANALOG_SERIAL); foreach (var ch in h.Channels) { //30144 3) High G channels should not appear in the Channel List table after changing // the Sample Rate from <= 500 to > 500 if they were not part of the Test Setup previously. if (SensorConstants.IsTSRAirHighGChannel(ch.ModuleSerialNumber) && (previousHiGChannels.Contains(ch.ToString()) || forceChanges)) { channelsForGroup.Insert(insertPoint, GroupChannel.CreateTSRAirChannel(ch, group, analog, defaults)); previousHiGChannels.Remove(ch.ToString()); insertPoint++; } } var start = channelsForGroup[0].TestSetupOrder; for (var i = 1; i < channelsForGroup.Count; i++) { channelsForGroup[i].TestSetupOrder = ++start; } ChannelsForGroup[group] = channelsForGroup.ToArray(); } /// /// stores a mapping of group channel objects for a given calculated channel, this lets us then go and update the ids /// for that calculated channel once we have ids for the channelsa /// private Tuple[] _calculatedChannelMappings = new Tuple[0]; /// /// updates the mapping of a calculated channel to group channels that make up the input channels, this is so the information can be used /// to update the input channel ids for the calculated channel during commit time /// 28099 Exporting test setup does not export calculated channels /// we set these mappings just prior to committing /// /// public void AddCalculatedChannelMappings(Tuple[] mappings) { _calculatedChannelMappings = mappings; } /// /// removes any mappings of calculated channels to group channels that make up the input channels /// we do this just for safety and to reduce the amount of items we are holding onto /// we remove the mappings after we are finished commiting a test setup /// public void ClearCalculatedChannelMappings() { _calculatedChannelMappings = new Tuple[0]; } /// /// fixes the input channel ids for any calculated channels in the test setup using a list of group channels for /// each calculated channel /// 28099 Exporting test setup does not export calculated channels /// public void UpdateCalculatedChannelInputIds() { if (null == _calculatedChannelMappings || 0 == _calculatedChannelMappings.Length) { return; } foreach (var cc in CalculatedChannels) { var match = _calculatedChannelMappings.FirstOrDefault(map => map.Item1 == cc); if (null == match) { continue; } var channelIds = new List(); foreach (var channel in match.Item2) { channelIds.Add(channel.Id.ToString()); } cc.InputChannelIds = channelIds.ToArray(); } } /// /// removes the ids for this test template creating a copy without the id associations /// public void CreateCopy() { //13565 All channels removed from test (created by using copy) after I modified a sensor //it was using the same group and channels for both test setups //we have to force it to create new test setups. UpdateFromDb(); Id = -1; Name = ""; foreach (var group in Groups) { group.Id = -1; group.Name = Guid.NewGuid().ToString(); } var channels = GetChannels(); var oldChannelIdToChannel = new Dictionary(); var channelIndex = -1; foreach (var ch in channels) { oldChannelIdToChannel[ch.Id] = ch; ch.Id = channelIndex--; } foreach (var cc in CalculatedChannels) { var inputChannels = new List(); foreach (var sid in cc.InputChannelIds) { if (long.TryParse(sid, out var l)) { if (oldChannelIdToChannel.ContainsKey(l)) { inputChannels.Add(oldChannelIdToChannel[l]); } } } cc.InputChannelIds = new string[0]; cc.SetChannels(inputChannels.ToArray()); } using (var enumLevelTriggers = LevelTriggerChannels.GetEnumerator()) { while (enumLevelTriggers.MoveNext()) { var lt = enumLevelTriggers.Current.Value; if (long.TryParse(lt.GroupChannelId, out var l)) { if (oldChannelIdToChannel.ContainsKey(l)) { lt.GroupChannelId = ""; lt.GroupChannel = oldChannelIdToChannel[l]; } } } } } //warnings that are ok for saving, but not running tests //FB 14623 Added EditTestSetupParameters_EmptySquibDelay to incomplete warnings list public static readonly string[] _incompleteWarnings = new[] { StringResources.EditTestSetupPageError_MissingSensor, StringResources.EditTestSetupParameters_EmptySquibDelay, StringResources.EditTestSetup_TTSUserValue2, StringResources.EditTestSetupPage_NoPhysicalHardwareInTest//, //StringResources.EditTestSetupPage_AAFilterRateOutOfRange }; private List _hardware = null; private List _nonDistributorHardware = null; private static readonly object HardwareLock = new object(); public void ClearHardware() { lock (HardwareLock) { _hardware = null; } } public void AddHardwareToMemory(DASHardware hardware) { lock (HardwareLock) { if (null == _hardware) { return; } var matches = from h in _hardware where h.DASId == hardware.DASId select h; if (!matches.Any()) { _hardware.Add(hardware); } } } public void RemoveHardwareFromMemory(DASHardware hardware) { lock (HardwareLock) { if (null == _hardware) { return; } var matches = from h in _hardware where h.DASId == hardware.DASId select h; if (matches.Any()) { foreach (var match in matches) { _hardware.Remove(match); } } } } public static void ValidateHardware(DASHardware h, bool warningsOnly, ref List warnings, ref List errors) { if (h.CalDateFailed && !h.IsTSRAIR()) { //Past the calibration date (overdue) if (SerializedSettings.CalHWPolicy == SerializedSettings.OVERDUE_HARDWARE_POLICIES.ALLOW_ALWAYS) { //Always allow the hardware to be used, but warn that the hardware is past the calibration date warnings.Add(string.Format(StringResources.EditObjectPageWarning_HardwareCalDateOverdue, h.SerialNumber, h.CalDueDate.ToShortDateString())); } else if (SerializedSettings.CalHWPolicy == SerializedSettings.OVERDUE_HARDWARE_POLICIES.ALLOW_UPTO_GRACE_PERIOD) { //Only allow the hardware to be used if we're still within the grace period if (h.CalDueDate.AddDays(SerializedSettings.CalHWGracePeriodDays) < DateTime.Now.Date) { //We're outside of the grace period, so we don't want to allow the overdue hardware to be used, but we want to only return a warning here when called //from the page, not an error, so that the Test Setup will be saved but incomplete. An error will be returned when the TestTemplate is validated. if (warningsOnly) { warnings.Add(string.Format(StringResources.EditObjectPageWarning_HardwareCalDateOverdue, h.SerialNumber, h.CalDueDate.ToShortDateString())); } else { errors.Add(string.Format(StringResources.EditObjectPageError_HardwareCalDateOverdue, h.SerialNumber, h.CalDueDate.ToShortDateString())); } } else { //We're still in the grace period, so just display a warning warnings.Add(string.Format(StringResources.EditObjectPageWarning_HardwareCalDateOverdue, h.SerialNumber, h.CalDueDate.ToShortDateString())); } } else { //The policy must be DONT_ALLOW, so we don't want to allow the overdue hardware to be used, but we want to only return a warning here when called //from the page, not an error, so that the Test Setup will be saved but incomplete. An error will be returned when the TestTemplate is validated. if (warningsOnly) { warnings.Add(string.Format(StringResources.EditObjectPageWarning_HardwareCalDateOverdue, h.SerialNumber, h.CalDueDate.ToShortDateString())); } else { errors.Add(string.Format(StringResources.EditObjectPageError_HardwareCalDateOverdue, h.SerialNumber, h.CalDueDate.ToShortDateString())); } } } else if (h.CalDateWarning && !h.IsTSRAIR()) { //Not past the calibration date, but past the warning date, so warn warnings.Add(string.Format(StringResources.EditObjectPageWarning_HardwareCalDateOverdue, h.SerialNumber, h.CalDueDate.ToShortDateString())); } } /// /// written as part of issue /// 15395 Download tile using wrong DAS when browsing to DTS file /// this function sets the test hardware so that it won't be required from the db /// it's currently only intended for emergency download, it's probably ok to use outside /// of that, but it was designed for a narrow use /// /// public void SetHardware(DASHardware[] hardwares) { lock (HardwareLock) { _hardware = new List(); foreach (var h in hardwares) { _hardware.Add(new DASHardware(h.GetHardware())); } } } /// /// MS 31861 detected where the hardware list for a test setup could have extra das in it based on /// stable groups having different hardware than embedded groups /// This function just corrects the current list to only look at what is in the /// IncludedHardware list /// public void CorrectHardwareList() { lock (HardwareLock) { if (null != _hardware) { var hardware = _hardware.ToArray(); var ids = GetAllIncludedHardware(); foreach (var h in hardware) { if (!ids.Contains(h.DASId)) { _hardware.Remove(h); } } } } } public DASHardware[] GetHardware() { lock (HardwareLock) { if (null == _hardware) { var allHardware = DASHardwareList.GetAllHardware(); //var isoHardware = Hardware.GetAllDAS(); //var lookup = isoHardware.ToDictionary(s => s.DASId); var lookup = new Dictionary(); foreach (var h in allHardware) { lookup[h.DASId] = h.GetHardware(); } var included = new HashSet(); var l = new List(); foreach (var hid in GetAllIncludedHardware()) { if (included.Contains(hid)) { continue; } if (lookup.ContainsKey(hid)) { included.Add(hid); l.Add(new DASHardware(lookup[hid])); } } _hardware = l; } } return _hardware.ToArray(); } public DASHardware[] GetNonDistributorHardware() { lock (HardwareLock) { if (null == _nonDistributorHardware) { GetHardware(); _nonDistributorHardware = new List(); foreach (var das in _hardware) { if (das.IsNonDistributorHardware)//temp { _nonDistributorHardware.Add(das); } } } } return _nonDistributorHardware.ToArray(); } #region var and const public List CalculatedChannels = new List(); private readonly List _channelKeysInOrder = new List(); private readonly List _hardwareChannelsInOrder = new List(); /// /// 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. /// public 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>>(); private static readonly object HardwareChannelLookupLock = new object(); private Dictionary _absoluteNumberToHardwareChannel; private Dictionary _dasChannelNumberToAbsoluteNumber; public new bool ViewRealtime { get => 0 != GetNumberOfRealtimeSupportedChannels() && base.ViewRealtime; set => base.ViewRealtime = value; } #endregion var and const #region Constructor public TestTemplate(TestSetupDefaults settings = null, bool bLoaded = true, SimpleHardware[] hardware = null, ITestSetupRecord copy = null) { _downloadFolder = DataModelSettings.DownloadFolder; _exportFolder = DataModelSettings.DownloadFolder; _defaultDownloadFolder = DataModelSettings.DownloadFolder; InitializeFromDefaults(settings, hardware); _bIsLoaded = bLoaded; if (null != copy) { Copy(copy); } LoadSettings(Settings); } public static int GetDefaultNumberOfRealtimeGraphs(RealtimeGraphsEnum realtimeGraphs) { switch (realtimeGraphs) { case RealtimeGraphsEnum.One: return 1; case RealtimeGraphsEnum.Three: return 3; case RealtimeGraphsEnum.Six: default: return 6; } } //gets all the hardware in the db in simple form (serial/parentdas/dasid) public static SimpleHardware[] GetSimpleHardwares() { List list = new List(); using (var cmd = DbOperations.GetSQLCommand(true)) { try { cmd.CommandType = CommandType.Text; cmd.CommandText = "SELECT [DASId], [SerialNumber], [ParentDAS], [Type] FROM [DAS] WHERE [Position] != 'Prototype'"; var reader = cmd.ExecuteReader(); while (reader.Read()) { int dasId = Convert.ToInt32(reader["DASId"]); var serialNumber = (string)reader["SerialNumber"]; var o = reader["ParentDAS"]; var parentDAS = DBNull.Value.Equals(o) ? "" : (string)o; list.Add(new SimpleHardware(serialNumber, parentDAS, dasId, Convert.ToInt32(reader["Type"]))); } } finally { cmd.Connection.Dispose(); } } return list.ToArray(); } public Dictionary ChannelsForGroup { get; set; } = new Dictionary(); public Dictionary DASClockMasterList { get; set; } = new Dictionary(200); public Dictionary DASSampleRateList { get; set; } = new Dictionary(200); public Dictionary DASAAFRateList { get; set; } = new Dictionary(); public Dictionary DASSamplesPerPacketList { get; set; } = new Dictionary(); public Dictionary DASPTPDomainIDList { get; set; } = new Dictionary(); public Dictionary> EncapsulatedDASList { get; set; } = new Dictionary>(200); public Dictionary ChannelSettingsForChannel { get; set; } = new Dictionary(200); public double GetSampleRate(string dasSerialNumber, double defaultValue) { if (!DASSampleRateList.ContainsKey(dasSerialNumber)) { DASSampleRateList[dasSerialNumber] = defaultValue; } return DASSampleRateList[dasSerialNumber]; } public double GetNextHighestAvailableSampleRate(string serialNumber, List availableSampleRates) { return GetNextHighestAvailableSampleRate(serialNumber, availableSampleRates, DASSampleRateList, DASAAFRateList); } public static double GetNextHighestAvailableSampleRate(string serialNumber, List availableSampleRates, Dictionary dasSampleRateList, Dictionary dasAAFRateList) { var requestedRate = dasSampleRateList[serialNumber]; var hardware = DASHardwareList.GetList().GetHardware(serialNumber); foreach (var sampleRate in availableSampleRates) { dasSampleRateList[serialNumber] = sampleRate; dasAAFRateList[serialNumber] = GetAAFForHardware(hardware, (int)sampleRate); if (sampleRate > requestedRate) { break; } } return dasSampleRateList[serialNumber]; } public Visibility TooltipVisibility { get { //calculating is complete can be expensive when you have a large list of test setups //for immediate time just disable this feature return Visibility.Collapsed; } } public string TooltipMessage => CompletionErrorMessage; public new double PreTriggerSeconds { get { return RecordingModeExtensions.CanProgramPreTrigger(RecordingMode) ? base.PreTriggerSeconds : 0D; } set => base.PreTriggerSeconds = value; } private void InitializeFromDefaults(TestSetupDefaults settingsParam = null, SimpleHardware[] hardware = null) { var settings = settingsParam ?? TestSetupDefaults.GetUserSettings(ApplicationProperties.CurrentUser.Id); _settings = CreateSettingsDictionary(settings); SuppressMissingSensorsWarning = settings.DefaultSuppressMissingSensorsWarning; CalibrationBehavior = settings.DefaultSensorCalibrationBehavior; RecordingMode = settings.AllowTSRAIRRecordingModes ? RecordingModes.Active : settings.DefaultRecordingMode; PreTriggerSeconds = settings.DefaultPreTriggerSeconds; PostTriggerSeconds = settings.DefaultPostTriggerSeconds; NumberOfEvents = settings.DefaultNumberOfEvents; IsComplete = false; RecordingMode = settings.DefaultRecordingMode; UploadFolder = settings.DefaultUploadFolder; AllowMissingSensors = settings.DefaultAllowMissingSensors; AllowSensorIdToBlankChannel = !settings.DefaultRequireSensorIdFound; ArmCheckListStep = settings.DefaultArmCheckListStep; AutomaticProgression = settings.DefaultAutomaticMode; AutomaticProgressionDelayMS = Convert.ToInt32(settings.DefaultTestSetupAutomaticProgressDelaySeconds * 1000D); CalibrationBehavior = settings.DefaultSensorCalibrationBehavior; CheckListBatteryVoltageCheck = settings.DefaultCheckListBatteryVoltageCheck; CheckListInputVoltageCheck = settings.DefaultCheckListInputVoltageCheck; CheckListRequirePass = settings.DefaultChecklistMustPass; CheckListSensorIdCheck = settings.DefaultCheckListSensorIdCheck; CheckListSquibResistanceCheck = settings.DefaultCheckListSquibResistanceCheck; CheckListTriggerStartCheck = settings.DefaultCheckListTriggerStartCheck; CheckListTiltSensorCheck = settings.DefaultCheckListTiltSensorCheck; CheckListTemperatureCheck = settings.DefaultCheckListTemperatureCheck; DownloadAll = settings.DefaultDownloadAll; DoROIDownload = settings.DefaultDownloadROI; ExcitationWarmupTimeMS = Convert.ToInt32(settings.DefaultTestExcitationWarmupSeconds * 1000D); DefaultNumberRealtimeGraphs = GetDefaultNumberOfRealtimeGraphs(settings.DefaultRealtimeGraphCount); StrictDiagnostics = settings.DefaultRequireAllUnitsPassDiagnostics; RequireUserConfirmationOnErrors = settings.DefaultRequireUserConfirmationOnErrors; RegionsOfInterest = new BindingList { new RegionOfInterest("", true, settings.DefaultROIStart, settings.DefaultROIEnd) }; ROIEnd = settings.DefaultROIEnd; ROIStart = settings.DefaultROIStart; PostTestDiagnosticsLevel = settings.DefaultRunPostTestDiagnostics; SamplesPerSecondAggregate = settings.DefaultSampleRate; if (null == hardware) { hardware = GetSimpleHardwares(); } foreach (var das in hardware) { DASClockMasterList[das.SerialNumber] = false; DASSampleRateList[das.SerialNumber] = settings.DefaultSampleRate; DASAAFRateList[das.SerialNumber] = GetAAFForHardware(das, (int)settings.DefaultSampleRate); DASSamplesPerPacketList[das.SerialNumber] = GetSamplesPerPacketForHardware((int)settings.DefaultSampleRate); DASPTPDomainIDList[das.SerialNumber] = settings.DefaultPTPDomainID; if (!string.IsNullOrWhiteSpace(das.ParentDAS)) { var childList = new List(); //This is a child if (EncapsulatedDASList.ContainsKey(das.ParentDAS)) { childList = EncapsulatedDASList[das.ParentDAS]; } childList.Add(das.SerialNumber); EncapsulatedDASList[das.ParentDAS] = childList; } } TriggerCheckStep = settings.DefaultTriggerCheckStep; ViewDownloadAll = settings.DefaultViewAll; ViewROIDownload = settings.DefaultViewROI; MeasureSquibResistancesStep = settings.DefaultMeasureSquibResistancesStep; if (settings.ExportChryslerDDAS) { ExportFormats |= SupportedExportFormatBitFlags.ChryslerDDAS; } if (settings.ExportCSVFiltered) { ExportFormats |= SupportedExportFormatBitFlags.csvfiltered; } if (settings.ExportCSVUnfiltered) { ExportFormats |= SupportedExportFormatBitFlags.csvunfiltered; } if (settings.ExportCSVADC) { ExportFormats |= SupportedExportFormatBitFlags.CSVADC; } if (settings.ExportCSVMV) { ExportFormats |= SupportedExportFormatBitFlags.CSVMV; } if (settings.ExportHDFADC) { ExportFormats |= SupportedExportFormatBitFlags.HDFADC; } if (settings.ExportHDFFiltered) { ExportFormats |= SupportedExportFormatBitFlags.HDFFiltered; } if (settings.ExportHDFMV) { ExportFormats |= SupportedExportFormatBitFlags.HDFMV; } if (settings.ExportHDFUnfiltered) { ExportFormats |= SupportedExportFormatBitFlags.HDFUnfiltered; } if (settings.ExportISOFiltered) { ExportFormats |= SupportedExportFormatBitFlags.isofiltered; } if (settings.ExportISOUnFiltered) { ExportFormats |= SupportedExportFormatBitFlags.isounfiltered; } if (settings.ExportRDFADC) { ExportFormats |= SupportedExportFormatBitFlags.rdfadc; } if (settings.ExportSomatFiltered) { ExportFormats |= SupportedExportFormatBitFlags.somatfiltered; } if (settings.ExportSomatUnfiltered) { ExportFormats |= SupportedExportFormatBitFlags.somatunfiltered; } if (settings.ExportTDASADC) { ExportFormats |= SupportedExportFormatBitFlags.tdasadc; } if (settings.ExportTDMSADC) { ExportFormats |= SupportedExportFormatBitFlags.tdmsadc; } if (settings.ExportToyotaFiltered) { ExportFormats |= SupportedExportFormatBitFlags.toyotafiltered; } if (settings.ExportToyotaUnfiltered) { ExportFormats |= SupportedExportFormatBitFlags.toyotaunfiltered; } if (settings.ExportTSVFiltered) { ExportFormats |= SupportedExportFormatBitFlags.tsvfiltered; } if (settings.ExportTSVUnfiltered) { ExportFormats |= SupportedExportFormatBitFlags.tsvunfiltered; } if (settings.ExportXLSXFiltered) { ExportFormats |= SupportedExportFormatBitFlags.xlsxfiltered; } if (settings.ExportXLSXUnfiltered) { ExportFormats |= SupportedExportFormatBitFlags.xlsxunfiltered; } if (settings.ExportDiademADC) { ExportFormats |= SupportedExportFormatBitFlags.diademadc; } if (settings.ExportASC) { ExportFormats |= SupportedExportFormatBitFlags.FIATASC; } //CheckoutMode = settings.DefaultCheckoutMode; QuitTestWithoutWarning = settings.SupressQuitTestWarning; NotAllChannelsRealTime = settings.SuppressViewAllRealtime; NotAllChannelsViewer = settings.SuppressViewAllViewer; CommonLine = settings.DefaultCommonStatusLine; WarnOnFailedBattery = settings.DefaultWarnOnFailedBattery; IgnoreShortedStartCompletion = settings.DefaultIgnoreShortedStart; IgnoreShortedTriggerCompletion = settings.DefaultIgnoreShortedTrigger; //17577 AutoArm Remove Auto-Arm from config file DoAutoArm = /* Properties.Settings.Default.Allow &&*/ settings.DefaultAutoArm; DoStreaming = settings.AllowStreamingModes && settings.DefaultStreaming; DoEnableRepeat = settings.DefaultAutoArmRepeatEnable; ClockSyncProfileMaster = settings.DefaultClockSyncProfileMaster; ClockSyncProfileSlave = settings.DefaultClockSyncProfileSlave; UploadData = settings.UploadDefault; UploadExportsOnly = settings.DefaultUploadExportsOnly; ViewExport = settings.DefaultExport; ViewRealtime = settings.DefaultViewRealtime; IntervalBetweenEventStartsMinutes = settings.DefaultIntervalBetweenEventStartsMinutes; RTCScheduleStartDateTime = DateTime.UtcNow; AlignUDPToPPS = settings.AlignUDPToPPS; ExtraProperties = new List(); } // FB14276: Add Custom ISO Key-Value Pairs public TestTemplate(bool NotUsed) { _downloadFolder = DataModelSettings.DownloadFolder; _exportFolder = DataModelSettings.DownloadFolder; _defaultDownloadFolder = DataModelSettings.DownloadFolder; InitializeFromDefaults(); ExpressTestSetup = true; Name = StringResources.TestTemplate_ExpressTestSetup; } private void OldTestTemplateLiteCopy(TestTemplate copy) { Id = copy.Id; foreach (var g in copy.Groups) { Groups.Add(g); } ChannelsForGroup = copy.ChannelsForGroup; AddedHardware = copy.AddedHardware; RemovedHardware = copy.RemovedHardware; DASSampleRateList = new Dictionary(); using (var e = copy.DASSampleRateList.GetEnumerator()) { while (e.MoveNext()) { DASSampleRateList[e.Current.Key] = e.Current.Value; } } _dasSettings.Clear(); using (var e = copy._dasSettings.GetEnumerator()) { while (e.MoveNext()) { _dasSettings[e.Current.Key] = new DASSettings(e.Current.Value); } } DASAAFRateList = copy.DASAAFRateList; DASSamplesPerPacketList = copy.DASSamplesPerPacketList; DASClockMasterList = copy.DASClockMasterList; DASPTPDomainIDList = copy.DASPTPDomainIDList; } public bool Filter(string term) { if (string.IsNullOrWhiteSpace(term)) { return true; } term = term.ToLower(); if (Name.ToLower().Contains(term)) { return true; } if (Description.ToLower().Contains(term)) { return true; } if (LastModifiedBy.ToLower().Contains(term)) { return true; } return false; } public void AddGroup(IGroup group, IDictionary sensorLookup, IDictionary hardwareLookup, IChannelSetting[] channelDefaults) { if (Groups.Any(g => g.Id == group.Id && group.Id >= 0)) { return; } Groups.Add(group); OnPropertyChanged("Groups"); var allChannels = group.GetAllChannels(true, sensorLookup, hardwareLookup, channelDefaults, SerializedSettings.AllowSensorPushAndPull); group.DeterminePositionAndTestObject(allChannels); ChannelsForGroup[group] = allChannels; } public void AddGroup(IGroup group, IDictionary sensorLookup, IDictionary channelLookup) { if (Groups.Any(g => g.Id == group.Id && group.Id >= 0)) { return; } Groups.Add(group); OnPropertyChanged("Groups"); var allChannels = new List(); foreach (var channel in channelLookup) { channel.Value.Group = group; allChannels.Add(channel.Value); } group.DeterminePositionAndTestObject(allChannels.ToArray()); ChannelsForGroup[group] = allChannels.ToArray(); } public int[] AddedHardware { get; set; } = new int[0]; public int[] RemovedHardware { get; set; } = new int[0]; public int[] GetAllIncludedHardware() { var list = new List(); foreach (var group in Groups) { foreach (var id in group.IncludedHardware) { if (!list.Contains(id)) { list.Add(id); } } } foreach (var id in AddedHardware) { if (!list.Contains(id)) { list.Add(id); } } foreach (var id in RemovedHardware) { if (list.Contains(id)) { list.Remove(id); } } return list.ToArray(); } public void AddHardware(int dasId) { if (RemovedHardware.Contains(dasId)) { var list = RemovedHardware.ToList(); list.Remove(dasId); RemovedHardware = list.ToArray(); } var newList = AddedHardware.ToList(); if (!newList.Contains(dasId)) { newList.Add(dasId); AddedHardware = newList.ToArray(); } } public void AddHardware(int dasId, string dasSerialNumber, IDASHardware[] allHardware, IDictionary lookup) { DASHardware hw = null; if (lookup.ContainsKey(dasId)) { hw = (DASHardware)lookup[dasId]; } if (RemovedHardware.Contains(dasId)) { var list = RemovedHardware.ToList(); list.Remove(dasId); if (null != hw && hw.IsTSRAIR()) { var moduleIdList = DASHardwareList.GetEmbeddedModules(allHardware, hw.DASId); foreach (var moduleId in moduleIdList) { list.Remove(moduleId); } } RemovedHardware = list.ToArray(); } var newList = AddedHardware.ToList(); if (!newList.Contains(dasId)) { newList.Add(dasId); if (null != hw && hw.IsTSRAIR()) { var moduleIdList = DASHardwareList.GetEmbeddedModules(allHardware, hw.DASId); foreach (var moduleId in moduleIdList) { newList.Add(moduleId); } } AddedHardware = newList.ToArray(); } } public void AddHardware(int dasId, IDictionary lookup) { DASHardware hw = null; if (lookup.ContainsKey(dasId)) { hw = (DASHardware)lookup[dasId]; } if (null == hw) { hw = DASHardwareList.GetList().GetHardware(dasId); } var allHardware = lookup.Values.ToArray(); if (RemovedHardware.Contains(dasId)) { var list = RemovedHardware.ToList(); list.Remove(dasId); if (hw != null && hw.IsTSRAIR()) { //var hardwareArray = DASHardwareList.GetAllHardware(); var moduleIdList = DASHardwareList.GetEmbeddedModules(allHardware, hw.DASId); foreach (var moduleId in moduleIdList) { list.Remove(moduleId); } } RemovedHardware = list.ToArray(); } var newList = AddedHardware.ToList(); if (!newList.Contains(dasId)) { newList.Add(dasId); if (hw != null && hw.IsTSRAIR()) { //var hardwareArray = DASHardwareList.GetAllHardware(); var moduleIdList = DASHardwareList.GetEmbeddedModules(allHardware, hw.DASId); foreach (var moduleId in moduleIdList) { newList.Add(moduleId); } } AddedHardware = newList.ToArray(); } } public void RemoveHardware(int dasId, IDASHardware[] allHardware, IDictionary lookup) { DASHardware hw = null; if (lookup.ContainsKey(dasId)) { hw = (DASHardware)lookup[dasId]; } if (AddedHardware.Contains(dasId)) { var list = AddedHardware.ToList(); list.Remove(dasId); if (null != hw && hw.IsTSRAIR()) { var moduleIdList = DASHardwareList.GetEmbeddedModules(allHardware, hw.DASId); foreach (var moduleId in moduleIdList) { list.Remove(moduleId); } } AddedHardware = list.ToArray(); } var newList = RemovedHardware.ToList(); if (!newList.Contains(dasId)) { newList.Add(dasId); } if (null != hw && hw.IsTSRAIR()) { var moduleIdList = DASHardwareList.GetEmbeddedModules(allHardware, hw.DASId); foreach (var moduleId in moduleIdList) { if (!newList.Contains(moduleId)) { newList.Add(moduleId); } } } RemovedHardware = newList.ToArray(); var newChannelsForGroup = new Dictionary(); using (var eGroups = ChannelsForGroup.GetEnumerator()) { while (eGroups.MoveNext()) { var groupChannelList = new List(); foreach (var ch in eGroups.Current.Value) { if (ch.DASId == dasId || RemovedHardware.Contains(ch.DASId)) { //When a TSR AIR or SLICE6 AIR-TC is removed, effectively remove its channels //from ChannelsForGroup by not adding them to the new list. if (null != ch.HardwareChannel && !ch.HardwareChannel.IsTSRAIR && !ch.HardwareChannel.IsSLICETC) { ch.SetHardwareChannel(null); groupChannelList.Add(ch); } else { ch.SetHardwareChannel(null); } } else { groupChannelList.Add(ch); } } newChannelsForGroup[eGroups.Current.Key] = groupChannelList.ToArray(); } } ChannelsForGroup = newChannelsForGroup; } public void SetTestSetupChannelOrder() { //When channels are added automatically to the Test channels Group, due to discovering //DAS with sensor IDs that match the IDs of sensors in the db, their TestSetupOrder //will not have been set yet, so do it here before calling SaveGroups. FB 17642 var list = new List(); foreach (var group in ChannelsForGroup) { foreach (var channel in group.Value) { list.Add(channel); } } list.Sort(); for (var i = 0; i < list.Count; i++) { list[i].TestSetupOrder = 1 + i; list[i].CanMoveDown = true; list[i].CanMoveUp = true; } } public void SaveGroups() { //channel id mappings may change when groups are committed, notably for //groups which aren't in the database yet or channels that aren't in the database yet //store the old channel id to the group channel so that the new id can be used var oldChannelIdToGroupChannel = new Dictionary(); //step 1, convert any non embedded groups foreach (var group in Groups) { if (group.Embedded) { continue; } if (!ChannelsForGroup.ContainsKey(group)) { continue; } group.ConvertToEmbedded(ChannelsForGroup[group]); } //step 2, remove any existing groups no longer needed var groupsToDelete = GetExistingGroupIds().ToList(); //step 3, update any existing groups var newGroupChannelList = new List(); foreach (var group in Groups) { if (group.Id < 0) { continue; } if (!ChannelsForGroup.ContainsKey(group)) { continue; } foreach (var channel in ChannelsForGroup[group]) { oldChannelIdToGroupChannel[channel.Id] = channel; } group.Save(ChannelsForGroup[group], ApplicationProperties.CanCurrentUserCommitChannelCodes, ref newGroupChannelList); groupsToDelete.Remove(group.Id); UpdateGroupInTestSetup(group); } //step 4, insert new groups foreach (var group in Groups) { if (group.Id >= 0) { continue; } if (!ChannelsForGroup.ContainsKey(group)) { continue; } foreach (var channel in ChannelsForGroup[group]) { oldChannelIdToGroupChannel[channel.Id] = channel; } group.Save(ChannelsForGroup[group], ApplicationProperties.CanCurrentUserCommitChannelCodes, ref newGroupChannelList); InsertGroupIntoTest(group); } if (newGroupChannelList.Any()) { ReplaceROIChannels(newGroupChannelList); } if (groupsToDelete.Any()) { DeleteGroupsFromTest(groupsToDelete.ToArray()); } //now finally, at least for calculated channels replace ids //28099 Exporting test setup does not export calculated channels var replacementMapping = new Dictionary(); using (var eChannel = oldChannelIdToGroupChannel.GetEnumerator()) { while (eChannel.MoveNext()) { if (!eChannel.Current.Key.ToString().Equals(eChannel.Current.Value.Id.ToString())) { replacementMapping[eChannel.Current.Key.ToString()] = eChannel.Current.Value.Id.ToString(); } } } if (replacementMapping.Any()) { ReplaceCalculatedChannel(replacementMapping); } } public void SaveHardware() { try { var includedHardware = GetAllIncludedHardware(); foreach (var g in Groups) { g.IncludedHardware = includedHardware; } var hardware = DASHardwareList.GetAllHardware(); var existingHardware = GetExistingHardwareIds(); var addedHardware = AddedHardware;//create a local copy rather than operate on property. //we shouldn't have a null addedHardware, but we should prep for if we do ... if (null == addedHardware) { addedHardware = new int[0]; } foreach (var id in addedHardware) { var hardwareRef = Array.Find(hardware, h => h.DASId == id); if (null == hardwareRef) { APILogger.Log($"Hardware id[{id}] has no match in the addedhardware list"); continue; } var hardwareSN = hardwareRef.SerialNumber; // Default to ClockMaster = false unless the module is in the DASClockMasterList var isClockMaster = false; if (null != DASClockMasterList && DASClockMasterList.ContainsKey(hardwareSN)) { isClockMaster = DASClockMasterList[hardwareSN]; } var ptpDomainId = TestSetupDefaults.GetUserSettings(ApplicationProperties.CurrentUser.Id).DefaultPTPDomainID; if (DASPTPDomainIDList.ContainsKey(hardwareSN)) { ptpDomainId = DASPTPDomainIDList[hardwareSN]; } if (existingHardware.Contains(id)) { if (!DASSampleRateList.ContainsKey(hardwareSN) || !DASAAFRateList.ContainsKey(hardwareSN) || !DASSamplesPerPacketList.ContainsKey(hardwareSN)) { APILogger.Log($"Hardware [{hardwareSN}] has no entries in sampleratelist or aafratelist or SamplesPerPacketList"); continue; } UpdateExistingHardware(id, 1, DASSampleRateList[hardwareSN], isClockMaster, DASAAFRateList[hardwareSN], ptpDomainId); } else { if (DASSampleRateList.ContainsKey(hardwareSN) && DASAAFRateList.ContainsKey(hardwareSN) && DASSamplesPerPacketList.ContainsKey(hardwareSN)) { InsertHardware(id, 1, DASSampleRateList[hardwareSN], isClockMaster, DASAAFRateList[hardwareSN], ptpDomainId); } else { var hFromDb = Array.Find(hardware, h => h.DASId == id); if (null == hFromDb) { APILogger.Log($"Hardware [{hardwareSN}] has no entry in hardware list"); continue; } var defaultAAFRate = GetAAFForHardware(hFromDb, (int)TestSetupDefaults.GetUserSettings(ApplicationProperties.CurrentUser.Id).DefaultSampleRate); InsertHardware(id, 1, TestSetupDefaults.GetUserSettings(ApplicationProperties.CurrentUser.Id).DefaultSampleRate, false, defaultAAFRate, TestSetupDefaults.GetUserSettings(ApplicationProperties.CurrentUser.Id).DefaultPTPDomainID); } } } var invalidRemovedHardware = new List(); foreach (var id in RemovedHardware) { if (existingHardware.Contains(id)) { // these are hardware that were assigned to the group but are no more // Is valid hardware in the test // typical case UpdateExistingHardware(id, 0, DASSampleRateList[hardware.First(h => h.DASId == id).SerialNumber], DASClockMasterList[hardware.First(h => h.DASId == id).SerialNumber], DASAAFRateList[hardware.First(h => h.DASId == id).SerialNumber], DASPTPDomainIDList[hardware.First(h => h.DASId == id).SerialNumber]); } else if (Array.Exists(hardware, h => h.DASId == id)) { //this shouldn't fail since we just proved there is an entry above ... //but this is more for convenience of using a short form and not querying for each lookup var hFromDb = Array.Find(hardware, h => h.DASId == id); if (null == hFromDb) { APILogger.Log($"Hardware [{id}] does not exist in hardware list"); continue; } // is hardware that wasn't assigned to the test but is now being explicitly excluded from the test // Is valid hardware in the db if (DASSampleRateList.ContainsKey(hFromDb.SerialNumber)) { if (!DASAAFRateList.ContainsKey(hFromDb.SerialNumber) || !DASClockMasterList.ContainsKey(hFromDb.SerialNumber) || !DASAAFRateList.ContainsKey(hFromDb.SerialNumber) || !DASSamplesPerPacketList.ContainsKey(hFromDb.SerialNumber)) { APILogger.Log($"hardware [{hFromDb.SerialNumber}] doesn't have an entry in either DASAAFRateList or DASClockMasterList or DASAAFRateList or DASSamplesPerPacketList"); continue; } InsertHardware(id, 0, DASSampleRateList[hFromDb.SerialNumber], DASClockMasterList[hFromDb.SerialNumber], DASAAFRateList[hFromDb.SerialNumber], DASPTPDomainIDList[hFromDb.SerialNumber]); } else { var defaultAAFRate = GetAAFForHardware(hFromDb, (int)TestSetupDefaults.GetUserSettings(ApplicationProperties.CurrentUser.Id).DefaultSampleRate); InsertHardware(id, 0, TestSetupDefaults.GetUserSettings(ApplicationProperties.CurrentUser.Id).DefaultSampleRate, false, defaultAAFRate, TestSetupDefaults.GetUserSettings(ApplicationProperties.CurrentUser.Id).DefaultPTPDomainID); } } else { // is not valid hardware in the db // likely orphaned? //Remove from the list of removed hardware because it is no longer valid. invalidRemovedHardware.Add(id); } } foreach (var id in invalidRemovedHardware) { // Clean up orphaned Ids RemovedHardware = RemovedHardware.Where(h => h != id).ToArray(); } foreach (var id in existingHardware) { if (!AddedHardware.Contains(id) && !RemovedHardware.Contains(id)) { DeleteHardware(id); } } } catch (Exception ex) { APILogger.Log($"Exception in SaveHardware", ex); } } private void UpdateExistingHardware(int id, int addOrRemove, double samplesPerSecond, bool isClockMaster, float antiAliasFilterRate, byte ptpDomainId) { var record = new TestSetupHardwareRecord() { AddDAS = 0 != addOrRemove, SamplesPerSecond = Convert.ToInt32(samplesPerSecond), DASId = id, TestSetupId = Id, IsClockMaster = isClockMaster, AntiAliasFilterRate = Convert.ToInt32(antiAliasFilterRate), PTPDomainId = ptpDomainId }; _ = DbOperations.TestSetupHardwareUpdate(record); } private void InsertHardware(int id, int addOrRemove, double samplesPerSecond, bool isClockMaster, float antiAliasFilterRate, byte ptpDomainId) { var record = new TestSetupHardwareRecord() { AddDAS = 0 != addOrRemove, SamplesPerSecond = Convert.ToInt32(samplesPerSecond), DASId = id, TestSetupId = Id, IsClockMaster = isClockMaster, AntiAliasFilterRate = Convert.ToInt32(antiAliasFilterRate), PTPDomainId = ptpDomainId }; _ = DbOperations.TestSetupHardwareInsert(record); } private void DeleteHardware(int id) { _ = DbOperations.TestSetupHardwareDelete(null, id, Id); } private int[] GetExistingHardwareIds() { var testSetupHardwareGetWrapper = new TestSetupHardwareGet(); var testSetupHardwareGetList = testSetupHardwareGetWrapper.TestSetupHardwareGet_Wrapper(Id, null); var list = new List(); foreach (var testSetupGet in testSetupHardwareGetList) { var id = testSetupGet.DASId; list.Add(id); } return list.ToArray(); } public void RemoveGroup(IGroup group) { ChannelsForGroup.Remove(group); Groups.Remove(group); for (var i = 0; i < Groups.Count; i++) { Groups[i].DisplayOrder = 1 + i; } OnPropertyChanged("Groups"); } public List GetChannels() { var channels = new List(); using (var enumChannels = ChannelsForGroup.GetEnumerator()) { while (enumChannels.MoveNext()) { channels.AddRange(enumChannels.Current.Value); } } channels.Sort(); return channels; } /// /// moves the group up, and all channels in the group before all channels in groups after this group /// /// public void MoveGroupUp(IGroup @group) { var index = Groups.IndexOf(group); if (0 == index) { return; } Swap(index, index - 1); var channels = GetChannels(); var groupAfterThisOne = new HashSet(); var insertIndex = -1; for (var i = index; i < Groups.Count; i++) { groupAfterThisOne.Add(Groups[i].Id); } //14045 Unhandled exception pulling updates for a group in a test. var channelsForThisGroup = ChannelsForGroup[group].ToList(); foreach (var channel in channelsForThisGroup) { channels.Remove(channel); } //now we want to find the first channel for all groups after this group for (var i = 0; i < channels.Count; i++) { if (null == channels[i].Group) { continue; } if (channels[i].IsBlank()) { continue; } if (groupAfterThisOne.Contains(channels[i].Group.Id)) { insertIndex = i; break; } } if (insertIndex < 0) { insertIndex = 0; } channelsForThisGroup.Sort(); //insert at the given point in reverse order for (var i = channelsForThisGroup.Count - 1; i >= 0; i--) { channels.Insert(insertIndex, channelsForThisGroup[i]); } for (var i = 0; i < channels.Count; i++) { if (channels[i].IsBlank()) { continue; } channels[i].TestSetupOrder = 1 + i; } } /// /// moves the group down and all channels in the group after all channels in groups before this group /// /// public void MoveGroupDown(IGroup @group) { var index = Groups.IndexOf(group); if (Groups.Last() == group) { return; } Swap(index, index + 1); //find the last channel for all groups before this one var groupsBeforeThisOne = new HashSet(); for (var i = 0; i < index + 1; i++) { groupsBeforeThisOne.Add(Groups[i].Id); } var channels = GetChannels(); var channelsForGroup = ChannelsForGroup[group].ToList(); channelsForGroup.Sort(); foreach (var channel in channelsForGroup) { channels.Remove(channel); } var insertPoint = -1; for (var i = channels.Count - 1; i >= 0; i--) { if (channels[i].IsBlank()) { continue; } if (null == channels[i].Group) { continue; } if (groupsBeforeThisOne.Contains(channels[i].Group.Id)) { insertPoint = i + 1; break; } } if (insertPoint < 0) { insertPoint = 0; } for (var i = channelsForGroup.Count - 1; i >= 0; i--) { channels.Insert(insertPoint, channelsForGroup[i]); } for (var i = 0; i < channels.Count; i++) { if (channels[i].IsBlank()) { continue; } channels[i].TestSetupOrder = 1 + i; } } private void Swap(int sourceIndex, int destIndex) { var displayOrder = Groups[sourceIndex].DisplayOrder; Groups[sourceIndex].DisplayOrder = Groups[destIndex].DisplayOrder; Groups[destIndex].DisplayOrder = displayOrder; Groups.Move(sourceIndex, destIndex); OnPropertyChanged("Groups"); } public void MoveGroupToDisplayOrder(IGroup groupToMove, int destDisplayOrder) { //14045 Unhandled exception pulling updates for a group in a test. if (destDisplayOrder < 1) { destDisplayOrder = 1; } var destIndex = destDisplayOrder - 1; // //Index is 0-relative while (!Groups[destIndex].Equals(groupToMove)) { MoveGroupUp(groupToMove); } } private void UpdateGroupInTestSetup(IGroup group) { _ = DbOperations.TestSetupGroupsUpdate(new TestSetupGroupRecord() { GroupId = group.Id, DisplayOrder = group.DisplayOrder, Position = group.Position, TestObjectType = group.TestObject, TestSetupId = Id }); } private void InsertGroupIntoTest(IGroup group) { _ = DbOperations.TestSetupGroupsInsert(new TestSetupGroupRecord() { GroupId = group.Id, TestSetupId = Id, DisplayOrder = group.DisplayOrder, Position = group.Position, TestObjectType = group.TestObject }); } private void DeleteGroupsFromTest(int[] ids) { foreach (var id in ids) { var errorNumber = DbOperations.GroupsDelete(id, out string errorMessage); if (errorNumber != 0) { throw new Exception(errorMessage); } } } private int[] GetExistingGroupIds() { var ids = new List(); var hr = DbOperations.TestSetupGroupsGet(null, Id, Name, out var records); if (0 == hr && null != records && records.Any()) { foreach (var record in records) { ids.Add(record.GroupId); } } return ids.ToArray(); } private bool GetIsCompleteNoFunctions() { return base.IsComplete; } public TestTemplate(TestTemplate copy) : base(copy) { //this constructor is already pretty long, so I put all the //TSRAir settings that need to be intiialized by copy constr into their own method CopyTSRAirSettings(copy); _downloadFolder = DataModelSettings.DownloadFolder; _exportFolder = DataModelSettings.DownloadFolder; _defaultDownloadFolder = DataModelSettings.DownloadFolder; IsQuickCheckout = copy.IsQuickCheckout; var settings = TestSetupDefaults.GetUserSettings(ApplicationProperties.CurrentUser.Id); _settings = new TestSettingDictionary(copy._settings); InitializeFromDefaults(settings.DefaultSensorCalibrationBehavior, settings.DefaultRecordingMode, settings.DefaultPreTriggerSeconds, settings.DefaultPostTriggerSeconds, settings.DefaultNumberOfEvents); OldTestTemplateLiteCopy(copy); IsComplete = copy.GetIsCompleteNoFunctions(); //FB16057: assign from lite, testtemplate.iscomplete.get() can cause a recalc and unload try { if (!copy.IsLoaded) { copy.Load(); } } catch (Exception ex) { APILogger.Log(ex); } QuickSensorCheck = copy.QuickSensorCheck; ISFFile = copy.ISFFile; ExpressTestSetup = copy.ExpressTestSetup; using (var eHardware = copy.HardwareOverrides.GetEnumerator()) { while (eHardware.MoveNext()) { HardwareOverrides[eHardware.Current.Key] = new HardwareInclusionInstruction(eHardware.Current.Value); } } using (var eLevelTrigger = copy.LevelTriggerChannels.GetEnumerator()) { while (eLevelTrigger.MoveNext()) { LevelTriggerChannels[eLevelTrigger.Current.Key] = new LevelTriggerChannel(eLevelTrigger.Current.Value); } } foreach (var cc in copy.CalculatedChannels) { CalculatedChannels.Add(new CalculatedValueClass(cc)); } AllowMissingSensors = copy.AllowMissingSensors; AllowSensorIdToBlankChannel = copy.AllowSensorIdToBlankChannel; AutomaticProgression = copy.AutomaticProgression; AutomaticProgressionDelayMS = copy.AutomaticProgressionDelayMS; AutoVerifyChannels = copy.AutoVerifyChannels; AutoVerifyDelaySeconds = copy.AutoVerifyDelaySeconds; CalibrationBehavior = copy.CalibrationBehavior; CheckedDASList = copy.CheckedDASList; if (null != copy.CustomerDetails) { CustomerDetails = new CustomerDetails(copy.CustomerDetails.GetISOCustomer()); } if (null != copy.TestEngineerDetails) { TestEngineerDetails = new TestEngineerDetails(copy.TestEngineerDetails.GetISOTestEngineer()); } foreach (var setting in copy.DASSettings) { var newSetting = new DASSettings(setting); _dasSettings[setting.DASSerialNumber] = newSetting; } DefaultNumberRealtimeGraphs = copy.DefaultNumberRealtimeGraphs; Description = copy.Description; WarnOnFailedBattery = copy.WarnOnFailedBattery; DoROIDownload = copy.DoROIDownload; DownloadAll = copy.DownloadAll; ExcitationWarmup = copy.ExcitationWarmup; ExcitationWarmupTimeMS = ExcitationWarmup; ExportFormats = copy.ExportFormats; InvertStartRecordCompletion = copy.InvertStartRecordCompletion; InvertTriggerCompletion = copy.InvertTriggerCompletion; IgnoreShortedStartCompletion = copy.IgnoreShortedStartCompletion; IgnoreShortedTriggerCompletion = copy.IgnoreShortedTriggerCompletion; TriggerCheckRealtime = copy.TriggerCheckRealtime; TriggerCheckStep = copy.TriggerCheckStep; PostTestDiagnosticsLevel = copy.PostTestDiagnosticsLevel; MeasureSquibResistancesStep = copy.MeasureSquibResistancesStep; if (null != copy.LabDetails) { LabDetails = new LabratoryDetails(copy.LabDetails.GetIsoLab()); } LastModified = copy.LastModified; LastModifiedBy = copy.LastModifiedBy; LocalOnly = copy.LocalOnly; Name = copy.Name; TestSetupUniqueId = copy.TestSetupUniqueId; PostTriggerSeconds = copy.PostTriggerSeconds; PreTriggerSeconds = copy.PreTriggerSeconds; RecordingMode = copy.RecordingMode; NumberOfEvents = copy.NumberOfEvents; WakeUpMotionTimeout = copy.WakeUpMotionTimeout; RequireUserConfirmationOnErrors = copy.RequireUserConfirmationOnErrors; RegionsOfInterest = copy.RegionsOfInterest; ROIEnd = copy.ROIEnd; ROIStart = copy.ROIStart; SamplesPerSecondAggregate = copy.SamplesPerSecondAggregate; StrictDiagnostics = copy.StrictDiagnostics; foreach (var tg in copy.TestGraphs) { _testGraphs.Add(new TestGraph(tg)); } //this is not needed, we could always sort whenever we use it, and it's probably already sorted //but just for safety since we just copied a bunch of stuff, sort it! TestObjectsAndAddedGroupsList.Sort(); TurnOffExcitation = copy.TurnOffExcitation; ExtraProperties = copy.ExtraProperties; TestDirectory = copy.TestDirectory; TestId = copy.TestId; TestIdNode = copy.TestIdNode; VerifyChannels = copy.VerifyChannels; CommonLine = copy.CommonLine; UploadData = copy.UploadData; UploadExportsOnly = copy.UploadExportsOnly; UploadFolder = copy.UploadFolder; ViewDiagnostics = copy.ViewDiagnostics; ViewDownloadAll = copy.ViewDownloadAll; ViewExport = copy.ViewExport; ViewRealtime = copy.ViewRealtime; ViewROIDownload = copy.ViewROIDownload; ExportFolder = copy.ExportFolder; DownloadFolder = copy.DownloadFolder; SameAsDownloadFolder = copy.SameAsDownloadFolder; DoAutoArm = copy.DoAutoArm; DoEnableRepeat = copy.DoEnableRepeat; DoStreaming = copy.DoStreaming; CheckoutMode = copy.CheckoutMode; QuitTestWithoutWarning = copy.QuitTestWithoutWarning; SuppressMissingSensorsWarning = copy.SuppressMissingSensorsWarning; NotAllChannelsRealTime = copy.NotAllChannelsRealTime; NotAllChannelsViewer = copy.NotAllChannelsViewer; ClockSyncProfileMaster = copy.ClockSyncProfileMaster; ClockSyncProfileSlave = copy.ClockSyncProfileSlave; foreach (var md in copy.GetMetaData()) { SetMetaData(md.TestObject, new TestObjectMetaData(md)); } SetTestSetupMetaData(new TestSetupMetaData(copy.GetTestMetaData())); lock (SensorLock) { SensorLookup = new Dictionary>>(); using (var e = copy.SensorLookup.GetEnumerator()) { while (e.MoveNext()) { if (!SensorLookup.ContainsKey(e.Current.Key)) { SensorLookup.Add(e.Current.Key, new Dictionary>()); } using (var e2 = e.Current.Value.GetEnumerator()) { while (e2.MoveNext()) { if (!SensorLookup[e.Current.Key].ContainsKey(e2.Current.Key)) { SensorLookup[e.Current.Key] .Add(e2.Current.Key, new Dictionary()); } using (var e3 = e2.Current.Value.GetEnumerator()) { while (e3.MoveNext()) { SensorLookup[e.Current.Key][e2.Current.Key][e3.Current.Key] = e3.Current.Value; } } } } } } } ErrorMessage = copy.ErrorMessage; //avoid recalculating, just copy the value IsComplete = copy.GetIsCompleteNoFunctions(); Dirty = copy.Dirty; _bIsLoaded = copy._bIsLoaded; TagsBlobBytes = copy.TagsBlobBytes; DestructiveTest = copy.DestructiveTest; } #endregion Constructor #region Properties 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; } } } private bool _bExpressTestSetup; public bool ExpressTestSetup { get => _bExpressTestSetup; set => SetProperty(ref _bExpressTestSetup, value, TestTemplateTags.ExpressTestSetup.ToString()); } /// /// this controls whether test id and description should be preserved /// this is generally set once the basic info page has been hit /// private bool _preserveTestId; public bool PreserveTestId { get => QuickSensorCheck || _preserveTestId; set => SetProperty(ref _preserveTestId, value, "PreserveTestId"); } /// /// 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 /// public bool CommonLine { get => CommonStatusLine; set { CommonStatusLine = value; OnPropertyChanged("CommonLine"); } } /// /// true if the test objects and other fields have already been loaded from the database, false otherwise /// internal bool _bIsLoaded; public bool IsLoaded => _bIsLoaded; public void SetIsLoaded(bool bIsLoaded) { _bIsLoaded = bIsLoaded; } public bool ArmCheckListStep { get { if (SerializedSettings.AutoAddArmChecklist && CheckForTOM() && DataModelSettings.ArmChecklistRequiredIfTOM) { return true; } return Convert.ToBoolean(GetSetting(TestSettingsEnum.ArmCheckListStep)); } set => SetSetting(TestSettingsEnum.ArmCheckListStep, value.ToString()); } public bool CheckListBatteryVoltageCheck { get => Convert.ToBoolean(GetSetting(TestSettingsEnum.CheckListBatteryVoltageCheck)); set => SetSetting(TestSettingsEnum.CheckListBatteryVoltageCheck, value.ToString()); } public bool CheckListInputVoltageCheck { get => Convert.ToBoolean(GetSetting(TestSettingsEnum.CheckListInputVoltageCheck)); set => SetSetting(TestSettingsEnum.CheckListInputVoltageCheck, value.ToString()); } public bool CheckListSquibResistanceCheck { get => Convert.ToBoolean(GetSetting(TestSettingsEnum.CheckListSquibResistanceCheck)); set => SetSetting(TestSettingsEnum.CheckListSquibResistanceCheck, value.ToString()); } public bool CheckListSensorIdCheck { get => Convert.ToBoolean(GetSetting(TestSettingsEnum.CheckListSensorIDCheck)); set => SetSetting(TestSettingsEnum.CheckListSensorIDCheck, value.ToString()); } public bool CheckListRequirePass { get => Convert.ToBoolean(GetSetting(TestSettingsEnum.CheckListMustPass)); set => SetSetting(TestSettingsEnum.CheckListMustPass, value.ToString()); } public bool CheckListTiltSensorCheck { get => Convert.ToBoolean(GetSetting(TestSettingsEnum.CheckListTiltSensorCheck)); set => SetSetting(TestSettingsEnum.CheckListTiltSensorCheck, value.ToString()); } public bool CheckListTemperatureCheck { get => Convert.ToBoolean(GetSetting(TestSettingsEnum.CheckListTemperatureCheck)); set => SetSetting(TestSettingsEnum.CheckListTemperatureCheck, value.ToString()); } public bool CheckListClockSyncCheck { get => Convert.ToBoolean(GetSetting(TestSettingsEnum.CheckListClockSyncCheck)); set => SetSetting(TestSettingsEnum.CheckListClockSyncCheck, value.ToString()); } /// /// engineer meta data (can differ from the record for the engineer in the db) /// public TestEngineerDetailsDbRecord EngineerOverride { get => GetEngineerFromMeta(); set => SetEngineerMetaData(value); } /// /// produces an engineer from meta data /// private TestEngineerDetailsDbRecord GetEngineerFromMeta() { var record = new TestEngineerDetailsDbRecord(); var meta = GetTestMetaData(); record.TestEngineerEmail = meta.GetPropertyValue(TestSetupMetaData.Fields.TestEngineerEmail); record.TestEngineerFax = meta.GetPropertyValue(TestSetupMetaData.Fields.TestEngineerFax); record.TestEngineerName = meta.GetPropertyValue(TestSetupMetaData.Fields.TestEngineerName); record.TestEngineerPhone = meta.GetPropertyValue(TestSetupMetaData.Fields.TestEngineerPhone); return record; } /// /// stores engineer in meta data /// public void SetEngineerMetaData(TestEngineerDetailsDbRecord record) { var meta = GetTestMetaData(); meta.SetPropertyValue(TestSetupMetaData.Fields.TestEngineerEmail, record.TestEngineerEmail); meta.SetPropertyValue(TestSetupMetaData.Fields.TestEngineerFax, record.TestEngineerFax); meta.SetPropertyValue(TestSetupMetaData.Fields.TestEngineerName, record.TestEngineerName); meta.SetPropertyValue(TestSetupMetaData.Fields.TestEngineerPhone, record.TestEngineerPhone); } /// /// customer meta data (can differ from customer record in db) /// public CustomerDetailsDbRecord CustomerOverride { get => GetCustomerFromMeta(); set => SetCustomerMetaData(value); } /// /// retrieves a customer from meta data /// private CustomerDetailsDbRecord GetCustomerFromMeta() { var record = new CustomerDetailsDbRecord(); var meta = GetTestMetaData(); record.CustomerCostUnit = meta.GetPropertyValue(TestSetupMetaData.Fields.CustomerCostUnit); record.CustomerName = meta.GetPropertyValue(TestSetupMetaData.Fields.CustomerName); record.CustomerOrderNumber = meta.GetPropertyValue(TestSetupMetaData.Fields.CustomerOrderNumber); record.ProjectRefNumber = meta.GetPropertyValue(TestSetupMetaData.Fields.CustomerProjectReferenceNumber); record.CustomerTestRefNumber = meta.GetPropertyValue(TestSetupMetaData.Fields.CustomerTestReferenceNumber); return record; } /// /// stores a customer in meta data /// public void SetCustomerMetaData(CustomerDetailsDbRecord record) { var meta = GetTestMetaData(); meta.SetPropertyValue(TestSetupMetaData.Fields.CustomerCostUnit, record.CustomerCostUnit); meta.SetPropertyValue(TestSetupMetaData.Fields.CustomerName, record.CustomerName); meta.SetPropertyValue(TestSetupMetaData.Fields.CustomerOrderNumber, record.CustomerOrderNumber); meta.SetPropertyValue(TestSetupMetaData.Fields.CustomerProjectReferenceNumber, record.ProjectRefNumber); meta.SetPropertyValue(TestSetupMetaData.Fields.CustomerTestReferenceNumber, record.CustomerTestRefNumber); } /// /// lab meta data (can differ from lab record in db) /// public LabratoryDetailsDbRecord LabOverride { get => GetLabFromMeta(); set => SetLabMetaData(value); } /// /// retrieves a lab from meta data /// private LabratoryDetailsDbRecord GetLabFromMeta() { var meta = GetTestMetaData(); var record = new LabratoryDetailsDbRecord(); record.LabratoryContactEmail = meta.GetPropertyValue(TestSetupMetaData.Fields.LaboratoryContactEmail); record.LabratoryContactFax = meta.GetPropertyValue(TestSetupMetaData.Fields.LaboratoryContactFax); record.LabratoryContactName = meta.GetPropertyValue(TestSetupMetaData.Fields.LaboratoryContactName); record.LabratoryContactPhone = meta.GetPropertyValue(TestSetupMetaData.Fields.LaboratoryContactPhone); record.LabratoryName = meta.GetPropertyValue(TestSetupMetaData.Fields.LaboratoryName); record.LabratoryProjectRefNumber = meta.GetPropertyValue(TestSetupMetaData.Fields.LaboratoryProjectReferenceNumber); record.LabratoryTestRefNumber = meta.GetPropertyValue(TestSetupMetaData.Fields.LaboratoryTestReferenceNumber); return record; } /// /// stores a lab in meta data /// public void SetLabMetaData(LabratoryDetailsDbRecord record) { var meta = GetTestMetaData(); meta.SetPropertyValue(TestSetupMetaData.Fields.LaboratoryContactEmail, record.LabratoryContactEmail); meta.SetPropertyValue(TestSetupMetaData.Fields.LaboratoryContactFax, record.LabratoryContactFax); meta.SetPropertyValue(TestSetupMetaData.Fields.LaboratoryContactName, record.LabratoryContactName); meta.SetPropertyValue(TestSetupMetaData.Fields.LaboratoryContactPhone, record.LabratoryContactPhone); meta.SetPropertyValue(TestSetupMetaData.Fields.LaboratoryName, record.LabratoryName); meta.SetPropertyValue(TestSetupMetaData.Fields.LaboratoryProjectReferenceNumber, record.LabratoryProjectRefNumber); meta.SetPropertyValue(TestSetupMetaData.Fields.LaboratoryTestReferenceNumber, record.LabratoryTestRefNumber); } /// /// returns the error messages for a test setup /// calculated at the same time is complete is calculated, however doesn't have a check against /// is dirty, like IsComplete does ... /// public string CompletionErrorMessage => ErrorMessage.Length > 250 ? ErrorMessage.Substring(0, 250) : ErrorMessage; /// /// returns whether the test setup is complete or not. /// WILL FORCE IsComplete TO BE CALCULATED IF IsDirty is true /// (you might not want this if you are adding a new test setup for instance...) /// public bool IsComplete { get { if (string.IsNullOrWhiteSpace(Name)) { return false; } //TODO: review property functionality if (IsDirty) { CalculateIsComplete(); } return base.IsComplete; } set => base.IsComplete = value; } public bool IsDirty { get => Dirty; set => Dirty = value; } private DTS.Serialization.Test _test; public DTS.Serialization.Test Test { get => _test; set => SetProperty(ref _test, value, TestTemplateTags.Test.ToString()); } private Dictionary _dasLookup; private Dictionary _filterLookup = new Dictionary(); public Dictionary FilterLookup { get => _filterLookup; set => _filterLookup = value; } /// /// excitation warmup time /// can be defined at test level, test object level, (and maybe at hardware or channel level?) /// if defined here, we return it right away, otherwise we pass it to the max of the test objets /// public int ExcitationWarmupTimeMS { get => ExcitationWarmup; set { if (int.MinValue != value) { foreach (var to in TestObjects) { to.ExcitationWarmupTimeMS = value; } ExcitationWarmup = value; OnPropertyChanged("ExcitationWarmupTimeMS"); } } } private List _testGraphs = new List(); public TestGraph[] TestGraphs { get => _testGraphs.ToArray(); set { SetProperty(ref _testGraphs, new List(value), TestTemplateTags.TestGraphs.ToString()); OnPropertyChanged(TestTemplateTags.GraphCount.ToString()); } } public int GraphCount => _testGraphs.Count; public void AddGroup(IGroup group, IChannelSetting[] channelDefaults) { var sensorLookup = new Dictionary(); var sensors = SensorsCollection.SensorsList.GetAllSensors(false); foreach (var s in sensors) { sensorLookup[s.DatabaseId] = s; } var hardwareLookup = new Dictionary(); var hardware = DASHardwareList.GetAllHardware(); foreach (var h in hardware) { hardwareLookup[h.DASId] = h; } AddGroup(group, sensorLookup, hardwareLookup, channelDefaults); OnPropertyChanged("Groups"); } public void AddGroup(IGroup group, IDictionary channelLookup, ISensorData[] sensors = null) { var sensorLookup = new Dictionary(); if (null == sensors) { sensors = SensorsCollection.SensorsList.GetAllSensors(false); } foreach (var s in sensors) { sensorLookup[s.DatabaseId] = s; } _groups.Add(group); AddGroup(group, sensorLookup, channelLookup); OnPropertyChanged("Groups"); } public TestGraph CurrentGraph { get { var selected = (from g in TestGraphs.AsParallel() where g.Selected select g); if (selected.Any()) { return selected.First(); } return null; } } public new bool UseCustomerDetails { get { var customerOverride = CustomerOverride; return !IsBlank(customerOverride); } } private const string NOVALUE = "NOVALUE"; private bool IsBlank(CustomerDetailsDbRecord record) { if (!string.IsNullOrEmpty(record.Name)) { return false; } if (!string.IsNullOrWhiteSpace(record.CustomerName) && !NOVALUE.Equals(record.CustomerName)) { return false; } if (!string.IsNullOrWhiteSpace(record.CustomerOrderNumber) && !NOVALUE.Equals(record.CustomerOrderNumber)) { return false; } if (!string.IsNullOrWhiteSpace(record.CustomerTestRefNumber) && !NOVALUE.Equals(record.CustomerTestRefNumber)) { return false; } if (!string.IsNullOrWhiteSpace(record.ProjectRefNumber) && !NOVALUE.Equals(record.ProjectRefNumber)) { return false; } return true; } public new bool UseTestEngineerDetails { get { var overrideEngineer = EngineerOverride; return !IsBlank(overrideEngineer); } } private bool IsBlank(TestEngineerDetailsDbRecord record) { if (!string.IsNullOrWhiteSpace(record.Name)) { return false; } if (!string.IsNullOrWhiteSpace(record.TestEngineerEmail) && !NOVALUE.Equals(record.TestEngineerEmail)) { return false; } if (!string.IsNullOrWhiteSpace(record.TestEngineerFax) && !NOVALUE.Equals(record.TestEngineerFax)) { return false; } if (!string.IsNullOrWhiteSpace(record.TestEngineerName) && !NOVALUE.Equals(record.TestEngineerName)) { return false; } if (!string.IsNullOrWhiteSpace(record.TestEngineerPhone) && !NOVALUE.Equals(record.TestEngineerPhone)) { return false; } return true; } public new bool UseLabratoryDetails { get { var labOverride = LabOverride; return !IsBlank(labOverride); } } private bool IsBlank(LabratoryDetailsDbRecord record) { if (!string.IsNullOrWhiteSpace(record.Name)) { return false; } if (!string.IsNullOrWhiteSpace(record.LabratoryContactEmail) && !NOVALUE.Equals(record.LabratoryContactEmail)) { return false; } if (!string.IsNullOrWhiteSpace(record.LabratoryContactFax) && !NOVALUE.Equals(record.LabratoryContactFax)) { return false; } if (!string.IsNullOrWhiteSpace(record.LabratoryContactName) && !NOVALUE.Equals(record.LabratoryContactName)) { return false; } if (!string.IsNullOrWhiteSpace(record.LabratoryContactPhone) & !NOVALUE.Equals(record.LabratoryContactPhone)) { return false; } if (!string.IsNullOrWhiteSpace(record.LabratoryName) && !NOVALUE.Equals(record.LabratoryName)) { return false; } if (!string.IsNullOrWhiteSpace(record.LabratoryProjectRefNumber) && !NOVALUE.Equals(record.LabratoryProjectRefNumber)) { return false; } if (!string.IsNullOrWhiteSpace(record.LabratoryTestRefNumber) && !NOVALUE.Equals(record.LabratoryTestRefNumber)) { return false; } return true; } 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(); } } //assumes list is in order and sets display orders accordingly private bool SampleRatesAreMixed() { try { var currentTestHardware = GetHardware(); var firstSampleRate = 0.0D; if (currentTestHardware.Length > 0) { firstSampleRate = DASSampleRateList[currentTestHardware[0].SerialNumber]; } //Determine if any are different by comparing to the first ([0]). foreach (var das in currentTestHardware) { if (DASSampleRateList[das.SerialNumber] != firstSampleRate) { return true; } } return false; } catch (Exception) { //found an issue when working on //16117 Sensors and groups tables not populated when using recovery controls feature in download tab //where the xml could contain many hardware, but the test wouldn't have definitions for them (at least not the first one) //so this approach may be safer when that happens, //but I didn't feel confident to make it the default behavior ... if (DASAAFRateList.Values.ToArray().Distinct().Count() > 1) { return true; } return false; } } public List TestObjectsAndAddedGroupsList = new List(); public TestTestObject[] TestObjectsAndAddedGroups => TestObjectsAndAddedGroupsList.ToArray(); private List _testObjects = new List(); public TestTestObject[] TestObjects { get => _testObjects.ToArray(); set => SetTestObjects(false, value); } 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 List _otherAddedGroups = new List(); public TestTestObject[] OtherAddedGroups { get => _otherAddedGroups.ToArray(); set => _otherAddedGroups = value.ToList(); } public double MaxSampleRate { get { if (0 < TestObjects.Length || 0 < AddedGroups.Length) { return (from TestObject to in AddedGroups from h in to.Hardware select h.GetMaxSampleRateDouble()).Concat(new[] { double.MaxValue }).Min(); } return double.MaxValue; } } public double MinSampleRate { get { if (0 < TestObjects.Length || 0 < AddedGroups.Length) { return (from TestObject to in AddedGroups from h in to.Hardware select h.GetMinSampleRateDouble()).Concat(new[] { 0.00 }).Max(); } return 0; } } private List _allTestObjects = new List(); public TestTestObject[] AllTestObjects { get { _allTestObjects = new List(); foreach (var to in TestObjects) { _allTestObjects.Add(to); } foreach (var to in AddedGroups) { if (to.Hardware.Any()) { _allTestObjects.Add(to); } } return _allTestObjects.ToArray(); } set { SetProperty(ref _allTestObjects, new List(value), TestTemplateTags.AllTestObjects.ToString()); OnPropertyChanged(TestTemplateTags.AvailableTestObjects.ToString()); } } public string SampleRateText { get => SamplesPerSecondAggregate.ToString(); set => SamplesPerSecondAggregate = double.Parse(value); } public Visibility ROIButtonVisibility => DoROIDownload ? Visibility.Visible : Visibility.Collapsed; public Visibility ViewROIDownloadVisibility => ViewROIDownload ? Visibility.Visible : Visibility.Collapsed; public Visibility ViewRealtimeButtonVisibility => ViewRealtime ? Visibility.Visible : Visibility.Collapsed; public Visibility DownloadAllButtonVisibility => DownloadAll ? Visibility.Visible : Visibility.Collapsed; public Visibility VerifyButtonVisibility => VerifyChannels ? Visibility.Visible : Visibility.Collapsed; public Visibility ViewDownloadAllButtonVisibility => ViewDownloadAll ? Visibility.Visible : Visibility.Collapsed; public string SPSText => SampleRatesAreMixed() ? StringResources.TestTemplate_MultipleSampleRates : SamplesPerSecondAggregate.ToString("N0"); public string RecordingModeText { get { switch (RecordingMode) { case RecordingModes.CircularBuffer: return StringResources.RecordingModes_CircularBuffer; case RecordingModes.RAMActive: return StringResources.RecordingModes_RAMActive; case RecordingModes.MultipleEventRAMActive: return StringResources.RecordingModes_MultipleEventRAMActive; case RecordingModes.CircularBufferAndStreamSubSample: return StringResources.RecordingModes_CircularBufferAndStreamSubSample; case RecordingModes.CircularBufferPlusUART: return StringResources.RecordingModes_CircularBufferPlusUART; case RecordingModes.Recorder: return StringResources.RecordingModes_Recorder; case RecordingModes.RecorderAndStreamSubSample: return StringResources.RecordingModes_RecorderAndStreamSubSample; case RecordingModes.RecorderPlusUART: return StringResources.RecordingModes_RecorderPlusUART; case RecordingModes.HybridRecorder: return StringResources.RecordingModes_HybridRecorder; case RecordingModes.ContinuousRecorder: return StringResources.RecordingModes_ContinuousRecorder; case RecordingModes.S6A_DeviceStreamingOnly: return StringResources.RecordingModes_S6A_DeviceStreamingOnly; case RecordingModes.Active: return StringResources.RecordingModes_Active; //FB 6399 Multiple Event support case RecordingModes.MultipleEventActive: return StringResources.RecordingModes_MultipleEventActive; case RecordingModes.MultipleEventCircularBuffer: return StringResources.RecordingModes_MultipleEventCircularBuffer; case RecordingModes.MultipleEventRecorder: return StringResources.RecordingModes_MultipleEventRecorder; case RecordingModes.MultipleEventHybridRecorder: return StringResources.RecordingModes_MultipleEventHybridRecorder; case RecordingModes.MultipleEventCircularBufferPlusUART: return StringResources.RecordingModes_MultipleEventCircularBufferPlusUART; case RecordingModes.MultipleEventRecorderPlusUART: return StringResources.RecordingModes_MultipleEventRecorderPlusUART; case RecordingModes.ContinuousRecorderPlusUART: return StringResources.RecordingModes_ContinuousRecorderPlusUART; case RecordingModes.Streaming: return StringResources.RecordingModes_Streaming; case RecordingModes.Scheduled: return StringResources.RecordingModes_Scheduled; case RecordingModes.Interval: return StringResources.RecordingModes_Interval; case RecordingModes.RecordOnBoot: return StringResources.RecordingModes_RecordOnBoot; case RecordingModes.RecordOnBootPlusUART: return StringResources.RecordingModes_RecordOnBootPlusUART; case RecordingModes.HybridAndStream: return StringResources.RecordingModes_HybridAndStream; case RecordingModes.MultipleEventHybridAndStream: return StringResources.RecordingModes_MultipleEventHybridAndStream; case RecordingModes.MultipleEventCircularBufferAndStream: return StringResources.RecordingModes_MultipleEventCircularBufferAndStream; case RecordingModes.MultipleEventRecorderAndStream: return StringResources.RecordingModes_MultipleEventRecorderAndStream; } return "N/A"; } } public string PreAndPostTriggerTimeText { get { if (double.IsNaN(PreTriggerSeconds) || double.IsNaN(PostTriggerSeconds)) { return StringResources.TestTemplate_MultipleRecordingPeriods; } return string.Format(StringResources.TestTemplate_RecordingPeriodText, PreTriggerSeconds, PostTriggerSeconds); } } private LabratoryDetails _labDetails; public new LabratoryDetails LabDetails { get => _labDetails; set { base.LabDetails = null == value ? string.Empty : value.Name; SetProperty(ref _labDetails, value, TestTemplateTags.LabDetails.ToString()); } } private CustomerDetails _customerDetails; public new CustomerDetails CustomerDetails { get => _customerDetails; set { base.CustomerDetails = null == value ? string.Empty : value.Name; SetProperty(ref _customerDetails, value, TestTemplateTags.CustomerDetails.ToString()); } } private TestEngineerDetails _testEngineerDetails; public new TestEngineerDetails TestEngineerDetails { get => _testEngineerDetails; set { base.TestEngineerDetails = null == value ? string.Empty : value.Name; SetProperty(ref _testEngineerDetails, value, TestTemplateTags.TestEngineerDetails.ToString()); } } private CustomerDetails[] _allCustomers = null; private static readonly object CustomersLock = new object(); public CustomerDetails[] AllCustomers { get { lock (CustomersLock) { if (null == _allCustomers) { _allCustomers = CustomerDetailsList.GetAllCustomers(); } return _allCustomers; } } } private readonly TestEngineerDetailsList _testEngineers = TestEngineerDetailsList.TestEngineerList; public TestEngineerDetails[] AllTestEngineers => _testEngineers.TestEngineers; private static readonly object LabLock = new object(); private LabratoryDetails[] _allLabs = null; public LabratoryDetails[] AllLabs { get { lock (LabLock) { if (null == _allLabs) { _allLabs = LabratoryDetailsList.GetAllLabs(); } } return _allLabs; } } public new int DefaultNumberRealtimeGraphs { get { //FB 13750 return 1 in UseTestChannelOrder mode if (DataModelSettings.UseTestChannelOrder) { return 1; } switch (base.DefaultNumberRealtimeGraphs) { case 6: return 6; case 3: return 3; case 1: default: return 1; } } set { base.DefaultNumberRealtimeGraphs = Convert.ToInt16(value); OnPropertyChanged(TestTemplateTags.DefaultNumberRealtimeGraphs.ToString()); } } public Visibility GraphDetailsVisibility { get { if (TestGraphs.Length < 1) { return Visibility.Collapsed; } var selected = from g in TestGraphs.AsParallel() where g.Selected select g; return selected.Any() ? Visibility.Visible : Visibility.Collapsed; } set { OnPropertyChanged(TestTemplateTags.GraphDetailsVisibility.ToString()); OnPropertyChanged(TestTemplateTags.CurrentGraph.ToString()); } } public int ChannelCount { get { lock (CountLock) { var channels = GetChannels().Select(ch => !ch.IsBlank() && ch.SensorValid); return channels.Count(); } } } private int _includedChannelCount; public int IncludedChannelCount { get { lock (CountLock) { _includedChannelCount = 0; { foreach (var to in TestObjects) { foreach (var ch in to.GetISOTestObject().AllChannels) { if (ch.Required && CheckedDASList.Contains(ch.GetDASId())) { _includedChannelCount++; } } } foreach (var to in AddedGroups) { foreach (var ch in to.GetISOTestObject().AllChannels) { if (ch.Required && CheckedDASList.Contains(ch.GetDASId())) { _includedChannelCount++; } } } } } return _includedChannelCount; } } #endregion Properties /// /// if you use rename serial number method to create a new test setup, we have to go and recreate the groups. /// groups are supposed to be unique to a test setup, and we don't want them to unintentionally share groups /// whether operating on static or dynamic groups /// public void Rename(bool bAddedGroups) { var objectsOut = new List(); var objectsIn = new List(); var oldIdToNewId = new Dictionary(); var oldGroupToNewGroupSerialNumber = new Dictionary(); if (bAddedGroups) { objectsIn.AddRange(AddedGroups); } else { objectsIn.AddRange(TestObjects); } foreach (var group in objectsIn) { TestTestObject ttoNew; if (bAddedGroups) { ttoNew = new TestTestObject(group); //note this, appears to be done in multiple disparate places, not sure why it's not done in one place //I'm copying it out of EditTestSetupObjectsControl, I don't have enough time to unify it all into one place right now //so I'll just add an issue to correct that later. ttoNew.SerialNumber = Name + "_" + ttoNew.SerialNumberConverted; ttoNew.Template.TemplateName = Guid.NewGuid().ToString(); ttoNew.Template.OriginalTemplateName = ttoNew.SerialNumber + "_Template"; var oldMetaData = GetTestMetaData(); oldMetaData.SetPropertyValue(TestSetupMetaData.Fields.Title, Name); } else { ttoNew = new TestTestObject(group, true); } objectsOut.Add(ttoNew); var isoOld = group.GetISOTestObject(); var isoNew = ttoNew.GetISOTestObject(); oldGroupToNewGroupSerialNumber[isoOld.SerialNumber] = isoNew.SerialNumber; for (var i = 0; i < isoOld.AllChannels.Length && i < isoNew.AllChannels.Length; i++) { var ch = isoOld.AllChannels[i]; if (!ch.Required) { continue; } var oldId = ch.GetId(); var newId = isoNew.AllChannels[i].GetId(); oldIdToNewId[oldId] = newId; if (!bAddedGroups) continue; isoNew.Template = ttoNew.Template.TemplateName; isoNew.OriginalTemplate = isoNew.OriginalTemplate.Replace(group.TestSetupName, Name); isoNew.OriginalSerialNumber = isoNew.OriginalSerialNumber.Replace(group.TestSetupName, Name); } } if (oldIdToNewId.Count > 0) { ReplaceLevelTriggerChannel(oldIdToNewId, oldGroupToNewGroupSerialNumber); ReplaceCalculatedChannel(oldIdToNewId); ReplaceChannelSetting(oldIdToNewId, oldGroupToNewGroupSerialNumber); } if (bAddedGroups) { AddedGroups = objectsOut.ToArray(); } else { TestObjects = objectsOut.ToArray(); } } public int GetMaxDisplayOrder() { var existingObjects = new List(TestObjects); existingObjects.AddRange(AddedGroups); return existingObjects.Aggregate(0, (current1, to) => to.GetISOTestObject().AllChannels.Aggregate(current1, (current, ch) => Math.Max(current, ch.DisplayOrder))); } /// /// returns true if the test has multiple sample rates for recording, false otherwise /// /// public bool HasMultipleSampleRates() { var firstSampleRate = 0.0D; var currentTestHardware = GetHardware(); if (currentTestHardware.Length > 0) { firstSampleRate = DASSampleRateList[currentTestHardware[0].SerialNumber]; } foreach (var das in currentTestHardware) { var dasType = das.GetHardwareTypeEnum(); var rate = DASSampleRateList[das.SerialNumber]; if (rate != firstSampleRate) { return true; } } return false; } public bool HasOBRDDR() { var currentTestHardware = GetHardware(); return Array.Exists(currentTestHardware, das => das.DASTypeEnum == HardwareTypes.S6A_EthernetRecorder); } #region LEVEL_TRIGGER public Dictionary LevelTriggerChannels = new Dictionary(); // ReSharper disable once InconsistentNaming public string GetLTKey(LevelTriggerChannel ch) { return $"{ch.HardwareChannelId}x{ch.SensorSerialNumber}"; } public void RemoveLevelTrigger(LevelTriggerChannel channel) { var key = GetLTKey(channel); if (LevelTriggerChannels.ContainsKey(key)) { LevelTriggerChannels.Remove(key); } } public void SetLevelTrigger(LevelTriggerChannel channel) { var key = GetLTKey(channel); LevelTriggerChannels[key] = new LevelTriggerChannel(channel);//do a copy just for safety if (null == channel.GroupChannel) { //lookup the channel? //issue discovered by Nate, no issue # yet if (long.TryParse(channel.GroupChannelId, out var l)) { if (l > 0) { using (var eGroups = ChannelsForGroup.GetEnumerator()) { while (eGroups.MoveNext()) { var channels = eGroups.Current.Value; if (!channels.Any()) { continue; } var matches = from ch in channels where ch.Id == l select ch; if (matches.Any()) { var match = matches.First(); if (null != match) { LevelTriggerChannels[key].GroupChannel = match; break; } } } } } } } else { LevelTriggerChannels[key].GroupChannel = channel.GroupChannel; } } public LevelTriggerChannel GetLevelTrigger(LevelTriggerChannel channel) { var key = GetLTKey(channel); return LevelTriggerChannels.ContainsKey(key) ? LevelTriggerChannels[key] : null; } /// /// replaces any level triggers that have a specific id, with another /// brand new id /// /// /// public void ReplaceLevelTriggerChannel(string oldId, string newId) { using (var e = LevelTriggerChannels.GetEnumerator()) { while (e.MoveNext()) { if (e.Current.Value.GroupChannelId == oldId) { e.Current.Value.GroupChannelId = newId; } } } } /// /// scans through all level triggers, looks for any level triggers that might be associated with an old group serial number /// and replaces those references with the new group serial number /// /// /// public void ReplaceLevelTriggerChannelGroupMapping(string oldGroupSerial, string newGroupSerial) { var allLevelTriggers = LevelTriggerChannels.Values.ToArray(); LevelTriggerChannels.Clear(); foreach (var lt in allLevelTriggers) { var key = GetLTKey(lt); var tokens = key.Split('x'); if (tokens.Length < 3) continue;//invalid if (tokens[0] == oldGroupSerial) { lt.GroupChannelId = lt.GroupChannelId.Replace(oldGroupSerial, newGroupSerial); tokens[0] = newGroupSerial; tokens[1] = tokens[1].Replace(oldGroupSerial, newGroupSerial); } LevelTriggerChannels[string.Join("x", tokens)] = lt; } } /// /// updates the channel settings for groups which have changed serial numbers [rename/clone group] /// 8404 Copy all channel parameters/settings/level triggers/graphs when cloning a test setup /// /// /// public void ReplaceChannelSetting(Dictionary oldIdToNewIdLookup, Dictionary oldGroupSnToNewGroupSn) { using (var e = oldGroupSnToNewGroupSn.GetEnumerator()) { while (e.MoveNext()) { lock (SensorLock) { if (SensorLookup.ContainsKey(e.Current.Key)) { var lookup = SensorLookup[e.Current.Key]; SensorLookup[e.Current.Value] = lookup; } } } } } public void ReplaceLevelTriggerChannel(Dictionary oldIdToNewIdLookup, Dictionary oldGroupSnToNewGroupSn) { var itemsToUpdate = new List(); using (var e = LevelTriggerChannels.GetEnumerator()) { while (e.MoveNext()) { if (!oldIdToNewIdLookup.ContainsKey(e.Current.Value.GroupChannelId)) continue; e.Current.Value.GroupChannelId = oldIdToNewIdLookup[e.Current.Value.GroupChannelId]; itemsToUpdate.Add(e.Current.Key); } } foreach (var item in itemsToUpdate) { var original = LevelTriggerChannels[item]; LevelTriggerChannels.Remove(item); LevelTriggerChannels[GetLTKey(original)] = original; } } public void RenameLevelTriggerChannels(string oldName, string newName) { var itemsToUpdate = new List(); using (var e = LevelTriggerChannels.GetEnumerator()) { while (e.MoveNext()) { if (!e.Current.Value.GroupChannelId.StartsWith(oldName)) continue; e.Current.Value.GroupChannelId = e.Current.Value.GroupChannelId.Replace(oldName, newName); itemsToUpdate.Add(e.Current.Key); } } foreach (var item in itemsToUpdate) { var original = LevelTriggerChannels[item]; LevelTriggerChannels.Remove(item); LevelTriggerChannels[GetLTKey(original)] = original; } } public void RemoveImpossibleLevelTriggers() { if (0 == LevelTriggerChannels.Count) { return; } var hardwareChannelLookup = new Dictionary(); var dasAlreadyProcessed = new Dictionary(); //var groupChannelIdLookup = new Dictionary(); var groupChannelIdLookup = new Dictionary(); var sensorLookup = new Dictionary(); var hardwareLookup = new Dictionary(); var sensors = SensorsCollection.SensorsList.GetAllSensors(false); foreach (var s in sensors) { sensorLookup[s.DatabaseId] = s; } var hardware = DASHardwareList.GetAllHardware(); foreach (var h in hardware) { hardwareLookup[h.DASId] = h; } var channelDefaults = DbOperations.GetChannelSettingDefaults(); foreach (var group in Groups) { //FB 18875 use the hardware list instead of query them on each loop iteration foreach (var h in hardware.Where(hw => group.IncludedHardware.Contains(hw.DASId))) { if (dasAlreadyProcessed.ContainsKey(h.SerialNumber)) continue; dasAlreadyProcessed.Add(h.SerialNumber, true); foreach (var ch in h.Channels) { hardwareChannelLookup[ch.GetId()] = ch; } } foreach (var ch in group.GetAllChannels(false, sensorLookup, hardwareLookup, channelDefaults)) { if (!string.IsNullOrWhiteSpace(ch.Sensor) && !string.IsNullOrWhiteSpace(ch.Hardware)) { groupChannelIdLookup[ch.Id] = ch; } } } var invalidLTs = new List(); foreach (var ltc in LevelTriggerChannels) { var lt = ltc.Value; //we shouldn't get here but if we do, don't die if (null == lt.GroupChannel) { continue; } if (!groupChannelIdLookup.ContainsKey(lt.GroupChannel.Id)) { invalidLTs.Add(lt); } else if (!hardwareChannelLookup.ContainsKey(lt.HardwareChannelId)) { invalidLTs.Add(lt); } else { //check that the sensor is valid if (null != groupChannelIdLookup[lt.GroupChannel.Id].SensorData) { var sd = groupChannelIdLookup[lt.GroupChannel.Id].SensorData; if (!SensorConstants.IsTestSpecificEmbedded(sd.SerialNumber) && lt.SensorSerialNumber != sd.SerialNumber) { APILogger.Log($"Eliminated level trigger for channel as {lt.SensorSerialNumber} is no longer the sensor on the channel"); invalidLTs.Add(lt); } } //check that the hardware is the same var groupChannel = groupChannelIdLookup[lt.GroupChannel.Id]; if ((groupChannel.DASId != lt.GroupChannel.DASId || groupChannel.DASChannelIndex != lt.GroupChannel.DASChannelIndex) && !invalidLTs.Contains(lt)) { APILogger.Log($"Eliminated level trigger as hardware [{lt.HardwareChannelId}] is no longer valid"); invalidLTs.Add(lt); } } } foreach (var invalid in invalidLTs) { RemoveLevelTrigger(invalid); } } #endregion LEVEL_TRIGGER #region Sensor /// /// get sensor from sensor db again (but preserving custom values such as range/polarity/CFC/etc) /// note, test objects already get sensors from from db every time. we hold onto them here, I guess for efficiency, but we could get them fresh every time as well /// public void RefreshSensorsFromDb() { lock (SensorLock) { //MarkIsCompleteUnchecked(); SensorLookup.Clear(); if (string.IsNullOrEmpty(Name)) return; using (var cmd = DbOperations.GetSQLCommand(true)) { try { cmd.CommandType = CommandType.StoredProcedure; cmd.CommandText = DbOperationsEnum.StoredProcedure.sp_TestChannelSettingsGet.ToString(); cmd.Parameters.Add(new SqlParameter("@TestSetupName", SqlDbType.NVarChar) { Value = Name }); cmd.Parameters.Add(new SqlParameter("@TestObjectName", SqlDbType.NVarChar) { Value = null }); cmd.Parameters.Add(new SqlParameter("@ChannelId", SqlDbType.NVarChar) { Value = null }); //cmd.ExecuteNonQuery(); using (var ds2 = DbOperations.Connection.QueryDataSet(cmd)) { if (ds2.Tables.Count <= 0 || ds2.Tables[0].Rows.Count <= 0) return; var cFields = Enum.GetValues(typeof(DbOperations.TestSetups.ChannelSettingFields)) .Cast().ToArray(); foreach (DataRow row in ds2.Tables[0].Rows) { string testobjectname = null; string channelname = null; string sensorserial = null; string setting = null; var disabled = false; foreach (var cf in cFields) { switch (cf) { case DbOperations.TestSetups.ChannelSettingFields.TestName: break; case DbOperations.TestSetups.ChannelSettingFields.Setting: setting = row[cf.ToString()] as string; break; case DbOperations.TestSetups.ChannelSettingFields.TestObjectName: testobjectname = row[cf.ToString()] as string; break; case DbOperations.TestSetups.ChannelSettingFields.ChannelId: channelname = row[cf.ToString()] as string; break; case DbOperations.TestSetups.ChannelSettingFields.SensorSerial: sensorserial = row[cf.ToString()] as string; break; case DbOperations.TestSetups.ChannelSettingFields.Disabled: disabled = Convert.ToBoolean(row[cf.ToString()]); break; } } SetDisabled(testobjectname, channelname, disabled); if (string.IsNullOrEmpty(testobjectname) || string.IsNullOrEmpty(channelname) || string.IsNullOrEmpty(setting)) continue; try { var sd = TestTemplateList.GetSensorFromSettings(setting, sensorserial); if (null != sd && sd.FilterClassIso == "?") { sd.FilterClassIso = "P"; // "Prefiltered > CFC 1000" (Unfiltered) } if (null != sd) { SetSensor(sd, testobjectname, channelname); } } catch (Exception ex) { APILogger.Log("failed to retreive sensor, ", ex); } } } } finally { cmd.Connection.Dispose(); } } } } /// /// 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(DTS.Common.Interface.Channels.IGroupChannel groupChannel, IReadOnlyDictionary calLookup = null) { //FB 18875 added cached sensor calibration var sd = SensorsCollection.SensorsList.GetSensorById(groupChannel.SensorId, true, calLookup); return (SensorData)groupChannel.Group.GetSensor(groupChannel, sd, SerializedSettings.UseISOCodeFilterMapping); } public SensorData GetSensor(string serialNumber, string testObjectSerial, string channelName) { lock (SensorLock) { var tto = GetTestTestObject(testObjectSerial); 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 new SensorData(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 new SensorData(SensorLookup[testObjectSerial][channelName][serialNumber]); } } } if (null == tto) { return new SensorData(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 null; } return new SensorData(ttosd); } } // FB5438 & FB5023 public void RemoveSensor(string sensorserialnumber, string testobjectserial, string channelname) { lock (SensorLock) { if (SensorLookup.ContainsKey(testobjectserial) && SensorLookup[testobjectserial].ContainsKey(channelname) && SensorLookup[testobjectserial][channelname].ContainsKey(sensorserialnumber)) { SensorLookup[testobjectserial][channelname].Remove(sensorserialnumber); } } } /// /// sets a sensor in the lookup for this test setup, however preserves any existing settings for that sensor /// this is done because we want to preserve the range and other custom settings for a test setup, but do want to /// get any of the non customizable information (like capacity and sensitivity) updated in the copy in memory. /// /// /// /// public void SetSensorWithoutChangingSettings(SensorData sd, string testobjectserial, string channelname) { var sdOld = GetSensor(sd.SerialNumber, testobjectserial, channelname); var settings = TestTemplateList.GetSensorSettings(sdOld); sd = TestTemplateList.GetSensorFromSettings(settings, sd.SerialNumber); SetSensor(sd, testobjectserial, channelname); } 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; } } public void ReplaceSensorsInLookup(TestTestObject group, string oldName, string newName) { foreach (var ch in group.GetISOTestObject().AllChannels) { if (ch.Required) { var sd = GetSensor(ch.SensorSerialNumber, group.SerialNumber.Replace(newName, oldName), ch.Name); if (sd == null) continue; SetSensor(sd, group.SerialNumber, ch.Name); RemoveSensor(sd.SerialNumber, group.SerialNumber.Replace(newName, oldName), ch.Name); } } } #endregion Sensor #region XML #region READING FROM XML /// /// reads a test template node from xml /// if we are doing an import, some objects like test objects, customers, and labs are also going to be in the import /// and not in the db yet /// /// node to process /// /// /// /// /// public void ReadXML(System.Xml.XmlElement root, Dictionary testObjectLookup, Dictionary groupLookup, Dictionary labLookup, Dictionary customerLookup, Dictionary testEngineerLookup, Dictionary sensorLookup, List hardware, List sensorCalibrations) { var allHardware = DASHardwareList.GetAllHardware(); var hwlookup = new Dictionary(); foreach (var h in allHardware) { hwlookup[h.DASId] = h; } //gets a list of channel defaults from the db //the import will override with any specific settings //would cause issues with the channel display in BasicInfo with //16117 Sensors and groups tables not populated when using recovery controls feature in download tab //otherwise with missing settings var channelDefaults = DbOperations.GetChannelSettingDefaults(); foreach (var node in root.ChildNodes) { if (!(node is System.Xml.XmlElement child)) { continue; } switch (child.Name) { //15727 Building and Replacing Racks/Mods case TESTID_ROOT: //first started in version 9 { if (int.TryParse(child.InnerText, out var id)) { Id = id; } } break; case DASLIST_ROOT://first started in version 4 { foreach (var sub in child.ChildNodes) { if (sub is System.Xml.XmlElement) { ProcessDASHardwareNode(sub as System.Xml.XmlElement); } } } break; case GROUPS_ROOT: //Groups first started in Version 3 { foreach (var sub in child.ChildNodes) { if (sub is System.Xml.XmlElement) { ProcessGroupNode(sub as System.Xml.XmlElement, groupLookup, sensorLookup, channelDefaults, sensorCalibrations); } } } break; case GRAPHS_ROOT: { var lookup = new Dictionary(); foreach (var group in Groups) { var channels = ChannelsForGroup[group]; foreach (var channel in channels) { lookup[channel.Id] = channel; } } foreach (var sub in child.ChildNodes) { if (sub is System.Xml.XmlElement) { ProcessGraphs(sub as System.Xml.XmlElement, lookup); } } } break; case FIELDS_ROOT: { var channelLookup = new Dictionary(); foreach (var group in Groups) { var channels = ChannelsForGroup[group]; foreach (var channel in channels) { channelLookup[channel.Id] = channel; } } foreach (var sub in child.ChildNodes) { if (sub is System.Xml.XmlElement) { ProcessFields(sub as System.Xml.XmlElement, customerLookup, testEngineerLookup, labLookup, sensorLookup, hardware, channelLookup); } } } break; case METADATAS_ROOT: { ProcessMetaDatas(child); } break; case HARDWAREOVERRIDES_ROOT://no longer used in version 3 { foreach (var sub in child.ChildNodes) { if (sub is System.Xml.XmlElement) { ProcessHardwareOverrides(sub as System.Xml.XmlElement); } } } break; case HARDWAREINCLUDES_ROOT://first started in version 3 { var s = child.GetAttribute(HARDWARELIST_ROOT); var tokens = s.Split(','); foreach (var token in tokens) { var id = Convert.ToInt32(token, CultureInfo.InvariantCulture); AddHardware(id, hwlookup); } } break; case HARDWAREREMOVES_ROOT://first started in version 3 { var s = child.GetAttribute(HARDWARELIST_ROOT); var tokens = s.Split(','); foreach (var token in tokens) { var id = Convert.ToInt32(token, CultureInfo.InvariantCulture); RemoveHardware(id, allHardware, hwlookup); } } break; case LEVELTRIGGERS_ROOT: { var lookup = new Dictionary(); foreach (var group in Groups) { var channels = ChannelsForGroup[group]; foreach (var channel in channels) { lookup[channel.Id] = channel; } } foreach (var sub in child.ChildNodes) { if (sub is System.Xml.XmlElement) { ProcessLevelTriggers(sub as System.Xml.XmlElement, lookup); } } } break; case CALCULATEDCHANNELS_ROOT: { var lookup = new Dictionary(); foreach (var group in Groups) { var channels = ChannelsForGroup[group]; foreach (var channel in channels) { lookup[channel.Id] = channel; } } foreach (var sub in child.ChildNodes) { if (sub is System.Xml.XmlElement) { ProcessCalculatedChannels(sub as System.Xml.XmlElement, lookup); } } } break; case TSRAIRSETTINGS_ROOT: ReadTSRAirSettings(child); break; default: throw new NotSupportedException("TestTemplate::ReadXML unsupported field: " + child.Name); } } } /// /// reads a test template node from xml /// if we are doing an import, some objects like test objects, customers, and labs are also going to be in the import /// and not in the db yet /// /// node to process /// /// /// /// /// public void ReadXMLPre20(System.Xml.XmlElement root, Dictionary testObjectLookup, Dictionary labLookup, Dictionary customerLookup, Dictionary testEngineerLookup, Dictionary sensorLookup, List hardware) { foreach (var node in root.ChildNodes) { if (!(node is System.Xml.XmlElement child)) { continue; } switch (child.Name) { case TESTOBJECTS_ROOT: { foreach (var sub in child.ChildNodes) { if (sub is System.Xml.XmlElement) { ProcessTestObjectNode(sub as System.Xml.XmlElement, testObjectLookup, sensorLookup); } } } break; case ADDEDGROUPS_ROOT: case SYSBUILTTESTOBJECTS_ROOT: { foreach (var sub in child.ChildNodes) { if (sub is System.Xml.XmlElement) { ProcessAddedGroupsPre20(sub as System.Xml.XmlElement, testObjectLookup, sensorLookup); } } } break; case GRAPHS_ROOT: { foreach (var sub in child.ChildNodes) { if (sub is System.Xml.XmlElement) { ProcessGraphsPre20(sub as System.Xml.XmlElement); } } } break; case FIELDS_ROOT: { foreach (var sub in child.ChildNodes) { if (sub is System.Xml.XmlElement) { ProcessFields(sub as System.Xml.XmlElement, customerLookup, testEngineerLookup, labLookup, sensorLookup, hardware, null); } } } break; case METADATAS_ROOT: { ProcessMetaDatas(child); } break; case HARDWAREOVERRIDES_ROOT: { foreach (var sub in child.ChildNodes) { if (sub is System.Xml.XmlElement) { ProcessHardwareOverrides(sub as System.Xml.XmlElement); } } } break; case LEVELTRIGGERS_ROOT: { foreach (var sub in child.ChildNodes) { if (sub is System.Xml.XmlElement) { ProcessLevelTriggersPre20(sub as System.Xml.XmlElement); } } } break; case CALCULATEDCHANNELS_ROOT: { foreach (var sub in child.ChildNodes) { if (sub is System.Xml.XmlElement) { ProcessCalculatedChannelsPre20(sub as System.Xml.XmlElement); } } } break; default: throw new NotSupportedException("TestTemplate::ReadXML unsupported field: " + child.Name); } } } /// /// processes a test setup meta data node from xml /// /// private void ProcessMetaDatas(System.Xml.XmlElement root) { try { var fields = Enum.GetValues(typeof(DbOperations.TestSetups.TestObjectMetaDataFields)) .Cast().ToArray(); foreach (var child in root.ChildNodes) { if (!(child is System.Xml.XmlElement node)) { continue; } var values = new Dictionary(); foreach (var field in fields) { values[field] = node.GetAttribute(field.ToString()).Trim(); } // FB 17951 Prevent empty TestObject process var testObject = values[DbOperations.TestSetups.TestObjectMetaDataFields.TestObject]; if (string.IsNullOrWhiteSpace(testObject)) { continue; } else if ("_" == testObject) { var meta = GetTestMetaData(); if (!Enum.TryParse(values[DbOperations.TestSetups.TestObjectMetaDataFields.PropName], out TestSetupMetaData.Fields field)) continue; meta.SetPropertyValue(field, values[DbOperations.TestSetups.TestObjectMetaDataFields.PropValue]); System.Diagnostics.Trace.WriteLine("Set " + field + " to " + values[DbOperations.TestSetups.TestObjectMetaDataFields.PropValue]); } else { var meta = GetMetaData(values[DbOperations.TestSetups.TestObjectMetaDataFields.TestObject].First()); meta.SetPropertyValue(values[DbOperations.TestSetups.TestObjectMetaDataFields.PropName], values[DbOperations.TestSetups.TestObjectMetaDataFields.PropValue]); } } } catch (Exception ex) { APILogger.Log("ProcessMetaDatas: ", ex); throw; } } private CalibrationBehaviors ParseCalibrationBehavior(string txt) { if (Enum.TryParse(txt, out CalibrationBehaviors calBehavior)) { return calBehavior; } if (txt.ToLower().Equals("alwayslinear")) { return CalibrationBehaviors.LinearIfAvailable; } throw new Exception($"Calibration setting: {txt} unknown"); } /// /// processes test setup fields from a test setup node in xml /// /// /// /// /// private void ProcessFields(System.Xml.XmlElement node , Dictionary customerLookup , Dictionary testEngineerLookup, Dictionary labLookup, Dictionary sensorLookup, List hardware, Dictionary channelLookup) { if (Enum.TryParse(node.Name, out DbOperations.TestSetups.Fields field)) { switch (field) { case DbOperations.TestSetups.Fields.AllowMissingSensors: AllowMissingSensors = bool.Parse(node.InnerText); break; case DbOperations.TestSetups.Fields.AllowSensorIdToBlankChannel: AllowSensorIdToBlankChannel = bool.Parse(node.InnerText); break; case DbOperations.TestSetups.Fields.AutomaticProgressionDelayMS: AutomaticProgressionDelayMS = int.Parse(node.InnerText, CultureInfo.InvariantCulture); break; case DbOperations.TestSetups.Fields.AutomaticTestProgression: AutomaticProgression = Convert.ToBoolean(node.InnerText); break; case DbOperations.TestSetups.Fields.AutoVerifyChannels: AutoVerifyChannels = Convert.ToBoolean(node.InnerText); break; case DbOperations.TestSetups.Fields.CalibrationBehavior: CalibrationBehavior = ParseCalibrationBehavior(node.InnerText); break; case DbOperations.TestSetups.Fields.CommonStatusLine: CommonLine = Convert.ToBoolean(node.InnerText); break; case DbOperations.TestSetups.Fields.CustomerDetails: { if (!string.IsNullOrWhiteSpace(node.InnerText)) { CustomerDetails = customerLookup.ContainsKey(node.InnerText) ? new CustomerDetails(customerLookup[node.InnerText]) : CustomerDetailsList.GetCustomerDetail(node.InnerText); } } break; case DbOperations.TestSetups.Fields.TestEngineerDetails: { if (!string.IsNullOrWhiteSpace(node.InnerText)) { TestEngineerDetails = testEngineerLookup.ContainsKey(node.InnerText) ? new TestEngineerDetails(testEngineerLookup[node.InnerText]) : TestEngineerDetailsList.TestEngineerList.GetTestEngineerDetail(node.InnerText); } } break; case DbOperations.TestSetups.Fields.DownloadAll: DownloadAll = Convert.ToBoolean(node.InnerText); break; case DbOperations.TestSetups.Fields.DownloadFolder: { var downloadFolder = node.InnerText; if (downloadFolder == @"..\Data") { downloadFolder = @"..\..\Data"; } DownloadFolder = downloadFolder; } break; case DbOperations.TestSetups.Fields.Export: ViewExport = Convert.ToBoolean(node.InnerText); break; case DbOperations.TestSetups.Fields.ExportFolder: { var exportFolder = node.InnerText; if (exportFolder == @"..\Data") { exportFolder = @"..\..\Data"; } ExportFolder = exportFolder; } break; case DbOperations.TestSetups.Fields.ExportFormat: ExportFormats = (SupportedExportFormatBitFlags)Convert.ToUInt64(node.InnerText, CultureInfo.InvariantCulture); break; case DbOperations.TestSetups.Fields.InvertStart: InvertStartRecordCompletion = Convert.ToBoolean(node.InnerText); break; case DbOperations.TestSetups.Fields.InvertTrigger: InvertTriggerCompletion = Convert.ToBoolean(node.InnerText); break; case DbOperations.TestSetups.Fields.IgnoreShortedStart: IgnoreShortedStartCompletion = Convert.ToBoolean(node.InnerText); break; case DbOperations.TestSetups.Fields.IgnoreShortedTrigger: IgnoreShortedTriggerCompletion = Convert.ToBoolean(node.InnerText); break; case DbOperations.TestSetups.Fields.LabDetails: { if (!string.IsNullOrWhiteSpace(node.InnerText)) { LabDetails = labLookup.ContainsKey(node.InnerText) ? new LabratoryDetails(labLookup[node.InnerText]) : LabratoryDetailsList.GetLab(node.InnerText); } } break; case DbOperations.TestSetups.Fields.LastModified: LastModified = DateTime.Parse(node.InnerText, CultureInfo.InvariantCulture); break; case DbOperations.TestSetups.Fields.LastModifiedBy: LastModifiedBy = node.InnerText; break; case DbOperations.TestSetups.Fields.LocalOnly: LocalOnly = Convert.ToBoolean(node.InnerText); break; case DbOperations.TestSetups.Fields.PostTestDiagnostics: PostTestDiagnosticsLevel = Convert.ToBoolean(node.InnerText); break; case DbOperations.TestSetups.Fields.PostTriggerSeconds: PostTriggerSeconds = Convert.ToDouble(node.InnerText, CultureInfo.InvariantCulture); break; case DbOperations.TestSetups.Fields.PreTriggerSeconds: PreTriggerSeconds = Convert.ToDouble(node.InnerText, CultureInfo.InvariantCulture); break; case DbOperations.TestSetups.Fields.NumberOfEvents: NumberOfEvents = Convert.ToInt32(node.InnerText, CultureInfo.InvariantCulture); break; case DbOperations.TestSetups.Fields.WakeUpMotionTimeout: WakeUpMotionTimeout = Convert.ToInt32(node.InnerText, CultureInfo.InvariantCulture); break; case DbOperations.TestSetups.Fields.ScheduledStartDateTime: RTCScheduleStartDateTime = Convert.ToDateTime(node.InnerText, System.Globalization.CultureInfo.InvariantCulture); break; case DbOperations.TestSetups.Fields.IntervalBetweenEventStartsMinutes: IntervalBetweenEventStartsMinutes = Convert.ToInt32(node.InnerText, System.Globalization.CultureInfo.InvariantCulture); break; case DbOperations.TestSetups.Fields.StartWithEvent: StartWithEvent = Convert.ToBoolean(node.InnerText); break; case DbOperations.TestSetups.Fields.WakeUpWithMotion: WakeUpWithMotion = Convert.ToBoolean(node.InnerText); break; case DbOperations.TestSetups.Fields.RealtimePlotCount: DefaultNumberRealtimeGraphs = Convert.ToInt32(node.InnerText, CultureInfo.InvariantCulture); break; case DbOperations.TestSetups.Fields.RecordingMode: RecordingMode = (RecordingModes)Enum.Parse(typeof(RecordingModes), node.InnerText); break; case DbOperations.TestSetups.Fields.RequireConfirmationOnErrors: RequireUserConfirmationOnErrors = Convert.ToBoolean(node.InnerText); break; case DbOperations.TestSetups.Fields.RegionsOfInterest: RegionsOfInterest.Clear(); foreach (System.Xml.XmlElement childNode in node.ChildNodes) { try { var roi = new RegionOfInterest( childNode.GetElementsByTagName("Suffix")[0].InnerText, true, Convert.ToDouble(childNode.GetElementsByTagName("Start")[0].InnerText, CultureInfo.InvariantCulture), Convert.ToDouble(childNode.GetElementsByTagName("End")[0].InnerText, CultureInfo.InvariantCulture)); var channelNames = childNode.GetElementsByTagName("ChannelNames")[0]?.InnerText.Split(",".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); var channelNameList = new List(); if (null == channelNames) { APILogger.Log("Warning: TestTemplate.ProcessFields has null for channelNames"); } else { foreach (var channelName in channelNames) { var newChannelName = channelName; if (!channelName.StartsWith("[") && !channelName.StartsWith("\\")) { //Since there is no hardware associated, see if there should be (it's OK if not). newChannelName = AddHardwareToImportedChannelIfAssigned(channelName, sensorLookup, hardware); } channelNameList.Add(newChannelName); } } roi.ChannelNames = channelNameList.ToArray(); if (channelLookup != null) { var channelIds = childNode.GetElementsByTagName("ChannelIds")[0]?.InnerText.Split(",".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); var channelIdList = new List(); if (null == channelIds) { APILogger.Log("Warning: TestTemplate.ProcessFields has null for channelIds"); } else { foreach (var channelId in channelIds) { channelIdList.Add(Convert.ToInt64(channelId)); } } roi.ChannelIds = channelIdList.ToArray(); } RegionsOfInterest.Add(roi); } catch (Exception e) { APILogger.Log("Error processing ROI XML"); APILogger.LogException(e); } } if (RegionsOfInterest.Count == 0) { RegionsOfInterest.Add(new RegionOfInterest(true)); } break; case DbOperations.TestSetups.Fields.ROIDownload: DoROIDownload = Convert.ToBoolean(node.InnerText); break; case DbOperations.TestSetups.Fields.ROIEnd: ROIEnd = Convert.ToDouble(node.InnerText, CultureInfo.InvariantCulture); break; case DbOperations.TestSetups.Fields.ROIStart: ROIStart = Convert.ToDouble(node.InnerText, CultureInfo.InvariantCulture); break; case DbOperations.TestSetups.Fields.SameAsDownloadFolder: SameAsDownloadFolder = Convert.ToBoolean(node.InnerText); break; case DbOperations.TestSetups.Fields.SamplesPerSecond: //If we find a possible bad export that had set SamplesPerSecond to "NaN", set SamplesPerSecondAggregate to the default if (node.InnerText == "NaN") //Double.TryParse will not return false if "NaN" is passed { SamplesPerSecondAggregate = Defaults.GetUserSettingValueDouble(ApplicationProperties.CurrentUser.Id, PropertyEnums.PropertyIds.DefaultTestSampleRate); } else { SamplesPerSecondAggregate = Convert.ToDouble(node.InnerText, CultureInfo.InvariantCulture); } //Make a list of SerialNumbers of DAS in the Test Setup so we don't stomp on their sample rates var testSetupDASList = new List(); foreach (var h in hardware) { testSetupDASList.Add(h.SerialNumber); } //Make a list of SerialNumbers of DAS that are not in the Test Setup. var nonTestSetupDASList = new List(); foreach (var dasRatePair in DASSampleRateList) { if (!testSetupDASList.Contains(dasRatePair.Key)) { nonTestSetupDASList.Add(dasRatePair.Key); } } //16285 Significant delay using the download data tile //this allows us to retrieve all DAS once and then avoid re-retrieving them in the loop //below var allDAS = DASHardwareList.GetAllHardware(); var lookup = new Dictionary(); foreach (var d in allDAS) { lookup[d.SerialNumber] = d; } //Set all DAS that are not in the Test Setup to the Test Setup sample rate in case they are added later. foreach (var nonTestSetupDAS in nonTestSetupDASList) { DASSampleRateList[nonTestSetupDAS] = SamplesPerSecondAggregate; //FB15759: if we don't know about the DAS yet, mark freq 0 and we'll fix later DASAAFRateList[nonTestSetupDAS] = lookup.ContainsKey(nonTestSetupDAS) ? GetAAFForHardware(lookup[nonTestSetupDAS], (int)SamplesPerSecondAggregate) : 0; } break; case DbOperations.TestSetups.Fields.SetupDescription: Description = node.InnerText; break; case DbOperations.TestSetups.Fields.WarnOnBatteryFail: WarnOnFailedBattery = Convert.ToBoolean(node.InnerText); break; case DbOperations.TestSetups.Fields.SetupName: Name = node.InnerText.Trim(); break; case DbOperations.TestSetups.Fields.Settings: _settings.LoadSettings(node.InnerText); break; case DbOperations.TestSetups.Fields.StrictDiagnostics: StrictDiagnostics = Convert.ToBoolean(node.InnerText); break; case DbOperations.TestSetups.Fields.TriggerCheckRealtime: TriggerCheckRealtime = Convert.ToBoolean(node.InnerText); break; case DbOperations.TestSetups.Fields.TriggerCheckStep: TriggerCheckStep = Convert.ToBoolean(node.InnerText); break; case DbOperations.TestSetups.Fields.TurnOffExcitation: TurnOffExcitation = Convert.ToBoolean(node.InnerText); break; case DbOperations.TestSetups.Fields.UploadData: UploadData = Convert.ToBoolean(node.InnerText); break; case DbOperations.TestSetups.Fields.UploadDataFolder: UploadFolder = node.InnerText; break; case DbOperations.TestSetups.Fields.UploadExportsOnly: UploadExportsOnly = Convert.ToBoolean(node.InnerText); break; case DbOperations.TestSetups.Fields.VerifyChannels: VerifyChannels = Convert.ToBoolean(node.InnerText); break; case DbOperations.TestSetups.Fields.VerifyChannelsDelayMS: AutoVerifyDelaySeconds = Convert.ToDouble(node.InnerText, CultureInfo.InvariantCulture) / 1000D; break; case DbOperations.TestSetups.Fields.ViewDiagnostics: ViewDiagnostics = Convert.ToBoolean(node.InnerText); break; case DbOperations.TestSetups.Fields.ViewDownloadAll: ViewDownloadAll = Convert.ToBoolean(node.InnerText); break; case DbOperations.TestSetups.Fields.ViewRealtime: ViewRealtime = Convert.ToBoolean(node.InnerText); break; case DbOperations.TestSetups.Fields.ViewROIDownload: ViewROIDownload = Convert.ToBoolean(node.InnerText); break; case DbOperations.TestSetups.Fields.Complete: SetIsComplete(Convert.ToBoolean(node.InnerText)); break; case DbOperations.TestSetups.Fields.Dirty: SetIsDirty(Convert.ToBoolean(node.InnerText)); break; case DbOperations.TestSetups.Fields.ErrorMessage: SetCompletionErrorMessage(node.InnerText); break; case DbOperations.TestSetups.Fields.UserTags: SetTagsFromCommaSeparatedString(node.InnerText, DbOperations.GetSQLCommand, DbOperations.TagsGet, DbOperations.TagsGetId, DbOperations.TagsInsert); break; case DbOperations.TestSetups.Fields.DoAutoArm: DoAutoArm = Convert.ToBoolean(node.InnerText); break; case DbOperations.TestSetups.Fields.DoEnableRepeat: DoEnableRepeat = Convert.ToBoolean(node.InnerText); break; case DbOperations.TestSetups.Fields.DoStreaming: DoStreaming = Convert.ToBoolean(node.InnerText); break; case DbOperations.TestSetups.Fields.CheckoutMode: CheckoutMode = Convert.ToBoolean(node.InnerText); break; case DbOperations.TestSetups.Fields.QuitTestWithoutWarning: QuitTestWithoutWarning = Convert.ToBoolean(node.InnerText); break; case DbOperations.TestSetups.Fields.SuppressMissingSensorsWarning: SuppressMissingSensorsWarning = Convert.ToBoolean(node.InnerText); break; case DbOperations.TestSetups.Fields.NotAllChannelsViewer: NotAllChannelsViewer = Convert.ToBoolean(node.InnerText); break; case DbOperations.TestSetups.Fields.NotAllChannelsRealTime: NotAllChannelsRealTime = Convert.ToBoolean(node.InnerText); break; case DbOperations.TestSetups.Fields.ClockSyncProfileMaster: ClockSyncProfileMaster = (ClockSyncProfile)Enum.Parse(typeof(ClockSyncProfile), node.InnerText); break; case DbOperations.TestSetups.Fields.ClockSyncProfileSlave: ClockSyncProfileSlave = (ClockSyncProfile)Enum.Parse(typeof(ClockSyncProfile), node.InnerText); break; case DbOperations.TestSetups.Fields.ISFFile: ISFFile = node.InnerText; break; case DbOperations.TestSetups.Fields.ExtraProperties: ExtraProperties.Clear(); foreach (System.Xml.XmlElement childNode in node.ChildNodes) { try { ExtraProperties.Add(new ExtraProperty() { Key = childNode.GetElementsByTagName("Key")[0].InnerText, Value = childNode.GetElementsByTagName("Value")[0].InnerText }); } catch (Exception e) { APILogger.Log("Error processing Extra Properties XML"); APILogger.LogException(e); } } break; case DbOperations.TestSetups.Fields.MeasureSquibResistancesStep: MeasureSquibResistancesStep = Convert.ToBoolean(node.InnerText); break; case DbOperations.TestSetups.Fields.TestSetupUniqueId: TestSetupUniqueId = node.InnerText.Trim(); break; case DbOperations.TestSetups.Fields.AlignUDPToPPS: AlignUDPToPPS = Convert.ToBoolean(node.InnerText); break; case DbOperations.TestSetups.Fields.UseCustomerDetails: break; case DbOperations.TestSetups.Fields.UseLabDetails: break; case DbOperations.TestSetups.Fields.UseTestEngineerDetails: break; default: throw new NotSupportedException("TestTemplate::ProcessFields unsupported field: " + field); } } else { if (node.Name == "TestId") { TestId = node.InnerText; } else { var err = "TestTemplate::ProcessFields unsupported field: " + node.Name; APILogger.Log(err); throw new NotSupportedException(err); } } } /// /// processes graphs from a test setup node xml /// /// private void ProcessGraphs(System.Xml.XmlElement node, IReadOnlyDictionary channelLookup) { var graphs = new List(TestGraphs); var g = new TestGraph(); g.ReadXML(node, channelLookup); graphs.Add(g); TestGraphs = graphs.ToArray(); } /// /// processes graphs from a test setup node xml /// /// private void ProcessGraphsPre20(System.Xml.XmlElement node) { var graphs = new List(TestGraphs); var g = new TestGraph(this); foreach (var child in node.ChildNodes) { if (child is System.Xml.XmlElement) { var element = child as System.Xml.XmlElement; if (!Enum.TryParse(element.Name, out DbOperations.TestSetups.GraphFields field)) continue; switch (field) { case DbOperations.TestSetups.GraphFields.Channels: g.SetChannelsFromSQL(element.InnerText); break; case DbOperations.TestSetups.GraphFields.DomainMax: { if (double.TryParse(element.InnerText, NumberStyles.Any, CultureInfo.InvariantCulture, out var d)) { g.DomainMax = d; } } break; case DbOperations.TestSetups.GraphFields.DomainMin: { if (double.TryParse(element.InnerText, NumberStyles.Any, CultureInfo.InvariantCulture, out var d)) { g.DomainMin = d; } } break; case DbOperations.TestSetups.GraphFields.GraphDescription: g.GraphDescription = element.InnerText; break; case DbOperations.TestSetups.GraphFields.GraphName: g.GraphName = element.InnerText; break; case DbOperations.TestSetups.GraphFields.LocalOnly: break; case DbOperations.TestSetups.GraphFields.RangeMax: { if (double.TryParse(element.InnerText, NumberStyles.Any, CultureInfo.InvariantCulture, out var d)) { g.RangeMax = d; } } break; case DbOperations.TestSetups.GraphFields.RangeMin: { if (double.TryParse(element.InnerText, NumberStyles.Any, CultureInfo.InvariantCulture, out var d)) { g.RangeMin = d; } } break; case DbOperations.TestSetups.GraphFields.TemplateName: break; case DbOperations.TestSetups.GraphFields.Thresholds: g.SetThresholdsFromSQL(element.InnerText); break; case DbOperations.TestSetups.GraphFields.UseDomainMax: g.UseDomainMax = Convert.ToBoolean(element.InnerText); break; case DbOperations.TestSetups.GraphFields.UseDomainMin: g.UseDomainMin = Convert.ToBoolean(element.InnerText); break; case DbOperations.TestSetups.GraphFields.UseRangeMax: g.UseRangeMax = Convert.ToBoolean(element.InnerText); break; case DbOperations.TestSetups.GraphFields.UseRangeMin: g.UseRangeMin = Convert.ToBoolean(element.InnerText); break; case DbOperations.TestSetups.GraphFields.TestSetupName: break; default: throw new NotSupportedException("TestTemplate::ProcessGraphs unsupported field: " + field); } } } graphs.Add(g); TestGraphs = graphs.ToArray(); } /// /// processes Hardware inclusion instructions (added or removed hardware in test setup) /// /// private void ProcessHardwareOverrides(System.Xml.XmlElement node) { HardwareInclusionInstruction instruction = null; var action = node.GetAttribute(HARDWAREOVERRIDE_ACTION); var hid = node.GetAttribute(HARDWAREOVERRIDE_HID); if (string.IsNullOrWhiteSpace(action) || string.IsNullOrWhiteSpace(hid) || !Enum.TryParse(action, out HardwareInclusionInstruction.Actions actionEnum)) { APILogger.Log("problem in import, hardware instruction is invalid: " + node.InnerText); } else { instruction = new HardwareInclusionInstruction(hid, actionEnum); } if (null != instruction) { HardwareOverrides[instruction.HardwareId] = instruction; } } private void ProcessCalculatedChannels(System.Xml.XmlElement node, IReadOnlyDictionary channelLookup) { try { var id = int.Parse(node.GetAttribute(CC_ID), CultureInfo.InvariantCulture); var testSetupName = node.GetAttribute(CC_TESTSETUPNAME); var operation = (Operations)Enum.Parse(typeof(Operations), node.GetAttribute(CC_OPERATION)); var name = node.GetAttribute(CC_NAME); var inputChannelIds = node.GetAttribute(CC_INPUTCHANNELIDS).Split(new[] { CultureInfo.InvariantCulture.TextInfo.ListSeparator }, StringSplitOptions.None); var inputGroupChannelsList = new List(); foreach (var token in inputChannelIds) { if (long.TryParse(token, out var l)) { if (channelLookup.ContainsKey(l)) { inputGroupChannelsList.Add(channelLookup[l]); } } } var cfcForOutput = node.GetAttribute(CC_CFCFOROUTPUTCHANNELS); var cfcInput = node.GetAttribute(CC_CFCFORINPUTCHANNELS); var calculatedValueCode = node.GetAttribute(CC_CALCULATEDCHANNELVALUE); var cc = new CalculatedValueClass { Id = id, TestSetupName = testSetupName, Operation = operation, Name = name, InputChannelIds = inputChannelIds, ChannelFilterClassForOutput = cfcForOutput, CFCForInputChannels = cfcInput, CalculatedValueCode = calculatedValueCode }; cc.SetChannels(inputGroupChannelsList.ToArray()); CalculatedChannels.Add(cc); } catch (Exception ex) { APILogger.Log(ex); } } private void ProcessCalculatedChannelsPre20(System.Xml.XmlElement node) { try { var id = int.Parse(node.GetAttribute(CC_ID), CultureInfo.InvariantCulture); var testSetupName = node.GetAttribute(CC_TESTSETUPNAME); var operation = (Operations)Enum.Parse(typeof(Operations), node.GetAttribute(CC_OPERATION)); var name = node.GetAttribute(CC_NAME); var inputChannelIds = node.GetAttribute(CC_INPUTCHANNELIDS).Split(new[] { CultureInfo.InvariantCulture.TextInfo.ListSeparator }, StringSplitOptions.None); var cfcForOutput = node.GetAttribute(CC_CFCFOROUTPUTCHANNELS); var cfcInput = node.GetAttribute(CC_CFCFORINPUTCHANNELS); var calculatedValueCode = node.GetAttribute(CC_CALCULATEDCHANNELVALUE); var cc = new CalculatedValueClass { Id = id, TestSetupName = testSetupName, Operation = operation, Name = name, InputChannelIds = inputChannelIds, ChannelFilterClassForOutput = cfcForOutput, CFCForInputChannels = cfcInput, CalculatedValueCode = calculatedValueCode }; SetCalculatedChannelInputChannelsFromXML(cc); CalculatedChannels.Add(cc); } catch (Exception ex) { APILogger.Log(ex); } } public void SetCalculatedChannelInputChannelsFromXML(CalculatedValueClass cc) { var lookup = new Dictionary(); if (null == AvailableCalculatedChannelInputChannels) { return; } foreach (var channel in AvailableCalculatedChannelInputChannels) { lookup[channel.GetId()] = channel; } var testObjectChannelsList = new List(); foreach (var sChannel in cc.InputChannelIds) { if (lookup.ContainsKey(sChannel)) { testObjectChannelsList.Add(lookup[sChannel]); } } cc.SetTestObjectChannels(testObjectChannelsList.ToArray()); } private readonly List _channels = new List(); private readonly Dictionary _lookup = new Dictionary(); public TestObjectChannel[] AvailableCalculatedChannelInputChannels { get { _lookup.Clear(); var channels = new List(); foreach (var setChannel in _channels) { _lookup[setChannel.GetId()] = setChannel; } if (Array.Exists(TestObjects, to => !AddCalculatedChannelInputChannels(to, channels))) { return null; } if (AddedGroups.Where(to => to.Hardware.Any()).Any(to => !AddCalculatedChannelInputChannels(to, channels))) { return null; } channels.Sort(); return channels.ToArray(); } } private bool AddCalculatedChannelInputChannels(TestTestObject to, List channels) { var isoTestObject = to.GetISOTestObject(); if (null == isoTestObject) { return false; } foreach (var channel in isoTestObject.AllChannels) { if (!channel.Required || channel.Disabled) { continue; } if (string.IsNullOrWhiteSpace(channel.SensorSerialNumber)) { continue; } var sd = SensorsCollection.SensorsList.GetSensorBySerialNumber(channel.SensorSerialNumber); if (null == sd || !sd.IsDigitalOutput()) { if ((sd != null) && (sd.Bridge == SensorConstants.BridgeType.SQUIB)) { //Put squib channels (Current and/or Voltage) in the //Available list if and only if they are not in the graph if (!ContainsCurrentChannel(channel)) { var currentChannel = new TestObjectChannel(channel, isoTestObject, new TestObjectTemplate().ToISOTestObjectTemplate()) { SquibChannelType = TestObjectChannel.SquibChannelTypes.Current, NameOfTheChannel = channel.Name + " " + StringResources.Graph_SquibCurrent }; channels.Add(currentChannel); } if (!ContainsVoltageChannel(channel)) { channel.SquibChannelType = TestObjectChannel.SquibChannelTypes.Voltage; channel.NameOfTheChannel = channel.Name + " " + StringResources.Graph_SquibVoltage; channels.Add(channel); } } else { if (ContainsCalculatedChannelInputChannel(channel)) { continue; } channels.Add(channel); } } } return true; } private bool ContainsChannel(TestObjectChannel channel) { return _lookup.ContainsKey(channel.GetGraphId()); } private bool ContainsCurrentChannel(TestObjectChannel channel) { if (channel.SquibChannelType == TestObjectChannel.SquibChannelTypes.Current) { return ContainsChannel(channel); } else { return _lookup.ContainsKey(channel.GetGraphId() + Constants.CURRENT_SUFFIX); } } private bool ContainsVoltageChannel(TestObjectChannel channel) { return ContainsChannel(channel); } private bool ContainsCalculatedChannelInputChannel(TestObjectChannel channel) { return _lookup.ContainsKey(channel.GetGraphId()); } private void ProcessLevelTriggers(System.Xml.XmlElement node, IReadOnlyDictionary channelLookup) { var sGreaterThanEnabled = node.GetAttribute(LT_GREATERTHANENABLED); var sGreaterThanEU = node.GetAttribute(LT_GREATERTHANTHRESHOLDEU); var sHardwareChannelId = node.GetAttribute(LT_HARDWARECHANNELID); var sLessThanEnabled = node.GetAttribute(LT_LESSTHANENABLED); var sLessThanEU = node.GetAttribute(LT_LESSTHANTHRESHOLDEU); var sensorSerialNumber = node.GetAttribute(LT_SENSORSERIALNUMBER); var groupChannelId = node.GetAttribute(LT_GROUPCHANNELID); var insideLowerLevelEU = 0D; var insideUpperLevelEU = 0D; var outsideLowerLevelEU = 0D; var outsideUpperLevelEU = 0D; var triggerOutsideBounds = false; var triggerInsideBounds = false; try { //these attributes were all added at the same time (later), so if one exists they all should exist, but it's possible none of them exist var sInsideLowerLevelEU = node.GetAttribute(LT_INSIDELOWEREU); var sInsideUpperLevelEU = node.GetAttribute(LT_INSIDEUPPEREU); var sOutsideLowerLevelEU = node.GetAttribute(LT_OUTSIDELOWEREU); var sOutsideUpperLevelEU = node.GetAttribute(LT_OUTSIDEUPPEREU); var sTriggerInsideBounds = node.GetAttribute(LT_TRIGGERINSIDE); var sTriggerOutsideBounds = node.GetAttribute(LT_TRIGGEROUTSIDE); double.TryParse(sInsideUpperLevelEU, NumberStyles.Any, CultureInfo.InvariantCulture, out insideUpperLevelEU); double.TryParse(sInsideLowerLevelEU, NumberStyles.Any, CultureInfo.InvariantCulture, out insideLowerLevelEU); double.TryParse(sOutsideLowerLevelEU, NumberStyles.Any, CultureInfo.InvariantCulture, out outsideLowerLevelEU); double.TryParse(sOutsideUpperLevelEU, NumberStyles.Any, CultureInfo.InvariantCulture, out outsideUpperLevelEU); bool.TryParse(sTriggerInsideBounds, out triggerInsideBounds); bool.TryParse(sTriggerOutsideBounds, out triggerOutsideBounds); } catch (Exception ex) { APILogger.Log(ex); } if (bool.TryParse(sGreaterThanEnabled, out var bGreaterThanEnabled) && bool.TryParse(sLessThanEnabled, out var bLessThanEnabled) && double.TryParse(sGreaterThanEU, NumberStyles.Any, CultureInfo.InvariantCulture, out var greaterThanValue) && double.TryParse(sLessThanEU, NumberStyles.Any, CultureInfo.InvariantCulture, out var lessThanValue)) { try { var lt = new LevelTriggerChannel( groupChannelId, sHardwareChannelId, sensorSerialNumber, bGreaterThanEnabled, greaterThanValue, bLessThanEnabled, lessThanValue, insideLowerLevelEU, insideUpperLevelEU, outsideLowerLevelEU, outsideUpperLevelEU, triggerOutsideBounds, triggerInsideBounds); //Assign GroupChannel so that we get the database ID of the Channel if (long.TryParse(groupChannelId, out var l)) { if (channelLookup.ContainsKey(l)) { lt.GroupChannel = channelLookup[l]; } } SetLevelTrigger(lt); } catch (Exception ex) { APILogger.Log(ex); } } } private void ProcessLevelTriggersPre20(System.Xml.XmlElement node) { var sGreaterThanEnabled = node.GetAttribute(LT_GREATERTHANENABLED); var sGreaterThanEU = node.GetAttribute(LT_GREATERTHANTHRESHOLDEU); var sHardwareChannelId = node.GetAttribute(LT_HARDWARECHANNELID); var sLessThanEnabled = node.GetAttribute(LT_LESSTHANENABLED); var sLessThanEU = node.GetAttribute(LT_LESSTHANTHRESHOLDEU); var sensorSerialNumber = node.GetAttribute(LT_SENSORSERIALNUMBER); var groupChannelId = node.GetAttribute(LT_TESTOBJECTCHANNELID); var insideLowerLevelEU = 0D; var insideUpperLevelEU = 0D; var outsideLowerLevelEU = 0D; var outsideUpperLevelEU = 0D; var triggerOutsideBounds = false; var triggerInsideBounds = false; try { //these attributes were all added at the same time (later), so if one exists they all should exist, but it's possible none of them exist var sInsideLowerLevelEU = node.GetAttribute(LT_INSIDELOWEREU); var sInsideUpperLevelEU = node.GetAttribute(LT_INSIDEUPPEREU); var sOutsideLowerLevelEU = node.GetAttribute(LT_OUTSIDELOWEREU); var sOutsideUpperLevelEU = node.GetAttribute(LT_OUTSIDEUPPEREU); var sTriggerInsideBounds = node.GetAttribute(LT_TRIGGERINSIDE); var sTriggerOutsideBounds = node.GetAttribute(LT_TRIGGEROUTSIDE); double.TryParse(sInsideUpperLevelEU, NumberStyles.Any, CultureInfo.InvariantCulture, out insideUpperLevelEU); double.TryParse(sInsideLowerLevelEU, NumberStyles.Any, CultureInfo.InvariantCulture, out insideLowerLevelEU); double.TryParse(sOutsideLowerLevelEU, NumberStyles.Any, CultureInfo.InvariantCulture, out outsideLowerLevelEU); double.TryParse(sOutsideUpperLevelEU, NumberStyles.Any, CultureInfo.InvariantCulture, out outsideUpperLevelEU); bool.TryParse(sTriggerInsideBounds, out triggerInsideBounds); bool.TryParse(sTriggerOutsideBounds, out triggerOutsideBounds); } catch (Exception ex) { APILogger.Log(ex); } if (bool.TryParse(sGreaterThanEnabled, out var bGreaterThanEnabled) && bool.TryParse(sLessThanEnabled, out var bLessThanEnabled) && double.TryParse(sGreaterThanEU, NumberStyles.Any, CultureInfo.InvariantCulture, out var greaterThanValue) && double.TryParse(sLessThanEU, NumberStyles.Any, CultureInfo.InvariantCulture, out var lessThanValue)) { try { var hardwareChannelIdToGroupChannelIdMap = new Dictionary(); foreach (var group in Groups) { var channels = ChannelsForGroup[group]; foreach (var groupChannel in channels) { hardwareChannelIdToGroupChannelIdMap[groupChannel.HardwareId] = groupChannel.Id; } } var lt = new LevelTriggerChannel( groupChannelId, sHardwareChannelId, sensorSerialNumber, bGreaterThanEnabled, greaterThanValue, bLessThanEnabled, lessThanValue, insideLowerLevelEU, insideUpperLevelEU, outsideLowerLevelEU, outsideUpperLevelEU, triggerOutsideBounds, triggerInsideBounds); SetLevelTrigger(lt); } catch (Exception ex) { APILogger.Log(ex); } } } /// /// Processes Added Groups from a Test Setup node in xml /// /// /// /// private void ProcessAddedGroupsPre20(System.Xml.XmlElement root, IDictionary testObjectLookup, Dictionary sensorLookup) { var values = new Dictionary(); var sensorsToBeSet = new List>(); foreach (var child in root.ChildNodes) { if (!(child is System.Xml.XmlElement node)) { continue; } if (Enum.TryParse(node.Name, out DbOperations.TestSetups.TestSetupObjectFields field)) { values[field] = node.InnerText.Trim(); } else if (node.Name == CHANNELSETTINGS_ROOT) { ProcessChannelSetting(node, sensorLookup, ref sensorsToBeSet); } else { System.Diagnostics.Trace.WriteLine(node.Name); } } if (values.ContainsKey(DbOperations.TestSetups.TestSetupObjectFields.TestObjectSerialNumber) || values.ContainsKey(DbOperations.TestSetups.TestSetupObjectFields.TestObjectName)) { var serial = values.ContainsKey(DbOperations.TestSetups.TestSetupObjectFields.TestObjectSerialNumber) ? values[DbOperations.TestSetups.TestSetupObjectFields.TestObjectSerialNumber] : values[DbOperations.TestSetups.TestSetupObjectFields.TestObjectName]; var group = testObjectLookup[serial]; int displayOrder = -1; if (values.ContainsKey(DbOperations.TestSetups.TestSetupObjectFields.DisplayOrder)) { var o = values[DbOperations.TestSetups.TestSetupObjectFields.DisplayOrder]; if (!DBNull.Value.Equals(o)) { displayOrder = Convert.ToInt32(o); } } AddAddedGroup(group, values[DbOperations.TestSetups.TestSetupObjectFields.TestObjectType], values[DbOperations.TestSetups.TestSetupObjectFields.TestObjectPosition], values[DbOperations.TestSetups.TestSetupObjectFields.TestSetupName], displayOrder); //} } else { APILogger.Log("TestTemplate::ProcessAddedGroups value not found [test object serial number]"); throw new NotSupportedException("TestTemplate::ProcessAddedGroups value not found [test object serial number]"); } if (sensorsToBeSet.Any()) { using (var eSensors = sensorsToBeSet.GetEnumerator()) { while (eSensors.MoveNext()) { SetSensor(eSensors.Current.Item3, eSensors.Current.Item1, eSensors.Current.Item2); } } } } /// /// processes a group from a test setup node in xml /// /// /// /// private void ProcessTestObjectNode(System.Xml.XmlElement root, IDictionary testObjectLookup, Dictionary sensorLookup) { var values = new Dictionary(); List> sensorsToBeSet = new List>(); foreach (var child in root.ChildNodes) { if (!(child is System.Xml.XmlElement node)) { continue; } if (Enum.TryParse(node.Name, out DbOperations.TestSetups.TestSetupObjectFields field)) { values[field] = node.InnerText.Trim(); } else { switch (node.Name) { case CHANNELSETTINGS_ROOT: ProcessChannelSetting(node, sensorLookup, ref sensorsToBeSet); break; case TO_SERIALNUMBER: //Handle the backward-compatibility of the switch from to values[DbOperations.TestSetups.TestSetupObjectFields.TestObjectName] = node.InnerText; break; } } } var excitationWarmupMs = Convert.ToInt32(values[DbOperations.TestSetups.TestSetupObjectFields.ExcitationWarmupTimeMS], CultureInfo.InvariantCulture); var targetSampleRate = Convert.ToDouble(values[DbOperations.TestSetups.TestSetupObjectFields.TargetSampleRate], CultureInfo.InvariantCulture); var key = values.ContainsKey(DbOperations.TestSetups.TestSetupObjectFields.TestObjectSerialNumber) ? DbOperations.TestSetups.TestSetupObjectFields.TestObjectSerialNumber : DbOperations.TestSetups.TestSetupObjectFields.TestObjectName; var groupkey = values[key]; if (testObjectLookup.ContainsKey(groupkey)) { var to = testObjectLookup[groupkey]; var displayOrder = -1; if (values.ContainsKey(DbOperations.TestSetups.TestSetupObjectFields.DisplayOrder)) { var value = values[DbOperations.TestSetups.TestSetupObjectFields.DisplayOrder]; if (!DBNull.Value.Equals(value)) { displayOrder = Convert.ToInt32(value); } } AddTestObject( to, excitationWarmupMs, targetSampleRate, values[DbOperations.TestSetups.TestSetupObjectFields.TestObjectType], values[DbOperations.TestSetups.TestSetupObjectFields.TestObjectPosition], displayOrder); } if (sensorsToBeSet.Any()) { using (var eSensors = sensorsToBeSet.GetEnumerator()) { while (eSensors.MoveNext()) { SetSensor(eSensors.Current.Item3, eSensors.Current.Item1, eSensors.Current.Item2); } } } } /// /// processes a DASHardware from a TestSetup node in xml /// /// private void ProcessDASHardwareNode(System.Xml.XmlElement root) { var values = new Dictionary(); foreach (var child in root.ChildNodes) { if (!(child is System.Xml.XmlElement node)) { continue; } if (Enum.TryParse(node.Name, out DbOperations.TestSetups.TestSetupDASHardwareFields field)) { values[field] = node.InnerText; } } DASSampleRateList[values[DbOperations.TestSetups.TestSetupDASHardwareFields.SerialNumber]] = Convert.ToDouble(values[DbOperations.TestSetups.TestSetupDASHardwareFields.SamplesPerSecond]); //FB15759: if an older file without AAF, mark freq 0 and we'll fix later DASAAFRateList[values[DbOperations.TestSetups.TestSetupDASHardwareFields.SerialNumber]] = values.ContainsKey(DbOperations.TestSetups.TestSetupDASHardwareFields.AntiAliasFilterRate) ? Convert.ToSingle(values[DbOperations.TestSetups.TestSetupDASHardwareFields.AntiAliasFilterRate]) : 0; DASClockMasterList[values[DbOperations.TestSetups.TestSetupDASHardwareFields.SerialNumber]] = values.ContainsKey(DbOperations.TestSetups.TestSetupDASHardwareFields.IsClockMaster) && Convert.ToBoolean(values[DbOperations.TestSetups.TestSetupDASHardwareFields.IsClockMaster]); DASPTPDomainIDList[values[DbOperations.TestSetups.TestSetupDASHardwareFields.SerialNumber]] = values.ContainsKey(DbOperations.TestSetups.TestSetupDASHardwareFields.PTPDomainID) ? Convert.ToByte(values[DbOperations.TestSetups.TestSetupDASHardwareFields.PTPDomainID]) : (byte)0; } private static IGroupListViewModel _vm = null; public IGroup CreateGroup(IGroupDbRecord groupRecord, List includedHardwareStringList, List dasIdList) { if (null == _vm) { var unityContainer = ContainerLocator.Container.Resolve(); _vm = unityContainer.Resolve(); } return _vm.CreateGroup(groupRecord, includedHardwareStringList, dasIdList); } public IGroup CreateGroup(List includedHardwareStringList) { if (null == _vm) { var unityContainer = ContainerLocator.Container.Resolve(); _vm = unityContainer.Resolve(); } return _vm.CreateGroup(includedHardwareStringList); } /// /// processes a Group from a TestSetup node in xml /// /// /// /// private void ProcessGroupNode(System.Xml.XmlElement root, IDictionary groupLookup, Dictionary sensorLookup, IChannelSetting[] channelDefaults, List sensorCalibrations) { var values = new Dictionary(); var includedHardwareStringList = new List(); var channelLookup = new Dictionary(); foreach (var child in root.ChildNodes) { if (!(child is System.Xml.XmlElement node)) { continue; } if (Enum.TryParse(node.Name, out DbOperations.TestSetups.TestSetupGroupFields field)) { values[field] = node.InnerText; } else { switch (node.Name) { case HARDWARE_LIST: includedHardwareStringList = ProcessHardwareXMLElement(node); break; case CHANNEL_ROOT: var channel = ProcessChannelNode(node, values[DbOperations.TestSetups.TestSetupGroupFields.DisplayName], groupLookup, channelDefaults, sensorLookup, sensorCalibrations); channelLookup[channel.Id] = channel; break; } } } IGroup group = null; var groupkey = ""; if (Application.Current.Dispatcher.CheckAccess()) { var key = DbOperations.TestSetups.TestSetupGroupFields.Name; groupkey = values[key]; group = CreateGroup(includedHardwareStringList); } else { ManualResetEvent resetEvent = new ManualResetEvent(false); Application.Current.Dispatcher.BeginInvoke(new Action(() => { var key = DbOperations.TestSetups.TestSetupGroupFields.Name; groupkey = values[key]; group = CreateGroup(includedHardwareStringList); resetEvent.Set(); })); resetEvent.WaitOne(); } group.Name = groupkey; group.Embedded = true; group.DisplayName = values[DbOperations.TestSetups.TestSetupGroupFields.DisplayName]; group.Description = values[DbOperations.TestSetups.TestSetupGroupFields.Description]; group.DisplayOrder = Convert.ToInt32(values[DbOperations.TestSetups.TestSetupGroupFields.DisplayOrder]); group.Position = values[DbOperations.TestSetups.TestSetupGroupFields.Position]; group.TestObject = values[DbOperations.TestSetups.TestSetupGroupFields.TestObjectType]; if (values.ContainsKey(DbOperations.TestSetups.TestSetupGroupFields.Id)) { group.Id = Convert.ToInt32(values[DbOperations.TestSetups.TestSetupGroupFields.Id]); } if (values.ContainsKey(DbOperations.TestSetups.TestSetupGroupFields.StaticGroupId)) { //Must be a 2.1 or later export if (string.IsNullOrWhiteSpace(values[DbOperations.TestSetups.TestSetupGroupFields.StaticGroupId]) || !groupLookup.ContainsKey(values[DbOperations.TestSetups.TestSetupGroupFields.DisplayName])) { group.StaticGroupId = null; } else { group.StaticGroupId = Convert.ToInt32(values[DbOperations.TestSetups.TestSetupGroupFields.StaticGroupId]); } } var sensors = sensorLookup.Values.ToArray(); AddGroup(group, channelLookup, sensors); } /// /// processes a channel setting for a test setup from a test setup node in xml /// /// /// private void ProcessChannelSetting(System.Xml.XmlElement root, Dictionary lookup, ref List> sensors) { var chid = ""; var sensorserial = ""; var setting = ""; var objname = ""; var disabled = false; foreach (var node in root.ChildNodes) { if (node is System.Xml.XmlElement) { var child = node as System.Xml.XmlElement; if (!Enum.TryParse(child.Name, out DbOperations.TestSetups.ChannelSettingFields field)) continue; switch (field) { case DbOperations.TestSetups.ChannelSettingFields.ChannelId: chid = child.InnerText; break; case DbOperations.TestSetups.ChannelSettingFields.SensorSerial: sensorserial = child.InnerText; break; case DbOperations.TestSetups.ChannelSettingFields.Setting: setting = child.InnerText; break; case DbOperations.TestSetups.ChannelSettingFields.TestName: break; case DbOperations.TestSetups.ChannelSettingFields.TestObjectName: objname = child.InnerText.Trim(); break; case DbOperations.TestSetups.ChannelSettingFields.Disabled: disabled = Convert.ToBoolean(child.InnerText, CultureInfo.InvariantCulture); break; } } } SetDisabled(objname, chid, disabled); var sd = TestTemplateList.GetSensorFromSettings(setting, sensorserial, lookup); if (null == sd) { return; } //SetSensor(sd, objname, chid); sensors.Add(new Tuple(objname, chid, sd)); } /// /// processes a channel setting for a test setup from a test setup node in xml /// /// /// private List ProcessHardwareXMLElement(System.Xml.XmlElement root) { var includedHardwareStringList = new List(); foreach (var child in root.ChildNodes) { if (!(child is System.Xml.XmlElement node)) { continue; } if (Enum.TryParse(node.Name, out DbOperations.TestSetups.GroupsGroupHardwareListFields field)) { switch (field) { case DbOperations.TestSetups.GroupsGroupHardwareListFields.Hardware: includedHardwareStringList.Add(node.InnerText); break; } } } return includedHardwareStringList; } private DTS.Common.Interface.Channels.IGroupChannel ProcessChannelNode(System.Xml.XmlElement root, string groupDisplayName, IDictionary groupLookup, IChannelSetting[] channelDefaults, Dictionary sensorLookup, List sensorCalibrations) { var channel = new GroupChannel(true, groupDisplayName, null, channelDefaults); foreach (var child in root.ChildNodes) { if (!(child is System.Xml.XmlElement node)) { continue; } if (Enum.TryParse(node.Name, out DbOperations.TestSetups.ChannelFields field)) { switch (field) { case DbOperations.TestSetups.ChannelFields.ISOChannelName: channel.IsoChannelName = node.InnerText; break; case DbOperations.TestSetups.ChannelFields.ISOCode: channel.IsoCode = node.InnerText; break; case DbOperations.TestSetups.ChannelFields.UserChannelName: channel.UserChannelName = node.InnerText; break; case DbOperations.TestSetups.ChannelFields.UserCode: channel.UserCode = node.InnerText; break; case DbOperations.TestSetups.ChannelFields.SensorId: channel.SensorId = Convert.ToInt32(node.InnerText, CultureInfo.InvariantCulture); break; case DbOperations.TestSetups.ChannelFields.DASId: channel.DASId = Convert.ToInt32(node.InnerText, CultureInfo.InvariantCulture); break; case DbOperations.TestSetups.ChannelFields.DASChannelIdx: channel.DASChannelIndex = Convert.ToInt32(node.InnerText, CultureInfo.InvariantCulture); break; case DbOperations.TestSetups.ChannelFields.TestSetupOrder: channel.TestSetupOrder = Convert.ToInt32(node.InnerText, CultureInfo.InvariantCulture); break; case DbOperations.TestSetups.ChannelFields.GroupOrder: channel.GroupChannelOrder = Convert.ToInt32(node.InnerText, CultureInfo.InvariantCulture); break; case DbOperations.TestSetups.ChannelFields.Disabled: channel.IsDisabled = Convert.ToBoolean(node.InnerText, CultureInfo.InvariantCulture); break; case DbOperations.TestSetups.ChannelFields.Settings: ProcessSettingsNode(node, channel, groupLookup, sensorLookup, sensorCalibrations); break; case DbOperations.TestSetups.ChannelFields.Id: channel.Id = Convert.ToInt32(node.InnerText, CultureInfo.InvariantCulture); break; } } } return channel; } /// /// processes a channel setting for a test setup from a test setup node in xml /// /// /// /// private void ProcessSettingsNode(System.Xml.XmlElement root, GroupChannel channel, IDictionary groupLookup, Dictionary sensorLookup, List sensorCalibrations) { var range = ""; //var cfc = ""; var filterClass = ""; var polarity = ""; var acCouplingEnabled = ""; var bridgeType = ""; var duration = ""; var delay = ""; var limitDuration = ""; var outputMode = ""; var digitalOutDuration = ""; var digitalOutDelay = ""; var digitalOutLimitDuration = ""; var sqMode = ""; var squibDuration = ""; var squibDelay = ""; var squibCurrent = ""; var squibLimitDuration = ""; var diMode = ""; var defaultValue = ""; var activeValue = ""; var uartBaudRate = ""; var uartDataBits = ""; var uartStopBits = ""; var uartParity = ""; var uartFlowControl = ""; var uartDataFormat = ""; var streamOutUDPProfile = ""; var streamOutUDPAddress = ""; var streamOutUDPTimeChannelId = ""; var streamOutUDPDataChannelId = ""; var streamOutUDPTmNSConfig = ""; var streamOutIRIGTimeDataPacketIntervalMs = ""; var streamOutTMATIntervalMs = ""; var streamInUDPAddress = ""; //15270 Sensor Import does not import most channel settings string userValue1, userValue2, userValue3, zeroMethod, zeroStart, zeroEnd, initialOffset; userValue1 = userValue2 = userValue3 = zeroMethod = zeroStart = zeroEnd = initialOffset = string.Empty; if (root.ChildNodes.Count > 0) { foreach (var node in root.ChildNodes) { if (node is System.Xml.XmlElement) { var child = node as System.Xml.XmlElement; if (Enum.TryParse(child.Name, out DbOperations.TestSetups.SettingsFields field)) { switch (field) { case DbOperations.TestSetups.SettingsFields.Range: range = string.IsNullOrWhiteSpace(child.InnerText) ? GetGroupRange(channel, groupLookup) : child.InnerText; break; case DbOperations.TestSetups.SettingsFields.CFC: //FB 13120 Convert pre version 5 xml to 6 var fc = FilterClass.GetFilterClassFromIsoCode(child.InnerText); filterClass = $"{fc.FClass},{fc.Frequency}"; break; case DbOperations.TestSetups.SettingsFields.FilterClass: filterClass = child.InnerText; break; case DbOperations.TestSetups.SettingsFields.Polarity: polarity = child.InnerText; break; case DbOperations.TestSetups.SettingsFields.LimitDuration: limitDuration = child.InnerText; break; case DbOperations.TestSetups.SettingsFields.Duration: duration = child.InnerText; break; case DbOperations.TestSetups.SettingsFields.Delay: delay = child.InnerText; break; case DbOperations.TestSetups.SettingsFields.OutputMode: outputMode = child.InnerText; break; case DbOperations.TestSetups.SettingsFields.DigitalOutDelay: digitalOutDelay = child.InnerText; break; case DbOperations.TestSetups.SettingsFields.DigitalOutDuration: digitalOutDuration = child.InnerText; break; case DbOperations.TestSetups.SettingsFields.DigitalOutLimitDuration: digitalOutLimitDuration = child.InnerText; break; case DbOperations.TestSetups.SettingsFields.SQMode: sqMode = child.InnerText; break; case DbOperations.TestSetups.SettingsFields.SquibDelay: squibDelay = child.InnerText; break; case DbOperations.TestSetups.SettingsFields.SquibCurrent: squibCurrent = child.InnerText; break; case DbOperations.TestSetups.SettingsFields.SquibDuration: squibDuration = child.InnerText; break; case DbOperations.TestSetups.SettingsFields.SquibLimitDuration: squibLimitDuration = child.InnerText; break; case DbOperations.TestSetups.SettingsFields.DIMode: diMode = child.InnerText; break; case DbOperations.TestSetups.SettingsFields.DefaultValue: defaultValue = child.InnerText; break; case DbOperations.TestSetups.SettingsFields.ActiveValue: activeValue = child.InnerText; break; case DbOperations.TestSetups.SettingsFields.UserValue1: userValue1 = child.InnerText; break; case DbOperations.TestSetups.SettingsFields.UserValue2: userValue2 = child.InnerText; break; case DbOperations.TestSetups.SettingsFields.UserValue3: userValue3 = child.InnerText; break; case DbOperations.TestSetups.SettingsFields.ZeroMethod: zeroMethod = child.InnerText; break; case DbOperations.TestSetups.SettingsFields.ZeroMethodStart: zeroStart = child.InnerText; break; case DbOperations.TestSetups.SettingsFields.ZeroMethodEnd: zeroEnd = child.InnerText; break; case DbOperations.TestSetups.SettingsFields.InitialOffset: initialOffset = child.InnerText; break; case DbOperations.TestSetups.SettingsFields.UartBaudRate: uartBaudRate = child.InnerText; break; case DbOperations.TestSetups.SettingsFields.UartDataBits: uartDataBits = child.InnerText; break; case DbOperations.TestSetups.SettingsFields.UartStopBits: uartStopBits = child.InnerText; break; case DbOperations.TestSetups.SettingsFields.UartParity: uartParity = child.InnerText; break; case DbOperations.TestSetups.SettingsFields.UartFlowControl: uartFlowControl = child.InnerText; break; case DbOperations.TestSetups.SettingsFields.UartDataFormat: uartDataFormat = child.InnerText; break; case DbOperations.TestSetups.SettingsFields.StreamOutUDPProfile: streamOutUDPProfile = child.InnerText; break; case DbOperations.TestSetups.SettingsFields.StreamOutUDPAddress: streamOutUDPAddress = child.InnerText; break; case DbOperations.TestSetups.SettingsFields.StreamOutUDPTimeChannelId: streamOutUDPTimeChannelId = child.InnerText; break; case DbOperations.TestSetups.SettingsFields.StreamOutUDPDataChannelId: streamOutUDPDataChannelId = child.InnerText; break; case DbOperations.TestSetups.SettingsFields.StreamOutUDPTmNSConfig: streamOutUDPTmNSConfig = child.InnerText; break; case DbOperations.TestSetups.SettingsFields.StreamOutIRIGTimeDataPacketIntervalMs: streamOutIRIGTimeDataPacketIntervalMs = child.InnerText; break; case DbOperations.TestSetups.SettingsFields.StreamOutTMATSIntervalMs: streamOutTMATIntervalMs = child.InnerText; break; case DbOperations.TestSetups.SettingsFields.ACCouplingEnabled: acCouplingEnabled = child.InnerText; break; //33415 Voltage insertion channel should be half bridge case DbOperations.TestSetups.SettingsFields.BridgeType: bridgeType = child.InnerText; break; } } } } } else { //Settings is empty, so get Range from Group range = GetGroupRange(channel, groupLookup); //18771 Get channel settings from sensors if is empty filterClass = GetSettingFromSensorLookup(sensorLookup, channel, DbOperations.TestSetups.SettingsFields.FilterClass); polarity = GetSettingFromSensorLookup(sensorLookup, channel, DbOperations.TestSetups.SettingsFields.Polarity); limitDuration = GetSettingFromSensorLookup(sensorLookup, channel, DbOperations.TestSetups.SettingsFields.LimitDuration); duration = GetSettingFromSensorLookup(sensorLookup, channel, DbOperations.TestSetups.SettingsFields.Duration); delay = GetSettingFromSensorLookup(sensorLookup, channel, DbOperations.TestSetups.SettingsFields.Delay); outputMode = GetSettingFromSensorLookup(sensorLookup, channel, DbOperations.TestSetups.SettingsFields.OutputMode); digitalOutDelay = GetSettingFromSensorLookup(sensorLookup, channel, DbOperations.TestSetups.SettingsFields.DigitalOutDelay); digitalOutDuration = GetSettingFromSensorLookup(sensorLookup, channel, DbOperations.TestSetups.SettingsFields.DigitalOutDuration); digitalOutLimitDuration = GetSettingFromSensorLookup(sensorLookup, channel, DbOperations.TestSetups.SettingsFields.DigitalOutLimitDuration); sqMode = GetSettingFromSensorLookup(sensorLookup, channel, DbOperations.TestSetups.SettingsFields.SQMode); squibDelay = GetSettingFromSensorLookup(sensorLookup, channel, DbOperations.TestSetups.SettingsFields.SquibDelay); squibCurrent = GetSettingFromSensorLookup(sensorLookup, channel, DbOperations.TestSetups.SettingsFields.SquibCurrent); squibDuration = GetSettingFromSensorLookup(sensorLookup, channel, DbOperations.TestSetups.SettingsFields.SquibDuration); squibLimitDuration = GetSettingFromSensorLookup(sensorLookup, channel, DbOperations.TestSetups.SettingsFields.SquibLimitDuration); diMode = GetSettingFromSensorLookup(sensorLookup, channel, DbOperations.TestSetups.SettingsFields.DIMode); defaultValue = GetSettingFromSensorLookup(sensorLookup, channel, DbOperations.TestSetups.SettingsFields.DefaultValue); activeValue = GetSettingFromSensorLookup(sensorLookup, channel, DbOperations.TestSetups.SettingsFields.ActiveValue); userValue1 = GetSettingFromSensorLookup(sensorLookup, channel, DbOperations.TestSetups.SettingsFields.UserValue1); userValue2 = GetSettingFromSensorLookup(sensorLookup, channel, DbOperations.TestSetups.SettingsFields.UserValue2); userValue3 = GetSettingFromSensorLookup(sensorLookup, channel, DbOperations.TestSetups.SettingsFields.UserValue3); //18363 add uart sensors uartBaudRate = GetSettingFromSensorLookup(sensorLookup, channel, DbOperations.TestSetups.SettingsFields.UartBaudRate); uartDataBits = GetSettingFromSensorLookup(sensorLookup, channel, DbOperations.TestSetups.SettingsFields.UartDataBits); uartStopBits = GetSettingFromSensorLookup(sensorLookup, channel, DbOperations.TestSetups.SettingsFields.UartStopBits); uartParity = GetSettingFromSensorLookup(sensorLookup, channel, DbOperations.TestSetups.SettingsFields.UartParity); uartFlowControl = GetSettingFromSensorLookup(sensorLookup, channel, DbOperations.TestSetups.SettingsFields.UartFlowControl); uartDataFormat = GetSettingFromSensorLookup(sensorLookup, channel, DbOperations.TestSetups.SettingsFields.UartDataFormat); //18362 add stream setting sensors streamOutUDPProfile = GetSettingFromSensorLookup(sensorLookup, channel, DbOperations.TestSetups.SettingsFields.StreamOutUDPProfile); streamOutUDPAddress = GetSettingFromSensorLookup(sensorLookup, channel, DbOperations.TestSetups.SettingsFields.StreamOutUDPAddress); streamOutUDPTimeChannelId = GetSettingFromSensorLookup(sensorLookup, channel, DbOperations.TestSetups.SettingsFields.StreamOutUDPTimeChannelId); streamOutUDPDataChannelId = GetSettingFromSensorLookup(sensorLookup, channel, DbOperations.TestSetups.SettingsFields.StreamOutUDPDataChannelId); streamOutUDPTmNSConfig = GetSettingFromSensorLookup(sensorLookup, channel, DbOperations.TestSetups.SettingsFields.StreamOutUDPTmNSConfig); streamOutIRIGTimeDataPacketIntervalMs = GetSettingFromSensorLookup(sensorLookup, channel, DbOperations.TestSetups.SettingsFields.StreamOutIRIGTimeDataPacketIntervalMs); streamOutTMATIntervalMs = GetSettingFromSensorLookup(sensorLookup, channel, DbOperations.TestSetups.SettingsFields.StreamOutTMATSIntervalMs); //18771 Get these channel settings from the sensor calibration zeroMethod = GetSettingFromCalibration(sensorLookup, channel, DbOperations.TestSetups.SettingsFields.ZeroMethod, sensorCalibrations); zeroStart = GetSettingFromCalibration(sensorLookup, channel, DbOperations.TestSetups.SettingsFields.ZeroMethodStart, sensorCalibrations); zeroEnd = GetSettingFromCalibration(sensorLookup, channel, DbOperations.TestSetups.SettingsFields.ZeroMethodEnd, sensorCalibrations); initialOffset = GetSettingFromCalibration(sensorLookup, channel, DbOperations.TestSetups.SettingsFields.InitialOffset, sensorCalibrations); } //Range,CFC,Polarity var settings = new List(); var dictionary = DbOperations.GetSettingsLookup(); if (!string.IsNullOrWhiteSpace(range)) { var rangeTuple = dictionary[ChannelSettingBase.RANGE]; settings.Add(new ChannelSettingBase(rangeTuple.Item1, rangeTuple.Item2, rangeTuple.Item3) { DoubleValue = Convert.ToDouble(range, CultureInfo.InvariantCulture) }); } if (!string.IsNullOrWhiteSpace(acCouplingEnabled)) { var acCouplingTuple = dictionary[ChannelSettingBase.ACCouplingEnabled]; var setting = new ChannelSettingBase(acCouplingTuple.Item1, acCouplingTuple.Item2, acCouplingTuple.Item3); if (bool.TryParse(acCouplingEnabled, out var b)) { setting.BoolValue = b; } settings.Add(setting); } //FB 13120 filter class will be used instead of cfc if (!string.IsNullOrWhiteSpace(filterClass)) { var FilterClassTuple = dictionary[ChannelSettingBase.FilterClass]; settings.Add(new ChannelSettingBase(FilterClassTuple.Item1, FilterClassTuple.Item2, FilterClassTuple.Item3) { Value = filterClass }); } if (!string.IsNullOrWhiteSpace(polarity)) { var polarityTuple = dictionary[ChannelSettingBase.POLARITY]; settings.Add(new ChannelSettingBase(polarityTuple.Item1, polarityTuple.Item2, polarityTuple.Item3) { Value = polarity }); } //33415 Voltage insertion channel should be half bridge if (!string.IsNullOrWhiteSpace(bridgeType)) { var bridgeTypeTuple = dictionary[ChannelSettingBase.BRIDGE_TYPE]; settings.Add(new ChannelSettingBase(bridgeTypeTuple.Item1, bridgeTypeTuple.Item2, bridgeTypeTuple.Item3) { Value = bridgeType }); } if (!string.IsNullOrWhiteSpace(limitDuration)) { var tuple = dictionary[ChannelSettingBase.DIGITALOUT_LIMIT_DURATION]; settings.Add(new ChannelSettingBase(tuple.Item1, tuple.Item2, tuple.Item3) { BoolValue = Convert.ToBoolean(limitDuration) }); tuple = dictionary[ChannelSettingBase.SQUIB_LIMIT_DURATION]; settings.Add(new ChannelSettingBase(tuple.Item1, tuple.Item2, tuple.Item3) { BoolValue = Convert.ToBoolean(limitDuration) }); } if (!string.IsNullOrWhiteSpace(duration)) { var tuple = dictionary[ChannelSettingBase.DIGITALOUT_DURATION]; settings.Add(new ChannelSettingBase(tuple.Item1, tuple.Item2, tuple.Item3) { DoubleValue = Convert.ToDouble(duration, CultureInfo.InvariantCulture) }); tuple = dictionary[ChannelSettingBase.SQUIB_DURATION]; settings.Add(new ChannelSettingBase(tuple.Item1, tuple.Item2, tuple.Item3) { DoubleValue = Convert.ToDouble(duration, CultureInfo.InvariantCulture) }); } if (!string.IsNullOrWhiteSpace(delay)) { var tuple = dictionary[ChannelSettingBase.DIGITALOUT_DELAY]; settings.Add(new ChannelSettingBase(tuple.Item1, tuple.Item2, tuple.Item3) { DoubleValue = Convert.ToDouble(delay, CultureInfo.InvariantCulture) }); tuple = dictionary[ChannelSettingBase.SQUIB_DELAY]; settings.Add(new ChannelSettingBase(tuple.Item1, tuple.Item2, tuple.Item3) { DoubleValue = Convert.ToDouble(delay, CultureInfo.InvariantCulture) }); } if (!string.IsNullOrWhiteSpace(outputMode)) { var tuple = dictionary[ChannelSettingBase.OUTPUT_MODE]; if (!int.TryParse(outputMode, out int iTemp)) { APILogger.Log("invalid output mode: outputMode for channel", channel.GetChannelName(SerializedSettings.ISOViewMode)); } settings.Add(new ChannelSettingBase(tuple.Item1, tuple.Item2, tuple.Item3) { IntValue = iTemp }); } if (!string.IsNullOrWhiteSpace(digitalOutDelay)) { var tuple = dictionary[ChannelSettingBase.DIGITALOUT_DELAY]; settings.Add(new ChannelSettingBase(tuple.Item1, tuple.Item2, tuple.Item3) { DoubleValue = Convert.ToDouble(digitalOutDelay, CultureInfo.InvariantCulture) }); } if (!string.IsNullOrWhiteSpace(digitalOutDuration)) { var tuple = dictionary[ChannelSettingBase.DIGITALOUT_DURATION]; settings.Add(new ChannelSettingBase(tuple.Item1, tuple.Item2, tuple.Item3) { DoubleValue = Convert.ToDouble(digitalOutDuration, CultureInfo.InvariantCulture) }); } if (!string.IsNullOrWhiteSpace(digitalOutLimitDuration)) { var tuple = dictionary[ChannelSettingBase.DIGITALOUT_LIMIT_DURATION]; settings.Add(new ChannelSettingBase(tuple.Item1, tuple.Item2, tuple.Item3) { BoolValue = Convert.ToBoolean(digitalOutLimitDuration) }); } if (!string.IsNullOrWhiteSpace(sqMode)) { var tuple = dictionary[ChannelSettingBase.SQMODE]; settings.Add(new ChannelSettingBase(tuple.Item1, tuple.Item2, tuple.Item3) { IntValue = Convert.ToInt32(sqMode) }); } if (!string.IsNullOrWhiteSpace(squibDelay)) { var tuple = dictionary[ChannelSettingBase.SQUIB_DELAY]; settings.Add(new ChannelSettingBase(tuple.Item1, tuple.Item2, tuple.Item3) { DoubleValue = Convert.ToDouble(squibDelay, CultureInfo.InvariantCulture) }); } if (!string.IsNullOrWhiteSpace(squibCurrent)) { var tuple = dictionary[ChannelSettingBase.SQUIB_CURRENT]; settings.Add(new ChannelSettingBase(tuple.Item1, tuple.Item2, tuple.Item3) { DoubleValue = Convert.ToDouble(squibCurrent, CultureInfo.InvariantCulture) }); } if (!string.IsNullOrWhiteSpace(squibDuration)) { var tuple = dictionary[ChannelSettingBase.SQUIB_DURATION]; settings.Add(new ChannelSettingBase(tuple.Item1, tuple.Item2, tuple.Item3) { DoubleValue = Convert.ToDouble(squibDuration, CultureInfo.InvariantCulture) }); } if (!string.IsNullOrWhiteSpace(squibLimitDuration)) { var tuple = dictionary[ChannelSettingBase.SQUIB_LIMIT_DURATION]; settings.Add(new ChannelSettingBase(tuple.Item1, tuple.Item2, tuple.Item3) { BoolValue = Convert.ToBoolean(squibLimitDuration) }); } if (!string.IsNullOrWhiteSpace(uartBaudRate)) { var tuple = dictionary[ChannelSettingBase.BAUD_RATE]; settings.Add(new ChannelSettingBase(tuple.Item1, tuple.Item2, tuple.Item3) { IntValue = Convert.ToInt32(uartBaudRate) }); } if (!string.IsNullOrWhiteSpace(uartDataBits)) { var tuple = dictionary[ChannelSettingBase.DATA_BITS]; settings.Add(new ChannelSettingBase(tuple.Item1, tuple.Item2, tuple.Item3) { IntValue = Convert.ToInt32(uartDataBits) }); } if (!string.IsNullOrWhiteSpace(uartStopBits)) { var tuple = dictionary[ChannelSettingBase.STOP_BITS]; settings.Add(new ChannelSettingBase(tuple.Item1, tuple.Item2, tuple.Item3) { Value = uartStopBits }); } if (!string.IsNullOrWhiteSpace(uartParity)) { var tuple = dictionary[ChannelSettingBase.PARITY]; settings.Add(new ChannelSettingBase(tuple.Item1, tuple.Item2, tuple.Item3) { Value = uartParity }); } if (!string.IsNullOrWhiteSpace(uartFlowControl)) { var tuple = dictionary[ChannelSettingBase.FLOW_CONTROL]; settings.Add(new ChannelSettingBase(tuple.Item1, tuple.Item2, tuple.Item3) { Value = uartFlowControl }); } if (!string.IsNullOrWhiteSpace(uartDataFormat)) { var tuple = dictionary[ChannelSettingBase.DATA_FORMAT]; settings.Add(new ChannelSettingBase(tuple.Item1, tuple.Item2, tuple.Item3) { Value = uartDataFormat }); } if (!string.IsNullOrWhiteSpace(diMode)) { var tuple = dictionary[ChannelSettingBase.DIMODE]; settings.Add(new ChannelSettingBase(tuple.Item1, tuple.Item2, tuple.Item3) { IntValue = Convert.ToInt32(diMode) }); } if (!string.IsNullOrWhiteSpace(defaultValue)) { var tuple = dictionary[ChannelSettingBase.DEFAULT_VALUE]; settings.Add(new ChannelSettingBase(tuple.Item1, tuple.Item2, tuple.Item3) { DoubleValue = Convert.ToDouble(defaultValue, CultureInfo.InvariantCulture) }); } if (!string.IsNullOrWhiteSpace(activeValue)) { var tuple = dictionary[ChannelSettingBase.ACTIVE_VALUE]; settings.Add(new ChannelSettingBase(tuple.Item1, tuple.Item2, tuple.Item3) { DoubleValue = Convert.ToDouble(activeValue, CultureInfo.InvariantCulture) }); } if (!string.IsNullOrWhiteSpace(streamInUDPAddress)) { var tuple = dictionary[ChannelSettingBase.UDP_ADDRESS_IN]; settings.Add(new ChannelSettingBase(tuple.Item1, tuple.Item2, tuple.Item3) { Value = streamInUDPAddress }); } if (!string.IsNullOrWhiteSpace(streamOutUDPProfile)) { var tuple = dictionary[ChannelSettingBase.UDP_PROFILE]; settings.Add(new ChannelSettingBase(tuple.Item1, tuple.Item2, tuple.Item3) { Value = streamOutUDPProfile }); } if (!string.IsNullOrWhiteSpace(streamOutUDPAddress)) { var tuple = dictionary[ChannelSettingBase.UDP_ADDRESS]; settings.Add(new ChannelSettingBase(tuple.Item1, tuple.Item2, tuple.Item3) { Value = streamOutUDPAddress }); } if (!string.IsNullOrWhiteSpace(streamOutUDPTimeChannelId)) { var tuple = dictionary[ChannelSettingBase.UDP_TIME_CHID]; settings.Add(new ChannelSettingBase(tuple.Item1, tuple.Item2, tuple.Item3) { IntValue = Convert.ToInt32(streamOutUDPTimeChannelId) }); } if (!string.IsNullOrWhiteSpace(streamOutUDPDataChannelId)) { var tuple = dictionary[ChannelSettingBase.UDP_DATA_CHID]; settings.Add(new ChannelSettingBase(tuple.Item1, tuple.Item2, tuple.Item3) { IntValue = Convert.ToInt32(streamOutUDPDataChannelId) }); } if (!string.IsNullOrWhiteSpace(streamOutUDPTmNSConfig)) { var tuple = dictionary[ChannelSettingBase.UDP_TMNS_CONFIG]; settings.Add(new ChannelSettingBase(tuple.Item1, tuple.Item2, tuple.Item3) { Value = streamOutUDPTmNSConfig }); } if (!string.IsNullOrWhiteSpace(streamOutIRIGTimeDataPacketIntervalMs)) { var tuple = dictionary[ChannelSettingBase.IRIG_TDP_INTERVAL_MS]; settings.Add(new ChannelSettingBase(tuple.Item1, tuple.Item2, tuple.Item3) { IntValue = Convert.ToInt32(streamOutIRIGTimeDataPacketIntervalMs) }); } if (!string.IsNullOrWhiteSpace(streamOutTMATIntervalMs)) { var tuple = dictionary[ChannelSettingBase.TMATS_INTERVAL_MS]; settings.Add(new ChannelSettingBase(tuple.Item1, tuple.Item2, tuple.Item3) { IntValue = Convert.ToInt32(streamOutTMATIntervalMs) }); } ProcessUserValues(userValue1, userValue2, userValue3, dictionary, ref settings); var bHadZeroMethod = ProcessZeroMethod(zeroMethod, zeroStart, zeroEnd, dictionary, ref settings); if (!string.IsNullOrWhiteSpace(initialOffset)) { var tuple = dictionary[ChannelSettingBase.INITIAL_OFFSET]; settings.Add(new ChannelSettingBase(tuple.Item1, tuple.Item2, tuple.Item3) { Value = initialOffset }); } channel.ChannelSettings = settings.ToArray(); //15262 Absolute zero sensors in test setup change to ave over time after import. //there's a FixZeroMethodSetting that will explicitly correct the zeromethod if it's not present //in the xml, however the default setting could have been set, so unset it here if (!bHadZeroMethod) { channel.RemoveZeroMethodSetting(); } } /// /// This is for use when there are no elements in the of a channel /// /// /// /// /// /// private string GetSettingFromCalibration(Dictionary sensorLookup, GroupChannel channel, DbOperations.TestSetups.SettingsFields field, List calibrations) { var result = string.Empty; var serialNumber = string.Empty; foreach (var entry in sensorLookup) { if (entry.Value.Comment == channel.ChannelName) { serialNumber = entry.Value.SerialNumber; break; } } if (!string.IsNullOrWhiteSpace(serialNumber)) { foreach (var calibration in calibrations) { if (calibration.SerialNumber == serialNumber) { switch (field) { case DbOperations.TestSetups.SettingsFields.ZeroMethod: result = calibration.ZeroMethods.Methods.ToList().Last().Method.ToString(); break; case DbOperations.TestSetups.SettingsFields.ZeroMethodStart: result = calibration.ZeroMethods.Methods.ToList().Last().Start.ToString(); break; case DbOperations.TestSetups.SettingsFields.ZeroMethodEnd: result = calibration.ZeroMethods.Methods.ToList().Last().End.ToString(); break; case DbOperations.TestSetups.SettingsFields.InitialOffset: var initialOffset = calibration.InitialOffsets.Offsets.ToList().Last(); result = $"{initialOffset.Form},{initialOffset.EU},{initialOffset.MV}"; break; } break; } } } return result; } /// /// This is for use when there are no elements in the of a channel /// /// /// /// /// private string GetSettingFromSensorLookup(Dictionary sensorLookup, GroupChannel channel, DbOperations.TestSetups.SettingsFields field) { var result = string.Empty; var key = string.Empty; foreach (var entry in sensorLookup) { var sensorSerialNumber = string.Empty; if (entry.Value.Bridge == SensorConstants.BridgeType.TOMDigital) { sensorSerialNumber = entry.Value.SerialNumber; } else { sensorSerialNumber = entry.Value.Comment; } if (sensorSerialNumber == channel.ChannelName) { key = entry.Key; break; } } if (!string.IsNullOrWhiteSpace(key)) { switch (field) { case DbOperations.TestSetups.SettingsFields.FilterClass: result = sensorLookup[key].FilterClass.ToString(); break; case DbOperations.TestSetups.SettingsFields.Polarity: result = sensorLookup[key].Polarity.ToString(); break; case DbOperations.TestSetups.SettingsFields.LimitDuration: result = sensorLookup[key].LimitDuration.ToString(); break; case DbOperations.TestSetups.SettingsFields.Duration: result = sensorLookup[key].DurationMS.ToString(); break; case DbOperations.TestSetups.SettingsFields.Delay: result = sensorLookup[key].DelayMS.ToString(); break; case DbOperations.TestSetups.SettingsFields.OutputMode: var intOutputMode = (int)sensorLookup[key].DigitalOutputMode; result = intOutputMode.ToString(); break; case DbOperations.TestSetups.SettingsFields.DigitalOutDelay: result = sensorLookup[key].DigitalOutputDelayMS.ToString(); break; case DbOperations.TestSetups.SettingsFields.DigitalOutDuration: result = sensorLookup[key].DigitalOutputDurationMS.ToString(); break; case DbOperations.TestSetups.SettingsFields.DigitalOutLimitDuration: result = sensorLookup[key].DigitalOutputLimitDuration.ToString(); break; case DbOperations.TestSetups.SettingsFields.SQMode: var intSquibFireMode = (int)sensorLookup[key].SquibFireMode; result = intSquibFireMode.ToString(); break; case DbOperations.TestSetups.SettingsFields.SquibDelay: result = sensorLookup[key].SquibFireDelayMS.ToString(); break; case DbOperations.TestSetups.SettingsFields.SquibCurrent: result = sensorLookup[key].SquibOutputCurrent.ToString(); break; case DbOperations.TestSetups.SettingsFields.SquibDuration: result = sensorLookup[key].SquibFireDurationMS.ToString(); break; case DbOperations.TestSetups.SettingsFields.SquibLimitDuration: result = sensorLookup[key].LimitSquibFireDuration.ToString(); break; case DbOperations.TestSetups.SettingsFields.DIMode: var intDigitalOutputMode = (int)sensorLookup[key].DigitalOutputMode; result = intDigitalOutputMode.ToString(); break; case DbOperations.TestSetups.SettingsFields.UserValue1: result = sensorLookup[key].UserValue1.ToString(); break; case DbOperations.TestSetups.SettingsFields.UserValue2: result = sensorLookup[key].UserValue2.ToString(); break; case DbOperations.TestSetups.SettingsFields.UserValue3: result = sensorLookup[key].UserValue3.ToString(); break; case DbOperations.TestSetups.SettingsFields.UartBaudRate: result = sensorLookup[key].UartBaudRate.ToString(); break; case DbOperations.TestSetups.SettingsFields.UartDataBits: result = sensorLookup[key].UartDataBits.ToString(); break; case DbOperations.TestSetups.SettingsFields.UartStopBits: result = sensorLookup[key].UartStopBits.ToString(); break; case DbOperations.TestSetups.SettingsFields.UartParity: result = sensorLookup[key].UartParity.ToString(); break; case DbOperations.TestSetups.SettingsFields.UartFlowControl: result = sensorLookup[key].UartFlowControl.ToString(); break; case DbOperations.TestSetups.SettingsFields.UartDataFormat: result = sensorLookup[key].UartDataFormat.ToString(); break; case DbOperations.TestSetups.SettingsFields.StreamOutUDPProfile: result = sensorLookup[key].StreamOutUDPProfile.ToString(); break; case DbOperations.TestSetups.SettingsFields.StreamOutUDPAddress: result = sensorLookup[key].StreamOutUDPAddress.ToString(); break; case DbOperations.TestSetups.SettingsFields.StreamOutUDPTimeChannelId: result = sensorLookup[key].StreamOutUDPTimeChannelId.ToString(); break; case DbOperations.TestSetups.SettingsFields.StreamOutUDPDataChannelId: result = sensorLookup[key].StreamOutUDPDataChannelId.ToString(); break; case DbOperations.TestSetups.SettingsFields.StreamOutUDPTmNSConfig: result = sensorLookup[key].StreamOutUDPTmNSConfig.ToString(); break; case DbOperations.TestSetups.SettingsFields.StreamOutIRIGTimeDataPacketIntervalMs: result = sensorLookup[key].StreamOutIRIGTimeDataPacketIntervalMs.ToString(); break; case DbOperations.TestSetups.SettingsFields.StreamOutTMATSIntervalMs: result = sensorLookup[key].StreamOutTMATSIntervalMs.ToString(); break; //33415 Voltage insertion channel should be half bridge case DbOperations.TestSetups.SettingsFields.BridgeType: result = sensorLookup[key].Bridge.ToString(); break; } } return result; } /// /// handles setting zero method properties from imported xml for a group channel for an embedded group /// there is equivalent code in Group.ReadXml, but apparently embedded groups don't go through that /// route in imports ... /// 15270 Sensor Import does not import most channel settings /// private bool ProcessZeroMethod(string zeroMethod, string zeroStart, string zeroEnd, IReadOnlyDictionary> dictionary, ref List settings) { var bHasZeroMethodInXML = false; if (!string.IsNullOrWhiteSpace(zeroMethod)) { var tuple = dictionary[ChannelSettingBase.ZEROMETHOD]; settings.Add(new ChannelSettingBase(tuple.Item1, tuple.Item2, tuple.Item3) { Value = zeroMethod }); bHasZeroMethodInXML = true; } if (!string.IsNullOrWhiteSpace(zeroStart)) { var tuple = dictionary[ChannelSettingBase.ZEROMETHODSTART]; if (double.TryParse(zeroStart, NumberStyles.Any, CultureInfo.InvariantCulture, out var d)) { settings.Add(new ChannelSettingBase(tuple.Item1, tuple.Item2, tuple.Item3) { DoubleValue = d }); } } if (!string.IsNullOrWhiteSpace(zeroEnd)) { var tuple = dictionary[ChannelSettingBase.ZEROMETHODEND]; if (double.TryParse(zeroEnd, NumberStyles.Any, CultureInfo.InvariantCulture, out var d)) { settings.Add(new ChannelSettingBase(tuple.Item1, tuple.Item2, tuple.Item3) { DoubleValue = d }); } } return bHasZeroMethodInXML; } /// /// handles setting user value settings from imported xml for an embedded group channel /// there is equivalent code in Group.ReadXml, but embedded groups dont' go through that route /// 15270 Sensor Import does not import most channel settings /// private void ProcessUserValues(string userValue1, string userValue2, string userValue3, IReadOnlyDictionary> dictionary, ref List settings) { if (!string.IsNullOrWhiteSpace(userValue1)) { var tuple = dictionary[ChannelSettingBase.USERVALUE1]; settings.Add(new ChannelSettingBase(tuple.Item1, tuple.Item2, tuple.Item3) { Value = userValue1 }); } if (!string.IsNullOrWhiteSpace(userValue2)) { var tuple = dictionary[ChannelSettingBase.USERVALUE2]; settings.Add(new ChannelSettingBase(tuple.Item1, tuple.Item2, tuple.Item3) { Value = userValue2 }); } if (!string.IsNullOrWhiteSpace(userValue3)) { var tuple = dictionary[ChannelSettingBase.USERVALUE3]; settings.Add(new ChannelSettingBase(tuple.Item1, tuple.Item2, tuple.Item3) { Value = userValue3 }); } } private string GetGroupRange(GroupChannel channel, IDictionary groupLookup) { foreach (var group in groupLookup) { if (group.Value.Name != channel.GroupName) { continue; } foreach (var groupChannel in group.Value.GroupChannelList) { if ((groupChannel as GroupChannel).ChannelName != channel.ChannelName) { continue; } foreach (var setting in groupChannel.ChannelSettings) { if (setting.SettingName == DbOperations.TestSetups.SettingsFields.Range.ToString()) { return setting.Value; } } } } return string.Empty; } #endregion READ FROM XML #region XML TAGS private const string TESTSETUP_ROOT = "TestSetup"; private const string DASLIST_ROOT = "DASList"; private const string TESTID_ROOT = "Id"; private const string DASHARDWARE_ROOT = "DASHardware"; private const string DAS_SERIALNUMBER = "SerialNumber"; private const string DAS_TESTSAMPLERATE = "SamplesPerSecond"; private const string DAS_ISCLOCKMASTER = "IsClockMaster"; private const string DAS_AAFRATE = "AntiAliasFilterRate"; private const string DAS_PTPDOMAINID = "PTPDomainId"; private const string ADDEDGROUPS_ROOT = "AddedGroups"; private const string SYSBUILTTESTOBJECTS_ROOT = "SysBuiltTestObjects"; private const string TESTOBJECT_ROOT = "TestObject"; private const string GROUP_ROOT = "Group"; private const string HARDWARE_LIST = "HardwareList"; private const string HARDWARE = "Hardware"; private const string CHANNEL_ROOT = "Channel"; private const string CHANNELSETTINGS_ROOT = "ChannelSettings"; private const string TESTOBJECTS_ROOT = "TestObjects"; private const string GROUPS_ROOT = "Groups"; private const string GRAPHS_ROOT = "Graphs"; private const string GRAPH_ROOT = "Graph"; private const string FIELDS_ROOT = "Fields"; private const string METADATAS_ROOT = "MetaDatas"; private const string METADATA_ROOT = "MetaData"; private const string HARDWAREOVERRIDE_ACTION = "Action"; private const string HARDWAREOVERRIDE_HID = "HID"; private const string HARDWAREOVERRIDE_ROOT = "HardwareOverride"; private const string HARDWAREOVERRIDES_ROOT = "HardwareOverrides"; private const string HARDWAREINCLUDES_ROOT = "HardwareIncludes"; private const string HARDWAREREMOVES_ROOT = "HardwareRemoves"; private const string HARDWARELIST_ROOT = "HWList"; private const string LEVELTRIGGERS_ROOT = "LevelTriggers"; private const string LEVELTRIGGER_ROOT = "LevelTrigger"; private const string LT_TESTOBJECTCHANNELID = "TestObjectChannelId"; private const string LT_GROUPCHANNELID = "GroupChannelId"; private const string LT_HARDWARECHANNELID = "HardwareChannelId"; private const string LT_SENSORSERIALNUMBER = "SensorSerialNumber"; private const string LT_LESSTHANTHRESHOLDEU = "LessThanValue"; private const string LT_LESSTHANENABLED = "LessThanEnabled"; private const string LT_GREATERTHANENABLED = "GreaterThanEnabled"; private const string LT_GREATERTHANTHRESHOLDEU = "GreaterThanValue"; private const string LT_TRIGGEROUTSIDE = "TriggerOutside"; private const string LT_TRIGGERINSIDE = "TriggerInside"; private const string LT_OUTSIDEUPPEREU = "OutsideUpperEU"; private const string LT_OUTSIDELOWEREU = "OutsideLowerEU"; private const string LT_INSIDEUPPEREU = "InsideUpperEU"; private const string LT_INSIDELOWEREU = "InsideLowerEU"; private const string CALCULATEDCHANNELS_ROOT = "CalculatedChannels"; private const string CALCULATEDCHANNEL_ROOT = "CalculatedChannel"; private const string CC_CALCULATEDCHANNELVALUE = "ISOCODE"; private const string CC_CFCFORINPUTCHANNELS = "CFCInputChannels"; private const string CC_CFCFOROUTPUTCHANNELS = "CFCOutput"; private const string CC_TESTSETUPNAME = "TestSetupName"; private const string CC_NAME = "Name"; private const string CC_INPUTCHANNELIDS = "InputChannelIds"; private const string CC_ID = "ID"; private const string CC_OPERATION = "Operation"; private const string TO_SERIALNUMBER = "TestObjectSerialNumber"; #endregion XML TAGS /// /// writes a test setup out to xml /// /// public void WriteXML(ref System.Xml.XmlWriter writer) { writer.WriteStartElement(TESTSETUP_ROOT); var f1 = Enum.GetValues(typeof(DbOperations.TestSetups.ChannelFields)).Cast().ToArray();//done var f6 = Enum.GetValues(typeof(DbOperations.TestSetups.TestSetupObjectFields)).Cast().ToArray();//done var testSetupGroupFieldsArray = Enum.GetValues(typeof(DbOperations.TestSetups.TestSetupGroupFields)).Cast().ToArray();//done var f7 = Enum.GetValues(typeof(DbOperations.TestSetups.GraphFields)).Cast().ToArray(); //done var f2 = Enum.GetValues(typeof(DbOperations.TestSetups.Fields)).Cast().ToArray();//done var f3 = Enum.GetValues(typeof(DbOperations.TestSetups.TestObjectMetaDataFields)).Cast().ToArray(); //new element in v9 //15727 Building and Replacing Racks/Mods writer.WriteStartElement(TESTID_ROOT); writer.WriteString(Id.ToString()); writer.WriteEndElement(); #region DASList if (DASSampleRateList.Count > 0) { writer.WriteStartElement(DASLIST_ROOT); // foreach (var pair in DASSampleRateList) { writer.WriteStartElement(DASHARDWARE_ROOT); // writer.WriteStartElement(DAS_SERIALNUMBER); // writer.WriteString(pair.Key); writer.WriteEndElement(); writer.WriteStartElement(DAS_TESTSAMPLERATE); // writer.WriteString(pair.Value.ToString(CultureInfo.InvariantCulture)); writer.WriteEndElement(); writer.WriteStartElement(DAS_ISCLOCKMASTER); // if (!DASClockMasterList.ContainsKey(pair.Key.ToLower())) { writer.WriteString(false.ToString()); } else { writer.WriteString(DASClockMasterList[pair.Key.ToString()].ToString()); } writer.WriteEndElement(); writer.WriteStartElement(DAS_AAFRATE); // writer.WriteString(DASAAFRateList[pair.Key.ToString()].ToString()); writer.WriteEndElement(); writer.WriteStartElement(DAS_PTPDOMAINID); // if (!DASPTPDomainIDList.ContainsKey(pair.Key.ToString())) { writer.WriteString("1"); } else { writer.WriteString(DASPTPDomainIDList[pair.Key.ToString()].ToString()); } writer.WriteEndElement(); writer.WriteEndElement(); } writer.WriteEndElement(); } #endregion #region Groups writer.WriteStartElement(GROUPS_ROOT); //foreach (var to in TestObjects) foreach (var g in Groups) { writer.WriteStartElement(GROUP_ROOT); // ReSharper disable once InconsistentNaming //var isoTO = to.GetISOTestObject(); foreach (var toField in testSetupGroupFieldsArray) { if (toField == DbOperations.TestSetups.TestSetupGroupFields.SerialNumber) continue; //Changed to Name in 2.0 writer.WriteStartElement(toField.ToString()); switch (toField) { case DbOperations.TestSetups.TestSetupGroupFields.Name: writer.WriteString(g.Name); break; //Changed from SerialNumber in 2.0 case DbOperations.TestSetups.TestSetupGroupFields.DisplayName: writer.WriteString(g.DisplayName); break; case DbOperations.TestSetups.TestSetupGroupFields.Description: writer.WriteString(g.Description); break; //FB 41857 check for null Name to prevent crash case DbOperations.TestSetups.TestSetupGroupFields.TestSetupName: writer.WriteString(Name?.Trim()); break; case DbOperations.TestSetups.TestSetupGroupFields.DisplayOrder: writer.WriteString(g.DisplayOrder.ToString(CultureInfo.InvariantCulture)); break; case DbOperations.TestSetups.TestSetupGroupFields.Position: writer.WriteString(g.Position); break; case DbOperations.TestSetups.TestSetupGroupFields.TestObjectType: writer.WriteString(g.TestObject); break; case DbOperations.TestSetups.TestSetupGroupFields.Id: writer.WriteString(g.Id.ToString(CultureInfo.InvariantCulture)); break; case DbOperations.TestSetups.TestSetupGroupFields.StaticGroupId: var staticGroupId = g.StaticGroupId == null ? string.Empty : g.StaticGroupId.ToString(); writer.WriteString(staticGroupId); break; default: throw new NotSupportedException("TestTemplate::WriteXML unsupported field: " + toField); } writer.WriteEndElement(); } writer.WriteStartElement(HARDWARE_LIST); foreach (var hardware in g.IncludedHardwareStringList) { writer.WriteStartElement(HARDWARE); writer.WriteString(hardware); writer.WriteEndElement(); //Hardware } writer.WriteEndElement(); //HardwareList //foreach (var ch in isoTO.AllChannels) var sensorLookup = new Dictionary(); var sensors = SensorsCollection.SensorsList.GetAllSensors(false); foreach (var s in sensors) { sensorLookup[s.DatabaseId] = s; } var channels = ChannelsForGroup[g]; foreach (var groupChannel in channels) { if (groupChannel.IsBlank()) { continue; } writer.WriteStartElement(CHANNEL_ROOT); if (groupChannel.DASId > 0 && groupChannel.DASChannelIndex >= 0) { var hardwareList = GetHardware(); groupChannel.Hardware = GetGroupChannelHardware(groupChannel.DASId, groupChannel.DASChannelIndex, hardwareList); groupChannel.HardwareId = GetGroupChannelHardwareId(groupChannel.DASId, groupChannel.DASChannelIndex, hardwareList); } foreach (var f in f1) { writer.WriteStartElement(f.ToString()); switch (f) { case DbOperations.TestSetups.ChannelFields.ISOChannelName: writer.WriteString(groupChannel.IsoChannelName); break; case DbOperations.TestSetups.ChannelFields.ISOCode: writer.WriteString(groupChannel.IsoCode); break; case DbOperations.TestSetups.ChannelFields.UserChannelName: writer.WriteString(groupChannel.UserChannelName); break; case DbOperations.TestSetups.ChannelFields.UserCode: writer.WriteString(groupChannel.UserCode); break; case DbOperations.TestSetups.ChannelFields.Id: writer.WriteString(groupChannel.Id.ToString(CultureInfo.InvariantCulture)); break; case DbOperations.TestSetups.ChannelFields.DASId: writer.WriteString( groupChannel.DASId.ToString(CultureInfo.InvariantCulture)); break; case DbOperations.TestSetups.ChannelFields.DASChannelIdx: writer.WriteString( groupChannel.DASChannelIndex.ToString(CultureInfo .InvariantCulture)); break; case DbOperations.TestSetups.ChannelFields.SensorId: writer.WriteString( groupChannel.SensorId.ToString(CultureInfo.InvariantCulture)); break; case DbOperations.TestSetups.ChannelFields.TestSetupOrder: writer.WriteString( groupChannel.TestSetupOrder.ToString(CultureInfo .InvariantCulture)); break; case DbOperations.TestSetups.ChannelFields.GroupOrder: writer.WriteString( groupChannel.GroupChannelOrder.ToString(CultureInfo .InvariantCulture)); break; case DbOperations.TestSetups.ChannelFields.Disabled: writer.WriteString(false.ToString(CultureInfo.InvariantCulture)); break; case DbOperations.TestSetups.ChannelFields.Settings: foreach (var channelSetting in groupChannel.ChannelSettings) { //FB 13120 Do not export CFC setting to xml file if (channelSetting.SettingName == "CFC") { continue; } writer.WriteStartElement(channelSetting.SettingName); writer.WriteString(channelSetting.Value); writer.WriteEndElement(); //channelSettingName (Range, etc.) } break; default: throw new NotSupportedException("TestTemplate::WriteXML unsupported field: " + f); } writer.WriteEndElement();//f.ToString() } writer.WriteEndElement();//Channel } writer.WriteEndElement();//testobject } writer.WriteEndElement();//testobjects #endregion TestObjects #region Graphs writer.WriteStartElement(GRAPHS_ROOT); foreach (var g in TestGraphs) { writer.WriteStartElement(GRAPH_ROOT); g.WriteXML(ref writer); // writer.WriteStartElement(GRAPH_ROOT); // foreach (var f in f7) // { // writer.WriteStartElement(f.ToString()); // switch (f) // { // case DbOperations.TestSetups.GraphFields.Channels: writer.WriteString(g.GetChannelsForSQL()); break; // case DbOperations.TestSetups.GraphFields.DomainMax: writer.WriteString(g.DomainMax.ToString(System.Globalization.CultureInfo.InvariantCulture)); break; // case DbOperations.TestSetups.GraphFields.DomainMin: writer.WriteString(g.DomainMin.ToString(System.Globalization.CultureInfo.InvariantCulture)); break; // case DbOperations.TestSetups.GraphFields.GraphDescription: writer.WriteString(g.GraphDescription); break; // case DbOperations.TestSetups.GraphFields.GraphName: writer.WriteString(g.GraphName); break; // case DbOperations.TestSetups.GraphFields.LocalOnly: writer.WriteString(LocalOnly.ToString()); break; // case DbOperations.TestSetups.GraphFields.RangeMax: writer.WriteString(g.RangeMax.ToString(System.Globalization.CultureInfo.InvariantCulture)); break; // case DbOperations.TestSetups.GraphFields.RangeMin: writer.WriteString(g.RangeMin.ToString(System.Globalization.CultureInfo.InvariantCulture)); break; // case DbOperations.TestSetups.GraphFields.TemplateName: writer.WriteString(Name); break; // case DbOperations.TestSetups.GraphFields.Thresholds: writer.WriteString(g.GetThresholdsSQL()); break; // case DbOperations.TestSetups.GraphFields.UseDomainMax: writer.WriteString(g.UseDomainMax.ToString()); break; // case DbOperations.TestSetups.GraphFields.UseDomainMin: writer.WriteString(g.UseDomainMin.ToString()); break; // case DbOperations.TestSetups.GraphFields.UseRangeMax: writer.WriteString(g.UseRangeMax.ToString()); break; // case DbOperations.TestSetups.GraphFields.UseRangeMin: writer.WriteString(g.UseRangeMin.ToString()); break; // case DbOperations.TestSetups.GraphFields.TestSetupName: writer.WriteString(Name); break; // default: throw new NotSupportedException("TestTemplate::WriteXML unsupported field: " + f); // } // writer.WriteEndElement();//f.toString() // } writer.WriteEndElement(); } writer.WriteEndElement();//graphs #endregion Graphs #region Fields writer.WriteStartElement(FIELDS_ROOT); foreach (var f in f2) { writer.WriteStartElement(f.ToString()); switch (f) { case DbOperations.TestSetups.Fields.AllowMissingSensors: writer.WriteString(AllowMissingSensors.ToString()); break; case DbOperations.TestSetups.Fields.AllowSensorIdToBlankChannel: writer.WriteString(AllowSensorIdToBlankChannel.ToString()); break; case DbOperations.TestSetups.Fields.AutomaticProgressionDelayMS: writer.WriteString(AutomaticProgressionDelayMS.ToString(CultureInfo.InvariantCulture)); break; case DbOperations.TestSetups.Fields.AutomaticTestProgression: writer.WriteString(AutomaticProgression.ToString()); break; case DbOperations.TestSetups.Fields.AutoVerifyChannels: writer.WriteString(AutoVerifyChannels.ToString()); break; case DbOperations.TestSetups.Fields.CommonStatusLine: writer.WriteString(CommonLine.ToString()); break; case DbOperations.TestSetups.Fields.CalibrationBehavior: writer.WriteString(CalibrationBehavior.ToString()); break; case DbOperations.TestSetups.Fields.CustomerDetails: writer.WriteString((null != CustomerDetails) ? CustomerDetails.Name : ""); break; case DbOperations.TestSetups.Fields.DownloadAll: writer.WriteString(DownloadAll.ToString()); break; case DbOperations.TestSetups.Fields.DownloadFolder: writer.WriteString(DownloadFolder); break; case DbOperations.TestSetups.Fields.Export: writer.WriteString(ViewExport.ToString()); break; case DbOperations.TestSetups.Fields.AlignUDPToPPS: writer.WriteString(AlignUDPToPPS.ToString()); break; case DbOperations.TestSetups.Fields.ExportFolder: writer.WriteString(ExportFolder); break; case DbOperations.TestSetups.Fields.ExportFormat: writer.WriteString(((ulong)ExportFormats).ToString(CultureInfo.InvariantCulture)); break; case DbOperations.TestSetups.Fields.InvertStart: writer.WriteString(InvertStartRecordCompletion.ToString()); break; case DbOperations.TestSetups.Fields.InvertTrigger: writer.WriteString(InvertTriggerCompletion.ToString()); break; case DbOperations.TestSetups.Fields.IgnoreShortedStart: writer.WriteString(IgnoreShortedStartCompletion.ToString()); break; case DbOperations.TestSetups.Fields.IgnoreShortedTrigger: writer.WriteString(IgnoreShortedTriggerCompletion.ToString()); break; case DbOperations.TestSetups.Fields.LabDetails: writer.WriteString((null != LabDetails) ? LabDetails.Name : ""); break; case DbOperations.TestSetups.Fields.LastModified: writer.WriteString(LastModified.ToString(CultureInfo.InvariantCulture)); break; case DbOperations.TestSetups.Fields.LastModifiedBy: writer.WriteString(LastModifiedBy); break; case DbOperations.TestSetups.Fields.LocalOnly: writer.WriteString(LocalOnly.ToString()); break; case DbOperations.TestSetups.Fields.PostTestDiagnostics: writer.WriteString(PostTestDiagnosticsLevel.ToString()); break; case DbOperations.TestSetups.Fields.PostTriggerSeconds: writer.WriteString(PostTriggerSeconds.ToString(CultureInfo.InvariantCulture)); break; case DbOperations.TestSetups.Fields.PreTriggerSeconds: writer.WriteString(PreTriggerSeconds.ToString(CultureInfo.InvariantCulture)); break; case DbOperations.TestSetups.Fields.NumberOfEvents: writer.WriteString(NumberOfEvents.ToString(CultureInfo.InvariantCulture)); break; case DbOperations.TestSetups.Fields.WakeUpMotionTimeout: writer.WriteString(WakeUpMotionTimeout.ToString(CultureInfo.InvariantCulture)); break; case DbOperations.TestSetups.Fields.ScheduledStartDateTime: writer.WriteString(RTCScheduleStartDateTime.ToString(System.Globalization.CultureInfo.InvariantCulture)); break; case DbOperations.TestSetups.Fields.IntervalBetweenEventStartsMinutes: writer.WriteString(IntervalBetweenEventStartsMinutes.ToString(System.Globalization.CultureInfo.InvariantCulture)); break; case DbOperations.TestSetups.Fields.StartWithEvent: writer.WriteString(StartWithEvent.ToString(System.Globalization.CultureInfo.InvariantCulture)); break; case DbOperations.TestSetups.Fields.WakeUpWithMotion: writer.WriteString(WakeUpWithMotion.ToString(System.Globalization.CultureInfo.InvariantCulture)); break; case DbOperations.TestSetups.Fields.RealtimePlotCount: writer.WriteString(DefaultNumberRealtimeGraphs.ToString(CultureInfo.InvariantCulture)); break; case DbOperations.TestSetups.Fields.RecordingMode: writer.WriteString(RecordingMode.ToString()); break; case DbOperations.TestSetups.Fields.RequireConfirmationOnErrors: writer.WriteString(RequireUserConfirmationOnErrors.ToString()); break; case DbOperations.TestSetups.Fields.ROIDownload: writer.WriteString(DoROIDownload.ToString()); break; case DbOperations.TestSetups.Fields.RegionsOfInterest: foreach (var roi in RegionsOfInterest) { writer.WriteStartElement("RegionOfInterest"); writer.WriteElementString("Suffix", roi.Suffix); writer.WriteElementString("Start", roi.Start.ToString(CultureInfo.InvariantCulture)); writer.WriteElementString("End", roi.End.ToString(CultureInfo.InvariantCulture)); writer.WriteElementString("ChannelNames", String.Join(",", roi.ChannelNames)); writer.WriteElementString("ChannelIds", String.Join(",", roi.ChannelIds)); writer.WriteEndElement(); } break; case DbOperations.TestSetups.Fields.ROIEnd: writer.WriteString(ROIEnd.ToString(CultureInfo.InvariantCulture)); break; case DbOperations.TestSetups.Fields.ROIStart: writer.WriteString(ROIStart.ToString(CultureInfo.InvariantCulture)); break; case DbOperations.TestSetups.Fields.SameAsDownloadFolder: writer.WriteString(SameAsDownloadFolder.ToString()); break; case DbOperations.TestSetups.Fields.SamplesPerSecond: writer.WriteString(SamplesPerSecondAggregate.ToString(CultureInfo.InvariantCulture)); break; case DbOperations.TestSetups.Fields.TestEngineerDetails: writer.WriteString((null != TestEngineerDetails) ? TestEngineerDetails.Name : ""); break; case DbOperations.TestSetups.Fields.WarnOnBatteryFail: writer.WriteString(WarnOnFailedBattery.ToString()); break; case DbOperations.TestSetups.Fields.SetupDescription: writer.WriteString(Description); break; case DbOperations.TestSetups.Fields.Settings: writer.WriteString(_settings.ToSerializeString()); break; //FB 41857 check for null Name to prevent crash case DbOperations.TestSetups.Fields.SetupName: writer.WriteString(Name?.Trim()); break; case DbOperations.TestSetups.Fields.StrictDiagnostics: writer.WriteString(StrictDiagnostics.ToString()); break; case DbOperations.TestSetups.Fields.TriggerCheckRealtime: writer.WriteString(TriggerCheckRealtime.ToString()); break; case DbOperations.TestSetups.Fields.TriggerCheckStep: writer.WriteString(TriggerCheckStep.ToString()); break; case DbOperations.TestSetups.Fields.TurnOffExcitation: writer.WriteString(TurnOffExcitation.ToString()); break; case DbOperations.TestSetups.Fields.UploadData: writer.WriteString(UploadData.ToString()); break; case DbOperations.TestSetups.Fields.UploadDataFolder: writer.WriteString(UploadFolder); break; case DbOperations.TestSetups.Fields.UploadExportsOnly: writer.WriteString(UploadExportsOnly.ToString()); break; case DbOperations.TestSetups.Fields.UseCustomerDetails: writer.WriteString(UseCustomerDetails.ToString()); break; case DbOperations.TestSetups.Fields.UseTestEngineerDetails: writer.WriteString(UseTestEngineerDetails.ToString()); break; case DbOperations.TestSetups.Fields.UseLabDetails: writer.WriteString(UseLabratoryDetails.ToString()); break; case DbOperations.TestSetups.Fields.VerifyChannels: writer.WriteString(VerifyChannels.ToString()); break; case DbOperations.TestSetups.Fields.VerifyChannelsDelayMS: writer.WriteString((AutoVerifyDelaySeconds * 1000D).ToString(CultureInfo.InvariantCulture)); break; case DbOperations.TestSetups.Fields.ViewDiagnostics: writer.WriteString(ViewDiagnostics.ToString()); break; case DbOperations.TestSetups.Fields.ViewDownloadAll: writer.WriteString(ViewDownloadAll.ToString()); break; case DbOperations.TestSetups.Fields.ViewRealtime: writer.WriteString(ViewRealtime.ToString()); break; case DbOperations.TestSetups.Fields.ViewROIDownload: writer.WriteString(ViewROIDownload.ToString()); break; case DbOperations.TestSetups.Fields.ErrorMessage: writer.WriteString(CompletionErrorMessage); break; case DbOperations.TestSetups.Fields.Dirty: writer.WriteString(IsDirty.ToString()); break; case DbOperations.TestSetups.Fields.Complete: writer.WriteString(IsComplete.ToString()); break; case DbOperations.TestSetups.Fields.UserTags: writer.WriteString(GetTagsAsCommaSeparatedString(DbOperations.TagsGet)); break; case DbOperations.TestSetups.Fields.DoAutoArm: writer.WriteString(DoAutoArm.ToString()); break; case DbOperations.TestSetups.Fields.DoEnableRepeat: writer.WriteString(DoEnableRepeat.ToString()); break; case DbOperations.TestSetups.Fields.DoStreaming: writer.WriteString(DoStreaming.ToString()); break; case DbOperations.TestSetups.Fields.CheckoutMode: writer.WriteString(CheckoutMode.ToString()); break; case DbOperations.TestSetups.Fields.QuitTestWithoutWarning: writer.WriteString(QuitTestWithoutWarning.ToString()); break; case DbOperations.TestSetups.Fields.SuppressMissingSensorsWarning: writer.WriteString(SuppressMissingSensorsWarning.ToString()); break; case DbOperations.TestSetups.Fields.NotAllChannelsViewer: writer.WriteString(NotAllChannelsViewer.ToString()); break; case DbOperations.TestSetups.Fields.NotAllChannelsRealTime: writer.WriteString(NotAllChannelsRealTime.ToString()); break; case DbOperations.TestSetups.Fields.ISFFile: writer.WriteString(ISFFile); break; case DbOperations.TestSetups.Fields.ClockSyncProfileMaster: writer.WriteString(ClockSyncProfileMaster.ToString()); break; case DbOperations.TestSetups.Fields.ClockSyncProfileSlave: writer.WriteString(ClockSyncProfileSlave.ToString()); break; case DbOperations.TestSetups.Fields.ExtraProperties: foreach (var exp in ExtraProperties) { writer.WriteStartElement("ExtraProperty"); writer.WriteElementString("Key", exp.Key); writer.WriteElementString("Value", exp.Value); writer.WriteEndElement(); } break; case DbOperations.TestSetups.Fields.MeasureSquibResistancesStep: writer.WriteString(MeasureSquibResistancesStep.ToString()); break; case DbOperations.TestSetups.Fields.TestSetupUniqueId: if (TestSetupUniqueId == null) { TestSetupUniqueId = Guid.NewGuid().ToString(); } writer.WriteString(TestSetupUniqueId.ToString()); break; default: throw new NotSupportedException("TestTemplate::WriteXML unsupported field: " + f); } writer.WriteEndElement();//f.ToString() } writer.WriteEndElement();//fields #endregion Fields #region HardwareOverrides if (AddedHardware.Any()) { writer.WriteStartElement(HARDWAREINCLUDES_ROOT); var sb = new StringBuilder(); foreach (var id in AddedHardware) { if (sb.Length > 0) { sb.Append(","); } sb.Append(id.ToString(CultureInfo.InvariantCulture)); } writer.WriteAttributeString(HARDWARELIST_ROOT, sb.ToString()); writer.WriteEndElement(); } if (RemovedHardware.Any()) { writer.WriteStartElement(HARDWAREREMOVES_ROOT); var sb = new StringBuilder(); foreach (var id in RemovedHardware) { if (sb.Length > 0) { sb.Append(","); } sb.Append(id.ToString(CultureInfo.InvariantCulture)); } writer.WriteAttributeString(HARDWARELIST_ROOT, sb.ToString()); writer.WriteEndElement(); } if (HardwareOverrides.Count > 0) { writer.WriteStartElement(HARDWAREOVERRIDES_ROOT); using (var e = HardwareOverrides.GetEnumerator()) { while (e.MoveNext()) { writer.WriteStartElement(HARDWAREOVERRIDE_ROOT); writer.WriteAttributeString(HARDWAREOVERRIDE_HID, e.Current.Value.HardwareId); writer.WriteAttributeString(HARDWAREOVERRIDE_ACTION, e.Current.Value.Action.ToString()); writer.WriteEndElement(); //hardwareoverride_root } } writer.WriteEndElement();//hardware overrides root } #endregion HardwareOverrides #region CalculatedChannels if (CalculatedChannels.Count > 0) { writer.WriteStartElement(CALCULATEDCHANNELS_ROOT); foreach (var cc in CalculatedChannels) { writer.WriteStartElement(CALCULATEDCHANNEL_ROOT); writer.WriteAttributeString(CC_CALCULATEDCHANNELVALUE, cc.CalculatedValueCode); writer.WriteAttributeString(CC_CFCFORINPUTCHANNELS, cc.CFCForInputChannels); writer.WriteAttributeString(CC_CFCFOROUTPUTCHANNELS, cc.ChannelFilterClassForOutput); writer.WriteAttributeString(CC_ID, cc.Id.ToString(CultureInfo.InvariantCulture)); writer.WriteAttributeString(CC_INPUTCHANNELIDS, string.Join(CultureInfo.InvariantCulture.TextInfo.ListSeparator, cc.InputChannelIds)); writer.WriteAttributeString(CC_NAME, cc.Name); writer.WriteAttributeString(CC_OPERATION, cc.Operation.ToString()); writer.WriteAttributeString(CC_TESTSETUPNAME, cc.TestSetupName); writer.WriteEndElement();//CALCULATEDCHANNEL_ROOT } writer.WriteEndElement();//CALCULATEDCHANNELS_ROOT } #endregion CalculatedChannels #region LevelTriggers if (LevelTriggerChannels.Count > 0) { writer.WriteStartElement(LEVELTRIGGERS_ROOT); using (var e = LevelTriggerChannels.GetEnumerator()) { while (e.MoveNext()) { //shouldn't get here, but if we do, don't die... var lt = e.Current.Value; if (lt.GroupChannel == null) { continue; } writer.WriteStartElement(LEVELTRIGGER_ROOT); writer.WriteAttributeString(LT_GROUPCHANNELID, lt.GroupChannel.Id.ToString(CultureInfo.InvariantCulture)); writer.WriteAttributeString(LT_HARDWARECHANNELID, lt.HardwareChannelId); writer.WriteAttributeString(LT_SENSORSERIALNUMBER, lt.SensorSerialNumber); writer.WriteAttributeString(LT_GREATERTHANENABLED, lt.GreaterThanEnabled.ToString(CultureInfo.InvariantCulture)); writer.WriteAttributeString(LT_GREATERTHANTHRESHOLDEU, lt.GreaterThanThresholdEU.ToString(CultureInfo.InvariantCulture)); writer.WriteAttributeString(LT_LESSTHANENABLED, lt.LessThanEnabled.ToString(CultureInfo.InvariantCulture)); writer.WriteAttributeString(LT_LESSTHANTHRESHOLDEU, lt.LessThanThresholdEU.ToString(CultureInfo.InvariantCulture)); writer.WriteAttributeString(LT_TRIGGERINSIDE, lt.TriggerBetweenBounds.ToString(CultureInfo.InvariantCulture)); writer.WriteAttributeString(LT_TRIGGEROUTSIDE, lt.TriggerOutsideBounds.ToString(CultureInfo.InvariantCulture)); writer.WriteAttributeString(LT_INSIDELOWEREU, lt.InsideLowerLevelEU.ToString(CultureInfo.InvariantCulture)); writer.WriteAttributeString(LT_INSIDEUPPEREU, lt.InsideUpperLevelEU.ToString(CultureInfo.InvariantCulture)); writer.WriteAttributeString(LT_OUTSIDELOWEREU, lt.OutsideLowerLevelEU.ToString(CultureInfo.InvariantCulture)); writer.WriteAttributeString(LT_OUTSIDEUPPEREU, lt.OutsideUpperLevelEU.ToString(CultureInfo.InvariantCulture)); writer.WriteEndElement(); //LEVELTRIGGER_ROOT } } writer.WriteEndElement();//LEVELTRIGGERS_ROOT } #endregion LevelTriggers #region MetaDatas writer.WriteStartElement(METADATAS_ROOT); var testMeta = GetTestMetaData(); if (!string.IsNullOrWhiteSpace(testMeta.TestObject.ToString())) { foreach (var prop in testMeta.Properties) { writer.WriteStartElement(METADATA_ROOT); foreach (var f in f3) { switch (f) { case DbOperations.TestSetups.TestObjectMetaDataFields.Optional: writer.WriteAttributeString(f.ToString(), prop.IsOptional.ToString()); break; case DbOperations.TestSetups.TestObjectMetaDataFields.PropName: writer.WriteAttributeString(f.ToString(), prop.Name); break; case DbOperations.TestSetups.TestObjectMetaDataFields.PropValue: writer.WriteAttributeString(f.ToString(), prop.Value); break; //FB 41857 check for null Name to prevent crash case DbOperations.TestSetups.TestObjectMetaDataFields.SetupName: writer.WriteAttributeString(f.ToString(), Name?.Trim()); break; case DbOperations.TestSetups.TestObjectMetaDataFields.TestObject: writer.WriteAttributeString(f.ToString(), testMeta.TestObject.ToString()); break; case DbOperations.TestSetups.TestObjectMetaDataFields.Version: writer.WriteAttributeString(f.ToString(), prop.Version.ToString(CultureInfo.InvariantCulture)); break; case DbOperations.TestSetups.TestObjectMetaDataFields.ISOTestObject: break;//ignore this field, per AF is covered by testobject field case DbOperations.TestSetups.TestObjectMetaDataFields.TestSetupName: break; default: throw new NotSupportedException("TestTemplate::WriteXML unsupported field: " + f); } } writer.WriteEndElement(); } } var toMetaDatas = GetMetaData(); foreach (var toMetaData in toMetaDatas) { // FB 17951 Prevent empty TestObject entry if (string.IsNullOrWhiteSpace(toMetaData.TestObject.ToString())) { continue; } foreach (var prop in toMetaData.Properties) { writer.WriteStartElement(METADATA_ROOT); foreach (var f in f3) { switch (f) { case DbOperations.TestSetups.TestObjectMetaDataFields.Optional: writer.WriteAttributeString(f.ToString(), prop.IsOptional.ToString()); break; case DbOperations.TestSetups.TestObjectMetaDataFields.PropName: writer.WriteAttributeString(f.ToString(), prop.Name); break; case DbOperations.TestSetups.TestObjectMetaDataFields.PropValue: writer.WriteAttributeString(f.ToString(), prop.Value); break; case DbOperations.TestSetups.TestObjectMetaDataFields.SetupName: writer.WriteAttributeString(f.ToString(), Name); break; case DbOperations.TestSetups.TestObjectMetaDataFields.TestObject: writer.WriteAttributeString(f.ToString(), toMetaData.TestObject.ToString()); break; case DbOperations.TestSetups.TestObjectMetaDataFields.Version: writer.WriteAttributeString(f.ToString(), prop.Version.ToString(CultureInfo.InvariantCulture)); break; case DbOperations.TestSetups.TestObjectMetaDataFields.TestSetupName: break; case DbOperations.TestSetups.TestObjectMetaDataFields.ISOTestObject: break; default: throw new NotSupportedException("TestTemplate::WriteXML unsupported field: " + f); } } writer.WriteEndElement(); } } writer.WriteEndElement(); #endregion MetaDatas WriteTSRAirSettings(ref writer); writer.WriteEndElement();//testsetup } #endregion XML private string GetGroupChannelHardware(int dasId, int dasChannelIndex, DASHardware[] hardwareList) { var matches = from hw in hardwareList where hw.DASId == dasId select hw; if (!matches.Any()) { return string.Empty; } var match = matches.First(); if (dasChannelIndex < 0 || dasChannelIndex >= match.Channels.Length) { return string.Empty; } return match.Channels[dasChannelIndex]?.ToString(hardwareList); } private string GetGroupChannelHardwareId(int dasId, int dasChannelIndex, DASHardware[] hardwareList) { var matches = from hw in hardwareList where hw.DASId == dasId select hw; if (!matches.Any()) { return string.Empty; } var match = matches.First(); if (dasChannelIndex < 0 || dasChannelIndex >= match.Channels.Length) { return string.Empty; } return match.Channels[dasChannelIndex]?.GetId(); } public void ReloadGroupsMemoryOnly() { ReloadGroups(true); } /// /// reloads static groups from list /// public void ReloadGroups(bool bMemoryOnly) { //if the groups aren't loaded yet, well we don't need to reload them because they'll automatically //be loaded before we need them anyhow if (!IsLoaded) { return; } var addedGroups = new List(); var staticGroups = new List(); var allGroups = new List(); allGroups.AddRange(TestObjects); allGroups.AddRange(AddedGroups); SetTestObjects(bMemoryOnly, staticGroups.ToArray()); _addedGroups = new List(addedGroups.ToArray()); } public bool CheckForIEPE() { var channels = GetChannels(); foreach (var ch in channels) { if (ch.IsBlank()) { continue; } if (ch.IsDisabled) { continue; } if (ch.IsDigitalIn || ch.IsDigitalOut || ch.IsSquib) { continue; } if (ch.SensorValid) { var sd = GetSensor(ch); if (null != sd) { if (sd.Bridge == SensorConstants.BridgeType.IEPE) { return true; } } } } return false; } /// /// returns whether there is a TOM in the test setup or not /// /// public bool CheckForTOM() { var hardware = GetHardware(); foreach (var h in hardware) { if (h.IsTOM()) { return true; } } return false; } /// /// returns whether there is a TOM with channels in use in the test setup or not /// /// public bool CheckForTOMInTest() { var hardware = GetHardware(); foreach (var h in hardware) { switch (h.GetHardwareTypeEnum()) { case HardwareTypes.SLICE2_SLT: case HardwareTypes.SLICE2_TOM: case HardwareTypes.TDAS_Pro_Rack: case HardwareTypes.TDAS_LabRack: if (h.Channels.Any(c => (c.IsSupportedBridgeType(SensorConstants.BridgeType.SQUIB) || c.IsSupportedBridgeType(SensorConstants.BridgeType.TOMDigital)) && (c.Sensor != null))) { return true; } break; } } return false; } /// /// returns true if there are any analog channels on the hardware /// /// object to check /// true if contains an analog channel, false otherwise private bool CheckForAnalog(DASHardware h) { if (!h.IsNonDistributorHardware) { return false; } switch (h.GetHardwareTypeEnum()) { case HardwareTypes.G5INDUMMY: //case HardwareTypes.G5IPORT: case HardwareTypes.G5VDS: case HardwareTypes.SLICE1_G5Stack: case HardwareTypes.Ribeye: case HardwareTypes.RibeyeLED: //do contain an analog, return true right away return true; case HardwareTypes.SLICE2_DIM: case HardwareTypes.SLICE2_SLD: case HardwareTypes.SLICE2_TOM: case HardwareTypes.SLICE2_SLT: break;//don't contain analog, keep going and look for analog default: //not sure, have to check channels... if (Array.Exists(h.Channels, c => c.IsSupportedBridgeType(SensorConstants.BridgeType.FullBridge) || c.IsSupportedBridgeType(SensorConstants.BridgeType.IEPE))) { return true; } break; } return false; } /// /// returns whether there is an analog channel in the test or not /// /// public bool CheckForAnalog() { foreach (var group in Groups) { if (ChannelsForGroup.ContainsKey(group)) { var channels = ChannelsForGroup[group]; foreach (var ch in channels) { if (ch.IsBlank() || ch.IsDisabled || !ch.SensorValid) { continue; } var sensor = GetSensor(ch); if (null != sensor) { switch (sensor.Bridge) { case SensorConstants.BridgeType.SQUIB: case SensorConstants.BridgeType.TOMDigital: case SensorConstants.BridgeType.DigitalInput: continue; case SensorConstants.BridgeType.IEPE: case SensorConstants.BridgeType.QuarterBridge: case SensorConstants.BridgeType.HalfBridge: case SensorConstants.BridgeType.FullBridge: case SensorConstants.BridgeType.HalfBridge_SigPlus: case SensorConstants.BridgeType.RTC: return true; } } } } } return false; } /// /// 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) { Dirty = 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) { base.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); } public double GetSampleRateForEmbeddedHardware(DASHardware h, DFConstantsAndEnums.ModuleType type) { switch (type) { case DFConstantsAndEnums.ModuleType.EmbeddedAngularAccel: case DFConstantsAndEnums.ModuleType.EmbeddedAngularRate: return AngularRate; case DFConstantsAndEnums.ModuleType.EmbeddedLinearAccelLowG: return LowgLinearAccRate; case DFConstantsAndEnums.ModuleType.EmbeddedLinearAccelHighG: return HighgLinearAccRate; case DFConstantsAndEnums.ModuleType.EmbeddedAtmospheric: return TemperatureHumidityPressureRate; case DFConstantsAndEnums.ModuleType.EmbeddedClockSecondsAndMarker: case DFConstantsAndEnums.ModuleType.EmbeddedClockNanosAndPad: return 0; default: return GetSampleRateForHardware(h); } } public double GetSampleRateForHardware(DASHardware h) { if (_dasSettings.ContainsKey(h.SerialNumber)) { return _dasSettings[h.SerialNumber].SampleRate; } if (!DASSampleRateList.ContainsKey(h.SerialNumber)) { DASSampleRateList[h.SerialNumber] = SamplesPerSecondAggregate; } return DASSampleRateList[h.SerialNumber]; } public double GetSampleRateForHardware(string h) { return !_dasSettings.ContainsKey(h) ? DASSampleRateList[h] : _dasSettings[h].SampleRate; } public void SetSampleRateForHardware(DASHardware h, double d) { if (!_dasSettings.ContainsKey(h.SerialNumber)) { _dasSettings[h.SerialNumber] = CreateDASSettings(h); } _dasSettings[h.SerialNumber].SampleRate = d; } public float GetAAFForHardware(IDASCommunication das) { return (float)(!_dasSettings.ContainsKey(das.SerialNumber) ? DASAAFRateList[das.SerialNumber] : _dasSettings[das.SerialNumber].HardwareAAF); } public float GetAAFForHardware(IDASCommunication das, int sps) { switch (das.GetHardwareType()) { case HardwareTypes.DIM: case HardwareTypes.G5INDUMMY: case HardwareTypes.G5VDS: case HardwareTypes.SIM: case HardwareTypes.TDAS_Pro_Rack: case HardwareTypes.TDAS_LabRack: case HardwareTypes.TOM: return GetAAFForHardware(SerializableAAF.DAS_TYPE.TDAS, sps); default: return GetAAFForHardware(SerializableAAF.DAS_TYPE.SLICE, sps); } } /// /// returns the Anti-Alias Filter (AAF) for the DAS in question /// this is dependent on the sample rate for the hardware and a ratio in the config file /// (RealtimeSampleRateAAFilterRatio) /// /// /// public float GetRealtimeAAFForHardware(IDASCommunication idas, double samplerate) { if (DataModelSettings.SLICETurnOffAAFRealtime && DFConstantsAndEnums.SupportsTurnOffAAFRealtime(idas)) { return Constants.SLICE2_NO_AAF_REALTIME_RATE; } //for now there is no additional work done if (0 == DataModelSettings.RealtimeSampleRateAAFilterRatio) { return Convert.ToSingle(samplerate); } return (float)samplerate / DataModelSettings.RealtimeSampleRateAAFilterRatio; } public float GetAAFForHardware(DASHardware h) { return (float)(!_dasSettings.ContainsKey(h.SerialNumber) ? DASAAFRateList[h.SerialNumber] : _dasSettings[h.SerialNumber].HardwareAAF); } /// /// returns the anti alias filter for a given simplehardware and sample rate /// private static float GetAAFForHardware(SimpleHardware simpleH, int sps) { var dasType = (HardwareTypes)simpleH.DASType; return GetAAFForHardware(dasType, sps); } /// /// returns the hardware rate given a hardware type and a sample rate /// public static float GetAAFForHardware(HardwareTypes hwType, int sps) { switch (hwType) { case HardwareTypes.DIM: case HardwareTypes.G5INDUMMY: case HardwareTypes.G5VDS: case HardwareTypes.SIM: case HardwareTypes.TDAS_Pro_Rack: case HardwareTypes.TDAS_LabRack: case HardwareTypes.TOM: return GetAAFForHardware(SerializableAAF.DAS_TYPE.TDAS, sps); default: return GetAAFForHardware(SerializableAAF.DAS_TYPE.SLICE, sps); } } /// /// returns a hardware anti alias filter rate given a DASHardware and a sample rate /// public static float GetAAFForHardware(DASHardware h, int sps) { return GetAAFForHardware(h.DASTypeEnum, sps); } public static float GetAAFForHardware(SerializableAAF.DAS_TYPE dasType, int sps) { return Convert.ToSingle(SerializedSettings.GetAAFException(dasType, sps)); } public void SetAAFRateForHardware(DASHardware h, double d) { if (!_dasSettings.ContainsKey(h.SerialNumber)) { _dasSettings[h.SerialNumber] = CreateDASSettings(h); } _dasSettings[h.SerialNumber].HardwareAAF = d; } public static int GetSamplesPerPacketForHardware(int sps) { return Convert.ToInt32(SerializedSettings.GetSamplesPerPacket(sps)); } private DASSettings CreateDASSettings(DASHardware h) { //just fill with all defaults for now, in the future we will probably want better filled in values, //but for now I'm only implementing the samplerate portion of the problem. return new DASSettings { DASSerialNumber = h.SerialNumber, BatteryCheck = true, BatteryVoltageMax = -1, BatteryVoltageMin = -1, ExcitationWarmupTimeMS = -1, HardwareAAF = -1, InputVoltageMax = -1, InputVoltageMin = -1, PostTriggerSeconds = PostTriggerSeconds, PreTriggerSeconds = PreTriggerSeconds, SampleRate = -1, StatusLineCheck = true }; } public void SetDefaultSetupFile() { SetupFile = Defaults.GetUserSettingValueString(ApplicationProperties.CurrentUser.Id, PropertyEnums.PropertyIds.LastRunTestSetup); } /// /// calculates whether the test setup is complete or not /// will cause the test setup to be loaded if it is not yet /// does not unload the test setup /// public void CalculateIsComplete(bool bSetInDb = true) { var eventAggregator = ContainerLocator.Container.Resolve(); eventAggregator.GetEvent().Publish(new ProgressBarEventArg() { SetText = true, ProgressBarText = $"{StringResources.Export_StatusValidating} {StringResources.Test} {Name}" }); bool bNeedToUnload = false; if (!IsLoaded) { bNeedToUnload = true; Load(); } var errorMessage = string.Empty; var errors = new List(); var warnings = new List(); var strictness = QuickSensorCheck ? StrictLevel.CheckoutOnly : StrictLevel.UpdateTable; var res = Validate(ref errors, ref warnings, false, this, true, strictness, true, true, false); if (errors.Count > MAX_ERRORS_TO_DISPLAY) { errorMessage = $"{string.Join(Environment.NewLine, errors.ToArray(), 0, MAX_ERRORS_TO_DISPLAY)}{Environment.NewLine}{string.Format(StringResources.TestTemplate_MoreErrors, (errors.Count - MAX_ERRORS_TO_DISPLAY))}"; } else if (errors.Count > 0) { errorMessage = string.Join(Environment.NewLine, errors.ToArray()); } if (!res) { IsComplete = false; } //FB14010: some warnings are OK for saving, but not for running tests var hasIncompleteWarnings = warnings.Exists(warning => Array.Exists(_incompleteWarnings, incomplete => Array.TrueForAll(Regex.Split(incomplete, "{\\d+}"), inc => warning.Contains(inc)))); IsComplete = errors.Count <= 0 && !hasIncompleteWarnings; ErrorMessage = errorMessage; Dirty = false; if (bSetInDb) { _ = DbOperations.TestSetupsMarkIsCompleteIsDirty(Name, Dirty, base.IsComplete, ErrorMessage); } if (bNeedToUnload) { UnLoad(); } } public void CheckSensorCalDates() { var sensorCalPolicy = CalibrationPolicy.GetCalibrationPolicy(); if (sensorCalPolicy.SelectedCalPolicy != SensorConstants.SensorCalPolicy.DONT_ALLOW) { //well if the problem before was an out of date sensor, reset it if (CompletionErrorMessage.Contains(StringResources.OverdueSensors_CalibrationDateOverdueOrNear)) { SetIsDirty(true); } } else { var bWeLoadedIt = false; if (!IsLoaded) { Load(); bWeLoadedIt = true; } var bHaveAnOutofDateSensor = false; var groups = new List(TestObjects); groups.AddRange(AddedGroups); foreach (var g in groups) { var isoTo = g.GetISOTestObject(); if ((from ch in isoTo.AllChannels where ch.Required where !string.IsNullOrWhiteSpace(ch.SensorSerialNumber) select GetSensor(ch.SensorSerialNumber, g.SerialNumber, ch.Name) into s where null != s where !s.IsDigitalInput() && !s.IsDigitalOutput() && !s.IsSquib() let sc = SensorCalibrationList.GetLatestCalibrationBySerialNumber(s) where null == sc || s.GetDueDate(sc) < DateTime.Now.Date select s).Any()) { bHaveAnOutofDateSensor = true; } if (bHaveAnOutofDateSensor) { break; } } if (bHaveAnOutofDateSensor && sensorCalPolicy.SelectedCalPolicy == SensorConstants.SensorCalPolicy.DONT_ALLOW) { SetIsDirty(true); } if (bWeLoadedIt) { UnLoad(); } } } /// /// marks a test setup as unchecked by setting the dirty flag, which means the next time completion is checked it will be calculated again /// this function will change that flag in the database immediately /// public void MarkIsCompleteUnchecked(bool skipMemoryCheck = false) { IsComplete = false; Dirty = true; ErrorMessage = string.Empty; if (string.IsNullOrEmpty(Name)) { return; } _ = DbOperations.TestSetupsMarkIsCompleteIsDirty(Name, true, false, ErrorMessage); } public void ClearOverrides() { HardwareOverrides?.Clear(); } /// /// frees up the memory assigned during Load, this includes channels, groups, meta data, etc /// various things may cause us to load the information and hold the information for some setups, /// but in general we want to unload it if we don't need it /// public void UnLoad() { _bIsLoaded = false; _testObjects?.Clear(); _addedGroups?.Clear(); TestObjectsAndAddedGroupsList?.Clear(); _channelKeysInOrder?.Clear(); lock (ChannelLookupLock) { _channelLookup?.Clear(); } _checkedDASList?.Clear(); _dasChannelNumberToAbsoluteNumber?.Clear(); _dasLookup?.Clear(); _dasSettings?.Clear(); _filterLookup?.Clear(); _hardwareChannelsInOrder?.Clear(); _metaDataLookup?.Clear(); lock (SensorLock) { SensorLookup?.Clear(); } _sessionOpen?.Clear(); _test = null; _testGraphs?.Clear(); _testSetupMeta?.Clear(); HardwareOverrides?.Clear(); LevelTriggerChannels?.Clear(); } /// /// signifies if the test setup is being used for a quick checkout /// quick checkout tests are not stored and are run time only /// public bool IsQuickCheckout { get; set; } public void Load(bool fromDB = false) { //13443 Quick checkout tile not functional //don't load quick check tests, they aren't stored ... if (IsQuickCheckout) { return; } Groups.Clear(); ChannelsForGroup.Clear(); LoadFromDb(); //Check for the possibility of a Test Setup that was imported from an earlier release that //didn't yet have the concept of multiple ROIs var settings = TestSetupDefaults.GetUserSettings(ApplicationProperties.CurrentUser.Id); if ((RegionsOfInterest.Count() == 1) && (RegionsOfInterest[0].Suffix == "") && (RegionsOfInterest[0].Start == settings.DefaultROIStart) && (RegionsOfInterest[0].End == settings.DefaultROIEnd) && (RegionsOfInterest[0].IsDefault == true) && (RegionsOfInterest[0].IsEnabled) //The above looks like a NULL RegionsOfInterest field was found in the Test Setups table, but let's make sure && ((RegionsOfInterest[0].Start != ROIStart) || (RegionsOfInterest[0].End != ROIEnd)) ) { RegionsOfInterest = new BindingList { new RegionOfInterest("", false, ROIStart, ROIEnd) }; } } public IISOHardware[] GetAllCachedHardware() { return new IISOHardware[0]; } /// /// this function removes specific hardware from the cache /// this is a 3 step process, /// 1) remove from _hardwareFromBinary, which is a list of hardware from the TestSetupXML /// 2) update the TestSetupXML to also remove the hardware in question /// 3) update the Hardware for any testobjects to the new hardware and away from whatever they had /// /// this is being done for /// 12496 Discover Hardware in test setup should refresh or "update items" automatically /// where the incoming hardwareToRemove may be updated configurations /// /// public void RemoveCachedHardware(DASHardware[] hardwareToRemove) { var dictionary = hardwareToRemove.ToDictionary(h => h.SerialNumber); //since groups have their own pointers to hardware //we have to make sure they aren't point to old hardware records //that way if someone accesses the hardware via the group, they have an updated copy foreach (var group in TestObjects) { var list = group.Hardware.ToList(); for (var i = list.Count - 1; i >= 0; i--) { if (dictionary.ContainsKey(list[i].SerialNumber)) { list[i] = dictionary[list[i].SerialNumber]; } } group.SetHardware(list.ToArray()); } foreach (var group in AddedGroups) { var list = group.Hardware.ToList(); for (var i = list.Count - 1; i >= 0; i--) { if (dictionary.ContainsKey(list[i].SerialNumber)) { list[i] = dictionary[list[i].SerialNumber]; } } group.SetHardware(list.ToArray()); } } public DASHardware GetCachedHardware(string serialNumber) { return null; } /// /// All items from db are updated in our cache /// public void UpdateFromDb() { ClearHardware(); DASHardwareList.GetList().ClearCache(); DASHardwareList.GetList().ReloadAll(); SensorCalibrationList.ClearCachedCalibrations(); SensorCalibrationList.Reload(); SensorsCollection.SensorsList.ClearCache(); SensorsCollection.SensorsList.Reload(); foreach (var group in Groups) { group.ClearGroupChannelSettingCache(group.Id); } TestTemplateList.TestTemplatesList.CachedCustomerDetails = null; TestTemplateList.TestTemplatesList.CachedLaboratoryDetails = null; TestTemplateList.TestTemplatesList.CachedTestEngineerDetails = null; Groups.Clear(); ChannelsForGroup.Clear(); LoadFromDb(); } public IDASHardware[] GetHardwareFromDb() { var includedIds = AddedHardware; var list = new List(); var lookup = DASHardwareList.GetAllHardware().ToDictionary(d => d.DASId); foreach (var id in includedIds) { if (lookup.ContainsKey(id)) { list.Add(lookup[id]); } } foreach (var id in RemovedHardware) { if (lookup.ContainsKey(id)) { list.Add(lookup[id]); } } return list.ToArray(); } public IDASHardware[] GetAllCachedHardware2() { return new IDASHardware[0]; } public ISensorCalibration[] GetCachedSensorCalibrations() { return new ISensorCalibration[0]; } public SensorCalibration[] GetCachedSensorCalibrations2() { return new SensorCalibration[0]; } public ISensorData[] GetCachedSensors() { return new ISensorData[0]; } //FB 18875 Populate Cached Sensors public SensorData[] GetCachedSensors2() { return GetAllSensorsFromDB(); } public SensorData[] GetOriginalCachedSensors() { return new SensorData[0]; } public SensorData GetCachedSensor(string serialNumber) { return null; } public SensorCalibration[] GetCalibrations(string serialNumber) { return null; } //FB 18875 Get all sesnors from database which can be used in cache /// /// Get all sensors from database /// /// SensorData list public SensorData[] GetAllSensorsFromDB() { var sensors = new List(); var cals = SensorsCollection.GetLatestCalibrations(); var hr = DbOperations.SensorsAnalogGet(null, null, null, out var records); if (0 == hr && null != records && records.Any()) { foreach (var record in records) { var sd = new SensorData(record, cals); sensors.Add(sd); } } hr = DbOperations.SensorsDigitalInGet(null, null, null, out var digitalIns); if (0 == hr && null != digitalIns && digitalIns.Any()) { foreach (var digitalIn in digitalIns) { sensors.Add(new DigitalInputSetting(digitalIn)); } } hr = DbOperations.SensorsSquibGet(null, null, null, out var squibrecords); if (0 == hr && null != squibrecords && squibrecords.Any()) { foreach (var record in squibrecords) { var squib = new SquibSetting(record); sensors.Add(squib); } } hr = DbOperations.SensorsUARTGet(null, null, out var uartRecords); if (0 == hr && null != uartRecords && uartRecords.Any()) { foreach (var uartRecord in uartRecords) { var uart = new UartSetting(uartRecord); sensors.Add(uart); } } hr = DbOperations.SensorsStreamOutputGet(null, null, out var outputRecords); if (0 == hr && null != outputRecords && outputRecords.Any()) { foreach (var record in outputRecords) { sensors.Add(new StreamOutputSetting(record)); } } return sensors.ToArray(); } //added to prevent a bad binding expecting this property to be present public System.Windows.Media.Brush Background => System.Windows.Media.Brushes.Transparent; public void InitializeFrom(TestTemplate source) { OldTestTemplateLiteCopy(source); AddedGroups = source.AddedGroups; AllowMissingSensors = source.AllowMissingSensors; AllowSensorIdToBlankChannel = source.AllowSensorIdToBlankChannel; ArmCheckListStep = source.ArmCheckListStep; AutomaticProgression = source.AutomaticProgression; AutomaticProgressionDelayMS = source.AutomaticProgressionDelayMS; AutoVerifyChannels = source.AutoVerifyChannels; AutoVerifyDelaySeconds = source.AutoVerifyDelaySeconds; CalibrationBehavior = source.CalibrationBehavior; CheckedDASList = source.CheckedDASList; CheckListBatteryVoltageCheck = source.CheckListBatteryVoltageCheck; CheckListInputVoltageCheck = source.CheckListInputVoltageCheck; CheckListRequirePass = source.CheckListRequirePass; CheckListSensorIdCheck = source.CheckListSensorIdCheck; CheckListSquibResistanceCheck = source.CheckListSquibResistanceCheck; CheckListTemperatureCheck = source.CheckListTemperatureCheck; CheckListTiltSensorCheck = source.CheckListTiltSensorCheck; CheckListTriggerStartCheck = source.CheckListTriggerStartCheck; CheckoutMode = source.CheckoutMode; CommonLine = source.CommonLine; CustomerDetails = source.CustomerDetails; DASSettings = source.DASSettings; DefaultNumberRealtimeGraphs = source.DefaultNumberRealtimeGraphs; Description = source.Description; DoAutoArm = source.DoAutoArm; DoEnableRepeat = source.DoEnableRepeat; DoStreaming = source.DoStreaming; DoROIDownload = source.DoROIDownload; DownloadAll = source.DownloadAll; DownloadFolder = source.DownloadFolder; ExcitationWarmup = source.ExcitationWarmup; ExportFolder = source.ExportFolder; ExportFormats = source.ExportFormats; ExpressTestSetup = source.ExpressTestSetup; FilterLookup = source.FilterLookup; GroupsStepValid = source.GroupsStepValid; InvertStartRecordCompletion = source.InvertStartRecordCompletion; InvertTriggerCompletion = source.InvertTriggerCompletion; IgnoreShortedStartCompletion = source.IgnoreShortedStartCompletion; IgnoreShortedTriggerCompletion = source.IgnoreShortedTriggerCompletion; base.IsComplete = source.GetIsCompleteNoFunctions(); IsDirty = source.IsDirty; ISFFile = source.ISFFile; LabDetails = source.LabDetails; LastModified = source.LastModified; LastModifiedBy = source.LastModifiedBy; LevelTriggerChannels = source.LevelTriggerChannels; LocalOnly = source.LocalOnly; Name = source.Name; NotAllChannelsRealTime = source.NotAllChannelsRealTime; NotAllChannelsViewer = source.NotAllChannelsViewer; OriginalTestDirectory = source.OriginalTestDirectory; OtherAddedGroups = source.OtherAddedGroups; PostTestDiagnosticsLevel = source.PostTestDiagnosticsLevel; PostTriggerSeconds = source.PostTriggerSeconds; PreserveTestId = source.PreserveTestId; PreTriggerSeconds = source.PreTriggerSeconds; QuickSensorCheck = source.QuickSensorCheck; QuitTestWithoutWarning = source.QuitTestWithoutWarning; RecordingMode = source.RecordingMode; NumberOfEvents = source.NumberOfEvents; WakeUpMotionTimeout = source.WakeUpMotionTimeout; RequireUserConfirmationOnErrors = source.RequireUserConfirmationOnErrors; ROIEnd = source.ROIEnd; ROIStart = source.ROIStart; SameAsDownloadFolder = source.SameAsDownloadFolder; SamplesPerSecondAggregate = source.SamplesPerSecondAggregate; SetupFile = source.SetupFile; StrictDiagnostics = source.StrictDiagnostics; SuppressMissingSensorsWarning = source.SuppressMissingSensorsWarning; TagIDs = source.TagIDs; TestDirectory = source.TestDirectory; TestEngineerDetails = source.TestEngineerDetails; TestGraphs = source.TestGraphs.Select(tg => new TestGraph(tg)).ToArray(); //use same method as new(copy) _id = source._id; TestIdNode = source.TestIdNode; _testObjects = source._testObjects; TestObjectsAndAddedGroupsList = source.TestObjectsAndAddedGroupsList; _groups = source._groups; Groups = source.Groups; ChannelsForGroup = source.ChannelsForGroup; _metaDataLookup?.Clear(); SetMetaData(source._metaDataLookup?.Values?.ToArray()); _testSetupMeta?.Clear(); SetTestSetupMetaData(source._testSetupMeta); TestTime = source.TestTime; TriggerCheckRealtime = source.TriggerCheckRealtime; TriggerCheckStep = source.TriggerCheckStep; TurnOffExcitation = source.TurnOffExcitation; UploadData = source.UploadData; UploadFolder = source.UploadFolder; VerifyChannels = source.VerifyChannels; ViewDiagnostics = source.ViewDiagnostics; ViewDownloadAll = source.ViewDownloadAll; ViewExport = source.ViewExport; _viewRealtime = source._viewRealtime; ViewROIDownload = source.ViewROIDownload; WarnOnFailedBattery = source.WarnOnFailedBattery; ClockSyncProfileMaster = source.ClockSyncProfileMaster; ClockSyncProfileSlave = source.ClockSyncProfileSlave; CalculatedChannels.Clear(); CalculatedChannels.AddRange(source.CalculatedChannels); MeasureSquibResistancesStep = source.MeasureSquibResistancesStep; HardwareOverrides.Clear(); using (var enumHardware = source.HardwareOverrides.GetEnumerator()) { while (enumHardware.MoveNext()) { HardwareOverrides[enumHardware.Current.Key] = new HardwareInclusionInstruction(enumHardware.Current.Value); } } } /// /// loads all the information for the test setup from tables in the db /// we do this so that we can keep the minimal information on hand until we need it /// public void LoadFromDb() { if (QuickSensorCheck) { return; } if (string.IsNullOrWhiteSpace(Name)) { return; } //nothing to load! var tblMetaData = new Dictionary>>(100); #region Load Groups var groupIds = new List(); var unityContainer = ContainerLocator.Container.Resolve(); //IGroupListViewModel vm = null; if (null == _vm) { if (!Application.Current.Dispatcher.CheckAccess()) { ManualResetEvent manualResetEvent = new ManualResetEvent(false); Application.Current.Dispatcher.BeginInvoke(new Action(() => { _vm = unityContainer.Resolve(); manualResetEvent.Set(); })); manualResetEvent.WaitOne(); } else { _vm = unityContainer.Resolve(); } } var groupIdToDisplayOrder = new Dictionary(); var groupIdToPosition = new Dictionary(); var groupIdToTestObject = new Dictionary(); var hr = DbOperations.TestSetupGroupsGet(null, null, Name, out var records); if (0 == hr && null != records && records.Any()) { foreach (var record in records) { groupIdToDisplayOrder[record.GroupId] = record.DisplayOrder; groupIdToPosition[record.GroupId] = record.Position; groupIdToTestObject[record.GroupId] = record.TestObjectType; groupIds.Add(record.GroupId); } } var sensorLookup = new Dictionary(); var sensors = SensorsCollection.SensorsList.GetAllSensors(false); foreach (var s in sensors) { sensorLookup[s.DatabaseId] = s; } var hardwareLookup = new Dictionary(); var hardware = DASHardwareList.GetAllHardware(); foreach (var h in hardware) { hardwareLookup[h.DASId] = h; } //Update the Tags in case they were modified by a different user DTS.Common.Classes.Tags.TagsInstance.GetTagsInstance(DbOperations.TagsGet).UpdateList(DbOperations.TagsGet); var groups = new List(); foreach (var id in groupIds) { //We don't need to update the Tags since it was just done above var group = _vm.GetGroup(id, false); group.DisplayOrder = groupIdToDisplayOrder[id]; group.LoadHardware(); groups.Add(group); } groups.Sort(); var channelDefaults = DbOperations.GetChannelSettingDefaults(); foreach (var group in groups) { AddGroup(group, sensorLookup, hardwareLookup, channelDefaults); } //Get the TestSetupHardware table //FB 23926 Refactor var testSetupHardwareGetWrapper = new TestSetupHardwareGet(); var testSetupHardwareGetList = testSetupHardwareGetWrapper.TestSetupHardwareGet_Wrapper(Id, hardwareLookup); var added = new List(); var removed = new List(); foreach (var testSetupHardwareGet in testSetupHardwareGetList) { DASSampleRateList[hardwareLookup[testSetupHardwareGet.DASId].SerialNumber] = testSetupHardwareGet.SamplesPerSecond; DASClockMasterList[hardwareLookup[testSetupHardwareGet.DASId].SerialNumber] = testSetupHardwareGet.IsClockMaster && testSetupHardwareGet.AddOrRemove; DASAAFRateList[hardwareLookup[testSetupHardwareGet.DASId].SerialNumber] = testSetupHardwareGet.AntiAliasFilterRate; DASSamplesPerPacketList[hardwareLookup[testSetupHardwareGet.DASId].SerialNumber] = GetSamplesPerPacketForHardware(testSetupHardwareGet.SamplesPerSecond); DASPTPDomainIDList[hardwareLookup[testSetupHardwareGet.DASId].SerialNumber] = testSetupHardwareGet.PTPDomainId; if (testSetupHardwareGet.AddOrRemove) { added.Add(testSetupHardwareGet.DASId); } else { removed.Add(testSetupHardwareGet.DASId); } } AddedHardware = added.ToArray(); RemovedHardware = removed.ToArray(); var defaultRate = TestSetupDefaults.GetUserSettings(ApplicationProperties.CurrentUser.Id).DefaultSampleRate; //Add the default sample rate for any DAS that's not included in the test in case it gets included the test later foreach (var h in hardware) { if (!DASSampleRateList.ContainsKey(h.SerialNumber)) { DASSampleRateList[h.SerialNumber] = defaultRate; } } #endregion Load Groups #region Load Meta Data try { using (var cmd = DbOperations.GetSQLCommand(true)) { try { cmd.CommandType = CommandType.StoredProcedure; cmd.CommandText = DbOperationsEnum.StoredProcedure.sp_TestSetupObjectMetaDataGet.ToString(); cmd.Parameters.Add(new SqlParameter("@TestSetupName", SqlDbType.NVarChar, 50) { Value = Name }); IDataReader reader = cmd.ExecuteReader(); TestTemplateList.ConvertToDictionary(reader, ref tblMetaData, DbOperations.TestSetups.TestObjectMetaDataFields.TestSetupName.ToString()); } finally { cmd.Connection.Dispose(); } } } catch (Exception ex) { APILogger.Log(ex); } #endregion Meta Data #region Load Level Triggers LevelTriggerChannels.Clear(); var levelTriggerChannels = new List(); try { using (var cmd = DbOperations.GetSQLCommand(true)) { try { cmd.CommandType = CommandType.StoredProcedure; cmd.CommandText = DbOperationsEnum.StoredProcedure.sp_LevelTriggersGet.ToString(); cmd.Parameters.Add( new SqlParameter("@Id", SqlDbType.Int) { Value = DBNull.Value }); cmd.Parameters.Add( new SqlParameter("@ChannelId", SqlDbType.Int) { Value = DBNull.Value }); cmd.Parameters.Add( new SqlParameter("@TestSetupId", SqlDbType.Int) { Value = DBNull.Value }); cmd.Parameters.Add(new SqlParameter("@TestSetupName", SqlDbType.NVarChar) { Value = Name }); using (var ds = DbOperations.Connection.QueryDataSet(cmd)) { foreach (DataRow row in ds.Tables[0].Rows) { try { levelTriggerChannels.Add(new LevelTriggerChannel(row)); } catch (Exception ex2) { APILogger.Log(ex2); } } } } finally { cmd.Connection.Dispose(); } } } catch (Exception ex) { APILogger.Log(ex); } #endregion ProcessMetaData(tblMetaData); _bIsLoaded = true; #region Process Level Triggers foreach (var lt in levelTriggerChannels) { SetLevelTrigger(lt); } #endregion Process Level Triggers #region Graphs LoadGraphsFromDb(); #endregion Graphs #region CC LoadCalculatedChannelsFromDb(); #endregion CC #region ROI // If we're using a db with the (new) TestSetupROIs and ROIPeriodChannels tables, we need // to use them to build the RegionsOfInterest. If not, the RegionsOfInterest was built // from the string RegionsOfInterest field in the (old) TestSetups table. var dbVersion = DbOperations.GetConnectionDbVersion(); if (dbVersion >= DTS.Common.Constants.ROITables_DB_VERSION) { LoadRegionsOfInterestFromDb(); } else { // We're using an old db, so add the hardware channel assignments if possible, similar // to how we do it in MigrateVersion91(); AddHardwareToROIChannelNames(); } if (dbVersion < Constants.ROIPERIODCHANNELS_CHANNELID_DB_VERSION) { AddROIChannelIds(); SqlCommand cmd = DbOperations.GetSQLCommand(true); var roiList = DbOperations.MigrateChannelNamesToChannelIds(cmd, Id, false); //Assign channelIds to the ROIs so the ROI x Channels step works correctly var roiIndex = 0; foreach (var roi in roiList) { foreach (var idNamePair in roi) { var channelNameIndex = 0; var found = false; while (!found && channelNameIndex < (RegionsOfInterest[roiIndex].ChannelNames.Length)) { if (RegionsOfInterest[roiIndex].ChannelNames[channelNameIndex] == idNamePair.Value) { RegionsOfInterest[roiIndex].ChannelIds[channelNameIndex] = idNamePair.Key; found = true; } channelNameIndex++; } } roiIndex++; } } if (dbVersion < Constants.EnableRepeat_DB_VERSION) { // The old db doesn't have the Enable Repeat value in // the TestSetups table, so get it from the System Settings. var settings = TestSetupDefaults.GetUserSettings(ApplicationProperties.CurrentUser.Id); DoEnableRepeat = settings.DefaultAutoArmRepeatEnable; } #endregion ROI } /// /// I abstracted this code out of LoadFromDB, it's just the code that was handling the iso meta data /// /// private void ProcessMetaData(IReadOnlyDictionary>> tblMetaData) { var metas = new Dictionary(); var setupMeta = new TestSetupMetaData(DataModelSettings.RequireXCrashCompatibilityForISOExports); if (tblMetaData.ContainsKey(Name)) { //Don't rely on LabName, CustName, or TEName being processed before their associated fields //so run through the table to ensure these are analyzed before the associated fields are processed. var useLabDetails = false; var useCustomerDetails = false; var useTestEngineerDetails = false; foreach (var row in tblMetaData[Name]) { var pname = Convert.ToString(row[DbOperations.TestSetups.TestObjectMetaDataFields.PropName.ToString()]); var pvalue = Convert.ToString(row[DbOperations.TestSetups.TestObjectMetaDataFields.PropValue.ToString()]); var to = GetTestObject(row); if (to.Equals(Constants.ISOTestInfo)) { //Only use xDetails if they are in the Test Setup AND there is not a legitimate entry in the //TestSetupObjectMetaDatatable (value is "NOVALUE"). Otherwise, use what's in the table. switch (pname) { case "LabName": useLabDetails = (LabDetails != null) && (pvalue == "NOVALUE"); break; case "CustName": useCustomerDetails = (CustomerDetails != null) && (pvalue == "NOVALUE"); break; case "TEName": useTestEngineerDetails = (TestEngineerDetails != null) && (pvalue == "NOVALUE"); break; } } } foreach (var row in tblMetaData[Name]) { var bOptional = Convert.ToBoolean(row[DbOperations.TestSetups.TestObjectMetaDataFields.Optional.ToString()]); var pname = Convert.ToString(row[DbOperations.TestSetups.TestObjectMetaDataFields.PropName.ToString()]); var pvalue = Convert.ToString(row[DbOperations.TestSetups.TestObjectMetaDataFields.PropValue.ToString()]); var to = GetTestObject(row); //little wonky here, sometimes the character comes in as a number, sometimes as a string. //if we end up with a string longer than 1 character, then we definitely need to convert it. //there are no strings of length 2 that are a valid character if (to.Length > 1) { try { if (int.TryParse(to, out int iTemp)) { to = Convert.ToString(Convert.ToChar(iTemp)); } } catch (Exception ex) { APILogger.Log(ex); } } var version = Convert.ToDouble(row[DbOperations.TestSetups.TestObjectMetaDataFields.Version.ToString()]); if (to.Equals(Constants.ISOTestInfo)) { if ((pname == "Title") && ((pvalue == "NOVALUE") || (string.IsNullOrWhiteSpace(pvalue)))) { pvalue = Name; } else if (pname == "DateOfTheTest") { pvalue = DateTime.Now.ToString("yyyy-MM-dd"); } //31883 Databases that are migrated from 3.3 or earlier may have empty strings in the //TestSetupObjectMetaData table, so use what's in the Laboratory/Customer/TestEngineerDetails //if there is a Laboratory/Customer/TestEngineer associated with the Test Setup. //Additionally, if there are non-empty values, they are from saving a Test Setup after modifying (overrides). switch (pname) { case "LabName": pvalue = GetMetaDataPValue(useLabDetails, pvalue, LabDetails?.Name); break; case "LaboratoryContactName": pvalue = GetMetaDataPValue(useLabDetails, pvalue, LabDetails?.LabratoryContactName); break; case "LaboratoryContactPhone": pvalue = GetMetaDataPValue(useLabDetails, pvalue, LabDetails?.LabratoryContactPhone); break; case "LaboratoryContactFax": pvalue = GetMetaDataPValue(useLabDetails, pvalue, LabDetails?.LabratoryContactFax); break; case "LaboratoryContactEmail": pvalue = GetMetaDataPValue(useLabDetails, pvalue, LabDetails?.LabratoryContactEmail); break; case "LaboratoryName": pvalue = GetMetaDataPValue(useLabDetails, pvalue, LabDetails?.LabratoryName); break; case "LaboratoryTestReferenceNumber": pvalue = GetMetaDataPValue(useLabDetails, pvalue, LabDetails?.LabratoryTestRefNumber); break; case "LaboratoryProjectReferenceNumber": pvalue = GetMetaDataPValue(useLabDetails, pvalue, LabDetails?.LabratoryProjectRefNumber); break; case "CustName": pvalue = GetMetaDataPValue(useCustomerDetails, pvalue, CustomerDetails?.Name); break; case "CustomerName": pvalue = GetMetaDataPValue(useCustomerDetails, pvalue, CustomerDetails?.CustomerName); break; case "CustomerTestReferenceNumber": pvalue = GetMetaDataPValue(useCustomerDetails, pvalue, CustomerDetails?.CustomerTestRefNumber); break; case "CustomerProjectReferenceNumber": pvalue = GetMetaDataPValue(useCustomerDetails, pvalue, CustomerDetails?.ProjectRefNumber); break; case "CustomerOrderNumber": pvalue = GetMetaDataPValue(useCustomerDetails, pvalue, CustomerDetails?.CustomerOrderNumber); break; case "CustomerCostUnit": pvalue = GetMetaDataPValue(useCustomerDetails, pvalue, CustomerDetails?.CustomerCostUnit); break; case "TEName": pvalue = GetMetaDataPValue(useTestEngineerDetails, pvalue, TestEngineerDetails?.Name); break; case "TestEngineerName": pvalue = GetMetaDataPValue(useTestEngineerDetails, pvalue, TestEngineerDetails?.TestEngineerName); break; case "TestEngineerPhone": pvalue = GetMetaDataPValue(useTestEngineerDetails, pvalue, TestEngineerDetails?.TestEngineerPhone); break; case "TestEngineerFax": pvalue = GetMetaDataPValue(useTestEngineerDetails, pvalue, TestEngineerDetails?.TestEngineerFax); break; case "TestEngineerEmail": pvalue = GetMetaDataPValue(useTestEngineerDetails, pvalue, TestEngineerDetails?.TestEngineerEmail); break; } setupMeta.SetProperty(new MetaData(pname, bOptional, pvalue, version), DataModelSettings.RequireXCrashCompatibilityForISOExports); } else { if (!metas.ContainsKey(to[0])) { metas.Add(to[0], new TestObjectMetaData(to[0])); } metas[to[0]].SetProperty(new MetaData(pname, bOptional, pvalue, version)); } } } foreach (var v in metas.Values) { SetMetaData(v.TestObject, v); } SetTestSetupMetaData(setupMeta); } /// /// returns iso meta data property value given some conditions /// I abstracted this code out of existing code, so I can't fully validate what it was tryign to do /// but I think the idea was if you are using an iso property then get the value from the global reference for that customer/ /// testengineer/lab /// but I modified it to also consider whether there was an override value or not /// http://manuscript.dts.local/f/cases/44545/Setting-test-meta-data-only-doesn-t-save-in-DP-4-2 /// /// /// /// /// private static string GetMetaDataPValue(bool getValue, string overrideValue, string fieldValue) { if (!getValue) { return overrideValue; } if (string.IsNullOrWhiteSpace(overrideValue) || TestObjectMetaData.NOVALUE.Equals(overrideValue)) { return fieldValue ?? TestObjectMetaData.NOVALUE; } return overrideValue; } /// /// returns the Test Object portion of an ISOcode /// /// /// private string GetTestObject(Dictionary row) { var to = "?"; try { if (row[DbOperations.TestSetups.TestObjectMetaDataFields.ISOTestObject.ToString()] is string) { to = row[DbOperations.TestSetups.TestObjectMetaDataFields.ISOTestObject.ToString()] as string; } else { to = Convert.ToString(Convert.ToChar(Convert.ToInt32(row[DbOperations.TestSetups.TestObjectMetaDataFields.ISOTestObject.ToString()]))); } } catch (Exception ex) { APILogger.Log(ex); } return to; } /// /// Prepends the hardware assignment, if possible, to the channel name to form, /// for example, "[S6A12345] CH-02\\ARS9003". If the channel has /// not been assigned to hardware, or if there are more than one with the same sensor, then /// the hardware will not be added. /// private void AddHardwareToROIChannelNames() { var regionsOfInterest = RegionsOfInterest; foreach (var regionOfInterest in regionsOfInterest) { var channelNames = regionOfInterest.ChannelNames; var channelNameIndex = 0; foreach (var channelName in channelNames) { //Find the channel's hardware channel if is assigned and can be determined. var newChannelName = channelName; var found = false; foreach (var groupOfChannels in ChannelsForGroup) { foreach (var channel in groupOfChannels.Value) { if (channel.Sensor == channelName) { if (channel.Hardware != null && channel.Hardware != "") { if (found) { //Uh oh, we found more than one with this sensor, so we can't //prepend the ChannelName with the Hardware newChannelName = channelName; } else { if (channel.SensorData.SerialNumber == SensorConstants.TEST_SPECIFIC_ANALOG_SERIAL) { newChannelName = GenerateEmbeddedChannelName(channel.Hardware.Substring(0, channel.Hardware.IndexOf('-')), $"\\{channelName}"); } else { newChannelName = $"{channel.Hardware}\\{channelName}"; } found = true; } } } } } regionOfInterest.ChannelNames[channelNameIndex] = newChannelName; channelNameIndex++; } } RegionsOfInterest = regionsOfInterest; } private void AddROIChannelIds() { var regionsOfInterest = RegionsOfInterest; foreach (var regionOfInterest in regionsOfInterest) { var channelNames = regionOfInterest.ChannelNames; var channelNameIndex = 0; foreach (var channelName in channelNames) { //Find the channel's id Int64 channelId = -1; var found = false; foreach (var groupOfChannels in ChannelsForGroup) { foreach (var channel in groupOfChannels.Value) { if (!found) //Don't stomp on an Id that we found { channelId = -1; } var possibleHardware = string.Empty; var possibleChannelNumber = string.Empty; if (string.IsNullOrWhiteSpace(channel.Hardware) || channel.Hardware.StartsWith("Assigned")) continue; possibleHardware = $"[{channel.HardwareChannel.ModuleSerialNumber}]"; possibleChannelNumber = channel.Hardware.Substring(channel.Hardware.LastIndexOf('-') + 1, 2); var possibleMatch = $"{possibleHardware} CH-{possibleChannelNumber}\\{channel.GetChannelName(SerializedSettings.ISOViewMode)}"; if (possibleMatch == channelName) { if (found) { //Uh oh, we found more than one with this sensor, so we can't channelId = -1; } else { channelId = channel.Id; found = true; } } } } regionOfInterest.ChannelIds[channelNameIndex] = channelId; channelNameIndex++; } } RegionsOfInterest = regionsOfInterest; } private void LoadCalculatedChannelsFromDb() { CalculatedChannels.Clear(); var hr = DbOperations.CalculatedChannelsGet(null, Name, out var records); var calculatedChannels = new List(); if (0 == hr && null != records && records.Any()) { foreach (var record in records) { calculatedChannels.Add(new CalculatedValueClass(record)); } } CalculatedChannels = calculatedChannels; } private void LoadRegionsOfInterestFromDb() { var hresult = DbOperations.RegionsOfInterestGet(Id, out var records); var regionsOfInterest = new BindingList(); if (0 == hresult && null != records && records.Any()) { foreach (var record in records) { var newRecord = AddHardwareToChannelsIfNeeded(record, Id); regionsOfInterest.Add(newRecord); } } RegionsOfInterest = regionsOfInterest; } /// /// If a version 91 client created a Test Setup with multiple ROIs, the hardware /// associated with the channels in the RegionsOfInterest record will not have /// been prepended to the values in the ChannelNames array. /// So, prepend the hardware if possible here. /// This code is similar to that in the database migration. /// /// Additionally, if a version 92 client created a Test Setup with multiple ROIs, but /// with no hardware assigned, the channel names will be preceded with "\\". /// /// /// /// private IRegionOfInterest AddHardwareToChannelsIfNeeded(IRegionOfInterest record, int testSetupId) { IRegionOfInterest newRecord = record; var index = 0; foreach (var channelName in record.ChannelNames) { var plainChannelName = channelName.TrimStart(); // Strip off Assigned by ID if (channelName.StartsWith(DTS.Common.Strings.Strings.AssignedByID)) { plainChannelName = channelName.Substring(channelName.IndexOf('\\') + 1).TrimStart(); } if (!plainChannelName.StartsWith("[")) { //Find the channel's hardware channel if it is assigned and can be determined. if (channelName.StartsWith("\\")) { plainChannelName = channelName.Substring(1); } var newChannelName = $"\\{plainChannelName}"; //Get all of the GroupId values from the TestSetupGroups table with this TestSetupId var groupIdList = GetGroupIds(testSetupId); if (groupIdList.Any()) { //Get the id from the Sensors table var tsrAirChannelFound = false; var sensorIdAndTypeDict = GetSensorId(plainChannelName); if (!sensorIdAndTypeDict.Any() && channelName == SensorConstants.TEST_SPECIFIC_ANALOG_SERIAL) { //None returned, so see if this is a TSR AIR channel sensorIdAndTypeDict = GetSensorId(SensorConstants.TEST_SPECIFIC_ANALOG_SERIAL); tsrAirChannelFound = sensorIdAndTypeDict.Any(); } //If more than one row returned give up if (sensorIdAndTypeDict.Count == 1) { //Get all of the DASIds and DASChannelIndex values from the Channels table with the GroupId and SensorId foreach (var groupId in groupIdList) { var dasIdAndDasChannelIndexList = GetDASIdAndDASChannelIndex(groupId, sensorIdAndTypeDict.FirstOrDefault().Key); if (tsrAirChannelFound) { var serialNumber = GetDASSerialNumber(dasIdAndDasChannelIndexList[0].Item1); newChannelName = GenerateEmbeddedChannelName(serialNumber, $"\\{plainChannelName}"); } else { //If more than one row returned, give up if (dasIdAndDasChannelIndexList.Count == 1) { // Get the SerialNumber and ParentDAS from the DAS table and combine with DASChannelIndex from Channels table // and the channelName before storing ([SPE00596:SPS01235] CH-04\\an1) var serialNumber = GetDASSerialNumber(dasIdAndDasChannelIndexList[0].Item1); if (serialNumber != "") { var indexOfColon = serialNumber.IndexOf(":"); if (indexOfColon > -1) { //Remove the left side of the colon when the format is SPE12345:SPS54321 serialNumber = serialNumber.Substring(indexOfColon + 1); } newChannelName = BuildChannelName(serialNumber, sensorIdAndTypeDict.FirstOrDefault().Value == 3, dasIdAndDasChannelIndexList[0].Item2, plainChannelName); } } } } } } newRecord.ChannelNames[index] = newChannelName; } index++; } return newRecord; } /// /// used to add hardware when importing a pre-4.0 Test Setup export with multiple ROIs /// /// /// /// /// string AddHardwareToImportedChannelIfAssigned(string channelName, Dictionary sensorLookup, List hardware) { var newChannelName = $"\\{channelName}"; var sensorId = sensorLookup[channelName].Id; foreach (var group in ChannelsForGroup) { foreach (var channel in group.Value) { if (channel.SensorId == sensorId) { var dasId = channel.DASId; if (dasId != -1) { foreach (var hw in hardware) { if (hw.DASId == dasId) { newChannelName = BuildChannelName(hw.SerialNumber, sensorLookup[channelName].Bridge == SensorConstants.BridgeType.SQUIB, channel.DASChannelIndex, channelName); break; } } } break; } } } return newChannelName; } /// /// builds a channel name with hardware when hardware is assigned to multiple ROI channels /// /// /// /// /// /// string BuildChannelName(string dasSerialNumber, bool sensorIsSquib, int dasChannelIndex, string channelName) { var sensorLabel = "CH"; var dasChannelNumber = dasChannelIndex + 1; if (sensorIsSquib) { sensorLabel = "SQ"; dasChannelNumber = ((dasChannelIndex / 2) + 1); } var newChannelName = $"[{dasSerialNumber}] {sensorLabel}-{dasChannelNumber.ToString("00")}\\{channelName}"; return newChannelName; } /// /// This function takes a TSR AIR serial number and a channel name that doesn't have /// hardware pre-pended, and returns a new channel name with the hardware pre-pended. /// /// /// /// public string GenerateEmbeddedChannelName(string serialNumber, string channelName) { var newChannelName = string.Empty; var channelNumber = 0; if (channelName.Contains(DFConstantsAndEnums.LOWG_SERIAL_APPEND)) { serialNumber = $"{serialNumber}{SensorConstants.LowG}"; channelNumber = Convert.ToInt32(channelName.Substring(channelName.Length - 1, 1)); } else if (channelName.Contains(DFConstantsAndEnums.HIGHG_SERIAL_APPEND)) { serialNumber = $"{serialNumber}{SensorConstants.HighG}"; channelNumber = Convert.ToInt32(channelName.Substring(channelName.Length - 1, 1)); } else if (channelName.Contains(DFConstantsAndEnums.USER_CHANNEL_NAME_ANGULAR_RATE)) { serialNumber = $"{serialNumber}{SensorConstants.ARS}"; channelNumber = Convert.ToInt32(channelName.Substring(channelName.Length - 1, 1)); } else if (channelName.Contains(DFConstantsAndEnums.USER_CHANNEL_NAME_TEMPERATURE) || channelName.Contains(DFConstantsAndEnums.USER_CHANNEL_NAME_HUMIDITY) || channelName.Contains(DFConstantsAndEnums.USER_CHANNEL_NAME_PRESSURE)) { serialNumber = $"{serialNumber}{SensorConstants.Atm}"; if (channelName.Contains(DFConstantsAndEnums.USER_CHANNEL_NAME_TEMPERATURE)) { channelNumber = 1; } else if (channelName.Contains(DFConstantsAndEnums.USER_CHANNEL_NAME_HUMIDITY)) { channelNumber = 2; } else if (channelName.Contains(DFConstantsAndEnums.USER_CHANNEL_NAME_PRESSURE)) { channelNumber = 3; } } newChannelName = $"[{serialNumber}] {StringResources.ChannelPrefix_Other}{(channelNumber).ToString("00")}{channelName}"; return newChannelName; } /// /// Get all of the GroupId values from the TestSetupGroups table with this TestSetupId /// /// The value that corresponds to the column in the TestSetups table /// private List GetGroupIds(int testSetupId) { var groupIds = new List(); var hresult = DbOperations.TestSetupGroupsGet(null, Id, null, out var records); if (0 == hresult && null != records && records.Any()) { foreach (var record in records) { groupIds.Add(record.GroupId); } } return groupIds; } /// /// Get the id from the Sensors table with this serial number /// /// The serial number of the sensor on this channel /// private Dictionary GetSensorId(string channelName) { var IdAndType = new Dictionary(); var hresult = DbOperations.SensorsGet(channelName, out var records); if (0 == hresult && null != records && records.Any()) { foreach (var record in records) { IdAndType[record.id] = record.SensorType; } } return IdAndType; } /// /// Get the DASId and DASChannelIndex values from the Channels table with this GroupId and SensorId /// /// The value that matches the column in the Groups table /// The value that matches the column in the Sensors table /// A list of DASId/DASChannelIndex tuples (hopefully a list of length 1) private List> GetDASIdAndDASChannelIndex(int groupId, int sensorId) { var dasIdAndChannelIndexList = new List>(); var hresult = DbOperations.ChannelsGet(null, groupId, null, null, null, null, out var records); if (0 == hresult && null != records && records.Any()) { foreach (var record in records) { if (record.SensorId == sensorId) { var dasAndChannelIndex = new Tuple(record.DASId, record.DASChannelIndex); dasIdAndChannelIndexList.Add(dasAndChannelIndex); } } } return dasIdAndChannelIndexList; } /// /// Get the SerialNumber and ParentDAS from the DAS table and combine with DASChannelIndex from Channels table /// and the channelName before storing ([SPE00596:SPS01235] CH-04\\an1) /// /// The value that matches the column in the DAS table /// private string GetDASSerialNumber(int dasId) { var hresult = DbOperations.DASGet(null, null, out var records); if (0 == hresult && null != records && records.Any()) { foreach (var record in records) { var foundDASId = record.DASId; if (foundDASId == dasId) { var dasString = ""; var parentDAS = record.ParentDAS; if (parentDAS != "") { dasString = $"{parentDAS}:"; } dasString = $"{dasString}{record.SerialNumber}"; return dasString; } } } return ""; } private IReadOnlyDictionary GetChannelIdToChannelLookup() { var lookup = new Dictionary(); foreach (var group in Groups) { var channels = ChannelsForGroup[group]; foreach (var ch in channels) { if (ch.IsBlank()) { continue; } if (ch.IsDisabled) { continue; } if (!ch.SensorValid) { continue; } lookup[ch.Id] = ch; } } return lookup; } private void LoadGraphsFromDb() { var lookup = GetChannelIdToChannelLookup(); var graphs = new List(); using (var sql = DbOperations.GetSQLCommand(true)) { var hresult = DbOperations.TestGraphsGet(null, Id, out var records); if (0 == hresult && null != records && records.Any()) { foreach (var record in records) { graphs.Add(new TestGraph(record, lookup)); } } } TestGraphs = graphs.ToArray(); } /// /// replaces one das in the test setup with a different one /// public void ReplaceDAS(DASHardware source, DASHardware dest) { var allHardware = DASHardwareList.GetAllHardware(); var lookup = new Dictionary(); foreach (var h in allHardware) { lookup[h.DASId] = h; } dest.ReconfigureAsNeeded(source); DASHardwareList.GetList().Commit(dest); AddHardware(dest.DASId, dest.SerialNumber, allHardware, lookup); //the sample rate of the test is default, but if the old hardware had a specific rate set, //we'd like to use that var rate = SamplesPerSecondAggregate; if (DASSampleRateList.ContainsKey(source.SerialNumber)) { rate = DASSampleRateList[source.SerialNumber]; } DASSampleRateList[dest.SerialNumber] = rate; var channels = GetChannels(); foreach (var ch in channels) { if (ch.DASId != source.DASId) { continue; } if (ch.DASChannelIndex >= 0) { var replacementChannel = dest.Channels[ch.DASChannelIndex]; ch.SetHardwareChannel(replacementChannel); } } RemoveHardware(source.DASId, allHardware, lookup); ClearHardware(); } #region Validate public static bool ValidateTestSetupName(ref List errors, TestTemplate CurrentTestSetup) { if (string.IsNullOrWhiteSpace(CurrentTestSetup.Name)) { errors.Add(StringResources.EditTestSetup_NameRequired); return false; } if (CurrentTestSetup.Name == StringResources.QuickSensorCheck_DefaultTestName && !CurrentTestSetup.QuickSensorCheck) { errors.Add(StringResources.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(StringResources.EditTestSetup_NameCannotBeISOCSVOrRealtime); return false; } var encoding = Encoding.GetEncoding("UTF-8"); var nameInBytes = encoding.GetBytes(nameToCheck); if (nameInBytes.Length == nameToCheck.Length) return true; errors.Add(StringResources.EditTestSetup_NameCannotContainDoubleByteCharacters); return false; } errors.Add(StringResources.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, bool warnWhenRun) { if (!Validate(ref errors, ref warnings, displayWindow, CurrentTestSetup, bValid, strictness, true, bWarnOnNotFullyAssigned, bRefreshHardware, warnWhenRun)) { CurrentTestSetup.SetIsComplete(false); return false; } if (errors.Any()) { CurrentTestSetup.SetIsComplete(false); return true; //Save it incomplete } CurrentTestSetup.SetIsComplete(true); return true; } private SensorCalibration GetSensorCalibration(SensorData sd, ExcitationVoltageOptions.ExcitationVoltageOption preferredExcitation) { return SensorCalibration.GetLatestCalibrationBySerialNumberAndExcitation(sd, preferredExcitation); } /// /// validates that the sensor is ok to use /// [only handles analog sensors and calibration currently] /// /// /// /// /// /// public static bool ValidateSensor(SensorData sd, ref List warnings, ref List errors, DTS.Common.Interface.Channels.IGroupChannel channel, StrictLevel strictness) { if (null == sd) { var error = string.Format(StringResources.EditTestSetupPageError_MissingSensor, channel.GetChannelName(SerializedSettings.ISOViewMode)); warnings.Add(error); return false; } //we only care about analog sensors currently if (sd.IsSquib()) { return true; } if (sd.IsDigitalInput()) { return true; } if (sd.IsDigitalOutput()) { return true; } if (sd.IsUart()) { return true; } if (sd.IsStreamOutput()) { return true; } if (sd.IsStreamInput()) { return true; } if (sd.IsThermocoupler()) { return true; } //For embedded sensors, calibration is done on a DAS basis. if (sd.IsTestSpecificEmbedded || sd.IsTestSpecificEmbeddedClock) { return true; } var bValid = true; if (SerializedSettings.DontAllowDataCollectionIfOverused) { //Validate sensor based on usage if (sd.UsageCount >= sd.MaximumUsage) { var error = string.Format(StringResources.EditObjectPageError_UsageExceeded, channel.GetChannelName(SerializedSettings.ISOViewMode), sd.SerialNumber, sd.MaximumUsage); if (!errors.Contains(error)) { errors.Add(error); } bValid = false; } else if (sd.UsageCount >= (sd.MaximumUsage - SerializedSettings.UsageRemainingForWarning)) { //just warn var warning = string.Format(StringResources.EditObjectPageWarning_UsageAlmostExceeded, channel.GetChannelName(SerializedSettings.ISOViewMode), sd.SerialNumber, sd.MaximumUsage - sd.UsageCount); if (!warnings.Contains(warning)) { warnings.Add(warning); } } } var sc = SensorCalibrationList.GetLatestCalibrationBySerialNumber(sd); if (null == sc) { var error = string.Format(StringResources.EditTestSetupPage_MissingCalibration, channel.GetChannelName(SerializedSettings.ISOViewMode), sd.SerialNumber); warnings.Add(error); return false; } else { if (Array.Exists(sc.ZeroMethods.Methods, method => method.Method == ZeroMethodType.AverageOverTime && method.Start >= method.End)) { errors.Add($"{StringResources.TestSetupError_InvalidZeroStartTime} {sd.ToDisplayString()}"); bValid = false; } if ((sc.LinearAdded && 0 == sc.LinearAddedSensitivity) || Array.Exists(sc.Records.Records, r => 0 == r.Sensitivity)) { var error = string.Format(StringResources.TestTemplate_InvalidSensitivity, channel.GetChannelName(SerializedSettings.ISOViewMode), sd.SerialNumber); errors.Add(error); } } // 13065 Sensor "First Use" Date var dueDate = sd.GetDueDate(sc); var warningDate = dueDate.AddDays(-1D * SerializedSettings.SensorCalWarningPeriodDays); if (StrictLevel.CheckoutOnly != strictness && dueDate.Date < DateTime.Now.Date) { if (SerializedSettings.CalSensorPolicy == SensorConstants.SensorCalPolicy.AllowAlways || strictness == StrictLevel.QuickCheckout) { //just warn var warning = string.Format(StringResources.EditObjectPageWarning_CalibrationOverdue, channel.GetChannelName(SerializedSettings.ISOViewMode), sd.SerialNumber); if (!warnings.Contains(warning)) { warnings.Add(warning); } } else { var error = string.Format(StringResources.EditObjectPageError_CalibrationOverdue, channel.GetChannelName(SerializedSettings.ISOViewMode), sd.SerialNumber); if (!errors.Contains(error)) { errors.Add(error); } bValid = false; } } else if (StrictLevel.CheckoutOnly != strictness && warningDate.Date <= DateTime.Now.Date) { var warning = string.Format(StringResources.EditObjectPageWarning_CalibrationDueSoon, channel.GetChannelName(SerializedSettings.ISOViewMode), sd.SerialNumber, dueDate.ToLongDateString()); if (!warnings.Contains(warning)) { warnings.Add(warning); } } return bValid; } private static void ValidateTestObjectAndPositionFields(ref List errors, ref List warnings, TestTemplate testSetup) { var channels = testSetup.GetChannels(); var validTestObjects = SerializedSettings.GetValidISOTestObjects(); var validPositions = SerializedSettings.GetValidISOPositions().ToList(); var viewMode = SerializedSettings.ISOViewMode; foreach (var channel in channels) { if (channel.IsDisabled || channel.IsBlank()) { continue; } var code = new DTS.Common.ISO.IsoCode(channel.IsoCode); if (!validTestObjects.Contains(code.TestObject)) { var msg = string.Format(StringResources.InvalidTestObject, code.TestObject, channel.GetChannelName(viewMode)); if (!errors.Contains(msg)) { errors.Add(msg); } } if (!validPositions.Contains(code.Position)) { var msg = string.Format(StringResources.InvalidPosition, code.Position, channel.GetChannelName(viewMode)); if (!errors.Contains(msg)) { errors.Add(msg); } } } } /// /// validates whether the test contains Stand-In DAS or not, a test containing Stand-In DAS is not /// ready to run ///16340 [old 16330] DataPRO 3.1 - Generic DAS/Replace DAS /// private static void ValidateStandIn(DASHardware h, ref List errors) { if (h.IsStandIn() && !errors.Contains(StringResources.EditTestSetupPage_NoPhysicalHardwareInTest)) { errors.Add(StringResources.EditTestSetupPage_NoPhysicalHardwareInTest); } } /// /// validates whether all hardware supports start/trigger completion modes /// (really only needs to test if either start or trigger is inverted) /// /// any errors discovered during validation /// any warnings discovered during validation /// the test to validate private static void ValidateEventLineCompletionModes(ref List errors, ref List warnings, TestTemplate test) { //if test doesn't have invert set, there's nothing to test, we only need to test if we are inverting a signal if (!test.InvertStartRecordCompletion && !test.InvertTriggerCompletion) { return; } var hardware = test.GetHardware(); //this will hold das serials for any das not supporting a completion mode var list = new List(); foreach (var h in hardware) { if (test.InvertStartRecordCompletion) { if (!HardwareConstants.SupportsStartInversion(h.DASTypeEnum, h.ProtocolVersion)) { if (!list.Contains(h.ToString())) { list.Add(h.ToString()); } } } if (test.InvertTriggerCompletion) { if (!HardwareConstants.SupportsTriggerInversion(h.DASTypeEnum, h.ProtocolVersion)) { if (!list.Contains(h.ToString())) { list.Add(h.ToString()); } } } } if (list.Any()) { var msg = $"{StringResources.Warning_UnsupportedEventLineCompletion}\r\n{string.Join("\r\n", list.ToArray())}"; if (!warnings.Contains(msg)) { warnings.Add(msg); } } } private static void ValidateTOMInIgnoreTrigger(ref List errors, ref List warnings, TestTemplate test) { if (null == test) { return; } //only need to warn when ignore shorted trigger is true ... if (!test.IgnoreShortedTriggerCompletion) { return; } if (test.CheckForTOM()) { if (!warnings.Contains(StringResources.Warning_EditTestIgnoreTriggerTOM)) { warnings.Add(StringResources.Warning_EditTestIgnoreTriggerTOM); } } } /// /// validates whether the test SetDASToAutoArm setting works with the test's recording mode /// /// any errors found during validation /// any warnings found during validation /// test to validate private static void ValidateAutoArmRecordingMode(ref List errors, ref List warnings, TestTemplate test) { //30164 If there is TSR AIR in the test, don't worry about validating for Auto-Arm //because the checkbox is hidden, and we don't Auto-arm TSR AIRs. if (!test.DoAutoArm || Array.Exists(test.GetHardware(), hw => hw.IsTSRAIR())) { return; } if (!RecordingModeExtensions.DoesModeSupportAutoArm(test.RecordingMode)) { var msg = string.Format(StringResources.Error_RecordingModeDoesntSupportAutoArm, EnumDescriptionTypeConverter.GetEnumDescription(test.RecordingMode)); if (!errors.Contains(msg)) { errors.Add(msg); } } } //FB 28186 Check for license and limit the hardware access, public static bool ValidateTsrAirLicense(ref List errors, TestTemplate test, DASHardware[] hardware) { if (test != null) { List selectedHardware = hardware.ToList(); var licenseValidationResult = ApplicationProperties.LicenseValidationResult; if (selectedHardware.Exists(p => !p.IsTSRAIR()) && licenseValidationResult.LicenseType == DataProLicensingEnums.LicenseType.TSRAir) { var nonTsrDas = selectedHardware.Where(p => !p.IsTSRAIR()).Select(p => p.DASSerialNumber); var nonTsrDasStr = string.Join(",", nonTsrDas); errors.Add(string.Format(StringResources.EditTestSetup_TSRAIRLicenseLimitationError, nonTsrDasStr)); //mark the test as incomplete in DB if the license is not valid for hardware _ = DbOperations.TestSetupsMarkIsCompleteIsDirty(test.Name, false, false, nonTsrDasStr); return false; } } return true; } /// /// returns the max streaming rate that digital filter rates are supported at /// http://manuscript.dts.local/f/cases/30273/Implement-Warning-Validation-for-Streaming-sample-rate-greater-than-10k-when-Digital-Filtering-is-configured /// http://manuscript.dts.local/f/cases/39106/Datapro-shouldnt-be-warning-about-digital-filter-availability-until-20k-sps /// this is just a warning and it's only done for S6A (not S6A-BR by LP's suggestion) /// /// /// private static double GetMaxDigitalFilterStreamRate(TestTemplate test) { if (null == test) { return Constants.SLICE6AIR_STREAMING_DIGITAL_FILTERING_MAX_SAMPLERATE; } try { var allHardware = test.GetHardware(); foreach (var h in allHardware) { if (h.GetHardwareTypeEnum() == HardwareTypes.SLICE6_AIR && h.ProtocolVersion < Constants.SLICE6AIR_20K_DIGITAL_FILTER_MIN_PROTOCOL) { return Constants.SLICE6AIR_STREAMING_DIGITAL_FILTERING_MAX_SAMPLERATE; } } return Constants.SLICE6AIR_STREAMING_DIGITAL_FILTERING_MAX_SAMPLERATE_20k; } catch (Exception ex) { APILogger.Log(ex); return Constants.SLICE6AIR_STREAMING_DIGITAL_FILTERING_MAX_SAMPLERATE; } } /// /// checks if isocode on channel already appears in list of isocodes in test setup /// /// /// /// private static void DoDuplicateIsoCodeCheck(ref List errors, IGroupChannel ch, ref Dictionary usedISOCodesLookup) { //these type of channels don't have isocodes that we care about //however until a sensor is assigned we can't tell the bridge type, so only bypass if a sensor is assigned if (null != ch.SensorData && (ch.IsDigitalOut || ch.IsStreamOut || ch.IsUart || ch.IsStreamIn)) { return; } if (usedISOCodesLookup.ContainsKey(ch.IsoCode)) { var channelName = ChannelHelper.GetWarningChannelName(ch); var err = string.Format(StringResources.EditObjectPageError_DuplicateIsoCode, channelName); //we put this into errors because it's fatal for running the test, but we //don't return false because we CAN save the test ... //13902 Cannot save test setup when leaving out one or more characters for a channels' ISO code if (!errors.Contains(err)) { errors.Add(err); } } else { usedISOCodesLookup[ch.IsoCode] = true; } } private static void ValidateTSRAIRRanges(ref List _, ref Listwarnings, TestTemplate test) { if (null == test) { return; } var channels = test.GetChannels(); var invalidLowG = channels.Exists(x => null != x.HardwareChannel && x.RangeModifiableSensorLowG && x.Range < SensorConstants.LowG64); var invalidARS = channels.Exists(x => null != x.HardwareChannel && x.RangeModifiableSensorARS && x.Range < SensorConstants.ARS2000); if (invalidLowG) { var msg = StringResources.TSRAIR_InvalidLowG; if (!warnings.Contains(msg)) { warnings.Add(msg); } } if (invalidARS) { var msg = StringResources.TSRAIR_InvalidARS; if (!warnings.Contains(msg)) { warnings.Add(msg); } } } private static void ValidateEuAtMv(ref List warnings, TestTemplate test) { if ( null == test) { return; } var channels = test.GetChannels(); foreach( var channel in channels) { ValidateEuAtMvChannel(channel, warnings); } } private static void ValidateEuAtMvChannel(IGroupChannel channel, List warnings) { if (null == channel) { return; } if (!channel.IsAnalog) { return; } if (null == channel.InitialOffset) { return; } if (channel.InitialOffset.Form != InitialOffsetTypes.EUAtMV) { return; } if (channel.ZeroMethod == ZeroMethodType.None) { return; } warnings.Add(string.Format(StringResources.Warning_EuAtMvUseNone, channel.GetChannelName(RunTestVariables.ISOViewMode))); } private static bool IsNotDSPFiltered(IStreamingFilterProfile dspFilter) { return dspFilter == null || DSPFilterCollection.NONE == dspFilter.EnumValue; } public static bool Validate(ref List errors, ref List warnings, bool displayWindow, TestTemplate CurrentTestSetup, bool bValid, StrictLevel strictness, bool markIsCompleteUnchecked, bool bWarnOnNotFullyAssigned, bool bRefreshHardware, bool warnWhenRun) { try { ValidateEuAtMv(ref warnings, CurrentTestSetup); ValidateTSRAIRRanges(ref errors, ref warnings, CurrentTestSetup); ValidateAutoArmRecordingMode(ref errors, ref warnings, CurrentTestSetup); ValidateEventLineCompletionModes(ref errors, ref warnings, CurrentTestSetup); ValidateTOMInIgnoreTrigger(ref errors, ref warnings, CurrentTestSetup); var viewMode = SerializedSettings.ISOViewMode; if (SerializedSettings.ValidateTestPositionAndTestObject && SerializedSettings.ShowISOCodes && StrictLevel.CheckoutOnly != strictness && StrictLevel.QuickCheckout != strictness) //15139, ignore ISO warnings in quick checkout { if (viewMode == IsoViewMode.ISOAndUserCode || viewMode == IsoViewMode.ISOOnly) { ValidateTestObjectAndPositionFields(ref errors, ref warnings, CurrentTestSetup); } } var serialNumbersProcessed = new Dictionary(); var eidsProcessed = 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; var hardware = CurrentTestSetup.GetHardware(); if (!ValidateTsrAirLicense(ref errors, CurrentTestSetup, hardware)) { //Test is not valid if the license is not valid bValid = false; } bool bHasHardware = false; bool bHasChannels = false; foreach (var h in hardware) { var hType = h.GetHardwareTypeEnum(); //We want to check the calibration dates on some hardware where //IsSLICEEthernetController is True (PowerPRO, for example). ValidateHardware(h, false, ref warnings, ref errors); //17744 Is standin check and warning is being skipped for ethernet controllers and powerpro ValidateStandIn(h, ref errors); if (h.IsSLICEEthernetController) { 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(StringResources.EditTestSetupPage_HardwareDoesNotSupportAutoArm); break; } } else if (CurrentTestSetup.DoStreaming) { if (hType != HardwareTypes.SLICE6_AIR_BR && hType != HardwareTypes.SLICE6_AIR && hType != HardwareTypes.SLICE6_Base && hType != HardwareTypes.TSR_AIR && hType != HardwareTypes.TSR_AIR_RevB /* && hType != HardwareTypes.SLICE6DB_AIR*/ && hType != HardwareTypes.SLICE6_AIR_TC) { errors.Add(StringResources.EditTestSetupPage_HardwareDoesNotSupportStreaming); } //30273 Implement 'Warning' Validation for Streaming sample rate greater than 10k when Digital Filtering is configured //39106 [allow 20k] if (hType == HardwareTypes.SLICE6_AIR && IsNotDSPFiltered(SerializedSettings.GetDefaultDSP()) && CurrentTestSetup.DASSampleRateList.ContainsKey(h.SerialNumber) ) { var minDigitalFilterStreamRate = GetMaxDigitalFilterStreamRate(CurrentTestSetup); if (CurrentTestSetup.DASSampleRateList[h.SerialNumber] > minDigitalFilterStreamRate) { if (warnWhenRun) { errors.Add(string.Format(StringResources.EditTestSetupPage_InvalidDigitalFilteringSampleRate, minDigitalFilterStreamRate, Environment.NewLine)); } else { warnings.Add(string.Format(StringResources.EditTestSetupPage_InvalidDigitalFilteringSampleRate, minDigitalFilterStreamRate, Environment.NewLine)); } } } } var foo = CurrentTestSetup.DASSampleRateList; if (CurrentTestSetup.UARTRecordingMode && CurrentTestSetup.DASSampleRateList[h.SerialNumber] > Constants.UART_MODE_MAX_SAMPLERATE) { errors.Add(StringResources.EditTestSEtupInfo_InvalidSampleRate); } for (var i = 0; i < h.Channels.Length; i++) { var ch = h.Channels[i]; var key = $"{ch.Hardware.DASId}_{ch.ChannelNumber}"; if (channelLookup.ContainsKey(key)) continue; channelLookup.Add(key, ch); if (ch.IsSupportedBridgeType(SensorConstants.BridgeType.SQUIB)) { availableSquibChannels++; i++; /*bypass next channel as it's also a squib*/ } else if (ch.IsSupportedBridgeType(SensorConstants.BridgeType.DigitalInput)) { availableDigitalInputChannels++; } else if (ch.IsSupportedBridgeType(SensorConstants.BridgeType.TOMDigital)) { availableDigitalOutputChannels++; } else { availableAnalogChannels++; } } } if (!bHasHardware && bWarnOnNotFullyAssigned) { errors.Add(StringResources.EditTestSetupPage_NoHardwareInTest); } ValidateSquibSettings(CurrentTestSetup, ref errors, ref warnings); //13914 Cannot save test setup as incomplete if a channel is not included in any of the multiple ROI periods. if (CurrentTestSetup.RegionsOfInterest.Count > 1 && CurrentTestSetup.DoROIDownload) { foreach (var region in CurrentTestSetup.RegionsOfInterest) { if (null == region.ChannelIds || !region.ChannelIds.Any()) { //No channel ids, but we might have called into an old database, so check for channel names too if (null == region.ChannelNames || !region.ChannelNames.Any()) { errors.Add(string.Format(StringResources.TestTemplate_ROIHasNoChannels, region.Suffix)); } } } } var uniqueISOCodesRequired = SerializedSettings.UniqueISOCodesRequired && strictness != StrictLevel.QuickCheckout; //FB 18875 use for chache var cals = SensorsCollection.GetLatestCalibrations(); var sensorCalPolicy = CalibrationPolicy.GetCalibrationPolicy(); foreach (var group in CurrentTestSetup.Groups) { var channels = CurrentTestSetup.ChannelsForGroup[group]; foreach (var ch in channels) { if (ch.IsBlank() || ch.IsDisabled) { continue; } if (StrictLevel.CheckoutOnly != strictness && uniqueISOCodesRequired && (viewMode == IsoViewMode.ISOAndUserCode || viewMode == IsoViewMode.ISOOnly) && !ch.IsUart && !ch.IsStreamOut && !ch.IsStreamIn) { if (ch.IsoCode.Length < 16) { var msg = string.Format(StringResources.EditTestSetup_WarningISOCodeInvalid, ch.IsoCode); if (!warnings.Contains(msg)) { warnings.Add(msg); } } } if ((CurrentTestSetup.ExportFormats & SupportedExportFormatBitFlags.toyotaunfiltered) == SupportedExportFormatBitFlags.toyotaunfiltered) { foreach (var setting in ch.ChannelSettings) { if (setting.SettingName != DbOperations.TestSetups.SettingsFields.UserValue2.ToString()) continue; if (!string.IsNullOrWhiteSpace(setting.Value) && !double.TryParse(setting.Value, out double cableMultiplier)) { warnings.Add(StringResources.EditTestSetup_TTSUserValue2); if (!SerializedSettings.ShowSensorChannelUserValues) { warnings.Add(StringResources.EditTestSetup_ShowSensorChannelUnchecked); } break; } } } if (uniqueISOCodesRequired && (viewMode == IsoViewMode.ISOAndUserCode || viewMode == IsoViewMode.ISOOnly)) { DoDuplicateIsoCodeCheck(ref errors, ch, ref usedISOCodesLookup); } if (!ch.SensorValid) { if (!errors.Contains(StringResources.EditTestSetupPage_NoSensorOnChannel) && bWarnOnNotFullyAssigned) { errors.Add(StringResources.EditTestSetupPage_NoSensorOnChannel); } } else { bHasChannels = true; //FB 18875 use for chache var sensor = CurrentTestSetup.GetSensor(ch, cals); if (null == sensor) { var err = string.Format(StringResources.EditTestSetupPage_CouldNotFindSensor, ch.SensorId, ch.GetChannelName(SerializedSettings.ISOViewMode), ch.GroupName); if (!errors.Contains(err)) { errors.Add(err); } return false; } //Set the sensor's Usage Count before validation sensor.UsageCount = sensor.GetLatestCalibration()?.UsageCount ?? 0; ValidateSensor(sensor, ref warnings, ref errors, ch, strictness); if (sensor.Broken || sensor.DoNotUse) { var error = string.Format(StringResources.EditTestSetupPageError_MissingSensor, ChannelHelper.GetWarningChannelName(ch)); if (!warnings.Contains(error) && !errors.Contains(error)) { errors.Add(error); } continue; } //33237 Don't unassign multiple ROI channels if Compact is selected in Hardware step //36746 Don't throw exception during TTS import due to null SensorData if (CurrentTestSetup.RegionsOfInterest.Count > 1 && !CurrentTestSetup.RegionsOfInterest.Any(roi => roi.ChannelIds.Contains(ch.Id)) && !sensor.IsDigitalOutput() && !sensor.IsTestSpecificDigitalOutput)//FB14896: scrub digital outs from ROI { var msg = string.Format(StringResources.TestTemplate_ChannelNoROIs, ch.GetChannelName(SerializedSettings.ISOViewMode), ch.Hardware); if (!errors.Contains(msg)) errors.Add(msg); //13914 Cannot save test setup as incomplete if a channel is not included in any of the multiple ROI periods. } if (eidsProcessed.ContainsKey(sensor.EID)) { //FB15726: make sure EID and serials are validated in TestTemplate and UI var msg = string.Format( StringResources.EditTestSetupPage_DuplicateSensorId, sensor.EID, sensor.SerialNumber); if (!errors.Contains(msg)) errors.Add(msg); return false; } if (!string.IsNullOrWhiteSpace(sensor.EID)) eidsProcessed.Add(sensor.EID, true); if (serialNumbersProcessed.ContainsKey(sensor.SerialNumber)) { //FB15726: Allow "reusable sensors", or sensors without EID to appear more than once in a test if (sensor.IsTestSpecificDigitalOutput || sensor.IsTestSpecificSquib || sensor.IsTestSpecificDigitalIn || sensor.IsTestSpecificEmbedded || sensor.IsTestSpecificEmbeddedClock || string.IsNullOrWhiteSpace(sensor.EID)) { continue; } } serialNumbersProcessed.Add(sensor.SerialNumber, true); if (sensor.IsSquib()) { availableSquibChannels--; if (sensor.SquibFireDelayMS + sensor.SquibFireDurationMS > CurrentTestSetup.PostTriggerSeconds * 1000) { errors.Add(string.Format( StringResources.TestTemplate_InsufficientPostTriggerLength, sensor.SerialNumber, ch.GetChannelName(SerializedSettings.ISOViewMode))); } } else if (sensor.IsDigitalInput()) { availableDigitalInputChannels--; } else if (sensor.IsDigitalOutput()) { availableDigitalOutputChannels--; if (sensor.DigitalOutputDelayMS + sensor.DigitalOutputDurationMS > CurrentTestSetup.PostTriggerSeconds * 1000) { errors.Add(string.Format( StringResources.TestTemplate_InsufficientPostTriggerLength, sensor.SerialNumber, ch.GetChannelName(SerializedSettings.ISOViewMode))); } } else { availableAnalogChannels--; } if (sensor != null) { if (StrictLevel.CheckoutOnly != strictness && (viewMode == IsoViewMode.ISOAndUserCode || viewMode == IsoViewMode.ISOOnly) && !sensor.IsUart() && !sensor.IsTestSpecificUart && !sensor.IsStreamOutput() && !sensor.IsTestSpecificStreamOutput && !sensor.IsStreamInput() && !sensor.IsTestSpecificStreamInput && !sensor.IsTestSpecificEmbeddedClock) //Embedded clocks & settings sensors aren't coded sensors { var sensorDimension = (new DTS.Common.ISO.IsoCode(sensor.ISOCode)).PhysicalDimension; var channelDimension = (new DTS.Common.ISO.IsoCode(ch.IsoCode)).PhysicalDimension; if (sensor.IncompatibleSensorAssignment(sensorDimension, channelDimension)) { var err = string.Format( StringResources.EditTestSetupPage_IncompatibleSensorNotSupported, sensorDimension, sensor.SerialNumber, channelDimension, ch.GetChannelName(SerializedSettings.ISOViewMode), ch.GroupName); switch (SerializedSettings.IsoChannelSensorCompatibilityLevel) { case IsoChannelSensorCompatibilityLevels.DontAllow: if (!errors.Contains(err)) { errors.Add(err); } bValid = false; break; case IsoChannelSensorCompatibilityLevels.Warn: if (!warnings.Contains( StringResources.EditObjectSensorsControl_Warning + ": " + err)) { warnings.Add(StringResources.EditObjectSensorsControl_Warning + ": " + err); } break; } } //13312 Cannot run my test that was migrated from 1.4 because my digital output channel doesn't contain 16 characters. //warn if we have incomplete iso except for digital outputs if (SerializedSettings.UniqueISOCodesRequired && ch.IsoCode.Length != 16 && !sensor.IsDigitalOutput() && !sensor.IsUart() && !sensor.IsStreamInput() && !sensor.IsStreamOutput() && strictness != StrictLevel.QuickCheckout) { var channelName = ChannelHelper.GetWarningChannelName(ch); var err = string.Format(StringResources.EditObjectPageError_IncompleteIsoCode, channelName); if (!errors.Contains(err)) { errors.Add(err); } bValid = false; } } if (!sensor.IsTestSpecificEmbedded && !sensor.IsTestSpecificEmbeddedClock) //Validation of calibration for embedded sensors is done at the DAS-level { SensorCalibration sc = null; var preferredExcitation = ExcitationVoltageOptions.ExcitationVoltageOption.Undefined; if (sensor.SupportedExcitation.Any()) { preferredExcitation = sensor.SupportedExcitation[0]; } else { bValid = false; var err = string.Format(StringResources.EditTestSetupPage_MissingExcitation, ch.GetChannelName(SerializedSettings.ISOViewMode), sensor.SerialNumber); if (!errors.Contains(err)) { errors.Add(err); } } if (preferredExcitation != ExcitationVoltageOptions.ExcitationVoltageOption.Undefined) { sc = CurrentTestSetup.GetSensorCalibration(sensor, preferredExcitation); if (null == sc) { bValid = false; var err = string.Format(StringResources.EditTestSetupPage_MissingCalibration, ch.GetChannelName(SerializedSettings.ISOViewMode), sensor.SerialNumber); if (!errors.Contains(err)) { errors.Add(err); } } else { var now = DateTime.Now; var days = sensor.GetDueDate(sc).Subtract(now).TotalDays; if (StrictLevel.CheckoutOnly != strictness && sensorCalPolicy.SelectedCalPolicy == SensorConstants.SensorCalPolicy.DONT_ALLOW && (days <= 0 || days < sensorCalPolicy.WarningPeriod)) { var err = string.Format(StringResources.OverdueSensors_CalibrationDateOverdueOrNear, ch.GetChannelName(SerializedSettings.ISOViewMode), sensor.SerialNumber); if (days <= 0) { if (!errors.Contains(err)) { errors.Add(err); } } else if (!warnings.Contains(err)) { warnings.Add(err); } } } } } } if (!ch.HardwareValid) continue; var key = $"{ch.DASId}_{ch.DASChannelIndex}"; if (!channelLookup.ContainsKey(key)) { var err = string.Format(StringResources.EditTestSetupPage_InvalidHardware, ch.GetChannelName(SerializedSettings.ISOViewMode), ch.GroupName); ch.SetHardwareChannel(null); if (!errors.Contains(err)) { errors.Add(err); var eventAggregator = ContainerLocator.Container.Resolve(); eventAggregator.GetEvent().Publish(new PageModifiedArg(PageModifiedArg.Status.Modified, null)); } } else { if (hardwareChannelIdsProcessed.ContainsKey(key)) { bValid = false; var err = string.Format( StringResources.EditTestSetupPage_HardwareAlreadyInUse, ch.GetChannelName(SerializedSettings.ISOViewMode), ch.GroupName); if (!errors.Contains(err)) { errors.Add(err); } } else { hardwareChannelIdsProcessed.Add(key, true); var hch = channelLookup[key]; if (sensor.ByPassFilter && ((hch.Hardware.GetHardwareTypeEnum() != HardwareTypes.TDAS_Pro_Rack) && (hch.Hardware.GetHardwareTypeEnum() != HardwareTypes.TDAS_LabRack))) { var err = string.Format( StringResources.EditTestSetupPage_BypassAAFilterNotSupported, ch.GetChannelName(SerializedSettings.ISOViewMode), ch.GroupName); if (!errors.Contains(err)) { errors.Add(err); } } else { if (hch.IsSupportedBridgeType(sensor.Bridge)) continue; var err = string.Format( StringResources.EditTestSetupPage_BridgeNotSupported, sensor.Bridge.ToString(), ch.GetChannelName(SerializedSettings.ISOViewMode), ch.GroupName); if (!errors.Contains(err)) { errors.Add(err); } } } } } } } if (!bHasChannels && !CurrentTestSetup.HasOBRDDR()) { errors.Add(StringResources.EditTestSetupPage_NoChannelsInTest); } if (availableAnalogChannels < 0 && !CurrentTestSetup.AllowMissingSensors && bWarnOnNotFullyAssigned) { if (!errors.Contains(StringResources.EditTestSetupPage_NeedMoreAnalogChannels) && !errors.Contains(StringResources.EditTestSetupPage_NoHardwareInTest)) { errors.Add(StringResources.EditTestSetupPage_NeedMoreAnalogChannels); } } if (availableDigitalInputChannels < 0 && !CurrentTestSetup.AllowMissingSensors && bWarnOnNotFullyAssigned) { if (!errors.Contains(StringResources.EditTestSetupPage_NeedMoreDigitalInputChannels) && !errors.Contains(StringResources.EditTestSetupPage_NoHardwareInTest)) { errors.Add(StringResources.EditTestSetupPage_NeedMoreDigitalInputChannels); } } if (availableDigitalOutputChannels < 0 && !CurrentTestSetup.AllowMissingSensors && bWarnOnNotFullyAssigned) { if (!errors.Contains(StringResources.EditTestSetupPage_NeedMoreDigitalOutputChannels) && !errors.Contains(StringResources.EditTestSetupPage_NoHardwareInTest)) { errors.Add(StringResources.EditTestSetupPage_NeedMoreDigitalOutputChannels); } } if (availableSquibChannels < 0 && !CurrentTestSetup.AllowMissingSensors && bWarnOnNotFullyAssigned) { if (!errors.Contains(StringResources.EditTestSetupPage_NeedMoreSquibChannels) && !errors.Contains(StringResources.EditTestSetupPage_NoHardwareInTest)) { errors.Add(StringResources.EditTestSetupPage_NeedMoreSquibChannels); } } if (CurrentTestSetup.RegionsOfInterest.Count > 1 && CurrentTestSetup.RegionsOfInterest.Any(roi => !roi.ChannelNames.Any())) { //13391 Too many warnings for the same thing when there are no channels selected for an ROI download //errors.Add(StringResources.TestTemplate_ROIHasNoChannels); //bValid = false; //13914 Cannot save test setup as incomplete if a channel is not included in any of the multiple ROI periods. } if (!ValidateTestSetupName(ref errors, CurrentTestSetup)) { bValid = false; } ValidateCommonStatusLine(ref errors, CurrentTestSetup); //not fatal ValidateSampleRates(ref errors, CurrentTestSetup); //not fatal ValidateAAFilterRates(ref errors, ref warnings, CurrentTestSetup, warnWhenRun); //not fatal ValidateStorageSpace(ref errors, CurrentTestSetup);//not fatal ValidateRecordingModes(ref errors, ref warnings, CurrentTestSetup); //not fatal if (StrictLevel.CheckoutOnly != strictness) { //15391 Quick-check tile requires level trigger and export settings that can't be configured. ValidateLevelTriggers(ref errors, CurrentTestSetup); //not fatal } ValidateAutoArmAllowability(ref errors, CurrentTestSetup); ValidateStreamingAllowability(ref errors, CurrentTestSetup); //30429 Invalidate/fail sooner than Arm step if DAS doesn't have streaming capability/channel (TSR AIR may or may not) ValidateStreamingSupported(ref errors, CurrentTestSetup); //30048 Allow multiple udp outs, but display a warning, unless suppressed in the config if (DataModelSettings.DisplayDuplicateUDPStreamOutWarning) { ValidateStreamingOutputAddresses(ref warnings, CurrentTestSetup); } if (ValidateStreamingProfiles(ref errors, CurrentTestSetup) && SerializedSettings.EnableStreamingWarnings) { ValidateSamplesPerPacket(ref warnings, CurrentTestSetup); } ValidateClockSyncProfiles(ref errors, CurrentTestSetup); if (!ValidateCalculatedChannels(ref errors, ref warnings, CurrentTestSetup)) { bValid = false; } if (!ValidateGraphs(ref errors, ref warnings, CurrentTestSetup)) { bValid = false; } if (!ValidateTestLength(ref errors, ref warnings, CurrentTestSetup)) { bValid = false; } if (StrictLevel.CheckoutOnly != strictness) { ValidateUploadFolder(ref errors, CurrentTestSetup); //not fatal } if (CurrentTestSetup.RecordingMode == RecordingModes.Scheduled || CurrentTestSetup.RecordingMode == RecordingModes.Interval) { ValidateScheduleStartTime(ref errors, CurrentTestSetup); if (CurrentTestSetup.RecordingMode == RecordingModes.Interval) { ValidateInterval(ref errors, CurrentTestSetup); } } } catch (Exception ex) { bValid = false; } return bValid; } /// /// returns a sensor calibration given a sensor and a hardware channel /// I couldn't find an abstracted function that already did this although there's similar code repeated in multiple places, like validation /// in this case it's used when downloaded where the hardware channel should be known, while it might not be known at validation time /// [you could have a sensor assigned to a channel without hardware assigned to that channel] /// /// /// /// public SensorCalibration GetLatestSensorCalibration(ISensorData sd, IHardwareChannel hc) { if (!sd.IsTestSpecificEmbedded && !sd.IsTestSpecificEmbeddedClock) //Validation of calibration for embedded sensors is done at the DAS-level { foreach (var exc in sd.SupportedExcitation) { if (exc == ExcitationVoltageOptions.ExcitationVoltageOption.Undefined) { continue; } if (!hc.IsSupportedExcitation(exc)) { continue; } var cal = GetSensorCalibration((SensorData)sd, exc); if (null == cal) { continue; } return cal; } return null; } return SensorCalibrationList.GetLatestCalibrationBySerialNumber((SensorData)sd); } public static bool ValidateInterval(ref List errors, TestTemplate CurrentTestSetup) { //Don't allow an event + the time to re-arm to be longer than the interval between events if (CurrentTestSetup.PostTriggerSeconds + DFConstantsAndEnums.TIME_TO_REARM_SECONDS >= CurrentTestSetup.IntervalBetweenEventStartsMinutes * 60) { errors.Add(Constants.IntervalError); return false; } return true; } public static bool ValidateScheduleStartTime(ref List errors, TestTemplate CurrentTestSetup) { if (DateTime.Compare(CurrentTestSetup.RTCScheduleStartDateTime, DateTime.UtcNow.AddMinutes(DFConstantsAndEnums.SCHEDULE_AHEAD_IN_MINUTES)) < 0) { errors.Add(Constants.ScheduleStartTimeError); return false; } return true; } private static void ValidateSquibSettings(TestTemplate CurrentTestSetup, ref List errors, ref List warnings) { var channels = CurrentTestSetup.GetChannels(); foreach (var ch in channels) { if (ch.IsBlank()) { continue; } if (!ch.IsSquib) { continue; } if (!ch.SensorValid) { continue; } if (ch.SquibFireMode == SquibFireMode.NONE) { var msg = string.Format(StringResources.TestTemplate_ChannelNoFireMode, ch.GetChannelName(SerializedSettings.ISOViewMode)); if (!errors.Contains(msg)) { errors.Add(msg); } } //FB 14623 Validation for SquibDelay if (ch.SquibDelay.HasValue) { continue; } var channelName = ChannelHelper.GetWarningChannelName(ch); string warningMessage = string.Format(StringResources.EditTestSetupParameters_EmptySquibDelay, channelName); if (!warnings.Contains(warningMessage)) { warnings.Add(warningMessage); } } //FB9812 warn if squib resistance check wanted, but no squibs in test if (CurrentTestSetup.MeasureSquibResistancesStep && !channels.Exists(ch => ch.IsSquib)) { if (!warnings.Contains(StringResources.EditTestSetupInfo_NoSquibsToCheck)) { warnings.Add(StringResources.EditTestSetupInfo_NoSquibsToCheck); } } } //http://manuscript.dts.local/f/cases/36728/Restriction-Alert-Test-Execution-for-TSR-AIR-with-Level-Trigger-Below-5g-and-Enabled-Wake-Up-Motion //per EF, whenever wake up on motion is enabled on a TSR AIR, prevent any level trigger <= 5G private const double MIN_MOTION_LEVELTRIGGER_TSRAIR = 5; /// /// returns true if there's a level trigger on a TSR AIR channel with 5EU or less /// /// public bool HasTSRAIRLevelTriggerLessThan5G() { if (null == LevelTriggerChannels || 0 == LevelTriggerChannels.Count) { return false; } using (var e = LevelTriggerChannels.GetEnumerator()) { while (e.MoveNext()) { if (null == e.Current.Value.GroupChannel || null == e.Current.Value.GroupChannel.HardwareChannel || !e.Current.Value.GroupChannel.HardwareChannel.IsTSRAIR) { continue; } if (e.Current.Value.GreaterThanThresholdEU <= MIN_MOTION_LEVELTRIGGER_TSRAIR && //we don't really need to check against 0 here, as you can't have a TSR AIR level trigger with a //value of 0EU, but it feels unsafe not to check for 0 ... e.Current.Value.GreaterThanThresholdEU != 0D) { return true; } } } return false; } public static bool ValidateLevelTriggers(ref List errors, TestTemplate currentTest) { var valid = true; if (currentTest.LevelTriggerChannels.Any()) { if (currentTest.HasMultipleSampleRates()) { errors.Add(StringResources.EditTestSetupPage_LevelTriggersNotSupportedWithMultipleSampleRates); } //http://manuscript.dts.local/f/cases/36728/ //don't allow wake up with motion with level trigger <=5G if (currentTest.WakeUpWithMotion) { if (currentTest.HasTSRAIRLevelTriggerLessThan5G()) { errors.Add(StringResources.EditTestSetupPage_LevelTriggerToLowForWakeupOnMotion); valid = false; } } // 29403 TSRAIR needs some pre-trigger samples to accurately mark T0 if (currentTest.TSRAIRInActiveMode() && 0 == Math.Round(currentTest.PreTriggerSeconds, 4) && !currentTest.StartWithEvent) { errors.Add(StringResources.EditTestSetupPage_TSRAIRActiveLevelTriggerSetupsRequireSomePretrigger); valid = false; } } else if (DataModelSettings.TestsRequireLevelTriggers) { //FB14604: Config file option to require level triggers errors.Add(StringResources.EditTestSetupPage_LevelTriggersRequired); valid = false; } return valid; } public bool TSRAIRInActiveMode() { var hardware = GetHardware(); return TestHasTSRAIRDevices(hardware) && (RecordingMode == RecordingModes.Active || RecordingMode == RecordingModes.MultipleEventActive); } /// /// Examines the hardware /// /// True if any DAS in a Test Setup contains a TSR AIR public bool TestHasTSRAIRDevices(DASHardware[] hardware) { return Array.Exists(hardware, h => h.IsTSRAIR()); } /// /// Examines the hardware, sample rate, and channels in a Test Setup. /// /// True if any DAS in a Test Setup contains a TSR AIR, and the Sample Rate is > 500, /// and the Test Setup has at least one TSR AIR High G channel. public bool TSRAIRsSupportLevelTrigger() { var hardware = GetHardware(); return TestHasTSRAIRDevices(hardware) && Array.Exists(hardware, h => GetSampleRateForHardware(h) > DFConstantsAndEnums.TSR_AIR_HIGH_G_CUTOFF_RATE_SPS) && TestHasTSRAIRLevelTriggerChannel(); } /// /// Looks for TSR AIR High G channels in a Test Setup /// /// True if a Test Setup has at least one TSR AIR High G channel, False otherwise. private bool TestHasTSRAIRLevelTriggerChannel() { foreach (var group in Groups) { foreach (var ch in ChannelsForGroup[group]) { if (ch.Hardware.Contains(DFConstantsAndEnums.USER_CHANNEL_NAME_HIGHG)) { return true; } } } return false; } public static bool ValidateTestLength(ref List errors, ref List warnings, TestTemplate test) { if (RecordingModeExtensions.IsACircularBufferMode(test.RecordingMode) || RecordingModeExtensions.IsARecorderMode(test.RecordingMode)) { if ((test.PreTriggerSeconds + test.PostTriggerSeconds) < .1) { var msg = StringResources.TestSetupNoRecordingTimeError; if (!errors.Contains(msg)) { errors.Add(msg); } } } return true; } public static bool ValidateGraphs(ref List errors, ref List warnings, TestTemplate test) { var graphNames = new HashSet(); foreach (var graph in test.TestGraphs) { if (graphNames.Contains(graph.GraphName.ToLower())) { var msg = $"{StringResources.TestSetupDuplicateGraphWarning} ({graph.GraphName})"; if (!warnings.Contains(msg)) { warnings.Add(msg); } } else { graphNames.Add(graph.GraphName.ToLower()); } if (!graph.GroupChannels.Any()) { var msg = string.Format(StringResources.TestSetupEmptyGraphWarning, graph.GraphName); if (!warnings.Contains(msg)) { warnings.Add(msg); } } } return true; } public static bool ValidateCalculatedChannels(ref List errors, ref List warnings, TestTemplate currentTest) { if (currentTest.CalculatedChannels.Count > 0 && !SerializedSettings.AllowCalculatedChannels) { //FB14483 "Should ignore validation/error in edit test setup save if Calculated Channel UI is not enabled" warnings.Add(StringResources.EditTestSetupCC_TestCCButNoSystemCC); return true; } var valid = true; var names = new HashSet(); var duplicates = new List(); var channelLookup = new Dictionary(); foreach (var group in currentTest.Groups) { var channels = currentTest.ChannelsForGroup[group]; foreach (var channel in channels) { if (channel.IsBlank() || channel.IsDisabled || !channel.SensorValid || !channel.HardwareValid) { continue; } channelLookup[channel.Id] = channel; } } var hardwareLookup = currentTest.GetHardware().ToDictionary(h => h.DASId); foreach (var cc in currentTest.CalculatedChannels) { if (cc.SupportsRealtime) { string serialNumber = null; //verify all channels are on the same DAS foreach (var id in cc.InputChannelIds) { //29782 If all Input Channels have been removed from a Calculated Channel, //the channelid may be "", so don't cause an Exception if it is. if (Int64.TryParse(id, out long lid)) { if (channelLookup.ContainsKey(lid)) { var channel = channelLookup[lid]; if (hardwareLookup.ContainsKey(channel.DASId)) { if (string.IsNullOrWhiteSpace(serialNumber)) { serialNumber = hardwareLookup[channel.DASId].SerialNumber; } else if (serialNumber != hardwareLookup[channel.DASId].SerialNumber) { errors.Add(string.Format(StringResources.TestTemplate_MultipleDAS, cc.Name)); break; } } } } else { //29782 cc.InputChannelIds.Length may be > 0, but one or more may //be an empty string, so display this error because it won't get displayed below. var err = string.Format(StringResources.EditTestSetupCC_NoInputChannels, cc.Name); if (!errors.Contains(err)) { errors.Add(err); } valid = false; continue; } } } double sampleRate = double.NaN; foreach (var channelid in cc.InputChannelIds) { //29782 If all Input Channels have been removed from a Calculated Channel, //the channelid may be "", so don't cause an Exception if it is. if (Int64.TryParse(channelid, out long lid)) { if (channelLookup.ContainsKey(lid)) { var channel = channelLookup[lid]; if (hardwareLookup.ContainsKey(channel.DASId)) { var h = hardwareLookup[channel.DASId]; var hardwareRate = currentTest.GetSampleRateForHardware(h); if (double.IsNaN(sampleRate)) { sampleRate = hardwareRate; } else if (sampleRate != hardwareRate) { errors.Add(string.Format(StringResources.TestTemplate_CCMultipleSampleRates, cc.Name)); break; } } } } else { //29782 cc.InputChannelIds.Length may be > 0, but one or more may //be an empty string, so display this error because it won't get displayed below. var err = string.Format(StringResources.EditTestSetupCC_NoInputChannels, cc.Name); if (!errors.Contains(err)) { errors.Add(err); } valid = false; continue; } } if (cc.InputChannelIds.Length < 1) { if (!string.IsNullOrWhiteSpace(cc.Name)) {//don't bother warning if name ="", it's caught by a different warning right below and this one will be confusing var err = string.Format(StringResources.EditTestSetupCC_NoInputChannels, cc.Name); if (!errors.Contains(err)) { errors.Add(err); } valid = false; continue; } } if (string.IsNullOrWhiteSpace(cc.Name)) { var err = StringResources.EditTestSetupCC_NameRequired; if (!errors.Contains(err)) { errors.Add(err); } valid = false; continue; } if (names.Contains(cc.Name)) { if (!duplicates.Contains(cc.Name)) { duplicates.Add(cc.Name); } } names.Add(cc.Name); if (cc.Name.Length >= 250) { var err = StringResources.EditTestSetupCC_NameLength; if (!errors.Contains(err)) { errors.Add(err); } valid = false; continue; } switch (cc.Operation) { case Operations.IRTRACC3D: case Operations.IRTRACC3D_ABDOMEN: case Operations.IRTRACC3D_LOWERTHORAX: if (cc.InputChannelIds.Length < 3) { valid = false; if (!errors.Contains(StringResources.All3DIRTRACCChannelsRequired)) { errors.Add(StringResources.All3DIRTRACCChannelsRequired); } } var usedIds = new HashSet(); foreach (var input in cc.InputChannelIds) { if (usedIds.Contains(input)) { valid = false; errors.Add(StringResources.RotationalPotsMustBeDifferent); } usedIds.Add(input); } break; case Operations.Resultant: case Operations.AVERAGE: case Operations.SUM: { CheckUnits(cc, ref errors, channelLookup, currentTest, ref valid); } break; case Operations.HIC: { CheckHIC(cc, ref errors, channelLookup, hardwareLookup, currentTest, ref valid); } break; } } if (duplicates.Any()) { if (!errors.Contains(StringResources.TestTemplate_CalculatedChannelDuplicateNames)) { errors.Add(StringResources.TestTemplate_CalculatedChannelDuplicateNames); valid = false; } } return valid; } /// /// examines HIC to see if the setup is valid /// /// /// /// /// /// /// private static void CheckHIC(CalculatedValueClass cc, ref List errors, IReadOnlyDictionary channelLookup, IReadOnlyDictionary hardwareLookup, TestTemplate currentTest, ref bool valid) { //must have 3 channels //all channels must be "g" or msec //all channels must have a hardware channel assigned? if (3 != cc.InputChannelIds.Length) { errors.Add(string.Format(StringResources.TestTemplate_InvalidChannelsHIC, cc.Name)); return; } CheckHICChannel(cc.InputChannelIds[0], channelLookup, ref errors, cc.Name, StringResources.TestTemplate_InvalidAccelerationXHIC); CheckHICChannel(cc.InputChannelIds[1], channelLookup, ref errors, cc.Name, StringResources.TestTemplate_InvalidAccelerationYHIC); CheckHICChannel(cc.InputChannelIds[2], channelLookup, ref errors, cc.Name, StringResources.TestTemplate_InvalidAccelerationZHIC); } /// /// examines whether the HIC channel is valid or not /// /// /// /// /// /// private static void CheckHICChannel(string channelId, IReadOnlyDictionary channelLookup, ref List errors, string ccName, string channelErrorText) { if (long.TryParse(channelId, out var id)) { if (!channelLookup.ContainsKey(id)) { errors.Add(string.Format(channelErrorText, ccName)); return; } var channel = channelLookup[id]; var units = channel.Units.ToLower(); if (!Constants.ACCELERATION_UNITS.Contains(units)) { errors.Add(string.Format(StringResources.TestTemplate_InvalidUnitsHIC, ccName, channel.ToString())); } } else { errors.Add(string.Format(channelErrorText, ccName)); return; } } /// /// checks that the units match for all input channels, if not records an error message and sets valid to false /// /// /// /// /// /// private static void CheckUnits(CalculatedValueClass cc, ref List errors, IDictionary channelLookup, TestTemplate currentTest, ref bool valid) { var unit = string.Empty; foreach (var ccid in cc.InputChannelIds) { if (string.IsNullOrWhiteSpace(ccid)) { continue; } if (!long.TryParse(ccid, out var l)) { var msg = string.Format(StringResources.CalculatedChannel_InvalidChannel, cc.Name); if (!errors.Contains(msg)) { errors.Add(msg); } continue; } if (channelLookup.ContainsKey(l)) { var sensor = currentTest.GetSensor(channelLookup[l]); if (null == sensor) continue; var sc = SensorCalibrationList.GetLatestCalibrationBySerialNumber(sensor); if (null == sc) { continue; } if (string.IsNullOrEmpty(unit)) { unit = sc.Records.Records[0].EngineeringUnits; } if (unit != sc.Records.Records[0].EngineeringUnits) { var error = string.Format( StringResources.EditTestSetupCC_UnitsDontMatch, cc.Name); if (!errors.Contains(error)) { valid = false; errors.Add(error); } //it's an error but not fatal .. just warn break; } } else { valid = false; var msg = string.Format(StringResources.CalculatedChannel_InvalidChannel, cc.Name); if (!errors.Contains(msg)) { errors.Add(msg); } } } } public static bool ValidateExports(ref List errors, TestTemplate currentTest) { if (!currentTest.ViewExport || currentTest.ExportFormats != SupportedExportFormatBitFlags.none) return true; errors.Add(StringResources.TestTEmplate_NoExportFormatsSelected); return false; } 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(StringResources.TestTestTemplate_InvalidUploadPath); return false; } private static bool ValidateAutoArmAllowability(ref List errors, TestTemplate currentTest) { //17577 AutoArm Remove Auto-Arm from config file //if (!currentTest.DoAutoArm /*|| Properties.Settings.Default.AllowAutoArm*/) return true; //always allowed now //errors.Add(StringResources.EditTestSetupInfo_AutoArmNotAllowed); //return false; return true; } private static bool ValidateStreamingAllowability(ref List errors, TestTemplate currentTest) { if (!currentTest.DoStreaming || Defaults.GetUserSettingValueBool(ApplicationProperties.CurrentView.Id, PropertyEnums.PropertyIds.AllowStreamingModes)) return true; errors.Add(StringResources.EditTestSetupInfo_StreamingNotAllowed); return false; } private static bool ValidateStreamingSupported(ref List errors, TestTemplate currentTest) { if (!currentTest.DoStreaming) return true; //30429 Invalidate/fail sooner than Arm step if DAS doesn't have streaming capability/channel (TSR AIR may or may not) //If using an older database, streamingChannelsForDAS may be non-null, but we'll catch 'em in //the Hardware step of Run Test when we read the DISABLE_STREAMING_FEATURE system attribute. var allChannels = currentTest.GetChannels(); var allDasSupportStreaming = true; foreach (var das in currentTest.GetHardware()) { if (das.DASTypeEnum == HardwareTypes.TSR_AIR || das.DASTypeEnum == HardwareTypes.TSR_AIR_RevB) { //var streamingChannelsForDAS = allChannels.Where(ch => ch.DASId == das.DASId && ch.IsStreamOut); var streamingChannelsForDAS = das.Channels.Where(ch => ch.IsStreamOut); if (streamingChannelsForDAS == null || !streamingChannelsForDAS.Any()) { //The Test Setup wants to stream, but this TSR AIR doesn't support it errors.Add(string.Format(StringResources.EditTestSetupInfo_StreamingNotAvailableOnDAS, das.SerialNumber, Environment.NewLine)); allDasSupportStreaming = false; } } } return allDasSupportStreaming; } /// /// This function returns a warning if the value in System Settings | Sample packet size control that corresponds to the DAS sample rate is invalid. /// The value is valid if: /// 1. Delta time between packets is less than or equal to 100 ms (CH10 maximum limit of 0.100 seconds). /// 2. Delta time between packets is greater than or equal to 2 ms (0.002 for S6A & S6ABR) or 4 ms (0.004 for TSR AIR) seconds. /// 3. Packet size + 42 < 1480. /// /// /// /// private static void ValidateSamplesPerPacket(ref List warnings, TestTemplate currentTest) { const double minDeltaS6A = 0.002; const double minDeltaTSRAIR = 0.004; const int S6AChannels = 6; //SLICE6Air will always stream 6 channels, plus any in-use UART channels (if FW has Protocol Version 53) const int AllS6ABRChannels = 6; //SLICE6Air-Br will always stream all 6 channels if (currentTest.DoStreaming) { //43623 We assume there is exactly one StreamOut channel per DAS var streamOutChannels = currentTest.GetChannels().Where(ch => ch.IsStreamOut && ch.HardwareChannel != null).ToList(); foreach (var streamOutChannel in streamOutChannels) { var numChannelsToStream = 0; var parentDAS = streamOutChannel.HardwareChannel.GetParentDAS(); switch (parentDAS.DASTypeEnum) { case HardwareTypes.SLICE6_AIR: ValidateDelta(ref warnings, currentTest, parentDAS.SerialNumber, minDeltaS6A); //43623 When support for streaming UART channels is available, only S6A (not S6A-BR) will do so, //so consider the presence/absence of UART channels when setting numChannels for S6A. var numUARTChannelsToStream = 0; if (SLICE6AIR.IsUARTChannelStreamingSupported(parentDAS.ProtocolVersion)) { numUARTChannelsToStream = currentTest.GetChannels().Count(ch => ch.IsUart && ch.HardwareChannel != null && ch.HardwareChannel.GetParentDAS().SerialNumber == parentDAS.SerialNumber); } numChannelsToStream = S6AChannels + numUARTChannelsToStream; break; case HardwareTypes.SLICE6_AIR_BR: ValidateDelta(ref warnings, currentTest, parentDAS.SerialNumber, minDeltaS6A); numChannelsToStream = AllS6ABRChannels; break; case HardwareTypes.TSR_AIR: case HardwareTypes.TSR_AIR_RevB: ValidateDelta(ref warnings, currentTest, parentDAS.SerialNumber, minDeltaTSRAIR); //43623 TSR AIRs can have 3, 6, 9, or 11 channels - UART channels do not stream (yet) numChannelsToStream = currentTest.GetChannels().Count(ch => ch.HardwareChannel != null && !ch.HardwareChannel.IsStreamOut && !ch.HardwareChannel.IsUart && ch.HardwareChannel.GetParentDAS().SerialNumber == parentDAS.SerialNumber); break; case HardwareTypes.SLICE6_AIR_TC: ValidateDelta(ref warnings, currentTest, parentDAS.SerialNumber, minDeltaS6A); numChannelsToStream = currentTest.GetChannels().Count(ch => ch.HardwareChannel != null && !ch.HardwareChannel.IsStreamOut && ch.HardwareChannel.GetParentDAS().SerialNumber == parentDAS.SerialNumber); break; default: if (!warnings.Contains(StringResources.EditTestSetupInfo_PacketDeltaNotValidated)) { warnings.Add(string.Format(StringResources.EditTestSetupInfo_PacketDeltaNotValidated, parentDAS.DASTypeEnum)); } break; } ValidatePacketSize(ref warnings, streamOutChannel.StreamOutUDPProfile, numChannelsToStream, currentTest.DASSamplesPerPacketList[parentDAS.SerialNumber]); } } } /// /// This function returns a warning if the time between packets is invalid. /// /// Use ADC samples per packet divided by sample rate to calculate the time between packets (delta). /// /// The delta is valid if it is greater than or equal to the minimum for the DAS type (0.002 seconds for S6A and S6ABR, /// and 0.004 seconds for TSR AIR) and less than or equal to the maximum 0.100 seconds. /// /// /// /// /// private static void ValidateDelta(ref List warnings, TestTemplate currentTest, string dasSerialNumber, double minDelta) { var delta = currentTest.DASSamplesPerPacketList[dasSerialNumber] / currentTest.DASSampleRateList[dasSerialNumber]; var sb = new StringBuilder($"Packet delta is {delta}"); if ((delta < minDelta) && (!warnings.Contains(StringResources.EditTestSetupInfo_StreamingPacketsTooSmall))) { sb.Append($"; which is less than the minimum ({minDelta})"); warnings.Add(StringResources.EditTestSetupInfo_StreamingPacketsTooSmall); } else if ((delta > 0.100) && (!warnings.Contains(StringResources.EditTestSetupInfo_StreamingPacketsTooLarge))) { sb.Append($"; which is greater than the maximum (0.100)"); warnings.Add(StringResources.EditTestSetupInfo_StreamingPacketsTooLarge); } APILogger.Log(sb); } /// /// This function returns a warning if the size of a packet exceeds the /// maximum allowed (1480), or if an unrecognized stream profile is passed in. /// /// /// /// /// private static void ValidatePacketSize(ref List warnings, UDPStreamProfile streamProfile, int numChannelsToStream, int adcSamplesPerPacket) { var udpStreamProfilePacket = new UDPStreamProfilePacket(); var sb = new StringBuilder($"Packet size is "); //Modify any field in the class that is not the default value var packetSize = 0; switch (streamProfile) { case UDPStreamProfile.CH10_ANALOG: packetSize = Calculate_CH10_ANALOG_Size(numChannelsToStream, adcSamplesPerPacket, udpStreamProfilePacket); break; case UDPStreamProfile.CH10_PCM128_MM: packetSize = Calculate_CH10_PCM128_MM_Size(numChannelsToStream, adcSamplesPerPacket, udpStreamProfilePacket); break; case UDPStreamProfile.CH10_ANALOG_2HDR: packetSize = Calculate_CH10_ANALOG_2HDR_Size(numChannelsToStream, adcSamplesPerPacket, udpStreamProfilePacket); break; case UDPStreamProfile.CH10_PCM_128BIT_2HDR: packetSize = Calculate_CH10_PCM_128BIT_2HDR_Size(numChannelsToStream, adcSamplesPerPacket, udpStreamProfilePacket); break; case UDPStreamProfile.TMNS_PCM_STANDARD: packetSize = Calculate_TMNS_PCM_STANDARD_Size(numChannelsToStream, adcSamplesPerPacket, udpStreamProfilePacket); break; case UDPStreamProfile.TMNS_PCM_SUPERCOM: packetSize = Calculate_TMNS_PCM_SUPERCOM_Size(numChannelsToStream, adcSamplesPerPacket); break; case UDPStreamProfile.IENA_PTYPE_STREAM: packetSize = Calculate_IENA_PTYPE_STREAM_Size(numChannelsToStream, adcSamplesPerPacket, udpStreamProfilePacket); break; case UDPStreamProfile.CH10_MANUAL_CONFIG: packetSize = Calculate_CH10_MANUAL_CONFIG_Size(numChannelsToStream, adcSamplesPerPacket); break; case UDPStreamProfile.UART_STREAM: packetSize = Calculate_UART_STREAM_Size(numChannelsToStream, adcSamplesPerPacket); break; default: if (!warnings.Contains(StringResources.EditTestSetupInfo_PacketSizeNotValidated)) { sb.Append($"unknown because of invalid Stream Profile ({streamProfile})"); warnings.Add(string.Format(StringResources.EditTestSetupInfo_PacketSizeNotValidated, streamProfile)); APILogger.Log(sb); } return; //Don't break here, just return without calling ValidateStreamProfilePacketSize } ValidateStreamProfilePacketSize(packetSize, ref sb, ref warnings); APILogger.Log(sb); } /// /// Calculates the size of a CH10_ANALOG packet /// /// /// /// /// private static int Calculate_CH10_ANALOG_Size(int numChannelsToStream, int adcSamplesPerPacket, UDPStreamProfilePacket udpStreamProfilePacket) { var packetSize = 0; udpStreamProfilePacket.SecondTimeHeader = 0; packetSize = CalculateStreamProfilePacketSize(numChannelsToStream, adcSamplesPerPacket, udpStreamProfilePacket); return packetSize; } /// /// Calculates the size of a CH10_PCM128_MM packet /// /// /// /// /// private static int Calculate_CH10_PCM128_MM_Size(int numChannelsToStream, int adcSamplesPerPacket, UDPStreamProfilePacket udpStreamProfilePacket) { var packetSize = 0; udpStreamProfilePacket.PCMChannelSpecificDataWord = 4; packetSize = CalculateStreamProfilePacketSize(numChannelsToStream, adcSamplesPerPacket, udpStreamProfilePacket); return packetSize; } /// /// Calculates the size of a CH10_ANALOG_2HDR packet /// /// /// /// /// private static int Calculate_CH10_ANALOG_2HDR_Size(int numChannelsToStream, int adcSamplesPerPacket, UDPStreamProfilePacket udpStreamProfilePacket) { var packetSize = 0; udpStreamProfilePacket.SecondTimeHeader = 12; packetSize = CalculateStreamProfilePacketSize(numChannelsToStream, adcSamplesPerPacket, udpStreamProfilePacket); return packetSize; } /// /// Calculates the size of a CH10_PCM_128BIT_2HDR packet /// /// /// /// /// private static int Calculate_CH10_PCM_128BIT_2HDR_Size(int numChannelsToStream, int adcSamplesPerPacket, UDPStreamProfilePacket udpStreamProfilePacket) { var packetSize = 0; udpStreamProfilePacket.SecondTimeHeader = 12; udpStreamProfilePacket.PCMChannelSpecificDataWord = 4; packetSize = CalculateStreamProfilePacketSize(numChannelsToStream, adcSamplesPerPacket, udpStreamProfilePacket); return packetSize; } /// /// Calculates the size of a TMNS_PCM_STANDARD packet /// /// /// /// /// private static int Calculate_TMNS_PCM_STANDARD_Size(int numChannelsToStream, int adcSamplesPerPacket, UDPStreamProfilePacket udpStreamProfilePacket) { var packetSize = 0; udpStreamProfilePacket.SecondTimeHeader = 12; udpStreamProfilePacket.ChannelSpecificDataWord = 4; udpStreamProfilePacket.PCMChannelSpecificDataWord = 4; udpStreamProfilePacket.Id = 4; udpStreamProfilePacket.SampleTime = 12; packetSize = CalculateStreamProfilePacketSize(numChannelsToStream, adcSamplesPerPacket, udpStreamProfilePacket); return packetSize; } /// /// Calculates the size of a TMNS_PCM_SUPERCOM packet /// /// /// /// private static int Calculate_TMNS_PCM_SUPERCOM_Size(int numChannelsToStream, int adcSamplesPerPacket) { var packetSize = 0; const int packetHeader = 32; const int payloadFactor = 2; const int packageHeader = 12; const int frameSync = 4; const int sfidWord = 2; const int stsWord = 2; var payLoad = payloadFactor * numChannelsToStream; var packagePayload = frameSync + sfidWord + stsWord + (payLoad * 4); packetSize = packetHeader + ((packageHeader + packagePayload) * (adcSamplesPerPacket / 4)); return packetSize; } /// /// Calculates the size of a IENA_PTYPE_STREAM packet /// /// /// /// /// private static int Calculate_IENA_PTYPE_STREAM_Size(int numChannelsToStream, int adcSamplesPerPacket, UDPStreamProfilePacket udpStreamProfilePacket) { var packetSize = 0; udpStreamProfilePacket.TransHeader = 14; udpStreamProfilePacket.TimeHeader = 0; udpStreamProfilePacket.SecondTimeHeader = 0; udpStreamProfilePacket.ChannelSpecificDataWord = 0; udpStreamProfilePacket.PCMChannelSpecificDataWord = 0; udpStreamProfilePacket.Trailer = 2; packetSize = CalculateStreamProfilePacketSize(numChannelsToStream, adcSamplesPerPacket, udpStreamProfilePacket); return packetSize; } /// /// Calculates the size of a CH10_MANUAL_CONFIG packet /// /// /// /// private static int Calculate_CH10_MANUAL_CONFIG_Size(int numChannelsToStream, int adcSamplesPerPacket) { var packetSize = 0; const int packetHeader = 32; const int payloadFactor = 2; var payload = payloadFactor * numChannelsToStream; packetSize = packetHeader + (payload * adcSamplesPerPacket); return packetSize; } /// /// Calculates the size of a UART_STREAM packet /// /// /// /// private static int Calculate_UART_STREAM_Size(int numChannelsToStream, int adcSamplesPerPacket) { var packetSize = 0; const int packetHeader = 14; const int payloadFactor = 2; const int crc = 2; var payload = payloadFactor * numChannelsToStream; packetSize = packetHeader + crc + (payload * adcSamplesPerPacket); return packetSize; } /// /// This function calculates the size of a packet, based on the UDPStreamProfilePacket class /// maximum allowed (1480). /// /// /// /// private static int CalculateStreamProfilePacketSize(int numChannelsToStream, int adcSamplesPerPacket, UDPStreamProfilePacket udpStreamProfilePacket) { var packetSize = 0; packetSize = udpStreamProfilePacket.TransHeader + udpStreamProfilePacket.TimeHeader + udpStreamProfilePacket.SecondTimeHeader + udpStreamProfilePacket.ChannelSpecificDataWord + ((udpStreamProfilePacket.PCMChannelSpecificDataWord + udpStreamProfilePacket.Id + (udpStreamProfilePacket.PayloadFactor * numChannelsToStream)) * adcSamplesPerPacket) + (udpStreamProfilePacket.SampleTime * (adcSamplesPerPacket - 1)) + udpStreamProfilePacket.Trailer; return packetSize; } /// /// Determines whether a packet will be fragmented /// /// /// /// private static void ValidateStreamProfilePacketSize(int packetSize, ref StringBuilder sb, ref List warnings) { const int additionalOverhead = 42; const int maxPacketSize = 1480; if (((packetSize + additionalOverhead) >= maxPacketSize) && !warnings.Contains(StringResources.EditTestSetupInfo_StreamingPacketsWillBeFragmented)) { sb.Append($"; which is greater than the maximum ({maxPacketSize})"); warnings.Add(StringResources.EditTestSetupInfo_StreamingPacketsWillBeFragmented); } } private static bool ValidateStreamingOutputAddresses(ref List warnings, TestTemplate currentTest) { var streamOutChannels = currentTest.GetChannels().Where(ch => ch.IsStreamOut).ToList(); if (streamOutChannels.GroupBy(ch => ch.StreamOutUDPAddress.ToLower()).Select(ch => ch.First()).Count() != streamOutChannels.Count) { //we have multiple udp outs on the same address //30048 Allow multiple udp outs, but display a warning warnings.Add(StringResources.TestTemplate_UDPAddressesNotUnique); } return true; } private static bool ValidateStreamingProfiles(ref List errors, TestTemplate currentTest) { var result = false; try { result = true; var availableProfiles = StreamOutputRecord.AvailableUDPStreamProfiles(DbOperations.GetConnectionDbVersion(), StreamOutputSettingDefaults.GetStreamOutputSettingsDefault(ApplicationProperties.CurrentUser.Id).UseAdvancedUDPStreamProfiles); var streamOutChannels = currentTest.GetChannels().Where(ch => ch.IsStreamOut).ToList(); foreach (var ch in streamOutChannels) { if ((ch.HardwareChannel != null) && !((DASHardware)ch.HardwareChannel.GetParentDAS()).SupportsStreamingProfile(ch.StreamOutUDPProfile)) { errors.Add(string.Format(StringResources.TestTemplate_UnsupportedStreamingProfile, EnumDescriptionTypeConverter.GetEnumDescription(ch.StreamOutUDPProfile), ch.HardwareChannel.GetParentDAS().ToString())); result = false; } if (!availableProfiles.Contains(ch.StreamOutUDPProfile)) { if (!errors.Contains(StringResources.EditStreamOutputControl_EnableAdvancedUDPProfiles)) { errors.Add(StringResources.EditStreamOutputControl_EnableAdvancedUDPProfiles); } var profileError = string.Format(StringResources.EditStreamOutputControl_InvalidUDPProfile, DTS.Common.Strings.Strings.ResourceManager.GetString(ch.StreamOutUDPProfile.GetEnumDescription())); if (!errors.Contains(profileError)) { errors.Add(profileError); } result = false; } ValidateStreamingProfiles(ref errors, ch.HardwareChannel?.GetParentDAS() ?? null); } } catch (Exception ex) { //This is a model of how other validation functions can more gracefully avoid a misleading "Failure to update lock" error, //and at the same time, give the user a clue as to where the validation failed. var errorString = string.Format(StringResources.TestSetupValidationError, "ValidateStreamingProfiles"); var exceptionString = $"{errorString}: {ex.Message}"; APILogger.Log(exceptionString); errors.Add(errorString); result = false; } return result; } public static void ValidateStreamingProfiles(ref List errors, IDASHardware hardware) { if (null == hardware) { return; } var filterProfile = SerializedSettings.GetDefaultDSP(); var dasType = hardware.DASTypeEnum.ToString(); //look for matching dastype or empty string dastype, compare protocol version to hardware protocol version var matchDasType = Array.Find(filterProfile.Restrictions, x => string.IsNullOrEmpty(x.DASType) || 0 == string.Compare(x.DASType, dasType, true)); if (null == matchDasType) { errors.Add(string.Format(StringResources.DASDoesntSupportFilterProfile, hardware.SerialNumber, filterProfile.DisplayString)); } else if (matchDasType.ProtocolVersion > hardware.ProtocolVersion) { errors.Add(string.Format(StringResources.DASProtocolDoesntSupportFilterProfile, hardware.SerialNumber, filterProfile.DisplayString)); } } public static bool ValidateCommonStatusLine(ref List errors, TestTemplate currentTest) { var sampleRatesAreMixed = currentTest.HasMultipleSampleRates(); if (!sampleRatesAreMixed || !currentTest.CommonLine) return true; var hardware = currentTest.GetHardware(); //http://manuscript.dts.local/f/cases/44058/Unable-to-arm-multiple-das-with-different-sample-rates-due-to-Common-Status-Line-not-supported-error //per EF if all devices are TSR AIR don't warn about this if (Array.Exists(hardware, h => !h.IsTSRAIR())) { //This needs to be an error here so that the Test Setup will be saved in an incomplete state. errors.Add(StringResources.EditObjectPageError_CommonStatusLineWithMultipleSampleRates); return false; } return true; } public static bool ValidateSampleRates(ref List errors, TestTemplate currentTest) { foreach (var comparableDas in currentTest.GetNonDistributorHardware()) { var comparableSampleRate = currentTest.DASSampleRateList[comparableDas.SerialNumber]; foreach (var das in currentTest.GetNonDistributorHardware()) { if ((currentTest.DASSampleRateList[das.SerialNumber] % comparableSampleRate != 0) && (comparableSampleRate % currentTest.DASSampleRateList[das.SerialNumber] != 0)) { errors.Add(StringResources.EditTestSetupPage_Error_DASSampleRatesNotEvenlyDivisible); return false; } } } return true; } private static bool AllowsZeroAAF(DASHardware hw) { switch (hw.DASTypeEnum) { case HardwareTypes.DIM: case HardwareTypes.G5INDUMMY: case HardwareTypes.G5VDS: case HardwareTypes.SIM: case HardwareTypes.TDAS_Pro_Rack: case HardwareTypes.TDAS_LabRack: case HardwareTypes.TOM: return true; default: return false; } } public static bool ValidateAAFilterRates(ref List errors, ref List warnings, TestTemplate currentTest, bool warnWhenRun) { var result = true; var currentTestHardware = currentTest.GetHardware(); foreach (var hw in currentTestHardware) { if (!hw.IsTSRAIR() && CaresAboutAAF(hw)) { if ((currentTest.DASAAFRateList[hw.SerialNumber] < 0) || //Allow TDAS hardware to run with 0 AAF ((currentTest.DASAAFRateList[hw.SerialNumber] == 0) && !AllowsZeroAAF(hw)) || (currentTest.DASAAFRateList[hw.SerialNumber] > Convert.ToDouble(hw.MaxAAFRate))) { //FB15759: the AAF we edited in is > what would be calculated, mark it error. errors.Add(String.Format(StringResources.EditTestSetupPage_AAFilterRateOutOfRange, hw.ToString(), hw.MaxAAFRate.Replace(".00", ""), Environment.NewLine)); result = false; } //Don't allow an AAF rate higher than the Sample rate if (currentTest.DASAAFRateList[hw.SerialNumber] > currentTest.DASSampleRateList[hw.SerialNumber]) { if (warnWhenRun) { errors.Add(string.Format(StringResources.EditTestSetupPage_AAFilterRateGreaterThanSampleRate, hw.ToString(), currentTest.DASAAFRateList[hw.SerialNumber].ToString(), currentTest.DASSampleRateList[hw.SerialNumber].ToString(), Environment.NewLine)); } else { warnings.Add(String.Format(StringResources.EditTestSetupPage_AAFilterRateGreaterThanSampleRate, hw.ToString(), currentTest.DASAAFRateList[hw.SerialNumber].ToString(), currentTest.DASSampleRateList[hw.SerialNumber].ToString(), Environment.NewLine)); } result = false; } } } return result; } /// /// returns true if the unit cares about AAF rates /// 18029 Error about AAF during diagnostics and in edit test setup /// private static bool CaresAboutAAF(DASHardware dh) { return dh.CareAboutSampleRate; } private static void ValidateHardwareRecordingModes(RecordingModes mode, DASHardware[] hardware, ref List errors, bool streaming) { foreach (var h in hardware) { //29609 At this point, test.RecordingMode may not yet have been set to the proper streaming value, so check test.DoStreaming also if (h.IsNonDistributorHardware && !h.SupportsRecordingMode(mode) && !streaming) { errors.Add(string.Format(StringResources.TestTemplate_UnsupportedRecordingMode, EnumDescriptionTypeConverter.GetEnumDescription(mode), h.ToString())); } } } private static void ValidateUARTRecordingMode(RecordingModes mode, ClockSyncProfile masterProfile, ClockSyncProfile slaveProfile, IGroupChannel[] channels, ref List errors, DASHardware[] hardware, string[] masterHardware, bool streaming, TestSetupDefaults settings) { //UART recording mode checks if (RecordingModeExtensions.IsAUartMode(mode)) { if (!Array.Exists(channels, gc => gc.IsUart)) { //is uart mode, has no uart channels errors.Add(StringResources.TestTemplate_UARTModeNoChannels); } else if (!Array.Exists(hardware, h => h.SupportsRecordingMode(mode, true))) { errors.Add(StringResources.TestTemplate_UARTModeNoDAS); } } //30479 don't warn if uart channel's being used for GPS clock sync var uartChannelsNotSync = channels.Where(gc => gc.IsUart && !(masterProfile.ToString().Contains("GPS") && masterHardware.Contains(gc.HardwareChannel.GetParentDAS().SerialNumber)) && !slaveProfile.ToString().Contains("GPS")).ToList(); var uartStreamOutChannels = channels.Where(gc => gc.IsStreamOut && gc.StreamOutUDPProfile == UDPStreamProfile.UART_STREAM).ToList(); if (!RecordingModeExtensions.IsAUartMode(mode) && uartChannelsNotSync.Any() && !RecordingModeExtensions.TestWillBeStreaming(mode, streaming)) { //not a uart mode has uart channels that aren't being used for gps clock sync errors.Add(StringResources.TestTemplate_UARTChannelsNoMode); if (!settings?.AllowUARTRecordingModes ?? true) { //"allow uart modes" not enabled. tell user to do so errors.Add(StringResources.TestTemplate_UARTModesNeedEnabling); } } //uart channel configured, but not used for gps nor adc-to-uart else if (RecordingModeExtensions.TestWillBeStreaming(mode, streaming) && uartChannelsNotSync.Exists(uart => !uartStreamOutChannels.Exists(usoc => uart.HardwareChannel.GetParentDAS().SerialNumber == usoc.HardwareChannel.GetParentDAS().SerialNumber))) { //streaming and a uart streamout channel doesn't have a corresponding uart config channel errors.Add(StringResources.TestTemplate_UARTChannelsNoStream); } } private static void ValidateStreamingModes(RecordingModes mode, bool streaming, IGroupChannel[] channels, ref List errors, DASHardware[] hardware, TestSetupDefaults settings) { //Streaming recording mode checks //29609 At this point, test.RecordingMode may not yet have been set to the proper streaming value, so check test.DoStreaming also if (RecordingModeExtensions.TestWillBeStreaming(mode, streaming) && !Array.Exists(channels, gc => gc.IsStreamOut)) { //is streaming mode, but no settings channels errors.Add(StringResources.TestTemplate_StreamOutModeNoChannels); } //29609 At this point, test.RecordingMode may not yet have been set to the proper streaming value, so check test.DoStreaming also if (RecordingModeExtensions.TestWillBeStreaming(mode, streaming) && hardware.Where(das => Array.Exists(das.Channels, ch => ch.IsStreamOut)).Any(das => !Array.Exists(channels, gc => null != gc.HardwareChannel && gc.HardwareChannel.IsStreamOut && gc.HardwareChannel.GetParentDAS().SerialNumber == das.SerialNumber))) { //all included das with channels in test are required to have a stream out setting when we're in stream out mode errors.Add(StringResources.TestTemplate_StreamOutModeDASWithNoSettings); } //30075 validate uart settings paired with streamout if streamout profile is adc-to-uart if (RecordingModeExtensions.TestWillBeStreaming(mode, streaming) && hardware.Where(das => Array.Exists(das.Channels, ch => ch.IsStreamOut)).Any(das => Array.Exists(channels, gc => null != gc.HardwareChannel && gc.HardwareChannel.IsStreamOut && gc.StreamOutUDPProfile == UDPStreamProfile.UART_STREAM && gc.HardwareChannel.GetParentDAS().SerialNumber == das.SerialNumber) && !Array.Exists(channels, gc => null != gc.HardwareChannel && gc.HardwareChannel.IsUart && gc.HardwareChannel.GetParentDAS().SerialNumber == das.SerialNumber))) { //ADC-to-UART is chosen as the streamout profile, but no corresponding UART settings included errors.Add(StringResources.TestTemplate_StreamOutUARTProfileNoUARTSettings); } //29609 At this point, test.RecordingMode may not yet have been set to the proper streaming value, so check test.DoStreaming also if (!RecordingModeExtensions.TestWillBeStreaming(mode, streaming) && Array.Exists(channels, gc => gc.IsStreamOut)) { //not a stream mode, but has settings channels errors.Add(StringResources.TestTemplate_StreamOutChannelsNoMode); if (!settings?.AllowStreamingModes ?? true) { //"allow streaming modes" not enabled. tell user to do so errors.Add(StringResources.TestTemplate_StreamOutModesNeedEnabling); } } //Streaming in HW check if (hardware.Where(das => Array.Exists(das.Channels, ch => ch.IsStreamIn)).Any(das => !Array.Exists(channels, gc => null != gc.HardwareChannel && gc.HardwareChannel.IsStreamIn && gc.HardwareChannel.GetParentDAS().SerialNumber == das.SerialNumber))) { //if we include any hardware with streaming in channels those channels must be included in the test errors.Add(StringResources.TestTemplate_StreamInDASWithNoSettings); } } private static void ValidateAllowTSRAirRecordingMode(TestSetupDefaults settings, RecordingModes mode, ref List errors, string recordingModeText, DASHardware[] hardware) { //http://manuscript.dts.local/f/cases/29853/Warn-when-selected-recording-mode-is-not-available //check AllowTSRAirRecordingModes against recording mode //this is following the assumption that das recording modes are already checked above and we just need to check allowTSRAIRrecordingmodes if (RecordingModeExtensions.IsTSRAIROnlyRecordingMode(mode) && !RecordingModeExtensions.IsTSRAirRecordingMode(mode)) { //allow tsr air recording modes is off but this is a TSR AIR only recording mode, warn to turn it on errors.Add(StringResources.TestTemplate_AllowTSRAirRecordingModesNotSet); } } /// /// returns false if there is a fatal error and the test setup should not be allowed to be saved /// returns true if there are no issues preventing saving the test setup /// adds an error if the test setup can be saved but there's an issue preventing running the test /// adds a warning if the test setup can be saved and run, but there is an issue warranting notifying the user /// 18490 stuck in preparing for data collection when you have a SPT attached and a test set for continuous recorder mode /// /// /// /// /// public static bool ValidateRecordingModes(ref List errors, ref List warnings, TestTemplate test) { var hardware = test.GetHardware(); ValidateHardwareRecordingModes(test.RecordingMode, hardware, ref errors, test.DoStreaming); var testChannels = test.GetChannels().ToArray(); var settings = TestSetupDefaults.GetUserSettings(ApplicationProperties.CurrentUser.Id); ValidateUARTRecordingMode(test.RecordingMode, test.ClockSyncProfileMaster, test.ClockSyncProfileSlave, testChannels, ref errors, hardware, test.DASClockMasterList.Where(kvp => true == kvp.Value).Select(kvp => kvp.Key).ToArray(), test.DoStreaming, settings); ValidateStreamingModes(test.RecordingMode, test.DoStreaming, testChannels, ref errors, hardware, settings); ValidateAllowTSRAirRecordingMode(settings, test.RecordingMode, ref errors, test.RecordingModeText, hardware); return true; } public void SetClockSyncInputSlave(OutputClockSource source) { var selectedOutputMode = GetSelectedOutputPTPClockMode(); switch (source) { case OutputClockSource.PTP: ClockSyncProfileSlave = "Auto" == selectedOutputMode ? ClockSyncProfile.Auto_E2E : "1PPS" == selectedOutputMode ? ClockSyncProfile.Slave_E2E_PPS_OUT : ClockSyncProfile.Slave_E2E; break; case OutputClockSource.None: ClockSyncProfileSlave = "1PPS" == selectedOutputMode ? ClockSyncProfile.PPS_OUT : ClockSyncProfile.None; break; } } public void SetClockSyncManualMaster(bool value) { ClockSyncProfileMaster = value ? ClockSyncProfile.Manual : ClockSyncProfile.None; } public void SetClockSyncManualSlave(bool value) { ClockSyncProfileSlave = value ? ClockSyncProfile.Manual : ClockSyncProfile.None; } public InputClockSource GetClockSyncInputSlave() { switch (ClockSyncProfileSlave) { case ClockSyncProfile.Slave_E2E: case ClockSyncProfile.Slave_E2E_PPS_OUT: case ClockSyncProfile.Auto_E2E: return InputClockSource.PTP; default: return InputClockSource.None; } } public InputClockSource GetClockSyncProfileSlave() { return GetClockSyncProfileSlave(ClockSyncProfileSlave); } public static InputClockSource GetClockSyncProfileSlave(ClockSyncProfile clockSyncProfileSlave) { switch (clockSyncProfileSlave) { case ClockSyncProfile.PPS_OUT: case ClockSyncProfile.Slave_E2E_PPS_OUT: return InputClockSource.OnePPS; case ClockSyncProfile.Slave_E2E: return InputClockSource.PTP; default: return InputClockSource.None; } } public void SetClockSyncOutputSlave(OutputClockSource source) { switch (source) { case OutputClockSource.OnePPS: switch (ClockSyncProfileSlave) { case ClockSyncProfile.Slave_E2E: ClockSyncProfileSlave = ClockSyncProfile.Slave_E2E_PPS_OUT; break; case ClockSyncProfile.None: ClockSyncProfileSlave = ClockSyncProfile.PPS_OUT; break; } break; case OutputClockSource.None: switch (ClockSyncProfileSlave) { case ClockSyncProfile.Slave_E2E_PPS_OUT: ClockSyncProfileSlave = ClockSyncProfile.Slave_E2E; break; case ClockSyncProfile.PPS_OUT: ClockSyncProfileSlave = ClockSyncProfile.None; break; } break; } } public string GetSelectedOutputPTPClockMode() { switch (ClockSyncProfileMaster) { case ClockSyncProfile.Master_E2E_IRIG: case ClockSyncProfile.Master_E2E_IRIG_EXT_PPS: case ClockSyncProfile.Master_E2E_GPS: case ClockSyncProfile.Master_E2E_GPS_EXT_PPS: case ClockSyncProfile.Master_E2E_EXT_PPS: case ClockSyncProfile.Master_E2E: case ClockSyncProfile.IRIG_Master_E2E_PPS_OUT: case ClockSyncProfile.Master_E2E_PPS_OUT: return "Master"; case ClockSyncProfile.Slave_E2E: case ClockSyncProfile.Slave_E2E_PPS_OUT: return "Slave"; case ClockSyncProfile.Auto_E2E: return "Auto"; } return "---"; } public InputClockSource GetClockSyncInputMaster() { return GetClockSyncInputMaster(ClockSyncProfileMaster); } public static InputClockSource GetClockSyncInputMaster(ClockSyncProfile clockSyncProfileMaster) { var master = InputClockSource.None; switch (clockSyncProfileMaster) { case ClockSyncProfile.IRIG: case ClockSyncProfile.Master_E2E_IRIG: case ClockSyncProfile.IRIG_PPS_OUT: case ClockSyncProfile.IRIG_Master_E2E_PPS_OUT: master = InputClockSource.IRIG; break; case ClockSyncProfile.IRIG_EXT_PPS: case ClockSyncProfile.IRIG_EXT_PPS_PPS_OUT: case ClockSyncProfile.Master_E2E_IRIG_EXT_PPS: case ClockSyncProfile.IRIG_EXT_PPS_Master_E2E_PPS_OUT: master = InputClockSource.IRIG_OnePPS; break; case ClockSyncProfile.GPS: case ClockSyncProfile.GPS_PPS_OUT: case ClockSyncProfile.Master_E2E_GPS: case ClockSyncProfile.GPS_Master_E2E_PPS_OUT: master = InputClockSource.GPS; break; case ClockSyncProfile.GPS_EXT_PPS: case ClockSyncProfile.GPS_EXT_PPS_PPS_OUT: case ClockSyncProfile.Master_E2E_GPS_EXT_PPS: case ClockSyncProfile.GPS_EXT_PPS_Master_E2E_PPS_OUT: master = InputClockSource.GPS_OnePPS; break; case ClockSyncProfile.EXT_PPS: case ClockSyncProfile.EXT_PPS_PPS_OUT: case ClockSyncProfile.Master_E2E_EXT_PPS: case ClockSyncProfile.EXT_PPS_Master_E2E_PPS_OUT: master = InputClockSource.OnePPS; break; } return master; } public OutputClockSource GetClockSyncOutputMaster() { return GetClockSyncOutputMaster(ClockSyncProfileMaster); } public static OutputClockSource GetClockSyncOutputMaster(ClockSyncProfile clockSyncProfileMaster) { var master = OutputClockSource.None; switch (clockSyncProfileMaster) { case ClockSyncProfile.Master_E2E_IRIG: case ClockSyncProfile.Master_E2E_IRIG_EXT_PPS: case ClockSyncProfile.Master_E2E_GPS: case ClockSyncProfile.Master_E2E_GPS_EXT_PPS: case ClockSyncProfile.Master_E2E_EXT_PPS: case ClockSyncProfile.Master_E2E: case ClockSyncProfile.Auto_E2E: master = OutputClockSource.PTP; break; case ClockSyncProfile.Master_E2E_PPS_OUT: case ClockSyncProfile.IRIG_Master_E2E_PPS_OUT: case ClockSyncProfile.IRIG_EXT_PPS_Master_E2E_PPS_OUT: case ClockSyncProfile.GPS_Master_E2E_PPS_OUT: case ClockSyncProfile.GPS_EXT_PPS_Master_E2E_PPS_OUT: case ClockSyncProfile.EXT_PPS_Master_E2E_PPS_OUT: master = OutputClockSource.PTP_OnePPS; break; case ClockSyncProfile.PPS_OUT: case ClockSyncProfile.IRIG_PPS_OUT: case ClockSyncProfile.IRIG_EXT_PPS_PPS_OUT: case ClockSyncProfile.GPS_PPS_OUT: case ClockSyncProfile.GPS_EXT_PPS_PPS_OUT: case ClockSyncProfile.EXT_PPS_PPS_OUT: master = OutputClockSource.OnePPS; break; } return master; } public void SetClockSyncInputMaster(InputClockSource source) { var clockSyncProfileMaster = ClockSyncProfileMaster; SetClockSyncInputMaster(source, ref clockSyncProfileMaster); ClockSyncProfileMaster = clockSyncProfileMaster; } public static void SetClockSyncInputMaster(InputClockSource source, ref ClockSyncProfile clockSyncProfileMaster) { var outputMaster = GetClockSyncOutputMaster(clockSyncProfileMaster); switch (source) { case InputClockSource.GPS: clockSyncProfileMaster = OutputClockSource.None == outputMaster ? ClockSyncProfile.GPS : OutputClockSource.OnePPS == outputMaster ? ClockSyncProfile.GPS_PPS_OUT : OutputClockSource.PTP_OnePPS == outputMaster ? ClockSyncProfile.GPS_Master_E2E_PPS_OUT : ClockSyncProfile.Master_E2E_GPS; break; case InputClockSource.GPS_OnePPS: clockSyncProfileMaster = OutputClockSource.None == outputMaster ? ClockSyncProfile.GPS_EXT_PPS : OutputClockSource.OnePPS == outputMaster ? ClockSyncProfile.GPS_EXT_PPS_PPS_OUT : OutputClockSource.PTP_OnePPS == outputMaster ? ClockSyncProfile.GPS_EXT_PPS_Master_E2E_PPS_OUT : ClockSyncProfile.Master_E2E_GPS_EXT_PPS; break; case InputClockSource.IRIG: clockSyncProfileMaster = OutputClockSource.None == outputMaster ? ClockSyncProfile.IRIG : OutputClockSource.OnePPS == outputMaster ? ClockSyncProfile.IRIG_PPS_OUT : OutputClockSource.PTP_OnePPS == outputMaster ? ClockSyncProfile.IRIG_Master_E2E_PPS_OUT : ClockSyncProfile.Master_E2E_IRIG; break; case InputClockSource.IRIG_OnePPS: clockSyncProfileMaster = OutputClockSource.None == outputMaster ? ClockSyncProfile.IRIG_EXT_PPS : OutputClockSource.OnePPS == outputMaster ? ClockSyncProfile.IRIG_EXT_PPS_PPS_OUT : OutputClockSource.PTP_OnePPS == outputMaster ? ClockSyncProfile.IRIG_EXT_PPS_Master_E2E_PPS_OUT : ClockSyncProfile.Master_E2E_IRIG_EXT_PPS; break; case InputClockSource.OnePPS: clockSyncProfileMaster = OutputClockSource.None == outputMaster ? ClockSyncProfile.EXT_PPS : OutputClockSource.OnePPS == outputMaster ? ClockSyncProfile.EXT_PPS_PPS_OUT : OutputClockSource.PTP_OnePPS == outputMaster ? ClockSyncProfile.EXT_PPS_Master_E2E_PPS_OUT : ClockSyncProfile.Master_E2E_EXT_PPS; break; default: clockSyncProfileMaster = OutputClockSource.None == outputMaster ? ClockSyncProfile.None : OutputClockSource.OnePPS == outputMaster ? ClockSyncProfile.PPS_OUT : OutputClockSource.PTP_OnePPS == outputMaster ? ClockSyncProfile.Master_E2E_PPS_OUT : ClockSyncProfile.Master_E2E; break; } } public void SetClockSyncOutputMaster(OutputClockSource source) { var clockSyncProfileMaster = ClockSyncProfileMaster; SetClockSyncOutputMaster(source, ref clockSyncProfileMaster); ClockSyncProfileMaster = clockSyncProfileMaster; } public static void SetClockSyncOutputMaster(OutputClockSource source, ref ClockSyncProfile clockSyncProfileMaster) { switch (source) { case OutputClockSource.PTP: switch (clockSyncProfileMaster) { case ClockSyncProfile.None: case ClockSyncProfile.PPS_OUT: case ClockSyncProfile.Master_E2E_PPS_OUT: clockSyncProfileMaster = ClockSyncProfile.Master_E2E; break; case ClockSyncProfile.IRIG: case ClockSyncProfile.IRIG_PPS_OUT: case ClockSyncProfile.IRIG_Master_E2E_PPS_OUT: clockSyncProfileMaster = ClockSyncProfile.Master_E2E_IRIG; break; case ClockSyncProfile.GPS: case ClockSyncProfile.GPS_PPS_OUT: case ClockSyncProfile.GPS_Master_E2E_PPS_OUT: clockSyncProfileMaster = ClockSyncProfile.Master_E2E_GPS; break; case ClockSyncProfile.EXT_PPS: case ClockSyncProfile.EXT_PPS_PPS_OUT: case ClockSyncProfile.EXT_PPS_Master_E2E_PPS_OUT: clockSyncProfileMaster = ClockSyncProfile.Master_E2E_EXT_PPS; break; case ClockSyncProfile.IRIG_EXT_PPS: case ClockSyncProfile.IRIG_EXT_PPS_PPS_OUT: case ClockSyncProfile.IRIG_EXT_PPS_Master_E2E_PPS_OUT: clockSyncProfileMaster = ClockSyncProfile.Master_E2E_IRIG_EXT_PPS; break; case ClockSyncProfile.GPS_EXT_PPS: case ClockSyncProfile.GPS_EXT_PPS_PPS_OUT: case ClockSyncProfile.GPS_EXT_PPS_Master_E2E_PPS_OUT: clockSyncProfileMaster = ClockSyncProfile.Master_E2E_GPS_EXT_PPS; break; } break; case OutputClockSource.PTP_OnePPS: switch (clockSyncProfileMaster) { case ClockSyncProfile.None: case ClockSyncProfile.PPS_OUT: case ClockSyncProfile.Master_E2E: clockSyncProfileMaster = ClockSyncProfile.Master_E2E_PPS_OUT; break; case ClockSyncProfile.IRIG: case ClockSyncProfile.Master_E2E_IRIG: case ClockSyncProfile.IRIG_PPS_OUT: clockSyncProfileMaster = ClockSyncProfile.IRIG_Master_E2E_PPS_OUT; break; case ClockSyncProfile.GPS: case ClockSyncProfile.Master_E2E_GPS: case ClockSyncProfile.GPS_PPS_OUT: clockSyncProfileMaster = ClockSyncProfile.GPS_Master_E2E_PPS_OUT; break; case ClockSyncProfile.EXT_PPS: case ClockSyncProfile.Master_E2E_EXT_PPS: case ClockSyncProfile.EXT_PPS_PPS_OUT: clockSyncProfileMaster = ClockSyncProfile.EXT_PPS_Master_E2E_PPS_OUT; break; case ClockSyncProfile.IRIG_EXT_PPS: case ClockSyncProfile.Master_E2E_IRIG_EXT_PPS: case ClockSyncProfile.IRIG_EXT_PPS_PPS_OUT: clockSyncProfileMaster = ClockSyncProfile.IRIG_EXT_PPS_Master_E2E_PPS_OUT; break; case ClockSyncProfile.GPS_EXT_PPS: case ClockSyncProfile.Master_E2E_GPS_EXT_PPS: case ClockSyncProfile.GPS_EXT_PPS_PPS_OUT: clockSyncProfileMaster = ClockSyncProfile.GPS_EXT_PPS_Master_E2E_PPS_OUT; break; } break; case OutputClockSource.OnePPS: switch (clockSyncProfileMaster) { case ClockSyncProfile.None: case ClockSyncProfile.Master_E2E: case ClockSyncProfile.Master_E2E_PPS_OUT: clockSyncProfileMaster = ClockSyncProfile.PPS_OUT; break; case ClockSyncProfile.IRIG: case ClockSyncProfile.Master_E2E_IRIG: case ClockSyncProfile.IRIG_Master_E2E_PPS_OUT: clockSyncProfileMaster = ClockSyncProfile.IRIG_PPS_OUT; break; case ClockSyncProfile.GPS: case ClockSyncProfile.Master_E2E_GPS: case ClockSyncProfile.GPS_Master_E2E_PPS_OUT: clockSyncProfileMaster = ClockSyncProfile.GPS_PPS_OUT; break; case ClockSyncProfile.EXT_PPS: case ClockSyncProfile.Master_E2E_EXT_PPS: case ClockSyncProfile.EXT_PPS_Master_E2E_PPS_OUT: clockSyncProfileMaster = ClockSyncProfile.EXT_PPS_PPS_OUT; break; case ClockSyncProfile.IRIG_EXT_PPS: case ClockSyncProfile.Master_E2E_IRIG_EXT_PPS: case ClockSyncProfile.IRIG_EXT_PPS_Master_E2E_PPS_OUT: clockSyncProfileMaster = ClockSyncProfile.IRIG_EXT_PPS_PPS_OUT; break; case ClockSyncProfile.GPS_EXT_PPS: case ClockSyncProfile.Master_E2E_GPS_EXT_PPS: case ClockSyncProfile.GPS_EXT_PPS_Master_E2E_PPS_OUT: clockSyncProfileMaster = ClockSyncProfile.GPS_EXT_PPS_PPS_OUT; break; } break; case OutputClockSource.None: switch (clockSyncProfileMaster) { case ClockSyncProfile.Master_E2E_IRIG: case ClockSyncProfile.IRIG_Master_E2E_PPS_OUT: clockSyncProfileMaster = ClockSyncProfile.IRIG; break; case ClockSyncProfile.Master_E2E_GPS: case ClockSyncProfile.GPS_Master_E2E_PPS_OUT: clockSyncProfileMaster = ClockSyncProfile.GPS; break; case ClockSyncProfile.Master_E2E_EXT_PPS: case ClockSyncProfile.EXT_PPS_Master_E2E_PPS_OUT: clockSyncProfileMaster = ClockSyncProfile.EXT_PPS; break; case ClockSyncProfile.Master_E2E_IRIG_EXT_PPS: case ClockSyncProfile.IRIG_EXT_PPS_Master_E2E_PPS_OUT: clockSyncProfileMaster = ClockSyncProfile.IRIG_EXT_PPS; break; case ClockSyncProfile.Master_E2E_GPS_EXT_PPS: case ClockSyncProfile.GPS_EXT_PPS_Master_E2E_PPS_OUT: clockSyncProfileMaster = ClockSyncProfile.GPS_EXT_PPS; break; default: clockSyncProfileMaster = ClockSyncProfile.None; break; } break; } } public static DTS.Common.Classes.ClockSync.ClockSyncProfile GetGrandMasterClock(TestTemplate test) { var collection = DTS.Common.Classes.ClockSync.ClockSyncProfileCollection.GetCollection(); switch(test.ClockSyncProfileMaster) { case ClockSyncProfile.Master_E2E: return collection.GetClockSyncProfile(DTS.Common.Classes.ClockSync.ClockSyncProfileCollection.NONE_NAME); case ClockSyncProfile.Master_E2E_IRIG: return collection.GetClockSyncProfile(DTS.Common.Classes.ClockSync.ClockSyncProfileCollection.IRIGB_NAME); case ClockSyncProfile.Master_E2E_EXT_PPS: return collection.GetClockSyncProfile(DTS.Common.Classes.ClockSync.ClockSyncProfileCollection._1PPS_NAME); case ClockSyncProfile.Master_E2E_IRIG_EXT_PPS: return collection.GetClockSyncProfile(DTS.Common.Classes.ClockSync.ClockSyncProfileCollection.IRIGB1PPS_NAME); case ClockSyncProfile.Master_E2E_GPS_EXT_PPS: return collection.GetClockSyncProfile(DTS.Common.Classes.ClockSync.ClockSyncProfileCollection.GPS1PPS_NAME); case ClockSyncProfile.Slave_E2E_Master_E2E_OUT: return collection.GetClockSyncProfile(DTS.Common.Classes.ClockSync.ClockSyncProfileCollection.PTPIEEE1588_NAME); } if ( test.ClockSyncProfileMaster != ClockSyncProfile.None) { return collection.GetClockSyncProfile(DTS.Common.Classes.ClockSync.ClockSyncProfileCollection.NONE_NAME); } switch(test.ClockSyncProfileSlave) { case ClockSyncProfile.Slave_E2E: return collection.GetClockSyncProfile(DTS.Common.Classes.ClockSync.ClockSyncProfileCollection.PTPIEEE1588_NAME); case ClockSyncProfile.GPS_EXT_PPS: return collection.GetClockSyncProfile(DTS.Common.Classes.ClockSync.ClockSyncProfileCollection.GPS1PPS_NAME); case ClockSyncProfile.IRIG_EXT_PPS: return collection.GetClockSyncProfile(DTS.Common.Classes.ClockSync.ClockSyncProfileCollection.IRIGB1PPS_NAME); case ClockSyncProfile.EXT_PPS: return collection.GetClockSyncProfile(DTS.Common.Classes.ClockSync.ClockSyncProfileCollection._1PPS_NAME); case ClockSyncProfile.IRIG: return collection.GetClockSyncProfile(DTS.Common.Classes.ClockSync.ClockSyncProfileCollection.IRIGB_NAME); case ClockSyncProfile.None: default: return collection.GetClockSyncProfile(DTS.Common.Classes.ClockSync.ClockSyncProfileCollection.NONE_NAME); } } [System.Diagnostics.CodeAnalysis.SuppressMessage("Minor Code Smell", "S3458:Empty \"case\" clauses that fall through to the \"default\" should be omitted", Justification = "clearer")] public static void SetGrandMasterClock(DTS.Common.Classes.ClockSync.ClockSyncProfile profile, TestTemplate test) { var hardware = test.GetHardware(); var masterFound = Array.Exists(hardware, x => test.DASClockMasterList.ContainsKey(x.SerialNumber) && test.DASClockMasterList[x.SerialNumber]); switch (profile.ProfileName) { case DTS.Common.Classes.ClockSync.ClockSyncProfileCollection.PTPIEEE1588_NAME: SetGrandMasterClockPTP(test, masterFound); break; case DTS.Common.Classes.ClockSync.ClockSyncProfileCollection.IRIGB1PPS_NAME: SetGrandMasterClockIRIG1PPS(test, masterFound); break; case DTS.Common.Classes.ClockSync.ClockSyncProfileCollection.GPS1PPS_NAME: SetGrandMasterClockGPS1PPS(test, masterFound); break; case DTS.Common.Classes.ClockSync.ClockSyncProfileCollection.IRIGB_NAME: SetGrandMasterClockIRIGB(test, masterFound); break; case DTS.Common.Classes.ClockSync.ClockSyncProfileCollection._1PPS_NAME: SetGrandMasterClock1PPS(test, masterFound); break; case DTS.Common.Classes.ClockSync.ClockSyncProfileCollection.NONE_NAME: default: SetGrandMasterClockNone(test, masterFound); break; } } private static void SetGrandMasterClock1PPS(TestTemplate test, bool masterFound) { if (masterFound) { test.ClockSyncProfileMaster = ClockSyncProfile.Master_E2E_EXT_PPS; test.ClockSyncProfileSlave = ClockSyncProfile.Slave_E2E; } else { test.ClockSyncProfileMaster = ClockSyncProfile.None; test.ClockSyncProfileSlave = ClockSyncProfile.EXT_PPS; } } private static void SetGrandMasterClockIRIGB(TestTemplate test, bool masterFound) { if (masterFound) { test.ClockSyncProfileMaster = ClockSyncProfile.Master_E2E_IRIG; test.ClockSyncProfileSlave = ClockSyncProfile.Slave_E2E; } else { test.ClockSyncProfileMaster = ClockSyncProfile.None; test.ClockSyncProfileSlave = ClockSyncProfile.IRIG; } } private static void SetGrandMasterClockGPS1PPS(TestTemplate test, bool masterFound) { if (masterFound) { test.ClockSyncProfileMaster = ClockSyncProfile.Master_E2E_GPS_EXT_PPS; test.ClockSyncProfileSlave = ClockSyncProfile.Slave_E2E; } else { test.ClockSyncProfileMaster = ClockSyncProfile.None; test.ClockSyncProfileSlave = ClockSyncProfile.GPS_EXT_PPS; } } private static void SetGrandMasterClockIRIG1PPS(TestTemplate test, bool masterFound) { if (masterFound) { test.ClockSyncProfileMaster = ClockSyncProfile.Master_E2E_IRIG_EXT_PPS; test.ClockSyncProfileSlave = ClockSyncProfile.Slave_E2E; } else { test.ClockSyncProfileMaster = ClockSyncProfile.None; test.ClockSyncProfileSlave = ClockSyncProfile.IRIG_EXT_PPS; } } private static void SetGrandMasterClockPTP(TestTemplate test, bool masterFound) { if (masterFound) { //note this is an invalid configuration! test.ClockSyncProfileMaster = ClockSyncProfile.Slave_E2E_Master_E2E_OUT; test.ClockSyncProfileSlave = ClockSyncProfile.Slave_E2E; } else { test.ClockSyncProfileMaster = ClockSyncProfile.None; test.ClockSyncProfileSlave = ClockSyncProfile.Slave_E2E; } } private static void SetGrandMasterClockNone(TestTemplate test, bool masterFound) { if (masterFound) { test.ClockSyncProfileMaster = ClockSyncProfile.Master_E2E; test.ClockSyncProfileSlave = ClockSyncProfile.Slave_E2E; } else { test.ClockSyncProfileMaster = ClockSyncProfile.None; test.ClockSyncProfileSlave = ClockSyncProfile.None; } } private static bool ValidateClockSyncProfiles(ref List errors, TestTemplate currentTest) { var result = true; try { if (null == currentTest) { return true; } //nothing more to check if (currentTest.ClockSyncProfileMaster == ClockSyncProfile.None) { return true; } var hardware = currentTest.GetHardware(); foreach (var h in hardware) { if (null == h) { continue; } if (h.IsConfigurableClockSync) { if (null == currentTest.DASClockMasterList) { continue; } if (!currentTest.DASClockMasterList.ContainsKey(h.SerialNumber)) { currentTest.DASClockMasterList[h.SerialNumber] = false; } switch (currentTest.DASClockMasterList[h.SerialNumber]) { case true: if (!h.SupportsClockSyncProfile(currentTest.ClockSyncProfileMaster, true)) { errors.Add(string.Format(StringResources.TestTemplate_UnsupportedClockSyncProfile, EnumDescriptionTypeConverter.GetEnumDescription(currentTest.ClockSyncProfileMaster), h.ToString())); result = false; } break; case false: if (!h.SupportsClockSyncProfile(currentTest.ClockSyncProfileSlave, false)) { errors.Add(string.Format(StringResources.TestTemplate_UnsupportedClockSyncProfile, EnumDescriptionTypeConverter.GetEnumDescription(currentTest.ClockSyncProfileSlave), h.ToString())); result = false; } break; } } } //check to see that there's a master clock specified if a master clock profile is set (with the exception of PTP which should only run with no master ...) var grandMasterType = TestTemplate.GetGrandMasterClock(currentTest); var hasMaster = currentTest.DASClockMasterList.Where(x => x.Value && Array.Exists(hardware, y => y.SerialNumber == x.Key)).Any(); if (currentTest.ClockSyncProfileMaster != ClockSyncProfile.None && grandMasterType.ProfileName != DTS.Common.Classes.ClockSync.ClockSyncProfileCollection.PTPIEEE1588_NAME) { var masterDAS = currentTest.DASClockMasterList.Where(kvp => kvp.Value); if (!masterDAS.Any()) { errors.Add(StringResources.NoClockMasters); return false; } } if (hasMaster && grandMasterType.ProfileName == DTS.Common.Classes.ClockSync.ClockSyncProfileCollection.PTPIEEE1588_NAME) { errors.Add(StringResources.InvalidClockSync_PTPMaster); return false; } //30487 GPS only clock sync option should be removed if (currentTest.ClockSyncProfileMaster.ToString().Contains("GPS") && !currentTest.ClockSyncProfileMaster.ToString().Contains("EXT_PPS")) { errors.Add(StringResources.TestTemplate_GPSOnlyNotSufficientAdd1PPS); var defaultCSP = TestSetupDefaults.GetUserSettingValueString(ApplicationProperties.CurrentUser.Id, PropertyEnums.PropertyIds.DefaultClockSyncProfileMaster); if (defaultCSP.Contains("GPS") && !defaultCSP.Contains("EXT_PPS")) { errors.Add(StringResources.TestTemplate_GPSOnlyTestSetupDefault); } result = false; } //30479 make sure GPS clock masters have UART configured if (currentTest.ClockSyncProfileMaster.ToString().Contains("GPS")) { var masterDASSerials = currentTest.DASClockMasterList.Where(kvp => kvp.Value).Select(kvp => kvp.Key).ToList(); var uartChannelDASSerials = currentTest.GetChannels().Where(gc => gc.IsUart).Select(gc => gc.HardwareChannel.GetParentDAS().SerialNumber).ToList(); if (masterDASSerials.Exists(serial => !uartChannelDASSerials.Contains(serial))) { // a master clock das reliant on GPS doesn't have a uart channel configured in the test errors.Add(StringResources.TestTemplate_GPSMasterButMissingUARTConfig); result = false; } } // 29842 LTS concerns for 1PPS out // if we've selected a 1PPS out profile, check whether we're using a central db and if so, see if it's new enough to support 1PPS out // database version is probably cached, otherwise will cache on this get if ((Constants.OnePPSOutProfiles.Contains(currentTest.ClockSyncProfileMaster) || Constants.OnePPSOutProfiles.Contains(currentTest.ClockSyncProfileSlave)) && DbOperations._usingCentralizedDB && DbOperations.GetConnectionDbVersion() < Constants.ONEPPS_OUT_DB_VERSION) { errors.Add(string.Format(StringResources.TestTemplate_UnsupportedClockSyncProfileLTS, Constants.ONEPPS_OUT_DB_VERSION, DbOperations.GetConnectionDbVersion())); result = false; } } catch (Exception ex) { //This is a model of how other validation functions can more gracefully avoid a misleading "Failure to update lock" error, //and at the same time, give the user a clue as to where the validation failed. var errorString = string.Format(StringResources.TestSetupValidationError, "ValidateClockSyncProfiles"); var exceptionString = $"{errorString}: {ex.Message}"; APILogger.Log(exceptionString); } return result; } public static bool ValidateStorageSpace(ref List errors, TestTemplate currentTest) { var bStorageSpaceValid = true; var preTriggerSeconds = currentTest.PreTriggerSeconds; var postTriggerSeconds = currentTest.PostTriggerSeconds; 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); } } } var allHardware = currentTest.GetHardware(); foreach (var h in allHardware) { if (!das.Contains(h)) { das.Add(h); } } var maxSampleRate = 0.0D; foreach (var d in das) { var rate = currentTest.GetSampleRateForHardware(d); if (rate > maxSampleRate) { maxSampleRate = rate; } } var secondsToRecord = preTriggerSeconds + postTriggerSeconds; var sampleClocksToRecord = (ulong)(secondsToRecord * maxSampleRate + 1); foreach (var d in das) { double maxSampleClockTicks = GetMaxSampleClockTicks(d, currentTest.RecordingMode); if (!(maxSampleClockTicks > 0)) continue; if (!(sampleClocksToRecord > maxSampleClockTicks)) continue; bStorageSpaceValid = false; int maxSeconds = (int)(maxSampleClockTicks / maxSampleRate); errors.Add(string.Format(StringResources.TestTemplate_InsufficientMemory, d.SerialNumber, maxSeconds)); } return bStorageSpaceValid; } /// /// returns the max sample clock ticks expected to be recordable on a particular das /// /// /// private static double GetMaxSampleClockTicks(DASHardware d, RecordingModes recordingMode) { double sampleClockTicks = d.GetMaxMemoryLong(); if (RecordingModeExtensions.IsAUartMode(recordingMode)) { //SLICE UART recording modes currently require reserving half of the memory for UART //http://manuscript.dts.local/f/cases/30489/Max-event-length-not-taking-UART-into-consideration-with-UART-recording-modes //indeterminate what this means for multiple event modes return sampleClockTicks / 2D; } return sampleClockTicks; } public static bool ValidateROI(ref List errors, TestTemplate currentTest) { if (currentTest.RegionsOfInterest.GroupBy(roi => roi.Suffix).Select(roi => roi.First()).Count() != currentTest.RegionsOfInterest.Count) { //we have multiple suffixes errors.Add(StringResources.TestTemplate_ROISuffixNotUnique); return false; } return true; } #endregion Validate #region PropertyChanged public override event PropertyChangedEventHandler PropertyChanged; public override void OnPropertyChanged(string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } private void _customers_PropertyChanged(object sender, PropertyChangedEventArgs e) { OnPropertyChanged(TestTemplateTags.AllCustomers.ToString()); } private void _testEngineers_PropertyChanged(object sender, PropertyChangedEventArgs e) { OnPropertyChanged(TestTemplateTags.AllTestEngineers.ToString()); } void _labs_PropertyChanged(object sender, PropertyChangedEventArgs e) { OnPropertyChanged(TestTemplateTags.AllLabs.ToString()); } #endregion PropertyChanged /// /// holds test settings that don't have individual db columns /// private TestSettingDictionary _settings; /// /// gets a serialized version of the settings table /// /// public string GetSettings() { return _settings.ToSerializeString(); } /// /// loads values from a serialized string into the settings table /// /// public void LoadSettings(string s) { _settings.LoadSettings(s); } /// /// constructs a settings table with some default values /// default values can then have their values changed by LoadSettings if we are loading from a db /// /// private static TestSettingDictionary CreateSettingsDictionary(TestSetupDefaults usersettings) { var settings = new TestSettingDictionary(); settings.SetValue(new TestSetting(TestSettingsEnum.ArmCheckListStep, usersettings.DefaultArmCheckListStep.ToString(), usersettings.DefaultArmCheckListStep.ToString())); settings.SetValue(new TestSetting(TestSettingsEnum.CheckListBatteryVoltageCheck, usersettings.DefaultCheckListBatteryVoltageCheck.ToString(), usersettings.DefaultCheckListBatteryVoltageCheck.ToString())); settings.SetValue(new TestSetting(TestSettingsEnum.CheckListInputVoltageCheck, usersettings.DefaultCheckListInputVoltageCheck.ToString(), usersettings.DefaultCheckListInputVoltageCheck.ToString())); settings.SetValue(new TestSetting(TestSettingsEnum.CheckListSensorIDCheck, usersettings.DefaultCheckListSensorIdCheck.ToString(), usersettings.DefaultCheckListSensorIdCheck.ToString())); settings.SetValue(new TestSetting(TestSettingsEnum.CheckListSquibResistanceCheck, usersettings.DefaultCheckListSquibResistanceCheck.ToString(), usersettings.DefaultCheckListSquibResistanceCheck.ToString())); settings.SetValue(new TestSetting(TestSettingsEnum.CheckListTriggerStartCheck, usersettings.DefaultCheckListTriggerStartCheck.ToString(), usersettings.DefaultCheckListTriggerStartCheck.ToString())); settings.SetValue(new TestSetting(TestSettingsEnum.CheckListTiltSensorCheck, usersettings.DefaultCheckListTiltSensorCheck.ToString(), usersettings.DefaultCheckListTiltSensorCheck.ToString())); settings.SetValue(new TestSetting(TestSettingsEnum.CheckListTemperatureCheck, usersettings.DefaultCheckListTemperatureCheck.ToString(), usersettings.DefaultCheckListTemperatureCheck.ToString())); settings.SetValue(new TestSetting(TestSettingsEnum.CheckListMustPass, usersettings.DefaultChecklistMustPass.ToString(), usersettings.DefaultChecklistMustPass.ToString())); var sValue = Convert.ToInt32(usersettings.DefaultTestExcitationWarmupSeconds * 1000D).ToString(CultureInfo.InvariantCulture); settings.SetValue(new TestSetting(TestSettingsEnum.EW, sValue, sValue)); //initialize here return settings; } /// /// ExcitationWarmupTimeMS is the public available property for test excitation warmup, it is a max of every object warmup time and the test /// warmup time itself. For now they are always going to be the same, which is okay, but we have the flexibility to change that in /// the future if desired. /// This property is just the test excitation warmup time itself without the consideration of test object excitation times. /// private int ExcitationWarmup { get => Convert.ToInt32(GetSetting(TestSettingsEnum.EW), CultureInfo.InvariantCulture); set => SetSetting(TestSettingsEnum.EW, value.ToString(CultureInfo.InvariantCulture)); } public bool CheckListTriggerStartCheck { get => Convert.ToBoolean(GetSetting(TestSettingsEnum.CheckListTriggerStartCheck)); set => SetSetting(TestSettingsEnum.CheckListTriggerStartCheck, value.ToString()); } private string GetSetting(TestSettingsEnum setting) { var defaultValue = GetDefaultValue(setting); var s = _settings.GetValue(setting, defaultValue); switch (setting) { case TestSettingsEnum.ArmCheckListStep: case TestSettingsEnum.CheckListBatteryVoltageCheck: case TestSettingsEnum.CheckListInputVoltageCheck: case TestSettingsEnum.CheckListSensorIDCheck: case TestSettingsEnum.CheckListSquibResistanceCheck: case TestSettingsEnum.CheckListTriggerStartCheck: case TestSettingsEnum.CheckListTiltSensorCheck: case TestSettingsEnum.CheckListTemperatureCheck: case TestSettingsEnum.CheckListClockSyncCheck: case TestSettingsEnum.CheckListMustPass: { return bool.TryParse(s, out var b) ? b.ToString() : defaultValue; } case TestSettingsEnum.EW: { return int.TryParse(s, out var i) ? i.ToString(CultureInfo.InvariantCulture) : defaultValue; } default: throw new NotSupportedException("unsupported: " + setting); } } private static string GetDefaultValue(TestSettingsEnum setting) { switch (setting) { case TestSettingsEnum.ArmCheckListStep: return Defaults.GetUserSettingValueBool(ApplicationProperties.CurrentUser.Id, PropertyEnums.PropertyIds.DefaultArmCheckListStep).ToString(); case TestSettingsEnum.CheckListBatteryVoltageCheck: return Defaults.GetUserSettingValueBool(ApplicationProperties.CurrentUser.Id, PropertyEnums.PropertyIds.DefaultCheckListBatteryVoltageCheck).ToString(); case TestSettingsEnum.CheckListInputVoltageCheck: return Defaults.GetUserSettingValueBool(ApplicationProperties.CurrentUser.Id, PropertyEnums.PropertyIds.DefaultCheckListInputVoltageCheck).ToString(); case TestSettingsEnum.CheckListSensorIDCheck: return Defaults.GetUserSettingValueBool(ApplicationProperties.CurrentUser.Id, PropertyEnums.PropertyIds.DefaultCheckListSensorIdCheck).ToString(); case TestSettingsEnum.CheckListSquibResistanceCheck: return Defaults.GetUserSettingValueBool(ApplicationProperties.CurrentUser.Id, PropertyEnums.PropertyIds.DefaultCheckListSquibResistanceCheck).ToString(); case TestSettingsEnum.CheckListMustPass: return Defaults.GetUserSettingValueBool(ApplicationProperties.CurrentUser.Id, PropertyEnums.PropertyIds.DefaultCheckListMustPass).ToString(); case TestSettingsEnum.CheckListTriggerStartCheck: return Defaults.GetUserSettingValueBool(ApplicationProperties.CurrentUser.Id, PropertyEnums.PropertyIds.DefaultCheckListTriggerStartCheck).ToString(); case TestSettingsEnum.CheckListTiltSensorCheck: return Defaults.GetUserSettingValueBool(ApplicationProperties.CurrentUser.Id, PropertyEnums.PropertyIds.DefaultCheckListTiltSensorCheck).ToString(); case TestSettingsEnum.CheckListTemperatureCheck: return Defaults.GetUserSettingValueBool(ApplicationProperties.CurrentUser.Id, PropertyEnums.PropertyIds.DefaultCheckListTemperatureCheck).ToString(); case TestSettingsEnum.CheckListClockSyncCheck: return Defaults.GetUserSettingValueBool(ApplicationProperties.CurrentUser.Id, PropertyEnums.PropertyIds.DefaultCheckListClockSyncCheck).ToString(); case TestSettingsEnum.EW: return Convert.ToInt32(Defaults.GetUserSettingValueDouble(ApplicationProperties.CurrentUser.Id, PropertyEnums.PropertyIds.DefaultTestExcitationWarmupSeconds)).ToString(CultureInfo.InvariantCulture); default: return true.ToString(); } } private void SetSetting(TestSettingsEnum setting, string value) { _settings.SetValue(setting, value); OnPropertyChanged(setting.ToString()); } public HardwareChannel GetHardwareChannel(int absoluteNumber) { lock (HardwareChannelLookupLock) { if (null == _absoluteNumberToHardwareChannel) { PopulateHardwareChannelLookup(); } return _absoluteNumberToHardwareChannel[absoluteNumber]; } } private string _currentSearchTerm = ""; public void FilterTestObjects(string term) { _currentSearchTerm = term; OnPropertyChanged(TestTemplateTags.AvailableTestObjects.ToString()); } //TODO: Review functionality - should not be a property public DTS.Common.ISO.TestObject[] AvailableTestObjects => throw new NotImplementedException(); private void PopulateHardwareChannelLookup() { var absoluteChannelNumber = 0; _absoluteNumberToHardwareChannel = new Dictionary(); _dasChannelNumberToAbsoluteNumber = new Dictionary(); foreach (var to in TestObjects) { foreach (var das in to.Hardware) { var dasChannelNumber = 0; var channels = das.Channels; if (null == channels || !channels.Any()) continue; foreach (var channel in channels) { _absoluteNumberToHardwareChannel.Add(absoluteChannelNumber, channel); var key = $"{das.SerialNumber}_{dasChannelNumber}"; if (!_dasChannelNumberToAbsoluteNumber.ContainsKey(key)) { _dasChannelNumberToAbsoluteNumber.Add(key, absoluteChannelNumber); } dasChannelNumber++; absoluteChannelNumber++; } } } } public int GetAbsoluteNumber(string serial, int dasChannelNumber) { lock (HardwareChannelLookupLock) { if (null == _dasChannelNumberToAbsoluteNumber) { PopulateHardwareChannelLookup(); } } return _dasChannelNumberToAbsoluteNumber[$"{serial}_{dasChannelNumber}"]; } public double GetPreTriggerSeconds(string serial) { return _dasSettings.ContainsKey(serial) ? _dasSettings[serial].PreTriggerSeconds : PreTriggerSeconds; } public void SetPreTriggerSeconds(DASHardware h, double d) { if (!_dasSettings.ContainsKey(h.SerialNumber)) { _dasSettings.Add(h.SerialNumber, CreateDASSettings(h)); } _dasSettings[h.SerialNumber].PreTriggerSeconds = d; } public double GetPostTriggerSeconds(string serial) { return _dasSettings.ContainsKey(serial) ? _dasSettings[serial].PostTriggerSeconds : PostTriggerSeconds; } public void SetPostTriggerSeconds(DASHardware h, double d) { if (!_dasSettings.ContainsKey(h.SerialNumber)) { _dasSettings.Add(h.SerialNumber, CreateDASSettings(h)); } _dasSettings[h.SerialNumber].PostTriggerSeconds = d; } public void SetTestObjects(bool bMemoryOnly, TestTestObject[] value) { if (!bMemoryOnly) { MarkIsCompleteUnchecked(); } SetProperty(ref _testObjects, new List(value), TestTemplateTags.TestObjects.ToString()); OnPropertyChanged(TestTemplateTags.AvailableTestObjects.ToString()); //No longer needed for added test objects combobox, but still used to update the remaining list of available test objects OnPropertyChanged(TestTemplateTags.AllTestObjects.ToString()); OnPropertyChanged("TestObjectsAndAddedGroups"); } public void SetAddedGroupsMemoryOnly(TestTestObject[] value) { SetProperty(ref _addedGroups, new List(value), TestTemplateTags.SysBuiltTestObjectTypes.ToString()); OnPropertyChanged(TestTemplateTags.AllTestObjects.ToString());//check this OnPropertyChanged("TestObjectsAndAddedGroups"); OnPropertyChanged(TestTemplateTags.TestObjectsAndAddedGroups.ToString()); } public void AddTestObject(TestObject to, int excitationWarmupMs, double targetSampleRate, string testobject, string position, int displayOrder) { if (null != to) { var tto = new TestTestObject(to) { ExcitationWarmupTimeMS = excitationWarmupMs, TargetSampleRate = targetSampleRate, DisplayOrder = displayOrder }; 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()); } TestObjectsAndAddedGroupsList.Sort(); } public void AddAddedGroup(TestObject to, string testobject, string position, string testSetupName, int displayOrder) { if (null == to) return; var tto = new TestTestObject(to); tto.SetPosition(position); tto.SetTestObject(testobject); tto.SerialNumberConverted = tto.SerialNumber.Remove(0, testSetupName.Length + 1); tto.TestSetupName = testSetupName; tto.DisplayOrder = displayOrder; _addedGroups.Add(tto); TestObjectsAndAddedGroupsList.Add(tto); TestObjectsAndAddedGroupsList.Sort(); 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 override string ToString() { return $"{Name} ({Description})"; } private readonly Dictionary _channelLookup = new Dictionary(); public void AddChannelLookup(string key, DTS.Slice.Control.Event.Module.Channel value) { lock (ChannelLookupLock) { _channelLookup[key] = value; } } private readonly Dictionary _sessionOpen = new Dictionary(); public HardwareChannel GetChannel(int index) { return ChannelCount > index ? _hardwareChannelsInOrder[index] : null; } public DTS.Slice.Control.Event.Module.Channel GetPersistentChannel(int index) { lock (CountLock) { if (_channelKeysInOrder.Count < 1) { foreach (var to in TestObjects) { foreach (var hardware in to.Hardware) { var dasChannelNumber = 0; foreach (var channel in hardware.Channels) { if (null != channel.Sensor) { var key = $"{hardware.SerialNumber}_{dasChannelNumber}"; if (!_channelKeysInOrder.Contains(key)) { _channelKeysInOrder.Add(key); _hardwareChannelsInOrder.Add(channel); } } dasChannelNumber++; } } } } } lock (ChannelLookupLock) { if (!_sessionOpen.ContainsKey(_channelKeysInOrder[index])) { _sessionOpen.Add(_channelKeysInOrder[index], true); } return _channelLookup[_channelKeysInOrder[index]]; } } /// /// for some reason it seems custom and lab details are held in two different areas, in meta deta and in the custom/lab detail objects /// this function merely synchronizes the meta data with the objects /// public void SynchronizeTestMetaData() { var fields = Enum.GetValues(typeof(TestSetupMetaData.Fields)).Cast().ToArray(); var customerOverride = CustomerOverride; var engineerOverride = EngineerOverride; var labOverride = LabOverride; foreach (var field in fields) { string sValue; switch (field) { case TestSetupMetaData.Fields.CustName: sValue = (null == CustomerDetails) ? string.Empty : CustomerDetails.Name; //the name of the record? we don't let you set this currently in the UI in Edit TestSetup ... so no override available break; case TestSetupMetaData.Fields.CustomerCostUnit: sValue = (null == CustomerDetails) ? Constants.NoValue : CustomerDetails.CustomerCostUnit; if (null != customerOverride) { sValue = customerOverride.CustomerCostUnit; } break; case TestSetupMetaData.Fields.CustomerName: sValue = (null == CustomerDetails) ? string.Empty : CustomerDetails.CustomerName; if (null != customerOverride) { sValue = customerOverride.CustomerName; } break; case TestSetupMetaData.Fields.CustomerOrderNumber: sValue = (null == CustomerDetails) ? Constants.NoValue : CustomerDetails.CustomerOrderNumber; if (null != customerOverride) { sValue = customerOverride.CustomerOrderNumber; } break; case TestSetupMetaData.Fields.CustomerProjectReferenceNumber: sValue = (null == CustomerDetails) ? Constants.NoValue : CustomerDetails.ProjectRefNumber; if (null != customerOverride) { sValue = customerOverride.ProjectRefNumber; } break; case TestSetupMetaData.Fields.TEName: sValue = (null == TestEngineerDetails) ? string.Empty : TestEngineerDetails.Name; //the record name? we don't let you change this in the UI currently, so there's no override for it ... break; case TestSetupMetaData.Fields.TestEngineerEmail: sValue = (null == TestEngineerDetails) ? Constants.NoValue : TestEngineerDetails.TestEngineerEmail; if (null != engineerOverride) { sValue = engineerOverride.TestEngineerEmail; } break; case TestSetupMetaData.Fields.TestEngineerFax: sValue = (null == TestEngineerDetails) ? Constants.NoValue : TestEngineerDetails.TestEngineerFax; if (null != engineerOverride) { sValue = engineerOverride.TestEngineerFax; } break; case TestSetupMetaData.Fields.TestEngineerName: sValue = (null == TestEngineerDetails) ? Constants.NoValue : TestEngineerDetails.TestEngineerName; if (null != engineerOverride) { sValue = engineerOverride.TestEngineerName; } break; case TestSetupMetaData.Fields.TestEngineerPhone: sValue = (null == TestEngineerDetails) ? Constants.NoValue : TestEngineerDetails.TestEngineerPhone; if (null != engineerOverride) { sValue = engineerOverride.TestEngineerPhone; } break; case TestSetupMetaData.Fields.CustomerTestReferenceNumber: sValue = (null == CustomerDetails) ? string.Empty : CustomerDetails.CustomerTestRefNumber; if (null != customerOverride) { sValue = customerOverride.CustomerTestRefNumber; } break; case TestSetupMetaData.Fields.LabName: sValue = (null == LabDetails) ? string.Empty : LabDetails.Name; //we don't expose the name in the UI in edit test setup, so no override value available break; case TestSetupMetaData.Fields.LaboratoryContactEmail: sValue = (null == LabDetails) ? Constants.NoValue : LabDetails.LabratoryContactEmail; if (null != labOverride) { sValue = labOverride.LabratoryContactEmail; } break; case TestSetupMetaData.Fields.LaboratoryContactFax: sValue = (null == LabDetails) ? Constants.NoValue : LabDetails.LabratoryContactFax; if (null != labOverride) { sValue = labOverride.LabratoryContactFax; } break; case TestSetupMetaData.Fields.LaboratoryContactName: sValue = (null == LabDetails) ? Constants.NoValue : LabDetails.LabratoryContactName; if (null != labOverride) { sValue = labOverride.LabratoryContactName; } break; case TestSetupMetaData.Fields.LaboratoryContactPhone: sValue = (null == LabDetails) ? Constants.NoValue : LabDetails.LabratoryContactPhone; if (null != labOverride) { sValue = labOverride.LabratoryContactPhone; } break; case TestSetupMetaData.Fields.LaboratoryName: sValue = (null == LabDetails) ? string.Empty : LabDetails.LabratoryName; if (null != labOverride) { sValue = labOverride.LabratoryName; } break; case TestSetupMetaData.Fields.LaboratoryTestReferenceNumber: sValue = (null == LabDetails) ? string.Empty : LabDetails.LabratoryTestRefNumber; if (null != labOverride) { sValue = labOverride.LabratoryTestRefNumber; } break; case TestSetupMetaData.Fields.LaboratoryProjectReferenceNumber: sValue = (null == LabDetails) ? string.Empty : LabDetails.LabratoryProjectRefNumber; if (null != labOverride) { sValue = labOverride.LabratoryProjectRefNumber; } break; default: continue; } if (null != sValue) { GetTestMetaData().SetPropertyValue(field, sValue); } } } #region CalculatedChannels public void RemoveCalculatedChannel(CalculatedValueClass ch) { CalculatedChannels.Remove(ch); } public void AddCalculatedChannel(CalculatedValueClass ch) { CalculatedChannels.Add(ch); } public void UpdateCalculatedChannelId(string[] names, int[] newIds) { var lookup = new Dictionary(); foreach (var cc in CalculatedChannels) { lookup[cc.Name] = cc; } for (var i = 0; i < names.Length && i < newIds.Length; i++) { if (lookup.ContainsKey(names[i])) { lookup[names[i]].Id = newIds[i]; } } } public void UpdateCalculatedChannelId(string name, int newId) { foreach (var cc in CalculatedChannels) { if (cc.Name == name) { cc.Id = newId; break; } } } public void RemoveImpossibleCalculatedChannels() { if (0 == CalculatedChannels.Count) { return; } var objects = new List(TestObjects); objects.AddRange(AddedGroups); var groupChannelIdLookup = new Dictionary(); foreach (var to in objects.ToArray()) { var isoto = to.GetISOTestObject(); foreach (var ch in isoto.AllChannels) { if (ch.Required && !string.IsNullOrWhiteSpace(ch.SensorSerialNumber)) { groupChannelIdLookup[ch.GetId()] = true; } } } var invalidCCs = new List(); foreach (var cc in CalculatedChannels) { if (!cc.IsValid(groupChannelIdLookup)) { invalidCCs.Add(cc); } } foreach (var invalid in invalidCCs) { RemoveCalculatedChannel(invalid); } } public void ReplaceCalculatedChannel(Dictionary oldIdToNewIdLookup) { foreach (var cc in CalculatedChannels) { for (var i = 0; i < cc.InputChannelIds.Length; i++) { if (oldIdToNewIdLookup.ContainsKey(cc.InputChannelIds[i])) { cc.ReplaceInputChannelIdAtIndex(i, oldIdToNewIdLookup[cc.InputChannelIds[i]]); } } } } public void RenameCalculatedChannels(string oldName, string newName) { foreach (var cc in CalculatedChannels) { for (var i = 0; i < cc.InputChannelIds.Length; i++) { if (cc.InputChannelIds[i].StartsWith(oldName)) { cc.ReplaceInputChannelIdAtIndex(i, cc.InputChannelIds[i].Replace(oldName, newName)); } } if (cc.TestSetupName.StartsWith(oldName)) { cc.TestSetupName = cc.TestSetupName.Replace(oldName, newName); } } } #endregion CalculatedChannels public void ReplaceROIChannels(List newGroupChannelList) { foreach (var roi in RegionsOfInterest) { roi.ChannelIds = new long[roi.ChannelNames.Length]; } foreach (var newGroupChannel in newGroupChannelList) { //If a new channel is in any of the ROIs, update the Id foreach (var roi in RegionsOfInterest) { var channelIndex = 0; //foreach (var channelGUID in roi.ChannelGuids) foreach (var channelId in roi.ChannelIds) //foreach (var channelName in roi.ChannelNames) { var hardwareChannelName = string.Empty; if ((newGroupChannel.HardwareChannel != null) && newGroupChannel.HardwareChannel.IsTSRAIR) { var hyphenIndex = newGroupChannel.HardwareChannel.ModuleSerialNumber.IndexOf('-'); hardwareChannelName = GenerateEmbeddedChannelName(newGroupChannel.HardwareChannel.ModuleSerialNumber.Substring(0, hyphenIndex), $"\\{newGroupChannel.GetChannelName(SerializedSettings.ISOViewMode)}"); } else { var channelName = newGroupChannel.Sensor == SensorConstants.VOLTAGE_INPUT ? newGroupChannel.Sensor : newGroupChannel.SensorData.SerialNumber; var newGroupChannelHardware = string.Empty; if (string.IsNullOrWhiteSpace(newGroupChannel.Hardware) && (newGroupChannel.DASId > 0) && newGroupChannel.DASChannelIndex >= 0) { var dasList = GetHardwareFromDb(); foreach (var das in dasList) { if (das.DASId == newGroupChannel.DASId) { if (newGroupChannel.IsSquib) { newGroupChannelHardware = $"[{das.SerialNumber}] {StringResources.ChannelPrefix_Squib}{((newGroupChannel.DASChannelIndex * 2) + 1).ToString("00")}"; } else { newGroupChannelHardware = $"[{das.SerialNumber}] {StringResources.ChannelPrefix_Other}{(newGroupChannel.DASChannelIndex + 1).ToString("00")}"; } break; //Found the DAS we were looking for } } } else { newGroupChannelHardware = $"{newGroupChannel.Hardware}"; } hardwareChannelName = $"{newGroupChannelHardware}\\{channelName}"; } //FB 44012 Remove Assigned By ID from channel name so it would find the mathc correctly if (roi.ChannelNames[channelIndex] == RegionOfInterest.RemoveAssignedByIDFromHardwareString(hardwareChannelName)) { roi.ChannelIds[channelIndex] = newGroupChannel.Id; break; //now check the other ROI(s) } channelIndex++; } } } } public void SetHardwareOverride(string hid, bool bAdd) { HardwareOverrides[hid] = new HardwareInclusionInstruction(hid, bAdd ? HardwareInclusionInstruction.Actions.Add : HardwareInclusionInstruction.Actions.Remove); OnPropertyChanged(TestTemplateTags.HardwareOverrides.ToString()); } /// /// takes a dynamic group in the test and replaces it with a snapshot of the static group /// also updates calculated channels and level trigger channels /// 15400 Group Push/Pull should allow the LT to be preserved in the Test Setup regardless of push/pull /// /// /// public void UpdateDynamicGroupFromStaticGroup(IGroup _updateGroup, IGroup staticGroup) { //before we remove the old group we need to make sure we cache all the channels belonging to the old group //so that when we add the new group we can restore connections to level triggers and calculated channels //15400 Group Push/Pull should allow the LT to be preserved in the Test Setup regardless of push/pull //now there's a stored procedure sp_CompareGroups which determines the difference-ness in groups //but we'll have to do it in code here var testSetupChannelIdToGroupChannelId = GetIGroupChannelIdToIGroupChannel(); var channelNameToChannelId = new Dictionary(); var viewMode = SerializedSettings.ISOViewMode; using (var eChannel = testSetupChannelIdToGroupChannelId.GetEnumerator()) { while (eChannel.MoveNext()) { channelNameToChannelId[eChannel.Current.Value.GetChannelName(viewMode)] = eChannel.Current.Key; } } var oldListCalculatedChannels = CalculatedChannels.ToArray(); //Remove the old embedded Group RemoveGroup(_updateGroup); //Add the static Group as the new embedded Group staticGroup.LoadHardware(); if (!Groups.Any()) { staticGroup.DisplayOrder = 1; } else { staticGroup.DisplayOrder = 1 + Groups.Max(g => g.DisplayOrder); } var allHardware = DASHardwareList.GetAllHardware(); var lookup = new Dictionary(); foreach (var h in allHardware) { lookup[h.DASId] = h; } var hardware = staticGroup.IncludedHardware; foreach (var id in hardware) { AddHardware(id, lookup); } ClearHardware(); var channelDefaults = DbOperations.GetChannelSettingDefaults(); staticGroup.StaticGroupId = _updateGroup.StaticGroupId; AddGroup(staticGroup, channelDefaults); var channelsForGroup = ChannelsForGroup[staticGroup].ToList(); channelsForGroup.Sort(); var channels = GetChannels(); //build a lookup of old channel to new channel for channels in the group that got pulled // 15400 Group Push/Pull should allow the LT to be preserved in the Test Setup regardless of push/pull var oldChannelToNewChannel = new Dictionary(); foreach (var channel in channelsForGroup) { channels.Remove(channel); if (channel.IsBlank()) { continue; } var name = channel.GetChannelName(viewMode); //find the old channel using the channel name if (!channelNameToChannelId.ContainsKey(name)) { //didn't find it, either it's a new channel or was renamed somewhere continue; } var old = testSetupChannelIdToGroupChannelId[channelNameToChannelId[name]]; //18096 Don't overwrite any embedded Group IsoCode characters with '?' from static Group var sb = new StringBuilder(channel.IsoCode); for (int i = 0; i < 16; i++) { if (channel.IsoCode[i] == '?') { sb[i] = old.IsoCode[i]; } } channel.IsoCode = sb.ToString(); oldChannelToNewChannel[old] = channel; } var errors = new List(); //now correct level trigger and calculated channels // 15400 Group Push/Pull should allow the LT to be preserved in the Test Setup regardless of push/pull CorrectCalculatedChannels(oldChannelToNewChannel, testSetupChannelIdToGroupChannelId, ref errors, viewMode); CorrectLevelTriggerChannels(oldChannelToNewChannel, testSetupChannelIdToGroupChannelId, ref errors, viewMode); //if there are any errors, push them to the user if (errors.Any()) { var eventAggregator = ContainerLocator.Container.Resolve(); eventAggregator.GetEvent().Publish(new PageErrorArg(errors.ToArray(), null)); } var insertPoint = -1; for (var i = channels.Count - 1; i >= 0; i--) { if (channels[i].IsBlank()) { continue; } insertPoint = i + 1; break; } if (insertPoint < 0) { insertPoint = 0; } for (var i = channelsForGroup.Count - 1; i >= 0; i--) { channels.Insert(insertPoint, channelsForGroup[i]); } for (var i = 0; i < channels.Count; i++) { if (channels[i].IsBlank()) { continue; } channels[i].TestSetupOrder = 1 + i; } //Re-evaluate the Position and Test Object since they may have changed staticGroup.DeterminePositionAndTestObject(channelsForGroup.ToArray()); MoveGroupToDisplayOrder(staticGroup, _updateGroup.DisplayOrder); //Needed to remove orange button staticGroup.IsDifferentThanStaticGroup = false; } /// /// returns a lookup of group channel id to group channel for ALL channels in a test /// this is useful when correcting level trigger and calculated channels post a group pull/push /// the structure returned by the function allows looking up say an input channel given an id /// even if those channels are no longer in the test (because of push/pull operation) /// 15400 Group Push/Pull should allow the LT to be preserved in the Test Setup regardless of push/pull /// /// /// private IReadOnlyDictionary GetIGroupChannelIdToIGroupChannel() { var lookup = new Dictionary(); var channels = GetChannels(); foreach (var ch in channels) { if (ch.IsBlank()) { continue; } lookup[ch.Id] = ch; } return lookup; } /// /// this function "corrects" level trigger channels after a pull operation /// it does this using the parameter input data which tells /// if any channels have changed as a result of a pull operation, it then just /// remaps those channels /// 15400 Group Push/Pull should allow the LT to be preserved in the Test Setup regardless of push/pull /// /// /// /// /// private void CorrectLevelTriggerChannels( IReadOnlyDictionary oldChannelToNewChannel, IReadOnlyDictionary testSetupChannelIdToGroupChannel, ref List errors, IsoViewMode viewMode) { var levelTriggerKeys = LevelTriggerChannels.Keys.ToArray(); foreach (var key in levelTriggerKeys) { var lt = LevelTriggerChannels[key]; if (oldChannelToNewChannel.ContainsKey(lt.GroupChannel)) { //at this point in time we are still pre-commit for the new group //so we just point the group channel to the new group channel //the new group channel doesn't have valid id yet (until we commit) lt.GroupChannel = oldChannelToNewChannel[lt.GroupChannel]; } } } /// /// this method "corrects" calculated channels after a push/pull operation /// it does this by replacing channels from the pulled group with new channels /// after the pull operation /// 15400 Group Push/Pull should allow the LT to be preserved in the Test Setup regardless of push/pull /// /// /// /// /// private void CorrectCalculatedChannels( IReadOnlyDictionary oldChannelToNewChannel, IReadOnlyDictionary testSetupChannelIdToGroupChannel, ref List errors, IsoViewMode viewMode) { var currentCalculatedChannels = CalculatedChannels; foreach (var cc in currentCalculatedChannels) { var inputIds = cc.InputChannelIds.ToArray(); var inputChannels = new List(); for (var i = 0; i < inputIds.Length; i++) { //this is the old for the channel, it'd be more ideal if we have access //to the group channel in the case it's a new channel not in the db, but //I don't see how to get that info currently... var oldId = inputIds[i]; if (long.TryParse(oldId, out var l)) { //now get that channel from the test //if we have a new channel, use it, otherwise use the old one if (testSetupChannelIdToGroupChannel.ContainsKey(l)) { var oldChannel = testSetupChannelIdToGroupChannel[l]; if (oldChannelToNewChannel.ContainsKey(oldChannel)) { //there's a new channel that replaces the old one, so use the new one inputChannels.Add(oldChannelToNewChannel[oldChannel]); } else { //there's no new channel replacing the old one, so just use the old one inputChannels.Add(oldChannel); } } } } cc.SetChannels(inputChannels.ToArray()); } } #region MetaData private readonly Dictionary _metaDataLookup = new Dictionary(); // FB14362 This method will keep the _metaDataLookup dictionary updated to prevent the missing data from database. public void UpdateMetaDataLookupKey(char oldKey, char newKey) { if (_metaDataLookup.ContainsKey(oldKey)) { var toMetaData = new TestObjectMetaData(newKey, _metaDataLookup[oldKey]); //Do not remove the oldKey if it presents in any other groups already. if (!Groups.Any(p => p.TestObject.Any() && p.TestObject[0] == oldKey) && oldKey != '?') { _metaDataLookup.Remove(oldKey); } if (!_metaDataLookup.ContainsKey(newKey)) { _metaDataLookup.Add(newKey, toMetaData); } } } public TestObjectMetaData[] GetMetaData() { var metas = new List(); foreach (KeyValuePair md in _metaDataLookup) { metas.Add(md.Value); } foreach (var to in TestObjects) { if (to.TestObject == null) continue; var m = GetMetaData(to.TestObject.Test_Object[0]); if (!metas.Contains(m)) { metas.Add(m); } } foreach (var to in AddedGroups) { if (to.TestObject == null) continue; var m = GetMetaData(to.TestObject.Test_Object[0]); if (!metas.Contains(m)) { metas.Add(m); } } metas.Sort(CompareMeta); return metas.ToArray(); } public TestObjectMetaData GetMetaData(char testobject) { if (!_metaDataLookup.ContainsKey(testobject)) { _metaDataLookup.Add(testobject, new TestObjectMetaData(testobject)); } return _metaDataLookup[testobject]; } public TestObjectMetaData GetMetaData(char testobject, int testObjectNumber) { if (!_metaDataLookup.ContainsKey(testobject)) { _metaDataLookup.Add(testobject, new TestObjectMetaData(testobject, testObjectNumber)); } return _metaDataLookup[testobject]; } private TestSetupMetaData _testSetupMeta = new TestSetupMetaData(DataModelSettings.RequireXCrashCompatibilityForISOExports); public TestSetupMetaData GetTestMetaData() { return _testSetupMeta; } public void SetTestSetupMetaData(TestSetupMetaData meta) { _testSetupMeta = meta; } public void SetMetaData(char testobject, TestObjectMetaData meta) { _metaDataLookup[testobject] = meta; } public void SetMetaData(TestObjectMetaData[] metaLookup) { foreach (var testObjectMetaData in metaLookup) { _metaDataLookup[testObjectMetaData.TestObject] = testObjectMetaData; } } private int CompareMeta(TestObjectMetaData left, TestObjectMetaData right) { if (left == right) { return 0; } if (null == left) { return -1; } return null == right ? 1 : left.TestObject.CompareTo(right.TestObject); } #endregion MetaData public void Rename(string newName) { foreach (var addedGroup in AddedGroups) { addedGroup.Rename(Name, newName); ReplaceSensorsInLookup(addedGroup, Name, newName); } RenameLevelTriggerChannels(Name, newName); RenameCalculatedChannels(Name, newName); GetTestMetaData().SetPropertyValue(TestSetupMetaData.Fields.Title, newName); Name = newName; } } }