using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Xml; using System.Xml.Serialization; using Applications.FDC; using Edge.Core.Configuration; using Edge.Core.IndustryStandardInterface.Pump; using Edge.Core.Processor; using FdcServerHost; using GenericDisplayCommand.Controls; using GenericDisplayCommand.Controls.V1; using HKCarPlateRecognize_App; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using ShellChina_EPS_Project_CarPlatePay_EpsClient_App.MessageEntity.Base; using ShellChina_EPS_Project_CarPlatePay_EpsClient_App.MessageEntity.Incoming; using ShellChina_EPS_Project_CarPlatePay_EpsClient_App.MessageEntity.Outgoing; using Wayne.FDCPOSLibrary; using Timer = System.Timers.Timer; namespace ShellChina_EPS_Project_CarPlatePay_EpsClient_App { public class CarPlatePayEpsClientApp : IAppProcessor, IFdcCommunicableController { private FdcServerHostApp fdcServerApp; private IEnumerable fdcPumpControllers; public string MetaConfigName { get; set; } public ShellChinaEPSClientHandler EpsClientHandler; public HkCarPlateRecognizeApp HkCarPlateRecognizeapp; private string terminalId; private int epsTimeOut; private int carPlateTimeOut; private string videoSource; public Dictionary CarPlateStatusInfos = new Dictionary(); //private static NLog.Logger logger = NLog.LogManager.LoadConfiguration("NLog.config").GetLogger("ShellChinaEPSClientApp"); private static ILogger logger; private Dictionary BackGroundColors = new Dictionary(); private const string ErrorTransTextColor = "#ffffff"; private const string TextColor = "#000000"; private Dictionary<(int, int), int> nozzles = new Dictionary<(int, int), int>(); private DateTime lastUpdateOutdoorScreenTime = DateTime.Now; private readonly System.Timers.Timer UpdateOutdoorScreenTimer = null; private int _updateOutdoorScreenInterval = 0; private int delayToSendPayment = 5; ///// ///// ///// ///// 此终端号由EPS分配,需要配置到终端内 ///// how long to wait for response from EPS ///// how long to keep the car plate if the sale finished public CarPlatePayEpsClientApp(string terminalId, int epsTimeOut, int carPlateTimeOut, int updateScreenInterval, int delay, string videoSrc, IServiceProvider services) { this.terminalId = terminalId; this.epsTimeOut = epsTimeOut; this.carPlateTimeOut = carPlateTimeOut; if (delay != 0) this.delayToSendPayment = delay; videoSource = videoSrc; var loggerFactory = services.GetRequiredService(); logger = loggerFactory.CreateLogger("DynamicPrivate_ShellChinaEPSClientApp"); BuildDefaultBackGroundColors(); OnMessageReceivedViaFdc += ProcessMessageReceivedViaFdc; if (updateScreenInterval > 0) { _updateOutdoorScreenInterval = updateScreenInterval; UpdateOutdoorScreenTimer = new Timer(); UpdateOutdoorScreenTimer.Interval = updateScreenInterval * 60 * 1000; } } private void BuildDefaultBackGroundColors() { BackGroundColors.Add("车牌付", "#bababa"); BackGroundColors.Add("普通用户", "#ffd700"); BackGroundColors.Add("车牌付加油中", "#ffff00"); BackGroundColors.Add("车牌付待支付", "#1e90ff"); BackGroundColors.Add("车牌付已支付", "#32cd32"); BackGroundColors.Add("交易异常", "#ff0000"); BackGroundColors.Add("车牌付待加油", "#bababa"); } public CarPlatePayEpsClientApp() { logger.LogDebug("Do nothing"); } public Task Start() { //OnMessageReceivedViaFdc += ProcessMessageReceivedViaFdc; HkCarPlateRecognizeapp.NewCarPlateCatched += HKCarPlateRecognizeapp_OnNewCarPlateCatched; foreach (var fdcPumpController in fdcPumpControllers) { fdcPumpController.OnStateChange += FdcPumpController_OnStateChange; } if (fdcServerApp != null) { fdcServerApp.OnCurrentFuellingStatusChange += FdcServer_OnCurrentFuellingStatusChange; fdcServerApp.OnStateChange += fdcServer_OnStateChange; } SetupSiteNozzles(fdcPumpControllers); //Send pump info to outdoor screen GenerateAndBrodcastMessageToOutdoorScreen(); //GenerateAndBrodcastMessageToOutdoorScreenViaFuellingPoint(); if (UpdateOutdoorScreenTimer != null) { UpdateOutdoorScreenTimer.Elapsed += (o, e) => { logger.LogDebug("update screen timer elapsed"); if (DateTime.Now.Subtract(lastUpdateOutdoorScreenTime).TotalMinutes > _updateOutdoorScreenInterval) { logger.LogDebug("update outdoor screen due to IDLE timer"); GenerateAndBrodcastMessageToOutdoorScreen(); // GenerateAndBrodcastMessageToOutdoorScreenViaFuellingPoint(); } }; UpdateOutdoorScreenTimer.Start(); } return Task.FromResult(true); } private void fdcServer_OnStateChange(object sender, FdcPumpControllerOnStateChangeEventArg e) { bool needUpdateScreen = false; try { logger.LogDebug("FdcPumpController_OnStateChange state:{0}", e.NewPumpState); if (e.StateChangedNozzles != null) { //check plate list to see if any plate binded to the nozzle var stateChangedNozzle = e.StateChangedNozzles.First(); string tempBindstr = "Pump" + stateChangedNozzle.PumpId + "_Nozzle" + stateChangedNozzle.LogicalId; logger.LogInformation("FdcPumpController_OnStateChange {0},state:{1}", tempBindstr, e.NewPumpState.ToString()); lock (CarPlateStatusInfos) { foreach (var carPlateStatusInfo in CarPlateStatusInfos) { if (carPlateStatusInfo.Value.BindingInfo != null && carPlateStatusInfo.Value.MemberShipId != "0" && carPlateStatusInfo.Value.BindingInfo.BindedNozzle.Equals(tempBindstr) && (carPlateStatusInfo.Value.PaymentState != CarPlateStatusInfo.PaymentStatus.车牌付已支付 && carPlateStatusInfo.Value.PaymentState != CarPlateStatusInfo.PaymentStatus.交易异常 && carPlateStatusInfo.Value.PaymentState != CarPlateStatusInfo.PaymentStatus.车牌付待支付)) { logger.LogDebug("Nozzle:{0} bind with plate {1}", stateChangedNozzle.LogicalId, carPlateStatusInfo.Key); switch (e.NewPumpState) { case LogicalDeviceState.FDC_AUTHORISED: case LogicalDeviceState.FDC_CALLING: if (carPlateStatusInfo.Value.PaymentState != CarPlateStatusInfo.PaymentStatus.车牌付待加油) { carPlateStatusInfo.Value.PaymentState = CarPlateStatusInfo.PaymentStatus.车牌付待加油; needUpdateScreen = true; } break; case LogicalDeviceState.FDC_FUELLING: if (carPlateStatusInfo.Value.PaymentState != CarPlateStatusInfo.PaymentStatus.车牌付加油中) { carPlateStatusInfo.Value.PaymentState = CarPlateStatusInfo.PaymentStatus.车牌付加油中; needUpdateScreen = true; } break; default: logger.LogDebug( "FdcPumpController_OnStateChange, current state is {0}, CarPlateStatusInfo PaymentStatus:{1}", e.NewPumpState, Enum.GetName(typeof(CarPlateStatusInfo.PaymentStatus), carPlateStatusInfo.Value.PaymentState)); break; } break; } } } } } catch (Exception ex) { logger.LogError("exception happened in FdcPumpController_OnStateChange:{0}", ex.Message); } if (needUpdateScreen) GenerateAndBrodcastMessageToOutdoorScreen();//GenerateAndBrodcastMessageToOutdoorScreenViaFuellingPoint(); } private void FdcServer_OnCurrentFuellingStatusChange(object sender, FdcServerTransactionDoneEventArg e) { try { logger.LogDebug("Fuelling state changed"); CarPlateStatusInfo bindedCarPlateStatusInfo = null; //when the fuel transaction finished, update the binded plate state to waiting for payment if (e.Transaction.Finished) { var tempBindStr = "Pump" + e.Transaction.Nozzle.PumpId + "_Nozzle" + e.Transaction.Nozzle.LogicalId; //var tempBindStr = "Pump" + e.Transaction.Nozzle.PumpId; logger.LogDebug("Fuelling state change on: {0}, Amount{1}", tempBindStr, e.Transaction.Amount); lock (CarPlateStatusInfos) { foreach (var carPlateStatusInfo in CarPlateStatusInfos) { //logger.Debug("carPlateStatusInfo MemberShipId:{0},plate:{1},paymentState:{2},count:{3}", carPlateStatusInfo.Value.MemberShipId, carPlateStatusInfo.Key, carPlateStatusInfo.Value.PaymentState, CarPlateStatusInfos.Count); if (carPlateStatusInfo.Value.MemberShipId != "0" && carPlateStatusInfo.Value.BindingInfo != null && carPlateStatusInfo.Value.BindingInfo.BindedNozzle.Equals(tempBindStr) && carPlateStatusInfo.Value.PaymentState == CarPlateStatusInfo.PaymentStatus.车牌付加油中) { //logger.Debug("in the if"); carPlateStatusInfo.Value.PaymentState = CarPlateStatusInfo.PaymentStatus.车牌付待支付; //logger.Debug("after payment state was set"); bindedCarPlateStatusInfo = carPlateStatusInfo.Value; //logger.Debug("binded statusInfo"); break; } //logger.Debug("exit for"); } } } if (bindedCarPlateStatusInfo != null) { GenerateAndBrodcastMessageToOutdoorScreen(); //GenerateAndBrodcastMessageToOutdoorScreenViaFuellingPoint(); var logicalNozzleId = nozzles[(e.Transaction.Nozzle.PumpId, e.Transaction.Nozzle.LogicalId)]; 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); //System.Timers.Timer timer = new Timer(5000); var paymentRequest = new PaymentRequest((decimal)e.Transaction.Amount / 100, bindedCarPlateStatusInfo.TransactionNumber, logicalNozzleId, bindedCarPlateStatusInfo.MemberShipId, bindedCarPlateStatusInfo.PlateLicense, terminalId) { TPDU = "6000300000" }; //timer.Elapsed += (o, te) => //{ // StartLicensePayment((decimal)e.Transaction.Amount / 100, bindedCarPlateStatusInfo.TransactionNumber, logicalNozzleId, bindedCarPlateStatusInfo.MemberShipId, bindedCarPlateStatusInfo.PlateLicense); //}; //timer.Start(); ThreadPool.QueueUserWorkItem(new WaitCallback(StartLicensePayment), paymentRequest); //StartLicensePayment((decimal)e.Transaction.Amount / 100, bindedCarPlateStatusInfo.TransactionNumber, logicalNozzleId, bindedCarPlateStatusInfo.MemberShipId, bindedCarPlateStatusInfo.PlateLicense); } } catch (Exception exception) { logger.LogError("exception happened on currentFuelling state change:{0}", exception.Message); } } private void FdcPumpController_OnStateChange(object sender, FdcPumpControllerOnStateChangeEventArg e) { bool needUpdateScreen = false; try { logger.LogDebug("FdcPumpController_OnStateChange state:{0}", e.NewPumpState); if (e.StateChangedNozzles != null) { //check plate list to see if any plate binded to the nozzle var stateChangedNozzle = e.StateChangedNozzles.First(); //For the shell site, treat one fuelling point as a nozzle string tempBindstr = "Pump" + stateChangedNozzle.PumpId + "_Nozzle" + stateChangedNozzle.LogicalId; //string tempBindstr = "Pump" + stateChangedNozzle.PumpId; logger.LogInformation("FdcPumpController_OnStateChange {0},state:{1}", tempBindstr, e.NewPumpState.ToString()); lock (CarPlateStatusInfos) { foreach (var carPlateStatusInfo in CarPlateStatusInfos) { if (carPlateStatusInfo.Value.BindingInfo != null && carPlateStatusInfo.Value.MemberShipId != "0" && carPlateStatusInfo.Value.BindingInfo.BindedNozzle.Equals(tempBindstr) && (carPlateStatusInfo.Value.PaymentState != CarPlateStatusInfo.PaymentStatus.车牌付已支付 && carPlateStatusInfo.Value.PaymentState != CarPlateStatusInfo.PaymentStatus.交易异常 && carPlateStatusInfo.Value.PaymentState != CarPlateStatusInfo.PaymentStatus.车牌付待支付)) { logger.LogInformation("Nozzle:{0} bind with plate {1}", stateChangedNozzle.LogicalId, carPlateStatusInfo.Key); switch (e.NewPumpState) { case LogicalDeviceState.FDC_AUTHORISED: case LogicalDeviceState.FDC_CALLING: if (carPlateStatusInfo.Value.PaymentState != CarPlateStatusInfo.PaymentStatus.车牌付待加油) { carPlateStatusInfo.Value.PaymentState = CarPlateStatusInfo.PaymentStatus.车牌付待加油; needUpdateScreen = true; } break; case LogicalDeviceState.FDC_FUELLING: if (carPlateStatusInfo.Value.PaymentState != CarPlateStatusInfo.PaymentStatus.车牌付加油中) { carPlateStatusInfo.Value.PaymentState = CarPlateStatusInfo.PaymentStatus.车牌付加油中; needUpdateScreen = true; } break; default: logger.LogInformation( "FdcPumpController_OnStateChange, current state is {0}, CarPlateStatusInfo PaymentStatus:{1}", e.NewPumpState, Enum.GetName(typeof(CarPlateStatusInfo.PaymentStatus), carPlateStatusInfo.Value.PaymentState)); break; } break; } } } } } catch (Exception ex) { logger.LogError("exception happened in FdcPumpController_OnStateChange:{0}", ex.Message); } if (needUpdateScreen) GenerateAndBrodcastMessageToOutdoorScreen();//GenerateAndBrodcastMessageToOutdoorScreenViaFuellingPoint(); } public Task Stop() { if (OnMessageReceivedViaFdc != null) { OnMessageReceivedViaFdc -= ProcessMessageReceivedViaFdc; } if (UpdateOutdoorScreenTimer.Enabled) UpdateOutdoorScreenTimer.Stop(); return Task.FromResult(true); } public void Init(IEnumerable processors) { var pumpControllers = new List(); foreach (dynamic processor in processors) { //get the car plate recognize processor if (processor is IAppProcessor) { var HKCarPlateRecognize = processor as HkCarPlateRecognizeApp; if (HKCarPlateRecognize != null) HkCarPlateRecognizeapp = HKCarPlateRecognize; FdcServerHostApp fdcServer = processor as FdcServerHostApp; if (fdcServer != null) { logger.LogInformation("FdcServerHostApp retrieved as an IApplication instance!"); fdcServerApp = processor; } continue; } var handler = processor.Context.Handler; if (handler is IFdcPumpController) pumpControllers.Add(handler); else if (handler is IEnumerable) pumpControllers.AddRange(handler); else if (handler is IDeviceHandler) EpsClientHandler = handler as ShellChinaEPSClientHandler; } fdcPumpControllers = pumpControllers; } /// /// Called when new car plate catched from HK car plate recognize /// /// /// public void HKCarPlateRecognizeapp_OnNewCarPlateCatched(object sender, PlateCatchedEventArgs plateCatchedEventArgs) { logger.LogDebug("HKCarPlateRecognizeapp_OnNewCarPlateCatched, plate license:{0}", plateCatchedEventArgs.PlateLicense); bool quiryMemberInfo = false; //1.send the car plate to shell EPS to get the member information //2.save it lock (CarPlateStatusInfos) { if (CarPlateStatusInfos.ContainsKey(plateCatchedEventArgs.PlateLicense)) { var carPlateStatusInfo = CarPlateStatusInfos[plateCatchedEventArgs.PlateLicense]; TimeSpan timeSpan = DateTime.Now - carPlateStatusInfo.Time; //remove the plate it haven't started fuelling or has finished the sale in carPlateTimeOut if (timeSpan.TotalMinutes > carPlateTimeOut && (carPlateStatusInfo.BindingInfo == null || (carPlateStatusInfo.BindingInfo != null && carPlateStatusInfo.PaymentState == CarPlateStatusInfo.PaymentStatus.车牌付已支付))) { CarPlateStatusInfos.Remove(plateCatchedEventArgs.PlateLicense); quiryMemberInfo = true; //CarPlateStatusInfos.Add(plateCatchedEventArgs.PlateLicense, new CarPlateStatusInfo() { Time = plateCatchedEventArgs.Time, PlateLicense = plateCatchedEventArgs.PlateLicense }); //QuiryMemberShipInfo(plateCatchedEventArgs.PlateLicense); } } else { quiryMemberInfo = true; //CarPlateStatusInfos.Add(plateCatchedEventArgs.PlateLicense, new CarPlateStatusInfo() { Time = plateCatchedEventArgs.Time, PlateLicense = plateCatchedEventArgs.PlateLicense }); //QuiryMemberShipInfo(plateCatchedEventArgs.PlateLicense); } } //System.Timers.Timer timer = new Timer(5000); //var paymentRequest = // new PaymentRequest((decimal)230 / 100, "1212", 25, "12222", plateCatchedEventArgs.PlateLicense, // terminalId) // { TPDU = "6000300000" }; //timer.Elapsed += (o, te) => //{ // StartLicensePayment(paymentRequest); //}; // ////timer.Start(); ////ThreadPool.QueueUserWorkItem(new WaitCallback(StartLicensePayment), paymentRequest); //logger.LogDebug("after thread pool"); if (quiryMemberInfo) QuiryMemberShipInfo(plateCatchedEventArgs.PlateLicense); } /// /// send MembershipInquiryRequest to EPS and handle the response /// /// car plate public void QuiryMemberShipInfo(string plateLicense) { logger.LogDebug("QuiryMemberShipInfo plate license:{0}", plateLicense); //this.EpsClientHandler.WriteAsync(new MessageBase(), ); //plateLicense = "京A12" + plateLicense; //Joy need this one to test with EPS this.EpsClientHandler.EPSClientContext.Outgoing.WriteAsync(new MembershipInquiryRequest(plateLicense, terminalId) { TPDU = "6000300000" }, (request, response) => request is MembershipInquiryRequest && response is MembershipInquiryResponse, (request, response) => { if (response != null) { var membershipResponse = response as MembershipInquiryResponse; var membershipInquiryRequest = request as MembershipInquiryRequest; if (membershipResponse != null && membershipResponse.P_39_交易响应码.Equals("00000"))//success { //int count = membershipResponse.P_24_会员ID.Length; //string a = membershipResponse.P_24_会员ID; //var b = a.Trim().Replace("\0", ""); ////string s = @"0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\"; //if (b.Equals(")) //{ // logger.Info("00"); //} if (membershipResponse.P_24_会员ID.Replace("\0", "").Equals("0")) { logger.LogInformation("The plate:{0} is not a member", membershipResponse.P_34_车牌号码); } else { //save the message and brodcast to outdoor screen lock (CarPlateStatusInfos) { if (!CarPlateStatusInfos.ContainsKey(membershipInquiryRequest.P_34_车牌号码)) { CarPlateStatusInfos.Add(membershipInquiryRequest.P_34_车牌号码, new CarPlateStatusInfo() { Time = DateTime.Now, PlateLicense = membershipInquiryRequest.P_34_车牌号码, MemberShipId = membershipResponse.P_24_会员ID.Replace("\0", ""), TransactionNumber = membershipResponse.P_12_交易流水号 }); } } } GenerateAndBrodcastMessageToOutdoorScreen(); //GenerateAndBrodcastMessageToOutdoorScreenViaFuellingPoint(); //StartLicensePayment((decimal)1.20, membershipResponse.P_12_交易流水号, 5, membershipResponse.P_24_会员ID, //membershipInquiryRequest.P_34_车牌号码);//joy this was added for testing } else//failed, log the error info { var membershipInquiryRequest1 = request as MembershipInquiryRequest; if (membershipInquiryRequest1 != null) logger.LogInformation( "Failed to get member ship info for carPlate:{0}, error message:{1}", membershipInquiryRequest1.P_34_车牌号码, membershipResponse.P_40_错误信息); //save the message and brodcast to outdoor screen //lock (CarPlateStatusInfos) //{ // //var statusInfo = CarPlateStatusInfos[membershipInquiryRequest.P_34_车牌号码]; // //statusInfo.MemberShipId = membershipInquiryRequest.P_34_车牌号码; // //statusInfo.TransactionNumber = membershipInquiryRequest.P_12_交易流水号; // try // { // CarPlateStatusInfos.Add(membershipInquiryRequest1.P_34_车牌号码, // new CarPlateStatusInfo() // { // Time = DateTime.Now, // PlateLicense = membershipInquiryRequest1.P_34_车牌号码, // MemberShipId = "0", // TransactionNumber = "123" // }); // logger.LogInformation("member ship added to list in quiry:{0}", CarPlateStatusInfos.Last().Value.MemberShipId); // } // catch (Exception exc) // { // logger.LogError("Exception happened in QuiryMemberShipInfo:" + exc.Message); // } //} GenerateAndBrodcastMessageToOutdoorScreen(); //GenerateAndBrodcastMessageToOutdoorScreenViaFuellingPoint(); } } else { var membershipInquiryRequest = request as MembershipInquiryRequest; if (membershipInquiryRequest != null) logger.LogInformation("Failed to QuiryMemberShipInfo for:{0},timeout,response is null", membershipInquiryRequest.P_34_车牌号码); //According to the request from Site, the car which is not a member will not displayed on outdoor screen //save the message and brodcast to outdoor screen //lock (CarPlateStatusInfos) //{ // //var statusInfo = CarPlateStatusInfos[membershipInquiryRequest.P_34_车牌号码]; // //statusInfo.MemberShipId = membershipInquiryRequest.P_34_车牌号码; // //statusInfo.TransactionNumber = membershipInquiryRequest.P_12_交易流水号; // try // { // CarPlateStatusInfos.Add(membershipInquiryRequest.P_34_车牌号码, // new CarPlateStatusInfo() // { // Time = DateTime.Now, // PlateLicense = membershipInquiryRequest.P_34_车牌号码, // MemberShipId = "0", // TransactionNumber = "123" // }); // logger.LogDebug("member ship added to list in quiry:{0}", CarPlateStatusInfos.Last().Value.MemberShipId); // } // catch (Exception exc) // { // logger.LogInformation("Exception happened in QuiryMemberShipInfo:" + exc.Message); // } //} GenerateAndBrodcastMessageToOutdoorScreen(); //GenerateAndBrodcastMessageToOutdoorScreenViaFuellingPoint(); } }, epsTimeOut); } /// /// Send payment request to EPS and handle the response /// //public void StartLicensePayment(decimal amount, string epsSequenceNo, int siteLevelNozzleId, string membershipId, string plateLicense) public void StartLicensePayment(object requestObject) { try { logger.LogInformation("StartLicensePayment"); //var paymentRequest = // new PaymentRequest(amount, epsSequenceNo, siteLevelNozzleId, membershipId, plateLicense, // terminalId) // { TPDU = "6000300000" }; Thread.Sleep(delayToSendPayment * 1000); var paymentRequest = requestObject as PaymentRequest; if (paymentRequest != null) { logger.LogDebug("StartLicensePayment after,amount:{0}", paymentRequest.P_4_交易金额); this.EpsClientHandler.EPSClientContext.Outgoing.WriteAsync(paymentRequest, (request, response) => request is PaymentRequest && response is PaymentResponse, (request, response) => { if (response != null) { var paymentResponse = response as PaymentResponse; if (paymentResponse.P_39_交易响应码.Equals("00000")) { lock (CarPlateStatusInfos) { CarPlateStatusInfos[(request as PaymentRequest)?.P_34_车牌号码].PaymentState = CarPlateStatusInfo.PaymentStatus.车牌付已支付; } } else { logger.LogInformation("License payment failed, the error message is:{0}", paymentResponse.P_40_错误信息); lock (CarPlateStatusInfos) { CarPlateStatusInfos[(request as PaymentRequest)?.P_34_车牌号码].PaymentState = CarPlateStatusInfo.PaymentStatus.交易异常; } } } else { logger.LogInformation("License payment failed, response is null"); lock (CarPlateStatusInfos) { CarPlateStatusInfos[(request as PaymentRequest)?.P_34_车牌号码].PaymentState = CarPlateStatusInfo.PaymentStatus.交易异常; } } logger.LogDebug("StartLicensePayment send to out door screen"); GenerateAndBrodcastMessageToOutdoorScreen(); //GenerateAndBrodcastMessageToOutdoorScreenViaFuellingPoint(); }, epsTimeOut); } } catch (Exception exception) { logger.LogError("Exception happend in StartLicensePayment:{0}", exception.Message); } } /// /// Gnerate the layout for each out door screen and check the car plate info dic to see if anything need to remove /// public void GenerateAndBrodcastMessageToOutdoorScreen() { logger.LogDebug("GenerateAndBrodcastMessageToOutdoorScreen"); List platesToRemove = new List(); string match_parent = "match_parent"; //video LinearLayout var videoLinearLayout = new LinearLayout(); videoLinearLayout.Width = match_parent; videoLinearLayout.Orientation = "vertical";//chuizhi var videoview = new VideoView() { Src = videoSource }; videoLinearLayout.Id = "VideoList"; //plate info for each pump LinearLayout var pumpLinearLayout = new LinearLayout(); pumpLinearLayout.Id = "NozzleList"; videoLinearLayout.Width = match_parent; if (fdcPumpControllers != null) { foreach (var fdcPumpController in this.fdcPumpControllers.OrderBy(pc => pc.PumpId)) { foreach (var nozzle in fdcPumpController.Nozzles.OrderBy(n => n.LogicalId)) { var nozzleLinearLayout = new LinearLayout(); nozzleLinearLayout.Id = "Pump" + fdcPumpController.PumpId + "_Nozzle" + nozzle.LogicalId; nozzleLinearLayout.Height = match_parent; nozzleLinearLayout.AllowActions = new List() { AllowAction.Drop }; nozzleLinearLayout.Views.Add(new NozzleView() { PumpId = fdcPumpController.PumpId, NozzleId = nozzles[(fdcPumpController.PumpId, nozzle.LogicalId)] }); pumpLinearLayout.Views.Add(nozzleLinearLayout); } } } //public car plate LinearLayout var commonCarPlateLinearLayout = new LinearLayout(); commonCarPlateLinearLayout.Width = match_parent; var unusedCarPlateLinearLayout = new LinearLayout(); unusedCarPlateLinearLayout.Id = "IdleList"; unusedCarPlateLinearLayout.Height = match_parent; unusedCarPlateLinearLayout.Orientation = "vertical"; var errorTransLinearLayout = new LinearLayout(); errorTransLinearLayout.Id = "ErrorList"; errorTransLinearLayout.Height = match_parent; errorTransLinearLayout.Orientation = "vertical"; commonCarPlateLinearLayout.Orientation = "horizontal"; commonCarPlateLinearLayout.Views.Add(unusedCarPlateLinearLayout); commonCarPlateLinearLayout.Views.Add(errorTransLinearLayout); lock (CarPlateStatusInfos) { //assign each plate to proper place foreach (var carPlateStatusInfo in CarPlateStatusInfos.OrderBy(o => o.Value.Time).ToDictionary(p => p.Key, o => o.Value)) { if ((DateTime.Now - carPlateStatusInfo.Value.Time).TotalMinutes > this.carPlateTimeOut && (carPlateStatusInfo.Value.PaymentState != CarPlateStatusInfo.PaymentStatus.车牌付待支付 && carPlateStatusInfo.Value.PaymentState != CarPlateStatusInfo.PaymentStatus.车牌付加油中 && carPlateStatusInfo.Value.PaymentState != CarPlateStatusInfo.PaymentStatus.交易异常)) { platesToRemove.Add(carPlateStatusInfo.Key); logger.LogDebug("Add car plate to the remove list:{0},count:{1}", carPlateStatusInfo.Key, platesToRemove.Count); continue; } var carplateLinearLayout = new LinearLayout(); carplateLinearLayout.Id = carPlateStatusInfo.Key; //plate view var plateLicenseView = new TextView(); plateLicenseView.Text = carPlateStatusInfo.Key; //plateLicenseView.Background = "White"; carplateLinearLayout.Views.Add(plateLicenseView); carplateLinearLayout.Orientation = "horizontal"; //status view var plateStatusView = new TextView(); plateStatusView.TextColor = TextColor; //plate was binded with nozzle, will add the linearlayout to associated nozzleLinearLayout if (carPlateStatusInfo.Value.BindingInfo != null) { carplateLinearLayout.AllowActions = new List() { AllowAction.Submit }; plateStatusView.Text = Enum.GetName(typeof(CarPlateStatusInfo.PaymentStatus), carPlateStatusInfo.Value.PaymentState); plateStatusView.Background = BackGroundColors[plateStatusView.Text]; carplateLinearLayout.Views.Add(plateStatusView); if (carPlateStatusInfo.Value.PaymentState == CarPlateStatusInfo.PaymentStatus.交易异常) { plateStatusView.TextColor = ErrorTransTextColor; errorTransLinearLayout.Views.Insert(0, carplateLinearLayout); } else { foreach (var nl in pumpLinearLayout.Views) { if (nl.Id == carPlateStatusInfo.Value.BindingInfo.BindedNozzle) { var tmp = new List(); foreach (var view in (nl as LinearLayout).Views) { if (view.Id != null && CarPlateStatusInfos.ContainsKey(view.Id)) tmp.Add(CarPlateStatusInfos[view.Id]); } tmp.Add(carPlateStatusInfo.Value); tmp = new List(tmp.OrderByDescending(c => c.BindingInfo.BingTime)); int index = tmp.FindIndex(c => c.PlateLicense == carPlateStatusInfo.Key); (nl as LinearLayout)?.Views.Insert(index + 1, carplateLinearLayout); break; } } } } else if (!string.IsNullOrEmpty(carPlateStatusInfo.Value.MemberShipId))//plate is not binding to any nozzles { plateStatusView.Text = carPlateStatusInfo.Value.MemberShipId.Equals("0") ? "普通用户" : "车牌付"; plateStatusView.Background = BackGroundColors[plateStatusView.Text]; carplateLinearLayout.Views.Add(plateStatusView); carplateLinearLayout.AllowActions = new List() { AllowAction.Drag, AllowAction.Drop }; unusedCarPlateLinearLayout.Views.Insert(0, carplateLinearLayout); } } logger.LogInformation("plate count need to remove1:{0}", platesToRemove.Count); } logger.LogDebug("plate count need to remove2:{0}", platesToRemove.Count); //root linearLayout var rootLinearLayout = new LinearLayout(); rootLinearLayout.Id = "root"; rootLinearLayout.Orientation = "vertical"; rootLinearLayout.Width = rootLinearLayout.Height = match_parent; rootLinearLayout.Views.Add(videoLinearLayout); rootLinearLayout.Views.Add(commonCarPlateLinearLayout); rootLinearLayout.Views.Add(pumpLinearLayout); var genericDisplayCommand = new GenericDisplayCommandV1Wrapper() { Control = rootLinearLayout, Version = 1 }; //broadcaset message to outdoor screen logger.LogDebug("broadcaset message to outdoor screen"); if (BroadcastMessageViaFdc != null) { logger.LogDebug("BroadcastMessageViaFdc not null"); XmlSerializer xmlSerializer = new XmlSerializer(typeof(GenericDisplayCommandV1Wrapper), new Type[] { typeof(LinearLayout), typeof(VideoView), typeof(TextView), typeof(NozzleView) }); //using (var sww = new StringWriter()) using (var ms = new MemoryStream()) { XmlWriterSettings settings = new XmlWriterSettings(); settings.Encoding = new UTF8Encoding(false); settings.Indent = false; using (XmlWriter writer = XmlWriter.Create(ms, settings)) { //writer.Settings.Encoding = Encoding.UTF8; xmlSerializer.Serialize(writer, genericDisplayCommand); //var xml = sww.ToString(); var xml = Encoding.UTF8.GetString(ms.ToArray()); BroadcastMessageViaFdc(xml); logger.LogDebug("Message broadcast to outdoor:{0}", xml); } } } logger.LogDebug("PlateToRemove Count:{0}", platesToRemove.Count); //remove the plates if (platesToRemove.Count > 0) { logger.LogDebug("{0} plates will be removed", platesToRemove.Count); lock (CarPlateStatusInfos) { foreach (var plate in platesToRemove) if (CarPlateStatusInfos.ContainsKey(plate)) CarPlateStatusInfos.Remove(plate); } } } /// /// 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 /// point as the same with site. /// public void GenerateAndBrodcastMessageToOutdoorScreenViaFuellingPoint() { logger.LogDebug("GenerateAndBrodcastMessageToOutdoorScreen"); List platesToRemove = new List(); string match_parent = "match_parent"; //video LinearLayout var videoLinearLayout = new LinearLayout(); videoLinearLayout.Width = match_parent; videoLinearLayout.Orientation = "vertical";//chuizhi var videoview = new VideoView() { Src = videoSource }; videoLinearLayout.Id = "VideoList"; //plate info for each pump LinearLayout var pumpLinearLayout = new LinearLayout(); pumpLinearLayout.Id = "NozzleList"; videoLinearLayout.Width = match_parent; if (fdcPumpControllers != null) { foreach (var fdcPumpController in this.fdcPumpControllers.OrderBy(pc => pc.PumpId)) { var nozzleLinearLayout = new LinearLayout(); nozzleLinearLayout.Id = "Pump" + fdcPumpController.PumpId;// + "_Nozzle" + fdcPumpController.PumpId; nozzleLinearLayout.Height = match_parent; nozzleLinearLayout.AllowActions = new List() { AllowAction.Drop }; nozzleLinearLayout.Views.Add(new NozzleView() { PumpId = fdcPumpController.PumpId, NozzleId = fdcPumpController.PumpId }); pumpLinearLayout.Views.Add(nozzleLinearLayout); } } //public car plate LinearLayout var commonCarPlateLinearLayout = new LinearLayout(); commonCarPlateLinearLayout.Width = match_parent; var unusedCarPlateLinearLayout = new LinearLayout(); unusedCarPlateLinearLayout.Id = "IdleList"; unusedCarPlateLinearLayout.Height = match_parent; unusedCarPlateLinearLayout.Orientation = "vertical"; var errorTransLinearLayout = new LinearLayout(); errorTransLinearLayout.Id = "ErrorList"; errorTransLinearLayout.Height = match_parent; errorTransLinearLayout.Orientation = "vertical"; commonCarPlateLinearLayout.Orientation = "horizontal"; commonCarPlateLinearLayout.Views.Add(unusedCarPlateLinearLayout); commonCarPlateLinearLayout.Views.Add(errorTransLinearLayout); lock (CarPlateStatusInfos) { lastUpdateOutdoorScreenTime = DateTime.Now; //assign each plate to proper place foreach (var carPlateStatusInfo in CarPlateStatusInfos.OrderBy(o => o.Value.Time).ToDictionary(p => p.Key, o => o.Value)) { if ((DateTime.Now - carPlateStatusInfo.Value.Time).TotalMinutes > this.carPlateTimeOut && (carPlateStatusInfo.Value.PaymentState != CarPlateStatusInfo.PaymentStatus.车牌付待支付 && carPlateStatusInfo.Value.PaymentState != CarPlateStatusInfo.PaymentStatus.车牌付加油中 && carPlateStatusInfo.Value.PaymentState != CarPlateStatusInfo.PaymentStatus.交易异常)) { platesToRemove.Add(carPlateStatusInfo.Key); logger.LogDebug("Add car plate to the remove list:{0},count:{1}", carPlateStatusInfo.Key, platesToRemove.Count); continue; } var carplateLinearLayout = new LinearLayout(); carplateLinearLayout.Id = carPlateStatusInfo.Key; //plate view var plateLicenseView = new TextView(); plateLicenseView.Text = carPlateStatusInfo.Key; //plateLicenseView.Background = "White"; carplateLinearLayout.Views.Add(plateLicenseView); carplateLinearLayout.Orientation = "horizontal"; //status view var plateStatusView = new TextView(); plateStatusView.TextColor = TextColor; //plate was binded with nozzle, will add the linearlayout to associated nozzleLinearLayout if (carPlateStatusInfo.Value.BindingInfo != null) { carplateLinearLayout.AllowActions = new List() { AllowAction.Submit }; plateStatusView.Text = Enum.GetName(typeof(CarPlateStatusInfo.PaymentStatus), carPlateStatusInfo.Value.PaymentState); plateStatusView.Background = BackGroundColors[plateStatusView.Text]; carplateLinearLayout.Views.Add(plateStatusView); if (carPlateStatusInfo.Value.PaymentState == CarPlateStatusInfo.PaymentStatus.交易异常) { plateStatusView.TextColor = ErrorTransTextColor; errorTransLinearLayout.Views.Insert(0, carplateLinearLayout); } else { foreach (var nl in pumpLinearLayout.Views) { if (nl.Id == carPlateStatusInfo.Value.BindingInfo.BindedNozzle) { var tmp = new List(); foreach (var view in (nl as LinearLayout).Views) { if (view.Id != null && CarPlateStatusInfos.ContainsKey(view.Id)) tmp.Add(CarPlateStatusInfos[view.Id]); } tmp.Add(carPlateStatusInfo.Value); tmp = new List(tmp.OrderByDescending(c => c.BindingInfo.BingTime)); int index = tmp.FindIndex(c => c.PlateLicense == carPlateStatusInfo.Key); (nl as LinearLayout)?.Views.Insert(index + 1, carplateLinearLayout); break; } } } } else if (!string.IsNullOrEmpty(carPlateStatusInfo.Value.MemberShipId))//plate is not binding to any nozzles { plateStatusView.Text = carPlateStatusInfo.Value.MemberShipId.Equals("0") ? "普通用户" : "车牌付"; plateStatusView.Background = BackGroundColors[plateStatusView.Text]; carplateLinearLayout.Views.Add(plateStatusView); carplateLinearLayout.AllowActions = new List() { AllowAction.Drag, AllowAction.Drop }; unusedCarPlateLinearLayout.Views.Insert(0, carplateLinearLayout); } } logger.LogInformation("plate count need to remove1:{0}", platesToRemove.Count); } logger.LogDebug("plate count need to remove2:{0}", platesToRemove.Count); //root linearLayout var rootLinearLayout = new LinearLayout(); rootLinearLayout.Id = "root"; rootLinearLayout.Orientation = "vertical"; rootLinearLayout.Width = rootLinearLayout.Height = match_parent; rootLinearLayout.Views.Add(videoLinearLayout); rootLinearLayout.Views.Add(commonCarPlateLinearLayout); rootLinearLayout.Views.Add(pumpLinearLayout); var genericDisplayCommand = new GenericDisplayCommandV1Wrapper() { Control = rootLinearLayout, Version = 1 }; //broadcaset message to outdoor screen logger.LogDebug("broadcaset message to outdoor screen"); if (BroadcastMessageViaFdc != null) { logger.LogDebug("BroadcastMessageViaFdc not null"); XmlSerializer xmlSerializer = new XmlSerializer(typeof(GenericDisplayCommandV1Wrapper), new Type[] { typeof(LinearLayout), typeof(VideoView), typeof(TextView), typeof(NozzleView) }); //using (var sww = new StringWriter()) using (var ms = new MemoryStream()) { XmlWriterSettings settings = new XmlWriterSettings(); settings.Encoding = new UTF8Encoding(false); settings.Indent = false; using (XmlWriter writer = XmlWriter.Create(ms, settings)) { //writer.Settings.Encoding = Encoding.UTF8; xmlSerializer.Serialize(writer, genericDisplayCommand); //var xml = sww.ToString(); var xml = Encoding.UTF8.GetString(ms.ToArray()); BroadcastMessageViaFdc(xml); logger.LogDebug("Message broadcast to outdoor:{0}", xml); } } } logger.LogDebug("PlateToRemove Count:{0}", platesToRemove.Count); //remove the plates if (platesToRemove.Count > 0) { logger.LogDebug("{0} plates will be removed", platesToRemove.Count); lock (CarPlateStatusInfos) { foreach (var plate in platesToRemove) if (CarPlateStatusInfos.ContainsKey(plate)) CarPlateStatusInfos.Remove(plate); } } } public Func BroadcastMessageViaFdc { get; set; } public Func SendMessageViaFdc { get; set; } public Func> OnMessageReceivedViaFdc { get; set; } /// /// Process message from screen /// /// /// public Tuple ProcessMessageReceivedViaFdc(string message) { logger.LogDebug("Message recevied via FDC:{0}", message); ThreadPool.QueueUserWorkItem(new WaitCallback(ProcessFDCMessage), message); //if (message.Contains("GenericDisplayCommand")) //{ // GenericDisplayCommandV1Wrapper genericDiaplayCommand = null; // XmlSerializer xmldeSerializer = new XmlSerializer(typeof(GenericDisplayCommandV1Wrapper), // new Type[] { typeof(LinearLayout), typeof(VideoView), typeof(TextView), typeof(NozzleView) }); // using (var sr = new StringReader(message)) // { // genericDiaplayCommand = (GenericDisplayCommandV1Wrapper)xmldeSerializer.Deserialize(sr); // } // if (genericDiaplayCommand != null) // { // var rootLinearLayount = genericDiaplayCommand.Control as LinearLayout; // if (rootLinearLayount != null && rootLinearLayount.Views.Count == 2) // { // BindCarplateWithNozzle(rootLinearLayount); // } // else if (rootLinearLayount != null && rootLinearLayount.Views.Count == 0) // { // ResetCarPlatePosition(rootLinearLayount); // } // else // { // logger.LogError("Invalid message received from outdoor screen"); // } // } // else // { // logger.LogError("Invalid message received from outdoor screen"); // } // //XmlDocument doc = new XmlDocument(); // //doc.LoadXml(message); // //XmlNodeList layoutItems = doc.SelectNodes(@"GenericDisplayCommand/LinearLayout/LinearLayout"); // //if (layoutItems.Count == 0) layoutItems = doc.SelectNodes(@"GenericDisplayCommand/LinearLayout"); // //if (layoutItems.Count == 2) // //{ // // BindCarplateWithNozzle(layoutItems); // //} // //else if (layoutItems.Count == 1) // //{ // // ResetCarPlatePosition(layoutItems[0]); // //} // //else // //{ // // logger.LogInformation("Invalid message received"); // //} //} return new Tuple("", OverallResult.Success); } private void ProcessFDCMessage(object messageStr) { try { var message = messageStr as string; if (message != null && message.Contains("GenericDisplayCommand")) { GenericDisplayCommandV1Wrapper genericDiaplayCommand = null; XmlSerializer xmldeSerializer = new XmlSerializer(typeof(GenericDisplayCommandV1Wrapper), new Type[] { typeof(LinearLayout), typeof(VideoView), typeof(TextView), typeof(NozzleView) }); using (var sr = new StringReader(message)) { genericDiaplayCommand = (GenericDisplayCommandV1Wrapper)xmldeSerializer.Deserialize(sr); } if (genericDiaplayCommand != null) { var rootLinearLayount = genericDiaplayCommand.Control as LinearLayout; if (rootLinearLayount != null && rootLinearLayount.Views.Count == 2) { BindCarplateWithNozzle(rootLinearLayount); } else if (rootLinearLayount != null && rootLinearLayount.Views.Count == 0) { ResetCarPlatePosition(rootLinearLayount); } else { logger.LogError("Invalid message received from outdoor screen"); } } else { logger.LogError("Invalid message received from outdoor screen"); } //XmlDocument doc = new XmlDocument(); //doc.LoadXml(message); //XmlNodeList layoutItems = doc.SelectNodes(@"GenericDisplayCommand/LinearLayout/LinearLayout"); //if (layoutItems.Count == 0) layoutItems = doc.SelectNodes(@"GenericDisplayCommand/LinearLayout"); //if (layoutItems.Count == 2) //{ // BindCarplateWithNozzle(layoutItems); //} //else if (layoutItems.Count == 1) //{ // ResetCarPlatePosition(layoutItems[0]); //} //else //{ // logger.LogInformation("Invalid message received"); //} } } catch (Exception e) { logger.LogError("Exception in ProcessFDCMessage:" + e.Message); } } private void BindCarplateWithNozzle(LinearLayout rootlayout) { logger.LogInformation("Bind car plate to nozzle,layoutItems count:{0}", rootlayout.Views.Count); string carPlateLicense = String.Empty; string bindingInfo = String.Empty; string plateAction = string.Empty; string nozzleAction = string.Empty; string errorMessage = string.Empty; foreach (var layoutItem in rootlayout.Views) { lock (CarPlateStatusInfos) { plateAction = "Drag"; if (layoutItem.AllowActions[0] == AllowAction.Drag /*&& CarPlateStatusInfos.ContainsKey(layoutItem.Id)*/) { if (CarPlateStatusInfos.ContainsKey(layoutItem.Id)) { plateAction = Enum.GetName(typeof(AllowAction), layoutItem.AllowActions[0]); carPlateLicense = layoutItem.Id; } else { errorMessage = "车牌" + layoutItem.Id + "已失效"; logger.LogInformation(errorMessage); } } else if (nozzles.ContainsValue(Convert.ToInt32(layoutItem.Id))) { bindingInfo = layoutItem.Id; nozzleAction = Enum.GetName(typeof(AllowAction), layoutItem.AllowActions[0]); } } } if (!string.IsNullOrEmpty(errorMessage)) { SendPromptToOutdoorScreen(bindingInfo, errorMessage); return; } logger.LogInformation("carPlateLicense{0},bindingInfo:{1},plateAction{2},nozzleAction{3}", carPlateLicense, bindingInfo, plateAction, nozzleAction); if (!string.IsNullOrEmpty(plateAction) && !string.IsNullOrEmpty(carPlateLicense) && !string.IsNullOrEmpty(nozzleAction) && !string.IsNullOrEmpty(bindingInfo)) { bool updated = false; var originalPumpInfo = nozzles.FirstOrDefault(q => q.Value.ToString(CultureInfo.InvariantCulture) == bindingInfo).Key; int pumpId = originalPumpInfo.Item1; int nozzleId = originalPumpInfo.Item2; //string bindingStr = "Pump" + pumpId;//for Shell, the site treat the fuelling point as one nozzle string bindingStr = "Pump" + pumpId.ToString(CultureInfo.InvariantCulture) + "_Nozzle" + nozzleId.ToString(CultureInfo.InvariantCulture); if (!HasBindedPalte(bindingStr)) { lock (CarPlateStatusInfos) { if (plateAction.Equals(Enum.GetName(typeof(AllowAction), AllowAction.Drag)) && nozzleAction.Equals(Enum.GetName(typeof(AllowAction), AllowAction.Drop))) { if (CarPlateStatusInfos.ContainsKey(carPlateLicense) && CarPlateStatusInfos[carPlateLicense].BindingInfo == null && !CarPlateStatusInfos[carPlateLicense].MemberShipId.Equals("0")) { CarPlateStatusInfos[carPlateLicense].BindingInfo = new BindingInfo() { BindedNozzle = bindingStr, BingTime = DateTime.Now }; LogicalDeviceState state = LogicalDeviceState.FDC_UNDEFINED; foreach (var pump in fdcPumpControllers) { if (pump.PumpId == pumpId) { state = pump.QueryStatusAsync().Result; break; } } CarPlateStatusInfos[carPlateLicense].PaymentState = state == LogicalDeviceState.FDC_FUELLING ? CarPlateStatusInfo.PaymentStatus.车牌付加油中 : CarPlateStatusInfo.PaymentStatus.车牌付待加油; updated = true; } else { logger.LogInformation("ignore the message, it's not a car plate member, membershipId is null"); SendPromptToOutdoorScreen(bindingInfo, "非车牌付用户"); } } } } else { logger.LogInformation("{0} has already binded with plate", bindingStr); SendPromptToOutdoorScreen(bindingInfo, "已有绑定的车牌"); } if (updated) //GenerateAndBrodcastMessageToOutdoorScreenViaFuellingPoint(); GenerateAndBrodcastMessageToOutdoorScreen(); } } /// /// This method called when trying to clear the error transaction or unbind the plate /// /// private void ResetCarPlatePosition(LinearLayout layoutItem) { logger.LogInformation("Reset car plate position"); bool updated = false; string carLicense = string.Empty; var action = layoutItem.AllowActions[0]; carLicense = layoutItem.Id; if (!string.IsNullOrEmpty(carLicense) && action.Equals(AllowAction.Submit)) { lock (CarPlateStatusInfos) { if (CarPlateStatusInfos.ContainsKey(carLicense) && CarPlateStatusInfos[carLicense].BindingInfo != null) { if (CarPlateStatusInfos[carLicense].PaymentState == CarPlateStatusInfo.PaymentStatus.交易异常) CarPlateStatusInfos.Remove(carLicense); else CarPlateStatusInfos[carLicense].BindingInfo = null; updated = true; } } if (updated) GenerateAndBrodcastMessageToOutdoorScreen();//GenerateAndBrodcastMessageToOutdoorScreenViaFuellingPoint(); } } private void BindCarplateWithNozzle(XmlNodeList layoutItems) { logger.LogInformation("Bind car plate to nozzle,layoutItems count:{0}", layoutItems.Count); string carPlateLicense = String.Empty; string bindingInfo = String.Empty; string plateAction = string.Empty; string nozzleAction = string.Empty; for (int i = 0; i < layoutItems.Count; i++) { lock (CarPlateStatusInfos) { //carPlateLicense = CarPlateStatusInfos.FirstOrDefault(a => a.Key != string.Empty).Key; plateAction = "Drag"; //var IdNode = layoutItems[i].SelectSingleNode(@"Id"); if (CarPlateStatusInfos.ContainsKey(layoutItems[i].SelectSingleNode(@"Id") ?.InnerText)) { plateAction = layoutItems[i].SelectSingleNode(@"AllowActions/AllowAction")?.InnerText; carPlateLicense = layoutItems[i].SelectSingleNode(@"Id")?.InnerText; } else if (nozzles.ContainsValue(Convert.ToInt32(layoutItems[i].SelectSingleNode(@"Id")?.InnerText.Trim()))) { bindingInfo = layoutItems[i].SelectSingleNode(@"Id")?.InnerText; nozzleAction = layoutItems[i].SelectSingleNode(@"AllowActions/AllowAction")?.InnerText; } } } logger.LogInformation("carPlateLicense{0},bindingInfo:{1},plateAction{2},nozzleAction{3}", carPlateLicense, bindingInfo, plateAction, nozzleAction); if (!string.IsNullOrEmpty(plateAction) && !string.IsNullOrEmpty(carPlateLicense) && !string.IsNullOrEmpty(nozzleAction) && !string.IsNullOrEmpty(bindingInfo)) { bool updated = false; var originalPumpInfo = nozzles.FirstOrDefault(q => q.Value.ToString(CultureInfo.InvariantCulture) == bindingInfo).Key; int pumpId = originalPumpInfo.Item1; int nozzleId = originalPumpInfo.Item2; string bindingStr = "Pump" + pumpId.ToString(CultureInfo.InvariantCulture) + "_Nozzle" + nozzleId.ToString(CultureInfo.InvariantCulture); //string bindingStr = "Pump" + pumpId;//for Shell, the site treat the fuelling po if (!HasBindedPalte(bindingStr)) { lock (CarPlateStatusInfos) { if (plateAction.Equals(Enum.GetName(typeof(AllowAction), AllowAction.Drag)) && nozzleAction.Equals(Enum.GetName(typeof(AllowAction), AllowAction.Drop))) { if (CarPlateStatusInfos.ContainsKey(carPlateLicense) && CarPlateStatusInfos[carPlateLicense].BindingInfo == null && !CarPlateStatusInfos[carPlateLicense].MemberShipId.Equals("0")) { //var originalPumpInfo = nozzles.FirstOrDefault(q => // q.Value.ToString(CultureInfo.InvariantCulture) == bindingInfo).Key; //int pumpId = originalPumpInfo.Item1; //int nozzleId = originalPumpInfo.Item2; CarPlateStatusInfos[carPlateLicense].BindingInfo = new BindingInfo() { //BindedNozzle = // "Pump" + pumpId.ToString(CultureInfo.InvariantCulture) + "_Nozzle" + // nozzleId.ToString(CultureInfo.InvariantCulture), BindedNozzle = bindingStr, BingTime = DateTime.Now }; LogicalDeviceState state = LogicalDeviceState.FDC_UNDEFINED; foreach (var pump in fdcPumpControllers) { if (pump.PumpId == pumpId) { state = pump.QueryStatusAsync().Result; break; } } CarPlateStatusInfos[carPlateLicense].PaymentState = state == LogicalDeviceState.FDC_FUELLING ? CarPlateStatusInfo.PaymentStatus.车牌付加油中 : CarPlateStatusInfo.PaymentStatus.车牌付待加油; updated = true; } else { logger.LogInformation("ignore the message, it's not a car plate member, membershipId is null"); SendPromptToOutdoorScreen(bindingInfo, "非车牌付用户"); } } } } else { logger.LogInformation("{0} has already binded with plate", bindingStr); SendPromptToOutdoorScreen(bindingInfo, "已有绑定的车牌"); } if (updated) GenerateAndBrodcastMessageToOutdoorScreen(); //GenerateAndBrodcastMessageToOutdoorScreenViaFuellingPoint(); } } /// /// This method called when trying to clear the error transaction or unbind the plate /// /// private void ResetCarPlatePosition(XmlNode layoutItem) { logger.LogInformation("Reset car plate position"); bool updated = false; string carLicense = string.Empty; var actionNode = layoutItem.SelectSingleNode(@"AllowActions/AllowAction"); carLicense = layoutItem.SelectSingleNode(@"Id")?.InnerText; if (!string.IsNullOrEmpty(carLicense) && actionNode != null && actionNode.InnerText .Equals(Enum.GetName(typeof(AllowAction), AllowAction.Submit))) { lock (CarPlateStatusInfos) { if (CarPlateStatusInfos.ContainsKey(carLicense) && CarPlateStatusInfos[carLicense].BindingInfo != null) { if (CarPlateStatusInfos[carLicense].PaymentState == CarPlateStatusInfo.PaymentStatus.交易异常) CarPlateStatusInfos.Remove(carLicense); else CarPlateStatusInfos[carLicense].BindingInfo = null; updated = true; } } if (updated) GenerateAndBrodcastMessageToOutdoorScreen(); //GenerateAndBrodcastMessageToOutdoorScreenViaFuellingPoint(); } } private bool HasBindedPalte(string bindString) { bool alreadyBind = false; lock (CarPlateStatusInfos) { foreach (var carplateStatusInfo in CarPlateStatusInfos) { if (carplateStatusInfo.Value.BindingInfo != null && carplateStatusInfo.Value.BindingInfo.BindedNozzle.Equals(bindString) && (carplateStatusInfo.Value.PaymentState == CarPlateStatusInfo.PaymentStatus.车牌付待加油 || carplateStatusInfo.Value.PaymentState == CarPlateStatusInfo.PaymentStatus.车牌付加油中)) { logger.LogInformation("HasbindPlate: {0},plate:{1},plateState:{2}", bindString, carplateStatusInfo.Value.PlateLicense, carplateStatusInfo.Value.PaymentState); alreadyBind = true; break; } } } return alreadyBind; } private void SendPromptToOutdoorScreen(string nozzleId, string prompt) { var genericDisplayCommand = new GenericDisplayCommandV1Wrapper() { Version = 1, NozzleId = nozzleId.ToString(CultureInfo.InvariantCulture), Prompt = prompt }; if (BroadcastMessageViaFdc != null) { logger.LogDebug("BroadcastMessageViaFdc not null"); XmlSerializer xmlSerializer = new XmlSerializer(typeof(GenericDisplayCommandV1Wrapper), new Type[] { typeof(LinearLayout), typeof(VideoView), typeof(TextView), typeof(NozzleView) }); //using (var sww = new StringWriter()) using (var ms = new MemoryStream()) { XmlWriterSettings settings = new XmlWriterSettings(); settings.Encoding = new UTF8Encoding(false); settings.Indent = false; using (XmlWriter writer = XmlWriter.Create(ms, settings)) { //writer.Settings.Encoding = Encoding.UTF8; xmlSerializer.Serialize(writer, genericDisplayCommand); //var xml = sww.ToString(); var xml = Encoding.UTF8.GetString(ms.ToArray()); BroadcastMessageViaFdc(xml); logger.LogDebug("Message broadcast to outdoor:{0}", xml); } } } } #region Site nozzles setup /// /// Set up the site nozzles, in particular the nozzle number /// Strategy: /// 1. Sort the Pumps by id /// 2. Sort the nozzles by logical id /// 3. The site nozzle number then will be (Pump id, Logical nozzle id) => site nozzle number /// e.g. (1, 1) => 1, (1, 2) => 2, (2, 1) => 3, (3, 1) => 4, (3, 2) => 5, (4, 1) => 6 ... /// /// The Fdc pump instance collection. private void SetupSiteNozzles(IEnumerable fdcPumpControllers) { nozzles.Clear(); var nozzleProductConfig = Configurator.Default.NozzleExtraInfoConfiguration.Mapping; foreach (NozzleExtraInfo np in nozzleProductConfig) { logger.LogDebug($"({np.PumpId}, {np.NozzleLogicalId}) => {np.SiteLevelNozzleId}"); nozzles.Add((np.PumpId, np.NozzleLogicalId), np.SiteLevelNozzleId ?? 0); } } #endregion } }