init
This commit is contained in:
19
DataPRO/SLICECommands/RealtimeCommands/EndRealtimeMode.cs
Normal file
19
DataPRO/SLICECommands/RealtimeCommands/EndRealtimeMode.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using DTS.Common.ICommunication;
|
||||
|
||||
namespace DTS.DASLib.Command.SLICE.RealtimeCommands
|
||||
{
|
||||
public class EndRealtimeMode : RealtimeCommandBase
|
||||
{
|
||||
protected override Commands _Command => Commands.EndRealtimeMode;
|
||||
|
||||
public EndRealtimeMode(DTS.Common.Interface.DASFactory.ICommunication sock)
|
||||
: base(sock)
|
||||
{
|
||||
}
|
||||
|
||||
public EndRealtimeMode(DTS.Common.Interface.DASFactory.ICommunication sock, int timeoutMillisec)
|
||||
: base(sock, timeoutMillisec)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
134
DataPRO/SLICECommands/RealtimeCommands/GetRealtimeSamples.cs
Normal file
134
DataPRO/SLICECommands/RealtimeCommands/GetRealtimeSamples.cs
Normal file
@@ -0,0 +1,134 @@
|
||||
using System.Collections.Generic;
|
||||
using DTS.Common.Enums.DASFactory;
|
||||
using DTS.Common.ICommunication;
|
||||
|
||||
namespace DTS.DASLib.Command.SLICE.RealtimeCommands
|
||||
{
|
||||
public class GetRealtimeSamples : RealtimeCommandBase, IGetRealtimeSamples
|
||||
{
|
||||
protected override Commands _Command => Commands.GetRealtimeSamples;
|
||||
|
||||
protected ulong _sampleNumber;
|
||||
/// <summary>
|
||||
/// The first sample number of the samples being returned. The remaining samples in
|
||||
/// the response packet will be numbered sequentially and without gaps from this number.
|
||||
/// If 12 samples are returned, and SampleNumber is 83042, the first sample will be number
|
||||
/// 83042, the second 83043, etc.
|
||||
/// </summary>
|
||||
public ulong SampleNumber => _sampleNumber;
|
||||
|
||||
protected ulong _timeStamp;
|
||||
|
||||
public ulong TimeStamp => _timeStamp;
|
||||
|
||||
protected ulong _sequenceNumber;
|
||||
|
||||
public ulong SequenceNumber => _sequenceNumber;
|
||||
|
||||
protected List<short[]> _data;
|
||||
public short[] GetChannelData(int zeroBasedChannel)
|
||||
{
|
||||
return _data[zeroBasedChannel];
|
||||
}
|
||||
|
||||
protected ushort _channels;
|
||||
public ushort Channels
|
||||
{
|
||||
get
|
||||
{
|
||||
if (null == _data) return 0;
|
||||
return _channels;
|
||||
}
|
||||
set => _channels = value;
|
||||
}
|
||||
|
||||
protected int _samplesReturned;
|
||||
/// <summary>
|
||||
/// Contains the number of samples that were returned in this realtime packet. Can be 0!
|
||||
/// </summary>
|
||||
public int SamplesReturned => _samplesReturned;
|
||||
|
||||
public GetRealtimeSamples(DTS.Common.Interface.DASFactory.ICommunication sock)
|
||||
: base(sock, 2000)
|
||||
{
|
||||
// don't need to put anything in here ... its presense
|
||||
// tells the firmware that we support multiple-sample
|
||||
// realtime
|
||||
command.Parameter = new byte[1];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// constructor for GetRealtimeSamples, a class implementing IGetRealtimeSamples
|
||||
/// this class is capable of returning realtime samples
|
||||
/// </summary>
|
||||
/// <param name="sock"></param>
|
||||
/// <param name="TimeoutMillisec"></param>
|
||||
/// <param name="bPolling">whether realtime is using polling or not</param>
|
||||
public GetRealtimeSamples(DTS.Common.Interface.DASFactory.ICommunication sock, int TimeoutMillisec, bool bPolling = false)
|
||||
: base(sock, TimeoutMillisec)
|
||||
{
|
||||
// don't need to put anything in here ... its presense
|
||||
// tells the firmware that we support multiple-sample
|
||||
// realtime
|
||||
command.Parameter = new byte[1];
|
||||
}
|
||||
|
||||
protected override CommandReceiveAction WholePackage()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (response.Status == DFConstantsAndEnums.CommandStatus.StatusNoError)
|
||||
{
|
||||
// Figure out the number of samples returned
|
||||
var samplesReturned = response.ParameterLength / (_channels * 2 + 8);
|
||||
_samplesReturned = samplesReturned;
|
||||
var unsignedData = new List<ushort[]>(_channels);
|
||||
_data = new List<short[]>(_channels);
|
||||
|
||||
// Grab the sample number
|
||||
if (response.ParameterLength > 0)
|
||||
{
|
||||
response.GetParameter(0, out _sampleNumber);
|
||||
}
|
||||
|
||||
// Create the data arrays by channel
|
||||
for (var i = 0; i < _channels; i++)
|
||||
{
|
||||
unsignedData.Add(new ushort[samplesReturned]);
|
||||
_data.Add(new short[samplesReturned]);
|
||||
}
|
||||
|
||||
// Grab the data
|
||||
for (var sample = 0; sample < samplesReturned; sample++)
|
||||
{
|
||||
// Data order for a 9 channel stack
|
||||
// 7 8 9 4 5 6 1 2 3 7 8 9 4 5 6 1 2 3 etc.
|
||||
for (var channel = 0; channel < _channels; channel++)
|
||||
{
|
||||
var sliceNumber = channel / 3;
|
||||
var sliceOffset = (_channels / 3 - sliceNumber - 1) * 3;
|
||||
var channelInSlice = channel % 3;
|
||||
var offset = sliceOffset + channelInSlice;
|
||||
|
||||
response.GetParameter(8 + sample * (8 + _channels * 2) + 2 * offset, out ushort val);
|
||||
unsignedData[channel][sample] = val;
|
||||
_data[channel][sample] = (short)(unsignedData[channel][sample] - 0x8000);
|
||||
}
|
||||
}
|
||||
return CommandReceiveAction.StopReceiving;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
_samplesReturned = 0;
|
||||
}
|
||||
return CommandReceiveAction.StopReceiving;
|
||||
}
|
||||
|
||||
public override void ResponseToString(ref List<List<string>> lines)
|
||||
{
|
||||
base.ResponseToString(ref lines);
|
||||
lines.Add(new List<string> { $"Sample number: {ResponseStatus}, Samples returned: {SampleNumber}, Time Stamp returned: {TimeStamp}, Sequence Number returned: {SequenceNumber}" });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
using System.Collections.Generic;
|
||||
using DTS.Common.Enums.DASFactory;
|
||||
using DTS.Common.ICommunication;
|
||||
|
||||
namespace DTS.DASLib.Command.SLICE.RealtimeCommands
|
||||
{
|
||||
public class GetRealtimeSamplesSLICE2 : GetRealtimeSamples
|
||||
{
|
||||
public GetRealtimeSamplesSLICE2(DTS.Common.Interface.DASFactory.ICommunication sock)
|
||||
: base(sock) { LogCommands = false; }
|
||||
|
||||
public GetRealtimeSamplesSLICE2(DTS.Common.Interface.DASFactory.ICommunication sock, int timeoutMillisec)
|
||||
: base(sock, timeoutMillisec) { LogCommands = false; }
|
||||
|
||||
protected override CommandReceiveAction WholePackage()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (response.Status == DFConstantsAndEnums.CommandStatus.StatusNoError)
|
||||
{
|
||||
// Figure out the number of samples returned
|
||||
var samplesReturned = (response.ParameterLength - 8) / (_channels * 2);
|
||||
_samplesReturned = samplesReturned;
|
||||
_data = new List<short[]>(_channels);
|
||||
|
||||
// Grab the sample number
|
||||
if (response.ParameterLength > 0)
|
||||
{
|
||||
response.GetParameter(0, out _sampleNumber);
|
||||
}
|
||||
|
||||
// Create the data arrays by channel
|
||||
for (var i = 0; i < _channels; i++)
|
||||
{
|
||||
_data.Add(new short[samplesReturned]);
|
||||
}
|
||||
|
||||
// Grab the data
|
||||
var parameter = 8;
|
||||
for (var sample = 0; sample < samplesReturned; sample++)
|
||||
{
|
||||
for (var channel = 0; channel < _channels; channel++)
|
||||
{
|
||||
response.GetParameter(parameter, out ushort val);
|
||||
//changed from unsigned to signed
|
||||
_data[channel][sample] = (short)((((val & 0x00FF) << 8) | ((val >> 8) & 0x00FF)) + 0x8000);
|
||||
|
||||
parameter += 2;
|
||||
}
|
||||
}
|
||||
return CommandReceiveAction.StopReceiving;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
_samplesReturned = 0;
|
||||
}
|
||||
return CommandReceiveAction.StopReceiving;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
using System.Collections.Generic;
|
||||
using DTS.Common.Enums.DASFactory;
|
||||
using DTS.Common.ICommunication;
|
||||
|
||||
namespace DTS.DASLib.Command.SLICE.RealtimeCommands
|
||||
{
|
||||
public class GetRealtimeSamplesSLICE6 : GetRealtimeSamples
|
||||
{
|
||||
public GetRealtimeSamplesSLICE6(DTS.Common.Interface.DASFactory.ICommunication sock)
|
||||
: base(sock) { }
|
||||
|
||||
public GetRealtimeSamplesSLICE6(DTS.Common.Interface.DASFactory.ICommunication sock, int timeoutMillisec)
|
||||
: base(sock, timeoutMillisec) { }
|
||||
|
||||
protected override CommandReceiveAction WholePackage()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (response.Status == DFConstantsAndEnums.CommandStatus.StatusNoError)
|
||||
{
|
||||
// Figure out the number of samples returned
|
||||
var samplesReturned = (response.ParameterLength - 8) / (_channels * 2);
|
||||
_samplesReturned = samplesReturned;
|
||||
_data = new List<short[]>(_channels);
|
||||
|
||||
// Grab the sample number
|
||||
if (response.ParameterLength > 0)
|
||||
{
|
||||
response.GetParameter(0, out _sampleNumber);
|
||||
}
|
||||
|
||||
// Create the data arrays by channel
|
||||
for (int i = 0; i < _channels; i++)
|
||||
{
|
||||
_data.Add(new short[samplesReturned]);
|
||||
}
|
||||
|
||||
// Grab the data
|
||||
var parameter = 8;
|
||||
for (var sample = 0; sample < samplesReturned; sample++)
|
||||
{
|
||||
for (var channel = 0; channel < _channels; channel++)
|
||||
{
|
||||
response.GetParameter(parameter, out ushort val);
|
||||
//Slice6 data is signed data. No need to convert.
|
||||
_data[channel][sample] = (short)((((val & 0x00FF) << 8) | ((val >> 8) & 0x00FF)));
|
||||
|
||||
parameter += 2;
|
||||
}
|
||||
}
|
||||
return CommandReceiveAction.StopReceiving;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
_samplesReturned = 0;
|
||||
}
|
||||
return CommandReceiveAction.StopReceiving;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using DTS.Common.Enums.DASFactory;
|
||||
using DTS.Common.Interface.DASFactory;
|
||||
|
||||
namespace DTS.DASLib.Command.SLICE.RealtimeCommands
|
||||
{
|
||||
public class GetRealtimeSamplesTSRAIR : GetRealtimeSamples
|
||||
{
|
||||
public GetRealtimeSamplesTSRAIR(ICommunication sock)
|
||||
: base(sock) { }
|
||||
|
||||
public GetRealtimeSamplesTSRAIR(ICommunication sock, int TimeoutMillisec)
|
||||
: base(sock, TimeoutMillisec) { }
|
||||
|
||||
protected List<UInt64> _timestamps;
|
||||
public List<UInt64> Timestamps => _timestamps;
|
||||
|
||||
|
||||
protected override CommandReceiveAction WholePackage()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (response.Status == DFConstantsAndEnums.CommandStatus.StatusNoError)
|
||||
{
|
||||
// Figure out the number of samples returned
|
||||
int samples_returned = (response.ParameterLength - 8) / (_channels * 2);
|
||||
_samplesReturned = samples_returned;
|
||||
int data_bytes_returned = samples_returned * _channels * 2;
|
||||
_data = new List<short[]>(_channels);
|
||||
|
||||
// Grab the sample number
|
||||
if (response.ParameterLength > 0)
|
||||
{
|
||||
response.GetParameter(0, out _sampleNumber);
|
||||
}
|
||||
|
||||
// Create the data arrays by channel
|
||||
for (int i = 0; i < _channels; i++)
|
||||
{
|
||||
_data.Add(new short[samples_returned]);
|
||||
}
|
||||
|
||||
// Grab the data
|
||||
int parameter = 8;
|
||||
for (int sample = 0; sample < samples_returned; sample++)
|
||||
{
|
||||
for (int channel = 0; channel < _channels; channel++)
|
||||
{
|
||||
response.GetParameter(parameter, out ushort uval);
|
||||
|
||||
//TSRAIR data is unsigned data. Convert sign, no need to normalize
|
||||
_data[channel][sample] = (short)((((uval & 0x00FF) << 8) | ((uval >> 8) & 0x00FF)));
|
||||
parameter += 2;
|
||||
}
|
||||
}
|
||||
return CommandReceiveAction.StopReceiving;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
_samplesReturned = 0;
|
||||
}
|
||||
return CommandReceiveAction.StopReceiving;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
namespace DTS.DASLib.Command.SLICE.RealtimeCommands
|
||||
{
|
||||
/// <summary>
|
||||
/// this interface describes objects that are able to get realtime samples from
|
||||
/// SLICE devices, the implementation differs based on whether it's using streaming
|
||||
/// or is a SLICE6 device, etc, but all still operate through the same interface
|
||||
/// </summary>
|
||||
public interface IGetRealtimeSamples
|
||||
{
|
||||
/// <summary>
|
||||
/// returns the sample number for the first sample, with all samples after that being consecutive
|
||||
/// </summary>
|
||||
ulong SampleNumber { get; }
|
||||
ulong TimeStamp { get; }
|
||||
|
||||
ulong SequenceNumber { get; }
|
||||
/// <summary>
|
||||
/// returns all samples for the given channel
|
||||
/// </summary>
|
||||
/// <param name="zeroBasedChannel"></param>
|
||||
/// <returns></returns>
|
||||
short[] GetChannelData(int zeroBasedChannel);
|
||||
/// <summary>
|
||||
/// right now this holds the total channel count for the unit
|
||||
/// </summary>
|
||||
ushort Channels { get; set; }
|
||||
/// <summary>
|
||||
/// the count of samples per channel that were returned
|
||||
/// if return value is 10 and there's 10 channels, there's 100 samples, or 10 samples per 10 channels
|
||||
/// </summary>
|
||||
int SamplesReturned { get; }
|
||||
/// <summary>
|
||||
/// whether to log the command or not
|
||||
/// (not applicable on streaming mode as no command is executed)
|
||||
/// default is normally false otherwise the log will be spammy
|
||||
/// </summary>
|
||||
bool LogCommands { get; set; }
|
||||
/// <summary>
|
||||
/// this is the execute that retrieves samples
|
||||
/// </summary>
|
||||
void SyncExecute();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using DTS.Common.ICommunication;
|
||||
|
||||
namespace DTS.DASLib.Command.SLICE.RealtimeCommands
|
||||
{
|
||||
public abstract class RealtimeCommandBase : CommandBase
|
||||
{
|
||||
protected enum Commands
|
||||
{
|
||||
Reserved = 0x00,
|
||||
StartRealtimeMode = 0x01,
|
||||
EndRealtimeMode = 0x02,
|
||||
GetRealtimeSamples = 0x03,
|
||||
RetrieveSingleSample = 0x04,
|
||||
RetrieveSampleAverage = 0x05,
|
||||
RetrieveInternalOffsetSampleAvg = 0x06,
|
||||
StartRealtimeStreamingMode = 0x07,
|
||||
GetRealtimeStreamSamples = 0x08,
|
||||
StartTimeStampStreamMode = 0x09, // CMDRT_START_TIMESTAMPED_STREAM_MODE = 0x09: Start IRIG realtime stream with destination IP address from system attribute
|
||||
GetTimeStampStreamSamples = 0x0A, // CMDRT_TIMESTAMPED_STREAM_SAMPLES = 0x0A: streaming data from cmd 0x09
|
||||
ChannelTappedTest = 0x0B,
|
||||
I106StreamConfigSet = 0x0C, // FB15313 add streaming config options
|
||||
I106StreamConfigGet = 0x0D,
|
||||
};
|
||||
|
||||
protected abstract Commands _Command { get; }
|
||||
|
||||
protected RealtimeCommandBase(DTS.Common.Interface.DASFactory.ICommunication sock)
|
||||
: base(sock)
|
||||
{
|
||||
command.Type = CommandPacket.CommandType.Realtime;
|
||||
command.SetCommand((byte)_Command, _Command.ToString());
|
||||
}
|
||||
|
||||
protected RealtimeCommandBase(DTS.Common.Interface.DASFactory.ICommunication sock, int timeoutMillisec)
|
||||
: base(sock, timeoutMillisec)
|
||||
{
|
||||
command.Type = CommandPacket.CommandType.Realtime;
|
||||
command.SetCommand((byte)_Command, _Command.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
166
DataPRO/SLICECommands/RealtimeCommands/RealtimeStreamDecoder.cs
Normal file
166
DataPRO/SLICECommands/RealtimeCommands/RealtimeStreamDecoder.cs
Normal file
@@ -0,0 +1,166 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace DTS.DASLib.Command.SLICE.RealtimeCommands
|
||||
{
|
||||
/// <summary>
|
||||
/// this class handles decoding a SPS realtime stream packet.
|
||||
/// </summary>
|
||||
public class RealtimeStreamDecoder
|
||||
{
|
||||
/* per lp,
|
||||
* Byte index:
|
||||
0 Sync = CMD_SYNC_BYTE = 0xFA
|
||||
1 type = CMD_TYPE_REALTIME = 0x08
|
||||
2 command = CMDRT_REALTIME_STREAM_SAMPLE_PUSH = 0x08
|
||||
3 status = STATUS_ERROR_NONE = 0x00
|
||||
4 group = 0x00;
|
||||
5 id = 0x00;
|
||||
6-7 datalength
|
||||
8-9 sequence number
|
||||
10-11 headerCRC16
|
||||
12-13 dataCRC16 (currently not used)
|
||||
… payload
|
||||
8-byte uint64_t Timestamp (for first sample in payload.)
|
||||
4-byte uint32_t Channel list (bit set = channel present. Bit clear = channel not present.
|
||||
8-byte uint64_t Sample Number (starting with first ADC scan in payload)
|
||||
Number of ADC-Scan sample = (datalength – 20) / (channellist*2)
|
||||
*/
|
||||
/// <summary>
|
||||
/// this is the order of information in the packet
|
||||
/// the enum value is the offset in bytes from the start of the packet
|
||||
/// </summary>
|
||||
public enum ByteIndex
|
||||
{
|
||||
Sync = 0,
|
||||
Type = 1,
|
||||
Command = 2,
|
||||
Status = 3,
|
||||
Group = 4,
|
||||
Id = 5,
|
||||
DataLength = 6,
|
||||
SequenceNumber = 8,
|
||||
HeaderCRC = 10,
|
||||
DataCRC = 12,
|
||||
TimeStamp = 14,
|
||||
ChannelList = 22,
|
||||
SampleNumber = 26,
|
||||
DataStart = 34
|
||||
}
|
||||
|
||||
public ushort SequenceNumber { get; }
|
||||
public ulong TimeStamp { get; }
|
||||
public ulong SampleNumber { get; }
|
||||
public int[] Channels { get; }
|
||||
|
||||
/// <summary>
|
||||
/// this is all data, not transformed into (short) yet, which requires 2's compliment
|
||||
/// it is also for all channels in the realtime stream
|
||||
/// </summary>
|
||||
public ushort[] RtData { get; }
|
||||
|
||||
/// <summary>
|
||||
/// constructors and decodes byte stream
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="bytes"></param>
|
||||
public RealtimeStreamDecoder(IReadOnlyList<byte> bytes)
|
||||
{
|
||||
if (bytes.Count > (int)ByteIndex.DataStart)
|
||||
{
|
||||
TimeStamp = GetULong(bytes, (int)ByteIndex.TimeStamp);
|
||||
SequenceNumber = GetUShort(bytes, (int)ByteIndex.SequenceNumber);
|
||||
SampleNumber = GetULong(bytes, (int)ByteIndex.SampleNumber);
|
||||
RtData = new ushort[(bytes.Count - (int)ByteIndex.DataStart) / 2];
|
||||
var uintChannelList = GetUINT(bytes, (int)ByteIndex.ChannelList);
|
||||
var channels = new List<int>();
|
||||
var ba = new BitArray(BitConverter.GetBytes(uintChannelList));
|
||||
for (var i = 0; i < ba.Length; i++)
|
||||
{
|
||||
if (ba.Get(i))
|
||||
{
|
||||
channels.Add(i);
|
||||
}
|
||||
}
|
||||
|
||||
Channels = channels.ToArray();
|
||||
for (var idx = 0; idx < RtData.Length; idx++)
|
||||
{
|
||||
//all the data is stored as uint16 with channel order so
|
||||
// AABBCCDDEEFF, so split out the ushorts
|
||||
RtData[idx] = GetRTUShort(bytes, (int)(ByteIndex.DataStart + idx * 2));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RtData = new ushort[0];
|
||||
SampleNumber = 0;
|
||||
Channels = new int[0];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// returns a ulong given a sequence of bytes and a starting offset in bytes
|
||||
/// that the ulong starts at
|
||||
/// SLICE header bytes are sent in reverse order so we have to fix them first
|
||||
/// </summary>
|
||||
/// <param name="bytes"></param>
|
||||
/// <param name="offset"></param>
|
||||
/// <returns></returns>
|
||||
private static ulong GetULong(IReadOnlyList<byte> bytes, int offset)
|
||||
{
|
||||
return (ulong)bytes[offset + 7] << 0 |
|
||||
(ulong)bytes[offset + 6] << 8 |
|
||||
(ulong)bytes[offset + 5] << 16 |
|
||||
(ulong)bytes[offset + 4] << 24 |
|
||||
(ulong)bytes[offset + 3] << 32 |
|
||||
(ulong)bytes[offset + 2] << 40 |
|
||||
(ulong)bytes[offset + 1] << 48 |
|
||||
(ulong)bytes[offset + 0] << 56;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// gets a ushort from the response parameters
|
||||
/// SLICE header bytes are sent in reverse order, so we have to fix
|
||||
/// them first
|
||||
/// </summary>
|
||||
/// <param name="bytes"></param>
|
||||
/// <param name="offset"></param>
|
||||
/// <returns></returns>
|
||||
private static uint GetUINT(IReadOnlyList<byte> bytes, int offset)
|
||||
{
|
||||
return (uint)bytes[offset + 3] << 0 |
|
||||
(uint)bytes[offset + 2] << 8 |
|
||||
(uint)bytes[offset + 1] << 16 |
|
||||
(uint)bytes[offset + 0] << 32;
|
||||
}
|
||||
/// <summary>
|
||||
/// returns a ushort given a sequence of bytes and a starting offset in bytes
|
||||
/// SLICE header bytes are sent in reverse order, so we have to fix the byte
|
||||
/// order first
|
||||
/// </summary>
|
||||
/// <param name="bytes"></param>
|
||||
/// <param name="offset"></param>
|
||||
/// <returns></returns>
|
||||
private static ushort GetUShort(IReadOnlyList<byte> bytes, int offset)
|
||||
{
|
||||
return (ushort)(bytes[offset + 1] |
|
||||
bytes[offset + 0] << 8);
|
||||
}
|
||||
/// <summary>
|
||||
/// returns a ushort given a sequence of bytes and a starting offset in bytes
|
||||
/// note that DATA is not in reverse order unlike the header, so we
|
||||
/// DON'T need to fix the order
|
||||
/// </summary>
|
||||
/// <param name="bytes"></param>
|
||||
/// <param name="offset"></param>
|
||||
/// <returns></returns>
|
||||
private static ushort GetRTUShort(IReadOnlyList<byte> bytes, int offset)
|
||||
{
|
||||
return (ushort)(bytes[offset + 0] |
|
||||
bytes[offset + 1] << 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
using System.Collections.Generic;
|
||||
using DTS.Common.Enums.DASFactory;
|
||||
using DTS.Common.ICommunication;
|
||||
using DTS.Common.Utilities;
|
||||
|
||||
namespace DTS.DASLib.Command.SLICE.RealtimeCommands
|
||||
{
|
||||
public class RetrieveSampleAverage : RealtimeCommandBase
|
||||
{
|
||||
protected override Commands _Command => Commands.RetrieveSampleAverage;
|
||||
|
||||
const int SAMPLES_POSITION = 0;
|
||||
const int DATA_POSITION = 2;
|
||||
|
||||
private ushort _samples;
|
||||
public ushort Samples
|
||||
{
|
||||
set { _samples = value; command.SetParameter(SAMPLES_POSITION, _samples); }
|
||||
get => _samples;
|
||||
}
|
||||
|
||||
public short[] _data;
|
||||
public short GetChannelData(int zeroBasedChannel)
|
||||
{
|
||||
return _data[zeroBasedChannel];
|
||||
}
|
||||
|
||||
public ushort Channels
|
||||
{
|
||||
get
|
||||
{
|
||||
if (null == _data) return 0;
|
||||
return (ushort)_data.Length;
|
||||
}
|
||||
}
|
||||
|
||||
public RetrieveSampleAverage(DTS.Common.Interface.DASFactory.ICommunication sock)
|
||||
: base(sock)
|
||||
{
|
||||
command.Parameter = new byte[DATA_POSITION];
|
||||
}
|
||||
|
||||
public RetrieveSampleAverage(DTS.Common.Interface.DASFactory.ICommunication sock, int timeoutMillisec)
|
||||
: base(sock, timeoutMillisec)
|
||||
{
|
||||
command.Parameter = new byte[DATA_POSITION];
|
||||
}
|
||||
|
||||
protected override CommandReceiveAction WholePackage()
|
||||
{
|
||||
if (response.Status != DFConstantsAndEnums.CommandStatus.StatusNoError)
|
||||
return CommandReceiveAction.StopReceiving;
|
||||
// Figure out how many samples were actually used to compute the means --
|
||||
// we may have requested more than the recorder has RAM to store
|
||||
response.GetParameter(SAMPLES_POSITION, out _samples);
|
||||
|
||||
// Figure out the number of channels returned and grab the means
|
||||
var numChannels = (response.Parameter.Length - 2) / 2;
|
||||
_data = new short[numChannels];
|
||||
for (var i = 0; i < numChannels; i++)
|
||||
{
|
||||
response.GetParameter(2 * i + DATA_POSITION, out _data[i]);
|
||||
}
|
||||
|
||||
return CommandReceiveAction.StopReceiving;
|
||||
}
|
||||
|
||||
public override void CommandToString(ref List<List<string>> lines)
|
||||
{
|
||||
base.CommandToString(ref lines);
|
||||
lines.Add(new List<string> { $"Samples Requested: {Samples}" });
|
||||
}
|
||||
|
||||
public override void ResponseToString(ref List<List<string>> lines)
|
||||
{
|
||||
base.ResponseToString(ref lines);
|
||||
lines.Add(new List<string>
|
||||
{
|
||||
$"Channels: {Channels}, Samples Used: {Samples}, Data: {ArrayToString.ArrayObjectToString(_data)}"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
using System.Collections.Generic;
|
||||
using DTS.Common.Enums.DASFactory;
|
||||
using DTS.Common.ICommunication;
|
||||
using DTS.Common.Utilities;
|
||||
|
||||
namespace DTS.DASLib.Command.SLICE.RealtimeCommands
|
||||
{
|
||||
public class RetrieveSingleSample : RealtimeCommandBase
|
||||
{
|
||||
protected override Commands _Command => Commands.RetrieveSingleSample;
|
||||
|
||||
public short[] _data;
|
||||
public short GetChannelData(int zeroBasedChannel)
|
||||
{
|
||||
return _data[zeroBasedChannel];
|
||||
}
|
||||
|
||||
public ushort Channels
|
||||
{
|
||||
get
|
||||
{
|
||||
if (null == _data) return 0;
|
||||
return (ushort)_data.Length;
|
||||
}
|
||||
}
|
||||
|
||||
public RetrieveSingleSample(DTS.Common.Interface.DASFactory.ICommunication sock)
|
||||
: base(sock)
|
||||
{
|
||||
}
|
||||
|
||||
public RetrieveSingleSample(DTS.Common.Interface.DASFactory.ICommunication sock, int timeoutMillisec)
|
||||
: base(sock, timeoutMillisec)
|
||||
{
|
||||
}
|
||||
|
||||
protected override CommandReceiveAction WholePackage()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (response.Status == DFConstantsAndEnums.CommandStatus.StatusNoError)
|
||||
{
|
||||
// Figure out the number of samples returned
|
||||
var unsignedData = new ushort[response.Parameter.Length / 2];
|
||||
_data = new short[response.Parameter.Length / 2];
|
||||
for (var i = 0; i < response.Parameter.Length / 2; i++)
|
||||
{
|
||||
response.GetParameter(2 * i, out unsignedData[i]);
|
||||
if (unsignedData[i] > 32768)
|
||||
{
|
||||
_data[i] = (short)(unsignedData[i] - 65536);
|
||||
}
|
||||
else
|
||||
{
|
||||
_data[i] = (short)unsignedData[i];
|
||||
}
|
||||
}
|
||||
return CommandReceiveAction.StopReceiving;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
return CommandReceiveAction.StopReceiving;
|
||||
}
|
||||
|
||||
public override void ResponseToString(ref List<List<string>> lines)
|
||||
{
|
||||
base.ResponseToString(ref lines);
|
||||
lines.Add(new List<string> { $"Channels: {Channels}, Data: {ArrayToString.ArrayObjectToString(_data)}" });
|
||||
}
|
||||
}
|
||||
}
|
||||
38
DataPRO/SLICECommands/RealtimeCommands/StartRealtimeMode.cs
Normal file
38
DataPRO/SLICECommands/RealtimeCommands/StartRealtimeMode.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using DTS.Common.ICommunication;
|
||||
|
||||
namespace DTS.DASLib.Command.SLICE.RealtimeCommands
|
||||
{
|
||||
public class StartRealtimeMode : RealtimeCommandBase
|
||||
{
|
||||
private bool _bSupportsMultipleSampleRealtime = true;
|
||||
public bool SupportsMultipleSampleRealtime
|
||||
{
|
||||
get => _bSupportsMultipleSampleRealtime;
|
||||
set
|
||||
{
|
||||
_bSupportsMultipleSampleRealtime = value;
|
||||
if (value) { command.Parameter = new byte[1]; }
|
||||
else { command.Parameter = new byte[0]; }
|
||||
}
|
||||
}
|
||||
protected override Commands _Command => Commands.StartRealtimeMode;
|
||||
|
||||
public StartRealtimeMode(DTS.Common.Interface.DASFactory.ICommunication sock)
|
||||
: base(sock)
|
||||
{
|
||||
// don't need to put anything in here ... its presense
|
||||
// tells the firmware that we support multiple-sample
|
||||
// realtime
|
||||
command.Parameter = new byte[1];
|
||||
}
|
||||
|
||||
public StartRealtimeMode(DTS.Common.Interface.DASFactory.ICommunication sock, int timeoutMillisec)
|
||||
: base(sock, timeoutMillisec)
|
||||
{
|
||||
// don't need to put anything in here ... its presense
|
||||
// tells the firmware that we support multiple-sample
|
||||
// realtime
|
||||
command.Parameter = new byte[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
using DTS.Common.ICommunication;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace DTS.DASLib.Command.SLICE.RealtimeCommands
|
||||
{
|
||||
public class StartTimeStampStreamMode : RealtimeCommandBase
|
||||
{
|
||||
private bool _bSupportsIrigTimeStampSampleRealtime = true;
|
||||
public bool SupportsMultipleSampleRealtime
|
||||
{
|
||||
get { return _bSupportsIrigTimeStampSampleRealtime; }
|
||||
set
|
||||
{
|
||||
_bSupportsIrigTimeStampSampleRealtime = value;
|
||||
if (value) { command.Parameter = new byte[1]; }
|
||||
else { command.Parameter = new byte[0]; }
|
||||
}
|
||||
}
|
||||
protected override Commands _Command => Commands.StartTimeStampStreamMode;
|
||||
|
||||
public StartTimeStampStreamMode(DTS.Common.Interface.DASFactory.ICommunication sock)
|
||||
: base(sock)
|
||||
{
|
||||
MinimumProtocolVersion = sock.GetMinProto(Common.Enums.DASFactory.DFConstantsAndEnums.ProtocolLimitedCommands.UDPRealtimeStream);
|
||||
}
|
||||
|
||||
public StartTimeStampStreamMode(DTS.Common.Interface.DASFactory.ICommunication sock, int timeoutMillisec)
|
||||
: base(sock, timeoutMillisec)
|
||||
{
|
||||
MinimumProtocolVersion = sock.GetMinProto(Common.Enums.DASFactory.DFConstantsAndEnums.ProtocolLimitedCommands.UDPRealtimeStream);
|
||||
}
|
||||
private byte[] _paramsToSend;
|
||||
public byte[] ParamsToSend
|
||||
{
|
||||
get => _paramsToSend;
|
||||
set
|
||||
{
|
||||
_paramsToSend = value;
|
||||
command.Parameter = new byte[value.Length];
|
||||
for (var i = 0; i < value.Length; i++)
|
||||
{
|
||||
command.SetParameter(i, _paramsToSend[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
public override void CommandToString(ref List<List<string>> list)
|
||||
{
|
||||
base.CommandToString(ref list);
|
||||
list.Add(new List<string>(new[] { $"ParamsToSend: {System.Text.Encoding.UTF8.GetString(ParamsToSend)}" }));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// this puts the firmware into realtime streaming mode
|
||||
/// in this mode the firmware will constantly send out realtime data
|
||||
/// </summary>
|
||||
public class StartRealtimeStreamingMode : RealtimeCommandBase
|
||||
{
|
||||
protected override Commands _Command => Commands.StartRealtimeStreamingMode;
|
||||
|
||||
public StartRealtimeStreamingMode(DTS.Common.Interface.DASFactory.ICommunication sock, byte[] channelList)
|
||||
: base(sock)
|
||||
{
|
||||
ChannelList = channelList;
|
||||
}
|
||||
|
||||
public StartRealtimeStreamingMode(DTS.Common.Interface.DASFactory.ICommunication sock, int timeoutMillisec, byte[] channelList)
|
||||
: base(sock, timeoutMillisec)
|
||||
{
|
||||
ChannelList = channelList;
|
||||
}
|
||||
|
||||
private byte[] _channelList;
|
||||
/// <summary>
|
||||
/// channels to collect data on, each channel should be represented as a byte
|
||||
/// </summary>
|
||||
public byte[] ChannelList
|
||||
{
|
||||
get => _channelList;
|
||||
set
|
||||
{
|
||||
_channelList = value;
|
||||
command.Parameter = new byte[value.Length];
|
||||
for (var i = 0; i < value.Length; i++)
|
||||
{
|
||||
command.SetParameter(i, value[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
307
DataPRO/SLICECommands/RealtimeCommands/StreamConfigUDP.cs
Normal file
307
DataPRO/SLICECommands/RealtimeCommands/StreamConfigUDP.cs
Normal file
@@ -0,0 +1,307 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using DTS.Common.Enums.DASFactory;
|
||||
|
||||
namespace DTS.DASLib.Command.SLICE.RealtimeCommands
|
||||
{
|
||||
/// <summary>
|
||||
/// these classes was ported from FWTU
|
||||
/// </summary>
|
||||
public class StreamConfigUDPGet : RealtimeCommandBase
|
||||
{
|
||||
private const int COMMAND_PAYLOAD_SIZE = 65;
|
||||
|
||||
protected override Commands _Command => Commands.I106StreamConfigGet;
|
||||
|
||||
private byte _stream_profile_number;
|
||||
private byte[] _udpipport;
|
||||
private ushort _timechannelid;
|
||||
private ushort _datachannelid;
|
||||
private uint _tmnsPcmSubFrameId; // _tmns0;
|
||||
private uint _tmnsMsgId; // _tmns1;
|
||||
private uint _tmnsPcmMinorPerMajor; // _tmns2;
|
||||
private uint _tmnsTmatsPortNumber; // _tmns3;
|
||||
private uint _ienaUdpSrcPortNumber; // _tmns4;
|
||||
private uint _udpRsv1; // _tmns5;
|
||||
private uint _udpRsv2; // _tmns6;
|
||||
private uint _udpRsv3; // _tmns7;
|
||||
|
||||
public byte Stream_Profile_Number
|
||||
{
|
||||
get => _stream_profile_number;
|
||||
set { _stream_profile_number = value; command.SetParameter(0, _stream_profile_number); }
|
||||
}
|
||||
public byte[] UdpIpPort
|
||||
{
|
||||
get { return _udpipport; }
|
||||
set { _udpipport = value; command.SetParameter(1, _udpipport); }
|
||||
}
|
||||
public ushort TimeChannelID
|
||||
{
|
||||
get => _timechannelid;
|
||||
set { _timechannelid = value; command.SetParameter(29, _timechannelid); }
|
||||
}
|
||||
|
||||
public ushort DataChannelID
|
||||
{
|
||||
get => _datachannelid;
|
||||
set { _datachannelid = value; command.SetParameter(31, _datachannelid); }
|
||||
}
|
||||
|
||||
public uint TMNS_PCMSUBFRAMEID
|
||||
{
|
||||
get => _tmnsPcmSubFrameId;
|
||||
set
|
||||
{
|
||||
_tmnsPcmSubFrameId = value;
|
||||
command.SetParameter(33, _tmnsPcmSubFrameId);
|
||||
}
|
||||
}
|
||||
public uint TMNS_MSGID
|
||||
{
|
||||
get { return _tmnsMsgId; }
|
||||
set
|
||||
{
|
||||
_tmnsMsgId = value; command.SetParameter(37, _tmnsMsgId);
|
||||
}
|
||||
}
|
||||
public uint TMNS_PCMINORPERMAJOR
|
||||
{
|
||||
get => _tmnsPcmMinorPerMajor;
|
||||
set
|
||||
{
|
||||
_tmnsPcmMinorPerMajor = value; command.SetParameter(41, _tmnsPcmMinorPerMajor);
|
||||
}
|
||||
}
|
||||
public uint TMNS_TMATSPORTNUMBER
|
||||
{
|
||||
get => _tmnsTmatsPortNumber;
|
||||
set
|
||||
{
|
||||
_tmnsTmatsPortNumber = value; command.SetParameter(45, _tmnsTmatsPortNumber);
|
||||
}
|
||||
}
|
||||
public uint IENAUDP_PortNumber
|
||||
{
|
||||
get => _ienaUdpSrcPortNumber;
|
||||
set
|
||||
{
|
||||
_ienaUdpSrcPortNumber = value; command.SetParameter(49, _ienaUdpSrcPortNumber);
|
||||
}
|
||||
}
|
||||
public uint TMNS5
|
||||
{
|
||||
get => _udpRsv1;
|
||||
set
|
||||
{
|
||||
_udpRsv1 = value; command.SetParameter(53, _udpRsv1);
|
||||
}
|
||||
}
|
||||
public uint TMNS6
|
||||
{
|
||||
get => _udpRsv2;
|
||||
set
|
||||
{
|
||||
_udpRsv2 = value; command.SetParameter(57, _udpRsv2);
|
||||
}
|
||||
}
|
||||
public uint TMNS7
|
||||
{
|
||||
get => _udpRsv3;
|
||||
set
|
||||
{
|
||||
_udpRsv3 = value; command.SetParameter(61, _udpRsv3);
|
||||
}
|
||||
}
|
||||
public StreamConfigUDPGet(Common.Interface.DASFactory.ICommunication sock)
|
||||
: base(sock)
|
||||
{
|
||||
command.Parameter = new byte[COMMAND_PAYLOAD_SIZE];
|
||||
}
|
||||
|
||||
public StreamConfigUDPGet(Common.Interface.DASFactory.ICommunication sock, int TimeoutMillisec)
|
||||
: base(sock, TimeoutMillisec)
|
||||
{
|
||||
command.Parameter = new byte[COMMAND_PAYLOAD_SIZE];
|
||||
}
|
||||
protected override CommandReceiveAction WholePackage()
|
||||
{
|
||||
if (response.Status == DFConstantsAndEnums.CommandStatus.StatusNoError)
|
||||
{
|
||||
if (response.ParameterLength > 0)
|
||||
{
|
||||
response.GetParameter(0, out _stream_profile_number);
|
||||
_udpipport = new byte[29];
|
||||
for (int i = 0; i < 28; i++)
|
||||
{
|
||||
response.GetParameter(1 + i, out _udpipport[i]);
|
||||
}
|
||||
response.GetParameter(29, out _timechannelid);
|
||||
response.GetParameter(31, out _datachannelid);
|
||||
response.GetParameter(33, out _tmnsPcmSubFrameId);
|
||||
response.GetParameter(37, out _tmnsMsgId);
|
||||
response.GetParameter(41, out _tmnsPcmMinorPerMajor);
|
||||
response.GetParameter(45, out _tmnsTmatsPortNumber);
|
||||
response.GetParameter(49, out _ienaUdpSrcPortNumber);
|
||||
response.GetParameter(53, out _udpRsv1);
|
||||
response.GetParameter(57, out _udpRsv2);
|
||||
response.GetParameter(61, out _udpRsv3);
|
||||
}
|
||||
else { _stream_profile_number = 0; }
|
||||
}
|
||||
return CommandReceiveAction.StopReceiving;
|
||||
}
|
||||
|
||||
public override void ResponseToString(ref List<List<string>> lines)
|
||||
{
|
||||
base.ResponseToString(ref lines);
|
||||
lines.Add(new List<string>() { $"Stream Profile Number: {_stream_profile_number}" });
|
||||
lines.Add(new List<string>() { $"UDP IP PORT: {_udpipport}" });
|
||||
lines.Add(new List<string>() { $"Time Channel ID: {_timechannelid}" });
|
||||
lines.Add(new List<string>() { $"Data Channel ID: {_timechannelid}" });
|
||||
lines.Add(new List<string>() { $"TMNS: {_tmnsPcmSubFrameId}, {_tmnsMsgId}, {_tmnsPcmMinorPerMajor}, {_tmnsTmatsPortNumber}, {_ienaUdpSrcPortNumber}, {_udpRsv1}, {_udpRsv2}, {_udpRsv3}," });
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public class StreamConfigUDPSet : RealtimeCommandBase
|
||||
{
|
||||
const int COMMAND_PAYLOAD_SIZE = 74;
|
||||
|
||||
protected override RealtimeCommandBase.Commands _Command
|
||||
{
|
||||
get { return Commands.I106StreamConfigSet; }
|
||||
}
|
||||
|
||||
private byte _stream_profile_number;
|
||||
private byte[] _udpipport;
|
||||
private ushort _irig106config0;
|
||||
private ushort _irig106config1;
|
||||
private uint _tmnsPcmSubFrameId; // _tmns0;
|
||||
private uint _tmnsMsgId; // _tmns1;
|
||||
private uint _tmnsPcmMinorPerMajor; // _tmns2;
|
||||
private uint _tmnsTmatsPortNumber; // _tmns3;
|
||||
private uint _ienaUdpSrcPortNumber; // _tmns4;
|
||||
private uint _udpRsv1; // _tmns5;
|
||||
private uint _udpRsv2; // _tmns6;
|
||||
private uint _udpRsv3; // _tmns7;
|
||||
|
||||
public byte Stream_Profile_Number
|
||||
{
|
||||
get => _stream_profile_number;
|
||||
set { _stream_profile_number = value; command.SetParameter(0, _stream_profile_number); }
|
||||
}
|
||||
public byte[] UdpIpPort
|
||||
{
|
||||
get => _udpipport;
|
||||
set
|
||||
{
|
||||
_udpipport = value;
|
||||
command.SetParameter(1, _udpipport);
|
||||
}
|
||||
}
|
||||
|
||||
public ushort Irig106Config0
|
||||
{
|
||||
get => _irig106config0;
|
||||
set
|
||||
{
|
||||
_irig106config0 = value; command.SetParameter(29, _irig106config0);
|
||||
}
|
||||
}
|
||||
|
||||
public ushort Irig106Config1
|
||||
{
|
||||
get => _irig106config1;
|
||||
set
|
||||
{
|
||||
_irig106config1 = value; command.SetParameter(31, _irig106config1);
|
||||
}
|
||||
}
|
||||
public uint TMNS_PCMSubFrameId
|
||||
{
|
||||
get => _tmnsPcmSubFrameId;
|
||||
set
|
||||
{
|
||||
_tmnsPcmSubFrameId = value; command.SetParameter(33, _tmnsPcmSubFrameId);
|
||||
}
|
||||
}
|
||||
|
||||
public uint TMNS_MsgId
|
||||
{
|
||||
get => _tmnsMsgId;
|
||||
set
|
||||
{
|
||||
_tmnsMsgId = value; command.SetParameter(37, _tmnsMsgId);
|
||||
}
|
||||
}
|
||||
public uint TMNS_PCMMinorPerMajor
|
||||
{
|
||||
get => _tmnsPcmMinorPerMajor;
|
||||
set
|
||||
{
|
||||
_tmnsPcmMinorPerMajor = value; command.SetParameter(41, _tmnsPcmMinorPerMajor);
|
||||
}
|
||||
}
|
||||
public uint TMNS_TMATSPortNumber
|
||||
{
|
||||
get => _tmnsTmatsPortNumber;
|
||||
set
|
||||
{
|
||||
_tmnsTmatsPortNumber = value; command.SetParameter(45, _tmnsTmatsPortNumber);
|
||||
}
|
||||
}
|
||||
public uint IENAUDP_PortNumber
|
||||
{
|
||||
get => _ienaUdpSrcPortNumber;
|
||||
set
|
||||
{
|
||||
_ienaUdpSrcPortNumber = value; command.SetParameter(49, _ienaUdpSrcPortNumber);
|
||||
}
|
||||
}
|
||||
public uint TMNS5
|
||||
{
|
||||
get => _udpRsv1;
|
||||
set
|
||||
{
|
||||
_udpRsv1 = value; command.SetParameter(53, _udpRsv1);
|
||||
}
|
||||
}
|
||||
public uint TMNS6
|
||||
{
|
||||
get => _udpRsv2;
|
||||
set
|
||||
{
|
||||
_udpRsv2 = value; command.SetParameter(57, _udpRsv2);
|
||||
}
|
||||
}
|
||||
public uint TMNS7
|
||||
{
|
||||
get { return _udpRsv3; }
|
||||
set
|
||||
{
|
||||
_udpRsv3 = value; command.SetParameter(61, _udpRsv3);
|
||||
}
|
||||
}
|
||||
public StreamConfigUDPSet(Common.Interface.DASFactory.ICommunication sock)
|
||||
: base(sock)
|
||||
{
|
||||
command.Parameter = new byte[COMMAND_PAYLOAD_SIZE];
|
||||
}
|
||||
|
||||
public StreamConfigUDPSet(Common.Interface.DASFactory.ICommunication sock, int TimeoutMillisec)
|
||||
: base(sock, TimeoutMillisec)
|
||||
{
|
||||
command.Parameter = new byte[COMMAND_PAYLOAD_SIZE];
|
||||
}
|
||||
|
||||
public override void ResponseToString(ref List<List<string>> lines)
|
||||
{
|
||||
base.ResponseToString(ref lines);
|
||||
lines.Add(new List<string>() { $"Stream Profile Number: {_stream_profile_number}" });
|
||||
lines.Add(new List<string>() { $"UDP IP PORT: {_udpipport}" });
|
||||
lines.Add(new List<string>() { $"Irig Config: {_irig106config0}" });
|
||||
lines.Add(new List<string>() { $"TMNS: {_tmnsPcmSubFrameId}, {_tmnsMsgId}, {_tmnsPcmMinorPerMajor}, {_tmnsTmatsPortNumber}, {_ienaUdpSrcPortNumber}, {_udpRsv1}, {_udpRsv2}, {_udpRsv3}," });
|
||||
}
|
||||
}
|
||||
}
|
||||
199
DataPRO/SLICECommands/RealtimeCommands/StreamReaderUDP.cs
Normal file
199
DataPRO/SLICECommands/RealtimeCommands/StreamReaderUDP.cs
Normal file
@@ -0,0 +1,199 @@
|
||||
using DTS.Common.Utilities.Logging;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using DTS.Common.Enums;
|
||||
|
||||
namespace DTS.DASLib.Command.SLICE.RealtimeCommands
|
||||
{
|
||||
/// <summary>
|
||||
/// this class was ported from FWTU
|
||||
/// </summary>
|
||||
public class StreamReaderUDP
|
||||
{
|
||||
public string StreamAddress { get; }
|
||||
|
||||
/// <summary>
|
||||
/// appears to be the parameters (line) sent to the start command
|
||||
/// </summary>
|
||||
public byte[] cmdline { get; set; }
|
||||
|
||||
private Socket _udpSocket { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// IP of our receive endpoint. Either 0.0.0.0 or a specified adapter's IP
|
||||
/// FB15531: Follow pattern for multicast AutoDiscovery in StreamReader
|
||||
/// </summary>
|
||||
public string HostIPAddress { get; set; } = IPAddress.Any.ToString();
|
||||
|
||||
public void CloseSocket()
|
||||
{
|
||||
try
|
||||
{
|
||||
_udpSocket.Close();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
APILogger.LogException(e);
|
||||
}
|
||||
}
|
||||
public UDPStreamProfile UDPStreamType
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
private EndPoint _uDPEndpoint;
|
||||
public EndPoint UDPEndpoint
|
||||
{
|
||||
get => _uDPEndpoint;
|
||||
set => _uDPEndpoint = value;
|
||||
}
|
||||
|
||||
public ulong UDPSampleNumber { get; set; }
|
||||
|
||||
public StreamReaderUDP(string streamAddress, string hostAddress, UDPStreamProfile uDPStreamType, byte[] channels)
|
||||
{
|
||||
StreamAddress = streamAddress.TrimEnd('/');
|
||||
UDPSampleNumber = 0;
|
||||
UDPStreamType = uDPStreamType;
|
||||
Channels = channels;
|
||||
HostIPAddress = hostAddress;
|
||||
Configure();
|
||||
}
|
||||
|
||||
public byte[] Channels { get; set; } = new byte[0];
|
||||
|
||||
private void Configure()
|
||||
{
|
||||
// setup parameter for command
|
||||
var channelMaskAndReserved = 0; // default to all channels.
|
||||
//I'm not sure this is supported yet...
|
||||
//if (Channels.Any())
|
||||
//{
|
||||
// foreach (var ch in Channels)
|
||||
// {
|
||||
// channelMaskAndReserved |= 1 << ch;
|
||||
// }
|
||||
//}
|
||||
|
||||
var channelList = BitConverter.GetBytes(channelMaskAndReserved);
|
||||
|
||||
// create parameter for streaming command.
|
||||
cmdline = new byte[4 + StreamAddress.Length];
|
||||
var paramNetAddr = Encoding.ASCII.GetBytes(StreamAddress);
|
||||
Buffer.BlockCopy(channelList, 0, cmdline, 0, channelList.Length);
|
||||
Buffer.BlockCopy(paramNetAddr, 0, cmdline, channelList.Length,
|
||||
paramNetAddr
|
||||
.Length); // System.Buffer.BlockCopy(netAddr.ToArray(), 0, cmdline, channelList.Length, netAddr.Length);
|
||||
|
||||
|
||||
// get IP and port udp://239.1.2.10:portID
|
||||
var parts = StreamAddress.Split(':');
|
||||
if (parts.Length != 3)
|
||||
{
|
||||
throw new Exception($"Invalid UDP address:{StreamAddress}");
|
||||
}
|
||||
|
||||
// remove '//' or '/' from IP and port
|
||||
var udphost = parts[1].Trim('/');
|
||||
var udpport = parts[2].Trim('/');
|
||||
|
||||
_udpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
|
||||
var iep = IPAddress.TryParse(HostIPAddress, out var address) ? new IPEndPoint(address, Convert.ToUInt16(udpport)) : new IPEndPoint(IPAddress.Any, Convert.ToUInt16(udpport));
|
||||
UDPEndpoint = iep;
|
||||
try
|
||||
{
|
||||
_udpSocket.Bind(iep);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
APILogger.LogException(e);
|
||||
_udpSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, 2000);
|
||||
_udpSocket.Bind(iep); // retry again.
|
||||
}
|
||||
|
||||
// check for udp broadcast
|
||||
var ipChecks = udphost.Split('.');
|
||||
var ipV4Check = Convert.ToInt32(ipChecks[0]);
|
||||
if ((ipV4Check >= 224) & (ipV4Check <= 239))
|
||||
{
|
||||
|
||||
var mcastOption = new MulticastOption(IPAddress.Parse(udphost));
|
||||
if (iep.Address != IPAddress.Any)
|
||||
{
|
||||
mcastOption = new MulticastOption(IPAddress.Parse(udphost), iep.Address);
|
||||
}
|
||||
|
||||
_udpSocket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, mcastOption); // "239.1.2.10")));
|
||||
}
|
||||
//set timer for recv_socket
|
||||
_udpSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 2000);
|
||||
}
|
||||
/// <summary>
|
||||
/// receives any packets waiting
|
||||
/// returns null if no information is ready, otherwise a structure filled with information
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public UDPStreamPacket Read()
|
||||
{
|
||||
//receive data
|
||||
var databuf = new byte[2000];
|
||||
var recv = _udpSocket.ReceiveFrom(databuf, ref _uDPEndpoint);
|
||||
if (recv <= 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var data = new byte[recv];
|
||||
Buffer.BlockCopy(databuf, 0, data, 0, recv);
|
||||
var udpData = new UDPRealtimeByteConverter(data);
|
||||
|
||||
var udpStreamPacket = new UDPStreamPacket();
|
||||
udpStreamPacket.ChannelData = new short[Channels.Length][];
|
||||
|
||||
//fun fact, sending all channels right now, so regardless of how many were configured to send, use this
|
||||
var numChannels = 6;
|
||||
//calculate how many samples are in the packet, but use the floor incase it's not complete
|
||||
var numSamples = Convert.ToUInt64(Math.Floor((double)udpData.RtData.Length / numChannels));
|
||||
for (var i = 0; i < Channels.Length; i++)
|
||||
{
|
||||
udpStreamPacket.ChannelData[i] = new short[numSamples];
|
||||
}
|
||||
|
||||
//forward declarations to reduce a little churn
|
||||
byte channel = 0;
|
||||
var list = Channels.ToList();
|
||||
var sampleIndex = 0UL;
|
||||
short adc = 0;
|
||||
|
||||
for (var i = 0; i < udpData.RtData.Length; i++)
|
||||
{
|
||||
//which channel this sample is for
|
||||
channel = Convert.ToByte(i % numChannels);
|
||||
//which channel in the list this is for
|
||||
var channelIdx = list.IndexOf(channel);
|
||||
//channel is not in list, don't care about this sample
|
||||
if (channelIdx < 0) { continue; }
|
||||
//which sample in a sequence of multiple samples this is
|
||||
sampleIndex = Convert.ToUInt64(Math.Floor((double)i / numChannels));
|
||||
//we should have all complete packets, but if there's an incomplete round
|
||||
//this will skip it
|
||||
if (sampleIndex >= numSamples) { continue; }
|
||||
//add the sample into the list of samples
|
||||
adc = (short)udpData.RtData[i];
|
||||
udpStreamPacket.ChannelData[channelIdx][sampleIndex] = adc;
|
||||
}
|
||||
udpStreamPacket.PTPTimesec = Convert.ToUInt32(udpData.PtpTimeStampSec);
|
||||
udpStreamPacket.PTPTimeNsec = Convert.ToUInt32(udpData.PtpTimeStampNsec);
|
||||
udpStreamPacket.PTPSyncStatusError = Convert.ToBoolean(udpData.PacketFlags & 0x20);
|
||||
udpStreamPacket.ADCOverflowStatus = Convert.ToBoolean(udpData.PacketFlags & 0x10);
|
||||
udpStreamPacket.TimeStamp = 0L;
|
||||
//packet does not contain a sample number, so we calculate it
|
||||
udpStreamPacket.SampleNumber = udpData.SequenceNumber * numSamples;
|
||||
return udpStreamPacket;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace DTS.DASLib.Command.SLICE.RealtimeCommands
|
||||
{
|
||||
/// <summary>
|
||||
/// this class was ported from FWTU RealtimeCommands, it appears almost verbatim to there
|
||||
/// </summary>
|
||||
public class UDPRealtimeByteConverter
|
||||
{
|
||||
public enum UDP_BYTE_INDEX
|
||||
{
|
||||
PKT_PATTERN = 0, // u16
|
||||
CHANNEL_ID = 2, // u16
|
||||
PKT_LEN = 4, // u32
|
||||
DATA_LEN = 8, // u32
|
||||
DATA_TYPE_VER = 12, // u8
|
||||
SEQ_NUMBER = 13, // u8
|
||||
PKT_FLAGS = 14, // u8
|
||||
DATA_TYPE = 15, // u8
|
||||
REL_TIME32 = 16, // u32 of 48
|
||||
REL_TIME16 = 20, // u16
|
||||
HDR_CRC16 = 22, // u16
|
||||
PTP_U32_LSW = 24, // u32
|
||||
PTP_U32_MSW = 28, // u32
|
||||
RSV_U16 = 32, // u32
|
||||
HDR2CRC16 = 34, // u16
|
||||
CSDW = 36, // u32
|
||||
DATA_START = 40, // data start address
|
||||
}
|
||||
|
||||
// This is list of data type for UDP to support
|
||||
public enum UDP_DATA_TYPE
|
||||
{
|
||||
// 0x21 for analog format 1. 0x01 = CGDP format 1 (setup record). 0x12 = Time Data, Format 2 (PTP time)
|
||||
CGDP_TYPE = 0x01, // Setup XML record. 1. Setup Record, periodic message sent out.
|
||||
TIME_DATA_PTP = 0x12, // Time data, format 2. Periodic message.
|
||||
ANALOG_DATA_FORMAT_1 = 0x21, // Analog data format 3. Periodic message.
|
||||
}
|
||||
public ushort PacketPattern { get; private set; }
|
||||
public ushort DataChannelID { get; private set; }
|
||||
public uint PacketLength { get; private set; }
|
||||
/// <summary>
|
||||
/// careful if you use this, it's not the same as
|
||||
/// RtData.Length ...
|
||||
/// </summary>
|
||||
public uint DataLength { get; private set; }
|
||||
public byte DataTypeVersion { get; private set; }
|
||||
public byte SequenceNumber { get; private set; }
|
||||
public byte PacketFlags { get; private set; }
|
||||
public byte DataType { get; private set; }
|
||||
public uint RelativeTime32 { get; private set; }
|
||||
public ushort RelativeTime16 { get; private set; }
|
||||
public ushort HdrCrc16 { get; private set; }
|
||||
public uint PtpTimeStampSec { get; private set; }
|
||||
public uint PtpTimeStampNsec { get; private set; }
|
||||
public uint Hdr2Crc16 { get; private set; }
|
||||
public uint ChannelMask { get; private set; }
|
||||
public ulong UdpSampleCount { get; private set; }
|
||||
|
||||
public ushort[] RtData { get; private set; }
|
||||
|
||||
public uint[] Channels { get; private set; }
|
||||
public byte SequenceNumberPrev { get; private set; }
|
||||
|
||||
public UDPRealtimeByteConverter(byte[] bytes)
|
||||
{
|
||||
// TBD: Currently expect 36byte header. If simplified version with 24Byte,
|
||||
// we have to account for the right offset. RTC will be used then.
|
||||
PacketPattern = GetUShort(bytes, (int)UDP_BYTE_INDEX.PKT_PATTERN);
|
||||
DataChannelID = GetUShort(bytes, (int)UDP_BYTE_INDEX.CHANNEL_ID);
|
||||
PacketLength = GetUInt(bytes, (int)UDP_BYTE_INDEX.PKT_LEN);
|
||||
DataLength = GetUInt(bytes, (int)UDP_BYTE_INDEX.DATA_LEN);
|
||||
DataTypeVersion = GetByte(bytes, (int)UDP_BYTE_INDEX.DATA_TYPE_VER);
|
||||
SequenceNumber = GetByte(bytes, (int)UDP_BYTE_INDEX.SEQ_NUMBER);
|
||||
PacketFlags = GetByte(bytes, (int)UDP_BYTE_INDEX.PKT_FLAGS);
|
||||
DataType = GetByte(bytes, (int)UDP_BYTE_INDEX.DATA_TYPE); // 0x21 for analog format 1. 0x01 = CGDP format 1 (setup record). 0x12 = Time Data, Format 2 (PTP time)
|
||||
RelativeTime32 = GetUInt(bytes, (int)UDP_BYTE_INDEX.REL_TIME32);
|
||||
RelativeTime16 = GetUShort(bytes, (int)UDP_BYTE_INDEX.REL_TIME16);
|
||||
HdrCrc16 = GetUShort(bytes, (int)UDP_BYTE_INDEX.HDR_CRC16);
|
||||
PtpTimeStampSec = GetUInt(bytes, (int)UDP_BYTE_INDEX.PTP_U32_MSW);
|
||||
PtpTimeStampNsec = GetUInt(bytes, (int)UDP_BYTE_INDEX.PTP_U32_LSW);
|
||||
Hdr2Crc16 = GetUShort(bytes, (int)UDP_BYTE_INDEX.HDR2CRC16);
|
||||
|
||||
if (DataType == (int)UDP_DATA_TYPE.ANALOG_DATA_FORMAT_1)
|
||||
{
|
||||
RtData = new ushort[(DataLength - 4) / 2]; // ushort[(bytes.Length - (int)UDP_BYTE_INDEX.DATA_START) / 2];
|
||||
|
||||
var mychannels = new List<uint>();
|
||||
|
||||
for (var i = 0; i < 6; i++) // var length = dataLength;
|
||||
{
|
||||
//if (ba.Get(i)) assume all 6 channels if UDP.
|
||||
{
|
||||
mychannels.Add(Convert.ToUInt32(i));
|
||||
}
|
||||
}
|
||||
Channels = mychannels.ToArray();
|
||||
UdpSampleCount = Convert.ToUInt64(RtData.Length / Channels.Length);
|
||||
|
||||
SequenceNumberPrev = SequenceNumber;
|
||||
|
||||
// TBD: check for pktFlag if the optional 2nd header is being used or not. If not,
|
||||
// we can start the data from UDP_BYTE_INDEX.PTP_U32_LSW
|
||||
for (var idx = 0; idx < RtData.Length; idx++)
|
||||
{
|
||||
RtData[idx] = GetRTUShort(bytes, (int)(UDP_BYTE_INDEX.DATA_START + idx * 2));
|
||||
RtData[idx] ^= 0x8000;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// TBD for other packet types.
|
||||
RtData = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static ulong GetULong(byte[] bytes, int offset)
|
||||
{
|
||||
return (ulong)bytes[offset + 7] << 0 |
|
||||
(ulong)bytes[offset + 6] << 8 |
|
||||
(ulong)bytes[offset + 5] << 16 |
|
||||
(ulong)bytes[offset + 4] << 24 |
|
||||
(ulong)bytes[offset + 3] << 32 |
|
||||
(ulong)bytes[offset + 2] << 40 |
|
||||
(ulong)bytes[offset + 1] << 48 |
|
||||
(ulong)bytes[offset + 0] << 56;
|
||||
}
|
||||
|
||||
private static ushort GetUShort(byte[] bytes, int offset)
|
||||
{
|
||||
return (ushort)(bytes[offset + 1] |
|
||||
bytes[offset + 0] << 8);
|
||||
}
|
||||
|
||||
private static byte GetByte(byte[] bytes, int offset)
|
||||
{
|
||||
return bytes[offset];
|
||||
}
|
||||
private static uint GetUInt(byte[] bytes, int offset)
|
||||
{
|
||||
return (uint)bytes[offset + 3] << 0 |
|
||||
(uint)bytes[offset + 2] << 8 |
|
||||
(uint)bytes[offset + 1] << 16 |
|
||||
(uint)bytes[offset + 0] << 24;
|
||||
}
|
||||
/// <summary>
|
||||
/// data apparently has a different byte order than parameters, so we have to
|
||||
/// rearrange byte order for parameters but not data.
|
||||
/// </summary>
|
||||
/// <param name="bytes"></param>
|
||||
/// <param name="offset"></param>
|
||||
/// <returns></returns>
|
||||
private static ushort GetRTUShort(byte[] bytes, int offset)
|
||||
{
|
||||
return (ushort)(bytes[offset + 0] |
|
||||
bytes[offset + 1] << 8);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
31
DataPRO/SLICECommands/RealtimeCommands/UDPStreamPacket.cs
Normal file
31
DataPRO/SLICECommands/RealtimeCommands/UDPStreamPacket.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
|
||||
namespace DTS.DASLib.Command.SLICE.RealtimeCommands
|
||||
{
|
||||
/// <summary>
|
||||
/// ported from FWTU, appears almost verbatim to there
|
||||
/// </summary>
|
||||
public class UDPStreamPacket
|
||||
{
|
||||
/// <summary>
|
||||
/// first array channels, second array sample index
|
||||
/// </summary>
|
||||
public short[][] ChannelData { get; set; }
|
||||
|
||||
public long TimeStamp { get; set; }
|
||||
public ulong SampleNumber { get; set; }
|
||||
public ulong SequenceNumber { get; set; }
|
||||
|
||||
public uint PTPTimesec { get; set; }
|
||||
public uint PTPTimeNsec { get; set; }
|
||||
|
||||
public bool PTPSyncStatusError { get; set; }
|
||||
public bool ADCOverflowStatus { get; set; }
|
||||
|
||||
public string PTPTimeString => string.Concat(PTPTimesec, ":", PTPTimeNsec.ToString("000000000"));
|
||||
|
||||
public UDPStreamPacket()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user