using Edge.Core.Parser.BinaryParser.Util; using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text; namespace VeederRoot_ATG_Console.MessageEntity.Incoming { /// /// i|I201 /// public class QueryInTankInventoryReportResponse : IncomingMessageBase { 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 int? TankNumberInFunctionCode { get { if (base.FunctionCode.Item1 == MessageFormat.Display) return null; var r = int.Parse(base.FunctionCode.Item3); return r; } } public List InventoryDatas { get { // 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); /* * TT - Tank Number (Decimal, 00=all) - 2 bytes * p - Product Code (one ASCII character [20h-7Eh]) - 1 bytes * ssss - Tank Status Bits - 4 bytes * NN - Number of eight character Data Fields to follow (Hex) - 2 bytes * * ABOVE TOTAL LEN IS 9 */ var datas = new List(); int previousTotalDataCount = 0; while (true) { var buffer = rawDataBody.Skip(previousTotalDataCount); if (buffer.Count() < 7) throw new ArgumentException("Min bytes count for describe an i201 tank report is 7, but now is " + buffer.Count()); var numberOfEightCharDataFieldsValue = int.Parse(Encoding.ASCII.GetString(buffer.Skip(7).Take(2).ToArray())); if (numberOfEightCharDataFieldsValue < 1 || numberOfEightCharDataFieldsValue > 99) throw new ArgumentException("number Of Eight Char DataFields to follow must range from 1 to 99, but now is: " + numberOfEightCharDataFieldsValue); datas.Add(new InventoryData( buffer.Take(7 + 2 + 8 * numberOfEightCharDataFieldsValue).ToArray())); previousTotalDataCount += 7 + 2 + 8 * numberOfEightCharDataFieldsValue; if (previousTotalDataCount == rawDataBody.Count()) break; } return datas; } } public override string ToLogString() { if (base.FunctionCode.Item1 == MessageFormat.Display) return Encoding.ASCII.GetString(base.DataFieldAndOptionalCheckSumAndETX.ToArray()); return this.GetType().Name + " Current DateTime: " + this.CurrentDateAndTime.Value.ToString("yyyy-MM-dd HH:mm") + ", TankNumber_InFunctionCode: " + this.TankNumberInFunctionCode + ", data: " + this.InventoryDatas.Select(d => d.ToFullLogString()) .Aggregate((acc, n) => acc + " || " + n); } public class InventoryData { private byte[] bytes; public enum TankState { DeliveryInProgress_LSB, LeakTestInProgress, InvalidFuelHeighAlarm_MagProbesOnly, Unknown, } public enum TankReadingDataName { Volume = 1, TC_Volume = 2, Ullage = 3, Height = 4, Water = 5, Temperature = 6, WaterVolume = 7, } public InventoryData(byte[] bytes) { this.bytes = bytes; } public int? TankNumber { get { var r = int.Parse(Encoding.ASCII.GetString(this.bytes.Take(2).ToArray())); return r; } } public char? ProductCode { get { return Encoding.ASCII.GetString( this.bytes.Skip(2).Take(1).ToArray())[0]; } } public List States { get { /* 4 ascii encoded bytes, * check 16 bits to get the states * say now the raw data here is: 0x30,0x30,0x30,0x39 * then target byte here is the last byte of value 9 which binary format is 0000 1001. * so is bit 0 is 1, indicates the TankState.DeliveryInProgress_LSB */ var states = new List(); var tankStateBit = Util.ConvertHexBcdStrToBytes(this.bytes.Skip(3).Take(4).ToArray()); if (tankStateBit.Last().GetBit(0) == 1) states.Add(TankState.DeliveryInProgress_LSB); if (tankStateBit.Last().GetBit(1) == 1) states.Add(TankState.LeakTestInProgress); if (tankStateBit.Last().GetBit(2) == 1) states.Add(TankState.InvalidFuelHeighAlarm_MagProbesOnly); return states; } } public int? NumberOfEightCharDataFieldsToFollow { get { var nn = int.Parse(Encoding.ASCII.GetString(this.bytes.Skip(7).Take(2).ToArray())); //this.QueryInTankInventoryReportDatas = new List(nn); return nn; } } public Dictionary TankReadingDatas { get { var datas = new Dictionary(); for (int i = 0; i < this.NumberOfEightCharDataFieldsToFollow.Value; i++) { var asciiEncodedBytes = Util.ConvertHexBcdStrToBytes(this.bytes.Skip(9 + 8 * i).Take(8).ToArray()); var dataValue = Util.ConvertIEEEWith4BytesToDouble(asciiEncodedBytes.ToArray()); switch (i + 1) { case 1: // VOLUME datas.Add(TankReadingDataName.Volume, dataValue); break; case 2: // TC VOLUME datas.Add(TankReadingDataName.TC_Volume, dataValue); break; case 3: // ULLAGE datas.Add(TankReadingDataName.Ullage, dataValue); break; case 4: // HEIGHT datas.Add(TankReadingDataName.Height, dataValue); break; case 5: // WATER HEIGHT datas.Add(TankReadingDataName.Water, dataValue); break; case 6: // TEMPERATURE datas.Add(TankReadingDataName.Temperature, dataValue); break; case 7: // WATER VOLUME datas.Add(TankReadingDataName.WaterVolume, dataValue); break; } } return datas; } } public string ToFullLogString() { return "Tank with Number: " + (this.TankNumber ?? -1) + " with ProductCode: " + (this.ProductCode ?? ' ') + " with States: " + (this.States != null && this.States.Any() ? this.States.Select(s => s.ToString()).Aggregate((acc, n) => acc + "-" + n) : "") + ", data read: " + this.TankReadingDatas.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 ?? ' '); } } } }