SignedFuellingInfo.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. using System;
  2. using System.Globalization;
  3. using System.Text;
  4. using System.IO;
  5. namespace Wayne.FDCPOSInterface
  6. {
  7. public class SignedFuellingInfo
  8. {
  9. /// <summary>
  10. /// A class that calculates a CRC32 value from various sources.
  11. /// </summary>
  12. public class Crc32
  13. {
  14. #region Fields
  15. private UInt32[] table;
  16. private UInt32 initialValue;
  17. private UInt32 crc;
  18. private UInt32 length;
  19. private byte[] tempBuffer;
  20. #endregion
  21. #region Construction
  22. /// <summary>
  23. /// Constructor.
  24. /// Initializes the CRC to 0xFFFFFFFF.
  25. /// </summary>
  26. public Crc32()
  27. : this(0xffffffff)
  28. {
  29. }
  30. /// <summary>
  31. /// Constructor.
  32. /// </summary>
  33. /// <param name="initialValue">The initial value of the CRC.</param>
  34. public Crc32(UInt32 initialValue)
  35. {
  36. this.initialValue = initialValue;
  37. UInt32 poly = 0xedb88320;
  38. table = new UInt32[256];
  39. UInt32 temp = 0;
  40. for (UInt32 i = 0; i < table.Length; i++)
  41. {
  42. temp = i;
  43. for (UInt32 j = 8; j > 0; j--)
  44. {
  45. if ((temp & 1) == 1)
  46. temp = (UInt32)((temp >> 1) ^ poly);
  47. else
  48. temp >>= 1;
  49. }
  50. table[i] = temp;
  51. }
  52. Reset();
  53. }
  54. #endregion
  55. #region Properties
  56. /// <summary>
  57. /// The CRC32 value.
  58. /// </summary>
  59. public UInt32 Value
  60. {
  61. get { return ~crc; }
  62. }
  63. /// <summary>
  64. /// The total length of the data.
  65. /// </summary>
  66. public UInt32 Length
  67. {
  68. get { return length; }
  69. }
  70. #endregion
  71. #region Methods
  72. /// <summary>
  73. /// Resets the CRC32 value.
  74. /// </summary>
  75. public void Reset()
  76. {
  77. crc = initialValue;
  78. length = 0;
  79. }
  80. /// <summary>
  81. /// Appends a byte array to the CRC32 value.
  82. /// </summary>
  83. /// <param name="bytes">The bytes to append.</param>
  84. public void Append(byte[] bytes)
  85. {
  86. Append(bytes, 0, bytes.Length);
  87. }
  88. /// <summary>
  89. /// Appends a byte array to the CRC32 value.
  90. /// </summary>
  91. /// <param name="bytes">The bytes to append.</param>
  92. /// <param name="offset">The offset of the array of where to start.</param>
  93. /// <param name="length">The number of bytes to read from the array.</param>
  94. public void Append(byte[] bytes, int offset, int length)
  95. {
  96. this.length += (UInt32)length;
  97. for (int i = offset; i < length; i++)
  98. {
  99. byte index = (byte)(((crc) & 0xff) ^ bytes[i]);
  100. crc = (UInt32)((crc >> 8) ^ table[index]);
  101. }
  102. }
  103. /// <summary>
  104. /// Appends a text string to the CRC32 value.
  105. /// </summary>
  106. /// <param name="text">The text to append.</param>
  107. public void Append(string text)
  108. {
  109. if (!string.IsNullOrEmpty(text))
  110. Append(Encoding.Unicode.GetBytes(text));
  111. }
  112. /// <summary>
  113. /// Appends the content of a file to the CRC32 value.
  114. /// </summary>
  115. /// <param name="filename">The filename of the file to append.</param>
  116. public void AppendFile(string filename)
  117. {
  118. AppendFile(filename, 16384);
  119. }
  120. /// <summary>
  121. /// Appends the content of a file to the CRC32 value.
  122. /// </summary>
  123. /// <param name="filename">The filename of the file to append.</param>
  124. /// <param name="bufferSize">The size of the read-buffer to use.</param>
  125. public void AppendFile(string filename, int bufferSize)
  126. {
  127. try
  128. {
  129. if ((tempBuffer == null) || (bufferSize != tempBuffer.Length))
  130. tempBuffer = new byte[bufferSize];
  131. using (FileStream fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read))
  132. {
  133. using (BinaryReader binaryReader = new BinaryReader(fileStream))
  134. {
  135. int usedBufferSize;
  136. do
  137. {
  138. usedBufferSize = binaryReader.Read(tempBuffer, 0, bufferSize);
  139. if (usedBufferSize > 0)
  140. Append(tempBuffer, 0, usedBufferSize);
  141. }
  142. while (usedBufferSize == bufferSize);
  143. }
  144. }
  145. }
  146. catch { }
  147. }
  148. #endregion
  149. }
  150. public class Data
  151. {
  152. public DateTime DateTime { get; set; }
  153. public int PumpNumber { get; set; }
  154. public string ProductName { get; set; }
  155. public decimal TotalAmount { get; set; }
  156. public decimal Volume { get; set; }
  157. public decimal Price { get; set; }
  158. public string CurrencySymbol { get; set; }
  159. public bool CurrencySymbolAfterValue { get; set; }
  160. public bool SpaceBetweenCurrencySymbolAndValue { get; set; }
  161. public string VolumeUnit { get; set; }
  162. public int ControllerModuleCrc { get; set; }
  163. public int PrinterWidth { get; set; }
  164. }
  165. private const string Iv = "@BEPQRV]";
  166. private readonly Crc32 crc32 = new Crc32((uint)(
  167. ((Iv[6] - Iv[0]) * (Iv[7] - Iv[0]) + (Iv[2] - Iv[0])) * 1024 * 1024 +
  168. ((Iv[3] - Iv[0]) * (Iv[5] - Iv[0]) + (Iv[1] - Iv[0])) * 1024 +
  169. ((Iv[6] - Iv[0]) * (Iv[7] - Iv[0]) + (Iv[4] - Iv[0]))));
  170. private const string SignChar = "^";
  171. private const string BadSignReplaceChar = " ";
  172. private const string SignError = "\r\n^*PUMP INFO SIGN ERROR*^\r\n";
  173. public string EncodeReceiptText(Data data)
  174. {
  175. StringBuilder result = new StringBuilder();
  176. NumberFormatInfo numberFormatInfo = new NumberFormatInfo
  177. {
  178. NumberDecimalDigits = 2,
  179. NumberDecimalSeparator = ","
  180. };
  181. if (data.PrinterWidth < 30)
  182. {
  183. // Design for 24 chars
  184. // ===================
  185. string preText = data.PrinterWidth > 24 ? new string(' ', data.PrinterWidth - 24) : string.Empty;
  186. //^ #12 [20100503085824] ^45697A3C
  187. result.Append(preText + GetSignedString(string.Concat(SignChar, " #", data.PumpNumber.ToString().PadRight(3),
  188. "[", data.DateTime.ToString("yyyyMMddHHmmss"), "] ", SignChar)));
  189. //^ Blyfri 95 ^2C5BD546
  190. result.Append(preText + GetSignedString(string.Concat(SignChar, " ", data.ProductName.PadRight(21), SignChar)));
  191. //^ 563,74 DKK ^65B9F8A3
  192. result.Append(preText + GetSignedString(string.Concat(SignChar, GetCurrencyString(numberFormatInfo, data).PadLeft(21), " ", SignChar)));
  193. //^ 54,52 L ^98C35479
  194. result.Append(preText + GetSignedString(string.Concat(SignChar, data.Volume.ToString(numberFormatInfo).PadLeft(10), " ", data.VolumeUnit.PadRight(11), SignChar)));
  195. //^ @ 10,34 DKK/L ^465D9872
  196. result.Append(preText + GetSignedString(string.Concat(SignChar, string.Concat("@ ", data.Price.ToString(numberFormatInfo)).PadLeft(10), " ", string.Concat(data.CurrencySymbol, "/", data.VolumeUnit).PadRight(11), SignChar)));
  197. //^ CS A8F0EC90:######## ^6A9C8FC4)
  198. result.Append(preText + GetSignedString(string.Concat(SignChar, " CS ", data.ControllerModuleCrc.ToString("X8"), " ######## ", SignChar)));
  199. }
  200. else
  201. {
  202. // Design for 30 chars
  203. // ===================
  204. string preText = data.PrinterWidth > 30 ? new string(' ', data.PrinterWidth - 30) : string.Empty;
  205. //123456789012345678901234567890
  206. //^ #12 [20100503085824] ^4A7692AE
  207. result.Append(preText + GetSignedString(string.Concat(SignChar, " #", data.PumpNumber.ToString().PadRight(3),
  208. "[", data.DateTime.ToString("yyyyMMddHHmmss"), "] ", SignChar)));
  209. //123456789012345678901234567890
  210. //^ Unlead 95 563,74 SEK ^3CE18720
  211. result.Append(preText + GetSignedString(string.Concat(SignChar, " ", data.ProductName.PadRight(14), GetCurrencyString(numberFormatInfo, data).PadLeft(12), " ", SignChar)));
  212. //123456789012345678901234567890
  213. //^ 54,52 L @ 10,34 SEK/L ^0A1B0FB2
  214. result.Append(preText + GetSignedString(string.Concat(SignChar, " ", string.Concat(data.Volume.ToString(numberFormatInfo), " ", data.VolumeUnit,
  215. " @ ", data.Price.ToString(numberFormatInfo), " ", data.CurrencySymbol, "/", data.VolumeUnit).PadRight(27), SignChar)));
  216. //123456789012345678901234567890
  217. //^ C/S A8F0EC90:######## ^05198FB7
  218. result.Append(preText + GetSignedString(string.Concat(SignChar, " C/S ", data.ControllerModuleCrc.ToString("X8"), " ######## ", SignChar)));
  219. }
  220. return result.ToString();
  221. }
  222. public string DecodeReceiptText(string text, int printerModuleCrc)
  223. {
  224. bool hasError = false;
  225. int offset = 0;
  226. int startCharIndex, endCharIndex;
  227. do
  228. {
  229. startCharIndex = text.IndexOf(SignChar, offset);
  230. if (startCharIndex > -1)
  231. {
  232. endCharIndex = text.IndexOf(SignChar, startCharIndex + 1);
  233. if (endCharIndex == -1)
  234. {
  235. text = text.Replace(SignChar, BadSignReplaceChar);
  236. hasError = true;
  237. }
  238. else
  239. {
  240. if (text.Length <= endCharIndex + 8)
  241. {
  242. text = text.Remove(startCharIndex, 1).Insert(startCharIndex, BadSignReplaceChar).Remove(endCharIndex, 1).Insert(endCharIndex, BadSignReplaceChar);
  243. hasError = true;
  244. }
  245. else
  246. {
  247. string crcString = text.Substring(endCharIndex + 1, 8);
  248. string signedText = text.Substring(startCharIndex, endCharIndex - startCharIndex + 1);
  249. if (!crcString.Equals(GetStringSignValue(signedText), StringComparison.InvariantCultureIgnoreCase))
  250. {
  251. text = text.Remove(startCharIndex, 1).Insert(startCharIndex, BadSignReplaceChar).Remove(endCharIndex, 1).Insert(endCharIndex, BadSignReplaceChar);
  252. hasError = true;
  253. }
  254. int printerModuleCrcIndex = signedText.IndexOf("########");
  255. if (printerModuleCrcIndex > -1)
  256. text = text.Remove(startCharIndex + printerModuleCrcIndex, 8).Insert(startCharIndex + printerModuleCrcIndex, printerModuleCrc.ToString("X8"));
  257. text = text.Remove(endCharIndex + 1, 8);
  258. }
  259. offset = endCharIndex + 1;
  260. }
  261. }
  262. else
  263. endCharIndex = -1;
  264. }
  265. while ((startCharIndex > -1) && (endCharIndex > -1));
  266. if (hasError)
  267. return text + SignError;
  268. return text;
  269. }
  270. private static string GetCurrencyString(IFormatProvider numberFormatInfo, Data data)
  271. {
  272. if (data.CurrencySymbolAfterValue)
  273. return string.Concat(data.TotalAmount.ToString(numberFormatInfo), data.SpaceBetweenCurrencySymbolAndValue ? " " : string.Empty, data.CurrencySymbol);
  274. return string.Concat(data.CurrencySymbol, data.SpaceBetweenCurrencySymbolAndValue ? " " : string.Empty, data.TotalAmount.ToString(numberFormatInfo));
  275. }
  276. private string GetSignedString(string text)
  277. {
  278. return string.Concat(text, GetStringSignValue(text));
  279. }
  280. private string GetStringSignValue(string text)
  281. {
  282. crc32.Reset();
  283. crc32.Append(text);
  284. return crc32.Value.ToString("X8");
  285. }
  286. }
  287. }