init
This commit is contained in:
527
DataPRO/TDASCommands/TDASCommandPacketBase.cs
Normal file
527
DataPRO/TDASCommands/TDASCommandPacketBase.cs
Normal file
@@ -0,0 +1,527 @@
|
||||
using DTS.Common.Utilities.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace DTS.DASLib.Command.TDAS
|
||||
{
|
||||
/*
|
||||
* typedef struct _md {
|
||||
int delim; delimiter pattern
|
||||
int key; key to mem state
|
||||
long itemSize; mem size that this MDB controls
|
||||
long dataSize; length of valid data buffer
|
||||
unsigned int nCrc; buffer crc
|
||||
void *flink; forward link to next buffer
|
||||
struct _md *next; used for doubly-linked list
|
||||
struct _md *prev; of all mds
|
||||
void *this; this buffer of size itemSize
|
||||
void *freeList; a free list of objects
|
||||
long nInst; number of memory blocks created
|
||||
long nActive; number still out there somewhere
|
||||
long nFree; length of free list
|
||||
}*/
|
||||
//note in CVI longs are the same size as ints, or 4 bytes
|
||||
internal class MDB_BLOCK
|
||||
{
|
||||
public enum MDB_Fields
|
||||
{
|
||||
delim = 0,
|
||||
key,
|
||||
itemSize,
|
||||
dataSize,
|
||||
nCrc,
|
||||
flink,
|
||||
next,
|
||||
prev,
|
||||
ptrThis,
|
||||
freeList,
|
||||
nInst,
|
||||
nActive,
|
||||
nFree
|
||||
}
|
||||
public int GetField(MDB_Fields field)
|
||||
{
|
||||
return IPAddress.NetworkToHostOrder(BitConverter.ToInt32(_mdb, 4 * (int)field));
|
||||
}
|
||||
private byte[] _mdb;
|
||||
public const int MDB_BLOCK_SIZE = 52;
|
||||
public const int DMA_SIZE = 1024;
|
||||
public const int DMA_BLOCK_SIZE = MDB_BLOCK_SIZE + DMA_SIZE;
|
||||
public const int MEM_KEY_START = 100;
|
||||
public static readonly byte[] DelimBytes = { 0xFE, 0x01, 0xFD, 0x02 };
|
||||
public static readonly int DelimPattern = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(DelimBytes, 0));
|
||||
public static readonly string DelimString = BitConverter.ToString(DelimBytes);
|
||||
public MDB_BLOCK(byte[] packet)
|
||||
{
|
||||
_mdb = packet;
|
||||
}
|
||||
public bool HasData => (null != _mdb && GetField(MDB_Fields.dataSize) > 0);
|
||||
|
||||
public short[] GetData()
|
||||
{
|
||||
var data = new List<short>();
|
||||
for (var i = 0; i < GetField(MDB_Fields.dataSize) / 2; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
var value = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(_mdb, MDB_BLOCK_SIZE + i * 2));
|
||||
data.Add(value);
|
||||
}
|
||||
catch (Exception) { Trace.WriteLine("failed to convert " + i); }
|
||||
}
|
||||
return data.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class TDASCommandPacketBase : CommandPacketBase
|
||||
{
|
||||
public bool RackCommand
|
||||
{
|
||||
get => _commandStrings[0].RackCommand;
|
||||
set => _commandStrings[0].RackCommand = value;
|
||||
}
|
||||
|
||||
public bool SingleModuleCommand
|
||||
{
|
||||
get { return _commandStrings[0].SingleModuleCommand; }
|
||||
set { _commandStrings[0].SingleModuleCommand = value; }
|
||||
}
|
||||
|
||||
public string RackSerialNumber
|
||||
{
|
||||
get { return _commandStrings[0].RackSerialNumber; }
|
||||
set { _commandStrings[0].RackSerialNumber = value; }
|
||||
}
|
||||
|
||||
|
||||
private char _moduleIndex = ' ';
|
||||
public int ModuleIndex
|
||||
{
|
||||
get => int.Parse(new string(_moduleIndex, 1));
|
||||
set
|
||||
{
|
||||
if (value > 9) { throw new Exception("ModuleIndex must be <10 for this command" + ModuleIndex); }
|
||||
_moduleIndex = value.ToString()[0];
|
||||
RackCommand = false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool AllModule { get; set; }
|
||||
|
||||
|
||||
public bool RebuildBytes { get; set; }
|
||||
|
||||
public bool ExpectsData
|
||||
{
|
||||
get => _commandStrings[0].ExpectsData;
|
||||
set => _commandStrings[0].ExpectsData = value;
|
||||
}
|
||||
public bool ExpectsMultipleLines
|
||||
{
|
||||
get => _commandStrings[0].ExpectsMultipleLines;
|
||||
set => _commandStrings[0].ExpectsMultipleLines = value;
|
||||
}
|
||||
public int LinesExpected
|
||||
{
|
||||
get => _commandStrings[0].LinesExpected;
|
||||
set => _commandStrings[0].LinesExpected = value;
|
||||
}
|
||||
|
||||
private const int MAX_CMD = 1;
|
||||
private const int MAX_CMD_SIZE = 512;
|
||||
public const int CDB_SIZE = 536;
|
||||
|
||||
private readonly byte _cdbId;
|
||||
private readonly byte _cdbQueue;
|
||||
public byte CharWait { get; set; }
|
||||
|
||||
public int ReplyWait { get; set; }
|
||||
|
||||
private readonly byte[] _reserved;
|
||||
private readonly int _rackId;
|
||||
private readonly byte[] _memoryAddress = GENERIC_MEMORY_ADDRESS;
|
||||
private readonly List<CommandString> _commandStrings = new List<CommandString>();
|
||||
private readonly byte[] _responseBytes;
|
||||
private static readonly byte[] GENERIC_MEMORY_ADDRESS = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
|
||||
public override byte[] ToBytes()
|
||||
{
|
||||
if (null != _responseBytes) { return _responseBytes; }
|
||||
|
||||
var ms = new MemoryStream(CDB_SIZE);
|
||||
|
||||
ms.WriteByte(_cdbId);
|
||||
ms.WriteByte(_cdbQueue);
|
||||
ms.WriteByte(Convert.ToByte(_commandStrings.Count));
|
||||
ms.WriteByte(CharWait);
|
||||
ms.Write(BitConverter.GetBytes(ReplyWait), 0, 4);
|
||||
ms.Write(_reserved, 0, _reserved.Length);
|
||||
ms.Write(BitConverter.GetBytes(IPAddress.HostToNetworkOrder(_rackId)), 0, 4);
|
||||
ms.Write(_memoryAddress, 0, _memoryAddress.Length);
|
||||
|
||||
if (RackCommand)
|
||||
{
|
||||
ms.Write(new byte[] { 0x31, 0x30, 0x30, 0x30 }, 0, 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (AllModule) { ms.WriteByte(Convert.ToByte('*')); }
|
||||
if (_moduleIndex != ' ')
|
||||
{
|
||||
ms.WriteByte(Convert.ToByte(Convert.ToByte(_moduleIndex)));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var cs in _commandStrings)
|
||||
{
|
||||
if (RebuildBytes) { cs.RebuildBytes(); }
|
||||
ms.Write(cs.GetBytes(), 0, cs.Length);
|
||||
}
|
||||
//17716 eliminate extra characters filling out outgoing TDAS coms
|
||||
//if (ms.Length < CDB_SIZE)
|
||||
//{
|
||||
// ms.Write(new byte[CDB_SIZE - (int)ms.Length], 0, CDB_SIZE - (int)ms.Length);
|
||||
//}
|
||||
return ms.ToArray();
|
||||
}
|
||||
public override object ConvertByteToCommandType(byte b)
|
||||
{
|
||||
throw new NotSupportedException("TDASCommandPacketBase::ConvertByteToCommandType not supported");
|
||||
}
|
||||
private int _expectedBytes;
|
||||
private int _bytesSoFar;
|
||||
public bool UseMDBMode { get; set; }
|
||||
|
||||
public bool MonitorDataMode { get; set; }
|
||||
|
||||
private readonly List<MDB_BLOCK> _data = new List<MDB_BLOCK>();
|
||||
private int _curOffset;
|
||||
|
||||
public int GetExpectedBytes()
|
||||
{
|
||||
return _expectedBytes;
|
||||
}
|
||||
public short[] GetData()
|
||||
{
|
||||
List<short> data = new List<short>();
|
||||
//we have to skip the first one for some odd reason?
|
||||
for (int i = 1; i < _data.Count; i++)
|
||||
{
|
||||
MDB_BLOCK mdb = _data[i];
|
||||
if (mdb.HasData)
|
||||
{
|
||||
data.AddRange(mdb.GetData());
|
||||
}
|
||||
}
|
||||
return data.ToArray();
|
||||
}
|
||||
public bool RealtimeCommand { get; set; }
|
||||
public override PacketState VerifyPacket(byte[] Bytes)
|
||||
{
|
||||
if (!ExpectsData && Bytes.Length > 0)
|
||||
{
|
||||
Thread.Sleep(100); //cut from 500 to 100 for performance reasons
|
||||
APILogger.Log($"VerifyPacket [{SequenceNumber}], returning OK as we don't expect data and have greater than 0 length ({Bytes.Length})");
|
||||
return PacketState.OK;
|
||||
}
|
||||
|
||||
if (UseMDBMode)
|
||||
{
|
||||
if (0 == _curOffset)
|
||||
{
|
||||
int index = -1;
|
||||
string s = BitConverter.ToString(Bytes);
|
||||
try { index = s.IndexOf(MDB_BLOCK.DelimString, 1); }
|
||||
catch (Exception) { }
|
||||
if (index < 0)
|
||||
{
|
||||
return PacketState.TooShort;
|
||||
}
|
||||
if (index > 0)
|
||||
{
|
||||
//need to advance to the start of the block
|
||||
for (var i = 1; i < Bytes.Length - MDB_BLOCK.DelimBytes.Length; i++)
|
||||
{
|
||||
if (Bytes[i] != MDB_BLOCK.DelimBytes[0] || Bytes[i + 1] != MDB_BLOCK.DelimBytes[1] ||
|
||||
Bytes[i + 2] != MDB_BLOCK.DelimBytes[2] ||
|
||||
Bytes[i + 3] != MDB_BLOCK.DelimBytes[3]) continue;
|
||||
_curOffset = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (Bytes.Length - _curOffset >= MDB_BLOCK.DMA_BLOCK_SIZE)
|
||||
{
|
||||
byte[] block;
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
//need to advance to the start of the block
|
||||
for (var i = _curOffset; i < Bytes.Length - MDB_BLOCK.DelimBytes.Length; i++)
|
||||
{
|
||||
if (Bytes[i] != MDB_BLOCK.DelimBytes[0] || Bytes[i + 1] != MDB_BLOCK.DelimBytes[1] ||
|
||||
Bytes[i + 2] != MDB_BLOCK.DelimBytes[2] ||
|
||||
Bytes[i + 3] != MDB_BLOCK.DelimBytes[3]) continue;
|
||||
_curOffset = i;
|
||||
break;
|
||||
}
|
||||
|
||||
//scan forward from _curOffset till we see the
|
||||
ms.Write(Bytes, _curOffset, MDB_BLOCK.DMA_BLOCK_SIZE);
|
||||
_curOffset += MDB_BLOCK.DMA_BLOCK_SIZE;
|
||||
block = ms.ToArray();
|
||||
}
|
||||
var mdb = new MDB_BLOCK(block);
|
||||
|
||||
_data.Add(mdb);
|
||||
if (mdb.GetField(MDB_BLOCK.MDB_Fields.delim) == MDB_BLOCK.DelimPattern)
|
||||
{
|
||||
if (mdb.GetField(MDB_BLOCK.MDB_Fields.key) == MDB_BLOCK.MEM_KEY_START)
|
||||
{
|
||||
var s = Encoding.ASCII.GetString(block, MDB_BLOCK.MDB_BLOCK_SIZE + 5, 10);
|
||||
var tokens = s.Split(' ');
|
||||
_expectedBytes = Convert.ToInt32(tokens[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
_bytesSoFar += mdb.GetField(MDB_BLOCK.MDB_Fields.dataSize);
|
||||
if (_bytesSoFar >= _expectedBytes && 0 != _expectedBytes)
|
||||
{
|
||||
return PacketState.OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return PacketState.OK;
|
||||
}
|
||||
}
|
||||
return PacketState.TooShort;
|
||||
}
|
||||
{
|
||||
string s = Encoding.ASCII.GetString(Bytes);
|
||||
//Could be a multi-line response where we've grabbed the newline as the first characters.
|
||||
if (s.Length > 2 && s.IndexOf("\r\n") >= 0)
|
||||
{
|
||||
if (!s.EndsWith("\r\n") && ExpectsMultipleLines)
|
||||
{
|
||||
APILogger.Log($"TDASCommandPacketBase:VerifyPacket [{SequenceNumber}], returning too short {s}");
|
||||
return PacketState.TooShort;
|
||||
}
|
||||
|
||||
if (!ExpectsMultipleLines)
|
||||
{
|
||||
APILogger.Log($"TDASCommandPacketBase:VerifyPacket [{SequenceNumber}], returning OK, not expecting multiple lines: {s}");
|
||||
return PacketState.OK;
|
||||
}
|
||||
var lines = s.Split(new[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
//G5 returns ~ and if we see that we know we still have more lines coming
|
||||
//TDAS PRO does not, but we do have an expectation of a certain number of lines
|
||||
|
||||
if (lines[lines.Length - 1].Contains('~'))
|
||||
{
|
||||
APILogger.Log($"[{SequenceNumber}] returning packet too short (contains ~) {s}");
|
||||
return PacketState.TooShort;
|
||||
}
|
||||
|
||||
if (lines.Length < LinesExpected)
|
||||
{
|
||||
APILogger.Log(
|
||||
$"[{SequenceNumber}] Lines less than expected, returning too short {lines.Length}<{LinesExpected} for {s}");
|
||||
return PacketState.TooShort;
|
||||
}
|
||||
//if command has a required response and we haven't seen it yet,
|
||||
//consider us as having not received the full response yet.
|
||||
//18127 Test Channel Run Broadcast can complete before response is received and processed
|
||||
if (null != _commandStrings && 1 == _commandStrings.Count &&
|
||||
!string.IsNullOrEmpty(_commandStrings[0].RequiredResponseString))
|
||||
{
|
||||
if (!s.Contains(_commandStrings[0].RequiredResponseString))
|
||||
{
|
||||
return PacketState.TooShort;
|
||||
}
|
||||
}
|
||||
return PacketState.OK;
|
||||
}
|
||||
|
||||
APILogger.Log($"[{SequenceNumber}] Returning too short - {s} - didn't find CR-EOL");
|
||||
return PacketState.TooShort;
|
||||
}
|
||||
}
|
||||
private static ushort _globalSequenceNumber;
|
||||
private static readonly object GLOBAL_SEQUENCE_NUMBER_LOCK = new object();
|
||||
public override void GetNextSequenceNumber()
|
||||
{
|
||||
lock (GLOBAL_SEQUENCE_NUMBER_LOCK)
|
||||
{
|
||||
SequenceNumber = _globalSequenceNumber;
|
||||
_globalSequenceNumber++;
|
||||
}
|
||||
}
|
||||
public override void ComputeCRCs()
|
||||
{
|
||||
}
|
||||
private void AddCommand(CommandString commandString)
|
||||
{
|
||||
if (_commandStrings.Count + 1 > MAX_CMD)
|
||||
{
|
||||
throw new CommandPacketException(CommandPacketException.ERROR_CODES.TOO_MANY_COMMANDS, CommandPacketException.ERROR_CODES.TOO_MANY_COMMANDS.ToString());
|
||||
}
|
||||
int totalBytes = commandString.Length;
|
||||
foreach (CommandString cs in _commandStrings) { totalBytes += cs.Length; }
|
||||
if (totalBytes > MAX_CMD_SIZE)
|
||||
{
|
||||
throw new CommandPacketException(CommandPacketException.ERROR_CODES.TOO_MANY_BYTES, CommandPacketException.ERROR_CODES.TOO_MANY_BYTES.ToString());
|
||||
}
|
||||
_commandStrings.Add(commandString);
|
||||
SetCommand(0x00, commandString.GetCommandDescription());
|
||||
Type = "TDAS";
|
||||
}
|
||||
public TDASCommandPacketBase(CommandString cs)
|
||||
{
|
||||
GetNextSequenceNumber();
|
||||
_cdbId = 1;
|
||||
_cdbQueue = Convert.ToByte(false);
|
||||
//support.dtsweb.com/hc/requests/7294
|
||||
//this is used to avoid flooding buffer, but was too large, TDC is using 1ms and presumably when we copied
|
||||
//tdc we must have used an older version
|
||||
CharWait = 0x01;
|
||||
ReplyWait = 30;
|
||||
_reserved = new byte[] { 0x00, 0x00, 0x00, 0x00 };
|
||||
_rackId = 1006;
|
||||
AddCommand(cs);
|
||||
}
|
||||
public TDASCommandPacketBase(byte[] buffer, CommandPacketBase command)
|
||||
{
|
||||
if (null != command)
|
||||
{
|
||||
SequenceNumber = command.SequenceNumber;
|
||||
}
|
||||
|
||||
var s = Encoding.ASCII.GetString(buffer);
|
||||
if (s.Contains("1000ARM"))
|
||||
{
|
||||
APILogger.Log($"Sequence [{SequenceNumber}] complete response", s);
|
||||
}
|
||||
_responseBytes = buffer;
|
||||
SetCommand(0x00, "");
|
||||
Type = "TDAS";
|
||||
}
|
||||
public string GetCommandString(int index)
|
||||
{
|
||||
return _commandStrings[index].GetCommandPortion();
|
||||
}
|
||||
public CommandString GetCommandStringObject(int index) { return _commandStrings[index]; }
|
||||
public string ToCommandString()
|
||||
{
|
||||
byte[] bytes = ToBytes();
|
||||
return Encoding.ASCII.GetString(bytes, 24, bytes.Length - 24).Replace("\r\n", string.Empty).Replace("\0", string.Empty);
|
||||
}
|
||||
}
|
||||
public abstract class CommandString
|
||||
{
|
||||
/// <summary>
|
||||
/// defines a required string to find in the response
|
||||
/// if string is not found response is not complete yet
|
||||
/// override to require a response
|
||||
/// 18127 Test Channel Run Broadcast can complete before response is received and processed
|
||||
/// </summary>
|
||||
public virtual string RequiredResponseString { get; set; } = string.Empty;
|
||||
|
||||
public bool ExpectsData { get; set; } = true;
|
||||
|
||||
public bool ExpectsMultipleLines { get; set; }
|
||||
|
||||
public int LinesExpected { get; set; }
|
||||
|
||||
public virtual byte[] GetParameters()
|
||||
{
|
||||
return new byte[] { };
|
||||
}
|
||||
|
||||
private bool _bRackCommand = true;
|
||||
public bool RackCommand
|
||||
{
|
||||
get { return _bRackCommand; }
|
||||
set { _bRackCommand = value; }
|
||||
}
|
||||
|
||||
private bool _bSingleModuleCommand = false;
|
||||
public bool SingleModuleCommand
|
||||
{
|
||||
get { return _bSingleModuleCommand; }
|
||||
set { _bSingleModuleCommand = value; }
|
||||
}
|
||||
|
||||
private string _RackSerialNumber = string.Empty;
|
||||
public string RackSerialNumber
|
||||
{
|
||||
get { return _RackSerialNumber; }
|
||||
set { _RackSerialNumber = value; }
|
||||
}
|
||||
|
||||
protected abstract string _CommandString { get; }
|
||||
public virtual string GetCommandPortion() { return _CommandString; }
|
||||
protected abstract string _CommandDescription { get; }
|
||||
public string GetCommandDescription() { return _CommandDescription; }
|
||||
private byte[] _bytes;
|
||||
public int Length
|
||||
{
|
||||
get
|
||||
{
|
||||
if (null == _bytes)
|
||||
{
|
||||
_bytes = GetBytes();
|
||||
}
|
||||
return _bytes.Length;
|
||||
}
|
||||
}
|
||||
|
||||
public void RebuildBytes() { _bytes = null; }
|
||||
|
||||
public byte[] GetBytes()
|
||||
{
|
||||
if (null != _bytes) return _bytes;
|
||||
var ms = new MemoryStream();
|
||||
|
||||
ms.Write(Encoding.ASCII.GetBytes(_CommandString), 0, _CommandString.Length);
|
||||
|
||||
byte[] parameters = GetParameters();
|
||||
if (parameters.Length > 0) { ms.Write(parameters, 0, parameters.Length); }
|
||||
|
||||
ms.Write(new byte[] { 0x0d, 0x0a }, 0, 2);
|
||||
_bytes = ms.ToArray();
|
||||
return _bytes;
|
||||
}
|
||||
}
|
||||
public class CommandPacketException : Exception
|
||||
{
|
||||
public enum ERROR_CODES
|
||||
{
|
||||
TOO_MANY_COMMANDS = 0,
|
||||
TOO_MANY_BYTES = 1,
|
||||
UNKNOWN = 2
|
||||
}
|
||||
public CommandPacketException(ERROR_CODES errorCode, string description)
|
||||
: base(description)
|
||||
{
|
||||
Data["ErrorCode"] = errorCode;
|
||||
}
|
||||
public ERROR_CODES ErrorCode
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!Data.Contains("ErrorCode")) { return ERROR_CODES.UNKNOWN; }
|
||||
return (ERROR_CODES)Data["ErrorCode"];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user