using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Xml.Serialization; namespace Dfs.WayneChina.CardTrxManager { public class StoreForwardCompletedEventArgs : EventArgs { public int PumpdId { get; set; } public int SeqNo { get; set; } public int ReleaseToken { get; set; } } public class StoreForwardManager { static NLog.Logger logger = NLog.LogManager.LoadConfiguration("NLog.config").GetLogger("PosTrxSubmitter"); private TrxSubmitter.TrxSubmitter submitter; private TransactionManager transactionManager; public event EventHandler<StoreForwardCompletedEventArgs> OnStoreForwardCompleted; private System.Timers.Timer scanTimer; private int count = 0; public StoreForwardManager(CloudCredential credential) { submitter = new TrxSubmitter.TrxSubmitter(100, credential); transactionManager = new TransactionManager(); scanTimer = new System.Timers.Timer(); scanTimer.Elapsed += ScanTimer_Elapsed; scanTimer.Interval = 30000; } private async void ScanTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { scanTimer.Stop(); count++; var firstFileName = Directory.GetFiles("StoreForward").FirstOrDefault(); if (!string.IsNullOrEmpty(firstFileName)) { logger.Info($" SF, {count}, found file: {firstFileName}"); var sfTrans = transactionManager.TryLoad(firstFileName); if (sfTrans != null) { logger.Info($" SF, {count}, Transaction reloaded"); var result = await submitter.SubmitTrxAsync(sfTrans); logger.Info($" SF, {count}, submit result: {result}"); if (result) { FuelingDoneItem fuelingDoneItem = (FuelingDoneItem)sfTrans.TransactionItems.FirstOrDefault(t => t is FuelingDoneItem); if (fuelingDoneItem != null) { OnStoreForwardCompleted?.Invoke(this, new StoreForwardCompletedEventArgs { PumpdId = fuelingDoneItem.PumpId, ReleaseToken = fuelingDoneItem.FdcSqNo, SeqNo = fuelingDoneItem.SeqNo }); } try { File.Delete(firstFileName); logger.Info($" SF success, delete file: {firstFileName}"); } catch (Exception ex) { logger.Error("Exception in deleting file " + ex.ToString()); } } } } scanTimer.Start(); } public void Start() { transactionManager.Init(); scanTimer.Start(); } } public class TransactionManager { private readonly string storeForwardFolder = "StoreForward"; private readonly string errorFolder = @"StoreForward/Error"; private XmlSerializer xmlSerializer; static NLog.Logger logger = NLog.LogManager.LoadConfiguration("NLog.config").GetLogger("PosTrxSubmitter"); public TransactionManager() { xmlSerializer = new XmlSerializer(typeof(StoreFowardTransaction), transactionItemTypes); } public void Init() { if (!Directory.Exists(storeForwardFolder)) Directory.CreateDirectory(storeForwardFolder); if (!Directory.Exists(errorFolder)) Directory.CreateDirectory(errorFolder); } Type[] transactionItemTypes = { typeof(TransactionItem), typeof(TransactionCreatedItem), typeof(ProductCodeIdentitiedItem), typeof(FuelingDoneItem), typeof(CommittedItem) }; public void SaveTransaction(StoreFowardTransaction transaction) { Save(storeForwardFolder, transaction); } public StoreFowardTransaction TryLoad(string fileName) { try { using (var reader = new StreamReader(fileName)) { var transaction = (StoreFowardTransaction)xmlSerializer.Deserialize(reader); return transaction; } } catch (Exception ex) { logger.Error($"TM: {ex.ToString()}"); } return null; } public void SetTransactionToError(StoreFowardTransaction transaction) { Save(errorFolder, transaction); } private void Save(string targetLocation, StoreFowardTransaction transaction) { FuelingDoneItem fuelingDoneItem = transaction.TransactionItems.FirstOrDefault(i => i is FuelingDoneItem) as FuelingDoneItem; if (fuelingDoneItem != null) { string fileName = $"Trans_{fuelingDoneItem.PumpId}_{DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss")}.xml"; try { using (var sw = new StreamWriter(targetLocation + "/" + fileName)) { xmlSerializer.Serialize(sw, transaction); } } catch (Exception ex) { logger.Error($"TM: {ex.ToString()}"); } } else { logger.Error("TM: No FuelingDoneItem"); } } } [XmlRoot("SFTrans")] [XmlInclude(typeof(TransactionItem))] public class StoreFowardTransaction { public StoreFowardTransaction() { TransactionItems = new List<TransactionItem>(); } [XmlArray("Items")] [XmlArrayItem("TransactionItem")] public List<TransactionItem> TransactionItems { get; } public int ErrorItemsCount { get { int count = 0; for (int i = 0; i < TransactionItems.Count; i++) { if (TransactionItems[i] is ErrorItem) count++; } return count; } } public TransactionState LastState => TransactionItems.OrderBy(t => t.AddedTime).Last().State; public int FuelingId { get { var fuelingDoneItem = TransactionItems.FirstOrDefault(t => t is FuelingDoneItem) as FuelingDoneItem; if (fuelingDoneItem != null) return fuelingDoneItem.SeqNo; return 0; } } } [XmlType("TransactionItem")] // define Type [XmlInclude(typeof(FuelingDoneItem)), XmlInclude(typeof(ProductCodeIdentitiedItem)), XmlInclude(typeof(TransactionCreatedItem)), XmlInclude(typeof(CommittedItem))] public class TransactionItem { public TransactionItem() { AddedTime = DateTime.Now; } [XmlAttribute("State")] public TransactionState State { get; set; } public DateTime AddedTime { get; set; } } public enum TransactionState { Unknown, FuelingDone, ProductCodeIdentified, Created, Committed, Error } [XmlType("FuelingDoneItem")] public class FuelingDoneItem : TransactionItem { public FuelingDoneItem() { State = TransactionState.FuelingDone; } public int Barcode { get; set; } public int PumpId { get; set; } public int NozzleId { get; set; } public int SiteNozzleNo { get; set; } public ushort FPosSqNo { get; set; } public int FdcSqNo { get; set; } public DateTime TimeStamp { get; set; } public decimal Volume { get; set; } public decimal Amount { get; set; } public decimal PayAmount { get; set; } public decimal UnitPrice { get; set; } public int SeqNo { get; set; } public string CardNo { get; set; } public decimal CurrentCardBalance { get; set; } public DateTime FuelingStartTime { get; set; } public DateTime FuelingFinishedTime { get; set; } public decimal VolumeTotalizer { get; set; } public string CardHolder { get; set; } public string AccountName { get; set; } } [XmlType("ProductCodeIdentitiedItem")] public class ProductCodeIdentitiedItem : TransactionItem { public ProductCodeIdentitiedItem() { State = TransactionState.ProductCodeIdentified; } public Guid ItemId { get; set; } } [XmlType("TransactionCreatedItem")] public class TransactionCreatedItem : TransactionItem { public TransactionCreatedItem() { State = TransactionState.Created; } public Guid TransactionId { get; set; } } [XmlType("CommittedItem")] public class CommittedItem : TransactionItem { public CommittedItem() { State = TransactionState.Committed; } public bool Success { get; set; } } [XmlType("ErrorItem")] public class ErrorItem : TransactionItem { public ErrorItem() { State = TransactionState.Error; } } }