FdcCommunicator.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  1. using Edge.Core.Processor;using Edge.Core.IndustryStandardInterface.Pump;
  2. using LSForecourtSimulatorImpl;
  3. using MessageRouter;
  4. using Microsoft.Extensions.Logging;
  5. using Edge.Core.Parser;
  6. using System;
  7. using System.Collections.Generic;
  8. using System.Collections.Specialized;
  9. using System.Threading;
  10. using Wayne.ForecourtControl;
  11. using Wayne.ForecourtControl.Fusion;
  12. using Wayne.Lib;
  13. using Wayne.Lib.IO;
  14. namespace GlobalCommunicator
  15. {
  16. public class FdcCommunicator<T> : IDisposable
  17. {
  18. public static ILogger fccLogger { get; set; }
  19. public event EventHandler<FccDataReceivedEventArgs> OnDataReceived;
  20. private const string DEFAULT_FDC_SERVER_CONNECT_STRING = "Host=127.0.0.1,Port=4710,ClientId=101,ClientName=PetroChinaProxy,PortB=4710,PortC=4710";
  21. private readonly string concreteFdcServerConnString = string.Empty;
  22. private readonly IForecourtControl forecourtControl;
  23. /// <summary>
  24. /// 0 for not started, 1 for started already.
  25. /// </summary>
  26. private int isStarted = 0;
  27. private int authorizationId = 0;
  28. private IMessageParser<object, T> parser;
  29. private event EventHandler<AsyncCompletedEventArgs<PumpAccumulatorReading>> OnPumpAccumulatorReceived;
  30. private readonly MessageRouterClient msgRouterClient;
  31. /// <summary>
  32. /// The Fdc communicator works as a FDC client which connected to FC.
  33. /// </summary>
  34. /// <param name="msgRouterClient">somehow, still need to communicate the Message Router</param>
  35. public FdcCommunicator(IMessageParser<object, T> parser, MessageRouterClient msgRouterClient, string fdcServerIpAddress, string clientId)
  36. {
  37. this.parser = parser;
  38. this.OnPumpAccumulatorReceived += FdcCommunicator_OnPumpAccumulatorReceived;
  39. this.concreteFdcServerConnString = DEFAULT_FDC_SERVER_CONNECT_STRING.Replace("127.0.0.1", fdcServerIpAddress)
  40. .Replace("ClientId=101", "ClientId=" + clientId);
  41. var fileSupport = FileSupport.fileSupport;
  42. var config = new FuelModeAndPriceModeConfig(fileSupport.Paths.Parse("FuelModeConfig.xml"), fileSupport);
  43. this.forecourtControl = FUSIONFactory.CreateForecourtControl(0, config, new InputParameterAuthorizationIdGenerator());
  44. this.forecourtControl.OnConnectionStateChange += forecourtControl_OnConnectionStateChange;
  45. this.msgRouterClient = msgRouterClient;
  46. this.msgRouterClient.Start();
  47. }
  48. void forecourtControl_OnConnectionStateChange(object sender, ConnectionChangedEventArgs e)
  49. {
  50. fccLogger.LogDebug("forecourtControl_OnConnectionStateChange(), new state: " + e.ConnectionState);
  51. if (e.ConnectionState == DeviceConnectionState.Disconnected)
  52. {
  53. foreach (var pump in this.forecourtControl.Pumps)
  54. {
  55. pump.OnFuellingStateChange -= FdcCommunicator_OnFuellingStateChange;
  56. pump.OnNozzleStateChange -= FdcCommunicator_OnNozzleStateChange;
  57. pump.OnEventOccured -= FdcCommunicator_OnEventOccured;
  58. }
  59. }
  60. else if (e.ConnectionState == DeviceConnectionState.Connected)
  61. {
  62. // sometimes could not receive any notification from FDC server even attached the event handler,
  63. // suspect some underlying bug in communication layer, so here try sleep a while to avoid(probably) this.
  64. //
  65. Thread.Sleep(500);
  66. foreach (var pump in this.forecourtControl.Pumps)
  67. {
  68. pump.OnFuellingStateChange += FdcCommunicator_OnFuellingStateChange;
  69. pump.OnNozzleStateChange += FdcCommunicator_OnNozzleStateChange;
  70. pump.OnEventOccured += FdcCommunicator_OnEventOccured;
  71. }
  72. const int maxRetryTimes = 10;
  73. int retriedTimes = 0;
  74. while (!this.msgRouterClient.SendMessage(MsgRouterMessageUtility.RefreshPumpStatus()))
  75. {
  76. if (++retriedTimes > maxRetryTimes) break;
  77. fccLogger.LogDebug("failed to send RefreshPumpStatus() to MsgRouterServer, will keep retrying until max times reached...");
  78. Thread.Sleep(2000);
  79. }
  80. this.forecourtControl.SetSiteOpenedAsync(true, (_, __) => { }, null);
  81. }
  82. }
  83. void FdcCommunicator_OnEventOccured(object sender, PumpEventOccuredEventArgs e)
  84. {
  85. fccLogger.LogDebug("FdcCommunicator_OnEventOccured(), args: " + e);
  86. }
  87. void FdcCommunicator_OnNozzleStateChange(object sender, NozzleStateChangeEventArgs e)
  88. {
  89. var callingPump = sender as IPump;
  90. fccLogger.LogDebug("FdcCommunicator_OnNozzleStateChange(), args: pumpId: " + callingPump.Id + " nozzleId: " + e.Nozzle.Id + ", newState: " + e.NozzleState);
  91. //if (e.NozzleState == NozzleState.In)
  92. //{
  93. // /* indicate for nozzle if replaced back */
  94. // var sizeLevelNozzleIdsOnPump = SiteConfigUtility.Default.GetSiteLevelNozzleIdsByPumpId(callingPump.Id);
  95. // if (!sizeLevelNozzleIdsOnPump.Any())
  96. // {
  97. // fccLogger.LogDebug("Could not found any site level nozzle ids for pump: " + callingPump.Id);
  98. // return;
  99. // }
  100. // using (var posSqlConnection = new SqlConnection(ConfigurationManager.ConnectionStrings["PosDatabaseConnStr"].ConnectionString))
  101. // {
  102. // try
  103. // {
  104. // /* idle would not carry nozzle id, so here reset all nozzles on target pump.*/
  105. // var setPumpOnIdleCommand
  106. // = new SqlCommand(sizeLevelNozzleIdsOnPump.Select(siteLevelNozzleId =>
  107. // {
  108. // var totalizer = SiteConfigUtility.Default.GetTotalizer(siteLevelNozzleId);
  109. // return
  110. // string.Format(
  111. // "Update jy_info set [status] = '{1}', qty=0, amount=0, fzqty={2}, fzamount={3}" +
  112. // " where jihao = '{0}'", siteLevelNozzleId, 'F',
  113. // totalizer.Item1, totalizer.Item2);
  114. // })
  115. // .Aggregate((acc, n) => acc + " " + n), posSqlConnection);
  116. // fccLogger.LogDebug("setPumpOnIdleCommand(via Fdc): " + setPumpOnIdleCommand.CommandText);
  117. // posSqlConnection.Open();
  118. // setPumpOnIdleCommand.ExecuteNonQuery();
  119. // }
  120. // catch (Exception ex)
  121. // {
  122. // fccLogger.LogDebug("executing setPumpOnIdleCommand(via Fdc) failed, exception detail: " + ex,
  123. // DebugLogLevel.Normal);
  124. // }
  125. // }
  126. //}
  127. //else if (this.autoAuthorizePumpWhenCalling && e.NozzleState == NozzleState.Out)
  128. //{
  129. // var authParameter = new AuthorizeParameters()
  130. // {
  131. // PriceGroup = PriceGroup.FullService,
  132. // LockToReleaseClient = false,
  133. // PresetType = PresetType.Amount,
  134. // PresetValue = 0,
  135. // Prepay = false,
  136. // PayType = "PC",
  137. // AuthorizationId = authorizationId++ % int.MaxValue
  138. // };
  139. // for (int i = 0; i < callingPump.Nozzles.Count; i++)
  140. // {
  141. // int idFuelGrade = callingPump.Nozzles[i].FuelGrade;
  142. // authParameter.AllowedFuelGrade[idFuelGrade] = true;
  143. // }
  144. // fccLogger.LogDebug("Authorizing for pumpId: " + callingPump.Id);
  145. // callingPump.AuthorizeAsync((byte)(callingPump.Id - 1), authParameter, (_, arg) =>
  146. // {
  147. // var pumpId = (int)(arg.UserToken);
  148. // if (arg.Success)
  149. // {
  150. // fccLogger.LogDebug("AuthorizeAsync finished successfully for pumpId: " + pumpId);
  151. // }
  152. // else
  153. // {
  154. // fccLogger.LogDebug("AuthorizeAsync failed for pumpId: " + pumpId);
  155. // }
  156. // }, callingPump.Id);
  157. //}
  158. }
  159. void FdcCommunicator_OnFuellingStateChange(object sender, FuellingStateChangeEventArgs e)
  160. {
  161. try
  162. {
  163. fccLogger.LogDebug("FdcCommunicator_OnFuellingStateChange(), args: " + e);
  164. fccLogger.LogDebug("\r\n PumpID = " + e.Fuelling.Pump.Id +
  165. "\r\n Nozzle = " + e.Fuelling.Nozzle.Id +
  166. "\r\n Quantity = " + e.Fuelling.Quantity +
  167. "\r\n Amount = $" + e.Fuelling.Amount +
  168. "\r\n FuelGrade = " + e.Fuelling.FuelGrade +
  169. "\r\n Price = $" + e.Fuelling.Price +
  170. "\r\n CompletionDateTime = $" + e.Fuelling.CompletionDateTime +
  171. "\r\n ReservingDeviceId = " + e.Fuelling.ReservingDeviceId +
  172. "\r\n ReservedBy = " + e.Fuelling.ReservedBy +
  173. "\r\n FuelPeriodID = " + e.Fuelling.FuelPeriodId +
  174. "\r\n FuellingSeqNumber = " + e.Fuelling.FuellingSequenceNumber +
  175. "\r\n State = " + e.State);
  176. var parameters = new StringDictionary();
  177. parameters["PumpID"] = e.Fuelling.Pump.Id.ToString();
  178. parameters["EventType"] = "FuellingStatusChange";
  179. parameters["Finished"] = "true";
  180. parameters["State"] = e.State.ToString();
  181. parameters["FuelingSequenceNo"] = e.Fuelling.FuellingSequenceNumber.ToString();
  182. parameters["ho"] = e.Fuelling.Nozzle.Id.ToString();
  183. parameters["GR"] = e.Fuelling.FuelGrade.ToString();
  184. parameters["VO"] = e.Fuelling.Quantity.ToString();
  185. parameters["AM"] = e.Fuelling.Amount.ToString();
  186. parameters["PU"] = e.Fuelling.Price.ToString();
  187. this.OnDataReceived?.Invoke(this, new FccDataReceivedEventArgs(parameters));
  188. }
  189. catch (Exception ex)
  190. {
  191. fccLogger.LogDebug("Exception in handling FdcCommunicator_OnFuellingStateChange:" + ex);
  192. }
  193. if (e.State == FuellingState.PayableTransaction)
  194. {
  195. /* 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*/
  196. this.forecourtControl.Pumps[e.Fuelling.Pump.Id - 1].Fuellings.ForEach(f =>
  197. {
  198. var fsn = f.FuellingSequenceNumber;
  199. fccLogger.LogDebug("Sending SetAsPaidAsync for pumpId: " + f.Pump.Id + ", FuellingSequenceNumber: " + fsn);
  200. f.SetAsPaidAsync((_, arg) =>
  201. {
  202. var pumpId = (int)(arg.UserToken);
  203. if (arg.Success)
  204. {
  205. fccLogger.LogDebug("SetAsPaidAsync finished successfully for pumpId: " + pumpId + ", FuellingSequenceNumber: " + fsn);
  206. }
  207. else
  208. {
  209. fccLogger.LogDebug("SetAsPaidAsync failed for pumpId: " + pumpId + ", FuellingSequenceNumber: " + fsn);
  210. }
  211. }, f.Pump.Id);
  212. });
  213. }
  214. //else if (e.State == FuellingState.Paid)
  215. //{
  216. // var posSqlConnection = new SqlConnection(ConfigurationManager.ConnectionStrings["PosDatabaseConnStr"].ConnectionString);
  217. // using (posSqlConnection)
  218. // {
  219. // try
  220. // {
  221. // // see a case in site that 0.1 price sale reported in here, checked with FC side which should
  222. // // just a 0 amount trx, not sure why here changed the price to 0.1(fdc client bug?), no time to fix, just add
  223. // // a check to skip.
  224. // if (e.Fuelling.Price <= 1 || e.Fuelling.Amount < 1)
  225. // {
  226. // fccLogger.LogDebug("insert xiaofei2 will be skipped due to unexpected price or amount fuel sale-> "
  227. // + "qty: " + e.Fuelling.Quantity
  228. // + "Price: " + e.Fuelling.Price
  229. // + "Amount: " + e.Fuelling.Amount
  230. // + "seqNo.: " + e.Fuelling.FuellingSequenceNumber,
  231. // DebugLogLevel.Maximized);
  232. // return;
  233. // }
  234. // var totalizer = SiteConfigUtility.Default.GetTotalizer(e.Fuelling.Pump.Id, e.Fuelling.Nozzle.Id);
  235. // var updateFuelingTrxDoneCommand =
  236. // new SqlCommand(
  237. // string.Format(
  238. // "insert xiaofei2 (jihao, youpin, qty, danjia, amount, xf_date, xf_time, liushuino, fzqty, fzamount)" +
  239. // " values({0}, N'{1}', {2}, {3}, {4}, '{5}', '{6}', {7}, {8}, {9})",
  240. // SiteConfigUtility.Default.GetSiteLevelNozzleIdByLogicalNozzleId(e.Fuelling.Pump.Id, e.Fuelling.Nozzle.Id),
  241. // Translator.GetFriendlyGradeName(SiteConfigUtility.Default.GetGradeNameByGradeId(e.Fuelling.FuelGrade)),
  242. // e.Fuelling.Quantity,
  243. // e.Fuelling.Price,
  244. // e.Fuelling.Amount,
  245. // DateTime.Now.Date.ToString("yyyy-MM-dd"),
  246. // DateTime.Now.ToString("HH:mm:ss"),
  247. // e.Fuelling.FuellingSequenceNumber,
  248. // totalizer.Item1,
  249. // totalizer.Item2),
  250. // posSqlConnection);
  251. // fccLogger.LogDebug("updateFuelingTrxDoneCommand: " + updateFuelingTrxDoneCommand.CommandText,
  252. // DebugLogLevel.Maximized);
  253. // posSqlConnection.Open();
  254. // updateFuelingTrxDoneCommand.ExecuteNonQuery();
  255. // }
  256. // catch (Exception ex)
  257. // {
  258. // fccLogger.LogDebug("executing updateFuelingTrxDoneCommand failed, exception detail: " + ex,
  259. // DebugLogLevel.Normal);
  260. // }
  261. // }
  262. //}
  263. }
  264. public void AuthorizePumpAsync(Dictionary<string, object> parameterDic)
  265. {
  266. IPump callingPump = null;
  267. int pumpId = int.Parse(parameterDic["PumpId"].ToString());
  268. int sitewiseNozzleId = int.Parse(parameterDic["SitewiseNozzleId"].ToString());
  269. decimal authAmount = 0;
  270. decimal volume = 0;
  271. AuthorizeParameters authParameter = null;
  272. foreach (var pump in this.forecourtControl.Pumps)
  273. {
  274. if (pump.Id == pumpId)
  275. {
  276. callingPump = pump;
  277. break;
  278. }
  279. }
  280. if (parameterDic.ContainsKey("AuthAmount"))
  281. {
  282. authAmount = decimal.Parse(parameterDic["AuthAmount"].ToString());
  283. authParameter = new AuthorizeParameters()
  284. {
  285. PriceGroup = PriceGroup.FullService,
  286. LockToReleaseClient = false,
  287. PresetType = PresetType.Amount,
  288. PresetValue = authAmount,
  289. Prepay = false,
  290. PayType = "PC",
  291. AuthorizationId = authorizationId++ % int.MaxValue
  292. };
  293. }
  294. else if (parameterDic.ContainsKey("Volume"))
  295. {
  296. volume = decimal.Parse(parameterDic["Volume"].ToString());
  297. authParameter = new AuthorizeParameters()
  298. {
  299. PriceGroup = PriceGroup.FullService,
  300. LockToReleaseClient = false,
  301. PresetType = PresetType.Quantity,
  302. PresetValue = volume,
  303. Prepay = false,
  304. PayType = "PC",
  305. AuthorizationId = authorizationId++ % int.MaxValue
  306. };
  307. }
  308. else
  309. {
  310. authParameter = new AuthorizeParameters()
  311. {
  312. PriceGroup = PriceGroup.FullService,
  313. LockToReleaseClient = false,
  314. PresetType = PresetType.Unknown,
  315. Prepay = false,
  316. PayType = "PC",
  317. AuthorizationId = authorizationId++ % int.MaxValue
  318. };
  319. }
  320. for (int i = 0; i < callingPump.Nozzles.Count; i++)
  321. {
  322. int idFuelGrade = callingPump.Nozzles[i].FuelGrade;
  323. authParameter.AllowedFuelGrade[idFuelGrade] = true;
  324. }
  325. fccLogger.LogDebug("Authorizing for pumpId: " + callingPump.Id + ", authAmount:" + authAmount);
  326. callingPump.AuthorizeAsync((byte)(callingPump.Id - 1), authParameter, (_, arg) =>
  327. {
  328. if (arg.Success)
  329. {
  330. fccLogger.LogDebug("AuthorizeAsync finished successfully for pumpId: " + pumpId);
  331. }
  332. else
  333. {
  334. fccLogger.LogDebug("AuthorizeAsync failed for pumpId: " + pumpId);
  335. }
  336. var parameters = new StringDictionary();
  337. parameters["EventType"] = "AuthorizePump";
  338. parameters["PumpID"] = arg.UserToken.ToString();
  339. parameters["Result"] = arg.Result.ToString();
  340. parameters["Success"] = arg.Success.ToString();
  341. this.OnDataReceived?.Invoke(this, new FccDataReceivedEventArgs(parameters));
  342. }, callingPump.Id);
  343. }
  344. public void QueryTotalizer(Dictionary<string, object> parameterDic)
  345. {
  346. var newFuelPrices = new List<IFuelPriceReading>();
  347. string pumpId = parameterDic["PumpId"].ToString();
  348. string nozzleId = parameterDic["LogicalNozzleId"].ToString();
  349. (forecourtControl as FUSIONForecourtControl).GetPumpTotalsAsync(new FuelTotalReading(pumpId, nozzleId, "FP"), OnPumpAccumulatorReceived, null);
  350. }
  351. void FdcCommunicator_OnPumpAccumulatorReceived(object sender, AsyncCompletedEventArgs<PumpAccumulatorReading> e)
  352. {
  353. try
  354. {
  355. var parameters = new StringDictionary();
  356. parameters["PumpID"] = e.Result.Pump.Id.ToString();
  357. parameters["EventType"] = "TotalizerReceived";
  358. parameters["NozzleID"] = e.Result.Nozzle.Id.ToString();
  359. parameters["AmountTotalizer"] = e.Result.Amount.ToString();
  360. parameters["VolumeTotalizer"] = e.Result.Quantity.ToString();
  361. fccLogger.LogDebug("FdcCommunicator_OnPumpAccumulatorReceived event for pump: " + parameters["PumpID"]);
  362. foreach (string key in parameters.Keys)
  363. {
  364. fccLogger.LogDebug(string.Format("Key: {0}, Value: {1}", key, parameters[key]));
  365. }
  366. this.OnDataReceived?.Invoke(this, new FccDataReceivedEventArgs(parameters));
  367. }
  368. catch (Exception ex)
  369. {
  370. fccLogger.LogDebug("Exception in handling FdcCommunicator_OnPumpAccumulatorReceived:" + ex);
  371. }
  372. }
  373. public void SetFuelPrice(Dictionary<string, object> parameterDic)
  374. {
  375. var newFuelPrices = new List<IFuelPriceReading>();
  376. int fuelGrade = int.Parse(parameterDic["FuelGrade"].ToString());
  377. decimal newPrice = decimal.Parse(parameterDic["NewFuelPrice"].ToString());
  378. var fuelPriceReading = new FuelPriceReading(fuelGrade, 1, newPrice, string.Empty);
  379. newFuelPrices.Add(fuelPriceReading);
  380. // IList<IFuelPriceReading> newFuelPrices, EventHandler< AsyncCompletedEventArgs > requestCompleted, object userToken
  381. forecourtControl.SetFuelPriceAsync(newFuelPrices, null, null);
  382. }
  383. public void Dispose()
  384. {
  385. this.forecourtControl.Dispose();
  386. }
  387. public bool Start()
  388. {
  389. if (0 == Interlocked.CompareExchange(ref this.isStarted, 1, 0))
  390. {
  391. fccLogger.LogDebug("Connecting to FDC server with connStr: " + this.concreteFdcServerConnString);
  392. this.forecourtControl.Connect(this.concreteFdcServerConnString);
  393. return true;
  394. }
  395. else
  396. {
  397. throw new InvalidOperationException("Already started.");
  398. }
  399. }
  400. public bool IsStarted
  401. {
  402. get { return this.isStarted == 1; }
  403. }
  404. }
  405. }