123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131 |
- using System;
- using System.Linq;
- using System.Collections.Generic;
- using Edge.Core.Processor;using Edge.Core.IndustryStandardInterface.Pump;
- using Dfs.WayneChina.SpsDbModels.Models;
- using Dfs.WayneChina.HengshanTerminalWrapper;
- using Dfs.WayneChina.HengshanTerminalWrapper.MessageEntity.Base;
- using Dfs.WayneChina.HengshanTerminalWrapper.MessageEntity.Incoming;
- using Dfs.WayneChina.HengshanTerminalWrapper.MessageEntity.Outgoing;
- using Edge.Core.Database;
- using Edge.Core.Database.Models;
- using Wayne.FDCPOSLibrary;
- using Dfs.WayneChina.CardTrxManager.TrxScanner;
- using Dfs.WayneChina.CardTrxManager.TrxSubmitter;
- using Dfs.WayneChina.HengshanPos.Model;
- using Applications.FDC;
- using Dfs.WayneChina.HengshanFPos.FPosDbManager.Model;
- using Dfs.WayneChina.HengshanFPos.FPosDbManager;
- using FdcServerHost;
- using Dfs.WayneChina.CardTrxManager;
- using System.Timers;
- using System.Threading;
- using Dfs.WayneChina.HyperPrinterHandler;
- using System.Threading.Tasks;
- namespace Dfs.WayneChina.HengshanPos
- {
- /// <summary>
- /// A retrofit of Hengshan iPOS (or EPS/后台), manages forecourt fuel sale transactions.
- /// </summary>
- public class HengshanPosApp : IAppProcessor
- {
- #region Properties
- //IProcessor property
- public Guid Id => Guid.Parse("D7E68991-E77A-432C-A2ED-19541FE45E7C");
-
- public string MetaConfigName { get; set; }
- public CampaignEngine CampaignEngine { get; private set; }
- #endregion
- #region Fields
- private FdcServerHostApp fdcServerApp;
- private IEnumerable<IFdcPumpController> fdcPumpControllers;
- private IEnumerable<IDeviceHandler<byte[], NonCardDispenserMessageTemplateBase>> hengshanTerminalHandlers;
- private FPosDbManager fPosDbManager;
- private TrxScanner trxScanner;
- private TrxSubmitter trxSubmitter;
- private static NLog.Logger logger = NLog.LogManager.LoadConfiguration("NLog.config").GetLogger("HengshanPos");
- private static NLog.Logger alarmLogger = NLog.LogManager.LoadConfiguration("NLog.config").GetLogger("Alarm");
- private Dictionary<(int, int), int> nozzles = new Dictionary<(int, int), int>();
- private Dictionary<int, FuelingManager> fuelingManagers = new Dictionary<int, FuelingManager>();
- private List<TrxSubmitter> trxSubmitters = new List<TrxSubmitter>();
- private SqliteDbContext fdcDbContext;
- private FPosDbContext fPosDbContext;
- private bool started = false;
- private object syncObjQueue = new object();
- private Queue<FPosTransaction> trxQueue = new Queue<FPosTransaction>();
- private CloudCredential cloudCredential;
- private System.Timers.Timer sfScanTimer;
- private PrinterHandler printerHandler;
- private Dictionary<int, ManualResetEvent> mreDict = new Dictionary<int, ManualResetEvent>();
- #endregion
- #region Constructor
- public HengshanPosApp(string otherParameter, string discountConfig, string enableSFScan,
- string username, string password, string authServiceBaseUrl, string transactionServiceBaseUrl, string deviceSN)
- {
- //this.logger = LogManager.GetLogger("DynamicPrivate_" + privateLogFileName);
- Console.ForegroundColor = ConsoleColor.Green;
- CampaignEngine = new CampaignEngine(discountConfig);
- cloudCredential = new CloudCredential
- {
- UserName = username,
- Password = password,
- AuthServiceBaseUrl = authServiceBaseUrl,
- TransactionServiceBaseUrl = transactionServiceBaseUrl,
- DeviceSN = deviceSN
- };
- fdcDbContext = new SqliteDbContext();
- fPosDbContext = new FPosDbContext();
- fPosDbManager = new FPosDbManager(fPosDbContext);
- trxScanner = new TrxScanner(fPosDbManager);
- trxSubmitter = new TrxSubmitter(0, fPosDbManager, cloudCredential);
- //Startup check
- IEnumerable<FPosTransaction> toBeSubmittedTrxs;
- if (!started)
- {
- toBeSubmittedTrxs = GetUnsubmittedTrxsAfterStartup();
- foreach (var t in toBeSubmittedTrxs)
- {
- lock (syncObjQueue)
- {
- logger.Info("Enqueue trx after startup");
- trxQueue.Enqueue(t);
- }
- }
- started = true;
- }
- //sfScanTimer = new System.Timers.Timer();
- //sfScanTimer.Elapsed += new ElapsedEventHandler(Eplased);
- //// Set it to go off every 20 seconds
- //sfScanTimer.Interval = 10 * 1000;
- //sfScanTimer.Enabled = true;
- bool sfScanEnabled = Convert.ToBoolean(enableSFScan);
- if (sfScanEnabled)
- {
- RegularScanTask.Run(new Action(PeriodicScan), TimeSpan.FromSeconds(20));
- }
- Console.WriteLine("HengshanPos APP STARTED...");
- }
- private void Eplased(object sender, ElapsedEventArgs e)
- {
- string receipt = "\r\n\r\n<table width=\"95%\" border=\"0\" align=\"center\" style=\"table-layout:fixed;margin-top:50px;font-family:monospace;font-size:15px\">\r\n <tr>\r\n <td colspan=\"4\" align=\"center\"></td>\r\n </tr>\r\n <tr>\r\n <td colspan=\"2\" align=\"left\">小票编号:</td>\r\n <td colspan=\"2\" align=\"left\">I1204102726</td>\r\n </tr>\r\n <tr>\r\n <td colspan=\"1\" align=\"left\">日期时间</td>\r\n <td colspan=\"3\" align=\"right\">2018-12-04 10:27:25</td>\r\n </tr>\r\n <tr>\r\n <td colspan=\"4\" align=\"center\" style=\"word-break:keep-all;white-space:nowrap;overflow:hidden\">------------------------------</td>\r\n </tr>\r\n <tr>\r\n <td width=\"25%\" align=\"left\">名称</td>\r\n <td width=\"25%\" align=\"left\">数量</td>\r\n <td width=\"25%\" align=\"left\">单价</td>\r\n <td width=\"25%\" align=\"left\">总额</td>\r\n </tr>\r\n <tr>\r\n <td width=\"25%\" align=\"left\">92#</td>\r\n <td width=\"25%\" align=\"left\">0.39</td>\r\n <td width=\"25%\" align=\"left\">7.70</td>\r\n <td width=\"25%\" align=\"left\">3.00</td>\r\n </tr>\r\n <tr> \r\n\t<td colspan=\"4\" align=\"left\">枪号:\r\n 4\r\n\t</td>\r\n </tr>\r\n <tr>\r\n <td colspan=\"4\" align=\"center\"> </td>\r\n </tr>\r\n <tr>\r\n <td width=\"75%\" align=\"left\">折扣:</td>\r\n <td width=\"25%\" align=\"left\">0.06</td>\r\n </tr>\r\n <tr>\r\n <td width=\"75%\" align=\"left\">共计:</td>\r\n <td width=\"25%\" align=\"left\">2.94</td>\r\n </tr>\r\n <tr>\r\n <td colspan=\"4\" align=\"center\" style=\"word-break:keep-all;white-space:nowrap;overflow:hidden\">------------------------------</td>\r\n </tr>\r\n <tr>\r\n <td colspan=\"2\" align=\"left\">支付方式</td>\r\n <td colspan=\"1\" align=\"center\">收款</td>\r\n <td colspan=\"1\" align=\"right\">找零</td>\r\n </tr>\r\n <tr>\r\n <td colspan=\"2\" align=\"left\">充值卡</td>\r\n <td colspan=\"1\" align=\"center\">2.94</td>\r\n <td colspan=\"1\" align=\"right\">0.00</td>\r\n </tr>\r\n\t<tr>\r\n\t <td colspan=\"2\" align=\"left\">卡号:</td>\r\n\t <td colspan=\"2\" align=\"left\">10010557</td>\r\n\t</tr>\t \r\n\t<tr>\r\n\t <td colspan=\"2\" align=\"left\">卡余额:</td>\r\n\t <td colspan=\"2\" align=\"left\">6085.28</td>\r\n\t</tr> \r\n <tr>\r\n <td colspan=\"4\" align=\"center\" style=\"word-break:keep-all;white-space:nowrap;overflow:hidden\">------------------------------</td>\r\n </tr>\r\n <tr>\r\n <td colspan=\"4\" align=\"center\">祝您愉快</td>\r\n </tr>\r\n <tr>\r\n <td colspan=\"4\" align=\"center\">欢迎下次光临</td>\r\n </tr>\r\n</table>\r\n\r\n";
- if (printerHandler != null)
- printerHandler.SendFormattedReceipt(4, receipt);
- }
- private IEnumerable<FPosTransaction> GetUnsubmittedTrxsAfterStartup()
- {
- return fPosDbContext.FPosTransactions.Where(t => t.Submitted == false && t.CardType == 3);
- }
- #endregion
- #region Background scanning
- private void PeriodicScan()
- {
- logger.Info("Periodic scanning started...");
- lock (syncObjQueue)
- {
- if (trxQueue.Count > 0)
- {
- logger.Info("Pending transaction to be submitted to cloud!");
- var outTrx = trxQueue.Dequeue();
- var fdcTransaction = fdcDbContext.PumpTransactionModels.FirstOrDefault(m => m.ReleaseToken == outTrx.ReleaseToken);
- if (fdcTransaction != null)
- {
- logger.Info($"Fdc Transaction exists, ask scanner to find it."
- + $" pumpId: {outTrx.PumpId},"
- + $" nozzleId:{outTrx.NozzleId},"
- + $" FPos SqNo:{outTrx.FPosSqNo},"
- + $" FC SqNo:{outTrx.FcSqNo}");
- trxScanner.StartScanAsync(outTrx.PumpId, outTrx.FPosSqNo, outTrx.FcSqNo, outTrx.ReleaseToken,
- new FdcTransaction
- {
- Finished = true,
- Nozzle = new LogicalNozzle(outTrx.PumpId, 0, Convert.ToByte(outTrx.NozzleId), null),
- Barcode = Convert.ToInt32(fdcTransaction.ProductBarcode),
- Volumn = fdcTransaction.Volumn,
- Price = fdcTransaction.UnitPrice,
- Amount = fdcTransaction.Amount,
- VolumeTotalizer = fdcTransaction.VolumeTotalizer,
- AmountTotalizer = fdcTransaction.AmountTotalizer,
- SequenceNumberGeneratedOnPhysicalPump = Convert.ToInt32(fdcTransaction.TransactionSeqNumberFromPhysicalPump),
- });
- }
- }
- }
- var unsubmittedTransaction = fPosDbContext.FPosTransactions
- .Where(t => t.Reserved == false && t.Submitted == false && t.CardType == 3);
- if (unsubmittedTransaction.Count() > 0)
- {
- logger.Info("Found not reserved and unsubmitted transactions");
- var oldestTrx = unsubmittedTransaction.OrderBy(t => t.Id).FirstOrDefault();
- if (oldestTrx != null)
- {
- var fdcTransaction = fdcDbContext.PumpTransactionModels.FirstOrDefault(m => m.ReleaseToken == oldestTrx.ReleaseToken);
- if (fdcTransaction != null)
- {
- logger.Info($"Fdc Transaction exists, ask scanner to find it."
- + $" pumpId: {oldestTrx.PumpId},"
- + $" nozzleId:{oldestTrx.NozzleId},"
- + $" FPos SqNo:{oldestTrx.FPosSqNo},"
- + $" FC SqNo:{oldestTrx.FcSqNo}");
- trxScanner.StartScanAsync(oldestTrx.PumpId, oldestTrx.FPosSqNo, oldestTrx.FcSqNo, oldestTrx.ReleaseToken,
- new FdcTransaction
- {
- Finished = true,
- Nozzle = new LogicalNozzle(oldestTrx.PumpId, 0, Convert.ToByte(oldestTrx.NozzleId), null),
- Barcode = Convert.ToInt32(fdcTransaction.ProductBarcode),
- Volumn = fdcTransaction.Volumn,
- Price = fdcTransaction.UnitPrice,
- Amount = fdcTransaction.Amount,
- VolumeTotalizer = fdcTransaction.VolumeTotalizer,
- AmountTotalizer = fdcTransaction.AmountTotalizer,
- SequenceNumberGeneratedOnPhysicalPump = Convert.ToInt32(fdcTransaction.TransactionSeqNumberFromPhysicalPump),
- });
- }
- }
- }
- else
- {
- logger.Info("Have not found any transaction that are NOT reserved and unsbumitted");
- }
- }
- #endregion
- #region Init
- //IApplication method implementation
- public void Init(IEnumerable<IProcessor> processors)
- {
- InfoLog("HengshanPosApp Init begins...");
- var pumpControllers = new List<IFdcPumpController>();
- var communicableControllers = new List<IFdcCommunicableController>();
- var hengshanTerminals = new List<IDeviceHandler<byte[], NonCardDispenserMessageTemplateBase>>();
- foreach (dynamic processor in processors)
- {
- if (processor is IAppProcessor)
- {
- FdcServerHostApp fdcServer = processor as FdcServerHostApp;
- if (fdcServer != null)
- {
- logger.Info("FdcServerHostApp retrieved as an IApplication instance!");
- fdcServerApp = processor;
- }
- PrinterHandler printer = processor as PrinterHandler;
- if (printer != null)
- {
- InfoLog("Printer handler is available");
- printerHandler = printer;
- }
- continue;
- }
- var handler = processor.Context.Handler;
- if (handler is IFdcPumpController)
- pumpControllers.Add(handler);
- else if (handler is IEnumerable<IFdcPumpController>)
- pumpControllers.AddRange(handler);
- else if (handler is IDeviceHandler<byte[], NonCardDispenserMessageTemplateBase>)
- hengshanTerminals.Add(handler);
- }
- fdcPumpControllers = pumpControllers; //all pumps
- hengshanTerminalHandlers = hengshanTerminals; //all Hengshan IC Terminals
- InfoLog("HengshanPosApp init ends...");
- }
- #endregion
- #region IProcessor implementation
- public Task<bool> Start()
- {
- InfoLog("HengshanPosApp Start begins...");
- SetupSiteNozzles(fdcPumpControllers);
- SetupFuelingManagers(fdcPumpControllers);
- SetupTransactionSubmitters(fdcPumpControllers);
- SetupMRE();
- foreach (var termHanlder in hengshanTerminalHandlers)
- {
- var icTermHandler = termHanlder as HengshanICTermHandler;
- if (icTermHandler != null)
- {
- icTermHandler.OnTerminalMessageReceived += IcTermHandler_OnTerminalMessageReceived;
- }
- }
- foreach (var fdcPumpController in fdcPumpControllers)
- {
- fdcPumpController.OnStateChange += FdcPumpController_OnStateChange;
- }
- if (fdcServerApp != null)
- {
- FdcServerHostApp fdcServer = fdcServerApp as FdcServerHostApp;
- fdcServer.OnCurrentFuellingStatusChange += FdcServer_OnCurrentFuellingStatusChange;
- }
- if (trxScanner != null)
- {
- trxScanner.OnMatchingTrxFound += TrxScanner_OnMatchingTrxFound;
- trxScanner.OnNoMatchFound += TrxScanner_OnNoMatchFound;
- }
- foreach (var transactionSubmitter in trxSubmitters)
- {
- transactionSubmitter.OnReceiptReceived += TransactionSubmitter_OnReceiptReceived;
- }
- InfoLog("HengshanPosApp Start ends...");
- return Task.FromResult(true);
- }
- private void TransactionSubmitter_OnReceiptReceived(object sender, ReceiptReceivedEventArgs e)
- {
- InfoLog("TrxSubmitter begins sending receipt data to printer...");
- if (printerHandler != null)
- printerHandler.SendFormattedReceipt(e.NozzleNo, e.ReceiptData);
- }
- public Task<bool> Stop()
- {
- foreach (var termHanlder in hengshanTerminalHandlers)
- {
- var icTermHandler = termHanlder as HengshanICTermHandler;
- if (icTermHandler != null)
- {
- icTermHandler.OnTerminalMessageReceived -= IcTermHandler_OnTerminalMessageReceived;
- }
- }
- foreach (var fdcPumpController in fdcPumpControllers)
- {
- fdcPumpController.OnStateChange -= FdcPumpController_OnStateChange;
- }
- fdcServerApp.OnCurrentFuellingStatusChange -= FdcServer_OnCurrentFuellingStatusChange;
- if (trxScanner != null)
- {
- trxScanner.OnMatchingTrxFound -= TrxScanner_OnMatchingTrxFound;
- trxScanner.OnNoMatchFound -= TrxScanner_OnNoMatchFound;
- }
- if (mreDict!= null && mreDict.Count > 0)
- {
- foreach (var mre in mreDict)
- {
- mre.Value?.Dispose();
- }
- }
- return Task.FromResult(true);
- }
- #endregion
- #region Event handlers
- private void FdcServer_OnCurrentFuellingStatusChange(object sender, FdcServerTransactionDoneEventArg e)
- {
- var fuelingManager = GetFuelingManager(e.Transaction.Nozzle.PumpId);
- if (fuelingManager != null)
- {
- //Update the fueling
- fuelingManager.UpdateFueling(e.Transaction);
- fuelingManager.UpdateFuelingState(FuelingState.Running);
- if (e.Transaction.Finished)
- {
- HandleFinishedFueling(fuelingManager, e);
- }
- }
- else
- {
- InfoLog(e.Transaction.Nozzle.PumpId, "could not get the fueling manager on fuelling state change");
- }
- }
- private void FdcPumpController_OnStateChange(object sender, FdcPumpControllerOnStateChangeEventArg e)
- {
- IFdcPumpController currentPump = sender as IFdcPumpController;
- if (currentPump != null)
- {
- var pumpState = e.NewPumpState;
- var fuelingManager = GetFuelingManager(currentPump.PumpId);
- if (fuelingManager != null)
- {
- if (pumpState == LogicalDeviceState.FDC_CALLING)
- {
- var callingNozzle = e.StateChangedNozzles.First().LogicalId;
- InfoLog(currentPump.PumpId, $"calling nozzle id: {callingNozzle}");
- if (fuelingManager.ActiveFueling != null && fuelingManager.ActiveFueling.ActiveNozzle != callingNozzle)
- {
- //calling nozzle changed, unauthorize the pump.
- var result = fuelingManager.UnauthorizePump();
- fuelingManager.ActiveFueling = null;
- }
- else
- {
- //Prepare the Active fueling.
- fuelingManager.CreateFueling(callingNozzle);
- }
- }
- }
- else
- {
- InfoLog($"Fueling manager is null on PumpState change for pump id: {currentPump.PumpId}");
- }
- }
- else
- {
- InfoLog($"Sender is not an IFdcPumpController, PumpStateChange, New State: {e.NewPumpState}");
- return;
- }
- }
- private void LockFuelSale(int clientId, int pumpId, int fdcSeqNo, int releaseToken)
- {
- mreDict[pumpId].Reset();
- InfoLog("Synchronizing locking");
- fdcServerApp.LockFuelSaleTrxAndNotifyAllFdcClientsAsync(clientId, pumpId, fdcSeqNo, releaseToken).GetAwaiter().GetResult();
- mreDict[pumpId].Set();
- InfoLog("Locking done, continue");
- }
- private void HandleFinishedFueling(FuelingManager fuelingManager, FdcServerTransactionDoneEventArg e)
- {
- //Lock the card transaction first
- if (fuelingManager.ActiveFueling != null && fuelingManager.ActiveFueling.AuthorizedByCard)
- {
- InfoLog($"Locking fuel sale transaction, "
- + $"Pump id: {e.Transaction.Nozzle.PumpId}"
- + $" FdcTrx SqNo: {e.Transaction.SequenceNumberGeneratedOnPhysicalPump},"
- + $" Trx amount: {e.Transaction.Amount}");
- ThreadPool.QueueUserWorkItem(
- new WaitCallback(delegate (object state)
- {
- LockFuelSale(100,
- e.Transaction.Nozzle.PumpId,
- e.Transaction.SequenceNumberGeneratedOnPhysicalPump,
- e.ReleaseToken.Value);
- }), null);
- }
- fuelingManager.UpdateFuelingState(FuelingState.Completed);
- InfoLog($"Fueling done on"
- + $" pump id: {e.Transaction.Nozzle.PumpId},"
- + $" nozzle id: {e.Transaction.Nozzle.LogicalId},"
- + $" PumpSqNo: {e.Transaction.SequenceNumberGeneratedOnPhysicalPump}");
- fPosDbManager.AddMapping(fuelingManager.ActiveFueling.FuelingSqNo,
- e.Transaction.SequenceNumberGeneratedOnPhysicalPump,
- e.Transaction.Nozzle.PumpId, e.Transaction.Nozzle.LogicalId,
- e.ReleaseToken.Value, (decimal)e.Transaction.Amount / 100);
- InfoLog(e.Transaction.Nozzle.PumpId, "start to search the matching IC card trx...");
- trxScanner.StartScanAsync(e.Transaction.Nozzle.PumpId,
- fuelingManager.ActiveFueling.FuelingSqNo,
- e.Transaction.SequenceNumberGeneratedOnPhysicalPump,
- e.ReleaseToken.Value,
- e.Transaction);
- //Active fueling is completed, then store it.
- fuelingManager.SetFuelingCompleted(e.Transaction);
- fuelingManager.StoreLastFueling(e.Transaction);
- }
- private void TrxScanner_OnMatchingTrxFound(object sender, MatchingTrxFoundEventArgs e)
- {
- //No need to lock non-customer card transactions.
- if (e.TrdInfo.CardType != (byte)ICCardType.Customer)
- {
- ThreadPool.QueueUserWorkItem(new WaitCallback(delegate (object state)
- {
- UnlockFuelSaleTransaction(100, e.FdcTrx.Nozzle.PumpId, (ushort)e.TrdInfo.SeqNo, e.FdcSqNo, e.ReleaseToken);
- }), null);
- //var fuelSaleTrx = fdcServerApp.UnlockFuelSaleTrxAndNotifyAllFdcClients(
- // 100, e.FdcTrx.Nozzle.PumpId, e.FdcSqNo, e.ReleaseToken);
- //InfoLog($"Not a customer transaction, SqNo: {e.TrdInfo.SeqNo}, "
- // + $"unlock it for further processing by POS, Success? {fuelSaleTrx != null}");
- return;
- }
- var submitter = GetTrxSubmitter(e.FdcTrx.Nozzle.PumpId);
- bool submitSuccess = submitter.SubmitTrx(e.TrdInfo, e.FdcSqNo, e.FdcTrx);
- DebugLog(e.FdcTrx.Nozzle.PumpId, $" Submitting FPOS transaction of SeqNo: {e.FdcSqNo} success: {submitSuccess}");
- //Clear the fuel sale transaction regardless of submission status
- ClearFuelSaleTransaction(e);
- }
- private void UnlockFuelSaleTransaction(int clientId, int pumpId, ushort fPosSqNo, int fdcTrxNo, int releaseToken)
- {
- InfoLog("Wait until locking is done");
- mreDict[pumpId].WaitOne();
- var fuelSaleTrx = fdcServerApp.UnlockFuelSaleTrxAndNotifyAllFdcClientsAsync(clientId, pumpId, fdcTrxNo, releaseToken).Result;
- InfoLog($"Not a customer transaction, SqNo: {fPosSqNo}, "
- + $"unlock it for further processing by POS, Success? {fuelSaleTrx != null}");
- }
- private async void TrxScanner_OnNoMatchFound(object sender, NoMatchFoundEventArgs e)
- {
- alarmLogger.Info($"\n No matching IC card record found\n Fdc SqNo: {e.FdcSqNo},"
- + $" on pump: {e.FdcTrx.Nozzle.PumpId}, nozzle: {e.FdcTrx.Nozzle.LogicalId}, amount: {e.FdcTrx.Amount} unlocking fuel sale transaction");
- await fdcServerApp.UnlockFuelSaleTrxAndNotifyAllFdcClientsAsync(100, e.FdcTrx.Nozzle.PumpId, e.FdcSqNo, e.ReleaseToken);
- }
- private void ClearFuelSaleTransaction(MatchingTrxFoundEventArgs e)
- {
- FdcServerHostApp fdcServer = fdcServerApp as FdcServerHostApp;
- if (fdcServer != null)
- {
- var fuelSaleTrx = fdcServer.ClearFuelSaleTrxAndNotifyAllFdcClientsAsync(
- e.TrdInfo.PumpNo, Convert.ToString(e.FdcSqNo), e.ReleaseToken, "100").Result;
- InfoLog($"Fuel sale transaction state: {fuelSaleTrx.State.ToString()}");
- }
- }
- private void IcTermHandler_OnTerminalMessageReceived(object sender, HengshanTerminalMessageEventArgs e)
- {
- #region GetNozzleStatus
- if (e.Message is GetNozzleStatusRequest)
- {
- HandleGetNozzleStatusRequest(e);
- return;
- }
- #endregion
- //fuelingManager and terminal for all request/response handling
- var fuelingManager = GetFuelingManager(e.PumpId);
- var terminal = GetTerminalById(e.PumpId);
- #region Reserve pump
- if (e.Message is ReservePumpWithAmountRequest)
- {
- InfoLog(e.PumpId, "ReservePumpWithAmountRequest" + $" amount: {(e.Message as ReservePumpWithAmountRequest).Amount}");
- if (fuelingManager != null)
- {
- var reservePumpWithAmountResponse = fuelingManager.ReservePumpWithAmount((e.Message as ReservePumpWithAmountRequest).Amount);
- terminal?.Write(reservePumpWithAmountResponse);
- }
- else
- {
- ErrorLog(e.PumpId, "ReservePumpWithAmountRequest, no pump found, respond with failure!");
- ReservePumpWithAmountResponse failedResponse = new ReservePumpWithAmountResponse
- {
- EnumResult = NonCardDispenserMessageTemplateBase.Result.失败
- };
- terminal?.Write(failedResponse);
- return;
- }
- }
- #endregion
- else if (e.Message is ReservePumpWithGallonRequest)
- {
- if (fuelingManager != null)
- {
- var reservePumpWithGallonResponse = fuelingManager.ReservePumpWithGallon((e.Message as ReservePumpWithGallonRequest).Gallon);
- terminal?.Write(reservePumpWithGallonResponse);
- }
- else
- {
- logger.Info($"ReservePumpWithGallonRequest: No pump found for this id - {e.PumpId}, respond with failure!");
- ReservePumpWithGallonResponse failedResponse = new ReservePumpWithGallonResponse
- {
- EnumResult = NonCardDispenserMessageTemplateBase.Result.失败
- };
- terminal?.Write(failedResponse);
- return;
- }
- }
- else if (e.Message is RoundUpByVolumeRequest)
- {
- if (fuelingManager != null)
- {
- var roundUpByVolumeResponse = fuelingManager.HandleVolumeRounding();
- terminal?.Write(roundUpByVolumeResponse);
- }
- else
- {
- logger.Error($"RoundUpByVolumeRequest: No pump found for pump {e.PumpId}, respond with failure!");
- RoundUpByVolumeResponse failureResponse = new RoundUpByVolumeResponse
- {
- EnumResult = NonCardDispenserMessageTemplateBase.Result.失败
- };
- terminal?.Write(failureResponse);
- return;
- }
- }
- #region Pump authorization
- //
- // Authorize pump with preset amount
- //
- else if (e.Message is AuthPumpWithAmountRequest)
- {
- #region Old way auth pump with amount request
- var fdcPump = GetPumpById(e.PumpId);
- if (fdcPump == null)
- {
- logger.Info($"AuthPumpWithAmountRequest: No pump found for this id - {e.PumpId}, respond with failure!");
- SendPumpAuthResponse(e.PumpId, false, PumpAuthType.Amount);
- return;
- }
- bool success = fdcPump.AuthorizeWithAmountAsync((e.Message as AuthPumpWithAmountRequest).Amount, (byte)e.PumpId).Result;
- SendPumpAuthResponse(e.PumpId, success, PumpAuthType.Amount);
- #endregion
- }
- //
- // Authorize pump with preset volume
- //
- else if (e.Message is AuthPumpWithGallonRequest)
- {
- #region old way auth pump with gallon request
- var fdcPump = GetPumpById(e.PumpId);
- if (fdcPump == null)
- {
- logger.Info($"AuthPumpWithGallonRequest: No pump found for this id - {e.PumpId}, respond with failure!");
- SendPumpAuthResponse(e.PumpId, false, PumpAuthType.Volume);
- return;
- }
- bool success = fdcPump.AuthorizeWithAmountAsync((e.Message as AuthPumpWithGallonRequest).Gallon, (byte)e.PumpId).Result;
- SendPumpAuthResponse(e.PumpId, success, PumpAuthType.Volume);
- #endregion
- }
- else if (e.Message is AuthPumpWithKiloRequest)
- {
- //To be implemented when it's required.
- }
- //
- // Hengshan terminal command, Command 'Open' equals 'authorize pump'
- //
- else if (e.Message is OpenRequest)
- {
- InfoLog($"OpenRequest for pump {e.PumpId}");
- OpenResponse openResponse = new OpenResponse
- {
- EnumResult = NonCardDispenserMessageTemplateBase.Result.成功
- };
- terminal?.Write(openResponse);
- if (fuelingManager != null)
- {
- fuelingManager.ActiveFueling.FuelingState = FuelingState.Authorized;
- fuelingManager.ActiveFueling.AuthorizedByCard = true;
- bool authResult = fuelingManager.AuthorizePump();
- logger.Info($"Authorizing pump {e.PumpId}, success? {authResult}");
- }
- else
- {
- logger.Info($"No fueling manager for this OpenRequest with pump id:{e.PumpId}");
- }
- }
- #endregion
- #region Pump un-authorization
- else if (e.Message is UnAuthorizePumpRequest)
- {
- #region old way un-authorize pump request
- var fdcPump = GetPumpById(e.PumpId);
- if (fdcPump == null)
- {
- logger.Info($"UnAuthorizePumpRequest: No pump found for this id - {e.PumpId}, respond with failure!");
- SendPumpUnauthResponse(e.PumpId, false, PumpUnauthType.Unauthorize);
- return;
- }
- #endregion
- }
- else if (e.Message is CloseRequest)
- {
- logger.Debug($"CloseRequest for Pump {e.PumpId}");
- if (fuelingManager != null)
- {
- bool unauthResult = fuelingManager.UnauthorizePump();
- logger.Debug($"Close resposne, success? {unauthResult}");
- SendPumpUnauthResponse(e.PumpId, unauthResult, PumpUnauthType.General);
- }
- else
- {
- logger.Info($"CloseRequest: No pump found for this id - {e.PumpId}, respond with failure!");
- SendPumpUnauthResponse(e.PumpId, false, PumpUnauthType.General);
- return;
- }
- }
- #endregion
- #region Pump Accumulator
- else if (e.Message is GetAccumulateRequest)
- {
- var terminalHandler = GetTerminalById(e.PumpId);
- if (fuelingManager != null)
- {
- InfoLog(e.PumpId, $"GetAccumulateRequest");
- var response = fuelingManager.GetAccumulator(1);
- terminalHandler?.Write(response);
- }
- else
- {
- GetAccumulateResponse getAccumulateResponse = new GetAccumulateResponse
- {
- 升累计 = 0,
- 金额累计 = 0
- };
- terminalHandler?.Write(getAccumulateResponse);
- }
- }
- #endregion
- #region Rounding request
- else if (e.Message is RoundingRequest)
- {
- DebugLog(e.PumpId, "RoundingRequest");
- if (fuelingManager != null)
- {
- SendRoundingResponse(e.PumpId, fuelingManager.GetRoundingResult());
- }
- else
- {
- var roundingResponse = new RoundingResponse
- {
- EnumResult = NonCardDispenserMessageTemplateBase.Result.失败
- };
- SendRoundingResponse(e.PumpId, roundingResponse);
- }
- }
- #endregion
- #region Cancel full monitor
- else if (e.Message is CancelFullMonitorRequest)
- {
- InfoLog(e.PumpId, "CancelFuellMonitorRequest");
- var response = new CancelFullMonitorResponse
- {
- EnumResult = NonCardDispenserMessageTemplateBase.Result.成功
- };
- terminal?.Write(response);
- InfoLog(e.PumpId, "CancelFullMonitorResponse");
- }
- #endregion
- }
- #endregion
- #region Send rounding response
- /// <summary>
- /// Sends the rounding (凑整) response to IC card reader.
- /// </summary>
- /// <param name="pumpId">Current pump id.</param>
- /// <param name="response">The rounding response.</param>
- private void SendRoundingResponse(int pumpId, RoundingResponse response)
- {
- var terminal = GetTerminalById(pumpId);
- terminal?.Write(response);
- }
- #endregion
- #region Authorization
- /// <summary>
- /// Sends pump authorization response to Hengshan EPS.
- /// </summary>
- /// <param name="pumpId">Current pump id.</param>
- /// <param name="success">Authorization result, success or failure.</param>
- /// <param name="authType">Pump authorization type.</param>
- private void SendPumpAuthResponse(int pumpId, bool success, PumpAuthType authType)
- {
- var terminal = GetTerminalById(pumpId);
- NonCardDispenserMessageTemplateBase authResponse = null;
- if (authType == PumpAuthType.Amount) //Authorize pump with preset amount
- {
- authResponse = new AuthPumpWithAmountResponse
- {
- EnumResult = success ?
- NonCardDispenserMessageTemplateBase.Result.成功 : NonCardDispenserMessageTemplateBase.Result.失败
- };
- }
- else if (authType == PumpAuthType.Volume) //Authorize pump with preset volume
- {
- authResponse = new AuthPumpWithGallonResponse
- {
- EnumResult = success ?
- NonCardDispenserMessageTemplateBase.Result.成功 : NonCardDispenserMessageTemplateBase.Result.失败
- };
- }
- else if (authType == PumpAuthType.General) //Open request
- {
- authResponse = new OpenResponse
- {
- EnumResult = success ?
- NonCardDispenserMessageTemplateBase.Result.成功 : NonCardDispenserMessageTemplateBase.Result.失败
- };
- }
- terminal?.Write(authResponse);
- }
- /// <summary>
- /// Send pump un-authoirzation response to Hengshan EPS.
- /// </summary>
- /// <param name="pumpId">Current pump id.</param>
- /// <param name="success">Un-authorization result, success or failure.</param>
- /// <param name="unauthType">Un-authorization type.</param>
- private void SendPumpUnauthResponse(int pumpId, bool success, PumpUnauthType unauthType)
- {
- var terminal = GetTerminalById(pumpId);
- NonCardDispenserMessageTemplateBase unauthResponse = null;
- if (unauthType == PumpUnauthType.Unauthorize) //Unauthorize pump, regardless of amount or volume preset
- {
- unauthResponse = new UnAuthorizePumpResponse
- {
- EnumResult = success ?
- NonCardDispenserMessageTemplateBase.Result.成功 : NonCardDispenserMessageTemplateBase.Result.失败
- };
- }
- else if (unauthType == PumpUnauthType.General) //Close request
- {
- unauthResponse = new CloseResponse
- {
- EnumResult = success ?
- NonCardDispenserMessageTemplateBase.Result.成功 : NonCardDispenserMessageTemplateBase.Result.失败
- };
- }
- terminal?.Write(unauthResponse);
- }
- #endregion
- #region Handle NozzleStatusRequest
- /// <summary>
- /// Handles the GetNozzleStatusRequest sent from Hengshan IC Terminal.
- /// </summary>
- /// <param name="e">The request message event.</param>
- private void HandleGetNozzleStatusRequest(HengshanTerminalMessageEventArgs e)
- {
- if (logger.IsDebugEnabled)
- logger.Debug("\n");
- DebugLog(e.PumpId, $"GetNozzleStatusRequest for pump id: {e.PumpId}");
- DebugLog(e.PumpId, "GNS Req");
- var fuelingManager = GetFuelingManager(e.PumpId);
- if (fuelingManager != null)
- {
- DebugLog(e.PumpId, $"Start to handle nozzle status request");
- var currentNozzleStatus = fuelingManager.GetNozzleStatus();
- SendNozzleStatusResponse(e.PumpId, currentNozzleStatus);
- var currentFdcPump = fdcPumpControllers.FirstOrDefault(p => p.PumpId == e.PumpId);
- if (currentFdcPump != null && currentFdcPump.QueryStatusAsync().Result == LogicalDeviceState.FDC_CALLING)
- {
- InfoLog(e.PumpId, $"{currentNozzleStatus.ToLogString()}");
- }
- DebugLog(e.PumpId, $"{currentNozzleStatus.ToLogString()}");
- }
- else
- {
- InfoLog($"Could not get the FuelingManager for pump id = {e.PumpId}");
- SendNozzleStatusResponse(e.PumpId, GetNozzleStatusResponse.PumpStatus.不允许加油);
- return;
- }
- }
- #endregion
- #region Send NozzleStatusResponse
- private void SendNozzleStatusResponse(int pumpId, GetNozzleStatusResponse response)
- {
- var terminal = GetTerminalById(pumpId);
- if (terminal == null)
- {
- throw new NullReferenceException("Hengshan Terminal could not be found!");
- }
- terminal.Write(response);
- }
- private void SendNozzleStatusResponse(int pumpId, GetNozzleStatusResponse.PumpStatus pumpStatus)
- {
- var getNozzleStatusResponse = new GetNozzleStatusResponse();
- getNozzleStatusResponse.AddPumpStatus(pumpStatus);
- SendNozzleStatusResponse(pumpId, getNozzleStatusResponse);
- }
- #endregion
- #region Get Pump
- /// <summary>
- /// Gets the FDC pump controller.
- /// </summary>
- /// <param name="pumpId">The pump id.</param>
- /// <returns></returns>
- private IFdcPumpController GetPumpById(int pumpId)
- {
- return fdcPumpControllers?.FirstOrDefault(f => f.PumpId == pumpId);
- }
- #endregion
- #region Get Terminal
- /// <summary>
- /// Gets the Hengshan IC terminal handler
- /// </summary>
- /// <param name="pumpId">The pump id.</param>
- /// <returns></returns>
- private HengshanICTermHandler GetTerminalById(int pumpId)
- {
- foreach (var termHandler in hengshanTerminalHandlers)
- {
- var terminal = termHandler as HengshanICTermHandler;
- if (terminal != null && terminal.AssociatedPumpId.Equals(pumpId))
- return terminal;
- }
- return null;
- }
- #endregion
- #region Site nozzles setup
- /// <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 void SetupSiteNozzles(IEnumerable<IFdcPumpController> fdcPumpControllers)
- {
- nozzles.Clear();
- int nozzleNo = 1; //Site nozzle number starts with 1
- var pumps = fdcPumpControllers.OrderBy(c => c.PumpId);
- foreach (var pump in pumps)
- {
- var pumpNozzles = pump.Nozzles.OrderBy(n => n.LogicalId);
- foreach (var nozzle in pumpNozzles)
- {
- InfoLog($"({pump.PumpId}, {nozzle.LogicalId}) => {nozzleNo}");
- nozzles.Add((pump.PumpId, nozzle.LogicalId), nozzleNo);
- nozzleNo++;
- }
- }
- }
- #endregion
- #region Fueling manager setup
- private void SetupFuelingManagers(IEnumerable<IFdcPumpController> fdcPumpControllers)
- {
- fuelingManagers.Clear();
- foreach (var fdcPump in fdcPumpControllers)
- {
- fuelingManagers.Add(fdcPump.PumpId,
- new FuelingManager(fdcPump.PumpId, fdcPump, fdcServerApp, fdcDbContext, fPosDbManager, CampaignEngine));
- InfoLog($"Fueling manager for pump id: {fdcPump.PumpId} added.");
- }
- }
- private FuelingManager GetFuelingManager(int pumpId)
- {
- DebugLog(pumpId, "get fueling manager");
- FuelingManager fuelingManager = null;
- fuelingManagers.TryGetValue(pumpId, out fuelingManager);
- return fuelingManager;
- }
- #endregion
- #region Set up transaction sumbitters
- private void SetupTransactionSubmitters(IEnumerable<IFdcPumpController> fdcPumpControllers)
- {
- trxSubmitters.Clear();
- foreach (var fdcPump in fdcPumpControllers)
- {
- trxSubmitters.Add(
- new TrxSubmitter(fdcPump.PumpId, fPosDbManager, cloudCredential) { SiteNozzles = nozzles });
- InfoLog($"Transaction submitter for pump id: {fdcPump.PumpId} added.");
- }
- }
- private TrxSubmitter GetTrxSubmitter(int pumpId)
- {
- return trxSubmitters.FirstOrDefault(t => t.Id == pumpId);
- }
- #endregion
- #region Setup MRE
- private void SetupMRE()
- {
- foreach (var fdcPump in fdcPumpControllers)
- {
- mreDict.Add(fdcPump.PumpId, new ManualResetEvent(false));
- }
- }
- #endregion
- #region Log methods
- private void InfoLog(string log)
- {
- if (logger.IsInfoEnabled)
- logger.Info(log);
- }
- private void InfoLog(int pumpId, string log)
- {
- if (logger.IsInfoEnabled)
- logger.Info($"Pump {pumpId}, {log}");
- }
- private void DebugLog(int pumpId, string log)
- {
- if (logger.IsDebugEnabled)
- logger.Debug($"Pump {pumpId}, {log}");
- }
- private void ErrorLog(int pumpId, string log)
- {
- if (logger.IsErrorEnabled)
- logger.Error($"Pump {pumpId}, {log}");
- }
- #endregion
- }
- }
|