QueryInTankMostRecentDeliveryReportResponse.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. using Edge.Core.IndustryStandardInterface.ATG;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Globalization;
  5. using System.Linq;
  6. using System.Text;
  7. namespace VeederRoot_ATG_Console.MessageEntity.Incoming
  8. {
  9. /// <summary>
  10. /// i|I20C
  11. /// </summary>
  12. public class QueryInTankMostRecentDeliveryReportResponse : IncomingMessageBase
  13. {
  14. public int? TankNumberInFunctionCode
  15. {
  16. get
  17. {
  18. if (base.FunctionCode.Item1 == MessageFormat.Display)
  19. return null;
  20. var r = int.Parse(base.FunctionCode.Item3);
  21. return r;
  22. }
  23. }
  24. public DateTime? CurrentDateAndTime
  25. {
  26. get
  27. {
  28. if (base.FunctionCode.Item1 == MessageFormat.Display)
  29. return null;
  30. return DateTime.ParseExact(
  31. Encoding.ASCII.GetString(base.DataFieldAndOptionalCheckSumAndETX.Take(10).ToArray()),
  32. "yyMMddHHmm", CultureInfo.InvariantCulture);
  33. }
  34. }
  35. public List<DeliveriesForTank> DeliveriesForTanks
  36. {
  37. get
  38. {
  39. /* 2 layer nested structure */
  40. // skip 10 bytes of current date and time
  41. var rawDataBody = base.DataFieldAndOptionalCheckSumAndETX.Skip(10)
  42. //and exclude tail of -> && + 4 bytes check sum + ETX <- which is total 7 bytes.
  43. .Take(base.DataFieldAndOptionalCheckSumAndETX.Count - 10 - 7);
  44. var datas = new List<DeliveriesForTank>();
  45. int previousTankElapsedByteCount = 0;
  46. while (true)
  47. {
  48. var buffer = rawDataBody.Skip(previousTankElapsedByteCount);
  49. if (!buffer.Any()) return datas;
  50. /*
  51. * TT - Tank Number (Decimal, 00=all) - 2 bytes
  52. * p - Product Code (one ASCII character [20h-7Eh]) - 1 bytes
  53. * dd - Number of Deliveries to follow (Decimal, 00 if no data available for this tank) - 2 bytes
  54. *
  55. * ABOVE TOTAL LEN IS fixed 5
  56. */
  57. //each tank section length is decided by a fixed length + variable length.
  58. var noOfDeliversToFollow =
  59. Util.ConvertHexBcdStrToBytes(buffer.Skip(3).Take(2).ToArray()).First();
  60. int previousDeliveryElapsedByteCount = 0;
  61. if (noOfDeliversToFollow == 0) { }
  62. else
  63. {
  64. int deliveriesProcessed = 0;
  65. //var deliveriesForTank = buffer.Skip(previousTankElapsedByteCount + 5);
  66. while (true)
  67. {
  68. if (deliveriesProcessed == noOfDeliversToFollow) break;
  69. var nextDelivery = buffer.Skip(5 + previousDeliveryElapsedByteCount + 10 + 10);
  70. if (!nextDelivery.Any()) throw new ArgumentException("Empty bytes for continue parsing Delivery");
  71. var numberOfEightCharDataFieldsValue
  72. = Util.ConvertHexBcdStrToBytes(nextDelivery.Take(2).ToArray()).First();
  73. previousDeliveryElapsedByteCount += 20 + 2 + numberOfEightCharDataFieldsValue * 8;
  74. deliveriesProcessed++;
  75. }
  76. };
  77. previousTankElapsedByteCount += 5 + previousDeliveryElapsedByteCount;
  78. datas.Add(new DeliveriesForTank(
  79. buffer.Take(previousDeliveryElapsedByteCount + 5).ToArray()));
  80. }
  81. }
  82. }
  83. public override string ToLogString()
  84. {
  85. return this.GetType().Name + " Deliveries: " +
  86. (DeliveriesForTanks != null && DeliveriesForTanks.Any() ?
  87. DeliveriesForTanks.Select(d => d.ToLogString()).Aggregate((acc, n) => acc + "||" + n)
  88. : "");
  89. }
  90. public class DeliveriesForTank
  91. {
  92. private byte[] bytes;
  93. /// <summary>
  94. ///
  95. /// </summary>
  96. /// <param name="bytes">TankNumber(2 bytes) + ProductCode(1 byte) + NoOfDeliveries(2 bytes)+ 20+2+No*8</param>
  97. public DeliveriesForTank(byte[] bytes)
  98. {
  99. this.bytes = bytes;
  100. }
  101. public int? TankNumber
  102. {
  103. get
  104. {
  105. var r = Encoding.ASCII.GetString(this.bytes.Take(2).ToArray());
  106. return int.Parse(r);
  107. }
  108. }
  109. public char? ProductCode
  110. {
  111. get
  112. {
  113. return
  114. Encoding.ASCII.GetString(this.bytes.Skip(2).Take(1).ToArray()).First();
  115. }
  116. }
  117. public int? NumberOfDeliveriesToFollow
  118. {
  119. get
  120. {
  121. var nn = Util.ConvertHexBcdStrToBytes(this.bytes.Skip(3).Take(2).ToArray()).First();
  122. return nn;
  123. }
  124. }
  125. public List<Delivery> Deliveries
  126. {
  127. get
  128. {
  129. if (this.NumberOfDeliveriesToFollow == 0) return null;
  130. // exclude 5 bytes of TT + p + dd.
  131. var loopParts = this.bytes.Skip(5);
  132. var datas = new List<Delivery>();
  133. int previousDeliveryByteCount = 0;
  134. while (true)
  135. {
  136. var numberOfEightCharDataFieldsValue
  137. = Util.ConvertHexBcdStrToBytes(
  138. loopParts.Skip(previousDeliveryByteCount + 20).Take(2).ToArray()).First();
  139. //if (numberOfEightCharDataFieldsValue < 1 || numberOfEightCharDataFieldsValue > 10)
  140. // throw new ArgumentException("number Of Eight Char DataFields to follow must range from 1 to 10, but now is: " + numberOfEightCharDataFieldsValue);
  141. datas.Add(
  142. new Delivery(this.TankNumber,
  143. loopParts.Skip(previousDeliveryByteCount)
  144. .Take(10 + 10 + 2 + 8 * numberOfEightCharDataFieldsValue).ToArray()));
  145. previousDeliveryByteCount += 10 + 10 + 2 + 8 * numberOfEightCharDataFieldsValue;
  146. if (previousDeliveryByteCount == loopParts.Count()) break;
  147. }
  148. return datas;
  149. }
  150. }
  151. public string ToLogString()
  152. {
  153. return "Tank with Number: " + (this.TankNumber ?? -1) + " has ProductCode: " + (this.ProductCode ?? ' ')
  154. + ", its NumberOfDeliveriesToFollow: " + (this?.NumberOfDeliveriesToFollow ?? 0)
  155. + " and details-> " +
  156. (Deliveries != null && Deliveries.Any() ?
  157. Deliveries.Select(d => d.ToFullLogString()).Aggregate((acc, n) => acc + "||" + n)
  158. : "");
  159. }
  160. }
  161. public class Delivery
  162. {
  163. //public enum ReadingDataName
  164. //{
  165. // StartingVolume = 1,
  166. // StartingTcVolume = 2,
  167. // StartingWater = 3,
  168. // StartingTemp = 4,
  169. // EndingVolume = 5,
  170. // EndingTcVolume = 6,
  171. // EndingWater = 7,
  172. // EndingTemp = 8,
  173. // StartingHeight = 9,
  174. // EndingHeight = 10,
  175. //}
  176. private byte[] bytes;
  177. public Delivery(int? tankNumber, byte[] bytes)
  178. {
  179. this.TankNumber = tankNumber;
  180. this.bytes = bytes;
  181. }
  182. public int? TankNumber { get; }
  183. public DateTime? StartingDateTime
  184. {
  185. get
  186. {
  187. return DateTime.ParseExact(
  188. Encoding.ASCII.GetString(this.bytes.Take(10).ToArray()),
  189. "yyMMddHHmm", CultureInfo.InvariantCulture);
  190. }
  191. }
  192. public DateTime? EndingDateTime
  193. {
  194. get
  195. {
  196. return DateTime.ParseExact(
  197. Encoding.ASCII.GetString(this.bytes.Take(10).ToArray()),
  198. "yyMMddHHmm", CultureInfo.InvariantCulture);
  199. }
  200. }
  201. public int? NumberOfEightCharDataFieldsToFollow
  202. {
  203. get
  204. {
  205. var nn = Util.ConvertHexBcdStrToBytes(this.bytes.Skip(20).Take(2).ToArray()).First();
  206. return nn;
  207. }
  208. }
  209. /// <summary>
  210. /// Get list of DelieveryReadingDataName:Value
  211. /// </summary>
  212. public Dictionary<DeliveryReadingDataName, double> Datas
  213. {
  214. get
  215. {
  216. var datas = new Dictionary<DeliveryReadingDataName, double>();
  217. for (int i = 0; i < this.NumberOfEightCharDataFieldsToFollow.Value; i++)
  218. {
  219. var asciiEncodedBytes = Util.ConvertHexBcdStrToBytes(this.bytes.Skip(20 + 2 + 8 * i).Take(8).ToArray());
  220. var dataValue =
  221. Util.ConvertIEEEWith4BytesToDouble(asciiEncodedBytes.ToArray());
  222. switch (i + 1)
  223. {
  224. case 1: // Starting Volume
  225. datas.Add(DeliveryReadingDataName.StartingVolume, dataValue);
  226. break;
  227. case 2: // Starting TC Volume
  228. datas.Add(DeliveryReadingDataName.StartingTcVolume, dataValue);
  229. break;
  230. case 3: // Starting Water
  231. datas.Add(DeliveryReadingDataName.StartingWater, dataValue);
  232. break;
  233. case 4: // Starting Temp
  234. datas.Add(DeliveryReadingDataName.StartingTemp, dataValue);
  235. break;
  236. case 5: // Ending Volume
  237. datas.Add(DeliveryReadingDataName.EndingVolume, dataValue);
  238. break;
  239. case 6: // Ending TC Volume
  240. datas.Add(DeliveryReadingDataName.EndingTcVolume, dataValue);
  241. break;
  242. case 7: // Ending Water
  243. datas.Add(DeliveryReadingDataName.EndingWater, dataValue);
  244. break;
  245. case 8: // Ending Temp
  246. datas.Add(DeliveryReadingDataName.EndingTemp, dataValue);
  247. break;
  248. case 9: // Starting Height
  249. datas.Add(DeliveryReadingDataName.StartingHeight, dataValue);
  250. break;
  251. case 10: // Ending Height
  252. datas.Add(DeliveryReadingDataName.EndingHeight, dataValue);
  253. break;
  254. default:
  255. // by seeing the real data from console, it may still hit here
  256. // the reason is the "Number of eight character Data Fields to follow (Hex)" > 10.
  257. // and meanwhile, extra bytes will be send in, may the VeederRoot leave it for extention
  258. // in future?
  259. break;
  260. }
  261. }
  262. return datas;
  263. }
  264. }
  265. public string ToFullLogString()
  266. {
  267. return "DeliveryDetail StartingDateTime: " + (this?.StartingDateTime?.ToString("yyMMddHHmm") ?? "")
  268. + ", EndingDateTime: " + (this?.EndingDateTime?.ToString("yyMMddHHmm") ?? "")
  269. + ", data(ReadingData:Value)-> "
  270. + this.Datas.Select(s => s.Key.ToString() + ":" + s.Value).Aggregate((acc, n) => acc + " | " + n);
  271. }
  272. //public string ToSimpleLogString()
  273. //{
  274. // return "Tank with Number: " + (this.TankNumber ?? -1) + " has ProductCode: " + (this.ProductCode ?? ' ');
  275. //}
  276. }
  277. }
  278. }