using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.IO; using System.Diagnostics; using System.Globalization; using DTS.DASLib.Command; using DTS.Common.Enums; using DTS.Common.ICommunication; using DTS.DASLib.Command.TDAS; using DTS.Common.Utilities; using DTS.Common.Utilities.Logging; using DTS.Common.Interface.Connection; using DTS.Common.Interface.DASFactory.Diagnostics; using DTS.Common.Enums.DASFactory; using DTS.Common.Interface.DASFactory; namespace DTS.DASLib.Service { public partial class TDAS : Communication, IDASCommunication, IConfigurationActions, IDiagnosticsActions, ITriggerCheckActions, IRealTimeActions, IArmActions, IDownloadActions where T : IConnection, new() { public IModuleDiagnosticsResult[] ModuleDiagnosticsResults { get; set; } private class DiagnosticsAsyncPacket { public TDASServiceAsyncInfo info { get; set; } public uint DiagnosticsSampleRateHz { get; set; } public float DiagnosticsAAFilterFrequencyHz { get; set; } public PrePostResults PreOrPost { get; set; } } private class SquibFireCheckArmPacket { public TDASServiceAsyncInfo info { get; set; } public UInt32 DiagnosticsSampleRateHz { get; set; } public float DiagnosticsAAFilterFrequencyHz { get; set; } public double duration; public double delay; } private class DiagnosticsTriggerCheckDownloadPacket { public TDASServiceAsyncInfo info { get; set; } public uint DiagnosticsSampleRateHz { get; set; } public float DiagnosticsAAFilterFrequencyHz { get; set; } public double duration; public double delay; } private class StatusIndicatorPacket { public TDASServiceAsyncInfo info { get; set; } public DiagnosticsStatusIndicatorState state { get; set; } } #region PrepareForDiagnostics void IDiagnosticsActions.ClearTriggerOut(ServiceCallback callback, object userData) { var info = new TDASServiceAsyncInfo(callback, userData); info.Success(); } void IDiagnosticsActions.ClearLatches(ServiceCallback callback, object userData) { var info = new TDASServiceAsyncInfo(callback, userData); info.Success(); } /// /// clears any das trigger lines /// void IDiagnosticsActions.ClearDASTriggerLine(ServiceCallback callback, object userData) { var info = new TDASServiceAsyncInfo(callback, userData); info.Success(); } void IDiagnosticsActions.PerformArmChecks(ServiceCallback callback, object userData) { var info = new TDASServiceAsyncInfo(callback, userData); LaunchAsyncWorker("TDAS.PerformArmChecks", AsyncPerformArmChecks, info); } private void AsyncPerformArmChecks(object o) { var info = o as TDASServiceAsyncInfo; var dasResults = new ArmCheckResults(); Dictionary squibResistances = new Dictionary(); Dictionary sensorIds = new Dictionary(); if (null != ArmCheckActions) { if (ArmCheckActions.PerformBatteryVoltageCheck) { if (IsG5()) { dasResults.BatteryVoltage = new double?[] { double.NaN }; } } if (ArmCheckActions.PerformInputVoltageCheck) { if (IsG5() && G5Mode == G5Modes.VDS) { try { dasResults.InputVoltage = GetInputVoltageTBCommand(true); } catch (Exception ex) { APILogger.Log(ex); } } } if (ArmCheckActions.PerformSquibResistanceCheck) { if (ContainsTOM()) { var iChannel = 0; for (var i = 0; i < ConfigData.Modules.Length; i++) { if (DASInfo.Modules[i].TypeOfModule == DFConstantsAndEnums.ModuleType.EMPTYBANK) { continue; } try { if (IsTom((DASModule)ConfigData.Modules[i])) { var tsa = new TestSquibArray(this); tsa.ModuleIndex = ConfigData.Modules[i].ModuleArrayIndex; tsa.SyncExecute(); for (var ch = 0; ch < tsa.ResistanceOhms.Length; ch++) { squibResistances[iChannel + 2 * ch] = tsa.ResistanceOhms[ch]; squibResistances[iChannel + 2 * ch + 1] = tsa.ResistanceOhms[ch]; } } } catch (Exception ex) { APILogger.Log(ex); } iChannel += ConfigData.Modules[i].Channels.Length; } } } if (ArmCheckActions.PerformEventLineCheck) { try { var ta = new TestAll(this); ta.SyncExecute(); dasResults.StartLineShorted = ta.StartedRecordingFlag == DFConstantsAndEnums.VoltageStatusColor.Red || ta.StartedRecordingFlag == DFConstantsAndEnums.VoltageStatusColor.Yellow; } catch (Exception ex) { APILogger.Log(ex); } try { var bTriggered = false; for (var i = 0; i < ConfigData.Modules.Length; i++) { if (DASInfo.Modules[i].TypeOfModule == DFConstantsAndEnums.ModuleType.EMPTYBANK) { continue; } if (i > 0 && IsG5()) { continue; } var qta = new QueryTriggerLine(this); qta.ModuleIndex = i; qta.SyncExecute(); if (qta.IsTriggered) { bTriggered = true; } } dasResults.EventLineShorted = bTriggered; } catch (Exception ex) { APILogger.Log(ex); } } if (ArmCheckActions.PerformSensorIdCheck) { if (IsG5()) { SensorIDCheckG5(sensorIds); } else { SensorIDCheckByModule(sensorIds); } } } dasResults.SensorIds = sensorIds; dasResults.SquibResistances = squibResistances; ArmCheckResults = dasResults; info.Success(); } /// /// retrieve all sensors and sets the sensor lookup accordingly /// /// lookup, keyed by channel index, values are array of EIDs private void SensorIDCheckG5(Dictionary sensorIds) { try { var idx = new RackIDX(this); idx.SyncExecute(); for (var i = 0; i < idx.IDs.Count; i++) { sensorIds[i] = idx.IDs[i].ToArray(); } } catch (Exception ex) { APILogger.Log(ex); } } /// /// populate EID lookup by going module to module /// note that channels being passed in was being set to 0 before getting called so /// /// lookup keyed by channel index with values of an array of EIDs for that channel private void SensorIDCheckByModule(Dictionary sensorIds) { var channels = 0; int moduleIndex = -1; foreach (var module in ConfigData.Modules) { moduleIndex++; if (DASInfo.Modules[moduleIndex].TypeOfModule == DFConstantsAndEnums.ModuleType.EMPTYBANK) { continue; } try { var read = new ReadSensorIDs(this); read.ModuleIndex = module.ModuleArrayIndex; read.SyncExecute(); for (var i = 0; i < read.IDs.Count; i++) { if (IsTom((DASModule)module)) { sensorIds[channels + i * 2] = new[] { read.IDs[i] }; sensorIds[1 + channels + i * 2] = new[] { read.IDs[i] }; } else { if (!string.IsNullOrWhiteSpace(read.IDs[i].Replace("0", ""))) { sensorIds[channels + i] = new[] { read.IDs[i] }; } } } } catch (Exception ex) { APILogger.Log(ex); } channels += module.Channels.Length; } } void IDiagnosticsActions.SaveTiltSensorDataPre(ServiceCallback callback, object userData) { var info = new TDASServiceAsyncInfo(callback, userData); LaunchAsyncWorker("TDAS.SaveTiltSensorDataPre", AsyncSaveTiltSensorDataPre, info); } private static void AsyncSaveTiltSensorDataPre(object o) { var info = o as TDASServiceAsyncInfo; info?.Success(); } void IDiagnosticsActions.SaveTemperaturesPre(ServiceCallback callback, object userData) { var info = new TDASServiceAsyncInfo(callback, userData); LaunchAsyncWorker("TDAS.SaveTemperaturesPre", AsyncSaveTemperaturesPre, info); } private static void AsyncSaveTemperaturesPre(object o) { var info = o as TDASServiceAsyncInfo; info?.Success(); } /// /// /// Perform diagnostics based on the property ChannelDiagnostics and stuff the /// result in ChannelDiagnosticsResults /// /// the diagnostics sample rate /// the AA filter frequency /// The function to call with information /// Whatever you want to pass along void IDiagnosticsActions.PrepareForDiagnostics(uint DiagnosticsSampleRateHz, float DiagnosticsAAFilterFrequencyHz, PrePostResults WhichResult, ServiceCallback callback, object userData) { var packet = new DiagnosticsAsyncPacket(); packet.info = new TDASServiceAsyncInfo(callback, userData); packet.DiagnosticsSampleRateHz = DiagnosticsSampleRateHz; packet.DiagnosticsAAFilterFrequencyHz = DiagnosticsAAFilterFrequencyHz; packet.PreOrPost = WhichResult; LaunchAsyncWorker("TDAS.PrepareForDiagnostics", AsyncPrepareForDiagnostics, packet); } /// /// the working guts of PrepareForDiagnostics /// /// our async data private void AsyncPrepareForDiagnostics(object asyncInfo) { var packet = asyncInfo as DiagnosticsAsyncPacket; SyncPrepareForDiagnostics(packet); // we're done packet?.info.Success(); } private void SyncPrepareForDiagnostics(DiagnosticsAsyncPacket packet) { try { if (packet.PreOrPost != PrePostResults.PreEventDiagnosticsResult) return; var bHasDigitalChannels = false; foreach (var m in ConfigData.Modules) { foreach (var c in m.Channels) { if (c is AnalogInputDASChannel) { if ((c as AnalogInputDASChannel).DigitalInputChannel && c.ConfigurationMode == DFConstantsAndEnums.ConfigMode.Normal) { bHasDigitalChannels = true; break; } } if (bHasDigitalChannels) { break; } } } var samplerate = Convert.ToInt32(packet.DiagnosticsSampleRateHz); if (samplerate > 10000) { samplerate = 10000; } var aarate = Convert.ToInt32(packet.DiagnosticsAAFilterFrequencyHz); if (aarate > MaxAAFilterRateHz) { aarate = Convert.ToInt32(MaxAAFilterRateHz); } if (0 == aarate) { if (IsG5()) { aarate = Convert.ToInt32(MaxAAFilterRateHz); } } if (DFConstantsAndEnums.DontDoSDL == false) { foreach (var module in ConfigData.Modules) { var dummyChannels = from c in module.Channels where c.ConfigurationMode == DFConstantsAndEnums.ConfigMode.DummyArm select c; var goodChannels = from c in module.Channels where c.ConfigurationMode == DFConstantsAndEnums.ConfigMode.Normal select c; if (DASInfo.Modules[module.ModuleArrayIndex].TypeOfModule == DFConstantsAndEnums.ModuleType.EMPTYBANK) { continue; } if (IsTom(module)) { var sdl = new SetupTOMDASLoad(this); sdl.ArmMode = SetupTOMDASLoad.ARMMode.DIAGW; sdl.ModuleIndex = module.ModuleArrayIndex; sdl.TestId = "D"; //trimmed for performance sdl.TestConfig = $"{"D"}"; //trimmed for performance{SETUPDASREAD_SENTINEL}{module.GetCRC32()}"; sdl.TriggerType = SetupTOMDASLoad.TriggerTypes.BUS; sdl.PostADWaitTime = -1; sdl.SampleRate = samplerate; sdl.PostTriggerSeconds = 1; sdl.PreTriggerSeconds = 1; if (null != dummyChannels && dummyChannels.Any() && !bHasDigitalChannels) { sdl.IsDummyArm = true; sdl.CollectData = false; } try { sdl.SyncExecute(); } catch (Exception ex) { packet.info.Error(ex.Message); return; } } else { var sdl = new SetupDASLoad(this); var sdlDIM = new SetupDASLoadDIM(this); try { if (null != dummyChannels && dummyChannels.Any() || null == goodChannels || !goodChannels.Any()) { sdl.SampleRate = samplerate; sdlDIM.SampleRate = samplerate; sdl.IsDummyArm = true; sdlDIM.IsDummyArm = true; sdl.CollectData = false; sdlDIM.CollectData = false; } sdl.ArmMode = SetupDASLoad.ARMMode.WAIT; sdlDIM.ArmMode = SetupDASLoadDIM.ArmModes.WAIT; sdlDIM.ModuleIndex = module.ModuleArrayIndex; sdl.ModuleIndex = module.ModuleArrayIndex; sdlDIM.SampleRate = samplerate; sdl.SampleRate = samplerate; sdlDIM.PreTriggerSeconds = 1; sdl.PreTriggerSeconds = 1; sdlDIM.PostTriggerSeconds = 1; sdl.PostTriggerSeconds = 1; sdl.TestConfig = $"{"D"}"; //trimmed for performance{SETUPDASREAD_SENTINEL}{module.GetCRC32()}"; sdlDIM.TestConfig = sdl.TestConfig; sdl.TestId = "D"; //trimmed for performance sdlDIM.TestId = sdl.TestId; sdl.PostADWaitTime = -1; sdlDIM.PostADWaitTime = -1; sdl.TriggerType = SetupDASLoad.TriggerTypes.HW; sdlDIM.TriggerType = SetupDASLoadDIM.TriggerTypes.HW; if (0 == aarate && IsG5()) { aarate = Convert.ToInt32(MaxAAFilterRateHz); } sdl.SetHardwareAAFilter(aarate, MaxAAFilterRateHz); } catch (NotSupportedException) { sdl.SetHardwareAAFilter(250, MaxAAFilterRateHz); } catch (Exception ex) { packet.info.Error(ex.Message); } try { if (DASInfo.Modules[module.ModuleArrayIndex].TypeOfModule == DFConstantsAndEnums.ModuleType.ProDIM && !IsG5()) { sdlDIM.SyncExecute(); } else { if (module.ModuleArrayIndex > 0 && IsG5()) { //we only need to do the first (and 4th) module in a g5 } else { sdl.SyncExecute(); } } } catch (Exception ex) { packet.info.Error(ex.Message); return; } } } } TurnOnExcitation(); } catch (CanceledException) { // like most tv shows, we have been canceled packet.info.Cancel(); } catch (Exception ex) { packet.info.Error(ex.Message); } } #endregion #region PrepareForBridgeResistanceMeasurement /// /// Perform diagnostics based on the property ChannelDiagnostics and stuff the /// result in ChannelDiagnosticsResults /// /// the sample rate for diagnostics /// the AA filter frequency /// The function to call with information /// Whatever you want to pass along void IDiagnosticsActions.PrepareForBridgeResistanceMeasurement(uint diagnosticsSampleRateHz, float diagnosticsAAFilterFrequencyHz, ServiceCallback callback, object userData) { var packet = new DiagnosticsAsyncPacket(); packet.info = new TDASServiceAsyncInfo(callback, userData); packet.DiagnosticsSampleRateHz = diagnosticsSampleRateHz; packet.DiagnosticsAAFilterFrequencyHz = diagnosticsAAFilterFrequencyHz; LaunchAsyncWorker("TDAS.PrepareForBridgeResistanceMeasurement", AsyncPrepareForBridgeResistanceMeasurement, packet); } private static void AsyncPrepareForBridgeResistanceMeasurement(object asyncInfo) { var packet = asyncInfo as DiagnosticsAsyncPacket; Thread.Sleep(100); // we're done packet?.info.Success(); } #endregion #region GetBridgeMeasurement void IDiagnosticsActions.GetBridgeMeasurement(ServiceCallback callback, object userData) { var packet = new DiagnosticsAsyncPacket(); packet.info = new TDASServiceAsyncInfo(callback, userData); LaunchAsyncWorker("TDAS.GetBridgeMeasurement", AsyncGetBridgeMeasurement, packet); } void IDiagnosticsActions.MeasureTransferSpeed(ServiceCallback callback, object userData) { var info = new TDASServiceAsyncInfo(callback, userData); info.Success(); } private void AsyncGetBridgeMeasurement(object asyncInfo) { var packet = asyncInfo as DiagnosticsAsyncPacket; //noticed the previous version of this code would only query one channel //and then would set all the rest to null bridge resistance //ideally we query them all //18246 [Internal] Measure Bridge Resistance issue in DP3.1.464 try { DiagnosticsResult[] results; if (ChannelDiagnostics != null && ChannelDiagnostics.Length > 0) { results = new DiagnosticsResult[ChannelDiagnostics.Length]; for (var idx = 0; idx < ChannelDiagnostics.Length; idx++) { results[idx] = new DiagnosticsResult(); results[idx].DASChannelNumber = ChannelDiagnostics[idx].DASChannelNumber; results[idx].EventNumber = DFConstantsAndEnums.EVENT_NUMBER_MEASURE_BRIDGE; // it's not really an event } } else { // this case will only generate the base input values results = null; } for (var idx = 0; idx < ChannelDiagnostics.Length; idx++) { var moduleIndex = DASInfo.MapDASChannelNumber2ModuleArrayIndex(ChannelDiagnostics[idx].DASChannelNumber); var channelNumber = DASInfo.MapDASChannelNumber2ModuleChannelNumber(ChannelDiagnostics[idx].DASChannelNumber) + 1; var excitation = 0.0; var current = 0.0; var inputRange = 0; if (DASInfo.Modules[0].TypeOfModule == DFConstantsAndEnums.ModuleType.G5Analog) { excitation = G5Mode == G5Modes.VDS ? 2 : 5; current = 0.5; inputRange = 2500; } else { excitation = 5; current = 0.8; inputRange = 5000; } var bridgeValue = 5000D; var gain = CalculateGainForExpectedBridge(bridgeValue, current, true); var sc = new SetupClear(this); sc.ModuleIndex = moduleIndex; sc.SyncExecute(); const int MAX_ATTEMPTS = 2; for (var attempt = 0; attempt < MAX_ATTEMPTS; attempt++) { if (0.8 * inputRange / gain < bridgeValue * current) { current = (0.8 * inputRange / gain) / bridgeValue; } //SCL var scl = new SetupChannelLoad(this, IsG5()); scl.ModuleIndex = moduleIndex; scl.Channel = channelNumber; scl.ExcitationSetting = excitation; scl.Gain = gain; scl.ShuntPosition = (int)(2 * bridgeValue); scl.SyncExecute(); //TEST PREPARE ON var tp = new TestPrepare(this); tp.On = true; tp.ModuleIndex = moduleIndex; tp.SyncExecute(); //SCR var scr = new SetupChannelRead(this); scr.ModuleIndex = moduleIndex; scr.Channel = channelNumber; scr.SyncExecute(); gain = scr.Gain; var scaleFactor = (2 * inputRange / gain) / 65536; //ZA var za = new ZeroAverage(this); za.ModuleIndex = moduleIndex; za.Channel = channelNumber - 1; za.SyncExecute(); //SA var sa = new SampleAverageWithFallback(this, moduleIndex); sa.SyncExecute(); var zero = sa.ChannelValues[channelNumber - 1]; //Cal DAC var c = new CalDAC(this); c.ModuleIndex = moduleIndex; c.Channel = channelNumber - 1; c.EOrX = 'E'; c.Current = current; c.Gain = gain; c.SyncExecute(); //SA sa = new SampleAverageWithFallback(this, moduleIndex); sa.SyncExecute(); var shuntedValue = sa.ChannelValues[channelNumber - 1]; //Cal DAC c = new CalDAC(this); c.ModuleIndex = moduleIndex; c.Channel = channelNumber - 1; c.EOrX = 'X'; c.Current = current; c.SyncExecute(); bridgeValue = (shuntedValue - zero) * scaleFactor / current; current = 1.0; gain = CalculateGainForExpectedBridge(bridgeValue, current, true); //G5 } var bridgeResistance = (int)(bridgeValue * 2); results[idx].BridgeResistance = bridgeResistance; } SetChannelDiagnosticsResults(results, false); Thread.Sleep(100); // we're done packet?.info.Success(); } catch (CanceledException cex) { var temp = cex.Message; // like most tv shows, we have been canceled packet?.info.Cancel(); } catch (Exception ex) { packet?.info.Error(ex.Message, ex); } } internal double CalculateGainForExpectedBridge(double dBridge, double dCurrent, bool IsG5) { var inputRange = 0; inputRange = IsG5 ? 2500 : 5000; var dGain = Math.Max(3, Math.Floor(0.8 * inputRange / (dBridge * dCurrent))); return dGain; } #endregion #region CalibrateAndGetResults void IDiagnosticsActions.SetStatusIndicator(DiagnosticsStatusIndicatorState state, ServiceCallback callback, object userData) { var packet = new StatusIndicatorPacket(); packet.info = new TDASServiceAsyncInfo(callback, userData); packet.state = state; try { LaunchAsyncWorker("TDAS.SetStatusIndicator", new WaitCallback(AsyncSetStatusIndicator), packet); } catch (Exception ex) { //This could be due to DAS getting disconnected and AsyncSetStatusIndicator //never getting called so wrap up the use of this thread. packet.info.Error(ex.Message); } } private void AsyncSetStatusIndicator(object asyncInfo) { var packet = asyncInfo as StatusIndicatorPacket; var stateString = new DescriptionAttributeCoder().DecodeAttributeValue(packet.state); var sl = new SetLED(this, stateString); try { sl.SyncExecute(); packet.info.Success(); } catch (Exception e) { //15606 communications error, mark failed APILogger.Log(e); packet.info.Error(e.Message); } } /// /// Perform diagnostics based on the property ChannelDiagnostics and stuff the /// result in ChannelDiagnosticsResults /// /// The function to call with information /// Whatever you want to pass along void IDiagnosticsActions.DiagnosAndGetResults(int EventNumber, PrePostResults WhichResult, ServiceCallback callback, object userData) { var packet = new EventDiagnosticsAsyncPacket(); packet.info = new TDASServiceAsyncInfo(callback, userData); if (WhichResult == PrePostResults.PreEventDiagnosticsResult) { var info = packet.info; LaunchAsyncWorker("TDAS.DiagnosAndGetResults", AsyncDiagnosAndGetResults, info); } else { //Post-test diagnostics for TDAS Rack has already been automatically run, so get the results; for G5, just return the pre-test diagnostics results packet.EventNumber = DFConstantsAndEnums.EVENT_NUMBER_POSTTEST_DIAG; packet.WhichResult = WhichResult; LaunchAsyncWorker("TDAS.GetEventDiagnosticsResults", AsyncGetEventDiagnosticsResults, packet); } } private bool IsActive(int moduleArrayIndex, int dasChannelNumber, AnalogInputDASChannel analog) { var ss = new SampleAverage(this) { ModuleIndex = moduleArrayIndex }; ss.SyncExecute(); bool bValue; if (IsG5()) { bValue = ss.DigitalValues[dasChannelNumber - 32]; } else { var index = dasChannelNumber; for (var i = 0; i < moduleArrayIndex; i++) { index -= ConfigData.Modules[i].NumberOfChannels(); } bValue = ss.ChannelValues[index] >= 1D; } switch (analog.DigitalMode) { case DigitalInputModes.CCNC: return bValue; case DigitalInputModes.CCNO: return !bValue; case DigitalInputModes.THL: return !bValue; case DigitalInputModes.TLH: return bValue; } return false; } /// /// returns the expected slowest module to run diagnostics on the system, this is a factor of /// number of configured channels and whether they are running offset removal and/or shunt /// 21200 Correct SlowestModule calculation for TDAS /// /// module array index of expected slowest module to run diagnostics on rack private int GetSlowestModule() { var slowestModule = -1; var weightOfSlowest = 0D; foreach (var m in ConfigData.Modules) { if (IsTom(m)) { continue; } var configured = m.NumberOfConfiguredChannels(); var weight = (double)configured; try { //http://manuscript.dts.local/f/cases/30004/selecting-DIM-for-slowest-module-can-cause-rack-to-stop-responding if (IsDIM(m)) { weight = .9; } //during testing without bridge plugs offset removal seemed by far the longest running //task of diagnostics, however that may be a result of large offsets being present //it is believed (Tim Kippen) that offset removal and shunt will take the most time //so I wanted to add a flat weight each channel requiring offset removal and for //shunt. This may be inaccurate based on how far the offset is from 0, but should still //handle the case of 21200 where there's a module with 8 HFNN vs 8 FFYE //additionally less channels is significantly faster (3s vs 21), so we still want to //consider the total number of channels as a weight as well //I'm weight remove offset and perform shunt slightly higher than my quick test indicates //I should due to observations in customer logs var removeOffsetCount = 0; var performShuntCount = 0; foreach (var channel in m.Channels) { if (!(channel is AnalogInputDASChannel aic)) { continue; } if (aic.RemoveOffset) { removeOffsetCount++; } if (aic.ShuntIsEnabled) { performShuntCount++; } } weight += (removeOffsetCount * DFConstantsAndEnums.TDASRemoveOffsetWeighting) + (performShuntCount * DFConstantsAndEnums.TDASShuntEmulationWeighting); } catch (Exception ex) { APILogger.Log($"Failed to determine weighting for module {m.ModuleArrayIndex} of {SerialNumber}", ex); } if (weight <= weightOfSlowest) { continue; } slowestModule = m.ModuleArrayIndex; weightOfSlowest = weight; } return slowestModule; } /// /// the working guts of DiagnosAndGetResults /// /// our async data private void AsyncDiagnosAndGetResults(object asyncInfo) { var info = asyncInfo as TDASServiceAsyncInfo; try { //this was part of a resharper irmprovement //I'm choosing to assert here as I'm not sure what the //behavior should be if info is null, and this will preserve //the existing behavior, but a little cleaner Debug.Assert(info != null, "info != null"); //this call is needed when we are re-running diagnostics ... TurnOnExcitation(); if (null != ChannelDiagnostics && ChannelDiagnostics.Any()) { var channelsToRunOn = 0; var channelToRunOn = 0; var bChannelToRunOnIsTOM = false; foreach (var ch in ChannelDiagnostics) { if (ch.AllActionsDisabled()) continue; channelsToRunOn++; channelToRunOn = ch.DASChannelNumber; if (ch.SquibFireCheck) { bChannelToRunOnIsTOM = true; } else { bChannelToRunOnIsTOM = false; } } if (channelsToRunOn > 0) { info.NewData(new ServiceCallbackData.DiagnosticNewData() { Action = ServiceCallbackData.DiagnosticNewData.Actions.QueryModules, DasChannelNumber = 0, Result = "" }); BaseInput = new BaseInputValues { InputMilliVolts = 1000D * GetInputVoltageTBCommand(IsG5()) }; } var configuredChannelPerModuleArrayIndex = new Dictionary(); // first get the scale factors if (ChannelDiagnosticsResults != null) { foreach (var m in ConfigData.Modules) { var configured = m.NumberOfConfiguredChannels(); configuredChannelPerModuleArrayIndex[m.ModuleArrayIndex] = configured; } //g5 doesn't seem to like test channel run pre all right now? if (IsG5()) { if (IsG5() && G5Mode == G5Modes.VDS) { try { var ds = new G5DockStat(this, true); ds.SyncExecute(); BaseInput.BatteryIsCharging = ds.BatteryCharging; //AF - 08/25/2016 BaseInput.BatteryMilliVolts = ds.BatteryMilliVolts; } catch (Exception) { /* tolerate failure in case we are not in a VDS */} BaseInput.BatteryMilliVolts = double.NaN; } var bRunTestChannelRun = false; if (null != ChannelDiagnostics && ChannelDiagnostics.Any()) { foreach (var cd in ChannelDiagnostics) { if (!cd.AllActionsDisabled()) { bRunTestChannelRun = true; } } } if (bRunTestChannelRun) { info.NewData(new ServiceCallbackData.DiagnosticNewData() { Action = ServiceCallbackData.DiagnosticNewData.Actions.TestChannelRun, DasChannelNumber = 0, Result = "" }); //could take 6 seconds for each channel, I give it a little leeway beyond that var tp = new TestChannelRun(this, 32 * 9 * 1000) { ChannelIndex = -1, RackCommand = true }; tp.SyncExecute(); } } else { if (1 == channelsToRunOn) { var moduleIndex = DASInfo.MapDASChannelNumber2ModuleArrayIndex(channelToRunOn); var channel = DASInfo.MapDASChannelNumber2ModuleChannelNumber(channelToRunOn); var module = DASInfo.Modules[moduleIndex].ModuleArrayIndex; var tr = new TestChannelRun(this); if (bChannelToRunOnIsTOM) { tr.ChannelIndex = (1 + channel) / 2; } else { tr.ChannelIndex = 1 + channel; } tr.ModuleIndex = module; tr.SyncExecute(); } else { var slowestModule = GetSlowestModule(); //toms have to be treated differnetly, they are in capable of updating with TESTCHANNELRUNBROADCAST and have //to be run directly ... if (slowestModule >= 0) { var qsn = new QuerySerialNumberBroadcast(this, slowestModule); qsn.SyncExecute(); var tp = new TestChannelRunBroadcast(this, 8 * DFConstantsAndEnums.ExpectedMaxTDASDiagnosticRunTimePerChannelMS, slowestModule); tp.SyncExecute(); } foreach (var m in ConfigData.Modules) { var configured = m.NumberOfConfiguredChannels(); if (configured <= 0 || !IsTom(m)) continue; var tp = new TestChannelRun(this) { ModuleIndex = m.ModuleArrayIndex, ChannelIndex = -1 }; tp.SyncExecute(); } } } Thread.Sleep(2000); TestChannelReadAll tcr2 = null; var currentModule = -1; for (var i = 0; i < ChannelDiagnostics.Length; i++) { if (ChannelDiagnostics[i].AllActionsDisabled()) continue; var moduleIndex = DASInfo.MapDASChannelNumber2ModuleArrayIndex(ChannelDiagnostics[i].DASChannelNumber); var channel = DASInfo.MapDASChannelNumber2ModuleChannelNumber(ChannelDiagnostics[i].DASChannelNumber); var module = DASInfo.Modules[moduleIndex].ModuleArrayIndex; //you can run diagnostics on a single module, which means your diagnostic actions will be smaller and in a different //order than your results, so find the result index separately than the diagnostics we are running through int resultIndex = -1; for (var idx = 0; idx < ChannelDiagnosticsResults.Length; idx++) { if (ChannelDiagnosticsResults[idx].DASChannelNumber == ChannelDiagnostics[i].DASChannelNumber) { resultIndex = i; break; } } //no result in place, won't run this diagnostic action ... if (resultIndex < 0) { continue; } if (IsTom(ConfigData.Modules[moduleIndex])) { continue; } info.NewData(new ServiceCallbackData.DiagnosticNewData() { Action = ServiceCallbackData.DiagnosticNewData.Actions.TestChannelRead, DasChannelNumber = 0, Result = "" }); if (1 == channelsToRunOn) { DASInfo.MapDASChannelNumber2ModuleArrayIndex(channelToRunOn); var channel1 = DASInfo.MapDASChannelNumber2ModuleChannelNumber(channelToRunOn); var module1 = DASInfo.Modules[moduleIndex].ModuleArrayIndex; var analog = ConfigData.Modules[moduleIndex].Channels[channel] as AnalogInputDASChannel; if (analog.DigitalInputChannel) { ChannelDiagnosticsResults[resultIndex].DigitalInputActiveState = IsActive(module1, channelToRunOn, analog); } else { var tcRead = new TestChannelRead(this) { ModuleIndex = module1, ChannelIndex = 1 + channel1 }; tcRead.SyncExecute(); ChannelDiagnosticsResults[resultIndex].MeasuredExcitationMilliVolts = tcRead.Excitation * 1000D; ChannelDiagnosticsResults[resultIndex].NegativeExcitation = tcRead.NegativeExcitation; switch (analog.Excitation) { case ExcitationVoltageOptions.ExcitationVoltageOption.Volt10: ChannelDiagnosticsResults[resultIndex].ExpectedExcitationMilliVolts = 10000; break; case ExcitationVoltageOptions.ExcitationVoltageOption.Volt2: ChannelDiagnosticsResults[resultIndex].ExpectedExcitationMilliVolts = 2000; break; case ExcitationVoltageOptions.ExcitationVoltageOption.Volt5: ChannelDiagnosticsResults[resultIndex].ExpectedExcitationMilliVolts = 5000; break; case ExcitationVoltageOptions.ExcitationVoltageOption.Undefined: ChannelDiagnosticsResults[resultIndex].ExpectedExcitationMilliVolts = 5000; break; default: throw new InvalidDataException("invalid excitation " + analog.Excitation); } var stddev = tcRead.ZeroStdDevAtGainADC; ChannelDiagnosticsResults[resultIndex].NoisePercentFullScale = 100D * stddev / (-1D * short.MinValue); ChannelDiagnosticsResults[resultIndex].FinalOffsetADC = Convert.ToInt16(tcRead.ZeroAverageADCValue); if (ChannelDiagnostics[i].MeasureOffset) { if (ChannelDiagnostics[i].RemoveOffset) { ChannelDiagnosticsResults[resultIndex].AutoZeroPercentDeviation = 100D * ChannelDiagnosticsResults[resultIndex].FinalOffsetADC / short.MaxValue; } else { ChannelDiagnosticsResults[resultIndex].AutoZeroPercentDeviation = null; } } try { ChannelDiagnosticsResults[resultIndex].ZeroMVInADC = Convert.ToInt16(tcRead.ZeroMvADC); } catch (Exception ex) { APILogger.Log(ex); ChannelDiagnosticsResults[resultIndex].ZeroMVInADC = 0; } try { ChannelDiagnosticsResults[resultIndex].ScalefactorMilliVoltsPerADC = tcRead.MvScaleFactorADC; if (ChannelDiagnostics[i].MeasureOffset) { ChannelDiagnosticsResults[resultIndex].MeasuredOffsetMilliVolts = tcRead.SensorOffsetMv; //The following will be subtracted out in GetInitialOffset() of HardwareChannel, but since it's //already accounted for in GetSensorOffsetmV we don't want to subtract anything (unlike SLICE) ChannelDiagnosticsResults[resultIndex].MeasuredInternalOffsetMilliVolts = 0D; } } catch (Exception ex) { ChannelDiagnosticsResults[resultIndex].ScalefactorMilliVoltsPerADC = 1; APILogger.Log(ex); } try { if (ChannelDiagnostics[i].PerformShuntCheck) { ChannelDiagnosticsResults[resultIndex].MeasuredShuntDeflectionMv = tcRead.ShuntCalibrationDeltaADC * tcRead.MvScaleFactorADC; ChannelDiagnosticsResults[resultIndex].TargetShuntDeflectionMv = analog.ShuntTargetADC * tcRead.MvScaleFactorADC; } } catch (Exception ex) { ChannelDiagnosticsResults[resultIndex].MeasuredShuntDeflectionMv = 1; ChannelDiagnosticsResults[resultIndex].TargetShuntDeflectionMv = 1; APILogger.Log(ex); } } } else { var analog = (AnalogInputDASChannel)ConfigData.Modules[moduleIndex].Channels[channel]; if (analog.DigitalInputChannel) { ChannelDiagnosticsResults[resultIndex].DigitalInputActiveState = IsActive(ConfigData.Modules[moduleIndex].ModuleArrayIndex, ChannelDiagnostics[i].DASChannelNumber, analog); continue; } if (module != currentModule) { tcr2 = new TestChannelReadAll(this, configuredChannelPerModuleArrayIndex[module]) { ModuleIndex = module }; currentModule = module; tcr2.SyncExecute(); tcr2.CheckData(); } ChannelDiagnosticsResults[resultIndex].MeasuredExcitationMilliVolts = tcr2.GetExcitation(1 + channel) * 1000D; ChannelDiagnosticsResults[resultIndex].NegativeExcitation = tcr2.GetNegativeExcitation(1 + channel); switch (analog.Excitation) { case ExcitationVoltageOptions.ExcitationVoltageOption.Volt10: ChannelDiagnosticsResults[resultIndex].ExpectedExcitationMilliVolts = 10000; break; case ExcitationVoltageOptions.ExcitationVoltageOption.Volt2: ChannelDiagnosticsResults[resultIndex].ExpectedExcitationMilliVolts = 2000; break; case ExcitationVoltageOptions.ExcitationVoltageOption.Volt5: ChannelDiagnosticsResults[resultIndex].ExpectedExcitationMilliVolts = 5000; break; case ExcitationVoltageOptions.ExcitationVoltageOption.Undefined: ChannelDiagnosticsResults[resultIndex].ExpectedExcitationMilliVolts = 5000; break; default: throw new InvalidDataException("invalid excitation " + analog.Excitation); } var stddev = tcr2.GetZeroStdDevAtGainADC(1 + channel); ChannelDiagnosticsResults[resultIndex].NoisePercentFullScale = 100D * stddev / (-1D * short.MinValue); ChannelDiagnosticsResults[resultIndex].FinalOffsetADC = Convert.ToInt16(tcr2.GetZeroAverageADCValue(1 + channel)); if (ChannelDiagnostics[i].MeasureOffset) { if (ChannelDiagnostics[resultIndex].RemoveOffset) { ChannelDiagnosticsResults[resultIndex].AutoZeroPercentDeviation = 100D * ChannelDiagnosticsResults[i].FinalOffsetADC / short.MaxValue; } else { ChannelDiagnosticsResults[resultIndex].AutoZeroPercentDeviation = null; } } try { ChannelDiagnosticsResults[resultIndex].ZeroMVInADC = Convert.ToInt16(tcr2.GetZeromVADC(1 + channel)); } catch (Exception ex) { ChannelDiagnosticsResults[resultIndex].ZeroMVInADC = 0; APILogger.Log(ex); } try { ChannelDiagnosticsResults[resultIndex].ScalefactorMilliVoltsPerADC = tcr2.GetmVScaleFactorADC(1 + channel); if (ChannelDiagnostics[i].MeasureOffset) { ChannelDiagnosticsResults[resultIndex].MeasuredOffsetMilliVolts = tcr2.GetSensorOffsetmV(1 + channel); //The following will be subtracted out in GetInitialOffset() of HardwareChannel, but since it's //already accounted for in GetSensorOffsetmV we don't want to subtract anything (unlike SLICE) ChannelDiagnosticsResults[resultIndex].MeasuredInternalOffsetMilliVolts = 0D; } } catch (Exception ex) { ChannelDiagnosticsResults[resultIndex].ScalefactorMilliVoltsPerADC = 1; APILogger.Log(ex); } try { if (ChannelDiagnostics[i].PerformShuntCheck) { ChannelDiagnosticsResults[resultIndex].MeasuredShuntDeflectionMv = tcr2.GetShuntCalibrationDeltaADC(1 + channel) * tcr2.GetmVScaleFactorADC(1 + channel); ChannelDiagnosticsResults[resultIndex].TargetShuntDeflectionMv = analog.ShuntTargetADC * tcr2.GetmVScaleFactorADC(1 + channel); } } catch (Exception ex) { ChannelDiagnosticsResults[resultIndex].MeasuredShuntDeflectionMv = 1; ChannelDiagnosticsResults[resultIndex].TargetShuntDeflectionMv = 1; APILogger.Log(ex); } } } } } info.Progress(100); info.Success(); } catch (CanceledException) { // we have been canceled info.Cancel(); } catch (Exception ex) { if (ex.Data.Contains("Status")) { var cs = ex.Data["Status"] as DFConstantsAndEnums.CommandStatus?; if (cs == DFConstantsAndEnums.CommandStatus.StatusSetupShuntDACOutputExceeded) { info.Error("Shunt DAC output exceeded while running diagnostics on channel.\n Calibration stopped."); return; } } info.Error(ex.Message, ex); } } #endregion #region GetEventDiagnosticsResults private class EventDiagnosticsAsyncPacket { public TDASServiceAsyncInfo info { get; set; } public int EventNumber { get; set; } public PrePostResults WhichResult { get; set; } } /// /// Retrieve the results from the implicit pre and post event diagnostics /// /// Which event number to Retrieve from /// The pre or post test results? /// The function to call with information /// Whatever you want to pass along void IDiagnosticsActions.GetEventDiagnosticsResults(int EventNumber, PrePostResults WhichResult, ServiceCallback callback, object userData) { //Debug.Assert(WhichResult == PrePostResults.PreEventDiagnosticsResult); var packet = new EventDiagnosticsAsyncPacket(); packet.info = new TDASServiceAsyncInfo(callback, userData); packet.EventNumber = EventNumber; packet.WhichResult = WhichResult; LaunchAsyncWorker("TDAS.GetEventDiagnosticsResults", AsyncGetEventDiagnosticsResults, packet); } private void AsyncGetEventDiagnosticsResults(object asyncInfo) { var packet = asyncInfo as EventDiagnosticsAsyncPacket; var attributeName = ""; var results = new List(); try { Debug.Assert(packet != null, "packet != null"); for (var i = 0; i < DASInfo.Modules.Length; i++) { var bSkipModule = false; var moduleResults = new List(); for (var z = 0; z < DASInfo.Modules[i].NumberOfChannels; z++) { var dr = new DiagnosticsResult(); DiagnosticsResult drNext = null; if ((z == 0) && (ConfigData.Modules[i].Channels[z].ConfigurationMode == DFConstantsAndEnums.ConfigMode.DummyArm) && !IsG5()) { bSkipModule = true; } if (bSkipModule) { dr = null; } else { dr.DASChannelNumber = DASInfo.MapModuleArrayIndexAndChannelNum2DASChannel(DASInfo.Modules[i].ModuleArrayIndex, z); if (ConfigData.Modules[i].Channels[z].ConfigurationMode == DFConstantsAndEnums.ConfigMode.Normal) { var preOrPost = TestChannelReadCommandString.TestType.Pre; if (ConfigData.Modules[i].Channels[z] is OutputSquibChannel osc) { if (osc.ConfigurationMode != DFConstantsAndEnums.ConfigMode.Normal) { continue; } if (osc.MeasurementType == SquibMeasurementType.CURRENT) { dr = null; continue; } drNext = new DiagnosticsResult { EventNumber = 1 }; if (packet.WhichResult == PrePostResults.PostEventDiagnosticsResult) { preOrPost = TestChannelReadCommandString.TestType.Post; } var tcr2 = new TestSquibChannelRead(this, preOrPost) { ChannelIndex = 1 + (z / 2), ModuleIndex = ConfigData.Modules[i].ModuleArrayIndex }; try { tcr2.SyncExecute(); } catch (Exception) { tcr2 = new TestSquibChannelRead(this) { ChannelIndex = 1 + (z / 2), ModuleIndex = ConfigData.Modules[i].ModuleArrayIndex }; } dr.NoisePercentFullScale = 100D * tcr2.VoltageStdDevADC / (-1D * short.MinValue); dr.FinalOffsetADC = Convert.ToInt16(tcr2.VoltagePreZero); dr.AutoZeroPercentDeviation = null;//never remove offset on a squib? dr.ScalefactorMilliVoltsPerADC = tcr2.VoltageScaleFactorMv; drNext.NoisePercentFullScale = 100D * tcr2.CurrentStdDevADC / (-1D * short.MinValue); drNext.ScalefactorMilliVoltsPerADC = tcr2.CurrentScaleFactorMv; drNext.FinalOffsetADC = Convert.ToInt16(tcr2.CurrentPreZero); drNext.DASChannelNumber = dr.DASChannelNumber + 1; } else if (ConfigData.Modules[i].Channels[z] is OutputTOMDigitalChannel) { } else { if (ConfigData.Modules[i].Channels[z] is AnalogInputDASChannel aic) { if (aic.DigitalInputChannel) { dr.FinalOffsetADC = 0; dr.AutoZeroPercentDeviation = null; dr.ZeroMVInADC = 0; dr.ScalefactorMilliVoltsPerADC = 1; dr.MeasuredOffsetMilliVolts = 0D; dr.MeasuredInternalOffsetMilliVolts = 0D; } else { if ((packet.WhichResult == PrePostResults.PostEventDiagnosticsResult) && !IsG5()) { preOrPost = TestChannelReadCommandString.TestType.Post; } var tcr2 = new TestChannelRead(this, 4000, preOrPost) { ChannelIndex = z + 1, ModuleIndex = ConfigData.Modules[i].ModuleArrayIndex }; tcr2.SyncExecute(); dr.MeasuredExcitationMilliVolts = tcr2.Excitation * 1000D; dr.NegativeExcitation = tcr2.NegativeExcitation; var analog = ConfigData.Modules[i].Channels[z] as AnalogInputDASChannel; switch (analog.Excitation) { case ExcitationVoltageOptions.ExcitationVoltageOption.Volt10: dr.ExpectedExcitationMilliVolts = 10000; break; case ExcitationVoltageOptions.ExcitationVoltageOption.Volt2: dr.ExpectedExcitationMilliVolts = 2000; break; case ExcitationVoltageOptions.ExcitationVoltageOption.Volt5: dr.ExpectedExcitationMilliVolts = 5000; break; case ExcitationVoltageOptions.ExcitationVoltageOption.Undefined: dr.ExpectedExcitationMilliVolts = 5000; break; default: throw new InvalidDataException("invalid excitation " + analog.Excitation); } var stddev = tcr2.ZeroStdDevAtGainADC; dr.NoisePercentFullScale = 100D * stddev / (-1D * short.MinValue); dr.FinalOffsetADC = Convert.ToInt16(tcr2.ZeroAverageADCValue); if (analog.RemoveOffset) { dr.AutoZeroPercentDeviation = 100D * (double)dr.FinalOffsetADC / short.MaxValue; } else { dr.AutoZeroPercentDeviation = null; } dr.ZeroMVInADC = Convert.ToInt16(tcr2.ZeroMvADC); dr.ScalefactorMilliVoltsPerADC = tcr2.MvScaleFactorADC; dr.MeasuredOffsetMilliVolts = tcr2.SensorOffsetMv; dr.MeasuredShuntDeflectionMv = tcr2.ShuntCalibrationDeltaADC * tcr2.MvScaleFactorADC; } } } dr.EventNumber = 1; } } if (null != dr) { moduleResults.Add(dr); } if (null != drNext) { moduleResults.Add(drNext); } } results.AddRange(moduleResults); } packet.info.Progress(100); SetChannelDiagnosticsResults(results.ToArray(), false); packet.info.Success(); } catch (CanceledException) { packet.info.Cancel(); } catch (Exception ex) { packet.info.Error(ex.Message + " Attribute: " + attributeName, ex); } } #endregion void IDiagnosticsActions.SquibFireCheckArm(double delay, double duration, ServiceCallback callback, object userData) { var packet = new SquibFireCheckArmPacket(); packet.info = new TDASServiceAsyncInfo(callback, userData); packet.duration = duration; packet.delay = delay; LaunchAsyncWorker("TDAS.SquibFireCheckArm", AsyncSquibFireCheckArm, packet); } /// /// returns true if a shorted trigger was detected /// 17822 DataPRO and TDAS rack unresponsive during diagnostics /// private bool CheckShortedTrigger() { try { var ta = new TestAll(this); ta.Mode = TestAllCommandString.Modes.PREV; ta.SyncExecute(); ta = new TestAll(this); ta.Mode = TestAllCommandString.Modes.CURR; ta.SyncExecute(); if (ta.TriggerFlag == DFConstantsAndEnums.VoltageStatusColor.Red) { return true; } return false; } catch (Exception ex) { APILogger.Log("Failed to check for shorted trigger: ", ex); } return false; } /// /// the working guts of PrepareForDiagnostics /// /// our async data private void AsyncSquibFireCheckArm(object asyncInfo) { var packet = asyncInfo as SquibFireCheckArmPacket; if (IsG5()) { packet?.info.Success(); return; } try { var bHasTOM = false; foreach (DASModule module in ConfigData.Modules) { if (DASInfo.Modules[module.ModuleArrayIndex].TypeOfModule == DFConstantsAndEnums.ModuleType.EMPTYBANK) { continue; } if (IsTom(module)) { //run test channel run now before arming, can't run it after arming without turning power back on and off //and we need some results from test channel read to process data from the squib fire check. var testChannelRun = new TestChannelRun(this); testChannelRun.ModuleIndex = module.ModuleArrayIndex; testChannelRun.ChannelIndex = -1; testChannelRun.SyncExecute(); bHasTOM = true; try { var sdl = new SetupTOMDASLoad(this); sdl.ModuleIndex = module.ModuleArrayIndex; sdl.ArmMode = SetupTOMDASLoad.ARMMode.DIAGW; sdl.SetHardwareAAFilter(2000, MaxAAFilterRateHz); sdl.SampleRate = 10000; sdl.OutputRate = 0; sdl.PreTriggerSeconds = .1D;//we want to have a little pre-trigger time incase delay = 0ms, //we'd like to check that the pre-trigger level is below thresholds. sdl.PostTriggerSeconds = .5D + (packet.delay + packet.duration) / 1000; sdl.PostADWaitTime = -1; sdl.TriggerType = SetupTOMDASLoad.TriggerTypes.BUS; sdl.TestId = "_TESTTRIG_"; sdl.TestConfig = $"{"TEST"}{SETUPDASREAD_SENTINEL}{ConfigData.Modules[module.ModuleArrayIndex].GetCRC32()}"; sdl.SyncExecute(); } catch (Exception ex) { APILogger.Log("problem with test trigger setup das load ", SerialNumber, ex); } } else { var sdl = new SetupDASLoad(this); var sdlDIM = new SetupDASLoadDIM(this); sdl.ModuleIndex = module.ModuleArrayIndex; sdlDIM.ModuleIndex = module.ModuleArrayIndex; sdl.ArmMode = SetupDASLoad.ARMMode.WAIT; sdlDIM.ArmMode = SetupDASLoadDIM.ArmModes.WAIT; sdl.SetHardwareAAFilter(2000, MaxAAFilterRateHz); sdl.SampleRate = 10000; sdlDIM.SampleRate = 10000; sdl.OutputRate = 0; sdl.IsDummyArm = false; sdlDIM.IsDummyArm = false; sdl.CollectData = false; sdlDIM.CollectData = false; sdl.PreTriggerSeconds = .1D;//we want to have a little pre-trigger time incase delay = 0ms, //we'd like to check that the pre-trigger level is below thresholds. sdlDIM.PreTriggerSeconds = .1D; sdl.PostTriggerSeconds = .5D + (packet.delay + packet.duration) / 1000; sdlDIM.PostTriggerSeconds = .5D + (packet.delay + packet.duration) / 1000D; sdl.PostADWaitTime = -1; sdlDIM.PostADWaitTime = -1; sdl.TriggerType = SetupDASLoad.TriggerTypes.HW; sdlDIM.TriggerType = SetupDASLoadDIM.TriggerTypes.HW; sdl.TestId = "TESTTRIG"; sdlDIM.TestId = sdl.TestId; sdl.TestConfig = $"{"TEST"}{SETUPDASREAD_SENTINEL}{ConfigData.Modules[module.ModuleArrayIndex].GetCRC32()}"; sdlDIM.TestConfig = sdl.TestConfig; if (DASInfo.Modules[module.ModuleArrayIndex].TypeOfModule == DFConstantsAndEnums.ModuleType.ProDIM && !IsG5()) { sdlDIM.SyncExecute(); } else { if (module.ModuleArrayIndex > 0 && IsG5()) { //only need to execute on first module in g5 } else { sdl.SyncExecute(); } } } } if (bHasTOM) { //warn if there's a shorted trigger before going any further //17822 DataPRO and TDAS rack unresponsive during diagnostics if (CheckShortedTrigger()) { packet?.info.Error("TriggerShorted", new TriggerShortedException(SerialNumber)); return; } foreach (var module in ConfigData.Modules) { if (DASInfo.Modules[module.ModuleArrayIndex].TypeOfModule == DFConstantsAndEnums.ModuleType.EMPTYBANK) { continue; } var qsn = new QuerySerialNumberBroadcast(this, module.ModuleArrayIndex); qsn.SyncExecute(); //17717 ModuleArmBroadcast must wait on G5 //the module arm broadcast now waits when G5, doesn't when not var mab = new ModuleArmBroadcast(this, module.ModuleArrayIndex, IsG5()); mab.SyncExecute(); if (!IsG5()) { //G5 is waiting on sync execute above, rack is not //thread is to prevent rack from executing another command too //soon //17717 ModuleArmBroadcast must wait on G5 Thread.Sleep(2500); } break; } var asl = new ArmSetupLoad(this); asl.ArmMode = ArmSetupLoad.ArmModes.WAIT; asl.PostADWait = -1; asl.RackArmMode = ArmSetupLoad.RackArmModes.SOLO; asl.SampleRate = 10000; asl.SyncExecute(); var ra = new RackArm(this); ra.ArmState = RackArm.ArmStates.NOW; ra.SyncExecute(); if (ra.IsErrored) { throw new Exception($"{SerialNumber}:{ra.ResponseData}"); } try { var ta = new TestAll(this); ta.SyncExecute(); } catch (Exception ex) { APILogger.Log(ex); } } packet?.info.Success(); } catch (Exception ex) { //15606 communications error, record & mark failed APILogger.Log(ex); packet.info.Error(ex.Message); } } void IDiagnosticsActions.TriggerCheckTrigger(ServiceCallback callback, object userData) { var packet = new DiagnosticsAsyncPacket(); packet.info = new TDASServiceAsyncInfo(callback, userData); LaunchAsyncWorker("TDAS.TriggerCheckTrigger", AsyncTriggerCheckTrigger, packet); } /// /// the working guts of PrepareForDiagnostics /// /// our async data private void AsyncTriggerCheckTrigger(object asyncInfo) { var packet = asyncInfo as DiagnosticsAsyncPacket; bool bHasTOM = false; foreach (DASModule module in ConfigData.Modules) { if (IsTom(module)) { bHasTOM = true; } } if (bHasTOM) { try { var rtc = new RackTriggerCommand(this); rtc.SyncExecute(); } catch (Exception ex) { if (!ex.Message.Contains("already triggered")) { packet.info.Error(ex.Message); return; } } } packet.info.Success(); } void IDiagnosticsActions.TriggerCheckDownload(double delay, double duration, float diagnosticsAAFilterFrequencyHz, uint diagnosticsSampleRateHz, ServiceCallback callback, object userData) { var packet = new DiagnosticsTriggerCheckDownloadPacket(); packet.delay = delay; packet.duration = duration; packet.DiagnosticsAAFilterFrequencyHz = diagnosticsAAFilterFrequencyHz; packet.DiagnosticsSampleRateHz = diagnosticsSampleRateHz; packet.info = new TDASServiceAsyncInfo(callback, userData); LaunchAsyncWorker("TDAS.TriggerCheckDownload", AsyncTriggerCheckDownload, packet); } /// /// the working guts of PrepareForDiagnostics /// /// our async data private void AsyncTriggerCheckDownload(object asyncInfo) { var packet = asyncInfo as DiagnosticsTriggerCheckDownloadPacket; DateTime waitForRack = DateTime.Now; int maxRackWaitSeconds = 10; if (!IsG5()) { try { while ((waitForRack - DateTime.Now).TotalSeconds < maxRackWaitSeconds) { var arm = new QueryArmStatus(this); arm.SyncExecute(); if (arm.Status == QueryArmStatus.ARMStatus.DONE) { break; } Thread.Sleep(1000); } //ARM OFF var armOff = new ARMOFF(this); armOff.SyncExecute(); } catch (Exception e) { //15606 communications error turning off, mark failed APILogger.Log(e); packet.info.Error(e.Message); return; } } //nothing to download on a G5 ... if (IsG5()) { packet.info.Success(); return; } foreach (var iDASModule in ConfigData.Modules) { var module = (DASModule)iDASModule; if (module.SerialNumber() == "EMPTY") { continue; } //note apparently on some units in a squib fire check //TEST PREPARE remains on, I have no idea, that seems like a firmware issue //however we want to explicitly turn off power during the download phase //so that regular diagnostics will properly turn on power //(currently only turns on power if ALL modules have power off) var testPrepareOff = new TestPrepare(this); testPrepareOff.ModuleIndex = module.ModuleArrayIndex; testPrepareOff.On = false; try { testPrepareOff.SyncExecute(); } catch (Exception ex) { APILogger.Log(ex); } if (IsTom(module)) { if (!IsTom(module)) { continue; } //If there exists a module without a configured channel, send it a ?N var moduleHasChannel = false; foreach (var channel in module.Channels) { if (!channel.IsConfigured()) { continue; } if (!(channel is OutputSquibChannel squib)) { continue; } if (squib.MeasurementType == SquibMeasurementType.CURRENT) { continue; } moduleHasChannel = true; } if (!moduleHasChannel) { if (module.SerialNumber() != "EMPTY") { //?N try { var qsn = new QuerySerialNumber(this, 2000); qsn.ModuleIndex = module.ModuleArrayIndex; qsn.SyncExecute(); } catch (Exception ex) { //15606 communications error, mark failed APILogger.Log(ex); packet.info.Error(ex.Message); return; } } } foreach (var channel in module.Channels) { if (!channel.IsConfigured()) { continue; } if (!(channel is OutputSquibChannel squib)) { continue; } if (squib.MeasurementType == SquibMeasurementType.CURRENT) { continue; } try { //surprisingly, don't use the module.samplerate, as it's set for what the test is, but apparently //diagnostics is running at a fixed 10k! const int sps = 10000; var qda = new QueryDataAvailable(this); qda.ModuleIndex = module.ModuleArrayIndex; qda.SyncExecute(); var d = new Download(this); d.ModuleIndex = module.ModuleArrayIndex; d.DoCurrentDownload = true; d.DoVoltageOrInsertionDownload = false; d.FirstPoint = Convert.ToInt64(-.1D * sps); d.LastPoint = Convert.ToInt64(.5 * sps + (packet.delay + packet.duration) * 10);//since we are doing things in sps 1000, just leave things in ms d.ChannelIndex = Convert.ToInt32(Math.Floor(channel.ModuleChannelNumber / 2D)); d.SyncExecute(); Thread.Sleep(200); var mydata = d.GetData(); d = new Download(this); d.ModuleIndex = module.ModuleArrayIndex; d.DoCurrentDownload = false; d.DoVoltageOrInsertionDownload = true; d.FirstPoint = Convert.ToInt64(-.1D * sps); d.LastPoint = Convert.ToInt64(.5D * sps + (packet.delay + packet.duration) * 10);//since we are doing things in sps 1000, just leave things in ms d.ChannelIndex = Convert.ToInt32(Math.Floor((double)channel.ModuleChannelNumber) / 2D); d.SyncExecute(); Thread.Sleep(200); var mydata2 = d.GetData(); var convertedCurrentData = new List(); var convertedVoltageData = new List(); var threshold = 0D; //notes http://foghat/dtswiki/index.php?title=Squib_Fire_Test //updated per http://fogbugz/fogbugz/default.asp?4860#26469 switch (squib.FireMode) { case SquibFireMode.AC: threshold = 1.5D; break; case SquibFireMode.CAP: threshold = 2.1D; break; case SquibFireMode.CONSTANT: { threshold = .75D * squib.SquibOutputCurrent; } break; } var indexOverThreshold = int.MaxValue;//marks where we first cross from below threshold to over //we will use this to ensure delay is accurate var indexBelowThresholdAgain = int.MaxValue; //make sure it's > low threshold and < max threshold //we also need to make sure it's above min threshold (and below max) after duration ms //we also need to make sure it's below threshold after the duration. //this is a bad assumption //12058 Run(Channel) sub nav step not functional for squibs in diagnostics. //int resultIndex = DASInfo.MapModuleArrayIndexAndChannelNum2DASChannel(module.ModuleArrayIndex, channel.ModuleChannelNumber); var dasChannelNumber = DASInfo.MapModuleArrayIndexAndChannelNum2DASChannel(module.ModuleArrayIndex, channel.ModuleChannelNumber); int resultIndex = -1; for (var i = 0; i < ChannelDiagnostics.Length; i++) { if (ChannelDiagnostics[i].DASChannelNumber == dasChannelNumber) { resultIndex = i; break; } } if (resultIndex < 0) { continue; } // we need the scale factor results for the channel // note that the test channel run will come during the test squib fire // we lead the results here for processing the squib fire data var tcr3 = new TestSquibChannelRead( this) { ModuleIndex = module.ModuleArrayIndex, ChannelIndex = 1 + (channel.ModuleChannelNumber / 2) }; tcr3.SyncExecute(); var scaleFactorMv = tcr3.CurrentScaleFactorMv; var scaleFactorMv2 = tcr3.VoltageScaleFactorMv; // voltage recording channel scale factor var offset = tcr3.CurrentPreZero; // current pre-zero var offset2 = tcr3.VoltagePreZero; // voltage pre-zero //we actually want the scale factors from the current channel, not the channel we have right now which is init/volt var dCurrent = 0D; var dVoltage = 0D; for (var i = 0; i < mydata.Length; i++) { try { dCurrent = (mydata[i] - offset) * scaleFactorMv; dVoltage = (mydata2[i] - offset2) * scaleFactorMv2; convertedCurrentData.Add(dCurrent); convertedVoltageData.Add(dVoltage); if (indexBelowThresholdAgain != int.MaxValue) continue; if (dCurrent > threshold) { indexOverThreshold = Math.Min(i, indexOverThreshold); } else { //only set indexbelowthreshold index once we have gone over the threshold. if (indexOverThreshold < int.MaxValue) { indexBelowThresholdAgain = Math.Min(i, indexBelowThresholdAgain); } } } catch (Exception) { } } var actualDelayMS = indexOverThreshold < int.MaxValue ? (indexOverThreshold - .1D * sps) / (sps / 1000D) : 0; var actualDurationMS = (indexBelowThresholdAgain - (double)indexOverThreshold) / (sps / 1000D);//10ksps var deltaDelay = actualDelayMS - squib.DelayMS; var deltaDuration = actualDurationMS - squib.DurationMS; var bPassed = true; ChannelDiagnosticsResults[resultIndex].SquibDurationPassed = true; ChannelDiagnosticsResults[resultIndex].SquibDelayPassed = true; if (Math.Abs(deltaDelay) >= 1) { bPassed = false; ChannelDiagnosticsResults[resultIndex].SquibDelayPassed = false; } if (squib.LimitDuration) { if (squib.DurationMS > 6) { if (actualDurationMS < 6) { bPassed = false; ChannelDiagnosticsResults[resultIndex].SquibDurationPassed = false; } } else { if (Math.Abs(deltaDuration) > .25) { bPassed = false; ChannelDiagnosticsResults[resultIndex].SquibDurationPassed = false; } } } if (squib.LimitDuration) { ChannelDiagnosticsResults[resultIndex].MeasuredDurationMS = actualDurationMS; } else { ChannelDiagnosticsResults[resultIndex].MeasuredDurationMS = null; } ChannelDiagnosticsResults[resultIndex].MeasuredDelayMS = actualDelayMS; ChannelDiagnosticsResults[resultIndex].SquibFirePassed = bPassed; //we want to start just before the squib was supposed to fire var startIndex = Convert.ToInt32(.099D * sps) + Convert.ToInt32(squib.DelayMS * 10); ChannelDiagnosticsResults[resultIndex].SquibFireCurrentData = convertedCurrentData.GetRange(startIndex, Convert.ToInt32(squib.DurationMS * 10D + 30D)).ToArray(); ChannelDiagnosticsResults[resultIndex].SquibFireVoltageData = convertedVoltageData.GetRange(startIndex, Convert.ToInt32(squib.DurationMS * 10D + 30D)).ToArray(); ChannelDiagnosticsResults[resultIndex].SquibThreshold = threshold; var timeAxis = new List(2000); var dCurrentTime = 0D; for (var i = 0; i < ChannelDiagnosticsResults[resultIndex].SquibFireCurrentData.Length; i++) { dCurrentTime = (startIndex + i - 1000D) / (sps / 1000D); timeAxis.Add(dCurrentTime); } ChannelDiagnosticsResults[resultIndex].SquibFireTimeAxis = timeAxis.ToArray(); var stddev = tcr3.CurrentStdDevADC; ChannelDiagnosticsResults[resultIndex].NoisePercentFullScale = 100D * stddev / (-1D * short.MinValue); ChannelDiagnosticsResults[resultIndex].FinalOffsetADC = Convert.ToInt16(tcr3.CurrentPreZero); ChannelDiagnosticsResults[resultIndex].AutoZeroPercentDeviation = null; ChannelDiagnosticsResults[resultIndex].ScalefactorMilliVoltsPerADC = tcr3.CurrentScaleFactorMv; stddev = tcr3.VoltageStdDevADC; ChannelDiagnosticsResults[resultIndex].NoisePercentFullScale = 100D * stddev / (-1D * short.MinValue); ChannelDiagnosticsResults[resultIndex].FinalOffsetADC = Convert.ToInt16(tcr3.VoltagePreZero); ChannelDiagnosticsResults[resultIndex].AutoZeroPercentDeviation = null; } catch (Exception ex) { //15606 communications error, mark failed APILogger.Log(ex); packet.info.Error(ex.Message); return; } } } } try { //clear download flags //7646 check sensor id navstep shouldn't warn about diagnostic data collection if (ConfigData?.Modules != null) { foreach (var module in ConfigData.Modules) { if (DASInfo.Modules[module.ModuleArrayIndex].TypeOfModule == DFConstantsAndEnums.ModuleType.EMPTYBANK) { continue; } //only the TOM should have data, rest of modules should be dummy armed //so shorten this by only marking TOM modules as downloaded if (!IsTom(module)) { continue; } var cda = new ClearDataAvailable(this); cda.ModuleIndex = module.ModuleArrayIndex; cda.SyncExecute(); //12058 Run (Channel) sub nav step not functional for squibs in diagnostics. //some modules will throw this, I'm unable to find a quick way of determining which ones //but if one does we probably want to continue on to the other modules anyhow... try { if (cda.IsErrored) { throw new Exception($"{SerialNumber}:{cda.ResponseData}"); } } catch (Exception ex) { APILogger.Log(ex); } // Only need to do once for G5 if (IsG5()) { break; } } } } catch (Exception ex) { APILogger.Log(ex); } try { if (!IsG5()) { //Turn on excitation and recharge caps var diagnosticsAsyncPacket = new DiagnosticsAsyncPacket(); diagnosticsAsyncPacket.info = packet.info; diagnosticsAsyncPacket.DiagnosticsSampleRateHz = packet.DiagnosticsSampleRateHz; diagnosticsAsyncPacket.DiagnosticsAAFilterFrequencyHz = packet.DiagnosticsAAFilterFrequencyHz; diagnosticsAsyncPacket.PreOrPost = PrePostResults.PreEventDiagnosticsResult; SyncPrepareForDiagnostics(diagnosticsAsyncPacket); } } catch (Exception ex) { APILogger.Log(ex); } packet.info.Success(); } void IDiagnosticsActions.PerformVoltageCheck(ServiceCallback callback, object userData) { var info = new TDASServiceAsyncInfo(callback, userData); LaunchAsyncWorker("TDAS.PerformVoltageChecks", new WaitCallback(AsyncVoltageCheck), info); } void IDiagnosticsActions.PerformVoltageCheckTAOnly(ServiceCallback callback, object userData) { var info = new TDASServiceAsyncInfo(callback, userData); LaunchAsyncWorker("TDAS.AsyncVotageCheckTAOnly", new WaitCallback(AsyncVoltageCheckTAOnly), info); } /// /// Get Voltage status for a hopefully unarmed device /// this should only get called like once onsetactive /// /// our async data private void AsyncVoltageCheckTAOnly(object asyncInfo) { var packet = asyncInfo as TDASServiceAsyncInfo; try { //using (var ds = new DiagnosticsService()) { #region Test All command var ta = new TestAll(this); ta.SyncExecute(); #endregion Test All command if (BaseInput.BatteryVoltageStatusColor == DFConstantsAndEnums.VoltageStatusColor.Off) { BaseInput.InputVoltageStatus = ta.InputPower.ToString(); BaseInput.InputVoltageStatusColor = ta.InputPower; BaseInput.StatusDisplayInput = ta.InputPower.ToString(); BaseInput.BatteryVoltageStatus = String.Empty; BaseInput.BatteryVoltageStatusColor = DFConstantsAndEnums.VoltageStatusColor.Off; BaseInput.StatusDisplayBattery = TestAll.VoltageStatus.None.ToString(); } else { BaseInput.InputVoltageStatus = String.Empty; BaseInput.InputVoltageStatusColor = DFConstantsAndEnums.VoltageStatusColor.Off; BaseInput.StatusDisplayInput = TestAll.VoltageStatus.None.ToString(); BaseInput.BatteryVoltageStatus = ta.InputPower.ToString(); BaseInput.BatteryVoltageStatusColor = ta.InputPower; BaseInput.StatusDisplayBattery = ta.InputPower.ToString(); } } } catch (Exception ex) { packet?.Error(ex.Message); return; } packet?.Success(); } /// /// the working guts of PrepareForDiagnostics /// /// our async data private void AsyncVoltageCheck(object asyncInfo) { var packet = asyncInfo as TDASServiceAsyncInfo; try { var inputMilliVolts = 0D; var inputVoltage = 0D; var inputVoltageStatus = TestAll.VoltageStatus.None.ToString(); var inputVoltageStatusColor = DFConstantsAndEnums.VoltageStatusColor.Off; var statusDisplayInput = String.Empty; var batteryMilliVolts = 0D; var batteryVoltage = 0D; var batteryIsCharging = true; var batteryVoltageStatus = TestAll.VoltageStatus.High.ToString(); var batteryVoltageStatusColor = DFConstantsAndEnums.VoltageStatusColor.Green; var statusDisplayBattery = String.Empty; #region G5 if (IsG5()) { var g5DockStat = new G5DockStat(this, true); if (G5Mode == G5Modes.VDS) { g5DockStat.SyncExecute(); } else { g5DockStat.DockStationPresent = false; g5DockStat.BatteryMilliVolts = double.NaN; g5DockStat.BatteryChargeLevel = G5DockStat.BatteryChargeStates.green; g5DockStat.BatteryCharging = false; } #region battery batteryMilliVolts = g5DockStat.BatteryMilliVolts; var noBattery = double.IsNaN(batteryMilliVolts); batteryVoltage = Math.Round(batteryMilliVolts / 1000D, 2); batteryVoltageStatusColor = noBattery ? DFConstantsAndEnums.VoltageStatusColor.Off : G5ConvertBatteryLevel2Color(g5DockStat.BatteryChargeLevel); batteryIsCharging = g5DockStat.BatteryCharging; if (noBattery) { statusDisplayBattery = TestAll.VoltageStatus.None.ToString(); } else { if (batteryVoltage > MaximumValidBatteryVoltage || batteryVoltage < MinimumValidBatteryVoltage) { statusDisplayBattery = TestAll.VoltageStatus.None.ToString(); } else { statusDisplayBattery = batteryVoltage.ToString(CultureInfo.InvariantCulture) + " V " + (batteryIsCharging ? "(+++) " : "(---)"); } } #endregion battery #region input //this is always taking from modules, so should always apply .7 (.35*2) inputVoltage = GetInputVoltageTBCommand(true); inputVoltage = Math.Round(inputVoltage, 2); if (noBattery) { inputVoltageStatusColor = ConvertG5InputVoltage2Color(inputVoltage); inputVoltageStatus = ConvertStatusColor2Status(inputVoltageStatusColor); statusDisplayInput = inputVoltage.ToString(CultureInfo.InvariantCulture) + " V"; } else { if (batteryVoltage < MinimumValidBatteryVoltage || batteryVoltage > MaximumValidBatteryVoltage) { inputVoltageStatusColor = ConvertG5InputVoltage2Color(inputVoltage); statusDisplayInput = $"{inputVoltage.ToString("N2")}V"; } else { inputVoltageStatusColor = inputVoltage >= InputHighVoltage ? DFConstantsAndEnums.VoltageStatusColor.Green : (InputMediumVoltage <= inputVoltage && inputVoltage < InputHighVoltage) ? DFConstantsAndEnums.VoltageStatusColor.Yellow : DFConstantsAndEnums.VoltageStatusColor.Red; inputVoltageStatus = batteryIsCharging ? ConvertStatusColor2Status(inputVoltageStatusColor) : TestAll.VoltageStatus.Low.ToString(); statusDisplayInput = InputMediumVoltage <= inputVoltage ? inputVoltage.ToString(CultureInfo.InvariantCulture) + " V" : inputVoltageStatus; } } #endregion input } #endregion G5 #region RACK else { #region Test All command var ta = new TestAll(this); ta.SyncExecute(); #endregion Test All command #region result processing var batteryVoltsResult = GetInputVoltageTBCommand(false); //per 8872 Low voltage reported on TDAS PRO systems is incorrect (and xlsx attached to issue) // 18737 Above max voltage on TDAS does not warn user // 18739 UI incorrect on TDAS when voltage below min var rackInputVoltage = batteryVoltsResult; if (rackInputVoltage > MaximumValidInputVoltage) { inputMilliVolts = rackInputVoltage * 1000; inputVoltage = rackInputVoltage; inputVoltageStatusColor = DFConstantsAndEnums.VoltageStatusColor.Off; batteryMilliVolts = double.NaN; batteryVoltage = double.NaN; batteryVoltageStatus = string.Empty; batteryIsCharging = false; batteryVoltageStatusColor = DFConstantsAndEnums.VoltageStatusColor.Off; statusDisplayInput = "---"; statusDisplayBattery = "---"; } else if (rackInputVoltage > InputHighVoltage) { inputMilliVolts = rackInputVoltage * 1000; inputVoltage = rackInputVoltage; inputVoltageStatusColor = DFConstantsAndEnums.VoltageStatusColor.Green; batteryMilliVolts = double.NaN; batteryVoltage = double.NaN; batteryVoltageStatus = string.Empty; batteryIsCharging = false; batteryVoltageStatusColor = DFConstantsAndEnums.VoltageStatusColor.Green; statusDisplayInput = string.Format("{0}V", rackInputVoltage.ToString("N1")); statusDisplayBattery = "Charging"; } else if (rackInputVoltage > InputMediumVoltage) { inputMilliVolts = rackInputVoltage * 1000; inputVoltage = rackInputVoltage; inputVoltageStatusColor = DFConstantsAndEnums.VoltageStatusColor.Red; batteryMilliVolts = double.NaN; batteryVoltage = double.NaN; batteryVoltageStatus = string.Empty; batteryIsCharging = false; batteryVoltageStatusColor = DFConstantsAndEnums.VoltageStatusColor.Green; statusDisplayInput = "Low"; statusDisplayBattery = string.Format("{0}V", rackInputVoltage.ToString("N1")); } else if (rackInputVoltage > InputLowVoltage) { inputMilliVolts = rackInputVoltage * 1000; inputVoltage = rackInputVoltage; inputVoltageStatusColor = DFConstantsAndEnums.VoltageStatusColor.Red; batteryMilliVolts = double.NaN; batteryVoltage = double.NaN; batteryVoltageStatus = string.Empty; batteryIsCharging = false; batteryVoltageStatusColor = DFConstantsAndEnums.VoltageStatusColor.Yellow; statusDisplayInput = "Low"; statusDisplayBattery = string.Format("{0}V", rackInputVoltage.ToString("N1")); } else if (rackInputVoltage > MinimumValidInputVoltage) { inputMilliVolts = rackInputVoltage * 1000; inputVoltage = rackInputVoltage; inputVoltageStatusColor = DFConstantsAndEnums.VoltageStatusColor.Red; batteryMilliVolts = double.NaN; batteryVoltage = double.NaN; batteryVoltageStatus = string.Empty; batteryIsCharging = false; batteryVoltageStatusColor = DFConstantsAndEnums.VoltageStatusColor.Red; statusDisplayInput = "Low"; statusDisplayBattery = string.Format("{0}V", rackInputVoltage.ToString("N1")); } else //below minimum valid input voltage { inputMilliVolts = rackInputVoltage * 1000; inputVoltage = rackInputVoltage; inputVoltageStatusColor = DFConstantsAndEnums.VoltageStatusColor.Off; batteryMilliVolts = double.NaN; batteryVoltage = double.NaN; batteryIsCharging = false; batteryVoltageStatusColor = DFConstantsAndEnums.VoltageStatusColor.Off; statusDisplayInput = "---"; statusDisplayBattery = "---"; } #endregion } #endregion RACK BaseInput = new BaseInputValues { InputMilliVolts = inputMilliVolts, MinimumValidInputVoltage = MinimumValidInputVoltage, MaximumValidInputVoltage = MaximumValidInputVoltage, InputVoltageStatus = inputVoltageStatus, InputVoltageStatusColor = inputVoltageStatusColor, StatusDisplayInput = statusDisplayInput, BatteryMilliVolts = batteryMilliVolts, MinimumValidBatteryVoltage = MinimumValidBatteryVoltage, MaximumValidBatteryVoltage = MaximumValidBatteryVoltage, BatteryIsCharging = batteryIsCharging, BatteryVoltageStatus = batteryVoltageStatus, BatteryVoltageStatusColor = batteryVoltageStatusColor, StatusDisplayBattery = statusDisplayBattery }; } catch (Exception ex) { packet?.Error(ex.Message); return; } packet?.Success(); } /// /// Convert unit reported color code to Voltage Status Color /// /// Color code reported by unit /// Voltage Status Color private string ConvertStatusColor2Status(DFConstantsAndEnums.VoltageStatusColor color) { switch (color) { case DFConstantsAndEnums.VoltageStatusColor.Green: return TestAll.VoltageStatus.High.ToString(); case DFConstantsAndEnums.VoltageStatusColor.Yellow: return TestAll.VoltageStatus.Medium.ToString(); case DFConstantsAndEnums.VoltageStatusColor.Red: return TestAll.VoltageStatus.Low.ToString(); default: return TestAll.VoltageStatus.None.ToString(); } } #region G5 functions /// /// Returns converted color code (G5 specific) /// /// Color code for BatteryChargeStates /// TestAll.VoltageStatusColor private DFConstantsAndEnums.VoltageStatusColor G5ConvertBatteryLevel2Color(G5DockStat.BatteryChargeStates color) { switch (color) { case G5DockStat.BatteryChargeStates.red: return DFConstantsAndEnums.VoltageStatusColor.Red; case G5DockStat.BatteryChargeStates.yellow: return DFConstantsAndEnums.VoltageStatusColor.Yellow; case G5DockStat.BatteryChargeStates.green: return DFConstantsAndEnums.VoltageStatusColor.Green; default: return DFConstantsAndEnums.VoltageStatusColor.Off; } } /// /// Returns converted color code (G5 specific) /// 18741: Minimum & under (gray) < InputLow (red) < Input Medium (yellow) < InputHigh (green) < Maximum & over (gray) /// /// input voltage /// Voltage Status Color private DFConstantsAndEnums.VoltageStatusColor ConvertG5InputVoltage2Color(double voltage) { if (voltage >= InputMediumVoltage) { return voltage > MaximumValidInputVoltage ? DFConstantsAndEnums.VoltageStatusColor.Off : DFConstantsAndEnums.VoltageStatusColor.Green; } return voltage > InputLowVoltage ? DFConstantsAndEnums.VoltageStatusColor.Yellow : voltage > MinimumValidInputVoltage ? DFConstantsAndEnums.VoltageStatusColor.Red : DFConstantsAndEnums.VoltageStatusColor.Off; } #endregion G5 functions /// /// Uses 1000S to determine INPUT voltage /// /// lowest battery voltage private double GetInputVoltageTBCommand(bool isG5) { try { var qm = new QueryModules(this); qm.SyncExecute(); if (isG5) { //offset is .35*2 for G5 return (qm.ModuleInputPowers.Min() + .7D); } //offset/diode drop is .8 for TDAS. return (qm.ModuleInputPowers.Min() + .8D); } catch (Exception ex) { APILogger.Log(ex); return double.NaN; } } } }