Files
DP44/DataPRO/IService/Classes/TDAS Service/Callibration.cs

2438 lines
120 KiB
C#
Raw Normal View History

2026-04-17 14:55:32 -04:00
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<T> : Communication<T>,
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();
}
/// <summary>
/// clears any das trigger lines
/// </summary>
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<int, double> squibResistances = new Dictionary<int, double>();
Dictionary<int, string[]> sensorIds = new Dictionary<int, string[]>();
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();
}
/// <summary>
/// retrieve all sensors and sets the sensor lookup accordingly
/// </summary>
/// <param name="sensorIds">lookup, keyed by channel index, values are array of EIDs</param>
private void SensorIDCheckG5(Dictionary<int, string[]> 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);
}
}
/// <summary>
/// populate EID lookup by going module to module
/// note that channels being passed in was being set to 0 before getting called so
/// </summary>
/// <param name="sensorIds">lookup keyed by channel index with values of an array of EIDs for that channel</param>
private void SensorIDCheckByModule(Dictionary<int, string[]> 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();
}
/// <inheritdoc />
/// <summary>
/// Perform diagnostics based on the property ChannelDiagnostics and stuff the
/// result in ChannelDiagnosticsResults
/// </summary>
/// <param name="DiagnosticsSampleRateHz">the diagnostics sample rate</param>
/// <param name="DiagnosticsAAFilterFrequencyHz">the AA filter frequency</param>
/// <param name="callback">The function to call with information</param>
/// <param name="userData">Whatever you want to pass along</param>
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);
}
/// <summary>
/// the working guts of PrepareForDiagnostics
/// </summary>
/// <param name="asyncInfo">our async data</param>
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
/// <summary>
/// Perform diagnostics based on the property ChannelDiagnostics and stuff the
/// result in ChannelDiagnosticsResults
/// </summary>
/// <param name="diagnosticsSampleRateHz">the sample rate for diagnostics</param>
/// <param name="diagnosticsAAFilterFrequencyHz">the AA filter frequency</param>
/// <param name="callback">The function to call with information</param>
/// <param name="userData">Whatever you want to pass along</param>
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<DiagnosticsStatusIndicatorState>().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);
}
}
/// <summary>
/// Perform diagnostics based on the property ChannelDiagnostics and stuff the
/// result in ChannelDiagnosticsResults
/// </summary>
/// <param name="callback">The function to call with information</param>
/// <param name="userData">Whatever you want to pass along</param>
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;
}
/// <summary>
/// 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
/// </summary>
/// <returns>module array index of expected slowest module to run diagnostics on rack</returns>
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;
}
/// <summary>
/// the working guts of DiagnosAndGetResults
/// </summary>
/// <param name="asyncInfo">our async data</param>
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<int, int>();
// 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; }
}
/// <summary>
/// Retrieve the results from the implicit pre and post event diagnostics
/// </summary>
/// <param name="EventNumber">Which event number to Retrieve from</param>
/// <param name="WhichResult">The pre or post test results?</param>
/// <param name="callback">The function to call with information</param>
/// <param name="userData">Whatever you want to pass along</param>
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<DiagnosticsResult>();
try
{
Debug.Assert(packet != null, "packet != null");
for (var i = 0; i < DASInfo.Modules.Length; i++)
{
var bSkipModule = false;
var moduleResults = new List<DiagnosticsResult>();
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);
}
/// <summary>
/// returns true if a shorted trigger was detected
/// 17822 DataPRO and TDAS rack unresponsive during diagnostics
/// <summary>
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;
}
/// <summary>
/// the working guts of PrepareForDiagnostics
/// </summary>
/// <param name="asyncInfo">our async data</param>
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);
}
/// <summary>
/// the working guts of PrepareForDiagnostics
/// </summary>
/// <param name="asyncInfo">our async data</param>
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);
}
/// <summary>
/// the working guts of PrepareForDiagnostics
/// </summary>
/// <param name="asyncInfo">our async data</param>
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<double>();
var convertedVoltageData = new List<double>();
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<double>(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);
}
/// <summary>
/// Get Voltage status for a hopefully unarmed device
/// this should only get called like once onsetactive
/// </summary>
/// <param name="asyncInfo">our async data</param>
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();
}
/// <summary>
/// the working guts of PrepareForDiagnostics
/// </summary>
/// <param name="asyncInfo">our async data</param>
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();
}
/// <summary>
/// Convert unit reported color code to Voltage Status Color
/// </summary>
/// <param name="color">Color code reported by unit</param>
/// <returns>Voltage Status Color</returns>
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
/// <summary>
/// Returns converted color code (G5 specific)
/// </summary>
/// <param name="color">Color code for BatteryChargeStates</param>
/// <returns>TestAll.VoltageStatusColor</returns>
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;
}
}
/// <summary>
/// Returns converted color code (G5 specific)
/// 18741: Minimum & under (gray) < InputLow (red) < Input Medium (yellow) < InputHigh (green) < Maximum & over (gray)
/// </summary>
/// <param name="voltage">input voltage</param>
/// <returns>Voltage Status Color</returns>
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
/// <summary>
/// Uses 1000S to determine INPUT voltage
/// </summary>
/// <returns>lowest battery voltage</returns>
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;
}
}
}
}