#define LEVEL_TRIGGER_DEFINED using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows.Forms; using System.Threading; using System.IO; using DTS.DASLib.Command.SLICE; using DTS.DASLib.Command; using DTS.Common.DAS.Concepts; using DTS.Common.DASResource; using DTS.Common.ICommunication; using DTS.Common.Interface.DASFactory; using DTS.Common.Utilities; using DTS.Common.Utilities.Logging; using DTS.DASLib.Command.SLICE.DownloadCommands; using DTS.Common.Enums.Sensors; using DTS.Common.Interface.Connection; using DTS.Common.Classes.Connection; using DTS.Common.Interface.DASFactory.Diagnostics; using DTS.Common.Enums.DASFactory; using DTS.Common.Interface.Communication; using DTS.Common.Interface.DASFactory.Config; using DTS.Common.Interface.StatusAndProgressBar; using DTS.Common.Enums; using DTS.Common.Utilities.LTLogging; using System.IO.Ports; using DTS.Common.Interface.DASFactory.Download; using DTS.Common.Classes.DSP; namespace DTS.DASLib.Service { public partial class Slice : Communication, IDASCommunication, IConfigurationActions, IDiagnosticsActions, ITriggerCheckActions, IRealTimeActions, IArmActions, IDownloadActions where T : IConnection, new() { protected virtual void SetUDPAlignOnPPS() { try { if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.UDPAlignOnPPS) && (this is IAlignUDPToPPSAware alignAware)) { SetSystemAttributeSLICE6AIR set = new SetSystemAttributeSLICE6AIR(this); set.SetValue(AttributeTypes.SystemAttributesSLICE6AIR.UDPAlignOnPpsEnable, alignAware.AlignUDPToPPS ? (ushort)1 : (ushort)0, true); set.SyncExecute(); } } catch (Exception ex) { APILogger.Log(ex); } } /// /// run a command to discover any connected devices to the DAS /// currently only S6DB supports directly querying for connected devices /// public virtual void QueryConnectedDevices() { } void IConfigurationActions.VerifyConfig(bool DoStrictCheck) { VerifyConfig(DoStrictCheck, null); } void IConfigurationActions.VerifyConfig(bool DoStrictCheck, ErrorCallback failedChallengeFunc) { VerifyConfig(DoStrictCheck, failedChallengeFunc); } /// /// returns true if this is a Digital Input Module, false otherwise /// private bool IsDIM() { try { if (null == SerialNumber) { return false; } var serial = SerialNumber.ToUpper(); return serial.StartsWith("SPD") || serial.StartsWith("SLD"); } catch( Exception ex ) { APILogger.Log(ex); } return false; } protected virtual void VerifyConfig(bool DoStrictCheck, ErrorCallback failedChallengeFunc) { // look thru all the config data and make sure it's fit to be used // our caller have already checked for null, but... if (ConfigData == null) { // "Slice.VerifyConfig: ConfigData is null" throw new ArgumentException(Strings.Slice_VerifyConfig_Err1); } if (string.IsNullOrEmpty(ConfigData.Description)) { // "Slice.VerifyConfig: ConfigData.Description is null" //throw new ArgumentException(Strings.Slice_VerifyConfig_Err2); ConfigData.Description = ""; } if (DoStrictCheck && string.IsNullOrEmpty(ConfigData.TestID)) { // "Slice.VerifyConfig: ConfigData.TestID is null" //throw new ArgumentException(Strings.Slice_VerifyConfig_Err3); APILogger.Log("ConfigData.TestID is null"); ConfigData.TestID = string.Empty; //ConfigData.TestID = "Default Test ID"; } // we don't care about EID's here if (ConfigData.Modules == null || ConfigData.Modules.Length == 0 || ConfigData.Modules.Length > DASInfo.Modules.Length) { // "Slice.VerifyConfig: ConfigData.Modules is null, empty or too long" throw new ArgumentException(Strings.Slice_VerifyConfig_Err4); } // SLICE have only DAS storage if (DASInfo.MaxEventStorageSpaceInBytes == null) { DASInfo.MaxEventStorageSpaceInBytes = 1024 * 10; } if (DASInfo.MaxEventStorageSpaceInBytes != null) { if (DASInfo.NumberOfBytesPerSampleClock == null) { throw new ArgumentException("Slice.VerifyConfig: DAS is missing NumberOfBytesPerSampleClock"); } // if data is stored on DAS all modules (skipping the clock modules) must have same samplerate and pre/post if (ConfigData.Modules.Where(mod => !mod.IsClock()).Select(module => module.SampleRateHz).Distinct().Count() != 1) { if (DoStrictCheck) { throw new ArgumentException("Slice.VerifyConfig: DAS modules have more than one unique samplerate"); } else { APILogger.Log("more than one sample rate, normalizing to the first one", ConfigData.Modules[0].SampleRateHz); foreach (var module in ConfigData.Modules) { module.SampleRateHz = ConfigData.Modules[0].SampleRateHz; } } } if (ConfigData.Modules.Where(mod => !mod.IsClock()).Select(module => module.PreTriggerSeconds).Distinct().Count() != 1) { if (DoStrictCheck) { throw new ArgumentException("Slice.VerifyConfig: DAS modules have more than one unique PreTriggerSeconds"); } else { APILogger.Log("more than one pretriggersecond, normalizing to first one.", ConfigData.Modules[0].PreTriggerSeconds); foreach (var module in ConfigData.Modules) { module.PreTriggerSeconds = ConfigData.Modules[0].PreTriggerSeconds; } } } if (ConfigData.Modules.Where(mod => !mod.IsClock()).Select(module => module.PostTriggerSeconds).Distinct().Count() != 1) { if (DoStrictCheck) { APILogger.Log("more than one posttriggersecond, normalizing to first one.", ConfigData.Modules[0].PostTriggerSeconds); } else { foreach (var module in ConfigData.Modules) { module.PostTriggerSeconds = ConfigData.Modules[0].PostTriggerSeconds; } } } var secondsToRecord = ConfigData.Modules[0].PreTriggerSeconds + ConfigData.Modules[0].PostTriggerSeconds; var sampleClocksToRecord = (ulong)(secondsToRecord * ConfigData.Modules[0].SampleRateHz + 1); var bytesToRecord = sampleClocksToRecord * (ulong)DASInfo.NumberOfBytesPerSampleClock; // FB15353: Bypass NAND logic for a for a streaming-only device in a streaming-only test if (bytesToRecord > (ulong)DASInfo.MaxEventStorageSpaceInBytes && ((null != DASInfo.DeviceStreamingOnly && !(bool)DASInfo.DeviceStreamingOnly) || null == DASInfo.DeviceStreamingOnly) && DFConstantsAndEnums.RecordingMode.S6A_DeviceStreamingOnly != ConfigData.Modules[0].RecordingMode) { var message = string.Format("Bytes to record exceeds maximum possible, {0}secs*{1}sps={2}>{3}", secondsToRecord + 1, ConfigData.Modules[0].SampleRateHz, bytesToRecord, DASInfo.MaxEventStorageSpaceInBytes); APILogger.Log(message); if (DoStrictCheck) { throw new ArgumentException(message); } } } else { throw new ArgumentException("DAS is missing MaxEventStorageSpaceInBytes"); } var bModuleAAFilterRateChallenge = false; for (var moduleIdx = 0; moduleIdx < ConfigData.Modules.Length; moduleIdx++) { var module = ConfigData.Modules[moduleIdx]; if (module.IsClock() || module.IsUart() || module.IsStreamIn() || module.IsStreamOut()) continue; if (module.ModuleArrayIndex != moduleIdx) { // "Slice.VerifyConfig: ConfigData.Module[{0}].ModuleNumber doesn't match index" throw new ArgumentException(string.Format(Strings.Slice_VerifyConfig_Err5, moduleIdx)); } // we don't care about EID's here if (module.AAFilterRateHz > MaxAAFilterRateHz) { module.AAFilterRateHz = MaxAAFilterRateHz; bModuleAAFilterRateChallenge = true; // "Slice.VerifyConfig: ConfigData.Module[{0}].AAFilterRateHz must be below {1}" //throw new ArgumentException(string.Format(Strings.Slice_VerifyConfig_Err6, moduleIdx, MaxAAFilterRateHz)); } switch (module.RecordingMode) { case DFConstantsAndEnums.RecordingMode.RecorderMode: case DFConstantsAndEnums.RecordingMode.CircularBuffer: case DFConstantsAndEnums.RecordingMode.RecorderModePlusUART: case DFConstantsAndEnums.RecordingMode.CircularBufferPlusUART: case DFConstantsAndEnums.RecordingMode.Streaming: case DFConstantsAndEnums.RecordingMode.AerospaceWithMotion: case DFConstantsAndEnums.RecordingMode.Scheduled: case DFConstantsAndEnums.RecordingMode.Interval: case DFConstantsAndEnums.RecordingMode.MultipleEventRecorderTriggerStart: break; case DFConstantsAndEnums.RecordingMode.AutoActiveMode: case DFConstantsAndEnums.RecordingMode.AutoRecorderMode: case DFConstantsAndEnums.RecordingMode.AutoCircularBufferMode: case DFConstantsAndEnums.RecordingMode.AutoRecorderModePlusUART: case DFConstantsAndEnums.RecordingMode.AutoCircularBufferPlusUART: case DFConstantsAndEnums.RecordingMode.S6A_DeviceStreamingOnly: if (!IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.AutoArm)) { throw new ArgumentException(string.Format(Strings.Slice_VerifyConfig_Err7, moduleIdx)); } break; case DFConstantsAndEnums.RecordingMode.ContinuousRecorderMode: case DFConstantsAndEnums.RecordingMode.HybridRecorderMode: case DFConstantsAndEnums.RecordingMode.MultiHybridRecorderMode: case DFConstantsAndEnums.RecordingMode.ContinuousRecorderModePlusUART: if (!IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.MultipleAndHybridEvents)) { throw new ArgumentException(string.Format(Strings.Slice_VerifyConfig_Err7, moduleIdx)); } break; case DFConstantsAndEnums.RecordingMode.a14_NormalRecorderAndStreamSubSampleMode: case DFConstantsAndEnums.RecordingMode.a16_CircularBufferAndStreamSubSampleMode: case DFConstantsAndEnums.RecordingMode.a17_MultiCircularBufferAndStreamSubSampleMode: case DFConstantsAndEnums.RecordingMode.a15_MultiRecorderAndStreamSubSampleMode: case DFConstantsAndEnums.RecordingMode.a18_HybridRecorderAndStreamSubSampleMode: case DFConstantsAndEnums.RecordingMode.a19_MultiHybridRecorderAndStreamSubSampleMode: if (!IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.RecordAndStreamSubSample)) { throw new ArgumentException($"{Strings.Slice_VerifyConfigSettings} module index: {moduleIdx}, mode: {module.RecordingMode}"); } break; case DFConstantsAndEnums.RecordingMode.MultipleEventRAMActive: case DFConstantsAndEnums.RecordingMode.RAMActive: if (!IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.ActiveRAM)) { throw new ArgumentException($"{Strings.Slice_VerifyConfigSettings} module index: {moduleIdx}, mode: {module.RecordingMode} not supported"); } break; case DFConstantsAndEnums.RecordingMode.a26_MultiRecordOnBootDataMode: case DFConstantsAndEnums.RecordingMode.a28_MultiRecordOnBootAndUartDataMode: if (!IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.RecordOnBoot)) { throw new ArgumentException(string.Format(Strings.Slice_VerifyConfig_Err7, moduleIdx)); } break; default: throw new ArgumentException(string.Format(Strings.Slice_VerifyConfig_Err7, moduleIdx)); } // if we have a dim, don't use the max sample rate as is // instead get the rate given the current configuration // I only did this for DIM to avoid side effects // http://manuscript.dts.local/f/cases/43548/Add-support-for-1-5MB-SPS-on-SPD var maxRate = MaxSampleRateHz; try { if (IsDIM() && MaxSampleRate(18) > maxRate) { maxRate = MaxSampleRate(18); } } catch( Exception ex) { APILogger.Log(ex); } if(module.SampleRateHz > maxRate) { // "Slice.VerifyConfig: ConfigData.Module[{0}].SampleRateHz must be below {1}" if (DoStrictCheck) { throw new ArgumentException(string.Format(Strings.Slice_VerifyConfig_Err8, moduleIdx, maxRate)); } else { module.SampleRateHz = maxRate; } } // module.MaxRecordingSamples is null for SLICE if (module.Channels == null || module.Channels.Length == 0 || module.Channels.Length > DASInfo.Modules[moduleIdx].NumberOfChannels) { // "Slice.VerifyConfig: ConfigData.Module[{0}].Channels is null, empty or too long" throw new ArgumentException(string.Format(Strings.Slice_VerifyConfig_Err10, moduleIdx)); } for (var channelIdx = 0; channelIdx < module.Channels.Length; channelIdx++) { var channel = (DASChannel)module.Channels[channelIdx]; if (channel.ConfigurationMode == DFConstantsAndEnums.ConfigMode.Disabled) continue; if (channel.OwningModule != module) { // "Slice.VerifyConfig: ConfigData.Module[{0}].Channels[{1}].OwningModule doesn't mach owner" throw new ArgumentException(string.Format(Strings.Slice_VerifyConfig_Err11, moduleIdx, channelIdx)); } if (channel.ModuleChannelNumber != channelIdx) { // "Slice.VerifyConfig: ConfigData.Module[{0}].Channels[{1}].ChannelNumber doesn't mach index" throw new ArgumentException(string.Format(Strings.Slice_VerifyConfig_Err12, moduleIdx, channelIdx)); } if (channel.ConfigurationMode != DFConstantsAndEnums.ConfigMode.Normal && channel.ConfigurationMode != DFConstantsAndEnums.ConfigMode.DummyArm && channel.ConfigurationMode != DFConstantsAndEnums.ConfigMode.Disabled) { // "Slice.VerifyConfig: ConfigData.Module[{0}].Channels[{1}].mode has incorrect value" throw new ArgumentException(string.Format(Strings.Slice_VerifyConfig_Err13, moduleIdx, channelIdx)); } var infoModule = DASInfo.Modules[module.ModuleArrayIndex]; if (!(channel is AnalogInputDASChannel) && infoModule.TypeOfModule != DFConstantsAndEnums.ModuleType.SLICEPro_TOM) { // "Slice.VerifyConfig: ConfigData.Module[{0}].Channels[{1}] is not an analog input channel" throw new ArgumentException(string.Format(Strings.Slice_VerifyConfig_Err14, moduleIdx, channelIdx)); } if (channel is AnalogInputDASChannel analog) { if (!analog.SupportedBridges.Contains(analog.TypeOfBridge)) { throw new ArgumentException("Unsupported bridge mode: " + analog.TypeOfBridge.ToString()); } if (analog.ShuntIsEnabled) { if (analog.BridgeResistanceOhms < SensorConstants.MIN_BRIDGE_RESISTANCE_OHMS) { analog.BridgeResistanceOhms = SensorConstants.MIN_BRIDGE_RESISTANCE_OHMS; } if (analog.BridgeResistanceOhms > SensorConstants.MAX_BRIDGE_RESISTANCE_OHMS) { analog.BridgeResistanceOhms = SensorConstants.MAX_BRIDGE_RESISTANCE_OHMS; } } if (analog.BypassAAFilter) { module.AAFilterRateHz = MaxAAFilterRateHz; // "Slice.VerifyConfig: ConfigData.Module[{0}].Channels[{1}], bypassing AA filter is not supported" //throw new ArgumentException(string.Format(Strings.Slice_VerifyConfig_Err17, moduleIdx, channelIdx)); } if (analog.DesiredRangeWithHeadroomEU < 0) { // "Slice.VerifyConfig: ConfigData.Module[{0}].Channels[{1}].DesiredRange must be positive" throw new ArgumentException(string.Format(Strings.Slice_VerifyConfig_Err18, moduleIdx, channelIdx)); } if (DoStrictCheck && string.IsNullOrEmpty(analog.EngineeringUnits) && channel.ConfigurationMode != DFConstantsAndEnums.ConfigMode.DummyArm) { // "Slice.VerifyConfig: ConfigData.Module[{0}].Channels[{1}].EngineeringUnits can't be null or empty" throw new ArgumentException(string.Format(Strings.Slice_VerifyConfig_Err19, moduleIdx, channelIdx)); } if (analog.ZeroMethod != ZeroMethodType.AverageOverTime && analog.ZeroMethod != ZeroMethodType.UsePreEventDiagnosticsZero && analog.ZeroMethod != ZeroMethodType.None) { // "Slice.VerifyConfig: ConfigData.Module[{0}].Channels[{1}].ZeroMethod has invalid value" throw new ArgumentException(string.Format(Strings.Slice_VerifyConfig_Err22, moduleIdx, channelIdx)); } if (DoStrictCheck && analog.ZeroMethod == ZeroMethodType.AverageOverTime) { if (analog.ZeroAverageStartSeconds >= analog.ZeroAverageStopSeconds) { // "Slice.VerifyConfig: ConfigData.Module[{0}].Channels[{1}] zero average start must be less than zero average stop" throw new ArgumentException(string.Format(Strings.Slice_VerifyConfig_Err23, moduleIdx, channelIdx)); } if (analog.ZeroAverageStopSeconds > module.PostTriggerSeconds) { // "Slice.VerifyConfig: ConfigData.Module[{0}].Channels[{1}] zero average stop must be less than post-trigger" if (DoStrictCheck) { throw new ArgumentException(string.Format(Strings.Slice_VerifyConfig_Err25, moduleIdx, channelIdx)); } } } } } } if (bModuleAAFilterRateChallenge) { var err = string.Format("Default hardware filter frequency for {0} is out of range. Press OK to continue and use the max hardware rate or cancel to cancel.", SerialNumber); var dr = DialogResult.Cancel; APILogger.Log("MessageBox", err, dr); if (dr == DialogResult.Cancel) { throw new ArgumentOutOfRangeException(DASModule.AAFilterRateHzTag); } } if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.InitHardwareInputLines) && DoStrictCheck) { var ihl = new InitializeHardwareLines(this, 6000); try { //performance improvement, reduce unnecessary commands //in systems where polarity doesn't need to get set, just eliminate the calls if (RunTestVariables.InRunTest && RunTestVariables.DontSetPolarityInRunTest) { //don't set } else { var ssaStart = new SetSystemAttribute(this, SetSystemAttribute.Default_IO_Timeout); var ssaTrigger = new SetSystemAttribute(this, SetSystemAttribute.Default_IO_Timeout); ssaStart.SetValue(AttributeTypes.SystemAttributes.StartRecordPolarity, (byte)(InvertStart ? 1 : 0), true); ssaTrigger.SetValue(AttributeTypes.SystemAttributes.TriggerPolarity, (byte)(InvertTrigger ? 1 : 0), true); ssaStart.SyncExecute(); ssaTrigger.SyncExecute(); } ihl.SyncExecute(); } catch (Exception ex) { if (ihl.StartRecordShorted) { if (!IgnoreShortedStart) { throw new StartShortedException(string.Format(Strings.StartRecordShorted, SerialNumber)); } } if (ihl.TriggerInputShorted) { if (!IgnoreShortedTrigger) { throw new TriggerShortedException(string.Format(Strings.TriggerShorted, SerialNumber)); } } else if (!ihl.StartRecordShorted && !ihl.TriggerInputShorted) { //rely on the handlers above for shorted start or trigger, only pass back any _other_ errors throw ex; } } } } protected const ushort SLICE_HALFBRIDGE_RESISTANCE = 3000; protected virtual uint MaxAAFilterRateHz => DTS.Common.Constant.DASSpecific.SLICE.MaxAAFilterRateHz; protected virtual uint MaxSampleRateHz => 200000; private const double InputRangeMV = 2500; private const double HeadRoomPercent = 0.0; #region Query configuration void IConfigurationActions.QueryTestSetup(ServiceCallback callback, object userData) { var info = new SliceServiceQueryTestSetupAsyncInfo(callback, userData, ConfigData.TestSetupUniqueId.ToString()); LaunchAsyncWorker("Slice.QueryTestSetup", new WaitCallback(AsyncQueryTestSetup), info); } /// /// Retrieve the info to fill in the ConfigData property /// /// The delegate to report to /// User supplied data that we pass along void IConfigurationActions.QueryConfiguration(ServiceCallback callback, object userData, uint crc, string strConfig, bool bReadIds, bool bDeviceScaleFactors, bool differentModuleCountsAreOK) { var info = new SliceServiceAsyncInfo(callback, userData); LaunchAsyncWorker("Slice.QueryConfiguration", new WaitCallback(AsyncQueryConfiguration), info); } void IConfigurationActions.UpdateConfigurationFromFile(ServiceCallback callback, object userData, string filePath) { var info = new SliceServiceAsyncInfo(callback, userData) { functionData = filePath }; LaunchAsyncWorker("Slice.UpdateConfigurationFromFile", new WaitCallback(AsyncUpdateConfigurationFromFile), info); } protected virtual void AsyncUpdateConfigurationFromFile(object asyncInfo) { var info = asyncInfo as SliceServiceAsyncInfo; info.Error("Not supported yet"); return; } /// /// The DAS may store configuration information, but the information could be invalid. /// in particular we need to throw it out if the old module is IEPE and we aren't in IEPE or vice versa /// /// protected bool VerifyConfig() { try { if (null == ConfigData) { return false; } for (var i = 0; i < ConfigData.Modules.Length && i < DASInfo.Modules.Length; i++) { //30429 Invalidate/fail sooner than Arm step if DAS doesn't have streaming capability/channel (TSR AIR may or may not) if (DASInfo.Modules[i] == null) { continue; } foreach (var channel in ConfigData.Modules[i].Channels) { //If a Slice DIM (internal) module was not configured correctly (SupportedBridges is not DigitalInput only), //return false so that if the module has been corrected after the bad config had been written to the DAS when //a previous test ran without the incorrect channels, a default config will be generated based on the corrections. if ((DASInfo.Modules[i].TypeOfModule == DFConstantsAndEnums.ModuleType.ProDIM) && (((channel as AnalogInputDASChannel).SupportedBridges.Length != 1) || ((channel as AnalogInputDASChannel).SupportedBridges[0] != SensorConstants.BridgeType.DigitalInput) || ((channel as AnalogInputDASChannel).TypeOfBridge != SensorConstants.BridgeType.DigitalInput))) { return false; } if (channel.ConfigurationMode == DFConstantsAndEnums.ConfigMode.Normal) { var IEPE = false; if (channel is AnalogInputDASChannel analog) { IEPE = analog.IEPEChannel; } if (IEPE) { if (DASInfo.Modules[i].IsProgrammable || //if we received an invalid mode during setup, then we don't know if it is programmable //so assume we are, otherwise the configuration will be thrown out as invalid //15932 Error when performing test when S6A is streaming (null != DASArmStatus && DASArmStatus.ReceivedInvalidModeDuringSetup)) {; } else if (DASInfo.Modules[i].TypeOfModule != DFConstantsAndEnums.ModuleType.SLICEIEPE && DASInfo.Modules[i].TypeOfModule != DFConstantsAndEnums.ModuleType.SliceIEPE2High) { return false; } } else { if (DASInfo.Modules[i].IsProgrammable) {; } else if (DASInfo.Modules[i].TypeOfModule == DFConstantsAndEnums.ModuleType.SLICEIEPE || DASInfo.Modules[i].TypeOfModule == DFConstantsAndEnums.ModuleType.SliceIEPE2High) { return false; } } } else if (channel.ConfigurationMode == DFConstantsAndEnums.ConfigMode.Clock) { // 17776 Clock modules get their own type if (channel is TimestampDASChannel stamp) {; } else { return false; } } else if (channel.ConfigurationMode == DFConstantsAndEnums.ConfigMode.StreamIn) { // 26826 Stream modules get their own type if (channel is StreamInputDASChannel streamin) {; } else { return false; } } } } return true; } catch (Exception ex) { APILogger.Log("Problem verifying config - ", ex); } return false; } /// /// returns true if should check for ids for batteries, /// false otherwise /// /// protected bool ShouldCheckForBatteryIds() { if (this is EthernetSlice6 || this is EthernetSlice6Air || this is EthernetSlice6AirBridge || this is EthernetTsrAir) { return false; } //doesn't have battery ids return true; } protected virtual void AsyncQueryTestSetup(object configAsyncInfo) { var info = configAsyncInfo as SliceServiceAsyncInfo; info.Success(); } protected virtual void AsyncQueryConfiguration(object configAsyncInfo) { var info = configAsyncInfo as SliceServiceAsyncInfo; var displayOrder = new int[] { -1 }; var dasDisplayOrder = -1; try { dasDisplayOrder = GetDASDisplayOrder(); } catch (Exception ex) { APILogger.Log("Failed to query user attributes", ex); } info.Progress(10); try { displayOrder = GetChannelDisplayOrder(); } catch (Exception ex) { APILogger.Log("Failed to query user attributes", ex); } info.Progress(20); try { // pickup the channel data var config = GetConfigAttributes(this);//new ConfigAttributes(this); try { var StoredInfoString = config.RetrieveXMLConfig(ConfigAttributes.FileStore.Diagnostic, this); ConfigData = ConfigurationData.DeserializeFromString(StoredInfoString); info.Progress(50); if (!VerifyConfig()) { ConfigData = null; } if (ConfigData == null || ConfigData.Modules == null || ConfigData.Modules.Length == 0) { APILogger.LogString("QueryConfiguration: ConfigData or Modules empty, using default config"); ConfigData = MakeDefaultConfigFromInfo(); } else if (DASInfo == null || DASInfo.Modules == null || DASInfo.Modules.Length != ConfigData.Modules.Length) { APILogger.LogString("QueryConfiguration: DASInfo or Modules empty, using default config"); ConfigData = MakeDefaultConfigFromInfo(); } else { for (var i = 0; i < ConfigData.Modules.Length && i < DASInfo.Modules.Length; i++) { var bIEPE = false; switch (DASInfo.Modules[i].TypeOfModule) { case DFConstantsAndEnums.ModuleType.SLICEIEPE: bIEPE = true; break; } foreach (var channel in ConfigData.Modules[i].Channels) { if (bIEPE) { ((AnalogInputDASChannel)channel).SupportedBridges = new[] { SensorConstants.BridgeType.IEPE }; } else { ((AnalogInputDASChannel)channel).SupportedBridges = new[]{ SensorConstants.BridgeType.FullBridge, SensorConstants.BridgeType.HalfBridge}; } } } } info.Progress(60); } catch (CanceledException) { // cancel must be propagated throw; } catch (NotConnectedException) { info.Error(string.Format("Connection with {0} failed", SerialNumber)); return; } catch (System.Net.Sockets.SocketException socketException) { APILogger.LogString("QueryConfiguration: RetrieveXMLConfig threw exception"); APILogger.LogException(socketException); info.Error($"Connection with {SerialNumber} failed"); return; } catch (Exception ex) { APILogger.LogString("QueryConfiguration: RetrieveXMLConfig threw exception"); APILogger.LogException(ex); if (ex.Message.Contains("ReceiveFailed")) { info.Error($"Connection with {SerialNumber} failed"); return; } // something happend that prevents us from getting the stored config, make a default ConfigData = MakeDefaultConfigFromInfo(); } if (null != ConfigData) { ConfigData.DisplayOrder = displayOrder; ConfigData.DasDisplayOrder = dasDisplayOrder; } // get scale factors try { var scaleFactors = GetScaleFactors(); #if LEVEL_TRIGGER_DEFINED // These are currently unutilized since we have no application for the offset-adjusted // level trigger values outside of the Slice firmware. // // var levelTriggers = GetLevelTriggers( ); #endif info.Progress(70); // set the OwningDAS and OwningModule since it was skipped during serialization foreach (var iDASModule in ConfigData.Modules) { var module = (DASModule)iDASModule; module.SampleRateHz = Convert.ToUInt32(config.ActualSampleRate); module.OwningDAS = this; foreach (var iDASChannel in module.Channels) { var channel = (DASChannel)iDASChannel; channel.OwningModule = module; // pick up the scalefactor for this channel if (channel is AnalogInputDASChannel analog) { var dasChannelNumber = analog.Number; if (dasChannelNumber < scaleFactors.Length) { analog.ScalefactorMilliVoltsPerADC = scaleFactors[dasChannelNumber]; } } } } info.Progress(80); } catch (System.Net.Sockets.SocketException socketException) { APILogger.LogString("QueryConfiguration: RetrieveXMLConfig threw exception"); APILogger.LogException(socketException); info.Error(string.Format("Connection with {0} failed", SerialNumber)); return; } catch (NotConnectedException) { info.Error($"Connection with {SerialNumber} failed"); return; } catch (CanceledException) { // cancel must be propagated throw; } catch (Exception ex) { APILogger.LogString("QueryConfiguration: GetScaleFactors threw exception"); APILogger.LogException(ex); // we don't need no stinking scale factors } // get sensor ID's if (ConfigData.Modules != null) { foreach (var iDASModule in ConfigData.Modules) { var module = (DASModule)iDASModule; module.OwningDAS = this; // in case we bailed out above foreach (var iDASchannel in module.Channels) { var channel = (DASChannel)iDASchannel; channel.OwningModule = module; // in case bailed out above var dasChannelNumber = DASInfo.MapModuleArrayIndexAndChannelNum2DASChannel(module.ModuleArrayIndex, channel.ModuleChannelNumber); channel.IDs = EIDReader.RetriveEIDs(this, dasChannelNumber, 0); } IEID[] ids = null; //performance improvement, skip getting battery ids if you don't need too if (ShouldCheckForBatteryIds()) { ids = EIDReader.RetriveEIDs(this, 255, 0); } else { ids = null; } if (null != ids && 0 != ids.Length) { module.OwningDAS.DASInfo.BatteryID = ids[0]; module.OwningDAS.SetDASInfo(); } } } info.Progress(90); RetrieveEventGuids(); DASInfo.MaxEventStorageSpaceInBytes = ReadSampleStorageSizeInBytes(); info.Progress(95); GetAutoArmStatus(); info.Success(); } catch (System.Net.Sockets.SocketException socketException) { APILogger.LogString("QueryConfiguration: RetrieveXMLConfig threw exception"); APILogger.LogException(socketException); info.Error(string.Format("Connection with {0} failed", SerialNumber)); return; } catch (NotConnectedException) { info.Error($"Connection with {SerialNumber} failed"); return; } catch (CanceledException) { info.Cancel(); } catch (Exception ex) { info.Error(ex.Message, ex); } } const UInt64 ReservedFlashSpace = 659 * 2 * 1024 * 1024; protected UInt64 ReadSampleStorageSizeInBytes() { //15949 S6A when streaming does a whole bunch of unnecessary queries //avoid getting these attributes while streaming, they will fail... if (GetIsStreaming()) { return 1024 * 1024 * 1024; }// 1GB try { var reservedFlashSpace = ReservedFlashSpace; try { if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.AttributeStoreBlocks)) { var qsa = new QuerySystemAttribute(this, QuerySystemAttribute.Default_IO_Timeout); qsa.DeviceID = 0; qsa.Key = AttributeTypes.SystemAttributes.AttributeStoreBlocks; qsa.SyncExecute(); var uValue = (ulong)qsa.Value; if (uValue > 0) { reservedFlashSpace = uValue; } } } catch (Exception ex) { APILogger.Log("Exception getting attribute store blocks", SerialNumber, ex); } var query = new QuerySystemAttribute(this, QuerySystemAttribute.Default_IO_Timeout); query.Key = AttributeTypes.SystemAttributes.FlashSizeBytes; query.SyncExecute(); var flashSize = (ulong)query.Value; if (flashSize >= reservedFlashSpace) { flashSize -= reservedFlashSpace; } return flashSize; } catch (CommandException ce) { if (ce.Error != CommandErrorReason.InvalidMode) { APILogger.LogString("DASFactory.ReadSampleStorageSizeInBytes: Exception " + ce.Message + " returning 1GB"); } return 1024 * 1024 * 1024; // 1GB } catch (Exception ex) { // we have to return something APILogger.LogString("DASFactory.ReadSampleStorageSizeInBytes: Exception " + ex.Message + " returning 1GB"); return 1024 * 1024 * 1024; // 1GB } } protected virtual void RetrieveEventGuids() { try { var EventCount = new QueryTotalEventCount(this, QueryTotalEventCount.Default_IO_Timeout); EventCount.SyncExecute(); if (EventCount.Count < 1) { // no events SetEventGuids(null); SetEventFaultFlags(null); SetEventArmAttemps(null); return; } var guids = new Guid[EventCount.Count]; var downloadedStatus = new bool[EventCount.Count]; var faultFlags = new ushort[EventCount.Count]; var armAttempts = new byte[EventCount.Count]; var extendedFaultFlags = new List(); var queryEventGuid = new QueryEventAttribute(this, QueryEventAttribute.Default_IO_Timeout); var queryFaultFlags = new QueryEventAttribute(this, QueryEventAttribute.Default_IO_Timeout); var queryEventArmAttempts = new QueryEventAttribute(this, QueryEventAttribute.Default_IO_Timeout); queryEventGuid.Key = AttributeTypes.ArmAndEventAttributes.EventGuid; queryFaultFlags.Key = AttributeTypes.ArmAndEventAttributes.FaultFlags; queryEventArmAttempts.Key = AttributeTypes.ArmAndEventAttributes.EventArmAttempts; for (ushort eventIdx = 0; eventIdx < EventCount.Count; eventIdx++) { queryEventGuid.EventNumber = eventIdx; queryFaultFlags.EventNumber = eventIdx; queryEventGuid.SyncExecute(); guids[eventIdx] = new Guid(queryEventGuid.Value as string); extendedFaultFlags.Add(GetExtendedFaultFlags(eventIdx)); faultFlags[eventIdx] = 0; armAttempts[eventIdx] = 0; if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.EventFaultFlags)) { try { queryFaultFlags.SyncExecute(); faultFlags[eventIdx] = (ushort)queryFaultFlags.Value; } catch (Exception ex) { APILogger.Log("could not get fault flags", ex); } try { var ca = GetConfigAttributes(this);//new Slice.ConfigAttributes(this); downloadedStatus[eventIdx] = ca.EventHasBeenDownloaded(eventIdx, out uint iflag); } catch (Exception) { } // Get Arm Attempts for this event if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.EventArmAttempts)) { try { queryEventArmAttempts.EventNumber = eventIdx; queryEventArmAttempts.SyncExecute(); armAttempts[eventIdx] = (byte)queryEventArmAttempts.Value; } catch (Exception ex) { APILogger.Log("could not get arm attempts", ex); } } } } SetEventGuids(guids); SetEventFaultFlags(faultFlags); ((IDownload)this)?.SetExtendedFaultFlags(extendedFaultFlags.ToArray()); SetEventArmAttemps(armAttempts); SetEventDownloadStatus(downloadedStatus); } catch (CommandException ce) { if (ce.Error != CommandErrorReason.InvalidMode) { APILogger.LogString("DASFactory.RetriveEventGuids: "); APILogger.LogException(ce); } } catch (Exception ex) { APILogger.LogString("DASFactory.RetriveEventGuids: "); APILogger.LogException(ex); } } protected virtual DASModule MakeConfigModuleFromInfoModule(InfoResult.Module infoModule) { var configModule = new DASModule(infoModule.ModuleArrayIndex, this); configModule.Channels = new AnalogInputDASChannel[infoModule.NumberOfChannels]; for (var i = 0; i < infoModule.NumberOfChannels; i++) { var channel = new AnalogInputDASChannel(configModule, i); if (infoModule.TypeOfModule == DFConstantsAndEnums.ModuleType.SLICEIEPE) { channel.IEPEChannel = true; channel.SupportedBridges = new SensorConstants.BridgeType[] { SensorConstants.BridgeType.IEPE }; } else { channel.IEPEChannel = false; channel.SupportedBridges = new SensorConstants.BridgeType[] {SensorConstants.BridgeType.FullBridge, SensorConstants.BridgeType.HalfBridge}; } configModule.Channels[i] = channel; } return configModule; } /// /// Calculate the EU to mV conversion factor for the specified channel. /// /// /// /// The whose property is being converted from /// EU to mV. /// /// /// /// The mV equivalent of 1 EU on the specified channel. /// /// protected double GetMvPerEu(AnalogInputDASChannel analog) { try { return Math.Abs(analog.SensitivityMilliVoltsPerEU); } catch (Exception ex) { throw new ApplicationException("encountered problem getting EU to MV conversion factor for channel " + (null != analog && null != analog.SerialNumber ? analog.SerialNumber : ""), ex); } } /// /// returns true if channel has linear added calibrations /// /// /// private bool HasLinearAdded(AnalogInputDASChannel analog) { //seems like the simplest way of checking currently return !string.IsNullOrEmpty(analog.LinearSensorCalibration); } protected double GetMvPerEUNormalized(AnalogInputDASChannel analog) { try { //14079 Gain selection inconsistent between non-linear poly with linear CAL and traditional non-linear poly sensor //if we have linear added we may be marked as proportional, but we want the non linear, non proportional mVPerEU bool isProportional = HasLinearAdded(analog) ? false : analog.IsProportionalToExcitation; return Math.Abs(analog.SensitivityMilliVoltsPerEU) * (isProportional && analog.SensitivityUnits != SensorConstants.SensUnits.mVperEU ? Test.Module.Channel.Sensor.GetExcitationVoltageMagnitudeFromEnum(analog.Excitation) : 1.0) / (analog.AtCapacity ? analog.CapacityOutputIsBasedOn : 1.0); } catch (Exception ex) { throw new ApplicationException("encountered problem getting EU to MV conversion factor for channel " + (null != analog && null != analog.SerialNumber ? analog.SerialNumber : ""), ex); } } protected virtual ConfigurationData MakeDefaultConfigFromInfo() { var conf = new ConfigurationData(); if (DASInfo != null && DASInfo.Modules != null && DASInfo.Modules.Length > 0) { conf.Modules = new DASModule[DASInfo.Modules.Length]; for (int moduleIdx = 0; moduleIdx < DASInfo.Modules.Length; moduleIdx++) { conf.Modules[moduleIdx] = MakeConfigModuleFromInfoModule((InfoResult.Module)DASInfo.Modules[moduleIdx]); } } return conf; } #endregion #region Set configuration public virtual double[] GetNominalRanges(SensorConstants.BridgeType bridgeType) { switch (bridgeType) { case SensorConstants.BridgeType.IEPE: return WinUSBSlice.StaticDASIEPEInfo.NominalRanges; default: return WinUSBSlice.StaticDASBridgeInfo.NominalRanges; } } public class CheckSafetyStateAsyncInfo : SliceServiceAsyncInfo { public bool Armed { get; set; } public CheckSafetyStateAsyncInfo(ServiceCallback callback, object userData) : base(callback, userData) { } } void IConfigurationActions.CheckSafetyState(bool bArmed, ServiceCallback callback, object userData) { var info = new CheckSafetyStateAsyncInfo(callback, userData); info.Armed = bArmed; LaunchAsyncWorker("Slice.CheckSafetyState", new WaitCallback(AsyncCheckSafetyState), info); } protected virtual bool CheckAutoDetectSupport() { return false; } bool IConfiguration.SupportsAutoDetect => CheckAutoDetectSupport(); void IConfigurationActions.AutoDetect(bool QueryConfiguration, ServiceCallback callback, object userData) { var info = new AutoDetectServiceAsyncInfo(QueryConfiguration, callback, userData); LaunchAsyncWorker("Slice.AutoDetect", new WaitCallback(AsyncAutoDetect), info); } protected virtual void AsyncAutoDetect(object o) { if (!(o is SliceServiceAsyncInfo info)) { return; } info.Success(); } public class SliceConfigServiceAsyncInfo : SliceServiceAsyncInfo { public bool EventConfig { get; set; } = false; public bool DummyConfig { get; set; } = false; //FB 26736 time & data channel Id public Dictionary TimeChannelIds { get; set; } public Dictionary DataChannelIds { get; set; } public Dictionary StreamProfiles { get; set; } public Dictionary StreamADCPerPacket { get; set; } public Dictionary IrigTDPIntervals { get; set; } public Dictionary Addresses { get; set; } public Dictionary TmNSConfigs { get; set; } public Dictionary BaudRates { get; set; } public Dictionary DataBits { get; set; } public Dictionary StopBits { get; set; } public Dictionary Parities { get; set; } public Dictionary FlowControls { get; set; } public Dictionary DataFormats { get; set; } public Dictionary TMATSIntervalMs { get; set; } public uint CRC { get; set; } = 0; public bool TurnOffAAFRealtime { get; set; } = true; /// /// determines whether digital outputs are configured /// only observed when DummyConfig is true /// public bool ConfigureDigitalOutput { get; set; } = false; public IStreamingFilterProfile DSPFilterType { get; set; } public bool DiscardDiagnostics { get; set; } = true; public SliceConfigServiceAsyncInfo(ServiceCallback callback, object userData, bool eventConfig, uint crc, bool turnOffAAFRealtime) : base(callback, userData) { EventConfig = eventConfig; CRC = crc; TurnOffAAFRealtime = turnOffAAFRealtime; } } void IConfigurationActions.Configure(ServiceCallback callback, object userData, bool EventConfig, bool DummyConfig, double [] maxAAF, bool configureDigitalOutputs, uint crc, bool turnOffAAFRealtime, IStreamingFilterProfile dspFilterType, bool discardDiagnostics, Dictionary timeChannelIds, Dictionary dataChannelIds, Dictionary streamProfiles, Dictionary streamADCPerPacket, Dictionary irigTDPIntervals, Dictionary addresses, Dictionary tmnsConfigs, Dictionary baudRates, Dictionary dataBits, Dictionary stopBits, Dictionary parities, Dictionary flowControls, Dictionary dataFormats, Dictionary tmatsIntervalMs ) { var info = new SliceConfigServiceAsyncInfo(callback, userData, EventConfig, crc, turnOffAAFRealtime && DFConstantsAndEnums.SupportsTurnOffAAFRealtime(this)); info.DummyConfig = DummyConfig; //FB 26736 set time & data channel Id info.DataChannelIds = dataChannelIds; info.TimeChannelIds = timeChannelIds; //FB 29434 Quick Build doesn't recognize non-default values of Stream Output settings info.StreamProfiles = streamProfiles; info.StreamADCPerPacket = streamADCPerPacket; info.IrigTDPIntervals = irigTDPIntervals; info.TMATSIntervalMs = tmatsIntervalMs; info.Addresses = addresses; info.TmNSConfigs = tmnsConfigs; info.ConfigureDigitalOutput = configureDigitalOutputs; info.DSPFilterType = dspFilterType; info.DiscardDiagnostics = discardDiagnostics; info.BaudRates = baudRates; info.DataBits = dataBits; info.StopBits = stopBits; info.Parities = parities; info.FlowControls = flowControls; info.DataFormats = dataFormats; LaunchAsyncWorker("Slice.Configure", new WaitCallback(AsyncConfigure), info); } void IConfigurationActions.ApplyLevelTriggers(ServiceCallback callback, object userData) { var info = new SliceServiceAsyncInfo(callback, userData); LaunchAsyncWorker("SLICE.ApplyLevelTriggers", AsyncApplyLevelTriggers, info); } private void AsyncCheckSafetyState(object configAsyncInfo) { var info = configAsyncInfo as CheckSafetyStateAsyncInfo; if (IsTOM()) { try { var qsi = new QuerySwitchImmediate(this); qsi.Switch = Convert.ToByte(Switches.BaseSwitches.SquibSafeArmSwitch); qsi.SwitchText = Switches.BaseSwitches.SquibSafeArmSwitch.ToString(); qsi.SyncExecute(); APILogger.Log("Checking SafetySwitch"); if (qsi.Setting == 0) { APILogger.Log("Safetyswitch is in safe"); if (info.Armed) { info.Error("SAFETYSWITCH_STATE"); } } else { APILogger.Log("Safetyswitch is in ARM"); if (!info.Armed) { //check squib resistance var resistances = new MeasureSquibChannelResistances(this); resistances.SyncExecute(); var bHaveResistance = false; foreach (var f in resistances.MeasuredResistanceOhms) { if (f > 1F && f <= 8F) { bHaveResistance = true; break; } } if (bHaveResistance) { info.Error("SQUIB_RESISTANCE"); } else { info.Error("SAFETYSWITCH_STATE"); } } } } catch (Exception ex) { APILogger.Log("failed to get checksafetystate, ", ex); info.Success(); } } info.Success(); } protected virtual float GetLevelTriggerThreshold(AnalogInputDASChannel analog, IDiagnosticResult diagnostics, double thresholdeu, double MvPerEu) { var threshold = Convert.ToSingle((thresholdeu * MvPerEu + diagnostics.GetExpectedDataZeroLevelADC(analog.ZeroMethod) * diagnostics.ScalefactorMilliVoltsPerADC)); try { var now = DateTime.Now; var s = string.Format( "{7} {8} - {6} : thresholdEU ({0}) * MvPerEU ({1}) + DataZeroLevelADC ({2}) * ScaleFactorMvPerADC ({3})={4}; SensitivityMv={5}\r\n", thresholdeu, MvPerEu, diagnostics.GetExpectedDataZeroLevelADC(analog.ZeroMethod), diagnostics.ScalefactorMilliVoltsPerADC, threshold, analog.SensitivityMilliVoltsPerEU, analog.SerialNumber, now.ToShortDateString(), now.ToShortTimeString()); LevelTriggerLogging.LevelTriggerLog(s); } catch (Exception ex) { APILogger.Log(ex); } return threshold; } private Dictionary SLICE1_MinimumProtocols = new Dictionary(); public override void InitMinProto() { // SLICE 1.0 Protocol Limitations SLICE1_MinimumProtocols.Add(DFConstantsAndEnums.ProtocolLimitedCommands.DiangosShuntDAC, 3); SLICE1_MinimumProtocols.Add(DFConstantsAndEnums.ProtocolLimitedCommands.QueryMSP430Firmware, 9); SLICE1_MinimumProtocols.Add(DFConstantsAndEnums.ProtocolLimitedCommands.MultipleAndHybridEvents, 10); SLICE1_MinimumProtocols.Add(DFConstantsAndEnums.ProtocolLimitedCommands.MultipleEvents, 10); SLICE1_MinimumProtocols.Add(DFConstantsAndEnums.ProtocolLimitedCommands.AutoArm, 7); SLICE1_MinimumProtocols.Add(DFConstantsAndEnums.ProtocolLimitedCommands.FlashCardInfo, 9); SLICE1_MinimumProtocols.Add(DFConstantsAndEnums.ProtocolLimitedCommands.PhysicalStartAddress, 2); SLICE1_MinimumProtocols.Add(DFConstantsAndEnums.ProtocolLimitedCommands.TestCommunication, 2); SLICE1_MinimumProtocols.Add(DFConstantsAndEnums.ProtocolLimitedCommands.StackLowPowerMode, 2); SLICE1_MinimumProtocols.Add(DFConstantsAndEnums.ProtocolLimitedCommands.SetRealtimeSampleRate, 4); SLICE1_MinimumProtocols.Add(DFConstantsAndEnums.ProtocolLimitedCommands.EventFaultFlags, 7); SLICE1_MinimumProtocols.Add(DFConstantsAndEnums.ProtocolLimitedCommands.EventArmAttempts, 12); SLICE1_MinimumProtocols.Add(DFConstantsAndEnums.ProtocolLimitedCommands.QueryActualSampleRateImmediate, 6); SLICE1_MinimumProtocols.Add(DFConstantsAndEnums.ProtocolLimitedCommands.InitHardwareInputLines, 8); SLICE1_MinimumProtocols.Add(DFConstantsAndEnums.ProtocolLimitedCommands.VoltageSysAttributes, 9); SLICE1_MinimumProtocols.Add(DFConstantsAndEnums.ProtocolLimitedCommands.LevelTrigger, 4); SLICE1_MinimumProtocols.Add(DFConstantsAndEnums.ProtocolLimitedCommands.AttributeStoreBlocks, 7); SLICE1_MinimumProtocols.Add(DFConstantsAndEnums.ProtocolLimitedCommands.QueryArmAndTriggerStatus_VoltageReadings, 7); SLICE1_MinimumProtocols.Add(DFConstantsAndEnums.ProtocolLimitedCommands.MaxEvents, 7); SLICE1_MinimumProtocols.Add(DFConstantsAndEnums.ProtocolLimitedCommands.AutoArmDiagnosticDelay, 9); SLICE1_MinimumProtocols.Add(DFConstantsAndEnums.ProtocolLimitedCommands.StackChannelAutoArmDiagLevel, 11); SLICE1_MinimumProtocols.Add(DFConstantsAndEnums.ProtocolLimitedCommands.FlashClear, 5); SLICE1_MinimumProtocols.Add(DFConstantsAndEnums.ProtocolLimitedCommands.MultipleSamplesRealtime, 4); SLICE1_MinimumProtocols.Add(DFConstantsAndEnums.ProtocolLimitedCommands.ResetAttributeStore, 9); SLICE1_MinimumProtocols.Add(DFConstantsAndEnums.ProtocolLimitedCommands.IgnoreShortedStartEvent, 13); SLICE1_MinimumProtocols.Add(DFConstantsAndEnums.ProtocolLimitedCommands.BaseCalibrationDate, 14); MinimumProtocols = SLICE1_MinimumProtocols; } protected void SetPolarity() { //performance improvement, don't set polarity in systems that don't need to //[reduce # of commands] if (RunTestVariables.InRunTest && RunTestVariables.DontSetPolarityInRunTest) { //dont' set } else { try { var ssaPolarity = new SetSystemAttribute(this, AbstractCommandBase.Default_IO_Timeout); ssaPolarity.SetValue(AttributeTypes.SystemAttributes.StartRecordPolarity, (byte)(InvertStart ? 1 : 0), true); ssaPolarity.SyncExecute(); } catch (Exception ex) { APILogger.Log("Problem setting Start Record Polarity", ex); } try { var ssaPolarity = new SetSystemAttribute(this, AbstractCommandBase.Default_IO_Timeout); ssaPolarity.SetValue(AttributeTypes.SystemAttributes.TriggerPolarity, (byte)(InvertTrigger ? 1 : 0), true); ssaPolarity.SyncExecute(); } catch (Exception ex) { APILogger.Log("Problem setting trigger polarity", ex); } } } protected void SetVoltageRequirements() { try { var ssaV = new SetSystemAttribute(this, AbstractCommandBase.Default_IO_Timeout); //input min, input max, battery min, battery max var requirements = new float[] { InputLowVoltage, InputHighVoltage, BatteryLowVoltage, BatteryHighVoltage }; ssaV.SetValue(AttributeTypes.SystemAttributes.VoltageRequirements, requirements, true); ssaV.SyncExecute(); } catch (Exception ex) { APILogger.Log("Problem setting Voltage Requirements", ex); } } protected virtual void AsyncConfigure(object configAsyncInfo) { var info = configAsyncInfo as SliceConfigServiceAsyncInfo; var progressValue = 0; var bReleased = true; SetVoltageRequirements(); try { SetPolarity(); if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.IgnoreShortedStartEvent)) { var shortedStartTrigger = new List(); shortedStartTrigger.Add(Convert.ToByte(IgnoreShortedStart)); // Start is First shortedStartTrigger.Add(Convert.ToByte(IgnoreShortedTrigger)); // Trigger is Second var ssaIgnoreShortedStartTrigger = new SetSystemAttribute(this, SetSystemAttribute.Default_IO_Timeout); ssaIgnoreShortedStartTrigger.SetValue(AttributeTypes.SystemAttributes.SA_IgnoreShortedStartTrigger, shortedStartTrigger.ToArray(), true); ssaIgnoreShortedStartTrigger.SyncExecute(); } Lock(); bReleased = false; // loop thru the modules (slices) and configure the channels var numChannels = DASInfo.Modules.Sum(mod => mod.NumberOfChannels); var rangeArray = new float[numChannels]; var bridgeModeArray = new byte[numChannels]; var BridgeResistanceArray = new ushort[numChannels]; var IsACCoupledArray = new bool[numChannels]; // level trigger values var enableLowerLevelTriggerThreshold = new bool[numChannels]; var enableUpperLevelTriggerThreshold = new bool[numChannels]; var lowerLevelTriggerThreshold = new float[numChannels]; var upperLevelTriggerThreshold = new float[numChannels]; var qualificationSamples = new int[numChannels]; for (var moduleIdx = 0; moduleIdx < ConfigData.Modules.Length; moduleIdx++) { var module = ConfigData.Modules[moduleIdx]; // configure the range for this bridge for (var channelIdx = 0; channelIdx < module.Channels.Length; channelIdx++) { var channel = module.Channels[channelIdx]; var dasChannelNumber = DASInfo.MapModuleArrayIndexAndChannelNum2DASChannel(moduleIdx, channelIdx); qualificationSamples[dasChannelNumber] = channel.QualificationSamples; if (channel.ConfigurationMode != DFConstantsAndEnums.ConfigMode.Disabled && (channel.ConfigurationMode == DFConstantsAndEnums.ConfigMode.Normal || channel.ConfigurationMode == DFConstantsAndEnums.ConfigMode.DummyArm)) { if (!(channel is AnalogInputDASChannel analog)) { if (!bReleased) { bReleased = true; Release(); } // "Configure: Slice bridge modules must only contain analog input channels" info.Error(Strings.Slice_Configure_Err1); return; } switch (analog.TypeOfBridge) { case SensorConstants.BridgeType.FullBridge: bridgeModeArray[dasChannelNumber] = 0; break; case SensorConstants.BridgeType.HalfBridge: bridgeModeArray[dasChannelNumber] = 1; break; case SensorConstants.BridgeType.IEPE: bridgeModeArray[dasChannelNumber] = 0; break; case SensorConstants.BridgeType.QuarterBridge: bridgeModeArray[dasChannelNumber] = 2; break; case SensorConstants.BridgeType.HalfBridge_SigPlus: bridgeModeArray[dasChannelNumber] = 3; break; default: bridgeModeArray[dasChannelNumber] = 0; break; } if (analog.IEPEChannel) { IsACCoupledArray[dasChannelNumber] = analog.CouplingMode == SensorConstants.CouplingModes.AC; } else { IsACCoupledArray[dasChannelNumber] = false; } switch (analog.TypeOfBridge) { case SensorConstants.BridgeType.HalfBridge: BridgeResistanceArray[dasChannelNumber] = SLICE_HALFBRIDGE_RESISTANCE; break; default: BridgeResistanceArray[dasChannelNumber] = (ushort)analog.BridgeResistanceOhms; break; } if (analog.SensitivityMilliVoltsPerEU != 0) { var MvPerEu = GetMvPerEUNormalized(analog); var requestedRange = analog.DesiredRangeWithHeadroomEU * MvPerEu; var nominalRanges = GetNominalRanges(analog.TypeOfBridge); if (requestedRange > nominalRanges[0]) { requestedRange = nominalRanges[0]; } else if (requestedRange < nominalRanges[nominalRanges.Length - 1]) { requestedRange = nominalRanges[nominalRanges.Length - 1]; } rangeArray[dasChannelNumber] = (float)requestedRange; // // push level trigger settings out to hardware // if (null != analog.TriggerBelowThresholdEu) { // // Careful here... if remove offset is NOT active, and zero method is "none", // then just send the level trigger as specified down to the hardware. Othewise, // we'll need to apply the appropriate offset to it so it triggers as expected. // if (analog.IsInverted || analog.SensitivityMilliVoltsPerEU < 0) { enableUpperLevelTriggerThreshold[dasChannelNumber] = true; try { var diagnostics = analog.Diagnostics; upperLevelTriggerThreshold[dasChannelNumber] = GetLevelTriggerThreshold(analog, diagnostics, (double)analog.TriggerBelowThresholdEu, MvPerEu); if (analog.SensitivityMilliVoltsPerEU < 0) { upperLevelTriggerThreshold[dasChannelNumber] = -upperLevelTriggerThreshold[dasChannelNumber]; } } catch (ApplicationException ex) { if (null != Exceptional.ExtractFirstExceptionOfTypeFromExceptionTree(ex)) upperLevelTriggerThreshold[dasChannelNumber] = (float)analog.TriggerBelowThresholdEu; else throw ex; } } else { enableLowerLevelTriggerThreshold[dasChannelNumber] = true; try { var diagnostics = analog.Diagnostics; lowerLevelTriggerThreshold[dasChannelNumber] = GetLevelTriggerThreshold(analog, diagnostics, (double)analog.TriggerBelowThresholdEu, MvPerEu); } catch (ApplicationException ex) { if (null != Exceptional.ExtractFirstExceptionOfTypeFromExceptionTree(ex)) lowerLevelTriggerThreshold[dasChannelNumber] = (float)analog.TriggerBelowThresholdEu; else throw ex; } } } else { if (analog.IsInverted || analog.SensitivityMilliVoltsPerEU < 0) { upperLevelTriggerThreshold[dasChannelNumber] = 0; enableUpperLevelTriggerThreshold[dasChannelNumber] = false; } else { lowerLevelTriggerThreshold[dasChannelNumber] = 0; enableLowerLevelTriggerThreshold[dasChannelNumber] = false; } } if (null != analog.TriggerAboveThresholdEu) { // // Careful here... if remove offset is NOT active, and zero method is "none", // then just send the level trigger as specified down to the hardware. Othewise, // we'll need to apply the appropriate offset to it so it triggers as expected. // if (analog.IsInverted || analog.SensitivityMilliVoltsPerEU < 0) { enableLowerLevelTriggerThreshold[dasChannelNumber] = true; try { var diagnostics = analog.Diagnostics; lowerLevelTriggerThreshold[dasChannelNumber] = GetLevelTriggerThreshold(analog, diagnostics, (double)analog.TriggerAboveThresholdEu, MvPerEu); if (analog.SensitivityMilliVoltsPerEU < 0) { lowerLevelTriggerThreshold[dasChannelNumber] = -lowerLevelTriggerThreshold[dasChannelNumber]; } } catch (ApplicationException ex) { if (null != Exceptional.ExtractFirstExceptionOfTypeFromExceptionTree(ex)) lowerLevelTriggerThreshold[dasChannelNumber] = (float)analog.TriggerAboveThresholdEu; else throw ex; } } else { enableUpperLevelTriggerThreshold[dasChannelNumber] = true; try { var diagnostics = analog.Diagnostics; upperLevelTriggerThreshold[dasChannelNumber] = GetLevelTriggerThreshold(analog, diagnostics, (double)analog.TriggerAboveThresholdEu, MvPerEu); } catch (ApplicationException ex) { if (null != Exceptional.ExtractFirstExceptionOfTypeFromExceptionTree(ex)) upperLevelTriggerThreshold[dasChannelNumber] = (float)analog.TriggerAboveThresholdEu; else throw ex; } } } else { if (analog.IsInverted || analog.SensitivityMilliVoltsPerEU < 0) { lowerLevelTriggerThreshold[dasChannelNumber] = 0; enableLowerLevelTriggerThreshold[dasChannelNumber] = false; } else { upperLevelTriggerThreshold[dasChannelNumber] = 0; enableUpperLevelTriggerThreshold[dasChannelNumber] = false; } } } else { rangeArray[dasChannelNumber] = 0; } } else { rangeArray[dasChannelNumber] = 0; bridgeModeArray[dasChannelNumber] = 0; BridgeResistanceArray[dasChannelNumber] = 0; enableLowerLevelTriggerThreshold[dasChannelNumber] = false; enableUpperLevelTriggerThreshold[dasChannelNumber] = false; lowerLevelTriggerThreshold[dasChannelNumber] = 0; upperLevelTriggerThreshold[dasChannelNumber] = 0; } } } var config = GetConfigAttributes(this);// new ConfigAttributes(this); // only set attributes to default if we haven't run diagnostics yet if (!DiagnosticsHasBeenRun) { config.PurgeStaleData(this); } // report progress progressValue = 25; info.Progress(progressValue); // store some attributes if (ConfigData.Modules.Length > 0) { config.SampleRate = ConfigData.Modules[0].SampleRateHz; var actualSampleRate = (float)ConfigData.Modules[0].SampleRateHz; if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.QueryActualSampleRateImmediate)) { actualSampleRate = config.ActualSampleRate; } config.AAFilter = ConfigData.Modules[0].AAFilterRateHz; config.TestID = ConfigData.TestID.TrimEnd(new char[] { '\0' }); config.TestSetupUniqueId = ConfigData.TestSetupUniqueId?.TrimEnd('\0'); config.TestDescription = ConfigData.Description; config.PreTriggerSamples = (ulong)(ConfigData.Modules[0].PreTriggerSeconds * actualSampleRate); config.PostTriggerSamples = (ulong)(ConfigData.Modules[0].PostTriggerSeconds * actualSampleRate); SetArmMode(ConfigData.Modules[0].RecordingMode); config.ConfigureRange(rangeArray); config.ConfigureBridge(bridgeModeArray); config.ConfigureCoupling(IsACCoupledArray); config.ConfigureBridgeResistance(BridgeResistanceArray); progressValue = 50; info.Progress(progressValue); // configure level trigger enable/threshold values ConfigureLevelTriggers(enableLowerLevelTriggerThreshold, enableUpperLevelTriggerThreshold, lowerLevelTriggerThreshold, upperLevelTriggerThreshold, qualificationSamples); // get scale factors var scaleFactors = config.GetScaleFactors(); if (numChannels > scaleFactors.Length) { if (!bReleased) { bReleased = true; Release(); } // "ConfigurationService.Configure: Firmware returned {0} channels but stack contains {1}" info.Error(string.Format(Strings.ConfigurationService_Configure_err1, scaleFactors.Length, numChannels)); return; } for (var moduleIdx = 0; moduleIdx < ConfigData.Modules.Length; moduleIdx++) { for (var channelIdx = 0; channelIdx < ConfigData.Modules[moduleIdx].Channels.Length; channelIdx++) { var analog = (AnalogInputDASChannel)ConfigData.Modules[moduleIdx].Channels[channelIdx]; var dasChannelNumber = DASInfo.MapModuleArrayIndexAndChannelNum2DASChannel(moduleIdx, channelIdx); analog.ScalefactorMilliVoltsPerADC = scaleFactors[dasChannelNumber]; } progressValue++; info.Progress(Math.Min(74, progressValue)); } } // now store the data var configStr = ConfigurationData.Serialize((ConfigurationData)ConfigData); progressValue = 75; info.Progress(progressValue); config.StoreXMLConfig(configStr, info.EventConfig ? ConfigAttributes.FileStore.Event : ConfigAttributes.FileStore.Diagnostic); ConfigureHasBeenRun = true; if (info.DiscardDiagnostics) { DiagnosticsHasBeenRun = false; } // report success' ConfigurationData.SetConfiguration(this, configStr, info.EventConfig ? (int)ConfigAttributes.FileStore.Event : (int)ConfigAttributes.FileStore.Diagnostic); if (!bReleased) { bReleased = true; Release(); } info.Success(); } catch (CanceledException) { if (!bReleased) { bReleased = true; Release(); } info.Cancel(); } catch (Exception ex) { if (!bReleased) { bReleased = true; Release(); } info.Error(ex.Message, ex); } finally { if (!bReleased) { bReleased = true; Release(); } } info.Progress(100); } #endregion // FB15335: Move UART and ClockProfile sets to RunTest -> Hardware NavStep, add Reboot void IConfigurationActions.Reboot(ServiceCallback callback, object userData) { var info = new SliceServiceAsyncInfo(callback, userData); LaunchAsyncWorker("Slice.Reboot", new WaitCallback(AsyncReboot), info); } protected virtual void AsyncReboot(object o) { if (!(o is SliceServiceAsyncInfo info)) { return; } try { var reboot = new Reboot(this, SetSystemAttribute.Default_IO_Timeout); reboot.SyncExecute(); info.Success(); if (!ConnectString.ToLower().Contains("usb")) { //disconnect, but reuse the socket since we're expecting it back var disconnectedEvent = new ManualResetEvent(false); Disconnect(false, DisconnectCallback, disconnectedEvent, 60000); disconnectedEvent.WaitOne(); } } catch (CanceledException ex) { APILogger.Log(ex); info.Cancel(); } catch (Exception ex) { APILogger.Log(ex); info.Error(ex.Message, ex); } } private bool DisconnectCallback(ICommunicationReport report) { (report.UserState as ManualResetEvent)?.Set(); return true; } protected virtual void AsyncApplyLevelTriggers(object asyncInfo) { if (!(asyncInfo is SliceServiceAsyncInfo info)) { return; } try { // loop thru the modules (slices) and configure the channels var numChannels = DASInfo.Modules.Sum(mod => mod.NumberOfChannels); // level trigger values var enableLowerLevelTriggerThreshold = new bool[numChannels]; var enableUpperLevelTriggerThreshold = new bool[numChannels]; var lowerLevelTriggerThreshold = new float[numChannels]; var upperLevelTriggerThreshold = new float[numChannels]; var qualificationSamples = new int[numChannels]; for (var moduleIdx = 0; moduleIdx < ConfigData.Modules.Length; moduleIdx++) { var module = ConfigData.Modules[moduleIdx]; // configure the range for this bridge for (var channelIdx = 0; channelIdx < module.Channels.Length; channelIdx++) { var channel = module.Channels[channelIdx]; var dasChannelNumber = DASInfo.MapModuleArrayIndexAndChannelNum2DASChannel(moduleIdx, channelIdx); qualificationSamples[dasChannelNumber] = channel.QualificationSamples; if (channel.ConfigurationMode != DFConstantsAndEnums.ConfigMode.Disabled && (channel.ConfigurationMode == DFConstantsAndEnums.ConfigMode.Normal || channel.ConfigurationMode == DFConstantsAndEnums.ConfigMode.DummyArm)) { if (!(channel is AnalogInputDASChannel analog)) { continue; } if (analog.SensitivityMilliVoltsPerEU == 0) continue; var mvPerEu = GetMvPerEUNormalized(analog); // // push level trigger settings out to hardware // if (null != analog.TriggerBelowThresholdEu) { // // Careful here... if remove offset is NOT active, and zero method is "none", // then just send the level trigger as specified down to the hardware. Othewise, // we'll need to apply the appropriate offset to it so it triggers as expected. // if (analog.IsInverted || analog.SensitivityMilliVoltsPerEU < 0) { enableUpperLevelTriggerThreshold[dasChannelNumber] = true; try { var diagnostics = analog.Diagnostics; upperLevelTriggerThreshold[dasChannelNumber] = GetLevelTriggerThreshold(analog, diagnostics, (double)analog.TriggerBelowThresholdEu, mvPerEu); if (analog.SensitivityMilliVoltsPerEU < 0) { upperLevelTriggerThreshold[dasChannelNumber] = -upperLevelTriggerThreshold[dasChannelNumber]; } } catch (ApplicationException ex) { if (null != Exceptional.ExtractFirstExceptionOfTypeFromExceptionTree(ex)) upperLevelTriggerThreshold[dasChannelNumber] = (float)analog.TriggerBelowThresholdEu; else throw; } } else { enableLowerLevelTriggerThreshold[dasChannelNumber] = true; try { var diagnostics = analog.Diagnostics; lowerLevelTriggerThreshold[dasChannelNumber] = GetLevelTriggerThreshold(analog, diagnostics, (double)analog.TriggerBelowThresholdEu, mvPerEu); } catch (ApplicationException ex) { if (null != Exceptional.ExtractFirstExceptionOfTypeFromExceptionTree(ex)) lowerLevelTriggerThreshold[dasChannelNumber] = (float)analog.TriggerBelowThresholdEu; else throw; } } } else { if (analog.IsInverted || analog.SensitivityMilliVoltsPerEU < 0) { upperLevelTriggerThreshold[dasChannelNumber] = 0; enableUpperLevelTriggerThreshold[dasChannelNumber] = false; } else { lowerLevelTriggerThreshold[dasChannelNumber] = 0; enableLowerLevelTriggerThreshold[dasChannelNumber] = false; } } if (null != analog.TriggerAboveThresholdEu) { // // Careful here... if remove offset is NOT active, and zero method is "none", // then just send the level trigger as specified down to the hardware. Othewise, // we'll need to apply the appropriate offset to it so it triggers as expected. // if (analog.IsInverted || analog.SensitivityMilliVoltsPerEU < 0) { enableLowerLevelTriggerThreshold[dasChannelNumber] = true; try { var diagnostics = analog.Diagnostics; lowerLevelTriggerThreshold[dasChannelNumber] = GetLevelTriggerThreshold(analog, diagnostics, (double)analog.TriggerAboveThresholdEu, mvPerEu); if (analog.SensitivityMilliVoltsPerEU < 0) { lowerLevelTriggerThreshold[dasChannelNumber] = -lowerLevelTriggerThreshold[dasChannelNumber]; } } catch (ApplicationException ex) { if (null != Exceptional.ExtractFirstExceptionOfTypeFromExceptionTree(ex)) lowerLevelTriggerThreshold[dasChannelNumber] = (float)analog.TriggerAboveThresholdEu; else throw; } } else { enableUpperLevelTriggerThreshold[dasChannelNumber] = true; try { var diagnostics = analog.Diagnostics; upperLevelTriggerThreshold[dasChannelNumber] = GetLevelTriggerThreshold(analog, diagnostics, (double)analog.TriggerAboveThresholdEu, mvPerEu); } catch (ApplicationException ex) { if (null != Exceptional.ExtractFirstExceptionOfTypeFromExceptionTree(ex)) upperLevelTriggerThreshold[dasChannelNumber] = (float)analog.TriggerAboveThresholdEu; else throw; } } } else { if (analog.IsInverted || analog.SensitivityMilliVoltsPerEU < 0) { lowerLevelTriggerThreshold[dasChannelNumber] = 0; enableLowerLevelTriggerThreshold[dasChannelNumber] = false; } else { upperLevelTriggerThreshold[dasChannelNumber] = 0; enableUpperLevelTriggerThreshold[dasChannelNumber] = false; } } } else { enableLowerLevelTriggerThreshold[dasChannelNumber] = false; enableUpperLevelTriggerThreshold[dasChannelNumber] = false; lowerLevelTriggerThreshold[dasChannelNumber] = 0; upperLevelTriggerThreshold[dasChannelNumber] = 0; } } } if (ConfigData.Modules.Length > 0) { // configure level trigger enable/threshold values ConfigureLevelTriggers(enableLowerLevelTriggerThreshold, enableUpperLevelTriggerThreshold, lowerLevelTriggerThreshold, upperLevelTriggerThreshold, qualificationSamples); } info.Success(); } catch (CanceledException ex) { APILogger.Log(ex); info.Cancel(); } catch (Exception ex) { APILogger.Log(ex); info.Error(ex.Message, ex); } } #region Update sensor IDs internal class UpdateIdAsyncInfo : SliceServiceAsyncInfo { public DASModule Module { get; set; } public DASChannel Channel { get; set; } public UpdateIdAsyncInfo(ServiceCallback callback, object userData, DASModule module, DASChannel channel) : base(callback, userData) { Module = module; Channel = channel; } } void IConfigurationActions.UpdateId(ServiceCallback callback, object userData, DASModule module, DASChannel channel) { var info = new UpdateIdAsyncInfo(callback, userData, module, channel); LaunchAsyncWorker("Slice.UpdateId", new WaitCallback(AsyncUpdateId), info); } protected virtual void AsyncUpdateId(object asyncInfo) { UpdateIdAsyncInfo info = asyncInfo as UpdateIdAsyncInfo; try { var dasChannelNumber = DASInfo.MapModuleArrayIndexAndChannelNum2DASChannel(info.Module.ModuleArrayIndex, info.Channel.ModuleChannelNumber); info.Channel.IDs = EIDReader.RetriveEIDs(this, dasChannelNumber, info.Channel.IdType); info.Success(); } catch (CanceledException) { info.Cancel(); } catch (Exception ex) { info.Error(ex.Message, ex); } } /// /// Retrieve the info to fill in the ConfigData property /// /// The delegate to report to /// User supplied data that we pass along void IConfigurationActions.UpdateIDs(ServiceCallback callback, object userData) { var info = new SliceServiceAsyncInfo(callback, userData); LaunchAsyncWorker("Slice.UpdateIDs", new WaitCallback(AsyncUpdateIDs), info); } protected virtual void AsyncUpdateIDs(object configAsyncInfo) { var info = configAsyncInfo as SliceServiceAsyncInfo; if (ConfigData == null) { info.Error("DAS doesn't have any ConfigData"); return; } try { // get sensor ID's if (ConfigData.Modules != null) { foreach (var module in ConfigData.Modules) { foreach (var channel in module.Channels) { var dasChannelNumber = DASInfo.MapModuleArrayIndexAndChannelNum2DASChannel(module.ModuleArrayIndex, channel.ModuleChannelNumber); channel.IDs = EIDReader.RetriveEIDs(this, dasChannelNumber, 0); } } } info.Success(); } catch (CanceledException) { info.Cancel(); } catch (Exception ex) { info.Error(ex.Message, ex); } } #endregion private void PurgeStaleData() { var cmd = new SetArmAttributesToDefaults(this); cmd.SyncExecute(); } #region Arm attribute helpers protected void SetArmAttribute(AttributeTypes.ArmAndEventAttributes key, object value, bool ShouldOverwrite) { var set = new SetArmAttribute(this); set.SetValue(key, value, ShouldOverwrite); set.SyncExecute(); } protected virtual void SetArmAttribute(AttributeTypes.ArmAndEventAttributes key, DFConstantsAndEnums.RecordingMode value) { SetArmAttribute(key, (byte)value, true); } protected void SetArmAttribute(AttributeTypes.ArmAndEventAttributes key, UInt32 value) { SetArmAttribute(key, value, true); } protected void SetArmAttribute(AttributeTypes.ArmAndEventAttributes key, float value) { SetArmAttribute(key, value, true); } protected void SetArmAttribute(AttributeTypes.ArmAndEventAttributes key, string value) { SetArmAttribute(key, value, true); } protected void SetArmAttribute(AttributeTypes.ArmAndEventAttributes key, UInt64 value) { SetArmAttribute(key, value, true); } protected void GetArmAttribute(AttributeTypes.ArmAndEventAttributes key, out byte value) { var query = new QueryArmAttribute(this); query.Key = key; query.SyncExecute(); value = (byte)query.Value; } protected void GetArmAttribute(AttributeTypes.ArmAndEventAttributes key, out DFConstantsAndEnums.RecordingMode value) { var query = new QueryArmAttribute(this); query.Key = key; query.SyncExecute(); if (query.DataType != AttributeTypes.AttributeDataTypes.UInt8) throw new Exception("ConfigurationService.GetArmAttribute.RecordingMode: Found type " + query.DataType); value = (DFConstantsAndEnums.RecordingMode)query.Value; } protected void GetArmAttribute(AttributeTypes.ArmAndEventAttributes key, out UInt32 value) { var query = new QueryArmAttribute(this); query.Key = key; query.SyncExecute(); if (query.DataType != AttributeTypes.AttributeDataTypes.UInt32) throw new Exception("ConfigurationService.GetArmAttribute.UInt32: Found type " + query.DataType); value = (uint)query.Value; } protected void GetArmAttribute(AttributeTypes.ArmAndEventAttributes key, out float value) { var query = new QueryArmAttribute(this); query.Key = key; query.SyncExecute(); if (query.DataType != AttributeTypes.AttributeDataTypes.Float32) throw new Exception("ConfigurationService.GetArmAttribute.float: Found type " + query.DataType); value = (float)query.Value; } protected void GetArmAttribute(AttributeTypes.ArmAndEventAttributes key, out string value) { var query = new QueryArmAttribute(this); query.Key = key; query.SyncExecute(); if (query.DataType != AttributeTypes.AttributeDataTypes.Ascii) throw new Exception("ConfigurationService.GetArmAttribute.string: Found type " + query.DataType); value = (string)query.Value; } protected void GetArmAttribute(AttributeTypes.ArmAndEventAttributes key, out ulong value) { var query = new QueryArmAttribute(this); query.Key = key; query.SyncExecute(); if (query.DataType != AttributeTypes.AttributeDataTypes.UInt64) throw new Exception("ConfigurationService.GetArmAttribute.UInt64: Found type " + query.DataType); value = (ulong)query.Value; } #endregion #region XML attributes private void StoreAttributes(string data, SliceServiceAsyncInfo info, double ProgressSteps) { // first calc how many we need var NumBlocks = data.Length / Slice.Service.Attribute.MaxSingleAttributeSize; if ((data.Length % Slice.Service.Attribute.MaxSingleAttributeSize) != 0) NumBlocks++; // write the blocks // for progress, assume that we'll have about 40 blocks to write var blockWeight = 40.0 / NumBlocks; for (var BlockIdx = 0; BlockIdx < NumBlocks; BlockIdx++) { var blockLength = data.Length - (BlockIdx * Slice.Service.Attribute.MaxSingleAttributeSize); if (blockLength > Slice.Service.Attribute.MaxSingleAttributeSize) blockLength = Slice.Service.Attribute.MaxSingleAttributeSize; var block = data.Substring(BlockIdx * Slice.Service.Attribute.MaxSingleAttributeSize, blockLength); var AttrSet = new SetArmAttribute(this); AttrSet.SetValue((AttributeTypes.ArmAndEventAttributes)(Slice.Service.Attribute.BulkAttributeStartNumber + BlockIdx), block, AttributeTypes.AttributeDataTypes.Ascii, true); AttrSet.SyncExecute(); info.Progress((int)((12.0 + blockWeight * BlockIdx) / ProgressSteps * 100.0)); } // write our speacial header attribute var BlockList = new StringBuilder(Slice.Service.Attribute.BulkAttributeStartNumber.ToString(), DTS.Slice.Service.Attribute.MaxSingleAttributeSize); for (var BlockIdx = 1; BlockIdx < NumBlocks; BlockIdx++) BlockList.Append("," + (Slice.Service.Attribute.BulkAttributeStartNumber + BlockIdx).ToString()); var HeaderAttrSet = new SetArmAttribute(this); HeaderAttrSet.SetValue(AttributeTypes.ArmAndEventAttributes.StoredConfigIndex, BlockList.ToString(), AttributeTypes.AttributeDataTypes.Ascii, true); HeaderAttrSet.SyncExecute(); } private string RetrieveAttributes() { // first get our special header attribute var AttrGet = new QueryArmAttribute(this); AttrGet.Key = AttributeTypes.ArmAndEventAttributes.StoredConfigIndex; AttrGet.SyncExecute(); // make sure we have something // parse the result var BlockListStr = AttrGet.Value as string; // still there? if (string.IsNullOrEmpty(BlockListStr)) { // "Slice.RetrieveAttributes: Header attribute as string is empty" throw new Exception(Strings.Slice_RetrieveAttributes_Err2); } var BlockListStrArr = BlockListStr.Split(','); var BlockList = new List(); foreach (var NumStr in BlockListStrArr) { if (!int.TryParse(NumStr, out int Number)) { // "Slice.RetrieveAttributes: Header attribute is invalid" throw new Exception(Strings.Slice_RetrieveAttributes_Err3); } BlockList.Add(Number); } var AllStrings = new StringBuilder(BlockList.Count * Slice.Service.Attribute.MaxSingleAttributeSize); foreach (int AttrNumber in BlockList) { AttrGet.Key = (AttributeTypes.ArmAndEventAttributes)AttrNumber; AttrGet.SyncExecute(); var str = AttrGet.Value as string; AllStrings.Append(str); } var WholeStr = AllStrings.ToString(); if (string.IsNullOrEmpty(WholeStr)) { // "Slice.RetrieveAttributes: Attributes are empty" throw new Exception(Strings.Slice_RetrieveAttributes_Err4); } return WholeStr; } #endregion /// /// Send all level trigger related setup information to the hardware associated with this /// service invocation. /// /// /// /// An array of s indicating whether or not the index-associated channel /// has "level trigger below..." functionality enabled. /// /// /// /// An array of s indicating whether or not the index-associated channel /// has "level trigger above..." functionality enabled. /// /// /// /// An array of s indicating the lower level trigger threshold for the /// index-associated channel. /// /// /// /// An array of s indicating the upper level trigger threshold for the /// index-associated channel. /// /// protected virtual void ConfigureLevelTriggers(bool[] enableLowerTrigger, bool[] enableUpperTrigger, float[] lowerTriggerThreshold, float[] upperTriggerThreshold, int[] qualificationSamples) { try { #if LEVEL_TRIGGER_DEFINED // CGO // This is a hacked change for now to support 00G8 and prior firmware // in the 1.04 release of SLICEWare. This and the other CGO commented // block of code need more general cleanup before 1.05. if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.LevelTrigger)) { var set = new SetArmAttribute(this); set.SetValue(AttributeTypes.ArmAndEventAttributes.LevelTriggerChannelLessThanEnabled, enableLowerTrigger, true); set.SyncExecute(); set = new SetArmAttribute(this); set.SetValue(AttributeTypes.ArmAndEventAttributes.LevelTriggerChannelGreaterThanEnabled, enableUpperTrigger, true); set.SyncExecute(); set = new SetArmAttribute(this); set.SetValue(AttributeTypes.ArmAndEventAttributes.LevelTriggerLessThanLimit, lowerTriggerThreshold, true); set.SyncExecute(); set = new SetArmAttribute(this); set.SetValue(AttributeTypes.ArmAndEventAttributes.LevelTriggerGreaterThanLimit, upperTriggerThreshold, true); set.SyncExecute(); SetLevelTriggerQualificationSamples(qualificationSamples); } #endif } catch (Exception ex) { throw new ApplicationException("encountered problem configuring level triggers", ex); } } /// /// Set the number of "level triggered" samples required for a level trigger to be declared by the DAS. /// /// /// /// The number of contiguous "level triggered" samples required for a level trigger event. /// /// protected void SetLevelTriggerQualificationSamples(int[] numberOfSamples) { try { var set = new SetArmAttribute(this); set.SetValue(AttributeTypes.ArmAndEventAttributes.LevelTriggerQualificationSamples, numberOfSamples, true); set.SyncExecute(); } catch (Exception ex) { throw new ApplicationException("encountered problem setting number of level trigger qualification samples in hardware", ex); } } /// /// Get the number of "level triggered" samples required for a level trigger to be declared by the DAS. /// /// /// /// The number of contiguous "level triggered" samples required for a level trigger event. /// /// private int[] GetLevelTriggerQualificationSamples() { try { var query = new QueryArmAttribute(this); query.Key = AttributeTypes.ArmAndEventAttributes.LevelTriggerQualificationSamples; query.SyncExecute(); return (int[])query.Value; } catch (Exception ex) { throw new ApplicationException("encountered problem getting number of level trigger qualification samples from hardware", ex); } } /// /// Get determination from hardware whether or not the associated DAS has directly experienced a level trigger event. /// /// /// /// True if the associated DAS has experienced a level trigger event; False otherwise. /// /// private bool[] GetLevelTriggerSeen() { try { var query = new QueryArmAttribute(this); query.Key = AttributeTypes.ArmAndEventAttributes.LevelTriggerSeen; query.SyncExecute(); return (bool[])query.Value; } catch (Exception ex) { throw new ApplicationException("encountered problem getting \"level trigger seen\" condition from hardware", ex); } } /// /// Get number of samples that the associated hardware adjusted its level trigger time value from the time it /// actually issued a trigger fault. This adjustment is necessary because several contiguous level-triggered /// samples are necessary before some flavors of DAS will declare an actual level trigger. Once the hardware has /// determined that a trigger has occured, it must "backdate" the T0 to match the time of the first /// level-triggered sample. This number only exists for hardware that has directly experienced a level trigger /// condition. /// /// /// /// The number of samples that this DAS' trigger time value has been adjusted to compensate for time required /// by the hardware for level trigger evaluation. If the associated hardware did not directly experience the /// level trigger, this value will be null. /// /// private int?[] GetLevelTriggerT0AdjustmentSamples() { try { var armLevTrigT0AdjSamp = new QueryArmAttribute(this); armLevTrigT0AdjSamp.Key = AttributeTypes.ArmAndEventAttributes.LevelTriggerT0AdjustmentSamples; armLevTrigT0AdjSamp.SyncExecute(); var levelTriggerAdjSamples = (int?[])(armLevTrigT0AdjSamp.Value); var levelTriggerSeen = GetLevelTriggerSeen(); var finalAdjustment = new int?[levelTriggerAdjSamples.Length]; for (var channel = 0; channel < finalAdjustment.Length; channel++) { finalAdjustment[channel] = levelTriggerSeen[channel] ? levelTriggerAdjSamples[channel] : null; } return finalAdjustment; } catch (Exception ex) { throw new ApplicationException("encountered problem getting number of level trigger T0 adjustment samples from hardware", ex); } } /// /// I'm not sure what the design philosophy is behind this object, and whether or not everything should /// happen through method invocation and not property access. I was thinking about main the "level trigger /// t0 adustment samples" concept something that was a property at the highest level, and then adding some /// automatic "get level trigger seen" guarding inside of that, but now I notice that this object seemed to /// have been designed with no properties... only pairs of what amount to "set" and "get" methods. I'm not /// sure whether or not that was a conscious decision, but I'm reluctant to do something different. /// /// Also, I'm wondering if such low-level checking using "GetLevelTriggerSeen" is such a good idea since /// depending on what happens at a higher level, that transaction may be issued multiple times, since it's not /// unreasonable for a user of this service to want to explicitly check whether or not get level trigger was /// seen by a module before querying it for it's level trigger t0 adjustment samples. In that case, the /// transaction will happen twice every time. On the other hand, if we don't explicitly check, the "get /// level trigger t0 adjustment samples" method will regularly throw an exception in response and I'm not sure /// that's a good design decision either. Of course that access method could be designed to return a nullable /// int... maybe that's the way to go. /// /// I would like to keep the application of the zero-shift within this object, if possible. I need to check, /// but if the DAS zeroes are read out also through this object, then it should be possible to apply the /// correction internally to each DAS zero, so it can be read by the download code as-is. /// /// Looks like it's the DAS module object that serves up the trigger sample number array that should probably /// be changed to internally apply the change... however that could be a problem because I think at some point /// the application wants to grab that value and change it, and if the module object doesn't realize that the /// modified number incorporates the off-DAS level trigger correction, it could end up re-applying it. /// /// After talking to Chuck, he has reminded me that the trigger sample number isn't rewritten unless there is /// no trigger, so this conflict should be entirely avoidable. So maybe we can handle it all inside the module /// representation after all. In this case, I think the class we need to target is DTS.DASLib.Service.DASModule. /// private void ConfigureRange(float[] ranges) { // this function shall just set the range attribute for channel var set = new SetArmAttribute(this); set.SetValue(AttributeTypes.ArmAndEventAttributes.StackChannelRangesMillivolts, ranges, true); set.SyncExecute(); } //private void ConfigureBridge(bool[] IsHalfBridgeArray) private void ConfigureBridge(byte[] bridgeModeArray) { // we need to construct a byte array, true==1, false==0 var set = new SetArmAttribute(this); set.SetValue(AttributeTypes.ArmAndEventAttributes.StackChannelBridgeCompletionEnable, bridgeModeArray, true); set.SyncExecute(); } private void ConfigureBridgeResistance(UInt16[] BridgeResistanceArray) { // this function shall just set the bridge resistance for the channels var set = new SetArmAttribute(this); set.SetValue(AttributeTypes.ArmAndEventAttributes.StackChannelBridgeResistanceOhms, BridgeResistanceArray, true); set.SyncExecute(); } /// /// A representation of all the level trigger information on a module. /// private class LevelTriggers { /// /// An array of s indicating whether or not the index-associated /// channel has "trigger below" functionality enabled. /// public bool[] EnableLowerTriggerThreshold { get; set; } /// /// An array of s indicating whether or not the index-associated /// channel has "trigger above" functionality enabled. /// public bool[] EnableUpperTriggerThreshold { get; set; } /// /// An array of s representing the "trigger below" threshold /// for the index-associated channel. /// public float[] LowerTriggerThreshold { get; set; } /// /// An array of s representing the "trigger above" threshold /// for the index-associated channel. /// public float[] UpperTriggerThreshold { get; set; } } /// /// Get the level trigger settings from the hardware associated with this object. /// /// /// /// A LevelTriggers object containing the level trigger settings for this object. /// /// private LevelTriggers GetLevelTriggers() { try { var levelTriggers = new LevelTriggers(); QueryArmAttribute query; // CGO // This is a hacked change for now to support 00G8 and prior firmware // in the 1.04 release of SLICEWare. This and the other CGO commented // block of code need more general cleanup before 1.05. // CPB // We're on 1.8 now... so... glad this is still hacked? if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.LevelTrigger)) { query = new QueryArmAttribute(this); query.Key = AttributeTypes.ArmAndEventAttributes.LevelTriggerChannelLessThanEnabled; query.SyncExecute(); levelTriggers.EnableLowerTriggerThreshold = query.Value as bool[]; query = new QueryArmAttribute(this); query.Key = AttributeTypes.ArmAndEventAttributes.LevelTriggerChannelGreaterThanEnabled; query.SyncExecute(); levelTriggers.EnableUpperTriggerThreshold = query.Value as bool[]; query = new QueryArmAttribute(this); query.Key = AttributeTypes.ArmAndEventAttributes.LevelTriggerLessThanLimit; query.SyncExecute(); levelTriggers.LowerTriggerThreshold = query.Value as float[]; query = new QueryArmAttribute(this); query.Key = AttributeTypes.ArmAndEventAttributes.LevelTriggerGreaterThanLimit; query.SyncExecute(); levelTriggers.UpperTriggerThreshold = query.Value as float[]; } else { levelTriggers.EnableLowerTriggerThreshold = null; levelTriggers.EnableUpperTriggerThreshold = null; levelTriggers.LowerTriggerThreshold = null; levelTriggers.UpperTriggerThreshold = null; } return levelTriggers; } catch (Exception ex) { throw new ApplicationException("encountered problem getting level triggers", ex); } } protected float[] GetScaleFactors() { // this function shall just set the range attribute for channel var query = new QueryArmAttribute(this); query.Key = AttributeTypes.ArmAndEventAttributes.StackChannelScaleFactorsMillivoltsPerADC; query.SyncExecute(); return query.Value as float[]; } #region Attributes private UInt32 SampleRate { get { GetArmAttribute(AttributeTypes.ArmAndEventAttributes.SampleRate, out uint Value); return Value; } set => SetArmAttribute(AttributeTypes.ArmAndEventAttributes.SampleRate, value); } private float AAFilter { get { GetArmAttribute(AttributeTypes.ArmAndEventAttributes.AAFilterFrequency, out float Value); return Value; } set => SetArmAttribute(AttributeTypes.ArmAndEventAttributes.AAFilterFrequency, value); } protected string TestID { get { GetArmAttribute(AttributeTypes.ArmAndEventAttributes.Name, out string Value); try { Value = ConfigData.TestID; } catch (Exception) { } return Value; } set { SetArmAttribute(AttributeTypes.ArmAndEventAttributes.Name, value); try { ConfigData.TestID = value; } catch (Exception) { } } } protected string TestDescription { get { GetArmAttribute(AttributeTypes.ArmAndEventAttributes.Description, out string Value); try { Value = ConfigData.Description; } catch (Exception) { } return Value; } set { if (string.IsNullOrEmpty(value)) { value = " "; } SetArmAttribute(AttributeTypes.ArmAndEventAttributes.Description, value); try { ConfigData.Description = value; } catch (Exception) { } } } private UInt64 PreTriggerSamples { get { GetArmAttribute(AttributeTypes.ArmAndEventAttributes.PreTriggerSamplesRequested, out ulong Value); return Value; } set => SetArmAttribute(AttributeTypes.ArmAndEventAttributes.PreTriggerSamplesRequested, value); } private ulong PostTriggerSamples { get { GetArmAttribute(AttributeTypes.ArmAndEventAttributes.PostTriggerSamplesRequested, out ulong Value); return Value; } set => SetArmAttribute(AttributeTypes.ArmAndEventAttributes.PostTriggerSamplesRequested, value); } #endregion } }