init
This commit is contained in:
@@ -0,0 +1,290 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using DTS.Common.Constant;
|
||||
using DTS.Common.Enums.Communication;
|
||||
using DTS.Common.Enums.DASFactory;
|
||||
using DTS.Common.ICommunication;
|
||||
using DTS.Common.Utilities;
|
||||
using DTS.Common.Utilities.Logging;
|
||||
|
||||
namespace DTS.DASLib.Command.SLICE.RealtimeCommands
|
||||
{
|
||||
/// <summary>
|
||||
/// this "command" gets the next set of samples
|
||||
/// note that in streaming mode no actual command is sent, we just pick up whatever data is in the RECV buffer
|
||||
/// </summary>
|
||||
public class RealtimeStreamingNextSamples : CommandBase, IGetRealtimeSamples
|
||||
{
|
||||
public RealtimeStreamingNextSamples(DTS.Common.Interface.DASFactory.ICommunication sock)
|
||||
: base(sock)
|
||||
{
|
||||
baseCommand = new CommandPacket();
|
||||
}
|
||||
public RealtimeStreamingNextSamples(DTS.Common.Interface.DASFactory.ICommunication sock, int msTimeout)
|
||||
: base(sock, msTimeout)
|
||||
{
|
||||
baseCommand = new CommandPacket();
|
||||
}
|
||||
public bool DigitalInput { get; set; }
|
||||
public bool[] TransitionMode { get; set; }
|
||||
/// <summary>
|
||||
/// the realtime data sample data
|
||||
/// </summary>
|
||||
public RealtimeStreamDecoder RtData { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// these are both used in garbage packet detection
|
||||
/// sample numbers are always increasing, sequence numbers are increasing but wrap around at ushort.max
|
||||
/// we could just use only sample number and not sample number and sequence
|
||||
/// for the garbage packet detection, but the number of samples per packet can be variable
|
||||
/// (especially with different SPS and number of channels) while the sequence is always +1 increase
|
||||
/// </summary>
|
||||
private ulong _lastProcessedSampleNumber = ulong.MaxValue;
|
||||
private ushort _lastSequenceNumber = 0;
|
||||
/// <summary>
|
||||
/// this is a fairly arbitrary choice, this is just used for garbage packet detection
|
||||
/// in practical use we are unlikely to drop more than 50 sequence at 2k sps
|
||||
/// so if we have more than a 1k difference there's a good chance the packet is garbage
|
||||
/// </summary>
|
||||
private const int MAX_SEQUENCE_DELTA = 1000;
|
||||
public bool SignedData { get; set; } = false;
|
||||
public void ProcessData()
|
||||
{
|
||||
try
|
||||
{
|
||||
#if REALTIME_LOGGING
|
||||
var dt = DateTime.Now;
|
||||
System.Diagnostics.Trace.WriteLine($"{dt.Hour}:{dt.Minute}:{dt.Second}.{dt.Millisecond} ProcessData");
|
||||
#endif
|
||||
RtData = null;
|
||||
System.Threading.Thread.Sleep(20);
|
||||
var bytes = baseResponse.ToBytes();
|
||||
var headerCRC = response.HeaderCRC;
|
||||
response.ComputeCRCs();
|
||||
if (response.HeaderCRC != headerCRC)
|
||||
{
|
||||
//garbage packet, don't process
|
||||
return;
|
||||
}
|
||||
if (!bytes.Any()) return;
|
||||
RtData = new RealtimeStreamDecoder(bytes);
|
||||
var bFailsSequenceCheck = false;
|
||||
|
||||
var sequenceDelta = Math.Abs(RtData.SequenceNumber - _lastSequenceNumber);
|
||||
|
||||
if (RtData.SequenceNumber != 0 && sequenceDelta > MAX_SEQUENCE_DELTA)
|
||||
{
|
||||
#if REALTIME_LOGGING
|
||||
System.Diagnostics.Trace.WriteLine("garbage packet");
|
||||
#endif
|
||||
//crc matched, but we've still got a garbage packet, as determined by looking at the sequence #
|
||||
bFailsSequenceCheck = true;
|
||||
}
|
||||
|
||||
_lastSequenceNumber = RtData.SequenceNumber;
|
||||
|
||||
if (null == RtData || RtData.SampleNumber == _lastProcessedSampleNumber || bFailsSequenceCheck)
|
||||
{
|
||||
#if REALTIME_LOGGING
|
||||
if (RtData.SampleNumber == _lastProcessedSampleNumber)
|
||||
{
|
||||
System.Diagnostics.Trace.WriteLine("Same packet #");
|
||||
}
|
||||
#endif
|
||||
RtData = null;
|
||||
return;
|
||||
}
|
||||
|
||||
#if REALTIME_LOGGING
|
||||
System.Diagnostics.Trace.WriteLine($"Processed: {RtData.SampleNumber}");
|
||||
#endif
|
||||
_lastProcessedSampleNumber = RtData.SampleNumber;
|
||||
ChannelData = new short[Channels][];
|
||||
for (var idx = 0; idx < Channels; idx++)
|
||||
{
|
||||
ChannelData[idx] = new short[SamplesReturned];
|
||||
}
|
||||
int insertPt;
|
||||
short adc;
|
||||
|
||||
for (var i = 0; i < RtData.RtData.Length; i++)
|
||||
{
|
||||
//data is in the form of ABC where A is channel 0, and C is channel 2, so we can
|
||||
//figure out the channel idx using modulo
|
||||
var channelIdx = RtData.Channels[i % RtData.Channels.Length];
|
||||
//per other realtime functions, we transform the ushort to a short
|
||||
if (SignedData) { adc = (short)RtData.RtData[i]; }
|
||||
else { adc = (short)(RtData.RtData[i] - 0x8000); }
|
||||
//data is in the form of ABC, so what sample we are on from the first sample can be computed
|
||||
//by dividing by the number of channels in the response
|
||||
insertPt = Convert.ToInt32(Math.Floor((double)i / RtData.Channels.Length));
|
||||
|
||||
ChannelData[channelIdx][insertPt] = adc;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
#if REALTIME_LOGGING
|
||||
System.Diagnostics.Trace.WriteLine(ex.Message);
|
||||
#endif
|
||||
RtData = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// the sample number of the first sample in the data stream
|
||||
/// </summary>
|
||||
public ulong SampleNumber => RtData.SampleNumber;
|
||||
|
||||
public ulong TimeStamp => RtData.TimeStamp;
|
||||
|
||||
public ulong SequenceNumber => RtData.SequenceNumber;
|
||||
/// <summary>
|
||||
/// All channel data for the DAS, note that if a channel is not in realtime
|
||||
/// then it's short data values or not defined
|
||||
/// this is all channels.
|
||||
/// </summary>
|
||||
private short[][] ChannelData { get; set; }
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// gets the sample data for a given channel index
|
||||
/// </summary>
|
||||
/// <param name="zeroBasedChannel"></param>
|
||||
/// <returns></returns>
|
||||
public short[] GetChannelData(int zeroBasedChannel)
|
||||
{
|
||||
return ChannelData[zeroBasedChannel];
|
||||
}
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// this is the total number of channels on the DAS
|
||||
/// this is used to build ChannelData
|
||||
/// </summary>
|
||||
public ushort Channels { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// count of how many samples have been performed
|
||||
/// if data is ABCABCABC, then there are 3 samples returned
|
||||
/// </summary>
|
||||
public int SamplesReturned => RtData?.RtData.Length / RtData?.Channels.Length ?? 0;
|
||||
|
||||
/// <summary>
|
||||
/// We need to override SyncExecute because we don't want to send anything. Instead we just want to
|
||||
/// read whatever is out there. Otherwise this is mostly cut and paste of normal SyncExecute with some streamlining for our specific case.
|
||||
/// </summary>
|
||||
public override void SyncExecute()
|
||||
{
|
||||
// this is a try/finally to handle the ExecuteIsBusy
|
||||
try
|
||||
{
|
||||
// there can be only one!
|
||||
recorder.ExecuteIsBusy = true;
|
||||
|
||||
if (recorder.IsCanceled())
|
||||
{
|
||||
throw new CanceledException();
|
||||
}
|
||||
|
||||
UserCallback = null;
|
||||
UserCallbackData = null;
|
||||
IsSynchronous = true;
|
||||
SyncEvent.Reset();
|
||||
recorder.PseudoExecute(new byte[0], ExecuteCallback, null, IO_Timeout);
|
||||
|
||||
var syncExecTimeout = IO_Timeout;
|
||||
|
||||
try
|
||||
{
|
||||
if (!WaitWithCondition.Wait(SyncEvent, syncExecTimeout,
|
||||
recorder.CancelEvent))
|
||||
{
|
||||
//timeout
|
||||
LogString("SyncExecute: timeout");
|
||||
throw new TimeoutException(MakeLogString("SyncExecute: timeout"));
|
||||
}
|
||||
}
|
||||
catch (WaitWithCondition.ConditionMetException)
|
||||
{
|
||||
throw new CanceledException();
|
||||
}
|
||||
|
||||
// we didn't timeout, check the result
|
||||
switch (ComReport.Result)
|
||||
{
|
||||
case CommunicationConstantsAndEnums.CommunicationResult.Canceled:
|
||||
throw new CanceledException();
|
||||
|
||||
case CommunicationConstantsAndEnums.CommunicationResult.ReceiveOK:
|
||||
if (baseResponse == null)
|
||||
{
|
||||
LogString("SyncExecute: ReceiveOK but response==null!");
|
||||
LogCommand(false);
|
||||
}
|
||||
|
||||
if (baseResponse.Status != DFConstantsAndEnums.CommandStatus.StatusNoError)
|
||||
{
|
||||
// didn't go well
|
||||
var msg = MakeLogString("SyncExecute: response.Status = " + baseResponse.Status);
|
||||
LogCommand(false);
|
||||
if (baseResponse.Status == DFConstantsAndEnums.CommandStatus.StatusInvalidModeForCommand)
|
||||
{
|
||||
throw new CommandException(CommandErrorReason.InvalidMode, msg);
|
||||
}
|
||||
if (baseResponse.Status == DFConstantsAndEnums.CommandStatus.StatusUnimplemented ||
|
||||
baseResponse.Status == DFConstantsAndEnums.CommandStatus.StatusInvalidCommand ||
|
||||
baseResponse.Status == DFConstantsAndEnums.CommandStatus.StatusInvalidCommandType)
|
||||
{
|
||||
throw new NotImplementedException(msg);
|
||||
}
|
||||
var ex = new Exception(msg);
|
||||
ex.Data.Add("Status", baseResponse.Status);
|
||||
APILogger.Log(ex);
|
||||
}
|
||||
|
||||
// everything is fine, let it exit
|
||||
if (LogCommands)
|
||||
{
|
||||
LogCommand(false);
|
||||
}
|
||||
break;
|
||||
|
||||
case CommunicationConstantsAndEnums.CommunicationResult.ReceiveFailed:
|
||||
{
|
||||
var msg = MakeLogString("SyncExecute: ComReport.Result == " + ComReport.Result);
|
||||
LogCommand(false);
|
||||
|
||||
throw new CommandException(CommandErrorReason.ReceiveFailed, msg);
|
||||
}
|
||||
case CommunicationConstantsAndEnums.CommunicationResult.ReceiveTimeout:
|
||||
{
|
||||
var msg = MakeLogString("SyncExecute: ComReport.Result == " + ComReport.Result);
|
||||
LogCommand(false);
|
||||
throw new CommandException(CommandErrorReason.ReceiveFailed, msg);
|
||||
}
|
||||
|
||||
case CommunicationConstantsAndEnums.CommunicationResult.SendFailed:
|
||||
case CommunicationConstantsAndEnums.CommunicationResult.SendTimeout:
|
||||
{
|
||||
var msg = MakeLogString("SyncExecute: ComReport.Result == " + ComReport.Result);
|
||||
LogCommand(false);
|
||||
throw new CommandException(CommandErrorReason.SendFailed, msg);
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
var msg = MakeLogString("SyncExecute: Unknown ComReport.Result == " + ComReport.Result);
|
||||
LogCommand(false);
|
||||
throw new Exception(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
recorder.ExecuteIsBusy = false;
|
||||
ProcessData();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user