FDCPOSClient.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Text;
  5. using System.Threading;
  6. using System.Runtime.InteropServices;
  7. using System.Net;
  8. using System.Net.Sockets;
  9. using Wayne.FDCPOSLibrary;
  10. using Microsoft.Extensions.Logging;
  11. using Microsoft.Extensions.DependencyInjection;
  12. namespace Wayne.FDCPOSInterface
  13. {
  14. public class FuellingInfo
  15. {
  16. public DateTime dateTime;
  17. public int pumpNumber;
  18. public int nozzle;
  19. public int productNo;
  20. public string productName;
  21. public string productUM;
  22. public decimal amount;
  23. public decimal volume;
  24. public decimal price;
  25. public int transactionId;
  26. public FuellingInfo(int pumpNumber, int nozzle, int productNo, string productName, string productUM, decimal amount, decimal volume, decimal price, DateTime dateTime, int transactionId)
  27. {
  28. this.pumpNumber = pumpNumber;
  29. this.nozzle = nozzle;
  30. this.productNo = productNo;
  31. this.productName = productName;
  32. this.productUM = productUM;
  33. this.amount = amount;
  34. this.volume = volume;
  35. this.price = price;
  36. this.dateTime = dateTime;
  37. this.transactionId = transactionId;
  38. }
  39. }
  40. public class FDCPOSClientIX : FDCPOSClient
  41. {
  42. public string currencySymbol;
  43. public bool currencySymbolAfterValue = true;
  44. public bool spaceBetweenCurrencySymbolAndValue = true;
  45. public int controllerCrc = 0;
  46. public int printerWidth = 24;
  47. public FDCPOSClientIX(FDCPOSManager _fdcposManager, Messages.HeartbeatCallback _heartbeatCallback, string posInfo) :
  48. base(_fdcposManager, _heartbeatCallback, null)
  49. {
  50. currencySymbol = System.Globalization.RegionInfo.CurrentRegion.CurrencySymbol;
  51. readPOSInfo(posInfo);
  52. }
  53. void readPOSInfo(string posInfo)
  54. {
  55. try
  56. {
  57. posInfo = posInfo.Substring(posInfo.IndexOf('(', 0) + 1, posInfo.Length - 1 - posInfo.IndexOf('(', 0) - 1);
  58. //FDCPOSManager.tracer.WriteLine(string.Format("posInfo={0}", posInfo));
  59. Dictionary<string, string> posInfoParamDict;
  60. posInfoParamDict = ParsePOSInfoString(posInfo);
  61. string key = "currencySymbol";
  62. if (posInfoParamDict.ContainsKey(key) && posInfoParamDict[key] != null && posInfoParamDict[key] != "")
  63. this.currencySymbol = posInfoParamDict[key];
  64. key = "currencySymbolAfterValue";
  65. if (posInfoParamDict.ContainsKey(key) && posInfoParamDict[key] != null && posInfoParamDict[key] != "")
  66. {
  67. if (posInfoParamDict[key] == "0" || posInfoParamDict[key].ToUpper() == "FALSE")
  68. this.currencySymbolAfterValue = false;
  69. }
  70. key = "spaceBetweenCurrencySymbolAndValue";
  71. if (posInfoParamDict.ContainsKey(key) && posInfoParamDict[key] != null && posInfoParamDict[key] != "")
  72. {
  73. if (posInfoParamDict[key] == "0" || posInfoParamDict[key].ToUpper() == "FALSE")
  74. this.spaceBetweenCurrencySymbolAndValue = false;
  75. }
  76. key = "controllerCrc";
  77. if (posInfoParamDict.ContainsKey(key) && posInfoParamDict[key] != null && posInfoParamDict[key] != "")
  78. {
  79. try
  80. {
  81. this.controllerCrc = Convert.ToInt32(posInfoParamDict[key]);
  82. }
  83. catch (Exception ex)
  84. {
  85. }
  86. }
  87. key = "printerWidth";
  88. if (posInfoParamDict.ContainsKey(key) && posInfoParamDict[key] != null && posInfoParamDict[key] != "")
  89. {
  90. try
  91. {
  92. this.printerWidth = Convert.ToInt32(posInfoParamDict[key]);
  93. }
  94. catch (Exception ex)
  95. {
  96. }
  97. }
  98. }
  99. catch (Exception ex)
  100. {
  101. //FDCPOSManager.tracer.WriteLine("Exception! " + ex.Message);
  102. }
  103. }
  104. public override byte[] getDSPFields(FuellingInfo fi)
  105. {
  106. byte[] temp;
  107. string sDSPFilelds = GenerateSignedPumpInfoLine(fi.dateTime, fi.pumpNumber, fi.productName, fi.amount, fi.volume, fi.price,
  108. currencySymbol, currencySymbolAfterValue, spaceBetweenCurrencySymbolAndValue, fi.productUM, controllerCrc, printerWidth);
  109. temp = System.Text.UTF8Encoding.UTF8.GetBytes(sDSPFilelds);
  110. return temp;
  111. }
  112. public override byte[] getCRC(FuellingInfo fi)
  113. {
  114. byte[] temp;
  115. string sCRC = "";
  116. temp = System.Text.ASCIIEncoding.ASCII.GetBytes(sCRC);
  117. return temp;
  118. }
  119. public static string GenerateSignedPumpInfoLine(
  120. DateTime dateTime,
  121. int pumpNumber,
  122. string productName,
  123. decimal totalAmount,
  124. decimal volume,
  125. decimal price,
  126. string currencySymbol,
  127. bool currencySymbolAfterValue,
  128. bool spaceBetweenCurrencySymbolAndValue,
  129. string volumeUnit,
  130. int controllerCrc,
  131. int printerWidth)
  132. {
  133. SignedFuellingInfo signedFillingInfo = new SignedFuellingInfo();
  134. SignedFuellingInfo.Data data = new SignedFuellingInfo.Data();
  135. data.ControllerModuleCrc = controllerCrc;
  136. data.CurrencySymbol = currencySymbol;
  137. data.CurrencySymbolAfterValue = currencySymbolAfterValue;
  138. data.DateTime = dateTime;
  139. data.Price = price;
  140. data.PrinterWidth = printerWidth;
  141. data.ProductName = productName;
  142. data.PumpNumber = pumpNumber;
  143. data.SpaceBetweenCurrencySymbolAndValue = spaceBetweenCurrencySymbolAndValue;
  144. data.TotalAmount = totalAmount;
  145. data.Volume = volume;
  146. data.VolumeUnit = volumeUnit;
  147. return signedFillingInfo.EncodeReceiptText(data);
  148. }
  149. }
  150. public class FDCPOSClient : IDisposable
  151. {
  152. //static NLog.Logger fdcSocketLogger = NLog.LogManager.LoadConfiguration("nlog.config").GetLogger("FdcServerSocket");
  153. private ILogger fdcSocketLogger;// = ServiceBuilder.Provider
  154. //.GetRequiredService<ILoggerFactory>().CreateLogger("FdcServerSocket");
  155. public string sID;
  156. public string workstationID;
  157. public string applicationSender;
  158. public FdcClientTcpHandler FdcClientTcpHandler;
  159. public string sIPAddress;
  160. private TcpClient _socketChannelResponse = null;
  161. [ComVisible(false)]
  162. public TcpClient socketChannelResponse
  163. {
  164. get { return _socketChannelResponse; }
  165. set { _socketChannelResponse = value; }
  166. }
  167. private bool _connected = false;
  168. [ComVisible(false)]
  169. public bool connected
  170. {
  171. get { return _connected; }
  172. set { _connected = value; }
  173. }
  174. private bool _logon = false;
  175. [ComVisible(false)]
  176. public bool logon
  177. {
  178. get { return _logon; }
  179. set { _logon = value; }
  180. }
  181. private TcpClient _socketChannelUnsolicited = null;
  182. [ComVisible(false)]
  183. public TcpClient socketChannelUnsolicited
  184. {
  185. get { return _socketChannelUnsolicited; }
  186. set { _socketChannelUnsolicited = value; }
  187. }
  188. FDCPOSManager fdcposManager;
  189. public Heartbeat heartbeat;
  190. Messages.HeartbeatCallback heartbeatCallback;
  191. static public POSType GetType(string posInfo)
  192. {
  193. if (posInfo.Contains(POSType.IXTerminal.ToString()))
  194. return POSType.IXTerminal;
  195. else
  196. return POSType.Generic;
  197. }
  198. public static Dictionary<string, string> ParsePOSInfoString(string posInfoString)
  199. {
  200. Dictionary<string, string> posInfoStringParamDict = new Dictionary<string, string>();
  201. string[] listItems = posInfoString.Split(';');
  202. foreach (string listItem in listItems)
  203. {
  204. string trimmedListItem = listItem.Trim();
  205. if (trimmedListItem.Length > 0)
  206. {
  207. int equalSignIndex = trimmedListItem.IndexOf('=');
  208. string paramName = trimmedListItem.Substring(0, equalSignIndex);
  209. string paramValue = trimmedListItem.Substring(equalSignIndex + 1, trimmedListItem.Length - equalSignIndex - 1);
  210. posInfoStringParamDict[paramName] = paramValue;
  211. }
  212. }
  213. return posInfoStringParamDict;
  214. }
  215. public FDCPOSClient(FDCPOSManager _fdcposManager,
  216. Messages.HeartbeatCallback _heartbeatCallback, ILogger logger)
  217. {
  218. this.fdcSocketLogger = logger;
  219. heartbeat = new Heartbeat();
  220. fdcposManager = _fdcposManager;
  221. heartbeat.OnHeartbeatInterval += new EventHandler(heartbeat_OnHeartbeatInterval);
  222. heartbeat.OnClientHeartbeatTimeout += new EventHandler(heartbeat_OnHeartbeatTimeout);
  223. heartbeatCallback = _heartbeatCallback;
  224. }
  225. ~FDCPOSClient()
  226. {
  227. if (heartbeat != null)
  228. heartbeat.Dispose();
  229. heartbeat = null;
  230. }
  231. public void Dispose()
  232. {
  233. try
  234. {
  235. if (this.fdcSocketLogger != null)
  236. this.fdcSocketLogger.LogInformation("FDCPOSClient: " + this.workstationID + "-" + this.applicationSender + " is on disposing");
  237. if (heartbeat != null)
  238. heartbeat.Dispose();
  239. heartbeat = null;
  240. this.FdcClientTcpHandler.Dispose();
  241. }
  242. catch { }
  243. }
  244. private void heartbeat_OnHeartbeatTimeout(object sender, EventArgs e)
  245. {
  246. try
  247. {
  248. string clientId = FDCPOSClient.getClientID(workstationID, applicationSender);
  249. //FDCPOSManager.tracer.WriteLine(string.Format("disconnect client '{0}'", clientId));
  250. if (this.fdcSocketLogger != null)
  251. this.fdcSocketLogger.LogInformation(string.Format("disconnecting client '{0}' due to OnHeartbeatTimeout", clientId));
  252. this.fdcposManager.DisconnectClient(this);
  253. }
  254. catch (Exception ex)
  255. {
  256. //FDCPOSManager.tracer.WriteLine(string.Format("Exception disconnect client '{0}', error: {1}", clientId, ex.Message));
  257. }
  258. }
  259. private void heartbeat_OnHeartbeatInterval(object sender, EventArgs e)
  260. {
  261. if (heartbeatCallback != null)
  262. heartbeatCallback.Invoke(workstationID, applicationSender);
  263. }
  264. public static string getClientID(string workstationID, string applicationSender)
  265. {
  266. return (workstationID ?? "") + "§§§" + (applicationSender ?? "");
  267. }
  268. public static void Remove(string clientID)
  269. {
  270. }
  271. public virtual byte[] getDSPFields(FuellingInfo fi)
  272. {
  273. byte[] temp;
  274. string sDSPFields = "";
  275. temp = System.Text.UTF8Encoding.UTF8.GetBytes(sDSPFields);
  276. return temp;
  277. }
  278. public virtual byte[] getCRC(FuellingInfo fi)
  279. {
  280. return getCRC("DeviceID TransactionNo Volume Amount ", fi.pumpNumber, fi.nozzle, fi.transactionId, fi.volume, fi.amount);
  281. }
  282. private byte[] getCRC(string fieldsNames, int deviceId, int nozzle, int transactionId, decimal volume, decimal amount)
  283. {
  284. try
  285. {
  286. //FDCPOSManager.tracer.WriteLineIf(//FDCPOSManager.tracer.CheckTraceLevel(4), "init");
  287. Crc16Ccitt crccrypter = new Crc16Ccitt(InitialCrcValue.Zeros);
  288. byte[] temp; // = new byte[8];
  289. byte[] data = new byte[fieldsNames.Length + 1 + 2 + 5 + 5 + 3];
  290. // add fields names
  291. System.Text.ASCIIEncoding.ASCII.GetBytes(fieldsNames).CopyTo(data, 0);
  292. //FDCPOSManager.tracer.WriteLineIf(//FDCPOSManager.tracer.CheckTraceLevel(4), "1");
  293. // add deviceId
  294. data[fieldsNames.Length] = Convert.ToByte(deviceId);
  295. //FDCPOSManager.tracer.WriteLineIf(//FDCPOSManager.tracer.CheckTraceLevel(4), "2");
  296. // add transactionId - bcd4
  297. string sTrId = transactionId.ToString();
  298. sTrId = sTrId.PadLeft(4, '0');
  299. temp = System.Text.ASCIIEncoding.ASCII.GetBytes(sTrId);
  300. data[fieldsNames.Length + 1] = (byte)((temp[0] - 0x30) * 16 + (temp[1] - 0x30));
  301. data[fieldsNames.Length + 2] = (byte)((temp[2] - 0x30) * 16 + (temp[3] - 0x30));
  302. //FDCPOSManager.tracer.WriteLineIf(//FDCPOSManager.tracer.CheckTraceLevel(4), "3");
  303. // add volume - bin8+bcd8
  304. string stemp = FDCConvert.ToString(volume);
  305. int decpos = stemp.IndexOf(FDCConvert.DecimalSeparator);
  306. if (decpos != -1)
  307. stemp = stemp.Replace(FDCConvert.DecimalSeparator, "");
  308. else
  309. decpos = stemp.Length;
  310. //for (int i = 0; i < 8; i++)
  311. // temp[i] = 0;
  312. temp = System.Text.ASCIIEncoding.ASCII.GetBytes(stemp);
  313. // bin8 is the decoimal position
  314. data[fieldsNames.Length + 3] = Convert.ToByte(decpos);
  315. for (int i = 0; i < 5; i++)
  316. {
  317. if (temp.Length > 2 * i + 1)
  318. data[fieldsNames.Length + 4 + i] = (byte)((temp[2 * i] - 0x30) * 16 + (temp[2 * i + 1] - 0x30));
  319. else if (temp.Length > 2 * i)
  320. data[fieldsNames.Length + 4 + i] = (byte)((temp[2 * i] - 0x30) * 16);
  321. else
  322. data[fieldsNames.Length + 4 + i] = 0;
  323. }
  324. ////FDCPOSManager.tracer.WriteLineIf(//FDCPOSManager.tracer.CheckTraceLevel(4), "4");
  325. // add amount - bin8+bcd8
  326. stemp = FDCConvert.ToString(amount);
  327. decpos = stemp.IndexOf(FDCConvert.DecimalSeparator);
  328. if (decpos != -1)
  329. stemp = stemp.Replace(FDCConvert.DecimalSeparator, "");
  330. else
  331. decpos = stemp.Length;
  332. //for (int i = 0; i < 8; i++ )
  333. // temp[i] = 0;
  334. temp = System.Text.ASCIIEncoding.ASCII.GetBytes(stemp);
  335. // bin8 is the decoimal position
  336. data[fieldsNames.Length + 8] = Convert.ToByte(decpos);
  337. for (int i = 0; i < 5; i++)
  338. {
  339. if (temp.Length > 2 * i + 1)
  340. data[fieldsNames.Length + 9 + i] = (byte)((temp[2 * i] - 0x30) * 16 + (temp[2 * i + 1] - 0x30));
  341. else if (temp.Length > 2 * i)
  342. data[fieldsNames.Length + 9 + i] = (byte)((temp[2 * i] - 0x30) * 16);
  343. else
  344. data[fieldsNames.Length + 9 + i] = 0;
  345. }
  346. //FDCPOSManager.tracer.WriteLineIf(//FDCPOSManager.tracer.CheckTraceLevel(4), "5");
  347. ushort checksum = crccrypter.ComputeChecksum(data);
  348. data[fieldsNames.Length + 13] = 0;
  349. data[fieldsNames.Length + 14] = (byte)((checksum & 0xFF00) / 256);
  350. data[fieldsNames.Length + 15] = (byte)(checksum & 0x00FF);
  351. //FDCPOSManager.tracer.WriteLineIf(//FDCPOSManager.tracer.CheckTraceLevel(4), "end ok");
  352. return data;
  353. }
  354. catch (Exception ex)
  355. {
  356. //FDCPOSManager.tracer.WriteLine("Exception! " + ex.Message + " - " + ex.StackTrace);
  357. }
  358. //FDCPOSManager.tracer.WriteLineIf(//FDCPOSManager.tracer.CheckTraceLevel(4), "end ko");
  359. return null;
  360. }
  361. }
  362. }