using Applications.FDC; using Edge.Core.Database; using Edge.Core.Processor;using Edge.Core.IndustryStandardInterface.Pump; using FdcServerHost; using FspWebApp.Entity.Client; using FspWebApp.Entity.Service; using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using Wayne.FDCPOSLibrary; using Edge.Core.Configuration; namespace FspWebApp { public class FspWebRun : IAppProcessor { #region Properties public string MetaConfigName { get; set; } public static string LocalServiceUrl { get; set; } public string AuthServiceBaseUrl { get; set; } public string TransactionServiceBaseUrl { get; set; } #endregion #region Fields private ILogger logger = NullLogger.Instance; private object lockTransactionMonitor = new object(); private Dictionary<(int, int), int> nozzles = new Dictionary<(int, int), int>(); private FdcServerHostApp fdcServerHostApp; #endregion #region Constructor public FspWebRun(string otherParameter, string localServiceUrl, string authServiceBaseUrl, string transactionServiceBaseUrl, IServiceProvider services) { if (services != null) { var loggerFactory = services.GetRequiredService<ILoggerFactory>(); this.logger = loggerFactory.CreateLogger("FspWebApp"); } string[] args = { }; LocalServiceUrl = localServiceUrl; AuthServiceBaseUrl = authServiceBaseUrl; TransactionServiceBaseUrl = transactionServiceBaseUrl; ThreadPool.QueueUserWorkItem(RunWeb, args); //var ts = new Test(36); } #endregion #region Init private void RunWeb(object state) { CreateWebHostBuilder(state as string[]).Build().Run(); } private static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args).UseUrls(LocalServiceUrl).UseStartup<Startup>() .ConfigureLogging(logging => { logging.ClearProviders(); }); //IApplication method implementation public void Init(IEnumerable<IProcessor> processors) { InfoLog("FspWebApp Init begins..."); try { fdcServerHostApp = processors.OfType<FdcServerHostApp>().FirstOrDefault(); if (fdcServerHostApp == null) throw new ArgumentNullException("Can't find the FdcServerHostApp from processors"); FuelProduct.onChangeFuelPrice = new ChangeFuel(FdcServerHostApp_OnChangeFuelPrice); FuelProduct.onChangeFuelProduct = new ChangeFuel(FdcServerHostApp_OnChangeFuelProduct); FuelProduct.onGetAuthToken = new CommonDelegate(FdcServerHostApp_OnGetAuthToken); var sino = Configurator.Default.DeviceProcessorConfiguration.Processor.Find(i => i.Parameter.Find(p => p.Name == "rawProductNameToPosProductNameStr") != null); string[] productNames = sino.Parameter.Find(i => i.Name == "rawProductNameToPosProductNameStr").Value.Split(';', StringSplitOptions.RemoveEmptyEntries); foreach (string pn in productNames) { string[] bcpn = pn.Split(':', StringSplitOptions.RemoveEmptyEntries); int barcode = int.Parse(bcpn[0]); DevicesConfig.fuelProducts[barcode] = new FuelProduct() { Barcode = barcode, FuelName = bcpn[1], CurrentPrice = 0.0, CurrentNozzles = new HashSet<int>() }; } nozzles.Clear(); InfoLog("(PumpId, Nozzle.LogicalId) => Site nozzle number"); IEnumerable<NozzleExtraInfo> nozzleProductConfig = Configurator.Default.NozzleExtraInfoConfiguration.Mapping; foreach (NozzleExtraInfo np in nozzleProductConfig) { InfoLog($"({np.PumpId}, {np.NozzleLogicalId}) => {np.SiteLevelNozzleId}"); nozzles.Add((np.PumpId, np.NozzleLogicalId), np.SiteLevelNozzleId ?? 0); DevicesConfig.fuelProducts[np.ProductBarcode].CurrentNozzles.Add(np.SiteLevelNozzleId ?? 0); } InitPumpData(fdcServerHostApp.FdcPumpControllers); DevicesConfig.pumpDatas = DevicesConfig.pumpDatas.OrderBy(o => o.Key).ToDictionary(o => o.Key, p => p.Value); DevicesConfig.fuelProducts = DevicesConfig.fuelProducts.OrderBy(o => o.Key).ToDictionary(o => o.Key, p => p.Value); } catch (Exception ex) { logger.LogError($"Init Exception: {ex}"); } InfoLog("FspWebApp init ends..."); } #endregion #region IProcessor implementation public async Task<bool> Start() { InfoLog("FspWebApp Start begins..."); fdcServerHostApp.OnStateChange += FdcPumpController_OnStateChange; fdcServerHostApp.OnCurrentFuellingStatusChange += FdcPumpController_OnCurrentFuellingStatusChange; fdcServerHostApp.OnFdcFuelSaleTransactinStateChange += FdcPumpController_OnFdcFuelSaleTransactinStateChange; InfoLog("FspWebApp Start ends..."); return true; } public async Task<bool> Stop() { return true; } private async Task<int> TransactionMonitor(int pumpId, byte logicalId) { try { var fuelPointTotals = await fdcServerHostApp.GetFuelPointTotalsAsync(pumpId, logicalId); lock (lockTransactionMonitor) { DevicesConfig.pumpDatas[nozzles[(pumpId, logicalId)]].VolumeTotalizer = fuelPointTotals.Item2; } } catch (Exception ex) { logger.LogError($"TransactionMonitor Exception: {ex}"); } return 0; } #endregion #region Init pump data /// <summary> /// 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 ... /// </summary> /// <param name="fdcPumpControllers">The Fdc pump instance collection.</param> private async void InitPumpData(IEnumerable<IFdcPumpController> fdcPumpControllers) { var pumps = fdcPumpControllers.OrderBy(c => c.PumpId); var payableSaleTrxs = await fdcServerHostApp.GetAvailableFuelSaleTrxsWithDetailsAsync(-1); foreach (var pump in pumps) { var pumpNozzles = pump.Nozzles.OrderBy(n => n.LogicalId); foreach (var nozzle in pumpNozzles) { int nozzleNo = nozzles[(pump.PumpId, nozzle.LogicalId)]; var fuelPointTotals = await fdcServerHostApp.GetFuelPointTotalsAsync(pump.PumpId, nozzle.LogicalId); DevicesConfig.pumpDatas[nozzleNo] = new PumpData() { SiteNozzleNumber = nozzleNo, PumpState = "Disconnected", VolumeTotalizer = fuelPointTotals.Item2, PayableTrxsCount = payableSaleTrxs.Where(t => t.PumpId == pump.PumpId && t.LogicalNozzleId == nozzle.LogicalId).Count() }; } } } #endregion #region Event handlers private string FdcServerHostApp_OnGetAuthToken(object value) { var user = JsonConvert.DeserializeObject<User>(value.ToString()); var tokenTask = Client.Default.GetAuthToken(AuthServiceBaseUrl + "token", user.userName, user.password); return tokenTask.Result; } private bool FdcServerHostApp_OnChangeFuelPrice(object value) { var fuelProducts = JsonConvert.DeserializeObject<Dictionary<int, FuelProduct>>(value.ToString()); var succeedNozzles = new List<LogicalNozzle>(); foreach (FuelProduct val in fuelProducts.Values) { if (val.NewPrice != null) { string deviceSn = "V105182A51890"; var posItem = new PosItem(); posItem.IsFuelItem = true; posItem.BarCode = val.Barcode.ToString(); posItem.ItemId = posItem.BarCode; posItem.ItemName = val.FuelName; posItem.Price = decimal.Parse(val.NewPrice.ToString()); var response = Client.Default.ChangeFuelPrice(TransactionServiceBaseUrl + "api/products/fuelgrades", deviceSn, posItem); int barcode = val.Barcode; double newPriceWithDecimalPoints = (double)val.NewPrice; var logicalNozzles = fdcServerHostApp.ChangeFuelPriceAsync(barcode, newPriceWithDecimalPoints); if (logicalNozzles.Result != null) succeedNozzles.AddRange(logicalNozzles.Result); //var pumps = fdcServerHostApp.FdcPumpControllers.OrderBy(c => c.PumpId); //foreach (var pump in pumps) //{ // if (pump.GetType().FullName == "Global_Pump_Fdc.PumpHandler") // { // pump.ChangeFuelPriceAsync((int)(newPriceWithDecimalPoints * 1000 + 10000), (byte)barcode); // break; // } //} } } string result = succeedNozzles.Aggregate( "(PumpId, LogicalId) =>" + Environment.NewLine, (str, item) => str += ("(" + item.PumpId + ", " + item.LogicalId + ")-"), str => str.Substring(0, str.Length - 1) ); InfoLog($"The successfully price changed nozzle: {result}"); return succeedNozzles.Count > 0; } private bool FdcServerHostApp_OnChangeFuelProduct(object value) { var succeedNozzles = new List<LogicalNozzle>(); //Client.Default.UploadDataAsync(null, null, AuthServiceBaseUrl); return false; } private void FdcPumpController_OnStateChange(object sender, FdcPumpControllerOnStateChangeEventArg e) { var currentPump = sender as IFdcPumpController; if (currentPump != null) { try { switch (e.NewPumpState) { case LogicalDeviceState.FDC_OFFLINE: foreach (var noz in currentPump.Nozzles) { DevicesConfig.pumpDatas[nozzles[(currentPump.PumpId, noz.LogicalId)]].PumpState = "Disconnected"; } break; case LogicalDeviceState.FDC_READY: if (e.StateChangedNozzles != null) { break; } foreach (var noz in currentPump.Nozzles) { DevicesConfig.pumpDatas[nozzles[(currentPump.PumpId, noz.LogicalId)]].PumpState = "Idle"; } break; case LogicalDeviceState.FDC_CALLING: if (e.StateChangedNozzles != null) { DevicesConfig.pumpDatas[nozzles[(currentPump.PumpId, e.StateChangedNozzles.First().LogicalId)]].PumpState = "Calling"; } break; case LogicalDeviceState.FDC_AUTHORISED: if (e.StateChangedNozzles != null) { DevicesConfig.pumpDatas[nozzles[(currentPump.PumpId, e.StateChangedNozzles.First().LogicalId)]].PumpState = "Authorised"; break; } foreach (var noz in currentPump.Nozzles) { DevicesConfig.pumpDatas[nozzles[(currentPump.PumpId, noz.LogicalId)]].PumpState = "Authorised"; } break; case LogicalDeviceState.FDC_FUELLING: if (e.StateChangedNozzles != null) { DevicesConfig.pumpDatas[nozzles[(currentPump.PumpId, e.StateChangedNozzles.First().LogicalId)]].PumpState = "Fueling"; } break; default: return; } } catch (Exception ex) { ErrorLog(currentPump.PumpId, $"Exception: {ex}"); } } else { InfoLog($"Sender is not an IFdcPumpController, PumpStateChange, New State: {e.NewPumpState}"); return; } } private async void FdcPumpController_OnCurrentFuellingStatusChange(object sender, FdcServerTransactionDoneEventArg e) { var currentPump = sender as IFdcPumpController; if (currentPump != null) { try { int key = nozzles[(currentPump.PumpId, e.Transaction.Nozzle.LogicalId)]; if (e.Transaction.Finished) { DevicesConfig.pumpDatas[key].PumpState = "Idle"; DevicesConfig.pumpDatas[key].Amount = e.Transaction.Amount / Math.Pow(10, currentPump.AmountDecimalDigits); DevicesConfig.pumpDatas[key].Volumn = e.Transaction.Volumn / Math.Pow(10, currentPump.VolumeDecimalDigits); DevicesConfig.fuelProducts[e.Transaction.Barcode].CurrentPrice = e.Transaction.Price / Math.Pow(10, currentPump.PriceDecimalDigits); DevicesConfig.pumpDatas[key].PayableTrxsCount++; await TransactionMonitor(currentPump.PumpId, e.Transaction.Nozzle.LogicalId); } else { DevicesConfig.pumpDatas[key].PumpState = "Fueling"; DevicesConfig.pumpDatas[key].Amount = e.Transaction.Amount / Math.Pow(10, currentPump.AmountDecimalDigits); DevicesConfig.pumpDatas[key].Volumn = e.Transaction.Volumn / Math.Pow(10, currentPump.VolumeDecimalDigits); } } catch (Exception ex) { ErrorLog(currentPump.PumpId, $"Exception: {ex}"); } } else { InfoLog($"Sender is not an IFdcPumpController, FuellingStateChange, New State: {e.Transaction}"); return; } } public void FdcPumpController_OnFdcFuelSaleTransactinStateChange(object sender, FdcFuelSaleTransactinStateChangeEventArg e) { try { if (e.State == Edge.Core.Database.Models.FuelSaleTransactionState.Paid) { DevicesConfig.pumpDatas[nozzles[(e.Transaction.PumpId, e.Transaction.LogicalNozzleId)]].PayableTrxsCount--; } } catch (Exception ex) { logger.LogError($"OnFdcFuelSaleTransactinStateChange Exception: {ex}"); } } #endregion #region Log methods private void InfoLog(string log) { if (logger.IsEnabled(LogLevel.Information)) logger.LogInformation(log); } private void InfoLog(int pumpId, string log) { if (logger.IsEnabled(LogLevel.Information)) logger.LogInformation($"Pump {pumpId}, {log}"); } private void DebugLog(int pumpId, string log) { if (logger.IsEnabled(LogLevel.Debug)) logger.LogDebug($"Pump {pumpId}, {log}"); } private void ErrorLog(int pumpId, string log) { if (logger.IsEnabled(LogLevel.Error)) logger.LogError($"Pump {pumpId}, {log}"); } #endregion } public class Test { int len = 0; int[] trxs = { 4, 3, 2, 1, 0 }; string[] states = { "Fueling", "Disconnected", "Idle", "Calling", "Authorised" }; double[] amounts = { 116, 116.8, 116.98, 18, 0 }; double[] volumeTotalizers = { 1169869, 1169869.8, 1169869.98, 18, 0 }; public Test(int length) { len = length; for (int i = 1; i <= len; i++) { DevicesConfig.pumpDatas[i] = new PumpData() { SiteNozzleNumber = i, PumpState = "Disconnected" }; } var timer = new System.Timers.Timer(1000); timer.Elapsed += new System.Timers.ElapsedEventHandler(TransactionFileMonitor_newTableEvent); timer.Start(); } void TransactionFileMonitor_newTableEvent(object sender, EventArgs e) { var ran = new Random(); for (int i = 1; i <= len; i++) { int randKey = ran.Next(0, 5); DevicesConfig.pumpDatas[i].PayableTrxsCount = trxs[randKey]; DevicesConfig.pumpDatas[i].PumpState = states[randKey]; DevicesConfig.pumpDatas[i].Amount = amounts[randKey]; DevicesConfig.pumpDatas[i].VolumeTotalizer = volumeTotalizers[randKey]; } //var timer = sender as System.Timers.Timer; //timer.Stop(); //Trace.WriteLine(DateTime.Now); } } }