5715 lines
266 KiB
C#
5715 lines
266 KiB
C#
|
|
//#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
|
|||
|
|
{
|
|||
|
|
/// <inheritdoc />
|
|||
|
|
/// <summary>
|
|||
|
|
/// 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
|
|||
|
|
/// </summary>
|
|||
|
|
public class WhatToDownloadSlice2 : DownloadRequest
|
|||
|
|
{
|
|||
|
|
/// <summary>
|
|||
|
|
/// 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
|
|||
|
|
/// </summary>
|
|||
|
|
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;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <inheritdoc />
|
|||
|
|
/// <summary>
|
|||
|
|
/// 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
|
|||
|
|
/// </summary>
|
|||
|
|
public override ulong EndSample
|
|||
|
|
{
|
|||
|
|
get => base.EndSample;
|
|||
|
|
set => base.EndSample = (value + 1) * _numberOfChannels;
|
|||
|
|
}
|
|||
|
|
/// <inheritdoc />
|
|||
|
|
/// <summary>
|
|||
|
|
/// 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
|
|||
|
|
/// </summary>
|
|||
|
|
public override ulong StartSample
|
|||
|
|
{
|
|||
|
|
get => base.StartSample;
|
|||
|
|
set => base.StartSample = value;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public class SLICE2_Base<T> : Slice<T>, IConfigurationActions, IVoltageInsertionEnabled, ITimeActions where T : IConnection, new()
|
|||
|
|
{
|
|||
|
|
/// <summary>
|
|||
|
|
/// indicates that voltage insertion was detected as being on
|
|||
|
|
/// http://manuscript.dts.local/f/cases/34284/Warn-when-VoltageInsertion-switches-are-set
|
|||
|
|
/// </summary>
|
|||
|
|
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;
|
|||
|
|
}
|
|||
|
|
public override int[] GetStackChannelConfigTypes() => new int[] { 0 };
|
|||
|
|
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<SetStackChannelTypeConfiguration.ChannelTypes>();
|
|||
|
|
|
|||
|
|
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();
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// clears trigger lines, used by AsyncClearDASTriggerLine and AsyncStartTriggerCheck
|
|||
|
|
/// </summary>
|
|||
|
|
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);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// complete any work we need to do before starting trigger check
|
|||
|
|
/// </summary>
|
|||
|
|
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();
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// clears any trigger lines for any das
|
|||
|
|
/// </summary>
|
|||
|
|
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);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
/// <inheritdoc />
|
|||
|
|
/// <summary>
|
|||
|
|
/// 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
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="moduleIndex"></param>
|
|||
|
|
/// <param name="channelOnModule"></param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
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<int, ushort>();
|
|||
|
|
var existingFineShunts = new Dictionary<int, ushort>();
|
|||
|
|
var existingSwitchValues = new Dictionary<int, Dictionary<byte, byte>>();
|
|||
|
|
|
|||
|
|
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<byte, byte>();
|
|||
|
|
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;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <inheritdoc />
|
|||
|
|
/// <summary>
|
|||
|
|
/// Convert gain code to value based on Gen2 vs. Gen3 conversion table
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="gainCode"></param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
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<int, string> _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<int, string>();
|
|||
|
|
|
|||
|
|
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)
|
|||
|
|
{
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 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
|
|||
|
|
/// </summary>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
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;
|
|||
|
|
}
|
|||
|
|
/// <inheritdoc />
|
|||
|
|
/// <summary>
|
|||
|
|
/// 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
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="order"></param>
|
|||
|
|
public override void SetDASDisplayOrder(int order)
|
|||
|
|
{
|
|||
|
|
SetUserAttribute(AttributeTypes.SliceUserAttributes.DasDisplayOrder,
|
|||
|
|
order.ToString(System.Globalization.CultureInfo.InvariantCulture));
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// sets the channel display order for slice 1.5/slice pro
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="order"></param>
|
|||
|
|
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());
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// gets the channel display order for slice 1.5/slice pro
|
|||
|
|
/// </summary>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
public override int[] GetChannelDisplayOrder()
|
|||
|
|
{
|
|||
|
|
var s = GetUserAttribute(AttributeTypes.SliceUserAttributes.DisplayOrder);
|
|||
|
|
var atts = new List<int>();
|
|||
|
|
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();
|
|||
|
|
|
|||
|
|
var query = new QueryBaseSystemTime(this);
|
|||
|
|
query.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)
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 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
|
|||
|
|
/// </summary>
|
|||
|
|
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
|
|||
|
|
});
|
|||
|
|
/// <summary>
|
|||
|
|
/// 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
|
|||
|
|
/// </summary>
|
|||
|
|
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
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// decorated with min/max/"middle" for
|
|||
|
|
/// http://fogbugz/fogbugz/default.asp?10080
|
|||
|
|
/// </summary>
|
|||
|
|
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
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 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
|
|||
|
|
/// </summary>
|
|||
|
|
public class GainSorterSLICE6IEPE : IComparer<GainCodesSLICE6IEPE>
|
|||
|
|
{
|
|||
|
|
public int Compare(GainCodesSLICE6IEPE left, GainCodesSLICE6IEPE right)
|
|||
|
|
{
|
|||
|
|
var leftRange = FirmwareInputRangeAttribute.GetFirmwareInputRangemV(left);
|
|||
|
|
var rightRange = FirmwareInputRangeAttribute.GetFirmwareInputRangemV(right);
|
|||
|
|
return leftRange.CompareTo(rightRange);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// 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
|
|||
|
|
/// </summary>
|
|||
|
|
public class GainSorter : IComparer<GainCodesGen3>
|
|||
|
|
{
|
|||
|
|
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;
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 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
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="bridgeType"></param>
|
|||
|
|
/// <param name="bModified">whether the SPS is "modified" or not(http://fogbugz/fogbugz/default.asp?10080)</param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
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);
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// 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
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="dasChannelNumber"> </param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
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);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// 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
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="ChannelActions"></param>
|
|||
|
|
/// <param name="results"></param>
|
|||
|
|
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 SupportsMultipleConfigurations() { return IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.MultipleConfigurations); }
|
|||
|
|
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;
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 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
|
|||
|
|
/// </summary>
|
|||
|
|
protected static object LeftOverDataLock = new object();
|
|||
|
|
protected ushort[] _leftOverData = null;
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 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
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="data"></param>
|
|||
|
|
public void PushLeftOverData(ushort[] data)
|
|||
|
|
{
|
|||
|
|
lock (LeftOverDataLock)
|
|||
|
|
{
|
|||
|
|
_leftOverData = data;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// retrieves held over data between calls to QueryEventData
|
|||
|
|
/// </summary>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
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();
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// created for 11282, this just clears the diagnostics mode for das
|
|||
|
|
/// this should be part of a generic services
|
|||
|
|
/// </summary>
|
|||
|
|
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);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// for SLICE2 we override WhatToDownload and transparently handle things our own way from the outside
|
|||
|
|
/// </summary>
|
|||
|
|
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);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// QueryEventData also is customized for SLICE2, it needs to perform SLICE2 specific data marshalling
|
|||
|
|
/// </summary>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
protected override QueryEventDataBase GetQueryEventData()
|
|||
|
|
{
|
|||
|
|
return new QueryEventData_SLICE2(this, AbstractCommandBase.Default_IO_Timeout);
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// 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
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="guid"></param>
|
|||
|
|
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);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// overrided as SLICE2 will not hold onto GUID right now
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="eventNum"></param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
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;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// hardcoded constants right now ... maybe these belong in attributes in the firmware!
|
|||
|
|
/// </summary>
|
|||
|
|
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);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 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
|
|||
|
|
/// </summary>
|
|||
|
|
private static readonly Dictionary<int, uint> _SPDModulesToMaxRate = new Dictionary<int, uint>()
|
|||
|
|
{
|
|||
|
|
{1, 1500000 },
|
|||
|
|
{2, 1200000 },
|
|||
|
|
{3, 1000000 },
|
|||
|
|
{4, 800000 },
|
|||
|
|
{5, 700000 },
|
|||
|
|
{6, 600000 }
|
|||
|
|
};
|
|||
|
|
/// <summary>
|
|||
|
|
/// the default max sample rate for an SPD if we don't have a matching module count
|
|||
|
|
/// </summary>
|
|||
|
|
private const uint DEFAULT_MAX_SPS_SPD = 600000;
|
|||
|
|
/// <summary>
|
|||
|
|
/// returns the max sample rate for a SLICE DIM
|
|||
|
|
/// http://manuscript.dts.local/f/cases/43548/Add-support-for-1-5MB-SPS-on-SPD
|
|||
|
|
/// </summary>
|
|||
|
|
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;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 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)
|
|||
|
|
/// </summary>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
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;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// returns a GetRealtimeSamples class object appropriate for the DAS
|
|||
|
|
/// this is partially necessary as SLICE2 uses a different channel ordering than SLICE1
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="iCommunication"></param>
|
|||
|
|
/// <param name="bPolling">whether realtime is polling for samples or not</param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
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);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// indicates whether the DAS supports streaming
|
|||
|
|
/// 10572 implement SW side for single command streaming realtime
|
|||
|
|
/// (note protocol >=152 required for streaming)
|
|||
|
|
/// </summary>
|
|||
|
|
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<short[]>(_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<DFConstantsAndEnums.ProtocolLimitedCommands, byte> SLICE2_MinimumProtocols = new Dictionary<DFConstantsAndEnums.ProtocolLimitedCommands, byte>();
|
|||
|
|
|
|||
|
|
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();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
private enum ModificationSettings
|
|||
|
|
{
|
|||
|
|
None = 0,
|
|||
|
|
UseFullGainList = 1 << 0,
|
|||
|
|
Excitation7_9V = 1 << 1
|
|||
|
|
};
|
|||
|
|
/// <summary>
|
|||
|
|
/// the TOM is so different it didn't make sense to reuse the old AsyncConfigure, so I created one
|
|||
|
|
/// specifically for TOM
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="configAsyncInfo"></param>
|
|||
|
|
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 || this is EthernetSlice6AirThermocoupler);
|
|||
|
|
}
|
|||
|
|
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 = ((Convert.ToInt32(queryFeatureConfig.Value)) & (int)ModificationSettings.UseFullGainList) == (int)ModificationSettings.UseFullGainList;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
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
|
|||
|
|
)
|
|||
|
|
{
|
|||
|
|
if (null == analog) { return; }
|
|||
|
|
var requestedRange = GetRequestedRange(analog, MvPerEU);
|
|||
|
|
APILogger.DebugLog($"CommonRangeWork HC={analog?.HardwareChannelName ?? "NULL"}, mVPerEu={MvPerEU}, DasChannelNumber={dasChannelNumber}, requestedRange={requestedRange}");
|
|||
|
|
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))
|
|||
|
|
{
|
|||
|
|
APILogger.DebugLog($"CommonRangeWork HC={analog?.HardwareChannelName ?? "NULL"}, mVPerEu={MvPerEU}, DasChannelNumber={dasChannelNumber}, AdjustingInputRange");
|
|||
|
|
requestedRange = AdjustInputRangeIfNeeded(requestedRange, bModified);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
APILogger.DebugLog($"CommonRangeWork HC={analog?.HardwareChannelName ?? "NULL"}, mVPerEu={MvPerEU}, DasChannelNumber={dasChannelNumber}, AssignedRange={requestedRange}");
|
|||
|
|
rangeArray[dasChannelNumber] = Convert.ToSingle(requestedRange);
|
|||
|
|
}
|
|||
|
|
protected void CommonConfigureWork(List<byte> 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)
|
|||
|
|
{
|
|||
|
|
APILogger.DebugLog($"CommonConfigureWork - {SerialNumber} Start");
|
|||
|
|
for (var moduleIdx = 0; moduleIdx < ConfigData.Modules.Length; moduleIdx++)
|
|||
|
|
{
|
|||
|
|
APILogger.DebugLog($"CommonConfigureWork - module={moduleIdx}");
|
|||
|
|
var module = ConfigData.Modules[moduleIdx];
|
|||
|
|
|
|||
|
|
// configure the range for this bridge
|
|||
|
|
for (var channelIdx = 0; channelIdx < module.Channels.Length; channelIdx++)
|
|||
|
|
{
|
|||
|
|
APILogger.DebugLog($"CommonConfigureWork - module={moduleIdx}, channel={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;
|
|||
|
|
}
|
|||
|
|
APILogger.DebugLog($"CommonConfigureWork {analog?.HardwareChannelName ?? "N/A"} sensitivity={analog?.SensitivityMilliVoltsPerEU}");
|
|||
|
|
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<NoDiagnosticsAvailable>(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<NoDiagnosticsAvailable>(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<NoDiagnosticsAvailable>(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<NoDiagnosticsAvailable>(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
|
|||
|
|
{
|
|||
|
|
APILogger.DebugLog($"CommonConfigureWork {analog?.HardwareChannelName ?? "N/A"} sensitivity 0, so range is 0");
|
|||
|
|
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));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// abstracted this method out from a series of duplicated and very long functions
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="progressValue"></param>
|
|||
|
|
/// <param name="info"></param>
|
|||
|
|
/// <param name="diagnosticChannels"></param>
|
|||
|
|
/// <param name="config"></param>
|
|||
|
|
/// <param name="bReleased"></param>
|
|||
|
|
/// <param name="scaleFactors">scale factors for channels, only used in TSR AIR for some reason
|
|||
|
|
/// <param name="ranges">ranges for channels, only used in TSR AIR for some reason</param>
|
|||
|
|
/// <param name="measuredOffset">measured offset, only used in TSR AIR for some reason</param>
|
|||
|
|
protected void RemainingConfigWork(ref int progressValue,
|
|||
|
|
SliceConfigServiceAsyncInfo info,
|
|||
|
|
List<byte> 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<byte>();
|
|||
|
|
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)
|
|||
|
|
{
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// 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
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="configAsyncInfo"></param>
|
|||
|
|
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<byte>();
|
|||
|
|
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);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// 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
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="originalRange"></param>
|
|||
|
|
/// <param name="bModified"></param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
protected virtual double AdjustInputRangeIfNeeded(double originalRange, bool bModified)
|
|||
|
|
{
|
|||
|
|
if (_hardwareRevision != GEN3_REV_B) return originalRange;
|
|||
|
|
var gains = Enum.GetValues(typeof(GainCodesGen3)).Cast<GainCodesGen3>().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);
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// SLICE2 config attributes, mostly inherits from SLICE.ConfigAttributes with some functionality removed
|
|||
|
|
/// </summary>
|
|||
|
|
protected class SLICE2ConfigAttributes : ConfigAttributes
|
|||
|
|
{
|
|||
|
|
/// <summary>
|
|||
|
|
/// 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
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="das"></param>
|
|||
|
|
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();
|
|||
|
|
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 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.
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="data"></param>
|
|||
|
|
/// <param name="info"></param>
|
|||
|
|
/// <param name="ProgressSteps"></param>
|
|||
|
|
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;
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// 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.
|
|||
|
|
/// </summary>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
public override string RetrieveXMLConfig(FileStore fileStore, IDASCommunication das)
|
|||
|
|
{
|
|||
|
|
lock (_storeXMLConfigLock)
|
|||
|
|
{
|
|||
|
|
var WholeStr = "";
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
|
|||
|
|
var allBytes = new List<byte>();
|
|||
|
|
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<byte>(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<byte> 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
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 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]
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="eventNum">The event number to get it from</param>
|
|||
|
|
/// <param name="progress"></param>
|
|||
|
|
/// <returns>The combined XML string</returns>
|
|||
|
|
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<DASChannel>();
|
|||
|
|
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<int, string[]> 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;
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 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
|
|||
|
|
/// </summary>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
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();
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// 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)
|
|||
|
|
/// </summary>
|
|||
|
|
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<T>.ConfigAttributes.FileStore.Event);
|
|||
|
|
config.StoreXMLConfig(fileData, Slice<T>.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<byte> 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];
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// 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)
|
|||
|
|
/// </summary>
|
|||
|
|
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
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// 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)
|
|||
|
|
/// </summary>
|
|||
|
|
protected virtual void QueryBaseFirmwareBuildId()
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
var query = new QuerySystemAttributeSLICE2(this);
|
|||
|
|
query.Key = AttributeTypes.SystemAttributesSLICE2.BaseFirmwareBuildID;
|
|||
|
|
query.SyncExecute();
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
APILogger.Log(ex);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// retrieves the number of times configuration has been written, increments the count, then stores the value
|
|||
|
|
/// </summary>
|
|||
|
|
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);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// retrieves the number of times the configuration has been retrieved, increments, and stores
|
|||
|
|
/// </summary>
|
|||
|
|
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);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// retrieves the number of times configuration was detected invalid, increments, and stores the count
|
|||
|
|
/// </summary>
|
|||
|
|
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);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// basically a copy of SLICE1 ... may be able to eliminate and use the base version.
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="configAsyncInfo"></param>
|
|||
|
|
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<IEventAggregator>();
|
|||
|
|
eventAggregator.GetEvent<DASConfigurationEvent>().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);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// 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.
|
|||
|
|
/// </summary>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
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<uint[]>();
|
|||
|
|
|
|||
|
|
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<T>.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);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// Retrieve the info to fill in the ConfigData property
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="callback">The delegate to report to</param>
|
|||
|
|
/// <param name="userData">User supplied data that we pass along</param>
|
|||
|
|
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
|
|||
|
|
/// <summary>
|
|||
|
|
/// AsyncInfo for IClockSyncActions that derive from here (S6, S6A, S6DB, etc)
|
|||
|
|
/// </summary>
|
|||
|
|
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<InputClockSource, bool>();
|
|||
|
|
List<InputClockSource> sources =
|
|||
|
|
Enum.GetValues(typeof(InputClockSource)).Cast<InputClockSource>().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
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// SLICE2 is basically a SLICE + some overrides for now
|
|||
|
|
/// so, we extend slice and override specific functionality as required for SLICE2
|
|||
|
|
/// </summary>
|
|||
|
|
/// <typeparam name="T"></typeparam>
|
|||
|
|
public class SLICE2<T> : SLICE2_Base<T>, IDASReconfigure where T : IConnection, new()
|
|||
|
|
{
|
|||
|
|
/// <summary>
|
|||
|
|
/// return values to QueryHardwareRevision
|
|||
|
|
/// </summary>
|
|||
|
|
private enum SLICEPRO_Generation
|
|||
|
|
{
|
|||
|
|
SLICEPRO_GEN2 = 0,
|
|||
|
|
SLICEPRO_GEN3 = 1
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// the different revisions of slice pro Gen III
|
|||
|
|
/// determined by querying Hardware_Revision
|
|||
|
|
/// </summary>
|
|||
|
|
private enum SLICEPro_GenIIIRevisions
|
|||
|
|
{
|
|||
|
|
MAX = 2, //200KHz fixed filter
|
|||
|
|
HI = 3, //100KHz fixed filter
|
|||
|
|
AUTO = 4 //4KHz fixed filter
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// gets the number of samples the hardware filter shifted collected data by
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="ModuleIndex"></param>
|
|||
|
|
/// <param name="ActualSampleRate"></param>
|
|||
|
|
/// <param name="HardwareAAF"></param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
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;
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// 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
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="actualSampleRate"></param>
|
|||
|
|
/// <param name="hardwareAAF"></param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
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");
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// 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
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="actualSampleRate"></param>
|
|||
|
|
/// <param name="hardwareAAF"></param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
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");
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// 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
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="actualSampleRate"></param>
|
|||
|
|
/// <param name="hardwareAAF"></param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
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");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 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
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="actualSampleRate"></param>
|
|||
|
|
/// <param name="hardwareAAF"></param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
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");
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// 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
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="actualSampleRate"></param>
|
|||
|
|
/// <param name="hardwareAAF"></param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
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");
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// 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
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="actualSampleRate"></param>
|
|||
|
|
/// <param name="hardwareAAF"></param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
private ulong GetPhaseShiftSamplesDIM(double actualSampleRate, uint HardwareAAF, ulong originalT0)
|
|||
|
|
{
|
|||
|
|
return GetPhaseShiftSamples(.35D, actualSampleRate, originalT0, "Gen3DIM");
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// 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
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="actualSampleRate"></param>
|
|||
|
|
/// <param name="hardwareAAF"></param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
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;
|
|||
|
|
/// <summary>
|
|||
|
|
/// gets the physical max number of modules.
|
|||
|
|
/// </summary>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
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; }
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// 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 ...]
|
|||
|
|
/// </summary>
|
|||
|
|
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<EthernetConnection>)
|
|||
|
|
{
|
|||
|
|
var slice2_Ethernet = recorder as SLICE2<EthernetConnection>;
|
|||
|
|
return (slice2_Ethernet.WhatToDownload as WhatToDownloadSlice2).RequestedStartSport;
|
|||
|
|
}
|
|||
|
|
else if (recorder is SLICE2<WINUSBConnection>)
|
|||
|
|
{
|
|||
|
|
var slice2_USB = recorder as SLICE2<WINUSBConnection>;
|
|||
|
|
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<WINUSBConnection> slice2_USB) { slice2_USB.PushLeftOverData(daters); }
|
|||
|
|
else if (recorder is SLICE2<EthernetConnection> 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<WINUSBConnection> slice2_USB)
|
|||
|
|
{
|
|||
|
|
return slice2_USB.PopLeftOverData();
|
|||
|
|
}
|
|||
|
|
else if (recorder is SLICE2<EthernetConnection> slice2_Ethernet)
|
|||
|
|
{
|
|||
|
|
return slice2_Ethernet.PopLeftOverData();
|
|||
|
|
}
|
|||
|
|
else if (recorder is SLICE6<EthernetConnection> slice6_Ethernet)
|
|||
|
|
{
|
|||
|
|
return slice6_Ethernet.PopLeftOverData();
|
|||
|
|
}
|
|||
|
|
else if (recorder is SLICE6AIR<EthernetConnection> slice6air_Ethernet)
|
|||
|
|
{
|
|||
|
|
return slice6air_Ethernet.PopLeftOverData();
|
|||
|
|
}
|
|||
|
|
else if (recorder is SLICE6DB<EthernetConnection> 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<ushort[]> GetRawIndexedChannelData(ref List<ushort> leftOver, ref ulong carryoverStartPoint)
|
|||
|
|
{
|
|||
|
|
carryoverStartPoint += Convert.ToUInt64(_data.Length);
|
|||
|
|
|
|||
|
|
var data = new List<ushort>(leftOver);
|
|||
|
|
data.AddRange(_data);
|
|||
|
|
var samplesPresent = Convert.ToInt32(data.Count / ChannelsDownloaded);
|
|||
|
|
|
|||
|
|
var output = new List<ushort[]>();
|
|||
|
|
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;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|