123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374 |
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading;
- using System.Threading.Tasks;
- using Edge.Core.Database;
- using Edge.Core.Database.Models;
- using Microsoft.EntityFrameworkCore;
- using Microsoft.Extensions.DependencyInjection;
- using Microsoft.Extensions.Logging;
- namespace Applications.FDC
- {
- public class FdcResourceArbitrator : IFdcResourceArbitrator
- {
- public static ILogger fdcLogger { get; set; }// = ServiceBuilder.Provider.GetRequiredService<ILoggerFactory>().CreateLogger("FdcServer");// NLog.LogManager.LoadConfiguration("nlog.config").GetLogger("FdcServer");
- private static FdcResourceArbitrator instance = new FdcResourceArbitrator();
- private object syncObject = new object();
- public static FdcResourceArbitrator Default => instance;
- //private SqliteDbContext dbContext;
- private FdcResourceArbitrator()
- {
- }
- private int __TryReserveFuelPointAsyncGuard = 0;
- public async Task<bool> TryReserveFuelPointAsync(int posId, int fuelPointId)
- {
- if (0 != Interlocked.CompareExchange(ref this.__TryReserveFuelPointAsyncGuard, 1, 0))
- return false;
- try
- {
- var dbContext = new SqliteDbContext();
- var existed = dbContext.FuelPointReservationModels.Where(f => f.FuelPointId == fuelPointId);
- if (!existed.Any())
- {
- dbContext.FuelPointReservationModels.Add(new FuelPointReservation()
- {
- FuelPointId = fuelPointId,
- ReservingTime = DateTime.Now,
- ReservedByFdcClientId = posId
- });
- await dbContext.SaveChangesAsync();
- fdcLogger.LogDebug(" created reservation");
- return true;
- }
- // add a protection for deadlock by someone, if locked by lasted over 5m, auto unlocked it.
- int maxHoldTime = 5;
- if (existed.Any())
- {
- var lockDue = DateTime.Now.Subtract(existed.First().ReservingTime);
- if (lockDue >= new TimeSpan(0, maxHoldTime, 0))
- {
- fdcLogger.LogInformation(" FuelPoint with id: " + fuelPointId + " have been hold by "
- + existed.First().ReservedByFdcClientId
- + " for " + lockDue.TotalMinutes + " minutes (since: "
- + existed.First().ReservingTime.ToString("yyyy-MM-dd HH:mm:ss") + "), will auto unlock it for avoid long lock");
- dbContext.FuelPointReservationModels.Remove(existed.First());
- await dbContext.SaveChangesAsync();
- existed = dbContext.FuelPointReservationModels.Where(f => f.FuelPointId == fuelPointId);
- }
- }
- if (!existed.Any())
- {
- dbContext.FuelPointReservationModels.Add(new FuelPointReservation()
- {
- FuelPointId = fuelPointId,
- ReservingTime = DateTime.Now,
- ReservedByFdcClientId = posId
- });
- await dbContext.SaveChangesAsync();
- fdcLogger.LogDebug(" Created a FuelPoint Reservation for FdcClientId: " + posId);
- return true;
- }
- else
- {
- if (existed.First().ReservedByFdcClientId != posId)
- {
- fdcLogger.LogDebug(" ReserveFuelPoint failed due to Fp already locked by FdcClientId: " + existed.First().ReservedByFdcClientId);
- return false;
- }
- else
- {
- // refresh the time
- existed.First().ReservingTime = DateTime.Now;
- await dbContext.SaveChangesAsync();
- fdcLogger.LogDebug(" Updated FuelPoint ReservingTime since previous already reserved by caller FdcClientId: " + posId);
- return true;
- }
- }
- }
- catch (Exception exxx)
- {
- fdcLogger.LogError("TryReserveFuelPoint exceptioned: " + exxx.ToString());
- return false;
- }
- finally
- {
- this.__TryReserveFuelPointAsyncGuard = 0;
- }
- }
- private int __TryUnreserveFuelPointAsyncGuard = 0;
- public async Task<bool> TryUnreserveFuelPointAsync(int posId, int fuelPointId)
- {
- if (0 != Interlocked.CompareExchange(ref this.__TryUnreserveFuelPointAsyncGuard, 1, 0))
- return false;
- try
- {
- var dbContext = new SqliteDbContext();
- var reservedBySelf = dbContext.FuelPointReservationModels.Where(f => f.FuelPointId == fuelPointId
- && f.ReservedByFdcClientId == posId);
- if (!reservedBySelf.Any())
- {
- fdcLogger.LogError(" failed on unreserve target pump with pump id:" + fuelPointId
- + " since it didn't get reserved or others already reserved it");
- return false;
- }
- else
- {
- dbContext.FuelPointReservationModels.Remove(reservedBySelf.First());
- await dbContext.SaveChangesAsync();
- return true;
- }
- }
- catch (Exception exxx)
- {
- fdcLogger.LogError("TryUnreserveFuelPoint exceptioned: " + exxx.ToString());
- return false;
- }
- finally
- {
- this.__TryUnreserveFuelPointAsyncGuard = 0;
- }
- }
- private int __TryLockFuelSaleTrxAsyncGuard = 0;
- public async Task<FuelSaleTransaction> TryLockFuelSaleTrxAsync(int posId, int fuelPointId, int transactionNo, int trxDbUniqueId)
- {
- if (0 != Interlocked.CompareExchange(ref this.__TryLockFuelSaleTrxAsyncGuard, 1, 0))
- return null;
- try
- {
- var dbContext = new SqliteDbContext();
- var targetTrx = dbContext.PumpTransactionModels.Where(f =>
- f.ReleaseToken == trxDbUniqueId
- && f.PumpId == fuelPointId
- && f.TransactionSeqNumberFromPhysicalPump == transactionNo.ToString()
- && f.State == FuelSaleTransactionState.Payable).OrderByDescending(t => t.SaleStartTime).FirstOrDefault();
- byte maxRetryTimes = 3;
- byte retriedTimes = 0;
- retry:
- if (targetTrx != null)
- {
- if (string.IsNullOrEmpty(targetTrx.LockedByFdcClientId))
- {
- targetTrx.LockedTime = DateTime.Now;
- targetTrx.LockedByFdcClientId = posId.ToString();
- targetTrx.State = FuelSaleTransactionState.Locked;
- try
- {
- await dbContext.SaveChangesAsync();
- }
- catch (DbUpdateConcurrencyException ex)
- {
- fdcLogger.LogInformation(" lock FuelSaleTrx failed due to DbUpdateConcurrencyException, targetTrx in app memory is: "
- + targetTrx.ToString());
- try
- {
- // Update the values of the entity that failed to save from the store
- ex.Entries.Single().Reload();
- fdcLogger.LogInformation(" while the actual in db is: "
- + targetTrx.ToString()
- + System.Environment.NewLine + "will retry saving...");
- retriedTimes++;
- if (retriedTimes > maxRetryTimes)
- { fdcLogger.LogInformation(" DbUpdateConcurrency retried time reached its max, will quit."); return null; }
- goto retry;
- }
- catch (Exception exxx)
- {
- fdcLogger.LogInformation(" exceptioned again for reload targetTrx from db: " + exxx);
- return null;
- }
- }
- return targetTrx;
- }
- else if (targetTrx.LockedByFdcClientId == posId.ToString())
- {
- fdcLogger.LogDebug(" lock FuelSaleTrx succeed since POS: " + posId + " already own it");
- return targetTrx;
- }
- else
- {
- fdcLogger.LogInformation(" lock FuelSaleTrx failed due to it already locked by other POS: "
- + targetTrx.LockedByFdcClientId + " at "
- + (targetTrx.LockedTime?.ToString("yyyy-MM-dd HH:mm:ss") ?? ""));
- return null;
- }
- }
- else
- {
- fdcLogger.LogInformation(" lock FuelSaleTrx failed since it not existed or state is not Payable");
- return null;
- }
- }
- catch (Exception exxx)
- {
- fdcLogger.LogError("TryLockFuelSaleTrx exceptioned: " + exxx.ToString());
- return null;
- }
- finally { this.__TryLockFuelSaleTrxAsyncGuard = 0; }
- }
- private int __TryUnlockFuelSaleTrxAsyncGuard = 0;
- /// <summary>
- /// Try set the SaleTrx state to Payable.
- /// </summary>
- /// <param name="posId"></param>
- /// <param name="fuelPointId"></param>
- /// <param name="transactionNo"></param>
- /// <param name="trxDbUniqueId"></param>
- /// <returns></returns>
- public async Task<FuelSaleTransaction> TryUnlockFuelSaleTrxAsync(int posId, int fuelPointId, int transactionNo, int trxDbUniqueId)
- {
- if (0 != Interlocked.CompareExchange(ref this.__TryUnlockFuelSaleTrxAsyncGuard, 1, 0))
- return null;
- try
- {
- var dbContext = new SqliteDbContext();
- var targetTrx = dbContext.PumpTransactionModels.Where(f =>
- f.ReleaseToken == trxDbUniqueId
- && f.PumpId == fuelPointId
- && f.TransactionSeqNumberFromPhysicalPump == transactionNo.ToString()
- && f.State == FuelSaleTransactionState.Locked
- && f.LockedByFdcClientId == posId.ToString()).FirstOrDefault();
- if (targetTrx != null)
- {
- fdcLogger.LogDebug(" unlock FuelSaleTrx succeed");
- targetTrx.LockedByFdcClientId = "";
- targetTrx.LockedTime = DateTime.MinValue;
- targetTrx.State = FuelSaleTransactionState.Payable;
- await dbContext.SaveChangesAsync();
- return targetTrx;
- }
- else
- {
- fdcLogger.LogInformation(" unlock FuelSaleTrx failed");
- return null;
- }
- }
- catch (Exception exxx)
- {
- fdcLogger.LogError("TryUnlockFuelSaleTrx exceptioned: " + exxx.ToString());
- return null;
- }
- finally { this.__TryUnlockFuelSaleTrxAsyncGuard = 0; }
- }
- //private int __TryLockNozzleAsyncGuard = 0;
- ///// <summary>
- /////
- ///// </summary>
- ///// <param name="posId"></param>
- ///// <param name="fuelPointId"></param>
- ///// <param name="logicalNozzleId"></param>
- ///// <returns></returns>
- //public async Task<bool> TryLockNozzleAsync(int posId, int fuelPointId, int logicalNozzleId)
- //{
- // if (0 != Interlocked.CompareExchange(ref this.__TryLockNozzleAsyncGuard, 1, 0))
- // return false;
- // try
- // {
- // var dbContext = new SqliteDbContext();
- // var existsLockingNozzleRecord = dbContext.NozzleLocks.Where(f =>
- // f.FuelPointId == fuelPointId
- // && f.LogicalNozzleId == logicalNozzleId).FirstOrDefault();
- // if (existsLockingNozzleRecord != null)
- // {
- // if (existsLockingNozzleRecord.LockedByFdcClientId != null
- // && existsLockingNozzleRecord.LockedByFdcClientId == posId)
- // {
- // existsLockingNozzleRecord.LockingTime = DateTime.Now;
- // try
- // {
- // await dbContext.SaveChangesAsync();
- // return true;
- // }
- // catch (Exception ex)
- // {
- // fdcLogger.LogInformation(" exceptioned for updating locking time for lock nozzle from db: " + ex);
- // return false;
- // }
- // }
- // else if (existsLockingNozzleRecord.LockedByFdcClientId != null
- // && existsLockingNozzleRecord.LockedByFdcClientId != posId)
- // {
- // fdcLogger.LogInformation(" lock nozzle failed due to already locked by POS: " + existsLockingNozzleRecord.LockedByFdcClientId);
- // return false;
- // }
- // }
- // try
- // {
- // var newNozzleLocking = new NozzleLock()
- // {
- // LockingTime = DateTime.Now,
- // LockedByFdcClientId = posId,
- // FuelPointId = fuelPointId,
- // LogicalNozzleId = logicalNozzleId
- // };
- // dbContext.NozzleLocks.Add(newNozzleLocking);
- // await dbContext.SaveChangesAsync();
- // return true;
- // }
- // catch (Exception ex)
- // {
- // fdcLogger.LogInformation(" exceptioned for create a lock nozzle from db: " + ex);
- // return false;
- // }
- // }
- // catch { return false; }
- // finally { this.__TryLockNozzleAsyncGuard = 0; }
- //}
- //private int __TryUnlockNozzleAsyncGuard = 0;
- ///// <summary>
- /////
- ///// </summary>
- ///// <param name="posId"></param>
- ///// <param name="fuelPointId"></param>
- ///// <param name="logicalNozzleId"></param>
- ///// <returns></returns>
- //public async Task<NozzleLock> TryUnlockNozzleAsync(int posId, int fuelPointId, int logicalNozzleId)
- //{
- // if (0 != Interlocked.CompareExchange(ref this.__TryUnlockNozzleAsyncGuard, 1, 0))
- // return null;
- // try
- // {
- // var dbContext = new SqliteDbContext();
- // var targetTrx = dbContext.NozzleLocks.Where(f =>
- // f.FuelPointId == fuelPointId
- // && f.LogicalNozzleId == logicalNozzleId
- // && f.LockedByFdcClientId == posId).FirstOrDefault();
- // if (targetTrx != null)
- // {
- // fdcLogger.LogDebug(" unlock NozzleAsync succeed");
- // targetTrx.LockedByFdcClientId = null;
- // targetTrx.LockingTime = null;
- // await dbContext.SaveChangesAsync();
- // return targetTrx;
- // }
- // else
- // {
- // fdcLogger.LogInformation(" unlock NozzleAsync failed");
- // return null;
- // }
- // }
- // catch (Exception exxx)
- // {
- // fdcLogger.LogError("TryUnlockNozzleAsync exceptioned: " + exxx.ToString());
- // return null;
- // }
- // finally { this.__TryUnlockNozzleAsyncGuard = 0; }
- //}
- }
- }
|