3698 lines
170 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|
|
}
|