using Edge.Core.IndustryStandardInterface.ATG;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
namespace VeederRoot_ATG_Console.MessageEntity.Incoming
{
///
/// i|I20C
///
public class QueryInTankMostRecentDeliveryReportResponse : IncomingMessageBase
{
public int? TankNumberInFunctionCode
{
get
{
if (base.FunctionCode.Item1 == MessageFormat.Display)
return null;
var r = int.Parse(base.FunctionCode.Item3);
return r;
}
}
public DateTime? CurrentDateAndTime
{
get
{
if (base.FunctionCode.Item1 == MessageFormat.Display)
return null;
return DateTime.ParseExact(
Encoding.ASCII.GetString(base.DataFieldAndOptionalCheckSumAndETX.Take(10).ToArray()),
"yyMMddHHmm", CultureInfo.InvariantCulture);
}
}
public List DeliveriesForTanks
{
get
{
/* 2 layer nested structure */
// skip 10 bytes of current date and time
var rawDataBody = base.DataFieldAndOptionalCheckSumAndETX.Skip(10)
//and exclude tail of -> && + 4 bytes check sum + ETX <- which is total 7 bytes.
.Take(base.DataFieldAndOptionalCheckSumAndETX.Count - 10 - 7);
var datas = new List();
int previousTankElapsedByteCount = 0;
while (true)
{
var buffer = rawDataBody.Skip(previousTankElapsedByteCount);
if (!buffer.Any()) return datas;
/*
* TT - Tank Number (Decimal, 00=all) - 2 bytes
* p - Product Code (one ASCII character [20h-7Eh]) - 1 bytes
* dd - Number of Deliveries to follow (Decimal, 00 if no data available for this tank) - 2 bytes
*
* ABOVE TOTAL LEN IS fixed 5
*/
//each tank section length is decided by a fixed length + variable length.
var noOfDeliversToFollow =
Util.ConvertHexBcdStrToBytes(buffer.Skip(3).Take(2).ToArray()).First();
int previousDeliveryElapsedByteCount = 0;
if (noOfDeliversToFollow == 0) { }
else
{
int deliveriesProcessed = 0;
//var deliveriesForTank = buffer.Skip(previousTankElapsedByteCount + 5);
while (true)
{
if (deliveriesProcessed == noOfDeliversToFollow) break;
var nextDelivery = buffer.Skip(5 + previousDeliveryElapsedByteCount + 10 + 10);
if (!nextDelivery.Any()) throw new ArgumentException("Empty bytes for continue parsing Delivery");
var numberOfEightCharDataFieldsValue
= Util.ConvertHexBcdStrToBytes(nextDelivery.Take(2).ToArray()).First();
previousDeliveryElapsedByteCount += 20 + 2 + numberOfEightCharDataFieldsValue * 8;
deliveriesProcessed++;
}
};
previousTankElapsedByteCount += 5 + previousDeliveryElapsedByteCount;
datas.Add(new DeliveriesForTank(
buffer.Take(previousDeliveryElapsedByteCount + 5).ToArray()));
}
}
}
public override string ToLogString()
{
return this.GetType().Name + " Deliveries: " +
(DeliveriesForTanks != null && DeliveriesForTanks.Any() ?
DeliveriesForTanks.Select(d => d.ToLogString()).Aggregate((acc, n) => acc + "||" + n)
: "");
}
public class DeliveriesForTank
{
private byte[] bytes;
///
///
///
/// TankNumber(2 bytes) + ProductCode(1 byte) + NoOfDeliveries(2 bytes)+ 20+2+No*8
public DeliveriesForTank(byte[] bytes)
{
this.bytes = bytes;
}
public int? TankNumber
{
get
{
var r = Encoding.ASCII.GetString(this.bytes.Take(2).ToArray());
return int.Parse(r);
}
}
public char? ProductCode
{
get
{
return
Encoding.ASCII.GetString(this.bytes.Skip(2).Take(1).ToArray()).First();
}
}
public int? NumberOfDeliveriesToFollow
{
get
{
var nn = Util.ConvertHexBcdStrToBytes(this.bytes.Skip(3).Take(2).ToArray()).First();
return nn;
}
}
public List Deliveries
{
get
{
if (this.NumberOfDeliveriesToFollow == 0) return null;
// exclude 5 bytes of TT + p + dd.
var loopParts = this.bytes.Skip(5);
var datas = new List();
int previousDeliveryByteCount = 0;
while (true)
{
var numberOfEightCharDataFieldsValue
= Util.ConvertHexBcdStrToBytes(
loopParts.Skip(previousDeliveryByteCount + 20).Take(2).ToArray()).First();
//if (numberOfEightCharDataFieldsValue < 1 || numberOfEightCharDataFieldsValue > 10)
// throw new ArgumentException("number Of Eight Char DataFields to follow must range from 1 to 10, but now is: " + numberOfEightCharDataFieldsValue);
datas.Add(
new Delivery(this.TankNumber,
loopParts.Skip(previousDeliveryByteCount)
.Take(10 + 10 + 2 + 8 * numberOfEightCharDataFieldsValue).ToArray()));
previousDeliveryByteCount += 10 + 10 + 2 + 8 * numberOfEightCharDataFieldsValue;
if (previousDeliveryByteCount == loopParts.Count()) break;
}
return datas;
}
}
public string ToLogString()
{
return "Tank with Number: " + (this.TankNumber ?? -1) + " has ProductCode: " + (this.ProductCode ?? ' ')
+ ", its NumberOfDeliveriesToFollow: " + (this?.NumberOfDeliveriesToFollow ?? 0)
+ " and details-> " +
(Deliveries != null && Deliveries.Any() ?
Deliveries.Select(d => d.ToFullLogString()).Aggregate((acc, n) => acc + "||" + n)
: "");
}
}
public class Delivery
{
//public enum ReadingDataName
//{
// StartingVolume = 1,
// StartingTcVolume = 2,
// StartingWater = 3,
// StartingTemp = 4,
// EndingVolume = 5,
// EndingTcVolume = 6,
// EndingWater = 7,
// EndingTemp = 8,
// StartingHeight = 9,
// EndingHeight = 10,
//}
private byte[] bytes;
public Delivery(int? tankNumber, byte[] bytes)
{
this.TankNumber = tankNumber;
this.bytes = bytes;
}
public int? TankNumber { get; }
public DateTime? StartingDateTime
{
get
{
return DateTime.ParseExact(
Encoding.ASCII.GetString(this.bytes.Take(10).ToArray()),
"yyMMddHHmm", CultureInfo.InvariantCulture);
}
}
public DateTime? EndingDateTime
{
get
{
return DateTime.ParseExact(
Encoding.ASCII.GetString(this.bytes.Take(10).ToArray()),
"yyMMddHHmm", CultureInfo.InvariantCulture);
}
}
public int? NumberOfEightCharDataFieldsToFollow
{
get
{
var nn = Util.ConvertHexBcdStrToBytes(this.bytes.Skip(20).Take(2).ToArray()).First();
return nn;
}
}
///
/// Get list of DelieveryReadingDataName:Value
///
public Dictionary Datas
{
get
{
var datas = new Dictionary();
for (int i = 0; i < this.NumberOfEightCharDataFieldsToFollow.Value; i++)
{
var asciiEncodedBytes = Util.ConvertHexBcdStrToBytes(this.bytes.Skip(20 + 2 + 8 * i).Take(8).ToArray());
var dataValue =
Util.ConvertIEEEWith4BytesToDouble(asciiEncodedBytes.ToArray());
switch (i + 1)
{
case 1: // Starting Volume
datas.Add(DeliveryReadingDataName.StartingVolume, dataValue);
break;
case 2: // Starting TC Volume
datas.Add(DeliveryReadingDataName.StartingTcVolume, dataValue);
break;
case 3: // Starting Water
datas.Add(DeliveryReadingDataName.StartingWater, dataValue);
break;
case 4: // Starting Temp
datas.Add(DeliveryReadingDataName.StartingTemp, dataValue);
break;
case 5: // Ending Volume
datas.Add(DeliveryReadingDataName.EndingVolume, dataValue);
break;
case 6: // Ending TC Volume
datas.Add(DeliveryReadingDataName.EndingTcVolume, dataValue);
break;
case 7: // Ending Water
datas.Add(DeliveryReadingDataName.EndingWater, dataValue);
break;
case 8: // Ending Temp
datas.Add(DeliveryReadingDataName.EndingTemp, dataValue);
break;
case 9: // Starting Height
datas.Add(DeliveryReadingDataName.StartingHeight, dataValue);
break;
case 10: // Ending Height
datas.Add(DeliveryReadingDataName.EndingHeight, dataValue);
break;
default:
// by seeing the real data from console, it may still hit here
// the reason is the "Number of eight character Data Fields to follow (Hex)" > 10.
// and meanwhile, extra bytes will be send in, may the VeederRoot leave it for extention
// in future?
break;
}
}
return datas;
}
}
public string ToFullLogString()
{
return "DeliveryDetail StartingDateTime: " + (this?.StartingDateTime?.ToString("yyMMddHHmm") ?? "")
+ ", EndingDateTime: " + (this?.EndingDateTime?.ToString("yyMMddHHmm") ?? "")
+ ", data(ReadingData:Value)-> "
+ this.Datas.Select(s => s.Key.ToString() + ":" + s.Value).Aggregate((acc, n) => acc + " | " + n);
}
//public string ToSimpleLogString()
//{
// return "Tank with Number: " + (this.TankNumber ?? -1) + " has ProductCode: " + (this.ProductCode ?? ' ');
//}
}
}
}