1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629 |
- 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.Concurrent;
- 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(
- "支持外部延迟授权模式的Fdc服务器App",
- "支持外部延迟授权模式的Fdc服务器App,并提供基于 IFSF-POS-FDC 协议定义的各类接口",
- new[] { "lang-zh-cn:延迟授权FdcServerlang-en-us:DelayAuthFdcServer" })]
- public class FdcServerHostAppDelayAuth : 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()
- {
- 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 = "Leave input <b>null</b> to retrieve alarm data for all nozzles" +
- "</br>input parameters are as follow, " +
- "</br>para.Name==\"NozzleId\" Site level nozzle Id, leave 0 for all nozzles" +
- "</br>\"PumpId\" the target pump id")]
- public async Task<object> GetAvailableTransactions(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;
- }
- [UniversalApi(Description = "Get Ifsf Fdc TLG Configuration, this data also could be get via Ifsf Fdc Protocol")]
- public async Task<IEnumerable<Grouping<Tank, Grouping<int, byte>>>> GetIfsfFdcTLGConfiguration()
- {
- /* 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()
- {
- //using the info from nozzle config
- ProductCode = tg.First().ProductBarcode.ToString(),
- ProductLabel = tg.First().ProductName
- }
- };
- //there're cases that the ATG side may has wrong product code and name,
- //so we perfer use the product code and name from nozzle config.
- var forceMapTankProductInfoFromConfigFile = true;
- if (forceMapTankProductInfoFromConfigFile)
- {
- tank.Product = new Product()
- {
- ProductCode = tg.First().ProductBarcode.ToString(),
- ProductLabel = tg.First().ProductName
- };
- }
- 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);
- });
- return tankGroups;
- }
- private bool config_AutoAuthCallingPumps;
- private int config_MaxStackUnpaidTrxPerPump;
- private int config_ListeningPort;
- private bool config_removeDelayAuthParameterAfterUseIt = true;
- 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");
- 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 FdcServerHostAppDelayAuth(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");
- //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 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 override string ToString()
- {
- if (this.Count > 0)
- return $"Key: {Key.ToString()} contains: {base.Count} items, they're: {this.Select(i => i.ToString()).Aggregate((acc, n) => acc + " | " + n)}";
- else
- return $"Key: {Key.ToString()} contains: 0 items";
- }
- }
- private class DelayAuthParameter
- {
- public int PumpId { get; set; }
- public IEnumerable<byte> NozzleLogicalIds { get; set; }
- public double? MaxTrxAmount { get; set; }
- public double? MaxTrxVolume { get; set; }
- public DateTime Timestamp { get; set; }
- public string WorkstationID { get; set; }
- public string ApplicationSender { get; set; }
- }
- /// <summary>
- /// key is the pumpid
- /// </summary>
- private ConcurrentDictionary<int, DelayAuthParameter> DelayAuthParameters = new ConcurrentDictionary<int, DelayAuthParameter>();
- 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 failed");
- fdcLogger.LogDebug(" OnLockFuelSaleTrxReq done");
- }
- catch (Exception exxx)
- {
- fdcLogger.LogError("OnLockFuelSaleTrxReq exceptioned: " + exxx.ToString());
- fdcServer.LockFuelSaleTrx(workstationID, applicationSender, requestId, deviceId,
- transactionNo, releaseToken, (int)ErrorCode.ERRCD_TRANSLOCKED, 1, OverallResult.Failure.ToString());
- }
- };
- 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());
- fdcServer.UnlockFuelSaleTrx(workstationID, applicationSender, requestId, deviceId,
- transactionNo, releaseToken, (int)ErrorCode.ERRCD_TRANSLOCKED, 1, OverallResult.Failure.ToString());
- }
- };
- #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())
- fdcServer.GetTankDataSend(workstationID, applicationSender, requestID,
- (int)ErrorCode.ERRCD_NOTPOSSIBLE, OverallResult.DeviceUnavailable.ToString());
- var deviceHandler = this.autoTankGaugeControllers.FirstOrDefault();
- if (deviceHandler == null)
- fdcServer.GetTankDataSend(workstationID, applicationSender, requestID,
- (int)ErrorCode.ERRCD_NOTPOSSIBLE, OverallResult.DeviceUnavailable.ToString());
- var tankReading = await deviceHandler.GetTankReadingAsync(deviceId);
- fdcLogger.LogDebug($" GetTankProbeReadingsAsync 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());
- }
- //try
- //{
- // fdcLogger.LogDebug("OnGetTankDataReq (wid: " + workstationID + ", appSender: " + applicationSender + ", requestId: " + requestID + ", deviceId: " + deviceId + ", tankNo: " + tankNo + ")");
- // fdcServer.GetTankDataAdd(workstationID, applicationSender, requestID,
- // deviceId,
- // tankNo, 0, (int)ErrorCode.ERRCD_OK,
- // new Random().Next(100, 9999),
- // new Random().Next(100, 9999),
- // new Random().Next(100, 9999),
- // new Random().Next(100, 9999),
- // new Random().Next(100, 9999),
- // 0,
- // (int)ErrorCode.ERRCD_OK, (int)LogicalDeviceState.FDC_READY);
- // fdcServer.GetTankDataSend(workstationID, applicationSender, requestID, (int)ErrorCode.ERRCD_OK, OverallResult.Success.ToString());
- // return;
- // if (this.autoTankGaugeControllers == null || !this.autoTankGaugeControllers.Any())
- // fdcServer.GetTankDataSend(workstationID, applicationSender, requestID,
- // (int)ErrorCode.ERRCD_NOTPOSSIBLE, OverallResult.DeviceUnavailable.ToString());
- // var deviceHandler = this.autoTankGaugeControllers.FirstOrDefault();
- // if (deviceHandler == null)
- // fdcServer.GetTankDataSend(workstationID, applicationSender, requestID,
- // (int)ErrorCode.ERRCD_NOTPOSSIBLE, OverallResult.DeviceUnavailable.ToString());
- // var tankReading = await deviceHandler.GetTankReadingAsync(tankNo);
- // fdcLogger.LogDebug($" GetTankProbeReadingsAsync for tankNumber: " + tankNo + " succeed " +
- // Environment.NewLine + tankReading.ToString());
- // fdcServer.GetTankDataAdd(workstationID, applicationSender, requestID,
- // deviceId,
- // tankNo, 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 = this.nozzleExtraInfos.GroupBy(p => p.ProductBarcode);
- foreach (var b in allProductBarcodes)
- fdcServer.GetProductTableAdd(workstationID, applicationSender, requestID, b.Key, (b.FirstOrDefault()?.ProductName ?? ("refer cloud" + b.Key)));
- fdcServer.GetProductTableSend(workstationID, applicationSender, requestID, (int)ErrorCode.ERRCD_OK, OverallResult.Success.ToString());
- };
- fdcServer.OnGetConfigurationReq += async (string workstationID, string applicationSender,
- int requestId, string deviceType) =>
- {
- try
- {
- fdcLogger.LogDebug("OnGetConfigurationReq (wid: " + workstationID + ", appSender: " + applicationSender + ", requestId: " + requestId + ", deviceType: " + deviceType);
- if (deviceType == "TLG")
- {
- var tankGroups = await this.GetIfsfFdcTLGConfiguration();
- 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
- + ", fuellingType: " + fuellingType
- + ", 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;
- // }
- //}
- if (fuellingType == 8)
- {
- /*indicates the request purpose is to set(update or add) a delayAuthParameter*/
- var targetController = fdcPumpControllers.FirstOrDefault(c => c.PumpId == deviceId);
- if (targetController == null)
- {
- fdcServer.AuthoriseFuelPoint(workstationID, applicationSender, requestId, -99, releaseToken, deviceId,
- (int)LogicalDeviceState.FDC_READY, (int)ErrorCode.ERRCD_BADDEVID, OverallResult.WrongDeviceNo.ToString());
- }
- else
- {
- this.DelayAuthParameters.AddOrUpdate(deviceId, newPumpId => new DelayAuthParameter()
- {
- PumpId = newPumpId,
- NozzleLogicalIds = null,
- ApplicationSender = applicationSender,
- WorkstationID = workstationID,
- MaxTrxAmount = (maxTrxAmount == 0 ? (double?)null : maxTrxAmount),
- MaxTrxVolume = (maxTrxVolume == 0 ? (double?)null : maxTrxVolume),
- Timestamp = DateTime.Now,
- }, (existedPumpId, existed) =>
- {
- existed.ApplicationSender = applicationSender;
- existed.WorkstationID = workstationID;
- existed.MaxTrxAmount = (maxTrxAmount == 0 ? (double?)null : maxTrxAmount);
- existed.MaxTrxVolume = (maxTrxVolume == 0 ? (double?)null : maxTrxVolume);
- existed.Timestamp = DateTime.Now;
- return existed;
- });
- //if (this.DelayAuthParameters.TryGetValue(deviceId, out DelayAuthParameter found))
- //{
- // fdcLogger.LogDebug(" Updating a DelayAuthParameter...");
- // found.ApplicationSender = applicationSender;
- // found.WorkstationID = workstationID;
- // found.MaxTrxAmount = (maxTrxAmount == 0 ? (double?)null : maxTrxAmount);
- // found.MaxTrxVolume = (maxTrxVolume == 0 ? (double?)null : maxTrxVolume);
- // found.Timestamp = DateTime.Now;
- //}
- //else
- //{
- // fdcLogger.LogDebug(" Adding a DelayAuthParameter...");
- // this.DelayAuthParameters.(deviceId, );
- //}
- fdcServer.AuthoriseFuelPoint(workstationID, applicationSender, requestId, -8, releaseToken, deviceId,
- (int)LogicalDeviceState.FDC_READY, (int)ErrorCode.ERRCD_OK, OverallResult.Success.ToString());
- }
- }
- else if (fuellingType == 9)
- {
- /*indicates the request purpose is to remove a delayAuthParameter*/
- if (this.DelayAuthParameters.TryRemove(deviceId, out DelayAuthParameter _))
- fdcServer.AuthoriseFuelPoint(workstationID, applicationSender, requestId, -9, releaseToken, deviceId,
- (int)LogicalDeviceState.FDC_READY, (int)ErrorCode.ERRCD_OK, OverallResult.Success.ToString());
- else
- fdcServer.AuthoriseFuelPoint(workstationID, applicationSender, requestId, -9, releaseToken, deviceId,
- (int)LogicalDeviceState.FDC_READY, (int)ErrorCode.ERRCD_INOP, OverallResult.Failure.ToString());
- }
- else
- {
- 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 + ")");
- try
- {
- /* interpreted as Remove delay auth parameter has no special reason but for a rush request from customer */
- fdcLogger.LogInformation(" OnTerminateFuellingReq will be interpreted as Remove delay auth parameter");
- if (this.DelayAuthParameters.TryRemove(deviceId, out DelayAuthParameter _))
- fdcServer.TerminateFuellingSend(workstationID, applicationSender, requestId, (int)ErrorCode.ERRCD_OK, OverallResult.Success.ToString());
- else
- {
- fdcLogger.LogInformation(" OnTerminateFuelling for deviceId: " + deviceId + " has not found DelayAuth set, however still give a Success response.");
- fdcServer.TerminateFuellingSend(workstationID, applicationSender, requestId, (int)ErrorCode.ERRCD_OK, OverallResult.Success.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());
- fdcServer.ClearFuelSaleTrx(workstationID, applicationSender, requestId, deviceId, transactionNo, releaseToken,
- (int)ErrorCode.ERRCD_BADVAL, 1, OverallResult.Failure.ToString());
- }
- };
- // 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)
- {
- foreach (var pumpController in fdcPumpControllers)
- {
- 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 if (nozzleId == -1)
- {
- var pumpController = fdcPumpControllers
- .First(c => c.PumpId == deviceId) as IFdcPumpController;
- 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
- {
- var targetController = fdcPumpControllers
- .First(c => c.PumpId == deviceId) as IFdcPumpController;
- 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 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);
- }
- //fdcLogger.LogDebug("@@@@@@@@@@@Pump " + pump.PumpId
- // + " StateChanged bitmap is: " + nozzleUpOrDownBitMap);
- fdcServer.DeviceStateChange(Wayne.FDCPOSLibrary.DeviceType.DT_FuellingPoint, pump.PumpId,
- (int)(stateChangeArg.NewPumpState), (int)LogicalDeviceState.FDC_UNDEFINED, "", "", "",
- pump.Nozzles.Count(),
- nozzleUpOrDownBitMap, nozzleLockedOrUnlockedBitMap, 0);
- if (stateChangeArg.NewPumpState == LogicalDeviceState.FDC_CALLING)
- {
- //CloudRestClient.Default.UploadDataAsync(
- // new PumpDeviceUploadData_V1(
- // this.pumpControllersWithDeviceSerialNumbers.First(f => f.Item2 == pump).Item1,
- // DataPriorityLevel.Info)
- // {
- // PumpId = pump.PumpId,
- // LogicalNozzleId = (a.StateChangedNozzles != null ?
- // (a.StateChangedNozzles.Any() ? a.StateChangedNozzles.First().LogicalId : 1) : 1),
- // PumpState = "Calling",
- // }, null);
- if (this.config_AutoAuthCallingPumps)
- {
- if (this.config_MaxStackUnpaidTrxPerPump > 0)
- {
- var targetController = fdcPumpControllers
- .First(c => c.PumpId == pump.PumpId) as IFdcPumpController;
- 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(" Auto authorizing is not permit since pump " + targetController.PumpId + " have: " + unpaidTrxCount + " unpaid trx");
- return;
- }
- }
- //must use another thread for release the com port thread, otherwise the thread which from COM port I/O threadpool will get stuck in Authorize(autoresetEvent.waitone), and the next
- //Hanlder.Process() would never get called.
- //ThreadPool.QueueUserWorkItem(async o =>
- //{
- fdcLogger.LogDebug("Auto authorizing Pump: " + fdcPumpController.PumpId);
- //var result = fdcPumpController.Authorize(1);
- int autoAuthDefaultAmount = 4567;
- var result = await fdcPumpController.AuthorizeWithAmountAsync(
- (int)(autoAuthDefaultAmount * Math.Pow(10, fdcPumpController.AmountDecimalDigits)),
- 1);
- if (!result)
- fdcLogger.LogError("Auto auth Pump: " + fdcPumpController.PumpId + " FAILED!");
- //}
- //);
- }
- else
- {
- var targetController = fdcPumpControllers
- .First(c => c.PumpId == pump.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(" Delay authorizing is not permit since pump " + targetController.PumpId + " has: " + unpaidTrxCount + " unpaid trx");
- return;
- }
- }
- if (this.DelayAuthParameters.TryGetValue(pump.PumpId, out DelayAuthParameter delayAuthParameter))
- {
- fdcLogger.LogDebug("Delay authorizing Pump: " + targetController.PumpId);
- if (delayAuthParameter.MaxTrxAmount == null && delayAuthParameter.MaxTrxVolume == null)
- {
- int autoAuthDefaultAmount = 9999;
- var result = await targetController.AuthorizeWithAmountAsync(
- (int)(autoAuthDefaultAmount * Math.Pow(10, targetController.AmountDecimalDigits)),
- 1);
- if (!result)
- fdcLogger.LogError("Delay auth with defaultAmount to Pump: " + fdcPumpController.PumpId + " FAILED!");
- }
- else if (delayAuthParameter.MaxTrxVolume == null)
- {
- var result = await targetController.AuthorizeWithAmountAsync(
- (int)(delayAuthParameter.MaxTrxAmount.Value * Math.Pow(10, targetController.AmountDecimalDigits)),
- 1);
- if (!result)
- fdcLogger.LogError("Delay auth with para.MaxTrxAmount(" + (delayAuthParameter.MaxTrxAmount ?? -1) + ") to Pump: " + fdcPumpController.PumpId + " FAILED!");
- }
- else if (delayAuthParameter.MaxTrxAmount == null)
- {
- var result = await targetController.AuthorizeWithVolumeAsync(
- (int)(delayAuthParameter.MaxTrxVolume.Value * Math.Pow(10, targetController.VolumeDecimalDigits)),
- 1);
- if (!result)
- fdcLogger.LogError("Delay auth with para.MaxTrxVolume(" + (delayAuthParameter.MaxTrxVolume ?? -1) + ") Pump: " + fdcPumpController.PumpId + " FAILED!");
- }
- if (this.config_removeDelayAuthParameterAfterUseIt)
- this.DelayAuthParameters.TryRemove(pump.PumpId, out DelayAuthParameter _);
- }
- else
- {
- fdcLogger.LogDebug("Bypass Delay authorizing Pump: " + targetController.PumpId + " as no DelayAuthParameter was set for it.");
- }
- }
- }
- }
- catch (Exception exxx)
- {
- fdcLogger.LogError("fdcPumpController.OnStateChange exceptioned: " + exxx.ToString());
- }
- };
- 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 = DateTime.Now.Subtract(new TimeSpan(0, 3, 0)),
- 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
- 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()
- + ", pump Ids are: " + (fdcPumpControllers.Any() ?
- (fdcPumpControllers.Select(c => c.PumpId.ToString()).Aggregate((acc, n) => acc + ", " + n) + ")") : ")"));
- foreach (var fdcPumpController in 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.configurator != null && 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="clientId">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>
- public async Task<FuelSaleTransaction> ClearFuelSaleTrxAndNotifyAllFdcClientsAsync(int pumpId, string transactionNo, int trxDbUniqueId, string clientId)
- {
- return await this.ClearFuelSaleTrxAndNotifyAllFdcClientsAsync(pumpId, transactionNo, trxDbUniqueId, clientId, 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 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>
- 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 with: " + !(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 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),
- "refer cloud" + 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.LogDebug("@@@@@TryLockFuelSaleTrx 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 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>
- public async Task<FuelSaleTransaction> UnlockFuelSaleTrxAndNotifyAllFdcClientsAsync(int lockingClientId,
- int pumpId,
- int transactionNo, int trxDbUniqueId)
- {
- return await this.UnlockFuelSaleTrxAndNotifyAllFdcClientsAsync(lockingClientId, "", -1, pumpId, transactionNo, trxDbUniqueId);
- }
- /// <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));
- 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),
- "refer cloud" + 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>
- public async Task<Tuple<double, double>> GetFuelPointTotalsAsync(int pumpId, byte nozzleLogicalId)
- {
- 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);
- }
- }
- /// <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, 1000);
- }
- /// <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>
- 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;
- lock (this.syncObject)
- {
- try
- {
- var dueDate = DateTime.Now.Subtract(new TimeSpan(maxReturnDays, 0, 0, 0));
- SqliteDbContext dbContext = new SqliteDbContext();
- if (pumpId == -1)
- {
- if (nozzleLogicalId == -1)
- {
- var all = dbContext.PumpTransactionModels.Where(t => t.State != FuelSaleTransactionState.Paid
- && t.State != FuelSaleTransactionState.Cleared
- && t.SaleStartTime >= dueDate)
- .OrderByDescending(r => r.SaleStartTime).Take(maxReturnDataCount);
- return all;
- }
- else
- {
- var all = dbContext.PumpTransactionModels.Where(t => t.LogicalNozzleId == nozzleLogicalId
- && t.State != FuelSaleTransactionState.Paid
- && t.State != FuelSaleTransactionState.Cleared
- && t.SaleStartTime >= dueDate)
- .OrderByDescending(r => r.SaleStartTime).Take(maxReturnDataCount);
- return all;
- }
- }
- if (nozzleLogicalId == -1)
- {
- var some = dbContext.PumpTransactionModels.Where(t => t.PumpId == pumpId
- && t.State != FuelSaleTransactionState.Paid
- && t.State != FuelSaleTransactionState.Cleared
- && t.SaleStartTime >= dueDate)
- .OrderByDescending(r => r.SaleStartTime).Take(maxReturnDataCount);
- return some;
- }
- else
- {
- var some = 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);
- return some;
- }
- }
- catch (Exception exxx)
- {
- fdcLogger.LogError("GetAvailableFuelSaleTrxsWithDetails for pumpId: " + pumpId
- + ", nozzleLogicalId: " + nozzleLogicalId
- + ", rowCount: " + rowCount + " exceptioned: "
- + exxx.ToString());
- 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
- }
- }
|