//#define _8868_MEASUREADCAT0MV_OLDSCHOOL
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DTS.DASLib.Command.SLICE;
using DTS.Common.Utilities;
using DTS.Common.DAS.Concepts;
using DTS.DASLib.Command;
using DTS.Common;
using DTS.Common.DASResource;
using DTS.Common.Enums;
using DTS.Common.ICommunication;
using DTS.Common.Utilities.Logging;
using DTS.Common.WINUSBConnection;
using DTS.DASLib.Command.SLICE.DownloadCommands;
using DTS.DASLib.Command.SLICE.RealtimeCommands;
using DTS.DASLib.Utility;
using DTS.Common.Classes.Sensors;
using System.IO;
using System.Xml;
using DTS.Common.Enums.Sensors;
using DTS.Common.Classes.Connection;
using DTS.Common.Interface.Connection;
using DTS.Common.Interface.DASFactory.Diagnostics;
using DTS.Common.Interface.DASFactory.Download;
using DTS.Common.Enums.DASFactory;
using DTS.Common.Interface.DASFactory;
using DTS.Common.Enums.Hardware;
using DTS.Common.Interface.DASFactory.Config;
using Prism.Ioc;
using DTS.Common.Events.DASFactory;
using DTS.Common.Constant.DASSpecific;
using DTS.Common.Utilities.LTLogging;
using DTS.DASLib.Service.FirmwareUtility;
using Prism.Events;
using DTS.Common.SharedResource.Strings;
namespace DTS.DASLib.Service
{
///
///
/// slice 2 does it's downloads a little differently
/// namely, it downloads pages at a time regardless of where the break in sample alignment is
/// it also requires that we make requests for samples based not on the sample number, but
/// the byte where the sample are.
/// this is partially because of ECC block size and partially for more efficient transfer
///
public class WhatToDownloadSlice2 : DownloadRequest
{
///
/// this will hold the number of channels in the download, this is needed
/// to calculate the sample alignment and request/response sample numbers.
/// the SLICE2 firmware uses samples counts irrespective of channels, so they must be realigned post
/// download
///
private readonly ulong _numberOfChannels = 0;
public ulong RequestedStartSport { get; }
public WhatToDownloadSlice2(IDownloadRequest dr, int numberOfChannels, int pageSize)
{
if (dr is WhatToDownloadSlice2 dr2)
{
_numberOfChannels = (ulong)numberOfChannels;
DASChannelNumber = dr.DASChannelNumber;
base.EndSample = dr.EndSample;
EventNumber = dr.EventNumber;
SamplesToSkip = dr.SamplesToSkip;
base.StartSample = dr.StartSample;
RequestedStartSport = dr2.RequestedStartSport;
StartRecordTimestampSec = dr.StartRecordTimestampSec;
TriggerTimestampSec = dr.TriggerTimestampSec;
}
else
{
_numberOfChannels = (ulong)numberOfChannels;
if (null == dr)
{
return;
}
DASChannelNumber = dr.DASChannelNumber;
//we add one to the end sample for consistency with SLICE1 based code? - basically
//we need one more sample then the firmware is sending right now ...
base.EndSample = (dr.EndSample + 1) * _numberOfChannels;
EventNumber = dr.EventNumber;
SamplesToSkip = dr.SamplesToSkip;
//find the start of the page the start sample is on
var nearestdiv = Math.Truncate(dr.StartSample * (double)numberOfChannels / pageSize);
base.StartSample = Convert.ToUInt64(nearestdiv * pageSize);
RequestedStartSport = dr.StartSample * (ulong)numberOfChannels;
StartRecordTimestampSec = dr.StartRecordTimestampSec;
TriggerTimestampSec = dr.TriggerTimestampSec;
}
}
///
///
/// if EndSample gets set by a generic service, it is not expecting sample counts to be irrespective of channels
/// so we convert it to be irrespective here
/// however, we want to return what we consider the endsample so looping code doesn't give up until we really are done
/// with all the samples we want
///
public override ulong EndSample
{
get => base.EndSample;
set => base.EndSample = (value + 1) * _numberOfChannels;
}
///
///
/// adjusting the start sample is a little more complicated than adjusting the endsample, we need to start
/// downloading from the start of the page that the start sample we want is on
///
public override ulong StartSample
{
get => base.StartSample;
set => base.StartSample = value;
}
}
public class SLICE2_Base : Slice, IConfigurationActions, IVoltageInsertionEnabled, ITimeActions where T : IConnection, new()
{
///
/// indicates that voltage insertion was detected as being on
/// http://manuscript.dts.local/f/cases/34284/Warn-when-VoltageInsertion-switches-are-set
///
public bool VoltageInsertionEnabled { get; protected set; }
public override void SetIsStreamingSupported(bool supported = false)
{
IsStreamingSupported = false;
}
public override void ReadFirstUseDate()
{
try
{
if (RunTestVariables.InRunTest && !RunTestVariables.CheckHardwareFirstUseDateInRunTest)
{
//skip
IsFirstUseDateSupported = false;
FirstUseDate = null;
}
else
{
var attr = GetUserAttribute(AttributeTypes.SliceUserAttributes.FirstUseDate);
if (string.IsNullOrWhiteSpace(attr))
{
IsFirstUseDateSupported = false;
FirstUseDate = null;
return;
}
if (DateTime.TryParse(attr, System.Globalization.CultureInfo.InvariantCulture,
System.Globalization.DateTimeStyles.None, out var dt))
{
IsFirstUseDateSupported = true;
if (dt.Year == DFConstantsAndEnums.FIRST_USE_DATE_NOT_SET.Year)
{
FirstUseDate = null;
}
else
{
FirstUseDate = dt;
}
return;
}
IsFirstUseDateSupported = true;
FirstUseDate = null;
}
}
catch (Exception ex)
{
APILogger.Log(ex);
IsFirstUseDateSupported = false;
FirstUseDate = null;
}
}
protected override void SetFirstUseDateAsync(object o)
{
if (!(o is SliceServiceAsyncInfo info)) { return; }
try
{
var date = DFConstantsAndEnums.FIRST_USE_DATE_NOT_SET.ToString("d",
System.Globalization.CultureInfo.InvariantCulture);
if (null != FirstUseDate)
{
date = ((DateTime)FirstUseDate).ToString("d", System.Globalization.CultureInfo.InvariantCulture);
}
SetUserAttribute(AttributeTypes.SliceUserAttributes.FirstUseDate, date);
}
catch (Exception ex)
{
info.Error(ex.Message);
return;
}
info.Success();
}
public override HardwareTypes GetHardwareType()
{
if (!string.IsNullOrWhiteSpace(SerialNumber))
{
if (SerialNumber.StartsWith("SPS"))
{
return HardwareTypes.SLICE2_SIM;
}
if (SerialNumber.StartsWith("SLS"))
{
return HardwareTypes.SLICE2_SLS;
}
if (SerialNumber.StartsWith("SPD"))
{
return HardwareTypes.SLICE2_DIM;
}
if (SerialNumber.StartsWith("SLD"))
{
return HardwareTypes.SLICE2_SLD;
}
if (SerialNumber.StartsWith("SLD"))
{
return HardwareTypes.SLICE2_SLD;
}
if (SerialNumber.StartsWith("SPT"))
{
return HardwareTypes.SLICE2_TOM;
}
if (SerialNumber.StartsWith("SLT"))
{
return HardwareTypes.SLICE2_SLT;
}
}
return HardwareTypes.SLICE2_Base;
}
protected override void AsyncAutoDetect(object o)
{
if (!(o is AutoDetectServiceAsyncInfo info)) { return; }
if (null == DASInfo || null == DASInfo.Modules || 0 == DASInfo.Modules.Length
|| null == ConfigData || 0 == ConfigData.Modules.Length)
{
info.Error($"no configuration on unit {SerialNumber}");
return;
}
if (DASInfo.Modules[0].IsProgrammable)
{
var channels = new List();
foreach (var module in ConfigData.Modules)
{
foreach (var _ in module.Channels)
{
channels.Add(SetStackChannelTypeConfiguration.ChannelTypes.AUTO_DETECT);
}
}
var cmd = new SetStackChannelTypeConfiguration(this);
cmd.SetValue(channels.ToArray());
if (info.QueryConfiguration)
{
var query = new QueryStackSensorIDs(this);
query.Exec();
var idx = 0;
foreach (var m in ConfigData.Modules)
{
foreach (var c in m.Channels)
{
if (c is AnalogInputDASChannel)
{
switch (Convert.ToInt32(query.Types[idx]))
{
case (int)SetStackChannelTypeConfiguration.ChannelTypes.FORCE_IEPE:
(c as AnalogInputDASChannel).IEPEChannel = true;
break;
case (int)SetStackChannelTypeConfiguration.ChannelTypes.FORCE_IEPE_LOW:
(c as AnalogInputDASChannel).IEPEChannel = true;
break;
default:
(c as AnalogInputDASChannel).IEPEChannel = false;
break;
}
var id = new EID(HexEncoding.ToString(query.IDs[idx]));
if (id.IsValid() && !RunTestVariables.BypassEIDRead)
{
c.IDs = new EID[] { id };
}
}
idx++;
}
}
}
}
info.Success();
}
protected override void AsyncClearLatches(object o)
{
if (!(o is SliceServiceAsyncInfo info)) { return; }
var QATS = new QueryArmAndTriggerStatus(this);
QATS.SyncExecute();
info.Success();
}
protected override void AsyncClearTriggerOut(object o)
{
if (!(o is SliceServiceAsyncInfo info)) { return; }
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();
info.Success();
}
///
/// clears trigger lines, used by AsyncClearDASTriggerLine and AsyncStartTriggerCheck
///
private void ClearTriggerLine()
{
try
{
//reset the FPGA of SLICE6/SLICE2 which may have latched and will remain latched (until something resets it)
//IHL will apparently NOT reset it
//this should come atleast 1.5s after the SLICE6DB/ECM pulse, however there's a mandatory 1.7s wait by genericservices.starttriggercheck between
//ECM/S6DB and regular units
var qats = new QueryArmAndTriggerStatus(this);
qats.SyncExecute();
}
catch (Exception ex)
{
APILogger.Log(ex);
}
//performance improvement, don't set polarity in systems that don't need to
//[reduce # of commands]
if (RunTestVariables.InRunTest && RunTestVariables.DontSetPolarityInRunTest)
{
//don't set
}
else
{
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();
}
catch (Exception ex)
{
APILogger.Log(ex);
}
}
}
///
/// complete any work we need to do before starting trigger check
///
protected override void AsyncPreStartTriggerCheck(object asyncInfo)
{
if (!(asyncInfo is SliceServiceAsyncInfo info))
{
return;
}
// 14459 Trigger shorted error after running check trigger twice.
// if software trigger was used, it will still be set, this is to make sure it's unset
// prior to starting trigger check, this is occuring before the QATS that will reset
// FPGA status
ClearTriggerLine();
info.Success();
}
///
/// clears any trigger lines for any das
///
protected override void AsyncClearDASTriggerLine(object asyncInfo)
{
if (!(asyncInfo is SliceServiceAsyncInfo info))
{
return;
}
ClearTriggerLine();
info.Success();
}
protected override void AsyncStartTriggerCheck(object asyncInfo)
{
if (!(asyncInfo is SliceServiceAsyncInfo info)) { return; }
try
{
ClearTriggerLine();
var ihl = new InitializeHardwareLines(this) { LogCommands = true };
try
{
if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.InitHardwareInputLines))
{
ihl.SyncExecute();
}
else
{
throw new Exception("command not supported: InitHardareInputLines");
}
}
catch (Exception ex)
{
InitializeHardwareLines.Log(ex, ihl);
}
if (ihl.TriggerInputShorted)
{
info.Error("TriggerInputShorted");
return;
}
if (ihl.StartRecordShorted)
{
info.Error("StartInputShorted");
return;
}
info.Success();
}
catch (CanceledException)
{
info.Cancel();
}
catch (Exception ex)
{
info.Error(ex.Message, ex);
}
}
///
///
/// gets the expected excitation for a given channel
/// note for SLICE2 we need to consider 2V,5V, and 10V, however 2V or 2.5V may both refer to 2V
/// returns 0 if excitation could not be retrieved, otherwise excitation in mV
///
///
///
///
protected override double GetExpectedExcitationMV(int moduleIndex, int channelOnModule)
{
if (ConfigData?.Modules == null || ConfigData.Modules.Length <= moduleIndex)
{
APILogger.Log("could not get expected excitaiton, no configuration data to base it on");
return 0D;
}
if (!(ConfigData.Modules[moduleIndex].Channels[channelOnModule] is AnalogInputDASChannel aic))
{
//we never expect to get here, but if we ever do, there's no excitation to consider ...
APILogger.Log("could not get expected excitation, channel is not analog");
return 0D;
}
var excitation = Test.Module.Channel.Sensor.GetExcitationVoltageMagnitudeFromEnum(aic.Excitation) * 1000D;
try
{
var qsa = new QuerySystemAttribute_Slice2Bridge(this);
switch (channelOnModule)
{
case 0:
switch (aic.Excitation)
{
case ExcitationVoltageOptions.ExcitationVoltageOption.Volt10:
qsa.Key = AttributeTypes.SystemAttributes_Bridge_SLICE2.FactoryCalibratedExcitationA_10000mV;
break;
case ExcitationVoltageOptions.ExcitationVoltageOption.Volt5:
qsa.Key = AttributeTypes.SystemAttributes_Bridge_SLICE2.FactoryCalibratedExcitationA_5000mV;
break;
default:
qsa.Key = AttributeTypes.SystemAttributes_Bridge_SLICE2.FactoryCalibratedExcitationA_2000mV;
break;
}
break;
case 1:
switch (aic.Excitation)
{
case ExcitationVoltageOptions.ExcitationVoltageOption.Volt10:
qsa.Key = AttributeTypes.SystemAttributes_Bridge_SLICE2.FactoryCalibratedExcitationB_10000mV;
break;
case ExcitationVoltageOptions.ExcitationVoltageOption.Volt5:
qsa.Key = AttributeTypes.SystemAttributes_Bridge_SLICE2.FactoryCalibratedExcitationB_5000mV;
break;
default:
qsa.Key = AttributeTypes.SystemAttributes_Bridge_SLICE2.FactoryCalibratedExcitationB_2000mV;
break;
}
break;
default:
switch (aic.Excitation)
{
case ExcitationVoltageOptions.ExcitationVoltageOption.Volt10:
qsa.Key = AttributeTypes.SystemAttributes_Bridge_SLICE2.FactoryCalibratedExcitationC_10000mV;
break;
case ExcitationVoltageOptions.ExcitationVoltageOption.Volt5:
qsa.Key = AttributeTypes.SystemAttributes_Bridge_SLICE2.FactoryCalibratedExcitationC_5000mV;
break;
default:
qsa.Key = AttributeTypes.SystemAttributes_Bridge_SLICE2.FactoryCalibratedExcitationC_2000mV;
break;
}
break;
}
//module 0 is the base, so we have to add one to offset this
qsa.DeviceID = Convert.ToByte(1 + moduleIndex);
qsa.SyncExecute();
var bridgeExcitation = Convert.ToDouble(qsa.Value);//already in mV, no conversion needed
var delta = Math.Abs(excitation - bridgeExcitation);
if (delta < 500)
{
excitation = bridgeExcitation;
}
}
catch (Exception ex)
{
APILogger.Log("failed to get expected excitation: ", ex);
}
return excitation;
}
private const ushort SLICE_SHUNT_DAC_MIDSCALE = 2116;
protected override void QueryInternalOffsets(ref DiagnosticsResult[] results)
{
}
protected override void MeasureInternalOffset(IDiagnosticActions[] ChannelActions,
SliceServiceAsyncInfo info, ref IDiagnosticResult[] results,
bool bFinalOffset)
{
if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.MeasureInternalOffset))
{
#region New internal offset measure
// first count how many we need to do it on
var numToMeasure = ChannelActions.Count(a => a.MeasureInternalOffset);
if (numToMeasure == 0)
return;
try
{
var measureInternalOffset = new MeasureStackInternalChannelOffset(this);
measureInternalOffset.NumberOfChannels = (byte)numToMeasure;
measureInternalOffset.DeviceID = 0; // send to base
var measureChannels = new byte[numToMeasure];
var channelCounter = 0;
for (var idx = 0; idx < ChannelActions.Length; idx++)
{
if (ChannelActions[idx].MeasureInternalOffset)
{
measureChannels[channelCounter++] = (byte)ChannelActions[idx].DASChannelNumber;
}
}
measureInternalOffset.ChannelList = measureChannels;
measureInternalOffset.SyncExecute();
// calculate result
channelCounter = 0;
var measuredChannelBiasMv = new double[ChannelDiagnostics.Length];
for (var idx = 0; idx < ChannelActions.Length; idx++)
{
if (ChannelActions[idx].MeasureInternalOffset)
{
var result = (double)measureInternalOffset.MeasuredInternalOffset[channelCounter];
measuredChannelBiasMv[idx] = result;
channelCounter++;
}
}
for (var idx = 0; idx < results.Length; idx++)
{
info.NewData(new ServiceCallbackData.DiagnosticNewData()
{
Result = measuredChannelBiasMv[idx],
DasChannelNumber = results[idx].DASChannelNumber,
Action = ServiceCallbackData.DiagnosticNewData.Actions.MeasureInternalOffset
});
//attempt to resolve the removed adc by using the before and after info
try
{
if (bFinalOffset)
{
if (null != results[idx] && ChannelActions[idx].RemoveOffset)
{
results[idx].RemovedInternalOffsetADC =
Convert.ToInt32((double)results[idx].MeasuredInternalOffsetMilliVolts / results[idx].ScalefactorMilliVoltsPerADC -
measuredChannelBiasMv[idx] / results[idx].ScalefactorMilliVoltsPerADC);
}
else
{
results[idx].RemovedInternalOffsetADC = 0;
}
}
else
{
results[idx].MeasuredInternalOffsetMilliVolts = measuredChannelBiasMv[idx];
if (results[idx].ScalefactorMilliVoltsPerADC == 0)
{
results[idx].ZeroMVInADC = 0;
}
else
{
results[idx].ZeroMVInADC = Convert.ToInt16(measuredChannelBiasMv[idx] / results[idx].ScalefactorMilliVoltsPerADC);
}
}
}
catch (Exception ex)
{
APILogger.Log(
$"Error Retrieving Internal Offset on {SerialNumber} channel {results[idx].DASChannelNumber}", ex);
results[idx].RemovedInternalOffsetADC = 0;
results[idx].MeasuredInternalOffsetMilliVolts = 0;
}
}
}
catch (Exception ex)
{
APILogger.Log("Error Retrieving Internal Offset", ex);
for (var idx = 0; idx < results.Length; idx++)
{
results[idx].RemovedInternalOffsetADC = 0;
results[idx].MeasuredInternalOffsetMilliVolts = 0;
}
}
#endregion
}
else if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.MeasureInternalOffsetSwitches) && GEN3_REV_B == _hardwareRevision)
{
#region 8668 MEASUREADCAT0MV SWITCHES
try
{
// first count how many we need to do it on
var numToMeasure = ChannelActions.Count(a => a.MeasureInternalOffset);
if (numToMeasure == 0)
return;
var existingCourseShunts = new Dictionary();
var existingFineShunts = new Dictionary();
var existingSwitchValues = new Dictionary>();
VoltageInsertionEnabled = false;
//query switches and DACs for resetting later
foreach (var module in DASInfo.Modules)
{
var qdac = new QueryDACImmediate(this);
qdac.DeviceID = Convert.ToByte(1 + module.ModuleArrayIndex);
qdac.DAC = (byte)DACs.BridgeDACS_SLICE2.CoarseShunt;
qdac.SyncExecute();
existingCourseShunts[module.ModuleArrayIndex] = qdac.Value;
qdac = new QueryDACImmediate(this);
qdac.DeviceID = Convert.ToByte(1 + module.ModuleArrayIndex);
qdac.DAC = (byte)DACs.BridgeDACS_SLICE2.FineShunt;
qdac.SyncExecute();
existingFineShunts[module.ModuleArrayIndex] = qdac.Value;
var qsi = new QuerySwitchImmediate(this);
qsi.DeviceID = Convert.ToByte(1 + module.ModuleArrayIndex);
qsi.Switch = (byte)Switches.BridgeSwitches_SLICE2.swAll_ReferenceShorted;
qsi.SwitchText = Switches.BridgeSwitches_SLICE2.swAll_ReferenceShorted.ToString();
qsi.SyncExecute();
existingSwitchValues[qsi.DeviceID] = new Dictionary();
existingSwitchValues[qsi.DeviceID][qsi.Switch] = qsi.Setting;
qsi = new QuerySwitchImmediate(this);
qsi.DeviceID = Convert.ToByte(1 + module.ModuleArrayIndex);
qsi.Switch = (byte)Switches.BridgeSwitches_SLICE2.swVoltageInsertionMode;
qsi.SwitchText = Switches.BridgeSwitches_SLICE2.swVoltageInsertionMode.ToString();
qsi.SyncExecute();
existingSwitchValues[qsi.DeviceID][qsi.Switch] = qsi.Setting;
if (Convert.ToBoolean(qsi.Setting)) { VoltageInsertionEnabled = true; }
qsi = new QuerySwitchImmediate(this);
qsi.DeviceID = Convert.ToByte(1 + module.ModuleArrayIndex);
qsi.Switch = (byte)Switches.BridgeSwitches_SLICE2.swVoltageInsertionAmp;
qsi.SwitchText = Switches.BridgeSwitches_SLICE2.swVoltageInsertionAmp.ToString();
qsi.SyncExecute();
existingSwitchValues[qsi.DeviceID][qsi.Switch] = qsi.Setting;
qsi = new QuerySwitchImmediate(this);
qsi.DeviceID = Convert.ToByte(1 + module.ModuleArrayIndex);
qsi.Switch = (byte)Switches.BridgeSwitches_SLICE2.swAll_SignalsShorted;
qsi.SwitchText = Switches.BridgeSwitches_SLICE2.swAll_SignalsShorted.ToString();
qsi.SyncExecute();
existingSwitchValues[qsi.DeviceID][qsi.Switch] = qsi.Setting;
qsi = new QuerySwitchImmediate(this);
qsi.DeviceID = Convert.ToByte(1 + module.ModuleArrayIndex);
qsi.Switch = (byte)Switches.BridgeSwitches_SLICE2.swVoltageInsertionA;
qsi.SwitchText = Switches.BridgeSwitches_SLICE2.swVoltageInsertionA.ToString();
qsi.SyncExecute();
existingSwitchValues[qsi.DeviceID][qsi.Switch] = qsi.Setting;
if (Convert.ToBoolean(qsi.Setting)) { VoltageInsertionEnabled = true; }
qsi = new QuerySwitchImmediate(this);
qsi.DeviceID = Convert.ToByte(1 + module.ModuleArrayIndex);
qsi.Switch = (byte)Switches.BridgeSwitches_SLICE2.swVoltageInsertionB;
qsi.SwitchText = Switches.BridgeSwitches_SLICE2.swVoltageInsertionB.ToString();
qsi.SyncExecute();
existingSwitchValues[qsi.DeviceID][qsi.Switch] = qsi.Setting;
if (Convert.ToBoolean(qsi.Setting)) { VoltageInsertionEnabled = true; }
qsi = new QuerySwitchImmediate(this);
qsi.DeviceID = Convert.ToByte(1 + module.ModuleArrayIndex);
qsi.Switch = (byte)Switches.BridgeSwitches_SLICE2.swVoltageInsertionC;
qsi.SwitchText = Switches.BridgeSwitches_SLICE2.swVoltageInsertionC.ToString();
qsi.SyncExecute();
existingSwitchValues[qsi.DeviceID][qsi.Switch] = qsi.Setting;
if (Convert.ToBoolean(qsi.Setting)) { VoltageInsertionEnabled = true; }
}
//set switches & DACs
uint fastestSPS = 0;
foreach (var module in DASInfo.Modules)
{
fastestSPS = Math.Max(ConfigData.Modules[module.ModuleArrayIndex].SampleRateHz, fastestSPS);
var ssi = new SetSwitchImmediate(this);
ssi.DeviceID = Convert.ToByte(1 + module.ModuleArrayIndex);
ssi.Switch = (byte)Switches.BridgeSwitches_SLICE2.swAll_ReferenceShorted;
ssi.SwitchText = Switches.BridgeSwitches_SLICE2.swAll_ReferenceShorted.ToString();
ssi.Setting = Convert.ToByte(false);
ssi.SyncExecute();
var sDac = new SetDACImmediate(this);
sDac.DeviceID = Convert.ToByte(1 + module.ModuleArrayIndex);
sDac.DAC = (byte)DACs.BridgeDACS_SLICE2.CoarseShunt;
sDac.Value = SLICE_SHUNT_DAC_MIDSCALE;
sDac.SyncExecute();
sDac = new SetDACImmediate(this);
sDac.DeviceID = Convert.ToByte(1 + module.ModuleArrayIndex);
sDac.DAC = (byte)DACs.BridgeDACS_SLICE2.FineShunt;
sDac.Value = SLICE_SHUNT_DAC_MIDSCALE;
sDac.SyncExecute();
ssi = new SetSwitchImmediate(this);
ssi.DeviceID = Convert.ToByte(1 + module.ModuleArrayIndex);
ssi.Switch = (byte)Switches.BridgeSwitches_SLICE2.swVoltageInsertionMode;
ssi.SwitchText = Switches.BridgeSwitches_SLICE2.swVoltageInsertionMode.ToString();
ssi.Setting = Convert.ToByte(true);
ssi.SyncExecute();
ssi = new SetSwitchImmediate(this);
ssi.DeviceID = Convert.ToByte(1 + module.ModuleArrayIndex);
ssi.Switch = (byte)Switches.BridgeSwitches_SLICE2.swVoltageInsertionAmp;
ssi.SwitchText = Switches.BridgeSwitches_SLICE2.swVoltageInsertionAmp.ToString();
ssi.Setting = Convert.ToByte(false);
ssi.SyncExecute();
ssi = new SetSwitchImmediate(this);
ssi.DeviceID = Convert.ToByte(1 + module.ModuleArrayIndex);
ssi.Switch = (byte)Switches.BridgeSwitches_SLICE2.swAll_SignalsShorted;
ssi.SwitchText = Switches.BridgeSwitches_SLICE2.swAll_SignalsShorted.ToString();
ssi.Setting = Convert.ToByte(true);
ssi.SyncExecute();
ssi = new SetSwitchImmediate(this);
ssi.DeviceID = Convert.ToByte(1 + module.ModuleArrayIndex);
ssi.Switch = (byte)Switches.BridgeSwitches_SLICE2.swVoltageInsertionA;
ssi.SwitchText = Switches.BridgeSwitches_SLICE2.swVoltageInsertionA.ToString();
ssi.Setting = Convert.ToByte(true);
ssi.SyncExecute();
ssi = new SetSwitchImmediate(this);
ssi.DeviceID = Convert.ToByte(1 + module.ModuleArrayIndex);
ssi.Switch = (byte)Switches.BridgeSwitches_SLICE2.swVoltageInsertionB;
ssi.SwitchText = Switches.BridgeSwitches_SLICE2.swVoltageInsertionB.ToString();
ssi.Setting = Convert.ToByte(true);
ssi.SyncExecute();
ssi = new SetSwitchImmediate(this);
ssi.DeviceID = Convert.ToByte(1 + module.ModuleArrayIndex);
ssi.Switch = (byte)Switches.BridgeSwitches_SLICE2.swVoltageInsertionC;
ssi.SwitchText = Switches.BridgeSwitches_SLICE2.swVoltageInsertionC.ToString();
ssi.Setting = Convert.ToByte(true);
ssi.SyncExecute();
}
//retrieve sample averages
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)(fastestSPS * avgTimeSeconds);
if (measureOffset.Samples < 1)
{
measureOffset.Samples = 1;
}
measureOffset.SyncExecute();
for (var i = 0; i < measureOffset._data.Length; i++)
{
if (bFinalOffset)
{
results[i].RemovedInternalOffsetADC = measureOffset._data[i];
}
else
{
try
{
results[i].MeasuredInternalOffsetMilliVolts = measureOffset._data[i] *
results[i].ScalefactorMilliVoltsPerADC;
results[i].ZeroMVInADC = measureOffset._data[i];
APILogger.Log("MeasuredInternalOffsetMv:", measureOffset._data[i],
"*", results[i].ScalefactorMilliVoltsPerADC, "=",
results[i].MeasuredInternalOffsetMilliVolts);
APILogger.Log(SerialNumber, string.Format("CH{0}", results[i].DASChannelNumber), "ZeroMVInADC=", results[i].ZeroMVInADC);
}
catch (Exception ex)
{
results[i].MeasuredInternalOffsetMilliVolts = 0D;
APILogger.Log(ex, "FinalOffset:", bFinalOffset);
}
}
}
//unset switches & DACs, do the opposite of everything/restore
foreach (var module in DASInfo.Modules)
{
var ssi = new SetSwitchImmediate(this);
ssi.DeviceID = Convert.ToByte(1 + module.ModuleArrayIndex);
ssi.Switch = (byte)Switches.BridgeSwitches_SLICE2.swAll_ReferenceShorted;
ssi.Setting = existingSwitchValues[ssi.DeviceID][(byte)Switches.BridgeSwitches_SLICE2.swAll_ReferenceShorted];
ssi.SwitchText = Switches.BridgeSwitches_SLICE2.swAll_ReferenceShorted.ToString();
ssi.SyncExecute();
var sDac = new SetDACImmediate(this);
sDac.DeviceID = Convert.ToByte(1 + module.ModuleArrayIndex);
sDac.DAC = (byte)DACs.BridgeDACS_SLICE2.CoarseShunt;
sDac.Value = existingCourseShunts[module.ModuleArrayIndex];
sDac.SyncExecute();
sDac = new SetDACImmediate(this);
sDac.DeviceID = Convert.ToByte(1 + module.ModuleArrayIndex);
sDac.DAC = (byte)DACs.BridgeDACS_SLICE2.FineShunt;
sDac.Value = existingFineShunts[module.ModuleArrayIndex];
sDac.SyncExecute();
ssi = new SetSwitchImmediate(this);
ssi.DeviceID = Convert.ToByte(1 + module.ModuleArrayIndex);
ssi.Switch = (byte)Switches.BridgeSwitches_SLICE2.swVoltageInsertionMode;
ssi.SwitchText = Switches.BridgeSwitches_SLICE2.swVoltageInsertionMode.ToString();
ssi.Setting = existingSwitchValues[ssi.DeviceID][ssi.Switch];
ssi.SyncExecute();
ssi = new SetSwitchImmediate(this);
ssi.DeviceID = Convert.ToByte(1 + module.ModuleArrayIndex);
ssi.Switch = (byte)Switches.BridgeSwitches_SLICE2.swVoltageInsertionAmp;
ssi.SwitchText = Switches.BridgeSwitches_SLICE2.swVoltageInsertionAmp.ToString();
ssi.Setting =
Convert.ToByte(
existingSwitchValues[ssi.DeviceID][
ssi.Switch]);
ssi.SyncExecute();
ssi = new SetSwitchImmediate(this);
ssi.DeviceID = Convert.ToByte(1 + module.ModuleArrayIndex);
ssi.Switch = (byte)Switches.BridgeSwitches_SLICE2.swAll_SignalsShorted;
ssi.Setting = existingSwitchValues[ssi.DeviceID][(byte)Switches.BridgeSwitches_SLICE2.swAll_SignalsShorted];
ssi.SwitchText = Switches.BridgeSwitches_SLICE2.swAll_SignalsShorted.ToString();
ssi.SyncExecute();
ssi = new SetSwitchImmediate(this);
ssi.DeviceID = Convert.ToByte(1 + module.ModuleArrayIndex);
ssi.Switch = (byte)Switches.BridgeSwitches_SLICE2.swVoltageInsertionA;
ssi.Setting = existingSwitchValues[ssi.DeviceID][ssi.Switch];
ssi.SwitchText = Switches.BridgeSwitches_SLICE2.swVoltageInsertionA.ToString();
ssi.SyncExecute();
ssi = new SetSwitchImmediate(this);
ssi.DeviceID = Convert.ToByte(1 + module.ModuleArrayIndex);
ssi.Switch = (byte)Switches.BridgeSwitches_SLICE2.swVoltageInsertionB;
ssi.Setting = existingSwitchValues[ssi.DeviceID][ssi.Switch];
ssi.SwitchText = Switches.BridgeSwitches_SLICE2.swVoltageInsertionB.ToString();
ssi.SyncExecute();
ssi = new SetSwitchImmediate(this);
ssi.DeviceID = Convert.ToByte(1 + module.ModuleArrayIndex);
ssi.Switch = (byte)Switches.BridgeSwitches_SLICE2.swVoltageInsertionC;
ssi.SwitchText = Switches.BridgeSwitches_SLICE2.swVoltageInsertionC.ToString();
ssi.Setting = existingSwitchValues[ssi.DeviceID][ssi.Switch];
ssi.SyncExecute();
}
}
catch (Exception ex)
{
APILogger.Log(ex, "finalOffset:", bFinalOffset);
foreach (var r in results)
{
if (bFinalOffset)
{
r.RemovedInternalOffsetADC = 0;
}
else
{
r.MeasuredInternalOffsetMilliVolts = 0D;
}
}
}
#endregion
}
else
{
//Command not supported
for (var idx = 0; idx < results.Length; idx++)
{
results[idx].RemovedInternalOffsetADC = 0;
results[idx].MeasuredInternalOffsetMilliVolts = 0;
}
}
}
///
///
/// Convert gain code to value based on Gen2 vs. Gen3 conversion table
///
///
///
protected override double GainCodeToGainValue(ushort gainCode)
{
var gainValueString = _hardwareRevision == GEN3_REV_B ? ((GainCodesGen3)gainCode).ToString() : ((GainCodesGen2)gainCode).ToString();
if (!double.TryParse(gainValueString.TrimStart('G'), out var gainValue))
{
gainValue = 1.0D;
}
return gainValue;
}
private SerializableDictionary _userAttributes = null;
private static readonly object USER_ATTRIBUTE_LOCK = new object();
public string GetUserAttribute(AttributeTypes.SliceUserAttributes attribute)
{
CreateUserAttributesIfNeeded();
lock (USER_ATTRIBUTE_LOCK)
{
if (!_userAttributes.ContainsKey((int)attribute))
{
return string.Empty;
}
return _userAttributes[(int)attribute];
}
}
public void SetUserAttribute(AttributeTypes.SliceUserAttributes attribute, string value)
{
CreateUserAttributesIfNeeded();
lock (USER_ATTRIBUTE_LOCK)
{
_userAttributes[(int)attribute] = value;
var sb2 = new StringBuilder();
using (var w = XmlWriter.Create(sb2))
{
_userAttributes.WriteXml(w);
}
try
{
var ca = GetConfigAttributes(this);
ca.StoreXMLConfig(sb2.ToString(), ConfigAttributes.FileStore.UserAttributes);
}
catch (Exception ex)
{
APILogger.Log(ex);
}
}
}
private void CreateUserAttributesIfNeeded()
{
lock (USER_ATTRIBUTE_LOCK)
{
if (null == _userAttributes)
{
_userAttributes = new SerializableDictionary();
try
{
var ca = GetConfigAttributes(this);
var xml = ca.RetrieveXMLConfig(ConfigAttributes.FileStore.UserAttributes, this);
if (!string.IsNullOrEmpty(xml))
{
//slice firmware tends to pad with \0, which is invalid in xml, strip them out first
xml = xml.Replace("\0", string.Empty);
var settings = new XmlReaderSettings();
settings.IgnoreComments = true;
settings.IgnoreWhitespace = true;
using (var sr = new StringReader(xml))
{
var r = XmlReader.Create(sr, settings);
_userAttributes.ReadXml(r);
}
}
}
catch (Exception)
{
}
}
}
}
///
/// slice pro/slice1.5 both don't support user attributes, so we store them in the filestore
/// specifically we store a dictionary in store 4, keyed by user attribute id, with a string value
///
///
public override int GetDASDisplayOrder()
{
var sDisplayOrder = GetUserAttribute(AttributeTypes.SliceUserAttributes.DasDisplayOrder);
if (int.TryParse(sDisplayOrder, System.Globalization.NumberStyles.Any,
System.Globalization.CultureInfo.InvariantCulture, out var temp))
{
return temp;
}
return -1;
}
///
///
/// slice 1.5 and slice pro don't have a user attribute store, so we use the file store instead
/// we store a dictionary in there keyed by attribute number with string value
///
///
public override void SetDASDisplayOrder(int order)
{
SetUserAttribute(AttributeTypes.SliceUserAttributes.DasDisplayOrder,
order.ToString(System.Globalization.CultureInfo.InvariantCulture));
}
///
/// sets the channel display order for slice 1.5/slice pro
///
///
public override void SetChannelDisplayOrder(int[] order)
{
var _ = GetUserAttribute(AttributeTypes.SliceUserAttributes.DisplayOrder);
var sb = new StringBuilder();
foreach (var i in order)
{
if (sb.Length > 0)
{
sb.Append(",");
}
sb.Append(i.ToString(System.Globalization.CultureInfo.InvariantCulture));
}
SetUserAttribute(AttributeTypes.SliceUserAttributes.DisplayOrder, sb.ToString());
}
///
/// gets the channel display order for slice 1.5/slice pro
///
///
public override int[] GetChannelDisplayOrder()
{
var s = GetUserAttribute(AttributeTypes.SliceUserAttributes.DisplayOrder);
var atts = new List();
if (!string.IsNullOrWhiteSpace(s))
{
var tokens = s.Split(',');
foreach (var token in tokens)
{
if (int.TryParse(token, out int iTemp))
{
atts.Add(iTemp);
}
}
}
return atts.ToArray();
}
internal class TimeSynchronizationAsyncInfo : SliceServiceAsyncInfo
{
public DateTime Time { get; set; }
public TimeSynchronizationAsyncInfo(ServiceCallback callback, object userData, DateTime time) :
base(callback, userData)
{
Time = time;
}
}
void ITimeActions.Synchronize(ServiceCallback callback, object userData, DateTime time)
{
var info = new TimeSynchronizationAsyncInfo(callback, userData, time);
LaunchAsyncWorker("Slice2.Synchronize", AsyncSynchronize, info);
}
private void AsyncSynchronize(object info)
{
var packet = (TimeSynchronizationAsyncInfo)info;
try
{
var set = new SetBaseSystemTime(this);
set.SystemTime = packet.Time;
set.SyncExecute();
}
catch (Exception ex)
{
packet.Error(ex.Message);
return;
}
packet.Success();
}
private DateTime _systemDateTime = DateTime.MinValue;
public override DateTime SystemBaseTime => _systemDateTime;
void ITimeActions.QueryTime(ServiceCallback callback, object userData)
{
var info = new SliceServiceAsyncInfo(callback, userData);
LaunchAsyncWorker("Slice2.QueryTime", AsyncQueryTime, info);
}
private void AsyncQueryTime(object info)
{
var packet = (SliceServiceAsyncInfo)info;
try
{
var query = new QueryBaseSystemTime(this);
query.SyncExecute();
packet.NewData(query.SystemTime);
}
catch (Exception ex)
{
packet.Error(ex.Message);
return;
}
packet.Success();
}
public override bool RequireDiagnosticRateMatchSampleRate() { return true; }
private volatile bool _rangeBandwithLimited = false;
public override bool RangeBandwidthLimited => _rangeBandwithLimited;
private volatile bool _supportsTimeSynchronization = false;
public override bool SupportsTimeSynchronization => _supportsTimeSynchronization;
private static readonly double IEPE_GAIN_DIVIDER = 4.9D;
//0.2040816327F, 2.0408163265F
public static StaticInformation StaticDASIEPESLICE2Info = new StaticInformation(new double[]
{
2400D / (1.0D / IEPE_GAIN_DIVIDER),
2400D / (2.0D / IEPE_GAIN_DIVIDER),
2400D / (5.0D / IEPE_GAIN_DIVIDER),
2400D / (10.0D / IEPE_GAIN_DIVIDER),
2400D / (20.0D / IEPE_GAIN_DIVIDER),
2400D / (50.0D / IEPE_GAIN_DIVIDER),
2400D / (100.0D / IEPE_GAIN_DIVIDER)
});
///
/// this is the SPS gen 3 input range table for modified SPS
/// http://fogbugz/fogbugz/default.asp?10080
/// note that we have artifically restricted this list to only provide
/// the gains we wish to make available
///
public static StaticInformation StaticDASRevCBridgeInfoModified = new StaticInformation(new[]
{
2400D/1D,
2400D/2D,
2400D/4D,
2400D/8D,
2400D/16D,
2400D/20D,
2400D/32D,
2400D/40D,
2400D/64D,
2400D/80D,
2400D/160D,
2400D/320D,
2400D/640D,
2400D/800D,
2400D/1600D
});
///
/// this is the SPS gen3 input range table for unmodified SPS
/// http://fogbugz/fogbugz/default.asp?10080
/// note that we have artifically restricted this list twice, once to
/// restricts gains above 1600 as not useful, and then we took out four
/// "noisy" gains
///
public static StaticInformation StaticDASRevCBridgeInfoNotModified = new StaticInformation(new[]
{
2400D/1D,
2400D/2D,
2400D/4D,
2400D/8D,
// 2400D/16D,
2400D/20D,
// 2400D/32D,
2400D/40D,
// 2400D/64D,
2400D/80D,
2400D/160D,
2400D/320D,
// 2400D/640D,
2400D/800D,
2400D/1600D
});
public static StaticInformation StaticDASRevCIEPEInfo = new StaticInformation(new double[]{
2400D/(1D/IEPE_GAIN_DIVIDER),
2400D/(2D/IEPE_GAIN_DIVIDER),
2400D/(4D/IEPE_GAIN_DIVIDER),
2400D/(8D/IEPE_GAIN_DIVIDER),
2400D/(16D/IEPE_GAIN_DIVIDER),
2400D/(32D/IEPE_GAIN_DIVIDER),
2400D/(64D/IEPE_GAIN_DIVIDER)
});
public enum GainCodesGen2 //Used by Slice PRO Gen2
{
G1 = 8,
G2 = 16,
G4 = 17,
G5 = 24,
G8 = 18,
G10 = 25,
G16 = 19,
G20 = 26,
G32 = 20,
G40 = 27,
G50 = 48,
G64 = 21,
G80 = 35,
G100 = 49,
G128 = 22,
G160 = 43,
G200 = 50,
G256 = 23,
G320 = 37,
G400 = 51,
G640 = 45,
G800 = 52,
G1280 = 46,
G1600 = 53,
G2560 = 47,
G3200 = 54,
G6400 = 62,
G12800 = 63
};
///
/// decorated with min/max/"middle" for
/// http://fogbugz/fogbugz/default.asp?10080
///
public enum GainCodesGen3 //Used by Slice PRO Gen3
{
[MaxInputRange(2500D)]
[MinInputRange(1251.25)]
[FirmwareInputRange(1875.625)]
G1 = 8,
[MaxInputRange(1250D)]
[MinInputRange(625.625)]
[FirmwareInputRange(937.8125)]
G2 = 16,
[MaxInputRange(625D)]
[MinInputRange(156.40625)]
[FirmwareInputRange(468.90625)]
G4 = 24,
[MaxInputRange(312.5D)]
[MinInputRange(156.40625)]
[FirmwareInputRange(234.45313)]
G8 = 32,
[GainAvailableUnmodified(false)]
[MaxInputRange(156.25)]
[MinInputRange(125.03125)]
[FirmwareInputRange(140.64063)]
G16 = 40,
[MaxInputRange(125D)]
[MinInputRange(78.17188)]
[FirmwareInputRange(101.58594)]
G20 = 17,
[GainAvailableUnmodified(false)]
[MaxInputRange(78.125)]
[MinInputRange(62.51563)]
[FirmwareInputRange(70.32031)]
G32 = 48,
[MaxInputRange(62.5)]
[MinInputRange(39.08594)]
[FirmwareInputRange(50.79297)]
G40 = 25,
[GainAvailableUnmodified(false)]
[MaxInputRange(39.0625)]
[MinInputRange(31.25781)]
[FirmwareInputRange(35.16016)]
G64 = 56,
[MaxInputRange(31.25)]
[MinInputRange(15.64063)]
[FirmwareInputRange(23.44531)]
G80 = 33,
[MaxInputRange(15.625)]
[MinInputRange(7.82031)]
[FirmwareInputRange(11.72266)]
G160 = 41,
[MaxInputRange(7.8125)]
[MinInputRange(3.91016)]
[FirmwareInputRange(5.86133)]
G320 = 49,
[GainAvailableUnmodified(false)]
[MaxInputRange(3.90625)]
[MinInputRange(3.12578)]
[FirmwareInputRange(3.51602)]
G640 = 57,
[MaxInputRange(3.125)]
[MinInputRange(1.56406)]
[FirmwareInputRange(2.34453)]
G800 = 34,
[MaxInputRange(1.5625)]
[MinInputRange(.78203)]
[FirmwareInputRange(1.17227)]
G1600 = 42,
[GainDisabled(true)]
[MaxInputRange(.78125)]
[MinInputRange(0.39102)]
[FirmwareInputRange(0.58613)]
G3200 = 50,
[GainDisabled(true)]
[MaxInputRange(.39063)]
[MinInputRange(.15648)]
[FirmwareInputRange(.27355)]
G6400 = 58,
[GainDisabled(true)]
[MaxInputRange(.15625)]
[MinInputRange(.0782)]
[FirmwareInputRange(.11723)]
G16000 = 43,
[GainDisabled(true)]
[MaxInputRange(.07813)]
[MinInputRange(.03910)]
[FirmwareInputRange(.05861)]
G32000 = 51,
[GainDisabled(true)]
[MaxInputRange(.03906)]
[MinInputRange(0)]
[FirmwareInputRange(.01953)]
G64000 = 59
};
///
/// 15462 Limit SLICE 6 AIR gains as they are not functional with IEPE accel
/// algorithm requires the gains to be in order of firmware input range explicitly
/// this can be used to sort enum values into order of firmware input range
///
public class GainSorterSLICE6IEPE : IComparer
{
public int Compare(GainCodesSLICE6IEPE left, GainCodesSLICE6IEPE right)
{
var leftRange = FirmwareInputRangeAttribute.GetFirmwareInputRangemV(left);
var rightRange = FirmwareInputRangeAttribute.GetFirmwareInputRangemV(right);
return leftRange.CompareTo(rightRange);
}
}
///
/// 10080 Implement SLICE PRO SIM Gain Limitation for Gen 3 SIMs
/// algorithm requires the gains to be in order of firmware input range explicitly
/// this can be used to sort enum values into order of firmware input range
///
public class GainSorter : IComparer
{
public int Compare(GainCodesGen3 left, GainCodesGen3 right)
{
var leftRange = FirmwareInputRangeAttribute.GetFirmwareInputRangemV(left);
var rightRange = FirmwareInputRangeAttribute.GetFirmwareInputRangemV(right);
return leftRange.CompareTo(rightRange);
}
}
protected int _hardwareRevision = UNKNOWN_REVISION;
protected int _baseType = -1;
///
/// returns the nominal input ranges for SLICE2
/// this was modified for
/// http://fogbugz/fogbugz/default.asp?10080
/// we now restrict the ranges by throwing out certain gains, etc
///
///
/// whether the SPS is "modified" or not(http://fogbugz/fogbugz/default.asp?10080)
///
public double[] GetNominalRanges(SensorConstants.BridgeType bridgeType, bool bModified)
{
//if we are not gen 3, do what we've always have done
if (_hardwareRevision != GEN3_REV_B)
return bridgeType == SensorConstants.BridgeType.IEPE
? StaticDASIEPESLICE2Info.NominalRanges
: WinUSBSlice.StaticDASBridgeInfo.NominalRanges;
//if we are IEPE, apparently we don't change anything
if (bridgeType == SensorConstants.BridgeType.IEPE)
{
return StaticDASRevCIEPEInfo.NominalRanges;
}
//finally, for not IEPE, we either use the modified or not modified ranges
return bModified
? StaticDASRevCBridgeInfoModified.NominalRanges
: StaticDASRevCBridgeInfoNotModified.NominalRanges;
}
private ushort _featureConfigValue = 1;
public override double[] GetNominalRanges(SensorConstants.BridgeType bridgeType)
{
if (_hardwareRevision != GEN3_REV_B)
{
//if we aren't gen3 we don't care whether we are modified or not
//http://fogbugz/fogbugz/default.asp?10080
return GetNominalRanges(bridgeType, false);
}
return GetNominalRanges(bridgeType, 1 == _featureConfigValue);
}
///
/// checks whether the channel should perform voltage insertion or not
/// currently this just checks that the input range for the channel is >= 10mV per LP
/// http://manuscript.dts.local/f/cases/31824/Don-t-run-voltage-insertion-when-the-input-range-is-10mV
/// http://manuscript.dts.local/f/cases/3472/Fwd-RE-SLICE-PRO-SIM-Gen3-SPS00134-Voltage-insertion-Error
///
///
///
protected bool ShouldPerformVoltageInsertionCheck(IDiagnosticActions action)
{
try
{
if (!action.PerformVoltageInsertCheck) { return false; }
var moduleNumber = DASInfo.MapDASChannelNumber2ModuleArrayIndex(action.DASChannelNumber);
var channelNumber = DASInfo.MapDASChannelNumber2ModuleChannelNumber(action.DASChannelNumber);
if (ConfigData.Modules[moduleNumber].Channels[channelNumber] is AnalogInputDASChannel aic)
{
if (double.IsNaN(aic.ScalefactorMilliVoltsPerADC) || 0 == aic.ScalefactorMilliVoltsPerADC)
{
//if any of these constraints fail treat the situation like existing behavior, run the check
return true;
}
var mvInputRange = Math.Abs(aic.ScalefactorMilliVoltsPerADC * short.MaxValue);
if (mvInputRange >= Constants.MINIMUM_VOLTAGE_INSERTION_RANGE_MV_SLICE) { return true; }
}
return false;
}
catch (Exception ex)
{
//if there's an exception, preserve the existing behavior, which would have been to run
//the check
APILogger.Log(ex);
return true;
}
}
protected override void PerformVoltageInsertionCheck(IDiagnosticActions[] ChannelActions, SliceServiceAsyncInfo info, ref IDiagnosticResult[] results)
{
// first count how many we need to do it on
var NumToMeasure = ChannelActions.Count(a => ShouldPerformVoltageInsertionCheck(a));
if (NumToMeasure == 0)
return;
var baseType = QueryBaseType();
var revision = QueryHardwareRevision();
//Min Protocol for QueryVoltageInsertion
if (baseType != PROBaseType || revision < 1)
{
// If the functionality doesn't exist, just make sure the results are set to null
// and the app will do the right thing.
//
ClearVoltageInsertionResults(ChannelActions, results);
}
var queryChannelInsertResults = new QueryVoltageInsertaionResult_SLICE2(this);
queryChannelInsertResults.DeviceID = 0; // send to base
var insertionChannelList = new byte[NumToMeasure];
var channelCounter = 0;
for (var idx = 0; idx < ChannelActions.Length; idx++)
{
var actions = ChannelActions[idx];
if (ShouldPerformVoltageInsertionCheck(actions))
{
insertionChannelList[channelCounter] = (byte)actions.DASChannelNumber;
channelCounter++;
}
}
queryChannelInsertResults.StackChannelList = insertionChannelList;
try
{
if (revision >= 1 && baseType == PROBaseType)
{
queryChannelInsertResults.SyncExecute();
channelCounter = 0;
for (var idx = 0; idx < ChannelActions.Length; idx++)
{
var actions = ChannelActions[idx];
if (ShouldPerformVoltageInsertionCheck(actions))
{
results[idx].MeasuredGain = queryChannelInsertResults.ActualGain[channelCounter];
results[idx].TargetGain = queryChannelInsertResults.ExpectedGain[channelCounter];
channelCounter++;
}
}
}
else
{
channelCounter = 0;
ClearVoltageInsertionResults(ChannelActions, results);
}
}
catch (Exception ex)
{
APILogger.Log("Failed to perform voltageinsertion check", ex);
// If the functionality doesn't exist, just make sure the results are set to null
// and the app will do the right thing.
//
ClearVoltageInsertionResults(ChannelActions, results);
}
}
///
/// clears the measure and target gain information for any diagnostic results which should perform voltage insertion checks
/// http://manuscript.dts.local/f/cases/31824/Don-t-run-voltage-insertion-when-the-input-range-is-10mV
///
///
///
protected void ClearVoltageInsertionResults(IDiagnosticActions[] ChannelActions, IDiagnosticResult[] results)
{
for (var idx = 0; idx < ChannelActions.Length; idx++)
{
var actions = ChannelActions[idx];
if (ShouldPerformVoltageInsertionCheck(actions))
{
results[idx].MeasuredGain = null;
results[idx].TargetGain = null;
}
}
}
public override bool SupportsMultipleEvents()
{
return IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.MultipleEvents);
}
public override bool SupportsAutoArm() { return IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.AutoArm); }
public override bool SupportsLevelTrigger() { return true; }
protected override bool SupportsIEPECalSignal
{
get
{
switch (DASInfo.Modules[0].TypeOfModule)
{
case DFConstantsAndEnums.ModuleType.ProDIM:
case DFConstantsAndEnums.ModuleType.SLICEPro_TOM:
return false;
default:
return true;
}
}
}
public override bool CheckAAF(float rate)
{
var configRate = 0F;
try { configRate = ConfigData.Modules[0].AAFilterRateHz; }
catch (Exception) { }
if (configRate < 100000 && rate < 100000) { return true; }
else if (configRate == 200000 && rate == 200000) { return true; }
else if (configRate >= 100000 && configRate <= 199000 && rate >= 100000 && rate <= 199000) { return true; }
return false;
}
//eventually this should be read from an attribute
public int PageSize => 16384;
///
/// left over data are the samples broken between requests
/// since SLICE2 sends data by blocks, a complete sample for a list of channels could (and will) get broken
/// over multiple calls to QueryEventData. This temporarily holds the data between calls
/// the lock is not actually necessary, but is provided for sanity sake
///
protected static object LeftOverDataLock = new object();
protected ushort[] _leftOverData = null;
///
/// holds onto sample data between calls
/// original implementation was dynamic, but since we are
/// guaranteed by the block size that will never need to push twice in a row
/// it's quicker and easier just to hold an array between calls
///
///
public void PushLeftOverData(ushort[] data)
{
lock (LeftOverDataLock)
{
_leftOverData = data;
}
}
///
/// retrieves held over data between calls to QueryEventData
///
///
public ushort[] PopLeftOverData()
{
lock (LeftOverDataLock)
{
if (null == _leftOverData) { return new ushort[0]; }
else { return _leftOverData; }
}
}
protected virtual bool SupportsDiagnosticsMode => IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.DiagnosticsMode) && !IsDIM() && !IsTOM();
///
/// created for 11282, this just clears the diagnostics mode for das
/// this should be part of a generic services
///
protected override void TurnOffDiagnosticsMode()
{
if (SupportsDiagnosticsMode)
{
try
{
var scd = new SetChannelDiagnosticVoltageInsertion_SLICE2(this);
// This command will clear the voltage insertion on any capable device.
scd.DeviceID = 0;
scd.StackChannelList = new byte[0]; // Empty Set
scd.SyncExecute(); // Send empty set to device
}
catch (Exception ex)
{
APILogger.Log("Failed to unset VoltageInsertion flag", ex);
}
}
}
///
/// for SLICE2 we override WhatToDownload and transparently handle things our own way from the outside
///
private WhatToDownloadSlice2 _whatToDownload;
public override IDownloadRequest WhatToDownload
{
get => _whatToDownload;
set
{
_leftOverData = new ushort[0];
var numChannels = NumberOfDownloadChannels();
if (IsTOM())
{
//number of channels is a lie! don't trust it!
numChannels = 9;
}
_whatToDownload = new WhatToDownloadSlice2(value, numChannels, PageSize);
}
}
///
/// QueryEventData also is customized for SLICE2, it needs to perform SLICE2 specific data marshalling
///
///
protected override QueryEventDataBase GetQueryEventData()
{
return new QueryEventData_SLICE2(this, AbstractCommandBase.Default_IO_Timeout);
}
///
/// SLICE2 currently doesn't have a way of storing a GUID
/// we temporarily fake it by storing it in a file
/// SLICE2 really should hold onto the GUID ... GUID is more unique than test id ...
/// and this is already functionality in SLICE1 and soft baked into the TDAS implementation
///
///
protected override void SetEventGuid(Guid guid)
{
try
{
File.WriteAllText($"{SerialNumber}_GUID", guid.ToString());
APILogger.Log("SetEventGuid (", SerialNumber, "): ", guid.ToString());
}
catch (Exception ex)
{
APILogger.Log("Failed to set guid", ex);
}
}
///
/// overrided as SLICE2 will not hold onto GUID right now
///
///
///
protected override Guid GetEventGuid(int eventNum)
{
try
{
if (File.Exists(string.Format("{0}_GUID", SerialNumber)))
{
var s = File.ReadAllText(string.Format("{0}_GUID", SerialNumber));
APILogger.Log("GetEventGuid: ", SerialNumber, ": ", s);
return new Guid(s);
}
}
catch (Exception ex)
{
APILogger.Log("Failed to get event guid", SerialNumber, ex);
}
return Guid.Empty;
}
///
/// hardcoded constants right now ... maybe these belong in attributes in the firmware!
///
protected override uint MaxAAFilterRateHz => SLICE2.MaxAAFilterRateHz;
protected override uint MaxSampleRateHz => 1000000;
public override long MaxMemory()
{
if (null == DASInfo || 0 == DASInfo.NumberOfBytesPerSampleClock ||
null == DASInfo.MaxEventStorageSpaceInBytes) { return 0; }
return (long)(DASInfo.MaxEventStorageSpaceInBytes / DASInfo.NumberOfBytesPerSampleClock);
}
///
/// this is a mapping of module count to max sample rate for an SPD
/// http://manuscript.dts.local/f/cases/43548/Add-support-for-1-5MB-SPS-on-SPD
///
private static readonly Dictionary _SPDModulesToMaxRate = new Dictionary()
{
{1, 1500000 },
{2, 1200000 },
{3, 1000000 },
{4, 800000 },
{5, 700000 },
{6, 600000 }
};
///
/// the default max sample rate for an SPD if we don't have a matching module count
///
private const uint DEFAULT_MAX_SPS_SPD = 600000;
///
/// returns the max sample rate for a SLICE DIM
/// http://manuscript.dts.local/f/cases/43548/Add-support-for-1-5MB-SPS-on-SPD
///
private uint MaxSampleRateDIM()
{
try
{
if (_SPDModulesToMaxRate.ContainsKey(DASInfo.Modules.Length))
{
return _SPDModulesToMaxRate[DASInfo.Modules.Length];
}
}
catch( Exception ex )
{
APILogger.Log(ex);
}
return DEFAULT_MAX_SPS_SPD;
}
///
/// calculates the max sample rate
/// these are not exact max sample rates, but convenient close enough limits
/// drop 100k every module after 3 (starting at 800k)
///
///
public override uint MaxSampleRate(int numberOfConfiguredChannels)
{
try
{
if (IsDIM()) { return MaxSampleRateDIM(); }
}
catch( Exception ex)
{
APILogger.Log(ex);
}
if (DASInfo.Modules.Length <= 3) { return MaxSampleRateHz; }
else
{
return Convert.ToUInt32((12 - DASInfo.Modules.Length) * 100000);
}
}
private uint _minSampleRate = uint.MinValue;
public override uint MinSampleRate()
{
//15949 S6A when streaming does a whole bunch of unnecessary queries
//avoid getting the min sample rate while streaming, it will fail
if (GetIsStreaming())
{
_minSampleRate = 50;
return _minSampleRate;
}
if (uint.MinValue == _minSampleRate)
{
try
{
var qsa = new QuerySystemAttributeSLICE2(this);
qsa.Key = AttributeTypes.SystemAttributesSLICE2.MinimumSampleRate;
qsa.SyncExecute();
_minSampleRate = Convert.ToUInt16(qsa.Value);
}
catch (Exception)
{
_minSampleRate = 50;
}
}
return _minSampleRate;
}
public override uint MaxAAFilterRate()
{
return MaxAAFilterRateHz;
}
///
/// returns a GetRealtimeSamples class object appropriate for the DAS
/// this is partially necessary as SLICE2 uses a different channel ordering than SLICE1
///
///
/// whether realtime is polling for samples or not
///
protected override IGetRealtimeSamples GetRealtimeSamplesClass(DTS.Common.Interface.DASFactory.ICommunication iCommunication, bool bPolling = false)
{
if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.StartRealtimeStream) && !bPolling)
{
return new RealtimeStreamingNextSamples(iCommunication);
}
return new GetRealtimeSamplesSLICE2(iCommunication);
}
///
/// indicates whether the DAS supports streaming
/// 10572 implement SW side for single command streaming realtime
/// (note protocol >=152 required for streaming)
///
public override bool SupportsIndividualChannelRealtimeStreaming => IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.StartRealtimeStream);
public class GetRealtimeSamplesSLICE2 : GetRealtimeSamples
{
public GetRealtimeSamplesSLICE2(DTS.Common.Interface.DASFactory.ICommunication sock)
: base(sock, 2000) { }
public GetRealtimeSamplesSLICE2(DTS.Common.Interface.DASFactory.ICommunication sock, int TimeoutMillisec)
: base(sock, TimeoutMillisec) { }
protected override CommandReceiveAction WholePackage()
{
try
{
if (response.Status == DFConstantsAndEnums.CommandStatus.StatusNoError)
{
// Figure out the number of samples returned
var samples_returned = (response.ParameterLength - 8) / (_channels * 2);
_samplesReturned = samples_returned;
var data_bytes_returned = samples_returned * _channels * 2;
_data = new List(_channels);
// Grab the sample number
if (response.ParameterLength > 0)
{
response.GetParameter(0, out _sampleNumber);
}
// Create the data arrays by channel
if (samples_returned > 0)
{
for (var i = 0; i < _channels; i++)
{
_data.Add(new short[samples_returned]);
}
}
// Grab the data
var parameter = 8;
for (var sample = 0; sample < samples_returned; sample++)
{
for (var channel = 0; channel < _channels; channel++)
{
response.GetParameter(parameter, out ushort val);
//changed from unsigned to signed
_data[channel][sample] = (short)(((((val & 0x00FF) << 8) | ((val >> 8) & 0x00FF))) + 0x8000);
try
{
if (recorder is IDASCommunication idas && null != idas.DASInfo && null != idas.DASInfo.Modules &&
idas.DASInfo.Modules.Any())
{
if (idas.DASInfo.Modules[0].TypeOfModule ==
DFConstantsAndEnums.ModuleType.ProDIM)
{
var ch = idas.ConfigData.Modules[
idas.DASInfo.MapDASChannelNumber2ModuleArrayIndex(channel)].Channels[
idas.DASInfo.MapDASChannelNumber2ModuleChannelNumber(channel)];
if (null == ch)
{
continue;
}
}
}
}
catch (Exception ex)
{
APILogger.Log(ex);
}
parameter += 2;
}
}
return CommandReceiveAction.StopReceiving;
}
}
catch
{
_samplesReturned = 0;
}
return CommandReceiveAction.StopReceiving;
}
}
private readonly Dictionary SLICE2_MinimumProtocols = new Dictionary();
public override void InitMinProto()
{
try
{
_baseType = QueryBaseType();
}
catch (Exception) { }
// SLICE 2.0 Protocol Limitations
if (_baseType != (int)SLICERecorder.SLICEPRO_BaseType.SLICEPRO_DIM)
{ SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.DiangosShuntDAC] = SLICE2.MIN_PROTOCOL_VER; }
SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.QueryMSP430Firmware] = SLICE2.MIN_PROTOCOL_VER;
SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.MultipleAndHybridEvents] = SLICE2.MIN_PROTOCOL_VER;
SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.MultipleEvents] = SLICE2.MULTIPLE_EVENTS;
SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.AutoArm] = SLICE2.MULTIPLE_EVENTS;
SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.FlashCardInfo] = SLICE2.MIN_PROTOCOL_VER;
if (_baseType == SLICE2.SLICEPRO_DIM_BASETYPE || _baseType == SLICE2.SLICEPRO_TOM_BASETYPE || _baseType == SLICE2.SLICE1_5_BASETYPE)
{
//empty, just here for readability
}
else
{
//http://manuscript.dts.local/f/cases/18457/Add-Protocol-155-support-for-voltage-insertion-on-SLICE-PRO
SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.VoltageInsertion] = 155;
}
SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.SetDefaultMIF] = SLICE2.MIN_PROTOCOL_VER_GEN3;
SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.StackFirmwareUpdate] = SLICE2.STACK_FIRMWARE_UPDATE;
SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.FileData] = SLICE2.FILE_DATA;
SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.StackSensors] = SLICE2.STACK_SENSORS;
SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.BaseSystemTime] = SLICE2.MIN_PROTOCOL_VER_GEN3;
SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.PhysicalStartAddress] = SLICE2.MIN_PROTOCOL_VER;
SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.TestCommunication] = SLICE2.MIN_PROTOCOL_VER;
SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.StackLowPowerMode] = SLICE2.MIN_PROTOCOL_VER;
if (_baseType == SLICE2.SLICE1_5_BASETYPE || _baseType == SLICE2.SLICEPRO_DIM_BASETYPE || _baseType == SLICE2.SLICEPRO_TOM_BASETYPE)
{
//empty, just here for readability
}
else { SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.StackChannelTypeConfiguration] = SLICE2.MULTIPLE_EVENTS; }
SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.SetRealtimeSampleRate] = SLICE2.MIN_PROTOCOL_VER;
SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.SLICE2_OneWireID] = SLICE2.SLICE2_ONE_WIRE_ID;
SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.HardwareRevision] = SLICE2.MIN_PROTOCOL_VER_GEN3;
SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.HardwareConfiguration] = SLICE2.MULTIPLE_EVENTS;
if (_baseType == SLICE2.SLICE1_5_BASETYPE || _baseType == SLICE2.SLICEPRO_DIM_BASETYPE || _baseType == SLICE2.SLICEPRO_TOM_BASETYPE)
{
//empty, just here for readability
}
else { SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.ProgramStackChannels] = SLICE2.STACK_SENSORS; }
SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.EventFaultFlags] = SLICE2.MIN_PROTOCOL_VER;
SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.EventArmAttempts] = SLICE2.EVENT_ARM_ATTEMPTS;
SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.QueryActualSampleRateImmediate] = SLICE2.MIN_PROTOCOL_VER;
SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.InitHardwareInputLines] = SLICE2.MIN_PROTOCOL_VER;
SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.VoltageSysAttributes] = SLICE2.MIN_PROTOCOL_VER;
SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.DiagnosticTwoVoltExcitation] = SLICE2.DIAGNOSTIC_TWO_VOLT_EXCITATION;
if (_baseType == SLICE2.SLICEPRO_DIM_BASETYPE || _baseType == SLICE2.SLICEPRO_TOM_BASETYPE || _baseType == SLICE2.SLICE1_5_BASETYPE)
{
//empty, just here for readability
}
else { SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.ExcitationLevel] = SLICE2.FILE_DATA; }
if (_baseType == SLICE2.SLICEPRO_DIM_BASETYPE || _baseType == SLICE2.SLICEPRO_TOM_BASETYPE)
{
//empty, just here for readability
}
else { SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.LevelTrigger] = SLICE2.MIN_PROTOCOL_VER; }
SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.AttributeStoreBlocks] = SLICE2.MIN_PROTOCOL_VER;
SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.QueryArmAndTriggerStatus_VoltageReadings] = SLICE2.MIN_PROTOCOL_VER;
SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.MaxEvents] = SLICE2.MIN_PROTOCOL_VER;
SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.AutoArmDiagnosticDelay] = SLICE2.MIN_PROTOCOL_VER;
SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.StackChannelAutoArmDiagLevel] = SLICE2.FILE_DATA;
SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.FlashClear] = SLICE2.MIN_PROTOCOL_VER;
SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.DiagnosticsMode] = SLICE2.FILE_DATA;
SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.MultipleSamplesRealtime] = SLICE2.MIN_PROTOCOL_VER;
SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.ResetAttributeStore] = SLICE2.MIN_PROTOCOL_VER;
SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.QueryArmAndTriggerStatus_TimeLeftInArm] = SLICE2.QUERY_ARM_AND_TRIGGER_STATUS_TIME_LEFT_IN_ARM;
SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.MeasureInternalOffset] = SLICE2.MEASURE_INTERNAL_OFFSET;
SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.MeasureInternalOffsetSwitches] = SLICE2.MIN_PROTOCOL_VER_GEN3;
SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.QueryBatterySOC] = SLICE2.MIN_PROTOCOL_VER_GEN3;
SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.StartRecDelayInSecond] = SLICE2.START_REC_DELAY_IN_SECONDS;
SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.StartRealtimeStream] = SLICE2.START_REALTIME_STREAM;
SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.HalfBridgeSigPlusSupport] = SLICE2.HALF_BRIDGE_SIG_PLUS_SUPPORT;
SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.AutoArmRepeatEnable] = SLICE2.MIN_PROTOCOL_VER;
SLICE2_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.GenerateEvent] = 156;
MinimumProtocols = SLICE2_MinimumProtocols;
}
private enum SquibFireModes
{
DISABLED = 0,
CapDischarge = 1,
ConstantCurrent = 2,
CapDischargeInternal = 3,
ConstantCurrentInternal = 4
}
private enum SquibMeasurementTypes
{
InitiationSignal = 0x00,
SquibVoltage = 0x01
}
private enum TOMDigitalOutputModes
{
Disabled = 0,
LHT = 1,
HLT = 2,
CCNC = 3,
CCNO = 4
}
private readonly float MaxSquibDurationMS = 40; //Kids need limits, so do unlimited-duration squibs
protected void SetArmDisableShortCheck()
{
if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.IgnoreShortedStartEvent))
{
var ignoreShortedStart = Convert.ToByte(IgnoreShortedStart);
var ssa = new SetSystemAttributeSLICE2(this, AbstractCommandBase.Default_IO_Timeout);
ssa.SetValue(AttributeTypes.SystemAttributesSLICE2.ArmDisableStartShortCheck, ignoreShortedStart, true);
ssa.SyncExecute();
}
if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.IgnoreShortedStartEvent))
{
var ignoreShortedTrigger = Convert.ToByte(IgnoreShortedTrigger);
var ssa = new SetSystemAttributeSLICE2(this, AbstractCommandBase.Default_IO_Timeout);
ssa.SetValue(AttributeTypes.SystemAttributesSLICE2.ArmDisableTriggerShortCheck, ignoreShortedTrigger, true);
ssa.SyncExecute();
}
}
///
/// the TOM is so different it didn't make sense to reuse the old AsyncConfigure, so I created one
/// specifically for TOM
///
///
private void AsyncConfigureTOM(object configAsyncInfo)
{
var info = configAsyncInfo as SliceConfigServiceAsyncInfo;
var progressValue = 0;
var bReleased = true;
PresetSampleRate();
SetVoltageRequirements();
SetPolarity();
try
{
Lock();
bReleased = false;
// report progress
progressValue = 5;
info.Progress(progressValue);
var config = GetConfigAttributes(this);// new ConfigAttributes(this);
// only set attributes to default if we haven't run diagnostics yet
if (!DiagnosticsHasBeenRun)
{
config.PurgeStaleData(this);
}
// store some attributes
if (ConfigData.Modules.Length > 0)
{
config.SampleRate = ConfigData.Modules[0].SampleRateHz;
//System.Diagnostics.Trace.WriteLine("Sample Rate: " + config.SampleRate);
var actualSampleRate = (float)ConfigData.Modules[0].SampleRateHz;
actualSampleRate = config.ActualSampleRate;
config.AAFilter = ConfigData.Modules[0].AAFilterRateHz;
config.TestID = ConfigData.TestID.TrimEnd(new char[] { '\0' });
config.TestSetupUniqueId = ConfigData.TestSetupUniqueId?.TrimEnd('\0');
config.TestDescription = ConfigData.Description;
config.PreTriggerSamples = (ulong)(ConfigData.Modules[0].PreTriggerSeconds * actualSampleRate);
config.PostTriggerSamples = (ulong)(ConfigData.Modules[0].PostTriggerSeconds * actualSampleRate);
SetArmMode(ConfigData.Modules[0].RecordingMode);
// get scale factors
var scaleFactors = config.GetScaleFactors();
for (var moduleIdx = 0; moduleIdx < ConfigData.Modules.Length; moduleIdx++)
{
for (var channelIdx = 0; channelIdx < ConfigData.Modules[moduleIdx].Channels.Length; channelIdx++)
{
if (ConfigData.Modules[moduleIdx].Channels[channelIdx] is OutputSquibChannel)
{
//var analog = ConfigData.Modules[moduleIdx].Channels[channelIdx] as AnalogInputDASChannel;
var dasChannelNumber = DASInfo.MapModuleArrayIndexAndChannelNum2DASChannel(moduleIdx, channelIdx);
//analog.ScalefactorMilliVoltsPerADC = scaleFactors[dasChannelNumber];
var squib = ConfigData.Modules[moduleIdx].Channels[channelIdx] as OutputSquibChannel;
squib.ScaleFactorMv = scaleFactors[dasChannelNumber];
}
}
progressValue++;
info.Progress(Math.Min(19, progressValue));
}
}
SetArmDisableShortCheck();
foreach (var module in ConfigData.Modules)
{
for (var i = 0; i < 8; i += 2)
{
var voltageSquib = module.Channels[i] as OutputSquibChannel;
var currentSquib = module.Channels[i + 1] as OutputSquibChannel;
System.Diagnostics.Trace.Assert((null != currentSquib && null != voltageSquib), "we are missing a voltage or a squib channel");
var sscc = new SetSquibChannelConfiguraion(this);
sscc.SetSquibChannelID(Convert.ToByte(i / 2));//we want 0-3 for channel id
if (currentSquib.ConfigurationMode == DFConstantsAndEnums.ConfigMode.Normal)
{
switch (voltageSquib.MeasurementType)
{
default:
case SquibMeasurementType.VOLTAGE:
sscc.SetSquibRecordMode(Convert.ToByte(SquibMeasurementTypes.SquibVoltage));
break;
case SquibMeasurementType.INIT_SIGNAL:
sscc.SetSquibRecordMode(Convert.ToByte(SquibMeasurementTypes.InitiationSignal));
break;
}
switch (currentSquib.FireMode)
{
case SquibFireMode.CAP:
{
if (info.DummyConfig) { sscc.SetSquibOutputMode(Convert.ToByte(SquibFireModes.CapDischargeInternal)); }
else { sscc.SetSquibOutputMode(Convert.ToByte(SquibFireModes.CapDischarge)); }
}
break;
case SquibFireMode.CONSTANT:
{
if (info.DummyConfig) { sscc.SetSquibOutputMode(Convert.ToByte(SquibFireModes.ConstantCurrentInternal)); }
else { sscc.SetSquibOutputMode(Convert.ToByte(SquibFireModes.ConstantCurrent)); }
}
break;
//default: sscc.SetSquibOutputMode(Convert.ToByte(SquibFireModes.DISABLED)); break;
}
if (currentSquib.LimitDuration) { sscc.SetSquibDurationMs(Convert.ToSingle(currentSquib.DurationMS)); }
else { sscc.SetSquibDurationMs(MaxSquibDurationMS); }
sscc.SetSquibDelayMs(Convert.ToSingle(currentSquib.DelayMS));
sscc.SetSquibResMax(Convert.ToSingle(currentSquib.SquibToleranceHigh));
sscc.SetSquibResMin(Convert.ToSingle(currentSquib.SquibToleranceLow));
sscc.SetSquibOutputCurrentAmp(Convert.ToSingle(currentSquib.SquibOutputCurrent));
}
else
{
sscc.SetSquibOutputMode(Convert.ToByte(SquibFireModes.DISABLED));
}
sscc.SyncExecute();
}
for (var i = 8; i < module.Channels.Length; i++)
{
var sscc = new SetDigitalChannelConfiguration(this);
sscc.SetDigitalChannelID(Convert.ToByte(i - 8));
var dout = module.Channels[i] as OutputTOMDigitalChannel;
System.Diagnostics.Trace.Assert(null != dout, "Missing a digital output channel");
if ((dout.ConfigurationMode == DFConstantsAndEnums.ConfigMode.Normal) ||
(dout.ConfigurationMode == DFConstantsAndEnums.ConfigMode.DummyArm))
{
if (!info.ConfigureDigitalOutput && info.DummyConfig)
{
sscc.SetDigitalOutputMode(Convert.ToByte(TOMDigitalOutputModes.Disabled));
}
else
{
switch (dout.OutputMode)
{
case DigitalOutputModes.CCNC: sscc.SetDigitalOutputMode(Convert.ToByte(TOMDigitalOutputModes.CCNC)); break;
case DigitalOutputModes.CCNO: sscc.SetDigitalOutputMode(Convert.ToByte(TOMDigitalOutputModes.CCNO)); break;
case DigitalOutputModes.FVHL: sscc.SetDigitalOutputMode(Convert.ToByte(TOMDigitalOutputModes.HLT)); break;
case DigitalOutputModes.FVLH: sscc.SetDigitalOutputMode(Convert.ToByte(TOMDigitalOutputModes.LHT)); break;
case DigitalOutputModes.NONE: sscc.SetDigitalOutputMode(Convert.ToByte(TOMDigitalOutputModes.Disabled)); break;
}
}
}
else
{
sscc.SetDigitalOutputMode(Convert.ToByte(TOMDigitalOutputModes.Disabled));
}
if ((dout.ConfigurationMode == DFConstantsAndEnums.ConfigMode.Normal) ||
((dout.DelayMS + dout.DurationMS) <= (ConfigData.Modules[0].PostTriggerSeconds * 1000)))
{
//As long as the delay + duration is less than the post-trigger seconds (1 if CB, 10 if Recorder), use it even if it's just a trigger check
sscc.SetDigitalDelayMs(Convert.ToSingle(dout.DelayMS));
if (dout.LimitDuration) { sscc.SetDigitalDurationMs(Convert.ToSingle(dout.DurationMS)); }
else { sscc.SetDigitalDurationMs(0); }
}
else
{
// Since we're not going to let trigger check testing go longer than post-trigger seconds, just fire immediately
sscc.SetDigitalDelayMs(0);
sscc.SetDigitalDurationMs(Convert.ToSingle(10));
}
sscc.SyncExecute();
}
}
progressValue = 30;
info.Progress(progressValue);
// now store the data
var configStr = ConfigurationData.Serialize((ConfigurationData)ConfigData);
config.StoreXMLConfig(configStr, info.EventConfig ? ConfigAttributes.FileStore.Event : ConfigAttributes.FileStore.Diagnostic);
ConfigurationData.SetConfiguration(this, configStr,
info.EventConfig
? (int)ConfigAttributes.FileStore.Event
: (int)ConfigAttributes.FileStore.Diagnostic);
//dummy read to flush buffer, per Loc
try
{
var qsa = new QuerySystemAttribute(this);
qsa.Key = AttributeTypes.SystemAttributes.SerialNumber;
qsa.SyncExecute();
}
catch (Exception) { }
progressValue = 60;
info.Progress(progressValue);
try
{
var retrieved = config.RetrieveXMLConfig(info.EventConfig ? ConfigAttributes.FileStore.Event : ConfigAttributes.FileStore.Diagnostic, this);
var utf8Version = Encoding.UTF8.GetString(Encoding.UTF8.GetBytes(configStr));
if (!retrieved.Equals(utf8Version))
{
APILogger.Log($"read does not match write {SerialNumber}", retrieved, configStr);
}
}
catch (Exception ex)
{
APILogger.Log("failed to read config", ex);
}
//this only needs to be done in datapro, which doesn't set position 0
//we'll have to clean up the code a bit to use it if we want for sliceware.
//config.StoreXMLConfig(configStr, !info.EventConfig);
config.StoreXMLConfig(configStr, info.EventConfig ? ConfigAttributes.FileStore.Diagnostic : ConfigAttributes.FileStore.Event);
progressValue = 70;
info.Progress(progressValue);
//dummy read to flush buffer, per Loc
try
{
var qsa = new QuerySystemAttribute(this);
qsa.Key = AttributeTypes.SystemAttributes.SerialNumber;
qsa.SyncExecute();
}
catch (Exception) { }
progressValue = 80;
info.Progress(progressValue);
try
{
var retrieved = config.RetrieveXMLConfig(info.EventConfig ? ConfigAttributes.FileStore.Event : ConfigAttributes.FileStore.Diagnostic, this);
var utf8Version = Encoding.UTF8.GetString(Encoding.UTF8.GetBytes(configStr));
if (!retrieved.Equals(utf8Version))
{
APILogger.Log($"read does not match write {SerialNumber}", retrieved, configStr);
}
}
catch (Exception ex)
{
APILogger.Log("failed to read config", ex);
}
ConfigureHasBeenRun = true;
if (info.DiscardDiagnostics) { DiagnosticsHasBeenRun = false; }
// report success'
if (!bReleased) { bReleased = true; Release(); }
progressValue = 90;
info.Progress(progressValue);
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) { bReleased = true; Release(); } }
info.Progress(100);
info.Success();
}
protected override float GetLevelTriggerThreshold(AnalogInputDASChannel analog, IDiagnosticResult diagnostics, double thresholdeu, double MvPerEu)
{
//I have modified this to be modeled after DataScaler and how it gets EU, it's a bit different than before ...
var dataZeroLevelADC = diagnostics.GetExpectedDataZeroLevelADC(analog.ZeroMethod);
var initialOffsetmVInADC = 0D;
var io = new InitialOffset();
io.FromDbSerializeString(analog.InitialOffset);
if (io.Form == InitialOffsetTypes.EUAtMV)
{
initialOffsetmVInADC = io.MV / diagnostics.ScalefactorMilliVoltsPerADC;
}
MvPerEu = analog.SensitivityMilliVoltsPerEU;
var adcToEUScalingFactor = (diagnostics.ScalefactorMilliVoltsPerADC / (analog.IsProportionalToExcitation
? analog.SensitivityMilliVoltsPerEU * analog.FactoryExcitationVolts : MvPerEu))
* (analog.AtCapacity ? analog.CapacityOutputIsBasedOn : 1.0)
* (analog.IsInverted ? -1.0 : 1.0);
var negativeSensitivity = analog.SensitivityMilliVoltsPerEU < 0;
var negativePolarity = analog.IsInverted;
var eu = 0D;
var adc = 0D;
var mV = 0D;
//16272 3.1 LevelTrigger issues
//there must be some double negative occurring in this case that doesn't occur in others?
if (negativeSensitivity && !negativePolarity)
{
eu = -1D * thresholdeu - analog.InitialEU - dataZeroLevelADC * (double)adcToEUScalingFactor +
initialOffsetmVInADC * (double)adcToEUScalingFactor;
adc = eu / (double)adcToEUScalingFactor;
mV = adc * diagnostics.ScalefactorMilliVoltsPerADC;
}
else
{
eu = thresholdeu - analog.InitialEU + dataZeroLevelADC * (double)adcToEUScalingFactor +
initialOffsetmVInADC * (double)adcToEUScalingFactor;
adc = eu / (double)adcToEUScalingFactor;
mV = adc * diagnostics.ScalefactorMilliVoltsPerADC;
}
var sb = new StringBuilder();
sb.AppendFormat("{0} {1} {2}:{3}\r\n", DateTime.Now.ToShortDateString(), DateTime.Now.ToShortTimeString(), SerialNumber, analog.Number);
sb.AppendFormat("DataZeroLevelADC={0}", dataZeroLevelADC);
sb.AppendFormat("ScaleFactormVPerADC={0}\r\n", diagnostics.ScalefactorMilliVoltsPerADC);
sb.AppendFormat("MvPerEU={0}\r\n", MvPerEu);
sb.AppendFormat("adcToEUScalingFactor = {0}\r\n", adcToEUScalingFactor);
sb.AppendFormat("eu={0}\r\n", eu);
sb.AppendFormat("ZeroMVINADC={0}", diagnostics.ZeroMVInADC);
sb.AppendFormat("adc={0}\r\n", adc);
sb.AppendFormat("mv={0}\r\n", mV);
LevelTriggerLogging.LevelTriggerLog(sb.ToString());
return Convert.ToSingle(mV);
}
protected bool ShouldQueryFeatureConfig()
{
return !(this is EthernetSlice6 || this is EthernetSlice6Air || this is EthernetSlice6AirBridge || this is EthernetSlice6DB || this is EthernetSlice6DB3 ||
this is WinUSBTsrAir || this is EthernetTsrAir);
}
protected void ConfigureDiagChannels(byte[] diagnosticChannels)
{
if (SupportsDiagnosticsMode)
{
var scd = new SetChannelDiagnosticVoltageInsertion_SLICE2(this, AbstractCommandBase.Default_IO_Timeout);
if (!diagnosticChannels.Any())
{
// This command will clear the voltage insertion on any capable device.
scd.DeviceID = 0;
scd.StackChannelList = new byte[0]; // Empty Set
scd.SyncExecute(); // Send empty set to device
}
else
{
// The following command will set the voltage insertion if there are diagnosticChannels
scd = new SetChannelDiagnosticVoltageInsertion_SLICE2(this, AbstractCommandBase.Default_IO_Timeout);
scd.DeviceID = 0;
scd.StackChannelList = diagnosticChannels; // List of diagnostic channels
scd.SyncExecute();
}
}
}
protected void PresetSampleRate()
{
//this is a bit of a hack, but lets make sure sample rate gets set first
try
{
if (ConfigData.Modules.Length > 0)
{
var config = GetConfigAttributes(this);
config.SampleRate = ConfigData.Modules[0].SampleRateHz;
var actualSampleRate = (float)ConfigData.Modules[0].SampleRateHz;
actualSampleRate = config.ActualSampleRate;
config.AAFilter = ConfigData.Modules[0].AAFilterRateHz;
}
}
catch (Exception ex) { APILogger.Log("failed to pre-set sample rate", ex); }
}
private void ReconfigureIfNeeded(ref bool bModified)
{
if (_hardwareRevision == GEN3_REV_B)
{
if (!ShouldQueryFeatureConfig())
{
_featureConfigValue = 1;
}
else
{
var queryFeatureConfig = new QuerySystemAttribute_Slice2Bridge(this);
queryFeatureConfig.Key = AttributeTypes.SystemAttributes_Bridge_SLICE2.FeatureConfig;
queryFeatureConfig.DeviceID = 1;
queryFeatureConfig.SyncExecute();
bModified = (ushort)queryFeatureConfig.Value == 1;
}
}
}
protected virtual bool AdjustInputRange(AnalogInputDASChannel analog)
{
//if our range is not at an edge of the gains table, then
//the requested range may need to be manipulated for
//10080 Implement SLICE PRO SIM Gain Limitation for Gen 3 SIMs
switch (analog.TypeOfBridge)
{
case SensorConstants.BridgeType.FullBridge:
case SensorConstants.BridgeType.HalfBridge:
case SensorConstants.BridgeType.HalfBridge_SigPlus:
case SensorConstants.BridgeType.QuarterBridge:
return true;
}
return false;
}
protected virtual bool RequiresNon0QualificationSamples { get => false; }
protected virtual void CommonRangeWork(AnalogInputDASChannel analog,
bool bModified,
float[] rangeArray,
byte dasChannelNumber,
double MvPerEU,
IDASModule module
)
{
var requestedRange = analog.DesiredRangeWithHeadroomEU * MvPerEU;
var nominalRanges = GetNominalRanges(analog.TypeOfBridge, bModified);
//if our range is at one of the edges, there's nothing we can do, just take the edge
if (requestedRange > nominalRanges[0])
{
requestedRange = nominalRanges[0];
}
else if (requestedRange < nominalRanges[nominalRanges.Length - 1])
{
requestedRange = nominalRanges[nominalRanges.Length - 1];
}
else
{
if (AdjustInputRange(analog))
{
requestedRange = AdjustInputRangeIfNeeded(requestedRange, bModified);
}
}
rangeArray[dasChannelNumber] = Convert.ToSingle(requestedRange);
}
protected void CommonConfigureWork(List diagnosticChannels,
int[] qualificationSamples,
ref bool bReleased,
SliceConfigServiceAsyncInfo info,
byte[] bridgeModeArray,
bool[] IsACCoupledArray,
ushort[] BridgeResistanceArray,
ref bool bModified,
float[] rangeArray,
bool[] enableUpperLevelTriggerThreshold,
float[] upperLevelTriggerThreshold,
bool[] enableLowerLevelTriggerThreshold,
float[] lowerLevelTriggerThreshold,
bool[] bridgeACCouplingArray)
{
for (var moduleIdx = 0; moduleIdx < ConfigData.Modules.Length; moduleIdx++)
{
var module = ConfigData.Modules[moduleIdx];
// configure the range for this bridge
for (var channelIdx = 0; channelIdx < module.Channels.Length; channelIdx++)
{
var channel = module.Channels[channelIdx];
var dasChannelNumber = DASInfo.MapModuleArrayIndexAndChannelNum2DASChannel(moduleIdx, channelIdx);
//diagnosticChannels[dasChannelNumber] = channel.DiagnosticsMode ? (byte)1 : (byte)0;
if (channel.DiagnosticsMode) { diagnosticChannels.Add(dasChannelNumber); }
if (RequiresNon0QualificationSamples)
{
if (0 == channel.QualificationSamples) { channel.QualificationSamples = 5; }
}
qualificationSamples[dasChannelNumber] = channel.QualificationSamples;
if (channel.ConfigurationMode != DFConstantsAndEnums.ConfigMode.Disabled &&
(channel.ConfigurationMode == DFConstantsAndEnums.ConfigMode.Normal
|| channel.ConfigurationMode == DFConstantsAndEnums.ConfigMode.DummyArm))
{
if (!(channel is AnalogInputDASChannel))
{
if (!bReleased) { bReleased = true; Release(); }
// "Configure: Slice bridge modules must only contain analog input channels"
info.Error("Slice bridge modules must only contain analog input channels");
return;
}
var analog = channel as AnalogInputDASChannel;
switch (analog.TypeOfBridge)
{
case SensorConstants.BridgeType.FullBridge:
bridgeModeArray[dasChannelNumber] = 0;
bridgeACCouplingArray[dasChannelNumber] = analog.ACCouplingModeEnabled;
break;
case SensorConstants.BridgeType.HalfBridge:
bridgeModeArray[dasChannelNumber] = 1;
bridgeACCouplingArray[dasChannelNumber] = analog.ACCouplingModeEnabled;
break;
case SensorConstants.BridgeType.IEPE:
bridgeModeArray[dasChannelNumber] = 0;
bridgeACCouplingArray[dasChannelNumber] = false;
break;
case SensorConstants.BridgeType.QuarterBridge:
bridgeModeArray[dasChannelNumber] = 2;
bridgeACCouplingArray[dasChannelNumber] = analog.ACCouplingModeEnabled;
break;
case SensorConstants.BridgeType.DigitalInput:
bridgeModeArray[dasChannelNumber] = 0;
analog.DigitalInputChannel = true;
bridgeACCouplingArray[dasChannelNumber] = false;
break;
case SensorConstants.BridgeType.HalfBridge_SigPlus:
bridgeModeArray[dasChannelNumber] = 3;
bridgeACCouplingArray[dasChannelNumber] = false;
break;
case SensorConstants.BridgeType.RTC:
bridgeModeArray[dasChannelNumber] = 8;
bridgeACCouplingArray[dasChannelNumber] = false;
break;
default:
bridgeModeArray[dasChannelNumber] = 0;
bridgeACCouplingArray[dasChannelNumber] = false;
break;
}
if (analog.IEPEChannel)
{
IsACCoupledArray[dasChannelNumber] = analog.CouplingMode == SensorConstants.CouplingModes.AC;
}
else { IsACCoupledArray[dasChannelNumber] = false; }
if (SensorConstants.IsTSRAirLowGChannel(module.SerialNumber()))
{
if (analog.ACCouplingModeEnabled && analog.CouplingMode == SensorConstants.CouplingModes.AC)
{
IsACCoupledArray[dasChannelNumber] = true;
}
}
switch (analog.TypeOfBridge)
{
case SensorConstants.BridgeType.HalfBridge:
case SensorConstants.BridgeType.HalfBridge_SigPlus:
BridgeResistanceArray[dasChannelNumber] = SLICE_HALFBRIDGE_RESISTANCE;
break;
case SensorConstants.BridgeType.RTC:
BridgeResistanceArray[dasChannelNumber] = 0;
break;
default:
BridgeResistanceArray[dasChannelNumber] = (ushort)analog.BridgeResistanceOhms;
break;
}
if (analog.SensitivityMilliVoltsPerEU != 0)
{
var MvPerEu = GetMvPerEUNormalized(analog);
CommonRangeWork(analog, bModified, rangeArray, dasChannelNumber, MvPerEu, module);
//
// push level trigger settings out to hardware
//
//if (enableLowerLevelTriggerThreshold[dasChannelNumber] = (null != analog.TriggerBelowThresholdEu))
if (null != analog.TriggerBelowThresholdEu)
{
//
// Careful here... if remove offset is NOT active, and zero method is "none",
// then just send the level trigger as specified down to the hardware. Othewise,
// we'll need to apply the appropriate offset to it so it triggers as expected.
//
if ((analog.IsInverted || analog.SensitivityMilliVoltsPerEU < 0)
&& !(analog.IsInverted && analog.SensitivityMilliVoltsPerEU < 0))
{
enableUpperLevelTriggerThreshold[dasChannelNumber] = true;
try
{
var diagnostics = analog.Diagnostics;
upperLevelTriggerThreshold[dasChannelNumber] = GetLevelTriggerThreshold(analog, diagnostics, (double)analog.TriggerBelowThresholdEu, MvPerEu);
if (analog.SensitivityMilliVoltsPerEU < 0)
{
upperLevelTriggerThreshold[dasChannelNumber] = -upperLevelTriggerThreshold[dasChannelNumber];
}
}
catch (ApplicationException ex)
{
if (null != Exceptional.ExtractFirstExceptionOfTypeFromExceptionTree(ex))
upperLevelTriggerThreshold[dasChannelNumber] = (float)analog.TriggerBelowThresholdEu;
else throw ex;
}
}
else
{
enableLowerLevelTriggerThreshold[dasChannelNumber] = true;
try
{
var diagnostics = analog.Diagnostics;
lowerLevelTriggerThreshold[dasChannelNumber] = GetLevelTriggerThreshold(analog, diagnostics, (double)analog.TriggerBelowThresholdEu, MvPerEu);
}
catch (ApplicationException ex)
{
if (null != Exceptional.ExtractFirstExceptionOfTypeFromExceptionTree(ex))
lowerLevelTriggerThreshold[dasChannelNumber] = (float)analog.TriggerBelowThresholdEu;
else throw ex;
}
}
}
else
{
if ((analog.IsInverted || analog.SensitivityMilliVoltsPerEU < 0)
&& !(analog.IsInverted && analog.SensitivityMilliVoltsPerEU < 0)
)
{
upperLevelTriggerThreshold[dasChannelNumber] = 0;
enableUpperLevelTriggerThreshold[dasChannelNumber] = false;
}
else
{
lowerLevelTriggerThreshold[dasChannelNumber] = 0;
enableLowerLevelTriggerThreshold[dasChannelNumber] = false;
}
}
if (null != analog.TriggerAboveThresholdEu)
{
//
// Careful here... if remove offset is NOT active, and zero method is "none",
// then just send the level trigger as specified down to the hardware. Othewise,
// we'll need to apply the appropriate offset to it so it triggers as expected.
//
if ((analog.IsInverted || analog.SensitivityMilliVoltsPerEU < 0)
&& !(analog.IsInverted && analog.SensitivityMilliVoltsPerEU < 0))
{
enableLowerLevelTriggerThreshold[dasChannelNumber] = true;
try
{
var diagnostics = analog.Diagnostics;
lowerLevelTriggerThreshold[dasChannelNumber] = GetLevelTriggerThreshold(analog, diagnostics, (double)analog.TriggerAboveThresholdEu, MvPerEu);
if (analog.SensitivityMilliVoltsPerEU < 0)
{
lowerLevelTriggerThreshold[dasChannelNumber] = -lowerLevelTriggerThreshold[dasChannelNumber];
}
}
catch (ApplicationException ex)
{
if (null != Exceptional.ExtractFirstExceptionOfTypeFromExceptionTree(ex))
lowerLevelTriggerThreshold[dasChannelNumber] = (float)analog.TriggerAboveThresholdEu;
else throw ex;
}
}
else
{
enableUpperLevelTriggerThreshold[dasChannelNumber] = true;
try
{
var diagnostics = analog.Diagnostics;
upperLevelTriggerThreshold[dasChannelNumber] = GetLevelTriggerThreshold(analog, diagnostics, (double)analog.TriggerAboveThresholdEu, MvPerEu);
}
catch (ApplicationException ex)
{
if (null != Exceptional.ExtractFirstExceptionOfTypeFromExceptionTree(ex))
upperLevelTriggerThreshold[dasChannelNumber] = (float)analog.TriggerAboveThresholdEu;
else throw ex;
}
}
}
else
{
if ((analog.IsInverted || analog.SensitivityMilliVoltsPerEU < 0)
&& !(analog.IsInverted && analog.SensitivityMilliVoltsPerEU < 0))
{
lowerLevelTriggerThreshold[dasChannelNumber] = 0;
enableLowerLevelTriggerThreshold[dasChannelNumber] = false;
}
else
{
upperLevelTriggerThreshold[dasChannelNumber] = 0;
enableUpperLevelTriggerThreshold[dasChannelNumber] = false;
}
}
}
else
{
rangeArray[dasChannelNumber] = 0;
}
}
else if (channel.ConfigurationMode == DFConstantsAndEnums.ConfigMode.StreamOut)
{
try
{
var streamOut = channel as StreamOutputDASChannel;
streamOut.UDPProfileName = info.StreamProfiles[this].ToString();
streamOut.UDPTimeChannelId = info.TimeChannelIds[this];
streamOut.UDPDataChannelId = info.DataChannelIds[this];
streamOut.IRIGTimeDataPacketIntervalMs = info.IrigTDPIntervals[this];
streamOut.UDPAddress = info.Addresses[this];
streamOut.UDPTmNSConfig = info.TmNSConfigs[this];
streamOut.TMATSIntervalMs = info.TMATSIntervalMs[this];
}
catch( Exception ex)
{
APILogger.Log("Failed to set stream output", ex);
}
}
else if (channel.ConfigurationMode == DFConstantsAndEnums.ConfigMode.UART)
{
try
{
var uart = channel as UARTInputDASChannel;
if (info.BaudRates.ContainsKey(this))
{
uart.BaudRate = info.BaudRates[this];
uart.DataBits = info.DataBits[this];
uart.StopBits = info.StopBits[this];
uart.Parity = info.Parities[this];
uart.FlowControl = info.FlowControls[this];
uart.DataFormat = info.DataFormats[this];
}
}
catch (Exception ex)
{
APILogger.Log("Failed to set UART", ex);
}
}
else if (channel.ConfigurationMode == DFConstantsAndEnums.ConfigMode.StreamIn)
{
var streamIn = channel as StreamInputDASChannel;
streamIn.UDPAddress = ConfigData.UDPReceiveAddress;
}
else
{
rangeArray[dasChannelNumber] = 0;
bridgeModeArray[dasChannelNumber] = 0;
BridgeResistanceArray[dasChannelNumber] = 0;
bridgeACCouplingArray[dasChannelNumber] = false;
enableLowerLevelTriggerThreshold[dasChannelNumber] = false;
enableUpperLevelTriggerThreshold[dasChannelNumber] = false;
lowerLevelTriggerThreshold[dasChannelNumber] = 0;
upperLevelTriggerThreshold[dasChannelNumber] = 0;
}
}
}
}
private bool IsStreamingOnlyMode()
{
if (null == ConfigData) { return false; }
if (null == ConfigData.Modules) { return false; }
if (0 >= ConfigData.Modules.Length) { return false; }
return ConfigData.Modules[0].RecordingMode == DFConstantsAndEnums.RecordingMode.S6A_DeviceStreamingOnly
|| ConfigData.Modules[0].RecordingMode == DFConstantsAndEnums.RecordingMode.Streaming;
}
protected virtual void SetStreamingRealtimeSampleRate(SliceConfigServiceAsyncInfo info)
{
//FB14613: When arming for stream-on-reboot, set RealtimeSampleRate to the DAS' test sample rate
try
{
//we don't need to set the realtime sample rate for record and stream recording modes, they won't use it
if (IsStreamingOnlyMode() &&
IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.SetRealtimeSampleRate))
{
var sampleRate = ConfigData.Modules[0].SampleRateHz;
var ssr = new SetRealtimeSampleRate(this);
ssr.SetValue(sampleRate);
ssr.SyncExecute();
//FB14966 - Can't get DataPRO to program RealtimeAAFilterFrequencyHz when running a streaming slice 6 air test
//Pass through Properties.SLICETurnOffAAFRealtime and use similar logic to TestTemplate.GetRealtimeAAFForHardware for the frequency
var aafRate = info.TurnOffAAFRealtime && DFConstantsAndEnums.SupportsTurnOffAAFRealtime(this)
? MaxAAFilterRateHz
: ConfigData.Modules[0].AAFilterRateHz;
var saaf = new SetArmAttribute(this);
saaf.SetValue(AttributeTypes.ArmAndEventAttributes.RealtimeAAFilterFrequencyHz, aafRate,
true);
saaf.SyncExecute();
}
}
catch (Exception ex)
{
APILogger.Log("Problem setting realtime sample rate", ex);
}
}
protected virtual void StoreConfigAttributes(SliceConfigServiceAsyncInfo info,
float[] rangeArray,
ref bool bReleased,
ref int progressValue,
byte[] bridgeModeArray,
bool[] IsACCoupledArray,
ushort[] BridgeResistanceArray,
bool[] enableLowerLevelTriggerThreshold,
float[] lowerLevelTriggerThreshold,
bool[] enableUpperLevelTriggerThreshold,
float[] upperLevelTriggerThreshold,
int[] qualificationSamples,
long numChannels,
out ConfigAttributes config,
bool[] bridgeACCouplingArray,
long numUartChannels,
long numStreamingChannels)
{
config = GetConfigAttributes(this);// new ConfigAttributes(this);
// only set attributes to default if we haven't run diagnostics yet
if (!DiagnosticsHasBeenRun)
{
config.PurgeStaleData(this);
}
// store some attributes
if (ConfigData.Modules.Length > 0)
{
config.SampleRate = ConfigData.Modules[0].SampleRateHz;
var actualSampleRate = (float)ConfigData.Modules[0].SampleRateHz;
actualSampleRate = config.ActualSampleRate;
config.AAFilter = ConfigData.Modules[0].AAFilterRateHz;
config.TestID = ConfigData.TestID.TrimEnd(new char[] { '\0' });
config.TestSetupUniqueId = ConfigData.TestSetupUniqueId?.TrimEnd('\0');
config.TestDescription = ConfigData.Description;
config.PreTriggerSamples = (ulong)(ConfigData.Modules[0].PreTriggerSeconds * actualSampleRate);
config.PostTriggerSamples = (ulong)(ConfigData.Modules[0].PostTriggerSeconds * actualSampleRate);
SetArmMode(ConfigData.Modules[0].RecordingMode);
SetStreamingRealtimeSampleRate(info);
try
{
config.ConfigureRange(rangeArray);
progressValue = 10;
info.Progress(progressValue);
}
catch (Exception ex)
{
var errorString = "Error attempting to set range to ";
foreach (var range in rangeArray)
{
errorString += range;
errorString += ", ";
}
errorString = errorString.Remove(errorString.Length - 2, 2);
if (ex.Message.Contains("StatusSetupRequestedSensorRangeTooSmall"))
{
APILogger.Log("Ignoring: " + errorString, ex);
}
else
{
APILogger.Log("Aborting: errorString", ex);
if (!bReleased) { bReleased = true; Release(); }
info.Error(string.Format(errorString));
return;
}
}
try
{
if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.StackChannelBridgeACCouplerEnable))
{
var set = new SetArmAttribute(this);
set.SetValue(AttributeTypes.ArmAndEventAttributes.StackChannelBridgeACCouplerEnable, bridgeACCouplingArray, true);
set.SyncExecute();
}
}
catch (Exception ex)
{
APILogger.Log($"Failed to set ACCouplingModeEnabled, {ex.Message}");
}
config.ConfigureBridge(bridgeModeArray);
config.ConfigureCoupling(IsACCoupledArray);
config.ConfigureBridgeResistance(BridgeResistanceArray);
// configure level trigger enable/threshold values
try
{
ConfigureLevelTriggers(enableLowerLevelTriggerThreshold,
enableUpperLevelTriggerThreshold,
lowerLevelTriggerThreshold,
upperLevelTriggerThreshold,
qualificationSamples);
}
catch (Exception ex) { APILogger.Log("Problem setting level triggers", ex); }
// get scale factors
var scaleFactors = config.GetScaleFactors();
if (numChannels > scaleFactors.Length + numUartChannels + numStreamingChannels)
{
if (!bReleased) { bReleased = true; Release(); }
// "ConfigurationService.Configure: Firmware returned {0} channels but stack contains {1}"
info.Error(string.Format("Firmware returned {0} channels but stack contains {1} analog channels",
scaleFactors.Length, numChannels - numUartChannels - numStreamingChannels));
return;
}
for (var moduleIdx = 0; moduleIdx < ConfigData.Modules.Length; moduleIdx++)
{
if (DFConstantsAndEnums.ModuleType.UART == ConfigData.Modules[moduleIdx].ModuleType()) continue; //only configuring the analogs
if (DFConstantsAndEnums.ModuleType.StreamOut == ConfigData.Modules[moduleIdx].ModuleType()) { continue; }
if (DFConstantsAndEnums.ModuleType.StreamIn == ConfigData.Modules[moduleIdx].ModuleType()) { continue; }
for (var channelIdx = 0;
channelIdx < ConfigData.Modules[moduleIdx].Channels.Length;
channelIdx++)
{
var analog = (AnalogInputDASChannel)ConfigData.Modules[moduleIdx].Channels[channelIdx];
var dasChannelNumber =
DASInfo.MapModuleArrayIndexAndChannelNum2DASChannel(moduleIdx, channelIdx);
analog.ScalefactorMilliVoltsPerADC = scaleFactors[dasChannelNumber];
}
progressValue++;
info.Progress(Math.Min(19, progressValue));
}
}
}
///
/// abstracted this method out from a series of duplicated and very long functions
///
///
///
///
///
///
/// scale factors for channels, only used in TSR AIR for some reason
/// ranges for channels, only used in TSR AIR for some reason
/// measured offset, only used in TSR AIR for some reason
protected void RemainingConfigWork(ref int progressValue,
SliceConfigServiceAsyncInfo info,
List diagnosticChannels,
ConfigAttributes config,
ref bool bReleased,
float[] scaleFactors,
float[] ranges,
float[] measuredOffset)
{
ConfigureDiagChannels(diagnosticChannels.ToArray());
progressValue = 20;
info.Progress(progressValue);
if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.ExcitationLevel)) // add const value for proto version
{
// Allocate list for base excitation values.
var excitationValues = new List();
for (byte moduleIndex = 0; moduleIndex < ConfigData.Modules.Length; moduleIndex++)
{
for (var channelIdx = 0; channelIdx < ConfigData.Modules[moduleIndex].Channels.Length; channelIdx++)
{
var analog = ConfigData.Modules[moduleIndex].Channels[channelIdx] as AnalogInputDASChannel;
var dasChannelNumber = DASInfo.MapModuleArrayIndexAndChannelNum2DASChannel(moduleIndex, channelIdx);
switch (analog.Excitation)
{
case ExcitationVoltageOptions.ExcitationVoltageOption.Volt2:
excitationValues.Add(2);
break;
case ExcitationVoltageOptions.ExcitationVoltageOption.Volt10:
excitationValues.Add(10);
break;
case ExcitationVoltageOptions.ExcitationVoltageOption.Volt5:
excitationValues.Add(5);
break;
default:
excitationValues.Add(0);
break;
}
}
progressValue++;
info.Progress(Math.Min(29, progressValue));
}
var saa = new SetArmAttribute(this, AbstractCommandBase.Default_IO_Timeout);
saa.DeviceID = 0;
saa.SetValue(AttributeTypes.ArmAndEventAttributes.StackExcitationLevel, excitationValues.ToArray(), true);
saa.SyncExecute();
}
progressValue = 30;
info.Progress(progressValue);
// now store the data
var configStr = ConfigurationData.Serialize((ConfigurationData)ConfigData);
config.StoreXMLConfig(configStr, info.EventConfig ? ConfigAttributes.FileStore.Event : ConfigAttributes.FileStore.Diagnostic);
ConfigurationData.SetConfiguration(this, configStr,
info.EventConfig
? (int)ConfigAttributes.FileStore.Event
: (int)ConfigAttributes.FileStore.Diagnostic);
//dummy read to flush buffer, per Loc
try
{
var qsa = new QuerySystemAttribute(this);
qsa.Key = AttributeTypes.SystemAttributes.SerialNumber;
qsa.SyncExecute();
}
catch (Exception) { }
progressValue = 60;
info.Progress(progressValue);
try
{
var retrieved = config.RetrieveXMLConfig(info.EventConfig ? ConfigAttributes.FileStore.Event : ConfigAttributes.FileStore.Diagnostic, this);
var utf8Version = Encoding.UTF8.GetString(Encoding.UTF8.GetBytes(configStr));
if (!retrieved.Equals(utf8Version))
{
APILogger.Log($"read does not match write {SerialNumber}", retrieved, configStr);
}
}
catch (Exception ex)
{
APILogger.Log("failed to read config", ex);
}
//this only needs to be done in datapro, which doesn't set position 0
//we'll have to clean up the code a bit to use it if we want for sliceware.
config.StoreXMLConfig(configStr, info.EventConfig ? ConfigAttributes.FileStore.Diagnostic : ConfigAttributes.FileStore.Event);
progressValue = 70;
info.Progress(progressValue);
//dummy read to flush buffer, per Loc
try
{
var qsa = new QuerySystemAttribute(this);
qsa.Key = AttributeTypes.SystemAttributes.SerialNumber;
qsa.SyncExecute();
}
catch (Exception) { }
progressValue = 80;
info.Progress(progressValue);
try
{
var retrieved = config.RetrieveXMLConfig(info.EventConfig ? ConfigAttributes.FileStore.Event : ConfigAttributes.FileStore.Diagnostic, this);
var utf8Version = Encoding.UTF8.GetString(Encoding.UTF8.GetBytes(configStr));
if (!retrieved.Equals(utf8Version))
{
APILogger.Log($"read does not match write {SerialNumber}", retrieved, configStr);
}
}
catch (Exception ex)
{
APILogger.Log("failed to read config", ex);
}
ConfigureHasBeenRun = true;
ConfigureStreaming(info, scaleFactors, ranges, measuredOffset);
ConfigureHasBeenRun = true;
if (info.DiscardDiagnostics) { DiagnosticsHasBeenRun = false; }
// report success'
if (!bReleased) { bReleased = true; Release(); }
progressValue = 90;
info.Progress(progressValue);
info.Success();
}
protected virtual void ConfigureStreaming(SliceConfigServiceAsyncInfo info,
float[] scaleFactors, float[] ranges, float[] measuredOffset)
{
}
///
/// we can probably simplify and take common items (slice2+slice1) out of this function, but for now
/// it's mostly a copy of SLICE1.AsyncConfigure
///
///
protected override void AsyncConfigure(object configAsyncInfo)
{
var info = configAsyncInfo as SliceConfigServiceAsyncInfo;
SetUDPAlignOnPPS();
IncrementNumberOfTimesWritten();
if (IsTOM()) { AsyncConfigureTOM(configAsyncInfo); }
else
{
//keeps track of whether the PC file was written or not
var bFileWritten = false;
try
{
var progressValue = 0;
var bReleased = true;
if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.ProgramStackChannels)) { ReconfigureAccordingToConfig(); }
PresetSampleRate();
SetVoltageRequirements();
SetPolarity();
SetArmDisableShortCheck();
try
{
Lock();
var bModified = false;
ReconfigureIfNeeded(ref bModified);
bReleased = false;
// loop thru the modules (slices) and configure the channels
var numChannels = DASInfo.Modules.Sum(mod => mod.NumberOfChannels);
var rangeArray = new float[numChannels];
//var IsHalfBridgeArray = new bool[numChannels];
var bridgeModeArray = new byte[numChannels];
var BridgeResistanceArray = new UInt16[numChannels];
var IsACCoupledArray = new bool[numChannels];
// level trigger values
var enableLowerLevelTriggerThreshold = new bool[numChannels];
var enableUpperLevelTriggerThreshold = new bool[numChannels];
var lowerLevelTriggerThreshold = new float[numChannels];
var upperLevelTriggerThreshold = new float[numChannels];
var qualificationSamples = new int[numChannels];
var diagnosticChannels = new List();
var bridgeACCouplingArray = new bool[numChannels];
CommonConfigureWork(diagnosticChannels,
qualificationSamples,
ref bReleased,
info,
bridgeModeArray,
IsACCoupledArray,
BridgeResistanceArray,
ref bModified,
rangeArray,
enableUpperLevelTriggerThreshold,
upperLevelTriggerThreshold,
enableLowerLevelTriggerThreshold,
lowerLevelTriggerThreshold,
bridgeACCouplingArray);
if (bReleased) { return; }
// report progress
progressValue = 5;
info.Progress(progressValue);
StoreConfigAttributes(info, rangeArray, ref bReleased, ref progressValue, bridgeModeArray,
IsACCoupledArray, BridgeResistanceArray, enableLowerLevelTriggerThreshold, lowerLevelTriggerThreshold,
enableUpperLevelTriggerThreshold, upperLevelTriggerThreshold, qualificationSamples,
numChannels, out var config, bridgeACCouplingArray, 0, 0);
if (bReleased) { return; }
RemainingConfigWork(ref progressValue, info, diagnosticChannels, config, ref bReleased,
null, null, null);
bFileWritten = true;
}
catch (CanceledException)
{
if (!bReleased) { bReleased = true; Release(); }
info.Cancel();
}
catch (Exception ex)
{
if (!bReleased) { bReleased = true; Release(); }
APILogger.Log("exception during configuration", ex);
info.Error(ex.Message, ex);
}
finally { if (!bReleased) { bReleased = true; Release(); } }
info.Progress(100);
info.Success();
}
finally
{
//if the pc file wasn't written warn the user
if (!bFileWritten)
{
APILogger.RaiseError(DTS.Common.Strings.Strings.FailedToWritePcConfigFile);
}
}
}
}
///
/// adjusts the requested input range to avoid certain gains if needed
/// we are doing this for
/// 10080 Implement SLICE PRO SIM Gain Limitation for Gen 3 SIMs
/// whenever the hardware is not modified we manipulate the input range requested so we fall
/// within acceptable gains
///
///
///
///
protected virtual double AdjustInputRangeIfNeeded(double originalRange, bool bModified)
{
if (_hardwareRevision != GEN3_REV_B) return originalRange;
var gains = Enum.GetValues(typeof(GainCodesGen3)).Cast().ToList();
gains.Sort(new GainSorter());
gains.Reverse();
var lastAcceptableValue = double.NaN;
//assume gains are in order from 1 to 1600, so find the first gain which is too restrictive and bounce back from that
//so for instance, if someone selected a range of 700mV, we would start with G1, pass, go to G2, pass, go to G4, fail, so we would use
//G2. Also note, while we know the min/max theoretical value foreach gain, when we request a range from the firmware we don't use that value
//we use somewhere inbetween (the FirmwareInputRangemV) to increase the chances we get the gain we want...
//note there are gains greater than 1600, but they are currently explicitly disabled by issue 10080
foreach (var gain in gains)
{
if (GainDisabledAttribute.IsGainDisabled(gain))
{
continue;
}
if (!bModified && !GainAvailableUnmodifiedAttribute.IsGainAvailableToUnmodified(gain))
{
continue;
}
var maxValue = MaxInputRangeAttribute.GetMaxInputRangemV(gain);
if (originalRange < maxValue)
{
lastAcceptableValue = FirmwareInputRangeAttribute.GetFirmwareInputRangemV(gain);
}
else
{
break;
}
}
return !double.IsNaN(lastAcceptableValue) ? lastAcceptableValue : originalRange;
}
protected override ConfigAttributes GetConfigAttributes(DTS.Common.Interface.DASFactory.ICommunication com)
{
return new SLICE2ConfigAttributes(com);
}
///
/// SLICE2 config attributes, mostly inherits from SLICE.ConfigAttributes with some functionality removed
///
protected class SLICE2ConfigAttributes : ConfigAttributes
{
///
/// I'm not aware of a purgeStaleData function in SLICE2 yet
/// this may be unnecessary if the firmware is intelligent enough, and maybe this is legacy
/// for now I just hollow it out
///
///
public override void PurgeStaleData(IDASCommunication das)
{
}
public SLICE2ConfigAttributes(DTS.Common.Interface.DASFactory.ICommunication _com)
: base(_com)
{ }
#region CONFIG_XML
private static readonly uint XML_HEADER_LENGTH = 8;
private static readonly byte[] XML_STORE_MAGIC_BYTES = new byte[] { 0xAA, 0x55, 0xA5, 0x5A };
private readonly object _storeXMLConfigLock = new object();
///
/// SLICE2 stores the xml in flash using SetFileData. We compress the XML string, write the header,
/// then write data 400 bytes at a time. For the header, we first write XML_STORE_MAGIC_BYTES followed
/// by the length of the compressed XML in bytes.
///
///
///
///
public override void StoreXMLConfig(string data, FileStore fileStore)
{
try
{
lock (_storeXMLConfigLock)
{
if (string.IsNullOrEmpty(data))
{
return;
}
// since this might contain multi byte characters, we need to convert it to a byte array first
// Compress Data
var d = GetBytes(fileStore, data);
var crc = new Utilities.Crc32();
var crcBefore = crc.Get(d);
var ByteArrayData = GetCompressedBytes(d);
var crcAfter = crc.Get(ByteArrayData);
APILogger.Log($"StoreXMLConfig {com.SerialNumber} crc uncompressed: {crcBefore} crc compressed: {crcAfter}");
var fileid = (ushort)fileStore;//EventConfig ? Convert.ToUInt16(1) : Convert.ToUInt16(0);
//Store Header - magic bytes and data length
//Store Magic bytes
var sfd = new SetFileData(com, 600000);
sfd.StartByteCount = 0;
sfd.FileID = fileid;
sfd.Data = XML_STORE_MAGIC_BYTES;
sfd.SyncExecute();
//Store Header - data length
sfd.StartByteCount = (XML_HEADER_LENGTH / 2);
sfd.FileID = fileid;
sfd.Data = BitConverter.GetBytes((uint)ByteArrayData.Length);
sfd.SyncExecute();
//Store Data
for (uint i = 0; i < ByteArrayData.Length; i += (uint)sfd.MaximumFileStreamBytes)
{
long array_size = sfd.MaximumFileStreamBytes;
if ((i + sfd.MaximumFileStreamBytes) > ByteArrayData.Length)
{
array_size = ByteArrayData.Length - i;
}
var dataToSend = new byte[array_size];
Array.Copy(ByteArrayData, i, dataToSend, 0, array_size);
sfd.Data = dataToSend;
sfd.FileID = fileid;
sfd.StartByteCount = i + XML_HEADER_LENGTH;
sfd.SyncExecute();
}
var fileName = Path.Combine(Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), Constants.DAS_CONFIGS), string.Format("{0}.xml", com.SerialNumber));
if ((FileStore.Diagnostic == fileStore) || (FileStore.Event == fileStore)) { StoreXmlConfigPC(data, Constants.DAS_CONFIGS, fileName); }
}
}
catch (Exception ex)
{
//log the error and rethrow it, this preserve the existing behavior at the time I added the handler
APILogger.Log($"SLICE2ConfigAttributes.StoreXMLConfig", ex);
throw ex;
}
}
private uint GetDeviceCRC(IDASCommunication das, bool eventConfig)
{
uint crc = 0;
string s = string.Empty;
if (das is EthernetSlice2 ethernet)
{
s = ethernet.GetUserAttribute(eventConfig
? AttributeTypes.SliceUserAttributes.EventConfigCRC
: AttributeTypes.SliceUserAttributes.ConfigCRC);
}
else if (das is CDCUSBSlice usbSLICE)
{
s = usbSLICE.GetUserAttribute(eventConfig
? AttributeTypes.SliceUserAttributes.EventConfigCRC
: AttributeTypes.SliceUserAttributes.ConfigCRC);
}
if (!string.IsNullOrWhiteSpace(s))
{
if (uint.TryParse(s, out var i))
{
return i;
}
}
return crc;
}
///
/// We retrieve the xml from SLICE 2 flash using QueryFileData. We read the header, then read data 400
/// bytes at a time. For the header, we first read XML_STORE_MAGIC_BYTES followed by the length of the
/// compressed XML in bytes. We then unzip the data and return the XML string.
///
///
public override string RetrieveXMLConfig(FileStore fileStore, IDASCommunication das)
{
lock (_storeXMLConfigLock)
{
var WholeStr = "";
try
{
var allBytes = new List();
allBytes.Clear();
//Get Header - magic bytes and data length
//Get Magic bytes
var qfd = new QueryFileData(com, 600000);
var fileid = (ushort)fileStore;
qfd.FileID = fileid;
qfd.StartByteCount = 0;
qfd.EndByteCount = (XML_HEADER_LENGTH / 2) - 1;
qfd.SyncExecute();
if (qfd.Data.Length < 4)
{
APILogger.Log($"{com.SerialNumber} RetrieveXMLConfig <4 bytes returning empty");
return "";
}
if (qfd.Data[0] == 0xFF && qfd.Data[1] == 0xFF && qfd.Data[2] == 0xFF && qfd.Data[3] == 0xFF)
{
APILogger.Log($"{com.SerialNumber} RetrieveXMLConfig first 4 bytes wrong, returning empty string");
return "";
}
if (BitConverter.ToUInt32(qfd.Data, 0) != BitConverter.ToUInt32(XML_STORE_MAGIC_BYTES, 0))
{
APILogger.Log($"{com.SerialNumber} RetrieveXMLConfig Magic bytes not found");
throw new InvalidDataException($"{com.SerialNumber} Magic bytes not found");
}
//Get data length
qfd.FileID = fileid;
qfd.StartByteCount = (XML_HEADER_LENGTH / 2); //account for magic bytes
qfd.EndByteCount = XML_HEADER_LENGTH - 1;
qfd.SyncExecute();
var ByteArrayDataLength = BitConverter.ToUInt32(qfd.Data, 0);
if (Math.Pow(2, 21) < ByteArrayDataLength)
{
APILogger.Log($"{com.SerialNumber} RetrieveXMLConfig data length is greater than store size");
throw new InvalidDataException($"data length is greater than store size {com.SerialNumber}");
}
//Get Data
for (uint i = 0; i < ByteArrayDataLength; i += (uint)qfd.MaximumFileStreamBytes)
{
uint array_size = (uint)qfd.MaximumFileStreamBytes;
if ((i + qfd.MaximumFileStreamBytes) > ByteArrayDataLength)
{
array_size = ByteArrayDataLength - i;
}
qfd.StartByteCount = (XML_HEADER_LENGTH) + i;
qfd.EndByteCount = (XML_HEADER_LENGTH - 1) + (i + array_size);
qfd.FileID = fileid;
qfd.SyncExecute();
APILogger.Log($"{com.SerialNumber} Read {i} of {ByteArrayDataLength} CRC: {CalculateCrc32(new List(qfd.Data)).ToString()}");
allBytes.InsertRange((int)i, qfd.Data);
}
if (allBytes.Count < 1)
{
APILogger.Log($"{com.SerialNumber} RetrieveXMLConfig all bytes count < 1");
// "Slice.RetrieveAttributes: Attributes are empty"
throw new Exception("Attributes are empty");
}
APILogger.Log($"{com.SerialNumber} read {allBytes.Count} bytes in total");
var crc = new Utilities.Crc32();
//DecideToFailOrNot(fileStore, ref allBytes);
WholeStr = GetDecompressedString(allBytes.ToArray(), fileStore);
}
catch (Exception ex)
{
APILogger.Log($"Failed to retrieve xml config - {fileStore} on {com.SerialNumber}. Error: {ex}");
if (FileStore.Event == fileStore)
{
//event file store FIRST falls back to diagnostics
//which will fall back onto pc if it fails
WholeStr = RetrieveXMLConfig(FileStore.Diagnostic, das);
}
else if (FileStore.Diagnostic == fileStore)
{
//diagnostics falls back to pc
WholeStr = RetrieveXmlConfigPC();
}
}
try { APILogger.ConfLog(com.SerialNumber, "XML Config\n", Common.Utils.Utils.PrettyPrint(WholeStr)); }
catch (Exception) { }
return WholeStr;
}
}
private static UInt32 CalculateCrc32(List data)
{
if (data.Count % 2 > 0) { data.Add(0x0); }
ushort crc = 0xFFFF;
byte[] b = data.ToArray();
for (int i = 0; i < b.Length; i += 2)
crc = Common.Utils.Utils.Math_DoCRC16Step(BitConverter.ToUInt16(b, i), crc);
return crc;
}
#endregion
///
/// Retrieve the XML string that was split and stored in the Arm attributes
/// note that SLICE2 is apparently not going to store separate event configs,
/// so we just grab the same one off the pc again
/// [this is a bad idea as the event could be from earlier and the system config could have been
/// changed after the use ran the test, and then reconfigured but wants to download the old test]
///
/// The event number to get it from
///
/// The combined XML string
public override string RetrieveEventXMLConfig(int eventNum, QueryDownloadProgress progress, IDASCommunication das)
{
lock (_storeXMLConfigLock)
{
var s = string.Empty;
try
{
s = RetrieveXMLConfig(FileStore.Event, das);
}
catch (Exception ex)
{
APILogger.Log("failed to retrieve event xml config for event ", eventNum, ex);
}
//fall back to whatever is on the live config
if (string.IsNullOrEmpty(s))
{
APILogger.Log("failed to retrieve event xml config (it was empty) for event ", eventNum);
s = RetrieveXMLConfig(FileStore.Diagnostic, das);
}
return s;
}
}
}
protected override void AsyncReadyForArming(object asyncInfo)
{
//Until DataPRO supports StartRecDelayInSecond, set to 0
if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.StartRecDelayInSecond))
{
var ssaS6 = new SetSystemAttributeSLICE6(this);
ssaS6.SetValue(AttributeTypes.SystemAttributesSLICE6.StartRecDelayInSecond, Convert.ToUInt16(0), true);
ssaS6.SyncExecute();
}
base.AsyncReadyForArming(asyncInfo);
}
protected override void AsyncArmNow(object asyncInfo)
{
base.AsyncArmNow(asyncInfo);
}
private bool IsDIM()
{
if (SerialNumber.StartsWith("SPD") || SerialNumber.StartsWith("SLD")) { return true; }
else { return false; }
}
private DASModule MakeConfigModuleFromInfoModuleTOM(InfoResult.Module infoModule)
{
var configModule = new DASModule(infoModule.ModuleArrayIndex, this);
var channels = new List();
for (var i = 0; i < 8; i++)
{
var c = new OutputSquibChannel(configModule, i);
c.SupportedSquibFireModes = new SquibFireMode[] { SquibFireMode.CAP, SquibFireMode.CONSTANT, SquibFireMode.NONE };
if (0 != i % 2) { c.MeasurementType = SquibMeasurementType.CURRENT; }
c.ModuleChannelNumber = i;
channels.Add(c);
}
for (var i = 8; i < 16; i++)
{
var c = new OutputTOMDigitalChannel(configModule, i);
channels.Add(c);
}
configModule.Channels = channels.ToArray();
return configModule;
}
protected override void PerformSensorIdCheck(ref Dictionary sensorIds)
{
try
{
if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.StackSensors))
{
var qsi = new QueryStackSensorIDs(this);
qsi.SyncExecute();
foreach (var module in ConfigData.Modules)
{
foreach (var channel in module.Channels)
{
var id = "";
if (channel is OutputSquibChannel)
{
//we just expect 4 ids, the id of this channel is one of those 4
id = HexEncoding.ToString(qsi.IDs[channel.ModuleChannelNumber / 2]);
}
else if (channel is AnalogInputDASChannel)
{
var dasChannelNumber =
DASInfo.MapModuleArrayIndexAndChannelNum2DASChannel(module.ModuleArrayIndex,
channel.ModuleChannelNumber);
id = HexEncoding.ToString(qsi.IDs[dasChannelNumber]);
}
if (!string.IsNullOrWhiteSpace(id))
{
sensorIds[channel.Number] = new[] { id };
}
}
}
}
else
{
PerformSensorIdCheck_SingleChannelMethod(ref sensorIds);
}
}
catch (Exception ex)
{
APILogger.Log(ex);
}
finally
{
try
{
var prepForDC = new PrepareForDataCollection(this);
prepForDC.SyncExecute();
}
catch (Exception ex)
{
throw new Exception($"Unable to turn on excitation: {ex.Message}");
}
}
}
protected override DASModule MakeConfigModuleFromInfoModule(InfoResult.Module infoModule)
{
if (infoModule.TypeOfModule == DFConstantsAndEnums.ModuleType.SLICEPro_TOM)
{
return MakeConfigModuleFromInfoModuleTOM(infoModule);
}
var configModule = new DASModule(infoModule.ModuleArrayIndex, this);
configModule.Channels = new AnalogInputDASChannel[infoModule.NumberOfChannels];
for (var i = 0; i < infoModule.NumberOfChannels; i++)
{
var channel = new AnalogInputDASChannel(configModule, i);
switch (infoModule.TypeOfModule)
{
case DFConstantsAndEnums.ModuleType.SliceBridge2High:
case DFConstantsAndEnums.ModuleType.SliceBridge2Low:
channel.IEPEChannel = false;
channel.DigitalInputChannel = false;
break;
case DFConstantsAndEnums.ModuleType.SliceIEPE2High:
case DFConstantsAndEnums.ModuleType.SliceIEPE2Low:
channel.IEPEChannel = true;
channel.DigitalInputChannel = false;
break;
case DFConstantsAndEnums.ModuleType.ProDIM:
channel.TypeOfBridge = SensorConstants.BridgeType.DigitalInput;
channel.IEPEChannel = false;
channel.DigitalInputChannel = true;
break;
}
QueryHardwareRevision();
//int hardwareRevision = QueryHardwareRevision();
var baseType = QueryBaseType();
if (_hardwareRevision == GEN3_REV_B && baseType == PROBaseType)
{
channel.SupportedExcitation = new ExcitationVoltageOptions.ExcitationVoltageOption[] { ExcitationVoltageOptions.ExcitationVoltageOption.Volt5,
ExcitationVoltageOptions.ExcitationVoltageOption.Volt10, ExcitationVoltageOptions.ExcitationVoltageOption.Volt2};
}
else if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.DiagnosticTwoVoltExcitation))//loc thinks this is where cal started for 2 volt
{
channel.SupportedExcitation = new ExcitationVoltageOptions.ExcitationVoltageOption[] {ExcitationVoltageOptions.ExcitationVoltageOption.Volt2,
ExcitationVoltageOptions.ExcitationVoltageOption.Volt5};
}
else
{
channel.SupportedExcitation = new ExcitationVoltageOptions.ExcitationVoltageOption[] { ExcitationVoltageOptions.ExcitationVoltageOption.Volt5 };
}
if (infoModule.TypeOfModule == DFConstantsAndEnums.ModuleType.ProDIM)
{
channel.SupportedBridges = new SensorConstants.BridgeType[] { SensorConstants.BridgeType.DigitalInput };
}
else if (_hardwareRevision == GEN3_REV_B && baseType == PROBaseType)
{
if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.HalfBridgeSigPlusSupport))
{
channel.SupportedBridges = new SensorConstants.BridgeType[]
{
SensorConstants.BridgeType.FullBridge,
SensorConstants.BridgeType.HalfBridge,
SensorConstants.BridgeType.HalfBridge_SigPlus,
SensorConstants.BridgeType.IEPE,
SensorConstants.BridgeType.QuarterBridge
};
}
else
{
channel.SupportedBridges = new SensorConstants.BridgeType[]
{
SensorConstants.BridgeType.FullBridge,
SensorConstants.BridgeType.HalfBridge,
SensorConstants.BridgeType.IEPE,
SensorConstants.BridgeType.QuarterBridge
};
}
}
else if (infoModule.IsProgrammable)
{
channel.SupportedBridges = new SensorConstants.BridgeType[] { SensorConstants.BridgeType.FullBridge,
SensorConstants.BridgeType.HalfBridge, SensorConstants.BridgeType.IEPE};
}
else
{
if (channel.IEPEChannel)
{
channel.SupportedBridges = new SensorConstants.BridgeType[] { SensorConstants.BridgeType.IEPE };
}
else
{
channel.SupportedBridges = new SensorConstants.BridgeType[] {SensorConstants.BridgeType.FullBridge,
SensorConstants.BridgeType.HalfBridge};
}
}
configModule.Channels[i] = channel;
}
return configModule;
}
protected override bool CheckAutoDetectSupport()
{
if (null == DASInfo) { return false; }
if (null == DASInfo.Modules) { return false; }
if (0 == DASInfo.Modules.Length) { return false; }
return DASInfo.Modules[0].IsProgrammable;
}
protected bool _haveInited = false;
///
/// returns whether the unit supports 2V or not
/// right now this is only used in one spot (SLICE2 AsyncQueryConfiguration)
/// so it may not be true for purposes outside of this function without
/// additional work
///
///
protected virtual bool GetSupports2Volt()
{
return true;
}
protected override void AsyncQueryTestSetup(object configAsyncInfo)
{
var info = configAsyncInfo as SliceServiceQueryTestSetupAsyncInfo;
var config = GetConfigAttributes(this); //Temp: This is just to use RetrieveEventXMLConfig for now
var testSetupString = config.RetrieveXMLConfig(ConfigAttributes.FileStore.TestSetup, this);
config.StoreXmlConfigPC(testSetupString, Constants.DAS_TEST_SETUPS, config.TestSetupUniqueId);
info.Success();
}
///
/// Updates the configuration on the unit using a given input file
/// 17872 Use DASConfig XMLs on disk when performing an emergency download with DAS that have blank filestore(s)
///
protected override void AsyncUpdateConfigurationFromFile(object asyncInfo)
{
var info = asyncInfo as SliceServiceAsyncInfo;
if (!(info.functionData is string filePath))
{
info.Error($"AsyncUpdateConfigurationFromFile: {SerialNumber} - Empty file path");
return;
}
if (!File.Exists(filePath))
{
info.Error($"SyncUpdateConfigurationFromFile: {SerialNumber} - invalid file path: {filePath}");
return;
}
try
{
IncrementNumberOfTimesCorrected();
var fileData = File.ReadAllText(filePath);
ConfigData = ConfigurationData.DeserializeFromString(fileData);
var config = GetConfigAttributes(this);
config.StoreXMLConfig(fileData, Slice.ConfigAttributes.FileStore.Event);
config.StoreXMLConfig(fileData, Slice.ConfigAttributes.FileStore.Diagnostic);
QueryConfigGetScaleFactors(info);
}
catch (Exception ex)
{
APILogger.Log(ex);
info.Error(ex.Message, ex);
return;
}
info.Success();
}
//for testing this is the percentage diagnostics file store should fail
private const int DIAGNOSTICS_FAIL_PERCENT = 20;
//for testing this is the percentage event file store should fail
private const int EVENT_FAIL_PERCENT = 50;
//for testing this is how many bytes to mess with when a file store fails
private const int NUM_BYTES_TO_MESS_WITH = 5;
//mess with some bytes
private static void DecideToFailOrNot(ConfigAttributes.FileStore fileStore, ref List allBytes)
{
if (allBytes.Count < NUM_BYTES_TO_MESS_WITH) { return; }
var r = new Random(DateTime.Now.Millisecond);
var failPercent = 0;
if (fileStore == ConfigAttributes.FileStore.Diagnostic) { failPercent = DIAGNOSTICS_FAIL_PERCENT; }
else if (fileStore == ConfigAttributes.FileStore.Event) { failPercent = EVENT_FAIL_PERCENT; }
failPercent = 100 - failPercent;
if (r.Next(0, 100) > failPercent)
{
//we failed, mess up some bytes!
var badBytes = new byte[NUM_BYTES_TO_MESS_WITH];
r.NextBytes(badBytes);
//pick a spot
for (var i = 0; i < NUM_BYTES_TO_MESS_WITH; i++)
{
var index = r.Next(0, allBytes.Count - 1);
allBytes[index] = badBytes[i];
}
}
}
///
/// This function was previously part of QueryConfiguration
/// I abstracted it out so that UpdateConfiguration could call it
/// it's needed to initialize some data structions that are part of the
/// configuration
/// 17872 Use DASConfig XMLs on disk when performing an emergency download with DAS that have blank filestore(s)
///
private void QueryConfigGetScaleFactors(SliceServiceAsyncInfo info, bool bReadDeviceData = true)
{
var config = GetConfigAttributes(this);
try
{
var scaleFactors = new float[0];
try
{
//15949 S6A when streaming does a whole bunch of unnecessary queries
//only get this attribute while not streaming (to avoid failing)
if (!GetIsStreaming())
{
// scaleFactors = GetScaleFactors();
}
}
catch (Exception ex)
{
APILogger.Log(ex);
}
#if LEVEL_TRIGGER_DEFINED
// These are currently unutilized since we have no application for the offset-adjusted
// level trigger values outside of the Slice firmware.
//
// var levelTriggers = GetLevelTriggers( );
#endif
if (null == scaleFactors)
{
scaleFactors = new float[0];
}
info.Progress(80);
// set the OwningDAS and OwningModule since it was skipped during serialization
if (null != ConfigData.Modules)
{
foreach (var iDASModule in ConfigData.Modules)
{
var module = (DASModule)iDASModule;
// 17873 outside of download, ask device for "arm" sample rate, otherwise ask for first event's
var fSampleRate = bReadDeviceData ? config.ActualSampleRate : config.GetEventSamplerate(0);
if (float.IsNaN(fSampleRate))
{
fSampleRate = 10000F;
APILogger.Log("Warning invalid actual sample rate detected, defaulting back to 10k sps");
}
//per CPB, use the realtime sample rate in this case
//14549 Unhandled exception when using hardware discovery with SLICE6 AIR
if (float.IsInfinity(fSampleRate))
{
QueryArmAttribute qaa = new QueryArmAttribute(this);
qaa.Key = AttributeTypes.ArmAndEventAttributes.RealtimeSampleRate;
qaa.SyncExecute();
fSampleRate = Convert.ToSingle(qaa.Value);
APILogger.Log(
$"Warning invalid actual sample rate detected (infinity), default back to realtime rate, {fSampleRate}");
}
module.ActualSampleRateHz = Convert.ToUInt32(fSampleRate);
module.OwningDAS = this;
foreach (var iDASChannel in module.Channels)
{
var channel = (DASChannel)iDASChannel;
channel.OwningModule = module;
// pick up the scalefactor for this channel
if (channel is AnalogInputDASChannel analog)
{
var dasChannelNumber = analog.Number;
if (dasChannelNumber < scaleFactors.Length)
{
analog.ScalefactorMilliVoltsPerADC = scaleFactors[dasChannelNumber];
}
//double EuPerMv = GetEuPerMv(analog);
}
}
}
}
}
catch (System.Net.Sockets.SocketException socketException)
{
APILogger.LogString("QueryConfiguration: RetrieveXMLConfig threw exception");
APILogger.LogException(socketException);
info.Error(string.Format("Connection with {0} failed", SerialNumber));
return;
}
catch (NotConnectedException)
{
info.Error(string.Format("Connection with {0} failed", SerialNumber));
return;
}
catch (CanceledException)
{
// cancel must be propagated
throw;
}
catch (Exception ex)
{
APILogger.LogString("QueryConfiguration: GetScaleFactors threw exception");
APILogger.LogException(ex);
// we don't need no stinking scale factors
}
}
///
/// queries the BaseFirmwareBuildID system attribute for the purpose of getting the string into the log
/// 26821 Add query of BaseFirmwareBuildID for SLICE PRO/ 1.5/ S6/S6A/PowerPRO/S6DB/S6DB3/ TSR AIR for Logs
/// All SLICE2 derivatives appear to support this (including S6DB)
///
protected virtual void QueryBaseFirmwareBuildId()
{
try
{
var query = new QuerySystemAttributeSLICE2(this);
query.Key = AttributeTypes.SystemAttributesSLICE2.BaseFirmwareBuildID;
query.SyncExecute();
}
catch (Exception ex)
{
APILogger.Log(ex);
}
}
///
/// retrieves the number of times configuration has been written, increments the count, then stores the value
///
protected void IncrementNumberOfTimesWritten()
{
var num = 0L;
try
{
var sValue = GetUserAttribute(AttributeTypes.SliceUserAttributes.NumberTimesWritten);
if (long.TryParse(sValue, out long temp))
{
num = temp;
}
}
catch (Exception ex)
{
APILogger.Log(ex);
}
num++;
try
{
SetUserAttribute(AttributeTypes.SliceUserAttributes.NumberTimesWritten, num.ToString());
APILogger.Log($"Setting NumberTimesWritten to {num}");
}
catch (Exception ex)
{
APILogger.Log(ex);
}
}
///
/// retrieves the number of times the configuration has been retrieved, increments, and stores
///
protected void IncrementNumberOfTimesCorrected()
{
var num = 0L;
try
{
var sValue = GetUserAttribute(AttributeTypes.SliceUserAttributes.NumberTimesCorrected);
if (long.TryParse(sValue, out long temp))
{
num = temp;
}
}
catch (Exception ex)
{
APILogger.Log(ex);
}
num++;
try
{
SetUserAttribute(AttributeTypes.SliceUserAttributes.NumberTimesCorrected, num.ToString());
APILogger.Log($"Setting NumberTimesCorrected to {num}");
}
catch (Exception ex)
{
APILogger.Log(ex);
}
}
///
/// retrieves the number of times configuration was detected invalid, increments, and stores the count
///
protected void IncrementNumberOfFailedReads()
{
var numberOfFailures = 0L;
try
{
var sValue = GetUserAttribute(AttributeTypes.SliceUserAttributes.NumberTimesDetected);
if (long.TryParse(sValue, out long temp))
{
numberOfFailures = temp;
}
}
catch (Exception ex)
{
APILogger.Log(ex);
}
numberOfFailures++;
try
{
SetUserAttribute(AttributeTypes.SliceUserAttributes.NumberTimesDetected, numberOfFailures.ToString());
APILogger.Log($"Setting NumberTimesDetected to {numberOfFailures}");
}
catch (Exception ex)
{
APILogger.Log(ex);
}
}
///
/// basically a copy of SLICE1 ... may be able to eliminate and use the base version.
///
///
protected override void AsyncQueryConfiguration(object configAsyncInfo)
{
try
{
if (!_haveInited)
{
InitMinProto(); _haveInited = true;
}
if (GetIsStreaming())
{
//15949 S6A when streaming does a whole bunch of unnecessary queries
//avoid getting this attribute if streaming, it will fail
}
else
{
QueryBaseFirmwareBuildId();
if (!ShouldQueryFeatureConfig())
{
_featureConfigValue = 1;
}
else
{
//hardware gen 3 we have to tell whether we are modified or not
//http://fogbugz/fogbugz/default.asp?10080
var qsa = new QuerySystemAttribute_Slice2Bridge_GEN3(this);
qsa.Key = AttributeTypes.SystemAttributes_Bridge_SLICE2_GEN3.FeatureConfig;
//this is a bridge attribute, we are making a small assumption that
//all the modules have this bit set...
qsa.DeviceID = 1;
qsa.SyncExecute();
if (qsa.Value is float f)
{
//this would throw an invalid cast, so avoid it
}
else
{
_featureConfigValue = (ushort)qsa.Value;
}
}
}
}
catch (Exception) { }
var info = configAsyncInfo as SliceServiceQueryConfigAsyncInfo;
try
{
if (this is IDASReconfigure) { (this as IDASReconfigure).GetMaxModuleCount(); }
}
catch (System.Net.Sockets.SocketException socketException)
{
APILogger.LogString("QueryConfiguration: RetrieveXMLConfig threw exception");
APILogger.LogException(socketException);
info.Error(string.Format("Connection with {0} failed", SerialNumber));
return;
}
catch (NotConnectedException) { info.Error(string.Format("Connection with {0} failed", SerialNumber)); return; }
catch (Exception) { }
var hardwareRev = -1;
var baseType = -1;
//15949 S6A when streaming does a whole bunch of unnecessary queries
//only get this attribute if not streaming (otherwise it will fail)
if (!GetIsStreaming())
{
try
{
hardwareRev = QueryHardwareRevision();
baseType = QueryBaseType();
_rangeBandwithLimited = hardwareRev < 1;
}
catch (Exception ex)
{
APILogger.Log("failed to query hardware revision and type", ex);
}
}
info.Progress(10);
if (hardwareRev >= 1)
{
_supportsTimeSynchronization = true;
try
{
var qbst = new QueryBaseSystemTime(this);
qbst.SyncExecute();
_systemDateTime = qbst.SystemTime;
}
catch (System.Net.Sockets.SocketException socketException)
{
APILogger.LogString("QueryConfiguration: RetrieveXMLConfig threw exception");
APILogger.LogException(socketException);
info.Error(string.Format("Connection with {0} failed", SerialNumber));
return;
}
catch (NotConnectedException) { info.Error(string.Format("Connection with {0} failed", SerialNumber)); return; }
catch (Exception ex)
{
APILogger.Log("failed to query system time", ex);
}
}
info.Progress(15);
var dasDisplayOrder = -1;
var displayOrder = new int[] { -1 };
try
{
dasDisplayOrder = GetDASDisplayOrder();
displayOrder = GetChannelDisplayOrder();
}
catch (Exception) { }
try
{
// pickup the channel data
var config = GetConfigAttributes(this);
info.Progress(40);
try
{
var StoredInfoString = config.RetrieveXMLConfig(ConfigAttributes.FileStore.Diagnostic, this);
ConfigData = ConfigurationData.DeserializeFromString(StoredInfoString);
var blank = string.IsNullOrWhiteSpace(StoredInfoString);
var failedValidation = false;
if (!VerifyConfig())
{
failedValidation = true;
ConfigData = null;
}
if (ConfigData == null || ConfigData.Modules == null || ConfigData.Modules.Length == 0)
{
//note that not all users may be using microsoft prism and event structures...
//so put this in a try in case they aren't (f/w test utility)
// 17872 Use DASConfig XMLs on disk when performing an emergency download with DAS that have blank filestore(s)
IncrementNumberOfFailedReads();
try
{
var eventAggregator = ContainerLocator.Container.Resolve();
eventAggregator.GetEvent().Publish(new DASConfigurationArg(this, blank, failedValidation));
}
catch (Exception ex)
{
APILogger.Log(ex);
}
APILogger.LogString("QueryConfiguration: ConfigData or Modules empty, using default config");
ConfigData = MakeDefaultConfigFromInfo();
}
else if (DASInfo == null || DASInfo.Modules == null || !ModuleLengthsAreOK(info.DifferentModuleCountsAreOK))
{
APILogger.LogString("QueryConfiguration: DASInfo empty or Modules not expected length, using default config");
ConfigData = MakeDefaultConfigFromInfo();
}
}
catch (NotConnectedException) { info.Error(string.Format("Connection with {0} failed", SerialNumber)); return; }
catch (System.Net.Sockets.SocketException socketException)
{
APILogger.LogString("QueryConfiguration: RetrieveXMLConfig threw exception");
APILogger.LogException(socketException);
info.Error(string.Format("Connection with {0} failed", SerialNumber));
return;
}
catch (Exception ex)
{
APILogger.LogString("QueryConfiguration: RetrieveXMLConfig threw exception");
APILogger.LogException(ex);
if (ex.Message.Contains("ReceiveFailed"))
{
info.Error(string.Format("Connection with {0} failed", SerialNumber));
return;
}
//for now we supress this order by requests from support
var error = string.Format(Strings.InvalidConfig, SerialNumber);
ConfigData = MakeDefaultConfigFromInfo();
}
info.Progress(50);
try
{
if (null != ConfigData.Modules)
{
foreach (var m in ConfigData.Modules)
{
foreach (var c in m.Channels)
{
if (c is AnalogInputDASChannel aic)
{
if (_hardwareRevision == GEN3_REV_B && _baseType == PROBaseType)
{
if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.HalfBridgeSigPlusSupport))
{
aic.SupportedBridges =
new SensorConstants.BridgeType[]
{
SensorConstants.BridgeType.FullBridge,
SensorConstants.BridgeType.HalfBridge,
SensorConstants.BridgeType.IEPE,
SensorConstants.BridgeType.QuarterBridge,
SensorConstants.BridgeType.HalfBridge_SigPlus
};
}
else
{
aic.SupportedBridges =
new SensorConstants.BridgeType[]
{
SensorConstants.BridgeType.FullBridge,
SensorConstants.BridgeType.HalfBridge,
SensorConstants.BridgeType.IEPE,
SensorConstants.BridgeType.QuarterBridge
};
}
aic.SupportedExcitation = new ExcitationVoltageOptions.ExcitationVoltageOption[] {
ExcitationVoltageOptions.ExcitationVoltageOption.Volt10,
ExcitationVoltageOptions.ExcitationVoltageOption.Volt2,
ExcitationVoltageOptions.ExcitationVoltageOption.Volt5};
}
else if (DASInfo.Modules[0].IsProgrammable && IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.ProgramStackChannels))
{
if (GetSupports2Volt())
{
aic.SupportedExcitation =
new ExcitationVoltageOptions.ExcitationVoltageOption[]
{
ExcitationVoltageOptions.ExcitationVoltageOption.Volt5,
ExcitationVoltageOptions.ExcitationVoltageOption.Volt2
};
}
else
{
aic.SupportedExcitation =
new ExcitationVoltageOptions.ExcitationVoltageOption[]
{
ExcitationVoltageOptions.ExcitationVoltageOption.Volt5
};
}
if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.HalfBridgeSigPlusSupport))
{
aic.SupportedBridges =
new SensorConstants.BridgeType[]
{
SensorConstants.BridgeType.FullBridge,
SensorConstants.BridgeType.HalfBridge,
SensorConstants.BridgeType.IEPE,
SensorConstants.BridgeType.HalfBridge_SigPlus
};
}
else
{
aic.SupportedBridges =
new SensorConstants.BridgeType[]
{
SensorConstants.BridgeType.FullBridge,
SensorConstants.BridgeType.HalfBridge,
SensorConstants.BridgeType.IEPE
};
}
}
if (aic.DigitalInputChannel) { aic.SupportedBridges = new SensorConstants.BridgeType[] { SensorConstants.BridgeType.DigitalInput }; }
}
}
}
}
}
catch (System.Net.Sockets.SocketException socketException)
{
APILogger.LogString("QueryConfiguration: RetrieveXMLConfig threw exception");
APILogger.LogException(socketException);
info.Error(string.Format("Connection with {0} failed", SerialNumber));
return;
}
catch (NotConnectedException) { info.Error(string.Format("Connection with {0} failed", SerialNumber)); return; }
catch (Exception) { }
info.Progress(60);
try
{
var idx = 0;
if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.ProgramStackChannels) &&
DASInfo.Modules.Any() &&
DASInfo.Modules[0].IsProgrammable)
{
if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.StackSensors) && info.ReadIds)
{
var query = new QueryStackSensorIDs(this);
query.SyncExecute();
if (null != ConfigData.Modules)
{
foreach (var m in ConfigData.Modules)
{
foreach (var c in m.Channels)
{
if (c is AnalogInputDASChannel aic)
{
var id = HexEncoding.ToString(query.IDs[idx]);
var eid = new EID(id);
if (eid.IsValid() && !RunTestVariables.BypassEIDRead)
{
c.IDs = new EID[] { eid };
}
else
{
c.IDs = new EID[0];
}
switch (Convert.ToInt32(query.Types[idx]))
{
case (int)SetStackChannelTypeConfiguration.ChannelTypes.FORCE_IEPE:
aic.IEPEChannel = true;
break;
case (int)SetStackChannelTypeConfiguration.ChannelTypes.FORCE_IEPE_LOW:
aic.IEPEChannel = true;
break;
default:
aic.IEPEChannel = false;
break;
}
}
idx++;
}
}
}
}
}
}
catch (System.Net.Sockets.SocketException socketException)
{
APILogger.LogString("QueryConfiguration: RetrieveXMLConfig threw exception");
APILogger.LogException(socketException);
info.Error(string.Format("Connection with {0} failed", SerialNumber));
return;
}
catch (NotConnectedException) { info.Error(string.Format("Connection with {0} failed", SerialNumber)); return; }
catch (Exception ex) { APILogger.Log("Failed to get channel types", ex); }
// get scale factors
info.Progress(70);
if (null != ConfigData) { ConfigData.DisplayOrder = displayOrder; ConfigData.DasDisplayOrder = dasDisplayOrder; }
try
{
QueryConfigGetScaleFactors(info, info.DeviceScaleFactors);
}
catch (Exception ex)
{
APILogger.Log(ex);
}
try
{
// get sensor ID's
if (ConfigData.Modules != null)
{
foreach (var iDASModule in ConfigData.Modules)
{
var module = (DASModule)iDASModule;
module.OwningDAS = this; // in case we bailed out above
if (!(IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.ProgramStackChannels) && DASInfo.Modules[0].IsProgrammable))
{
foreach (var iDASChannel in module.Channels)
{
var channel = (DASChannel)iDASChannel;
channel.OwningModule = module; // in case bailed out above
var dasChannelNumber = DASInfo.MapModuleArrayIndexAndChannelNum2DASChannel(module.ModuleArrayIndex, channel.ModuleChannelNumber);
if (channel is OutputSquibChannel)
{
dasChannelNumber = DASInfo.MapModuleArrayIndexAndChannelNum2DASChannel(module.ModuleArrayIndex, channel.ModuleChannelNumber / 2);
}
//15949 S6A when streaming does a whole bunch of unnecessary queries
//avoid getting ids while streaming, it will fail
if (!GetIsStreaming())
{
channel.IDs = EIDReader.RetriveEIDs(this, dasChannelNumber, 0);
}
}
//15949 S6A when streaming does a whole bunch of unnecessary queries
//avoid getting ids while streaming, it will fail
if (!GetIsStreaming())
{
IEID[] ids = null;
//performance improvement, don't check (and re-check 3 times) for a battery id on channel 255
//update - SLICE2 and beyond don't have battery ids, don't check ...
if (null != ids && 0 != ids.Length)
{
module.OwningDAS.DASInfo.BatteryID = ids[0];
module.OwningDAS.SetDASInfo();
}
}
}
}
}
}
catch (System.Net.Sockets.SocketException socketException)
{
APILogger.LogString("QueryConfiguration: RetrieveXMLConfig threw exception");
APILogger.LogException(socketException);
info.Error(string.Format("Connection with {0} failed", SerialNumber));
return;
}
catch (NotConnectedException) { info.Error(string.Format("Connection with {0} failed", SerialNumber)); return; }
catch (Exception) { }
info.Progress(90);
try { RetrieveEventGuids(); }
catch (Exception ex) { APILogger.Log("problem getting event guids", ex); }
try { DASInfo.MaxEventStorageSpaceInBytes = ReadSampleStorageSizeInBytes(); }
catch (System.Net.Sockets.SocketException socketException)
{
APILogger.LogString("QueryConfiguration: RetrieveXMLConfig threw exception");
APILogger.LogException(socketException);
info.Error(string.Format("Connection with {0} failed", SerialNumber));
return;
}
catch (NotConnectedException) { info.Error(string.Format("Connection with {0} failed", SerialNumber)); return; }
catch (Exception ex) { APILogger.Log("problem getting sample storage size in bytes", ex); }
info.Progress(95);
try { GetAutoArmStatus(); }
catch (System.Net.Sockets.SocketException socketException)
{
APILogger.LogString("QueryConfiguration: RetrieveXMLConfig threw exception");
APILogger.LogException(socketException);
info.Error(string.Format("Connection with {0} failed", SerialNumber));
return;
}
catch (NotConnectedException) { info.Error(string.Format("Connection with {0} failed", SerialNumber)); return; }
catch (Exception ex) { APILogger.Log("problem getting autoarm status", ex); }
info.Success();
}
catch (System.Net.Sockets.SocketException socketException)
{
APILogger.LogString("QueryConfiguration: RetrieveXMLConfig threw exception");
APILogger.LogException(socketException);
info.Error(string.Format("Connection with {0} failed", SerialNumber));
return;
}
catch (NotConnectedException) { info.Error(string.Format("Connection with {0} failed", SerialNumber)); return; }
catch (CanceledException)
{
info.Cancel();
}
catch (Exception ex)
{
info.Error(ex.Message, ex);
}
}
///
/// This function returns True when the number of DAS modules is equal to the
/// number of Config modules or when it is acceptable if they differ.
///
///
private bool ModuleLengthsAreOK(bool differentModuleCountsAreOK)
{
var result = DASInfo.Modules.Length == ConfigData.Modules.Length;
if (!result && differentModuleCountsAreOK)
{
try
{
// 28263 The config from a SLICE6Air that has a config that's not built from DataPRO 4.0 or later will not have a 3rd module for
// the UART channel and not have a 4th module for the Stream Output channel, so ignore the difference in the number of modules
// only if this call is for the purposes of reading an old config for a Quick Build.
if (_baseType == (int)SLICERecorder.SLICEPRO_BaseType.SLICE6AIR)
{
//Pre-DataPRO 4.0, the config only had two 3-channel modules, but as of 4.0
//additional modules exist for a UART channel, and a Stream Output channel.
//Since differentModuleCountsAreOK is True, we can add the new modules.
var newModules = new IDASModule[DASInfo.Modules.Length];
//Copy all of the existing module(s) from ConfigData
var index = 0;
foreach (var oldModule in ConfigData.Modules)
{
newModules[index] = oldModule;
index++;
}
//Add the new module(s) from DASInfo
for (var i = index; i < newModules.Length; i++)
{
newModules[i] = MakeConfigModuleFromInfoModule((InfoResult.Module)DASInfo.Modules[i]);
}
//Replace the modules in the config
ConfigData.Modules = newModules;
result = true;
}
}
catch (Exception ex)
{
APILogger.Log($"Exception in ModuleLengthsAreOK: {ex.Message}");
result = false;
}
}
return result;
}
protected override void AsyncEnterLowPowerMode(object asyncInfo)
{
var info = (SliceServiceAsyncInfo)asyncInfo;
try
{
//clear any 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 (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.StackLowPowerMode))
{
var low = new SetStackLowPowerMode(this);
low.SyncExecute();
}
else
{
if (DASInfo != null && DASInfo.Modules != null)
{
// DeviceID is the module index + 1
for (var 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);
}
}
}
}
info.Success();
}
catch (CanceledException)
{
info.Cancel();
}
catch (Exception ex)
{
info.Error(ex.Message, ex);
}
}
protected override void RetrieveEventGuids()
{
try
{
//15949 S6A when streaming does a whole bunch of unnecessary queries
//avoid getting event guids while streaming, it will fail
if (GetIsStreaming())
{
SetEventGuids(null);
SetEventFaultFlags(null);
SetEventArmAttemps(null);
((IDownload)this)?.SetExtendedFaultFlags(null);
return;
}
var EventCount = new QueryTotalEventCount(this, AbstractCommandBase.Default_IO_Timeout);
EventCount.SyncExecute();
if (EventCount.Count < 1)
{
// no events
SetEventGuids(null);
SetEventFaultFlags(null);
((IDownload)this)?.SetExtendedFaultFlags(null);
SetEventArmAttemps(null);
return;
}
var guids = new Guid[EventCount.Count];
var downloadedStatus = new bool[EventCount.Count];
var query2 = new QueryEventAttribute(this, AbstractCommandBase.Default_IO_Timeout);
var faultFlags = new ushort[EventCount.Count];
var extendedFaultFlags = new List();
query2.Key = AttributeTypes.ArmAndEventAttributes.FaultFlags;
for (ushort eventIdx = 0; eventIdx < EventCount.Count; eventIdx++)
{
query2.EventNumber = eventIdx;
guids[eventIdx] = GetEventGuid(eventIdx);
extendedFaultFlags.Add(GetExtendedFaultFlags(eventIdx));
faultFlags[eventIdx] = 0;
if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.EventFaultFlags))
{
try
{
query2.SyncExecute();
faultFlags[eventIdx] = (ushort)query2.Value;
}
catch (Exception ex)
{
APILogger.Log("could not get fault flags", ex);
}
}
try
{
var ca = GetConfigAttributes(this);//new Slice.ConfigAttributes(this);
downloadedStatus[eventIdx] = ca.EventHasBeenDownloaded(eventIdx, out uint iflag);
}
catch (Exception) { }
}
SetEventGuids(guids);
SetEventFaultFlags(faultFlags);
((IDownload)this)?.SetExtendedFaultFlags(extendedFaultFlags.ToArray());
SetEventDownloadStatus(downloadedStatus);
}
catch (CommandException ce)
{
if (ce.Error != CommandErrorReason.InvalidMode)
{
APILogger.LogString("DASFactory.RetriveEventGuids: ");
APILogger.LogException(ce);
}
}
catch (Exception ex)
{
APILogger.LogString("DASFactory.RetriveEventGuids: ");
APILogger.LogException(ex);
}
}
protected override void AsyncUpdateIDs(object configAsyncInfo)
{
var info = configAsyncInfo as SliceServiceAsyncInfo;
if (!IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.ProgramStackChannels) || !DASInfo.Modules[0].IsProgrammable)
{
// Not Programmable
base.AsyncUpdateIDs(configAsyncInfo);
}
else
{
try
{
if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.StackSensors))
{
var query = new QueryStackSensorIDs(this);
query.SyncExecute();
var idx = 0;
foreach (var m in ConfigData.Modules)
{
foreach (var c in m.Channels)
{
if (c is AnalogInputDASChannel analog)
{
var eid = new EID(HexEncoding.ToString(query.IDs[idx]));
if (eid.IsValid() && !RunTestVariables.BypassEIDRead)
{
c.IDs = new EID[] { eid };
}
else
{
c.IDs = new EID[0];
}
switch (Convert.ToInt32(query.Types[idx]))
{
case (int)SetStackChannelTypeConfiguration.ChannelTypes.FORCE_IEPE:
analog.IEPEChannel = true;
break;
default:
analog.IEPEChannel = false;
break;
}
}
idx++;
}
}
}
info.Success();
}
catch (Exception ex)
{
APILogger.Log("Failed to update ids - ", ex);
info.Error("Failed to update IDS", ex);
}
}
}
///
/// Retrieve the info to fill in the ConfigData property
///
/// The delegate to report to
/// User supplied data that we pass along
void IConfigurationActions.QueryConfiguration(ServiceCallback callback, object userData, uint crc, string strConfig, bool bReadIds, bool bDeviceScaleFactors,
bool differentModuleCountsAreOK)
{
var info = new SliceServiceQueryConfigAsyncInfo(callback, userData, crc, strConfig, bReadIds, bDeviceScaleFactors, differentModuleCountsAreOK);
LaunchAsyncWorker("Slice2.QueryConfiguration", AsyncQueryConfiguration, info);
}
protected const int UNKNOWN_REVISION = -1;
protected const int GEN2_REV_A = 0;
protected const int GEN3_REV_B = 1;
protected int QueryHardwareRevision()
{
if (_hardwareRevision != UNKNOWN_REVISION)
{
return _hardwareRevision;
}
if (!IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.HardwareRevision)) { return UNKNOWN_REVISION; }
else
{
if (RunTestVariables.InRunTest && this is EthernetSlice6)
{
_hardwareRevision = RunTestVariables.AssumedBaseHardwareRevisionSLICE6;
return _hardwareRevision;
}
try
{
var qsa = new QuerySystemAttributeSLICE2(this);
qsa.Key = AttributeTypes.SystemAttributesSLICE2.BaseHardwareRevision;
qsa.SyncExecute();
_hardwareRevision = Convert.ToInt32(qsa.Value ?? -1);
QueryHardwareRevisionPost();
return Convert.ToInt32(qsa.Value ?? -1);
}
catch (Exception ex)
{
APILogger.Log("failed to get hardware revision", ex);
}
return UNKNOWN_REVISION;
}
}
protected virtual void QueryHardwareRevisionPost()
{
}
protected const int UNKNOWNBaseType = -1;
protected const int PROBaseType = 0;
protected const int NANOBaseType = 1;
protected int QueryBaseType()
{
//this shouldn't change if we've already gotten it once ...
if (_baseType != UNKNOWNBaseType) { return _baseType; }
if (RunTestVariables.InRunTest && this is EthernetSlice6 && RunTestVariables.AssumeBaseTypeSLICE6 > 0)
{
_baseType = RunTestVariables.AssumeBaseTypeSLICE6;
return _baseType;
}
try
{
//15949 S6A when streaming does a whole bunch of unnecessary queries
//avoid getting this attribute if streaming, it will fail
if (GetIsStreaming())
{
return UNKNOWNBaseType;
}
var qsa = new QuerySystemAttributeSLICE2(this);
qsa.Key = AttributeTypes.SystemAttributesSLICE2.BaseType;
qsa.SyncExecute();
_baseType = Convert.ToInt32(qsa.Value);
return _baseType;
}
catch (Exception ex) { APILogger.Log("failed to get base type", ex); }
return UNKNOWNBaseType;
}
#region IClockSyncActions
///
/// AsyncInfo for IClockSyncActions that derive from here (S6, S6A, S6DB, etc)
///
internal class SetClockSyncConfigPacket
{
public SliceServiceAsyncInfo Info { get; set; }
public SetClockSyncConfigPacket(SliceServiceAsyncInfo info)
{
Info = info;
}
}
internal class GetClockSyncStatusPacket
{
public SliceServiceAsyncInfo Info { get; set; }
public GetClockSyncStatusPacket(SliceServiceAsyncInfo info)
{
Info = info;
}
}
protected virtual void AsyncSetClockSyncConfig(object asyncInfo)
{
if (!(asyncInfo is SetClockSyncConfigPacket packet))
{
return;
}
if (!IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.SetClockSyncConfig))
{
packet.Info.Error("Set Clock Config not supported");
return;
}
var info = packet.Info;
var bLocked = false;
try
{
Lock();
bLocked = true;
try
{
var ssaClockSyncProfile =
new SetSystemAttributeSLICE2(this, SetSystemAttribute.Default_IO_Timeout);
ssaClockSyncProfile.SetValue(AttributeTypes.SystemAttributesSLICE2.ClockSyncProfile,
packet.Info.functionData, true);
ssaClockSyncProfile.SyncExecute();
//no exception, so update the current profile
DASClockSyncProfile = (ClockSyncProfile)packet.Info.functionData;
}
catch (Exception ex)
{
APILogger.Log("Problem setting clock sync profile", ex);
APILogger.RaiseError(string.Format(StringResources.UnableToSetClockSync, ex.Message, SerialNumber));
}
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("SetClockSyncConfig error - has failed ", straightFailures, " times, giving up", ce);
info.Error(ce.Message, ce);
}
else
{
info.Success();
APILogger.Log("SetClockSyncConfig error", ce);
}
}
catch (Exception ex)
{
if (bLocked)
{
bLocked = false;
Release();
}
info.Error(ex.Message, ex);
}
finally
{
if (bLocked)
{
Release();
}
}
}
protected virtual void AsyncGetClockSyncStatus(object asyncInfo)
{
if (!(asyncInfo is GetClockSyncStatusPacket packet)) { return; }
if (!IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.PTPSyncStatus))
{
packet.Info.Error("Get PTP sync status not supported");
return;
}
var info = packet.Info;
var bLocked = false;
//SLICE6/S6DB only has PTP Sync
try
{
Lock();
bLocked = true;
try
{
var qsaClockSyncProfile = new QuerySystemAttributeSLICE2(this, QuerySystemAttribute.Default_IO_Timeout);
qsaClockSyncProfile.Key = AttributeTypes.SystemAttributesSLICE2.ClockSyncProfile;
qsaClockSyncProfile.SyncExecute();
DASClockSyncProfile = (ClockSyncProfile)qsaClockSyncProfile.Value;
}
catch (Exception ex)
{
APILogger.Log("Problem getting clock sync profile", ex);
}
try
{
var status = new Ptp1588GetSyncStatus(this);
status.SyncExecute();
if (null == DASClockSyncStatus)
{
DASClockSyncStatus = new Dictionary();
List sources =
Enum.GetValues(typeof(InputClockSource)).Cast().ToList();
for (int i = 1; i < sources.Count; i++)
{
// init all sources not synced
DASClockSyncStatus.Add(sources[i], false);
}
}
DASClockSyncStatus[InputClockSource.PTP] = Ptp1588Commands.PtpSyncStatus.Synced == status.SyncStatus;
if (DASClockSyncStatus[InputClockSource.PTP])
{
DoRTCInUTCCheck();
}
}
catch (Exception ex)
{
APILogger.Log("Problem getting PTP sync status", ex);
}
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("GetClockSyncStatus error - has failed ", straightFailures, " times, giving up", ce);
info.Error(ce.Message, ce);
}
else
{
if (info.userData is DTS.DASLib.Service.ServiceBase.CallbackData cbData)
{
cbData.Target.DASClockSyncStatus = null;
}
info.Success();
APILogger.Log("GetClockSyncStatus error", ce);
}
}
catch (Exception ex)
{
if (bLocked) { bLocked = false; Release(); }
info.Error(ex.Message, ex);
}
finally { if (bLocked) { Release(); } }
}
internal class SetPTPDomainIDPacket
{
public SliceServiceAsyncInfo Info { get; set; }
public SetPTPDomainIDPacket(SliceServiceAsyncInfo info)
{
Info = info;
}
}
internal class GetPTPDomainIDPacket
{
public SliceServiceAsyncInfo Info { get; set; }
public GetPTPDomainIDPacket(SliceServiceAsyncInfo info)
{
Info = info;
}
}
protected virtual void AsyncSetPTPDomainID(object asyncInfo)
{
if (!(asyncInfo is SetPTPDomainIDPacket packet))
{
return;
}
if (!IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.PTPDomainID))
{
packet.Info.Error("Set PTP Domain ID not supported");
return;
}
var info = packet.Info;
var bLocked = false;
try
{
Lock();
bLocked = true;
try
{
var ssaPTPDomainID =
new SetSystemAttributeSLICE2(this, SetSystemAttribute.Default_IO_Timeout);
ssaPTPDomainID.SetValue(AttributeTypes.SystemAttributesSLICE2.PTP_DomainNumber,
packet.Info.functionData, true);
ssaPTPDomainID.SyncExecute();
}
catch (Exception ex)
{
APILogger.Log("Problem setting ptp domain id", ex);
}
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("SetPTPDomainID error - has failed ", straightFailures, " times, giving up", ce);
info.Error(ce.Message, ce);
}
else
{
info.Success();
APILogger.Log("SetPTPDomainID error", ce);
}
}
catch (Exception ex)
{
if (bLocked)
{
bLocked = false;
Release();
}
info.Error(ex.Message, ex);
}
finally
{
if (bLocked)
{
Release();
}
}
}
protected virtual void AsyncGetPTPDomainID(object asyncInfo)
{
if (!(asyncInfo is GetPTPDomainIDPacket packet)) { return; }
if (!IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.PTPDomainID))
{
packet.Info.Error("Get PTP domain ID not supported");
return;
}
var info = packet.Info;
var bLocked = false;
try
{
Lock();
bLocked = true;
try
{
var qsaPTPDomainID = new QuerySystemAttributeSLICE2(this, QuerySystemAttribute.Default_IO_Timeout);
qsaPTPDomainID.Key = AttributeTypes.SystemAttributesSLICE2.PTP_DomainNumber;
qsaPTPDomainID.SyncExecute();
PTPDomainID = (byte)qsaPTPDomainID.Value;
}
catch (Exception ex)
{
APILogger.Log("Problem getting ptp domain id", ex);
}
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("GetPTPDomainID error - has failed ", straightFailures, " times, giving up", ce);
info.Error(ce.Message, ce);
}
else
{
if (info.userData is DTS.DASLib.Service.ServiceBase.CallbackData cbData)
{
cbData.Target.PTPDomainID = 0;
}
info.Success();
APILogger.Log("GetPTPDomainID error", ce);
}
}
catch (Exception ex)
{
if (bLocked) { bLocked = false; Release(); }
info.Error(ex.Message, ex);
}
finally { if (bLocked) { Release(); } }
}
#endregion
}
///
/// SLICE2 is basically a SLICE + some overrides for now
/// so, we extend slice and override specific functionality as required for SLICE2
///
///
public class SLICE2 : SLICE2_Base, IDASReconfigure where T : IConnection, new()
{
///
/// return values to QueryHardwareRevision
///
private enum SLICEPRO_Generation
{
SLICEPRO_GEN2 = 0,
SLICEPRO_GEN3 = 1
}
///
/// the different revisions of slice pro Gen III
/// determined by querying Hardware_Revision
///
private enum SLICEPro_GenIIIRevisions
{
MAX = 2, //200KHz fixed filter
HI = 3, //100KHz fixed filter
AUTO = 4 //4KHz fixed filter
}
///
/// gets the number of samples the hardware filter shifted collected data by
///
///
///
///
///
public override ulong GetPhaseShiftSamples(uint ModuleIndex, double ActualSampleRate, uint HardwareAAF, ulong originalT0)
{
/*int baseHardwareRevision = QueryHardwareRevision();
int BaseType = QueryBaseType();
SLICEPRO_Generation gen = (SLICEPRO_Generation)baseHardwareRevision;
SLICEPro_BaseTypes type = (SLICEPro_BaseTypes)BaseType;
switch (type)
{
case SLICEPro_BaseTypes.DIM: return GetPhaseShiftSamplesDIM(ActualSampleRate, HardwareAAF, originalT0);
case SLICEPro_BaseTypes.TOM: return GetPhaseShiftSamplesTOM(ActualSampleRate, HardwareAAF, originalT0);
case SLICEPro_BaseTypes.SIM:
{
switch (gen)
{
case SLICEPRO_Generation.SLICEPRO_GEN2:
{
QueryHardwareConfiguration qhc = new QueryHardwareConfiguration(this, Convert.ToInt32(ModuleIndex));
qhc.SyncExecute();
switch (qhc.HardwareType)
{
case QueryHardwareConfiguration.Types.IEPE_Configurable:
case QueryHardwareConfiguration.Types.Bridge_Configurable:
case QueryHardwareConfiguration.Types.Bridge:
case QueryHardwareConfiguration.Types.IEPE:
return GetPhaseShiftSamplesGen2HBW(ActualSampleRate, HardwareAAF, originalT0);
case QueryHardwareConfiguration.Types.Bridge_Configurable_Low:
case QueryHardwareConfiguration.Types.IEPE_Configurable_Low:
return GetPhaseShiftSamplesGen2LBW(ActualSampleRate, HardwareAAF, originalT0);
}
}break;
case SLICEPRO_Generation.SLICEPRO_GEN3:
{
QuerySystemAttribute_Slice2Bridge_GEN3 qsa = new QuerySystemAttribute_Slice2Bridge_GEN3(this);
qsa.DeviceID = Convert.ToByte(ModuleIndex);
qsa.Key = AttributeTypes.SystemAttributes_Bridge_SLICE2_GEN3.Hardware_Revision;
qsa.SyncExecute();
int value = Convert.ToInt32(qsa.Value);
SLICEPro_GenIIIRevisions rev = (SLICEPro_GenIIIRevisions)value;
switch (rev)
{
case SLICEPro_GenIIIRevisions.AUTO: return GetPhaseShiftSamplesGen3Auto(ActualSampleRate, HardwareAAF, originalT0);
case SLICEPro_GenIIIRevisions.HI: return GetPhaseShiftSamplesGen3Hi(ActualSampleRate, HardwareAAF, originalT0);
case SLICEPro_GenIIIRevisions.MAX: return GetPhaseShiftSamplesGen3Max(ActualSampleRate, HardwareAAF, originalT0);
}
}break;
}
}break;
}
//note, we should never get here...*/
return 0;
}
///
/// computes the number of samples hardware AAF shifted collected data
/// see
/// http://foghat/dtswiki/index.php/SLICE_PRO_Hardware_Overview#SLICE_PRO_Hardware_Input_Signal_Delay_vs_Filter_Rate
///
///
///
///
private ulong GetPhaseShiftSamplesGen3Max(double actualSampleRate, uint hardwareAAF, ulong originalT0)
{
var microSeconds = 0D;
if (hardwareAAF < 50000) { microSeconds = 4.8D + 556D / hardwareAAF; }
else if (hardwareAAF < 200000) { microSeconds = 4.8D + 556D / 45000D; }
else { microSeconds = 4.8; }
return GetPhaseShiftSamples(microSeconds, actualSampleRate, originalT0, "Gen3MAX");
}
///
/// computes the number of samples hardware AAF shifted collected data
/// see
/// http://foghat/dtswiki/index.php/SLICE_PRO_Hardware_Overview#SLICE_PRO_Hardware_Input_Signal_Delay_vs_Filter_Rate
///
///
///
///
private ulong GetPhaseShiftSamplesGen3Hi(double actualSampleRate, uint hardwareAAF, ulong originalT0)
{
var microSeconds = 0D;
if (hardwareAAF < 50000) { microSeconds = 9D + 556D / hardwareAAF; }
else if (hardwareAAF < 100000) { microSeconds = 9D + 556D / 45000D; }
else { microSeconds = 9D; }
return GetPhaseShiftSamples(microSeconds, actualSampleRate, originalT0, "Gen3HI");
}
///
/// computes the number of samples hardware AAF shifted collected data
/// see
/// http://foghat/dtswiki/index.php/SLICE_PRO_Hardware_Overview#SLICE_PRO_Hardware_Input_Signal_Delay_vs_Filter_Rate
///
///
///
///
private ulong GetPhaseShiftSamplesGen3Auto(double actualSampleRate, uint hardwareAAF, ulong originalT0)
{
var microSeconds = 0D;
if (hardwareAAF < 4000) { microSeconds = 225D + 556D / hardwareAAF; }
else { microSeconds = 225D; }
return GetPhaseShiftSamples(microSeconds, actualSampleRate, originalT0, "Gen3AUTO");
}
///
/// computes the number of samples hardware AAF shifted collected data
/// see
/// http://foghat/dtswiki/index.php/SLICE_PRO_Hardware_Overview#SLICE_PRO_Hardware_Input_Signal_Delay_vs_Filter_Rate
///
///
///
///
private ulong GetPhaseShiftSamplesGen2LBW(double actualSampleRate, uint hardwareAAF, ulong originalT0)
{
var microSeconds = 0D;
if (hardwareAAF < 50000) { microSeconds = 8D + 556D / hardwareAAF; }
else if (hardwareAAF < 100000) { microSeconds = 8D; }
else if (hardwareAAF < 200000) { microSeconds = 4D; }
else { microSeconds = 4; }
return GetPhaseShiftSamples(microSeconds, actualSampleRate, originalT0, "Gen2LBW");
}
///
/// computes the number of samples hardware AAF shifted collected data
/// see
/// http://foghat/dtswiki/index.php/SLICE_PRO_Hardware_Overview#SLICE_PRO_Hardware_Input_Signal_Delay_vs_Filter_Rate
///
///
///
///
private ulong GetPhaseShiftSamplesGen2HBW(double actualSampleRate, uint hardwareAAF, ulong originalT0)
{
var microSeconds = 0D;
if (hardwareAAF < 50000) { microSeconds = 4D + 556D / hardwareAAF; }
else if (hardwareAAF < 100000) { microSeconds = 4D + 556D / 45000; }
else if (hardwareAAF < 200000) { microSeconds = 4D; }
else { microSeconds = 2; }
return GetPhaseShiftSamples(microSeconds, actualSampleRate, originalT0, "Gen2HBW");
}
///
/// computes the number of samples hardware AAF shifted collected data
/// see
/// http://foghat/dtswiki/index.php/SLICE_PRO_Hardware_Overview#SLICE_PRO_Hardware_Input_Signal_Delay_vs_Filter_Rate
///
///
///
///
private ulong GetPhaseShiftSamplesDIM(double actualSampleRate, uint HardwareAAF, ulong originalT0)
{
return GetPhaseShiftSamples(.35D, actualSampleRate, originalT0, "Gen3DIM");
}
///
/// computes the number of samples hardware AAF shifted collected data
/// see
/// http://foghat/dtswiki/index.php/SLICE_PRO_Hardware_Overview#SLICE_PRO_Hardware_Input_Signal_Delay_vs_Filter_Rate
///
///
///
///
private ulong GetPhaseShiftSamplesTOM(double actualSampleRate, uint HardwareAAF, ulong originalT0)
{
var microSeconds = 0D;
if (HardwareAAF < 50000) { microSeconds = 9D + 556D / HardwareAAF; }
else if (HardwareAAF < 100000) { microSeconds = 9D + 556D / 45000D; }
else { microSeconds = 9; }
return GetPhaseShiftSamples(microSeconds, actualSampleRate, originalT0, "Gen3TOM");
}
void IDASReconfigure.SetMaxModuleCount(int count)
{
try
{
//turn off power supplies first
if (DASInfo != null && DASInfo.Modules != null && DASInfo.Modules.Length > 0)
{
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);
}
}
private int _maxModuleCount = -1;
///
/// gets the physical max number of modules.
///
///
int IDASReconfigure.GetMaxModuleCount()
{
if (-1 == _maxModuleCount)
{
try
{
var q = new QuerySystemAttributeSLICE2(this);
q.Key = AttributeTypes.SystemAttributesSLICE2.SLICECountOnStack;
q.SyncExecute();
_maxModuleCount = Convert.ToInt32(q.Value);
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; }
}
}
///
/// this meaty class handles skipping parts of the download not needed (start of page to desired start sample)
/// [also note we'll need to do the same thing with the end sample too if we want to use the ECC properly,
/// but ECC isn't even implemented yet ...]
///
public class QueryEventData_SLICE2 : QueryEventDataBase
{
public override UInt64 FirstSample
{
get => base.FirstSample;
set => base.FirstSample = value;
}
public override UInt64 LastSample
{
get => base.LastSample;
set => base.LastSample = value;
}
public QueryEventData_SLICE2(DTS.Common.Interface.DASFactory.ICommunication sock)
: base(sock) { LogCommands = false; }
public QueryEventData_SLICE2(DTS.Common.Interface.DASFactory.ICommunication sock, int TimeoutMillisec)
: base(sock, TimeoutMillisec) { LogCommands = false; }
private ulong GetRequestedStartSport()
{
if (recorder is SLICE2)
{
var slice2_Ethernet = recorder as SLICE2;
return (slice2_Ethernet.WhatToDownload as WhatToDownloadSlice2).RequestedStartSport;
}
else if (recorder is SLICE2)
{
var slice2_USB = recorder as SLICE2;
return (slice2_USB.WhatToDownload as WhatToDownloadSlice2).RequestedStartSport;
}
else { throw new NotSupportedException("GetRequestedStartSport not supported for " + recorder.ConnectString); }
}
private void PushLeftOverData(ushort[] daters)
{
if (recorder is SLICE2 slice2_USB) { slice2_USB.PushLeftOverData(daters); }
else if (recorder is SLICE2 slice2_Ethernet){ slice2_Ethernet.PushLeftOverData(daters); }
}
protected override CommandReceiveAction WholePackagePost()
{
// now send the data to the user
var stat = CommandStatus.Success;
if (response.Status != DFConstantsAndEnums.CommandStatus.StatusNoError)
{
var s = (int)response.Status;
APILogger.LogString("QueryEventData.WholePackagePost: reporting failure, status==" + CommandPacketBase.StatusLabels[s] + " (0x" + s.ToString("X") + ")");
stat = CommandStatus.Failure;
}
var cbReport = new QueryEventDataReport(stat, UserCallbackData);
cbReport.Data = new short[_channelsDownloaded][];
for (var i = 0; i < _channelsDownloaded; i++)
GetChannelData(i, out cbReport.Data[i]);
//we have processed some data, but there may be some left over (since data isn't channel sample aligned ...)
//figure out what we used and what's left over
//now we have two situations, one, we have already skimmed beyond all the data we need
//or two, we are somewhere in between, we need to skip a few samples
var requestedStartSpot = GetRequestedStartSport();
if ((FirstSample + (ulong)_data.Length) < requestedStartSpot)
{
//push no data, we don't want it!
}
else if (FirstSample > requestedStartSpot)
{//we want everything in here ...
var samplesProcessed = Convert.ToInt32(Math.Truncate((double)_data.Length / ChannelsDownloaded));
//performance improvement, do less calculations over iterations
var product = samplesProcessed * ChannelsDownloaded;
var leftover = new ushort[_data.Length - product];
for (var i = 0; i < leftover.Length; i++)
{
leftover[i] = _data[i + product];
}
PushLeftOverData(leftover);
}
else
{
//we need to calculate samples only from the start of the data we are interested in
var offset = Convert.ToInt32(requestedStartSpot - FirstSample);
var samplesProcessed = Convert.ToInt32(Math.Truncate(((double)_data.Length - offset) / ChannelsDownloaded));
var leftover = new ushort[_data.Length - offset - (samplesProcessed * ChannelsDownloaded)];
//performance improvement, do less calculations over iterations
offset += samplesProcessed * ChannelsDownloaded;
for (var i = 0; i < leftover.Length; i++)
{
leftover[i] = _data[i + offset];
}
PushLeftOverData(leftover);
}
return UserCallback(cbReport);
}
private ushort[] PopLeftOverData()
{
if (recorder is SLICE2 slice2_USB)
{
return slice2_USB.PopLeftOverData();
}
else if (recorder is SLICE2 slice2_Ethernet)
{
return slice2_Ethernet.PopLeftOverData();
}
else if (recorder is SLICE6 slice6_Ethernet)
{
return slice6_Ethernet.PopLeftOverData();
}
else if (recorder is SLICE6AIR slice6air_Ethernet)
{
return slice6air_Ethernet.PopLeftOverData();
}
else if (recorder is SLICE6DB slice6db_Ethernet)
{
return slice6db_Ethernet.PopLeftOverData();
}
else { throw new NotImplementedException(); }
}
protected override CommandReceiveAction WholePackage()
{
if (response.Status != DFConstantsAndEnums.CommandStatus.StatusNoError)
{
return CommandReceiveAction.StopReceiving;
}
//we are going to process the data shortly, but before we do we'll need to
//pre-pend any left over data we have to the new incoming data
//since we already count the samples downloaded for samples in the left over stuff
//we don't need to recount it, just the new incoming samples
_samplesDownloaded = (ulong)response.Parameter.Length / 2;
var leftover = PopLeftOverData();
_data = new ushort[_samplesDownloaded + (ulong)leftover.Length];
leftover.CopyTo(_data, 0);
for (var i = 0; (ulong)i < _samplesDownloaded; i++)
{
response.GetParameter(2 * i, out _data[i + leftover.Length]);
}
return CommandReceiveAction.StopReceiving;
}
public override void GetChannelData(int channel, out short[] signedADC)
{
if (channel < 0 || channel > _channelsDownloaded)
{
throw new ApplicationException("QueryEventData.GetChannelData: Data requested on a channel that wasn't downloaded.");
}
//first short circuit if we know we are still completely skipping data
if (((ulong)_data.Length + FirstSample) < GetRequestedStartSport())//(slice2.WhatToDownload as WhatToDownloadSlice2).RequestedStartSport)
{
signedADC = new short[0];//nothing to see here (we are completely before the start of our requested data)
return;
}
//now we have two situations, one, we have already skimmed beyond all the data we need
//or two, we are somewhere in between, we need to skip a few samples
var offset = 0;
if (GetRequestedStartSport() > FirstSample)
//slice2.WhatToDownload as WhatToDownloadSlice2).RequestedStartSport > FirstSample)
{
offset = Convert.ToInt32(GetRequestedStartSport() - FirstSample);
}
// Data order for a 9 channel stack
// 1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9 etc.
ushort val;
var completeSamples = Convert.ToInt32(Math.Truncate((double)(_data.Length - offset) / ChannelsDownloaded));
var rv = new short[completeSamples];
short currentVal = 0;
offset += channel;
//performance improvement, skip unnecessary stuff ...
for (var i = 0; i < completeSamples; i++)
{
val = _data[i * ChannelsDownloaded + offset];
currentVal = (short)((((val & 0x00FF) << 8) | ((val >> 8) & 0x00FF)) + 0x8000);
rv[i] = currentVal;
}
signedADC = rv;
}
// this function isn't used by SLICEWare, but it is used by the FirmwareTestUtility
// SW uses the GetChannelData above
public override void GetRawIndexedData(int index, out ushort[] data)
{
data = new ushort[_samplesDownloaded];
for (var i = 0; i < data.Length; i++)
{
data[i] = _data[i + index];
}
}
public List GetRawIndexedChannelData(ref List leftOver, ref ulong carryoverStartPoint)
{
carryoverStartPoint += Convert.ToUInt64(_data.Length);
var data = new List(leftOver);
data.AddRange(_data);
var samplesPresent = Convert.ToInt32(data.Count / ChannelsDownloaded);
var output = new List();
for (var i = 0; i < 8; i++)
{
output.Add(new ushort[samplesPresent]);
for (var iSample = 0; iSample < samplesPresent; iSample++)
{
output[i][iSample] = data[iSample * ChannelsDownloaded + i];
}
}
data.RemoveRange(0, samplesPresent * ChannelsDownloaded);
leftOver.Clear();
leftOver.AddRange(data);
return output;
}
}
}