1117 lines
54 KiB
Plaintext
1117 lines
54 KiB
Plaintext
|
|
using System;
|
||
|
|
using System.Collections.Generic;
|
||
|
|
using System.Linq;
|
||
|
|
using DTS.DASLib.Command.SLICE;
|
||
|
|
using DTS.Common.DAS.Concepts;
|
||
|
|
using DTS.DASLib.Command;
|
||
|
|
using System.Threading;
|
||
|
|
using DTS.Common;
|
||
|
|
using DTS.Common.ICommunication;
|
||
|
|
using DTS.Common.Utilities.Logging;
|
||
|
|
using DTS.DASLib.Command.SLICE.DownloadCommands;
|
||
|
|
using System.IO;
|
||
|
|
using DTS.DASLib.Command.SLICE.RealtimeCommands;
|
||
|
|
using DTS.Common.DAS.Concepts.DAS.Channel;
|
||
|
|
using DTS.Common.Enums.Sensors;
|
||
|
|
using DTS.Common.Interface.Connection;
|
||
|
|
using DTS.Common.Interface.DASFactory.Diagnostics;
|
||
|
|
using DTS.Common.Enums.DASFactory;
|
||
|
|
using DTS.Common.Interface.DASFactory;
|
||
|
|
using DTS.DASLib.Service.Interfaces;
|
||
|
|
using DTS.Common.Constant.DASSpecific;
|
||
|
|
using DTS.Common.Enums.Hardware;
|
||
|
|
using DTS.DASLib.Service.Classes;
|
||
|
|
using Prism.Ioc;
|
||
|
|
using DTS.Common.Events;
|
||
|
|
using Prism.Events;
|
||
|
|
|
||
|
|
namespace DTS.DASLib.Service
|
||
|
|
{
|
||
|
|
|
||
|
|
public class SLICE6<T> : SLICE6_Base<T> where T : IConnection, new()
|
||
|
|
{
|
||
|
|
}
|
||
|
|
|
||
|
|
public class SLICE6_Base<T> : SLICE2_Base<T>, IClockSyncActions, ITiltSensorCalAware, ITMATSStreamingDevice where T : IConnection, new()
|
||
|
|
{
|
||
|
|
public virtual int GetMaxFileLengthTMATS() { return InformationCommands.MAX_FILE_LENGTH_ID100; }
|
||
|
|
protected override void RestoreOriginalStartRecordDelay()
|
||
|
|
{
|
||
|
|
//DO NOT do this for S6 and beyond, the attribute is used differently, see
|
||
|
|
//http://manuscript.dts.local/f/cases/43048/
|
||
|
|
}
|
||
|
|
|
||
|
|
protected override void StoreOriginalStartRecordDelayAndClear()
|
||
|
|
{
|
||
|
|
//DO NOT do this for S6 and beyond, the attribute is used differently, see
|
||
|
|
//http://manuscript.dts.local/f/cases/43048/
|
||
|
|
}
|
||
|
|
protected override bool AdjustInputRange(AnalogInputDASChannel analog)
|
||
|
|
{
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
/// <summary>
|
||
|
|
/// returns whether the device supports start completion inversion or not
|
||
|
|
/// <inheritdoc cref="IDASCommunication"/>
|
||
|
|
/// </summary>
|
||
|
|
/// <returns>true if the device supports start inversion, false otherwise</returns>
|
||
|
|
public override bool SupportsStartInversion() => HardwareConstants.SupportsStartInversion(GetHardwareType(), ProtocolVersion);
|
||
|
|
/// <summary>
|
||
|
|
/// returns whether the device supports trigger completion inversion or not
|
||
|
|
/// <inheritdoc cref="IDASCommunication"/>
|
||
|
|
/// </summary>
|
||
|
|
/// <returns>true if the device supports trigger inversion, false otherwise</returns>
|
||
|
|
public override bool SupportsTriggerInversion() => HardwareConstants.SupportsTriggerInversion(GetHardwareType(), ProtocolVersion);
|
||
|
|
public double[] TiltSensorCals { get; protected set; }
|
||
|
|
/// <summary>
|
||
|
|
/// indicates whether the DAS supports streaming
|
||
|
|
/// 10572 implement SW side for single command streaming real time
|
||
|
|
/// (probably needs to check protocol of devices ... but I don't have info on protocol currently)
|
||
|
|
/// </summary>
|
||
|
|
public override bool SupportsUDPRealtimeStreaming
|
||
|
|
{
|
||
|
|
get
|
||
|
|
{
|
||
|
|
return ProtocolVersion >= GetMinProto(DFConstantsAndEnums.ProtocolLimitedCommands.UDPRealtimeStream);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
protected override void ResetEventListPriorToArm()
|
||
|
|
{
|
||
|
|
//12638 DAS does not record data in recorder mode during calibration ~ 40% of time.
|
||
|
|
//reseteventlist only gets called before configuring with SLICE6
|
||
|
|
}
|
||
|
|
|
||
|
|
protected void ResetEventListPriorToConfigure()
|
||
|
|
{
|
||
|
|
var resetEvents = new ResetEventList(this);
|
||
|
|
resetEvents.SyncExecute();
|
||
|
|
}
|
||
|
|
|
||
|
|
public override void Download(ServiceCallback callback, object userData)
|
||
|
|
{
|
||
|
|
if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.DownloadStreaming))
|
||
|
|
{
|
||
|
|
DownloadStream(callback, userData);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
base.Download(callback, userData);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private void DownloadStream(ServiceCallback callback, object userData)
|
||
|
|
{
|
||
|
|
var state = new SliceDownloadState(callback, userData, WhatToDownload);
|
||
|
|
try
|
||
|
|
{
|
||
|
|
var start = new StartDownloadStreamData(this)
|
||
|
|
{
|
||
|
|
FirstSample = WhatToDownload.StartSample,
|
||
|
|
EventNumber = WhatToDownload.EventNumber,
|
||
|
|
LastSample = WhatToDownload.EndSample
|
||
|
|
};
|
||
|
|
APILogger.Log("Starting stream ", WhatToDownload.StartSample, WhatToDownload.EndSample);
|
||
|
|
start.SyncExecute();
|
||
|
|
|
||
|
|
int samplesToExtract;
|
||
|
|
ulong totalDownloaded = 0;
|
||
|
|
var getDownloadData = new GetNextDownloadStreamDataSamples(this);
|
||
|
|
var needed = WhatToDownload.EndSample - WhatToDownload.StartSample;
|
||
|
|
needed -= 1248;
|
||
|
|
var queue = new Queue<ushort>();
|
||
|
|
|
||
|
|
while (totalDownloaded < needed)
|
||
|
|
{
|
||
|
|
getDownloadData.SyncExecute();
|
||
|
|
getDownloadData.ProcessData();
|
||
|
|
if (getDownloadData.DlData?.DlData == null)
|
||
|
|
{
|
||
|
|
Thread.Sleep(10);
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
foreach (var sample in getDownloadData.DlData.DlData)
|
||
|
|
{
|
||
|
|
queue.Enqueue(sample);
|
||
|
|
}
|
||
|
|
samplesToExtract = Convert.ToInt32(Math.Floor(queue.Count / 6D));
|
||
|
|
if (samplesToExtract > 0)
|
||
|
|
{
|
||
|
|
var newData = new short[6][];
|
||
|
|
for (var i = 0; i < 6; i++)
|
||
|
|
{
|
||
|
|
newData[i] = new short[samplesToExtract];
|
||
|
|
}
|
||
|
|
for (var sampleIndex = 0; sampleIndex < samplesToExtract; sampleIndex++)
|
||
|
|
{
|
||
|
|
for (var channelIndex = 0; channelIndex < 6; channelIndex++)
|
||
|
|
{
|
||
|
|
var unsignedValue = queue.Dequeue();
|
||
|
|
var adc = (short)((((unsignedValue & 0x00FF) << 8) | ((unsignedValue >> 8) & 0x00FF)) + 0x8000);
|
||
|
|
newData[channelIndex][sampleIndex] = adc;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
state.NewData(newData, 0, ulong.MinValue, ulong.MinValue);
|
||
|
|
}
|
||
|
|
var ratio = 100D * totalDownloaded / needed;
|
||
|
|
if (ratio < 0)
|
||
|
|
{
|
||
|
|
ratio = 0;
|
||
|
|
}
|
||
|
|
else if (ratio > 100)
|
||
|
|
{
|
||
|
|
ratio = 100D;
|
||
|
|
}
|
||
|
|
state.Progress(Convert.ToInt32(ratio));
|
||
|
|
totalDownloaded += (ulong)getDownloadData.DlData.DlData.Length;
|
||
|
|
}
|
||
|
|
samplesToExtract = Convert.ToInt32(Math.Floor(queue.Count / 6D));
|
||
|
|
if (samplesToExtract > 0)
|
||
|
|
{
|
||
|
|
var newData = new short[6][];
|
||
|
|
for (var i = 0; i < 6; i++)
|
||
|
|
{
|
||
|
|
newData[i] = new short[samplesToExtract];
|
||
|
|
}
|
||
|
|
for (var sampleIndex = 0; sampleIndex < samplesToExtract; sampleIndex++)
|
||
|
|
{
|
||
|
|
for (var channelIndex = 0; channelIndex < 6; channelIndex++)
|
||
|
|
{
|
||
|
|
var unsignedValue = queue.Dequeue();
|
||
|
|
var adc = (short)((((unsignedValue & 0x00FF) << 8) | ((unsignedValue >> 8) & 0x00FF)) + 0x8000);
|
||
|
|
newData[channelIndex][sampleIndex] = adc;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
state.NewData(newData, 0, ulong.MinValue, ulong.MinValue);
|
||
|
|
}
|
||
|
|
state.Success();
|
||
|
|
}
|
||
|
|
catch (Exception ex)
|
||
|
|
{
|
||
|
|
APILogger.Log(ex);
|
||
|
|
state.Error(ex.Message);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// 10826 Parent Case for missing Data in SLICE 6 Downloads
|
||
|
|
/// a SLICE6 will ALWAYS have 6 channels, so warn in the logs we have a different value, but
|
||
|
|
/// stick with the 6...
|
||
|
|
/// </summary>
|
||
|
|
/// <param name="eventNum"></param>
|
||
|
|
/// <returns></returns>
|
||
|
|
protected override uint GetEventTotalChannels(int eventNum)
|
||
|
|
{
|
||
|
|
try
|
||
|
|
{
|
||
|
|
var eventTC = new QueryEventAttribute(this);
|
||
|
|
eventTC.EventNumber = (ushort)eventNum;
|
||
|
|
eventTC.Key = AttributeTypes.ArmAndEventAttributes.TotalChannels;
|
||
|
|
eventTC.SyncExecute();
|
||
|
|
var value = Convert.ToUInt32(eventTC.Value);
|
||
|
|
if (value != 6)
|
||
|
|
{
|
||
|
|
APILogger.Log($"Invalid number of channels ({value}) for unit {SerialNumber} - ignoring");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
catch (Exception ex)
|
||
|
|
{
|
||
|
|
APILogger.Log("failed to get event total channels: ", ex);
|
||
|
|
}
|
||
|
|
return 0x06;
|
||
|
|
}
|
||
|
|
/// <summary>
|
||
|
|
/// gets the expected excitation for a channel
|
||
|
|
/// note in SLICE6 theres only one REAL module, although there is 2 in DataPRO
|
||
|
|
/// this is for consistency of the API with SLICE1
|
||
|
|
/// also note we have different attributes for SLICE6 and only support 5V excitation
|
||
|
|
/// 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)
|
||
|
|
{
|
||
|
|
//convert from channel ABC, ABC to ABCDEF [only 1x6 arrangement on base 2x3 in datapro]
|
||
|
|
if (1 == moduleIndex)
|
||
|
|
{
|
||
|
|
channelOnModule += 3;
|
||
|
|
}
|
||
|
|
if (ConfigData?.Modules == null || ConfigData.Modules.Length <= moduleIndex)
|
||
|
|
{
|
||
|
|
APILogger.Log("unabled to get expected excitation, no config data to base excitation on");
|
||
|
|
return 0D;
|
||
|
|
}
|
||
|
|
if (!(ConfigData.Modules[moduleIndex].Channels[channelOnModule] is AnalogInputDASChannel aic))
|
||
|
|
{
|
||
|
|
//we don't expect to ever get here [SLICE6 should only have analog channels] but if we do, don't crash
|
||
|
|
APILogger.Log(
|
||
|
|
"unable to get expected excitation, channel has no excitation information (is not analog)");
|
||
|
|
return 0D;
|
||
|
|
}
|
||
|
|
var excitation = Test.Module.Channel.Sensor.GetExcitationVoltageMagnitudeFromEnum(aic.Excitation) * 1000D;
|
||
|
|
try
|
||
|
|
{
|
||
|
|
var qsa = new QuerySystemAttribute_BridgeSlice6(this);
|
||
|
|
|
||
|
|
switch (channelOnModule)
|
||
|
|
{
|
||
|
|
case 0:
|
||
|
|
qsa.Key = AttributeTypes.SystemAttributes_BridgeSlice6.FactoryCal_ChA_5V;
|
||
|
|
break;
|
||
|
|
case 1:
|
||
|
|
qsa.Key = AttributeTypes.SystemAttributes_BridgeSlice6.FactoryCal_ChB_5V;
|
||
|
|
break;
|
||
|
|
case 2:
|
||
|
|
qsa.Key = AttributeTypes.SystemAttributes_BridgeSlice6.FactoryCal_ChC_5V;
|
||
|
|
break;
|
||
|
|
case 3:
|
||
|
|
qsa.Key = AttributeTypes.SystemAttributes_BridgeSlice6.FactoryCal_ChD_5V;
|
||
|
|
break;
|
||
|
|
case 4:
|
||
|
|
qsa.Key = AttributeTypes.SystemAttributes_BridgeSlice6.FactoryCal_ChE_5V;
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
qsa.Key = AttributeTypes.SystemAttributes_BridgeSlice6.FactoryCal_ChF_5V;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
qsa.DeviceID = 0x01; //per Loc we can always use 1 with slice6
|
||
|
|
qsa.SyncExecute();
|
||
|
|
var bridgeExcitation = Convert.ToDouble(qsa.Value);//these appear to already by in mV, no conversion needed...
|
||
|
|
var delta = Math.Abs(excitation - bridgeExcitation);
|
||
|
|
//if this one isn't with 500mV of target, it's likely invalid too ... don't use it
|
||
|
|
if (delta < 500)
|
||
|
|
{
|
||
|
|
excitation = bridgeExcitation;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
catch (Exception ex)
|
||
|
|
{
|
||
|
|
APILogger.Log("failed to get excitation: ", ex);
|
||
|
|
}
|
||
|
|
return excitation;
|
||
|
|
}
|
||
|
|
|
||
|
|
public override bool CheckAAF(float rate) { return true; }
|
||
|
|
public override bool SupportsTimeSynchronization => true;
|
||
|
|
|
||
|
|
public override double[] GetNominalRanges(SensorConstants.BridgeType bridgeType)
|
||
|
|
{
|
||
|
|
//FB15462 separate S6 gain limits from S6A
|
||
|
|
switch (bridgeType)
|
||
|
|
{
|
||
|
|
case SensorConstants.BridgeType.IEPE:
|
||
|
|
return WinUSBSlice.StaticDASS6EIEPEInfo.NominalRanges;
|
||
|
|
default:
|
||
|
|
return WinUSBSlice.StaticDASBridgeInfo.NominalRanges;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Convert gain code to value based on Slice 1 conversion table
|
||
|
|
/// </summary>
|
||
|
|
/// <param name="gainCode"></param>
|
||
|
|
/// <returns></returns>
|
||
|
|
protected override double GainCodeToGainValue(ushort gainCode)
|
||
|
|
{
|
||
|
|
//Run the same code as Slice 1.0, not base:GainCodeToGainValue which is SLICE 2
|
||
|
|
var gainValueString = ((GainCodes)gainCode).ToString();
|
||
|
|
if (!double.TryParse(gainValueString.ToString().TrimStart('G'), out double gainValue))
|
||
|
|
{
|
||
|
|
gainValue = 1.0D;
|
||
|
|
}
|
||
|
|
|
||
|
|
return gainValue;
|
||
|
|
}
|
||
|
|
|
||
|
|
private readonly Dictionary<DFConstantsAndEnums.ProtocolLimitedCommands, byte> SLICE6_MinimumProtocols = new Dictionary<DFConstantsAndEnums.ProtocolLimitedCommands, byte>();
|
||
|
|
|
||
|
|
|
||
|
|
protected override bool SupportsIEPECalSignal => IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.IEPE);
|
||
|
|
protected virtual int MIN_PROTOCOL_TMATS_INTERVAL => int.MaxValue;
|
||
|
|
public override void InitMinProto()
|
||
|
|
{
|
||
|
|
// SLICE 6.0 Protocol Limitations
|
||
|
|
SLICE6_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.MultipleAndHybridEvents] = SLICE6.MIN_PROTOCOL_VER;
|
||
|
|
SLICE6_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.MultipleEvents] = SLICE6.MIN_PROTOCOL_VER;
|
||
|
|
SLICE6_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.AutoArm] = SLICE6.MIN_PROTOCOL_VER;
|
||
|
|
SLICE6_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.AutoArmRepeatEnable] = SLICE6.MIN_PROTOCOL_VER;
|
||
|
|
SLICE6_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.SetDefaultMIF] = SLICE6.MIN_PROTOCOL_VER;
|
||
|
|
SLICE6_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.FileData] = SLICE6.MIN_PROTOCOL_VER;
|
||
|
|
SLICE6_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.StackSensors] = SLICE6.STACK_SENSORS;
|
||
|
|
SLICE6_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.BaseSystemTime] = SLICE6.MIN_PROTOCOL_VER;
|
||
|
|
SLICE6_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.TestCommunication] = SLICE6.MIN_PROTOCOL_VER;
|
||
|
|
SLICE6_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.StackLowPowerMode] = SLICE6.MIN_PROTOCOL_VER;
|
||
|
|
SLICE6_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.SetRealtimeSampleRate] = SLICE6.MIN_PROTOCOL_VER;
|
||
|
|
SLICE6_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.SLICE2_OneWireID] = SLICE6.MIN_PROTOCOL_VER;
|
||
|
|
SLICE6_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.HardwareRevision] = SLICE6.MIN_PROTOCOL_VER;
|
||
|
|
SLICE6_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.HardwareConfiguration] = SLICE6.MIN_PROTOCOL_VER;
|
||
|
|
SLICE6_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.EventFaultFlags] = SLICE6.MIN_PROTOCOL_VER;
|
||
|
|
SLICE6_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.EventArmAttempts] = SLICE6.MIN_PROTOCOL_VER;
|
||
|
|
SLICE6_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.QueryActualSampleRateImmediate] = SLICE6.MIN_PROTOCOL_VER;
|
||
|
|
SLICE6_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.InitHardwareInputLines] = SLICE6.MIN_PROTOCOL_VER;
|
||
|
|
SLICE6_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.VoltageSysAttributes] = SLICE6.MIN_PROTOCOL_VER;
|
||
|
|
SLICE6_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.LevelTrigger] = SLICE6.MIN_PROTOCOL_VER;
|
||
|
|
SLICE6_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.AttributeStoreBlocks] = SLICE6.MIN_PROTOCOL_VER;
|
||
|
|
SLICE6_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.QueryArmAndTriggerStatus_VoltageReadings] = SLICE6.MIN_PROTOCOL_VER;
|
||
|
|
SLICE6_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.MaxEvents] = SLICE6.MIN_PROTOCOL_VER;
|
||
|
|
SLICE6_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.AutoArmDiagnosticDelay] = SLICE6.MIN_PROTOCOL_VER;
|
||
|
|
SLICE6_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.StackChannelAutoArmDiagLevel] = SLICE6.MIN_PROTOCOL_VER;
|
||
|
|
SLICE6_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.FlashClear] = SLICE6.MIN_PROTOCOL_VER;
|
||
|
|
SLICE6_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.MultipleSamplesRealtime] = SLICE6.MIN_PROTOCOL_VER;
|
||
|
|
SLICE6_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.BaseCalibrationDate] = SLICE6.MIN_PROTOCOL_VER;
|
||
|
|
SLICE6_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.IgnoreShortedStartEvent] = SLICE6.MIN_PROTOCOL_VER;
|
||
|
|
SLICE6_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.ResetAttributeStore] = SLICE6.MIN_PROTOCOL_VER;
|
||
|
|
|
||
|
|
SLICE6_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.DiangosShuntDAC] = SLICE6.DIAGNOS_SHUNT_DAC;
|
||
|
|
SLICE6_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.VoltageInsertion] = SLICE6.DIAGNOS_SHUNT_DAC;
|
||
|
|
|
||
|
|
SLICE6_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.PTPTimestamp] = SLICE6.MIN_PROTOCOL_VER;
|
||
|
|
SLICE6_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.StartRecDelayInSecond] = SLICE6.START_REC_DELAY_IN_SECONDS;
|
||
|
|
SLICE6_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.QueryTiltSensorData] = SLICE6.START_REC_DELAY_IN_SECONDS;
|
||
|
|
SLICE6_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.InSliceTiltSensorADCPre] = SLICE6.IN_SLICE_TILT_SENSOR_ADC_PRE;
|
||
|
|
SLICE6_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.StartRealtimeStream] = SLICE6.START_REALTIME_STREAM;
|
||
|
|
SLICE6_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.UDPRealtimeStream] = SLICE6.UDP_REALTIME_STREAM;
|
||
|
|
SLICE6_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.GenerateEvent] = 18;
|
||
|
|
|
||
|
|
SLICE6_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.PTPSyncStatus] = SLICE6.MIN_PROTOCOL_VER;
|
||
|
|
SLICE6_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.SetClockSyncConfig] = SLICE6.MIN_PROTOCOL_VER;
|
||
|
|
SLICE6_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.PTPDomainID] = SLICE6.PTP_DOMAIN_ID_VER;
|
||
|
|
SLICE6_MinimumProtocols[DFConstantsAndEnums.ProtocolLimitedCommands.ActiveRAM] = SLICE6.MIN_PROTOCOL_VER;
|
||
|
|
MinimumProtocols = SLICE6_MinimumProtocols;
|
||
|
|
}
|
||
|
|
protected override void AsyncBeginFlashErase(object asyncInfo)
|
||
|
|
{
|
||
|
|
//14042 Flash Clear turns of excitation for s6
|
||
|
|
//we have to cache the sample average for now, but in the future we can not do this check
|
||
|
|
try
|
||
|
|
{
|
||
|
|
var measureOffset = new RetrieveSampleAverage(this);
|
||
|
|
measureOffset.DeviceID = 0; // send to base
|
||
|
|
var avgTimeSeconds = 1.0 / 60.0 * 2.0; // 2 * 60Hz cycles
|
||
|
|
measureOffset.Samples = (ushort)(10000 * avgTimeSeconds);//should use sample rate here?
|
||
|
|
if (measureOffset.Samples < 1) { measureOffset.Samples = 1; }
|
||
|
|
measureOffset.SyncExecute();
|
||
|
|
|
||
|
|
foreach (var m in ConfigData.Modules)
|
||
|
|
{
|
||
|
|
foreach (var ch in m.Channels)
|
||
|
|
{
|
||
|
|
if (ch is AnalogInputDASChannel)
|
||
|
|
{
|
||
|
|
if (!(ch is ILevelTriggerable triggerable)) { continue; }
|
||
|
|
triggerable.SampleAverageADC = null;
|
||
|
|
var aCh = ch as AnalogInputDASChannel;
|
||
|
|
if (aCh.LevelTriggerType == LevelTriggerTypes.NONE ||
|
||
|
|
aCh.ConfigurationMode != DFConstantsAndEnums.ConfigMode.Normal) continue;
|
||
|
|
try
|
||
|
|
{
|
||
|
|
var ADC = measureOffset.GetChannelData(aCh.Number);
|
||
|
|
triggerable.SampleAverageADC = ADC;
|
||
|
|
}
|
||
|
|
catch (Exception ex)
|
||
|
|
{
|
||
|
|
APILogger.Log(ex);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
catch (Exception ex)
|
||
|
|
{
|
||
|
|
APILogger.Log(ex);
|
||
|
|
}
|
||
|
|
base.AsyncBeginFlashErase(asyncInfo);
|
||
|
|
}
|
||
|
|
/// <summary>
|
||
|
|
/// complete any work we need to do before starting trigger check.
|
||
|
|
/// 18736 in S6, clear the LT cache too so we don't use the old values when checking
|
||
|
|
/// </summary>
|
||
|
|
protected override void AsyncPreStartTriggerCheck(object asyncInfo)
|
||
|
|
{
|
||
|
|
base.AsyncPreStartTriggerCheck(asyncInfo);
|
||
|
|
TriggerCheckService.ClearLevelTriggerCache(ConfigData);
|
||
|
|
}
|
||
|
|
protected override ConfigAttributes GetConfigAttributes(DTS.Common.Interface.DASFactory.ICommunication com)
|
||
|
|
{
|
||
|
|
return new SLICE6ConfigAttributes(com);
|
||
|
|
}
|
||
|
|
/// <summary>
|
||
|
|
/// SLICE6 config attributes, mostly inherits from SLICE.ConfigAttributes with some functionality removed
|
||
|
|
/// </summary>
|
||
|
|
protected class SLICE6ConfigAttributes : SLICE2ConfigAttributes
|
||
|
|
{
|
||
|
|
public override void ConfigureCoupling(bool[] IsACCoupledArray)
|
||
|
|
{
|
||
|
|
}
|
||
|
|
/// <summary>
|
||
|
|
/// I'm not aware of a purgeStaleData function in SLICE6 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 SLICE6ConfigAttributes(DTS.Common.Interface.DASFactory.ICommunication _com) : base(_com) { }
|
||
|
|
}
|
||
|
|
public override int GetDASDisplayOrder()
|
||
|
|
{
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
public override void SetDASDisplayOrder(int order)
|
||
|
|
{
|
||
|
|
}
|
||
|
|
public override int[] GetChannelDisplayOrder()
|
||
|
|
{
|
||
|
|
return new[] { -1 };
|
||
|
|
}
|
||
|
|
public override void SetChannelDisplayOrder(int[] order)
|
||
|
|
{
|
||
|
|
}
|
||
|
|
/// <summary>
|
||
|
|
/// QueryEventData also is customized for SLICE6, it needs to perform SLICE6 specific data marshalling
|
||
|
|
/// </summary>
|
||
|
|
/// <returns></returns>
|
||
|
|
protected override QueryEventDataBase GetQueryEventData()
|
||
|
|
{
|
||
|
|
return new QueryEventData_SLICE6(this, QueryEventData_SLICE6.Default_IO_Timeout);
|
||
|
|
}
|
||
|
|
protected virtual void ConfigureTMATS(SliceConfigServiceAsyncInfo info,
|
||
|
|
float[] scaleFactors, float[] ranges, float[] measuredOffset)
|
||
|
|
{
|
||
|
|
if (ShouldWriteStreamInfo() && null != ChannelDiagnosticsResults && ChannelDiagnosticsResults.Any())
|
||
|
|
{
|
||
|
|
try
|
||
|
|
{
|
||
|
|
var outMod = Array.Find(ConfigData.Modules, m => m.ModuleType() == DFConstantsAndEnums.ModuleType.StreamOut);
|
||
|
|
//FB 30035 Refactored to use strategy patten based on profile type
|
||
|
|
ITmtFile slice6AirTmtFile = null != outMod ? TmtFile.GetS6ATMATSFileTypeForProfile(outMod.StreamProfile, SerialNumber, DASInfo, ConfigData, ChannelDiagnosticsResults)
|
||
|
|
: new Slice6AirAnalogTmtFile(SerialNumber, DASInfo, ConfigData, ChannelDiagnosticsResults);
|
||
|
|
//FB 25526
|
||
|
|
var uartLookup = new Dictionary<IDASCommunication, ushort>();
|
||
|
|
slice6AirTmtFile.WriteTmtFile(this, info.DataChannelIds, info.TimeChannelIds, uartLookup, DASIndex, this);
|
||
|
|
}
|
||
|
|
catch (ArgumentOutOfRangeException ex)
|
||
|
|
{
|
||
|
|
var eventAggregator = ContainerLocator.Container.Resolve<IEventAggregator>();
|
||
|
|
var msg =
|
||
|
|
$"Failed to write TMT file for: {SerialNumber}\r\nReduce channel name lengths or TMT template size";
|
||
|
|
eventAggregator.GetEvent<PageErrorEvent>().Publish(new PageErrorArg(new[]
|
||
|
|
{
|
||
|
|
msg
|
||
|
|
}, null));
|
||
|
|
throw new ArgumentOutOfRangeException(msg, ex);
|
||
|
|
}
|
||
|
|
catch (Exception ex)
|
||
|
|
{
|
||
|
|
var eventAggregator = ContainerLocator.Container.Resolve<IEventAggregator>();
|
||
|
|
var msg =
|
||
|
|
$"Failed to write TMT file for: {SerialNumber}\r\n{ex.Message}";
|
||
|
|
eventAggregator.GetEvent<PageErrorEvent>().Publish(new PageErrorArg(new[]
|
||
|
|
{
|
||
|
|
msg
|
||
|
|
}, null));
|
||
|
|
throw new Exception(ex.Message, ex);
|
||
|
|
}
|
||
|
|
SetTMATSInterval(info.TMATSIntervalMs);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
/// <summary>
|
||
|
|
/// the order of this DAS among multiple das
|
||
|
|
/// </summary>
|
||
|
|
public int DASIndex { get; set; } = -1;
|
||
|
|
protected override void ConfigureStreaming(SliceConfigServiceAsyncInfo info,
|
||
|
|
float[] scaleFactors,
|
||
|
|
float[] ranges,
|
||
|
|
float[] measuredOffset)
|
||
|
|
{
|
||
|
|
//14531 Implement TMATS support for S6A stream on boot
|
||
|
|
ConfigureTMATS(info, scaleFactors, ranges, measuredOffset);
|
||
|
|
if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.SetDSPFilterSettings))
|
||
|
|
{
|
||
|
|
// Implement DSP IIR/FIR Profile API http://manuscript.dts.local/f/cases/17913/Implement-DSP-IIR-FIR-Profile-API
|
||
|
|
try
|
||
|
|
{
|
||
|
|
uint[] filterConfig = new uint[6];
|
||
|
|
|
||
|
|
|
||
|
|
uint dspFilterType = (uint)(info.DSPFilterType?.EnumValue ?? 0);
|
||
|
|
|
||
|
|
// ALL of these values are fixed. They are intended for FW tuning only.
|
||
|
|
uint overSampleRate = 80000;
|
||
|
|
uint oversampleSCFClock = 8000;
|
||
|
|
uint recordWhileStreamConfig = 0; // Reserved as of 1/22/2021
|
||
|
|
uint filterConfigStage2 = 0;
|
||
|
|
uint overSampleRateStage2 = 20000;
|
||
|
|
|
||
|
|
filterConfig[0] = dspFilterType;
|
||
|
|
filterConfig[1] = overSampleRate;
|
||
|
|
filterConfig[2] = oversampleSCFClock;
|
||
|
|
filterConfig[3] = recordWhileStreamConfig;
|
||
|
|
filterConfig[4] = filterConfigStage2;
|
||
|
|
filterConfig[5] = overSampleRateStage2;
|
||
|
|
|
||
|
|
|
||
|
|
var filterSet = new SetSystemAttributeSLICE6AIR(this);
|
||
|
|
filterSet.SetValue(AttributeTypes.SystemAttributesSLICE6AIR.DspFilterAndStreamWhileRecordConfig,
|
||
|
|
filterConfig, true);
|
||
|
|
filterSet.SyncExecute();
|
||
|
|
var s6ADSPFilterFilename = string.Empty;
|
||
|
|
if (!string.IsNullOrEmpty(s6ADSPFilterFilename) && File.Exists(s6ADSPFilterFilename))
|
||
|
|
{
|
||
|
|
byte[] ByteArrayData = System.IO.File.ReadAllBytes(s6ADSPFilterFilename); // new char[maxLen];
|
||
|
|
var maxLen = ByteArrayData.Length;
|
||
|
|
SetFileData sfd = new SetFileData(this, 600000)
|
||
|
|
{
|
||
|
|
StartByteCount = 0,
|
||
|
|
FileID = Constants.FILE_STORE_DSP_FILTER_ID,
|
||
|
|
Data = Constants.XML_STORE_MAGIC_BYTES
|
||
|
|
};
|
||
|
|
sfd.SyncExecute();
|
||
|
|
|
||
|
|
//Store Header - data length
|
||
|
|
sfd.StartByteCount = (Constants.XML_HEADER_LENGTH / 2);
|
||
|
|
sfd.FileID = Constants.FILE_STORE_DSP_FILTER_ID;
|
||
|
|
sfd.Data = BitConverter.GetBytes((uint)maxLen);
|
||
|
|
sfd.SyncExecute();
|
||
|
|
|
||
|
|
//Store Data
|
||
|
|
for (uint i = 0; i < maxLen; i += (uint)sfd.MaximumFileStreamBytes)
|
||
|
|
{
|
||
|
|
long array_size = sfd.MaximumFileStreamBytes;
|
||
|
|
if ((i + sfd.MaximumFileStreamBytes) > maxLen)
|
||
|
|
{
|
||
|
|
array_size = maxLen - i;
|
||
|
|
}
|
||
|
|
byte[] dataToSend = new byte[array_size];
|
||
|
|
Array.Copy(ByteArrayData, i, dataToSend, 0, array_size);
|
||
|
|
|
||
|
|
sfd.Data = dataToSend;
|
||
|
|
sfd.FileID = Constants.FILE_STORE_DSP_FILTER_ID;
|
||
|
|
sfd.StartByteCount = i + Constants.XML_HEADER_LENGTH;
|
||
|
|
sfd.SyncExecute();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
catch
|
||
|
|
{
|
||
|
|
// doing nothing. exit.
|
||
|
|
APILogger.Log("Realtime stream DSP Filtering feature is not supported in this device.");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
/// <summary>
|
||
|
|
/// we can probably simplify and take common items (slice6+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;
|
||
|
|
IncrementNumberOfTimesWritten();
|
||
|
|
//12638 DAS does not record data in recorder mode during calibration ~ 40% of time.
|
||
|
|
//for SLICE6 we call reseteventlist here, prior to configuring and NOT before arming
|
||
|
|
ResetEventListPriorToConfigure();
|
||
|
|
|
||
|
|
int progressValue = 0;
|
||
|
|
bool bReleased = true;
|
||
|
|
|
||
|
|
if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.ProgramStackChannels)) { ReconfigureAccordingToConfig(); }
|
||
|
|
PresetSampleRate();
|
||
|
|
|
||
|
|
SetVoltageRequirements();
|
||
|
|
|
||
|
|
SetPolarity();
|
||
|
|
|
||
|
|
SetArmDisableShortCheck();
|
||
|
|
|
||
|
|
try
|
||
|
|
{
|
||
|
|
Lock();
|
||
|
|
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 ushort[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 bridgeACCouplingArray = new bool[numChannels];
|
||
|
|
var diagnosticChannels = new List<byte>();
|
||
|
|
|
||
|
|
var bModified = false;
|
||
|
|
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);
|
||
|
|
}
|
||
|
|
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();
|
||
|
|
}
|
||
|
|
// SLICE6 doesn't have a battery, don't make the measurement ...
|
||
|
|
protected override double MeasureBackupMilliVolts()
|
||
|
|
{
|
||
|
|
return double.NaN;
|
||
|
|
}
|
||
|
|
protected override bool SupportsDiagnosticsMode => false;
|
||
|
|
|
||
|
|
protected override void PerformVoltageInsertionCheck(IDiagnosticActions[] ChannelActions, SliceServiceAsyncInfo info, ref IDiagnosticResult[] results)
|
||
|
|
{
|
||
|
|
if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.VoltageInsertion))
|
||
|
|
{
|
||
|
|
// first count how many we need to do it on
|
||
|
|
var NumToMeasure = ChannelActions.Count(a => ShouldPerformVoltageInsertionCheck(a));
|
||
|
|
if (NumToMeasure == 0)
|
||
|
|
return;
|
||
|
|
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
|
||
|
|
{
|
||
|
|
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++;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
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);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
ClearVoltageInsertionResults(ChannelActions, results);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// hardcoded constants right now ... maybe these belong in attributes in the firmware!
|
||
|
|
/// </summary>
|
||
|
|
protected override uint MaxAAFilterRateHz => SLICE6.MaxAAFilterRateHz;
|
||
|
|
protected override uint MaxSampleRateHz => 400000;
|
||
|
|
|
||
|
|
/// <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 500k)
|
||
|
|
/// </summary>
|
||
|
|
/// <returns></returns>
|
||
|
|
public override uint MaxSampleRate(int numberOfConfiguredChannels)
|
||
|
|
{
|
||
|
|
return MaxSampleRateHz;
|
||
|
|
}
|
||
|
|
public override uint MaxAAFilterRate()
|
||
|
|
{
|
||
|
|
return MaxAAFilterRateHz;
|
||
|
|
}
|
||
|
|
protected override DASModule MakeConfigModuleFromInfoModule(InfoResult.Module 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);
|
||
|
|
if (infoModule.TypeOfModule == DFConstantsAndEnums.ModuleType.SLICEIEPE)
|
||
|
|
{
|
||
|
|
channel.IEPEChannel = true;
|
||
|
|
channel.SupportedBridges = new SensorConstants.BridgeType[] { SensorConstants.BridgeType.IEPE };
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
channel.IEPEChannel = false;
|
||
|
|
channel.SupportedBridges = new SensorConstants.BridgeType[] {SensorConstants.BridgeType.FullBridge,
|
||
|
|
SensorConstants.BridgeType.HalfBridge};
|
||
|
|
}
|
||
|
|
if (infoModule.IsProgrammable)
|
||
|
|
{
|
||
|
|
channel.SupportedBridges = new SensorConstants.BridgeType[] { SensorConstants.BridgeType.FullBridge,
|
||
|
|
SensorConstants.BridgeType.HalfBridge, SensorConstants.BridgeType.IEPE};
|
||
|
|
}
|
||
|
|
configModule.Channels[i] = channel;
|
||
|
|
}
|
||
|
|
return configModule;
|
||
|
|
}
|
||
|
|
|
||
|
|
protected override void AsyncArmNow(object asyncInfo)
|
||
|
|
{
|
||
|
|
// We dont want to do this here. We want to do this on connect.
|
||
|
|
TriggerCheckService.ClearLevelTriggerCache(ConfigData);
|
||
|
|
base.AsyncArmNow(asyncInfo);
|
||
|
|
}
|
||
|
|
|
||
|
|
#region IClockSyncActions
|
||
|
|
void IClockSyncActions.SetClockSyncConfig(ServiceCallback callback, object userData, ClockSyncProfile profile)
|
||
|
|
{
|
||
|
|
var info = new SliceServiceAsyncInfo(callback, userData) { functionData = profile };
|
||
|
|
|
||
|
|
var packet = new SetClockSyncConfigPacket(info);
|
||
|
|
LaunchAsyncWorker("Slice.SetClockSyncConfig", AsyncSetClockSyncConfig, packet);
|
||
|
|
}
|
||
|
|
|
||
|
|
void IClockSyncActions.GetClockSyncStatus(ServiceCallback callback, object userData)
|
||
|
|
{
|
||
|
|
var info = new SliceServiceAsyncInfo(callback, userData);
|
||
|
|
|
||
|
|
var packet = new GetClockSyncStatusPacket(info);
|
||
|
|
LaunchAsyncWorker("Slice.GetClockSyncStatus", AsyncGetClockSyncStatus, packet);
|
||
|
|
}
|
||
|
|
void IClockSyncActions.GetPTPDomainID(ServiceCallback callback, object userData)
|
||
|
|
{
|
||
|
|
var info = new SliceServiceAsyncInfo(callback, userData);
|
||
|
|
|
||
|
|
var packet = new GetPTPDomainIDPacket(info);
|
||
|
|
LaunchAsyncWorker("Slice.GetPTPDomainID", AsyncGetPTPDomainID, packet);
|
||
|
|
}
|
||
|
|
|
||
|
|
void IClockSyncActions.SetPTPDomainID(ServiceCallback callback, object userData, byte domainID)
|
||
|
|
{
|
||
|
|
var info = new SliceServiceAsyncInfo(callback, userData) { functionData = domainID };
|
||
|
|
|
||
|
|
var packet = new SetPTPDomainIDPacket(info);
|
||
|
|
LaunchAsyncWorker("Slice.SetPTPDomainID", AsyncSetPTPDomainID, packet);
|
||
|
|
}
|
||
|
|
#endregion
|
||
|
|
|
||
|
|
protected virtual double[] GetTiltCalFactors()
|
||
|
|
{
|
||
|
|
try
|
||
|
|
{
|
||
|
|
if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.QueryTiltSensorData))
|
||
|
|
{
|
||
|
|
var tiltSensorCals = new double[18];
|
||
|
|
for (int tiltCalKeyOffset = 0; tiltCalKeyOffset < 18; tiltCalKeyOffset++)
|
||
|
|
{
|
||
|
|
var qSA_BS6 = new QuerySystemAttribute_BridgeSlice6(this);
|
||
|
|
qSA_BS6.DeviceID = 1;
|
||
|
|
qSA_BS6.Key = (AttributeTypes.SystemAttributes_BridgeSlice6)((int)AttributeTypes.SystemAttributes_BridgeSlice6.TILTSENSOR_CAL_1 + tiltCalKeyOffset);
|
||
|
|
qSA_BS6.SyncExecute();
|
||
|
|
tiltSensorCals[tiltCalKeyOffset] = (float)qSA_BS6.Value;
|
||
|
|
}
|
||
|
|
return tiltSensorCals;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
catch (Exception ex)
|
||
|
|
{
|
||
|
|
APILogger.Log(ex);
|
||
|
|
}
|
||
|
|
return new double[0];
|
||
|
|
}
|
||
|
|
|
||
|
|
protected override void GetTiltResults(ArmCheckResults dasResults)
|
||
|
|
{
|
||
|
|
if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.QueryTiltSensorData))
|
||
|
|
{
|
||
|
|
if (!ConfigData.Modules.Any()) { return; }
|
||
|
|
var tiltSensorCals = GetTiltCalFactors();
|
||
|
|
|
||
|
|
TiltSensorCals = tiltSensorCals;
|
||
|
|
var qtsd = new QueryTiltSensorData(this);
|
||
|
|
qtsd.DeviceID = 1;
|
||
|
|
qtsd.SyncExecute();
|
||
|
|
|
||
|
|
dasResults.TiltSensorDataPre = new short[]
|
||
|
|
{qtsd.Channel1ValueAdc, qtsd.Channel2ValueAdc, qtsd.Channel3ValueAdc};
|
||
|
|
|
||
|
|
dasResults.TiltDegrees = DTS.Common.DAS.Concepts.Test.Module.GetTiltDegreesEU(
|
||
|
|
dasResults.TiltSensorDataPre,
|
||
|
|
tiltSensorCals,
|
||
|
|
ConfigData.Modules[0].TiltAxes,
|
||
|
|
ConfigData.Modules[0].AxisIgnored,
|
||
|
|
new float[]
|
||
|
|
{
|
||
|
|
(float)ConfigData.Modules[0].MountOffsetAxisOne,
|
||
|
|
(float)ConfigData.Modules[0].MountOffsetAxisTwo
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
/// <summary>
|
||
|
|
/// sets Attribute 145 (TMATS interval) if information is present, logs otherwise
|
||
|
|
/// http://manuscript.dts.local/f/cases/29987/Add-CG-DP-TMATS-interval-UI-support
|
||
|
|
/// </summary>
|
||
|
|
/// <param name="tmatsIntervalLookup">lookup </param>
|
||
|
|
protected void SetTMATSInterval(IReadOnlyDictionary<IDASCommunication, ushort> tmatsIntervalLookup)
|
||
|
|
{
|
||
|
|
try
|
||
|
|
{
|
||
|
|
if (null == tmatsIntervalLookup || !tmatsIntervalLookup.ContainsKey(this))
|
||
|
|
{
|
||
|
|
APILogger.Log($"TMATS interval information not present for {SerialNumber} attribute will not be set");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (ProtocolVersion < MIN_PROTOCOL_TMATS_INTERVAL)
|
||
|
|
{
|
||
|
|
APILogger.Log($"TMATS interval not supported by {SerialNumber} attribute will not be set");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
var interval = tmatsIntervalLookup[this];
|
||
|
|
var setSystemAttribute = new SetSystemAttributeSLICE6AIR(this);
|
||
|
|
setSystemAttribute.SetValue(AttributeTypes.SystemAttributesSLICE6AIR.S6A_IrigCGDPSendIntervalMsec, interval, true);
|
||
|
|
setSystemAttribute.SyncExecute();
|
||
|
|
}
|
||
|
|
catch (Exception ex)
|
||
|
|
{
|
||
|
|
APILogger.Log("SetTMATSInterval failed", ex);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#region QueryEventData_SLICE6
|
||
|
|
|
||
|
|
|
||
|
|
/// <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_SLICE6 : 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_SLICE6(DTS.Common.Interface.DASFactory.ICommunication sock)
|
||
|
|
: base(sock) { LogCommands = false; }
|
||
|
|
|
||
|
|
public QueryEventData_SLICE6(DTS.Common.Interface.DASFactory.ICommunication sock, int TimeoutMillisec)
|
||
|
|
: base(sock, TimeoutMillisec) { LogCommands = false; }
|
||
|
|
|
||
|
|
private ulong GetRequestedStartSpot()
|
||
|
|
{
|
||
|
|
if (recorder is SLICE6<EthernetConnection> slice6_Ethernet)
|
||
|
|
{
|
||
|
|
return ((WhatToDownloadSlice2)slice6_Ethernet.WhatToDownload).RequestedStartSport;
|
||
|
|
}
|
||
|
|
else if (recorder is SLICE6AIR<EthernetConnection> slice6air_Ethernet)
|
||
|
|
{
|
||
|
|
return ((WhatToDownloadSlice2)slice6air_Ethernet.WhatToDownload).RequestedStartSport;
|
||
|
|
}
|
||
|
|
else if (recorder is SLICE6AIRBR<EthernetConnection> slice6air_br_Ethernet)
|
||
|
|
{
|
||
|
|
return ((WhatToDownloadSlice2)slice6air_br_Ethernet.WhatToDownload).RequestedStartSport;
|
||
|
|
}
|
||
|
|
else { throw new NotSupportedException("GetRequestedStartSport not supported for " + recorder.ConnectString); }
|
||
|
|
}
|
||
|
|
private void PushLeftOverData(ushort[] daters)
|
||
|
|
{
|
||
|
|
if (recorder is SLICE6<EthernetConnection> slice6_Ethernet)
|
||
|
|
{
|
||
|
|
slice6_Ethernet.PushLeftOverData(daters);
|
||
|
|
}
|
||
|
|
else if (recorder is SLICE6AIR<EthernetConnection> slice6air_Ethernet)
|
||
|
|
{
|
||
|
|
slice6air_Ethernet.PushLeftOverData(daters);
|
||
|
|
}
|
||
|
|
else if (recorder is SLICE6AIRBR<EthernetConnection> slice6air_br_Ethernet)
|
||
|
|
{
|
||
|
|
slice6air_br_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==" + CommandPacket.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 = GetRequestedStartSpot();
|
||
|
|
|
||
|
|
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));
|
||
|
|
var offset = samplesProcessed * ChannelsDownloaded;
|
||
|
|
var leftover = new ushort[_data.Length - offset];
|
||
|
|
for (var i = 0; i < leftover.Length; i++)
|
||
|
|
{
|
||
|
|
leftover[i] = _data[i + offset];
|
||
|
|
}
|
||
|
|
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);
|
||
|
|
int samplesProcessed = Convert.ToInt32(Math.Truncate(((double)_data.Length - offset) / ChannelsDownloaded));
|
||
|
|
var product = samplesProcessed * ChannelsDownloaded;
|
||
|
|
var leftover = new ushort[_data.Length - offset - product];
|
||
|
|
offset += product;
|
||
|
|
for (var i = 0; i < leftover.Length; i++)
|
||
|
|
{
|
||
|
|
leftover[i] = _data[i + offset];
|
||
|
|
}
|
||
|
|
PushLeftOverData(leftover);
|
||
|
|
}
|
||
|
|
|
||
|
|
return UserCallback(cbReport);
|
||
|
|
}
|
||
|
|
|
||
|
|
private ushort[] PopLeftOverData()
|
||
|
|
{
|
||
|
|
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 SLICE6AIRBR<EthernetConnection> slice6air_br_Ethernet)
|
||
|
|
{
|
||
|
|
return slice6air_br_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) < GetRequestedStartSpot())//(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 (GetRequestedStartSpot() > FirstSample)
|
||
|
|
{
|
||
|
|
offset = Convert.ToInt32(GetRequestedStartSpot() - 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));
|
||
|
|
//performance improvements, use simpler structures and do less calculations over the iterations
|
||
|
|
var rv = new short[completeSamples];
|
||
|
|
offset += channel;
|
||
|
|
for (var i = 0; i < completeSamples; i++)
|
||
|
|
{
|
||
|
|
val = _data[i * ChannelsDownloaded + offset];
|
||
|
|
rv[i] = (short)((((val & 0x00FF) << 8) | ((val >> 8) & 0x00FF)) + 0x8000);
|
||
|
|
}
|
||
|
|
signedADC = rv.ToArray();
|
||
|
|
}
|
||
|
|
// 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];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
#endregion
|
||
|
|
}
|