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

2077 lines
84 KiB
C#

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using System.Threading;
using DTS.Common.Utilities.Logging;
using DTS.DASLib.Command.SLICE;
using DTS.DASLib.Command;
using DTS.Common.Interface.DASFactory;
using DTS.DASLib.Command.SLICE.DownloadCommands;
using DTS.DASLib.Command.SLICE.RealtimeCommands;
using DTS.Common.DAS.Concepts.DAS.Channel;
using DTS.Common.Interface.Connection;
using DTS.Common.Enums.DASFactory;
using DTS.Common.ICommunication;
using DTS.Common.Interface.DASFactory.Diagnostics;
using DTS.Common.Enums;
using System.IO;
using DTS.Common.DASResource;
using DTS.Common.Utilities.LTLogging;
using DTS.Common.Enums.Hardware;
using System.Linq;
namespace DTS.DASLib.Service
{
public partial class Slice<T> : Communication<T>,
IDASCommunication,
IConfigurationActions,
IDiagnosticsActions,
ITriggerCheckActions,
IRealTimeActions,
IArmActions,
IDownloadActions where T : IConnection, new()
{
/// <summary>
/// sets the arm mode for das
/// in the case of CIRCULARBUFFER+UART, etc will set to CIRCULAR BUFFER if the DAS does not
/// support UART
/// </summary>
protected void SetArmMode(DFConstantsAndEnums.RecordingMode mode)
{
var modeToSet = mode;
var enum2Mode = RecordingModeExtensions.ToRecordingModes(mode);
switch (mode)
{
case DFConstantsAndEnums.RecordingMode.CircularBufferPlusUART:
if (!HardwareConstants.IsRecordingModeSupported(enum2Mode, GetHardwareType(), ProtocolVersion, true))
{
modeToSet = DFConstantsAndEnums.RecordingMode.CircularBuffer;
}
break;
case DFConstantsAndEnums.RecordingMode.RecorderModePlusUART:
if (!HardwareConstants.IsRecordingModeSupported(enum2Mode, GetHardwareType(), ProtocolVersion, true))
{
modeToSet = DFConstantsAndEnums.RecordingMode.RecorderMode;
}
break;
}
SetArmAttribute(AttributeTypes.ArmAndEventAttributes.ArmMode, modeToSet);
}
/// <summary>
/// <inheritdoc cref="IConfiguration"/>
/// </summary>
public string TestDirectory { get; set; }
/// <summary>
/// returns true if the unit is capable of reading arm status
/// 17800 Trigger status is "waiting" but PPRO has indeed triggered
/// </summary>
/// <returns></returns>
bool IDASCommunication.GetCanCheckArmStatus() { return true; }
void IConfigurationActions.SetFirstUseDate(DateTime firstUseDate, ServiceCallback callback, object userData)
{
var info = new SliceServiceAsyncInfo(callback, userData);
FirstUseDate = firstUseDate;
LaunchAsyncWorker("IConfigurationActions.SetFirstUseDate", SetFirstUseDateAsync, info);
}
protected virtual void SetFirstUseDateAsync(object o)
{
if (!(o is SliceServiceAsyncInfo info))
{
return;
}
info.Error("Not supported");
}
public bool ConnectionCheck()
{
try
{
QuerySerialNumber querySerial = new QuerySerialNumber(this);
querySerial.SyncExecute();
return true;
}
catch (Exception ex)
{
APILogger.Log(ex);
}
return false;
}
void IConfigurationActions.StoreTestSetupXML(ServiceCallback callback, object userData, string testSetupXML)
{
var info = new SliceServiceSetTestSetupAsyncInfo(callback, userData, testSetupXML);
LaunchAsyncWorker("Slice.StoreTestSetupXML", AsyncStoreTestSetupXML, info);
}
private void AsyncStoreTestSetupXML(object asyncInfo)
{
if (!(asyncInfo is SliceServiceSetTestSetupAsyncInfo info))
{
return;
}
try
{
var config = GetConfigAttributes(this);
config.StoreXMLConfig(info.TestSetupXML, ConfigAttributes.FileStore.TestSetup);
}
catch (Exception ex)
{
APILogger.Log(ex);
info.Error(ex.Message, ex);
}
info.Success();
}
void IConfigurationActions.CheckAAFilterRate(ServiceCallback callback, object userData)
{
var info = new SliceServiceAsyncInfo(callback, userData);
// look thru all the config data and make sure it's fit to be used
// our caller have already checked for null, but...
if (ConfigData == null)
{
// "Slice.VerifyConfig: ConfigData is null"
throw new ArgumentException(Strings.Slice_VerifyConfig_Err1);
}
if (ConfigData.Modules == null || ConfigData.Modules.Length == 0 || ConfigData.Modules.Length > DASInfo.Modules.Length)
{
// "Slice.VerifyConfig: ConfigData.Modules is null, empty or too long"
throw new ArgumentException(Strings.Slice_VerifyConfig_Err4);
}
var AAFilterRateReducedToMax = false;
for (var moduleIdx = 0; moduleIdx < ConfigData.Modules.Length; moduleIdx++)
{
var module = ConfigData.Modules[moduleIdx];
if (module.IsClock()) continue;
if (module.ModuleArrayIndex != moduleIdx)
{
// "Slice.VerifyConfig: ConfigData.Module[{0}].ModuleNumber doesn't mach index"
throw new ArgumentException(string.Format(Strings.Slice_VerifyConfig_Err5, moduleIdx));
}
if (module.AAFilterRateHz > MaxAAFilterRateHz)
{
module.AAFilterRateHz = MaxAAFilterRateHz;
AAFilterRateReducedToMax = true;
}
}
if (AAFilterRateReducedToMax)
{
info.Error(SerialNumber);
}
else
{
info.Success();
}
}
void IConfigurationActions.ResetHardwareLines(ServiceCallback callback, object userData)
{
var info = new SliceServiceAsyncInfo(callback, userData);
LaunchAsyncWorker("Slice.ResetHardwareLines", AsyncResetHardwareLines, info);
}
private void AsyncResetHardwareLines(object asyncInfo)
{
if (!(asyncInfo is SliceServiceAsyncInfo info))
{
return;
}
if (!IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.InitHardwareInputLines))
{
info.Success();
return;
}
var ssaStart = new SetSystemAttribute(this);
var ssaTrigger = new SetSystemAttribute(this);
try
{
ssaStart.SetValue(AttributeTypes.SystemAttributes.StartRecordPolarity, (byte)(InvertStart ? 1 : 0), true);
ssaTrigger.SetValue(AttributeTypes.SystemAttributes.TriggerPolarity, (byte)(InvertTrigger ? 1 : 0), true);
ssaStart.SyncExecute();
ssaTrigger.SyncExecute();
var ssi = new SetSwitchImmediate(this)
{
DeviceID = 0x00,
Switch = (byte)Switches.BaseSwitches.TriggerOut,
SwitchText = Switches.BaseSwitches.TriggerOut.ToString(),
Setting = InvertTrigger ? Convert.ToByte(1) : Convert.ToByte(0)
};
ssi.SyncExecute();
}
catch (Exception ex)
{
APILogger.Log(ex);
}
info.Success();
}
void IArmActions.CheckAlreadyLevelTriggered(ServiceCallback callback, object userData)
{
var info = new SliceServiceAsyncInfo(callback, userData);
LaunchAsyncWorker("Slice.CheckAlreadyLevelTriggered", AsyncCheckAlreadyLevelTriggered, info);
}
protected virtual bool ShouldCheckLevelTrigger(double eu)
{
return true;
}
private void AsyncCheckAlreadyLevelTriggered(object asyncInfo)
{
var info = asyncInfo as SliceServiceAsyncInfo;
try
{
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)(10000 * avgTimeSeconds);//should use sample rate here?
if (measureOffset.Samples < 1) { measureOffset.Samples = 1; }
measureOffset.SyncExecute();
foreach (var m in ConfigData.Modules)
{
foreach (var ch in m.Channels.Where(x => x is AnalogInputDASChannel
&& !(x is ILevelTriggerable)))
{
var aCh = (AnalogInputDASChannel)ch;
var triggerable = (ILevelTriggerable)ch;
aCh.AlreadyLevelTriggered = false;
aCh.MeasuredEULevelTriggerCheck = double.NaN;
if (aCh.LevelTriggerType == LevelTriggerTypes.NONE ||
aCh.ConfigurationMode != DFConstantsAndEnums.ConfigMode.Normal) continue;
try
{
var ADC = measureOffset.GetChannelData(aCh.Number);
if (null != triggerable.SampleAverageADC &&
!double.IsNaN((double)triggerable.SampleAverageADC))
{
ADC = (short)triggerable.SampleAverageADC;
}
LevelTriggerLogging.LevelTriggerLog($"AsyncCheckAlreadyLevelTriggered {SerialNumber}:{ch.Number} ADC:{ADC}\r\n");
_ = aCh.ScalefactorMilliVoltsPerADC;
try
{
var diagnostics = aCh.Diagnostics;
_ = diagnostics.ScalefactorMilliVoltsPerADC;
diagnostics.GetExpectedDataZeroLevelADC(aCh.ZeroMethod);
}
catch (Exception ex)
{
APILogger.Log(ex);
}
if (null != aCh.DiagnosticInformation) { _ = aCh.DiagnosticInformation.GetExpectedDataZeroLevelADC(aCh.ZeroMethod); }
var ds = aCh.GetDataScaler();
var eu = ds.GetEU(ADC);
aCh.MeasuredEULevelTriggerCheck = eu;
if (null != aCh.TriggerAboveThresholdEu)
{
var limit = (double)aCh.TriggerAboveThresholdEu;
if (eu > limit && ShouldCheckLevelTrigger(limit))
{
aCh.AlreadyLevelTriggered = true;
LevelTriggerLogging.LevelTriggerLog(
$"AlreadyLevelTriggeredCheck {SerialNumber}:{aCh.Number} is already LevelTriggered ({eu}>{limit})\r\n");
}
}
if (null != aCh.TriggerBelowThresholdEu)
{
var limit = (double)aCh.TriggerBelowThresholdEu;
if (eu < limit && ShouldCheckLevelTrigger(limit))
{
aCh.AlreadyLevelTriggered = true;
LevelTriggerLogging.LevelTriggerLog(
$"AlreadyLevelTriggeredCheck {SerialNumber}:{aCh.Number} is already LevelTriggered ({eu}<{limit})\r\n");
}
}
}
catch (Exception ex)
{
APILogger.Log(ex);
}
}
}
info?.Success();
}
catch (Exception ex)
{
info.Error(ex.Message, ex);
}
}
public virtual int MaxModules
{
get => 8;
set {; }
}
/// <summary>
/// returns the number of samples taken up by the time specified in microseconds
/// </summary>
/// <param name="delayInMicroSeconds">delay in microseconds</param>
/// <param name="actualSampleRate">sample rate of collected data</param>
/// <param name="originalT0"></param>
/// <param name="rule"></param>
/// <returns>samples the collected data got shifted (rounded up)</returns>
protected ulong GetPhaseShiftSamples(double delayInMicroSeconds, double actualSampleRate, ulong originalT0, string rule)
{
//we have SamplesPerSecond, so convert to common unit
var delayInSeconds = delayInMicroSeconds / 1000000D;
if (delayInSeconds < 0) { delayInSeconds = 0; }//we only consider the filter shifting things further in time for now
//now round up the number of samples and return as a discrete number
var d = Math.Round(delayInSeconds * actualSampleRate);
APILogger.Log("PhaseShift: " + d.ToString(CultureInfo.InvariantCulture), SerialNumber);
var shift = Convert.ToUInt64(d);
APILogger.LogPhaseShift(SerialNumber, delayInMicroSeconds, originalT0, originalT0 + shift, shift, rule);
return shift;
}
/// <inheritdoc />
/// <summary>
/// returns the number of phase shift samples the hardware filter causes (rounded up)
/// </summary>
/// <param name="moduleIndex"></param>
/// <param name="actualSampleRate"></param>
/// <param name="hardwareAAF"></param>
/// <param name="originalT0"></param>
/// <returns></returns>
public virtual ulong GetPhaseShiftSamples(uint ModuleIndex, double ActualSampleRate, uint HardwareAAF, ulong originalT0)
{
//note http://foghat/dtswiki/index.php/SLICE_PRO_Hardware_Overview#SLICE_PRO_Hardware_Input_Signal_Delay_vs_Filter_Rate
return 0;
}
/// <summary>
/// initiates background flash erase for units that support it
/// will return immediately after flash erase begins
/// </summary>
/// <param name="callbck"></param>
/// <param name="userData"></param>
void IArmActions.BeginBackgroundFlashErase(ServiceCallback callback, object userData)
{
var info = new SliceServiceAsyncInfo(callback, userData);
info.Success();
}
/// <summary>
/// returns whether unit is capable of flash erase
/// </summary>
bool IArmActions.SupportsBackgroundFlashErase => false;
/// <summary>
/// returns whether unit has started background flash erase
/// </summary>
bool IArmActions.BackgroundFlashEraseStarted => false;
DateTime? IArmActions.BackgroundFlashEraseStartTime => null;
/// <inheritdoc />
/// <summary>
/// slice 1.0 can simply use user attributes to store the display order
/// </summary>
/// <param name="order"></param>
public virtual void SetDASDisplayOrder(int order)
{
try
{
var sua = new SetUserAttribute(this);
sua.SetValue(AttributeTypes.SliceUserAttributes.DasDisplayOrder, Convert.ToByte(order), true);
sua.SyncExecute();
}
catch (Exception ex)
{
APILogger.Log("failed to set das display order,", ex);
}
}
public virtual void SetChannelDisplayOrder(int[] order)
{
try
{
var sua = new SetUserAttribute(this);
sua.SetValue(AttributeTypes.SliceUserAttributes.DisplayOrder, order, true);
sua.SyncExecute();
}
catch (Exception ex)
{
APILogger.Log("failed to set channel display order,", ex);
}
}
public virtual int GetDASDisplayOrder()
{
try
{
var qa = new QueryUserAttribute(this);
qa.Key = AttributeTypes.SliceUserAttributes.DasDisplayOrder;
qa.SyncExecute();
return Convert.ToInt32(qa.Value);
}
catch (Exception)
{
// ignored - if there's no das display order it's okay
}
return -1;
}
public virtual int[] GetChannelDisplayOrder()
{
int[] ret = { -1 };
try
{
var qa = new QueryUserAttribute(this);
qa.Key = AttributeTypes.SliceUserAttributes.DisplayOrder;
qa.SyncExecute();
return (int[])qa.Value;
}
catch (Exception)
{
// ignored - if there's no channel display order it's okay
}
return ret;
}
void IArmActions.ReadyForArming(ServiceCallback callback, object userData, Guid eventGuid, int
armNowTimeout, bool testingMode, int maxNumberEvents, bool DummyArm, bool SysMode)
{
var info = new ArmPacket(callback, userData, eventGuid, armNowTimeout, testingMode, maxNumberEvents);
LaunchAsyncWorker("Slice.ReadyForArming", AsyncReadyForArming, info);
}
public bool AutoArmed { get; set; } = false;
protected class ArmPacket : SliceServiceAsyncInfo
{
public Guid EventGuid { get; }
public int ArmNowTimeout { get; }
public bool TestingMode { get; }
public int MaxNumberEvents { get; set; }
public ArmPacket(ServiceCallback cb, object ud, Guid eg, int armNowTimeout, bool testingMode,
int maxNumberEvents)
: base(cb, ud)
{
EventGuid = eg;
ArmNowTimeout = armNowTimeout;
TestingMode = testingMode;
MaxNumberEvents = maxNumberEvents;
}
}
protected class AutoArmPacket : ArmPacket
{
public uint DiagnosticsDelayMs { get; }
public bool RepeatEnable { get; }
public bool PreserveDiagnostics { get; }
public AutoArmPacket(ServiceCallback cb, object ud, Guid eg, int armNowTimeout, bool testingMode, uint diagnosticsDelayMs, int maxEventCount, bool repeatEnable, bool preserveDiagnostics)
: base(cb, ud, eg, armNowTimeout, testingMode, maxEventCount)
{
DiagnosticsDelayMs = diagnosticsDelayMs;
RepeatEnable = repeatEnable;
PreserveDiagnostics = preserveDiagnostics;
}
}
#region FlashErase
void IArmActions.BeginFlashErase(ServiceCallback callback, object userData, bool DummyArm)
{
var info = new SliceServiceAsyncInfo(callback, userData);
LaunchAsyncWorker("Slice.BeginFlashErase", AsyncBeginFlashErase, info);
}
protected virtual void AsyncBeginFlashErase(object asyncInfo)
{
var info = asyncInfo as SliceServiceAsyncInfo;
if (!IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.FlashClear))
{
info?.Success();
return;
}
var bReleased = true;
try
{
Lock();
bReleased = false;
var startErase = new BeginFlashErase(this);
startErase.SyncExecute();
bReleased = true;
Release();
info?.Success();
}
catch (CanceledException)
{
if (!bReleased) { bReleased = true; Release(); }
info?.Cancel();
}
catch (Exception ex)
{
if (!bReleased) { bReleased = true; Release(); }
info?.Error(ex.Message, ex);
}
finally { if (!bReleased) { Release(); } }
}
void IArmActions.QueryFlashEraseStatus(ServiceCallback callback, object userData)
{
var info = new SliceServiceAsyncInfo(callback, userData);
LaunchAsyncWorker("Slice.QueryFlashEraseStatus", AsyncQueryFlashEraseStatus, info);
}
private void AsyncQueryFlashEraseStatus(object asyncInfo)
{
var info = asyncInfo as SliceServiceAsyncInfo;
if (!IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.FlashClear))
{
info?.Progress(100);
info?.Success();
return;
}
try
{
var done = false;
while (!done)
{
var bReleased = true;
try
{
Lock();
bReleased = false;
var queryStatus = new QueryFlashEraseStatus(this, 1 * 30 * 1000);
queryStatus.SyncExecute();
var eraseStatus = new FlashEraseStatus();
eraseStatus.PercentComplete = queryStatus.PercentComplete;
eraseStatus.LastError = queryStatus.LastError;
DASFlashEraseStatus = eraseStatus;
if (eraseStatus.LastError != DFConstantsAndEnums.CommandStatus.StatusNoError)
{
info?.Progress(0);
bReleased = true; Release();
info?.Error("Error while clearing flash: " + eraseStatus.LastError.ToString());
done = true;
}
else if (eraseStatus.PercentComplete < 100.0f)
{
info?.Progress((int)eraseStatus.PercentComplete);
bReleased = true; Release();
Thread.Sleep(500);
}
else
{
info?.Progress(100);
bReleased = true; Release();
info?.Success();
done = true;
}
}
catch (Exception ex)
{
APILogger.Log(ex);
if (!bReleased) { bReleased = true; Release(); }
throw;
}
finally { if (!bReleased) { Release(); } }
}
}
catch (CanceledException)
{
info?.Cancel();
}
catch (Exception ex)
{
info?.Error(ex.Message, ex);
}
}
#endregion
#region ArmNow
void IArmActions.PrepareForArmNow(ServiceCallback callback, object userData, Guid eventGuid, int armNowTimeout, bool testingMode,
int maxNumberEvents, bool SysMode)
{
var info = new ArmPacket(callback, userData, eventGuid, armNowTimeout, testingMode, maxNumberEvents);
LaunchAsyncWorker("Slice.PrepareForArmNow", AsyncPrepareForArmNow, info);
}
internal class RearmAsyncInfo : SliceServiceAsyncInfo
{
public bool AutoArm { get; set; } = false;
public bool Arm { get; set; } = false;
public bool RepeatEnable { get; set; } = false;
public RearmAsyncInfo(ServiceCallback _callback, object _userData, bool autoArm, bool arm, bool repeatEnable) : base(_callback, _userData)
{
AutoArm = autoArm;
Arm = arm;
RepeatEnable = repeatEnable;
}
}
void IArmActions.ReArm(ServiceCallback callback, object userData, bool autoArm, bool arm, bool repeatEnable)
{
var info = new RearmAsyncInfo(callback, userData, autoArm, arm, repeatEnable);
LaunchAsyncWorker("Slice.ReArm", AsyncRearm, info);
}
private void AsyncRearm(object data)
{
var info = (RearmAsyncInfo)data;
try
{
if (info.AutoArm) { SetAutoArm(); }
if (info.RepeatEnable) { SetRepeatEnable(); }
if (info.Arm)
{
var arm = new Arm(this);
arm.SyncExecute();
}
}
catch(Exception ex)
{
APILogger.Log(ex);
info.Error(ex.Message);
return;
}
info.Success();
}
void IArmActions.PreparedArmNow(ServiceCallback callback, object userData, Guid eventGuid, int armNowTimeout, bool checkoutMode,
int maxNumberEvents, bool SysMode)
{
var info = new ArmPacket(callback, userData, eventGuid, armNowTimeout, checkoutMode, maxNumberEvents);
LaunchAsyncWorker("Slice.PreparedArm", AsyncPreparedArm, info);
}
void IArmActions.ArmNow(ServiceCallback callback, object userData, Guid eventGuid, int armNowTimeout, bool testingMode,
int maxNumberEvents, bool SysMode)
{
var info = new ArmPacket(callback, userData, eventGuid, armNowTimeout, testingMode, maxNumberEvents);
LaunchAsyncWorker("Slice.ArmNow", AsyncArmNow, info);
}
protected virtual void AsyncArmNow(object asyncInfo)
{
if (!(asyncInfo is ArmPacket info)) { return; }
var bReleased = true;
var armNow = new Arm(this, info.ArmNowTimeout); // 5 minute timeout
try
{
Lock();
bReleased = false;
// we must have at least 7V to arm
var inputs = new SLICEBaseInputReader(this);
if (inputs.InputMilliVolts < 7000.0 && !info.TestingMode)
{
bReleased = true; Release();
info.Error($"{SerialNumber}: Input is less than 7 volts");
return;
}
SetMaxEvents(info.MaxNumberEvents);
//performance improvement from WIAMan per CPB/LP
//this command is unnecessary since firmware will do the same work during the arm
if (RunTestVariables.EliminatePrepareForDataCollection)
{
//skip
}
else
{
try
{
var prepare = new PrepareForDataCollection(this);
prepare.SyncExecute();
}
catch (Exception ex)
{
APILogger.Log(ex);
try
{
//this was a way of adding more information in the case
//of a failed prepare for data collection, this is just
//a quick running command that was added to see if it
//executes?
var qsa2 = new QuerySystemAttribute(this);
qsa2.Key = AttributeTypes.SystemAttributes.AutoArmed;
qsa2.DeviceID = 0;
qsa2.SyncExecute();
}
catch (Exception ex2)
{
APILogger.Log(ex2);
}
}
}
ResetEventListPriorToArm();
// After we've reset the event list, our (the DAS) EventInfo property is now invalid
SetEventInfo(null);
// store a unique GUID for this event
SetEventGuid(info.EventGuid);
if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.AutoArm))
{
try
{
var ssa = new SetSystemAttribute(this);
ssa.SetValue(AttributeTypes.SystemAttributes.AutoDiagnosticLevel, AUTO_ARM_PERFORM_ALL_DIAG,
true);
ssa.SyncExecute();
}
catch (Exception ex)
{
APILogger.Log(ex);
}
}
armNow.SyncExecute();
bReleased = true;
Release();
info.Success();
}
catch (CanceledException)
{
if (!bReleased) { bReleased = true; Release(); }
info.Cancel();
}
catch (CommandException ex)
{
if (!bReleased) { bReleased = true; Release(); }
if (ex.Error == CommandErrorReason.ReceiveFailed || ex.Error == CommandErrorReason.SendFailed)
{
info.Error(string.Format(Resources.ArmFailtureDisconnect, SerialNumber));
}
else
{
info.Error(string.Format(Resources.ArmFailureCommandException, SerialNumber));
}
}
catch (Exception ex)
{
APILogger.Log(ex);
if (!bReleased) { bReleased = true; Release(); }
info.Error(armNow.ArmStatus.ToString());
}
finally { if (!bReleased) { Release(); } }
}
/// <summary>
/// 12638 DAS does not record data in recorder mode during calibration ~ 40% of time.
/// for non SLICE6 we reset the event list prior to arming
/// SLICE6 will only call reset event list prior to configuring
/// we are doing this to minimize the changes and impact of the fix for 12638
/// </summary>
protected virtual void ResetEventListPriorToArm()
{
var resetEvents = new ResetEventList(this);
resetEvents.SyncExecute();
}
protected virtual void AsyncReadyForArming(object asyncInfo)
{
var info = asyncInfo as ArmPacket;
info?.Success();
}
protected virtual void AsyncPrepareForArmNow(object asyncInfo)
{
if (!(asyncInfo is ArmPacket info)) { return; }
var bReleased = true;
try
{
Lock();
bReleased = false;
// We must have a valid voltage, and at least InputLowVoltage for this DAS type to arm.
// Previously, this check was hardcoded to > 7V, so that users would not experience a
// brown out during a test. However, with the introduction of PowerPRO, this would fail
// if it did not have external power (a valid configuration).
// FB 17687
var inputs = new SLICEBaseInputReader(this);
if (inputs.InputMilliVolts > MinimumValidInputVoltage && inputs.InputMilliVolts < MaximumValidInputVoltage &&
(inputs.InputMilliVolts < InputLowVoltage) && !info.TestingMode)
{
bReleased = true;
Release();
info.Error($"{SerialNumber}: Input is valid and less than {InputLowVoltage} volts");
return;
}
//31885 Implement RecordOnBootMode
//We must set AutoArmed to 1, and AutoArmRepeatEnable to 1
if (((ConfigData.Modules[0].RecordingMode == DFConstantsAndEnums.RecordingMode.a26_MultiRecordOnBootDataMode) ||
(ConfigData.Modules[0].RecordingMode == DFConstantsAndEnums.RecordingMode.a28_MultiRecordOnBootAndUartDataMode)) &&
IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.AutoArmRepeatEnable))
{
try
{
SetAutoArm();
SetRepeatEnable();
}
catch (Exception ex)
{
APILogger.Log("Failed to set AutoArm repeat enable/disable", ex);
}
}
SetMaxEvents(info.MaxNumberEvents);
//we always have to set post trigger
var setPostTrigger = new SetArmAttribute(this);
ulong samples = Convert.ToUInt64(Math.Abs(ConfigData.Modules[0].PostTriggerSeconds) *
ConfigData.Modules[0].SampleRateHz);
setPostTrigger.SetValue(AttributeTypes.ArmAndEventAttributes.PostTriggerSamplesRequested,
samples, true);
setPostTrigger.SyncExecute();
samples = Convert.ToUInt64(Math.Abs(ConfigData.Modules[0].PreTriggerSeconds) *
ConfigData.Modules[0].SampleRateHz);
if (ConfigData.Modules[0].RecordingMode == DFConstantsAndEnums.RecordingMode.CircularBuffer)
{
GetArmAttribute(AttributeTypes.ArmAndEventAttributes.ArmMode, out byte value);
var Value = (DFConstantsAndEnums.RecordingMode)value;
if (Value != DFConstantsAndEnums.RecordingMode.CircularBuffer)
{
SetArmMode(DFConstantsAndEnums.RecordingMode.CircularBuffer);
}
}
if (ConfigData.Modules[0].RecordingMode == DFConstantsAndEnums.RecordingMode.AutoCircularBufferMode)
{
GetArmAttribute(AttributeTypes.ArmAndEventAttributes.ArmMode, out byte value);
var Value = (DFConstantsAndEnums.RecordingMode)value;
if (Value != DFConstantsAndEnums.RecordingMode.AutoCircularBufferMode)
{
SetArmMode(DFConstantsAndEnums.RecordingMode.AutoCircularBufferMode);
}
}
//we should also set pre trigger for circular buffer
if (ConfigData.Modules[0].RecordingMode ==
DFConstantsAndEnums.RecordingMode.CircularBuffer ||
ConfigData.Modules[0].RecordingMode ==
DFConstantsAndEnums.RecordingMode.CircularBufferPlusUART ||
ConfigData.Modules[0].RecordingMode ==
DFConstantsAndEnums.RecordingMode.AutoCircularBufferMode ||
ConfigData.Modules[0].RecordingMode == DFConstantsAndEnums.RecordingMode.a14_NormalRecorderAndStreamSubSampleMode ||
ConfigData.Modules[0].RecordingMode == DFConstantsAndEnums.RecordingMode.RAMActive ||
ConfigData.Modules[0].RecordingMode == DFConstantsAndEnums.RecordingMode.MultipleEventRAMActive
)
{
setPostTrigger = new SetArmAttribute(this);
setPostTrigger.SetValue(AttributeTypes.ArmAndEventAttributes.PreTriggerSamplesRequested,
samples, true);
setPostTrigger.SyncExecute();
}
if (ConfigData.Modules[0].RecordingMode == DFConstantsAndEnums.RecordingMode.Scheduled ||
ConfigData.Modules[0].RecordingMode == DFConstantsAndEnums.RecordingMode.Interval)
{
//Make sure there's at least 2 minutes before the scheduled start
if (ConfigData.Modules[0].ScheduledStartTime < DateTime.UtcNow.AddMinutes(DFConstantsAndEnums.SCHEDULE_AHEAD_IN_MINUTES))
{
if (!bReleased)
{
bReleased = true;
Release();
}
var error = $"Scheduled start time must be at least 2 minutes in the future";
APILogger.Log(error);
info.Error(error);
return;
}
var intervalInSeconds = Convert.ToUInt32(0);
if (ConfigData.Modules[0].RecordingMode == DFConstantsAndEnums.RecordingMode.Interval)
{
//Convert to seconds from minutes
intervalInSeconds = Convert.ToUInt32(ConfigData.Modules[0].RecordingInterval * 60);
}
var setScheduleStart = new SetArmAttribute(this);
var startTimeInSeconds = DTS.Common.Utilities.Time.DateTimeToUnixTimestamp(ConfigData.Modules[0].ScheduledStartTime);
var scheduleUnixTimeIntervalInSec = new UInt32[2] { startTimeInSeconds, intervalInSeconds };
setScheduleStart.SetValue(AttributeTypes.ArmAndEventAttributes.ScheduleUnixTimeIntervalInSec, scheduleUnixTimeIntervalInSec, true);
setScheduleStart.SyncExecute();
}
//performance improvement per LP/CPB
//eliminate PrepareForDataCollection since same work is done by arm
if (RunTestVariables.EliminatePrepareForDataCollection)
{
//skip
}
else
{
try
{
var prepare = new PrepareForDataCollection(this);
prepare.SyncExecute();
}
catch (Exception ex)
{
APILogger.Log(ex);
try
{
//this was added to just add more debug information in the case
//of a failed preparefordatacollection call
//I believe the reason was primarily because it should be fast executing
var qsa2 = new QuerySystemAttribute(this);
qsa2.Key = AttributeTypes.SystemAttributes.AutoArmed;
qsa2.DeviceID = 0;
qsa2.SyncExecute();
}
catch (Exception ex2)
{
APILogger.Log(ex2);
}
}
}
bReleased = true;
Release();
info.Success();
}
catch (CanceledException)
{
if (!bReleased)
{
bReleased = true;
Release();
}
info.Cancel();
}
catch (CommandException ex)
{
if (!bReleased)
{
bReleased = true;
Release();
}
if (ex.Error == CommandErrorReason.ReceiveFailed || ex.Error == CommandErrorReason.SendFailed)
{
info.Error(string.Format(Resources.ArmFailtureDisconnect, SerialNumber));
}
else
{
info.Error(string.Format(Resources.ArmFailureCommandException, SerialNumber));
}
}
catch (Exception ex)
{
APILogger.Log(ex);
if (!bReleased)
{
bReleased = true;
Release();
}
info.Error(string.Format(Resources.ArmFailureGeneralException, SerialNumber));
}
finally
{
if (!bReleased)
{
Release();
}
}
}
protected void SetRTCBaseTime()
{
//14269 Implement SLICE PRO and Base+ RTC
//set the RTC, this will cause the EventStartTime event attribute to have
//meaningful values
try
{
if (SupportsTimeSynchronization)
{
SetBaseSystemTime sbst = new SetBaseSystemTime(this);
var dt = DateTime.Now;
Thread.Sleep(1000 - dt.Millisecond - 1);
sbst.SystemTime = DateTime.UtcNow; //18361 using UTC time over local time in case of later data processing
sbst.SyncExecute();
}
}
catch (Exception ex)
{
APILogger.Log("Failed to set system time: ", ex);
}
}
protected void RetrieveDASBootCount()
{
try
{
var query = new QuerySystemAttribute(this) { Key = AttributeTypes.SystemAttributes.DASBootCount };
query.SyncExecute();
}
catch (Exception ex)
{
APILogger.Log(ex);
}
}
protected void AsyncPreparedArm(object asyncInfo)
{
if (!(asyncInfo is ArmPacket info)) { return; }
var bReleased = true;
var armNow = new Arm(this, info.ArmNowTimeout); // 5 minute timeout
RetrieveDASBootCount();
try
{
Lock();
bReleased = false;
// we must have at least 7V to arm
var inputs = new SLICEBaseInputReader(this);
if (inputs.InputMilliVolts < 7000.0 && !info.TestingMode)
{
bReleased = true; Release();
info.Error($"{SerialNumber}: Input is less than 7 volts");
return;
}
SetMaxEvents(info.MaxNumberEvents);
ResetEventListPriorToArm();
// After we've reset the event list, our (the DAS) EventInfo property is now invalid
SetEventInfo(null);
// store a unique GUID for this event
SetEventGuid(info.EventGuid);
if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.AutoArm))
{
try
{
var ssa = new SetSystemAttribute(this);
ssa.SetValue(AttributeTypes.SystemAttributes.AutoDiagnosticLevel, AUTO_ARM_PERFORM_ALL_DIAG,
true);
ssa.SyncExecute();
}
catch (Exception ex)
{
APILogger.Log(ex);
}
}
SetRTCBaseTime();
armNow.SyncExecute();
bReleased = true; Release();
info.Success();
}
catch (CanceledException)
{
if (!bReleased) { bReleased = true; Release(); }
info.Cancel();
}
catch (CommandException ex)
{
if (!bReleased) { bReleased = true; Release(); }
if (ex.Error == CommandErrorReason.ReceiveFailed || ex.Error == CommandErrorReason.SendFailed)
{
info.Error(string.Format(Resources.ArmFailtureDisconnect, SerialNumber));
}
else
{
info.Error(string.Format(Resources.ArmFailureCommandException, SerialNumber));
}
}
catch (Exception ex)
{
if (IsSquibFault(ex))
{
NotifySquibFault();
}
APILogger.Log(ex);
if (!bReleased) { bReleased = true; Release(); }
info.Error(armNow.ArmStatus.ToString());
}
finally { if (!bReleased) { Release(); } }
}
/// <summary>
/// returns true if the error contains a squib resistance fault
/// </summary>
/// <param name="ex"></param>
/// <returns></returns>
private bool IsSquibFault(Exception ex)
{
if (null == ex) { return false; }
if (!string.IsNullOrEmpty(ex.Message))
{
return ex.Message.Contains(DFConstantsAndEnums.CommandStatus.StatusArmSquibResistanceFault.ToString());
}
return false;
}
/// <summary>
/// notifies consumer of any squib faults during arming process
/// </summary>
private void NotifySquibFault()
{
try
{
var cmd = new MeasureSquibChannelResistances(this);
cmd.SyncExecute();
var failingSquibs = new List<string>();
for (int i = 0; i < 4 && i < cmd.MeasuredResistanceOhms.Length; i++)
{
var dResistance = cmd.MeasuredResistanceOhms[i];
var squib = GetSquibChannel(i);
if (null == squib) { continue; }
if (squib.ConfigurationMode != DFConstantsAndEnums.ConfigMode.Normal) { continue; }
var dRoundUp = Math.Ceiling(dResistance * 10D) / 10D;
var dRoundDown = Math.Floor(dResistance * 10D) / 10D;
if (dRoundUp > squib.SquibToleranceHigh)
{
failingSquibs.Add(string.Format(Strings.SquibFailedToleranceHigh, i + 1, dRoundUp, squib.SquibToleranceHigh));
}
else if (dRoundDown < squib.SquibToleranceLow)
{
failingSquibs.Add(string.Format(Strings.SquibFailedToleranceLow, i + 1, dRoundDown, squib.SquibToleranceLow));
}
}
var sb = new StringBuilder();
sb.AppendLine(string.Format(Strings.SquibToleranceFailure, SerialNumber));
foreach (var line in failingSquibs) { sb.AppendLine(line); }
APILogger.RaiseError(sb.ToString());
}
catch (Exception ex)
{
APILogger.Log("Failed to notify of squib fault", ex);
}
}
/// <summary>
/// gets the squib record by index of that squib among the four squibs
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
private OutputSquibChannel GetSquibChannel(int index)
{
if (null == ConfigData || null == ConfigData.Modules) { return null; }
if (!ConfigData.Modules.Any()) { return null; }
//each squib has two records, a current channel and a voltage/init channel
//just return the first of the two channels for the squib in question
var channel = ConfigData.Modules[0].Channels[index * 2];
return channel as OutputSquibChannel;
}
/// <summary>
/// the UDP settings to broadcast auto arm status to on auto arm boot
/// 17583 Monitor Test UI
/// </summary>
public string AutoArmUDPSetting { get; set; } = "239.1.2.3:8504";
void IArmActions.AutoArmNow(ServiceCallback callback, object userData, Guid eventGuid, int armNowTimeout,
bool testingMode, uint diagnosticsDelayMs, int MaxEventCount, bool repeatEnable, bool preserveDiagnostics)
{
var info = new AutoArmPacket(callback, userData, eventGuid, armNowTimeout, testingMode, diagnosticsDelayMs, MaxEventCount, repeatEnable, preserveDiagnostics);
LaunchAsyncWorker("Slice.AutoArmNow", AsyncAutoArmNow, info);
}
protected enum diaglevels
{
AUTODIAG_REMOVE_OFFSET,
AUTODIAG_VOLTAGE_INSERTION,
AUTODIAG_SHUNT_TEST,
AUTODIAG_EXCITATION_CHECK,
AUTODIAG_NOISE_MEASUREMENT,
AUTODIAG_INPUTV_BACKUPV,
AUTODIAG_LT_CHECK,
AUTODIAG_SATURATION,
AUTODIAG_CALSIG_CHECK
}
//Always do all diagnostic options in older 00M3 based firmware
protected const UInt16 AUTO_ARM_PERFORM_ALL_DIAG = 1;
protected virtual void SetAutoArmUDPSettings()
{
}
protected virtual void SetAutoArmRecordDelay()
{
try
{
var ssa = new SetSystemAttributeSLICE2(this);
ssa.SetValue(AttributeTypes.SystemAttributesSLICE2.StartRecDelayInSecond, Convert.ToUInt16(RunTestVariables.AutoArmRecordDelaySeconds), true);
ssa.SyncExecute();
}
catch (Exception ex)
{
APILogger.Log(ex);
}
}
private void SetRepeatEnable()
{
if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.AutoArmRepeatEnable))
{
var ssa = new SetSystemAttributeSLICE2(this);
ssa.DeviceID = 0;
ssa.SetValue(AttributeTypes.SystemAttributesSLICE2.AutoArmRepeatEnable, (byte)0x01, true);
ssa.SyncExecute();
}
}
private void SetAutoArm()
{
if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.AutoArm))
{
var ssa = new SetSystemAttribute(this);
ssa.DeviceID = 0;
ssa.SetValue(AttributeTypes.SystemAttributes.AutoArmed, (byte)0x01, true);
ssa.SyncExecute();
}
}
private void AsyncAutoArmNow(object asyncInfo)
{
if (!(asyncInfo is AutoArmPacket info)) { return; }
SetRTCBaseTime();
try
{
if (DFConstantsAndEnums.UseUDPForAutoArmATDMonitor)
{
var infoFile = new Common.Classes.Hardware.DASMonitorInfo(this, IsoViewModeStatic.ViewMode);
infoFile.WriteToFile(Path.Combine(DTS.Common.Constants.DAS_CONFIGS, $"{SerialNumber}_MonitorInfo.txt"));
}
if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.AutoArmUDPSetting))
{
try
{
SetAutoArmUDPSettings();
}
catch (Exception ex)
{
APILogger.Log(ex);
}
}
if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.AutoArmRecordDelay))
{
SetAutoArmRecordDelay();
}
// we must have at least 7V to arm
var inputs = new SLICEBaseInputReader(this);
if (inputs.InputMilliVolts < 7000.0 && !info.TestingMode)
{
info.Error($"{SerialNumber}: Input is less than 7 volts");
return;
}
ResetEventListPriorToArm();
// After we've reset the event list, our (the DAS) EventInfo property is now invalid
SetEventInfo(null);
// store a unique GUID for this event
SetEventGuid(info.EventGuid);
//this is protocol 10
SetAutoArm();
try
{
var ssa = new SetSystemAttribute(this);
ssa.DeviceID = 0;
ssa.SetValue(AttributeTypes.SystemAttributes.AutoDiagnosticLevel, AUTO_ARM_PERFORM_ALL_DIAG, true);
ssa.SyncExecute();
}
catch (Exception ex)
{
APILogger.Log("Failed to set AutoArm failure checking", ex);
}
try
{
if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.AutoArmRepeatEnable))
{
// FB15507: Allow user to elect to repeat auto-arm/streaming after power cycle(s)
var ssa = new SetSystemAttributeSLICE2(this);
ssa.DeviceID = 0;
ssa.SetValue(AttributeTypes.SystemAttributesSLICE2.AutoArmRepeatEnable, info.RepeatEnable ? (byte)0x01 : (byte)0x00, true);
ssa.SyncExecute();
}
}
catch (Exception ex)
{
APILogger.Log("Failed to set AutoArm repeat enable/disable", ex);
}
try
{
if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.AutoArmDiagnosticDelay))
{
var saa = new SetArmAttribute(this);
saa.LogCommands = true;
saa.SetValue(AttributeTypes.ArmAndEventAttributes.AutoDiagnosticStabilizationTimeoutMs, info.DiagnosticsDelayMs, true);
saa.SyncExecute();
}
}
catch (Exception)
{
APILogger.Log("Failed to set diagnostics delay time");
}
try
{
if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.StackChannelAutoArmDiagLevel))
{
var scadl = new List<ushort>();
foreach (var da in ChannelDiagnostics)
{
// FB15507: Allow user to elect to preserve Run Test diagnostics through auto-arm/streaming
scadl.Add(
(ushort)
(
(Convert.ToUInt16(!info.PreserveDiagnostics && da.RemoveOffset) << (ushort)diaglevels.AUTODIAG_REMOVE_OFFSET) +
(Convert.ToUInt16(!info.PreserveDiagnostics && da.PerformShuntCheck) << (ushort)diaglevels.AUTODIAG_SHUNT_TEST) +
(Convert.ToUInt16(!info.PreserveDiagnostics && da.MeasureExcitation) << (ushort)diaglevels.AUTODIAG_EXCITATION_CHECK) +
(Convert.ToUInt16(!info.PreserveDiagnostics && da.MeasureNoise) << (ushort)diaglevels.AUTODIAG_NOISE_MEASUREMENT) +
(Convert.ToUInt16(false) << (ushort)diaglevels.AUTODIAG_VOLTAGE_INSERTION) +
(Convert.ToUInt16(!info.PreserveDiagnostics && da.PerformCalSignalCheck) << (ushort)diaglevels.AUTODIAG_CALSIG_CHECK)
)
);
}
var saa = new SetArmAttribute(this);
saa.LogCommands = true;
saa.SetValue(AttributeTypes.ArmAndEventAttributes.StackChannelAutoArmDiagnosticLevel, scadl.ToArray(), true);
saa.SyncExecute();
}
}
catch (Exception)
{
APILogger.Log("Failed to set diagnostics delay time");
}
SetMaxEvents(info.MaxNumberEvents);
AutoArmed = true;
info.Success();
}
catch (CanceledException)
{
info.Cancel();
}
catch (CommandException ex)
{
if (ex.Error == CommandErrorReason.ReceiveFailed || ex.Error == CommandErrorReason.SendFailed)
{
info.Error(string.Format(Resources.ArmFailtureDisconnect, SerialNumber));
}
else
{
info.Error(string.Format(Resources.ArmFailureCommandException, SerialNumber));
}
}
catch (Exception)
{
info.Error(string.Format(Resources.ArmFailureGeneralException, SerialNumber));
}
}
protected virtual void SetEventGuid(Guid guid)
{
var set = new SetArmAttribute(this);
set.SetValue(AttributeTypes.ArmAndEventAttributes.EventGuid, guid.ToString(), true);
set.SyncExecute();
}
protected virtual Guid GetEventGuid(int eventNum)
{
var eventGuid = new QueryEventAttribute(this);
eventGuid.EventNumber = (ushort)eventNum;
eventGuid.Key = AttributeTypes.ArmAndEventAttributes.EventGuid;
eventGuid.SyncExecute();
return new Guid(eventGuid.Value as string);
}
#endregion
#region EnableFaultChecking
void IArmActions.EnableFaultChecking(ServiceCallback callback, object userData)
{
var info = new SliceServiceAsyncInfo(callback, userData);
LaunchAsyncWorker("Slice.EnableFaultChecking", AsyncEnableFaultChecking, info);
}
private void AsyncEnableFaultChecking(object asyncInfo)
{
var info = asyncInfo as SliceServiceAsyncInfo;
try
{
var enableFaultChecking = new EnableFaultChecking(this);
enableFaultChecking.SyncExecute();
info?.Success();
}
catch (CanceledException)
{
info?.Cancel();
}
catch (Exception ex)
{
info?.Error(ex.Message, ex);
}
}
#endregion
#region Disarm
void IArmActions.DisAutoArm(ServiceCallback callback, object userData)
{
var info = new SliceServiceAsyncInfo(callback, userData);
LaunchAsyncWorker("Slice.DisAutoArm", AsyncDisAutoArm, info);
}
/// <summary>
/// abstracted this functionality to it's own method - unsets the auto arm flag
/// </summary>
protected void DisAutoArm()
{
var ssa = new SetSystemAttribute(this);
ssa.DeviceID = 0;
ssa.SetValue(AttributeTypes.SystemAttributes.AutoArmed, (byte)0x00, true);
ssa.SyncExecute();
AutoArmed = false;
}
private void AsyncDisAutoArm(object asyncInfo)
{
var info = asyncInfo as SliceServiceAsyncInfo;
try
{
DisAutoArm();
info?.Success();
}
catch (CanceledException)
{
info?.Cancel();
}
catch (Exception ex)
{
info?.Error(ex.Message, ex);
}
}
void IArmActions.Disarm(ServiceCallback callback, object userData)
{
var info = new SliceServiceAsyncInfo(callback, userData);
LaunchAsyncWorker("Slice.Disarm", AsyncDisarm, info);
}
private void AsyncDisarm(object asyncInfo)
{
var info = asyncInfo as SliceServiceAsyncInfo;
try
{
var disarmRequest = new Disarm(this, 30000);
disarmRequest.SyncExecute();
info?.Success();
}
catch (CanceledException)
{
info?.Cancel();
}
catch (Exception ex)
{
info?.Error(ex.Message, ex);
}
}
#endregion
#region GetArmStatus
protected class GetArmStatusPacket
{
public SliceServiceAsyncInfo Info { get; set; }
public uint InputVoltageCutoff { get; set; }
public GetArmStatusPacket(SliceServiceAsyncInfo info, uint inputVoltageCutoff)
{
Info = info;
InputVoltageCutoff = inputVoltageCutoff;
}
}
void IArmActions.GetArmStatus(ServiceCallback callback, object userData, uint inputVoltageCutoff, int maxTimeout)
{
var info = new SliceServiceAsyncInfo(callback, userData) { MaxTimeout = maxTimeout };
var packet = new GetArmStatusPacket(info, inputVoltageCutoff);
LaunchAsyncWorker("Slice.GetArmStatus", AsyncGetArmStatus, packet);
}
void IArmActions.GetAutoArmStatus(ServiceCallback callback, object userData)
{
var info = new SliceServiceAsyncInfo(callback, userData);
LaunchAsyncWorker("Slice.GetAutoArmStatus", AsyncGetAutoArmStatus, info);
}
void IArmActions.GetExtendedFaultIds(ServiceCallback callback, object userData)
{
var info = new SliceServiceAsyncInfo(callback, userData);
LaunchAsyncWorker("Slice.GetExtendedFaultIds", new WaitCallback(SLICEAsyncGetExtendedFaultIds), info);
}
private void SLICEAsyncGetExtendedFaultIds(object asyncInfo)
{
var info = (SliceServiceAsyncInfo)asyncInfo;
try
{
var faults = GetExtendedFaultFlags();
var bytes = new List<byte>();
foreach (var fault in faults)
{
bytes.AddRange(BitConverter.GetBytes((int)fault));
}
info.NewData(bytes.ToArray());
}
catch (Exception ex)
{
APILogger.Log(ex);
}
info.Success();
}
protected ushort GetEventCount()
{
try
{
var eventCountQuery = new QuerySystemAttribute(this);
eventCountQuery.Key = AttributeTypes.SystemAttributes.TotalEventsStored;
eventCountQuery.SyncExecute();
return (ushort)eventCountQuery.Value;
}
catch (Exception ex)
{
APILogger.Log(ex);
}
return 0;
}
protected volatile int straightFailures = 0;
protected const int PERMITTED_FAILURES = 30;
protected virtual void AsyncGetArmStatus(object asyncInfo)
{
if (!(asyncInfo is GetArmStatusPacket packet)) { return; }
var info = packet.Info;
var bLocked = false;
try
{
Lock();
bLocked = true;
var status = new QueryArmAndTriggerStatus(this);
if (null != info.MaxTimeout)
{
status.IO_Timeout = (int)info.MaxTimeout;
}
status.SyncExecute();
var armStatus = new ArmStatus();
armStatus.IsRearming = false;
armStatus.IsArmed = status.IsArmed;
armStatus.IsInRealtime = status.IsInRealtime; // this one is accurate
// if we're not armed, the other values are useless
if (status.IsArmed)
{
armStatus.IsFaulted = status.IsFaulted;
armStatus.IsRecording = status.IsRecording;
armStatus.IsTriggered = status.IsTriggered;
armStatus.TotalSamples = status.TotalSamples;
armStatus.CurrentSample = status.CurrentSample;
armStatus.SampleRate = status.EventSampleRate;
armStatus.TimeRemainingSeconds = (status.TotalSamples - Math.Min(status.TotalSamples, status.CurrentSample)) / (double)status.EventSampleRate;
armStatus.EventNumber = status.EventNumber;
if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.QueryArmAndTriggerStatus_TimeLeftInArm))
{
try
{
//Remaining samples divided by sample rate
armStatus.TimeLeftInArm = (status.MaxSampleAvailable - status.ActualCurrentSample) / status.EventSampleRate;
}
catch (Exception ex)
{
APILogger.Log(ex);
}
}
}
else
{
//when QuerySwitchImmediate works
if (this is WinUSBTsrAir || this is EthernetTsrAir)
{
armStatus.IsRecording = status.IsRecording;
armStatus.IsTriggered = status.IsTriggered;
if (null != DASArmStatus && !DASArmStatus.IsArmed && !status.IsArmed)
{
armStatus.MaxEventsPossible = DASArmStatus.MaxEventsPossible;
}
else { armStatus.MaxEventsPossible = GetMaxEvents(); }
}
else
{
armStatus.IsRecording = GetRecordingFlag();
armStatus.IsTriggered = GetTriggeredFlag();
}
armStatus.TotalSamples = 0;
armStatus.CurrentSample = 0;
armStatus.SampleRate = 0;
armStatus.TimeRemainingSeconds = 0;
armStatus.RecordingMode = 0;
armStatus.EventNumber = null;
}
if (status.IsArmed || status.IsInRealtime)
{
armStatus.RecordingMode = Convert.ToInt32(status.ArmMode);
}
if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.QueryArmAndTriggerStatus_VoltageReadings))
{
armStatus.InputMilliVolts = status.InputVoltage * 1000;
if (status.BackupVoltage >= 4) { armStatus.BatteryMilliVolts = status.BackupVoltage * 1000; }
}
if (status.IsDiagnostics || status.IsFlashClear || status.IsArming)
{
armStatus.IsArmed = true;
armStatus.IsRearming = true;
}
// update input voltage readings (for optional display)
// based on test with 100kHz+ recording, it's not safe to get input voltage check during because we could accidently
// stomp on the recording portion.
if (!IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.QueryArmAndTriggerStatus_VoltageReadings) &&
!armStatus.IsRecording &&
armStatus.SampleRate < packet.InputVoltageCutoff)
{
var inputs = new SLICEBaseInputReader(this);
armStatus.InputMilliVolts = inputs.InputMilliVolts;
if (DASInfo != null && DASInfo.HasBattery)
{
armStatus.BatteryMilliVolts = inputs.DirectBackupMilliVolts;
}
else
{
armStatus.BatteryMilliVolts = null;
}
}
if (armStatus.IsArmed && null != armStatus.BatteryMilliVolts && armStatus.BatteryMilliVolts < 200)
{
status = new QueryArmAndTriggerStatus(this, 8000);
status.SyncExecute();
armStatus.IsArmed = status.IsArmed;
}
SetBaseInputs(status);
try
{
if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.MeasurePowerProAllDiagnosticChannel))
{
var mppadc = new MeasurePowerProAllDiagnosticChannel(this);
mppadc.SyncExecute(); // Just Log it for now
}
}
catch (Exception ex) { APILogger.Log("Failed to Measure All Diagnostic Channel", ex); }
var faults = new List<string>();
foreach (var flag in status.CurrentFaultFlags)
{
faults.Add(flag.ToString());
}
foreach (var flag in status.ExtendedFaultFlags)
{
faults.Add(flag.ToString());
}
if (armStatus.IsArmed) { ExcitationStatus = DFConstantsAndEnums.ExcitationStatus.On; }
else
{
if (null != DASArmStatus && DASArmStatus.EventNumber > 0 && !armStatus.IsArmed && !DASArmStatus.IsArmed)
{
//we make an assumption here that if we aren't and weren't armed, then it didn't change
armStatus.EventNumber = DASArmStatus.EventNumber;
}
else { armStatus.EventNumber = GetEventCount(); }
}
armStatus.FaultMessage = string.Join(", ", faults);
SetDASArmStatus(armStatus, true);
bLocked = false; Release();
info.Success();
}
catch (CanceledException)
{
if (bLocked) { bLocked = false; Release(); }
info.Cancel();
}
catch (CommandException ce)
{
if (bLocked) { Release(); bLocked = false; }
straightFailures++;
if (straightFailures > PERMITTED_FAILURES)
{
APILogger.Log("GetArmStatus error - has failed ", straightFailures, " times, giving up", ce);
info.Error(ce.Message, ce);
}
else
{
var status = new ArmStatus();
status.IsRecording = false;
status.IsArmed = true;
status.IsRearming = true;
status.RecordingMode = 0;
status.InputMilliVolts = null;
status.EventNumber = null;
status.CurrentSample = 0;
SetDASArmStatus(status, true);
if (ce.Error == CommandErrorReason.ReceiveFailed && null != info.MaxTimeout) { info.Error(ce.Error.ToString()); }
else { info.Success(); }
APILogger.Log("GetArmStatus error", ce);
}
}
catch (Exception ex)
{
if (bLocked) { bLocked = false; Release(); }
info.Error(ex.Message, ex);
}
finally { if (bLocked) { Release(); } }
}
protected void GetAutoArmStatus()
{
//performance improvement, eliminate unnecessary commands when possible
if (RunTestVariables.InRunTest && !RunTestVariables.CheckForAutoArmedInRunTest)
{
AutoArmed = false;
return;
}
//15949 S6A when streaming does a whole bunch of unnecessary queries
//avoid getting autoam status if streaming, it will fail
if (GetIsStreaming())
{
AutoArmed = false;
AutoArmStatus = DFConstantsAndEnums.CommandStatus.StatusNoError;
return;
}
if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.AutoArm))
{
var qsa2 = new QuerySystemAttribute(this);
qsa2.Key = AttributeTypes.SystemAttributes.AutoArmed;
qsa2.DeviceID = 0;
qsa2.SyncExecute();
if ((byte)qsa2.Value == 0x02)
{
var qsa = new QuerySystemAttribute(this);
qsa.DeviceID = 0;
qsa.Key = AttributeTypes.SystemAttributes.AutoArmError;
qsa.SyncExecute();
var iValue = (ushort)qsa.Value;
AutoArmStatus = (DFConstantsAndEnums.CommandStatus)iValue;
SetAutoArm();
AutoArmed = false;
}
else if ((byte)qsa2.Value == 0x01)
{
AutoArmed = true;
AutoArmStatus = DFConstantsAndEnums.CommandStatus.StatusNoError;
}
else
{
AutoArmed = false;
AutoArmStatus = DFConstantsAndEnums.CommandStatus.StatusNoError;
}
}
else { AutoArmStatus = DFConstantsAndEnums.CommandStatus.StatusUnimplemented; }
}
private void AsyncGetAutoArmStatus(object asyncInfo)
{
var info = asyncInfo as SliceServiceAsyncInfo;
try
{
GetAutoArmStatus();
info?.Success();
}
catch (Exception ex)
{
info?.Error("Problem querying auto arm status", ex);
}
}
protected bool GetFaultedFlag()
{
var qs = new QuerySwitchImmediate(this);
qs.Switch = (byte)Switches.BaseSwitches.StatusMonitor;
qs.SwitchText = Switches.BaseSwitches.StatusMonitor.ToString();
qs.SyncExecute();
return qs.Setting == 1;
}
protected bool GetRecordingFlag()
{
var qs = new QuerySwitchImmediate(this);
qs.Switch = (byte)Switches.BaseSwitches.StartRecordOut;
qs.SwitchText = Switches.BaseSwitches.StartRecordOut.ToString();
qs.SyncExecute();
return qs.Setting == 1;
}
protected bool GetTriggeredFlag()
{
var qs = new QuerySwitchImmediate(this);
qs.Switch = (byte)Switches.BaseSwitches.TriggerOut;
qs.SwitchText = Switches.BaseSwitches.TriggerOut.ToString();
qs.SyncExecute();
return qs.Setting == 1;
}
#endregion
#region EnterLowPowerMode
void IArmActions.EnterLowPowerMode(ServiceCallback callback, object userData)
{
var info = new SliceServiceAsyncInfo(callback, userData);
LaunchAsyncWorker("Slice.EnterLowPowerMode", AsyncEnterLowPowerMode, info);
}
protected virtual void AsyncEnterLowPowerMode(object asyncInfo)
{
var info = asyncInfo as SliceServiceAsyncInfo;
DiagnosticsHasBeenRun = false;
try
{
//clear any diagnostic results we have, they will be invalid after going to low power
//16164 User can proceed past diag. without running on all channels
ClearChannelDiagnosticsResults();
if (DASInfo != null && DASInfo.Modules != null)
{
// DeviceID is the module index + 1
for (int i = 0; i < DASInfo.Modules.Length; i++)
{
try
{
DisablePowerSupply(DASInfo.Modules[i].TypeOfModule, (byte)(i + 1));
}
catch (Exception ex)
{
APILogger.Log("problem disabling power supply", ex);
}
}
}
ExcitationStatus = DFConstantsAndEnums.ExcitationStatus.Off;
info?.Success();
}
catch (CanceledException)
{
info?.Cancel();
}
catch (Exception ex)
{
info?.Error(ex.Message, ex);
}
}
/// <summary>
/// sets the SLICEMaxEnable attribute for the das in question
/// does not use services, so does not protect from other commands being run at same time
/// </summary>
protected void SetMaxModuleCount(int count, bool disablePowerSupply)
{
try
{
//turn off power supplies first
if (DASInfo != null && DASInfo.Modules != null && DASInfo.Modules.Length > 0 && disablePowerSupply)
{
DisablePowerSupply(DASInfo.Modules[0].TypeOfModule, 1);
}
var ssa = new SetSystemAttributeSLICE2(this);
ssa.SetValue(AttributeTypes.SystemAttributesSLICE2.MaxSLICEEnable, (byte)count, true);
ssa.SyncExecute();
}
catch (Exception ex)
{
APILogger.Log("Failed to set max module count", SerialNumber, ex);
}
}
/// <summary>
/// gets the physical max number of modules.
/// only retrieves from DAS if passed in value is invalid, otherwise returns the value passed in
/// does not use services, so does not protect from other commands being run at same time
/// </summary>
/// <returns></returns>
protected int GetMaxModuleCount(int maxModuleCount)
{
if (-1 == maxModuleCount)
{
try
{
var q = new QuerySystemAttributeSLICE2(this);
q.Key = AttributeTypes.SystemAttributesSLICE2.SLICECountOnStack;
q.SyncExecute();
return Convert.ToInt32(q.Value);
}
catch (Exception ex) { APILogger.Log("Failed to get max module count", SerialNumber, ex); }
return DASInfo.Modules.Length;
}
else { return maxModuleCount; }
}
protected void DisablePowerSupply(DFConstantsAndEnums.ModuleType moduleType, byte DeviceID)
{
var setSwitch = new SetSwitchImmediate(this, 10000);
switch (moduleType)
{
case DFConstantsAndEnums.ModuleType.SLICEIEPE:
setSwitch.Switch = (byte)Switches.EIPESwitches.IS_EnableAnalogPowerSupply;
setSwitch.SwitchText = Switches.EIPESwitches.IS_EnableAnalogPowerSupply.ToString();
break;
default:
setSwitch.Switch = (byte)Switches.BridgeSwitches.EnableAnalogPowerSupply;
setSwitch.SwitchText = Switches.BridgeSwitches.EnableAnalogPowerSupply.ToString();
break;
}
setSwitch.Setting = 0;
setSwitch.DeviceGroup = 0;
setSwitch.DeviceID = DeviceID;
setSwitch.SyncExecute();
}
#endregion
#region Start & Trigger
void IArmActions.StartRecord(ServiceCallback callback, object userData)
{
var info = new SliceServiceAsyncInfo(callback, userData);
LaunchAsyncWorker("Slice.StartRecord", AsyncStartRecord, info);
}
private void AsyncStartRecord(object asyncInfo)
{
var info = asyncInfo as SliceServiceAsyncInfo;
try
{
var setSwitch = new SetSwitchImmediate(this);
setSwitch.Switch = (byte)Switches.BaseSwitches.StartRecordOut;
setSwitch.SwitchText = Switches.BaseSwitches.StartRecordOut.ToString();
setSwitch.Setting = 1;
setSwitch.DeviceGroup = 0;
setSwitch.DeviceID = 0;
setSwitch.SyncExecute();
info?.Success();
}
catch (CanceledException)
{
info?.Cancel();
}
catch (Exception ex)
{
info?.Error(ex.Message, ex);
}
}
void IArmActions.Trigger(ServiceCallback callback, object userData)
{
var info = new SliceServiceAsyncInfo(callback, userData);
LaunchAsyncWorker("Slice.Trigger", AsyncTrigger, info);
}
protected virtual void AsyncTrigger(object asyncInfo)
{
var info = asyncInfo as SliceServiceAsyncInfo;
try
{
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();
info?.Success();
}
catch (CanceledException)
{
info?.Cancel();
}
catch (Exception ex)
{
info?.Error(ex.Message, ex);
}
}
#endregion
#region Voltage Check
public void PerformVoltageCheck(ServiceCallback callback, object userData)
{
var info = new SliceServiceAsyncInfo(callback, userData);
LaunchAsyncWorker("Slice.PerformVoltageCheck", AsyncPerformVoltageCheck, info);
}
private void AsyncPerformVoltageCheck(object o)
{
var info = o as SliceServiceAsyncInfo;
try
{
if (GetIsStreaming())
{
//15949 S6A when streaming does a whole bunch of unnecessary queries
//avoid getting inputs, it will fail
}
else { GetBaseInputs(); }
info?.Success();
}
catch (Exception ex)
{
APILogger.Log(ex);
info?.Error(ex.Message);
}
}
public void PerformVoltageCheckTAOnly(ServiceCallback callback, object userData)
{
var info = new SliceServiceAsyncInfo(callback, userData);
LaunchAsyncWorker("Slice.PerformVoltageCheckTAOnly", AsyncPerformVoltageCheckTAOnly, info);
}
private void AsyncPerformVoltageCheckTAOnly(object o)
{
var info = o as SliceServiceAsyncInfo;
var status = new QueryArmAndTriggerStatus(this);
status.SyncExecute();
SetBaseInputs(status);
info?.Success();
}
#endregion Voltage Check
}
}