FdcCommunicator.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. using SinoChemFC2PosProxy.Communicator;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Configuration;
  5. using System.Data.SqlClient;
  6. using System.Linq;
  7. using System.Text;
  8. using System.Threading;
  9. using Wayne.ForecourtControl;
  10. using Wayne.ForecourtControl.Com;
  11. using Wayne.Fusion.Framework.Core;
  12. using Wayne.Lib;
  13. using Wayne.Lib.Log;
  14. using IPump = Wayne.ForecourtControl.IPump;
  15. namespace SinoChemFC2PosProxy
  16. {
  17. public delegate void NozzleLiftedHandler(int sitewiseNozzleId, IPump callingPump);
  18. public delegate void NozzleReplacedHandler(int sitewiseNozzleId);
  19. public delegate void FuelingDoneHandler(int sitewiseNozzleId, int seqNum, decimal fuelAmount, decimal quantity, long authId);
  20. public delegate void AuthOkHandler(int sitewiseNozzleId, long? authId);
  21. public delegate void AuthFailedHandler(int sitewiseNozzleId);
  22. public class FdcCommunicator : IDisposable, ICommunicator
  23. {
  24. public event NozzleLiftedHandler NozzleLifted;
  25. public event NozzleReplacedHandler NozzleReplaced;
  26. public event FuelingDoneHandler FuelingDone;
  27. public event AuthOkHandler AuthOk;
  28. public event AuthFailedHandler AuthFailed;
  29. /// <summary>
  30. /// 0 for not started, 1 for started already.
  31. /// </summary>
  32. private int isStarted = 0;
  33. private const string DEFAULT_FDC_SERVER_CONNECT_STRING = "Host=127.0.0.1,Port=4710,ClientId=101,ClientName=PetroChinaProxy,PortB=4710,PortC=4710";
  34. private readonly string concreteFdcServerConnString = string.Empty;
  35. //private readonly DebugLogger debugLogger =
  36. // new DebugLogger(new IdentifiableEntity(0, "FC2PosProxyMain", "", null));
  37. static NLog.Logger debugLogger = NLog.LogManager.LoadConfiguration("nlog.config").GetLogger("PumpHandler");
  38. //private readonly Wayne.ForecourtControl.IForecourtControl forecourtControl;
  39. //private readonly MessageRouterClient msgRouterClient;
  40. private bool autoAuthorizePumpWhenCalling = false;
  41. /// <summary>
  42. /// The Fdc communicator works as a FDC client which connected to FC.
  43. /// </summary>
  44. /// <param name="msgRouterClient">somehow, still need to communicate the Message Router</param>
  45. public FdcCommunicator()
  46. {
  47. //if (!String.IsNullOrEmpty(
  48. // ConfigurationManager.AppSettings["AutoAuthorizePumpWhenCalling"]))
  49. //{
  50. // this.autoAuthorizePumpWhenCalling =
  51. // (ConfigurationManager.AppSettings["AutoAuthorizePumpWhenCalling"].ToLower() ==
  52. // "true"
  53. // ? true
  54. // : false);
  55. //}
  56. //var fdcServerIpAddress = ConfigurationManager.AppSettings["FdcServerIpAddress"];
  57. //this.concreteFdcServerConnString = DEFAULT_FDC_SERVER_CONNECT_STRING.Replace("127.0.0.1", fdcServerIpAddress)
  58. // .Replace("ClientId=101", "ClientId=" + ConfigurationManager.AppSettings["ClientId"]);
  59. //this.forecourtControl = Wayne.ForecourtControl.Fusion.FUSIONFactory.CreateForecourtControl(0);
  60. //this.forecourtControl.OnConnectionStateChange += forecourtControl_OnConnectionStateChange;
  61. //this.msgRouterClient = msgRouterClient;
  62. //this.msgRouterClient.Start();
  63. }
  64. //void forecourtControl_OnConnectionStateChange(object sender, ConnectionChangedEventArgs e)
  65. //{
  66. // debugLogger.Add("forecourtControl_OnConnectionStateChange(), new state: " + e.ConnectionState, DebugLogLevel.Normal);
  67. // if (e.ConnectionState == Wayne.Lib.DeviceConnectionState.Disconnected)
  68. // {
  69. // foreach (var pump in this.forecourtControl.Pumps)
  70. // {
  71. // pump.OnFuellingStateChange -= FdcCommunicator_OnFuellingStateChange;
  72. // pump.OnNozzleStateChange -= FdcCommunicator_OnNozzleStateChange;
  73. // pump.OnEventOccured -= FdcCommunicator_OnEventOccured;
  74. // }
  75. // }
  76. // else if (e.ConnectionState == Wayne.Lib.DeviceConnectionState.Connected)
  77. // {
  78. // // sometimes could not receive any notification from FDC server even attached the event handler,
  79. // // suspect some underlying bug in communication layer, so here try sleep a while to avoid(probably) this.
  80. // //
  81. // Thread.Sleep(500);
  82. // foreach (var pump in this.forecourtControl.Pumps)
  83. // {
  84. // pump.OnFuellingStateChange += FdcCommunicator_OnFuellingStateChange;
  85. // pump.OnNozzleStateChange += FdcCommunicator_OnNozzleStateChange;
  86. // pump.OnEventOccured += FdcCommunicator_OnEventOccured;
  87. // }
  88. // const int maxRetryTimes = 10;
  89. // int retriedTimes = 0;
  90. // while (!this.msgRouterClient.SendMessage(MsgRouterMessageUtility.RefreshPumpStatus()))
  91. // {
  92. // if (++retriedTimes > maxRetryTimes) break;
  93. // debugLogger.Add("failed to send RefreshPumpStatus() to MsgRouterServer, will keep retrying until max times reached...", DebugLogLevel.Normal);
  94. // Thread.Sleep(2000);
  95. // }
  96. // this.forecourtControl.SetSiteOpenedAsync(true, (_, __) => { }, null);
  97. // }
  98. //}
  99. //void FdcCommunicator_OnEventOccured(object sender, Wayne.ForecourtControl.PumpEventOccuredEventArgs e)
  100. //{
  101. // debugLogger.Add("FdcCommunicator_OnEventOccured(), args: " + e, DebugLogLevel.Maximized);
  102. //}
  103. void FdcCommunicator_OnNozzleStateChange(object sender, Wayne.ForecourtControl.NozzleStateChangeEventArgs e)
  104. {
  105. var callingPump = sender as IPump;
  106. debugLogger.Debug("FdcCommunicator_OnNozzleStateChange(), args: pumpId: " + callingPump.Id + " nozzleId: " + e.Nozzle.Id + ", newState: " + e.NozzleState);
  107. int sitewiseNozzleId = SiteConfigUtility.Default.GetSiteLevelNozzleIdByLogicalNozzleId(callingPump.Id, e.Nozzle.Id);
  108. if (e.NozzleState == NozzleState.In)
  109. {
  110. /* indicate for nozzle if replaced back */
  111. var sizeLevelNozzleIdsOnPump = SiteConfigUtility.Default.GetSiteLevelNozzleIdsByPumpId(callingPump.Id);
  112. if (!sizeLevelNozzleIdsOnPump.Any())
  113. {
  114. debugLogger.Debug("Could not found any site level nozzle ids for pump: " + callingPump.Id);
  115. return;
  116. }
  117. using (var posSqlConnection = new SqlConnection(ConfigurationManager.ConnectionStrings["PosDatabaseConnStr"].ConnectionString))
  118. {
  119. try
  120. {
  121. /* idle would not carry nozzle id, so here reset all nozzles on target pump.*/
  122. var setPumpOnIdleCommand
  123. = new SqlCommand(sizeLevelNozzleIdsOnPump.Select(siteLevelNozzleId =>
  124. {
  125. var totalizer = SiteConfigUtility.Default.GetTotalizer(siteLevelNozzleId);
  126. return
  127. string.Format(
  128. "Update jy_info set [status] = '{1}', qty=0, amount=0, fzqty='{2}', fzamount={3}" +
  129. " where jihao = {0}", siteLevelNozzleId, 'F',
  130. totalizer.Item1, totalizer.Item2);
  131. })
  132. .Aggregate((acc, n) => acc + " " + n), posSqlConnection);
  133. debugLogger.Add("setPumpOnIdleCommand(via Fdc): " + setPumpOnIdleCommand.CommandText, DebugLogLevel.Maximized);
  134. posSqlConnection.Open();
  135. setPumpOnIdleCommand.ExecuteNonQuery();
  136. }
  137. catch (Exception ex)
  138. {
  139. debugLogger.Add("executing setPumpOnIdleCommand(via Fdc) failed, exception detail: " + ex,
  140. DebugLogLevel.Normal);
  141. }
  142. }
  143. NozzleReplaced?.Invoke(sitewiseNozzleId);
  144. }
  145. else if (e.NozzleState == NozzleState.Out)
  146. {
  147. debugLogger.Add("fire nozzle out event");
  148. NozzleLifted?.Invoke(sitewiseNozzleId, callingPump);
  149. }
  150. }
  151. public void AuthorizePumpAsync(IPump callingPump, int sitewiseNozzleId, decimal authAmount)
  152. {
  153. if (this.autoAuthorizePumpWhenCalling)
  154. {
  155. var authParameter = new AuthorizeParameters()
  156. {
  157. PriceGroup = PriceGroup.FullService,
  158. LockToReleaseClient = false,
  159. PresetType = PresetType.Amount,
  160. PresetValue = authAmount,
  161. Prepay = false,
  162. PayType = "PC",
  163. };
  164. for (int i = 0; i < callingPump.Nozzles.Count; i++)
  165. {
  166. int idFuelGrade = callingPump.Nozzles[i].FuelGrade;
  167. authParameter.AllowedFuelGrade[idFuelGrade] = true;
  168. }
  169. debugLogger.Add(
  170. "Authorizing for pumpId: " + callingPump.Id + ", authAmount:" + authAmount,
  171. DebugLogLevel.Normal);
  172. callingPump.AuthorizeAsync(authParameter, (_, arg) =>
  173. {
  174. var pumpId = (int)(arg.UserToken);
  175. if (arg.Success)
  176. {
  177. debugLogger.Add(
  178. "AuthorizeAsync finished successfully for pumpId: " + pumpId,
  179. DebugLogLevel.Detailed);
  180. AuthOk?.Invoke(sitewiseNozzleId, arg.Result);
  181. }
  182. else
  183. {
  184. debugLogger.Add(
  185. "AuthorizeAsync failed for pumpId: " + pumpId,
  186. DebugLogLevel.Normal);
  187. AuthFailed?.Invoke(sitewiseNozzleId);
  188. }
  189. }, callingPump.Id);
  190. }
  191. else
  192. {
  193. debugLogger.Add(
  194. "No need to auth before fueling for pumpId: " + callingPump.Id,
  195. DebugLogLevel.Normal);
  196. AuthOk?.Invoke(sitewiseNozzleId, null);
  197. }
  198. }
  199. void FdcCommunicator_OnFuellingStateChange(object sender, Wayne.ForecourtControl.FuellingStateChangeEventArgs e)
  200. {
  201. debugLogger.Add("FdcCommunicator_OnFuellingStateChange(), args: " + e, DebugLogLevel.Detailed);
  202. debugLogger.Add("\r\n PumpID = " + e.Fuelling.Pump.Id +
  203. "\r\n Nozzle = " + e.Fuelling.Nozzle.Id +
  204. "\r\n Amount = $" + e.Fuelling.Amount +
  205. "\r\n State = " + e.State +
  206. "\r\n FuelGrade = " + e.Fuelling.FuelGrade +
  207. "\r\n ReservingDeviceId = " + e.Fuelling.ReservingDeviceId +
  208. "\r\n Quantity = " + e.Fuelling.Quantity +
  209. "\r\n ReservedBy = " + e.Fuelling.ReservedBy +
  210. "\r\n FuelPeriodID = " + e.Fuelling.FuelPeriodId +
  211. "\r\n FuellingSeqNumber = " + e.Fuelling.FuellingSequenceNumber +
  212. "\r\n Price = $" + e.Fuelling.Price);
  213. if (e.State == FuellingState.PayableTransaction)
  214. {
  215. /* in SinoChem project, the pump was set to FullService mode, so the PayableTransaction case here is impossible to happen, but just leave the code here*/
  216. this.forecourtControl.Pumps[e.Fuelling.Pump.Id - 1].Fuellings.ToList().ForEach(f =>
  217. {
  218. var fsn = f.FuellingSequenceNumber;
  219. debugLogger.Add("Sending SetAsPaidAsync for pumpId: " + f.Pump.Id + ", FuellingSequenceNumber: " + fsn,
  220. DebugLogLevel.Detailed);
  221. f.SetAsPaidAsync((_, arg) =>
  222. {
  223. var pumpId = (int)(arg.UserToken);
  224. if (arg.Success)
  225. {
  226. debugLogger.Add(
  227. "SetAsPaidAsync finished successfully for pumpId: " + pumpId + ", FuellingSequenceNumber: " + fsn,
  228. DebugLogLevel.Detailed);
  229. }
  230. else
  231. {
  232. debugLogger.Add(
  233. "SetAsPaidAsync failed for pumpId: " + pumpId + ", FuellingSequenceNumber: " + fsn,
  234. DebugLogLevel.Normal);
  235. }
  236. }, f.Pump.Id);
  237. });
  238. }
  239. else if (e.State == FuellingState.Paid)
  240. {
  241. var posSqlConnection =
  242. new SqlConnection(ConfigurationManager.ConnectionStrings["PosDatabaseConnStr"].ConnectionString);
  243. int sitewiseNozzleId = SiteConfigUtility.Default.GetSiteLevelNozzleIdByLogicalNozzleId(e.Fuelling.Pump.Id, e.Fuelling.Nozzle.Id);
  244. using (posSqlConnection)
  245. {
  246. try
  247. {
  248. var totalizer = SiteConfigUtility.Default.GetTotalizer(e.Fuelling.Pump.Id, e.Fuelling.Nozzle.Id);
  249. var updateFuelingTrxDoneCommand =
  250. new SqlCommand(
  251. string.Format(
  252. "insert xiaofei2 (jihao, youpin, qty, danjia, amount, xf_date, xf_time, liushuino, fzqty, fzamount)" +
  253. " values({0}, N'{1}', {2}, {3}, {4}, '{5}', '{6}', '{7}', '{8}', {9})",
  254. sitewiseNozzleId,
  255. Translator.GetFriendlyGradeName(SiteConfigUtility.Default.GetGradeNameByGradeId(e.Fuelling.FuelGrade)),
  256. e.Fuelling.Quantity,
  257. e.Fuelling.Price,
  258. e.Fuelling.Amount,
  259. DateTime.Now.Date.ToString("yyyy-MM-dd"),
  260. DateTime.Now.ToString("HH:mm:ss"),
  261. e.Fuelling.FuellingSequenceNumber,
  262. totalizer.Item1,
  263. totalizer.Item2),
  264. posSqlConnection);
  265. debugLogger.Add("updateFuelingTrxDoneCommand: " + updateFuelingTrxDoneCommand.CommandText,
  266. DebugLogLevel.Maximized);
  267. posSqlConnection.Open();
  268. updateFuelingTrxDoneCommand.ExecuteNonQuery();
  269. }
  270. catch (Exception ex)
  271. {
  272. debugLogger.Add("executing updateFuelingTrxDoneCommand failed, exception detail: " + ex,
  273. DebugLogLevel.Normal);
  274. }
  275. }
  276. FuelingDone?.Invoke
  277. (sitewiseNozzleId, e.Fuelling.FuellingSequenceNumber, e.Fuelling.Amount, e.Fuelling.Quantity, e.Fuelling.AuthorizationId);
  278. }
  279. }
  280. public void Dispose()
  281. {
  282. this.forecourtControl.Dispose();
  283. this.debugLogger.Dispose();
  284. }
  285. public bool Start()
  286. {
  287. if (0 == Interlocked.CompareExchange(ref this.isStarted, 1, 0))
  288. {
  289. debugLogger.Add("Connecting to FDC server with connStr: " + this.concreteFdcServerConnString, DebugLogLevel.Normal);
  290. this.forecourtControl.Connect(this.concreteFdcServerConnString);
  291. return true;
  292. }
  293. else
  294. {
  295. throw new InvalidOperationException("Already started.");
  296. }
  297. }
  298. public bool IsStarted
  299. {
  300. get { return this.isStarted == 1; }
  301. }
  302. }
  303. }