Files
DP44/DataPRO/IService/Classes/SLICEService/SLICE Service.Calibration.cs
2026-04-17 14:55:32 -04:00

3698 lines
170 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Diagnostics;
using DTS.Common.Constant;
using DTS.Common.Utilities;
using DTS.DASLib.Command.SLICE;
using DTS.DASLib.Command;
using DTS.Common.Enums;
using DTS.Common.Interface.DASFactory;
using DTS.Common.Utilities.Logging;
using DTS.DASLib.Command.SLICE.RealtimeCommands;
using DTS.Common.Interface.Connection;
using DTS.Common.Interface.DASFactory.Diagnostics;
using DTS.Common.Enums.DASFactory;
using DTS.Common.ICommunication;
namespace DTS.DASLib.Service
{
public partial class Slice<T> : Communication<T>,
IDASCommunication,
IConfigurationActions,
IDiagnosticsActions,
ITriggerCheckActions,
IRealTimeActions,
IArmActions,
IDownloadActions where T : IConnection, new()
{
protected DateTime GetCurrentSystemTime()
{
var query = new QueryBaseSystemTime(this);
query.SyncExecute();
return query.SystemTime;
}
protected void DoRTCInUTCCheck()
{
try
{
var dt = GetCurrentSystemTime();
if (DateTime.UtcNow.Subtract(dt).TotalMinutes > 1)
{
ClockSyncInUTC = false;
}
else { ClockSyncInUTC = true; }
}
catch( Exception ex)
{
APILogger.Log(ex);
}
}
private const short ZeroTargetValue = 0;
private const ushort TargetToleranceValue = 50;
private const float BridgeResistanceMeasurementRangeMV = 250.0f;
private const int SmartChargeResistorSettingDisabledValue = 128;
private const double FirstInputVoltage = 9.0;
private const double SecondInputVoltage = 11.0;
protected virtual bool SupportsIEPECalSignal => true;
private class DiagnosticsAsyncPacket
{
public SliceServiceAsyncInfo info { get; set; }
public uint DiagnosticsSampleRateHz { get; set; }
public float DiagnosticsAAFilterFrequencyHz { get; set; }
}
private class SquibFireCheckArmPacket : DiagnosticsAsyncPacket
{
public double MaxDelay { get; set; }
public double MaxDuration { get; set; }
}
private class StatusIndicatorPacket
{
public SliceServiceAsyncInfo info { get; set; }
public DiagnosticsStatusIndicatorState state { get; set; }
}
#region PrepareForDiagnostics
void IDiagnosticsActions.PerformArmChecks(ServiceCallback callback, object userData)
{
var info = new SliceServiceAsyncInfo(callback, userData);
LaunchAsyncWorker("Slice.PerformArmChecks", new WaitCallback(AsyncPerformArmChecks), info);
}
/// <summary>
/// returns true if should check for battery
/// right now this is just a check against SLICE6
/// since SLICE6 does not have batteries,
/// </summary>
/// <returns></returns>
private bool ShouldCheckForBattery()
{
return this is EthernetSlice6 || this is EthernetSlice6AirBridge;
}
private void AsyncPerformArmChecks(object o)
{
if (!(o is SliceServiceAsyncInfo info)) { return; }
//14684 DataPRO crashed while running arm checklist in check trigger
//this is operating on it's own thread, so don't throw any exception, return an error instead
try
{
var dasResults = new ArmCheckResults();
var sensorIds = new Dictionary<int, string[]>();
if (null != ArmCheckActions)
{
if (ArmCheckActions.PerformBatteryVoltageCheck)
{
var checkBatteryVoltage = false;
if (ShouldCheckForBatteryIds())
{
var batteryid = new QueryOneWireID(this, QueryOneWireID.Default_IO_Timeout);
batteryid.StackChannel = 0xFF;
batteryid.SyncExecute();
if (batteryid.IDs.Count > 0)
{
if (!string.Equals(new EID(HexEncoding.ToString(batteryid.IDs[0])).ID,
EIDReader.BLANK_ID))
{
checkBatteryVoltage = true;
}
}
}
else
{
//tsrair has a battery, but no one-wire id. otherwise skip
if (this is EthernetTsrAir)
{
checkBatteryVoltage = true;
}
}
if (checkBatteryVoltage)
{
GetBaseInputs();
if (BaseInput.BatteryMilliVolts > 3000D)
{
dasResults.BatteryVoltage = new double?[1];
dasResults.BatteryVoltage[0] = BaseInput.BatteryMilliVolts / 1000D;
}
}
info.Progress(25);
}
if (ArmCheckActions.PerformInputVoltageCheck)
{
GetBaseInputs();
dasResults.InputVoltage = BaseInput.InputMilliVolts / 1000D;
}
if (ArmCheckActions.PerformEventLineCheck)
{
((ITriggerCheckActions)this).DoTriggerCheckSync();
}
info.Progress(50);
if (ArmCheckActions.PerformSensorIdCheck)
{
try
{
PerformSensorIdCheck(ref sensorIds);
}
catch (Exception ex)
{
APILogger.Log(ex);
info.Error(ex.Message);
return;
}
}
if (ArmCheckActions.PerformTiltSensorCheck)
{
GetTiltResults(dasResults);
}
if (ArmCheckActions.PerformClockSyncCheck &&
IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.QueryClockSyncStatus))
{
//SLICE 6 AIR
var query = new QueryClockSyncStatus(this);
try
{
query.SyncExecute();
dasResults.InputClockLocks = query.InputSyncStatus;
}
catch (Exception ex)
{
info.Error(ex.Message, ex);
}
}
else if (ArmCheckActions.PerformClockSyncCheck &&
IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.PTPSyncStatus))
{
//SLICE 6, S6DB, etc.
var query = new Ptp1588GetSyncStatus(this);
try
{
query.SyncExecute();
var syncStatus = new Dictionary<DTS.Common.InputClockSource, bool>();
List<DTS.Common.InputClockSource> sources =
Enum.GetValues(typeof(DTS.Common.InputClockSource)).Cast<DTS.Common.InputClockSource>().ToList();
for (int i = 1; i < sources.Count; i++)
{
// init all sources not synced
syncStatus.Add(sources[i], false);
}
syncStatus[DTS.Common.InputClockSource.PTP] = (Ptp1588Commands.PtpSyncStatus.Synced == query.SyncStatus);
dasResults.InputClockLocks = syncStatus;
}
catch (Exception ex)
{
info.Error(ex.Message, ex);
}
}
}
info.Progress(75);
dasResults.SensorIds = sensorIds;
if (IsTOM() && ArmCheckActions.PerformSquibResistanceCheck)
{
dasResults.SquibResistances = new Dictionary<int, double>();
var measureSquibs = new MeasureSquibChannelResistances(this);
measureSquibs.SyncExecute();
for (int i = 0; i < 4 && i < measureSquibs.MeasuredResistanceOhms.Length; i++)
{
double d = measureSquibs.MeasuredResistanceOhms[i];
dasResults.SquibResistances.Add(i * 2, d);
dasResults.SquibResistances.Add(1 + (i * 2), d);
}
}
else
{
dasResults.SquibResistances = null; //squibResistances;
}
info.Progress(100);
ArmCheckResults = dasResults;
info.Success();
}
catch (Exception ex)
{
info.Error($"Arm checklist failed {SerialNumber} - {ex.Message}");
}
}
protected virtual void GetTiltResults(ArmCheckResults dasResults)
{
return;
}
void IDiagnosticsActions.SaveTiltSensorDataPre(ServiceCallback callback, object userData)
{
var info = new SliceServiceAsyncInfo(callback, userData);
LaunchAsyncWorker("Slice.SaveTiltSensorDataPre", new WaitCallback(AsyncSaveTiltSensorDataPre), info);
}
private void AsyncSaveTiltSensorDataPre(object o)
{
if (!(o is SliceServiceAsyncInfo info)) { return; }
if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.InSliceTiltSensorADCPre))
{
var setTS = new SetArmAttribute(this);
setTS.SetValue(AttributeTypes.ArmAndEventAttributes.tiltSensorPreEventADC, ArmCheckResults.TiltSensorDataPre, true);
setTS.SyncExecute();
}
info.Success();
}
void IDiagnosticsActions.SaveTemperaturesPre(ServiceCallback callback, object userData)
{
var info = new SliceServiceAsyncInfo(callback, userData);
LaunchAsyncWorker("Slice.SaveTemperaturesPre", new WaitCallback(AsyncSaveTemperaturesPre), info);
}
private void AsyncSaveTemperaturesPre(object o)
{
var info = (SliceServiceAsyncInfo)o;
info.Success();
}
protected virtual void PerformSensorIdCheck(ref Dictionary<int, string[]> sensorIds)
{
PerformSensorIdCheck_SingleChannelMethod(ref sensorIds);
}
protected void PerformSensorIdCheck_SingleChannelMethod(ref Dictionary<int, string[]> sensorIds)
{
foreach (var module in ConfigData.Modules)
{
foreach (var channel in module.Channels)
{
var dasChannelNumber = DASInfo.MapModuleArrayIndexAndChannelNum2DASChannel(module.ModuleArrayIndex, channel.ModuleChannelNumber);
channel.IDs = EIDReader.RetriveEIDs(this, dasChannelNumber, channel.IdType);
if (null != channel.IDs)
{
if (IsTOM())
{
sensorIds[channel.Number * 2] = new[] { channel.IDs[0].ID };
sensorIds[1 + (channel.Number * 2)] = new[] { channel.IDs[0].ID };
}
else
{
var ids = new List<string>();
foreach (var id in channel.IDs)
{
ids.Add(id.ID);
}
sensorIds[channel.Number] = ids.ToArray();
}
}
}
}
}
/// <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(UInt32 DiagnosticsSampleRateHz,
float DiagnosticsAAFilterFrequencyHz,
DTS.DASLib.Service.PrePostResults WhichResult,
ServiceCallback callback,
object userData)
{
var packet = new DiagnosticsAsyncPacket();
packet.info = new SliceServiceAsyncInfo(callback, userData);
packet.DiagnosticsSampleRateHz = DiagnosticsSampleRateHz;
packet.DiagnosticsAAFilterFrequencyHz = DiagnosticsAAFilterFrequencyHz;
LaunchAsyncWorker("Slice.PrepareForDiagnostics", new WaitCallback(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;
try
{
// set diagnostics AAfilter and samplerate
var setSR = new SetArmAttribute(this);
setSR.SetValue(AttributeTypes.ArmAndEventAttributes.DiagnosticsSampleRateHz, packet.DiagnosticsSampleRateHz, true);
setSR.SyncExecute();
// set diagnostics AAfilter and samplerate
var setFF = new SetArmAttribute(this);
setFF.SetValue(AttributeTypes.ArmAndEventAttributes.DiagnosticsAAFilterFrequencyHz, packet.DiagnosticsAAFilterFrequencyHz, true);
setFF.SyncExecute();
// prepare for diagnostics
var prep = new PrepareForDiagnostics(this);
prep.SyncExecute();
// we're done
packet.info.Success();
}
catch (CanceledException)
{
// like most tv shows, we have been canceled
packet.info.Cancel();
}
catch (Exception ex)
{
packet.info.Error(ex.Message, ex);
}
}
#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(UInt32 DiagnosticsSampleRateHz,
float DiagnosticsAAFilterFrequencyHz,
ServiceCallback callback,
object userData)
{
var packet = new DiagnosticsAsyncPacket();
packet.info = new SliceServiceAsyncInfo(callback, userData);
packet.DiagnosticsSampleRateHz = DiagnosticsSampleRateHz;
packet.DiagnosticsAAFilterFrequencyHz = DiagnosticsAAFilterFrequencyHz;
LaunchAsyncWorker("Slice.PrepareForBridgeResistanceMeasurement", new WaitCallback(AsyncPrepareForBridgeResistanceMeasurement), packet);
}
/// <summary>
/// the working guts of PrepareForBridgeResistanceMeasurement
/// </summary>
/// <param name="asyncInfo">our async data</param>
private void AsyncPrepareForBridgeResistanceMeasurement(object asyncInfo)
{
var packet = asyncInfo as DiagnosticsAsyncPacket;
try
{
// Turn on excitations explicitly so that sensor will warm up as much as possible
// set diagnostics AAfilter and samplerate
// Should only be one channel, but just in case
for (uint CurrentChannel = 0; CurrentChannel < ChannelDiagnostics.Length; CurrentChannel++)
{
var ExcitationSwitch = new SetSwitchImmediate(this);
ExcitationSwitch.Setting = 1;
ExcitationSwitch.DeviceID = DASInfo.MapDASChannelNumber2ModuleDeviceID(ChannelDiagnostics[0].DASChannelNumber);
var module = DASInfo.Modules[ExcitationSwitch.DeviceID - 1];
if (module.TypeOfModule == DFConstantsAndEnums.ModuleType.SLICEIEPE)
{
ExcitationSwitch.Switch = (byte)Switches.EIPESwitches.IS_EnableAnalogPowerSupply;
ExcitationSwitch.SwitchText = Switches.EIPESwitches.IS_EnableAnalogPowerSupply.ToString();
}
else
{
ExcitationSwitch.Switch = (byte)Switches.BridgeSwitches.EnableAnalogPowerSupply;
ExcitationSwitch.SwitchText = Switches.BridgeSwitches.EnableAnalogPowerSupply.ToString();
}
ExcitationSwitch.SyncExecute();
if (module.TypeOfModule == DFConstantsAndEnums.ModuleType.SLICEIEPE)
{
ExcitationSwitch.Switch = (byte)Switches.EIPESwitches.IS_Enable24Volt;
ExcitationSwitch.SwitchText = Switches.EIPESwitches.IS_Enable24Volt.ToString();
}
else
{
ExcitationSwitch.Switch = (byte)Switches.BridgeSwitches.EnableAmps;
ExcitationSwitch.SwitchText = Switches.BridgeSwitches.EnableAmps.ToString();
}
ExcitationSwitch.SyncExecute();
if (module.TypeOfModule != DFConstantsAndEnums.ModuleType.SLICEIEPE)
{
ExcitationSwitch.Switch = (byte)Switches.BridgeSwitches.EnableExcitation;
}
ExcitationSwitch.SyncExecute();
}
// set calibration AAfilter and samplerate
var setSR = new SetArmAttribute(this);
setSR.SetValue(AttributeTypes.ArmAndEventAttributes.DiagnosticsSampleRateHz, packet.DiagnosticsSampleRateHz, true);
setSR.SyncExecute();
// set diagnostics AAfilter and samplerate
var setFF = new SetArmAttribute(this);
setFF.SetValue(AttributeTypes.ArmAndEventAttributes.DiagnosticsAAFilterFrequencyHz, packet.DiagnosticsAAFilterFrequencyHz, true);
setFF.SyncExecute();
// Dummy set the requested range--we are relying on this being overriden later when the
// software sets the configuration on the module. The channel may not have a valid requested
// range stored on it, so we need to at least dummy set it here so we can do proper data
// collection to compute the bridge resistance. We use the target range set in
// BridgeResistanceMeasurementRangeMV, which should give us about a gain of 10.
// note - we also reset the configure has been run as we are relying on re-configuring again.
ConfigureHasBeenRun = false;
var getRange = new QueryArmAttribute(this);
getRange.Key = AttributeTypes.ArmAndEventAttributes.StackChannelRangesMillivolts;
getRange.SyncExecute();
var rangeArray = getRange.Value as float[];
for (var i = 0; i < rangeArray.Length; i++)
{
rangeArray[i] = BridgeResistanceMeasurementRangeMV;
}
var config = new ConfigAttributes(this);
try
{
config.AAFilter = ConfigData.Modules[0].AAFilterRateHz;
}
catch (Exception ex)
{
APILogger.Log("Failed to set AAF min", ex);
}
var setRange = new SetArmAttribute(this);
setRange.SetValue(AttributeTypes.ArmAndEventAttributes.StackChannelRangesMillivolts, rangeArray, true);
setRange.SyncExecute();
// prepare for diagnostics
var prep = new PrepareForDiagnostics(this);
prep.SyncExecute();
// Since we're not allowing the user to let the sensor warm up, we do it here
Thread.Sleep(100);
// we're done
packet.info.Success();
}
catch (CanceledException)
{
// like most tv shows, we have been canceled
packet.info.Cancel();
}
catch (Exception ex)
{
packet.info.Error(ex.Message, ex);
}
}
#endregion
#region GetBridgeMeasurement
void IDiagnosticsActions.GetBridgeMeasurement(ServiceCallback callback, object userData)
{
var info = new SliceServiceAsyncInfo(callback, userData);
LaunchAsyncWorker("Slice.GetBridgeMeasurement", new WaitCallback(AsyncDiagnosAndGetResults), info);
}
#endregion
#region MeasureTransferSpeed
void IDiagnosticsActions.MeasureTransferSpeed(ServiceCallback callback, object userData)
{
var info = new SliceServiceAsyncInfo(callback, userData);
LaunchAsyncWorker("Slice.MeasureTransferSpeed", new WaitCallback(AsyncMeasureTransferSpeedResults), info);
}
#endregion
#region CalibrateAndGetResults
public void SetStatusIndicator(DiagnosticsStatusIndicatorState state, ServiceCallback callback, object userData)
{
var packet = new StatusIndicatorPacket();
packet.info = new SliceServiceAsyncInfo(callback, userData);
LaunchAsyncWorker("SLICE.SetStatusIndicator", new WaitCallback(AsyncSetStatusIndicator), packet);
}
private void AsyncSetStatusIndicator(object asyncInfo)
{
var packet = asyncInfo as StatusIndicatorPacket;
packet.info.Success();
}
public void TurnOffT0Lights(ServiceCallback callback, object userData)
{
var packet = new SliceServiceAsyncInfo(callback, userData);
packet.Success();
}
/// <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 info = new SliceServiceAsyncInfo(callback, userData);
info.PreOrPost = WhichResult;
LaunchAsyncWorker("Slice.DiagnosAndGetResults", new WaitCallback(AsyncDiagnosAndGetResults), info);
}
protected void ReconfigureAccordingToConfig()
{
try
{
var cmd = new SetStackChannelTypeConfiguration(this);
var channels = new List<SetStackChannelTypeConfiguration.ChannelTypes>();
foreach (var iDASModule in ConfigData.Modules)
{
var module = (DASModule)iDASModule;
var moduleType = module.OwningDAS.DASInfo.Modules[module.ModuleArrayIndex].TypeOfModule;
foreach (var c in module.Channels)
{
if (c is AnalogInputDASChannel)
{
if ((c as AnalogInputDASChannel).IEPEChannel)
{
switch (moduleType)
{
case DFConstantsAndEnums.ModuleType.SliceBridge2Low:
case DFConstantsAndEnums.ModuleType.SliceIEPE2Low:
channels.Add(SetStackChannelTypeConfiguration.ChannelTypes.FORCE_IEPE_LOW);
break;
default:
channels.Add(SetStackChannelTypeConfiguration.ChannelTypes.FORCE_IEPE);
break;
}
}
else
{
switch (moduleType)
{
case DFConstantsAndEnums.ModuleType.SliceBridge2Low:
case DFConstantsAndEnums.ModuleType.SliceIEPE2Low:
channels.Add(SetStackChannelTypeConfiguration.ChannelTypes.FORCE_BRIDGE_LOW);
break;
default:
channels.Add(SetStackChannelTypeConfiguration.ChannelTypes.FORCE_BRIDGE);
break;
}
}
}
else { channels.Add(SetStackChannelTypeConfiguration.ChannelTypes.AUTO_DETECT); }
}
}
cmd.SetValue(channels.ToArray());
}
catch (Exception ex) { APILogger.Log(ex); }
}
public bool IsTOM()
{
return SerialNumber.StartsWith("SPT") || SerialNumber.StartsWith("SLT");
}
//FB 6416 Measure the transfer speed based on writing /reading a particular data size to/from DAS
private float MeasureTransferSpeed(int bufferSize, ICommunication comm)
{
Stopwatch stopwatch = new Stopwatch();
var sfd = new SetFileData(comm, 600000);
if (bufferSize > sfd.MaximumFileStreamBytes)
{
throw new ArgumentOutOfRangeException($"buffer Size: {bufferSize} > {sfd.MaximumFileStreamBytes}");
}
var bytes = new byte[bufferSize];
for (int i = 0; i < bufferSize; i++)
{
bytes[i] = 0x2A;
}
sfd.StartByteCount = 0;
sfd.FileID = (ushort)ConfigAttributes.FileStore.SpeedTest;
sfd.Data = bytes;
stopwatch.Reset();
stopwatch.Start();
sfd.SyncExecute();
stopwatch.Stop();
var gfd = new QueryFileData(comm, 600000);
gfd.FileID = (ushort)ConfigAttributes.FileStore.SpeedTest;
gfd.StartByteCount = 0;
gfd.EndByteCount = (uint)bufferSize - 1;
stopwatch.Start();
gfd.SyncExecute();
stopwatch.Stop();
float speed = (float)gfd.Data.Length * 8 / stopwatch.ElapsedMilliseconds;
return speed;
}
//FB 6416 Calculate the average transfer speed based on 3 tries
private void AsyncMeasureTransferSpeedResults(object asyncInfo)
{
if (!(asyncInfo is SliceServiceAsyncInfo info)) { return; }
try
{
float totalSpeed = 0;
int tries = 3;
for (int i = 0; i < tries; i++)
{
totalSpeed += MeasureTransferSpeed(400, this);
}
float averageSpeed = 0;
averageSpeed = totalSpeed / tries;
OptimizationValues = new Classes.Diagnostics.OptimizationValues { TransferSpeed = averageSpeed };
info.Success();
}
catch (Exception ex)
{
info.Error("Measuring transfer speed threw exception", ex);
}
}
private void AsyncDiagnosAndGetResultsTOM(object asyncInfo)
{
if (!(asyncInfo is SliceServiceAsyncInfo info)) { return; }
try
{
// prepare the result array
var channelToResult = new Dictionary<int, DiagnosticsResult>();
if (ChannelDiagnostics != null && ChannelDiagnostics.Length > 0)
{
for (int idx = 0; idx < ChannelDiagnostics.Length; idx++)
{
var result = Array.Find(ChannelDiagnosticsResults, ch => ch.DASChannelNumber == ChannelDiagnostics[idx].DASChannelNumber);
if (null != result)
{
channelToResult[ChannelDiagnostics[idx].DASChannelNumber] = (DiagnosticsResult)result;
}
}
}
var ProgressSteps = 12D;
var ProgressCounter = 1;
var mtbdc = new MeasureTomBaseDiagnosticChannel(this);
mtbdc.Channel = MeasureTomBaseDiagnosticChannel.TomBaseDiagnosticChannelList.Power17V;
mtbdc.SyncExecute();
var queryScaleFactors = new QueryArmAttribute(this);
queryScaleFactors.Key = AttributeTypes.ArmAndEventAttributes.StackChannelScaleFactorsMillivoltsPerADC;
queryScaleFactors.SyncExecute();
var scaleFactors = (float[])queryScaleFactors.Value;
var mscr = new MeasureSquibChannelResistances(this);
mscr.SyncExecute();
var queryFinalOffsetADC = new QueryArmAttribute(this);
queryFinalOffsetADC.Key = AttributeTypes.ArmAndEventAttributes.StackChannelFinalOffsetCounts;
queryFinalOffsetADC.SyncExecute();
var finalOffsetADC = queryFinalOffsetADC.Value as short[];
for (var i = 0; i < 8; i += 2)
{
var squibVoltage = ConfigData.Modules[0].Channels[i] as OutputSquibChannel;
var squibCurrent = ConfigData.Modules[0].Channels[i + 1] as OutputSquibChannel;
if (squibVoltage.ConfigurationMode == DFConstantsAndEnums.ConfigMode.Normal)
{
if (channelToResult.ContainsKey(i))
{
channelToResult[i].NoisePercentFullScale = 0;
channelToResult[i].FinalOffsetADC = 0;
channelToResult[i].AutoZeroPercentDeviation = null;
channelToResult[i].ScalefactorMilliVoltsPerADC = scaleFactors[i];
channelToResult[i].MeasuredExcitationMilliVolts = mtbdc.Measurement * 1000D;
//14233 Negative Excitation Reported by TDAS hardware not showing in Diagnostics
//SLICE excitation is not absoluted, but carrying through the values here for completeness
channelToResult[i].NegativeExcitation = mtbdc.Measurement < 0;
squibVoltage.ScaleFactorMv = scaleFactors[i];
squibVoltage.PreTestDataZeroLevelADC = finalOffsetADC[i];
channelToResult[i].BridgeResistance = mscr.MeasuredResistanceOhms[i / 2];
}
if (channelToResult.ContainsKey(i + 1))
{
channelToResult[i + 1].NoisePercentFullScale = 0;
channelToResult[i + 1].AutoZeroPercentDeviation = null;
channelToResult[i + 1].FinalOffsetADC = 0;
channelToResult[i + 1].ScalefactorMilliVoltsPerADC = scaleFactors[i + 1];
channelToResult[i + 1].BridgeResistance = mscr.MeasuredResistanceOhms[i / 2];
squibCurrent.ScaleFactorMv = scaleFactors[i + 1];
squibCurrent.PreTestDataZeroLevelADC = finalOffsetADC[i + 1]; ;
}
}
}
// measure inputs
GetBaseInputs();
info.Progress((int)(ProgressCounter++ / ProgressSteps * 100D));
info.Success();
}
catch (CanceledException)
{
// like most tv shows, we have been canceled
info.Cancel();
}
catch (Exception ex)
{
info.Error(ex.Message, ex);
}
}
/// <summary>
/// the working guts of DiagnosAndGetResults
/// </summary>
/// <param name="asyncInfo">our async data</param>
protected virtual void AsyncDiagnosAndGetResults(object asyncInfo)
{
if (IsTOM()) { AsyncDiagnosAndGetResultsTOM(asyncInfo); }
else
{
if (!(asyncInfo is SliceServiceAsyncInfo info)) { return; }
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");
var oldResults = new Dictionary<int, IDiagnosticResult>();
if (null != ChannelDiagnosticsResults && ChannelDiagnosticsResults.Length > 0)
{
foreach (var r in ChannelDiagnosticsResults)
{
oldResults[r.DASChannelNumber] = r;
}
}
// prepare the result array
IDiagnosticResult[] results;
if (ChannelDiagnostics != null && ChannelDiagnostics.Length > 0)
{
results = new DiagnosticsResult[ChannelDiagnostics.Length];
for (var idx = 0; idx < ChannelDiagnostics.Length; idx++)
{
results[idx] = new DiagnosticsResult
{
DASChannelNumber = ChannelDiagnostics[idx].DASChannelNumber
};
if (oldResults.ContainsKey(results[idx].DASChannelNumber))
{
oldResults.Remove(results[idx].DASChannelNumber);//we'll have a new value, don't hold to old one
}
results[idx].EventNumber = info.PreOrPost == PrePostResults.PreEventDiagnosticsResult
? DFConstantsAndEnums.EVENT_NUMBER_PRETEST_DIAG
: DFConstantsAndEnums.EVENT_NUMBER_POSTTEST_DIAG;
}
}
else
{
// this case will only generate the base input values
results = null;
}
const double progressSteps = 14.0;
var progressCounter = 1;
if (results != null)
{
// Need to map IEPE channel AC coupling from Configuration so we can restore later.
#region MAP_IEPE_CH_COUPLING
var numChannels = DASInfo.Modules.Sum(mod => mod.NumberOfChannels);
var IsACCoupledArray = new bool[numChannels];
for (int moduleIdx = 0; moduleIdx < ConfigData.Modules.Length; moduleIdx++)
{
var module = ConfigData.Modules[moduleIdx];
for (var channelIdx = 0; channelIdx < module.Channels.Length; channelIdx++)
{
var channel = module.Channels[channelIdx];
var dasChannelNumber =
DASInfo.MapModuleArrayIndexAndChannelNum2DASChannel(moduleIdx, channelIdx);
var analog = channel as AnalogInputDASChannel;
if (analog?.IEPEChannel ?? false)
{
IsACCoupledArray[dasChannelNumber] =
analog.CouplingMode == Common.Enums.Sensors.SensorConstants.CouplingModes.AC;
}
else
{
IsACCoupledArray[dasChannelNumber] = false;
}
}
}
var acCouplingExists = Array.Exists(IsACCoupledArray, x => x == true);
var config = GetConfigAttributes(this);
TurnOffACCoupling(acCouplingExists, config, numChannels);
#endregion MAP_IEPE_CH_COUPLING
// first get the scale factors
GetScaleFactorMVForChannel(info, ref results);
info.Progress((int)(progressCounter++ / progressSteps * 100.0));
QueryGain(info, ref results);
// query factory excitation
FactoryExcitation(ChannelDiagnostics, info, ref results);
info.Progress((int)(progressCounter++ / progressSteps * 100.0));
// measure excitation
MeasureExcitation(ChannelDiagnostics, info, ref results);
info.Progress((int)(progressCounter++ / progressSteps * 100.0));
// measure offset
MeasureOffset(ChannelDiagnostics, info, ref results);
info.Progress((int)(progressCounter++ / progressSteps * 100.0));
// measure internal offset (signal chain)
MeasureInternalOffset(ChannelDiagnostics, info, ref results, false);
info.Progress((int)(progressCounter++ / progressSteps * 100.0));
var dasChannelNumberToZeromVinAdc = new Dictionary<int, short>();
foreach (var res in results)
{
dasChannelNumberToZeromVinAdc[res.DASChannelNumber] = res.ZeroMVInADC;
}
foreach (var module in ConfigData.Modules)
{
foreach (var ch in module.Channels)
{
if (!(ch is AnalogInputDASChannel aic)) continue;
if (dasChannelNumberToZeromVinAdc.ContainsKey(aic.Number))
{
aic.ZeromVInADC = dasChannelNumberToZeromVinAdc[aic.Number];
}
}
}
CheckDigitalValues(ChannelDiagnostics, info, ref results);
//FB 29510
if (IsTSRAIR())
{
MarkOffsetForRemoval();
}
RestoreIEPE(acCouplingExists, config, IsACCoupledArray, info);
// remove offset
RemoveOffset(ChannelDiagnostics, info, ref results);
info.Progress((int)(progressCounter++ / progressSteps * 100.0));
// measure final offset
MeasureFinalOffset(ChannelDiagnostics, info, ref results);
if (this is EthernetTsrAir)
{
foreach (var module in ConfigData.Modules)
{
if (module.ModuleType() != DFConstantsAndEnums.ModuleType.EmbeddedAtmospheric) continue;
//don't record offsets for the non-accel
foreach (var ch in module.Channels)
{
if (!(ch is AnalogInputDASChannel aic)) continue;
results.First(res => res.DASChannelNumber == aic.Number).FinalOffsetADC = 0;
}
}
}
info.Progress((int)(progressCounter++ / progressSteps * 100.0));
// measure final internal offset (signal chain) again since RemoveOffset was called
MeasureInternalOffset(ChannelDiagnostics, info, ref results, true);
info.Progress((int)(progressCounter++ / progressSteps * 100.0));
// measure noise
MeasureNoise(ChannelDiagnostics, info, ref results);
info.Progress((int)(progressCounter++ / progressSteps * 100.0));
// perform shunt check
PerformShuntCheck(ChannelDiagnostics, info, ref results);
info.Progress((int)(progressCounter++ / progressSteps * 100.0));
// measure the bridge resistance
PerformBridgeResistanceMeasurement(ChannelDiagnostics, info, ref results);
info.Progress((int)(progressCounter++ / progressSteps * 100.0));
PerformVoltageInsertionCheck(ChannelDiagnostics, info, ref results);
info.Progress((int)(progressCounter++ / progressSteps * 100D));
// perform cal signal check
PerformCalSignalCheck(ChannelDiagnostics, info, ref results);
info.Progress((int)(progressCounter++ / progressSteps * 100D));
}
var newResults = new List<IDiagnosticResult>(oldResults.Values.ToArray());
// measure inputs
GetBaseInputs();
if (results != null)
{
info.Progress((int)(progressCounter / progressSteps * 100.0));
newResults.AddRange(results);
}
SetChannelDiagnosticsResults(newResults.ToArray(), true);
info.Success();
}
catch (CanceledException)
{
// like most tv shows, we have been canceled
info.Cancel();
}
catch (Exception ex)
{
if (ex.Data.Contains("Status"))
{
if (ex.Data["Status"] is DFConstantsAndEnums.CommandStatus cs)
{
if (cs != DFConstantsAndEnums.CommandStatus.StatusSetupShuntDACOutputExceeded)
{
if (cs == DFConstantsAndEnums.CommandStatus.StatusSetupCALDACOutputExceeded)
{
info.Error(
"Cal DAC output exceeded while running diagnostics on channel.\n Calibration stopped.");
return;
}
}
else
{
info.Error(
"Shunt DAC output exceeded while running diagnostics on channel.\n Calibration stopped.");
return;
}
}
}
info.Error(ex.Message, ex);
}
}
}
/// <summary>
/// turns off AC coupling on ALL channels this was original done for
/// http://manuscript.dts.local/f/cases/16524/diagnostic-result-should-include-the-resting-voltage-of-an-IEPE-sensor
/// I split it off here for beter readability
/// </summary>
/// <param name="acCouplingExists"></param>
/// <param name="config"></param>
/// <param name="numChannels"></param>
private void TurnOffACCoupling(bool acCouplingExists, ConfigAttributes config, long numChannels)
{
if (acCouplingExists)
{
config.ConfigureCoupling(new bool[numChannels]); // Set all to false
// prepare for diagnostics
var prep = new PrepareForDiagnostics(this);
prep.SyncExecute();
// Since we're not allowing the user to let the sensor warm up, we do it here
Thread.Sleep(100);
}
}
private void MarkOffsetForRemoval()
{
//FB 29510 Set remove offset to true only for High G channels index 3.4.5
foreach (var channelDiagnostic in ChannelDiagnostics)
{
channelDiagnostic.RemoveOffset = channelDiagnostic.DASChannelNumber ==
DFConstantsAndEnums.High_g_Linear_1_Index ||
channelDiagnostic.DASChannelNumber == DFConstantsAndEnums.High_g_Linear_2_Index ||
channelDiagnostic.DASChannelNumber == DFConstantsAndEnums.High_g_Linear_3_Index;
}
}
/// <summary>
/// restores AC coupling for channels that have it selected;
/// http://manuscript.dts.local/f/cases/16524/diagnostic-result-should-include-the-resting-voltage-of-an-IEPE-sensor
/// </summary>
/// <param name="acCouplingExists"></param>
/// <param name="config"></param>
/// <param name="IsACCoupledArray"></param>
private void RestoreIEPE(bool acCouplingExists, ConfigAttributes config, bool[] IsACCoupledArray,
SliceServiceAsyncInfo info)
{
// Need to restore AC couping if there were any channels configured this way
// Only Restore coupling mode if we adjusted in the first place
if (acCouplingExists)
{
config.ConfigureCoupling(IsACCoupledArray); // Restore to channel config settings
// prepare for diagnostics
var prep = new PrepareForDiagnostics(this);
prep.SyncExecute();
var warmup = RunTestVariables.IEPEExcitationWamupTimeMS;
for (var i = 0; i < warmup; i += 200)
{
var percentDone = 100 * i / warmup;
APILogger.Log($"IEPE warmup {percentDone:00}%");
Thread.Sleep(200);
}
}
}
/// <summary>
/// Combine nominal gain, gain calibrated adjustment and millivolts per count
/// to produce a scale factor to convert counts to millivolts
/// </summary>
/// <param name="info">The channel to query</param>
/// <param name="results"><see cref="DTS.DASLib.Service.DiagnosticsResult" /> object that
/// will have the scale factor mV per ADC assigned if all goes right
/// </param>
/// <returns>scale factor to convert counts to millivolts</returns>
private void GetScaleFactorMVForChannel(SliceServiceAsyncInfo info, ref IDiagnosticResult[] results)
{
var query = new QueryArmAttribute(this);
query.Key = AttributeTypes.ArmAndEventAttributes.StackChannelScaleFactorsMillivoltsPerADC;
query.SyncExecute();
if (query.DataType != AttributeTypes.AttributeDataTypes.Float32Star)
throw new Exception("DiagnosticsService.GetScaleFactorMVForChannel: Found type " + query.DataType);
var resultArray = (float[])query.Value;
foreach (var diagResult in results)
{
//TODO: REMOVE THIS HACK
//when scale factor works
//or we've cut it from tsr-air
if (this is WinUSBTsrAir || this is EthernetTsrAir)
{
diagResult.ScalefactorMilliVoltsPerADC = 0;
diagResult.ScalefactorEngineeringUnitsPerADC = resultArray[diagResult.DASChannelNumber];
}
else
{
diagResult.ScalefactorMilliVoltsPerADC = resultArray[diagResult.DASChannelNumber];
diagResult.ScalefactorEngineeringUnitsPerADC = 0;
}
info.NewData(new ServiceCallbackData.DiagnosticNewData()
{
DasChannelNumber = diagResult.DASChannelNumber,
Action = ServiceCallbackData.DiagnosticNewData.Actions.ScaleFactorMv,
Result = diagResult.ScalefactorMilliVoltsPerADC
});
}
}
/// <summary>
/// Query the channels' gain codes, convert to gain values, and write to the log
/// </summary>
/// <param name="info"></param>
/// <param name="results"></param>
private void QueryGain(SliceServiceAsyncInfo info, ref IDiagnosticResult[] results)
{
const string StackChannelGainValues = "Stack Channel Gain Values";
var query = new QueryArmAttribute(this);
query.Key = AttributeTypes.ArmAndEventAttributes.StackChannelGainCodes;
query.SyncExecute();
var resultCodesArray = (ushort[])query.Value;
var resultValuesArray = new List<double?>();
foreach (var diagResult in results)
{
diagResult.QueriedGain = GainCodeToGainValue(resultCodesArray[diagResult.DASChannelNumber]);
resultValuesArray.Add(diagResult.QueriedGain);
}
var sb = new StringBuilder(StackChannelGainValues);
sb.Append(" [" + SerialNumber + "] ");
sb.Append(ArrayToString.ArrayObjectToString(resultValuesArray.ToArray()));
APILogger.LogString(sb.ToString());
}
/// <summary>
/// gets the expected excitation in mV for a given channel
/// returns 0 if excitation could not be retrieved, otherwise excitation in mV
/// </summary>
/// <param name="moduleIndex"></param>
/// <param name="channelOnModule"></param>
/// <returns></returns>
protected virtual double GetExpectedExcitationMV(int moduleIndex, int channelOnModule)
{
if (ConfigData?.Modules == null || ConfigData.Modules.Length <= moduleIndex)
{
APILogger.Log("unable to get excitation, no ConfigData to base excitation on");
return 0D;
}
var aic = ConfigData.Modules[moduleIndex].Channels[channelOnModule] as AnalogInputDASChannel;
if (null == aic)
{
//only have excitation to consider on analog channels
APILogger.Log("unable to get excitation, channel has no excitation (is not analog)");
return 0D;
}
var excitation = Common.DAS.Concepts.Test.Module.Channel.Sensor.GetExcitationVoltageMagnitudeFromEnum(aic.Excitation) * 1000D;
try
{
var qsa = new QuerySystemAttribute_Bridge(this);
switch (channelOnModule)
{
case 0:
qsa.Key = AttributeTypes.SystemAttributes_Bridge.FactoryCalibratedExcitationAVolts;
break;
case 1:
qsa.Key = AttributeTypes.SystemAttributes_Bridge.FactoryCalibratedExcitationBVolts;
break;
default:
qsa.Key = AttributeTypes.SystemAttributes_Bridge.FactoryCalibratedExcitationCVolts;
break;
}
//note device 0 is the base, the first module starts at 1, so we have to start at an offset of 1
qsa.DeviceID = Convert.ToByte(1 + moduleIndex);
qsa.SyncExecute();
var bridgeExcitation = Convert.ToDouble(qsa.Value) * 1000D;//convert from V to mV
var delta = Math.Abs(excitation - bridgeExcitation);
if (delta < 500)
{
excitation = bridgeExcitation;
}
}
catch (Exception ex)
{
APILogger.Log(ex);
}
return excitation;
}
/// <summary>
/// Query the factory excitation for all channels.
/// </summary>
/// <param name="ChannelActions">An array of actions. One per channel</param>
/// <param name="info">Our async data</param>
/// <param name="results">An array of results. One entry per channel</param>
private void FactoryExcitation(IDiagnosticActions[] ChannelActions, SliceServiceAsyncInfo info, ref IDiagnosticResult[] results)
{
if (!SupportsDiagnosticsFactoryExcitation)
{
foreach (var t in results)
{
t.ExpectedExcitationMilliVolts = 0.0;
}
return;
}
// pickup factory excitation
var query = new QueryArmAttribute(this);
query.Key = AttributeTypes.ArmAndEventAttributes.DiagnosticsFactoryExcitation;
try
{
query.SyncExecute();
if (!(query.Value is float[] factoryExcitations)) { throw new ApplicationException("no factory excitations"); }
else
{
//when we are only doing a single channel diagnostics, this code will cause a problem, we only have
// one result, not the full set
// 6/8/2010 - dtm
var bNeedToSet = false;
for (var idx = 0; idx < results.Length; idx++)
{
if (idx >= ChannelActions.Length) { throw new ApplicationException("no corresponding channel action for this result"); }
int actualIdx = ChannelActions[idx].DASChannelNumber;
if (actualIdx < 0 || actualIdx >= factoryExcitations.Length)
{
throw new ApplicationException("result doesn't match a channel action or a factory excitation index");
}
//https://support.dtsweb.com/hc/en-us/community/posts/115000905934-SLICE6-Fails-Diagnostics-across-all-6-channels-Excitation-Tolerances-are-Zero
//http://fogbugz/fogbugz/default.asp?10853
//we look for a clearly bogus value, if we find it we try to recover
//recovered value is either the bridge factory excitation or the nominal value given the channel setting
if (factoryExcitations[actualIdx] < 1D)
{
var moduleIdx = DASInfo.MapDASChannelNumber2ModuleArrayIndex(ChannelActions[idx].DASChannelNumber);
var channelInModule = DASInfo.MapDASChannelNumber2ModuleChannelNumber(ChannelActions[idx].DASChannelNumber);
var qsa = new QuerySystemAttribute_Bridge(this);
qsa.DeviceID = Convert.ToByte(1 + moduleIdx);
var expectedValue = GetExpectedExcitationMV(moduleIdx, channelInModule);
//if we have 0 or less, it's not a valid value, we don't want to change the value in the existing arm attribute...
APILogger.Log($"unexpected factory excitation:{factoryExcitations[actualIdx]}, should have been {expectedValue}");
if (expectedValue > 0D)
{
factoryExcitations[actualIdx] = Convert.ToSingle(expectedValue);
bNeedToSet = true;
}
results[idx].ExpectedExcitationMilliVolts = expectedValue;
}
else { results[idx].ExpectedExcitationMilliVolts = factoryExcitations[actualIdx]; }
info.NewData(new ServiceCallbackData.DiagnosticNewData()
{
Action = ServiceCallbackData.DiagnosticNewData.Actions.FactoryExcitation,
Result = factoryExcitations[actualIdx],
DasChannelNumber = ChannelActions[idx].DASChannelNumber
});
}
if (!bNeedToSet) return;
//set it for future posterity and retrieval
//http://fogbugz/fogbugz/default.asp?10853
try
{
if (SupportsDiagnosticsFactoryExcitation)
{
var set = new SetArmAttribute(this);
set.SetValue(AttributeTypes.ArmAndEventAttributes.DiagnosticsFactoryExcitation,
factoryExcitations.ToArray(), AttributeTypes.AttributeDataTypes.Float32Star, true);
set.SyncExecute();
}
else
{
foreach (var t in results)
{
t.ExpectedExcitationMilliVolts = 0.0;
}
}
}
catch (Exception ex)
{
APILogger.Log("failed to set factory excitations: ", ex);
}
}
}
catch (Exception ex)
{
APILogger.Log(ex);
foreach (var t in results)
{
t.ExpectedExcitationMilliVolts = 0.0;
}
}
}
/// <summary>
/// Measure the excitation on the channels that have it flagged
/// </summary>
/// <param name="ChannelActions">An array of actions. One entry per channel</param>
/// <param name="info">Our async data</param>
/// <param name="results">An array of results. One entry per channel</param>
private void MeasureExcitation(IDiagnosticActions[] ChannelActions, SliceServiceAsyncInfo info, ref IDiagnosticResult[] results)
{
// first count how many we need to do it on
var NumToMeasure = ChannelActions.Count(a => a.MeasureExcitation);
if (NumToMeasure == 0)
return;
var measureExcitation = new MeasureBridgeDiagnosticChannel(this);
var measure24V = new MeasureIEPEDiagnosticChannel(this);
for (var idx = 0; idx < ChannelActions.Length; idx++)
{
var module = Convert.ToInt32(DASInfo.MapDASChannelNumber2ModuleArrayIndex(ChannelActions[idx].DASChannelNumber));
if (DASInfo.Modules[module].TypeOfModule == DFConstantsAndEnums.ModuleType.SLICEIEPE)
{
measure24V.DeviceID = DASInfo.MapDASChannelNumber2ModuleDeviceID(ChannelActions[idx].DASChannelNumber);
measure24V.DiagnosticField = MeasureIEPEDiagnosticChannel.IEPEDiagnosticChannelList.IM_24VPowerSupply;
measure24V.SyncExecute();
results[idx].MeasuredExcitationMilliVolts = measure24V.Measurement * 1000D;
results[idx].ExpectedExcitationMilliVolts = 24000D;
}
else
{
if (ChannelActions[idx].MeasureExcitation)
{
switch (DASInfo.MapDASChannelNumber2ModuleChannelNumber(ChannelActions[idx].DASChannelNumber))
{
case 0:
measureExcitation.Channel = MeasureBridgeDiagnosticChannel.BridgeDiagnosticChannelList.ExcitationA;
break;
case 1:
measureExcitation.Channel = MeasureBridgeDiagnosticChannel.BridgeDiagnosticChannelList.ExcitationB;
break;
case 2:
measureExcitation.Channel = MeasureBridgeDiagnosticChannel.BridgeDiagnosticChannelList.ExcitationC;
break;
default:
info.Error("MeasureExcitation: Unknown channel number: " + DASInfo.MapDASChannelNumber2ModuleChannelNumber(ChannelActions[idx].DASChannelNumber).ToString());
break;
}
for (var retryCount = 0; retryCount < 2; retryCount++)
{
measureExcitation.DeviceID = DASInfo.MapDASChannelNumber2ModuleDeviceID(ChannelActions[idx].DASChannelNumber);
measureExcitation.SyncExecute();
results[idx].MeasuredExcitationMilliVolts = measureExcitation.Measurement * 1000.0;
//14233 Negative Excitation Reported by TDAS hardware not showing in Diagnostics
//SLICE excitation is not absoluted, but carrying through the values h
results[idx].NegativeExcitation = measureExcitation.Measurement < 0;
info.NewData(new ServiceCallbackData.DiagnosticNewData()
{
DasChannelNumber = ChannelActions[idx].DASChannelNumber,
Result = results[idx].MeasuredExcitationMilliVolts,
Action = ServiceCallbackData.DiagnosticNewData.Actions.MeasureExcitation
});
// first figure out what our target is
const float VoltageMultiplier = 0.98F;
var targetVoltage = 0F;
var moduleIdx = DASInfo.MapDASChannelNumber2ModuleArrayIndex(ChannelActions[idx].DASChannelNumber);
var channelIdx = DASInfo.MapDASChannelNumber2ModuleChannelNumber(ChannelActions[idx].DASChannelNumber);
if (ConfigData != null && ConfigData.Modules != null && ConfigData.Modules.Length > moduleIdx &&
ConfigData.Modules[moduleIdx].Channels != null && ConfigData.Modules[moduleIdx].Channels.Length > channelIdx)
{
if (ConfigData.Modules[moduleIdx].Channels[channelIdx] is AnalogInputDASChannel analog)
{
var excitation = Common.DAS.Concepts.Test.Module.Channel.Sensor.GetExcitationVoltageMagnitudeFromEnum(analog.Excitation);
targetVoltage = (float)(excitation * VoltageMultiplier);
}
else
{
// it's not an AnalogInputDASChannel
throw new NotSupportedException("Slice::MeasureExcitation Only analog channels implemented!");
}
}
else
{
// no config data, assume 5V
targetVoltage = 5.0F * VoltageMultiplier;
}
if (measureExcitation.Measurement >= targetVoltage)
{
// it's OK, no retry
break;
}
}
}
else
{
results[idx].MeasuredExcitationMilliVolts = 0;
results[idx].NegativeExcitation = false;
}
}
}
}
/// <summary>
/// Measure the offset on the channels that have it flagged
/// </summary>
/// <param name="ChannelActions">An array of actions. One entry per channel</param>
/// <param name="info">Our async data</param>
/// <param name="results">An array of results. One entry per channel</param>
private void MeasureOffset(IDiagnosticActions[] ChannelActions, SliceServiceAsyncInfo info, ref IDiagnosticResult[] results)
{
// first count how many we need to do it on
var NumToMeasure = ChannelActions.Count(a => a.MeasureOffset);
if (NumToMeasure == 0)
return;
var measureOffset = new MeasureStackChannelOffset(this);
measureOffset.NumberOfChannels = (byte)NumToMeasure;
measureOffset.DeviceID = 0; // send to base
var measureChannels = new byte[NumToMeasure];
var channelCounter = 0;
for (int idx = 0; idx < ChannelActions.Length; idx++)
{
if (ChannelActions[idx].MeasureOffset)
{
measureChannels[channelCounter++] = (byte)ChannelActions[idx].DASChannelNumber;
}
}
measureOffset.ChannelList = measureChannels;
measureOffset.SyncExecute();
var measureOffset2 = new RetrieveSampleAverage(this);
measureOffset2.DeviceID = 0; // send to base
var avgTimeSeconds = 1.0 / 60.0 * 2.0; // 2 * 60Hz cycles
measureOffset2.Samples = (ushort)(10000 * avgTimeSeconds);
if (measureOffset2.Samples < 1)
{
measureOffset2.Samples = 1;
}
measureOffset2.SyncExecute();
// calculate result
channelCounter = 0;
for (var idx = 0; idx < ChannelActions.Length; idx++)
{
if (ChannelActions[idx].MeasureOffset)
{
var result = (double)measureOffset.MeasuredOffset[channelCounter];
if (this is EthernetTsrAir)
{
results[idx].MeasuredOffsetEngineeringUnits = result;
}
else
{
results[idx].MeasuredOffsetMilliVolts = result;
}
info.NewData(new ServiceCallbackData.DiagnosticNewData()
{
Action = ServiceCallbackData.DiagnosticNewData.Actions.MeasureOffset,
Result = result,
DasChannelNumber = ChannelActions[idx].DASChannelNumber
});
channelCounter++;
}
}
}
/// <summary>
/// Remove the offset on the channels that have it flagged
/// </summary>
/// <param name="ChannelActions">An array of actions. One entry per channel</param>
/// <param name="info">Our async data</param>
/// <param name="results">An array of results. One entry per channel</param>
private void RemoveOffset(IDiagnosticActions[] ChannelActions, SliceServiceAsyncInfo info, ref IDiagnosticResult[] results)
{
// first count how many we need to do it on
var NumToRemove = ChannelActions.Count(a => a.RemoveOffset);
if (NumToRemove == 0)
return;
var removeOffset = new OffsetChannel(this, 1000 * 60 * 10);
removeOffset.DeviceID = 0; // send to base
var removeOffsetChannels = new byte[NumToRemove];
var ZeroTargets = new short[NumToRemove];
var TargetTolerances = new ushort[NumToRemove];
var channelCounter = 0;
for (var idx = 0; idx < ChannelActions.Length; idx++)
{
if (ChannelActions[idx].RemoveOffset)
{
removeOffsetChannels[channelCounter] = (byte)ChannelActions[idx].DASChannelNumber;
ZeroTargets[channelCounter] = ZeroTargetValue;
TargetTolerances[channelCounter] = TargetToleranceValue;
channelCounter++;
}
}
removeOffset.StackChannelList = removeOffsetChannels;
removeOffset.ZeroTarget = ZeroTargets;
removeOffset.TargetTolerance = TargetTolerances;
removeOffset.SyncExecute();
}
private void CheckDigitalValues(IDiagnosticActions[] channelActions, SliceServiceAsyncInfo info,
ref IDiagnosticResult[] results)
{
var onesToDo = from ca in channelActions where ca.CheckDigitalState select ca;
if (!onesToDo.Any()) return;
var measureOffset = new RetrieveSampleAverage(this) { DeviceID = 0 }; // send to base
const double avgTimeSeconds = 1.0 / 60.0 * 2.0; // 2 * 60Hz cycles
measureOffset.Samples = (ushort)(10000 * avgTimeSeconds);
if (measureOffset.Samples < 1)
{
measureOffset.Samples = 1;
}
measureOffset.SyncExecute();
var inputModes = new Dictionary<int, DigitalInputModes>();
var idx = 0;
foreach (var module in ConfigData.Modules)
{
foreach (var ch in module.Channels)
{
if (ch.IsConfigured())
{
inputModes[idx] = ((AnalogInputDASChannel)ch).DigitalMode;
}
idx++;
}
}
for (idx = 0; idx < results.Length; idx++)
{
if (!inputModes.ContainsKey(idx)) { continue; }
//9955 adjusting these to match what SPD00036 is doing
//9955 reported that CCNO and CCNC values were good
//but H2L and L2H transitions were backwards
//I tested each setting using ch1 on SPD00036
switch (inputModes[idx])
{
case DigitalInputModes.CCNO:
results[idx].DigitalInputActiveState = measureOffset._data[idx] <= DigitalInputs.ConstantCurrentBreakPoint;
break;
case DigitalInputModes.THL:
results[idx].DigitalInputActiveState = measureOffset._data[idx] <= DigitalInputs.VoltageInputBreakPoint;
break;
case DigitalInputModes.TLH:
results[idx].DigitalInputActiveState = measureOffset._data[idx] > DigitalInputs.VoltageInputBreakPoint;
break;
case DigitalInputModes.CCNC:
results[idx].DigitalInputActiveState = measureOffset._data[idx] > DigitalInputs.ConstantCurrentBreakPoint;
break;
}
}
}
/// <summary>
/// Measure the offset on the channels that have it flagged
/// </summary>
/// <param name="ChannelActions">An array of actions. One entry per channel</param>
/// <param name="info">Our async data</param>
/// <param name="results">An array of results. One entry per channel</param>
protected virtual void MeasureFinalOffset(IDiagnosticActions[] ChannelActions, SliceServiceAsyncInfo info, ref IDiagnosticResult[] results)
{
// verify the sample rate
var srQuery = new QueryArmAttribute(this);
srQuery.Key = AttributeTypes.ArmAndEventAttributes.DiagnosticsSampleRateHz;
srQuery.SyncExecute();
var samplerate = (double)(uint)srQuery.Value;
if (samplerate == 0)
{
throw new Exception("MeasureFinalOffset: Samplerate is 0");
}
// get the average offset
var measureOffset = new RetrieveSampleAverage(this);
measureOffset.DeviceID = 0; // send to base
var avgTimeSeconds = 1.0 / 60.0 * 2.0; // 2 * 60Hz cycles
measureOffset.Samples = (ushort)(samplerate * avgTimeSeconds);
if (measureOffset.Samples < 1) { measureOffset.Samples = 1; }
measureOffset.SyncExecute();
// store the values in arm attributes
var setSR = new SetArmAttribute(this);
setSR.SetValue(AttributeTypes.ArmAndEventAttributes.StackChannelMeasuredOffsetADC, measureOffset._data, true);
setSR.SyncExecute();
// set the calibration result
for (var idx = 0; idx < results.Length; idx++)
{
results[idx].FinalOffsetADC = measureOffset.GetChannelData(results[idx].DASChannelNumber);
if (ChannelActions[idx].RemoveOffset)
{
results[idx].AutoZeroPercentDeviation = 100D * (double)results[idx].FinalOffsetADC / short.MaxValue;
}
else { results[idx].AutoZeroPercentDeviation = null; }
info.NewData(new ServiceCallbackData.DiagnosticNewData()
{
Result = results[idx].FinalOffsetADC,
DasChannelNumber = results[idx].DASChannelNumber,
Action = ServiceCallbackData.DiagnosticNewData.Actions.FinalOffset
});
//attempt to resolve the removed adc by using the before and after info
try
{
if (null != results[idx].MeasuredOffsetMilliVolts && ChannelActions[idx].RemoveOffset)
{
results[idx].RemovedOffsetADC = Convert.ToInt32((double)results[idx].MeasuredOffsetMilliVolts / results[idx].ScalefactorMilliVoltsPerADC - (double)results[idx].FinalOffsetADC);
}
else { results[idx].RemovedOffsetADC = 0; }
}
catch (Exception)
{
}
}
}
/// <summary>
/// Measure the internal offset on the channels that have it flagged
/// </summary>
/// <param name="ChannelActions">An array of actions. One entry per channel</param>
/// <param name="info">Our async data</param>
/// <param name="results">An array of results. One entry per channel</param>
protected virtual void MeasureInternalOffset(IDiagnosticActions[] ChannelActions, SliceServiceAsyncInfo info, ref IDiagnosticResult[] results, bool bFinalOffset)
{
// first count how many we need to do it on
var numToMeasure = ChannelActions.Count(a => a.MeasureInternalOffset);
if (numToMeasure == 0)
return;
foreach (var r in results)
{
r.RemovedInternalOffsetADC = 0;
r.MeasuredInternalOffsetMilliVolts = 0;
}
}
/// <summary>
/// Convert gain code to value based on Slice 1 conversion table
/// </summary>
/// <param name="gainCode"></param>
/// <returns></returns>
protected virtual double GainCodeToGainValue(ushort gainCode)
{
var gainValueString = ((GainCodes)gainCode).ToString();
if (!double.TryParse(gainValueString.ToString().TrimStart('G'), out double gainValue))
{
gainValue = 1.0D;
}
return gainValue;
}
public enum GainCodes //Used by Slice 1 and 1.5 (Base +)
{
G1 = 0,
G2 = 1,
G4 = 2,
G8 = 3,
G16 = 4,
G32 = 5,
G64 = 6,
G128 = 7,
G10 = 8,
G20 = 9,
G40 = 10,
G80 = 11,
G160 = 12,
G320 = 13,
G640 = 14,
G1280 = 15
}
/// <summary>
/// Measure the noise on the channels that have it flagged
/// </summary>
/// <param name="ChannelActions">An array of actions. One entry per channel</param>
/// <param name="info">Our async data</param>
/// <param name="results">An array of results. One entry per channel</param>
private void MeasureNoise(IDiagnosticActions[] ChannelActions, SliceServiceAsyncInfo info, ref IDiagnosticResult[] results)
{
// first count how many we need to do it on
var NumToMeasure = ChannelActions.Count(a => a.MeasureNoise);
if (NumToMeasure == 0)
return;
var measureChannelNoiseStat = new MeasureChannelNoiseStatistic(this);
measureChannelNoiseStat.DeviceID = 0; // send to base
var noiseChannelList = new byte[NumToMeasure];
var noiseUseGainOfOne = new bool[NumToMeasure];
var channelCounter = 0;
for (var idx = 0; idx < ChannelActions.Length; idx++)
{
if (ChannelActions[idx].MeasureNoise)
{
noiseChannelList[channelCounter] =
(byte)ChannelActions[idx].DASChannelNumber;
noiseUseGainOfOne[channelCounter] = false;
channelCounter++;
}
}
measureChannelNoiseStat.ChannelList = noiseChannelList;
measureChannelNoiseStat.UseGainOfOne = noiseUseGainOfOne;
measureChannelNoiseStat.SyncExecute();
channelCounter = 0;
for (var idx = 0; idx < ChannelActions.Length; idx++)
{
if (ChannelActions[idx].MeasureNoise)
{
// convert standard deviation to % of full scale
var stddev = measureChannelNoiseStat.StandardDeviation[channelCounter];
var percent = 100.0f * stddev / 32768.0f;
results[idx].NoisePercentFullScale = percent;
info.NewData(new ServiceCallbackData.DiagnosticNewData()
{
Action = ServiceCallbackData.DiagnosticNewData.Actions.MeasureNoise,
DasChannelNumber = results[idx].DASChannelNumber,
Result = results[idx].NoisePercentFullScale
});
channelCounter++;
}
}
}
/// <summary>
/// Check the shunt on the channels that have it flagged
/// </summary>
/// <param name="ChannelActions">An array of actions. One entry per channel</param>
/// <param name="info">Our async data</param>
/// <param name="results">An array of results. One entry per channel</param>
private void PerformCalSignalCheck(IDiagnosticActions[] ChannelActions, SliceServiceAsyncInfo info, ref IDiagnosticResult[] results)
{
// first count how many we need to do it on
var NumToMeasure = ChannelActions.Count(a => a.PerformCalSignalCheck);
if (NumToMeasure == 0)
return;
//var queryChannelShuntResults = new QueryChannelShuntResults(this);
var queryChannelShuntResults = new QueryChannelCalSignalResults(this);
queryChannelShuntResults.DeviceID = 0; // send to base
var shuntChannelList = new byte[NumToMeasure];
var channelCounter = 0;
for (var idx = 0; idx < ChannelActions.Length; idx++)
{
if (ChannelActions[idx].PerformCalSignalCheck)
{
shuntChannelList[channelCounter] = (byte)ChannelActions[idx].DASChannelNumber;
channelCounter++;
}
}
queryChannelShuntResults.StackChannelList = shuntChannelList;
try
{
queryChannelShuntResults.SyncExecute();
channelCounter = 0;
for (var idx = 0; idx < ChannelActions.Length; idx++)
{
if (ChannelActions[idx].PerformCalSignalCheck)
{
results[idx].CalSignalCheckFailed = false;
results[idx].MeasuredCalSignalMv = queryChannelShuntResults.ActualMV[channelCounter];
results[idx].TargetCalSignalMv = queryChannelShuntResults.TargetMV[channelCounter];
info.NewData(new ServiceCallbackData.DiagnosticNewData()
{
Result = results[idx].MeasuredCalSignalMv,
DasChannelNumber = results[idx].DASChannelNumber,
Action = ServiceCallbackData.DiagnosticNewData.Actions.CalSignalCheck
});
channelCounter++;
}
}
}
catch (NotSupportedException)
{
// If the functionality doesn't exist, just make sure the results are set to null
// and the app will do the right thing.
//
for (var idx = 0; idx < ChannelActions.Length; idx++)
{
if (ChannelActions[idx].PerformCalSignalCheck)
{
results[idx].MeasuredCalSignalMv = null;
results[idx].TargetCalSignalMv = null;
}
}
}
catch (NotImplementedException)
{
// If the functionality doesn't exist, just make sure the results are set to null
// and the app will do the right thing.
//
for (var idx = 0; idx < ChannelActions.Length; idx++)
{
if (ChannelActions[idx].PerformCalSignalCheck)
{
results[idx].MeasuredCalSignalMv = null;
results[idx].TargetCalSignalMv = null;
}
}
}
catch (Exception)
{
if (queryChannelShuntResults.ResponseStatus == DFConstantsAndEnums.CommandStatus.StatusSetupCALDACOutputExceeded)
{
// we couldn't do a shunt check
for (int idx = 0; idx < ChannelActions.Length; idx++)
{
if (ChannelActions[idx].PerformCalSignalCheck)
{
results[idx].CalSignalCheckFailed = true;
results[idx].MeasuredCalSignalMv = null;
results[idx].TargetCalSignalMv = null;
}
}
}
else
{
throw;
}
}
}
/// <summary>
/// Check the shunt on the channels that have it flagged
/// </summary>
/// <param name="ChannelActions">An array of actions. One entry per channel</param>
/// <param name="info">Our async data</param>
/// <param name="results">An array of results. One entry per channel</param>
private void PerformShuntCheck(IDiagnosticActions[] ChannelActions, SliceServiceAsyncInfo info, ref IDiagnosticResult[] results)
{
if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.DiangosShuntDAC))
{
// first count how many we need to do it on
var NumToMeasure = ChannelActions.Count(a => a.PerformShuntCheck);
if (NumToMeasure == 0)
return;
var queryChannelShuntResults = new QueryChannelShuntResults(this);
queryChannelShuntResults.DeviceID = 0; // send to base
byte[] shuntChannelList = new byte[NumToMeasure];
int channelCounter = 0;
for (int idx = 0; idx < ChannelActions.Length; idx++)
{
if (ChannelActions[idx].PerformShuntCheck)
{
shuntChannelList[channelCounter] = (byte)ChannelActions[idx].DASChannelNumber;
channelCounter++;
}
}
queryChannelShuntResults.StackChannelList = shuntChannelList;
#region tryCatch
try
{
queryChannelShuntResults.SyncExecute();
channelCounter = 0;
for (int idx = 0; idx < ChannelActions.Length; idx++)
{
if (ChannelActions[idx].PerformShuntCheck)
{
results[idx].ShuntDeflectionFailed = false;
results[idx].MeasuredShuntDeflectionMv = queryChannelShuntResults.ActualDeflectionMV[channelCounter];
results[idx].TargetShuntDeflectionMv = queryChannelShuntResults.TargetDeflectionMV[channelCounter];
info.NewData(new ServiceCallbackData.DiagnosticNewData()
{
Action = ServiceCallbackData.DiagnosticNewData.Actions.ShuntCheck,
DasChannelNumber = results[idx].DASChannelNumber,
Result = results[idx].MeasuredShuntDeflectionMv
});
channelCounter++;
}
}
}
catch (System.NotSupportedException)
{
// If the functionality doesn't exist, just make sure the results are set to null
// and the app will do the right thing.
//
for (int idx = 0; idx < ChannelActions.Length; idx++)
{
if (ChannelActions[idx].PerformShuntCheck)
{
results[idx].MeasuredShuntDeflectionMv = null;
results[idx].TargetShuntDeflectionMv = null;
}
}
}
catch (System.NotImplementedException)
{
// If the functionality doesn't exist, just make sure the results are set to null
// and the app will do the right thing.
//
for (int idx = 0; idx < ChannelActions.Length; idx++)
{
if (ChannelActions[idx].PerformShuntCheck)
{
results[idx].MeasuredShuntDeflectionMv = null;
results[idx].TargetShuntDeflectionMv = null;
}
}
}
catch (System.Exception)
{
if (queryChannelShuntResults.ResponseStatus == DFConstantsAndEnums.CommandStatus.StatusSetupShuntDACOutputExceeded)
{
// we couldn't do a shunt check
for (int idx = 0; idx < ChannelActions.Length; idx++)
{
if (ChannelActions[idx].PerformShuntCheck)
{
results[idx].ShuntDeflectionFailed = true;
results[idx].MeasuredShuntDeflectionMv = null;
results[idx].TargetShuntDeflectionMv = null;
}
}
}
else
{
throw;
}
}
#endregion
}
else
{
for (int idx = 0; idx < ChannelActions.Length; idx++)
{
if (ChannelActions[idx].PerformShuntCheck)
{
results[idx].ShuntDeflectionFailed = true;
results[idx].MeasuredShuntDeflectionMv = null;
results[idx].TargetShuntDeflectionMv = null;
}
}
}
}
/// <summary>
/// Check the VoltageInsertionCheck on the channels that have it flagged
/// </summary>
/// <param name="ChannelActions">An array of actions. One entry per channel</param>
/// <param name="info">Our async data</param>
/// <param name="results">An array of results. One entry per channel</param>
protected virtual void PerformVoltageInsertionCheck(IDiagnosticActions[] ChannelActions, SliceServiceAsyncInfo info, ref IDiagnosticResult[] results)
{
// first count how many we need to do it on
var NumToMeasure = ChannelActions.Count(a => a.PerformVoltageInsertCheck);
if (NumToMeasure == 0)
return;
//slice 1 can not handle voltage insertion ...
for (int idx = 0; idx < ChannelActions.Length; idx++)
{
if (ChannelActions[idx].PerformVoltageInsertCheck)
{
//results[idx].ShuntDeflectionFailed = true;
results[idx].MeasuredGain = null;
results[idx].TargetGain = null;
}
}
}
protected virtual void PerformBridgeResistanceMeasurement(IDiagnosticActions[] ChannelActions, SliceServiceAsyncInfo info, ref IDiagnosticResult[] results)
{
if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.DiangosShuntDAC))
{
// first count how many we need to do it on
var NumToMeasure = ChannelActions.Count(a => a.MeasureBridgeResistance);
if (NumToMeasure == 0)
return;
var measureChannelBridgeResistance = new MeasureStackChannelBridgeResistance(this);
measureChannelBridgeResistance.DeviceID = 0; // send to base
byte[] channelList = new byte[NumToMeasure];
int channelCounter = 0;
for (int idx = 0; idx < ChannelActions.Length; idx++)
{
if (ChannelActions[idx].MeasureBridgeResistance)
{
channelList[channelCounter] =
(byte)ChannelActions[idx].DASChannelNumber;
channelCounter++;
}
}
measureChannelBridgeResistance.StackChannelList = channelList;
measureChannelBridgeResistance.SyncExecute();
channelCounter = 0;
for (int idx = 0; idx < ChannelActions.Length; idx++)
{
if (ChannelActions[idx].MeasureBridgeResistance)
{
// Store results
results[idx].BridgeResistance =
measureChannelBridgeResistance.MeasuredBridgeResistanceOhms[channelCounter];
channelCounter++;
}
}
}
else
{
for (int idx = 0; idx < ChannelActions.Length; idx++)
{
if (ChannelActions[idx].MeasureBridgeResistance)
{
// Store results
results[idx].BridgeResistance = null;
}
}
}
}
public DFConstantsAndEnums.VoltageStatusColor ConvertInputVoltage2Color(double voltage)
{
if (voltage > MaximumValidInputVoltage) { return DFConstantsAndEnums.VoltageStatusColor.Off; }
if (voltage > InputHighVoltage) { return DFConstantsAndEnums.VoltageStatusColor.Red; }
if (voltage > InputMediumVoltage) { return DFConstantsAndEnums.VoltageStatusColor.Green; }
if (voltage >= InputLowVoltage) { return DFConstantsAndEnums.VoltageStatusColor.Yellow; }
if (voltage >= MinimumValidInputVoltage) { return DFConstantsAndEnums.VoltageStatusColor.Red; }
return DFConstantsAndEnums.VoltageStatusColor.Off;
}
public DFConstantsAndEnums.VoltageStatusColor ConvertBatteryVoltage2Color(double batteryVoltage)
{
if (batteryVoltage > MaximumValidBatteryVoltage) { return DFConstantsAndEnums.VoltageStatusColor.Off; }
if (batteryVoltage > BatteryHighVoltage) { return DFConstantsAndEnums.VoltageStatusColor.Red; }
if (batteryVoltage > BatteryMediumVoltage) { return DFConstantsAndEnums.VoltageStatusColor.Green; }
if (batteryVoltage >= BatteryLowVoltage) { return DFConstantsAndEnums.VoltageStatusColor.Yellow; }
if (batteryVoltage >= MinimumValidBatteryVoltage) { return DFConstantsAndEnums.VoltageStatusColor.Red; }
return DFConstantsAndEnums.VoltageStatusColor.Off;
}
public DFConstantsAndEnums.VoltageStatusColor ConvertBatteryCapacity2Color(double voltage, double capacity)
{
if (voltage >= 5.0)
{
if (capacity > 90) { return DFConstantsAndEnums.VoltageStatusColor.Green; }
else if (capacity <= 90 && capacity >= 40) { return DFConstantsAndEnums.VoltageStatusColor.Yellow; }
else if (capacity < 40) { return DFConstantsAndEnums.VoltageStatusColor.Red; }
}
return DFConstantsAndEnums.VoltageStatusColor.Off;
}
public string ConvertInputStatusColor2Status(DFConstantsAndEnums.VoltageStatusColor color, double inputVoltage)
{
switch (color)
{
case DFConstantsAndEnums.VoltageStatusColor.Green:
case DFConstantsAndEnums.VoltageStatusColor.Yellow:
case DFConstantsAndEnums.VoltageStatusColor.Red:
return inputVoltage.ToString();
default: return Command.TDAS.TestAll.VoltageStatus.None.ToString();
}
}
/// <summary>
/// created for 11282, this just clears the diagnostics mode for das
/// this should be part of a generic services
/// </summary>
protected virtual void TurnOffDiagnosticsMode()
{
}
public string ConvertBatteryStatusColor2Status(DFConstantsAndEnums.VoltageStatusColor color, double batteryVoltage)
{
switch (color)
{
case DFConstantsAndEnums.VoltageStatusColor.Green:
case DFConstantsAndEnums.VoltageStatusColor.Yellow:
case DFConstantsAndEnums.VoltageStatusColor.Red:
return batteryVoltage.ToString();
default: return Command.TDAS.TestAll.VoltageStatus.None.ToString();
}
}
/// <summary>
/// FB 15815
/// Return true if the SmartChargeResistorSetting is not 128 otherwise return ture.
/// return null if there is any exception getting the value or the command is not supported in this protocol
/// </summary>
public virtual bool? ChargingEnabled
{
get
{
try
{
//http://manuscript.dts.local/f/cases/39462/SPS-unnecessarily-queries-SmartChargeResistorSetting-while-armed
if (null != DASArmStatus && DASArmStatus.IsArmed) { return null; }
if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.QueryBatterySOC))
{
var qsa = new QuerySystemAttributeSLICE2(this);
qsa.Key = AttributeTypes.SystemAttributesSLICE2.SmartChargeResistorSetting;
qsa.SyncExecute();
//Charging status enabled/disabled
return Convert.ToInt32(qsa.Value) != SmartChargeResistorSettingDisabledValue;
}
else
{
//Command is not supported , charging status is unknown
return null;
}
}
catch (Exception ex)
{
//Command is not supported , charging status is unknown
APILogger.Log(ex);
return null;
}
}
}
/// <summary>
/// returns whether the device supports the smart charging resistor setting.
/// it's possible only SLICE2 supports, this, but the existing code assumes they all do while
/// TSR AIR does not
/// I'm not using it currently as maybe for now return "(UNKNOWN)" is the right thing to do
/// as we don't explicitly know what the Voltage values should be for charging, discharging
/// I leave it here as a future clue when the issue comes up again.
/// </summary>
/// <returns></returns>
private bool HasSmartChargingResistorSetting()
{
switch (this)
{
case EthernetTsrAir _:
return false;
}
return true;
}
public virtual string ConvertInputVoltage2BatteryCharging(double inputVoltage)
{
var chargingEnabled = ChargingEnabled;
if (!chargingEnabled.HasValue)
{
//FB 15815 return unknown if there is any error in charging status
return Resources.Unknown;
}
//FB 15815 charging is disabled
if (!chargingEnabled.Value)
{
return Resources.NotCharging;
}
if (inputVoltage <= SecondInputVoltage && inputVoltage >= FirstInputVoltage)
{
return Resources.NotCharging;
}
else if (inputVoltage > SecondInputVoltage)
{
return Resources.Charging;
}
else
{
return Resources.Discharging;
}
}
/// <summary>
/// Measure the input and battery voltages
/// </summary>
/// <param name="info">Our async data</param>
protected void GetBaseInputs(bool powerPro = false)
{
var inputValues = new BaseInputValues();
var inputs = powerPro ? new SLICEPowerProInputReader(this) : new SLICEBaseInputReader(this);
try
{
var batteryMilliVolts = powerPro ? inputs.DirectBackupMilliVolts : inputs.BackupMilliVolts;
var batteryVoltage = Math.Round(batteryMilliVolts / 1000D, 1);
if ((batteryVoltage < MinimumValidBatteryVoltage) || (batteryVoltage > MaximumValidBatteryVoltage))
{
batteryVoltage = 0D;
batteryMilliVolts = 0D;
}
var inputVoltage = Math.Round(inputs.InputMilliVolts / 1000D, 1);
var batteryVoltageStatusColor = DFConstantsAndEnums.VoltageStatusColor.Off;
if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.QueryBatterySOC) && inputValues.ChargeCapacityValid)
{
batteryVoltageStatusColor = ConvertBatteryCapacity2Color(batteryVoltage, inputValues.ChargeCapacity);
}
else
{
batteryVoltageStatusColor = ConvertBatteryVoltage2Color(batteryVoltage);
}
var batteryVoltageStatus = ConvertBatteryStatusColor2Status(batteryVoltageStatusColor, batteryVoltage);
var batteryChargingStatus = string.Empty;
if (batteryVoltage >= MinimumValidBatteryVoltage)
{
batteryChargingStatus = ConvertInputVoltage2BatteryCharging(inputVoltage);
}
var statusDisplayBattery = ((batteryVoltage < MinimumValidBatteryVoltage) || (batteryVoltage > MaximumValidBatteryVoltage)) ?
"---" :
batteryVoltage.ToString(System.Globalization.CultureInfo.InvariantCulture) + " V " + batteryChargingStatus;
inputValues.InputVoltage = inputVoltage;
inputValues.InputMilliVolts = inputVoltage * 1000D;
var inputVoltageStatusColor = ConvertInputVoltage2Color(inputVoltage);
var inputVoltageStatus = ConvertInputStatusColor2Status(inputVoltageStatusColor, inputVoltage);
inputValues.InputVoltageStatus = inputVoltageStatus;
inputValues.InputVoltageStatusColor = inputVoltageStatusColor;
inputValues.StatusDisplayInput = ((inputVoltage < MinimumValidInputVoltage) || (inputVoltage > MaximumValidInputVoltage)) ?
"---" :
inputVoltage.ToString(System.Globalization.CultureInfo.InvariantCulture) + " V";
inputValues.MinimumValidInputVoltage = MinimumValidInputVoltage;
inputValues.MaximumValidInputVoltage = MaximumValidInputVoltage;
// measure the temperature
inputValues.TemperatureC = inputs.TemperatureC;
// measure the battery power
inputValues.BatteryVoltage = batteryVoltage;
inputValues.BatteryMilliVolts = batteryMilliVolts;
inputValues.BatteryVoltageStatus = batteryVoltageStatus;
inputValues.BatteryVoltageStatusColor = batteryVoltageStatusColor;
inputValues.MinimumValidBatteryVoltage = MinimumValidBatteryVoltage;
inputValues.MaximumValidBatteryVoltage = MaximumValidBatteryVoltage;
inputValues.StatusDisplayBattery = statusDisplayBattery;
}
catch (Exception ex)
{
APILogger.Log(ex);
}
// store the result
BaseInput = inputValues;
}
private void SetBaseInputs(QueryArmAndTriggerStatus status)
{
var inputValues = new BaseInputValues();
if (null == status)
{
BaseInput = inputValues;
return;
}
var batteryMilliVolts = status.BackupVoltage * 1000D;
var batteryVoltage = Math.Round(status.BackupVoltage, 1);
if ((batteryVoltage < MinimumValidBatteryVoltage) || (batteryVoltage > MaximumValidBatteryVoltage))
{
batteryVoltage = 0D;
batteryMilliVolts = 0D;
}
var inputVoltage = Math.Round(status.InputVoltage, 1);
DFConstantsAndEnums.VoltageStatusColor batteryVoltageStatusColor;
if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.QueryBatterySOC)
&& status.BatterySoc > 0.0 && status.BatterySoc <= 100)
{
//Use battery charge capacity (state of charge (SOC)) instead of voltage for status color
batteryVoltageStatusColor = ConvertBatteryCapacity2Color(status.BackupVoltage, status.BatterySoc);
}
else
{
batteryVoltageStatusColor = ConvertBatteryVoltage2Color(batteryVoltage);
}
var batteryVoltageStatus = ConvertBatteryStatusColor2Status(batteryVoltageStatusColor, batteryVoltage);
var batteryChargingStatus = string.Empty;
if (batteryVoltage >= MinimumValidBatteryVoltage)
{
batteryChargingStatus = ConvertInputVoltage2BatteryCharging(inputVoltage);
}
var statusDisplayBattery = batteryVoltage == 0.0 ?
"---" :
batteryVoltage.ToString(System.Globalization.CultureInfo.InvariantCulture) + " V " + batteryChargingStatus;
inputValues.InputVoltage = inputVoltage;
inputValues.InputMilliVolts = inputVoltage * 1000D;
inputValues.MinimumValidInputVoltage = MinimumValidInputVoltage;
inputValues.MaximumValidInputVoltage = MaximumValidInputVoltage;
var inputVoltageStatusColor = ConvertInputVoltage2Color(inputVoltage);
var inputVoltageStatus = ConvertInputStatusColor2Status(inputVoltageStatusColor, inputVoltage);
inputValues.InputVoltageStatus = inputVoltageStatus;
inputValues.InputVoltageStatusColor = inputVoltageStatusColor;
inputValues.StatusDisplayInput = ((inputVoltage < MinimumValidInputVoltage) || (inputVoltage > MaximumValidInputVoltage)) ?
"---" :
inputVoltage.ToString(System.Globalization.CultureInfo.InvariantCulture) + " V";
inputValues.BatteryVoltage = batteryVoltage;
inputValues.BatteryMilliVolts = batteryMilliVolts;
inputValues.MinimumValidBatteryVoltage = MinimumValidBatteryVoltage;
inputValues.MaximumValidBatteryVoltage = MaximumValidBatteryVoltage;
inputValues.BatteryVoltageStatus = batteryVoltageStatus;
inputValues.BatteryVoltageStatusColor = batteryVoltageStatusColor;
inputValues.StatusDisplayBattery = statusDisplayBattery;
BaseInput = inputValues;
//if we have QATS information and if SoC is populated, set it for baseinput
//http://manuscript.dts.local/f/cases/39490/Refine-Battery-SoC-column-definition-and-implement
if (status.BatterySoc > 0 && status.BatterySoc <= 100)
{
BaseInput.BatterySoC = status.BatterySoc;
}
}
/// <summary>
/// returns true if the unit should check/enable/disable
/// backup power
/// originally written as a performance improvement for S6
/// since it has no battery [reduce # of unnecessary commands]
/// </summary>
/// <returns></returns>
private bool ShouldUseBackupPower()
{
if (this is EthernetTsrAir) { return true; }
return !(this is EthernetSlice6 || this is EthernetSlice6AirBridge);
}
protected virtual double MeasureInputMilliVolts()
{
var measure = new MeasureBaseDiagnosticChannel(this);
measure.Channel = MeasureBaseDiagnosticChannel.BaseDiagnosticChannelList.InputVoltage;
measure.DeviceGroup = 0;
measure.DeviceID = 0;
measure.SyncExecute();
return measure.Measurement * 1000D;
}
private double MeasureTemperatureC()
{
var measure = new MeasureBaseDiagnosticChannel(this);
measure.Channel = MeasureBaseDiagnosticChannel.BaseDiagnosticChannelList.TemperatureC;
measure.DeviceGroup = 0;
measure.DeviceID = 0;
measure.SyncExecute();
return measure.Measurement;
}
protected virtual double MeasureBackupMilliVolts()
{
if (!ShouldUseBackupPower()) { return 0D; }
var measure = new MeasureBaseDiagnosticChannel(this);
measure.Channel = MeasureBaseDiagnosticChannel.BaseDiagnosticChannelList.BackupVoltage;
measure.DeviceGroup = 0;
measure.DeviceID = 0;
measure.SyncExecute();
return measure.Measurement * 1000.0;
}
private void EnableBackupPower()
{
if (!ShouldUseBackupPower()) { return; }
var setSwitch = new SetSwitchImmediate(this);
setSwitch.Switch = (byte)Switches.BaseSwitches.BackupPower;
setSwitch.SwitchText = Switches.BaseSwitches.BackupPower.ToString();
setSwitch.Setting = 1;
setSwitch.SyncExecute();
}
private void DisableBackupPower()
{
if (!ShouldUseBackupPower()) { return; }
var setSwitch = new SetSwitchImmediate(this);
setSwitch.Switch = (byte)Switches.BaseSwitches.BackupPower;
setSwitch.SwitchText = Switches.BaseSwitches.BackupPower.ToString();
setSwitch.Setting = 0;
setSwitch.SyncExecute();
}
#endregion
#region GetEventDiagnosticsResults
private class EventDiagnosticsAsyncPacket
{
public SliceServiceAsyncInfo 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 SliceServiceAsyncInfo(callback, userData);
packet.EventNumber = EventNumber;
packet.WhichResult = WhichResult;
LaunchAsyncWorker("Slice.GetEventDiagnosticsResults", new WaitCallback(AsyncGetEventDiagnosticsResults), packet);
}
private void GetEventDiagnosticFactoryExcitation(EventDiagnosticsAsyncPacket packet,
ref DiagnosticsResult[] resultArray,
ref string AttributeName)
{
var query = new QueryArmAttribute(this);
if (SupportsDiagnosticsFactoryExcitation)
{
query.Key = AttributeTypes.ArmAndEventAttributes.DiagnosticsFactoryExcitation;
AttributeName = AttributeTypes.ArmAndEventAttributes.DiagnosticsFactoryExcitation.ToString();
try
{
query.SyncExecute();
var factoryExcitations = query.Value as float[];
if (factoryExcitations == null || factoryExcitations.Length != resultArray.Length)
{
throw new ApplicationException(); // use the default values below
}
else
{
if (factoryExcitations.Length != resultArray.Length)
{
packet.info.Error("Slice.GetEventDiagnosticsResults: factoryExcitations result is different length than scale factors");
return;
}
for (var idx = 0; idx < resultArray.Length; idx++)
{
resultArray[idx].ExpectedExcitationMilliVolts = factoryExcitations[idx];
}
}
}
catch
{
for (var idx = 0; idx < resultArray.Length; idx++)
{
resultArray[idx].ExpectedExcitationMilliVolts = 0.0;
}
}
}
else
{
for (var idx = 0; idx < resultArray.Length; idx++)
{
resultArray[idx].ExpectedExcitationMilliVolts = 0.0;
}
}
}
private void SetFinalOffsetADC(short[] measuredOffsetADC, DiagnosticsResult[] resultArray)
{
for (var idx = 0; idx < resultArray.Length; idx++)
{
resultArray[idx].FinalOffsetADC = null;
resultArray[idx].AutoZeroPercentDeviation = null;
}
var removeOffsetArray = new bool[resultArray.Length];
var index = 0;
foreach (var module in ConfigData.Modules)
{
foreach (var channel in module.Channels)
{
if (!(channel is AnalogInputDASChannel)) { continue; }
if (index >= removeOffsetArray.Length) { break; }
removeOffsetArray[index] = (channel as AnalogInputDASChannel)?.RemoveOffset ?? false;
index++;
}
}
for (var idx = 0; idx < resultArray.Length; idx++)
{
if (null != measuredOffsetADC && idx < measuredOffsetADC.Length)
{
resultArray[idx].FinalOffsetADC = measuredOffsetADC[idx];
if (0 == resultArray[idx].FinalOffsetADC && idx < measuredOffsetADC.Length)
{
resultArray[idx].FinalOffsetADC = measuredOffsetADC[idx];
}
if (removeOffsetArray[idx])
{
resultArray[idx].AutoZeroPercentDeviation = 100D * measuredOffsetADC[idx] / short.MaxValue;
}
else { resultArray[idx].AutoZeroPercentDeviation = null; }
try
{
if (0 == resultArray[idx].ScalefactorMilliVoltsPerADC) { resultArray[idx].RemovedOffsetADC = 0; }
else
{
if (resultArray[idx].MeasuredOffsetMilliVolts == null || !removeOffsetArray[idx]) { resultArray[idx].RemovedOffsetADC = 0; }
else
{
resultArray[idx].RemovedOffsetADC = Convert.ToInt32((double)resultArray[idx].MeasuredOffsetMilliVolts / resultArray[idx].ScalefactorMilliVoltsPerADC - (double)resultArray[idx].FinalOffsetADC);
}
}
}
catch (Exception ex)
{
APILogger.Log(ex);
}
}
}
}
private static void ClearMeasuredGainAllEvents(IDiagnosticResult[] resultArray)
{
for (var idx = 0; idx < resultArray.Length; idx++)
{
resultArray[idx].MeasuredGain = null;
}
}
protected virtual void GetStackChannelVInsertActualGain(IDiagnosticResult[] resultArray )
{
try
{
if ( IsTSRAIR())
{
ClearMeasuredGainAllEvents(resultArray);
return;
}
var query = new QueryEventAttribute(this);
query.Key = AttributeTypes.ArmAndEventAttributes.StackChannelVInsertActualGain;
query.SyncExecute();
if (!(query.Value is float[] actualGain) || actualGain.Length != resultArray.Length)
{
ClearMeasuredGainAllEvents(resultArray);
}
else
{
for (var idx = 0; idx < resultArray.Length; idx++)
{
resultArray[idx].MeasuredGain = actualGain[idx];
}
}
}
catch (Exception)
{
for (var idx = 0; idx < resultArray.Length; idx++)
{
resultArray[idx].MeasuredGain = null;
}
}
}
private static void ClearMeasuredExcitation(IDiagnosticResult[] resultArray)
{
try
{
for (var idx = 0; idx < resultArray.Length; idx++)
{
resultArray[idx].MeasuredExcitationMilliVolts = null;
resultArray[idx].NegativeExcitation = false;
}
}
catch(Exception ex) { APILogger.Log(ex); }
}
private void GetMeasuredExcitation(IDiagnosticResult[] resultArray)
{
try
{
if(IsTSRAIR())
{
ClearMeasuredExcitation(resultArray);
return;
}
var query = new QueryEventAttribute(this);
query.Key = AttributeTypes.ArmAndEventAttributes.DiagnosticsExcitationReading;
query.SyncExecute();
if (!(query.Value is float[] excitationReading) || excitationReading.Length != resultArray.Length)
{
ClearMeasuredExcitation(resultArray);
}
else
{
for (var idx = 0; idx < resultArray.Length; idx++)
{
resultArray[idx].MeasuredExcitationMilliVolts = excitationReading[idx] * 1000D;
//14233 Negative Excitation Reported by TDAS hardware not showing in Diagnostics
//SLICE excitation is not absoluted, but carrying through the values h
resultArray[idx].NegativeExcitation = excitationReading[idx] < 0;
}
}
}
catch (Exception)
{
for (var idx = 0; idx < resultArray.Length; idx++)
{
resultArray[idx].MeasuredExcitationMilliVolts = null;
resultArray[idx].NegativeExcitation = false;
}
}
}
private static void ClearStackChannelNoiseStatistic(IDiagnosticResult[] resultArray)
{
try
{
for (var idx = 0; idx < resultArray.Length; idx++)
{
resultArray[idx].NoisePercentFullScale = null;
}
}
catch( Exception ex)
{
APILogger.Log(ex);
}
}
private void GetStackChannelNoiseStatistic(IDiagnosticResult[] resultArray)
{
try
{
if (IsTSRAIR())
{
ClearStackChannelNoiseStatistic(resultArray);
return;
}
var query = new QueryEventAttribute(this);
query.Key = AttributeTypes.ArmAndEventAttributes.StackChannelNoiseStatistic;
query.SyncExecute();
if (!(query.Value is float[] noiseStatisticStdDev) || noiseStatisticStdDev.Length != resultArray.Length)
{
ClearStackChannelNoiseStatistic(resultArray);
}
else
{
for (var idx = 0; idx < resultArray.Length; idx++)
{
resultArray[idx].NoisePercentFullScale = 100.0 * noiseStatisticStdDev[idx] / 32768.0;
}
}
}
catch (Exception)
{
for (var idx = 0; idx < resultArray.Length; idx++)
{
resultArray[idx].NoisePercentFullScale = null;
}
}
}
/// <summary>
/// returns true if the DAS supports DiagnosticFactoryExcitation attribute,
/// false otherwise
/// </summary>
protected virtual bool SupportsDiagnosticsFactoryExcitation => true;
private void AsyncGetEventDiagnosticsResults(object asyncInfo)
{
//performance improvement, don't check for existing event data [optional]
//part of reducing unnecessary commands
var packet = asyncInfo as EventDiagnosticsAsyncPacket;
if (RunTestVariables.InRunTest && RunTestVariables.OptimizeQueryEventData
// http://manuscript.dts.local/f/cases/18876/Voltage-and-current-data-incorrect-for-squibs-when-downloading-in-Run-Test
// FIXME; HACK
// CPB 2021-08-12
// Optimaztion seems to work fine for all channels but SLICE PRO TOM. I suspect that the real issue
// is in the population of the ChannelDiagnosticResults because that is the object that appears to
// be holding the needed cached values for analog channels. At the time of writing this, the result
// of this optimization on SPT data is missing datazero and incorrect EU scalefactor
&& !IsTOM())
{
if (null != ChannelDiagnosticsResults && ChannelDiagnosticsResults.Any())
{
packet.info.Success();
return;
}
}
if (IsTOM()) { AsyncGetEventDiagnosticsResultsTOM(asyncInfo); }
else
{
var AttributeName = "";
try
{
DiagnosticsResult[] resultArray = null;
var query = new QueryEventAttribute(this);
// first get the scale factors
query.EventNumber = (ushort)packet.EventNumber;
query.Key = AttributeTypes.ArmAndEventAttributes.StackChannelScaleFactorsMillivoltsPerADC;
AttributeName = AttributeTypes.ArmAndEventAttributes.StackChannelScaleFactorsMillivoltsPerADC.ToString();
query.SyncExecute();
if (!(query.Value is float[] scalefactors) || scalefactors.Length == 0)
{
packet.info.Error("Slice.GetEventDiagnosticsResults: no scale factors found");
return;
}
else
{
//TSR AIR currently returns 24 scalefactors, but we only want the first 12
if (this is WinUSBTsrAir || this is EthernetTsrAir)
{
resultArray = new DiagnosticsResult[scalefactors.Length<12? scalefactors.Length:12];
}
else
{
resultArray = new DiagnosticsResult[scalefactors.Length];
}
for (int idx = 0; idx < resultArray.Length; idx++)
{
resultArray[idx] = new DiagnosticsResult();
resultArray[idx].DASChannelNumber = idx;
resultArray[idx].EventNumber = packet.EventNumber;
if (this is WinUSBTsrAir || this is EthernetTsrAir)
{
resultArray[idx].ScalefactorEngineeringUnitsPerADC = scalefactors[idx];
}
else
{
resultArray[idx].ScalefactorMilliVoltsPerADC = scalefactors[idx];
}
}
}
packet.info.Progress(100 / 9 * 1);
#region factory excitation
GetEventDiagnosticFactoryExcitation(packet, ref resultArray, ref AttributeName);
packet.info.Progress(100 / 9 * 2);
#endregion
#region measured excitation (optional)
GetMeasuredExcitation(resultArray);
packet.info.Progress(100 / 9 * 3);
#endregion
#region StackChannelTargetShuntDeflectionMV
try
{
float[] targetShuntDeflectionMv = null;
if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.DiangosShuntDAC))
{
query.Key = AttributeTypes.ArmAndEventAttributes.StackChannelTargetShuntDeflectionMV;
query.SyncExecute();
targetShuntDeflectionMv = query.Value as float[];
}
if (targetShuntDeflectionMv == null || targetShuntDeflectionMv.Length != resultArray.Length)
{
for (var idx = 0; idx < resultArray.Length; idx++)
{
resultArray[idx].TargetShuntDeflectionMv = null;
}
}
else
{
for (var idx = 0; idx < resultArray.Length; idx++)
{
resultArray[idx].TargetShuntDeflectionMv = targetShuntDeflectionMv[idx];
}
}
}
catch (Exception)
{
for (var idx = 0; idx < resultArray.Length; idx++)
{
resultArray[idx].TargetShuntDeflectionMv = null;
}
}
packet.info.Progress(100 / 9 * 4);
#endregion
#region StackChannelActualShuntDeflectionMV
try
{
query.Key = AttributeTypes.ArmAndEventAttributes.StackChannelActualShuntDeflectionMV;
query.SyncExecute();
if (!(query.Value is float[] actualShuntDeflectionMv) || actualShuntDeflectionMv.Length != resultArray.Length)
{
for (var idx = 0; idx < resultArray.Length; idx++)
{
resultArray[idx].MeasuredShuntDeflectionMv = null;
}
}
else
{
for (var idx = 0; idx < resultArray.Length; idx++)
{
resultArray[idx].MeasuredShuntDeflectionMv = actualShuntDeflectionMv[idx];
}
}
}
catch (Exception)
{
for (var idx = 0; idx < resultArray.Length; idx++)
{
resultArray[idx].MeasuredShuntDeflectionMv = null;
}
}
packet.info.Progress(100 / 9 * 5);
#endregion
GetStackChannelVInsertActualGain(resultArray);
#region StackChannelTargetCalSignalMV
if (SupportsIEPECalSignal)
{
try
{
query.Key = AttributeTypes.ArmAndEventAttributes.StackChannelTargetCalSignalMV;
query.SyncExecute();
if (!(query.Value is float[] targetCalSignalMv) || targetCalSignalMv.Length != resultArray.Length)
{
for (var idx = 0; idx < resultArray.Length; idx++)
{
resultArray[idx].TargetCalSignalMv = null;
}
}
else
{
for (var idx = 0; idx < resultArray.Length; idx++)
{
resultArray[idx].TargetCalSignalMv = targetCalSignalMv[idx];
}
}
}
catch (Exception)
{
for (var idx = 0; idx < resultArray.Length; idx++)
{
resultArray[idx].TargetCalSignalMv = null;
}
}
}
else
{
for (var idx = 0; idx < resultArray.Length; idx++)
{
resultArray[idx].TargetCalSignalMv = null;
}
}
#endregion
#region StackChannelActualCalSignalMV
if (true == SupportsIEPECalSignal)
{
try
{
query.Key = AttributeTypes.ArmAndEventAttributes.StackChannelActualCalSignalMV;
query.SyncExecute();
if (!(query.Value is float[] actualCalSignalMv) || actualCalSignalMv.Length != resultArray.Length)
{
for (var idx = 0; idx < resultArray.Length; idx++)
{
resultArray[idx].MeasuredCalSignalMv = null;
}
}
else
{
for (var idx = 0; idx < resultArray.Length; idx++)
{
resultArray[idx].MeasuredCalSignalMv = actualCalSignalMv[idx];
}
}
}
catch (Exception)
{
for (var idx = 0; idx < resultArray.Length; idx++)
{
resultArray[idx].MeasuredCalSignalMv = null;
}
}
}
else
{
for (var idx = 0; idx < resultArray.Length; idx++)
{
resultArray[idx].MeasuredCalSignalMv = null;
}
}
#endregion
#region measured offset (optional)
try
{
query.Key = AttributeTypes.ArmAndEventAttributes.StackChannelMeasuredOffsetMV;
query.SyncExecute();
if (!(query.Value is float[] measuredOffsetMV) || measuredOffsetMV.Length != resultArray.Length)
{
for (var idx = 0; idx < resultArray.Length; idx++)
{
resultArray[idx].MeasuredOffsetMilliVolts = null;
}
}
else
{
for (var idx = 0; idx < resultArray.Length; idx++)
{
resultArray[idx].MeasuredOffsetMilliVolts = measuredOffsetMV[idx];
}
}
}
catch (Exception)
{
for (var idx = 0; idx < resultArray.Length; idx++)
{
resultArray[idx].MeasuredOffsetMilliVolts = null;
}
}
packet.info.Progress(100 / 9 * 6);
#endregion
#region final offset (optional)
try
{
query.Key = AttributeTypes.ArmAndEventAttributes.StackChannelFinalOffsetCounts;
query.SyncExecute();
var query2 = new QueryEventAttribute(this);
// first get the scale factors
query2.EventNumber = (ushort)packet.EventNumber;
query2.Key = AttributeTypes.ArmAndEventAttributes.StackChannelMeasuredOffsetADC;
query2.SyncExecute();
var measuredOffsetADC = query2.Value as short[];
SetFinalOffsetADC(measuredOffsetADC, resultArray);
}
catch (Exception)
{
for (var idx = 0; idx < resultArray.Length; idx++)
{
resultArray[idx].FinalOffsetADC = null;
resultArray[idx].AutoZeroPercentDeviation = null;
}
}
packet.info.Progress(100 / 9 * 7);
#endregion
#region noise (optional)
GetStackChannelNoiseStatistic(resultArray);
packet.info.Progress(100 / 9 * 8);
#endregion
// pickup shunt check (optional)
packet.info.Progress(100);
//apparently we can't requerying them as it's borked now
var dasChannelNumberToZeromVADC = new Dictionary<int, short>();
foreach (var module in ConfigData.Modules)
{
foreach (var ch in module.Channels)
{
if (ch is AnalogInputDASChannel aic)
{
dasChannelNumberToZeromVADC[aic.Number] = aic.ZeromVInADC;
}
}
}
foreach (var result in resultArray)
{
if (dasChannelNumberToZeromVADC.ContainsKey(result.DASChannelNumber))
{
result.ZeroMVInADC = dasChannelNumberToZeromVADC[result.DASChannelNumber];
}
}
SetChannelDiagnosticsResults(resultArray, false);
packet.info.Success();
}
catch (CanceledException)
{
packet.info.Cancel();
}
catch (Exception ex)
{
packet.info.Error(ex.Message + " Attribute: " + AttributeName, ex);
}
}
}
protected virtual void QueryInternalOffsets(ref DiagnosticsResult[] resultsArray)
{
return;
}
private void AsyncGetEventDiagnosticsResultsTOM(object asyncInfo)
{
var packet = asyncInfo as EventDiagnosticsAsyncPacket;
var AttributeName = "";
try
{
DiagnosticsResult[] resultArray = null;
var query = new QueryEventAttribute(this);
// first get the scale factors
query.EventNumber = (ushort)packet.EventNumber;
query.Key = AttributeTypes.ArmAndEventAttributes.StackChannelScaleFactorsMillivoltsPerADC;
AttributeName = AttributeTypes.ArmAndEventAttributes.StackChannelScaleFactorsMillivoltsPerADC.ToString();
query.SyncExecute();
if (!(query.Value is float[] scalefactors) || scalefactors.Length == 0)
{
packet.info.Error("Slice.GetEventDiagnosticsResults: no scale factors found");
return;
}
else
{
resultArray = new DiagnosticsResult[16];//save space for 8 digital outs
for (var idx = 0; idx < scalefactors.Length; idx++)
{
resultArray[idx] = new DiagnosticsResult();
resultArray[idx].DASChannelNumber = idx;
resultArray[idx].EventNumber = packet.EventNumber;
resultArray[idx].ScalefactorMilliVoltsPerADC = scalefactors[idx] / 1000D;
}
for (var i = 8; i < 16; i++)
{
resultArray[i] = new DiagnosticsResult();
resultArray[i].DASChannelNumber = i;
resultArray[i].EventNumber = packet.EventNumber;
resultArray[i].ScalefactorMilliVoltsPerADC = 1;
}
}
packet.info.Progress(100 / 9 * 1);
#region factory excitation
#endregion
#region measured excitation (optional)
#endregion
#region StackChannelTargetShuntDeflectionMV
packet.info.Progress(100 / 9 * 4);
#endregion
#region StackChannelActualShuntDeflectionMV
packet.info.Progress(100 / 9 * 5);
#endregion
#region StackChannelTargetCalSignalMV
#endregion
#region StackChannelActualCalSignalMV
#endregion
#region measured offset (optional)
/*
try
{
query.Key = AttributeTypes.ArmAndEventAttributes.StackChannelMeasuredOffsetMV;
query.SyncExecute();
var measuredOffsetMV = query.Value as float[];
if (measuredOffsetMV == null || measuredOffsetMV.Length != resultArray.Length)
{
for (int idx = 0; idx < resultArray.Length; idx++)
{
resultArray[idx].MeasuredOffsetMilliVolts = null;
}
}
else
{
for (int idx = 0; idx < resultArray.Length; idx++)
{
resultArray[idx].MeasuredOffsetMilliVolts = measuredOffsetMV[idx];
}
}
}
catch (System.Exception)
{
for (int idx = 0; idx < resultArray.Length; idx++)
{
resultArray[idx].MeasuredOffsetMilliVolts = null;
}
}
packet.info.Progress(100 / 9 * 6);
#endregion
*/
//#region final offset (optional)
query.Key = AttributeTypes.ArmAndEventAttributes.StackChannelFinalOffsetCounts;
query.SyncExecute();
var finalOffsetsADC = query.Value as short[];
for (int idx = 0; idx < resultArray.Length && idx < finalOffsetsADC.Length; idx++)
{
resultArray[idx].FinalOffsetADC = finalOffsetsADC[idx];
resultArray[idx].AutoZeroPercentDeviation = null;
}
/*
try
{
query.Key = AttributeTypes.ArmAndEventAttributes.StackChannelMeasuredOffsetADC;
query.SyncExecute();
var finalOffsetADC = query.Value as Int16[];
if (finalOffsetADC == null || finalOffsetADC.Length != resultArray.Length)
{
for (int idx = 0; idx < resultArray.Length; idx++)
{
resultArray[idx].FinalOffsetADC = null;
}
}
else
{
for (int idx = 0; idx < resultArray.Length; idx++)
{
resultArray[idx].FinalOffsetADC = finalOffsetADC[idx];
try
{
if (0 == resultArray[idx].ScalefactorMilliVoltsPerADC) { resultArray[idx].RemovedOffsetADC = 0; }
else
{
if (resultArray[idx].MeasuredOffsetMilliVolts == null) { resultArray[idx].RemovedOffsetADC = 0; }
else
{
resultArray[idx].RemovedOffsetADC = Convert.ToInt32((double)resultArray[idx].MeasuredOffsetMilliVolts / resultArray[idx].ScalefactorMilliVoltsPerADC - (double)resultArray[idx].FinalOffsetADC);
}
}
}
catch (System.Exception) { }
}
}
}
catch (System.Exception)
{
for (int idx = 0; idx < resultArray.Length; idx++)
{
resultArray[idx].FinalOffsetADC = null;
}
}*/
packet.info.Progress(100 / 9 * 7);
#endregion
#region noise (optional)
packet.info.Progress(100 / 9 * 8);
#endregion
#region InternallOffsetADC
QueryInternalOffsets(ref resultArray);
#endregion
// pickup shunt check (optional)
packet.info.Progress(100);
SetChannelDiagnosticsResults(resultArray, 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 SliceServiceAsyncInfo(callback, userData);
packet.MaxDelay = delay;
packet.MaxDuration = duration;
packet.DiagnosticsSampleRateHz = 10000;
packet.DiagnosticsAAFilterFrequencyHz = 2000;
LaunchAsyncWorker("SLICE.SquibFireCheckArm", new WaitCallback(AsyncSquibFireCheckArm), packet);
}
/// <summary>
/// a flag to indicate that start record delay was not set to 0 and/or didn't get queried
/// </summary>
private const ushort START_RECORD_DELAY_UNINITIALIZED = ushort.MaxValue;
/// <summary>
/// the original start record delay before it gets to 0 in squib fire check
/// 25462 StartRecDelayInSecond in System Atr#104 when set before arming is overwritten by datapro during diagnostics
/// </summary>
private ushort OriginalStartRecordDelay = START_RECORD_DELAY_UNINITIALIZED;
protected virtual ushort? GetMaxEvents()
{
if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.MaxEvents))
{
try
{
QuerySystemAttribute qsa = new QuerySystemAttribute(this, 1000);
qsa.DeviceID = 0;
qsa.Key = AttributeTypes.SystemAttributes.MaxEvent;
qsa.SyncExecute();
return (ushort)qsa.Value;
}
catch (Exception ex)
{
APILogger.Log("Exception getting max number of events", ex);
}
}
return null;
}
/// <summary>
/// sets the maximum number of events to record
/// </summary>
/// <param name="numEvents"></param>
protected virtual void SetMaxEvents(int numEvents)
{
if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.MaxEvents))
{
try
{
var ssa3 = new SetSystemAttribute(this);
ssa3.DeviceID = 0;
ssa3.SetValue(AttributeTypes.SystemAttributes.MaxEvent, Convert.ToByte(numEvents), true);
ssa3.SyncExecute();
}
catch (Exception ex)
{
APILogger.Log("Exception setting max number of events", ex);
}
}
}
/// <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 (IsTOM())
{
var bfe = new BeginFlashErase(this, 5000);
bfe.DeviceGroup = 0;
bfe.DeviceID = 0;
bfe.SyncExecute();
var done = false;
while (!done)
{
var qfes = new QueryFlashEraseStatus(this, 30 * 1000);
qfes.DeviceGroup = 0;
qfes.DeviceID = 0;
qfes.SyncExecute();
if (qfes.LastError != DFConstantsAndEnums.CommandStatus.StatusNoError)
{
done = true;
return;
}
else if (qfes.PercentComplete < 100.0f)
{
Thread.Sleep(200);
}
else
{
done = true;
}
}
ResetEventListPriorToArm();
SetArmMode(DFConstantsAndEnums.RecordingMode.CircularBuffer);
SetArmAttribute(AttributeTypes.ArmAndEventAttributes.SampleRate, packet.DiagnosticsSampleRateHz, true);
SetArmAttribute(AttributeTypes.ArmAndEventAttributes.AAFilterFrequency, Convert.ToSingle(packet.DiagnosticsAAFilterFrequencyHz), true);
SetArmAttribute(AttributeTypes.ArmAndEventAttributes.Name, Encoding.ASCII.GetBytes("TESTTRIG"), true);
SetArmAttribute(AttributeTypes.ArmAndEventAttributes.Description, Encoding.ASCII.GetBytes(" "), true);
SetArmAttribute(AttributeTypes.ArmAndEventAttributes.PreTriggerSamplesRequested, Convert.ToUInt64(.1D * packet.DiagnosticsSampleRateHz), true);
var PostTriggerSeconds = .5D + (packet.MaxDelay + packet.MaxDuration) / 1000D;
SetArmAttribute(AttributeTypes.ArmAndEventAttributes.PostTriggerSamplesRequested, Convert.ToUInt64(PostTriggerSeconds * packet.DiagnosticsSampleRateHz), true);
SetMaxEvents(1);
StoreOriginalStartRecordDelayAndClear();
var prepForDC = new PrepareForDataCollection(this);
prepForDC.SyncExecute();
// Arm
try
{
var a = new Arm(this);
a.SyncExecute();
}
catch (Exception armex)
{
packet.info.Error(armex.Message);
}
}
packet.info.Success();
}
/// <summary>
/// part of squib fire check, set the start record delay to 0 before squib fire check?
/// I'm not quite sure why we were doing this, but we were and so we still will,
/// but we'll store the original delay if we do so we can restore it later
/// 25462 StartRecDelayInSecond in System Atr#104 when set before arming is overwritten by datapro during diagnostics
/// </summary>
protected virtual void StoreOriginalStartRecordDelayAndClear()
{
//initialize to uninitalized so we don't set it unnecessarily later
OriginalStartRecordDelay = START_RECORD_DELAY_UNINITIALIZED;
//Until DataPRO supports StartRecDelayInSecond, set to 0
if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.StartRecDelayInSecond))
{
var queryDelay = new QuerySystemAttributeSLICE2(this);
queryDelay.Key = AttributeTypes.SystemAttributesSLICE2.StartRecDelayInSecond;
queryDelay.SyncExecute();
var delay = Convert.ToUInt16(queryDelay.Value);
if (0 == delay) { return; } //if the original delay is already 0, we don't need to set startrecorddelay, and we don't
//need to restore it ...
OriginalStartRecordDelay = delay;
var startRecDelayInSecond = new SetSystemAttributeSLICE2(this);
startRecDelayInSecond.SetValue(AttributeTypes.SystemAttributesSLICE2.StartRecDelayInSecond, Convert.ToUInt16(0), true);
startRecDelayInSecond.SyncExecute();
}
}
void IDiagnosticsActions.ClearTriggerOut(ServiceCallback callback, object userData)
{
var packet = new SliceServiceAsyncInfo(callback, userData);
LaunchAsyncWorker("Slice.ClearTriggerOut", new WaitCallback(AsyncClearTriggerOut), packet);
}
protected virtual void AsyncClearTriggerOut(object asyncInfo)
{
if (!(asyncInfo is SliceServiceAsyncInfo info)) { return; }
info.Success();
}
void IDiagnosticsActions.ClearLatches(ServiceCallback callback, object userData)
{
var packet = new SliceServiceAsyncInfo(callback, userData);
LaunchAsyncWorker("Slice.ClearLatches", new WaitCallback(AsyncClearLatches), packet);
}
protected virtual void AsyncClearLatches(object asyncInfo)
{
if (!(asyncInfo is SliceServiceAsyncInfo info)) { return; }
info.Success();
}
/// <summary>
/// clears any das trigger lines (n/a to slice 1)
/// </summary>
void IDiagnosticsActions.ClearDASTriggerLine(ServiceCallback callback, object userData)
{
var packet = new SliceServiceAsyncInfo(callback, userData);
LaunchAsyncWorker("SLICE.ClearDASTriggerLine", new WaitCallback(AsyncClearDASTriggerLine), packet);
}
/// <summary>
/// clears any das trigger lines asynchronously
/// </summary>
protected virtual void AsyncClearDASTriggerLine(object asyncInfo)
{
if (!(asyncInfo is SliceServiceAsyncInfo info)) { return; }
info.Success();
}
void IDiagnosticsActions.TriggerCheckTrigger(ServiceCallback callback, object userData)
{
var packet = new DiagnosticsAsyncPacket();
packet.info = new SliceServiceAsyncInfo(callback, userData);
LaunchAsyncWorker("SLICE.TriggerCheckTrigger", new WaitCallback(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;
if (IsTOM())
{
var SetSwitch = new SetSwitchImmediate(this);
SetSwitch.Switch = (byte)Switches.BaseSwitches.TriggerOut;
SetSwitch.SwitchText = Switches.BaseSwitches.TriggerOut.ToString();
SetSwitch.Setting = 1;
SetSwitch.DeviceGroup = 0;
SetSwitch.DeviceID = 0;
SetSwitch.SyncExecute();
}
packet.info.Success();
}
void IDiagnosticsActions.TriggerCheckDownload(double delay, double duration, float dummyAAFilterFrequencyHz, uint dummySampleRateHz, ServiceCallback callback, object userData)
{
var packet = new SquibFireCheckArmPacket();
packet.info = new SliceServiceAsyncInfo(callback, userData);
packet.MaxDelay = delay;
packet.MaxDuration = duration;
packet.DiagnosticsSampleRateHz = 10000;
packet.DiagnosticsAAFilterFrequencyHz = 2000;
LaunchAsyncWorker("SLICE.TriggerCheckDownload", new WaitCallback(AsyncTriggerCheckDownload), packet);
}
/// <summary>
/// ALWAYS returns an array of 8 short int values - ideally from stack channel final offset attribute, but
/// a constant if not available
/// http://manuscript.dts.local/f/cases/45361/handle-bad-SPT-attributes-being-used-for-squibfiretest
/// </summary>
/// <returns></returns>
private short[] GetDataZeroLevelOffetTOM()
{
try
{
var query = new QueryArmAttribute(this);
query.Key = AttributeTypes.ArmAndEventAttributes.StackChannelFinalOffsetCounts;
query.SyncExecute();
var finalOffsetsADC = query.Value as short[];
if (null == finalOffsetsADC || finalOffsetsADC.Length < 8) { throw new InvalidOperationException("offset attribute invalid"); }
return finalOffsetsADC;
}
catch (Exception ex)
{
APILogger.Log(ex);
}
//this is a constant that will allow the operation to run normally but we'd prefer to use the attribute above
//as long as it's available
return new short[] { -18511, -26107, -18390, -26190, -18529, -26314, -18409, -26259, -32768 };
}
/// <summary>
/// the working guts of PrepareForDiagnostics
/// </summary>
/// <param name="asyncInfo">our async data</param>
private void AsyncTriggerCheckDownload(object asyncInfo)
{
var packet = asyncInfo as SquibFireCheckArmPacket;
if (IsTOM())
{
Thread.Sleep(100);
var qea = new QueryEventAttribute(this);
qea.EventNumber = 0;
qea.Key = AttributeTypes.ArmAndEventAttributes.TriggerSampleNumber;
qea.SyncExecute();
var trigger = (ulong)qea.Value;
var totalChannelsQuery = new QueryEventAttribute(this);
totalChannelsQuery.EventNumber = 0;
totalChannelsQuery.Key = AttributeTypes.ArmAndEventAttributes.TotalChannels;
totalChannelsQuery.SyncExecute();
var totalChannels = (byte)totalChannelsQuery.Value;
double sps = packet.DiagnosticsSampleRateHz;
var dataQuery = new QueryEventData_SLICE2(this);
var LastSample = trigger + Convert.ToUInt64((packet.MaxDelay + packet.MaxDuration + 50D) * sps / 1000D);
var FirstSample = trigger - Convert.ToUInt64(.1D * sps);
dataQuery.EventNumber = 0;
dataQuery.LastSample = LastSample * totalChannels;
dataQuery.FirstSample = FirstSample * totalChannels;
dataQuery.ChannelsDownloaded = totalChannels;
var SamplesToGet = LastSample - FirstSample;
var SamplesGotten = 0UL;
var channelToRawData = new Dictionary<int, List<ushort>>();
var channelToADCData = new Dictionary<int, List<short>>();
var leftOver = new List<ushort>();
var carryoverStartPoint = FirstSample * totalChannels;
while (SamplesGotten < SamplesToGet)
{
dataQuery.FirstSample = carryoverStartPoint;
dataQuery.SyncExecute();
var data = dataQuery.GetRawIndexedChannelData(ref leftOver, ref carryoverStartPoint);
var SamplesThisTime = 0UL;
ushort[] tmp;
for (var i = 0; i < 8; i++)
{
tmp = data[i];
if (!channelToRawData.ContainsKey(i))
{
channelToRawData.Add(i, new List<ushort>());
channelToADCData.Add(i, new List<short>());
}
channelToRawData[i].AddRange(tmp);
SamplesThisTime = (ulong)tmp.Length;
Trace.WriteLine("samples this time: " + SamplesThisTime.ToString());
}
SamplesGotten += SamplesThisTime;
}
for (var i = 0; i < 8; i++)
{
for (var currentSample = 0; currentSample < channelToRawData[i].Count; currentSample++)
{
var source = channelToRawData[i][currentSample];
channelToADCData[i].Add((short)((((source & 0x00FF) << 8) | ((source >> 8) & 0x00FF)) + 0x8000));
}
}
var dataZeroLevelOffsetsFromAttribute = GetDataZeroLevelOffetTOM();
for (var i = 0; i < 8; i += 2)
{
var squibVoltage = ConfigData.Modules[0].Channels[i] as OutputSquibChannel;
var squibCurrent = ConfigData.Modules[0].Channels[i + 1] as OutputSquibChannel;
Trace.Assert(null != squibVoltage && null != squibCurrent, "Missing SQUIB Channel");
if (!squibCurrent.IsConfigured()) { continue; }
try
{
var mydataCurrent = channelToADCData[i + 1].ToArray();
var mydataVoltage = channelToADCData[i].ToArray();
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 (squibCurrent.FireMode)
{
case SquibFireMode.CAP:
threshold = 2.1D;
break;
case SquibFireMode.CONSTANT:
{
threshold = .75D * squibCurrent.SquibOutputCurrent;
}
break;
default: throw new NotSupportedException("Invalid firemode for SLICEPRO TOM: " + squibCurrent.FireMode.ToString());
}
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.
var scaleFactorMv = squibCurrent.ScaleFactorMv;
double offset = squibCurrent.PreTestDataZeroLevelADC;
//note current is i+1, while voltage is i, not sure why we switched up order here
//10000 is an arbitrary value here - calstation was using a value of 0, but ordinary values are
//greater than abs 18000, so anything less than that is pretty sus and retrieving from attributes
//is probably called for then
if (Math.Abs(offset) < 10000) { offset = dataZeroLevelOffsetsFromAttribute[i+1]; }
var scaleFactorMv2 = squibVoltage.ScaleFactorMv;
double offset2 = squibVoltage.PreTestDataZeroLevelADC;
if (Math.Abs(offset2) < 10000) { offset2 = dataZeroLevelOffsetsFromAttribute[i];}
//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 iSample = 0; iSample < mydataCurrent.Length && iSample < mydataVoltage.Length; iSample++)
{
try
{
dCurrent = (mydataCurrent[iSample] - offset) * scaleFactorMv / 1000D;
dVoltage = (mydataVoltage[iSample] - offset2) * scaleFactorMv2 / 1000D;
convertedCurrentData.Add(dCurrent);
convertedVoltageData.Add(dVoltage);
if (indexBelowThresholdAgain == int.MaxValue)
{
if (dCurrent > threshold)
{
indexOverThreshold = Math.Min(iSample, indexOverThreshold);
}
else
{
//only set indexbelowthreshold index once we have gone over the threshold.
if (indexOverThreshold < int.MaxValue)
{
indexBelowThresholdAgain = Math.Min(iSample, indexBelowThresholdAgain);
}
}
}
}
catch (Exception) { }
}
double actualDelayMS = indexOverThreshold < int.MaxValue ? (indexOverThreshold - .1D * sps) / (sps / 1000D) : 0;
double actualDurationMS = (indexBelowThresholdAgain - (double)indexOverThreshold) / (sps / 1000D);//10ksps
var deltaDelay = actualDelayMS - squibCurrent.DelayMS;
var deltaDuration = actualDurationMS - squibCurrent.DurationMS;
var bPassed = true;
ChannelDiagnosticsResults[i].SquibDurationPassed = true;
ChannelDiagnosticsResults[i].SquibDelayPassed = true;
if (Math.Abs(deltaDelay) >= 1) { bPassed = false; ChannelDiagnosticsResults[i].SquibDelayPassed = false; }
if (squibCurrent.DurationMS > 6)
{
if (actualDurationMS < 6) { bPassed = false; ChannelDiagnosticsResults[i].SquibDurationPassed = false; }
}
else
{
if (Math.Abs(deltaDuration) > .25 && squibCurrent.LimitDuration) { bPassed = false; ChannelDiagnosticsResults[i].SquibDurationPassed = false; }
}
if (squibCurrent.LimitDuration)
{
ChannelDiagnosticsResults[i].MeasuredDurationMS = actualDurationMS;
}
else
{
ChannelDiagnosticsResults[i].MeasuredDurationMS = null;
}
ChannelDiagnosticsResults[i].MeasuredDelayMS = actualDelayMS;
ChannelDiagnosticsResults[i].SquibFirePassed = bPassed;
//we want to start just before the squib was supposed to fire
//index 0 is -.1, so if delay was 0 and we want to start exactly on the first sample
//we'd want startindex of .1*sps, so if we start at .09*sps, we've left in .01 of extra pad
var startIndex = Convert.ToInt32(.09D * sps + (squibCurrent.DelayMS * sps) / 1000D);
var samplesNeeded = .02D * sps + (squibCurrent.DurationMS * sps / 1000D);
ChannelDiagnosticsResults[i].SquibFireCurrentData = convertedCurrentData.GetRange(startIndex,
Convert.ToInt32(samplesNeeded)).ToArray();
ChannelDiagnosticsResults[i].SquibFireVoltageData = convertedVoltageData.GetRange(startIndex,
Convert.ToInt32(samplesNeeded)).ToArray();
ChannelDiagnosticsResults[i].SquibThreshold = threshold;
var timeAxis = new List<double>(2000);
var dCurrentTime = 0D;
//startindex is at .01s before the squib fire, which is at delayMS
//note .01 is 10ms
var startOffset = -10D + squibCurrent.DelayMS;
for (var iSample = 0; iSample < ChannelDiagnosticsResults[i].SquibFireCurrentData.Length; iSample++)
{
//here I calculate it first in ms, then add it as in seconds
dCurrentTime = startOffset + 1000D * (iSample / sps);
//dCurrentTime /= 1000D;
timeAxis.Add(dCurrentTime);
}
ChannelDiagnosticsResults[i].SquibFireTimeAxis = timeAxis.ToArray();
var config = GetConfigAttributes(this);
config.SetEventDownloaded(0, 1);
}
catch (Exception ex)
{
packet.info.Error(ex.Message);
}
}
RestoreOriginalStartRecordDelay();
}
packet.info.Success();
}
/// <summary>
/// this restores the original delay after squib fire check
/// 25462 StartRecDelayInSecond in System Atr#104 when set before arming is overwritten by datapro during diagnostics
/// </summary>
protected virtual void RestoreOriginalStartRecordDelay()
{
try
{
// restore the squib start record delay
if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.StartRecDelayInSecond) && OriginalStartRecordDelay != START_RECORD_DELAY_UNINITIALIZED)
{
var setStartRecordDelay = new SetSystemAttributeSLICE2(this);
setStartRecordDelay.SetValue(AttributeTypes.SystemAttributesSLICE2.StartRecDelayInSecond, OriginalStartRecordDelay, true);
setStartRecordDelay.SyncExecute();
}
}
catch (Exception ex)
{
APILogger.Log(ex);
}
}
}
}