using Edge.Core.Processor;using Edge.Core.IndustryStandardInterface.Pump; using LSForecourtSimulatorImpl; using MessageRouter; using Microsoft.Extensions.Logging; using Edge.Core.Parser; using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Threading; using Wayne.ForecourtControl; using Wayne.ForecourtControl.Fusion; using Wayne.Lib; using Wayne.Lib.IO; namespace GlobalCommunicator { public class FdcCommunicator : IDisposable { public static ILogger fccLogger { get; set; } public event EventHandler OnDataReceived; private const string DEFAULT_FDC_SERVER_CONNECT_STRING = "Host=127.0.0.1,Port=4710,ClientId=101,ClientName=PetroChinaProxy,PortB=4710,PortC=4710"; private readonly string concreteFdcServerConnString = string.Empty; private readonly IForecourtControl forecourtControl; /// /// 0 for not started, 1 for started already. /// private int isStarted = 0; private int authorizationId = 0; private IMessageParser parser; private event EventHandler> OnPumpAccumulatorReceived; private readonly MessageRouterClient msgRouterClient; /// /// The Fdc communicator works as a FDC client which connected to FC. /// /// somehow, still need to communicate the Message Router public FdcCommunicator(IMessageParser parser, MessageRouterClient msgRouterClient, string fdcServerIpAddress, string clientId) { this.parser = parser; this.OnPumpAccumulatorReceived += FdcCommunicator_OnPumpAccumulatorReceived; this.concreteFdcServerConnString = DEFAULT_FDC_SERVER_CONNECT_STRING.Replace("127.0.0.1", fdcServerIpAddress) .Replace("ClientId=101", "ClientId=" + clientId); var fileSupport = FileSupport.fileSupport; var config = new FuelModeAndPriceModeConfig(fileSupport.Paths.Parse("FuelModeConfig.xml"), fileSupport); this.forecourtControl = FUSIONFactory.CreateForecourtControl(0, config, new InputParameterAuthorizationIdGenerator()); this.forecourtControl.OnConnectionStateChange += forecourtControl_OnConnectionStateChange; this.msgRouterClient = msgRouterClient; this.msgRouterClient.Start(); } void forecourtControl_OnConnectionStateChange(object sender, ConnectionChangedEventArgs e) { fccLogger.LogDebug("forecourtControl_OnConnectionStateChange(), new state: " + e.ConnectionState); if (e.ConnectionState == DeviceConnectionState.Disconnected) { foreach (var pump in this.forecourtControl.Pumps) { pump.OnFuellingStateChange -= FdcCommunicator_OnFuellingStateChange; pump.OnNozzleStateChange -= FdcCommunicator_OnNozzleStateChange; pump.OnEventOccured -= FdcCommunicator_OnEventOccured; } } else if (e.ConnectionState == DeviceConnectionState.Connected) { // sometimes could not receive any notification from FDC server even attached the event handler, // suspect some underlying bug in communication layer, so here try sleep a while to avoid(probably) this. // Thread.Sleep(500); foreach (var pump in this.forecourtControl.Pumps) { pump.OnFuellingStateChange += FdcCommunicator_OnFuellingStateChange; pump.OnNozzleStateChange += FdcCommunicator_OnNozzleStateChange; pump.OnEventOccured += FdcCommunicator_OnEventOccured; } const int maxRetryTimes = 10; int retriedTimes = 0; while (!this.msgRouterClient.SendMessage(MsgRouterMessageUtility.RefreshPumpStatus())) { if (++retriedTimes > maxRetryTimes) break; fccLogger.LogDebug("failed to send RefreshPumpStatus() to MsgRouterServer, will keep retrying until max times reached..."); Thread.Sleep(2000); } this.forecourtControl.SetSiteOpenedAsync(true, (_, __) => { }, null); } } void FdcCommunicator_OnEventOccured(object sender, PumpEventOccuredEventArgs e) { fccLogger.LogDebug("FdcCommunicator_OnEventOccured(), args: " + e); } void FdcCommunicator_OnNozzleStateChange(object sender, NozzleStateChangeEventArgs e) { var callingPump = sender as IPump; fccLogger.LogDebug("FdcCommunicator_OnNozzleStateChange(), args: pumpId: " + callingPump.Id + " nozzleId: " + e.Nozzle.Id + ", newState: " + e.NozzleState); //if (e.NozzleState == NozzleState.In) //{ // /* indicate for nozzle if replaced back */ // var sizeLevelNozzleIdsOnPump = SiteConfigUtility.Default.GetSiteLevelNozzleIdsByPumpId(callingPump.Id); // if (!sizeLevelNozzleIdsOnPump.Any()) // { // fccLogger.LogDebug("Could not found any site level nozzle ids for pump: " + callingPump.Id); // return; // } // using (var posSqlConnection = new SqlConnection(ConfigurationManager.ConnectionStrings["PosDatabaseConnStr"].ConnectionString)) // { // try // { // /* idle would not carry nozzle id, so here reset all nozzles on target pump.*/ // var setPumpOnIdleCommand // = new SqlCommand(sizeLevelNozzleIdsOnPump.Select(siteLevelNozzleId => // { // var totalizer = SiteConfigUtility.Default.GetTotalizer(siteLevelNozzleId); // return // string.Format( // "Update jy_info set [status] = '{1}', qty=0, amount=0, fzqty={2}, fzamount={3}" + // " where jihao = '{0}'", siteLevelNozzleId, 'F', // totalizer.Item1, totalizer.Item2); // }) // .Aggregate((acc, n) => acc + " " + n), posSqlConnection); // fccLogger.LogDebug("setPumpOnIdleCommand(via Fdc): " + setPumpOnIdleCommand.CommandText); // posSqlConnection.Open(); // setPumpOnIdleCommand.ExecuteNonQuery(); // } // catch (Exception ex) // { // fccLogger.LogDebug("executing setPumpOnIdleCommand(via Fdc) failed, exception detail: " + ex, // DebugLogLevel.Normal); // } // } //} //else if (this.autoAuthorizePumpWhenCalling && e.NozzleState == NozzleState.Out) //{ // var authParameter = new AuthorizeParameters() // { // PriceGroup = PriceGroup.FullService, // LockToReleaseClient = false, // PresetType = PresetType.Amount, // PresetValue = 0, // Prepay = false, // PayType = "PC", // AuthorizationId = authorizationId++ % int.MaxValue // }; // for (int i = 0; i < callingPump.Nozzles.Count; i++) // { // int idFuelGrade = callingPump.Nozzles[i].FuelGrade; // authParameter.AllowedFuelGrade[idFuelGrade] = true; // } // fccLogger.LogDebug("Authorizing for pumpId: " + callingPump.Id); // callingPump.AuthorizeAsync((byte)(callingPump.Id - 1), authParameter, (_, arg) => // { // var pumpId = (int)(arg.UserToken); // if (arg.Success) // { // fccLogger.LogDebug("AuthorizeAsync finished successfully for pumpId: " + pumpId); // } // else // { // fccLogger.LogDebug("AuthorizeAsync failed for pumpId: " + pumpId); // } // }, callingPump.Id); //} } void FdcCommunicator_OnFuellingStateChange(object sender, FuellingStateChangeEventArgs e) { try { fccLogger.LogDebug("FdcCommunicator_OnFuellingStateChange(), args: " + e); fccLogger.LogDebug("\r\n PumpID = " + e.Fuelling.Pump.Id + "\r\n Nozzle = " + e.Fuelling.Nozzle.Id + "\r\n Quantity = " + e.Fuelling.Quantity + "\r\n Amount = $" + e.Fuelling.Amount + "\r\n FuelGrade = " + e.Fuelling.FuelGrade + "\r\n Price = $" + e.Fuelling.Price + "\r\n CompletionDateTime = $" + e.Fuelling.CompletionDateTime + "\r\n ReservingDeviceId = " + e.Fuelling.ReservingDeviceId + "\r\n ReservedBy = " + e.Fuelling.ReservedBy + "\r\n FuelPeriodID = " + e.Fuelling.FuelPeriodId + "\r\n FuellingSeqNumber = " + e.Fuelling.FuellingSequenceNumber + "\r\n State = " + e.State); var parameters = new StringDictionary(); parameters["PumpID"] = e.Fuelling.Pump.Id.ToString(); parameters["EventType"] = "FuellingStatusChange"; parameters["Finished"] = "true"; parameters["State"] = e.State.ToString(); parameters["FuelingSequenceNo"] = e.Fuelling.FuellingSequenceNumber.ToString(); parameters["ho"] = e.Fuelling.Nozzle.Id.ToString(); parameters["GR"] = e.Fuelling.FuelGrade.ToString(); parameters["VO"] = e.Fuelling.Quantity.ToString(); parameters["AM"] = e.Fuelling.Amount.ToString(); parameters["PU"] = e.Fuelling.Price.ToString(); this.OnDataReceived?.Invoke(this, new FccDataReceivedEventArgs(parameters)); } catch (Exception ex) { fccLogger.LogDebug("Exception in handling FdcCommunicator_OnFuellingStateChange:" + ex); } if (e.State == FuellingState.PayableTransaction) { /* in SinoChem project, the pump was set to FullService mode, so the PayableTransaction case here is impossible to happen, but just leave the code here*/ this.forecourtControl.Pumps[e.Fuelling.Pump.Id - 1].Fuellings.ForEach(f => { var fsn = f.FuellingSequenceNumber; fccLogger.LogDebug("Sending SetAsPaidAsync for pumpId: " + f.Pump.Id + ", FuellingSequenceNumber: " + fsn); f.SetAsPaidAsync((_, arg) => { var pumpId = (int)(arg.UserToken); if (arg.Success) { fccLogger.LogDebug("SetAsPaidAsync finished successfully for pumpId: " + pumpId + ", FuellingSequenceNumber: " + fsn); } else { fccLogger.LogDebug("SetAsPaidAsync failed for pumpId: " + pumpId + ", FuellingSequenceNumber: " + fsn); } }, f.Pump.Id); }); } //else if (e.State == FuellingState.Paid) //{ // var posSqlConnection = new SqlConnection(ConfigurationManager.ConnectionStrings["PosDatabaseConnStr"].ConnectionString); // using (posSqlConnection) // { // try // { // // see a case in site that 0.1 price sale reported in here, checked with FC side which should // // just a 0 amount trx, not sure why here changed the price to 0.1(fdc client bug?), no time to fix, just add // // a check to skip. // if (e.Fuelling.Price <= 1 || e.Fuelling.Amount < 1) // { // fccLogger.LogDebug("insert xiaofei2 will be skipped due to unexpected price or amount fuel sale-> " // + "qty: " + e.Fuelling.Quantity // + "Price: " + e.Fuelling.Price // + "Amount: " + e.Fuelling.Amount // + "seqNo.: " + e.Fuelling.FuellingSequenceNumber, // DebugLogLevel.Maximized); // return; // } // var totalizer = SiteConfigUtility.Default.GetTotalizer(e.Fuelling.Pump.Id, e.Fuelling.Nozzle.Id); // var updateFuelingTrxDoneCommand = // new SqlCommand( // string.Format( // "insert xiaofei2 (jihao, youpin, qty, danjia, amount, xf_date, xf_time, liushuino, fzqty, fzamount)" + // " values({0}, N'{1}', {2}, {3}, {4}, '{5}', '{6}', {7}, {8}, {9})", // SiteConfigUtility.Default.GetSiteLevelNozzleIdByLogicalNozzleId(e.Fuelling.Pump.Id, e.Fuelling.Nozzle.Id), // Translator.GetFriendlyGradeName(SiteConfigUtility.Default.GetGradeNameByGradeId(e.Fuelling.FuelGrade)), // e.Fuelling.Quantity, // e.Fuelling.Price, // e.Fuelling.Amount, // DateTime.Now.Date.ToString("yyyy-MM-dd"), // DateTime.Now.ToString("HH:mm:ss"), // e.Fuelling.FuellingSequenceNumber, // totalizer.Item1, // totalizer.Item2), // posSqlConnection); // fccLogger.LogDebug("updateFuelingTrxDoneCommand: " + updateFuelingTrxDoneCommand.CommandText, // DebugLogLevel.Maximized); // posSqlConnection.Open(); // updateFuelingTrxDoneCommand.ExecuteNonQuery(); // } // catch (Exception ex) // { // fccLogger.LogDebug("executing updateFuelingTrxDoneCommand failed, exception detail: " + ex, // DebugLogLevel.Normal); // } // } //} } public void AuthorizePumpAsync(Dictionary parameterDic) { IPump callingPump = null; int pumpId = int.Parse(parameterDic["PumpId"].ToString()); int sitewiseNozzleId = int.Parse(parameterDic["SitewiseNozzleId"].ToString()); decimal authAmount = 0; decimal volume = 0; AuthorizeParameters authParameter = null; foreach (var pump in this.forecourtControl.Pumps) { if (pump.Id == pumpId) { callingPump = pump; break; } } if (parameterDic.ContainsKey("AuthAmount")) { authAmount = decimal.Parse(parameterDic["AuthAmount"].ToString()); authParameter = new AuthorizeParameters() { PriceGroup = PriceGroup.FullService, LockToReleaseClient = false, PresetType = PresetType.Amount, PresetValue = authAmount, Prepay = false, PayType = "PC", AuthorizationId = authorizationId++ % int.MaxValue }; } else if (parameterDic.ContainsKey("Volume")) { volume = decimal.Parse(parameterDic["Volume"].ToString()); authParameter = new AuthorizeParameters() { PriceGroup = PriceGroup.FullService, LockToReleaseClient = false, PresetType = PresetType.Quantity, PresetValue = volume, Prepay = false, PayType = "PC", AuthorizationId = authorizationId++ % int.MaxValue }; } else { authParameter = new AuthorizeParameters() { PriceGroup = PriceGroup.FullService, LockToReleaseClient = false, PresetType = PresetType.Unknown, Prepay = false, PayType = "PC", AuthorizationId = authorizationId++ % int.MaxValue }; } for (int i = 0; i < callingPump.Nozzles.Count; i++) { int idFuelGrade = callingPump.Nozzles[i].FuelGrade; authParameter.AllowedFuelGrade[idFuelGrade] = true; } fccLogger.LogDebug("Authorizing for pumpId: " + callingPump.Id + ", authAmount:" + authAmount); callingPump.AuthorizeAsync((byte)(callingPump.Id - 1), authParameter, (_, arg) => { if (arg.Success) { fccLogger.LogDebug("AuthorizeAsync finished successfully for pumpId: " + pumpId); } else { fccLogger.LogDebug("AuthorizeAsync failed for pumpId: " + pumpId); } var parameters = new StringDictionary(); parameters["EventType"] = "AuthorizePump"; parameters["PumpID"] = arg.UserToken.ToString(); parameters["Result"] = arg.Result.ToString(); parameters["Success"] = arg.Success.ToString(); this.OnDataReceived?.Invoke(this, new FccDataReceivedEventArgs(parameters)); }, callingPump.Id); } public void QueryTotalizer(Dictionary parameterDic) { var newFuelPrices = new List(); string pumpId = parameterDic["PumpId"].ToString(); string nozzleId = parameterDic["LogicalNozzleId"].ToString(); (forecourtControl as FUSIONForecourtControl).GetPumpTotalsAsync(new FuelTotalReading(pumpId, nozzleId, "FP"), OnPumpAccumulatorReceived, null); } void FdcCommunicator_OnPumpAccumulatorReceived(object sender, AsyncCompletedEventArgs e) { try { var parameters = new StringDictionary(); parameters["PumpID"] = e.Result.Pump.Id.ToString(); parameters["EventType"] = "TotalizerReceived"; parameters["NozzleID"] = e.Result.Nozzle.Id.ToString(); parameters["AmountTotalizer"] = e.Result.Amount.ToString(); parameters["VolumeTotalizer"] = e.Result.Quantity.ToString(); fccLogger.LogDebug("FdcCommunicator_OnPumpAccumulatorReceived event for pump: " + parameters["PumpID"]); foreach (string key in parameters.Keys) { fccLogger.LogDebug(string.Format("Key: {0}, Value: {1}", key, parameters[key])); } this.OnDataReceived?.Invoke(this, new FccDataReceivedEventArgs(parameters)); } catch (Exception ex) { fccLogger.LogDebug("Exception in handling FdcCommunicator_OnPumpAccumulatorReceived:" + ex); } } public void SetFuelPrice(Dictionary parameterDic) { var newFuelPrices = new List(); int fuelGrade = int.Parse(parameterDic["FuelGrade"].ToString()); decimal newPrice = decimal.Parse(parameterDic["NewFuelPrice"].ToString()); var fuelPriceReading = new FuelPriceReading(fuelGrade, 1, newPrice, string.Empty); newFuelPrices.Add(fuelPriceReading); // IList newFuelPrices, EventHandler< AsyncCompletedEventArgs > requestCompleted, object userToken forecourtControl.SetFuelPriceAsync(newFuelPrices, null, null); } public void Dispose() { this.forecourtControl.Dispose(); } public bool Start() { if (0 == Interlocked.CompareExchange(ref this.isStarted, 1, 0)) { fccLogger.LogDebug("Connecting to FDC server with connStr: " + this.concreteFdcServerConnString); this.forecourtControl.Connect(this.concreteFdcServerConnString); return true; } else { throw new InvalidOperationException("Already started."); } } public bool IsStarted { get { return this.isStarted == 1; } } } }