924 lines
44 KiB
C#
924 lines
44 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Net;
|
|
using System.Net.Sockets;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using DTS.Common.ICommunication;
|
|
using DTS.DASLib.Command.SLICE;
|
|
using DTS.Common.Utilities.Logging;
|
|
using DTS.DASLib.Command;
|
|
using DTS.DASLib.Command.SLICE.RealtimeCommands;
|
|
using DTS.Common.Interface.Connection;
|
|
using DTS.Common.Enums.DASFactory;
|
|
using DTS.Common.Interface.DASFactory;
|
|
using System.Threading.Tasks;
|
|
using DTS.Common.Enums;
|
|
using DTS.Common.Classes.DASFactory;
|
|
|
|
namespace DTS.DASLib.Service
|
|
{
|
|
public partial class Slice<T> : Communication<T>,
|
|
IDASCommunication,
|
|
IConfigurationActions,
|
|
IDiagnosticsActions,
|
|
ITriggerCheckActions,
|
|
IRealTimeActions,
|
|
IArmActions,
|
|
IDownloadActions where T : IConnection, new()
|
|
{
|
|
#region Real time
|
|
public virtual bool ControlsDAQ() { return true; }
|
|
|
|
public virtual bool SupportsRealtime()
|
|
{
|
|
return !IsTOM();
|
|
}
|
|
|
|
public virtual bool SupportsMultipleEvents() { return IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.MultipleAndHybridEvents); }
|
|
public virtual bool SupportsMultipleConfigurations() { return IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.MultipleConfigurations); }
|
|
public virtual bool SupportsAutoArm() { return IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.AutoArm); }
|
|
public virtual bool SupportsLevelTrigger() { return IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.LevelTrigger); }
|
|
public bool SupportsHardwareInputCheck() { return IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.InitHardwareInputLines); }
|
|
protected bool _bSupportsMultipleSampleRealtime = true;
|
|
public bool SupportsMultipleSampleRealtime() { return _bSupportsMultipleSampleRealtime; }
|
|
|
|
|
|
protected class RealTimeAsyncPacket
|
|
{
|
|
public SliceServiceAsyncInfo Info { get; set; }
|
|
public Timer Timer { get; set; }
|
|
public int SamplesPerSecond { get; set; }
|
|
public int MillisecBetweenSamples { get; set; }
|
|
public bool AllowMultipleSampleRealtime { get; set; }
|
|
public ManualResetEvent StopEvent { get; set; }
|
|
public byte[] Channels { get; set; }
|
|
public double AAF { get; set; }
|
|
public int minCallbackUpdateTimeMs { get; set; }
|
|
public bool UseUDPStreaming { get; set; } = false;
|
|
public int StreamPortOffset { get; set; }
|
|
public string HostIPAddress { get; set; } = IPAddress.Any.ToString();
|
|
}
|
|
|
|
protected class RealTimeTiltAsyncPacket
|
|
{
|
|
public SliceServiceAsyncInfo Info { get; set; }
|
|
public double[] TiltInDegrees { get; set; }
|
|
public ManualResetEvent StopEvent { get; set; }
|
|
}
|
|
|
|
// FB15313: Add UDP streaming options
|
|
protected class RealTimeUDPStreamProfileAsyncPacket
|
|
{
|
|
public SliceServiceAsyncInfo Info { get; set; }
|
|
public UDPStreamProfile Profile { get; set; }
|
|
public string UDPAddress { get; set; }
|
|
public ushort TimeDataChannelId { get; set; }
|
|
public uint[] TMNSConfig { get; set; }
|
|
public ushort DataChannelId { get; set; }
|
|
public ushort IRIGTimeDataPacketIntervalMs { get; set; }
|
|
}
|
|
|
|
void IRealTimeActions.RealTime(int samplesPerSecond,
|
|
int millisecBetweenSamples,
|
|
ServiceCallback callback,
|
|
object userData,
|
|
bool allowMultipleSampleRealtime,
|
|
int moduleIndex,
|
|
ManualResetEvent stopEvent,
|
|
byte[] channels,
|
|
double aaf,
|
|
int minCallbackUpdateTimeMs,
|
|
bool UseUDPStreaming,
|
|
string hostIPAddress)
|
|
{
|
|
var packet = new RealTimeAsyncPacket();
|
|
packet.StopEvent = stopEvent;
|
|
packet.Info = new SliceServiceAsyncInfo(callback, userData);
|
|
packet.MillisecBetweenSamples = millisecBetweenSamples;
|
|
packet.SamplesPerSecond = samplesPerSecond;
|
|
packet.AllowMultipleSampleRealtime = allowMultipleSampleRealtime;
|
|
packet.Channels = channels;
|
|
packet.AAF = aaf;
|
|
packet.minCallbackUpdateTimeMs = minCallbackUpdateTimeMs;
|
|
packet.UseUDPStreaming = UseUDPStreaming;
|
|
packet.StreamPortOffset = moduleIndex; // FB15388: udp ports must be unique, start index-0 unit at the default and iterate offset from there
|
|
packet.HostIPAddress = hostIPAddress;
|
|
LaunchAsyncWorker("Slice.RealTime", AsyncRealTime, packet);
|
|
}
|
|
|
|
void IRealTimeActions.RealTimePolling(ServiceCallback callback,
|
|
object userData,
|
|
ManualResetEvent stopEvent,
|
|
byte[] channels)
|
|
{
|
|
var packet = new RealTimeAsyncPacket
|
|
{
|
|
StopEvent = stopEvent,
|
|
Info = new SliceServiceAsyncInfo(callback, userData),
|
|
Channels = channels
|
|
};
|
|
LaunchAsyncWorker("Slice.RealtimePolling", AsyncRealTimePolling, packet);
|
|
}
|
|
/// <summary>
|
|
/// returns an object implementing IGetRealtimeSamples capable of returning realtime samples
|
|
/// </summary>
|
|
/// <param name="iCommunication"></param>
|
|
/// <param name="bPolling">whether realtime is polling or not</param>
|
|
/// <returns></returns>
|
|
protected virtual IGetRealtimeSamples GetRealtimeSamplesClass(DTS.Common.Interface.DASFactory.ICommunication iCommunication, bool bPolling = false)
|
|
{
|
|
return new GetRealtimeSamples(iCommunication, 2000, bPolling);
|
|
}
|
|
|
|
/// <summary>
|
|
/// indicates whether the DAS supports streaming
|
|
/// 10572 implement SW side for single command streaming realtime
|
|
/// </summary>
|
|
public virtual bool SupportsIndividualChannelRealtimeStreaming => false;
|
|
|
|
/// <summary>
|
|
/// indicates whether the das supports udp streaming
|
|
/// 15160 Port UDP Realtime Stream from FWTU
|
|
/// </summary>
|
|
public virtual bool SupportsUDPRealtimeStreaming => false;
|
|
/// <summary>
|
|
/// depending on firmware, some devices will send back all channels even if you request less while others will send back what's requested.
|
|
/// DataPRO is always expecting all channels to be returned. fix for these models is to always request all
|
|
/// 31829 Datapro s6air br shows noise in realtime view chart that is not real noise and does not show up in fwtu realtime or recorded
|
|
/// </summary>
|
|
/// <param name="packet"></param>
|
|
/// <returns></returns>
|
|
protected virtual byte[] GetRTChannelIndices(RealTimeAsyncPacket packet)
|
|
{
|
|
return packet.Channels;
|
|
}
|
|
/// <summary>
|
|
/// this is the minimum of time between data callbacks for realtime
|
|
/// this should be moved to the settings file, but it is fine here
|
|
/// for now since there is the ms between samples setting and
|
|
/// only the g8 and older firmware need to request samples at a fast rate
|
|
/// </summary>
|
|
//protected const int MIN_CALLBACK_UPDATE_TIME = 20;
|
|
private void AsyncRealTime(object asyncInfo)
|
|
{
|
|
var packet = asyncInfo as RealTimeAsyncPacket;
|
|
try
|
|
{
|
|
var ssaPolarity = new SetSystemAttribute(this, AbstractCommandBase.Default_IO_Timeout);
|
|
ssaPolarity.SetValue(AttributeTypes.SystemAttributes.TriggerPolarity, (byte)(InvertTrigger ? 1 : 0), true);
|
|
ssaPolarity.SyncExecute();
|
|
ssaPolarity = new SetSystemAttribute(this, AbstractCommandBase.Default_IO_Timeout);
|
|
ssaPolarity.SetValue(AttributeTypes.SystemAttributes.StartRecordPolarity, (byte)(InvertStart ? 1 : 0), true);
|
|
ssaPolarity.SyncExecute();
|
|
|
|
// Set sample rate and adjustable anti-alias filter frequency in the
|
|
// hardware based on the milliseconds between samples parameter that
|
|
// was passed in via the RealTimeAsyncPacket object. Keep the filter
|
|
// -3dB point at 1/5 the frequency of the sample rate.
|
|
// number of samples per second
|
|
var rtSampleRate = Convert.ToUInt32(packet.SamplesPerSecond);
|
|
var uiMillisecondsBetweenSamples = packet.MillisecBetweenSamples;
|
|
var setSampleRate = new SetRealtimeSampleRate(this);
|
|
setSampleRate.SetValue(rtSampleRate);
|
|
setSampleRate.SyncExecute();
|
|
|
|
var lastUpdate = DateTime.MinValue;
|
|
StreamReaderUDP streamReader = null;
|
|
|
|
if (SupportsIndividualChannelRealtimeStreaming)
|
|
{
|
|
try
|
|
{
|
|
var saa = new SetArmAttribute(this);
|
|
saa.SetValue(AttributeTypes.ArmAndEventAttributes.RealtimeAAFilterFrequencyHz,
|
|
Convert.ToSingle(packet.AAF), true);
|
|
saa.SyncExecute();
|
|
|
|
if (packet.UseUDPStreaming && SupportsUDPRealtimeStreaming)
|
|
{
|
|
//FB 18152 find the host ip
|
|
string dasIp;
|
|
string hostIpAddress = "";
|
|
if (Common.Utils.NetworkUtils.TryParseConnectionString(ConnectString, out dasIp))
|
|
{
|
|
hostIpAddress = Common.Utils.PingUtils.DasToHost[dasIp].HostIpAddress;
|
|
}
|
|
else
|
|
{ // We should not get here since SupportsUDPRealtimeStreaming is false for USB devices but just in case have a guard
|
|
APILogger.Log("Streaming realtime with UDP is not supported for USB devices.");
|
|
return;
|
|
}
|
|
|
|
var udpAddress = new UriBuilder(DFConstantsAndEnums.RealtimeUDPAddress);
|
|
udpAddress.Port += packet.StreamPortOffset;
|
|
streamReader = new StreamReaderUDP(udpAddress.Uri.AbsoluteUri, hostIpAddress, UDPStreamProfile.DTS_UDP, packet.Channels);
|
|
|
|
var startTimeStampRealtime = new StartTimeStampStreamMode(this);
|
|
startTimeStampRealtime.ParamsToSend = streamReader.cmdline.ToArray();
|
|
startTimeStampRealtime.SyncExecute();
|
|
}
|
|
else
|
|
{
|
|
var startRT = new StartRealtimeStreamingMode(this, GetRTChannelIndices(packet));
|
|
startRT.SyncExecute();
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
APILogger.Log(ex);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var startRT = new StartRealtimeMode(this);
|
|
startRT.SupportsMultipleSampleRealtime =
|
|
packet.AllowMultipleSampleRealtime && SupportsMultipleSampleRealtime();
|
|
startRT.SyncExecute();
|
|
}
|
|
var sampleNumbers = new List<ulong>(100);
|
|
var timeStamps = new List<ulong>(100);
|
|
var sequenceNumbers = new List<ulong>(100);
|
|
var data = new List<short[][]>(100);
|
|
// start calling for RT data
|
|
|
|
var getRTdata = GetRealtimeSamplesClass(this);
|
|
getRTdata.LogCommands = false;
|
|
|
|
var numChannels = 0;
|
|
|
|
foreach (var mod in DASInfo.Modules)
|
|
{
|
|
switch (mod.TypeOfModule)
|
|
{
|
|
case DFConstantsAndEnums.ModuleType.EmbeddedMicrophone:
|
|
case DFConstantsAndEnums.ModuleType.EmbeddedMagnetometer:
|
|
case DFConstantsAndEnums.ModuleType.EmbeddedMagnetInput:
|
|
case DFConstantsAndEnums.ModuleType.UART:
|
|
case DFConstantsAndEnums.ModuleType.StreamOut:
|
|
case DFConstantsAndEnums.ModuleType.StreamIn:
|
|
continue;
|
|
default:
|
|
numChannels += (int)mod.NumberOfChannels;
|
|
break;
|
|
}
|
|
}
|
|
getRTdata.Channels = (ushort)numChannels;
|
|
if (SerialNumber.StartsWith("SPD") || SerialNumber.StartsWith("SLD"))
|
|
{
|
|
if (getRTdata is RealtimeStreamingNextSamples cmd)
|
|
{
|
|
cmd.DigitalInput = true;
|
|
var transitions = new List<bool>();
|
|
foreach (var mod in ConfigData.Modules)
|
|
{
|
|
foreach (var channel in mod.Channels)
|
|
{
|
|
var aic = (AnalogInputDASChannel)channel;
|
|
switch (aic.DigitalMode)
|
|
{
|
|
case DigitalInputModes.CCNC:
|
|
case DigitalInputModes.CCNO:
|
|
transitions.Add(false);
|
|
break;
|
|
case DigitalInputModes.THL:
|
|
case DigitalInputModes.TLH:
|
|
transitions.Add(true);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
cmd.TransitionMode = transitions.ToArray();
|
|
}
|
|
}
|
|
|
|
while (!packet.StopEvent.WaitOne(0, false))
|
|
{
|
|
if (null != streamReader)
|
|
{
|
|
try
|
|
{
|
|
var udpPacket = streamReader.Read();
|
|
if (null != udpPacket)
|
|
{
|
|
data.Add(udpPacket.ChannelData);
|
|
sampleNumbers.Add(udpPacket.SampleNumber);
|
|
timeStamps.Add( /*Convert.ToUInt64(udpPacket.TimeStamp)*/0);
|
|
sequenceNumbers.Add(udpPacket.SequenceNumber);
|
|
|
|
var newData = new NewDataData(data.ToArray(), sampleNumbers.ToArray(),
|
|
timeStamps.ToArray(),
|
|
sequenceNumbers.ToArray());
|
|
|
|
Task.Run(() => { packet.Info.NewData(newData); });
|
|
|
|
sampleNumbers.Clear();
|
|
timeStamps.Clear();
|
|
sequenceNumbers.Clear();
|
|
data.Clear();
|
|
}
|
|
}
|
|
catch (SocketException sox) //FB15531: Don't silently consume realtime errors
|
|
{
|
|
//if it's a timeout it could just be that there's no data yet,
|
|
//give the device a chance to send data
|
|
if (sox.SocketErrorCode != SocketError.TimedOut)
|
|
{
|
|
throw sox;
|
|
}
|
|
}
|
|
catch (CommandException ce)
|
|
{
|
|
if (ce.Error == CommandErrorReason.ReceiveFailed ||
|
|
ce.Error == CommandErrorReason.SendFailed)
|
|
{
|
|
packet.Info.Error(ce.Message, new Common.Classes.Connection.NotConnectedException(ce.Message));
|
|
return;
|
|
}
|
|
//15909 Crash in Realtime when connection is lost
|
|
//in the case of a communication error in start realtime, notify consumer
|
|
packet.Info.Error(ce.Message, ce);
|
|
return;
|
|
}
|
|
catch (Common.Classes.Connection.NotConnectedException nce)
|
|
{
|
|
//15909 Crash in Realtime when connection is lost
|
|
//in the case of a communication error in start realtime, notify consumer
|
|
packet.Info.Error(nce.Message, nce);
|
|
return;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
System.Diagnostics.Trace.WriteLine(ex.Message);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// call HW to get RT samples
|
|
try
|
|
{
|
|
getRTdata.SyncExecute();
|
|
|
|
}
|
|
catch (CommandException ce)
|
|
{
|
|
if (ce.Error == CommandErrorReason.ReceiveFailed ||
|
|
ce.Error == CommandErrorReason.SendFailed)
|
|
{
|
|
packet.Info.Error(ce.Message, new Common.Classes.Connection.NotConnectedException(ce.Message));
|
|
return;
|
|
}
|
|
//15909 Crash in Realtime when connection is lost
|
|
//in the case of a communication error in start realtime, notify consumer
|
|
packet.Info.Error(ce.Message, ce);
|
|
return;
|
|
}
|
|
catch (Common.Classes.Connection.NotConnectedException nce)
|
|
{
|
|
//15909 Crash in Realtime when connection is lost
|
|
//in the case of a communication error in start realtime, notify consumer
|
|
packet.Info.Error(nce.Message, nce);
|
|
return;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
APILogger.Log(ex);
|
|
}
|
|
|
|
if (getRTdata.SamplesReturned > 0)
|
|
{
|
|
var rtData = new short[getRTdata.Channels][];
|
|
for (int idx = 0; idx < getRTdata.Channels; idx++)
|
|
{
|
|
rtData[idx] = getRTdata.GetChannelData(idx);
|
|
}
|
|
|
|
sampleNumbers.Add(getRTdata.SampleNumber);
|
|
timeStamps.Add(getRTdata.TimeStamp);
|
|
sequenceNumbers.Add(getRTdata.SequenceNumber);
|
|
data.Add(rtData);
|
|
}
|
|
|
|
if (DateTime.Now.Subtract(lastUpdate).TotalMilliseconds >= packet.minCallbackUpdateTimeMs)
|
|
{
|
|
lastUpdate = DateTime.Now;
|
|
|
|
if (!IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.StartRealtimeStream))
|
|
{
|
|
var ihl =
|
|
new InitializeHardwareLines(this, AbstractCommandBase.Default_IO_Timeout);
|
|
ihl.LogCommands = false;
|
|
try
|
|
{
|
|
if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands
|
|
.InitHardwareInputLines) &&
|
|
!packet.StopEvent.WaitOne(0, false))
|
|
{
|
|
ihl.SyncExecute();
|
|
}
|
|
}
|
|
catch (Exception)
|
|
{
|
|
} //TODO: Where is an Exception?
|
|
|
|
var d1 = new short[1][];
|
|
|
|
d1[0] = new short[2];
|
|
d1[0][0] = (short)(ihl.StartRecordShorted ? 1 : 0);
|
|
d1[0][1] = (short)(ihl.TriggerInputShorted ? 1 : 0);
|
|
|
|
data.Add(d1);
|
|
sampleNumbers.Add(ulong.MaxValue);
|
|
timeStamps.Add(ulong.MinValue);
|
|
sequenceNumbers.Add(ulong.MinValue);
|
|
}
|
|
else
|
|
{
|
|
//Thread.Sleep(50);
|
|
}
|
|
|
|
ThreadPool.QueueUserWorkItem(packet.Info.NewData,
|
|
new NewDataData(data.ToArray(), sampleNumbers.ToArray(), timeStamps.ToArray(),
|
|
sequenceNumbers.ToArray()));
|
|
|
|
sampleNumbers.Clear();
|
|
timeStamps.Clear();
|
|
sequenceNumbers.Clear();
|
|
data.Clear();
|
|
}
|
|
}
|
|
Thread.Sleep(uiMillisecondsBetweenSamples);
|
|
}
|
|
var endRealtime = new EndRealtimeMode(this);
|
|
endRealtime.SyncExecute();
|
|
packet.Info.Success();
|
|
}
|
|
catch (CanceledException)
|
|
{
|
|
packet.Info.Cancel();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
packet.Info.Error(ex.Message, ex);
|
|
}
|
|
}
|
|
private void AsyncRealTimePolling(object asyncInfo)
|
|
{
|
|
var packet = asyncInfo as RealTimeAsyncPacket;
|
|
|
|
try
|
|
{
|
|
var ssaPolarity = new SetSystemAttribute(this, AbstractCommandBase.Default_IO_Timeout);
|
|
ssaPolarity.SetValue(AttributeTypes.SystemAttributes.TriggerPolarity, (byte)(InvertTrigger ? 1 : 0), true);
|
|
ssaPolarity.SyncExecute();
|
|
ssaPolarity = new SetSystemAttribute(this, AbstractCommandBase.Default_IO_Timeout);
|
|
ssaPolarity.SetValue(AttributeTypes.SystemAttributes.StartRecordPolarity, (byte)(InvertStart ? 1 : 0), true);
|
|
ssaPolarity.SyncExecute();
|
|
|
|
// Set sample rate and adjustable anti-alias filter frequency in the
|
|
// hardware based on the milliseconds between samples parameter that
|
|
// was passed in via the RealTimeAsyncPacket object. Keep the filter
|
|
// -3dB point at 1/5 the frequency of the sample rate.
|
|
// number of samples per second
|
|
var rtSampleRate = Convert.ToUInt32(50);
|
|
const int uiMillisecondsBetweenSamples = 200;
|
|
var setSampleRate = new SetRealtimeSampleRate(this);
|
|
setSampleRate.SetValue(rtSampleRate);
|
|
setSampleRate.SyncExecute();
|
|
|
|
|
|
DateTime lastUpdate = DateTime.MinValue;
|
|
// set RT mode in HW
|
|
var startRT = new StartRealtimeMode(this);
|
|
startRT.SupportsMultipleSampleRealtime = true;
|
|
startRT.SyncExecute();
|
|
var sampleNumbers = new List<ulong>(100);
|
|
var timeStamps = new List<ulong>(100);
|
|
var sequenceNumbers = new List<ulong>(100);
|
|
var data = new List<short[][]>(100);
|
|
// start calling for RT data
|
|
var getRTdata = GetRealtimeSamplesClass(this, true);
|
|
getRTdata.LogCommands = false;
|
|
getRTdata.Channels = (ushort)DASInfo.Modules.Sum(m => m.NumberOfChannels);
|
|
while (!packet.StopEvent.WaitOne(0, false))
|
|
{
|
|
// call HW to get RT samples
|
|
try
|
|
{
|
|
getRTdata.SyncExecute();
|
|
}
|
|
catch (Exception ex) { APILogger.Log(ex); }
|
|
if (getRTdata.SamplesReturned > 0)
|
|
{
|
|
var rtData = new short[getRTdata.Channels][];
|
|
for (var idx = 0; idx < getRTdata.Channels; idx++)
|
|
{
|
|
rtData[idx] = getRTdata.GetChannelData(idx);
|
|
}
|
|
sampleNumbers.Add(getRTdata.SampleNumber);
|
|
timeStamps.Add(getRTdata.TimeStamp);
|
|
sequenceNumbers.Add(getRTdata.SequenceNumber);
|
|
data.Add(rtData);
|
|
}
|
|
if (DateTime.Now.Subtract(lastUpdate).TotalMilliseconds >= packet.minCallbackUpdateTimeMs)
|
|
{
|
|
lastUpdate = DateTime.Now;
|
|
|
|
var ihl = new InitializeHardwareLines(this, AbstractCommandBase.Default_IO_Timeout);
|
|
ihl.LogCommands = false;
|
|
try
|
|
{
|
|
if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.InitHardwareInputLines) && !packet.StopEvent.WaitOne(0, false))
|
|
{
|
|
ihl.SyncExecute();
|
|
}
|
|
}
|
|
catch (Exception ex) { APILogger.Log(ex); }
|
|
|
|
if (Common.Constants.CheckStatusLinesInRealtime)
|
|
{
|
|
var d1 = new short[1][];
|
|
|
|
d1[0] = new short[2];
|
|
d1[0][0] = (short)(ihl.StartRecordShorted ? 1 : 0);
|
|
d1[0][1] = (short)(ihl.TriggerInputShorted ? 1 : 0);
|
|
|
|
data.Add(d1);
|
|
sampleNumbers.Add(ulong.MaxValue);
|
|
}
|
|
//15244 Cannot run meter mode in realtime
|
|
timeStamps.Add(ulong.MinValue);
|
|
ThreadPool.QueueUserWorkItem(packet.Info.NewData, new NewDataData(data.ToArray(), sampleNumbers.ToArray(), timeStamps.ToArray(), sequenceNumbers.ToArray()));
|
|
|
|
sampleNumbers.Clear();
|
|
timeStamps.Clear();
|
|
sequenceNumbers.Clear();
|
|
data.Clear();
|
|
}
|
|
Thread.Sleep(uiMillisecondsBetweenSamples);
|
|
}
|
|
var exitRT = new EndRealtimeMode(this);
|
|
exitRT.SyncExecute();
|
|
packet.Info.Success();
|
|
}
|
|
catch (CanceledException)
|
|
{
|
|
packet.Info.Cancel();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
packet.Info.Error(ex.Message, ex);
|
|
}
|
|
}
|
|
void IRealTimeActions.ExitRealTimeMode(ServiceCallback callback, object userData)
|
|
{
|
|
var info = new SliceServiceAsyncInfo(callback, userData);
|
|
|
|
LaunchAsyncWorker("Slice.ExitRealTimeMode", new WaitCallback(AsyncExitRealTimeMode), info);
|
|
}
|
|
|
|
private void AsyncExitRealTimeMode(object asyncInfo)
|
|
{
|
|
var info = asyncInfo as SliceServiceAsyncInfo;
|
|
if (!SupportsRealtime())
|
|
{
|
|
if (null != DASArmStatus) DASArmStatus.IsInRealtime = false;// FB15550: Update IsInRealtime flag if we exit RT so DataPRO is aware
|
|
info.Success();
|
|
return;
|
|
}
|
|
try
|
|
{
|
|
var exitRT = new EndRealtimeMode(this);
|
|
exitRT.SyncExecute();
|
|
if (null != DASArmStatus && RecordingModeExtensions.IsAStreamMode((DFConstantsAndEnums.RecordingMode)DASArmStatus.RecordingMode))
|
|
{
|
|
//for safety unset the auto arm flag in case the user reboots ...
|
|
DisAutoArm();
|
|
}
|
|
if (null != DASArmStatus) DASArmStatus.IsInRealtime = false; // FB15550: Update IsInRealtime flag if we exit RT so DataPRO is aware
|
|
info.Success();
|
|
}
|
|
catch (CanceledException)
|
|
{
|
|
info.Cancel();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
info.Error(ex.Message, ex);
|
|
}
|
|
}
|
|
|
|
void IRealTimeActions.RealTimeTiltPolling(ServiceCallback callback,
|
|
object userData,
|
|
ManualResetEvent stopEvent)
|
|
|
|
{
|
|
var packet = new RealTimeTiltAsyncPacket();
|
|
packet.StopEvent = stopEvent;
|
|
packet.Info = new SliceServiceAsyncInfo(callback, userData);
|
|
LaunchAsyncWorker("Slice.RealTimeTiltPolling", AsyncRealTimeTiltPolling, packet);
|
|
}
|
|
|
|
private void AsyncRealTimeTiltPolling(object asyncInfo)
|
|
{
|
|
var packet = asyncInfo as RealTimeTiltAsyncPacket;
|
|
|
|
try
|
|
{
|
|
while (!packet.StopEvent.WaitOne(0, false))
|
|
{
|
|
var axisADCData = new short[3] { 0, 0, 0 };
|
|
var calFactor = new double[3] { 1, 1, 1 };
|
|
var zeroData = new double[3] { 0, 0, 0 };
|
|
|
|
// Get raw tilt ADC
|
|
var qtsd = new QueryTiltSensorData(this);
|
|
qtsd.DeviceID = 1;
|
|
qtsd.SyncExecute();
|
|
axisADCData[0] = qtsd.Channel1ValueAdc;
|
|
axisADCData[1] = qtsd.Channel2ValueAdc;
|
|
axisADCData[2] = qtsd.Channel3ValueAdc;
|
|
|
|
//Get tilt sensor scale factors
|
|
var scaleFactorGPerADC = new float[3] { 1, 1, 1 };
|
|
if (RunTestVariables.InRunTest && RunTestVariables.TiltSensorScaleFactor > 0)
|
|
{
|
|
scaleFactorGPerADC = new[]
|
|
{
|
|
RunTestVariables.TiltSensorScaleFactor, RunTestVariables.TiltSensorScaleFactor,
|
|
RunTestVariables.TiltSensorScaleFactor
|
|
};
|
|
}
|
|
else
|
|
{
|
|
try
|
|
{
|
|
var scaleFactorQuery = new QueryArmAttribute(this)
|
|
{
|
|
Key = AttributeTypes.ArmAndEventAttributes.InSliceTiltSensorScaleFactorGPerADC
|
|
};
|
|
scaleFactorQuery.SyncExecute();
|
|
scaleFactorGPerADC = scaleFactorQuery.Value as float[];
|
|
}
|
|
catch (Exception)
|
|
{
|
|
}
|
|
}
|
|
|
|
int dominantAxis = (axisADCData.Max() < Math.Abs(axisADCData.Min())) ?
|
|
Array.IndexOf(axisADCData, axisADCData.Min()) : Array.IndexOf(axisADCData, axisADCData.Max());
|
|
|
|
var query = new QuerySystemAttribute_BridgeSlice6(this);
|
|
query.DeviceID = 1;
|
|
|
|
try
|
|
{
|
|
query.Key = AttributeTypes.SystemAttributes_BridgeSlice6.TILTSENSOR_CAL_1;
|
|
query.SyncExecute();
|
|
calFactor[0] = Convert.ToDouble(query.Value) == 0D ? 1D : Convert.ToDouble(query.Value);
|
|
|
|
query.Key = AttributeTypes.SystemAttributes_BridgeSlice6.TILTSENSOR_CAL_7;
|
|
query.SyncExecute();
|
|
calFactor[1] = Convert.ToDouble(query.Value) == 0D ? 1D : Convert.ToDouble(query.Value);
|
|
|
|
query.Key = AttributeTypes.SystemAttributes_BridgeSlice6.TILTSENSOR_CAL_13;
|
|
query.SyncExecute();
|
|
calFactor[2] = Convert.ToDouble(query.Value) == 0D ? 1D : Convert.ToDouble(query.Value);
|
|
}
|
|
catch { }
|
|
|
|
|
|
// Calculate Accel Data
|
|
var accelData = new double[3];
|
|
for (int i = 0; i < accelData.Length; i++)
|
|
{
|
|
var channel_2G = (axisADCData[i] / calFactor[i]) * scaleFactorGPerADC[i];
|
|
if (channel_2G > 1)
|
|
{
|
|
channel_2G = 1;
|
|
}
|
|
else if (channel_2G < -1)
|
|
{
|
|
channel_2G = -1;
|
|
}
|
|
|
|
accelData[i] = channel_2G;
|
|
}
|
|
|
|
|
|
bool positivePolarity = (axisADCData[dominantAxis] > 0);
|
|
|
|
switch (dominantAxis)
|
|
{
|
|
case 0:
|
|
query.Key = AttributeTypes.SystemAttributes_BridgeSlice6.TILTSENSOR_CAL_2;
|
|
break;
|
|
case 1:
|
|
query.Key = AttributeTypes.SystemAttributes_BridgeSlice6.TILTSENSOR_CAL_8;
|
|
break;
|
|
case 2:
|
|
query.Key = AttributeTypes.SystemAttributes_BridgeSlice6.TILTSENSOR_CAL_14;
|
|
break;
|
|
default:
|
|
query.Key = AttributeTypes.SystemAttributes_BridgeSlice6.TILTSENSOR_CAL_2;
|
|
break;
|
|
}
|
|
|
|
try
|
|
{
|
|
query.SyncExecute();
|
|
zeroData[dominantAxis] = Convert.ToDouble(query.Value);
|
|
}
|
|
catch { }
|
|
|
|
var query2 = new QuerySystemAttribute_BridgeSlice6(this);
|
|
query2.DeviceID = 1;
|
|
|
|
switch (dominantAxis)
|
|
{
|
|
case 0:
|
|
query.Key = positivePolarity ? AttributeTypes.SystemAttributes_BridgeSlice6.TILTSENSOR_CAL_9 : AttributeTypes.SystemAttributes_BridgeSlice6.TILTSENSOR_CAL_10;
|
|
query2.Key = positivePolarity ? AttributeTypes.SystemAttributes_BridgeSlice6.TILTSENSOR_CAL_15 : AttributeTypes.SystemAttributes_BridgeSlice6.TILTSENSOR_CAL_16;
|
|
break;
|
|
case 1:
|
|
query.Key = positivePolarity ? AttributeTypes.SystemAttributes_BridgeSlice6.TILTSENSOR_CAL_3 : AttributeTypes.SystemAttributes_BridgeSlice6.TILTSENSOR_CAL_4;
|
|
query2.Key = positivePolarity ? AttributeTypes.SystemAttributes_BridgeSlice6.TILTSENSOR_CAL_17 : AttributeTypes.SystemAttributes_BridgeSlice6.TILTSENSOR_CAL_18;
|
|
break;
|
|
case 2:
|
|
query.Key = positivePolarity ? AttributeTypes.SystemAttributes_BridgeSlice6.TILTSENSOR_CAL_5 : AttributeTypes.SystemAttributes_BridgeSlice6.TILTSENSOR_CAL_6;
|
|
query2.Key = positivePolarity ? AttributeTypes.SystemAttributes_BridgeSlice6.TILTSENSOR_CAL_11 : AttributeTypes.SystemAttributes_BridgeSlice6.TILTSENSOR_CAL_12;
|
|
break;
|
|
default:
|
|
query.Key = positivePolarity ? AttributeTypes.SystemAttributes_BridgeSlice6.TILTSENSOR_CAL_9 : AttributeTypes.SystemAttributes_BridgeSlice6.TILTSENSOR_CAL_10;
|
|
query2.Key = positivePolarity ? AttributeTypes.SystemAttributes_BridgeSlice6.TILTSENSOR_CAL_15 : AttributeTypes.SystemAttributes_BridgeSlice6.TILTSENSOR_CAL_16;
|
|
break;
|
|
}
|
|
|
|
try
|
|
{
|
|
query.SyncExecute();
|
|
query2.SyncExecute();
|
|
|
|
switch (dominantAxis)
|
|
{
|
|
case 0:
|
|
zeroData[1] = Convert.ToDouble(query.Value);
|
|
zeroData[2] = Convert.ToDouble(query2.Value);
|
|
break;
|
|
case 1:
|
|
zeroData[0] = Convert.ToDouble(query.Value);
|
|
zeroData[2] = Convert.ToDouble(query2.Value);
|
|
break;
|
|
case 2:
|
|
zeroData[0] = Convert.ToDouble(query.Value);
|
|
zeroData[1] = Convert.ToDouble(query2.Value);
|
|
break;
|
|
}
|
|
}
|
|
catch { }
|
|
|
|
// Calculate Tilt Data
|
|
var Sx = (axisADCData[0] - zeroData[0]) / calFactor[0];
|
|
var Sy = (axisADCData[1] - zeroData[1]) / calFactor[1];
|
|
var Sz = (axisADCData[2] - zeroData[2]) / calFactor[2];
|
|
var SG = Math.Sqrt(Math.Pow(Sx, 2) + Math.Pow(Sy, 2) + Math.Pow(Sz, 2));
|
|
|
|
var tiltInDegrees = new double[3];
|
|
tiltInDegrees[0] = (180.0 / Math.PI) * Math.Asin(Sx / SG);
|
|
tiltInDegrees[1] = (180.0 / Math.PI) * Math.Asin(Sy / SG);
|
|
tiltInDegrees[2] = (180.0 / Math.PI) * Math.Asin(Sz / SG);
|
|
|
|
TiltAxisData = tiltInDegrees.ToList();
|
|
ThreadPool.QueueUserWorkItem(packet.Info.NewData, new ServiceCallbackData.TiltNewData()
|
|
{
|
|
TiltData = tiltInDegrees,
|
|
AccelData = accelData,
|
|
});
|
|
}
|
|
packet.Info.Success();
|
|
|
|
}
|
|
catch (CanceledException)
|
|
{
|
|
packet.Info.Cancel();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
packet.Info.Error(ex.Message, ex);
|
|
}
|
|
}
|
|
|
|
public string UDPStreamAddress
|
|
{
|
|
get;
|
|
private set;
|
|
}
|
|
// FB15313: Add UDP Streaming options
|
|
void IRealTimeActions.SetUDPStreamProfile(ServiceCallback callback, object userData,
|
|
UDPStreamProfile streamProfile, string udpAddress, ushort timeChannelId, ushort dataChannelId,
|
|
uint[] tmnsConfig, ushort irigTimeDataPacketIntervalMs)
|
|
{
|
|
var packet = new RealTimeUDPStreamProfileAsyncPacket()
|
|
{
|
|
Info = new SliceServiceAsyncInfo(callback, userData),
|
|
Profile = streamProfile,
|
|
UDPAddress = udpAddress,
|
|
TimeDataChannelId = timeChannelId,
|
|
DataChannelId = dataChannelId,
|
|
TMNSConfig = tmnsConfig,
|
|
IRIGTimeDataPacketIntervalMs = irigTimeDataPacketIntervalMs
|
|
};
|
|
|
|
LaunchAsyncWorker("Slice.SetUDPStreamProfile", AsyncSetUDPStreamProfile, packet);
|
|
}
|
|
|
|
private void AsyncSetUDPStreamProfile(object asyncInfo)
|
|
{
|
|
var packet = asyncInfo as RealTimeUDPStreamProfileAsyncPacket;
|
|
|
|
try
|
|
{
|
|
if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.UDPRealtimeStream))
|
|
{
|
|
var rtUDPAddress = packet.UDPAddress;
|
|
var profile = (byte)packet.Profile;
|
|
var timeChannelId = packet.TimeDataChannelId;
|
|
var dataChannelId = packet.DataChannelId;
|
|
var tmnsConfig = new TMNSConfig(packet.TMNSConfig);
|
|
|
|
var udpAddress = new UriBuilder(rtUDPAddress);
|
|
byte[] tmp = Encoding.ASCII.GetBytes(udpAddress.Uri.AbsoluteUri.TrimEnd('/'));
|
|
byte[] ipport = new byte[28];
|
|
for (int i = 0; i < tmp.Length; i++)
|
|
{
|
|
ipport[i] = tmp[i];
|
|
}
|
|
|
|
var ssaUDPStreamProfile = new StreamConfigUDPSet(this, AbstractCommandBase.Default_IO_Timeout)
|
|
{
|
|
Stream_Profile_Number = profile,
|
|
Irig106Config0 = timeChannelId,
|
|
Irig106Config1 = dataChannelId,
|
|
TMNS_PCMSubFrameId = tmnsConfig.TMNS_PCMSubFrameId,
|
|
TMNS_MsgId = tmnsConfig.TMNS_MsgId,
|
|
TMNS_PCMMinorPerMajor = tmnsConfig.TMNS_PCMMinorPerMajor,
|
|
TMNS_TMATSPortNumber = tmnsConfig.TMNS_TMATSPortNumber,
|
|
IENAUDP_PortNumber = tmnsConfig.IENAUDP_PortNumber,
|
|
TMNS5 = tmnsConfig.TMNS5,
|
|
TMNS6 = tmnsConfig.TMNS6,
|
|
TMNS7 = tmnsConfig.TMNS7,
|
|
UdpIpPort = ipport
|
|
};
|
|
ssaUDPStreamProfile.SyncExecute();
|
|
UDPStreamAddress = udpAddress.Uri.AbsoluteUri.TrimEnd('/');
|
|
// FB15354: Enforce IRIG Time Data Packet Interval in order to read streaming data in NetView, etc.
|
|
var ssaIRIGTimeData = new SetSystemAttributeSLICE2(this, AbstractCommandBase.Default_IO_Timeout);
|
|
ssaIRIGTimeData.SetValue(AttributeTypes.SystemAttributesSLICE2.S6A_IrigTimeDataPacketIntervalMsec, packet.IRIGTimeDataPacketIntervalMs.ToString(), true);
|
|
ssaIRIGTimeData.SyncExecute();
|
|
}
|
|
packet.Info.Success();
|
|
}
|
|
catch (CanceledException)
|
|
{
|
|
packet.Info.Cancel();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
packet.Info.Error(ex.Message, ex);
|
|
}
|
|
}
|
|
|
|
void IRealTimeActions.GetUDPStreamProfile(ServiceCallback callback, object userData)
|
|
{
|
|
var info = new SliceServiceAsyncInfo(callback, userData);
|
|
LaunchAsyncWorker("Slice.GetUDPStreamProfile", AsyncGetUDPStreamProfile, info);
|
|
}
|
|
|
|
private void AsyncGetUDPStreamProfile(object asyncInfo)
|
|
{
|
|
var info = asyncInfo as SliceServiceAsyncInfo;
|
|
try
|
|
{
|
|
if (IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.UDPRealtimeStream))
|
|
{
|
|
var qsaUDPStreamProfile = new StreamConfigUDPGet(this, AbstractCommandBase.Default_IO_Timeout);
|
|
qsaUDPStreamProfile.SyncExecute();
|
|
var len = Array.IndexOf(qsaUDPStreamProfile.UdpIpPort, (byte)0x00);
|
|
UDPStreamAddress = Encoding.ASCII.GetString(qsaUDPStreamProfile.UdpIpPort, 0, len < 0 ? qsaUDPStreamProfile.UdpIpPort.Length : len);
|
|
}
|
|
info.Success();
|
|
}
|
|
catch (CanceledException)
|
|
{
|
|
info.Cancel();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
info.Error(ex.Message, ex);
|
|
}
|
|
}
|
|
#endregion
|
|
}
|
|
}
|