1297 lines
46 KiB
C#
1297 lines
46 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using System.Text;
|
||
using DTS.Common.DASResource;
|
||
using DTS.Common.Enums.DASFactory;
|
||
using DTS.Common.ICommunication;
|
||
using DTS.Common.Interface.DASFactory;
|
||
using DTS.Common.Utilities;
|
||
|
||
namespace DTS.DASLib.Command.SLICE
|
||
{
|
||
/* from firmware
|
||
* typedef struct _tFirmwareFileHeader
|
||
{
|
||
// file format: totalLen = firmwaresize + 108B header.
|
||
uint16_t magicNum; // 2 2 marker: 0x1234
|
||
uint16_t headerCRC16; // 2 4 crc16 for header
|
||
uint32_t fileLen; // 4 8 firmwaresize in originaltxt format + header size
|
||
uint16_t formatType; // 2 10 0 = text. other TBD.
|
||
uint16_t fileCRC16; // 2 12 crc16 for firmware in txt format.
|
||
uint16_t binCRC16; // 2 14 TBD crc16 for extracted bin format.
|
||
uint16_t reserved[7]; // 14 28 TBD.
|
||
uint8_t data[80]; // 80 108 any note from host. Filename for example...
|
||
} tFirmwareFileHeader;
|
||
*/
|
||
public struct SliceProFirmwareImageHeader
|
||
{
|
||
// file format: totalLen = firmwaresize + 108B header.
|
||
public ushort magicNum; // 2 2 marker: 0x1234
|
||
public ushort headerCRC16; // 2 4 crc16 for header
|
||
public uint fileLen; // 4 8 firmwaresize in originaltxt format + header size
|
||
public ushort formatType; // 2 10 0 = text. other TBD.
|
||
public ushort fileCRC16; // 2 12 crc16 for firmware in txt format.
|
||
public ushort binCRC16; // 2 14 TBD crc16 for extracted bin format.
|
||
public ushort[] reserved; // 14 28 TBD.
|
||
public byte[] data; // 80 108 any note from host. Filename for example...
|
||
}
|
||
public abstract class InformationCommands : CommandBase
|
||
{
|
||
protected enum Commands
|
||
{
|
||
Reserved = 0x00,
|
||
QueryStackContents = 0x01,
|
||
QueryProtocolVersion = 0x02,
|
||
QueryGroup = 0x03,
|
||
SetGroup = 0x04,
|
||
QueryFirmwareVersion = 0x05,
|
||
QuerySerialNumber = 0x06,
|
||
SetSerialNumber = 0x07,
|
||
QueryType = 0x08,
|
||
QueryOneWireID = 0x09,
|
||
QueryOneWireEEPROM = 0x0A,
|
||
SetOneWireEEPROM = 0x0B,
|
||
QueryMSP430FirmwareVersion = 0x0C,
|
||
SetFileData = 0x0D,
|
||
QueryFileData = 0x0E,
|
||
QueryFirmwareBuildVersion = 0x0F,
|
||
QueryStackSensorIDs = 0x10, // new command after protocol 136 to query all sensor IDs on stack.
|
||
SetStackInfoData = 0x11, // TBD. Not yet implemented. set 64-byte data block to infoB page.
|
||
QueryStackInfoData = 0x12, // TBD. Not yet implemented. query 64-byte data block from infoB page.
|
||
QueryFirmwareBuildID = 0x13, // TBD. Now supporting via system attribute. query build information from slice.
|
||
QueryBaseSystemTime = 0x14, // query current system time: sec.min.hr.days.weekdays.years (from 20xx)
|
||
SetBaseSystemTime = 0x15, // set current system time: sec.min.hr.days.weekdays.years (from 20xx)
|
||
QueryTempLogFile = 0x16,
|
||
};
|
||
|
||
//100 is now the max, per 14531 Implement TMATS support for S6A stream on boot
|
||
protected const int MAX_FILE_ID = 100; // user = 10, firmware = 20, max internally = 65. 10;
|
||
protected const int MAX_FILE_LENGTH = 400;
|
||
public const int MAX_FILE_LENGTH_ID100 = 16000;
|
||
protected const int MAX_FILE_READ_LENGTH = 1024;
|
||
|
||
protected abstract Commands _Command
|
||
{
|
||
get;
|
||
}
|
||
|
||
protected InformationCommands(Common.Interface.DASFactory.ICommunication sock)
|
||
: base(sock)
|
||
{
|
||
command.Type = CommandPacket.CommandType.Information;
|
||
command.SetCommand((byte)_Command, _Command.ToString());
|
||
}
|
||
|
||
protected InformationCommands(Common.Interface.DASFactory.ICommunication sock, int TimeoutMillisec)
|
||
: base(sock, TimeoutMillisec)
|
||
{
|
||
command.Type = CommandPacket.CommandType.Information;
|
||
command.SetCommand((byte)_Command, _Command.ToString());
|
||
}
|
||
|
||
}
|
||
public class QueryStackContents : InformationCommands
|
||
{
|
||
const int SliceDescriptorSize = 24;
|
||
const int AddressPosition = 0;
|
||
const int GroupPosition = 1;
|
||
const int TypePosition = 2;
|
||
const int SerialNumberPosition = 3;
|
||
const int FirmwareVersionPosition = 19;
|
||
|
||
public int SliceCount { get; private set; } = 0;
|
||
|
||
public string[] SerialNumber { get; private set; }
|
||
|
||
public string[] FirmwareVersion { get; private set; }
|
||
|
||
public byte[] SliceType { get; private set; }
|
||
|
||
public byte[] SliceAddress { get; private set; }
|
||
|
||
public byte[] SliceGroup { get; private set; }
|
||
|
||
protected override Commands _Command => Commands.QueryStackContents;
|
||
|
||
public QueryStackContents(Common.Interface.DASFactory.ICommunication sock)
|
||
: base(sock)
|
||
{
|
||
}
|
||
|
||
public QueryStackContents(Common.Interface.DASFactory.ICommunication sock, int TimeoutMillisec)
|
||
: base(sock, TimeoutMillisec)
|
||
{
|
||
}
|
||
|
||
protected override CommandReceiveAction WholePackage()
|
||
{
|
||
// we have a whole package
|
||
if (response.Status == DFConstantsAndEnums.CommandStatus.StatusNoError)
|
||
{
|
||
SliceCount = response.ParameterLength / SliceDescriptorSize;
|
||
SerialNumber = new string[SliceCount];
|
||
FirmwareVersion = new string[SliceCount];
|
||
SliceType = new byte[SliceCount];
|
||
SliceAddress = new byte[SliceCount];
|
||
SliceGroup = new byte[SliceCount];
|
||
|
||
for (int i = 0; i < SliceCount; i++)
|
||
{
|
||
response.GetParameter(SliceDescriptorSize * i + AddressPosition, out SliceAddress[i]);
|
||
response.GetParameter(SliceDescriptorSize * i + GroupPosition, out SliceGroup[i]);
|
||
response.GetParameter(SliceDescriptorSize * i + TypePosition, out SliceType[i]);
|
||
response.GetParameter(SliceDescriptorSize * i + SerialNumberPosition, out SerialNumber[i]);
|
||
response.GetParameter(SliceDescriptorSize * i + FirmwareVersionPosition, out FirmwareVersion[i]);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
SliceCount = 0;
|
||
}
|
||
return CommandReceiveAction.StopReceiving;
|
||
}
|
||
|
||
public override void ResponseToString(ref List<List<string>> lines)
|
||
{
|
||
base.ResponseToString(ref lines);
|
||
lines.Add(new List<string>()
|
||
{
|
||
$"SliceCount: {SliceCount}",
|
||
$"Connection: {recorder.ConnectString}"
|
||
});
|
||
|
||
for (int i = 0; i < SliceCount; i++)
|
||
{
|
||
var line = new List<string>();
|
||
line.Add($"Serial Number: {(SerialNumber != null && SerialNumber.Length > 0 ? SerialNumber[i] : "<null>")}, ");
|
||
line.Add($"Firmware version: {(FirmwareVersion != null && FirmwareVersion.Length > 0 ? FirmwareVersion[i] : "<null>")}, ");
|
||
line.Add($"Type: {(SliceType != null ? ((ChannelTypes)SliceType[i]).ToString() : "<null>")}, ");
|
||
line.Add($"Address: {(SliceAddress != null ? SliceAddress[i].ToString() : "<null>")}, ");
|
||
line.Add($"Group: {(SliceGroup != null ? SliceGroup[i].ToString() : "<null>")}");
|
||
lines.Add(line);
|
||
}
|
||
}
|
||
}
|
||
|
||
public class QueryProtocolVersion : InformationCommands
|
||
{
|
||
private byte _versionbyte = 0;
|
||
|
||
public byte Version => _versionbyte;
|
||
|
||
protected override Commands _Command => Commands.QueryProtocolVersion;
|
||
|
||
public QueryProtocolVersion(Common.Interface.DASFactory.ICommunication sock)
|
||
: base(sock)
|
||
{
|
||
}
|
||
|
||
public QueryProtocolVersion(Common.Interface.DASFactory.ICommunication sock, int TimeoutMillisec)
|
||
: base(sock, TimeoutMillisec)
|
||
{
|
||
}
|
||
|
||
protected override CommandReceiveAction WholePackage()
|
||
{
|
||
// we have a whole package
|
||
if (response.Status == DFConstantsAndEnums.CommandStatus.StatusNoError)
|
||
{
|
||
response.GetParameter(0, out _versionbyte);
|
||
}
|
||
return CommandReceiveAction.StopReceiving;
|
||
}
|
||
|
||
public override void ResponseToString(ref List<List<string>> lines)
|
||
{
|
||
base.ResponseToString(ref lines);
|
||
lines.Add(new List<string>()
|
||
{
|
||
$"Version: {Version}"
|
||
});
|
||
}
|
||
}
|
||
|
||
public class QueryFirmwareVersion : InformationCommands
|
||
{
|
||
string _versionstring = string.Empty;
|
||
|
||
public string Version => _versionstring;
|
||
|
||
protected override Commands _Command => Commands.QueryFirmwareVersion;
|
||
|
||
public QueryFirmwareVersion(Common.Interface.DASFactory.ICommunication sock)
|
||
: base(sock)
|
||
{
|
||
}
|
||
|
||
public QueryFirmwareVersion(Common.Interface.DASFactory.ICommunication sock, int TimeoutMillisec)
|
||
: base(sock, TimeoutMillisec)
|
||
{
|
||
}
|
||
|
||
protected override CommandReceiveAction WholePackage()
|
||
{
|
||
// we have a whole package
|
||
if (response.Status == DFConstantsAndEnums.CommandStatus.StatusNoError)
|
||
{
|
||
response.GetParameter(0, out _versionstring);
|
||
}
|
||
return CommandReceiveAction.StopReceiving;
|
||
}
|
||
|
||
public override void ResponseToString(ref List<List<string>> lines)
|
||
{
|
||
base.ResponseToString(ref lines);
|
||
lines.Add(new List<string>() { Version });
|
||
}
|
||
}
|
||
|
||
public class QuerySerialNumber : InformationCommands
|
||
{
|
||
private string _serialnumber = string.Empty;
|
||
|
||
public string SerialNumber => _serialnumber;
|
||
|
||
protected override Commands _Command => Commands.QuerySerialNumber;
|
||
|
||
public QuerySerialNumber(Common.Interface.DASFactory.ICommunication sock)
|
||
: base(sock)
|
||
{
|
||
}
|
||
|
||
public QuerySerialNumber(Common.Interface.DASFactory.ICommunication sock, int TimeoutMillisec)
|
||
: base(sock, TimeoutMillisec)
|
||
{
|
||
}
|
||
|
||
protected override CommandReceiveAction WholePackage()
|
||
{
|
||
// we have a whole package
|
||
if (response.Status == DFConstantsAndEnums.CommandStatus.StatusNoError)
|
||
{
|
||
response.GetParameter(0, out _serialnumber);
|
||
}
|
||
return CommandReceiveAction.StopReceiving;
|
||
}
|
||
|
||
public override void ResponseToString(ref List<List<string>> lines)
|
||
{
|
||
base.ResponseToString(ref lines);
|
||
lines.Add(new List<string>() { $"SerialNumber: {SerialNumber}" });
|
||
}
|
||
}
|
||
|
||
public class SetSerialNumber : InformationCommands
|
||
{
|
||
private string _sn = string.Empty;
|
||
|
||
protected override Commands _Command => Commands.SetSerialNumber;
|
||
|
||
public string SerialNumber
|
||
{
|
||
get => _sn;
|
||
set
|
||
{
|
||
_sn = value;
|
||
command.Parameter = new byte[value.Length + 1];
|
||
command.SetParameter(0, value.ToCharArray());
|
||
}
|
||
}
|
||
|
||
public SetSerialNumber(Common.Interface.DASFactory.ICommunication sock)
|
||
: base(sock)
|
||
{
|
||
}
|
||
|
||
public SetSerialNumber(Common.Interface.DASFactory.ICommunication sock, int TimeoutMillisec)
|
||
: base(sock, TimeoutMillisec)
|
||
{
|
||
}
|
||
|
||
public override void CommandToString(ref List<List<string>> lines)
|
||
{
|
||
base.CommandToString(ref lines);
|
||
lines.Add(new List<string>() { $"SerialNumber: {SerialNumber}" });
|
||
}
|
||
}
|
||
|
||
public class QueryOneWireID : InformationCommands
|
||
{
|
||
private const int StackChannelPosition = 0;
|
||
private const int OneWireIDLength = 8;
|
||
|
||
protected override Commands _Command => Commands.QueryOneWireID;
|
||
|
||
public List<byte[]> IDs { get; private set; } = new List<byte[]>();
|
||
|
||
private byte _stackchannel;
|
||
public byte StackChannel
|
||
{
|
||
get => _stackchannel;
|
||
set
|
||
{
|
||
_stackchannel = value;
|
||
command.SetParameter(StackChannelPosition, _stackchannel);
|
||
}
|
||
}
|
||
|
||
private bool _bSupportsIdTypes = false;
|
||
public enum IdTypes
|
||
{
|
||
Default = 0x0,
|
||
Bridge = 0x1,
|
||
IEPE = 0x2
|
||
};
|
||
|
||
private IdTypes _idType = IdTypes.Default;
|
||
public IdTypes IdType
|
||
{
|
||
get => _idType;
|
||
set
|
||
{
|
||
if (_bSupportsIdTypes)
|
||
{
|
||
_idType = value;
|
||
command.SetParameter(1, (byte)value);
|
||
}
|
||
}
|
||
}
|
||
|
||
public QueryOneWireID(Common.Interface.DASFactory.ICommunication sock)
|
||
: base(sock)
|
||
{
|
||
if (sock.IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.SLICE2_OneWireID))
|
||
{
|
||
command.Parameter = new byte[2];
|
||
_bSupportsIdTypes = true;
|
||
}
|
||
else { command.Parameter = new byte[1]; }
|
||
|
||
}
|
||
|
||
public QueryOneWireID(Common.Interface.DASFactory.ICommunication sock, int TimeoutMillisec)
|
||
: base(sock, TimeoutMillisec)
|
||
{
|
||
if (sock.IsCommandSupported(DFConstantsAndEnums.ProtocolLimitedCommands.SLICE2_OneWireID))
|
||
{
|
||
command.Parameter = new byte[2];
|
||
_bSupportsIdTypes = true;
|
||
}
|
||
else { command.Parameter = new byte[1]; }
|
||
}
|
||
|
||
|
||
protected override CommandReceiveAction WholePackage()
|
||
{
|
||
// we have a whole package
|
||
if (response.Status == DFConstantsAndEnums.CommandStatus.StatusNoError)
|
||
{
|
||
int numberOfIDs = response.ParameterLength / OneWireIDLength;
|
||
IDs = new List<byte[]>();
|
||
for (int i = 0; i < numberOfIDs; i++)
|
||
{
|
||
var id = new byte[OneWireIDLength];
|
||
var reversedId = new byte[OneWireIDLength];
|
||
Buffer.BlockCopy(response.Parameter, OneWireIDLength * i, reversedId, 0, OneWireIDLength);
|
||
|
||
for (int currentByte = reversedId.Length - 1; currentByte >= 0; currentByte--)
|
||
{
|
||
id[OneWireIDLength - currentByte - 1] = reversedId[currentByte];
|
||
}
|
||
|
||
IDs.Add(id);
|
||
}
|
||
}
|
||
return CommandReceiveAction.StopReceiving;
|
||
}
|
||
|
||
public override void CommandToString(ref List<List<string>> lines)
|
||
{
|
||
base.CommandToString(ref lines);
|
||
lines.Add(new List<string>() { $"Stack Channel: {StackChannel}" });
|
||
}
|
||
|
||
public override void ResponseToString(ref List<List<string>> lines)
|
||
{
|
||
base.ResponseToString(ref lines);
|
||
lines.Add(new List<string>()
|
||
{
|
||
$"Number of IDs: {IDs.Count}"
|
||
});
|
||
|
||
var ids = new List<string>();
|
||
for (int i = 0; null != IDs && i < IDs.Count; i++)
|
||
{
|
||
if (0 == i) { ids.Add($"IDS: {HexEncoding.ToString(IDs[i])}"); }
|
||
else { ids.Add(HexEncoding.ToString(IDs[i])); }
|
||
}
|
||
if (ids.Count > 0)
|
||
{
|
||
lines.Add(ids);
|
||
}
|
||
}
|
||
}
|
||
|
||
public class QueryMSP430FirmwareVersion : InformationCommands
|
||
{
|
||
private string _versionstring = string.Empty;
|
||
|
||
public string Version => _versionstring;
|
||
|
||
protected override Commands _Command => Commands.QueryMSP430FirmwareVersion;
|
||
|
||
public QueryMSP430FirmwareVersion(Common.Interface.DASFactory.ICommunication sock)
|
||
: base(sock)
|
||
{
|
||
}
|
||
|
||
public QueryMSP430FirmwareVersion(Common.Interface.DASFactory.ICommunication sock, int TimeoutMillisec)
|
||
: base(sock, TimeoutMillisec)
|
||
{
|
||
}
|
||
|
||
protected override CommandReceiveAction WholePackage()
|
||
{
|
||
// we have a whole package
|
||
if (response.Status == DFConstantsAndEnums.CommandStatus.StatusNoError)
|
||
{
|
||
response.GetParameter(0, out _versionstring);
|
||
}
|
||
return CommandReceiveAction.StopReceiving;
|
||
}
|
||
|
||
public override void ResponseToString(ref List<List<string>> lines)
|
||
{
|
||
base.ResponseToString(ref lines);
|
||
lines.Add(new List<string>() { Version });
|
||
}
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// Command ID 0x0D – Information FILE Data Set
|
||
/// STU shall accept data from USER and save to file ID indicated in message.
|
||
/// </summary>
|
||
public class SetFileData : InformationCommands
|
||
{
|
||
protected override Commands _Command => Commands.SetFileData;
|
||
|
||
#region Private Variables
|
||
private ushort _fileID; //Byte 0-1: 16-bit file ID ranges from 1-10.
|
||
private uint _startByteCount;
|
||
private byte[] _data;
|
||
private static byte PARAM_NUM = 6;
|
||
#endregion
|
||
|
||
#region Command Parameters
|
||
public ushort FileID
|
||
{
|
||
get => _fileID;
|
||
set
|
||
{
|
||
if (MAX_FILE_ID < value)
|
||
{
|
||
throw new NotSupportedException($"FileId: {value.ToString()} > {MAX_FILE_ID.ToString()}");
|
||
}
|
||
_fileID = value;
|
||
command.SetParameter(0, _fileID);
|
||
}
|
||
}
|
||
|
||
public uint StartByteCount
|
||
{
|
||
get => _startByteCount;
|
||
set { _startByteCount = value; command.SetParameter(2, _startByteCount); }
|
||
}
|
||
|
||
/// <summary>
|
||
/// This is the TMT file ID, per 14531 Implement TMATS support for S6A stream on boot
|
||
/// </summary>
|
||
public const ushort TMT_FILE_ID = 100;
|
||
public byte[] Data
|
||
{
|
||
get => _data;
|
||
set
|
||
{
|
||
if (null == value)
|
||
{
|
||
return;
|
||
}
|
||
|
||
var maxLength = MAX_FILE_LENGTH;
|
||
if (FileID == TMT_FILE_ID)
|
||
{
|
||
maxLength = MAX_FILE_LENGTH_ID100;
|
||
if (recorder is ITMATSStreamingDevice tmatsStreamer)
|
||
{
|
||
maxLength = tmatsStreamer.GetMaxFileLengthTMATS();
|
||
}
|
||
}
|
||
if (maxLength < value.Length)
|
||
{
|
||
throw new ArgumentOutOfRangeException($"File length: {value.Length} > {maxLength.ToString()}");
|
||
}
|
||
|
||
_data = value;
|
||
//if (this.command.Parameter.Length < value.Length+ PARAM_NUM)
|
||
//{
|
||
var newparameter = new byte[value.Length + PARAM_NUM];
|
||
Buffer.BlockCopy(command.Parameter, 0, newparameter, 0, PARAM_NUM);
|
||
Size = value.Length;
|
||
command.Parameter = newparameter;
|
||
Buffer.BlockCopy(_data, 0, command.Parameter, PARAM_NUM, _data.Length);
|
||
//}
|
||
}
|
||
}
|
||
|
||
public int Size { get; private set; }
|
||
|
||
public int MaximumFileStreamBytes => MAX_FILE_LENGTH;
|
||
|
||
#endregion
|
||
|
||
#region Command Functions
|
||
public SetFileData(Common.Interface.DASFactory.ICommunication sock)
|
||
: base(sock)
|
||
{
|
||
command.Parameter = new byte[PARAM_NUM];
|
||
MinimumProtocolVersion = sock.GetMinProto(DFConstantsAndEnums.ProtocolLimitedCommands.FileData);
|
||
_data = null;
|
||
|
||
_fileID = 0;
|
||
_startByteCount = 0;
|
||
|
||
command.ShouldLog = false;
|
||
}
|
||
|
||
public SetFileData(Common.Interface.DASFactory.ICommunication sock, int TimeoutMillisec)
|
||
: base(sock, TimeoutMillisec)
|
||
{
|
||
command.Parameter = new byte[PARAM_NUM];
|
||
MinimumProtocolVersion = sock.GetMinProto(DFConstantsAndEnums.ProtocolLimitedCommands.FileData);
|
||
_data = null;
|
||
|
||
_fileID = 0;
|
||
_startByteCount = 0;
|
||
|
||
command.ShouldLog = false;
|
||
}
|
||
#endregion
|
||
|
||
#region Log Functions
|
||
public override void CommandToString(ref List<List<string>> lines)
|
||
{
|
||
base.CommandToString(ref lines);
|
||
lines.Add(new List<string>() { $"Store ID: {FileID}, Start Byte: {StartByteCount}, End Byte: {Size + StartByteCount}" });
|
||
}
|
||
|
||
public void LogResponse()
|
||
{
|
||
LogCommand(false);
|
||
}
|
||
#endregion
|
||
}
|
||
/// <summary>
|
||
/// Command ID 0x0E – Information FILE Data Query
|
||
/// STU shall respond with data requested by host similar to download process.
|
||
/// </summary>
|
||
public class QueryFileData : InformationCommands
|
||
{
|
||
protected override Commands _Command => Commands.QueryFileData;
|
||
|
||
#region Private Variables
|
||
|
||
private ushort _fileID; //Byte 0-1: 16-bit file ID ranges from 1-10.
|
||
private uint _startByteCount; //Byte 2-5: 32-bit StartBytecount which ranges from 0 to EndBytecount
|
||
private uint _endByteCount; //Byte 6-9: 32-bit EndBytecount.
|
||
|
||
#endregion
|
||
|
||
#region Command Parameters
|
||
public ushort FileID
|
||
{
|
||
get => _fileID;
|
||
set
|
||
{
|
||
if (MAX_FILE_ID < value)
|
||
{
|
||
throw new ArgumentOutOfRangeException($"FileId {value.ToString()} > {MAX_FILE_ID.ToString()}");
|
||
}
|
||
_fileID = value;
|
||
command.SetParameter(0, _fileID);
|
||
}
|
||
}
|
||
public uint StartByteCount
|
||
{
|
||
get => _startByteCount;
|
||
set { _startByteCount = value; command.SetParameter(2, _startByteCount); }
|
||
}
|
||
public uint EndByteCount
|
||
{
|
||
get => _endByteCount;
|
||
set { _endByteCount = value; command.SetParameter(6, _endByteCount); }
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Command Variables
|
||
public uint BytesDownloaded { get; set; }
|
||
public byte[] Data { get; private set; }
|
||
|
||
public int MaximumFileStreamBytes => MAX_FILE_LENGTH;
|
||
|
||
public int MaxFileReadStreamBytes => MAX_FILE_READ_LENGTH;
|
||
|
||
#endregion
|
||
|
||
#region Command Functions
|
||
public QueryFileData(Common.Interface.DASFactory.ICommunication sock) : base(sock)
|
||
{
|
||
command.Parameter = new byte[10];
|
||
MinimumProtocolVersion = sock.GetMinProto(DFConstantsAndEnums.ProtocolLimitedCommands.FileData);
|
||
Data = null;
|
||
|
||
_fileID = 0;
|
||
_startByteCount = 0;
|
||
_endByteCount = 0;
|
||
|
||
command.ShouldLog = false;
|
||
}
|
||
|
||
public QueryFileData(Common.Interface.DASFactory.ICommunication sock, int TimeoutMillisec) : base(sock, TimeoutMillisec)
|
||
{
|
||
command.Parameter = new byte[10];
|
||
MinimumProtocolVersion = sock.GetMinProto(DFConstantsAndEnums.ProtocolLimitedCommands.FileData);
|
||
Data = null;
|
||
|
||
_fileID = 0;
|
||
_startByteCount = 0;
|
||
_endByteCount = 0;
|
||
|
||
command.ShouldLog = false;
|
||
}
|
||
|
||
protected override CommandReceiveAction WholePackage()
|
||
{
|
||
if (response.Status != DFConstantsAndEnums.CommandStatus.StatusNoError)
|
||
{
|
||
return CommandReceiveAction.StopReceiving;
|
||
}
|
||
BytesDownloaded = (uint)(response.Parameter.Length);
|
||
Data = new byte[BytesDownloaded];
|
||
for (int i = 0; (uint)i < BytesDownloaded; i++)
|
||
{
|
||
response.GetParameter(i, out Data[i]);
|
||
}
|
||
return CommandReceiveAction.StopReceiving;
|
||
}
|
||
|
||
public override void SyncExecute()
|
||
{
|
||
// Do a little parameter checking
|
||
if (_startByteCount > _endByteCount)
|
||
{
|
||
// "QueryEventData.SyncExecute: First Sample cannot be greater than Last Sample"
|
||
throw new ApplicationException(Strings.QueryEventData_SyncExecute_Err1);
|
||
}
|
||
base.SyncExecute();
|
||
}
|
||
#endregion
|
||
|
||
#region Log Functions
|
||
public override void CommandToString(ref List<List<string>> lines)
|
||
{
|
||
base.CommandToString(ref lines);
|
||
lines.Add(new List<string>() { $"Store ID: {FileID}, Start Byte: {StartByteCount}, End Byte: {EndByteCount}" });
|
||
}
|
||
|
||
public override void ResponseToString(ref List<List<string>> lines)
|
||
{
|
||
base.ResponseToString(ref lines);
|
||
lines.Add(new List<string>()
|
||
{
|
||
$"Store ID: {FileID}, BytesDownloaded: {BytesDownloaded}"
|
||
});
|
||
}
|
||
|
||
public void LogResponse()
|
||
{
|
||
LogCommand(false);
|
||
}
|
||
#endregion
|
||
}
|
||
|
||
public class QueryStackSensorIDs : InformationCommands
|
||
{
|
||
private const byte StackChannelPosition = 0;
|
||
private const byte ParameterLength = 1;
|
||
private const byte ChannelDataLength = 10;
|
||
private const byte IDLength = 8;
|
||
private const byte TypeIndex = 9;
|
||
private const byte ChannelIndex = 10;
|
||
|
||
|
||
protected override Commands _Command => Commands.QueryStackSensorIDs;
|
||
|
||
public List<byte[]> IDs { get; private set; } = new List<byte[]>();
|
||
public List<byte> Types { get; private set; } = new List<byte>();
|
||
public List<byte> Channels { get; private set; } = new List<byte>();
|
||
|
||
private byte _stackchannel;
|
||
public byte StackChannel
|
||
{
|
||
get => _stackchannel;
|
||
set
|
||
{
|
||
_stackchannel = value;
|
||
command.SetParameter(StackChannelPosition, _stackchannel);
|
||
}
|
||
}
|
||
|
||
public QueryStackSensorIDs(Common.Interface.DASFactory.ICommunication sock)
|
||
: base(sock)
|
||
{
|
||
command.Parameter = new byte[ParameterLength];
|
||
MinimumProtocolVersion = sock.GetMinProto(DFConstantsAndEnums.ProtocolLimitedCommands.StackSensors);
|
||
}
|
||
|
||
public QueryStackSensorIDs(Common.Interface.DASFactory.ICommunication sock, int TimeoutMillisec)
|
||
: base(sock, TimeoutMillisec)
|
||
{
|
||
command.Parameter = new byte[ParameterLength];
|
||
MinimumProtocolVersion = sock.GetMinProto(DFConstantsAndEnums.ProtocolLimitedCommands.StackSensors);
|
||
}
|
||
|
||
public void Exec()
|
||
{
|
||
SyncExecute();
|
||
}
|
||
protected override CommandReceiveAction WholePackage()
|
||
{
|
||
// we have a whole package
|
||
if (response.Status == DFConstantsAndEnums.CommandStatus.StatusNoError)
|
||
{
|
||
int numberOfIDs = response.ParameterLength / ChannelDataLength;
|
||
IDs = new List<byte[]>();
|
||
Types = new List<byte>();
|
||
Channels = new List<byte>();
|
||
|
||
for (int i = 0; i < numberOfIDs; i++)
|
||
{
|
||
var data = new byte[ChannelDataLength];
|
||
var reversed_data = new byte[ChannelDataLength];
|
||
var id = new byte[IDLength];
|
||
var type = new byte();
|
||
var channel = new byte();
|
||
Buffer.BlockCopy(response.Parameter, ChannelDataLength * i, reversed_data, 0, ChannelDataLength);
|
||
|
||
for (int currentByte = reversed_data.Length - 1; currentByte >= 0; currentByte--)
|
||
{
|
||
data[ChannelDataLength - currentByte - 1] = reversed_data[currentByte];
|
||
}
|
||
for (int currentByte = 0; currentByte < data.Length; currentByte++)
|
||
{
|
||
if (currentByte < IDLength)
|
||
{
|
||
id[currentByte] = data[currentByte];
|
||
}
|
||
else if (currentByte < TypeIndex)
|
||
{
|
||
type = data[currentByte];
|
||
}
|
||
else
|
||
{
|
||
channel = data[currentByte];
|
||
}
|
||
}
|
||
|
||
IDs.Add(id);
|
||
Types.Add(type);
|
||
Channels.Add(channel);
|
||
}
|
||
|
||
}
|
||
return CommandReceiveAction.StopReceiving;
|
||
}
|
||
|
||
public override void CommandToString(ref List<List<string>> lines)
|
||
{
|
||
base.CommandToString(ref lines);
|
||
lines.Add(new List<string>()
|
||
{
|
||
$"Stack Channel: {StackChannel}"
|
||
});
|
||
}
|
||
|
||
public override void ResponseToString(ref List<List<string>> lines)
|
||
{
|
||
base.ResponseToString(ref lines);
|
||
lines.Add(new List<string>()
|
||
{
|
||
$"Number of Stack Channels: {Channels.Count}"
|
||
});
|
||
|
||
var info = new List<string>();
|
||
for (int i = 0; null != IDs && i < IDs.Count; i++)
|
||
{
|
||
info.Add($"Channel: {Channels[i].ToString()}, Type {Types[i].ToString()}, IDS: {HexEncoding.ToString(IDs[i])}");
|
||
}
|
||
if (info.Count > 0)
|
||
{
|
||
lines.Add(info);
|
||
}
|
||
}
|
||
}
|
||
public class QueryBaseSystemTime : InformationCommands
|
||
{
|
||
public byte[] Calendar { get; } = new byte[7];
|
||
|
||
private DateTime _systemTime = DateTime.MinValue;
|
||
public DateTime SystemTime
|
||
{
|
||
get
|
||
{
|
||
if (_systemTime == DateTime.MinValue)
|
||
{
|
||
var cal = new byte[7];
|
||
|
||
for (int i = 0; i < cal.Length; i++)
|
||
{
|
||
cal[i] = Calendar[i];
|
||
}
|
||
// convert from BCD to binary
|
||
uint dec, dec2;
|
||
for (int i = 0; i < 7; i++)
|
||
{
|
||
dec = (uint)cal[i] & 0x0f;
|
||
dec2 = (uint)cal[i] >> 4;
|
||
dec2 = dec2 * 10;
|
||
dec = dec + dec2;
|
||
cal[i] = (byte)dec;
|
||
}
|
||
_systemTime = new DateTime(cal[6] + 2000, // year
|
||
cal[5], // month
|
||
cal[3], // day
|
||
cal[2], // hh
|
||
cal[1], // mm
|
||
cal[0]); // ss
|
||
}
|
||
return _systemTime;
|
||
}
|
||
}
|
||
|
||
protected override Commands _Command => Commands.QueryBaseSystemTime;
|
||
|
||
public QueryBaseSystemTime(Common.Interface.DASFactory.ICommunication sock)
|
||
: base(sock)
|
||
{
|
||
MinimumProtocolVersion = sock.GetMinProto(DFConstantsAndEnums.ProtocolLimitedCommands.BaseSystemTime);
|
||
}
|
||
|
||
public QueryBaseSystemTime(Common.Interface.DASFactory.ICommunication sock, int TimeoutMillisec)
|
||
: base(sock, TimeoutMillisec)
|
||
{
|
||
MinimumProtocolVersion = sock.GetMinProto(DFConstantsAndEnums.ProtocolLimitedCommands.BaseSystemTime);
|
||
}
|
||
|
||
protected override CommandReceiveAction WholePackage()
|
||
{
|
||
// we have a whole package
|
||
if (response.Status == DFConstantsAndEnums.CommandStatus.StatusNoError)
|
||
{
|
||
for (int i = 0; i < 7; i++)
|
||
{
|
||
response.GetParameter(i, out Calendar[i]);
|
||
}
|
||
}
|
||
return CommandReceiveAction.StopReceiving;
|
||
}
|
||
public override void ResponseToString(ref List<List<string>> lines)
|
||
{
|
||
base.ResponseToString(ref lines);
|
||
lines.Add(new List<string>() { $"System Time: {SystemTime}" });
|
||
}
|
||
}
|
||
|
||
public class SetBaseSystemTime : InformationCommands
|
||
{
|
||
private DateTime _systemTime = new DateTime();
|
||
|
||
protected override Commands _Command => Commands.SetBaseSystemTime;
|
||
|
||
public DateTime SystemTime
|
||
{
|
||
get => _systemTime;
|
||
set
|
||
{
|
||
var timestream = new byte[7];
|
||
timestream[0] = (byte)value.Second;
|
||
timestream[1] = (byte)value.Minute;
|
||
timestream[2] = (byte)value.Hour;
|
||
timestream[3] = (byte)value.Day;
|
||
timestream[4] = (byte)value.DayOfWeek;
|
||
timestream[5] = (byte)value.Month;
|
||
timestream[6] = (byte)(value.Year - 2000);
|
||
// convert binary to BCD
|
||
uint bcd, bcd2;
|
||
for (int i = 0; i < 7; i++)
|
||
{
|
||
bcd = ((uint)timestream[i]) / 10;
|
||
bcd2 = ((uint)timestream[i]) % 10;
|
||
bcd = (bcd << 4) + bcd2;
|
||
timestream[i] = (byte)bcd;
|
||
}
|
||
command.Parameter = timestream;
|
||
_systemTime = value;
|
||
}
|
||
}
|
||
|
||
public SetBaseSystemTime(Common.Interface.DASFactory.ICommunication sock)
|
||
: base(sock)
|
||
{
|
||
MinimumProtocolVersion = sock.GetMinProto(DFConstantsAndEnums.ProtocolLimitedCommands.BaseSystemTime);
|
||
}
|
||
|
||
public SetBaseSystemTime(Common.Interface.DASFactory.ICommunication sock, int TimeoutMillisec)
|
||
: base(sock, TimeoutMillisec)
|
||
{
|
||
MinimumProtocolVersion = sock.GetMinProto(DFConstantsAndEnums.ProtocolLimitedCommands.BaseSystemTime);
|
||
}
|
||
|
||
public override void CommandToString(ref List<List<string>> lines)
|
||
{
|
||
base.CommandToString(ref lines);
|
||
lines.Add(new List<string>()
|
||
{
|
||
$"System Time: {SystemTime}"
|
||
});
|
||
}
|
||
}
|
||
|
||
/*
|
||
* cmdInfo_queryTempLogfile - query tempurature logfile in S6DB
|
||
*
|
||
* @param
|
||
* uint8_t cmd. No param or param value = 0. Readonly. 1 = readclear.
|
||
* uint8_t u8-rsv; // for future subcommand.
|
||
* uint32_t time offset from the current time in second.
|
||
* uint32_t timeROI in second.
|
||
* for example:
|
||
* u8 cmd = 0.
|
||
* u8 rsv. Maybe sampleByteSize;
|
||
* u32 timeOffset = 0. Start from last sample taken.
|
||
* u32 timeDurationSec = 120. Asking for sample in last two hours.
|
||
*
|
||
* @return Response status with logging data in payload.
|
||
* Data structure in payload.
|
||
* - u32 timeOffset; // confirmed with software command.
|
||
* // samplePeriodInSec can be tracked with system attr.
|
||
* // channelMask can be tracked with system attribute.
|
||
* - Repeated structure of following:
|
||
* {
|
||
* . uint32_t RTC Timestamp-Second.
|
||
* . float32_t temp1;
|
||
* . float32_t temp2;
|
||
* }
|
||
*
|
||
* Note: Default of 2 ADC channel.
|
||
* 48hr*60min/hr
|
||
**/
|
||
public class QueryTempLogFile : InformationCommands
|
||
{
|
||
//private const byte ParameterLength = 1;
|
||
uint _timeOffsetSec = 0;
|
||
uint _timeDurationSec = 48 * 60 * 60; // full log
|
||
byte _readClear = 0;
|
||
byte _rsvCmd = 0;
|
||
|
||
public uint TimeOffsetFromNowSec
|
||
{
|
||
get => _timeOffsetSec;
|
||
set
|
||
{
|
||
_timeOffsetSec = value;
|
||
command.SetParameter(2, _timeOffsetSec);
|
||
}
|
||
}
|
||
|
||
public uint TimeDurationSec
|
||
{
|
||
get => _timeDurationSec;
|
||
set
|
||
{
|
||
_timeDurationSec = value;
|
||
command.SetParameter(6, _timeDurationSec);
|
||
}
|
||
}
|
||
|
||
public byte ReadClear
|
||
{
|
||
get => _readClear;
|
||
set
|
||
{
|
||
_readClear = value;
|
||
command.SetParameter(0, _readClear);
|
||
}
|
||
}
|
||
|
||
public byte ReadRsvCmd
|
||
{
|
||
get => _rsvCmd;
|
||
set
|
||
{
|
||
_rsvCmd = value;
|
||
command.SetParameter(1, _rsvCmd);
|
||
}
|
||
}
|
||
|
||
protected override Commands _Command => Commands.QueryTempLogFile;
|
||
|
||
|
||
|
||
|
||
public QueryTempLogFile(Common.Interface.DASFactory.ICommunication sock)
|
||
: base(sock)
|
||
{
|
||
command.Parameter = new byte[10];
|
||
MinimumProtocolVersion = sock.GetMinProto(DFConstantsAndEnums.ProtocolLimitedCommands.QueryTempLogFile);
|
||
}
|
||
|
||
public QueryTempLogFile(Common.Interface.DASFactory.ICommunication sock, int TimeoutMillisec)
|
||
: base(sock, TimeoutMillisec)
|
||
{
|
||
command.Parameter = new byte[10];
|
||
MinimumProtocolVersion = sock.GetMinProto(DFConstantsAndEnums.ProtocolLimitedCommands.QueryTempLogFile);
|
||
}
|
||
|
||
public static DateTime UnixTimeStampToDateTime(uint unixTimeStamp)
|
||
{
|
||
// Unix timestamp is seconds past epoch
|
||
DateTime dtDateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
|
||
dtDateTime = dtDateTime.AddSeconds(unixTimeStamp).ToLocalTime();
|
||
return dtDateTime;
|
||
}
|
||
|
||
public List<Tuple<DateTime, double, double, uint>> TempData = new List<Tuple<DateTime, double, double, uint>>();
|
||
|
||
protected override CommandReceiveAction WholePackage()
|
||
{
|
||
// we have a whole package
|
||
if (response.Status != DFConstantsAndEnums.CommandStatus.StatusNoError
|
||
|| response.ParameterLength == 0) return CommandReceiveAction.StopReceiving;
|
||
response.GetParameter(2, out int dataOffset);
|
||
var sampleCount = (response.ParameterLength - 6) / 12;
|
||
|
||
uint curTicks;
|
||
float channel1Sample;
|
||
float channel2Sample;
|
||
for (int i = 6; i < response.ParameterLength - 6; i += 12)
|
||
{
|
||
response.GetParameter(i + 0, out curTicks);
|
||
|
||
response.GetParameter(i + 4, out channel1Sample);
|
||
response.GetParameter(i + 8, out channel2Sample);
|
||
TempData.Add(new Tuple<DateTime, double, double, uint>(UnixTimeStampToDateTime(curTicks).ToLocalTime(), channel1Sample, channel2Sample, curTicks));
|
||
}
|
||
TempData = TempData.OrderBy(a => a.Item1).ToList();
|
||
|
||
// check to see if any dates are bogus
|
||
var badDates = TempData.Where(x => x.Item1.Year < 1990).Select(x => x.Item1).ToList();
|
||
if (badDates.Count == 1)
|
||
{
|
||
// fix the single date
|
||
TempData[0] = TempDataReplaceDate(TempData[0], DateTime.Now - new TimeSpan(0, 1, 0));
|
||
}
|
||
else if (badDates.Count > 1)
|
||
{
|
||
// we need to extrapolate from a known good time stamp
|
||
var goodDate = DateTime.Now;
|
||
|
||
// lets see how far back our data should go
|
||
var lastBadSpan = badDates[badDates.Count - 1] - badDates[badDates.Count - 2];
|
||
var badDateTimeSpan = badDates.Last() - badDates[0] + lastBadSpan;
|
||
|
||
if (TempData.Exists(x => x.Item1.Year > 1990))
|
||
{
|
||
// if we have a good date, use it to seed the bad date extrapolation
|
||
goodDate = TempData.Where(x => x.Item1.Year > 1990).Select(x => x.Item1).ToList()[0];
|
||
}
|
||
|
||
// Fix the first data point
|
||
TempData[0] = TempDataReplaceDate(TempData[0], goodDate - badDateTimeSpan);
|
||
|
||
// Fix all the data points in between
|
||
for (int i = 1; i < TempData.Select(x => x.Item1).ToList().IndexOf(badDates.Last()); i++)
|
||
{
|
||
TempData[i] = TempDataReplaceDate(TempData[i], TempData[i - 1].Item1 + (TempData[i + 1].Item1 - TempData[i].Item1));
|
||
}
|
||
|
||
// fix the last data point
|
||
var indesxOfLastBadDate = TempData.Select(x => x.Item1).ToList().IndexOf(badDates.Last());
|
||
TempData[indesxOfLastBadDate] = TempDataReplaceDate(TempData[indesxOfLastBadDate], goodDate - lastBadSpan);
|
||
}
|
||
return CommandReceiveAction.StopReceiving;
|
||
}
|
||
public override void ResponseToString(ref List<List<string>> lines)
|
||
{
|
||
base.ResponseToString(ref lines);
|
||
lines.Add(new List<string>() { $"QueryTempLogFile: From:{TimeOffsetFromNowSec}s Duration:{TimeDurationSec}s Samples Returned: {TempData.Count}" });
|
||
}
|
||
/// <summary>
|
||
/// Only replaces the new date and returns the rest of the tuple for the temp log point
|
||
/// </summary>
|
||
/// <param name="origTuple"></param>
|
||
/// <param name="dateTime"></param>
|
||
/// <returns></returns>
|
||
private Tuple<DateTime, double, double, uint> TempDataReplaceDate(Tuple<DateTime, double, double, uint> origTuple, DateTime dateTime)
|
||
{
|
||
return new Tuple<DateTime, double, double, uint>(dateTime, origTuple.Item2, origTuple.Item3, origTuple.Item4);
|
||
}
|
||
}
|
||
public class QueryEeprom : InformationCommands
|
||
{
|
||
private byte _eeprom_id = 0;
|
||
private ushort _data_offset = 0;
|
||
private ushort _data_length = 0;
|
||
private byte[] _data;
|
||
|
||
public byte ID
|
||
{
|
||
set
|
||
{
|
||
_eeprom_id = value;
|
||
command.SetParameter(0, value);
|
||
}
|
||
|
||
get => _eeprom_id;
|
||
}
|
||
|
||
public ushort Data_Offset
|
||
{
|
||
get => _data_offset;
|
||
set
|
||
{
|
||
_data_offset = value;
|
||
command.SetParameter(1, _data_offset);
|
||
}
|
||
}
|
||
|
||
public ushort DataLength
|
||
{
|
||
get => _data_length;
|
||
set
|
||
{
|
||
_data_length = value;
|
||
command.SetParameter(3, _data_length);
|
||
}
|
||
}
|
||
|
||
public byte[] Data
|
||
{
|
||
set => _data = value;
|
||
|
||
get => _data;
|
||
}
|
||
|
||
protected override Commands _Command => Commands.QueryOneWireEEPROM;
|
||
public QueryEeprom(Common.Interface.DASFactory.ICommunication sock)
|
||
: base(sock)
|
||
{
|
||
command.Parameter = new byte[5];
|
||
}
|
||
|
||
public QueryEeprom(Common.Interface.DASFactory.ICommunication sock, int TimeoutMillisec)
|
||
: base(sock, TimeoutMillisec)
|
||
{
|
||
command.Parameter = new byte[5];
|
||
}
|
||
|
||
protected override CommandReceiveAction WholePackage()
|
||
{
|
||
if (response.Status == DFConstantsAndEnums.CommandStatus.StatusNoError)
|
||
{
|
||
response.GetParameter(0, out _eeprom_id);
|
||
response.GetParameter(1, out _data_offset);
|
||
response.GetParameter(3, out _data_length);
|
||
_data = new byte[_data_length];
|
||
|
||
for (int i = 0; i < _data_length; i++)
|
||
{
|
||
response.GetParameter(5 + i, out _data[i]);
|
||
}
|
||
}
|
||
return CommandReceiveAction.StopReceiving;
|
||
}
|
||
|
||
public override void ResponseToString(ref List<List<string>> lines)
|
||
{
|
||
base.ResponseToString(ref lines);
|
||
lines.Add(new List<string>() { string.Format("Data: {0}", Encoding.UTF8.GetString(_data)) });
|
||
}
|
||
}
|
||
|
||
public class SetEeprom : InformationCommands
|
||
{
|
||
private byte _eeprom_id = 0;
|
||
private ushort _data_offset = 0;
|
||
private ushort _data_length = 0;
|
||
private string _data = string.Empty;
|
||
|
||
protected override Commands _Command => Commands.SetOneWireEEPROM;
|
||
|
||
public byte ID
|
||
{
|
||
set => _eeprom_id = value;
|
||
|
||
get => _eeprom_id;
|
||
}
|
||
|
||
public ushort Data_Offset
|
||
{
|
||
get => _data_offset;
|
||
set => _data_offset = value;
|
||
}
|
||
|
||
public ushort DataLength
|
||
{
|
||
get => _data_length;
|
||
set => _data_length = value;
|
||
}
|
||
|
||
public string Data
|
||
{
|
||
set => _data = value;
|
||
|
||
get => _data;
|
||
}
|
||
public void SetEEPROM()
|
||
{
|
||
command.Parameter = new byte[6 + _data_length];
|
||
for (int i = 0; i < _data_length; i++)
|
||
{
|
||
command.SetParameter(5 + i, _data[i]);
|
||
}
|
||
command.SetParameter(0, _eeprom_id);
|
||
command.SetParameter(1, _data_offset);
|
||
command.SetParameter(3, _data_length);
|
||
}
|
||
|
||
public SetEeprom(Common.Interface.DASFactory.ICommunication sock)
|
||
: base(sock)
|
||
{
|
||
}
|
||
|
||
public SetEeprom(Common.Interface.DASFactory.ICommunication sock, int TimeoutMillisec)
|
||
: base(sock, TimeoutMillisec)
|
||
{
|
||
}
|
||
|
||
public override void CommandToString(ref List<List<string>> lines)
|
||
{
|
||
base.CommandToString(ref lines);
|
||
lines.Add(new List<string>() { string.Format("Data: {0}", _data) });
|
||
}
|
||
}
|
||
}
|