using Edge.Core.Processor; using Edge.Core.IndustryStandardInterface.Pump; using Dfs.WayneChina.CardTrxMonitor.Models; using System; using System.Linq; using System.Collections.Generic; using System.Text; using Wayne.FDCPOSLibrary; using System.Threading.Tasks; using System.Threading; using Dfs.WayneChina.CardTrxManager; using Dfs.WayneChina.CardTrxManager.TrxSubmitter; namespace Dfs.WayneChina.CardTrxMonitor { /// /// Entity that pulls a finished IC card transaction from MySQL and publishes it to FdcServer. /// public class CardTrxMonitorApp : IAppProcessor, IFdcPumpController { #region Properties public string Name => "CardTrxMonitor"; public int PumpId { get; private set; } public byte NozzleId { get; private set; } //This is a fake pump. public int PumpPhysicalId => 0; private List nozzles = new List(); public IEnumerable Nozzles => nozzles; //China domestic standard public int AmountDecimalDigits => 2; public int VolumeDecimalDigits => 2; public int PriceDecimalDigits => 2; public int VolumeTotalizerDecimalDigits => 2; public string MetaConfigName { get; set; } #endregion #region Fields private System.Timers.Timer _timer; private int scanInterval; private int siteNozzleNo; private int bindingBarcode; private CloudCredential cloudCredential; private TrxSubmitter submitter; #endregion #region Logger NLog.Logger logger = NLog.LogManager.LoadConfiguration("NLog.config").GetLogger("CardTrxMonitor"); #endregion #region Constructor public CardTrxMonitorApp(int pumpId, int nozzleId, int siteNozzleNo, int barcode, int scanInterval, string username, string password, string authServiceBaseUrl, string transactionServiceBaseUrl, string deviceSN) { PumpId = pumpId; NozzleId = Convert.ToByte(nozzleId); this.siteNozzleNo = siteNozzleNo; bindingBarcode = barcode; nozzles.Add(new LogicalNozzle(pumpId, 1, NozzleId, null)); this.scanInterval = scanInterval; cloudCredential = new CloudCredential { UserName = username, Password = password, AuthServiceBaseUrl = authServiceBaseUrl, TransactionServiceBaseUrl = transactionServiceBaseUrl, DeviceSN = deviceSN }; submitter = new TrxSubmitter(PumpId, cloudCredential); } #endregion #region IApplication implementation public void Init(IEnumerable processors) { } public Task Start() { _timer = new System.Timers.Timer(); _timer.Interval = scanInterval * 1000; _timer.Elapsed += TimeElapsed; _timer.Start(); return Task.FromResult(true); } private void TimeElapsed(object sender, System.Timers.ElapsedEventArgs e) { Log("Timer reset..."); try { FindTransaction(); } catch (Exception ex) { Log($"{ex}"); } } private void Log(string message) { logger.Info($"Monitor{PumpId}, {message}"); } private void FindTransaction() { Log("start to check db..."); try { using (var _context = new SpsDbContext()) { var cardTrx = _context.TCardtrx.FirstOrDefault(t => t.PumpNo == PumpId); if (cardTrx != null && IsTrdTypeAllowed(cardTrx.TrdType) && cardTrx.Mon != 0) { //Before processing is done against the found transaction, stop the timer in case that //it takes a while and the timer is triggerred for a second time and exception raised. _timer.Stop(); if (cardTrx.TrdType == 6) { Log("Non-card fuelling"); } else if (cardTrx.TrdType == 1) { Log("Gray card transaction, remove it otherwise it will block further transactions"); RemoveTransaction(cardTrx, _context); } //103, Operator card if (cardTrx.PaymodeId == 103) { Log($"Found finished IC card transaction: Pump: {cardTrx.PumpNo}, Volume: {cardTrx.Vol}, Amount: {cardTrx.Mon}"); FdcTransaction fdcTransaction = new FdcTransaction(); fdcTransaction.Amount = cardTrx.Mon.Value; fdcTransaction.Volumn = Convert.ToInt32(cardTrx.Vol.Value); fdcTransaction.VolumeTotalizer = Convert.ToInt32(cardTrx.EndPumpId.Value); fdcTransaction.Price = Convert.ToInt32(cardTrx.Prc.Value); fdcTransaction.SequenceNumberGeneratedOnPhysicalPump = Convert.ToInt32(cardTrx.Gid); fdcTransaction.Nozzle = new LogicalNozzle(cardTrx.PumpNo, 0, Convert.ToByte(NozzleId), 0); fdcTransaction.Finished = true; OnCurrentFuellingStatusChange?.Invoke(this, new FdcTransactionDoneEventArg(fdcTransaction)); RemoveTransaction(cardTrx, _context); } else if (cardTrx.PaymodeId == 100) //100, Customer card 定位卡问题 { Log($"Found customer card transaction: Pump: {cardTrx.PumpNo}, Volume: {cardTrx.Vol}, Amount: {cardTrx.Mon}"); var submitResult = Task.Run(async () => { var result = await SubmitTrxAsync(cardTrx, bindingBarcode); return result; }); Log($"submit transaction, result: {submitResult.Result}"); RemoveTransaction(cardTrx, _context); } else { //105: //108: } } else if (cardTrx != null && cardTrx.Mon == 0) { Log($"Zero amount transaction, Pump No: {cardTrx.PumpNo}, SeqNo: {cardTrx.SeqNo}, delete it!"); RemoveTransaction(cardTrx, _context); } else if (cardTrx != null && cardTrx.Gid != 0) { Log($"Trd Type: {cardTrx?.TrdType}"); RemoveTransaction(cardTrx, _context); } //Reacitivate the timer. _timer.Start(); } } catch (Exception ex) { Log($"Database operation: {ex}"); } } /// ///00:正常卡交易 ///01:灰卡交易 ///02:解灰交易(解灰后即为正常交易) ///04:现金后台授权交易 ///05:撤消授权卡交易 ///06:非卡交易 ///07:撤消授权非卡交易 ///08:油价下载记录 /// /// /// private bool IsTrdTypeAllowed(byte trdType) { if (trdType == 0 || trdType == 2 || trdType == 6) return true; return false; } private void RemoveTransaction(TCardtrx cardTrx, SpsDbContext context) { try { context.Remove(cardTrx); context.SaveChanges(); } catch (Exception ex) { Log($"Exception in removing trx: {ex}"); } } private async Task SubmitTrxAsync(TCardtrx cardTrx, int barcode) { var clientTrxInfo = new ClientTrxInfo { CardNo = cardTrx.CardNo, CurrentCardBalance = Convert.ToDecimal(cardTrx.CardBal.Value) / 100, UnitPrice = Convert.ToDecimal(cardTrx.Prc) / 100, Amount = Convert.ToDecimal(cardTrx.Mon) / 100, PayAmount = Convert.ToDecimal(cardTrx.RealMon) / 100, Volume = Convert.ToDecimal(cardTrx.Vol) / 100, PumpId = cardTrx.PumpNo, NozzleId = Convert.ToByte(cardTrx.NozNo), SiteNozzleNo = siteNozzleNo, Barcode = barcode, FuelingStartTime = cardTrx.Ttctime, FuelingFinishedTime = cardTrx.TtctimeEnd.Value, SeqNo = Convert.ToInt32(cardTrx.SeqNo) }; var result = await submitter.SubmitTrxAsync(clientTrxInfo); return result; } public Task Stop() { if (_timer != null) { _timer.Dispose(); } return Task.FromResult(true); } #endregion #region Event handlers public event EventHandler OnStateChange; public event EventHandler OnCurrentFuellingStatusChange; #endregion #region Methods, not useful public bool Authorize(byte logicalNozzleId) { return false; } public bool AuthorizeWithAmount(int moneyAmountWithoutDecimalPoint, byte logicalNozzleId) { return false; } public bool AuthorizeWithVolumn(int volumnWithoutDecimalPoint, byte logicalNozzleId) { return false; } public bool ChangeFuelPrice(int newPriceWithoutDecimalPoint, byte logicalNozzleId) { return false; } public bool FuelingRoundUpByAmount(int amount) { return false; } public bool FuelingRoundUpByVolumn(int volume) { return false; } public void OnFdcServerInit(Dictionary parameters) { //the parameters don't make any sense for this fake pump handler. } public LogicalDeviceState QueryStatus() { return LogicalDeviceState.FDC_READY; } public Tuple QueryTotalizer(byte logicalNozzleId) { return new Tuple(-1, -1); } public bool ResumeFuelling() { return false; } public bool SuspendFuelling() { return false; } public bool UnAuthorize(byte logicalNozzleId) { return false; } public Task QueryStatusAsync() { throw new NotImplementedException(); } public Task> QueryTotalizerAsync(byte logicalNozzleId) { return Task.FromResult(new Tuple(-1, -1)); } public Task SuspendFuellingAsync() { throw new NotImplementedException(); } public Task ResumeFuellingAsync() { throw new NotImplementedException(); } public Task ChangeFuelPriceAsync(int newPriceWithoutDecimalPoint, byte logicalNozzleId) { throw new NotImplementedException(); } public Task AuthorizeAsync(byte logicalNozzleId) { throw new NotImplementedException(); } public Task UnAuthorizeAsync(byte logicalNozzleId) { throw new NotImplementedException(); } public Task AuthorizeWithAmountAsync(int moneyAmountWithoutDecimalPoint, byte logicalNozzleId) { throw new NotImplementedException(); } public Task AuthorizeWithVolumeAsync(int volumnWithoutDecimalPoint, byte logicalNozzleId) { throw new NotImplementedException(); } public Task FuelingRoundUpByAmountAsync(int amount) { throw new NotImplementedException(); } public Task FuelingRoundUpByVolumeAsync(int volume) { throw new NotImplementedException(); } public async Task LockNozzleAsync(byte logicalNozzleId) { return false; } public async Task UnlockNozzleAsync(byte logicalNozzleId) { return false; } #endregion } }