675 lines
31 KiB
C#
675 lines
31 KiB
C#
|
|
using System;
|
|||
|
|
using System.Collections.Generic;
|
|||
|
|
using System.Linq;
|
|||
|
|
using System.Text;
|
|||
|
|
using System.Threading;
|
|||
|
|
using DTS.Common.Enums;
|
|||
|
|
using DTS.Common.Enums.DASFactory;
|
|||
|
|
using DTS.Common.Enums.Hardware;
|
|||
|
|
using DTS.Common.ICommunication;
|
|||
|
|
using DTS.Common.Interface.Connection;
|
|||
|
|
using DTS.Common.Interface.DASFactory;
|
|||
|
|
using DTS.Common.Utilities.Logging;
|
|||
|
|
using DTS.DASLib.Command.TDAS;
|
|||
|
|
|
|||
|
|
namespace DTS.DASLib.Service
|
|||
|
|
{
|
|||
|
|
public partial class TDAS<T> : Communication<T>,
|
|||
|
|
IDASCommunication,
|
|||
|
|
IConfigurationActions,
|
|||
|
|
IDiagnosticsActions,
|
|||
|
|
ITriggerCheckActions,
|
|||
|
|
IRealTimeActions,
|
|||
|
|
IArmActions,
|
|||
|
|
IDownloadActions where T : IConnection, new()
|
|||
|
|
{
|
|||
|
|
#region Real time
|
|||
|
|
public bool ControlsDAQ() { return false; }
|
|||
|
|
public bool SupportsHardwareInputCheck() { return false; }
|
|||
|
|
public bool InvertStart
|
|||
|
|
{
|
|||
|
|
get { return false; }
|
|||
|
|
set { if (value) { throw new NotSupportedException("TDAS does not support inverted start"); } }
|
|||
|
|
}
|
|||
|
|
public bool InvertTrigger
|
|||
|
|
{
|
|||
|
|
get { return false; }
|
|||
|
|
set { if (value) { throw new NotSupportedException("TDAS does not support inverted trigger"); } }
|
|||
|
|
}
|
|||
|
|
public bool SupportsTriggerInversion() => HardwareConstants.SupportsTriggerInversion(GetHardwareType(), ProtocolVersion);
|
|||
|
|
public bool SupportsStartInversion() => HardwareConstants.SupportsStartInversion(GetHardwareType(), ProtocolVersion);
|
|||
|
|
public bool SupportsRealtime()
|
|||
|
|
{
|
|||
|
|
if (DFConstantsAndEnums.ModuleType.G5Analog == DASInfo.Modules[0].TypeOfModule)
|
|||
|
|
{
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
foreach (var module in DASInfo.Modules)
|
|||
|
|
{
|
|||
|
|
switch (module.TypeOfModule)
|
|||
|
|
{
|
|||
|
|
case DFConstantsAndEnums.ModuleType.ProDIM:
|
|||
|
|
case DFConstantsAndEnums.ModuleType.ProSIM:
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
public bool IgnoreShortedStart
|
|||
|
|
{
|
|||
|
|
get { return false; }
|
|||
|
|
set { if (value) { throw new NotSupportedException("TDAS does not support ignore shorted start"); } }
|
|||
|
|
}
|
|||
|
|
public bool IgnoreShortedTrigger
|
|||
|
|
{
|
|||
|
|
get { return false; }
|
|||
|
|
set { if (value) { throw new NotSupportedException("TDAS does not support ignore shorted trigger"); } }
|
|||
|
|
}
|
|||
|
|
public bool SupportsAutoArm() { return false; }
|
|||
|
|
public bool SupportsLevelTrigger() { return false; }
|
|||
|
|
public bool SupportsMultipleEvents() { return false; }
|
|||
|
|
public bool SupportsMultipleSampleRealtime() { return false; }
|
|||
|
|
public bool SupportsMultiChannelRealtime() { return false; }
|
|||
|
|
|
|||
|
|
private class RealTimeAsyncPacket
|
|||
|
|
{
|
|||
|
|
public TDASServiceAsyncInfo info { get; set; }
|
|||
|
|
public System.Threading.Timer timer { get; set; }
|
|||
|
|
public int samplesPerSecond { get; set; }
|
|||
|
|
public int millisecBetweenSamples { get; set; }
|
|||
|
|
public bool realtimeMultipleSamplesEnabled { get; set; }
|
|||
|
|
public int ModuleIndex { get; set; }
|
|||
|
|
public System.Threading.ManualResetEvent StopEvent { get; set; }
|
|||
|
|
public bool CareAboutSampleNumber { get; set; }
|
|||
|
|
public int minCallbackUpdateTimeMs { get; set; }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void IRealTimeActions.RealTimePolling(ServiceCallback callback,
|
|||
|
|
object userData,
|
|||
|
|
ManualResetEvent mre,
|
|||
|
|
byte[] channels)
|
|||
|
|
{
|
|||
|
|
var packet = new RealTimeAsyncPacket();
|
|||
|
|
packet.info = new TDASServiceAsyncInfo(callback, userData);
|
|||
|
|
packet.StopEvent = mre;
|
|||
|
|
if (IsG5())
|
|||
|
|
{
|
|||
|
|
packet.millisecBetweenSamples = 50;
|
|||
|
|
packet.samplesPerSecond = 1000;
|
|||
|
|
packet.CareAboutSampleNumber = false;
|
|||
|
|
LaunchAsyncWorker("TDAS.Realtime", new WaitCallback(AsyncRealTime), packet);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
LaunchAsyncWorker("TDAS.RealtimePolling", new WaitCallback(AsyncRealTimePolling), packet);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void IRealTimeActions.RealTime(int samplesPerSecond,
|
|||
|
|
int millisecBetweenSamples,
|
|||
|
|
ServiceCallback callback,
|
|||
|
|
object userData,
|
|||
|
|
bool realtimeMultipleSamplesEnabled,
|
|||
|
|
int moduleIndex,
|
|||
|
|
ManualResetEvent stopEvent,
|
|||
|
|
byte[] channels,
|
|||
|
|
double aaf,
|
|||
|
|
int minCallbackUpdateTimeMs,
|
|||
|
|
bool UseUDPStreaming,
|
|||
|
|
string hostIPAddress)
|
|||
|
|
{
|
|||
|
|
|
|||
|
|
var packet = new RealTimeAsyncPacket
|
|||
|
|
{
|
|||
|
|
info = new TDASServiceAsyncInfo(callback, userData),
|
|||
|
|
millisecBetweenSamples = millisecBetweenSamples,
|
|||
|
|
samplesPerSecond = samplesPerSecond,
|
|||
|
|
realtimeMultipleSamplesEnabled = realtimeMultipleSamplesEnabled,
|
|||
|
|
ModuleIndex = moduleIndex,
|
|||
|
|
CareAboutSampleNumber = true,
|
|||
|
|
StopEvent = stopEvent,
|
|||
|
|
minCallbackUpdateTimeMs = minCallbackUpdateTimeMs
|
|||
|
|
};
|
|||
|
|
LaunchAsyncWorker("TDAS.RealTime", new WaitCallback(AsyncRealTime), packet);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void IRealTimeActions.RealTimeTiltPolling(ServiceCallback callback, object userData, ManualResetEvent stopEvent)
|
|||
|
|
{
|
|||
|
|
var info = new TDASServiceAsyncInfo(callback, userData);
|
|||
|
|
info.Success();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public string UDPStreamAddress { get; }
|
|||
|
|
void IRealTimeActions.SetUDPStreamProfile(ServiceCallback callback, object userData, UDPStreamProfile streamProfile, string udpAddress, ushort timeChannelId, ushort dataChannelId, uint[] tmnsConfig, ushort irigTimeDataPacketIntervalMs)
|
|||
|
|
{
|
|||
|
|
var info = new TDASServiceAsyncInfo(callback, userData);
|
|||
|
|
info.Success();
|
|||
|
|
}
|
|||
|
|
void IRealTimeActions.GetUDPStreamProfile(ServiceCallback callback, object userData)
|
|||
|
|
{
|
|||
|
|
var info = new TDASServiceAsyncInfo(callback, userData);
|
|||
|
|
info.Success();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private object CallbackLock = new object();
|
|||
|
|
private DateTime lastUpdate = DateTime.MinValue;
|
|||
|
|
private const int MIN_CALLBACK_UPDATE_TIME = 10;
|
|||
|
|
private List<UInt64> _timeStamps = new List<ulong>();
|
|||
|
|
private List<UInt64> _sequenceNumbers = new List<ulong>();
|
|||
|
|
private List<UInt64> _sampleNumbers = new List<ulong>();
|
|||
|
|
private List<short[][]> _dataSamples = new List<short[][]>();
|
|||
|
|
|
|||
|
|
private const int BufferSize = 4096;
|
|||
|
|
private readonly byte[] _buffer = new byte[BufferSize];
|
|||
|
|
private RealTimeAsyncPacket _packet;
|
|||
|
|
private const int NumChannels = 8;
|
|||
|
|
private const int StartChannel = 0;
|
|||
|
|
|
|||
|
|
private void ProcessData(IAsyncResult ar)
|
|||
|
|
{
|
|||
|
|
lock (CallbackLock)
|
|||
|
|
{
|
|||
|
|
var bytes = sock.EndReceive(ar);
|
|||
|
|
if (0 == bytes) { return; }
|
|||
|
|
var s = Encoding.ASCII.GetString(_buffer, 0, bytes);
|
|||
|
|
s = s.Replace("\r\n", Convert.ToChar(0xBF).ToString());
|
|||
|
|
var lines = s.Split(Convert.ToChar(0xBF));
|
|||
|
|
foreach (var line in lines)
|
|||
|
|
{
|
|||
|
|
var rtData = new short[NumChannels][];
|
|||
|
|
if (!line.Contains("MD")) { continue; }
|
|||
|
|
var tokens = line.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
|||
|
|
if (tokens.Length < 8) { continue; }
|
|||
|
|
//0 = command, 1 = time, 2 = channel 0, etc
|
|||
|
|
for (var i = 0; i < NumChannels; i++)
|
|||
|
|
{
|
|||
|
|
rtData[i] = new short[1];
|
|||
|
|
rtData[i][0] = short.MaxValue;
|
|||
|
|
if (i >= StartChannel)
|
|||
|
|
{
|
|||
|
|
var index = i - StartChannel + 2;
|
|||
|
|
if (index < tokens.Length)
|
|||
|
|
{
|
|||
|
|
short.TryParse(tokens[i - StartChannel + 2], out rtData[i][0]);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
_dataSamples.Add(rtData);
|
|||
|
|
}
|
|||
|
|
//TDAS reporting interval is slow enough, don't restrict it further!
|
|||
|
|
//if (DateTime.Now.Subtract(lastUpdate).TotalMilliseconds > MIN_CALLBACK_UPDATE_TIME)
|
|||
|
|
{
|
|||
|
|
_packet.info.NewData(_dataSamples, _sampleNumbers, _timeStamps, _sequenceNumbers);
|
|||
|
|
|
|||
|
|
_dataSamples.Clear();
|
|||
|
|
//lastUpdate = DateTime.Now;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
sock.BeginReceive(_buffer, 0, BufferSize, new AsyncCallback(ProcessData), null);
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// only used with RACK,
|
|||
|
|
/// is a polling (sampleaverage/single sample) realtime mode
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="asyncInfo"></param>
|
|||
|
|
private void AsyncRealTimePolling(object asyncInfo)
|
|||
|
|
{
|
|||
|
|
var packet = asyncInfo as RealTimeAsyncPacket;
|
|||
|
|
|
|||
|
|
//List<ulong> sampleNumbers = new List<ulong>();
|
|||
|
|
//List<short[][]> data = new List<short[][]>();
|
|||
|
|
const int numTDASDIMChannels = 16;
|
|||
|
|
|
|||
|
|
ulong sampleNumber = 0;
|
|||
|
|
var numChannels = ConfigData.Modules.Sum(module => module.NumberOfChannels());
|
|||
|
|
|
|||
|
|
while (!(this as DTS.Common.Interface.DASFactory.ICommunication).IsCanceled() && !packet.StopEvent.WaitOne(0))
|
|||
|
|
{
|
|||
|
|
var curChannel = 0;
|
|||
|
|
//first array is channels, second array are samples
|
|||
|
|
var rtData = new short[numChannels][];
|
|||
|
|
|
|||
|
|
foreach (var module in DASInfo.Modules)
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
switch (module.TypeOfModule)
|
|||
|
|
{
|
|||
|
|
case DFConstantsAndEnums.ModuleType.EMPTYBANK:
|
|||
|
|
continue;
|
|||
|
|
case DFConstantsAndEnums.ModuleType.ProTOM:
|
|||
|
|
{
|
|||
|
|
curChannel += 16; //8squibs, 8 digitals
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var ss = new SampleAverage(this)
|
|||
|
|
{
|
|||
|
|
ModuleIndex = module.ModuleArrayIndex
|
|||
|
|
};
|
|||
|
|
ss.SyncExecute();
|
|||
|
|
if (module.TypeOfModule == DFConstantsAndEnums.ModuleType.ProDIM)
|
|||
|
|
{
|
|||
|
|
//apparently the DIM for SA just returns 1.0, 0
|
|||
|
|
for (int ch = 0; ch < numTDASDIMChannels; ch++)
|
|||
|
|
{
|
|||
|
|
rtData[curChannel++] = new short[]
|
|||
|
|
{
|
|||
|
|
0 == ss.ChannelValues[ch] ? short.MinValue : short.MaxValue
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
for (int ch = 0; ch < 8; ch++)
|
|||
|
|
{
|
|||
|
|
rtData[curChannel++] = new short[] { Convert.ToInt16(ss.ChannelValues[ch]) };
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
//if (IsG5() && i == 0)
|
|||
|
|
//{
|
|||
|
|
// int digitalbits = (ss.ChannelValues[9] << 16) | ss.ChannelValues[8];
|
|||
|
|
// BitVector32 bv = new BitVector32(digitalbits);
|
|||
|
|
// for (int digCh = 0; digCh < 16; digCh++)
|
|||
|
|
// {
|
|||
|
|
// if (bv[(1 << digCh)])
|
|||
|
|
// {
|
|||
|
|
// rtData[32 + digCh] = new short[] {short.MinValue};
|
|||
|
|
// }
|
|||
|
|
// else
|
|||
|
|
// {
|
|||
|
|
// rtData[32 + digCh] = new short[] {short.MaxValue};
|
|||
|
|
// }
|
|||
|
|
// }
|
|||
|
|
//}
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
APILogger.Log(ex);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
packet.info.NewData(new List<short[][]>() { rtData }, new List<ulong>(new ulong[] { sampleNumber }), new List<ulong>(new ulong[] { ulong.MinValue }), new List<ulong>(new ulong[] { ulong.MinValue }));
|
|||
|
|
Thread.Sleep(50);
|
|||
|
|
sampleNumber++;
|
|||
|
|
//if (DateTime.Now.Subtract(lastUpdate).TotalMilliseconds >= MIN_CALLBACK_UPDATE_TIME)
|
|||
|
|
//{
|
|||
|
|
// lastUpdate = DateTime.Now;
|
|||
|
|
// packet.info.NewData(data, sampleNumbers);
|
|||
|
|
// sampleNumbers.Clear();
|
|||
|
|
// data.Clear();
|
|||
|
|
//}
|
|||
|
|
}
|
|||
|
|
packet.info.Success();
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// indicates whether the DAS supports streaming
|
|||
|
|
/// 10572 implement SW side for single command streaming realtime
|
|||
|
|
/// </summary>
|
|||
|
|
public bool SupportsIndividualChannelRealtimeStreaming => false;
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// this is used to space out the samples in a multiple sample packet of g5 realtime
|
|||
|
|
/// I based it on the max SPS, which is what we always send to the g5 (1k SPS)
|
|||
|
|
/// through empirical testing with the sig-gen, I changed to 1/978 for U5
|
|||
|
|
/// </summary>
|
|||
|
|
private const double _g5RealtimeInterval = 1 / 978D;
|
|||
|
|
//private DateTime _lastRTLogTime = DateTime.MinValue;
|
|||
|
|
//private int _samplesProcessed = 0;
|
|||
|
|
/// <summary>
|
|||
|
|
/// The worker for realtime service.
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="asyncInfo">A RealTimeAsyncPacket for responses</param>
|
|||
|
|
private void AsyncRealTime(object asyncInfo)
|
|||
|
|
{
|
|||
|
|
var packet = asyncInfo as RealTimeAsyncPacket;
|
|||
|
|
_packet = packet;
|
|||
|
|
|
|||
|
|
//tdas devices for realtime have both a sampling rate and a reporting rate
|
|||
|
|
//ultimately the reporting rate will drive what we see in realtime
|
|||
|
|
//the user has requested a specific realtime rate, however right now
|
|||
|
|
//we just force the rate on TDAS equipment, this means we have to go and
|
|||
|
|
//adjust our sample numbers as if we were running at the requested rate.
|
|||
|
|
double expectedSampleRate = packet.samplesPerSecond;
|
|||
|
|
bool bG5 = IsG5();
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
if (!SupportsRealtime()) { packet.info.Success(); return; }
|
|||
|
|
|
|||
|
|
FlushTimeoutMilliSec = 0;
|
|||
|
|
var sampleNumbers = new List<ulong>();
|
|||
|
|
var timeStamps = new List<ulong>();
|
|||
|
|
var sequenceNumbers = new List<ulong>();
|
|||
|
|
var data = new List<short[][]>();
|
|||
|
|
var dtSTart = DateTime.Now;
|
|||
|
|
var sampleNumberCarryover = 0;
|
|||
|
|
|
|||
|
|
if (bG5)
|
|||
|
|
{
|
|||
|
|
var mdx = new G5MonitorData(this);
|
|||
|
|
mdx.SyncExecute();
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
Command.TDAS.ProMonitorData md = new Command.TDAS.ProMonitorData(this, DASInfo.Modules[packet.ModuleIndex].TypeOfModule == DFConstantsAndEnums.ModuleType.ProSIM);
|
|||
|
|
md.ModuleIndex = packet.ModuleIndex;
|
|||
|
|
md.SyncExecute();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
// Then consume the constant responses. Be sure to turn off logging for performance.
|
|||
|
|
Command.TDAS.MonitorDataNextSampleBase.MonitorType mt;
|
|||
|
|
if (IsG5())
|
|||
|
|
{
|
|||
|
|
mt = Command.TDAS.MonitorDataNextSampleBase.MonitorType.G5;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
if (DASInfo.Modules[packet.ModuleIndex].TypeOfModule == DFConstantsAndEnums.ModuleType.ProDIM)
|
|||
|
|
{
|
|||
|
|
mt = MonitorDataNextSampleBase.MonitorType.DIM;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
mt = MonitorDataNextSampleBase.MonitorType.Pro;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Command.TDAS.MonitorDataNextSampleBase next = new Command.TDAS.MonitorDataNextSampleBase(this, mt);
|
|||
|
|
|
|||
|
|
next.LogCommands = false;
|
|||
|
|
while (true && !(this as DTS.Common.Interface.DASFactory.ICommunication).IsCanceled() && !packet.StopEvent.WaitOne(1))
|
|||
|
|
{
|
|||
|
|
Thread.Sleep(50);
|
|||
|
|
sampleNumbers.Clear();
|
|||
|
|
timeStamps.Clear();
|
|||
|
|
data.Clear();
|
|||
|
|
|
|||
|
|
// get the next response(s)
|
|||
|
|
next.SyncExecute();
|
|||
|
|
|
|||
|
|
var moreData = next.RTData;
|
|||
|
|
var timeSamples = next.Times;
|
|||
|
|
// Maybe we're ahead of the das.
|
|||
|
|
if (0 == moreData.Count())
|
|||
|
|
{
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (bG5)
|
|||
|
|
{
|
|||
|
|
for (int currentSample = 0; currentSample < moreData.Count; currentSample++)
|
|||
|
|
{
|
|||
|
|
short digitalBits = moreData[currentSample][32];
|
|||
|
|
short[] newData = new short[48];
|
|||
|
|
Array.Copy(moreData[currentSample], newData, 32);
|
|||
|
|
|
|||
|
|
System.Collections.Specialized.BitVector32 bv = new System.Collections.Specialized.BitVector32(Convert.ToInt32(digitalBits));
|
|||
|
|
|
|||
|
|
for (int iChannelIdx = 0; iChannelIdx < 16; iChannelIdx++)
|
|||
|
|
{
|
|||
|
|
//also note that the bitvector [] operator expects a bitmask, not an index ...
|
|||
|
|
if (bv[(1 << iChannelIdx)]) { newData[32 + iChannelIdx] = short.MinValue; }
|
|||
|
|
else { newData[32 + iChannelIdx] = short.MaxValue; }
|
|||
|
|
}
|
|||
|
|
moreData[currentSample] = newData;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
//the service is expecting a DAS full of realtime sample data,
|
|||
|
|
//however with realtime on a rack, we're only going to have one module responding with data, so we need to fake data from the other modules
|
|||
|
|
//for now since the first SIM module is the only module we're accepting data from I can spoof data from all other channels
|
|||
|
|
bool bFound = false;
|
|||
|
|
List<List<short>> largerDataArray = new List<List<short>>();
|
|||
|
|
|
|||
|
|
for (int sample = 0; sample < moreData.Count; sample++)
|
|||
|
|
{
|
|||
|
|
bFound = false;
|
|||
|
|
for (int iModule = 0; iModule < DASInfo.Modules.Length; iModule++)
|
|||
|
|
{
|
|||
|
|
if (largerDataArray.Count <= sample) { largerDataArray.Add(new List<short>()); }
|
|||
|
|
switch (DASInfo.Modules[iModule].TypeOfModule)
|
|||
|
|
{
|
|||
|
|
case DFConstantsAndEnums.ModuleType.EMPTYBANK://ignore
|
|||
|
|
break;
|
|||
|
|
case DFConstantsAndEnums.ModuleType.ProDIM:
|
|||
|
|
if (!bFound && iModule == packet.ModuleIndex)
|
|||
|
|
{
|
|||
|
|
bFound = true;
|
|||
|
|
foreach (var d in moreData[sample])
|
|||
|
|
{
|
|||
|
|
System.Collections.Specialized.BitVector32 bv = new System.Collections.Specialized.BitVector32(d);
|
|||
|
|
short[] array = new short[16];
|
|||
|
|
for (int i = 0; i < 16; i++)
|
|||
|
|
{
|
|||
|
|
if (bv[1 << i])
|
|||
|
|
{
|
|||
|
|
if (!IsG5())
|
|||
|
|
{
|
|||
|
|
array[i] = short.MaxValue;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
array[i] = short.MaxValue;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
if (!IsG5())
|
|||
|
|
{
|
|||
|
|
array[i] = short.MinValue;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
array[i] = short.MinValue;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
largerDataArray[sample].AddRange(array);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
largerDataArray[sample].AddRange(new short[]
|
|||
|
|
{
|
|||
|
|
short.MinValue,
|
|||
|
|
short.MinValue,
|
|||
|
|
short.MinValue,
|
|||
|
|
short.MinValue,
|
|||
|
|
short.MinValue,
|
|||
|
|
short.MinValue,
|
|||
|
|
short.MinValue,
|
|||
|
|
short.MinValue,
|
|||
|
|
short.MinValue,
|
|||
|
|
short.MinValue,
|
|||
|
|
short.MinValue,
|
|||
|
|
short.MinValue,
|
|||
|
|
short.MinValue,
|
|||
|
|
short.MinValue,
|
|||
|
|
short.MinValue,
|
|||
|
|
short.MinValue
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
break;
|
|||
|
|
case DFConstantsAndEnums.ModuleType.ProSIM:
|
|||
|
|
if (!bFound && iModule == packet.ModuleIndex)
|
|||
|
|
{
|
|||
|
|
bFound = true;
|
|||
|
|
largerDataArray[sample].AddRange(moreData[sample]);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
//add 8 channels of short.minvalue
|
|||
|
|
largerDataArray[sample].AddRange(new short[]
|
|||
|
|
{
|
|||
|
|
short.MinValue,
|
|||
|
|
short.MinValue,
|
|||
|
|
short.MinValue,
|
|||
|
|
short.MinValue,
|
|||
|
|
short.MinValue,
|
|||
|
|
short.MinValue,
|
|||
|
|
short.MinValue,
|
|||
|
|
short.MinValue
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
break;
|
|||
|
|
default:
|
|||
|
|
for (int i = 0; i < DASInfo.Modules[iModule].NumberOfChannels; i++)
|
|||
|
|
{
|
|||
|
|
largerDataArray[sample].Add(short.MinValue);
|
|||
|
|
}
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
moreData = new List<short[]>(largerDataArray.Count);
|
|||
|
|
for (int sample = 0; sample < largerDataArray.Count; sample++)
|
|||
|
|
{
|
|||
|
|
moreData.Add(largerDataArray[sample].ToArray());
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
// Need to transpose the data to comply with the service interface.
|
|||
|
|
int maxSample = moreData.Count();
|
|||
|
|
if (!packet.CareAboutSampleNumber)
|
|||
|
|
{
|
|||
|
|
maxSample = 1;//just do one sample back, we are in meter table mode and don't need the update speed...
|
|||
|
|
}
|
|||
|
|
for (int currentSample = 0; currentSample < maxSample; currentSample++)
|
|||
|
|
{
|
|||
|
|
short[][] transposedData = new short[moreData[0].Count()][];
|
|||
|
|
for (int currentChannel = 0; currentChannel < transposedData.Length; currentChannel++)
|
|||
|
|
{
|
|||
|
|
transposedData[currentChannel] = new short[1];
|
|||
|
|
transposedData[currentChannel][0] = moreData[currentSample][currentChannel];
|
|||
|
|
}
|
|||
|
|
data.Add(transposedData.ToArray());
|
|||
|
|
if (packet.CareAboutSampleNumber)
|
|||
|
|
{
|
|||
|
|
if (IsG5())
|
|||
|
|
{
|
|||
|
|
//compute sample numbers by starting at 0 and then just incrementing
|
|||
|
|
//finally, convert from that number to what the requested rate is. ideally the request rate is faster to prevent sample
|
|||
|
|
//number overlap, but it's more important the time scale is correct
|
|||
|
|
//this should just cause aliasing if the sample numbers overlap
|
|||
|
|
sampleNumbers.Add(
|
|||
|
|
Convert.ToUInt64(Convert.ToDouble(sampleNumberCarryover * _g5RealtimeInterval) *
|
|||
|
|
expectedSampleRate));
|
|||
|
|
timeStamps.Add(ulong.MinValue);
|
|||
|
|
if (sampleNumberCarryover == Int32.MaxValue)
|
|||
|
|
{
|
|||
|
|
sampleNumberCarryover = 0;
|
|||
|
|
}
|
|||
|
|
sampleNumberCarryover++;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
sampleNumbers.Add(Convert.ToUInt64(timeSamples[currentSample] * expectedSampleRate));
|
|||
|
|
timeStamps.Add(ulong.MinValue);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
if (sampleNumberCarryover == Int32.MaxValue)
|
|||
|
|
{
|
|||
|
|
sampleNumberCarryover = 0;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
sampleNumberCarryover++;
|
|||
|
|
}
|
|||
|
|
sampleNumbers.Add(Convert.ToUInt64(sampleNumberCarryover));
|
|||
|
|
timeStamps.Add(ulong.MinValue);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
// Send the data to the caller.
|
|||
|
|
packet.info.NewData(data, sampleNumbers, timeStamps, sequenceNumbers);
|
|||
|
|
}
|
|||
|
|
if ((this as DTS.Common.Interface.DASFactory.ICommunication).IsCanceled())
|
|||
|
|
{
|
|||
|
|
packet.info.Cancel();
|
|||
|
|
}
|
|||
|
|
StopRealtime();
|
|||
|
|
packet.info.Success();
|
|||
|
|
}
|
|||
|
|
catch (CanceledException)
|
|||
|
|
{
|
|||
|
|
// We land here when the user cancels real time.
|
|||
|
|
packet.info.Cancel();
|
|||
|
|
}
|
|||
|
|
catch (System.Exception ex)
|
|||
|
|
{
|
|||
|
|
packet.info.Error(ex.Message, ex);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Call this service to terminate real time streaming.
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="callback">User provided callback</param>
|
|||
|
|
/// <param name="userData">User provided data object</param>
|
|||
|
|
void IRealTimeActions.ExitRealTimeMode(ServiceCallback callback, object userData)
|
|||
|
|
{
|
|||
|
|
var info = new TDASServiceAsyncInfo(callback, userData);
|
|||
|
|
|
|||
|
|
LaunchAsyncWorker("TDAS.ExitRealTimeMode", new WaitCallback(AsyncExitRealTimeMode), info);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void StopRealtime()
|
|||
|
|
{
|
|||
|
|
if (!SupportsRealtime()) { return; }
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
|
|||
|
|
if (IsG5())
|
|||
|
|
{
|
|||
|
|
QuitG5Monitoring qm = new QuitG5Monitoring(this);
|
|||
|
|
qm.SyncExecute();
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
QuerySerialNumber qs = new QuerySerialNumber(this, 2000);
|
|||
|
|
qs.SyncExecute();
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
APILogger.Log(ex);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
private void AsyncExitRealTimeMode(object asyncInfo)
|
|||
|
|
{
|
|||
|
|
var info = asyncInfo as TDASServiceAsyncInfo;
|
|||
|
|
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
StopRealtime();
|
|||
|
|
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 (System.Exception ex)
|
|||
|
|
{
|
|||
|
|
info.Error(ex.Message, ex);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
}
|
|||
|
|
}
|