CarPlatePayEPSClientApp.cs 77 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Globalization;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Text;
  7. using System.Threading;
  8. using System.Threading.Tasks;
  9. using System.Xml;
  10. using System.Xml.Serialization;
  11. using Applications.FDC;
  12. using Edge.Core.Configuration;
  13. using Edge.Core.IndustryStandardInterface.Pump;
  14. using Edge.Core.Processor;
  15. using FdcServerHost;
  16. using GenericDisplayCommand.Controls;
  17. using GenericDisplayCommand.Controls.V1;
  18. using HKCarPlateRecognize_App;
  19. using Microsoft.Extensions.DependencyInjection;
  20. using Microsoft.Extensions.Logging;
  21. using ShellChina_EPS_Project_CarPlatePay_EpsClient_App.MessageEntity.Base;
  22. using ShellChina_EPS_Project_CarPlatePay_EpsClient_App.MessageEntity.Incoming;
  23. using ShellChina_EPS_Project_CarPlatePay_EpsClient_App.MessageEntity.Outgoing;
  24. using Wayne.FDCPOSLibrary;
  25. using Timer = System.Timers.Timer;
  26. namespace ShellChina_EPS_Project_CarPlatePay_EpsClient_App
  27. {
  28. public class CarPlatePayEpsClientApp : IAppProcessor, IFdcCommunicableController
  29. {
  30. private FdcServerHostApp fdcServerApp;
  31. private IEnumerable<IFdcPumpController> fdcPumpControllers;
  32. public string MetaConfigName { get; set; }
  33. public ShellChinaEPSClientHandler EpsClientHandler;
  34. public HkCarPlateRecognizeApp HkCarPlateRecognizeapp;
  35. private string terminalId;
  36. private int epsTimeOut;
  37. private int carPlateTimeOut;
  38. private string videoSource;
  39. public Dictionary<string, CarPlateStatusInfo> CarPlateStatusInfos = new Dictionary<string, CarPlateStatusInfo>();
  40. //private static NLog.Logger logger = NLog.LogManager.LoadConfiguration("NLog.config").GetLogger("ShellChinaEPSClientApp");
  41. private static ILogger logger;
  42. private Dictionary<string, string> BackGroundColors = new Dictionary<string, string>();
  43. private const string ErrorTransTextColor = "#ffffff";
  44. private const string TextColor = "#000000";
  45. private Dictionary<(int, int), int> nozzles = new Dictionary<(int, int), int>();
  46. private DateTime lastUpdateOutdoorScreenTime = DateTime.Now;
  47. private readonly System.Timers.Timer UpdateOutdoorScreenTimer = null;
  48. private int _updateOutdoorScreenInterval = 0;
  49. private int delayToSendPayment = 5;
  50. ///// <summary>
  51. /////
  52. ///// </summary>
  53. ///// <param name="terminalId">此终端号由EPS分配,需要配置到终端内</param>
  54. ///// <param name="epsTimeOut">how long to wait for response from EPS</param>
  55. ///// <param name="carPlateTimeOut">how long to keep the car plate if the sale finished</param>
  56. public CarPlatePayEpsClientApp(string terminalId, int epsTimeOut, int carPlateTimeOut, int updateScreenInterval, int delay, string videoSrc, IServiceProvider services)
  57. {
  58. this.terminalId = terminalId;
  59. this.epsTimeOut = epsTimeOut;
  60. this.carPlateTimeOut = carPlateTimeOut;
  61. if (delay != 0) this.delayToSendPayment = delay;
  62. videoSource = videoSrc;
  63. var loggerFactory = services.GetRequiredService<ILoggerFactory>();
  64. logger = loggerFactory.CreateLogger("DynamicPrivate_ShellChinaEPSClientApp");
  65. BuildDefaultBackGroundColors();
  66. OnMessageReceivedViaFdc += ProcessMessageReceivedViaFdc;
  67. if (updateScreenInterval > 0)
  68. {
  69. _updateOutdoorScreenInterval = updateScreenInterval;
  70. UpdateOutdoorScreenTimer = new Timer();
  71. UpdateOutdoorScreenTimer.Interval = updateScreenInterval * 60 * 1000;
  72. }
  73. }
  74. private void BuildDefaultBackGroundColors()
  75. {
  76. BackGroundColors.Add("车牌付", "#bababa");
  77. BackGroundColors.Add("普通用户", "#ffd700");
  78. BackGroundColors.Add("车牌付加油中", "#ffff00");
  79. BackGroundColors.Add("车牌付待支付", "#1e90ff");
  80. BackGroundColors.Add("车牌付已支付", "#32cd32");
  81. BackGroundColors.Add("交易异常", "#ff0000");
  82. BackGroundColors.Add("车牌付待加油", "#bababa");
  83. }
  84. public CarPlatePayEpsClientApp()
  85. {
  86. logger.LogDebug("Do nothing");
  87. }
  88. public Task<bool> Start()
  89. {
  90. //OnMessageReceivedViaFdc += ProcessMessageReceivedViaFdc;
  91. HkCarPlateRecognizeapp.NewCarPlateCatched += HKCarPlateRecognizeapp_OnNewCarPlateCatched;
  92. foreach (var fdcPumpController in fdcPumpControllers)
  93. {
  94. fdcPumpController.OnStateChange += FdcPumpController_OnStateChange;
  95. }
  96. if (fdcServerApp != null)
  97. {
  98. fdcServerApp.OnCurrentFuellingStatusChange += FdcServer_OnCurrentFuellingStatusChange;
  99. fdcServerApp.OnStateChange += fdcServer_OnStateChange;
  100. }
  101. SetupSiteNozzles(fdcPumpControllers);
  102. //Send pump info to outdoor screen
  103. GenerateAndBrodcastMessageToOutdoorScreen();
  104. //GenerateAndBrodcastMessageToOutdoorScreenViaFuellingPoint();
  105. if (UpdateOutdoorScreenTimer != null)
  106. {
  107. UpdateOutdoorScreenTimer.Elapsed += (o, e) =>
  108. {
  109. logger.LogDebug("update screen timer elapsed");
  110. if (DateTime.Now.Subtract(lastUpdateOutdoorScreenTime).TotalMinutes > _updateOutdoorScreenInterval)
  111. {
  112. logger.LogDebug("update outdoor screen due to IDLE timer");
  113. GenerateAndBrodcastMessageToOutdoorScreen();
  114. // GenerateAndBrodcastMessageToOutdoorScreenViaFuellingPoint();
  115. }
  116. };
  117. UpdateOutdoorScreenTimer.Start();
  118. }
  119. return Task.FromResult(true);
  120. }
  121. private void fdcServer_OnStateChange(object sender, FdcPumpControllerOnStateChangeEventArg e)
  122. {
  123. bool needUpdateScreen = false;
  124. try
  125. {
  126. logger.LogDebug("FdcPumpController_OnStateChange state:{0}", e.NewPumpState);
  127. if (e.StateChangedNozzles != null)
  128. {
  129. //check plate list to see if any plate binded to the nozzle
  130. var stateChangedNozzle = e.StateChangedNozzles.First();
  131. string tempBindstr = "Pump" + stateChangedNozzle.PumpId + "_Nozzle" + stateChangedNozzle.LogicalId;
  132. logger.LogInformation("FdcPumpController_OnStateChange {0},state:{1}", tempBindstr, e.NewPumpState.ToString());
  133. lock (CarPlateStatusInfos)
  134. {
  135. foreach (var carPlateStatusInfo in CarPlateStatusInfos)
  136. {
  137. if (carPlateStatusInfo.Value.BindingInfo != null &&
  138. carPlateStatusInfo.Value.MemberShipId != "0" &&
  139. carPlateStatusInfo.Value.BindingInfo.BindedNozzle.Equals(tempBindstr) &&
  140. (carPlateStatusInfo.Value.PaymentState != CarPlateStatusInfo.PaymentStatus.车牌付已支付 &&
  141. carPlateStatusInfo.Value.PaymentState != CarPlateStatusInfo.PaymentStatus.交易异常 &&
  142. carPlateStatusInfo.Value.PaymentState != CarPlateStatusInfo.PaymentStatus.车牌付待支付))
  143. {
  144. logger.LogDebug("Nozzle:{0} bind with plate {1}", stateChangedNozzle.LogicalId,
  145. carPlateStatusInfo.Key);
  146. switch (e.NewPumpState)
  147. {
  148. case LogicalDeviceState.FDC_AUTHORISED:
  149. case LogicalDeviceState.FDC_CALLING:
  150. if (carPlateStatusInfo.Value.PaymentState !=
  151. CarPlateStatusInfo.PaymentStatus.车牌付待加油)
  152. {
  153. carPlateStatusInfo.Value.PaymentState =
  154. CarPlateStatusInfo.PaymentStatus.车牌付待加油;
  155. needUpdateScreen = true;
  156. }
  157. break;
  158. case LogicalDeviceState.FDC_FUELLING:
  159. if (carPlateStatusInfo.Value.PaymentState !=
  160. CarPlateStatusInfo.PaymentStatus.车牌付加油中)
  161. {
  162. carPlateStatusInfo.Value.PaymentState =
  163. CarPlateStatusInfo.PaymentStatus.车牌付加油中;
  164. needUpdateScreen = true;
  165. }
  166. break;
  167. default:
  168. logger.LogDebug(
  169. "FdcPumpController_OnStateChange, current state is {0}, CarPlateStatusInfo PaymentStatus:{1}",
  170. e.NewPumpState,
  171. Enum.GetName(typeof(CarPlateStatusInfo.PaymentStatus),
  172. carPlateStatusInfo.Value.PaymentState));
  173. break;
  174. }
  175. break;
  176. }
  177. }
  178. }
  179. }
  180. }
  181. catch (Exception ex)
  182. {
  183. logger.LogError("exception happened in FdcPumpController_OnStateChange:{0}", ex.Message);
  184. }
  185. if (needUpdateScreen) GenerateAndBrodcastMessageToOutdoorScreen();//GenerateAndBrodcastMessageToOutdoorScreenViaFuellingPoint();
  186. }
  187. private void FdcServer_OnCurrentFuellingStatusChange(object sender, FdcServerTransactionDoneEventArg e)
  188. {
  189. try
  190. {
  191. logger.LogDebug("Fuelling state changed");
  192. CarPlateStatusInfo bindedCarPlateStatusInfo = null;
  193. //when the fuel transaction finished, update the binded plate state to waiting for payment
  194. if (e.Transaction.Finished)
  195. {
  196. var tempBindStr = "Pump" + e.Transaction.Nozzle.PumpId + "_Nozzle" + e.Transaction.Nozzle.LogicalId;
  197. //var tempBindStr = "Pump" + e.Transaction.Nozzle.PumpId;
  198. logger.LogDebug("Fuelling state change on: {0}, Amount{1}", tempBindStr, e.Transaction.Amount);
  199. lock (CarPlateStatusInfos)
  200. {
  201. foreach (var carPlateStatusInfo in CarPlateStatusInfos)
  202. {
  203. //logger.Debug("carPlateStatusInfo MemberShipId:{0},plate:{1},paymentState:{2},count:{3}", carPlateStatusInfo.Value.MemberShipId, carPlateStatusInfo.Key, carPlateStatusInfo.Value.PaymentState, CarPlateStatusInfos.Count);
  204. if (carPlateStatusInfo.Value.MemberShipId != "0" &&
  205. carPlateStatusInfo.Value.BindingInfo != null &&
  206. carPlateStatusInfo.Value.BindingInfo.BindedNozzle.Equals(tempBindStr) &&
  207. carPlateStatusInfo.Value.PaymentState == CarPlateStatusInfo.PaymentStatus.车牌付加油中)
  208. {
  209. //logger.Debug("in the if");
  210. carPlateStatusInfo.Value.PaymentState = CarPlateStatusInfo.PaymentStatus.车牌付待支付;
  211. //logger.Debug("after payment state was set");
  212. bindedCarPlateStatusInfo = carPlateStatusInfo.Value;
  213. //logger.Debug("binded statusInfo");
  214. break;
  215. }
  216. //logger.Debug("exit for");
  217. }
  218. }
  219. }
  220. if (bindedCarPlateStatusInfo != null)
  221. {
  222. GenerateAndBrodcastMessageToOutdoorScreen();
  223. //GenerateAndBrodcastMessageToOutdoorScreenViaFuellingPoint();
  224. var logicalNozzleId = nozzles[(e.Transaction.Nozzle.PumpId, e.Transaction.Nozzle.LogicalId)];
  225. logger.LogInformation("send payment request to EPS amount:{0},transNum:{1},nozzle:{2},membership:{3},plate:{4},sitelevelNozzleId:{5}", e.Transaction.Amount, bindedCarPlateStatusInfo.TransactionNumber, e.Transaction.Nozzle.LogicalId, bindedCarPlateStatusInfo.MemberShipId, bindedCarPlateStatusInfo.PlateLicense, logicalNozzleId);
  226. //System.Timers.Timer timer = new Timer(5000);
  227. var paymentRequest =
  228. new PaymentRequest((decimal)e.Transaction.Amount / 100, bindedCarPlateStatusInfo.TransactionNumber, logicalNozzleId, bindedCarPlateStatusInfo.MemberShipId, bindedCarPlateStatusInfo.PlateLicense,
  229. terminalId)
  230. { TPDU = "6000300000" };
  231. //timer.Elapsed += (o, te) =>
  232. //{
  233. // StartLicensePayment((decimal)e.Transaction.Amount / 100, bindedCarPlateStatusInfo.TransactionNumber, logicalNozzleId, bindedCarPlateStatusInfo.MemberShipId, bindedCarPlateStatusInfo.PlateLicense);
  234. //};
  235. //timer.Start();
  236. ThreadPool.QueueUserWorkItem(new WaitCallback(StartLicensePayment), paymentRequest);
  237. //StartLicensePayment((decimal)e.Transaction.Amount / 100, bindedCarPlateStatusInfo.TransactionNumber, logicalNozzleId, bindedCarPlateStatusInfo.MemberShipId, bindedCarPlateStatusInfo.PlateLicense);
  238. }
  239. }
  240. catch (Exception exception)
  241. {
  242. logger.LogError("exception happened on currentFuelling state change:{0}", exception.Message);
  243. }
  244. }
  245. private void FdcPumpController_OnStateChange(object sender, FdcPumpControllerOnStateChangeEventArg e)
  246. {
  247. bool needUpdateScreen = false;
  248. try
  249. {
  250. logger.LogDebug("FdcPumpController_OnStateChange state:{0}", e.NewPumpState);
  251. if (e.StateChangedNozzles != null)
  252. {
  253. //check plate list to see if any plate binded to the nozzle
  254. var stateChangedNozzle = e.StateChangedNozzles.First();
  255. //For the shell site, treat one fuelling point as a nozzle
  256. string tempBindstr = "Pump" + stateChangedNozzle.PumpId + "_Nozzle" + stateChangedNozzle.LogicalId;
  257. //string tempBindstr = "Pump" + stateChangedNozzle.PumpId;
  258. logger.LogInformation("FdcPumpController_OnStateChange {0},state:{1}", tempBindstr, e.NewPumpState.ToString());
  259. lock (CarPlateStatusInfos)
  260. {
  261. foreach (var carPlateStatusInfo in CarPlateStatusInfos)
  262. {
  263. if (carPlateStatusInfo.Value.BindingInfo != null &&
  264. carPlateStatusInfo.Value.MemberShipId != "0" &&
  265. carPlateStatusInfo.Value.BindingInfo.BindedNozzle.Equals(tempBindstr) &&
  266. (carPlateStatusInfo.Value.PaymentState != CarPlateStatusInfo.PaymentStatus.车牌付已支付 &&
  267. carPlateStatusInfo.Value.PaymentState != CarPlateStatusInfo.PaymentStatus.交易异常 &&
  268. carPlateStatusInfo.Value.PaymentState != CarPlateStatusInfo.PaymentStatus.车牌付待支付))
  269. {
  270. logger.LogInformation("Nozzle:{0} bind with plate {1}", stateChangedNozzle.LogicalId,
  271. carPlateStatusInfo.Key);
  272. switch (e.NewPumpState)
  273. {
  274. case LogicalDeviceState.FDC_AUTHORISED:
  275. case LogicalDeviceState.FDC_CALLING:
  276. if (carPlateStatusInfo.Value.PaymentState !=
  277. CarPlateStatusInfo.PaymentStatus.车牌付待加油)
  278. {
  279. carPlateStatusInfo.Value.PaymentState =
  280. CarPlateStatusInfo.PaymentStatus.车牌付待加油;
  281. needUpdateScreen = true;
  282. }
  283. break;
  284. case LogicalDeviceState.FDC_FUELLING:
  285. if (carPlateStatusInfo.Value.PaymentState !=
  286. CarPlateStatusInfo.PaymentStatus.车牌付加油中)
  287. {
  288. carPlateStatusInfo.Value.PaymentState =
  289. CarPlateStatusInfo.PaymentStatus.车牌付加油中;
  290. needUpdateScreen = true;
  291. }
  292. break;
  293. default:
  294. logger.LogInformation(
  295. "FdcPumpController_OnStateChange, current state is {0}, CarPlateStatusInfo PaymentStatus:{1}",
  296. e.NewPumpState,
  297. Enum.GetName(typeof(CarPlateStatusInfo.PaymentStatus),
  298. carPlateStatusInfo.Value.PaymentState));
  299. break;
  300. }
  301. break;
  302. }
  303. }
  304. }
  305. }
  306. }
  307. catch (Exception ex)
  308. {
  309. logger.LogError("exception happened in FdcPumpController_OnStateChange:{0}", ex.Message);
  310. }
  311. if (needUpdateScreen) GenerateAndBrodcastMessageToOutdoorScreen();//GenerateAndBrodcastMessageToOutdoorScreenViaFuellingPoint();
  312. }
  313. public Task<bool> Stop()
  314. {
  315. if (OnMessageReceivedViaFdc != null)
  316. {
  317. OnMessageReceivedViaFdc -= ProcessMessageReceivedViaFdc;
  318. }
  319. if (UpdateOutdoorScreenTimer.Enabled)
  320. UpdateOutdoorScreenTimer.Stop();
  321. return Task.FromResult(true);
  322. }
  323. public void Init(IEnumerable<IProcessor> processors)
  324. {
  325. var pumpControllers = new List<IFdcPumpController>();
  326. foreach (dynamic processor in processors)
  327. {
  328. //get the car plate recognize processor
  329. if (processor is IAppProcessor)
  330. {
  331. var HKCarPlateRecognize = processor as HkCarPlateRecognizeApp;
  332. if (HKCarPlateRecognize != null)
  333. HkCarPlateRecognizeapp = HKCarPlateRecognize;
  334. FdcServerHostApp fdcServer = processor as FdcServerHostApp;
  335. if (fdcServer != null)
  336. {
  337. logger.LogInformation("FdcServerHostApp retrieved as an IApplication instance!");
  338. fdcServerApp = processor;
  339. }
  340. continue;
  341. }
  342. var handler = processor.Context.Handler;
  343. if (handler is IFdcPumpController)
  344. pumpControllers.Add(handler);
  345. else if (handler is IEnumerable<IFdcPumpController>)
  346. pumpControllers.AddRange(handler);
  347. else if (handler is IDeviceHandler<byte[], MessageBase>)
  348. EpsClientHandler = handler as ShellChinaEPSClientHandler;
  349. }
  350. fdcPumpControllers = pumpControllers;
  351. }
  352. /// <summary>
  353. /// Called when new car plate catched from HK car plate recognize
  354. /// </summary>
  355. /// <param name="sender"></param>
  356. /// <param name="plateCatchedEventArgs"></param>
  357. public void HKCarPlateRecognizeapp_OnNewCarPlateCatched(object sender, PlateCatchedEventArgs plateCatchedEventArgs)
  358. {
  359. logger.LogDebug("HKCarPlateRecognizeapp_OnNewCarPlateCatched, plate license:{0}", plateCatchedEventArgs.PlateLicense);
  360. bool quiryMemberInfo = false;
  361. //1.send the car plate to shell EPS to get the member information
  362. //2.save it
  363. lock (CarPlateStatusInfos)
  364. {
  365. if (CarPlateStatusInfos.ContainsKey(plateCatchedEventArgs.PlateLicense))
  366. {
  367. var carPlateStatusInfo = CarPlateStatusInfos[plateCatchedEventArgs.PlateLicense];
  368. TimeSpan timeSpan = DateTime.Now - carPlateStatusInfo.Time;
  369. //remove the plate it haven't started fuelling or has finished the sale in carPlateTimeOut
  370. if (timeSpan.TotalMinutes > carPlateTimeOut && (carPlateStatusInfo.BindingInfo == null ||
  371. (carPlateStatusInfo.BindingInfo != null && carPlateStatusInfo.PaymentState == CarPlateStatusInfo.PaymentStatus.车牌付已支付)))
  372. {
  373. CarPlateStatusInfos.Remove(plateCatchedEventArgs.PlateLicense);
  374. quiryMemberInfo = true;
  375. //CarPlateStatusInfos.Add(plateCatchedEventArgs.PlateLicense, new CarPlateStatusInfo() { Time = plateCatchedEventArgs.Time, PlateLicense = plateCatchedEventArgs.PlateLicense });
  376. //QuiryMemberShipInfo(plateCatchedEventArgs.PlateLicense);
  377. }
  378. }
  379. else
  380. {
  381. quiryMemberInfo = true;
  382. //CarPlateStatusInfos.Add(plateCatchedEventArgs.PlateLicense, new CarPlateStatusInfo() { Time = plateCatchedEventArgs.Time, PlateLicense = plateCatchedEventArgs.PlateLicense });
  383. //QuiryMemberShipInfo(plateCatchedEventArgs.PlateLicense);
  384. }
  385. }
  386. //System.Timers.Timer timer = new Timer(5000);
  387. //var paymentRequest =
  388. // new PaymentRequest((decimal)230 / 100, "1212", 25, "12222", plateCatchedEventArgs.PlateLicense,
  389. // terminalId)
  390. // { TPDU = "6000300000" };
  391. //timer.Elapsed += (o, te) =>
  392. //{
  393. // StartLicensePayment(paymentRequest);
  394. //};
  395. // ////timer.Start();
  396. ////ThreadPool.QueueUserWorkItem(new WaitCallback(StartLicensePayment), paymentRequest);
  397. //logger.LogDebug("after thread pool");
  398. if (quiryMemberInfo)
  399. QuiryMemberShipInfo(plateCatchedEventArgs.PlateLicense);
  400. }
  401. /// <summary>
  402. /// send MembershipInquiryRequest to EPS and handle the response
  403. /// </summary>
  404. /// <param name="plateLicense">car plate</param>
  405. public void QuiryMemberShipInfo(string plateLicense)
  406. {
  407. logger.LogDebug("QuiryMemberShipInfo plate license:{0}", plateLicense);
  408. //this.EpsClientHandler.WriteAsync(new MessageBase(), );
  409. //plateLicense = "京A12" + plateLicense; //Joy need this one to test with EPS
  410. this.EpsClientHandler.EPSClientContext.Outgoing.WriteAsync(new MembershipInquiryRequest(plateLicense, terminalId) { TPDU = "6000300000" }, (request, response) => request is MembershipInquiryRequest && response is MembershipInquiryResponse,
  411. (request, response) =>
  412. {
  413. if (response != null)
  414. {
  415. var membershipResponse = response as MembershipInquiryResponse;
  416. var membershipInquiryRequest = request as MembershipInquiryRequest;
  417. if (membershipResponse != null && membershipResponse.P_39_交易响应码.Equals("00000"))//success
  418. {
  419. //int count = membershipResponse.P_24_会员ID.Length;
  420. //string a = membershipResponse.P_24_会员ID;
  421. //var b = a.Trim().Replace("\0", "");
  422. ////string s = @"0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\";
  423. //if (b.Equals("))
  424. //{
  425. // logger.Info("00");
  426. //}
  427. if (membershipResponse.P_24_会员ID.Replace("\0", "").Equals("0"))
  428. {
  429. logger.LogInformation("The plate:{0} is not a member", membershipResponse.P_34_车牌号码);
  430. }
  431. else
  432. {
  433. //save the message and brodcast to outdoor screen
  434. lock (CarPlateStatusInfos)
  435. {
  436. if (!CarPlateStatusInfos.ContainsKey(membershipInquiryRequest.P_34_车牌号码))
  437. {
  438. CarPlateStatusInfos.Add(membershipInquiryRequest.P_34_车牌号码,
  439. new CarPlateStatusInfo()
  440. {
  441. Time = DateTime.Now,
  442. PlateLicense = membershipInquiryRequest.P_34_车牌号码,
  443. MemberShipId = membershipResponse.P_24_会员ID.Replace("\0", ""),
  444. TransactionNumber = membershipResponse.P_12_交易流水号
  445. });
  446. }
  447. }
  448. }
  449. GenerateAndBrodcastMessageToOutdoorScreen();
  450. //GenerateAndBrodcastMessageToOutdoorScreenViaFuellingPoint();
  451. //StartLicensePayment((decimal)1.20, membershipResponse.P_12_交易流水号, 5, membershipResponse.P_24_会员ID,
  452. //membershipInquiryRequest.P_34_车牌号码);//joy this was added for testing
  453. }
  454. else//failed, log the error info
  455. {
  456. var membershipInquiryRequest1 = request as MembershipInquiryRequest;
  457. if (membershipInquiryRequest1 != null)
  458. logger.LogInformation(
  459. "Failed to get member ship info for carPlate:{0}, error message:{1}",
  460. membershipInquiryRequest1.P_34_车牌号码, membershipResponse.P_40_错误信息);
  461. //save the message and brodcast to outdoor screen
  462. //lock (CarPlateStatusInfos)
  463. //{
  464. // //var statusInfo = CarPlateStatusInfos[membershipInquiryRequest.P_34_车牌号码];
  465. // //statusInfo.MemberShipId = membershipInquiryRequest.P_34_车牌号码;
  466. // //statusInfo.TransactionNumber = membershipInquiryRequest.P_12_交易流水号;
  467. // try
  468. // {
  469. // CarPlateStatusInfos.Add(membershipInquiryRequest1.P_34_车牌号码,
  470. // new CarPlateStatusInfo()
  471. // {
  472. // Time = DateTime.Now,
  473. // PlateLicense = membershipInquiryRequest1.P_34_车牌号码,
  474. // MemberShipId = "0",
  475. // TransactionNumber = "123"
  476. // });
  477. // logger.LogInformation("member ship added to list in quiry:{0}", CarPlateStatusInfos.Last().Value.MemberShipId);
  478. // }
  479. // catch (Exception exc)
  480. // {
  481. // logger.LogError("Exception happened in QuiryMemberShipInfo:" + exc.Message);
  482. // }
  483. //}
  484. GenerateAndBrodcastMessageToOutdoorScreen();
  485. //GenerateAndBrodcastMessageToOutdoorScreenViaFuellingPoint();
  486. }
  487. }
  488. else
  489. {
  490. var membershipInquiryRequest = request as MembershipInquiryRequest;
  491. if (membershipInquiryRequest != null)
  492. logger.LogInformation("Failed to QuiryMemberShipInfo for:{0},timeout,response is null", membershipInquiryRequest.P_34_车牌号码);
  493. //According to the request from Site, the car which is not a member will not displayed on outdoor screen
  494. //save the message and brodcast to outdoor screen
  495. //lock (CarPlateStatusInfos)
  496. //{
  497. // //var statusInfo = CarPlateStatusInfos[membershipInquiryRequest.P_34_车牌号码];
  498. // //statusInfo.MemberShipId = membershipInquiryRequest.P_34_车牌号码;
  499. // //statusInfo.TransactionNumber = membershipInquiryRequest.P_12_交易流水号;
  500. // try
  501. // {
  502. // CarPlateStatusInfos.Add(membershipInquiryRequest.P_34_车牌号码,
  503. // new CarPlateStatusInfo()
  504. // {
  505. // Time = DateTime.Now,
  506. // PlateLicense = membershipInquiryRequest.P_34_车牌号码,
  507. // MemberShipId = "0",
  508. // TransactionNumber = "123"
  509. // });
  510. // logger.LogDebug("member ship added to list in quiry:{0}", CarPlateStatusInfos.Last().Value.MemberShipId);
  511. // }
  512. // catch (Exception exc)
  513. // {
  514. // logger.LogInformation("Exception happened in QuiryMemberShipInfo:" + exc.Message);
  515. // }
  516. //}
  517. GenerateAndBrodcastMessageToOutdoorScreen();
  518. //GenerateAndBrodcastMessageToOutdoorScreenViaFuellingPoint();
  519. }
  520. }, epsTimeOut);
  521. }
  522. /// <summary>
  523. /// Send payment request to EPS and handle the response
  524. /// </summary>
  525. //public void StartLicensePayment(decimal amount, string epsSequenceNo, int siteLevelNozzleId, string membershipId, string plateLicense)
  526. public void StartLicensePayment(object requestObject)
  527. {
  528. try
  529. {
  530. logger.LogInformation("StartLicensePayment");
  531. //var paymentRequest =
  532. // new PaymentRequest(amount, epsSequenceNo, siteLevelNozzleId, membershipId, plateLicense,
  533. // terminalId)
  534. // { TPDU = "6000300000" };
  535. Thread.Sleep(delayToSendPayment * 1000);
  536. var paymentRequest = requestObject as PaymentRequest;
  537. if (paymentRequest != null)
  538. {
  539. logger.LogDebug("StartLicensePayment after,amount:{0}", paymentRequest.P_4_交易金额);
  540. this.EpsClientHandler.EPSClientContext.Outgoing.WriteAsync(paymentRequest,
  541. (request, response) => request is PaymentRequest && response is PaymentResponse,
  542. (request, response) =>
  543. {
  544. if (response != null)
  545. {
  546. var paymentResponse = response as PaymentResponse;
  547. if (paymentResponse.P_39_交易响应码.Equals("00000"))
  548. {
  549. lock (CarPlateStatusInfos)
  550. {
  551. CarPlateStatusInfos[(request as PaymentRequest)?.P_34_车牌号码].PaymentState =
  552. CarPlateStatusInfo.PaymentStatus.车牌付已支付;
  553. }
  554. }
  555. else
  556. {
  557. logger.LogInformation("License payment failed, the error message is:{0}",
  558. paymentResponse.P_40_错误信息);
  559. lock (CarPlateStatusInfos)
  560. {
  561. CarPlateStatusInfos[(request as PaymentRequest)?.P_34_车牌号码].PaymentState =
  562. CarPlateStatusInfo.PaymentStatus.交易异常;
  563. }
  564. }
  565. }
  566. else
  567. {
  568. logger.LogInformation("License payment failed, response is null");
  569. lock (CarPlateStatusInfos)
  570. {
  571. CarPlateStatusInfos[(request as PaymentRequest)?.P_34_车牌号码].PaymentState =
  572. CarPlateStatusInfo.PaymentStatus.交易异常;
  573. }
  574. }
  575. logger.LogDebug("StartLicensePayment send to out door screen");
  576. GenerateAndBrodcastMessageToOutdoorScreen();
  577. //GenerateAndBrodcastMessageToOutdoorScreenViaFuellingPoint();
  578. }, epsTimeOut);
  579. }
  580. }
  581. catch (Exception exception)
  582. {
  583. logger.LogError("Exception happend in StartLicensePayment:{0}", exception.Message);
  584. }
  585. }
  586. /// <summary>
  587. /// Gnerate the layout for each out door screen and check the car plate info dic to see if anything need to remove
  588. /// </summary>
  589. public void GenerateAndBrodcastMessageToOutdoorScreen()
  590. {
  591. logger.LogDebug("GenerateAndBrodcastMessageToOutdoorScreen");
  592. List<string> platesToRemove = new List<string>();
  593. string match_parent = "match_parent";
  594. //video LinearLayout
  595. var videoLinearLayout = new LinearLayout();
  596. videoLinearLayout.Width = match_parent;
  597. videoLinearLayout.Orientation = "vertical";//chuizhi
  598. var videoview = new VideoView() { Src = videoSource };
  599. videoLinearLayout.Id = "VideoList";
  600. //plate info for each pump LinearLayout
  601. var pumpLinearLayout = new LinearLayout();
  602. pumpLinearLayout.Id = "NozzleList";
  603. videoLinearLayout.Width = match_parent;
  604. if (fdcPumpControllers != null)
  605. {
  606. foreach (var fdcPumpController in this.fdcPumpControllers.OrderBy(pc => pc.PumpId))
  607. {
  608. foreach (var nozzle in fdcPumpController.Nozzles.OrderBy(n => n.LogicalId))
  609. {
  610. var nozzleLinearLayout = new LinearLayout();
  611. nozzleLinearLayout.Id = "Pump" + fdcPumpController.PumpId + "_Nozzle" + nozzle.LogicalId;
  612. nozzleLinearLayout.Height = match_parent;
  613. nozzleLinearLayout.AllowActions = new List<AllowAction>() { AllowAction.Drop };
  614. nozzleLinearLayout.Views.Add(new NozzleView() { PumpId = fdcPumpController.PumpId, NozzleId = nozzles[(fdcPumpController.PumpId, nozzle.LogicalId)] });
  615. pumpLinearLayout.Views.Add(nozzleLinearLayout);
  616. }
  617. }
  618. }
  619. //public car plate LinearLayout
  620. var commonCarPlateLinearLayout = new LinearLayout();
  621. commonCarPlateLinearLayout.Width = match_parent;
  622. var unusedCarPlateLinearLayout = new LinearLayout();
  623. unusedCarPlateLinearLayout.Id = "IdleList";
  624. unusedCarPlateLinearLayout.Height = match_parent;
  625. unusedCarPlateLinearLayout.Orientation = "vertical";
  626. var errorTransLinearLayout = new LinearLayout();
  627. errorTransLinearLayout.Id = "ErrorList";
  628. errorTransLinearLayout.Height = match_parent;
  629. errorTransLinearLayout.Orientation = "vertical";
  630. commonCarPlateLinearLayout.Orientation = "horizontal";
  631. commonCarPlateLinearLayout.Views.Add(unusedCarPlateLinearLayout);
  632. commonCarPlateLinearLayout.Views.Add(errorTransLinearLayout);
  633. lock (CarPlateStatusInfos)
  634. {
  635. //assign each plate to proper place
  636. foreach (var carPlateStatusInfo in CarPlateStatusInfos.OrderBy(o => o.Value.Time).ToDictionary(p => p.Key, o => o.Value))
  637. {
  638. if ((DateTime.Now - carPlateStatusInfo.Value.Time).TotalMinutes > this.carPlateTimeOut && (carPlateStatusInfo.Value.PaymentState != CarPlateStatusInfo.PaymentStatus.车牌付待支付 &&
  639. carPlateStatusInfo.Value.PaymentState != CarPlateStatusInfo.PaymentStatus.车牌付加油中 && carPlateStatusInfo.Value.PaymentState != CarPlateStatusInfo.PaymentStatus.交易异常))
  640. {
  641. platesToRemove.Add(carPlateStatusInfo.Key);
  642. logger.LogDebug("Add car plate to the remove list:{0},count:{1}", carPlateStatusInfo.Key, platesToRemove.Count);
  643. continue;
  644. }
  645. var carplateLinearLayout = new LinearLayout();
  646. carplateLinearLayout.Id = carPlateStatusInfo.Key;
  647. //plate view
  648. var plateLicenseView = new TextView();
  649. plateLicenseView.Text = carPlateStatusInfo.Key;
  650. //plateLicenseView.Background = "White";
  651. carplateLinearLayout.Views.Add(plateLicenseView);
  652. carplateLinearLayout.Orientation = "horizontal";
  653. //status view
  654. var plateStatusView = new TextView();
  655. plateStatusView.TextColor = TextColor;
  656. //plate was binded with nozzle, will add the linearlayout to associated nozzleLinearLayout
  657. if (carPlateStatusInfo.Value.BindingInfo != null)
  658. {
  659. carplateLinearLayout.AllowActions = new List<AllowAction>() { AllowAction.Submit };
  660. plateStatusView.Text = Enum.GetName(typeof(CarPlateStatusInfo.PaymentStatus), carPlateStatusInfo.Value.PaymentState);
  661. plateStatusView.Background = BackGroundColors[plateStatusView.Text];
  662. carplateLinearLayout.Views.Add(plateStatusView);
  663. if (carPlateStatusInfo.Value.PaymentState == CarPlateStatusInfo.PaymentStatus.交易异常)
  664. {
  665. plateStatusView.TextColor = ErrorTransTextColor;
  666. errorTransLinearLayout.Views.Insert(0, carplateLinearLayout);
  667. }
  668. else
  669. {
  670. foreach (var nl in pumpLinearLayout.Views)
  671. {
  672. if (nl.Id == carPlateStatusInfo.Value.BindingInfo.BindedNozzle)
  673. {
  674. var tmp = new List<CarPlateStatusInfo>();
  675. foreach (var view in (nl as LinearLayout).Views)
  676. {
  677. if (view.Id != null && CarPlateStatusInfos.ContainsKey(view.Id))
  678. tmp.Add(CarPlateStatusInfos[view.Id]);
  679. }
  680. tmp.Add(carPlateStatusInfo.Value);
  681. tmp = new List<CarPlateStatusInfo>(tmp.OrderByDescending(c => c.BindingInfo.BingTime));
  682. int index = tmp.FindIndex(c => c.PlateLicense == carPlateStatusInfo.Key);
  683. (nl as LinearLayout)?.Views.Insert(index + 1, carplateLinearLayout);
  684. break;
  685. }
  686. }
  687. }
  688. }
  689. else if (!string.IsNullOrEmpty(carPlateStatusInfo.Value.MemberShipId))//plate is not binding to any nozzles
  690. {
  691. plateStatusView.Text = carPlateStatusInfo.Value.MemberShipId.Equals("0") ? "普通用户" : "车牌付";
  692. plateStatusView.Background = BackGroundColors[plateStatusView.Text];
  693. carplateLinearLayout.Views.Add(plateStatusView);
  694. carplateLinearLayout.AllowActions = new List<AllowAction>() { AllowAction.Drag, AllowAction.Drop };
  695. unusedCarPlateLinearLayout.Views.Insert(0, carplateLinearLayout);
  696. }
  697. }
  698. logger.LogInformation("plate count need to remove1:{0}", platesToRemove.Count);
  699. }
  700. logger.LogDebug("plate count need to remove2:{0}", platesToRemove.Count);
  701. //root linearLayout
  702. var rootLinearLayout = new LinearLayout();
  703. rootLinearLayout.Id = "root";
  704. rootLinearLayout.Orientation = "vertical";
  705. rootLinearLayout.Width = rootLinearLayout.Height = match_parent;
  706. rootLinearLayout.Views.Add(videoLinearLayout);
  707. rootLinearLayout.Views.Add(commonCarPlateLinearLayout);
  708. rootLinearLayout.Views.Add(pumpLinearLayout);
  709. var genericDisplayCommand = new GenericDisplayCommandV1Wrapper() { Control = rootLinearLayout, Version = 1 };
  710. //broadcaset message to outdoor screen
  711. logger.LogDebug("broadcaset message to outdoor screen");
  712. if (BroadcastMessageViaFdc != null)
  713. {
  714. logger.LogDebug("BroadcastMessageViaFdc not null");
  715. XmlSerializer xmlSerializer = new XmlSerializer(typeof(GenericDisplayCommandV1Wrapper),
  716. new Type[] { typeof(LinearLayout), typeof(VideoView), typeof(TextView), typeof(NozzleView) });
  717. //using (var sww = new StringWriter())
  718. using (var ms = new MemoryStream())
  719. {
  720. XmlWriterSettings settings = new XmlWriterSettings();
  721. settings.Encoding = new UTF8Encoding(false);
  722. settings.Indent = false;
  723. using (XmlWriter writer = XmlWriter.Create(ms, settings))
  724. {
  725. //writer.Settings.Encoding = Encoding.UTF8;
  726. xmlSerializer.Serialize(writer, genericDisplayCommand);
  727. //var xml = sww.ToString();
  728. var xml = Encoding.UTF8.GetString(ms.ToArray());
  729. BroadcastMessageViaFdc(xml);
  730. logger.LogDebug("Message broadcast to outdoor:{0}", xml);
  731. }
  732. }
  733. }
  734. logger.LogDebug("PlateToRemove Count:{0}", platesToRemove.Count);
  735. //remove the plates
  736. if (platesToRemove.Count > 0)
  737. {
  738. logger.LogDebug("{0} plates will be removed", platesToRemove.Count);
  739. lock (CarPlateStatusInfos)
  740. {
  741. foreach (var plate in platesToRemove)
  742. if (CarPlateStatusInfos.ContainsKey(plate)) CarPlateStatusInfos.Remove(plate);
  743. }
  744. }
  745. }
  746. /// <summary>
  747. /// For the Shell project, the site treat the whole fuelling point as a nozzle, in order to match the configuration as site, need treat the fuelling
  748. /// point as the same with site.
  749. /// </summary>
  750. public void GenerateAndBrodcastMessageToOutdoorScreenViaFuellingPoint()
  751. {
  752. logger.LogDebug("GenerateAndBrodcastMessageToOutdoorScreen");
  753. List<string> platesToRemove = new List<string>();
  754. string match_parent = "match_parent";
  755. //video LinearLayout
  756. var videoLinearLayout = new LinearLayout();
  757. videoLinearLayout.Width = match_parent;
  758. videoLinearLayout.Orientation = "vertical";//chuizhi
  759. var videoview = new VideoView() { Src = videoSource };
  760. videoLinearLayout.Id = "VideoList";
  761. //plate info for each pump LinearLayout
  762. var pumpLinearLayout = new LinearLayout();
  763. pumpLinearLayout.Id = "NozzleList";
  764. videoLinearLayout.Width = match_parent;
  765. if (fdcPumpControllers != null)
  766. {
  767. foreach (var fdcPumpController in this.fdcPumpControllers.OrderBy(pc => pc.PumpId))
  768. {
  769. var nozzleLinearLayout = new LinearLayout();
  770. nozzleLinearLayout.Id = "Pump" + fdcPumpController.PumpId;// + "_Nozzle" + fdcPumpController.PumpId;
  771. nozzleLinearLayout.Height = match_parent;
  772. nozzleLinearLayout.AllowActions = new List<AllowAction>() { AllowAction.Drop };
  773. nozzleLinearLayout.Views.Add(new NozzleView() { PumpId = fdcPumpController.PumpId, NozzleId = fdcPumpController.PumpId });
  774. pumpLinearLayout.Views.Add(nozzleLinearLayout);
  775. }
  776. }
  777. //public car plate LinearLayout
  778. var commonCarPlateLinearLayout = new LinearLayout();
  779. commonCarPlateLinearLayout.Width = match_parent;
  780. var unusedCarPlateLinearLayout = new LinearLayout();
  781. unusedCarPlateLinearLayout.Id = "IdleList";
  782. unusedCarPlateLinearLayout.Height = match_parent;
  783. unusedCarPlateLinearLayout.Orientation = "vertical";
  784. var errorTransLinearLayout = new LinearLayout();
  785. errorTransLinearLayout.Id = "ErrorList";
  786. errorTransLinearLayout.Height = match_parent;
  787. errorTransLinearLayout.Orientation = "vertical";
  788. commonCarPlateLinearLayout.Orientation = "horizontal";
  789. commonCarPlateLinearLayout.Views.Add(unusedCarPlateLinearLayout);
  790. commonCarPlateLinearLayout.Views.Add(errorTransLinearLayout);
  791. lock (CarPlateStatusInfos)
  792. {
  793. lastUpdateOutdoorScreenTime = DateTime.Now;
  794. //assign each plate to proper place
  795. foreach (var carPlateStatusInfo in CarPlateStatusInfos.OrderBy(o => o.Value.Time).ToDictionary(p => p.Key, o => o.Value))
  796. {
  797. if ((DateTime.Now - carPlateStatusInfo.Value.Time).TotalMinutes > this.carPlateTimeOut && (carPlateStatusInfo.Value.PaymentState != CarPlateStatusInfo.PaymentStatus.车牌付待支付 &&
  798. carPlateStatusInfo.Value.PaymentState != CarPlateStatusInfo.PaymentStatus.车牌付加油中 && carPlateStatusInfo.Value.PaymentState != CarPlateStatusInfo.PaymentStatus.交易异常))
  799. {
  800. platesToRemove.Add(carPlateStatusInfo.Key);
  801. logger.LogDebug("Add car plate to the remove list:{0},count:{1}", carPlateStatusInfo.Key, platesToRemove.Count);
  802. continue;
  803. }
  804. var carplateLinearLayout = new LinearLayout();
  805. carplateLinearLayout.Id = carPlateStatusInfo.Key;
  806. //plate view
  807. var plateLicenseView = new TextView();
  808. plateLicenseView.Text = carPlateStatusInfo.Key;
  809. //plateLicenseView.Background = "White";
  810. carplateLinearLayout.Views.Add(plateLicenseView);
  811. carplateLinearLayout.Orientation = "horizontal";
  812. //status view
  813. var plateStatusView = new TextView();
  814. plateStatusView.TextColor = TextColor;
  815. //plate was binded with nozzle, will add the linearlayout to associated nozzleLinearLayout
  816. if (carPlateStatusInfo.Value.BindingInfo != null)
  817. {
  818. carplateLinearLayout.AllowActions = new List<AllowAction>() { AllowAction.Submit };
  819. plateStatusView.Text = Enum.GetName(typeof(CarPlateStatusInfo.PaymentStatus), carPlateStatusInfo.Value.PaymentState);
  820. plateStatusView.Background = BackGroundColors[plateStatusView.Text];
  821. carplateLinearLayout.Views.Add(plateStatusView);
  822. if (carPlateStatusInfo.Value.PaymentState == CarPlateStatusInfo.PaymentStatus.交易异常)
  823. {
  824. plateStatusView.TextColor = ErrorTransTextColor;
  825. errorTransLinearLayout.Views.Insert(0, carplateLinearLayout);
  826. }
  827. else
  828. {
  829. foreach (var nl in pumpLinearLayout.Views)
  830. {
  831. if (nl.Id == carPlateStatusInfo.Value.BindingInfo.BindedNozzle)
  832. {
  833. var tmp = new List<CarPlateStatusInfo>();
  834. foreach (var view in (nl as LinearLayout).Views)
  835. {
  836. if (view.Id != null && CarPlateStatusInfos.ContainsKey(view.Id))
  837. tmp.Add(CarPlateStatusInfos[view.Id]);
  838. }
  839. tmp.Add(carPlateStatusInfo.Value);
  840. tmp = new List<CarPlateStatusInfo>(tmp.OrderByDescending(c => c.BindingInfo.BingTime));
  841. int index = tmp.FindIndex(c => c.PlateLicense == carPlateStatusInfo.Key);
  842. (nl as LinearLayout)?.Views.Insert(index + 1, carplateLinearLayout);
  843. break;
  844. }
  845. }
  846. }
  847. }
  848. else if (!string.IsNullOrEmpty(carPlateStatusInfo.Value.MemberShipId))//plate is not binding to any nozzles
  849. {
  850. plateStatusView.Text = carPlateStatusInfo.Value.MemberShipId.Equals("0") ? "普通用户" : "车牌付";
  851. plateStatusView.Background = BackGroundColors[plateStatusView.Text];
  852. carplateLinearLayout.Views.Add(plateStatusView);
  853. carplateLinearLayout.AllowActions = new List<AllowAction>() { AllowAction.Drag, AllowAction.Drop };
  854. unusedCarPlateLinearLayout.Views.Insert(0, carplateLinearLayout);
  855. }
  856. }
  857. logger.LogInformation("plate count need to remove1:{0}", platesToRemove.Count);
  858. }
  859. logger.LogDebug("plate count need to remove2:{0}", platesToRemove.Count);
  860. //root linearLayout
  861. var rootLinearLayout = new LinearLayout();
  862. rootLinearLayout.Id = "root";
  863. rootLinearLayout.Orientation = "vertical";
  864. rootLinearLayout.Width = rootLinearLayout.Height = match_parent;
  865. rootLinearLayout.Views.Add(videoLinearLayout);
  866. rootLinearLayout.Views.Add(commonCarPlateLinearLayout);
  867. rootLinearLayout.Views.Add(pumpLinearLayout);
  868. var genericDisplayCommand = new GenericDisplayCommandV1Wrapper() { Control = rootLinearLayout, Version = 1 };
  869. //broadcaset message to outdoor screen
  870. logger.LogDebug("broadcaset message to outdoor screen");
  871. if (BroadcastMessageViaFdc != null)
  872. {
  873. logger.LogDebug("BroadcastMessageViaFdc not null");
  874. XmlSerializer xmlSerializer = new XmlSerializer(typeof(GenericDisplayCommandV1Wrapper),
  875. new Type[] { typeof(LinearLayout), typeof(VideoView), typeof(TextView), typeof(NozzleView) });
  876. //using (var sww = new StringWriter())
  877. using (var ms = new MemoryStream())
  878. {
  879. XmlWriterSettings settings = new XmlWriterSettings();
  880. settings.Encoding = new UTF8Encoding(false);
  881. settings.Indent = false;
  882. using (XmlWriter writer = XmlWriter.Create(ms, settings))
  883. {
  884. //writer.Settings.Encoding = Encoding.UTF8;
  885. xmlSerializer.Serialize(writer, genericDisplayCommand);
  886. //var xml = sww.ToString();
  887. var xml = Encoding.UTF8.GetString(ms.ToArray());
  888. BroadcastMessageViaFdc(xml);
  889. logger.LogDebug("Message broadcast to outdoor:{0}", xml);
  890. }
  891. }
  892. }
  893. logger.LogDebug("PlateToRemove Count:{0}", platesToRemove.Count);
  894. //remove the plates
  895. if (platesToRemove.Count > 0)
  896. {
  897. logger.LogDebug("{0} plates will be removed", platesToRemove.Count);
  898. lock (CarPlateStatusInfos)
  899. {
  900. foreach (var plate in platesToRemove)
  901. if (CarPlateStatusInfos.ContainsKey(plate)) CarPlateStatusInfos.Remove(plate);
  902. }
  903. }
  904. }
  905. public Func<string, bool> BroadcastMessageViaFdc { get; set; }
  906. public Func<string, string, string, bool> SendMessageViaFdc { get; set; }
  907. public Func<string, Tuple<string, OverallResult>> OnMessageReceivedViaFdc { get; set; }
  908. /// <summary>
  909. /// Process message from screen
  910. /// </summary>
  911. /// <param name="message"></param>
  912. /// <returns></returns>
  913. public Tuple<string, OverallResult> ProcessMessageReceivedViaFdc(string message)
  914. {
  915. logger.LogDebug("Message recevied via FDC:{0}", message);
  916. ThreadPool.QueueUserWorkItem(new WaitCallback(ProcessFDCMessage), message);
  917. //if (message.Contains("GenericDisplayCommand"))
  918. //{
  919. // GenericDisplayCommandV1Wrapper genericDiaplayCommand = null;
  920. // XmlSerializer xmldeSerializer = new XmlSerializer(typeof(GenericDisplayCommandV1Wrapper),
  921. // new Type[] { typeof(LinearLayout), typeof(VideoView), typeof(TextView), typeof(NozzleView) });
  922. // using (var sr = new StringReader(message))
  923. // {
  924. // genericDiaplayCommand = (GenericDisplayCommandV1Wrapper)xmldeSerializer.Deserialize(sr);
  925. // }
  926. // if (genericDiaplayCommand != null)
  927. // {
  928. // var rootLinearLayount = genericDiaplayCommand.Control as LinearLayout;
  929. // if (rootLinearLayount != null && rootLinearLayount.Views.Count == 2)
  930. // {
  931. // BindCarplateWithNozzle(rootLinearLayount);
  932. // }
  933. // else if (rootLinearLayount != null && rootLinearLayount.Views.Count == 0)
  934. // {
  935. // ResetCarPlatePosition(rootLinearLayount);
  936. // }
  937. // else
  938. // {
  939. // logger.LogError("Invalid message received from outdoor screen");
  940. // }
  941. // }
  942. // else
  943. // {
  944. // logger.LogError("Invalid message received from outdoor screen");
  945. // }
  946. // //XmlDocument doc = new XmlDocument();
  947. // //doc.LoadXml(message);
  948. // //XmlNodeList layoutItems = doc.SelectNodes(@"GenericDisplayCommand/LinearLayout/LinearLayout");
  949. // //if (layoutItems.Count == 0) layoutItems = doc.SelectNodes(@"GenericDisplayCommand/LinearLayout");
  950. // //if (layoutItems.Count == 2)
  951. // //{
  952. // // BindCarplateWithNozzle(layoutItems);
  953. // //}
  954. // //else if (layoutItems.Count == 1)
  955. // //{
  956. // // ResetCarPlatePosition(layoutItems[0]);
  957. // //}
  958. // //else
  959. // //{
  960. // // logger.LogInformation("Invalid message received");
  961. // //}
  962. //}
  963. return new Tuple<string, OverallResult>("", OverallResult.Success);
  964. }
  965. private void ProcessFDCMessage(object messageStr)
  966. {
  967. try
  968. {
  969. var message = messageStr as string;
  970. if (message != null && message.Contains("GenericDisplayCommand"))
  971. {
  972. GenericDisplayCommandV1Wrapper genericDiaplayCommand = null;
  973. XmlSerializer xmldeSerializer = new XmlSerializer(typeof(GenericDisplayCommandV1Wrapper),
  974. new Type[] { typeof(LinearLayout), typeof(VideoView), typeof(TextView), typeof(NozzleView) });
  975. using (var sr = new StringReader(message))
  976. {
  977. genericDiaplayCommand = (GenericDisplayCommandV1Wrapper)xmldeSerializer.Deserialize(sr);
  978. }
  979. if (genericDiaplayCommand != null)
  980. {
  981. var rootLinearLayount = genericDiaplayCommand.Control as LinearLayout;
  982. if (rootLinearLayount != null && rootLinearLayount.Views.Count == 2)
  983. {
  984. BindCarplateWithNozzle(rootLinearLayount);
  985. }
  986. else if (rootLinearLayount != null && rootLinearLayount.Views.Count == 0)
  987. {
  988. ResetCarPlatePosition(rootLinearLayount);
  989. }
  990. else
  991. {
  992. logger.LogError("Invalid message received from outdoor screen");
  993. }
  994. }
  995. else
  996. {
  997. logger.LogError("Invalid message received from outdoor screen");
  998. }
  999. //XmlDocument doc = new XmlDocument();
  1000. //doc.LoadXml(message);
  1001. //XmlNodeList layoutItems = doc.SelectNodes(@"GenericDisplayCommand/LinearLayout/LinearLayout");
  1002. //if (layoutItems.Count == 0) layoutItems = doc.SelectNodes(@"GenericDisplayCommand/LinearLayout");
  1003. //if (layoutItems.Count == 2)
  1004. //{
  1005. // BindCarplateWithNozzle(layoutItems);
  1006. //}
  1007. //else if (layoutItems.Count == 1)
  1008. //{
  1009. // ResetCarPlatePosition(layoutItems[0]);
  1010. //}
  1011. //else
  1012. //{
  1013. // logger.LogInformation("Invalid message received");
  1014. //}
  1015. }
  1016. }
  1017. catch (Exception e)
  1018. {
  1019. logger.LogError("Exception in ProcessFDCMessage:" + e.Message);
  1020. }
  1021. }
  1022. private void BindCarplateWithNozzle(LinearLayout rootlayout)
  1023. {
  1024. logger.LogInformation("Bind car plate to nozzle,layoutItems count:{0}", rootlayout.Views.Count);
  1025. string carPlateLicense = String.Empty;
  1026. string bindingInfo = String.Empty;
  1027. string plateAction = string.Empty;
  1028. string nozzleAction = string.Empty;
  1029. string errorMessage = string.Empty;
  1030. foreach (var layoutItem in rootlayout.Views)
  1031. {
  1032. lock (CarPlateStatusInfos)
  1033. {
  1034. plateAction = "Drag";
  1035. if (layoutItem.AllowActions[0] == AllowAction.Drag /*&& CarPlateStatusInfos.ContainsKey(layoutItem.Id)*/)
  1036. {
  1037. if (CarPlateStatusInfos.ContainsKey(layoutItem.Id))
  1038. {
  1039. plateAction = Enum.GetName(typeof(AllowAction), layoutItem.AllowActions[0]);
  1040. carPlateLicense = layoutItem.Id;
  1041. }
  1042. else
  1043. {
  1044. errorMessage = "车牌" + layoutItem.Id + "已失效";
  1045. logger.LogInformation(errorMessage);
  1046. }
  1047. }
  1048. else if (nozzles.ContainsValue(Convert.ToInt32(layoutItem.Id)))
  1049. {
  1050. bindingInfo = layoutItem.Id;
  1051. nozzleAction = Enum.GetName(typeof(AllowAction), layoutItem.AllowActions[0]);
  1052. }
  1053. }
  1054. }
  1055. if (!string.IsNullOrEmpty(errorMessage))
  1056. {
  1057. SendPromptToOutdoorScreen(bindingInfo, errorMessage);
  1058. return;
  1059. }
  1060. logger.LogInformation("carPlateLicense{0},bindingInfo:{1},plateAction{2},nozzleAction{3}", carPlateLicense, bindingInfo, plateAction, nozzleAction);
  1061. if (!string.IsNullOrEmpty(plateAction) && !string.IsNullOrEmpty(carPlateLicense) &&
  1062. !string.IsNullOrEmpty(nozzleAction) && !string.IsNullOrEmpty(bindingInfo))
  1063. {
  1064. bool updated = false;
  1065. var originalPumpInfo = nozzles.FirstOrDefault(q =>
  1066. q.Value.ToString(CultureInfo.InvariantCulture) == bindingInfo).Key;
  1067. int pumpId = originalPumpInfo.Item1;
  1068. int nozzleId = originalPumpInfo.Item2;
  1069. //string bindingStr = "Pump" + pumpId;//for Shell, the site treat the fuelling point as one nozzle
  1070. string bindingStr = "Pump" + pumpId.ToString(CultureInfo.InvariantCulture) + "_Nozzle" +
  1071. nozzleId.ToString(CultureInfo.InvariantCulture);
  1072. if (!HasBindedPalte(bindingStr))
  1073. {
  1074. lock (CarPlateStatusInfos)
  1075. {
  1076. if (plateAction.Equals(Enum.GetName(typeof(AllowAction), AllowAction.Drag)) &&
  1077. nozzleAction.Equals(Enum.GetName(typeof(AllowAction), AllowAction.Drop)))
  1078. {
  1079. if (CarPlateStatusInfos.ContainsKey(carPlateLicense) &&
  1080. CarPlateStatusInfos[carPlateLicense].BindingInfo == null &&
  1081. !CarPlateStatusInfos[carPlateLicense].MemberShipId.Equals("0"))
  1082. {
  1083. CarPlateStatusInfos[carPlateLicense].BindingInfo = new BindingInfo()
  1084. {
  1085. BindedNozzle = bindingStr,
  1086. BingTime = DateTime.Now
  1087. };
  1088. LogicalDeviceState state = LogicalDeviceState.FDC_UNDEFINED;
  1089. foreach (var pump in fdcPumpControllers)
  1090. {
  1091. if (pump.PumpId == pumpId)
  1092. {
  1093. state = pump.QueryStatusAsync().Result;
  1094. break;
  1095. }
  1096. }
  1097. CarPlateStatusInfos[carPlateLicense].PaymentState =
  1098. state == LogicalDeviceState.FDC_FUELLING
  1099. ? CarPlateStatusInfo.PaymentStatus.车牌付加油中
  1100. : CarPlateStatusInfo.PaymentStatus.车牌付待加油;
  1101. updated = true;
  1102. }
  1103. else
  1104. {
  1105. logger.LogInformation("ignore the message, it's not a car plate member, membershipId is null");
  1106. SendPromptToOutdoorScreen(bindingInfo, "非车牌付用户");
  1107. }
  1108. }
  1109. }
  1110. }
  1111. else
  1112. {
  1113. logger.LogInformation("{0} has already binded with plate", bindingStr);
  1114. SendPromptToOutdoorScreen(bindingInfo, "已有绑定的车牌");
  1115. }
  1116. if (updated)
  1117. //GenerateAndBrodcastMessageToOutdoorScreenViaFuellingPoint();
  1118. GenerateAndBrodcastMessageToOutdoorScreen();
  1119. }
  1120. }
  1121. /// <summary>
  1122. /// This method called when trying to clear the error transaction or unbind the plate
  1123. /// </summary>
  1124. /// <param name="layoutItem"></param>
  1125. private void ResetCarPlatePosition(LinearLayout layoutItem)
  1126. {
  1127. logger.LogInformation("Reset car plate position");
  1128. bool updated = false;
  1129. string carLicense = string.Empty;
  1130. var action = layoutItem.AllowActions[0];
  1131. carLicense = layoutItem.Id;
  1132. if (!string.IsNullOrEmpty(carLicense) && action.Equals(AllowAction.Submit))
  1133. {
  1134. lock (CarPlateStatusInfos)
  1135. {
  1136. if (CarPlateStatusInfos.ContainsKey(carLicense) &&
  1137. CarPlateStatusInfos[carLicense].BindingInfo != null)
  1138. {
  1139. if (CarPlateStatusInfos[carLicense].PaymentState == CarPlateStatusInfo.PaymentStatus.交易异常)
  1140. CarPlateStatusInfos.Remove(carLicense);
  1141. else
  1142. CarPlateStatusInfos[carLicense].BindingInfo = null;
  1143. updated = true;
  1144. }
  1145. }
  1146. if (updated) GenerateAndBrodcastMessageToOutdoorScreen();//GenerateAndBrodcastMessageToOutdoorScreenViaFuellingPoint();
  1147. }
  1148. }
  1149. private void BindCarplateWithNozzle(XmlNodeList layoutItems)
  1150. {
  1151. logger.LogInformation("Bind car plate to nozzle,layoutItems count:{0}", layoutItems.Count);
  1152. string carPlateLicense = String.Empty;
  1153. string bindingInfo = String.Empty;
  1154. string plateAction = string.Empty;
  1155. string nozzleAction = string.Empty;
  1156. for (int i = 0; i < layoutItems.Count; i++)
  1157. {
  1158. lock (CarPlateStatusInfos)
  1159. {
  1160. //carPlateLicense = CarPlateStatusInfos.FirstOrDefault(a => a.Key != string.Empty).Key;
  1161. plateAction = "Drag";
  1162. //var IdNode = layoutItems[i].SelectSingleNode(@"Id");
  1163. if (CarPlateStatusInfos.ContainsKey(layoutItems[i].SelectSingleNode(@"Id")
  1164. ?.InnerText))
  1165. {
  1166. plateAction = layoutItems[i].SelectSingleNode(@"AllowActions/AllowAction")?.InnerText;
  1167. carPlateLicense = layoutItems[i].SelectSingleNode(@"Id")?.InnerText;
  1168. }
  1169. else if (nozzles.ContainsValue(Convert.ToInt32(layoutItems[i].SelectSingleNode(@"Id")?.InnerText.Trim())))
  1170. {
  1171. bindingInfo = layoutItems[i].SelectSingleNode(@"Id")?.InnerText;
  1172. nozzleAction = layoutItems[i].SelectSingleNode(@"AllowActions/AllowAction")?.InnerText;
  1173. }
  1174. }
  1175. }
  1176. logger.LogInformation("carPlateLicense{0},bindingInfo:{1},plateAction{2},nozzleAction{3}", carPlateLicense, bindingInfo, plateAction, nozzleAction);
  1177. if (!string.IsNullOrEmpty(plateAction) && !string.IsNullOrEmpty(carPlateLicense) &&
  1178. !string.IsNullOrEmpty(nozzleAction) && !string.IsNullOrEmpty(bindingInfo))
  1179. {
  1180. bool updated = false;
  1181. var originalPumpInfo = nozzles.FirstOrDefault(q =>
  1182. q.Value.ToString(CultureInfo.InvariantCulture) == bindingInfo).Key;
  1183. int pumpId = originalPumpInfo.Item1;
  1184. int nozzleId = originalPumpInfo.Item2;
  1185. string bindingStr = "Pump" + pumpId.ToString(CultureInfo.InvariantCulture) + "_Nozzle" +
  1186. nozzleId.ToString(CultureInfo.InvariantCulture);
  1187. //string bindingStr = "Pump" + pumpId;//for Shell, the site treat the fuelling po
  1188. if (!HasBindedPalte(bindingStr))
  1189. {
  1190. lock (CarPlateStatusInfos)
  1191. {
  1192. if (plateAction.Equals(Enum.GetName(typeof(AllowAction), AllowAction.Drag)) &&
  1193. nozzleAction.Equals(Enum.GetName(typeof(AllowAction), AllowAction.Drop)))
  1194. {
  1195. if (CarPlateStatusInfos.ContainsKey(carPlateLicense) &&
  1196. CarPlateStatusInfos[carPlateLicense].BindingInfo == null &&
  1197. !CarPlateStatusInfos[carPlateLicense].MemberShipId.Equals("0"))
  1198. {
  1199. //var originalPumpInfo = nozzles.FirstOrDefault(q =>
  1200. // q.Value.ToString(CultureInfo.InvariantCulture) == bindingInfo).Key;
  1201. //int pumpId = originalPumpInfo.Item1;
  1202. //int nozzleId = originalPumpInfo.Item2;
  1203. CarPlateStatusInfos[carPlateLicense].BindingInfo = new BindingInfo()
  1204. {
  1205. //BindedNozzle =
  1206. // "Pump" + pumpId.ToString(CultureInfo.InvariantCulture) + "_Nozzle" +
  1207. // nozzleId.ToString(CultureInfo.InvariantCulture),
  1208. BindedNozzle = bindingStr,
  1209. BingTime = DateTime.Now
  1210. };
  1211. LogicalDeviceState state = LogicalDeviceState.FDC_UNDEFINED;
  1212. foreach (var pump in fdcPumpControllers)
  1213. {
  1214. if (pump.PumpId == pumpId)
  1215. {
  1216. state = pump.QueryStatusAsync().Result;
  1217. break;
  1218. }
  1219. }
  1220. CarPlateStatusInfos[carPlateLicense].PaymentState =
  1221. state == LogicalDeviceState.FDC_FUELLING
  1222. ? CarPlateStatusInfo.PaymentStatus.车牌付加油中
  1223. : CarPlateStatusInfo.PaymentStatus.车牌付待加油;
  1224. updated = true;
  1225. }
  1226. else
  1227. {
  1228. logger.LogInformation("ignore the message, it's not a car plate member, membershipId is null");
  1229. SendPromptToOutdoorScreen(bindingInfo, "非车牌付用户");
  1230. }
  1231. }
  1232. }
  1233. }
  1234. else
  1235. {
  1236. logger.LogInformation("{0} has already binded with plate", bindingStr);
  1237. SendPromptToOutdoorScreen(bindingInfo, "已有绑定的车牌");
  1238. }
  1239. if (updated)
  1240. GenerateAndBrodcastMessageToOutdoorScreen();
  1241. //GenerateAndBrodcastMessageToOutdoorScreenViaFuellingPoint();
  1242. }
  1243. }
  1244. /// <summary>
  1245. /// This method called when trying to clear the error transaction or unbind the plate
  1246. /// </summary>
  1247. /// <param name="layoutItem"></param>
  1248. private void ResetCarPlatePosition(XmlNode layoutItem)
  1249. {
  1250. logger.LogInformation("Reset car plate position");
  1251. bool updated = false;
  1252. string carLicense = string.Empty;
  1253. var actionNode = layoutItem.SelectSingleNode(@"AllowActions/AllowAction");
  1254. carLicense = layoutItem.SelectSingleNode(@"Id")?.InnerText;
  1255. if (!string.IsNullOrEmpty(carLicense) && actionNode != null && actionNode.InnerText
  1256. .Equals(Enum.GetName(typeof(AllowAction), AllowAction.Submit)))
  1257. {
  1258. lock (CarPlateStatusInfos)
  1259. {
  1260. if (CarPlateStatusInfos.ContainsKey(carLicense) &&
  1261. CarPlateStatusInfos[carLicense].BindingInfo != null)
  1262. {
  1263. if (CarPlateStatusInfos[carLicense].PaymentState == CarPlateStatusInfo.PaymentStatus.交易异常)
  1264. CarPlateStatusInfos.Remove(carLicense);
  1265. else
  1266. CarPlateStatusInfos[carLicense].BindingInfo = null;
  1267. updated = true;
  1268. }
  1269. }
  1270. if (updated) GenerateAndBrodcastMessageToOutdoorScreen(); //GenerateAndBrodcastMessageToOutdoorScreenViaFuellingPoint();
  1271. }
  1272. }
  1273. private bool HasBindedPalte(string bindString)
  1274. {
  1275. bool alreadyBind = false;
  1276. lock (CarPlateStatusInfos)
  1277. {
  1278. foreach (var carplateStatusInfo in CarPlateStatusInfos)
  1279. {
  1280. if (carplateStatusInfo.Value.BindingInfo != null &&
  1281. carplateStatusInfo.Value.BindingInfo.BindedNozzle.Equals(bindString) && (carplateStatusInfo.Value.PaymentState == CarPlateStatusInfo.PaymentStatus.车牌付待加油 ||
  1282. carplateStatusInfo.Value.PaymentState == CarPlateStatusInfo.PaymentStatus.车牌付加油中))
  1283. {
  1284. logger.LogInformation("HasbindPlate: {0},plate:{1},plateState:{2}", bindString, carplateStatusInfo.Value.PlateLicense, carplateStatusInfo.Value.PaymentState);
  1285. alreadyBind = true;
  1286. break;
  1287. }
  1288. }
  1289. }
  1290. return alreadyBind;
  1291. }
  1292. private void SendPromptToOutdoorScreen(string nozzleId, string prompt)
  1293. {
  1294. var genericDisplayCommand = new GenericDisplayCommandV1Wrapper() { Version = 1, NozzleId = nozzleId.ToString(CultureInfo.InvariantCulture), Prompt = prompt };
  1295. if (BroadcastMessageViaFdc != null)
  1296. {
  1297. logger.LogDebug("BroadcastMessageViaFdc not null");
  1298. XmlSerializer xmlSerializer = new XmlSerializer(typeof(GenericDisplayCommandV1Wrapper),
  1299. new Type[] { typeof(LinearLayout), typeof(VideoView), typeof(TextView), typeof(NozzleView) });
  1300. //using (var sww = new StringWriter())
  1301. using (var ms = new MemoryStream())
  1302. {
  1303. XmlWriterSettings settings = new XmlWriterSettings();
  1304. settings.Encoding = new UTF8Encoding(false);
  1305. settings.Indent = false;
  1306. using (XmlWriter writer = XmlWriter.Create(ms, settings))
  1307. {
  1308. //writer.Settings.Encoding = Encoding.UTF8;
  1309. xmlSerializer.Serialize(writer, genericDisplayCommand);
  1310. //var xml = sww.ToString();
  1311. var xml = Encoding.UTF8.GetString(ms.ToArray());
  1312. BroadcastMessageViaFdc(xml);
  1313. logger.LogDebug("Message broadcast to outdoor:{0}", xml);
  1314. }
  1315. }
  1316. }
  1317. }
  1318. #region Site nozzles setup
  1319. /// <summary>
  1320. /// Set up the site nozzles, in particular the nozzle number
  1321. /// Strategy:
  1322. /// 1. Sort the Pumps by id
  1323. /// 2. Sort the nozzles by logical id
  1324. /// 3. The site nozzle number then will be (Pump id, Logical nozzle id) => site nozzle number
  1325. /// e.g. (1, 1) => 1, (1, 2) => 2, (2, 1) => 3, (3, 1) => 4, (3, 2) => 5, (4, 1) => 6 ...
  1326. /// </summary>
  1327. /// <param name="fdcPumpControllers">The Fdc pump instance collection.</param>
  1328. private void SetupSiteNozzles(IEnumerable<IFdcPumpController> fdcPumpControllers)
  1329. {
  1330. nozzles.Clear();
  1331. var nozzleProductConfig = Configurator.Default.NozzleExtraInfoConfiguration.Mapping;
  1332. foreach (NozzleExtraInfo np in nozzleProductConfig)
  1333. {
  1334. logger.LogDebug($"({np.PumpId}, {np.NozzleLogicalId}) => {np.SiteLevelNozzleId}");
  1335. nozzles.Add((np.PumpId, np.NozzleLogicalId), np.SiteLevelNozzleId ?? 0);
  1336. }
  1337. }
  1338. #endregion
  1339. }
  1340. }