123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667 |
- using Edge.Core.Configuration;
- using Edge.Core.Database;
- using Edge.Core.Database.Models;
- using Edge.Core.IndustryStandardInterface.ATG;
- using Edge.Core.IndustryStandardInterface.Pump;
- using Edge.Core.Processor;
- using Edge.Core.Processor.Dispatcher.Attributes;
- using Edge.Core.UniversalApi;
- using FdcServerHost;
- using Microsoft.EntityFrameworkCore;
- using Microsoft.Extensions.DependencyInjection;
- using Microsoft.Extensions.Logging;
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text.Json;
- using System.Text.RegularExpressions;
- using System.Threading.Tasks;
- using Wayne.FDCPOSInterface;
- using Wayne.FDCPOSLibrary;
- namespace Applications.FDC
- {
- [UniversalApi(Name = "OnFdcControllerStateChange", EventDataType = typeof(FdcPumpControllerOnStateChangeEventArg), Description = "When pump state changed, the event will fired")]
- [UniversalApi(Name = "OnError", EventDataType = typeof(string))]
- [UniversalApi(Name = "OnCurrentFuellingStatusChange", EventDataType = typeof(FdcServerTransactionDoneEventArg), Description = "When pump in fueling state, the fueling progress will be reported via this event")]
- [UniversalApi(Name = "OnFdcFuelSaleTransactinStateChange", EventDataType = typeof(FdcFuelSaleTransactinStateChangeEventArg), Description = "When pump transaction state changed, the event will fired")]
- [MetaPartsDescriptor(
- "lang-zh-cn:Fdc 服务器 Applang-en-us:Fdc Server App",
- "lang-zh-cn:用于管理已经配置和连接在此FCC中的所有加油机,并提供基于 IFSF-POS-FDC 协议定义的各类接口" +
- "lang-en-us:Used for manage all pumps configed and connected in this FCC, and providing varies of APIs based on IFSF-FDC-POS protocol",
- new[] { "lang-zh-cn:加油机lang-en-us:Pump", "lang-zh-cn:IfsfFdcServerlang-en-us:IfsfFdcServer" })]
- public class FdcServerHostApp : IAppProcessor
- {
- [UniversalApi(Description = "Get the overall pumps, nozzles info.")]
- public async Task<IEnumerable<object>> GetPumpsLayout(ApiData input)
- {
- //if (input == null || input.Parameters == null || !input.Parameters.Any())
- // return this.fdcPumpControllers;
- //var targetPumpIds = input.Parameters.Where(v => v.Name != null && v.Name.ToLower() == "pumpid" && int.TryParse(v.Value, out _))
- // .Select(v => int.Parse(v.Value));
- ////if (!targetPumpIds.Any()) return this.fdcPumpControllers;
- return this.fdcPumpControllers.Select(p => new
- {
- p.Name,
- p.PumpId,
- Nozzles = p.Nozzles.Select(n => new
- {
- n.LogicalId,
- n.RealPriceOnPhysicalPump,
- SiteLevelNozzleId = this.nozzleExtraInfos.FirstOrDefault(c =>
- c.PumpId == n.PumpId && c.NozzleLogicalId == n.LogicalId)?.SiteLevelNozzleId,
- ProductBarcode = this.nozzleExtraInfos.FirstOrDefault(c =>
- c.PumpId == n.PumpId && c.NozzleLogicalId == n.LogicalId)?.ProductBarcode,
- ProductName = this.nozzleExtraInfos.FirstOrDefault(c =>
- c.PumpId == n.PumpId && c.NozzleLogicalId == n.LogicalId)?.ProductName,
- }),
- p.AmountDecimalDigits,
- p.VolumeDecimalDigits,
- p.PriceDecimalDigits,
- p.VolumeTotalizerDecimalDigits
- });
- }
- [UniversalApi(Description = "Get FuelSaleTrx Details info by searching conditions. </br>" +
- "Searching condition 1: by providing para.Name=='ReleaseToken' will ignore other conditions.</br>" +
- "Searching condition 2: para.Name=='PumpId' and para.Name=='LogicalNozzleId' must be provided, and para.Name=='TransactionNumber' is optional.")]
- public async Task<IEnumerable<object>> GetFuelSaleTrxDetailsAsync(ApiData input)
- {
- if (input == null || input.Parameters == null || !input.Parameters.Any()) throw new ArgumentException(nameof(input));
- List<FuelSaleTransaction> transactions;
- if (int.TryParse(input.Parameters.FirstOrDefault(p => p.Name.ToLower() == "releasetoken")?.Value, out int targetReleaseToken))
- {
- SqliteDbContext dbContext = new SqliteDbContext();
- transactions = await dbContext.PumpTransactionModels.Where(t => t.ReleaseToken == targetReleaseToken).ToListAsync();
- }
- else
- {
- if (!int.TryParse(input.Parameters.FirstOrDefault(p => p.Name.ToLower() == "pumpid")?.Value, out int targetPumpId)
- || !int.TryParse(input.Parameters.FirstOrDefault(p => p.Name.ToLower() == "logicalnozzleid")?.Value, out int targetLogicalNozzleId))
- throw new ArgumentException("Must provide valid parameter value for PumpId and LogicalNozzleId");
- var targetTransactionNumber = input.Parameters.FirstOrDefault(p => p.Name.ToLower() == "transactionnumber")?.Value;
- SqliteDbContext dbContext = new SqliteDbContext();
- int maxReturnDataCount = 50;
- if (string.IsNullOrEmpty(targetTransactionNumber))
- transactions = await dbContext.PumpTransactionModels.Where(t =>
- t.PumpId == targetPumpId && t.LogicalNozzleId == targetLogicalNozzleId).OrderByDescending(t => t.SaleStartTime).Take(maxReturnDataCount).ToListAsync();
- else
- transactions = await dbContext.PumpTransactionModels.Where(t =>
- t.PumpId == targetPumpId && t.LogicalNozzleId == targetLogicalNozzleId
- && t.TransactionSeqNumberFromPhysicalPump == targetTransactionNumber).OrderByDescending(t => t.SaleStartTime).Take(maxReturnDataCount).ToListAsync();
- }
- return transactions.Select(trx =>
- {
- //NOTE, the queried trx may from old times, but the this.fdcPumpControllers and this.nozzleProductConfig
- //are always the latest, so they may mismatch and produce incorrect data.
- var pumpHandler = this.fdcPumpControllers.FirstOrDefault(fpc => fpc.PumpId == trx.PumpId);
- return new
- {
- trx.ReleaseToken,
- trx.PumpId,
- trx.LogicalNozzleId,
- SiteLevelNozzleId = this.nozzleExtraInfos.FirstOrDefault(c =>
- c.PumpId == trx.PumpId && c.NozzleLogicalId == trx.LogicalNozzleId)?.SiteLevelNozzleId,
- trx.TransactionSeqNumberFromPhysicalPump,
- trx.ProductBarcode,
- ProductName = this.nozzleExtraInfos.FirstOrDefault(c =>
- c.PumpId == trx.PumpId && c.NozzleLogicalId == trx.LogicalNozzleId)?.ProductName,
- RawAmount = trx.Amount,
- Amount = pumpHandler == null ? (double?)null : (trx.Amount / Math.Pow(10, pumpHandler.AmountDecimalDigits)),
- RawVolume = trx.Volumn,
- Volume = pumpHandler == null ? (double?)null : trx.Volumn / Math.Pow(10, pumpHandler.VolumeDecimalDigits),
- RawPrice = trx.UnitPrice,
- Price = pumpHandler == null ? (double?)null : trx.UnitPrice / Math.Pow(10, pumpHandler.PriceDecimalDigits),
- RawAmountTotalizer = trx.AmountTotalizer,
- AmountTotalizer = pumpHandler == null ? (double?)null : trx.AmountTotalizer / Math.Pow(10, pumpHandler.AmountDecimalDigits),
- RawVolumeTotalizer = trx.VolumeTotalizer,
- VolumeTotalizer = pumpHandler == null ? (double?)null : trx.VolumeTotalizer / Math.Pow(10, pumpHandler.VolumeDecimalDigits),
- trx.State,
- trx.SaleStartTime,
- trx.SaleEndTime,
- };
- });
- }
- [UniversalApi]
- public int PumpCount => this.fdcPumpControllers?.Count ?? 0;
- [UniversalApi]
- public async Task<IEnumerable<object>> GetDeviceState(ApiData input)
- {
- List<object> resultList = new List<object>();
- foreach (var controller in this.FdcPumpControllers)
- {
- var state = await controller.QueryStatusAsync();
- //var nozzles = controller.Nozzles.Select(n => new
- //{
- // n.LogicalId,
- // VolumeTotalizer = GetVolumeTotalizer(controller.PumpId, n.LogicalId),
- // Amount = GetLatestTransactionAmount(controller.PumpId, n.LogicalId),
- // n.LogicalState
- //});
- List<object> nozzles = new List<object>();
- foreach (var n in controller.Nozzles)
- {
- if (n.VolumeTotalizer == null)
- {
- var result = await GetFuelPointTotalsAsync(controller.PumpId, n.LogicalId);
- n.VolumeTotalizer = (int?)(result.Item2 * Math.Pow(10, controller.VolumeDecimalDigits));
- }
- var Amount = GetLatestTransactionAmount(controller.PumpId, n.LogicalId);
- nozzles.Add(new { n.LogicalId, n.VolumeTotalizer, Amount });
- }
- resultList.Add(new { controller.PumpId, DeviceState = state, Nozzles = nozzles });
- }
- return resultList;
- }
- private int GetLatestTransactionAmount(int pumpId, int nozzleId)
- {
- var dueDate = DateTime.Now.Subtract(new TimeSpan(30, 0, 0, 0));
- SqliteDbContext dbContext = new SqliteDbContext();
- var trans = dbContext.PumpTransactionModels.Where(t => t.State != FuelSaleTransactionState.Cleared
- && t.PumpId == pumpId && t.LogicalNozzleId == nozzleId
- && t.SaleStartTime >= dueDate).OrderByDescending(r => r.SaleStartTime).Take(1);
- return trans.Any() ? trans.FirstOrDefault().Amount : 0;
- }
- [UniversalApi(Description = "Get lastest 1000 count of Payable or Locked state fuel trx for nozzles." +
- "</br>input parameters are explained as below, " +
- "</br>para.Name==\"nozzleid\" nozzle logical id" +
- "</br>\"pumpid\" the target pump id" +
- "</br>Leave input as Null to get for all nozzles in all pumps")]
- public async Task<object> GetLastAvailableTransaction(ApiData input)
- {
- var nozzleId = -1;
- var pumpId = -1;
- if (input != null && !input.IsEmpty())//with no search condition specified, return latest 1000 records.
- {
- var temp = "";
- foreach (var p in input.Parameters)
- {
- temp += p.Name + " " + p.Value + " ";
- }
- //fdcLogger.LogDebug("input is not null, parameters:{0}", temp);
- nozzleId = input.Get("nozzleid", -1);
- pumpId = input.Get("pumpid", -1);
- }
- fdcLogger.LogDebug("GetAvailableTransactions for nozzle:{0},pump:{1}", nozzleId, pumpId);
- List<object> resultList = new List<object>();
- var result = await GetAvailableFuelSaleTrxsWithDetailsAsync(pumpId, nozzleId, 1000);
- if (result != null && result.Any())
- {
- var all = result.GroupBy(r => new { r.PumpId, r.LogicalNozzleId });
- foreach (var a in all)
- {
- var trans = a.Where(_ =>
- _.State == FuelSaleTransactionState.Payable || _.State == FuelSaleTransactionState.Locked);
- resultList.Add(new { a.Key.PumpId, a.Key.LogicalNozzleId, hasPayableTransactions = trans.Any(), trans.FirstOrDefault().VolumeTotalizer, trans.FirstOrDefault().Amount });
- }
- return resultList;
- }
- resultList.Add(new { LogicalNozzleId = nozzleId, PumpId = pumpId, hasPayableTransactions = false });
- return resultList;
- }
- private bool config_AutoAuthCallingPumps;
- private int config_MaxStackUnpaidTrxPerPump;
- private int config_ListeningPort;
- private FDCPOSInterfaceServer fdcServer;
- public string MetaConfigName { get; set; }
- private IEnumerable<NozzleExtraInfo> nozzleExtraInfos;
- private List<IFdcPumpController> fdcPumpControllers;
- private List<IFdcCommunicableController> fdcCommunicableControllers;
- private List<IAutoTankGaugeController> autoTankGaugeControllers = new List<IAutoTankGaugeController>();
- private IServiceProvider services;
- private Configurator configurator;
- private EventHandler onConfiguratorConfigFileChangedEventHandler;
- private System.Timers.Timer purgeDatabaseTimer;
- static ILogger fdcLogger;//= NLog.LogManager.LoadConfiguration("nlog.config").GetLogger("FdcServer");
- static ILogger cloudLogger;// = NLog.LogManager.LoadConfiguration("nlog.config").GetLogger("CloudRestClient");
- private object syncObject = new object();
- enum FDCLogicalState
- {
- FDC_LOGICAL_ST_UNLOCKED = 0,
- FDC_LOGICAL_ST_LOCKED = 1,
- };
- #region App configs
- /// <summary>
- /// when doing major schema change of the config class, the class should not be modify directly.
- /// instead, for better fallback support, create a new class with a version suffix, and also
- /// update the version string in appCtorParamsJsonSchema.json file.
- /// </summary>
- public class AppConfigV1
- {
- public bool AutoAuthCallingPumps { get; set; }
- public int MaxStackUnpaidTrxPerPump { get; set; }
- public int FdcServerListeningPort { get; set; }
- public int? PurgePayableTrxOlderThanByMin { get; set; }
- public int? PurgeClearedTrxOlderThanByDay { get; set; }
- public List<AppProductConfigV1> ProductConfigs { get; set; }
- public List<AppPumpConfigV1> PumpConfigs { get; set; }
- }
- public class AppPumpConfigV1
- {
- public int PumpId { get; set; }
- public List<AppNozzleConfigV1> NozzleConfigs { get; set; }
- }
- public class AppProductConfigV1
- {
- public int ProductCode { get; set; }
- public string ProductName { get; set; }
- }
- public class AppNozzleConfigV1
- {
- public int NozzleLogicalId { get; set; }
- public int? SiteLevelNozzleId { get; set; }
- public int ProductCode { get; set; }
- /// <summary>
- /// Gets or sets the tank number of this nozzle undergroud linked.
- /// </summary>
- public int? TankNumber { get; set; }
- public string Description { get; set; }
- }
- #endregion
- /// <summary>
- /// A callback function that will be called on DefaultDispatcher(a framework util for instantiate App and DeviceHandler)
- /// is trying to serialize param strings(most likely saved into a database) into params of this class's ctor, and got an error.
- ///
- /// should handle the json str that cause the serialize error, resolve and reformat it to a compatible content.
- /// </summary>
- /// <param name="incompatibleCtorParamsJsonStr">sample: [{"UITemplateVersion":"1.1","AutoAuthCallingPumps":false,"MaxStackUnpaidTrxPerPump":3}, "2nd string parameter", 3]</param>
- /// <returns>need follow format of json array, sample: [{firstObjectParameter},"2nd string parameter",3]</returns>
- private static List<object> ResolveCtorMetaPartsConfigCompatibility(string incompatibleCtorParamsJsonStr)
- {
- var jsonParams = JsonDocument.Parse(incompatibleCtorParamsJsonStr).RootElement.EnumerateArray().ToArray();
- //sample: "UITemplateVersion":"1.0"
- string uiTemplateVersionRegex = @"(?<=""UITemplateVersion""\:\"").+?(?="")";
- var match = Regex.Match(jsonParams.First().GetRawText(), uiTemplateVersionRegex, RegexOptions.IgnoreCase | RegexOptions.Multiline);
- if (match.Success)
- {
- // the curVersion string has been found, can handle it with proper fallback.
- var curVersion = match.Value;
- if (curVersion == "1.0")
- {
- var existsAppConfigV1 = JsonSerializer.Deserialize(jsonParams.First().GetRawText(), typeof(AppConfigV1));
- // AppConfigV2 appConfigV2 = new AppConfigV2();
- // appConfigV2.aaa = existsAppConfigV1.aaaa;
- // appConfigV2.bbb = existsAppConfigV1.bbbb;
- // ...
- // return new List<object>(){appConfigV2};
- }
- //else if(curVersion == "2.0")
- //{
- // var existsAppConfigV2 = JsonSerializer.Deserialize(jsonParams.First().GetRawText(), typeof(AppConfigV2));
- // AppConfigV3 appConfigV3 = new AppConfigV3();
- // appConfigV3.aaa = existsAppConfigV2.aaaa;
- // appConfigV3.bbb = existsAppConfigV2.bbbb;
- // ...
- // return new List<object>(){appConfigV3};
- //}
- }
- else
- {
- // try fallback as best as you can by reading the raw input string,
- // if you have no idea to do any fallback, then return a most common config for let the user can go through easier.
- return new List<object>() {
- new AppConfigV1() {
- AutoAuthCallingPumps = false,
- FdcServerListeningPort = 4711,
- MaxStackUnpaidTrxPerPump = 3 } };
- }
- return new List<object>() {
- new AppConfigV1() {
- AutoAuthCallingPumps = false,
- FdcServerListeningPort = 4711,
- MaxStackUnpaidTrxPerPump = 3 } };
- }
- public Task Test(params object[] parameters)
- {
- if ((!this.fdcServer?.IsStarted) ?? false)
- throw new InvalidOperationException("Fdc server failed to start on a port");
- return Task.CompletedTask;
- }
- [ParamsJsonSchemas("appCtorParamsJsonSchema")]
- public FdcServerHostApp(AppConfigV1 appConfig, IServiceProvider services)
- {
- this.config_AutoAuthCallingPumps = appConfig.AutoAuthCallingPumps;
- this.config_MaxStackUnpaidTrxPerPump = appConfig.MaxStackUnpaidTrxPerPump;
- this.config_ListeningPort = appConfig.FdcServerListeningPort;
- if (appConfig.PurgeClearedTrxOlderThanByDay.HasValue || appConfig.PurgePayableTrxOlderThanByMin.HasValue)
- {
- this.purgeDatabaseTimer = new System.Timers.Timer();
- this.purgeDatabaseTimer.Interval = 1000 * 60;
- this.purgeDatabaseTimer.Elapsed += (s, a) =>
- {
- if (appConfig.PurgeClearedTrxOlderThanByDay.HasValue)
- {
- try
- {
- using (var dbContext = new SqliteDbContext())
- {
- var due = DateTime.Now.Subtract(new TimeSpan(appConfig.PurgeClearedTrxOlderThanByDay.Value, 0, 0, 0));
- var deleting = dbContext.PumpTransactionModels.Where(t => t.State == FuelSaleTransactionState.Cleared && t.PaidTime.HasValue && t.PaidTime <= due);
- dbContext.RemoveRange(deleting);
- var deletedRowCount = dbContext.SaveChanges();
- if (deletedRowCount > 0)
- fdcLogger.LogDebug($"PurgeClearedTrxOlderThanByDay purged: {deletedRowCount} rows");
- }
- }
- catch (Exception exxx)
- {
- fdcLogger.LogError("PurgeClearedTrxOlderThanByDay exceptioned: " + exxx.ToString());
- }
- try
- {
- if (appConfig.PurgePayableTrxOlderThanByMin.HasValue)
- {
- using (var dbContext = new SqliteDbContext())
- {
- var due = DateTime.Now.Subtract(new TimeSpan(0, appConfig.PurgePayableTrxOlderThanByMin.Value, 0));
- var deleting = dbContext.PumpTransactionModels.Where(t => t.State == FuelSaleTransactionState.Payable && t.SaleStartTime.HasValue && t.SaleStartTime <= due);
- dbContext.RemoveRange(deleting);
- var deletedRowCount = dbContext.SaveChanges();
- if (deletedRowCount > 0)
- fdcLogger.LogDebug($"PurgePayableTrxOlderThanByMin purged: {deletedRowCount} rows");
- }
- }
- }
- catch (Exception exxx)
- {
- fdcLogger.LogError("PurgePayableTrxOlderThanByMin exceptioned: " + exxx.ToString());
- }
- }
- };
- this.purgeDatabaseTimer.Start();
- }
- this.services = services;
- var loggerFactory = services.GetRequiredService<ILoggerFactory>();
- fdcLogger = loggerFactory.CreateLogger("DynamicPrivate_FdcServer");
- cloudLogger = loggerFactory.CreateLogger("CloudRestClient");
- //this.configurator = services.GetService<Configurator>();
- this.nozzleExtraInfos = appConfig.PumpConfigs
- ?.SelectMany(p => p.NozzleConfigs,
- (p, ns) =>
- new NozzleExtraInfo()
- {
- PumpId = p.PumpId,
- NozzleLogicalId = ns.NozzleLogicalId,
- SiteLevelNozzleId = ns.SiteLevelNozzleId,
- ProductBarcode = ns.ProductCode,
- ProductName = appConfig.ProductConfigs?.FirstOrDefault(p => p.ProductCode == ns.ProductCode)?.ProductName,
- TankNumber = ns.TankNumber,
- Description = ns.Description,
- });
- FdcResourceArbitrator.fdcLogger = fdcLogger;
- this.fdcServer = new FDCPOSInterfaceServer(services);
- }
- public FdcServerHostApp(string autoAuthCallingPumps, int maxStackUnpaidTrxPerPump, int listeningPort
- , IServiceProvider services)
- {
- //QRCoder.AsciiQRCode c = new QRCoder.AsciiQRCode();
- this.config_AutoAuthCallingPumps = autoAuthCallingPumps.ToLower() == "true" ? true : false;
- this.config_MaxStackUnpaidTrxPerPump = maxStackUnpaidTrxPerPump;
- this.config_ListeningPort = listeningPort;
- this.services = services;
- var loggerFactory = services.GetRequiredService<ILoggerFactory>();
- fdcLogger = loggerFactory.CreateLogger("DynamicPrivate_FdcServer");
- cloudLogger = loggerFactory.CreateLogger("CloudRestClient");
- this.configurator = services.GetService<Configurator>();
- this.nozzleExtraInfos = Configurator.Default.NozzleExtraInfoConfiguration.Mapping;
- FdcResourceArbitrator.fdcLogger = fdcLogger;
- this.fdcServer = new FDCPOSInterfaceServer(services);
- this.onConfiguratorConfigFileChangedEventHandler = (_, __) =>
- {
- try
- {
- var c = Configurator.Default.DeviceProcessorConfiguration.Processor
- .FirstOrDefault(p => p.Name == this.MetaConfigName);
- if (c == null) return;
- var newAutoAuthCallingPumps = c.Parameter.First(p => p.Name == "AutoAuthCallingPumps").Value.ToLower() == "true" ? true : false;
- var newMaxStackUnpaidTrxPerPump = int.Parse(c.Parameter.First(p => p.Name == "MaxStackUnpaidTrxPerPump").Value);
- if (this.config_AutoAuthCallingPumps != newAutoAuthCallingPumps)
- {
- fdcLogger.LogInformation("Configuration change detected on parameter: autoAuthCallingPumps, " +
- "will update its value from: " + this.config_AutoAuthCallingPumps + " to: " + newAutoAuthCallingPumps);
- this.config_AutoAuthCallingPumps = newAutoAuthCallingPumps;
- }
- if (this.config_MaxStackUnpaidTrxPerPump != newMaxStackUnpaidTrxPerPump)
- {
- fdcLogger.LogInformation("Configuration change detected on parameter: maxStackUnpaidTrxPerPump, " +
- "will update its value from: " + this.config_MaxStackUnpaidTrxPerPump + " to: " + newMaxStackUnpaidTrxPerPump);
- this.config_MaxStackUnpaidTrxPerPump = newMaxStackUnpaidTrxPerPump;
- }
- }
- catch (Exception exxx)
- {
- fdcLogger.LogError("Configuration auto reload exceptioned: " + exxx);
- }
- };
- this.configurator.OnConfigFileChanged += this.onConfiguratorConfigFileChangedEventHandler;
- }
- public void Init(IEnumerable<IProcessor> processors)
- {
- this.fdcPumpControllers = new List<IFdcPumpController>();
- this.fdcPumpControllers.AddRange(
- processors.WithHandlerOrApp<IFdcPumpController>().SelectHandlerOrAppThenCast<IFdcPumpController>());
- foreach (var p in processors)
- {
- if (p.IsWithHandlerOrApp<IEnumerable<IFdcPumpController>>())
- {
- var pumpGroupHandler = p.SelectHandlerOrAppThenCast<IEnumerable<IFdcPumpController>>();
- this.fdcPumpControllers.AddRange(pumpGroupHandler);
- // dynamic check does `OnFdcServerInit` defined
- if (pumpGroupHandler.GetType().GetMethod("OnFdcServerInit")?.GetParameters()
- ?.FirstOrDefault()?.ParameterType?.IsAssignableFrom(typeof(Dictionary<string, object>)) ?? false)
- {
- pumpGroupHandler.GetType().GetMethod("OnFdcServerInit")?.Invoke(
- pumpGroupHandler,
- new[]{
- new Dictionary<string, object>() {
- { "NozzleProductMapping", this.nozzleExtraInfos }
- }
- });
- }
- }
- }
- if (this.fdcPumpControllers.GroupBy(p => p.PumpId).Any(g => g.Count() > 1))
- throw new ArgumentException("Duplicate PumpId in Fdc Pump Controllers, PumpId must be unique");
- this.fdcCommunicableControllers = new List<IFdcCommunicableController>();
- this.fdcCommunicableControllers.AddRange(processors.WithHandlerOrApp<IFdcCommunicableController>().SelectHandlerOrAppThenCast<IFdcCommunicableController>());
- this.autoTankGaugeControllers = new List<IAutoTankGaugeController>();
- this.autoTankGaugeControllers.AddRange(processors.WithHandlerOrApp<IAutoTankGaugeController>().SelectHandlerOrAppThenCast<IAutoTankGaugeController>());
- }
- public class Grouping<TKey, TElement> : List<TElement>, IGrouping<TKey, TElement>
- {
- public Grouping(TKey key) : base() => Key = key;
- public Grouping(TKey key, int capacity) : base(capacity) => Key = key;
- public Grouping(TKey key, IEnumerable<TElement> collection)
- : base(collection) => Key = key;
- public TKey Key { get; private set; }
- }
- public Task<bool> Start()
- {
- //var fdcServer = new FDCPOSInterfaceServer();
- #region OnGetCountrySettings, OnLogOnReq and OnLogOffReq
- fdcServer.OnGetCountrySettingsReq += (string workstationID, string applicationSender, int requestID) =>
- {
- fdcLogger.LogDebug("OnGetCountrySettingsReq (wid: " + workstationID + ", appSender: " + applicationSender + ", requestId: " + requestID + ")");
- fdcServer.GetCountrySettings(workstationID, applicationSender, requestID, "Litre", "CNY", "Litre", "C", "3", ".", "CHN", "zh-cn", (int)ErrorCode.ERRCD_OK, OverallResult.Success.ToString());
- };
- fdcServer.OnStartForecourtReq += (string workstationID, string applicationSender, int requestID) =>
- {
- fdcLogger.LogDebug("OnStartForecourtReq (wid: " + workstationID + ", appSender: " + applicationSender + ", requestId: " + requestID + ")");
- fdcServer.StartForecourt(workstationID, applicationSender, requestID, (int)ErrorCode.ERRCD_OK, OverallResult.Success.ToString());
- };
- fdcServer.OnLogOnReq += (string workstationID, string applicationSender, int requestId, int responsePort, int unsolicitedPort, int version) =>
- {
- fdcLogger.LogInformation("OnLogOnReq(wid: " + workstationID + ", appSender: " + applicationSender + ", requestId: " + requestId + ", ver: " + version + ")");
- fdcServer.LogOn(workstationID, applicationSender, requestId, responsePort, unsolicitedPort, (int)ErrorCode.ERRCD_OK, OverallResult.Success.ToString());
- };
- fdcServer.OnLogOffReq += (string workstationID, string applicationSender, int requestId) =>
- {
- fdcLogger.LogInformation("OnLogOffReq(wid: " + workstationID + ", appSender: " + applicationSender + ", requestId: " + requestId + ")");
- fdcServer.LogOff(workstationID, applicationSender, requestId, (int)ErrorCode.ERRCD_OK, OverallResult.Success.ToString());
- };
- #endregion
- #region reserve related
- fdcServer.OnLockNozzleReq += async (string workstationID, string applicationSender, int requestId, int deviceId, int NozzleNo) =>
- {
- try
- {
- fdcLogger.LogDebug("OnLockNozzleReq (wid: " + workstationID + ", appSender: "
- + applicationSender + ", requestId: " + requestId
- + ", deviceId: " + deviceId + ", NozzleNo: " + NozzleNo);
- var result = await this.LockNozzleAndNotifyAllFdcClientsAsync(int.Parse(workstationID), applicationSender, requestId, deviceId, NozzleNo);
- if (!result)
- fdcLogger.LogInformation(" LockNozzle failed");
- fdcLogger.LogDebug(" OnLockNozzleReq done");
- }
- catch (Exception exxx)
- {
- fdcLogger.LogError("OnLockNozzleReq exceptioned: " + exxx.ToString());
- }
- };
- fdcServer.OnUnlockNozzleReq += async (string workstationID, string applicationSender, int requestId, int deviceId, int NozzleNo) =>
- {
- try
- {
- fdcLogger.LogDebug("OnUnlockNozzleReq (wid: " + workstationID + ", appSender: "
- + applicationSender + ", requestId: " + requestId
- + ", deviceId: " + deviceId + ", NozzleNo: " + NozzleNo);
- var result = await this.UnlockNozzleAndNotifyAllFdcClientsAsync(int.Parse(workstationID), applicationSender, requestId, deviceId, NozzleNo);
- //if (!result)
- // fdcServer.UnlockNozzle(workstationID, applicationSender, requestId, deviceId, deviceId, NozzleNo, (int)ErrorCode.ERRCD_OK, OverallResult.Success.ToString());
- //else
- // fdcServer.UnlockNozzle(workstationID, applicationSender, requestId, deviceId, deviceId, NozzleNo, (int)ErrorCode.ERRCD_OK, OverallResult.Failure.ToString());
- fdcLogger.LogDebug(" OnUnlockNozzleReq done");
- }
- catch (Exception exxx)
- {
- fdcLogger.LogError("OnUnlockNozzleReq exceptioned: " + exxx.ToString());
- }
- };
- fdcServer.OnReserveFuelPointReq += async (string workstationID, string applicationSender, int requestId, int deviceId) =>
- {
- try
- {
- fdcLogger.LogDebug("OnReserveFuelPointReq (wid: " + workstationID + ", appSender: " + applicationSender + ", requestId: " + requestId + ", deviceId: " + deviceId);
- var result = await FdcResourceArbitrator.Default.TryReserveFuelPointAsync(int.Parse(workstationID), deviceId);
- if (result)
- fdcServer.ReserveFuelPoint(workstationID, applicationSender, requestId, deviceId, deviceId, (int)ErrorCode.ERRCD_OK, OverallResult.Success.ToString());
- else
- {
- fdcLogger.LogInformation(" ReserveFuelPoint failed");
- fdcServer.ReserveFuelPoint(workstationID, applicationSender, requestId, deviceId, deviceId, (int)ErrorCode.ERRCD_FPLOCK, OverallResult.Failure.ToString());
- }
- fdcLogger.LogDebug(" OnReserveFuelPointReq done");
- }
- catch (Exception exxx)
- {
- fdcLogger.LogError("OnReserveFuelPointReq exceptioned: " + exxx.ToString());
- fdcServer.ReserveFuelPoint(workstationID, applicationSender, requestId, deviceId, deviceId, (int)ErrorCode.ERRCD_OK, OverallResult.Failure.ToString());
- }
- };
- fdcServer.OnFreeFuelPointReq += async (string workstationID, string applicationSender, int requestId, int deviceId) =>
- {
- try
- {
- fdcLogger.LogDebug("OnFreeFuelPointReq (wid: " + workstationID + ", appSender: " + applicationSender + ", requestId: " + requestId + ", deviceId: " + deviceId);
- var result = await FdcResourceArbitrator.Default.TryUnreserveFuelPointAsync(int.Parse(workstationID), deviceId);
- if (result)
- fdcServer.FreeFuelPoint(workstationID, applicationSender, requestId, deviceId, deviceId, (int)ErrorCode.ERRCD_OK, OverallResult.Success.ToString());
- else
- fdcServer.FreeFuelPoint(workstationID, applicationSender, requestId, deviceId, deviceId, (int)ErrorCode.ERRCD_OK, OverallResult.Failure.ToString());
- fdcLogger.LogDebug(" OnFreeFuelPointReq done");
- }
- catch (Exception exxx)
- {
- fdcLogger.LogError("OnFreeFuelPointReq exceptioned: " + exxx.ToString());
- fdcServer.FreeFuelPoint(workstationID, applicationSender, requestId, deviceId, deviceId, (int)ErrorCode.ERRCD_NOTPOSSIBLE, OverallResult.Failure.ToString());
- }
- };
- fdcServer.OnLockFuelSaleTrxReq += async (string workstationID, string applicationSender, int requestId, int deviceId,
- int transactionNo, string releaseToken) =>
- {
- try
- {
- fdcLogger.LogDebug("OnLockFuelSaleTrxReq (wid: " + workstationID + ", appSender: " + applicationSender + ", requestId: "
- + requestId + ", deviceId: " + deviceId + ", transactionNo: " + transactionNo + ", releaseToken: " + releaseToken + ")");
- var result = await this.LockFuelSaleTrxAndNotifyAllFdcClientsAsync(int.Parse(workstationID), applicationSender, requestId, deviceId, transactionNo, int.Parse(releaseToken));
- //var result = this.LockFuelSaleTrxAndNotifyAllFdcClients(int.Parse(workstationID), deviceId, transactionNo, int.Parse(releaseToken));
- //if (result != null)
- //{
- // var targetController = fdcPumpControllers
- // .First(c => c.PumpId == deviceId) as IFdcPumpController;
- // fdcServer.LockFuelSaleTrx(workstationID, applicationSender, requestId, deviceId,
- // transactionNo, releaseToken, (int)ErrorCode.ERRCD_OK, 1, OverallResult.Success.ToString());
- // fdcServer.FuelSaleTrx(deviceId,
- // result.Volumn / Math.Pow(10, targetController.VolumeDecimalDigits),
- // result.Amount / Math.Pow(10, targetController.AmountDecimalDigits),
- // result.UnitPrice / Math.Pow(10, targetController.PriceDecimalDigits),
- // result.LogicalNozzleId,
- // int.Parse(result.ProductBarcode),
- // "refer cloud" + result.ProductBarcode, "", 1,
- // int.Parse(result.TransactionSeqNumberFromPhysicalPump),
- // (int)FuelSaleTransactionState.Locked,
- // 0, releaseToken,
- // result.SaleStartTime?.ToString("yyyy-MM-dd HH:mm:ss"),
- // result.SaleEndTime?.ToString("yyyy-MM-dd HH:mm:ss"),
- // workstationID, "", int.Parse(workstationID), 0);
- //}
- //else
- //{
- // fdcServer.LockFuelSaleTrx(workstationID, applicationSender, requestId, deviceId,
- // transactionNo, releaseToken, (int)ErrorCode.ERRCD_TRANSLOCKED, 1, OverallResult.Failure.ToString());
- //}
- if (result == null)
- fdcLogger.LogDebug($" OnLockFuelSaleTrxReq(releaseToken: {releaseToken}) failed");
- fdcLogger.LogDebug($" OnLockFuelSaleTrxReq(releaseToken: {releaseToken}) done");
- }
- catch (Exception exxx)
- {
- fdcLogger.LogError("OnLockFuelSaleTrxReq exceptioned: " + exxx.ToString());
- try
- {
- fdcServer.LockFuelSaleTrx(workstationID, applicationSender, requestId, deviceId,
- transactionNo, releaseToken, (int)ErrorCode.ERRCD_TRANSLOCKED, 1, OverallResult.Failure.ToString());
- }
- catch (Exception eeee)
- {
- fdcLogger.LogError("Fdc server is trying to respond a failed LocFuelSaleTrx response to a fdcclient that issue current LockFuleSaleTrx request, but still failed with reason: " + eeee);
- }
- }
- };
- fdcServer.OnUnlockFuelSaleTrxReq += async (string workstationID, string applicationSender, int requestId, int deviceId,
- int transactionNo, string releaseToken) =>
- {
- try
- {
- fdcLogger.LogDebug("OnUnlockFuelSaleTrxReq (wid: " + workstationID + ", appSender: " + applicationSender
- + ", requestId: " + requestId + ", deviceId: " + deviceId + ", transactionNo: " + transactionNo
- + ", releaseToken: " + releaseToken);
- var result = await this.UnlockFuelSaleTrxAndNotifyAllFdcClientsAsync(int.Parse(workstationID), applicationSender, requestId, deviceId, transactionNo, int.Parse(releaseToken));
- //var result = this.UnlockFuelSaleTrxAndNotifyAllFdcClients(int.Parse(workstationID), deviceId, transactionNo, int.Parse(releaseToken));
- //if (result != null)
- //{
- // var targetController = fdcPumpControllers
- // .First(c => c.PumpId == deviceId) as IFdcPumpController;
- // fdcServer.UnlockFuelSaleTrx(workstationID, applicationSender, requestId, deviceId,
- // transactionNo, releaseToken, (int)ErrorCode.ERRCD_OK, 1, OverallResult.Success.ToString());
- // fdcServer.FuelSaleTrx(deviceId,
- // result.Volumn / Math.Pow(10, targetController.VolumeDecimalDigits),
- // result.Amount / Math.Pow(10, targetController.AmountDecimalDigits),
- // result.UnitPrice / Math.Pow(10, targetController.PriceDecimalDigits),
- // result.LogicalNozzleId,
- // int.Parse(result.ProductBarcode),
- // "refer cloud" + result.ProductBarcode, "", 1,
- // int.Parse(result.TransactionSeqNumberFromPhysicalPump),
- // (int)FuelSaleTransactionState.Payable,
- // 0, releaseToken,
- // result.SaleStartTime?.ToString("yyyy-MM-dd HH:mm:ss"),
- // result.SaleEndTime?.ToString("yyyy-MM-dd HH:mm:ss"),
- // "", "", int.Parse(workstationID), 0);
- //}
- //else
- //{
- // fdcServer.UnlockFuelSaleTrx(workstationID, applicationSender, requestId, deviceId,
- // transactionNo, releaseToken, (int)ErrorCode.ERRCD_TRANSLOCKED, 1, OverallResult.Failure.ToString());
- //}
- if (result == null)
- fdcLogger.LogDebug(" OnUnlockFuelSaleTrxReq failed");
- fdcLogger.LogDebug(" OnUnlockFuelSaleTrxReq done");
- }
- catch (Exception exxx)
- {
- fdcLogger.LogError("OnUnlockFuelSaleTrxReq exceptioned: " + exxx.ToString() + Environment.NewLine);
- try
- {
- fdcServer.UnlockFuelSaleTrx(workstationID, applicationSender, requestId, deviceId,
- transactionNo, releaseToken, (int)ErrorCode.ERRCD_TRANSLOCKED, 1, OverallResult.Failure.ToString());
- }
- catch (Exception eeee)
- {
- fdcLogger.LogError("Fdc server is trying to respond a failed UnlocFuelSaleTrx response to a fdcclient that issue current UnlockFuleSaleTrx request, but still failed with reason: " + eeee);
- }
- }
- };
- #endregion
- fdcServer.OnGetTankDataReq += async (string workstationID, string applicationSender, int requestID, int deviceId, int tankNo) =>
- {
- try
- {
- fdcLogger.LogDebug("OnGetTankDataReq (wid: " + workstationID + ", appSender: " + applicationSender + ", requestId: " + requestID + ", deviceId: " + deviceId + ", tankNo: " + tankNo + ")");
- if (this.autoTankGaugeControllers == null || !this.autoTankGaugeControllers.Any())
- {
- fdcLogger.LogDebug($" failed for OnGetTankDataReq since there's an empty AutoTankGaugeControllers list.");
- fdcServer.GetTankDataSend(workstationID, applicationSender, requestID,
- (int)ErrorCode.ERRCD_NOTPOSSIBLE, OverallResult.DeviceUnavailable.ToString());
- return;
- }
- var atgController = this.autoTankGaugeControllers.FirstOrDefault();
- if (!atgController.Tanks.Any())
- {
- fdcLogger.LogDebug($" failed for OnGetTankDataReq since there's no Tanks at all in AutoTankGaugeController.");
- fdcServer.GetTankDataSend(workstationID, applicationSender, requestID,
- (int)ErrorCode.ERRCD_NOTPOSSIBLE, OverallResult.DeviceUnavailable.ToString());
- return;
- }
- if (deviceId != -1
- && !atgController.Tanks.Any(t => t.TankNumber == deviceId))
- {
- fdcLogger.LogDebug($" failed for OnGetTankDataReq since there's no Tank with number: {deviceId} in AutoTankGaugeController.");
- fdcServer.GetTankDataSend(workstationID, applicationSender, requestID,
- (int)ErrorCode.ERRCD_NOTPOSSIBLE, OverallResult.DeviceUnavailable.ToString());
- return;
- }
- //fdcLogger.LogDebug($" atgController has {atgController.Tanks?.Count() ?? -1} tanks configed.");
- if (deviceId == -1)
- {
- foreach (var tank in atgController.Tanks)
- {
- try
- {
- var tankReading = await atgController.GetTankReadingAsync(tank.TankNumber);
- fdcLogger.LogDebug($" GetTankReadingAsync for tankNumber: {tank.TankNumber} succeed, data-> { tankReading.ToString()}");
- fdcServer.GetTankDataAdd(workstationID, applicationSender, requestID,
- tank.TankNumber,
- tank.TankNumber, 0, 0,
- tankReading.Height ?? 0,
- tankReading.Volume ?? 0,
- tankReading.TcVolume ?? 0,
- tankReading.Temperature ?? 0,
- tankReading.Water ?? 0,
- 0,
- (int)ErrorCode.ERRCD_OK, (int)LogicalDeviceState.FDC_READY);
- }
- catch (Exception exx)
- {
- fdcLogger.LogInformation($" failed for GetTankReadingAsync for tankNumber: {tank.TankNumber} by exception: {exx}" +
- $"{Environment.NewLine} will skip this tank and continue on next tank...");
- continue;
- }
- }
- }
- else
- {
- var tankReading = await atgController.GetTankReadingAsync(deviceId);
- fdcLogger.LogDebug($" GetTankReadingAsync for tankNumber: " + deviceId + " succeed " +
- Environment.NewLine + tankReading.ToString());
- fdcServer.GetTankDataAdd(workstationID, applicationSender, requestID,
- deviceId,
- deviceId, 0, 0,
- tankReading.Height ?? 0,
- tankReading.Volume ?? 0,
- tankReading.TcVolume ?? 0,
- tankReading.Temperature ?? 0,
- tankReading.Water ?? 0,
- 0,
- (int)ErrorCode.ERRCD_OK, (int)LogicalDeviceState.FDC_READY);
- }
- fdcServer.GetTankDataSend(workstationID, applicationSender, requestID, (int)ErrorCode.ERRCD_OK, OverallResult.Success.ToString());
- }
- catch (Exception exxx)
- {
- fdcLogger.LogError("OnGetTankDataReq exceptioned: " + exxx.ToString());
- fdcServer.GetTankDataSend(workstationID, applicationSender, requestID,
- (int)ErrorCode.ERRCD_BADVAL, OverallResult.Failure.ToString());
- }
- };
- fdcServer.OnGetProductTableReq += (string workstationID, string applicationSender, int requestID) =>
- {
- fdcLogger.LogDebug("OnGetProductTableReq (wid: " + workstationID + ", appSender: " + applicationSender + ", requestId: " + requestID + ")");
- var allProductBarcodes = nozzleExtraInfos.GroupBy(p => p.ProductBarcode);
- foreach (var b in allProductBarcodes)
- fdcServer.GetProductTableAdd(workstationID, applicationSender, requestID, b.Key, (b.FirstOrDefault()?.ProductName ?? ("refer cloud for name of " + b.Key)));
- fdcServer.GetProductTableSend(workstationID, applicationSender, requestID, (int)ErrorCode.ERRCD_OK, OverallResult.Success.ToString());
- };
- fdcServer.OnGetConfigurationReq += (string workstationID, string applicationSender, int requestId, string deviceType) =>
- {
- lock (this.syncObject)
- {
- try
- {
- fdcLogger.LogDebug("OnGetConfigurationReq (wid: " + workstationID + ", appSender: " + applicationSender + ", requestId: " + requestId + ", deviceType: " + deviceType);
- if (deviceType == "TLG")
- {
- /* here based on local manual config to generate tanks and return to POS side, probly use the concrete AutoTankGaugeControl.Tanks
- * is a better choice but the known issue is the product code and label read from VR ATG console may not correct(may bug in VR console).
- * so keep this way of local manual config with high priority.
- */
- var tankGroups = this.nozzleExtraInfos.Where(np => np.TankNumber.HasValue)
- .GroupBy(np => np.TankNumber)
- .Select(tg =>
- {
- /* if real ATG exists, will use ATG Tank info, otherwise create a logical Tank object but fill with info from nozzleExtraInfo*/
- var tank = this.autoTankGaugeControllers?.SelectMany(c => c.Tanks ?? new Tank[] { })?.FirstOrDefault(t => t.TankNumber == tg.Key)
- ?? new Tank()
- {
- TankNumber = (byte)tg.Key.Value,
- Label = "Logical tank",
- Product = new Product()
- {
- ProductCode = tg.First().ProductBarcode.ToString(),
- ProductLabel = tg.First().Description
- }
- };
- var pumpGroups = tg.GroupBy(np => np.PumpId).Select(pg => new Grouping<int, byte>(pg.Key, pg.Select(x => (byte)x.NozzleLogicalId)));
- return new Grouping<Tank, Grouping<int, byte>>(tank, pumpGroups);
- });
- if (!tankGroups.Any())
- {
- fdcServer.GetConfigurationSend(workstationID, applicationSender, requestId,
- deviceType, OverallResult.WrongConfiguration.ToString());
- fdcLogger.LogDebug(" OnGetConfigurationReq with TLG done with WrongConfiguration as no tanks info generated.");
- return;
- }
- fdcLogger.LogDebug(" OnGetConfigurationReq with TLG done with success, returned data-> " +
- tankGroups.Select(tg => $"Tank with number: {tg.Key.TankNumber}, " +
- $"label: {tg.Key.Label ?? ""}, productCode: {tg.Key.Product?.ProductCode ?? ""} linked to PumpIds: " +
- $"{tg.Select(p => $"{p.Key}(nzlLogiIds: {p.Select(n => n.ToString()).Aggregate("", (acc, n) => acc + ", " + n)})").Aggregate("", (acc, n) => acc + ", " + n)}")
- .Aggregate((acc, n) => acc + "; " + n));
- fdcServer.GetConfigurationAddTLG(workstationID, applicationSender, requestId, tankGroups);
- }
- else
- {
- foreach (var fdcPumpController in fdcPumpControllers)
- {
- foreach (var nozzle in fdcPumpController.Nozzles)
- {
- var bindProduct = nozzleExtraInfos.FirstOrDefault(n =>
- n.PumpId == fdcPumpController.PumpId
- && n.NozzleLogicalId == nozzle.LogicalId);
- var fuelPrice = nozzle.RealPriceOnPhysicalPump == null ? 0 :
- (nozzle.RealPriceOnPhysicalPump.Value / Math.Pow(10, fdcPumpController.PriceDecimalDigits));
- if (string.IsNullOrEmpty(deviceType) || deviceType == "DSP")
- {
- int bindProductNo = (bindProduct == null ? 0 : bindProduct.ProductBarcode);
- if (bindProduct == null)
- fdcLogger.LogInformation("Could not find bind product for pumpId: " + fdcPumpController.PumpId + ", nozzleId: " + nozzle.LogicalId + ", will use 0 instead");
- fdcLogger.LogDebug(" OnGetConfigurationReq pumpId: "
- + fdcPumpController.PumpId
- + ", nozzle logicalId: " + nozzle.LogicalId + ", nozzle phyId: " + nozzle.PhysicalId + ", productNo: " + bindProductNo
- + ", price: " + fuelPrice);
- fdcServer.GetConfigurationAddDSP(workstationID, applicationSender, requestId,
- fdcPumpController.PumpId,
- bindProductNo,
- bindProduct?.ProductName ?? ("refer cloud for name of " + bindProductNo),
- 1,
- fuelPrice);
- fdcServer.GetConfigurationAddFP(workstationID, applicationSender, requestId,
- fdcPumpController.PumpId,
- fdcPumpController.PumpId, nozzle.LogicalId, (bindProduct == null ? 0 : bindProduct.ProductBarcode), 0, 100, 0);
- }
- //if (string.IsNullOrEmpty(deviceType) || deviceType == "PP")
- // fdcServer.GetConfigurationAddPP(workstationID, applicationSender, requestId, fdcPumpController.PumpId);
- }
- }
- }
- fdcServer.GetConfigurationSend(workstationID, applicationSender, requestId,
- deviceType, OverallResult.Success.ToString());
- fdcLogger.LogDebug(" OnGetConfigurationReq done");
- }
- catch (Exception exxx)
- {
- fdcLogger.LogError("OnGetConfigurationReq exceptioned: " + exxx.ToString());
- fdcServer.GetConfigurationSend(workstationID, applicationSender, requestId, deviceType, OverallResult.Failure.ToString());
- }
- }
- };
- fdcServer.OnGetDeviceStateReq += async (string workstationID, string applicationSender, int requestId, string deviceType, int deviceId) =>
- {
- try
- {
- fdcLogger.LogDebug("OnGetDeviceStateReq (wid: " + workstationID + ", appSender: " + applicationSender + ", requestId: " + requestId + ", deviceId: " + deviceId + ")");
- // -1 indicates query all pumps
- if (deviceId == -1)
- {
- var controllers = fdcPumpControllers;
- foreach (var c in controllers)
- {
- var s = await c.QueryStatusAsync();
- fdcLogger.LogDebug(" Pump with pumpId: " + c.PumpId + " is in state: " + s.ToString()
- + ", nozzles states are(LogicalId-State): "
- + (c.Nozzles != null ?
- (c.Nozzles.Any() ?
- c.Nozzles.Select(n =>
- n.LogicalId.ToString() + "-" + (n.LogicalState?.ToString() ?? ""))
- .Aggregate((n, acc) => n + ", " + acc) : "") : ""));
- byte nozzleLockedOrUnlockedBitMap = 0;
- foreach (var nozzle in c.Nozzles)
- {
- if (nozzle.LogicalState.HasValue && nozzle.LogicalState == LogicalDeviceState.FDC_LOCKED)
- nozzleLockedOrUnlockedBitMap = SetBit(nozzleLockedOrUnlockedBitMap, nozzle.LogicalId - 1, nozzle.LogicalId, 1);
- else
- nozzleLockedOrUnlockedBitMap = SetBit(nozzleLockedOrUnlockedBitMap, nozzle.LogicalId - 1, nozzle.LogicalId, 0);
- }
- fdcServer.GetDeviceStateAdd(workstationID, applicationSender, requestId, deviceType, c.PumpId, (int)s,
- -1, "", "", (int)FDCLogicalState.FDC_LOGICAL_ST_UNLOCKED, "",
- c.Nozzles.Count(), 0, nozzleLockedOrUnlockedBitMap, 0);
- }
- }
- else
- {
- var targetController = fdcPumpControllers.First(c => c.PumpId == deviceId) as IFdcPumpController;
- byte nozzleLockedOrUnlockedBitMap = 0;
- foreach (var nozzle in targetController.Nozzles)
- {
- if (nozzle.LogicalState.HasValue && nozzle.LogicalState == LogicalDeviceState.FDC_LOCKED)
- nozzleLockedOrUnlockedBitMap = SetBit(nozzleLockedOrUnlockedBitMap, nozzle.LogicalId - 1, nozzle.LogicalId, 1);
- else
- nozzleLockedOrUnlockedBitMap = SetBit(nozzleLockedOrUnlockedBitMap, nozzle.LogicalId - 1, nozzle.LogicalId, 0);
- }
- fdcServer.GetDeviceStateAdd(workstationID, applicationSender, requestId, deviceType, deviceId,
- (int)targetController.QueryStatusAsync().Result,
- -1, "", "", (int)FDCLogicalState.FDC_LOGICAL_ST_UNLOCKED, "",
- targetController.Nozzles.Count(),
- 0, nozzleLockedOrUnlockedBitMap, 0);
- }
- fdcServer.GetDeviceStateSend(workstationID, applicationSender, requestId, (int)ErrorCode.ERRCD_OK, OverallResult.Success.ToString());
- fdcLogger.LogDebug(" OnGetDeviceStateReq done");
- }
- catch (Exception exxx)
- {
- fdcLogger.LogError("OnGetDeviceStateReq exceptioned: " + exxx.ToString());
- fdcServer.GetDeviceStateSend(workstationID, applicationSender, requestId, (int)ErrorCode.ERRCD_BADCONF, OverallResult.Failure.ToString());
- }
- };
- fdcServer.OnAuthoriseFuelPointReq += async
- (string workstationID, string applicationSender, int requestId, string releaseToken, int fuellingType,
- int deviceId, int reservingDeviceId, double maxTrxAmount, double maxTrxVolume,
- string products, int mode, bool lockFuelSaleTrx, string payType) =>
- {
- //enum DeviceStatus
- //{
- // FDC_ST_NOTHING = -1,
- // FDC_ST_CONFIGURE = 0,
- // FDC_ST_DISABLED = 1,
- // FDC_ST_ERRORSTATE = 2,
- // FDC_ST_FUELLING = 3,
- // FDC_ST_INVALIDSTATE = 4,
- // FDC_ST_LOCKED = 5,
- // FDC_ST_OFFLINE = 6,
- // FDC_ST_OUTOFORDER = 7,
- // FDC_ST_READY = 8,
- // FDC_ST_REQUESTED = 9,
- // FDC_ST_STARTED = 10,
- // FDC_ST_SUSPENDED = 11,
- // FDC_ST_CALLING = 12,
- // FDC_ST_TEST = 13,
- // FDC_ST_SUSPENDED_STARTING = 14, // Added in FDC version 00.05
- // FDC_ST_SUSPENDED_FUELLING = 15,
- // FDC_ST_CLOSED = 16,
- // FDC_ST_AUTHORISED = 17, // Added in FDC version 00.07
- //};
- try
- {
- fdcLogger.LogDebug("OnAuthoriseFuelPointReq(wid: " + workstationID + ", appSender: " + applicationSender
- + ", requestId: " + requestId + ", releaseToken: " + releaseToken
- + ", deviceId: " + deviceId + ", reservingDeviceId: " + reservingDeviceId + ", maxTrxAmount: "
- + maxTrxAmount + ", maxTrxVolume: " + maxTrxVolume + ", products: " + products
- + ", lockFuelSaleTrx: " + lockFuelSaleTrx + ", payType: " + payType + ")");
- //if (targetController.QueryStatus() == LogicalDeviceState.FDC_FUELLING)
- //{
- //fdcLogger.LogDebug(" Pump: " + targetController.PumpId + " is in fueling so re-authorising request will be interpreted as RoundUpByAmount request");
- //var result = targetController.FuelingRoundUpByAmount();
- //fdcLogger.LogDebug(" Pump: " + targetController.PumpId + " RoundUpByAmount returned with: " + result);
- //fdcServer.AuthoriseFuelPoint(workstationID, applicationSender, requestId, -99, releaseToken, deviceId,
- // (int)LogicalDeviceState.FDC_READY, (int)ErrorCode.ERRCD_OK, OverallResult.Success.ToString());
- //return;
- //}
- //if (!string.IsNullOrEmpty(maxStackUnpaidTrxConfig))
- //{
- // SqliteDbContext dbContext = new SqliteDbContext();
- // var unpaidTrxCount = dbContext.PumpTransactionModels.Count(t => t.PumpId == targetController.PumpId && t.State == FuelSaleTransactionState.Payable);
- // if (unpaidTrxCount >= int.Parse(maxStackUnpaidTrxConfig))
- // {
- // fdcLogger.LogInformation(" Authorizing failed due to pump: " + targetController.PumpId + " have: " + unpaidTrxCount + " unpaid trx");
- // fdcServer.AuthoriseFuelPoint(workstationID, applicationSender, requestId, -99, releaseToken, deviceId,
- // (int)LogicalDeviceState.FDC_READY, (int)ErrorCode.ERRCD_MAXSTACKLIMIT, OverallResult.Failure.ToString());
- // return;
- // }
- //}
- var succeed = await this.AuthorizePumpAsync(deviceId, maxTrxAmount, maxTrxVolume);
- if (succeed)
- fdcServer.AuthoriseFuelPoint(workstationID, applicationSender, requestId, -99, releaseToken, deviceId,
- (int)LogicalDeviceState.FDC_READY, (int)ErrorCode.ERRCD_OK, OverallResult.Success.ToString());
- else
- {
- fdcLogger.LogError("Authorising FP(requestId: " + requestId + ", releaseToken: " + releaseToken + ", deviceId: " + deviceId
- + ", reservingDeviceId: " + reservingDeviceId + ", maxTrxAmount: " + maxTrxAmount + ", maxTrxVolume: " + maxTrxVolume
- + ", products: " + products + ", lockFuelSaleTrx: " + lockFuelSaleTrx + ", payType: " + payType + ") failed");
- fdcServer.AuthoriseFuelPoint(workstationID, applicationSender, requestId, -99, releaseToken, deviceId,
- (int)LogicalDeviceState.FDC_READY, (int)ErrorCode.ERRCD_NOPERM, OverallResult.Failure.ToString());
- }
- fdcLogger.LogDebug(" Authorising FP done");
- }
- catch (Exception exxx)
- {
- fdcLogger.LogError("OnAuthoriseFuelPointReq exceptioned: " + exxx.ToString());
- fdcServer.AuthoriseFuelPoint(workstationID, applicationSender, requestId, -99, releaseToken, deviceId,
- (int)LogicalDeviceState.FDC_READY, (int)ErrorCode.ERRCD_NOPERM, OverallResult.Failure.ToString());
- }
- };
- fdcServer.OnTerminateFuellingReq += async (string workstationID, string applicationSender, int requestId, int deviceId) =>
- {
- fdcLogger.LogInformation("OnTerminateFuellingReq(wid: " + workstationID + ", appSender: " + applicationSender
- + ", requestId: " + requestId + ", deviceId: " + deviceId + ")");
- var targetController = fdcPumpControllers.FirstOrDefault(c => c.PumpId == deviceId);
- if (targetController == null)
- {
- fdcServer.TerminateFuellingSend(workstationID, applicationSender, requestId, (int)ErrorCode.ERRCD_BADDEVID, OverallResult.WrongDeviceNo.ToString());
- return;
- }
- try
- {
- /* interpreted as UnAuthorize has no special reason but for a rush request from customer */
- fdcLogger.LogInformation(" OnTerminateFuellingReq will be interpreted as UnAuthorize to pump handler.");
- var result = await targetController.UnAuthorizeAsync(1);
- if (result)
- fdcServer.TerminateFuellingSend(workstationID, applicationSender, requestId, (int)ErrorCode.ERRCD_OK, OverallResult.Success.ToString());
- else
- fdcServer.TerminateFuellingSend(workstationID, applicationSender, requestId, (int)ErrorCode.ERRCD_INOP, OverallResult.Failure.ToString());
- }
- catch (Exception exx)
- {
- fdcLogger.LogError("OnTerminateFuellingReq exceptioned: " + exx);
- fdcServer.TerminateFuellingSend(workstationID, applicationSender, requestId, (int)ErrorCode.ERRCD_NOTPOSSIBLE, OverallResult.Failure.ToString());
- }
- };
- fdcServer.OnGetAvailableFuelSaleTrxsReq += (string workstationID, string applicationSender, int requestId, int deviceId) =>
- {
- // for limit data size, only show latest N days' data.
- int maxReturnDays = 365;
- // for further limit data size, only show latest N count of data.
- int maxReturnDataCount = 1000;
- lock (this.syncObject)
- {
- try
- {
- fdcLogger.LogDebug("OnGetAvailableFuelSaleTrxsReq (wid: " + workstationID + ", appSender: " + applicationSender + ", requestId: " + requestId + ", deviceId: " + deviceId);
- var dueDate = DateTime.Now.Subtract(new TimeSpan(maxReturnDays, 0, 0, 0));
- int totalReturnTrxCount = 0;
- SqliteDbContext dbContext = new SqliteDbContext();
- if (deviceId == -1)
- {
- var all = dbContext.PumpTransactionModels.Where(t => t.State != FuelSaleTransactionState.Paid
- && t.State != FuelSaleTransactionState.Cleared
- && t.SaleStartTime >= dueDate)
- .Select(t => new { t.PumpId, t.TransactionSeqNumberFromPhysicalPump, t.State, t.SaleStartTime, t.ReleaseToken })
- .OrderByDescending(r => r.SaleStartTime).Take(maxReturnDataCount);
- foreach (var unpaidTrx in all)
- {
- // only show the trx in active pump list.
- if (!fdcPumpControllers.Select(p => p.PumpId).Any(p => p == unpaidTrx.PumpId))
- continue;
- totalReturnTrxCount++;
- fdcServer.GetAvailableFuelSaleTrxsAdd(workstationID, applicationSender, requestId,
- unpaidTrx.PumpId,
- int.Parse(unpaidTrx.TransactionSeqNumberFromPhysicalPump),
- unpaidTrx.ReleaseToken.ToString(),
- (int)unpaidTrx.State);
- }
- }
- else
- {
- var all = dbContext.PumpTransactionModels.Where(t => t.PumpId == deviceId
- && t.State != FuelSaleTransactionState.Paid
- && t.State != FuelSaleTransactionState.Cleared
- && t.SaleStartTime >= dueDate)
- .Select(t => new { t.PumpId, t.TransactionSeqNumberFromPhysicalPump, t.State, t.SaleStartTime, t.ReleaseToken })
- .OrderByDescending(r => r.SaleStartTime).Take(maxReturnDataCount);
- foreach (var unpaidTrx in all)
- {
- // only show the trx in active pump list.
- if (!fdcPumpControllers.Select(p => p.PumpId).Any(p => p == unpaidTrx.PumpId))
- continue;
- totalReturnTrxCount++;
- fdcServer.GetAvailableFuelSaleTrxsAdd(workstationID, applicationSender, requestId,
- unpaidTrx.PumpId,
- int.Parse(unpaidTrx.TransactionSeqNumberFromPhysicalPump),
- unpaidTrx.ReleaseToken.ToString(),
- (int)unpaidTrx.State);
- }
- }
- fdcServer.GetAvailableFuelSaleTrxsSend(workstationID, applicationSender, requestId,
- (int)ErrorCode.ERRCD_OK, (totalReturnTrxCount > 0 ? OverallResult.Success.ToString() : OverallResult.NoData.ToString()));
- fdcLogger.LogDebug(" OnGetAvailableFuelSaleTrxsReq done (total count: " + totalReturnTrxCount + ")");
- }
- catch (Exception exxx)
- {
- fdcLogger.LogError("OnGetAvailableFuelSaleTrxsReq exceptioned: " + exxx.ToString());
- fdcServer.GetAvailableFuelSaleTrxsSend(workstationID, applicationSender, requestId,
- (int)ErrorCode.ERRCD_BADCONF, OverallResult.Failure.ToString());
- }
- }
- };
- fdcServer.OnGetFuelSaleTrxDetailsReq += (string workstationID, string applicationSender, int requestId, int deviceId, int transactionNo, string releaseToken) =>
- {
- lock (this.syncObject)
- {
- try
- {
- /* Fdc client may send wildchar `*` in transactionNo which will be interpreted as -1 here, that means query all trx */
- fdcLogger.LogDebug("OnGetFuelSaleTrxDetailsReq (wid: " + workstationID
- + ", appSender: " + applicationSender + ", requestId: " + requestId
- + ", deviceId: " + deviceId + ", transactionNo: " + transactionNo + ", releaseToken: " + releaseToken);
- int databaseId = int.Parse(releaseToken);
- SqliteDbContext dbContext = new SqliteDbContext();
- List<Edge.Core.Database.Models.FuelSaleTransaction> target;
- if (transactionNo == -1)
- target = dbContext.PumpTransactionModels.Where(t => t.PumpId == deviceId).ToList();
- else
- target = dbContext.PumpTransactionModels.Where(t =>
- t.ReleaseToken == databaseId
- && t.PumpId == deviceId
- && t.TransactionSeqNumberFromPhysicalPump == transactionNo.ToString()).ToList();
- if (target.Any())
- {
- var targetController = fdcPumpControllers
- .First(c => c.PumpId == deviceId) as IFdcPumpController;
- foreach (var trx in target)
- {
- var reservedBy = string.IsNullOrEmpty(trx.LockedByFdcClientId) ?
- -1 : int.Parse(trx.LockedByFdcClientId);
- fdcServer.GetFuelSaleTrxDetailsAdd(workstationID, applicationSender, requestId,
- deviceId,
- trx.Volumn / Math.Pow(10, targetController.VolumeDecimalDigits),
- trx.Amount / Math.Pow(10, targetController.AmountDecimalDigits),
- trx.UnitPrice / Math.Pow(10, targetController.PriceDecimalDigits),
- trx.LogicalNozzleId,
- int.Parse(trx.ProductBarcode),
- "", "", 1,
- int.Parse(trx.TransactionSeqNumberFromPhysicalPump), releaseToken,
- (int)trx.State,
- trx.SaleStartTime?.ToString("yyyy-MM-dd HH:mm:ss"),
- trx.SaleEndTime?.ToString("yyyy-MM-dd HH:mm:ss"),
- trx.LockedByFdcClientId,
- "9999",
- reservedBy, 1);
- }
- fdcServer.GetFuelSaleTrxDetailsSend(workstationID, applicationSender, requestId,
- (int)ErrorCode.ERRCD_OK, OverallResult.Success.ToString());
- }
- else
- {
- fdcLogger.LogInformation(" Trx with releaseToken: " + releaseToken + " is not found");
- fdcServer.GetFuelSaleTrxDetailsSend(workstationID, applicationSender, requestId,
- (int)ErrorCode.ERRCD_OK, OverallResult.NoData.ToString());
- }
- fdcLogger.LogDebug(" OnGetFuelSaleTrxDetailsReq done");
- }
- catch (Exception exxx)
- {
- fdcLogger.LogError("OnGetFuelSaleTrxDetailsReq exceptioned: " + exxx.ToString());
- fdcServer.GetFuelSaleTrxDetailsSend(workstationID, applicationSender, requestId,
- (int)ErrorCode.ERRCD_BADVAL, OverallResult.Failure.ToString());
- }
- }
- };
- fdcServer.OnClearFuelSaleTrxReq += async (string workstationID, string applicationSender, int requestId, int deviceId, int transactionNo, string releaseToken) =>
- {
- try
- {
- fdcLogger.LogDebug("OnClearFuelSaleTrxReq (wid: " + workstationID + ", appSender: " + applicationSender
- + ", requestId: " + requestId + ", deviceId: " + deviceId
- + ", transactionNo: " + transactionNo + ", releaseToken: " + releaseToken + ")");
- var target = await this.ClearFuelSaleTrxAndNotifyAllFdcClientsAsync(deviceId, transactionNo.ToString(), int.Parse(releaseToken), workstationID, applicationSender, requestId);
- if (target == null)
- fdcLogger.LogError("OnClearFuelSaleTrxReq failed(wid: " + workstationID + ", appSender: " + applicationSender
- + ", requestId: " + requestId + ", deviceId: " + deviceId
- + ", transactionNo: " + transactionNo + ", releaseToken: " + releaseToken + ")");
- }
- catch (Exception exxx)
- {
- fdcLogger.LogError("OnClearFuelSaleTrxReq exceptioned: " + exxx.ToString());
- try
- {
- fdcServer.ClearFuelSaleTrx(workstationID, applicationSender, requestId, deviceId, transactionNo, releaseToken,
- (int)ErrorCode.ERRCD_BADVAL, 1, OverallResult.Failure.ToString());
- }
- catch (Exception eeee)
- {
- fdcLogger.LogError("Fdc server is trying to respond a failed ClearFuelSaleTrx response to a fdcclient that issue current ClearFuelSaleTrx request, but still failed with reason: " + eeee);
- }
- }
- };
- // FdcClient send ServiceRequestChangeFuelPrice will be routed into here
- fdcServer.OnChangeFuelPriceInStringReq += (string workstationID,
- string applicationSender, int requestId, string formattedValues) =>
- {
- try
- {
- fdcLogger.LogInformation("OnChangeFuelPriceInStringReq (requestId:" +
- requestId + ") with data-> (barcode; PriceNew; ModeNo; PriceOld; EffectiveDatetime;!): " + formattedValues);
- //formattedValues is: gradeId(barCode);PriceNew;ModeNo;EffectiveDatetime! e.g.: 5;1980;1;!
- //this function should return: gradeId(barCode);PriceNew;ModeNo;PriceOld;EffectiveDatetime;! e.g.: 2;1998;1;1981;!
- var targetProductBarcode = int.Parse(formattedValues.Split(';')[0]);
- // raw means the price is send from POS, and typically it contains decimal points,
- // need re-caculate with money digits since pump does not recognize decimal points.
- var rawNewPriceWithHumanReadableFormat = double.Parse(formattedValues.Split(';')[1]);
- var succeedNozzles = this.ChangeFuelPriceAsync(targetProductBarcode, rawNewPriceWithHumanReadableFormat).Result;
- fdcLogger.LogInformation(" OnChangeFuelPriceInStringReq (requestId:" + requestId + ") is done");
- // even only one succeed, return a success. need refine?
- if (succeedNozzles != null && succeedNozzles.Any())
- {
- // notify all tcp FdcClients that price changed.
- fdcServer.FuelPriceChange(targetProductBarcode, 1,
- rawNewPriceWithHumanReadableFormat,
- 0,
- // always set older time since the new price is already activated on pump.
- DateTime.Now.Subtract(new TimeSpan(0, 5, 0)).ToString("yyyy-MM-dd HH:mm:ss"));
- return targetProductBarcode.ToString() + ";" + rawNewPriceWithHumanReadableFormat.ToString() + ";1;" + 0 + ";!";
- }
- else return null;
- }
- catch (Exception exxx)
- {
- fdcLogger.LogError("OnChangeFuelPriceInStringReq exceptioned: " + exxx);
- return null;
- }
- };
- //fdcServer.OnChangeFuelPriceAddReq += (string workstationID, string applicationSender,
- // int requestId, int product, double price, int mode, string effectiveDateTime) =>
- // {
- // };
- //fdcServer.OnChangeFuelPriceEndReq += (string workstationID, string applicationSender, int requestId) =>
- // {
- // fdcServer.ChangeFuelPriceEndReq(workstationID, applicationSender, requestId);
- // return "";
- // };
- fdcServer.OnGetCurrentFuellingStatusReq += (string workstationID, string applicationSender,
- int requestId, int deviceId) =>
- {
- /* do nothing for now, the fuelling status will notify by unsolicited event. */
- var targetController = fdcPumpControllers.First(c => c.PumpId == deviceId) as IFdcPumpController;
- //if (targetController.QueryStatus() == LogicalDeviceState.FDC_FUELLING)
- //{
- // fdcServer.GetCurrentFuellingStatusAdd(workstationID, applicationSender, requestId, deviceId)
- //}
- };
- fdcServer.OnGetFuelPointTotalsReq += async (string workstationID, string applicationSender, int requestId, int deviceId, int nozzleId) =>
- {
- try
- {
- fdcLogger.LogDebug("OnGetFuelPointTotalsReq (wid: " + workstationID + ", appSender: " + applicationSender + ", requestId: " + requestId + ", deviceId: " + deviceId
- + ", nozzleId: " + nozzleId);
- if (deviceId == -1)
- {
- /* query all pumps and all its nozzles */
- var everReadSuccessfully = false;
- foreach (var pumpController in fdcPumpControllers)
- {
- var pumpState = await pumpController.QueryStatusAsync();
- if (pumpState != LogicalDeviceState.FDC_READY)
- {
- fdcLogger.LogDebug($" OnGetFuelPointTotalsReq, pump: {pumpController.PumpId} is in state: {pumpState}, will skip to query its totalizer.");
- continue;
- }
- foreach (var nozzle in pumpController.Nozzles)
- {
- var result = await this.GetFuelPointTotalsAsync(pumpController.PumpId, (byte)nozzle.LogicalId);
- everReadSuccessfully = true;
- fdcLogger.LogInformation(" OnGetFuelPointTotalsReq for pump: " + pumpController.PumpId
- + ", nozzle: " + nozzle.LogicalId + " result(with decimal points) is: " + result.Item1 + " <-> " + result.Item2);
- fdcServer.GetFuelPointTotalsAdd(workstationID, applicationSender, requestId, pumpController.PumpId, nozzle.LogicalId,
- nozzleExtraInfos.FirstOrDefault(n => n.PumpId == pumpController.PumpId && n.NozzleLogicalId == nozzle.LogicalId).ProductBarcode,
- result.Item2,
- result.Item1,
- (pumpController.Nozzles.FirstOrDefault(n => n.LogicalId == nozzleId)?.RealPriceOnPhysicalPump ?? 0) / Math.Pow(10, pumpController.PriceDecimalDigits));
- }
- }
- if (everReadSuccessfully)
- fdcServer.GetFuelPointTotalsSend(workstationID, applicationSender, requestId, (int)ErrorCode.ERRCD_OK, OverallResult.Success.ToString());
- else
- fdcServer.GetFuelPointTotalsSend(workstationID, applicationSender, requestId, (int)ErrorCode.ERRCD_NOTALLOWED, OverallResult.DeviceUnavailable.ToString());
- }
- else if (nozzleId == -1)
- {
- /* query a specified pump and all its nozzles */
- var pumpController = fdcPumpControllers
- .First(c => c.PumpId == deviceId) as IFdcPumpController;
- var pumpState = await pumpController.QueryStatusAsync();
- if (pumpState != LogicalDeviceState.FDC_READY)
- {
- fdcLogger.LogDebug($" OnGetFuelPointTotalsReq, pump: {pumpController.PumpId} is in state: {pumpState}, will skip to query its totalizer.");
- fdcServer.GetFuelPointTotalsSend(workstationID, applicationSender, requestId, (int)ErrorCode.ERRCD_NOTALLOWED, OverallResult.DeviceUnavailable.ToString());
- }
- else
- {
- foreach (var nozzle in pumpController.Nozzles)
- {
- var result = await this.GetFuelPointTotalsAsync(pumpController.PumpId, (byte)nozzle.LogicalId);
- fdcLogger.LogInformation(" OnGetFuelPointTotalsReq for pump: " + pumpController.PumpId + ", nozzle: " + nozzle.LogicalId + " result(with decimal points) is: " + result.Item1 + " <-> " + result.Item2);
- fdcServer.GetFuelPointTotalsAdd(workstationID, applicationSender, requestId, pumpController.PumpId, nozzle.LogicalId,
- nozzleExtraInfos.FirstOrDefault(n => n.PumpId == pumpController.PumpId && n.NozzleLogicalId == nozzle.LogicalId).ProductBarcode,
- result.Item2,
- result.Item1,
- (pumpController.Nozzles.FirstOrDefault(n => n.LogicalId == nozzleId)?.RealPriceOnPhysicalPump ?? 0) / Math.Pow(10, pumpController.PriceDecimalDigits));
- }
- fdcServer.GetFuelPointTotalsSend(workstationID, applicationSender, requestId, (int)ErrorCode.ERRCD_OK, OverallResult.Success.ToString());
- }
- }
- else
- {
- /* query a specified pump and a specified nozzle */
- var targetController = fdcPumpControllers
- .First(c => c.PumpId == deviceId) as IFdcPumpController;
- var pumpState = await targetController.QueryStatusAsync();
- if (pumpState != LogicalDeviceState.FDC_READY)
- {
- fdcLogger.LogDebug($" OnGetFuelPointTotalsReq, pump: {targetController.PumpId} is in state: {pumpState}, will skip to query its totalizer.");
- fdcServer.GetFuelPointTotalsSend(workstationID, applicationSender, requestId, (int)ErrorCode.ERRCD_NOTALLOWED, OverallResult.DeviceUnavailable.ToString());
- }
- else
- {
- var result = await this.GetFuelPointTotalsAsync(targetController.PumpId, (byte)nozzleId);
- fdcLogger.LogInformation(" OnGetFuelPointTotalsReq for pump: " + targetController.PumpId + ", nozzle: " + nozzleId + " result(with decimal points) is: " + result.Item1 + " <-> " + result.Item2);
- fdcServer.GetFuelPointTotalsAdd(workstationID, applicationSender, requestId, deviceId, nozzleId,
- nozzleExtraInfos.FirstOrDefault(n => n.PumpId == targetController.PumpId && n.NozzleLogicalId == nozzleId).ProductBarcode,
- result.Item2,
- result.Item1,
- (targetController.Nozzles.FirstOrDefault(n => n.LogicalId == nozzleId)?.RealPriceOnPhysicalPump ?? 0) / Math.Pow(10, targetController.PriceDecimalDigits));
- fdcServer.GetFuelPointTotalsSend(workstationID, applicationSender, requestId, (int)ErrorCode.ERRCD_OK, OverallResult.Success.ToString());
- }
- }
- fdcLogger.LogDebug(" OnGetFuelPointTotals done");
- }
- catch (Exception exxx)
- {
- fdcLogger.LogError("OnGetFuelPointTotalsReq exceptioned: " + exxx.ToString());
- fdcServer.GetFuelPointTotalsSend(workstationID, applicationSender, requestId,
- (int)ErrorCode.ERRCD_BADCONF, OverallResult.Failure.ToString());
- }
- };
- #region setup Fdc unsolicited event
- foreach (var fdcPumpController in this.fdcPumpControllers)
- {
- fdcPumpController.OnStateChange += async (s, stateChangeArg) =>
- {
- var pump = s as IFdcPumpController;
- try
- {
- fdcLogger.LogDebug("Pump " + pump.PumpId
- + " StateChanged to: " + stateChangeArg.NewPumpState.ToString() + ", nozzles states are(LogicalId-State): "
- + (pump.Nozzles != null ?
- (pump.Nozzles.Any() ?
- pump.Nozzles.Select(n =>
- n.LogicalId.ToString() + "-" + (n.LogicalState?.ToString() ?? ""))
- .Aggregate((n, acc) => n + ", " + acc) : "") : "")
- + ", StateChangedNozzles are: "
- + (stateChangeArg.StateChangedNozzles != null ?
- (stateChangeArg.StateChangedNozzles.Any() ?
- stateChangeArg.StateChangedNozzles.Select(n => n.LogicalId.ToString())
- .Aggregate((n, acc) => n + ", " + acc) : "") : ""));
- this.OnStateChange?.Invoke(s, stateChangeArg);
- var universalApiHub = this.services.GetRequiredService<UniversalApiHub>();
- await universalApiHub.FireEvent(this, "OnFdcControllerStateChange",
- new
- {
- pump.PumpId,
- logicalId = (stateChangeArg.StateChangedNozzles != null && stateChangeArg.StateChangedNozzles.Any()) ?
- stateChangeArg.StateChangedNozzles.FirstOrDefault().LogicalId.ToString() : "",
- logicalState = stateChangeArg.NewPumpState
- });
- fdcLogger.LogTrace("Pump " + pump.PumpId + " StateChanged event fired and back");
- // this is used for send to fdc client.
- byte nozzleUpOrDownBitMap = 0;
- if (stateChangeArg.StateChangedNozzles != null)
- foreach (var upNozzle in stateChangeArg.StateChangedNozzles)
- {
- nozzleUpOrDownBitMap = SetBit(nozzleUpOrDownBitMap, upNozzle.LogicalId - 1, upNozzle.LogicalId, 1);
- }
- byte nozzleLockedOrUnlockedBitMap = 0;
- foreach (var nozzle in pump.Nozzles)
- {
- if (nozzle.LogicalState.HasValue && nozzle.LogicalState == LogicalDeviceState.FDC_LOCKED)
- nozzleLockedOrUnlockedBitMap = SetBit(nozzleLockedOrUnlockedBitMap, nozzle.LogicalId - 1, nozzle.LogicalId, 1);
- else
- nozzleLockedOrUnlockedBitMap = SetBit(nozzleLockedOrUnlockedBitMap, nozzle.LogicalId - 1, nozzle.LogicalId, 0);
- }
- fdcServer.DeviceStateChange(Wayne.FDCPOSLibrary.DeviceType.DT_FuellingPoint, pump.PumpId,
- (int)(stateChangeArg.NewPumpState), (int)LogicalDeviceState.FDC_UNDEFINED, "", "", "",
- pump.Nozzles.Count(), nozzleUpOrDownBitMap, nozzleLockedOrUnlockedBitMap, 0);
- fdcLogger.LogTrace($"Pump {pump.PumpId } Fdc DeviceStateChange event fired and back");
- if (stateChangeArg.NewPumpState == LogicalDeviceState.FDC_CALLING)
- {
- if (this.config_AutoAuthCallingPumps)
- {
- if (this.config_MaxStackUnpaidTrxPerPump > 0)
- {
- SqliteDbContext dbContext = new SqliteDbContext();
- var unpaidTrxCount = dbContext.PumpTransactionModels.Count(t => t.PumpId == pump.PumpId && t.State == FuelSaleTransactionState.Payable);
- if (unpaidTrxCount >= this.config_MaxStackUnpaidTrxPerPump)
- {
- fdcLogger.LogInformation(" Auto authorizing is not permit since pump " + pump.PumpId + " has: " + unpaidTrxCount + " unpaid trx");
- return;
- }
- }
- fdcLogger.LogDebug("Auto authorizing Pump: " + pump.PumpId);
- int autoAuthDefaultAmount = 9999;
- var result = await pump.AuthorizeWithAmountAsync((int)(autoAuthDefaultAmount * Math.Pow(10, pump.AmountDecimalDigits)), 1);
- if (!result)
- fdcLogger.LogError("Auto auth Pump: " + pump.PumpId + " FAILED!");
- }
- }
- }
- catch (Exception exxx)
- {
- fdcLogger.LogError($"PumpId: {pump.PumpId}, fdcPumpController.OnStateChange exceptioned: {exxx}");
- }
- };
- fdcPumpController.OnCurrentFuellingStatusChange += async (s, a) =>
- {
- var pump = s as IFdcPumpController;
- try
- {
- fdcLogger.LogDebug($"Pump {pump.PumpId }, Nozzle: {a?.Transaction?.Nozzle?.LogicalId.ToString() ?? ""}, OnCurrentFuellingStatusChange");
- var product = nozzleExtraInfos.FirstOrDefault(c => c.PumpId == pump.PumpId && c.NozzleLogicalId == a.Transaction.Nozzle.LogicalId);
- if (product != null)
- a.Transaction.Barcode = product.ProductBarcode;
- else
- a.Transaction.Barcode = 1;
- if (a.Transaction.Finished)
- {
- fdcLogger.LogInformation("Pump " + pump.PumpId + ", transaction is finished, vol: " + a.Transaction.Volumn + ", amount: " + a.Transaction.Amount
- + ", price: " + a.Transaction.Price + ", nozzleNo: " + a.Transaction.Nozzle.LogicalId
- + ", volumeTotalizer: " + (a.Transaction.VolumeTotalizer ?? -1)
- + ", amountTotalizer: " + (a.Transaction.AmountTotalizer ?? -1)
- + ", seqNumber: " + a.Transaction.SequenceNumberGeneratedOnPhysicalPump
- + ", productBarcode: " + product?.ProductBarcode
- + ", productName: " + (product?.ProductName ?? ""));
- //enum SaleTrxStatus
- //{
- // SALE_TRX_UNDEFINED = 0,
- // SALE_TRX_PAYABLE = 1,
- // SALE_TRX_LOCKED = 2,
- // SALE_TRX_PAID = 3,
- // SALE_TRX_CLEARED = 4,
- //};
- // will do a duplication search in a short time range(by days) since physical pump will reuse seq id.
- var duplicationDectectTimeRange = 3;
- var range = DateTime.Now.Subtract(new TimeSpan(duplicationDectectTimeRange, 0, 0, 0));
- SqliteDbContext dbContext = new SqliteDbContext();
- var existed = dbContext.PumpTransactionModels.Where(f =>
- f.PumpId == pump.PumpId
- && f.TransactionSeqNumberFromPhysicalPump == a.Transaction.SequenceNumberGeneratedOnPhysicalPump.ToString()
- && f.UnitPrice == a.Transaction.Price
- && f.Amount == a.Transaction.Amount
- && f.Volumn == a.Transaction.Volumn
- && f.SaleEndTime > range).ToList();
- if (existed.Any())
- {
- fdcLogger.LogWarning("A new trx duplicated with an existed trx in db which done in recent "
- + duplicationDectectTimeRange + " days, it was with " +
- "releaseToken:" + existed.First().ReleaseToken + ", pumpId: " + pump.PumpId + " and seqNo: "
- + a.Transaction.SequenceNumberGeneratedOnPhysicalPump.ToString()
- + ", will do nothing and NOT notify any POS, while the existed one in database detail is-> "
- + "Vol: " + existed.First().Volumn
- + ", Amount: " + existed.First().Amount
- + ", State: " + existed.First().State.ToString()
- + ", SaleStartTime: " + (existed.First().SaleStartTime?.ToString("yyyy-MM-dd HH:mm:ss") ?? "")
- + ", EndStartTime: " + (existed.First().SaleEndTime?.ToString("yyyy-MM-dd HH:mm:ss") ?? "")
- + ", ProductBarcode: " + existed.First().ProductBarcode.ToString()
- + "");
- }
- else
- {
- var trx = new Edge.Core.Database.Models.FuelSaleTransaction()
- {
- TransactionSeqNumberFromPhysicalPump = a.Transaction.SequenceNumberGeneratedOnPhysicalPump.ToString(),
- PumpId = pump.PumpId,
- LogicalNozzleId = a.Transaction.Nozzle.LogicalId,
- Amount = a.Transaction.Amount,
- Volumn = a.Transaction.Volumn,
- UnitPrice = a.Transaction.Price,
- VolumeTotalizer = a.Transaction.VolumeTotalizer ?? -1,
- AmountTotalizer = a.Transaction.AmountTotalizer ?? -1,
- ProductBarcode = product?.ProductBarcode.ToString() ?? "9999",
- //ProductName = "refer cloud",
- State = FuelSaleTransactionState.Payable,
- LockedByFdcClientId = "",
- // hard code for now, need do it in PumpHandler
- SaleStartTime = a.Transaction.SaleStartTime ?? ((a.Transaction.SaleEndTime ?? DateTime.Now).Subtract(new TimeSpan(0, 3, 0))),
- SaleEndTime = a.Transaction.SaleEndTime ?? DateTime.Now,
- };
- dbContext.PumpTransactionModels.Add(trx);
- dbContext.SaveChanges();
- fdcLogger.LogDebug(" ######transaction is done saving to db with ReleaseToken(Id): " + trx.ReleaseToken);
- var safe = this.OnFdcFuelSaleTransactinStateChange;
- safe?.Invoke(this, new FdcFuelSaleTransactinStateChangeEventArg(trx, FuelSaleTransactionState.Payable));
- var universalApiHub = this.services.GetRequiredService<UniversalApiHub>();
- await universalApiHub.FireEvent(this, "OnFdcFuelSaleTransactinStateChange", new FdcFuelSaleTransactinStateChangeEventArg(trx, FuelSaleTransactionState.Payable));
- var startingTime = DateTime.Now;
- fdcServer.FuelSaleTrx(pump.PumpId,
- trx.Volumn / Math.Pow(10, pump.VolumeDecimalDigits),
- trx.Amount / Math.Pow(10, pump.AmountDecimalDigits),
- trx.UnitPrice / Math.Pow(10, pump.PriceDecimalDigits),
- trx.LogicalNozzleId,
- product?.ProductBarcode ?? 0,
- product?.ProductName ?? ("refer cloud for name of " + (product?.ProductBarcode ?? -1)),
- "", 1,
- int.Parse(trx.TransactionSeqNumberFromPhysicalPump),
- (int)trx.State,
- 0, trx.ReleaseToken.ToString(),
- trx.SaleStartTime?.ToString("yyyy-MM-dd HH:mm:ss"),
- trx.SaleEndTime?.ToString("yyyy-MM-dd HH:mm:ss"),
- "", "", 0, 0);
- fdcLogger.LogDebug($" ######transaction(releaseToken: {trx.ReleaseToken}) is done broadcasting to POSes, used: {DateTime.Now.Subtract(startingTime).TotalMilliseconds}");
- this.OnCurrentFuellingStatusChange?.Invoke(s,
- new FdcServerTransactionDoneEventArg(a.Transaction)
- { ReleaseToken = trx.ReleaseToken });
- fdcLogger.LogTrace("Pump " + pump.PumpId + " OnCurrentFuellingStatusChange event fired and back");
- if (a.Transaction.VolumeTotalizer == null)
- {
- var result = await GetFuelPointTotalsAsync(pump.PumpId, a.Transaction.Nozzle.LogicalId);
- a.Transaction.AmountTotalizer = (int?)(result.Item1 * Math.Pow(10, pump.AmountDecimalDigits));
- a.Transaction.VolumeTotalizer = (int?)(result.Item2 * Math.Pow(10, pump.VolumeDecimalDigits));
- }
- var targetNozzle = pump.Nozzles.First(n => n.LogicalId == a.Transaction.Nozzle.LogicalId);
- if (targetNozzle != null) targetNozzle.VolumeTotalizer = a.Transaction.VolumeTotalizer;
- await universalApiHub.FireEvent(this, "OnCurrentFuellingStatusChange", new FdcServerTransactionDoneEventArg(a.Transaction) { FuelingEndTime = trx.SaleEndTime });
- }
- }
- else
- {
- fdcLogger.LogDebug(" transaction is ongoing, vol: " + a.Transaction.Volumn + ", amount: " + a.Transaction.Amount
- + ", price: " + a.Transaction.Price + ", nozzleNo: " + a.Transaction.Nozzle.LogicalId
- + ", seqNumber: " + a.Transaction.SequenceNumberGeneratedOnPhysicalPump ?? ""
- + ", productBarcode: " + product.ProductBarcode
- + ", productName: " + (product.ProductName ?? ""));
- fdcServer.CurrentFuellingStatus(pump.PumpId,
- a.Transaction.Volumn / Math.Pow(10, pump.VolumeDecimalDigits),
- a.Transaction.Amount / Math.Pow(10, pump.AmountDecimalDigits),
- a.Transaction.Price / Math.Pow(10, pump.PriceDecimalDigits),
- a.Transaction.Nozzle.LogicalId,
- a.Transaction.SequenceNumberGeneratedOnPhysicalPump, "9999", 1, a.Transaction.Nozzle.LogicalId);
- this.OnCurrentFuellingStatusChange?.Invoke(s, new FdcServerTransactionDoneEventArg(a.Transaction));
- fdcLogger.LogTrace("Pump " + pump.PumpId + " OnCurrentFuellingStatusChange event fired and back");
- var universalApiHub = this.services.GetRequiredService<UniversalApiHub>();
- await universalApiHub.FireEvent(this, "OnCurrentFuellingStatusChange", new FdcServerTransactionDoneEventArg(a.Transaction));
- }
- }
- catch (Exception exxx)
- {
- fdcLogger.LogError($"PumpId: { pump?.PumpId ?? -1}, fdcPumpController.OnCurrentFuellingStatusChange exceptioned: " + exxx);
- }
- };
- }
- #endregion
- #region handle BroadcastGenericTypelessMessage
- if (this.fdcCommunicableControllers != null)
- {
- this.fdcCommunicableControllers.ToList().ForEach(c =>
- {
- c.BroadcastMessageViaFdc += (msg) =>
- {
- fdcServer.SendGenericTypelessMessageToFdcClient(null, null, msg);
- return true;
- };
- });
- this.fdcCommunicableControllers.ToList().ForEach(c =>
- {
- c.SendMessageViaFdc += (workstationID, applicationSender, msg) =>
- {
- fdcServer.SendGenericTypelessMessageToFdcClient(workstationID, applicationSender, msg);
- return true;
- };
- });
- }
- fdcServer.OnGetGenericTypelessMessageReq += (string workstationID, string applicationSender, int requestId, string message) =>
- {
- this.fdcCommunicableControllers.ToList().ForEach(c =>
- {
- if (c.OnMessageReceivedViaFdc != null)
- {
- var returnResult = c.OnMessageReceivedViaFdc(message);
- fdcServer.GenericTypelessMessageSend(workstationID, applicationSender, requestId, returnResult.Item1, returnResult.Item2.ToString());
- }
- });
- };
- #endregion
- fdcLogger.LogDebug("Start all FdcPumpController initing...(total: "
- + (fdcPumpControllers?.Count() ?? -1)
- + ", pump Ids are: " + ((fdcPumpControllers?.Any() ?? false) ?
- (fdcPumpControllers.Select(c => c.PumpId.ToString()).Aggregate((acc, n) => acc + ", " + n) + ")") : ")"));
- foreach (var fdcPumpController in this.fdcPumpControllers)
- {
- var onFdcServerInitParams = new Dictionary<string, object>();
- try
- {
- #region LastPriceChange for each nozzles
- // nozzle logical id:rawPrice
- var innerParams0 = new Dictionary<byte, int>();
- foreach (var nozzle in fdcPumpController.Nozzles)
- {
- var dbContext = new SqliteDbContext();
- var lastPriceChange = dbContext.FuelPriceChanges
- .Where(t => t.PumpId == fdcPumpController.PumpId
- && t.LogicalNozzleId == nozzle.LogicalId)
- .OrderByDescending(b => b.Id).FirstOrDefault();
- if (lastPriceChange != null)
- innerParams0.Add(nozzle.LogicalId, lastPriceChange.NewPriceWithoutDecimal);
- }
- onFdcServerInitParams.Add("LastPriceChange", innerParams0);
- #endregion
- #region LastFuelSaleTrx for each nozzles
- // nozzle logical id:LastSaleTrx
- var innerParams1 = new Dictionary<byte, FuelSaleTransaction>();
- foreach (var nozzle in fdcPumpController.Nozzles)
- {
- var dbContext = new SqliteDbContext();
- var lastPaidTrx = dbContext.PumpTransactionModels
- .Where(t => t.PumpId == fdcPumpController.PumpId
- && t.LogicalNozzleId == nozzle.LogicalId)
- .OrderByDescending(b => b.SaleStartTime).FirstOrDefault();
- if (lastPaidTrx != null)
- innerParams1.Add(nozzle.LogicalId, lastPaidTrx);
- }
- onFdcServerInitParams.Add("LastFuelSaleTrx", innerParams1);
- #endregion
- }
- catch (Exception exxx)
- {
- fdcLogger.LogError("Retrieve lastPriceChange for OnFdcServerInit from db for Pump with pumpid: "
- + fdcPumpController.PumpId + " exceptioned: " + exxx.ToString()
- + System.Environment.NewLine + "Will skip this pump and continue for next pump.");
- continue;
- }
- try
- {
- fdcPumpController.OnFdcServerInit(onFdcServerInitParams);
- }
- catch (Exception exxx)
- {
- fdcLogger.LogError("FdcPumpController with pumpid: "
- + fdcPumpController.PumpId + " exceptioned in OnFdcServerInit, detail: " + exxx.ToString()
- + System.Environment.NewLine + "Will skip and continue for next FdcPumpController.");
- continue;
- }
- }
- fdcLogger.LogDebug("Done all FdcPumpController init, start FdcServer tcp listening...");
- var _ = fdcServer.Start(this.config_ListeningPort, true, "WINCOR", 2, true, "NONE");
- fdcLogger.LogDebug("Done FdcServer tcp listening...");
- return Task.FromResult(_);
- }
- public Task<bool> Stop()
- {
- if (this.onConfiguratorConfigFileChangedEventHandler != null)
- this.configurator.OnConfigFileChanged -= this.onConfiguratorConfigFileChangedEventHandler;
- this.purgeDatabaseTimer?.Stop();
- // set fdcPumpControllers to an empty list, as it needs to be stopped from firing any event to any substriber.
- this.fdcPumpControllers = new List<IFdcPumpController>();
- var stopFdcServerResult = fdcServer.Stop();
- return Task.FromResult(stopFdcServerResult);
- }
- private static byte SetBit(byte target, int bitStartIndex, int bitEndIndex, int replacedValue)
- {
- if (bitStartIndex < 0 || bitEndIndex > 7 || bitEndIndex < bitStartIndex)
- {
- throw new ArgumentException("bitStartIndex or bitEndIndex value is not valid");
- }
- byte mask = 0;
- for (int i = 0; i < bitEndIndex - bitStartIndex + 1; i++)
- {
- mask += (byte)Math.Pow(2, i);
- }
- if (replacedValue > mask)
- {
- throw new ArgumentOutOfRangeException("Replaced value: " + replacedValue + " cannot fit the bits range");
- }
- byte maskedValue = (byte)(target & (255 - (mask << bitStartIndex)));
- return (byte)(maskedValue + (replacedValue << bitStartIndex));
- }
- public void Dispose()
- {
- }
- #region pump control interface opened for local call, used in other fc app.
- /// <summary>
- /// fired once PumpController state changed.
- /// </summary>
- public event EventHandler<FdcPumpControllerOnStateChangeEventArg> OnStateChange;
- /// <summary>
- /// fired once the fuelling trx from PumpController state changed.
- /// used to watch fuelling progress for a PumpController.
- /// </summary>
- public event EventHandler<FdcServerTransactionDoneEventArg> OnCurrentFuellingStatusChange;
- /// <summary>
- /// fired once the Fdc fuel sale trx state changed, like it's turn into Payable, Locked, Unlocked state etc.
- /// used for local app processors to watch the trx state, the state change mostly triggered by Fdc client request.
- /// </summary>
- public event EventHandler<FdcFuelSaleTransactinStateChangeEventArg> OnFdcFuelSaleTransactinStateChange;
- public IEnumerable<IFdcPumpController> FdcPumpControllers => this.fdcPumpControllers;
- /// <summary>
- /// maxTrxAmount has the high priority.
- /// </summary>
- /// <param name="pumpId">the target pump, which for authorizing</param>
- /// <param name="maxTrxAmount">human readable number, with decimal point, like 5 RMB, should input 5. leave 0 if set with unlimited amount.</param>
- /// <param name="maxTrxVolume">human readable number, with decimal point, like 6.5L, should input 6.5. leave 0 if set with unlimited vol</param>
- /// <returns></returns>
- public async Task<bool> AuthorizePumpAsync(int pumpId, double maxTrxAmount, double maxTrxVolume)
- {
- fdcLogger.LogDebug("Authorizing Pump: " + pumpId + ", maxTrxAmount: " + maxTrxAmount + ", maxTrxVolume: " + maxTrxVolume);
- var targetController = fdcPumpControllers
- .First(c => c.PumpId == pumpId) as IFdcPumpController;
- if (this.config_MaxStackUnpaidTrxPerPump > 0)
- {
- SqliteDbContext dbContext = new SqliteDbContext();
- var unpaidTrxCount = dbContext.PumpTransactionModels.Count(t => t.PumpId == targetController.PumpId
- && t.State == FuelSaleTransactionState.Payable);
- if (unpaidTrxCount >= this.config_MaxStackUnpaidTrxPerPump)
- {
- fdcLogger.LogInformation(" Authorizing from FdcClient is not permit since pump " + targetController.PumpId + " has: " + unpaidTrxCount + " unpaid trx");
- //fdcServer.AuthoriseFuelPoint(workstationID, applicationSender, requestId, -99, releaseToken, deviceId,
- // (int)LogicalDeviceState.FDC_READY, (int)ErrorCode.ERRCD_MAXSTACKLIMIT, OverallResult.Failure.ToString());
- return false;
- }
- }
- bool succeed = false;
- if (maxTrxAmount == 0 && maxTrxVolume == 0)
- // fuel with unlimited
- succeed = await targetController.AuthorizeAsync(1);
- else if (maxTrxAmount == 0 && maxTrxVolume != 0)
- succeed = await targetController.AuthorizeWithVolumeAsync((int)(maxTrxVolume * Math.Pow(10, targetController.VolumeDecimalDigits)), 1);
- else
- succeed = await targetController.AuthorizeWithAmountAsync((int)(maxTrxAmount * Math.Pow(10, targetController.AmountDecimalDigits)), 1);
- fdcLogger.LogDebug(" AuthorizePump: " + pumpId + " finished with: " + succeed.ToString());
- return succeed;
- }
- /// <summary>
- /// Clear an unpaid fuel sale trx from db and notify all tcp FdcClients.
- /// This function is used for non-tcp-FdcClient caller, which are most likely the Fcc Apps,
- /// so it's a local in-process call.
- /// </summary>
- /// <param name="pumpId">pump id of the target trx belongs</param>
- /// <param name="transactionNo">trx Number of the target trx that is on clearing</param>
- /// <param name="trxDbUniqueId">database row unique id of the target trx that is on clearing</param>
- /// <param name="lockingClientId">the caller identity, the trx will be marked as paid by it, make sure it unique in process
- /// , otherwise and the only impact, you lose the chance to know who really mark this trx with paid in db.</param>
- /// <returns>the trx successfully cleared(marked as state `Paid`) from db, otherwise failed to clear and return null value</returns>
- [UniversalApi(Description = "Clear an unpaid fuel sale trx from db and notify all tcp FdcClients.")]
- public async Task<FuelSaleTransaction> ClearFuelSaleTrxAndNotifyAllFdcClientsAsync(int pumpId, string transactionNo, int trxDbUniqueId, string lockingClientId)
- {
- return await this.ClearFuelSaleTrxAndNotifyAllFdcClientsAsync(pumpId, transactionNo, trxDbUniqueId, lockingClientId, null, -1);
- }
- /// <summary>
- /// Clear an unpaid fuel sale trx from db and notify all tcp FdcClients.
- /// This function is used for Tcp FdcClient caller, the caller most likely a POS which connected in via TCP.
- /// Will return a clearFuelSaleTrx response to tcp Fdc client caller.
- /// </summary>
- /// <param name="pumpId"></param>
- /// <param name="transactionNo"></param>
- /// <param name="trxDbUniqueId"></param>
- /// <param name="workstationID">fdc client's workstation id</param>
- /// <param name="appSenderId">must specify the correct value that used for send respone back via tcp</param>
- /// <param name="fdcClientRequestId">must specify the correct value that used for send respone back via tcp, for pair with request</param>
- /// <returns></returns>
- public async Task<FuelSaleTransaction> ClearFuelSaleTrxAndNotifyAllFdcClientsAsync(int pumpId, string transactionNo,
- int trxDbUniqueId, string workstationID, string appSenderId, int fdcClientRequestId)
- {
- try
- {
- fdcLogger.LogDebug("ClearFuelSaleTrxAndNotifyAllFdcClients (wid: " + workstationID + ", appSenderId: " + (appSenderId ?? "") + ", pumpId: " + pumpId
- + ", transactionNo: " + transactionNo + ", releaseToken: " + trxDbUniqueId + ")");
- SqliteDbContext dbContext = new SqliteDbContext();
- int databaseId = trxDbUniqueId;
- var target = await dbContext.PumpTransactionModels.FirstOrDefaultAsync(t =>
- t.ReleaseToken == databaseId
- && t.PumpId == pumpId
- && t.TransactionSeqNumberFromPhysicalPump == transactionNo);
- if (target != null)
- {
- if (target.State == FuelSaleTransactionState.Paid)
- {
- fdcLogger.LogInformation("ClearFuelSaleTrx for workstationId: " + workstationID + " on pump with pumpId: " + pumpId + ", transactionNo: " + transactionNo
- + ", releaseToken: " + trxDbUniqueId
- + " failed due to target trx is already a Paid trx");
- // null or empty appSenderId indicates the clear request is not send from logged in FdcClient, but from some other source, like
- // litefccCore internal app, then do not reply a response.
- if (!string.IsNullOrEmpty(appSenderId))
- {
- int.TryParse(transactionNo, out int p_trxNo);
- fdcServer.ClearFuelSaleTrx(workstationID, appSenderId, fdcClientRequestId, pumpId, p_trxNo, trxDbUniqueId.ToString(),
- (int)ErrorCode.ERRCD_NOTPOSSIBLE, 1, OverallResult.Failure.ToString());
- }
- return null;
- }
- else
- {
- target.State = FuelSaleTransactionState.Paid;
- target.PaidByFdcClientId = workstationID;
- target.PaidTime = DateTime.Now;
- await dbContext.SaveChangesAsync();
- var targetController = fdcPumpControllers
- .First(c => c.PumpId == pumpId) as IFdcPumpController;
- int.TryParse(transactionNo, out int p_trxNo);
- // null or empty appSenderId indicates the clear request is not send from logged in FdcClient, but from some other source, like
- // litefccCore internal app, then do not reply a response.
- if (!string.IsNullOrEmpty(appSenderId))
- fdcServer.ClearFuelSaleTrx(workstationID, appSenderId, fdcClientRequestId, pumpId, p_trxNo, trxDbUniqueId.ToString(),
- (int)ErrorCode.ERRCD_OK, 1, OverallResult.Success.ToString());
- var safe = this.OnFdcFuelSaleTransactinStateChange;
- safe?.Invoke(this,
- new FdcFuelSaleTransactinStateChangeEventArg(target, FuelSaleTransactionState.Paid));
- var universalApiHub = this.services.GetRequiredService<UniversalApiHub>();
- await universalApiHub.FireEvent(this, "OnFdcFuelSaleTransactinStateChange", new FdcFuelSaleTransactinStateChangeEventArg(target, FuelSaleTransactionState.Paid));
- fdcServer.FuelSaleTrx(target.PumpId,
- target.Volumn / Math.Pow(10, targetController.VolumeDecimalDigits),
- target.Amount / Math.Pow(10, targetController.AmountDecimalDigits),
- target.UnitPrice / Math.Pow(10, targetController.PriceDecimalDigits),
- target.LogicalNozzleId,
- int.Parse(target.ProductBarcode),
- "", "", 1,
- int.Parse(target.TransactionSeqNumberFromPhysicalPump),
- // looks like should return 'Paid', but from old code, we put 'Cleared'
- (int)FuelSaleTransactionState.Cleared,
- 0, trxDbUniqueId.ToString(),
- target.SaleStartTime?.ToString("yyyy-MM-dd HH:mm:ss"),
- target.SaleEndTime?.ToString("yyyy-MM-dd HH:mm:ss"),
- target.LockedByFdcClientId ?? "",
- "", -1, 0);
- return target;
- }
- }
- else
- {
- fdcLogger.LogInformation("ClearFuelSaleTrx for workstationId: " + workstationID + " on pump with pumpId: " + pumpId + ", transactionNo: " + transactionNo
- + ", releaseToken: " + trxDbUniqueId
- + " failed due to target trx could not found");
- // null or empty appSenderId indicates the clear request is not send from logged in FdcClient, but from some other source, like
- // litefccCore internal app, then do not reply a response.
- if (!string.IsNullOrEmpty(appSenderId))
- {
- int.TryParse(transactionNo, out int p_trxNo);
- fdcServer.ClearFuelSaleTrx(workstationID, appSenderId, fdcClientRequestId, pumpId, p_trxNo, trxDbUniqueId.ToString(),
- (int)ErrorCode.ERRCD_BADVAL, 1, OverallResult.Failure.ToString());
- }
- return null;
- }
- }
- catch (Exception exxx)
- {
- fdcLogger.LogInformation("ClearFuelSaleTrx for workstationId: " + workstationID + " on pump with pumpId: " + pumpId + ", transactionNo: " + transactionNo
- + ", releaseToken: " + trxDbUniqueId
- + " failed due to exception: " + exxx);
- return null;
- }
- }
- /// <summary>
- /// Lock an unlocked state fuel sale trx from db and notify all tcp FdcClients.
- /// This function is used for non-tcp-FdcClient caller, which are most likely the Fcc Apps,
- /// so it's a local in-process call.
- /// </summary>
- /// <param name="lockingClientId">the caller identity, the trx will be marked as locked by it, make sure it unique in process
- /// , otherwise, the locked trx may get unlocked unexpectly by other caller with same id.</param>
- /// <param name="pumpId"></param>
- /// <param name="transactionNo"></param>
- /// <param name="trxDbUniqueId"></param>
- /// <returns></returns>
- [UniversalApi(Description = "Lock an unlocked state fuel sale trx from db and notify all tcp FdcClients.")]
- public async Task<FuelSaleTransaction> LockFuelSaleTrxAndNotifyAllFdcClientsAsync(int lockingClientId,
- int pumpId,
- int transactionNo, int trxDbUniqueId)
- {
- return await this.LockFuelSaleTrxAndNotifyAllFdcClientsAsync(lockingClientId, "", -1, pumpId, transactionNo, trxDbUniqueId);
- }
- /// <summary>
- /// Lock an unlocked fuel sale trx from db and notify all tcp FdcClients.
- /// This function is used for tcp FdcClient caller, the caller most likely a POS which connected in via TCP.
- /// Will return a response to tcp Fdc client caller.
- /// </summary>
- /// <param name="lockingClientId">the caller identity, the trx will be marked as locked by it, make sure it unique in process
- /// , otherwise, the locked trx may get unlocked unexpectly by other caller with same id.</param>
- /// <param name="fdcClientAppSender"></param>
- /// <param name="fdcClientRequestId"></param>
- /// <param name="pumpId"></param>
- /// <param name="transactionNo"></param>
- /// <param name="trxDbUniqueId"></param>
- /// <returns></returns>
- public async Task<FuelSaleTransaction> LockFuelSaleTrxAndNotifyAllFdcClientsAsync(int lockingClientId, string fdcClientAppSender, int fdcClientRequestId,
- int pumpId,
- int transactionNo, int trxDbUniqueId)
- {
- try
- {
- fdcLogger.LogDebug("LockFuelSaleTrxAndNotifyAllFdcClients from lockingClientId: " + lockingClientId
- + ", fdcClientAppSender: " + (fdcClientAppSender ?? "")
- + ", fdcClientRequestId: " + fdcClientRequestId
- + ", pumpId: " + pumpId + ", transactionNo: " + transactionNo + ", trxDbUniqueId: " + trxDbUniqueId);
- var result = await FdcResourceArbitrator.Default.TryLockFuelSaleTrxAsync(lockingClientId, pumpId, transactionNo, trxDbUniqueId);
- fdcLogger.LogDebug($" TryLockFuelSaleTrx(trxDbUniqueId: {trxDbUniqueId}) with result: {!(result == null)}");
- //this.LockFuelSaleTrx(int.Parse(workstationID), deviceId, transactionNo, int.Parse(releaseToken));
- if (result != null)
- {
- // null or empty appSenderId indicates the clear request is not send from logged in FdcClient, but from some other source, like
- // litefccCore internal app, then do not reply a response.
- if (!string.IsNullOrEmpty(fdcClientAppSender))
- fdcServer.LockFuelSaleTrx(lockingClientId.ToString(), fdcClientAppSender, fdcClientRequestId, pumpId,
- transactionNo, trxDbUniqueId.ToString(), (int)ErrorCode.ERRCD_OK, 1, OverallResult.Success.ToString());
- var safe = this.OnFdcFuelSaleTransactinStateChange;
- safe?.Invoke(this,
- new FdcFuelSaleTransactinStateChangeEventArg(result, FuelSaleTransactionState.Locked));
- var universalApiHub = this.services.GetRequiredService<UniversalApiHub>();
- await universalApiHub.FireEvent(this, "OnFdcFuelSaleTransactinStateChange", new FdcFuelSaleTransactinStateChangeEventArg(result, FuelSaleTransactionState.Locked));
- var targetController = fdcPumpControllers
- .First(c => c.PumpId == pumpId) as IFdcPumpController;
- fdcServer.FuelSaleTrx(pumpId,
- result.Volumn / Math.Pow(10, targetController.VolumeDecimalDigits),
- result.Amount / Math.Pow(10, targetController.AmountDecimalDigits),
- result.UnitPrice / Math.Pow(10, targetController.PriceDecimalDigits),
- result.LogicalNozzleId,
- int.Parse(result.ProductBarcode),
- this.nozzleExtraInfos
- .FirstOrDefault(n => n.PumpId == pumpId && n.NozzleLogicalId == result.LogicalNozzleId)?.ProductName ??
- ("refer cloud for name of " + result.ProductBarcode), "", 1,
- int.Parse(result.TransactionSeqNumberFromPhysicalPump),
- (int)FuelSaleTransactionState.Locked,
- 0, trxDbUniqueId.ToString(),
- result.SaleStartTime?.ToString("yyyy-MM-dd HH:mm:ss"),
- result.SaleEndTime?.ToString("yyyy-MM-dd HH:mm:ss"),
- lockingClientId.ToString(), "", lockingClientId, 0);
- }
- else
- {
- // null or empty appSenderId indicates the clear request is not send from logged in FdcClient, but from some other source, like
- // litefccCore internal app, then do not reply a response.
- if (!string.IsNullOrEmpty(fdcClientAppSender))
- fdcServer.LockFuelSaleTrx(lockingClientId.ToString(), fdcClientAppSender, fdcClientRequestId, pumpId,
- transactionNo, trxDbUniqueId.ToString(), (int)ErrorCode.ERRCD_TRANSLOCKED, 1, OverallResult.Failure.ToString());
- }
- fdcLogger.LogTrace($" LockFuelSaleTrxAndNotifyAllFdcClients(trxDbUniqueId: {trxDbUniqueId}) returned");
- return result;
- }
- catch (Exception exx)
- {
- fdcLogger.LogError("LockFuelSaleTrxAndNotifyAllFdcClients exceptioned for lockingClientId: "
- + lockingClientId + ", pumpId: " + pumpId + ", transactionNo: " + transactionNo + ", trxDbUniqueId: " + trxDbUniqueId + ", exception detail: " + exx);
- throw;
- }
- }
- /// <summary>
- /// Unlock a locked state fuel sale trx from db and notify all tcp FdcClients.
- /// This function is used for non-tcp-FdcClient caller, which are most likely the Fcc Apps,
- /// so it's a local in-process call.
- /// </summary>
- /// <param name="lockingClientId">the id of the caller when locking the trx, unlocking must be performed by the client who locked the trx</param>
- /// <param name="pumpId"></param>
- /// <param name="transactionNo"></param>
- /// <param name="trxDbUniqueId"></param>
- /// <returns></returns>
- [UniversalApi(Description = "Unlock a locked state fuel sale trx from db and notify all tcp FdcClients.")]
- public async Task<FuelSaleTransaction> UnlockFuelSaleTrxAndNotifyAllFdcClientsAsync(int lockingClientId,
- int pumpId,
- int transactionNo, int trxDbUniqueId)
- {
- return await this.UnlockFuelSaleTrxAndNotifyAllFdcClientsAsync(lockingClientId, "", -1, pumpId, transactionNo, trxDbUniqueId);
- }
- public IEnumerable<NozzleExtraInfo> GetNozzleExtraInfos()
- {
- return this.nozzleExtraInfos;
- }
- /// <summary>
- /// Unlock a locked fuel sale trx from db and notify all tcp FdcClients.
- /// This function is used for tcp FdcClient caller, the caller most likely a POS which connected in via TCP.
- /// Will return a response to tcp Fdc client caller.
- /// </summary>
- /// <param name="unlockingClientId">the id of the caller when locking the trx, unlocking must be performed by the client who locked the trx</param>
- /// <param name="fdcClientAppSender"></param>
- /// <param name="fdcClientRequestId"></param>
- /// <param name="pumpId"></param>
- /// <param name="transactionNo"></param>
- /// <param name="trxDbUniqueId"></param>
- /// <returns></returns>
- public async Task<FuelSaleTransaction> UnlockFuelSaleTrxAndNotifyAllFdcClientsAsync(int unlockingClientId, string fdcClientAppSender, int fdcClientRequestId, int pumpId,
- int transactionNo, int trxDbUniqueId)
- {
- try
- {
- fdcLogger.LogDebug("UnlockFuelSaleTrxAndNotifyAllFdcClients from lockingClientId: " + unlockingClientId
- + ", fdcClientAppSender: " + (fdcClientAppSender ?? "")
- + ", fdcClientRequestId: " + fdcClientRequestId
- + ", pumpId: " + pumpId + ", transactionNo: " + transactionNo + ", trxDbUniqueId: " + trxDbUniqueId);
- var result = await FdcResourceArbitrator.Default.TryUnlockFuelSaleTrxAsync(unlockingClientId, pumpId, transactionNo, trxDbUniqueId);
- if (result != null)
- {
- var targetController = fdcPumpControllers
- .First(c => c.PumpId == pumpId) as IFdcPumpController;
- // null or empty appSenderId indicates the clear request is not send from logged in FdcClient, but from some other source, like
- // litefccCore internal app, then do not reply a response.
- if (!string.IsNullOrEmpty(fdcClientAppSender))
- fdcServer.UnlockFuelSaleTrx(unlockingClientId.ToString(), fdcClientAppSender, fdcClientRequestId, pumpId,
- transactionNo, trxDbUniqueId.ToString(), (int)ErrorCode.ERRCD_OK, 1, OverallResult.Success.ToString());
- var safe = this.OnFdcFuelSaleTransactinStateChange;
- safe?.Invoke(this,
- new FdcFuelSaleTransactinStateChangeEventArg(result, FuelSaleTransactionState.Payable));
- var universalApiHub = this.services.GetRequiredService<UniversalApiHub>();
- await universalApiHub.FireEvent(this, "OnFdcFuelSaleTransactinStateChange", new FdcFuelSaleTransactinStateChangeEventArg(result, FuelSaleTransactionState.Payable));
- fdcServer.FuelSaleTrx(pumpId,
- result.Volumn / Math.Pow(10, targetController.VolumeDecimalDigits),
- result.Amount / Math.Pow(10, targetController.AmountDecimalDigits),
- result.UnitPrice / Math.Pow(10, targetController.PriceDecimalDigits),
- result.LogicalNozzleId,
- int.Parse(result.ProductBarcode),
- this.nozzleExtraInfos
- .FirstOrDefault(n => n.PumpId == pumpId && n.NozzleLogicalId == result.LogicalNozzleId)?.ProductName ??
- ("refer cloud for name of " + result.ProductBarcode), "", 1,
- int.Parse(result.TransactionSeqNumberFromPhysicalPump),
- (int)FuelSaleTransactionState.Payable,
- 0, trxDbUniqueId.ToString(),
- result.SaleStartTime?.ToString("yyyy-MM-dd HH:mm:ss"),
- result.SaleEndTime?.ToString("yyyy-MM-dd HH:mm:ss"),
- "", "", unlockingClientId, 0);
- }
- else
- {
- // null or empty appSenderId indicates the clear request is not send from logged in FdcClient, but from some other source, like
- // litefccCore internal app, then do not reply a response.
- if (!string.IsNullOrEmpty(fdcClientAppSender))
- fdcServer.UnlockFuelSaleTrx(unlockingClientId.ToString(), fdcClientAppSender, fdcClientRequestId, pumpId,
- transactionNo, trxDbUniqueId.ToString(), (int)ErrorCode.ERRCD_TRANSLOCKED, 1, OverallResult.Failure.ToString());
- }
- return result;
- }
- catch (Exception exxx)
- {
- fdcLogger.LogError("UnlockFuelSaleTrxAndNotifyAllFdcClients exceptioned for unlockingClientId: "
- + unlockingClientId + ", pumpId: " + pumpId + ", transactionNo: " + transactionNo + ", trxDbUniqueId: " + trxDbUniqueId + ", exception detail: " + exxx);
- throw;
- }
- }
- /// <summary>
- /// Lock a nozzle by call into a PumpHandler and then notify all tcp FdcClients.
- /// This function is used for non-tcp-FdcClient caller, which are most likely the Fcc Apps,
- /// so it's a local in-process call.
- /// </summary>
- /// <param name="lockingClientId">the caller identity, the trx will be marked as locked by it, make sure it unique in process
- /// , otherwise, the locked trx may get unlocked unexpectly by other caller with same id.</param>
- /// <param name="pumpId"></param>
- /// <param name="logicalNozzleId">logical nozzle in the pump for locking</param>
- /// <returns></returns>
- public async Task<bool> LockNozzleAndNotifyAllFdcClientsAsync(int lockingClientId,
- int pumpId,
- int logicalNozzleId)
- {
- return await this.LockNozzleAndNotifyAllFdcClientsAsync(lockingClientId, "", -1, pumpId, logicalNozzleId);
- }
- /// <summary>
- /// Lock a nozzle by call into a PumpHandler and then notify all tcp FdcClients.
- /// This function is used for tcp FdcClient caller, the caller most likely a POS which connected in via TCP.
- /// Will return a response to tcp Fdc client caller.
- /// </summary>
- /// <param name="lockingClientId">the caller identity, the trx will be marked as locked by it, make sure it unique in process
- /// , otherwise, the locked trx may get unlocked unexpectly by other caller with same id.</param>
- /// <param name="fdcClientAppSender"></param>
- /// <param name="fdcClientRequestId"></param>
- /// <param name="pumpId"></param>
- /// <param name="logicalNozzleId"></param>
- /// <returns></returns>
- public async Task<bool> LockNozzleAndNotifyAllFdcClientsAsync(int lockingClientId, string fdcClientAppSender, int fdcClientRequestId,
- int pumpId,
- int logicalNozzleId)
- {
- try
- {
- fdcLogger.LogDebug("LockNozzleAndNotifyAllFdcClientsAsync from lockingClientId: " + lockingClientId
- + ", fdcClientAppSender: " + (fdcClientAppSender ?? "")
- + ", fdcClientRequestId: " + fdcClientRequestId
- + ", pumpId: " + pumpId + ", logicalNozzleId: " + logicalNozzleId);
- var targetController = fdcPumpControllers
- .First(c => c.PumpId == pumpId) as IFdcPumpController;
- var result = await targetController.LockNozzleAsync((byte)logicalNozzleId);
- if (result)
- {
- // null or empty appSenderId indicates the clear request is not send from logged in FdcClient, but from some other source, like
- // litefccCore internal app, then do not reply a response.
- if (!string.IsNullOrEmpty(fdcClientAppSender))
- fdcServer.LockNozzle(lockingClientId.ToString(), fdcClientAppSender, fdcClientRequestId,
- pumpId, pumpId, logicalNozzleId,
- (int)ErrorCode.ERRCD_OK, OverallResult.Success.ToString());
- }
- else
- {
- fdcLogger.LogDebug("LockNozzleAndNotifyAllFdcClientsAsync failed");
- // null or empty appSenderId indicates the clear request is not send from logged in FdcClient, but from some other source, like
- // litefccCore internal app, then do not reply a response.
- if (!string.IsNullOrEmpty(fdcClientAppSender))
- fdcServer.LockNozzle(lockingClientId.ToString(), fdcClientAppSender, fdcClientRequestId,
- pumpId, pumpId, logicalNozzleId,
- (int)ErrorCode.ERRCD_NOTPOSSIBLE, OverallResult.Failure.ToString());
- }
- return result;
- }
- catch (Exception exx)
- {
- fdcLogger.LogError("LockNozzleAndNotifyAllFdcClientsAsync exceptioned for lockingClientId: "
- + lockingClientId + ", pumpId: " + pumpId + ", logicalNozzleId: " + logicalNozzleId + ", exception detail: " + exx);
- fdcServer.LockNozzle(lockingClientId.ToString(), fdcClientAppSender, fdcClientRequestId,
- pumpId, pumpId, logicalNozzleId,
- (int)ErrorCode.ERRCD_NOTALLOWED, OverallResult.Failure.ToString());
- return false;
- }
- }
- /// <summary>
- /// Unlock a nozzle by call into a PumpHandler and then notify all tcp FdcClients
- /// This function is used for non-tcp-FdcClient caller, which are most likely the Fcc Apps,
- /// so it's a local in-process call.
- /// </summary>
- /// <param name="lockingClientId">the id of the caller when locking the trx, unlocking must be performed by the client who locked the trx</param>
- /// <param name="pumpId"></param>
- /// <param name="logicalNozzleId"></param>
- /// <returns></returns>
- public async Task<bool> UnlockNozzleAndNotifyAllFdcClientsAsync(int lockingClientId,
- int pumpId,
- int logicalNozzleId)
- {
- return await this.UnlockNozzleAndNotifyAllFdcClientsAsync(lockingClientId, "", -1, pumpId, logicalNozzleId);
- }
- /// <summary>
- /// Unlock a nozzle by call into a PumpHandler and then notify all tcp FdcClients
- /// This function is used for tcp FdcClient caller, the caller most likely a POS which connected in via TCP.
- /// Will return a response to tcp Fdc client caller.
- /// </summary>
- /// <param name="unlockingClientId">the id of the caller when locking the trx, unlocking must be performed by the client who locked the trx</param>
- /// <param name="fdcClientAppSender"></param>
- /// <param name="fdcClientRequestId"></param>
- /// <param name="pumpId"></param>
- /// <param name="logicalNozzleId"></param>
- /// <returns></returns>
- public async Task<bool> UnlockNozzleAndNotifyAllFdcClientsAsync(int unlockingClientId, string fdcClientAppSender, int fdcClientRequestId, int pumpId,
- int logicalNozzleId)
- {
- try
- {
- fdcLogger.LogDebug("UnlockNozzleAndNotifyAllFdcClientsAsync from lockingClientId: " + unlockingClientId
- + ", fdcClientAppSender: " + (fdcClientAppSender ?? "")
- + ", fdcClientRequestId: " + fdcClientRequestId
- + ", pumpId: " + pumpId + ", logicalNozzleId: " + logicalNozzleId);
- var targetController = fdcPumpControllers
- .First(c => c.PumpId == pumpId) as IFdcPumpController;
- var result = await targetController.UnlockNozzleAsync((byte)logicalNozzleId);
- if (result)
- {
- // null or empty appSenderId indicates the clear request is not send from logged in FdcClient, but from some other source, like
- // litefccCore internal app, then do not reply a response.
- if (!string.IsNullOrEmpty(fdcClientAppSender))
- fdcServer.UnlockNozzle(unlockingClientId.ToString(), fdcClientAppSender, fdcClientRequestId,
- pumpId, pumpId,
- logicalNozzleId,
- (int)ErrorCode.ERRCD_OK, OverallResult.Success.ToString());
- }
- else
- {
- fdcLogger.LogDebug("UnlockNozzleAndNotifyAllFdcClientsAsync failed");
- // null or empty appSenderId indicates the clear request is not send from logged in FdcClient, but from some other source, like
- // litefccCore internal app, then do not reply a response.
- if (!string.IsNullOrEmpty(fdcClientAppSender))
- fdcServer.UnlockNozzle(unlockingClientId.ToString(), fdcClientAppSender, fdcClientRequestId,
- pumpId, pumpId,
- logicalNozzleId, (int)ErrorCode.ERRCD_NOTPOSSIBLE, OverallResult.Failure.ToString());
- }
- return result;
- }
- catch (Exception exx)
- {
- fdcLogger.LogError("UnlockNozzleAndNotifyAllFdcClientsAsync exceptioned for lockingClientId: "
- + unlockingClientId + ", pumpId: " + pumpId + ", logicalNozzleId: " + logicalNozzleId + ", exception detail: " + exx);
- fdcServer.UnlockNozzle(unlockingClientId.ToString(), fdcClientAppSender, fdcClientRequestId,
- pumpId, pumpId,
- logicalNozzleId, (int)ErrorCode.ERRCD_NOTALLOWED, OverallResult.Failure.ToString());
- return false;
- }
- }
- /// <summary>
- /// Query the totalizer value (money and volume) with decimal point, with default timeout 10 seconds.
- /// </summary>
- /// <param name="pumpId"></param>
- /// <param name="nozzleLogicalId">from 1 to N</param>
- /// <returns>With decimal points value, Money:Volume</returns>
- [UniversalApi(Description = "returned value are With decimal points value, format-> Money:Volume")]
- public async Task<Tuple<double, double>> GetFuelPointTotalsAsync(int pumpId, byte nozzleLogicalId)
- {
- try
- {
- if (fdcLogger.IsEnabled(LogLevel.Debug))
- fdcLogger.LogDebug("GetFuelPointTotals for pumpId: " + pumpId + ", nozzleLogicalId: " + nozzleLogicalId);
- var targetController = fdcPumpControllers.First(c => c.PumpId == pumpId) as IFdcPumpController;
- var totalizerResultTask = targetController.QueryTotalizerAsync(nozzleLogicalId);
- await Task.WhenAny(totalizerResultTask, Task.Delay(10000));
- if (totalizerResultTask.IsCompleted)
- {
- if (fdcLogger.IsEnabled(LogLevel.Debug))
- fdcLogger.LogDebug(" GetFuelPointTotals for pumpId: " + pumpId
- + ", nozzleLogicalId: " + nozzleLogicalId
- + ", no decimal point Money<->Volume pair is: " + totalizerResultTask.Result.Item1 + "<->" + totalizerResultTask.Result.Item2);
- return new Tuple<double, double>(
- totalizerResultTask.Result.Item1 / Math.Pow(10, targetController.AmountDecimalDigits),
- totalizerResultTask.Result.Item2 / Math.Pow(10, targetController.VolumeDecimalDigits));
- }
- else
- {
- fdcLogger.LogInformation(" GetFuelPointTotals for pumpId: " + pumpId
- + ", nozzleLogicalId: " + nozzleLogicalId + " timed out.");
- return new Tuple<double, double>(-1, -1);
- }
- }
- catch (Exception ex)
- {
- fdcLogger.LogError("GetFuelPointTotals for pumpId:" + pumpId +
- ", nozzleLogicalId: " + nozzleLogicalId + " exception:" + ex.Message);
- return new Tuple<double, double>(-1, -1);
- }
- }
- /// <summary>
- /// Query the IFdcPumpController
- /// </summary>
- /// <param name="pumpId"></param>
- /// <returns>IFdcPumpController</returns>
- public IFdcPumpController GetFdcPumpController(int pumpId)
- {
- if (fdcLogger.IsEnabled(LogLevel.Debug))
- fdcLogger.LogDebug("GetFdcPumpController for pumpId: " + pumpId);
- var targetController = fdcPumpControllers
- .First(c => c.PumpId == pumpId) as IFdcPumpController;
- return targetController;
- }
- /// <summary>
- /// Get the fuel sale trxs with details which are in state: Undefined, Payable, or Locked for target Pump.
- /// NOTE, for performance purpose, internal throttle is enabled to only return latest 365 days
- /// or latest 1000 rows of data.
- /// </summary>
- /// <param name="pumpId">the target pump id, or -1 which means the target is all pumps</param>
- /// <returns>list of FuelSaleTransaction, the most recent trx is in more front position</returns>
- public async Task<IEnumerable<FuelSaleTransaction>> GetAvailableFuelSaleTrxsWithDetailsAsync(int pumpId)
- {
- return await this.GetAvailableFuelSaleTrxsWithDetailsAsync(pumpId, -1, 100);
- }
- /// <summary>
- /// Get the fuel sale trxs with details which are in state: Undefined, Payable, or Locked for target Pump.
- /// </summary>
- /// <param name="pumpId">the target pump id, or -1 which means the target is all pumps</param>
- /// <param name="nozzleLogicalId">further specify the nozzle logical id, or -1 which means all nozzles in the pump.</param>
- /// <param name="rowCount">limit the returned row count</param>
- /// <returns>list of FuelSaleTransaction, the most recent trx is in more front position</returns>
- [UniversalApi(Description = "get the latest fuel transactions with state Payable or Locked.")]
- public async Task<IEnumerable<FuelSaleTransaction>> GetAvailableFuelSaleTrxsWithDetailsAsync(int pumpId, int nozzleLogicalId, int rowCount)
- {
- // for limit data size, only show latest N days' data.
- int maxReturnDays = 365;
- // for further limit data size, only show latest N count of data.
- int maxReturnDataCount = rowCount;
- try
- {
- var dueDate = DateTime.Now.Subtract(new TimeSpan(maxReturnDays, 0, 0, 0));
- SqliteDbContext dbContext = new SqliteDbContext();
- if (pumpId == -1)
- {
- if (nozzleLogicalId == -1)
- {
- var all = await dbContext.PumpTransactionModels.Where(t => t.State != FuelSaleTransactionState.Paid
- && t.State != FuelSaleTransactionState.Cleared
- && t.SaleStartTime >= dueDate).OrderByDescending(r => r.SaleStartTime).Take(maxReturnDataCount).ToListAsync();
- return all;
- }
- else
- {
- var all = await dbContext.PumpTransactionModels.Where(t => t.LogicalNozzleId == nozzleLogicalId
- && t.State != FuelSaleTransactionState.Paid
- && t.State != FuelSaleTransactionState.Cleared
- && t.SaleStartTime >= dueDate).OrderByDescending(r => r.SaleStartTime).Take(maxReturnDataCount).ToListAsync();
- return all;
- }
- }
- if (nozzleLogicalId == -1)
- {
- var some = await dbContext.PumpTransactionModels.Where(t => t.PumpId == pumpId
- && t.State != FuelSaleTransactionState.Paid
- && t.State != FuelSaleTransactionState.Cleared
- && t.SaleStartTime >= dueDate).OrderByDescending(r => r.SaleStartTime).Take(maxReturnDataCount).ToListAsync();
- return some;
- }
- else
- {
- var some = await dbContext.PumpTransactionModels.Where(t => t.PumpId == pumpId
- && t.LogicalNozzleId == nozzleLogicalId
- && t.State != FuelSaleTransactionState.Paid
- && t.State != FuelSaleTransactionState.Cleared
- && t.SaleStartTime >= dueDate)
- .OrderByDescending(r => r.SaleStartTime).Take(maxReturnDataCount).ToListAsync();
- return some;
- }
- }
- catch (Exception exxx)
- {
- fdcLogger.LogError("GetAvailableFuelSaleTrxsWithDetails for pumpId: " + pumpId
- + ", nozzleLogicalId: " + nozzleLogicalId
- + ", rowCount: " + rowCount + " exceptioned: "
- + exxx.ToString());
- return null;
- }
- }
- /// <summary>
- /// Get the fuel sale trxes with details by varies of conditions.
- /// </summary>
- /// <param name="pumpId">the target pump id, or -1 for all pumps</param>
- /// <param name="nozzleLogicalId">further specify the nozzle logical id, or -1 for all nozzles in the pump.</param>
- /// <param name="pageRowCount">for paging the returned data, controls how many data rows in a single page</param>
- /// <param name="pageIndex">for paging the returned data, controls return which data page</param>
- /// <param name="filterTimestamp">controls trx that only with its SaleStartTime >= filterTimestamp will be returned, leave null to ignore this check, the results are ordered by SaleStartTime (with ascending).</param>
- /// <returns></returns>
- [UniversalApi(Description = "get the latest fuel transactions by conditions.</br>" +
- "<param name=\"pumpId\">the target pump id, or null for all pumps</param></br>" +
- "<param name=\"nozzleLogicalId\">further specify the nozzle logical id, or null for all nozzles in the pump.</param></br>" +
- "<param name=\"pageRowCount\">for paging the returned data, controls how many data rows in a single page</param></br>" +
- "<param name=\"pageIndex\">for paging the returned data, controls return which data page</param></br>" +
- "<param name=\"filterTimestamp\">controls trx that only with its SaleStartTime >= filterTimestamp will be returned, leave null to ignore this check, the results are ordered by SaleStartTime (with default ascending)</param>")]
- public async Task<IEnumerable<FuelSaleTransaction>> GetFuelSaleTrxsWithDetailsAsync(int? pumpId, int? nozzleLogicalId,
- FuelSaleTransactionState transactionState,
- int pageRowCount = 10, int pageIndex = 0, DateTime? filterTimestamp = null, bool isAscending = true)
- {
- // for limit data size, only show latest N days' data.
- int maxReturnDays = 60;
- try
- {
- var dueDate = DateTime.Now.Subtract(new TimeSpan(maxReturnDays, 0, 0, 0));
- if (filterTimestamp != null)
- dueDate = filterTimestamp.Value;
- SqliteDbContext dbContext = new SqliteDbContext();
- if (!pumpId.HasValue)
- {
- IOrderedQueryable<FuelSaleTransaction> orderedResults = null;
- if (isAscending)
- orderedResults = dbContext.PumpTransactionModels.Where(t => t.State == transactionState
- && t.SaleStartTime >= dueDate).OrderBy(r => r.SaleStartTime);
- else
- orderedResults = dbContext.PumpTransactionModels.Where(t => t.State == transactionState
- && t.SaleStartTime >= dueDate).OrderByDescending(r => r.SaleStartTime);
- var all = await orderedResults.Skip(pageRowCount * pageIndex).Take(pageRowCount).ToListAsync();
- return all;
- }
- if (!nozzleLogicalId.HasValue)
- {
- IOrderedQueryable<FuelSaleTransaction> orderedResults = null;
- if (isAscending)
- orderedResults = dbContext.PumpTransactionModels.Where(t => t.PumpId == pumpId
- && t.State == transactionState && t.SaleStartTime >= dueDate).OrderBy(r => r.SaleStartTime);
- else
- orderedResults = dbContext.PumpTransactionModels.Where(t => t.PumpId == pumpId
- && t.State == transactionState && t.SaleStartTime >= dueDate).OrderByDescending(r => r.SaleStartTime);
- var some = await orderedResults.Skip(pageRowCount * pageIndex).Take(pageRowCount).ToListAsync();
- return some;
- }
- else
- {
- IOrderedQueryable<FuelSaleTransaction> orderedResults = null;
- if (isAscending)
- orderedResults = dbContext.PumpTransactionModels.Where(t => t.PumpId == pumpId
- && t.LogicalNozzleId == nozzleLogicalId
- && t.State == transactionState && t.SaleStartTime >= dueDate).OrderBy(r => r.SaleStartTime);
- else
- orderedResults = dbContext.PumpTransactionModels.Where(t => t.PumpId == pumpId
- && t.LogicalNozzleId == nozzleLogicalId
- && t.State == transactionState && t.SaleStartTime >= dueDate).OrderByDescending(r => r.SaleStartTime);
- var some = await orderedResults.Skip(pageRowCount * pageIndex).Take(pageRowCount).ToListAsync();
- return some;
- }
- }
- catch (Exception exxx)
- {
- fdcLogger.LogError($"GetFuelSaleTrxsWithDetailsAsync for pumpId: { pumpId }, nozzleLogicalId: { nozzleLogicalId }, " +
- $"pageRowCount: { pageRowCount }, pageIndex: {pageIndex}, filterTimestamp: {filterTimestamp ?? DateTime.MinValue}, isAscending: {isAscending}, exceptioned:{exxx}");
- return null;
- }
- }
- /// <summary>
- /// Change a price for pumps that have the target barcode product configurated for their nozzles.
- /// </summary>
- /// <param name="barcode">fuel product barcode</param>
- /// <param name="newPriceWithDecimalPoints">new price with decimal points</param>
- /// <returns>the successfully price changed nozzle will be returned</returns>
- [UniversalApi]
- public async Task<IEnumerable<LogicalNozzle>> ChangeFuelPriceAsync(int barcode, double newPriceWithDecimalPoints)
- {
- try
- {
- fdcLogger.LogInformation("ChangeFuelPrice for product with barcode: " + barcode
- + " to new price(with decimal points): " + newPriceWithDecimalPoints);
- var targetNozzles = fdcPumpControllers.SelectMany(p => p.Nozzles).Join(
- nozzleExtraInfos.Where(c => c.ProductBarcode == barcode),
- n => n.PumpId + "," + n.LogicalId,
- c => c.PumpId + "," + c.NozzleLogicalId,
- (n, c) => n);
- if (!targetNozzles.Any())
- {
- fdcLogger.LogError("ChangeFuelPrice, have NOT seen product with barcode: " + barcode
- + " bound to any nozzles from all pumps, will quit.");
- return null;
- }
- //var oldPriceWithNoHumanReadableFormat = targetNozzles.First().RealPriceOnPhysicalPump;
- var succeedNozzles = new List<LogicalNozzle>();
- foreach (var nozzle in targetNozzles)
- {
- bool succeed = false;
- var targetController = fdcPumpControllers.First(p => p.PumpId == nozzle.PumpId);
- var pumpRawFormatPrice = (int)(
- Math.Round(newPriceWithDecimalPoints * Math.Pow(10, targetController.PriceDecimalDigits),
- MidpointRounding.AwayFromZero));
- var previousPrice = targetController.Nozzles
- .First(n => n.LogicalId == nozzle.LogicalId)?.RealPriceOnPhysicalPump;
- //if (previousPrice.HasValue
- // && previousPrice.Value == pumpRawFormatPrice)
- //{
- // succeed = true;
- // fdcLogger.LogInformation(" price change, Pump " + targetController.PumpId + ", NozzleLogicalId " + nozzle.LogicalId + ", new price EQUALS the previous price, will NOT perform ChangeFuelPrice request");
- //}
- //else
- //{
- FuelPriceChange dbFuelPriceChange = null;
- try
- {
- dbFuelPriceChange = new FuelPriceChange()
- {
- PumpId = nozzle.PumpId,
- LogicalNozzleId = nozzle.LogicalId,
- NewPriceWithoutDecimal = pumpRawFormatPrice,
- StartTime = DateTime.Now,
- FinishTime = null
- };
- var dbContext = new SqliteDbContext();
- dbContext.FuelPriceChanges.Add(dbFuelPriceChange);
- await dbContext.SaveChangesAsync();
- //fdcLogger.LogDebug(" ChangeFuelPrice, new price for pump: " + targetController.PumpId + " pre-save into database succeed");
- }
- catch (Exception exxx)
- {
- fdcLogger.LogError(" ChangeFuelPrice, new price pre-save into database for pump: "
- + targetController.PumpId + ", logical Nozzle: " + nozzle.LogicalId
- + " exceptioned: " + exxx
- + Environment.NewLine
- + "Will ignore this nozzle and continue to next...");
- continue;
- }
- try
- {
- succeed = await targetController.ChangeFuelPriceAsync(pumpRawFormatPrice, nozzle.LogicalId);
- fdcLogger.LogInformation(" ChangeFuelPrice succeed in IFdcController side for pump with PumpId: " + targetController.PumpId
- + ", LogicalNozzleId: " + nozzle.LogicalId);
- }
- catch (Exception exxx)
- {
- fdcLogger.LogError(" ChangeFuelPrice, pump " + targetController.PumpId
- + ", logical nozzle " + nozzle.LogicalId + " exceptioned in pump side: "
- + exxx
- + Environment.NewLine
- + "Will continue to next nozzle.");
- continue;
- }
- if (succeed)
- {
- succeedNozzles.Add(nozzle);
- //fdcLogger.LogDebug("ChangeFuelPrice for pump: " + targetController.PumpId
- // + ", logicalNozzle: " + nozzle.LogicalId + " succeed on pump side, will post-save to db");
- try
- {
- dbFuelPriceChange.FinishTime = DateTime.Now;
- var dbContext = new SqliteDbContext();
- await dbContext.SaveChangesAsync();
- }
- catch (Exception exxx)
- {
- fdcLogger.LogError(" ChangeFuelPrice, new price post-save into database for pump: "
- + targetController.PumpId
- + ", logicalNozzle: " + nozzle.LogicalId + " exceptioned: "
- + exxx
- + Environment.NewLine
- + "Will ignore and continue to next nozzle");
- }
- }
- else
- {
- fdcLogger.LogError(" ChangeFuelPrice, Pump " + targetController.PumpId
- + ", NozzleLogicalId " + nozzle.LogicalId + " apply new price failed on pump side");
- }
- }
- return succeedNozzles;
- }
- catch (Exception exxx)
- {
- fdcLogger.LogError("ChangeFuelPrice, generic exception: " + exxx);
- return null;
- }
- }
- #endregion
- }
- }
|