123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698 |
- using HengshanPaymentTerminal.MessageEntity.Incoming;
- using HengshanPaymentTerminal.MessageEntity;
- using HengshanPaymentTerminal.Support;
- using HengshanPaymentTerminal;
- using System;
- using System.Collections.Concurrent;
- using System.Collections;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- using Edge.Core.Processor.Dispatcher.Attributes;
- using Edge.Core.IndustryStandardInterface.Pump;
- using Edge.Core.IndustryStandardInterface.Pump.Fdc;
- using Edge.Core.Processor;
- using Edge.Core.Core.database;
- using Edge.Core.Domain.FccStationInfo.Output;
- using Edge.Core.Domain.FccNozzleInfo;
- using Edge.Core.Domain.FccNozzleInfo.Output;
- using System.Net.Sockets;
- using Edge.Core.Domain.FccOrderInfo;
- using Microsoft.EntityFrameworkCore;
- using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities;
- using static Microsoft.AspNetCore.Hosting.Internal.HostingApplication;
- using HengshanPaymentTerminal.Mqtt.Request;
- using HengshanPaymentTerminal.Http;
- using HengshanPaymentTerminal.Http.Request;
- using System.Text.Json;
- using Newtonsoft.Json;
- using HengshanPaymentTerminal.Http.Response;
- using HengshanPaymentTerminal.MessageEntity.Outgoing;
- using Microsoft.IdentityModel.Tokens;
- using Org.BouncyCastle.Asn1.Ocsp;
- using Newtonsoft.Json.Linq;
- using System.Net;
- namespace HengshanPaymentTerminal
- {
- /// <summary>
- /// Handler that communicates directly with the Hengshan Payment Terminal for card handling and pump handling via serial port.
- /// </summary>
- [MetaPartsDescriptor(
- "lang-zh-cn:恒山IC卡终端(UI板) App lang-en-us:Hengshan IC card terminal (UI Board)",
- "lang-zh-cn:用于与UI板通讯控制加油机" +
- "lang-en-us:Used for terminal communication to control pumps",
- new[]
- {
- "lang-zh-cn:恒山IC卡终端lang-en-us:HengshanICTerminal"
- })]
- public class HengshanPayTermHandler : IEnumerable<IFdcPumpController>, IDeviceHandler<byte[], CommonMessage>
- {
- #region Fields
- private string pumpIds;
- private string pumpSubAddresses;
- private string pumpNozzles;
- private string pumpSiteNozzleNos;
- private string nozzleLogicIds;
- private IContext<byte[], CommonMessage> _context;
- private List<HengshanPumpHandler> pumpHandlers = new List<HengshanPumpHandler>();
- public Queue<CardMessageBase> queue = new Queue<CardMessageBase>();
- public Queue<CommonMessage> commonQueue = new Queue<CommonMessage>();
- private object syncObj = new object();
- private ConcurrentDictionary<int, PumpStateHolder> statusDict = new ConcurrentDictionary<int, PumpStateHolder>();
- public ConcurrentDictionary<int, PumpStateHolder> PumpStatusDict => statusDict;
- private Dictionary<int, int> pumpIdSubAddressDict;
- public Dictionary<int, List<int>> PumpNozzlesDict { get; private set; }
- public Dictionary<int, int> NozzleLogicIdDict { get; private set; }
- public Dictionary<int, List<int>> PumpSiteNozzleNoDict { get; private set; }
- //public MysqlDbContext MysqlDbContext { get; private set; }
- public StationInfo stationInfo { get; set; }
- public Dictionary<long,string> stationPayment = new Dictionary<long,string>();
- public List<DetailsNozzleInfoOutput> nozzleInfoList { get; private set; }
- public TcpClient? client { get; set; }
- public int? serverPort { get; set; }
- //private readonly ConcurrentDictionary<string,TaskCompletionSource<CommonMessage>> _tcsDictionary = new ConcurrentDictionary<string, TaskCompletionSource<CommonMessage>>();
- //private TaskCompletionSource<ErrorMessage> checkDisConnectTask = new TaskCompletionSource<ErrorMessage>();
- private byte frame = 0x00;
- private object lockFrame = new object();
- private IHttpClientUtil httpClientUtil;
- //记录油枪状态,key-枪号,value:01:离线 02:锁枪 03:空闲 04:提枪 06:开始加油 08:加油中
- private ConcurrentDictionary<int, int> nozzleStatusDic = new ConcurrentDictionary<int, int>();
- #endregion
- #region Logger
- private static NLog.Logger logger = NLog.LogManager.LoadConfiguration("NLog.config").GetLogger("IPosPlusApp");
- #endregion
- #region Constructor
- //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)
- // {
- // var curVersion = match.Value;
- // if (curVersion == "1.0")
- // {
- // var existsAppConfigV1 = JsonSerializer.Deserialize(jsonParams.First().GetRawText(), typeof(HengshanPayTerminalHanlderGroupConfigV1));
- // }
- // else
- // {
- // }
- // }
- // return null;
- //}
- [ParamsJsonSchemas("TermHandlerGroupCtorParamsJsonSchemas")]
- public HengshanPayTermHandler(HengshanPayTerminalHanlderGroupConfigV2 config)
- : this(config.PumpIds,
- string.Join(";", config.PumpSubAddresses.Select(m => $"{m.PumpId}={m.SubAddress}")),
- string.Join(";", config.PumpNozzleLogicIds.Select(m => $"{m.PumpId}={m.LogicIds}")),
- string.Join(";", config.PumpSiteNozzleNos.Select(m => $"{m.PumpId}={m.SiteNozzleNos}")),
- string.Join(";", config.NozzleLogicIds.Select(m => $"{m.NozzleNo}={m.LogicId}")))
- //clientUtil)
- {
- }
- public HengshanPayTermHandler(
- string pumpIds,
- string pumpSubAddresses,
- string pumpNozzles,
- string pumpSiteNozzleNos,
- string nozzleLogicIds)
- //IHttpClientUtil clientUtil)
- {
- this.pumpIds = pumpIds;
- this.pumpSubAddresses = pumpSubAddresses;
- this.pumpNozzles = pumpNozzles;
- this.pumpSiteNozzleNos = pumpSiteNozzleNos;
- this.nozzleLogicIds = nozzleLogicIds;
-
- AssociatedPumpIds = GetPumpIdList(pumpIds);
- pumpIdSubAddressDict = InitializePumpSubAddressMapping();
- PumpNozzlesDict = ParsePumpNozzlesList(pumpNozzles);
- PumpSiteNozzleNoDict = ParsePumpSiteNozzleNoList(pumpSiteNozzleNos);
- NozzleLogicIdDict = InitializeNozzleLogicIdMapping(nozzleLogicIds);
- InitializePumpHandlers();
- }
- #endregion
- public void OnFdcServerInit(Dictionary<string, object> parameters)
- {
- logger.Info("OnFdcServerInit called");
- if (parameters.ContainsKey("LastPriceChange"))
- {
- // nozzle logical id:rawPrice
- var lastPriceChanges = parameters["LastPriceChange"] as Dictionary<byte, int>;
- foreach (var priceChange in lastPriceChanges)
- {
- }
- }
- }
- #region Event handler
- public event EventHandler<TerminalMessageEventArgs> OnTerminalMessageReceived;
- public event EventHandler<TotalizerDataEventArgs> OnTotalizerReceived;
- public event EventHandler<FuelPriceChangeRequestEventArgs> OnFuelPriceChangeRequested;
- public event EventHandler<FuelPriceDownloadRequestedEventArgs> OnTerminalFuelPriceDownloadRequested;
- public event EventHandler<CheckCommandEventArgs> OnCheckCommandReceived;
- public event EventHandler<LockUnlockEventArgs> OnLockUnlockCompleted;
- #endregion
- #region Properties
- public List<int> AssociatedPumpIds { get; private set; }
- public IContext<byte[], CommonMessage> Context
- {
- get { return _context; }
- }
- public string PumpIdList => pumpIds;
- //public LockUnlockOperation LockUnlockOperationType { get; set; } = LockUnlockOperation.Undefined;
- #endregion
- #region Methods
- public int GetSubAddressForPump(int pumpId)
- {
- return pumpIdSubAddressDict.First(d => d.Key == pumpId).Value;
- }
- private List<int> GetPumpIdList(string pumpIds)
- {
- var pumpIdList = new List<int>();
- if (!string.IsNullOrEmpty(pumpIds) && pumpIds.Contains(',')) //multiple pumps per serial port, Hengshan TQC pump
- {
- var arr = pumpIds.Split(',');
- foreach (var item in arr)
- {
- pumpIdList.Add(int.Parse(item));
- }
- return pumpIdList;
- }
- else if (!string.IsNullOrEmpty(pumpIds) && pumpIds.Length == 1 || pumpIds.Length == 2) //only 1 pump per serial port, Hengshan pump
- {
- return new List<int> { int.Parse(pumpIds) };
- }
- else
- {
- throw new ArgumentException("Pump id list not specified!");
- }
- }
- private Dictionary<int, int> InitializePumpSubAddressMapping()
- {
- var dict = new Dictionary<int, int>();
- if (!string.IsNullOrEmpty(pumpSubAddresses))
- {
- var sequence = pumpSubAddresses.Split(';')
- .Select(s => s.Split('='))
- .Select(a => new { PumpId = int.Parse(a[0]), SubAddress = int.Parse(a[1]) });
- foreach (var pair in sequence)
- {
- if (!dict.ContainsKey(pair.PumpId))
- {
- dict.Add(pair.PumpId, pair.SubAddress);
- }
- }
- return dict;
- }
- else
- {
- throw new ArgumentException("Pump id and sub address mapping does not exist");
- }
- }
- private Dictionary<int, List<int>> ParsePumpNozzlesList(string pumpNozzles)
- {
- Dictionary<int, List<int>> pumpNozzlesDict = new Dictionary<int, List<int>>();
- if (!string.IsNullOrEmpty(pumpNozzles) && pumpNozzles.Contains(';'))
- {
- var arr = pumpNozzles.Split(';');
- foreach (var subMapping in arr)
- {
- var pair = new KeyValuePair<int, int>(int.Parse(subMapping.Split('=')[0]), int.Parse(subMapping.Split('=')[1]));
- Console.WriteLine($"{pair.Key}, {pair.Value}");
- if (!pumpNozzlesDict.ContainsKey(pair.Key))
- {
- pumpNozzlesDict.Add(pair.Key, new List<int> { pair.Value });
- }
- else
- {
- List<int> nozzlesForThisPump;
- pumpNozzlesDict.TryGetValue(pair.Key, out nozzlesForThisPump);
- if (nozzlesForThisPump != null && !nozzlesForThisPump.Contains(pair.Value))
- {
- nozzlesForThisPump.Add(pair.Value);
- }
- }
- }
- }
- else if (!string.IsNullOrEmpty(pumpNozzles) && pumpNozzles.Count(c => c == '=') == 1) // only one pump per serial port
- {
- try
- {
- pumpNozzlesDict.Add(
- int.Parse(pumpNozzles.Split('=')[0]),
- new List<int> { int.Parse(pumpNozzles.Split('=')[1]) });
- }
- catch (Exception ex)
- {
- Console.WriteLine(ex);
- }
- }
- else
- {
- throw new ArgumentException("Wrong mapping between pump and its associated nozzles!");
- }
- return pumpNozzlesDict;
- }
- static Dictionary<int, List<int>> ParsePumpSiteNozzleNoList(string pumpSiteNozzleNos)
- {
- Dictionary<int, List<int>> pumpSiteNozzleNoDict = new Dictionary<int, List<int>>();
- if (!string.IsNullOrEmpty(pumpSiteNozzleNos) && pumpSiteNozzleNos.Contains(';'))
- {
- var arr = pumpSiteNozzleNos.Split(';');
- foreach (var subMapping in arr)
- {
- var pair = new KeyValuePair<int, List<int>>(
- int.Parse(subMapping.Split('=')[0]), subMapping.Split('=')[1].Split(',').Select(a => int.Parse(a)).ToList());
- Console.WriteLine($"{pair.Key}, {pair.Value}");
- if (!pumpSiteNozzleNoDict.ContainsKey(pair.Key))
- {
- pumpSiteNozzleNoDict.Add(pair.Key, pair.Value);
- }
- }
- }
- else if (!string.IsNullOrEmpty(pumpSiteNozzleNos) && pumpSiteNozzleNos.Count(c => c == '=') == 1)
- {
- try
- {
- string[] strArr = pumpSiteNozzleNos.Split('=');
- pumpSiteNozzleNoDict.Add(
- int.Parse(strArr[0]), new List<int> { int.Parse(strArr[1]) });
- }
- catch (Exception ex)
- {
- Console.WriteLine(ex);
- }
- }
- else
- {
- throw new ArgumentException("Wrong mapping between pump and its associated nozzles!");
- }
- return pumpSiteNozzleNoDict;
- }
- private Dictionary<int, int> InitializeNozzleLogicIdMapping(string nozzleLogicIds)
- {
- var dict = new Dictionary<int, int>();
- if (!string.IsNullOrEmpty(nozzleLogicIds))
- {
- var sequence = nozzleLogicIds.Split(';')
- .Select(s => s.Split('='))
- .Select(a => new { NozzleNo = int.Parse(a[0]), LogicId = int.Parse(a[1]) });
- foreach (var pair in sequence)
- {
- if (!dict.ContainsKey(pair.NozzleNo))
- {
- Console.WriteLine($"nozzle, logic id: {pair.NozzleNo} - {pair.LogicId}");
- dict.Add(pair.NozzleNo, pair.LogicId);
- }
- }
- return dict;
- }
- else if (!string.IsNullOrEmpty(nozzleLogicIds) && nozzleLogicIds.Count(c => c == '=') == 1)
- {
- try
- {
- string[] sequence = nozzleLogicIds.Split('=');
- dict.Add(int.Parse(sequence[0]), int.Parse(sequence[1]));
- }
- catch (Exception ex)
- {
- Console.WriteLine(ex);
- }
- return dict;
- }
- else
- {
- throw new ArgumentException("Pump id and sub address mapping does not exist");
- }
- }
- private void InitializePumpHandlers()
- {
- var pumpIdList = GetPumpIdList(pumpIds);
- foreach (var item in pumpIdList)
- {
- var nozzleList = GetNozzleListForPump(item);
- var siteNozzleNoList = PumpSiteNozzleNoDict[item];
- HengshanPumpHandler pumpHandler = new HengshanPumpHandler(this, $"Pump_{item}", item, nozzleList, siteNozzleNoList);
- pumpHandler.OnFuelPriceChangeRequested += PumpHandler_OnFuelPriceChangeRequested;
- pumpHandlers.Add(pumpHandler);
- }
- }
- private List<int> GetNozzleListForPump(int pumpId)
- {
- List<int> nozzles;
- PumpNozzlesDict.TryGetValue(pumpId, out nozzles);
- return nozzles;
- }
- private void PumpHandler_OnFuelPriceChangeRequested(object sender, FuelPriceChangeRequestEventArgs e)
- {
- InfoLog($"Change price, Pump {e.PumpId}, Nozzle {e.NozzleId}, Price {e.Price}");
- OnFuelPriceChangeRequested?.Invoke(sender, e);
- }
- IEnumerator<IFdcPumpController> IEnumerable<IFdcPumpController>.GetEnumerator()
- {
- return pumpHandlers.GetEnumerator();
- }
- #endregion
- #region IHandler implementation
- public void Init(IContext<byte[], CommonMessage> context)
- {
- CommIdentity = context.Processor.Communicator.Identity;
- _context = context;
- //this.MysqlDbContext = new MysqlDbContext();
- this.httpClientUtil = new HttpClientUtils();
- this.serverPort = CommIdentity.Replace("*:", "").ToInt();
- GetInfo();
- }
- public string CommIdentity { get; private set; }
- public async Task Process(IContext<byte[], CommonMessage> context)
- {
-
- switch(context.Incoming.Message.Handle)
- {
- //心跳,带油枪状态信息
- case 0x10:
- {
- //将油枪状态区分为空闲或非空闲,记录在内存。当状态有发生变化,发送到云端
- HeartBeatMessage heartBeatMessage = (HeartBeatMessage)context.Incoming.Message;
- SendNozzleStatus(heartBeatMessage);
- break;
- }
- //订单
- case 0x18:
- {
- //添加或修改数据库订单
- OrderFromMachine orderFromMachine = (OrderFromMachine)context.Incoming.Message;
- FccOrderInfo fccOrderInfo = UpLoadOrder(orderFromMachine);
- logger.Info($"receive order from machine,database had change");
- CreateTransaction(fccOrderInfo);
- break;
- }
- /**
- //普通应答
- case 0x55:
- {
- CommonAnswerBack commonAnswerBack = (CommonAnswerBack)context.Incoming.Message;
- if (commonAnswerBack.Command == 0x63) //二维码回复
- {
- byte[] keyBytes = { commonAnswerBack.Command, (byte)commonAnswerBack.NozzleNum };
- var key = BitConverter.ToString(keyBytes).Replace("-", "");
- if (_tcsDictionary.TryGetValue(key, out var value))
- {
- value.SetResult(commonAnswerBack);
- }
- else
- {
- logger.Info($"qrcode response:can not get tcs for dictionary");
- }
- }
- break;
- }
- // 授权回复
- case 0x65:
- {
- AuthorizationResponse authorizationResponse = (AuthorizationResponse)context.Incoming.Message;
- byte[] keyBytes = { authorizationResponse.Handle, (byte)authorizationResponse.NozzleNum };
- var key = BitConverter.ToString(keyBytes).Replace("-", "");
- if (_tcsDictionary.TryGetValue(key, out var value))
- {
- value.SetResult(authorizationResponse);
- }
- else
- {
- logger.Info($"authorization response:can not get tcs for dictionary");
- }
- break;
- }
- // 取消授权回复
- case 0x66:
- {
- UnAhorizationResponse unauthorizationResponse = (UnAhorizationResponse)context.Incoming.Message;
- byte[] keyBytes = { unauthorizationResponse.Handle, (byte)unauthorizationResponse.NozzleNum };
- var key = BitConverter.ToString(keyBytes).Replace("-", "");
- if (_tcsDictionary.TryGetValue(key, out var value))
- {
- value.SetResult(unauthorizationResponse);
- }
- else
- {
- logger.Info($"unauthorization response:can not get tcs for dictionary");
- }
- break;
- }
- */
- }
-
- //油机的应答不用回复
- if(context.Incoming.Message.Handle != 0x55) context.Outgoing.Write(context.Incoming.Message);
- }
- private void CheckStatus(CheckCmdRequest request)
- {
- if (!statusDict.ContainsKey(request.FuelingPoint.PumpNo))
- {
- var result = statusDict.TryAdd(request.FuelingPoint.PumpNo,
- new PumpStateHolder
- {
- PumpNo = request.FuelingPoint.PumpNo,
- NozzleNo = 1,
- State = request,
- OperationType = LockUnlockOperation.None
- });
- logger.Info($"Adding FuelingPoint {request.FuelingPoint.PumpNo} to dict");
- if (!result)
- {
- statusDict.TryAdd(request.FuelingPoint.PumpNo, null);
- }
- }
- else
- {
- PumpStateHolder stateHolder = null;
- statusDict.TryGetValue(request.FuelingPoint.PumpNo, out stateHolder);
- if (stateHolder != null)
- {
- logger.Debug($"State holder, PumpNo: {stateHolder.PumpNo}, dispenser state: {stateHolder.State.DispenserState}, " +
- $"operation: {stateHolder.OperationType}");
- }
- if (stateHolder != null && stateHolder.OperationType != LockUnlockOperation.None)
- {
- logger.Debug($"PumpNo: {request.FuelingPoint.PumpNo}, Last Dispenser State: {stateHolder.State.DispenserState}, " +
- $"Current Dispenser State: {request.DispenserState}");
- if (stateHolder.State.DispenserState == 3 && request.DispenserState == 2)
- {
- //Pump is locked due to lock operation
- if (stateHolder.OperationType != LockUnlockOperation.None)
- {
- logger.Info("Locking done!");
- stateHolder.State = request; //Update the state
- OnLockUnlockCompleted?.Invoke(this, new LockUnlockEventArgs(stateHolder.OperationType, true));
- }
- }
- else if (stateHolder.State.DispenserState == 2 && request.DispenserState == 3)
- {
- //Pump is unlocked due to unlock operation
- if (stateHolder.OperationType != LockUnlockOperation.None)
- {
- logger.Info($"Unlocking done!");
- stateHolder.State = request; //Update the state
- OnLockUnlockCompleted?.Invoke(this, new LockUnlockEventArgs(stateHolder.OperationType, true));
- }
- }
- }
- else if (stateHolder != null && stateHolder.OperationType == LockUnlockOperation.None)
- {
- if (stateHolder.State.DispenserState != request.DispenserState)
- {
- logger.Warn($"Observed a pump state change, {stateHolder.State.DispenserState} -> {request.DispenserState}");
- stateHolder.State = request; //Update the state.
- }
- }
- }
- }
- public void Write(CommonMessage cardMessage)
- {
- _context.Outgoing.Write(cardMessage);
- }
- public async Task<CommonMessage> WriteAsync(CommonMessage request, Func<CommonMessage, CommonMessage, bool> responseCapture,
- int timeout)
- {
- var resp = await _context.Outgoing.WriteAsync(request, responseCapture, timeout);
- return resp;
- }
- #endregion
- #region IEnumerable<IFdcPumpController> implementation
- public IEnumerator<IFdcPumpController> GetEnumerator()
- {
- return pumpHandlers.GetEnumerator();
- }
- IEnumerator IEnumerable.GetEnumerator()
- {
- return pumpHandlers.GetEnumerator();
- }
- #endregion
- public void PendMessage(CardMessageBase message)
- {
- lock (syncObj)
- {
- queue.Enqueue(message);
- }
- }
- public bool TrySendNextMessage()
- {
- lock (syncObj)
- {
- if (queue.Count > 0)
- {
- DebugLog($"queue count: {queue.Count}");
- var message = commonQueue.Dequeue();
- Write(message);
- return true;
- }
- }
- return false;
- }
- public void StoreLatestFrameSqNo(int pumpId, byte frameSqNo)
- {
- var pump = GetPump(pumpId);
- if (pump != null)
- {
- pump.FrameSqNo = frameSqNo;
- }
- }
- public void UpdatePumpState(int pumpId, int logicId, LogicalDeviceState state)
- {
- var currentPump = GetPump(pumpId);
- currentPump?.FirePumpStateChange(state, Convert.ToByte(logicId));
- }
- public void UpdateFuelingStatus(int pumpId, FdcTransaction fuelingTransaction)
- {
- var currentPump = GetPump(pumpId);
- currentPump?.FireFuelingStatusChange(fuelingTransaction);
- }
- private HengshanPumpHandler GetPump(int pumpId)
- {
- return pumpHandlers.FirstOrDefault(p => p.PumpId == pumpId);
- }
- public void SetRealPrice(int pumpId, int price)
- {
- var currentPump = GetPump(pumpId);
- var nozzle = currentPump?.Nozzles.FirstOrDefault();
- if (nozzle != null)
- nozzle.RealPriceOnPhysicalPump = price;
- }
- #region Log methods
- private void InfoLog(string info)
- {
- logger.Info("PayTermHdlr " + info);
- }
- private void DebugLog(string debugMsg)
- {
- logger.Debug("PayTermHdlr " + debugMsg);
- }
- #endregion
- #region 二维码加油机相关方法
- /// <summary>
- /// 获取站点信息
- /// </summary>
- private async void GetInfo()
- {
- MysqlDbContext mysqlDbContext = new MysqlDbContext();
- Edge.Core.Domain.FccStationInfo.FccStationInfo? fccStationInfo = mysqlDbContext.FccStationInfos.FirstOrDefault();
- if(fccStationInfo != null)
- {
- stationInfo = new StationInfo(fccStationInfo);
- string paymentType = stationInfo.PaymentType;
- string[] paymentGround = paymentType.Split(",");
- foreach (var item in paymentGround)
- {
- string[] payment = item.Split("+");
- if (payment.Length == 2)
- {
- if (long.TryParse(payment[0], out long id))
- {
- stationPayment.Add(id, payment[1]);
- }
- }
- }
- }
- Edge.Core.Domain.FccMachineInfo.FccMachineInfo? fccMachineInfo = await mysqlDbContext.FccMachineInfos.FirstOrDefaultAsync(machine => machine.Port == serverPort);
- if(fccMachineInfo == null)
- {
- nozzleInfoList = new List<DetailsNozzleInfoOutput>();
- } else
- {
- nozzleInfoList = mysqlDbContext.NozzleInfos.Where(nozzle => nozzle.MachineId == fccMachineInfo.Id).Select(n => new DetailsNozzleInfoOutput(n)).ToList();
- }
-
- }
- /// <summary>
- /// 接收到MQTT
- /// </summary>
- /// <param name="message"></param>
- public async void OnReceiveMqttMessage(string message)
- {
- MqttRequest? mqttRequest = JsonConvert.DeserializeObject<MqttRequest>(message);
- if (mqttRequest == null)
- {
- logger.Error($"mqtt message turn on object fail,message:{message}");
- return;
- }
- switch (mqttRequest.type)
- {
- case MQTT_TYPE.AUTHORIZATION:
- {
-
- MqttAuthorizationRequest? mqttAuthorizationRequest = JsonConvert.DeserializeObject<MqttAuthorizationRequest>(mqttRequest.data);
- await SendAuthorizationAsync(mqttAuthorizationRequest);
- break;
- }
- case MQTT_TYPE.UNAUTHORIZATION:
- {
- MqttUnAhorizationRequest? mqttUnAhorizationRequest = JsonConvert.DeserializeObject<MqttUnAhorizationRequest>(mqttRequest.data);
- await SendUnAuthorizartion(mqttUnAhorizationRequest);
- break;
- }
- case MQTT_TYPE.PAID:
- {
- MqttPaidRequest? mqttPaidRequest = JsonConvert.DeserializeObject<MqttPaidRequest>(mqttRequest.data);
- await SendActuallyPaid(mqttPaidRequest, mqttRequest.UserName, mqttRequest.UserPhoneNumber);
- break;
- }
- case MQTT_TYPE.REFUND:
- {
- MqttRefundRequest? mqttRefundRequest = JsonConvert.DeserializeObject<MqttRefundRequest>(mqttRequest.data);
- await OnRecieveOrderRefund(mqttRefundRequest);
- break;
- }
- }
- }
- /// <summary>
- /// 发送二维码信息给油机
- /// </summary>
- /// <param name="tcpClient"></param>
- public async void SendQRCodeAsync()
- {
- string smallProgram = stationInfo?.SmallProgram ?? "";
- if (string.IsNullOrEmpty(smallProgram))
- {
- logger.Info($"can not get smallProgram link");
- return;
- }
-
- foreach (var item in nozzleInfoList)
- {
- //List<Byte> list = new List<Byte>();
- //byte[] commandAndNozzle = { 0x63, (byte)item.NozzleNum };
- //string qrCode = smallProgram + "/" + item.NozzleNum;
- //byte[] qrCodeBytes = Encoding.ASCII.GetBytes(qrCode);
- //list.AddRange(commandAndNozzle);
- //list.Add((byte)qrCodeBytes.Length);
- //list.AddRange(qrCodeBytes);
- //byte[] sendBytes = content2data(list.ToArray(), null);
- SendQrCode sendQrCode = new SendQrCode((int)item.CloundNozzleId, smallProgram, getFrame(null));
- byte[] commandAndNozzle = { sendQrCode.Handle, (byte)sendQrCode.NozzleNum };
- Thread.Sleep(5000);
- CommonMessage commonMessage = await SendMessageToMaichine($"发送{sendQrCode.NozzleNum}号枪二维码",(request,response) =>
- {
- if(response.Handle == (byte)CommonMessage.Command.COMMON)
- {
- CommonAnswerBack commonAnswerBack = (CommonAnswerBack)response;
- return commonAnswerBack.Command == (byte)CommonMessage.Command.SEND_QR_CODE && commonAnswerBack.NozzleNum == sendQrCode.NozzleNum;
- }
- return false;
- }, sendQrCode);
- //CommonMessage commonMessage = await SendMessageToMaichine("发送二维码", BitConverter.ToString(commandAndNozzle).Replace("-", ""), sendQrCode);
- //CommonMessage commonMessage = await SendRequestToMachine("发送二维码", BitConverter.ToString(commandAndNozzle).Replace("-", ""), sendBytes);
- if (commonMessage.IsError && commonMessage.TheErrorType == CommonMessage.ErrorType.DISCONNECT) break;
- }
- //var testAuthorization = new MqttAuthorizationRequest()
- //{
- // NozzleNum = 1,
- // AuthorizationTime = DateTime.Now,
- // AuthorizationType = 1,
- // Value = 3.00m
- //};
- //await SendAuthorization(testAuthorization);
- //var testUnAuthorization = new MqttUnAhorizationRequest()
- //{
- // NozzleNum = 1,
- // AuthorizationTime = DateTime.Now,
- // Ttc = 111
- //};
- //await SendUnAuthorizartion(testUnAuthorization);
- }
- /// <summary>
- /// 发送实付金额给油机
- /// </summary>
- /// <param name="orderInfo"></param>
- public async Task SendActuallyPaid(MqttPaidRequest? request,string? userName,string? phoneNumber)
- {
- MysqlDbContext mysqlDbContext = new MysqlDbContext();
- if (request == null)
- {
- logger.Error($"mqtt get paid request is null");
- return;
- }
- //通知云端当前已收到消息
- OnGetPaidInfo onGetPaidInfo = new OnGetPaidInfo()
- {
- Id = request.Id,
- Result = 1
- };
- await httpClientUtil.SendRecievePaidNotice(JsonConvert.SerializeObject(onGetPaidInfo));
- FccOrderInfo? fccOrderInfo = mysqlDbContext.FccOrderInfos.FirstOrDefault(order =>
- order.NozzleNum == request.NozzleId
- && order.Ttc.ToString() == request.TransactionNumber
- && order.PumpCode == request.FuelItemPumpTotalizerVolume);
- if (fccOrderInfo == null)
- {
- logger.Error($"[mqtt paid order notice]:can not find order by clounid:{request.Id}");
- return;
- }
- fccOrderInfo.CloundOrderId = request.Id;
- fccOrderInfo.AmountPayable = request.ActualPaymentAmount;
- fccOrderInfo.PaymentTime = request.TransactionTime;
- if(request.PaymentMethod != null)
- {
- fccOrderInfo.PayType = (int)request.PaymentMethod;
- fccOrderInfo.PaymentName = stationPayment[request.PaymentMethod ?? 0] ?? "未知类型";
- }
- fccOrderInfo.UserName = userName??"";
- fccOrderInfo.PhoneNumber = phoneNumber ?? string.Empty;
- fccOrderInfo.PaymentStatus = 1;
- mysqlDbContext.SaveChanges();
- //SendActuallyPaid sendActuallyPaid = new SendActuallyPaid(orderInfo.NozzleNum, orderInfo.Ttc, orderInfo.AmountPayable ?? orderInfo.Amount, getFrame(null));
- //byte[] commandAndNozzle = { sendActuallyPaid.Handle, (byte)sendActuallyPaid.NozzleNum };
- //await SendMessageToMaichine("发送实付金额", (request, response) =>
- //{
- // if (response.Handle == (byte)CommonMessage.Command.SEND_NEED_AMOUNT)
- // {
- // CommonAnswerBack commonAnswerBack = (CommonAnswerBack)response;
- // return commonAnswerBack.Command == (byte)CommonMessage.Command.SEND_NEED_AMOUNT && commonAnswerBack.NozzleNum == sendActuallyPaid.NozzleNum;
- // }
- // return false;
- //}, sendActuallyPaid);
- //await SendMessageToMaichine("发送实付金额", BitConverter.ToString(commandAndNozzle).Replace("-", ""), sendActuallyPaid);
- }
- /// <summary>
- /// 发送授权请求给油机
- /// </summary>
- /// <param name="request"></param>
- /// <returns></returns>
- public async Task SendAuthorizationAsync(MqttAuthorizationRequest? request)
- {
- MysqlDbContext mysqlDbContext = new MysqlDbContext();
- if(request == null)
- {
- logger.Error($"mqtt authorization request is null");
- return;
- }
- //添加订单到数据库
- DateTime authorizationTime = request.AuthorizationTime ?? DateTime.Now;
- FccOrderInfo fccOrderInfo = request.ToComponent(authorizationTime);
- mysqlDbContext.FccOrderInfos.Add(fccOrderInfo);
- //发送授权申请到油机
- SendAuthorization sendAuthorization = new SendAuthorization((int)request.NozzleId, authorizationTime, 1,request.OriginalAmount, getFrame(null));
- byte[] commandAndNozzle = { sendAuthorization.Handle, (byte)sendAuthorization.NozzleNum };
- CommonMessage commonMessage = await SendMessageToMaichine("发送授权请求", (request, response) =>
- {
- if (response.Handle == (byte)CommonMessage.Command.ACCREDIT)
- {
- AuthorizationResponse authorization = (AuthorizationResponse)response;
- return authorization.NozzleNum == sendAuthorization.NozzleNum;
- }
- return false;
- }, sendAuthorization);
- //发送授权结果给云端
- string authorizationResultJson = string.Empty;
- SendAuthorizationResult sendAuthorizationResult = new SendAuthorizationResult();
- sendAuthorizationResult.NozzleId = request.NozzleId;
- if (commonMessage.IsError)
- {
- ErrorMessage errorMessage = (ErrorMessage)commonMessage;
- switch (errorMessage.TheErrorType)
- {
- case CommonMessage.ErrorType.DISCONNECT:
- sendAuthorizationResult.OilMachineStatus = OilMachineStatus.Disconnected;
- break;
- case CommonMessage.ErrorType.TIMEOUT:
- sendAuthorizationResult.OilMachineStatus = OilMachineStatus.AuthorizationTimeout;
- break;
- }
- }
- else
- {
- AuthorizationResponse authorization = (AuthorizationResponse)commonMessage;
- if (authorization.Result == 0)
- {
- sendAuthorizationResult.OilMachineStatus = OilMachineStatus.Failed;
- }
- else
- {
- sendAuthorizationResult.OilMachineStatus = OilMachineStatus.Success;
- sendAuthorizationResult.TransactionNumber = authorization.Ttc.ToString();
- fccOrderInfo.Ttc = authorization.Result;
- }
- }
- HttpResponseMessage httpResponseMessage = await httpClientUtil.SendAuthorizationResult(JsonConvert.SerializeObject(sendAuthorizationResult));
- logger.Info($"send authorization result response:{JsonConvert.SerializeObject(httpResponseMessage.Content)}");
- //更新订单
- mysqlDbContext.SaveChanges();
- }
- /// <summary>
- /// 发送取消授权请求给油机
- /// </summary>
- /// <param name="request"></param>
- public async Task SendUnAuthorizartion(MqttUnAhorizationRequest? request)
- {
- MysqlDbContext mysqlDbContext = new MysqlDbContext();
- if (request == null)
- {
- logger.Error($"mqtt unauthorization request is null");
- return;
- }
- //从请求信息中获取流水号与授权时间,没有就到数据库查找
- int ttc = 0;
- DateTime authorizationTime = request.AuthorizationTime ?? DateTime.Now;
- bool ttsIntResult = int.TryParse(request.TransactionNumber, out ttc);
- if (request.AuthorizationTime == null || !ttsIntResult)
- {
- FccOrderInfo? fccOrderInfo = mysqlDbContext.FccOrderInfos.FirstOrDefault(order => order.CloundOrderId == request.Id);
- if(fccOrderInfo != null)
- {
- ttc = fccOrderInfo.Ttc;
- authorizationTime = fccOrderInfo.AuthorizationTime;
- }
- }
- SendUnAuthorizationResult sendUnAuthorizationResult = new SendUnAuthorizationResult();
- sendUnAuthorizationResult.NozzleId = request.NozzleId;
- sendUnAuthorizationResult.OilMachineStatus = OilMachineStatus.Success;
- if (ttc != 0)
- {
- SendUnAuthorization sendUnAuthorization = new SendUnAuthorization((int)request.NozzleId, authorizationTime, ttc, getFrame(null));
- byte[] commandAndNozzle = { sendUnAuthorization.Handle, (byte)sendUnAuthorization.NozzleNum };
- CommonMessage commonMessage = await SendMessageToMaichine("发送取消授权请求", (request, response) =>
- {
- if (response.Handle == (byte)CommonMessage.Command.CANCEL_ACCREDIT)
- {
- UnAhorizationResponse unauthorization = (UnAhorizationResponse)response;
- return unauthorization.NozzleNum == sendUnAuthorization.NozzleNum;
- }
- return false;
- }, sendUnAuthorization);
- if (commonMessage.IsError)
- {
- ErrorMessage errorMessage = (ErrorMessage)commonMessage;
- switch (errorMessage.TheErrorType)
- {
- case CommonMessage.ErrorType.DISCONNECT:
- sendUnAuthorizationResult.OilMachineStatus = OilMachineStatus.Disconnected;
- break;
- case CommonMessage.ErrorType.TIMEOUT:
- sendUnAuthorizationResult.OilMachineStatus = OilMachineStatus.AuthorizationTimeout;
- break;
- }
- }
- else
- {
- UnAhorizationResponse unAuthorization = (UnAhorizationResponse)commonMessage;
- if (unAuthorization.Result == 0)
- {
- sendUnAuthorizationResult.OilMachineStatus = OilMachineStatus.Failed;
- }
- else
- {
- sendUnAuthorizationResult.OilMachineStatus = OilMachineStatus.Success;
- }
- }
- }
- else
- {
- sendUnAuthorizationResult.OilMachineStatus = OilMachineStatus.TransactionNumberNotFound;
- }
- HttpResponseMessage httpResponseMessage = await httpClientUtil.SendUnAuthorizationResult(JsonConvert.SerializeObject(sendUnAuthorizationResult));
- logger.Info($"send Unauthorization result response:{JsonConvert.SerializeObject(httpResponseMessage.Content)}");
- }
- /// <summary>
- /// 接收到云端发送订单退款信息
- /// </summary>
- /// <param name="request"></param>
- /// <returns></returns>
- private async Task OnRecieveOrderRefund(MqttRefundRequest? request)
- {
- MysqlDbContext mysqlDbContext = new MysqlDbContext();
- if (request == null)
- {
- logger.Error($"mqtt OnRecieveOrderRefund request is null");
- return;
- }
- //通知云端当前已收到消息
- OnGetRefundInfo onGetRefundInfo = new OnGetRefundInfo()
- {
- Id = request.Id,
- Result = 1
- };
- await httpClientUtil.SendRecieveRefundNotice(JsonConvert.SerializeObject(onGetRefundInfo));
- FccOrderInfo? fccOrderInfo = mysqlDbContext.FccOrderInfos.FirstOrDefault(order => order.CloundOrderId == request.Id);
- if (fccOrderInfo == null)
- {
- logger.Error($"[mqtt refund order notice]:can not find order by clounid:{request.Id}");
- return;
- }
- fccOrderInfo.AmountPayable = request.ActualPaymentAmount;
- fccOrderInfo.PaymentStatus = 2;
- mysqlDbContext.SaveChanges();
- }
- //public void SetTcpClient(TcpClient? tcpClient, int? serverPort)
- //{
- // this.client = tcpClient;
- // this.serverPort = serverPort;
- // checkDisConnectTask = new TaskCompletionSource<ErrorMessage>();
- //}
- //public void OnTcpDisconnect()
- //{
- // this.client = null;
- // ErrorMessage errorMessage = new ErrorMessage()
- // {
- // IsError = true,
- // TheErrorType = CommonMessage.ErrorType.DISCONNECT,
- // ErrorMessage = $"the client is disconnet"
- // };
- // checkDisConnectTask.SetResult(errorMessage);
- //}
- /// <summary>
- /// 发送消息到油机,3秒的超时,重试三次
- /// </summary>
- /// <param name="sendTag">发送的消息类型,用于日志记录</param>
- /// <param name="sendKey">发送的消息key,用于存储 TaskCompletionSource</param>
- /// <param name="requestBytes">实际发送消息</param>
- /// <returns></returns>
- private async Task<CommonMessage> SendMessageToMaichine(string sendTag, Func<CommonMessage, CommonMessage, bool> responseCapture,CommonMessage sendMessage)
- {
- int retryCount = 0;
- while (retryCount < 3)
- {
- try
- {
- var response = await this.Context.Outgoing.WriteAsyncAndCheckIsConnect(sendMessage, responseCapture, 3000);
- //超时重试
- if (response.ResponseType == WriteResponseType.TIME_OUT || response.Data == null)
- {
- retryCount++;
- logger.Info($"{sendTag}: time out,retrying... ({retryCount} / 3)");
- continue;
- }
- //链接断开不再发送
- if (response.ResponseType == WriteResponseType.DISCONNECT)
- {
- var isConnect = (bool)(response.Data ?? false);
- if (!isConnect) return new ErrorMessage()
- {
- IsError = true,
- TheErrorType = CommonMessage.ErrorType.DISCONNECT,
- ErrorMessage = $"the client is disconnet"
- };
- }
- Console.WriteLine("");
- //返回信息
- return (CommonMessage)response.Data;
- }
- catch (Exception)
- {
- retryCount++;
- logger.Info($"{sendTag}: error,retrying... ({retryCount} / 3)");
- }
- finally
- {
- if (retryCount >= 3)
- {
- logger.Info($"{sendTag}: is time out add retry 3 time");
- }
- }
- }
- return new ErrorMessage()
- {
- IsError = true,
- TheErrorType = CommonMessage.ErrorType.TIMEOUT,
- ErrorMessage = $"{sendTag}: can not receive response after 3 retries"
- };
- }
- ///// <summary>
- ///// 发送消息到油机,3秒的超时,重试三次
- ///// </summary>
- ///// <param name="sendTag">发送的消息类型,用于日志记录</param>
- ///// <param name="sendKey">发送的消息key,用于存储 TaskCompletionSource</param>
- ///// <param name="requestBytes">实际发送消息</param>
- ///// <returns></returns>
- //private async Task<CommonMessage> SendMessageToMaichine(string sendTag, string sendKey,CommonMessage sendMessage)
- //{
- // int retryCount = 0;
- // while (retryCount < 3)
- // {
- // try
- // {
- // var cts = new CancellationTokenSource(TimeSpan.FromSeconds(3));
- // bool isAdd = _tcsDictionary.TryAdd(sendKey, new TaskCompletionSource<CommonMessage>());
- // logger.Info($"{sendTag}: add request {sendKey} to dic is {isAdd}");
- // Write(sendMessage);
- // TaskCompletionSource<CommonMessage>? value;
- // TaskCompletionSource<CommonMessage> tcs;
- // if (_tcsDictionary.TryGetValue(sendKey, out value))
- // {
- // tcs = value;
- // }
- // else
- // {
- // tcs = new TaskCompletionSource<CommonMessage>();
- // }
- // Task checkOutTime = Task.Delay(Timeout.Infinite, cts.Token);
- // var response = await Task.WhenAny(tcs.Task, checkOutTime, checkDisConnectTask.Task);
- // //超时重试
- // if (response == checkOutTime)
- // {
- // retryCount++;
- // logger.Info($"{sendTag}-{sendKey}: time out,retrying... ({retryCount} / 3)");
- // continue;
- // }
- // //CommonMessage response = await tcs.Task.WaitAsync(cts.Token);
- // _tcsDictionary.TryRemove(sendKey, out _);
- // //链接断开不再发送
- // if(response == checkDisConnectTask.Task)
- // {
- // return new ErrorMessage()
- // {
- // IsError = true,
- // TheErrorType = CommonMessage.ErrorType.DISCONNECT,
- // ErrorMessage = $"the client is disconnet"
- // };
- // }
- // //返回信息
- // return await (Task<CommonMessage>)response;
- // }
- // catch (Exception)
- // {
- // retryCount++;
- // logger.Info($"{sendTag}-{sendKey}: error,retrying... ({retryCount} / 3)");
- // }
- // finally
- // {
- // if (retryCount >= 3)
- // {
- // logger.Info($"{sendTag}-{sendKey}: is time out add retry 3 time");
- // _tcsDictionary.TryRemove(sendKey, out _);
- // }
- // }
- // }
- // return new ErrorMessage()
- // {
- // IsError = true,
- // TheErrorType = CommonMessage.ErrorType.TIMEOUT,
- // ErrorMessage = $"{sendTag}: can not receive response after 3 retries"
- // };
- //}
- ///// <summary>
- ///// 发送消息到油机,3秒的超时,重试三次
- ///// </summary>
- ///// <param name="sendTag">发送的消息类型,用于日志记录</param>
- ///// <param name="sendKey">发送的消息key,用于存储 TaskCompletionSource</param>
- ///// <param name="requestBytes">实际发送消息</param>
- ///// <returns></returns>
- ///// <exception cref="TimeoutException"></exception>
- //private async Task<CommonMessage> SendRequestToMachine(string sendTag,string sendKey, byte[] requestBytes)
- //{
- // int retryCount = 0;
- // while(retryCount < 3)
- // {
- // try
- // {
- // var cts = new CancellationTokenSource(TimeSpan.FromSeconds(3));
- // bool isAdd = _tcsDictionary.TryAdd(sendKey, new TaskCompletionSource<CommonMessage>());
- // logger.Info($"{sendTag}: add request {sendKey} to dic is {isAdd}");
- // if (client != null)
- // {
- // client?.Client?.Send(requestBytes);
- // } else
- // {
- // return new ErrorMessage()
- // {
- // IsError = true,
- // TheErrorType = CommonMessage.ErrorType.DISCONNECT,
- // ErrorMessage = $"the client is disconnet"
- // };
- // }
-
- // logger.Info($"send request to machine:{BitConverter.ToString(requestBytes).Replace("-", " ")}");
- // TaskCompletionSource<CommonMessage>? value;
- // TaskCompletionSource<CommonMessage> tcs;
- // if(_tcsDictionary.TryGetValue(sendKey, out value))
- // {
- // tcs = value;
- // } else
- // {
- // tcs = new TaskCompletionSource<CommonMessage>();
- // }
- // Task checkOutTime = Task.Delay(Timeout.Infinite, cts.Token);
- // var response = await Task.WhenAny(tcs.Task, checkOutTime, checkDisConnectTask.Task);
- // if(response == checkOutTime)
- // {
- // retryCount++;
- // logger.Info($"{sendTag}-{sendKey}: time out,retrying... ({retryCount} / 3)");
- // continue;
- // }
- // //CommonMessage response = await tcs.Task.WaitAsync(cts.Token);
- // _tcsDictionary.TryRemove(sendKey, out _);
- // if (response == checkDisConnectTask.Task)
- // {
- // return new ErrorMessage()
- // {
- // IsError = true,
- // TheErrorType = CommonMessage.ErrorType.DISCONNECT,
- // ErrorMessage = $"the client is disconnet"
- // };
- // }
- // return await (Task<CommonMessage>)response;
- // } catch (Exception)
- // {
- // retryCount++;
- // logger.Info($"{sendTag}-{sendKey}: error,retrying... ({retryCount} / 3)");
- // } finally
- // {
- // if(retryCount >= 3)
- // {
- // logger.Info($"{sendTag}-{sendKey}: is time out add retry 3 time");
- // _tcsDictionary.TryRemove(sendKey,out _);
- // }
- // }
- // }
- // return new ErrorMessage()
- // {
- // IsError = true,
- // TheErrorType = CommonMessage.ErrorType.TIMEOUT,
- // ErrorMessage = $"{sendTag}: can not receive response after 3 retries"
- // };
- //}
- /// <summary>
- /// 添加或修改订单
- /// </summary>
- /// <param name="order">接收到油机的订单信息</param>
- /// <returns></returns>
- public FccOrderInfo UpLoadOrder(OrderFromMachine order)
- {
- MysqlDbContext mysqlDbContext = new MysqlDbContext();
- //接收到油机发送过来的订单信息
- OrderFromMachine orderFromMachine = (OrderFromMachine)order;
- string? oilName = mysqlDbContext.OilInfos.Where(oil => orderFromMachine.oilCode.Equals(oil.Code)).Select(oil => oil.Name).FirstOrDefault();
- FccOrderInfo orderByMessage = orderFromMachine.ToComponent(oilName);
- /** 根据枪号+流水号+授权时间来确定订单,因为冷启动后流水号会从头开始计算
- * 后支付时直接将数据库直接插入
- * 预支付时由于是云端先创建订单,发起授权响应成功后会插入数据库,响应成功时会回复授权时间,枪号,流水号
- */
- FccOrderInfo? fccOrderInfo = mysqlDbContext.FccOrderInfos
- .Where(order =>
- order.NozzleNum == orderFromMachine.nozzleNum && order.Ttc == orderFromMachine.ttc
- && order.AuthorizationTime == orderFromMachine.dispenserTime)
- .FirstOrDefault();
- if (fccOrderInfo == null)
- {
- logger.Info($"receive order from machine,find order from database is null");
- mysqlDbContext.FccOrderInfos.Add(orderByMessage);
- mysqlDbContext.SaveChanges();
- return orderByMessage;
- }
- else
- {
- logger.Info($"receive order from machine,padding data right now");
- orderFromMachine.PaddingAuthorizationOrderData(fccOrderInfo);
- mysqlDbContext.SaveChanges();
- return fccOrderInfo;
- }
-
- }
- private async void CreateTransaction(FccOrderInfo fccOrderInfo)
- {
- MysqlDbContext mysqlDbContext = new MysqlDbContext();
- CreateTransaction createTransaction = new CreateTransaction(fccOrderInfo);
- logger.Info($"create transaction, {JsonConvert.SerializeObject(createTransaction)}");
- HttpResponseMessage httpResponseMessage = await httpClientUtil.CreateTransaction(JsonConvert.SerializeObject(createTransaction));
- //var b = httpResponseMessage.Content;
- //var a = httpResponseMessage;
- string responseStr = await httpResponseMessage.Content.ReadAsStringAsync();
- Response<CreateTransactionResponse>? response = JsonConvert.DeserializeObject<Response<CreateTransactionResponse>>(responseStr);
- logger.Info($"reveice create transaction response:{JsonConvert.SerializeObject(response)}");
-
- fccOrderInfo.CloundOrderId = response?.data?.Id;
- fccOrderInfo.UploadState = response?.data == null ? 0 : 1;
- mysqlDbContext.SaveChanges();
- }
- /// <summary>
- /// 发送油枪状态给云端
- /// </summary>
- /// <param name="nozzleState"></param>
- private async void SendNozzleStatus(HeartBeatMessage heartBeatMessage)
- {
- //提取出状态有变化的油枪,打包成要发送至云端的数据,添加到列表
- List<SendNozzleStatu> sendNozzleStatus = new List<SendNozzleStatu>();
- foreach (var nozzleState in heartBeatMessage.NozzleStatus)
- {
- if (nozzleStatusDic.TryGetValue(nozzleState.NozzleNum, out var value))
- {
- if (nozzleState.STATU == value) continue;
- }
- //保存变量
- nozzleStatusDic[nozzleState.NozzleNum] = nozzleState.STATU;
- //查找fcc数据库油枪id
- DetailsNozzleInfoOutput? detailsNozzleInfoOutput = nozzleInfoList.Find(nozzle => nozzle.NozzleNum == nozzleState.NozzleNum);
- if (detailsNozzleInfoOutput == null)
- {
- logger.Error($"can not find nozzleInfo from nozzleInfoList:{nozzleState.NozzleNum} ,send nozzle state fail");
- continue;
- }
- SendNozzleStatu sendNozzleStatu = new SendNozzleStatu(detailsNozzleInfoOutput.Id, nozzleState);
- sendNozzleStatus.Add(sendNozzleStatu);
- }
- if (sendNozzleStatus.IsNullOrEmpty()) return;
- //发送云端
- string reuqestJson = JsonConvert.SerializeObject(sendNozzleStatus);
- logger.Info($"send nozzle state to cloud,{reuqestJson}");
- try
- {
- HttpResponseMessage httpResponseMessage = await httpClientUtil.SendNozzleStatu(reuqestJson);
- Response<object>? response = JsonConvert.DeserializeObject<Response<object>>(await httpResponseMessage.Content.ReadAsStringAsync());
- logger.Info($"reveice send nozzle state response:{JsonConvert.SerializeObject(response)}");
- } catch (Exception ex)
- {
- logger.Error($"send nozzle stat fail:{ex.Message}");
- }
-
- }
- /// <summary>
- /// 获取发送帧号
- /// </summary>
- /// <param name="sendFrame"></param>
- /// <returns></returns>
- private byte getFrame(byte? sendFrame)
- {
- byte frameNo = 0x00;
- if (sendFrame == null)
- {
- lock (lockFrame)
- {
- if (frame == 0x3f)
- {
- frameNo = 0x00;
- }
- else
- {
- frameNo = (byte)(frame++);
- }
- }
- }
- else
- {
- frameNo = sendFrame.Value;
- }
- return frameNo;
- }
- ///// <summary>
- ///// 传入有效数据,拼接为要发送给油机包
- ///// </summary>
- ///// <param name="content"></param>
- ///// <returns></returns>
- //public byte[] content2data(byte[] content, byte? sendFrame)
- //{
- // List<byte> list = new List<byte>();
- // //目标地址,源地址,帧号
- // byte frameNo = 0x00;
- // if (sendFrame == null)
- // {
- // lock (lockFrame)
- // {
- // if (frame == 0x3f)
- // {
- // frameNo = 0x00;
- // }
- // else
- // {
- // frameNo = (byte)(frame++);
- // }
- // }
- // }
- // else
- // {
- // frameNo = sendFrame.Value;
- // }
- // byte[] head = new byte[] { 0xFF, 0xE0, frameNo };
- // byte[] length = Int2BCD(content.Length);
- // list.AddRange(head);
- // list.AddRange(length);
- // list.AddRange(content);
- // byte[] crc = HengshanCRC16.ComputeChecksumToBytes(list.ToArray());
- // list.AddRange(crc);
- // List<byte> addFAList = addFA(list);
- // addFAList.Insert(0, 0xFA);
- // return addFAList.ToArray();
- //}
- //public int Bcd2Int(byte byte1, byte byte2)
- //{
- // // 提取第一个字节的高四位和低四位
- // int digit1 = (byte1 >> 4) & 0x0F; // 高四位
- // int digit2 = byte1 & 0x0F; // 低四位
- // // 提取第二个字节的高四位和低四位
- // int digit3 = (byte2 >> 4) & 0x0F; // 高四位
- // int digit4 = byte2 & 0x0F; // 低四位
- // // 组合成一个整数
- // int result = digit1 * 1000 + digit2 * 100 + digit3 * 10 + digit4;
- // return result;
- //}
- //public byte[] Int2BCD(int number)
- //{
- // // 提取千位、百位、十位和个位
- // int thousands = number / 1000;
- // int hundreds = (number / 100) % 10;
- // int tens = (number / 10) % 10;
- // int units = number % 10;
- // // 将千位和百位组合成一个字节(千位在高四位,百位在低四位)
- // byte firstByte = (byte)((thousands * 16) + hundreds); // 乘以16相当于左移4位
- // // 将十位和个位组合成一个字节(十位在高四位,个位在低四位)
- // byte secondByte = (byte)((tens * 16) + units);
- // // 返回结果数组
- // return new byte[] { firstByte, secondByte };
- //}
- //public List<Byte> addFA(List<Byte> list)
- //{
- // List<byte> result = new List<byte>();
- // foreach (byte b in list)
- // {
- // if (b == 0xFA)
- // {
- // result.Add(0xFA);
- // result.Add(0xFA);
- // }
- // else
- // {
- // result.Add(b);
- // }
- // }
- // return result;
- //}
- ///// <summary>
- ///// 将数值转为byte[]
- ///// </summary>
- ///// <param name="value">数值</param>
- ///// <param name="length">数组长度,不够高位补0</param>
- ///// <returns></returns>
- ///// <exception cref="ArgumentException"></exception>
- //public static byte[] NumberToByteArrayWithPadding(int value, int length)
- //{
- // if (length < 0)
- // {
- // throw new ArgumentException("Length must be non-negative.");
- // }
- // // 创建一个指定长度的字节数组
- // byte[] paddedBytes = new byte[length];
- // // 确保是大端序
- // for (int i = 0; i < length && i < 4; i++)
- // {
- // paddedBytes[length - 1 - i] = (byte)(value >> (i * 8));
- // }
- // return paddedBytes;
- //}
- //public static byte[] FormatDecimal(decimal value)
- //{
- // // 四舍五入到两位小数
- // decimal roundedValue = Math.Round(value, 2, MidpointRounding.AwayFromZero);
- // int valueInt = (int)(roundedValue * 100m);
- // return NumberToByteArrayWithPadding(valueInt, 3); ;
- //}
- ///// <summary>
- ///// 将时间转为 BCD
- ///// </summary>
- ///// <param name="dateTime"></param>
- ///// <returns></returns>
- //public static byte[] ConvertDateTimeToByteArray(DateTime dateTime)
- //{
- // // 创建byte数组
- // byte[] result = new byte[7];
- // // 年份处理
- // int year = dateTime.Year;
- // result[0] = (byte)((year / 1000) * 16 + (year / 100) % 10); // 千年和百年
- // result[1] = (byte)((year / 10) % 10 * 16 + year % 10); // 十年和个年
- // // 月、日、小时、分钟、秒直接转换为BCD
- // result[2] = (byte)(dateTime.Month / 10 * 16 + dateTime.Month % 10);
- // result[3] = (byte)(dateTime.Day / 10 * 16 + dateTime.Day % 10);
- // result[4] = (byte)(dateTime.Hour / 10 * 16 + dateTime.Hour % 10);
- // result[5] = (byte)(dateTime.Minute / 10 * 16 + dateTime.Minute % 10);
- // result[6] = (byte)(dateTime.Second / 10 * 16 + dateTime.Second % 10);
- // return result;
- //}
- // CRC16 constants
- const ushort CRC_ORDER16 = 16;
- const ushort CRC_POLYNOM16 = 0x1021;
- const ushort CRC_CRCINIT16 = 0xFFFF;
- const ushort CRC_CRCXOR16 = 0x0000;
- const ushort CRC_MASK = 0xFFFF;
- const ushort CRC_HIGHEST_BIT = (ushort)(1 << (CRC_ORDER16 - 1));
- const ushort TGT_CRC_DEFAULT_INIT = 0xFFFF;
- public static ushort Crc16(byte[] buffer, ushort length)
- {
- ushort crc_rc = TGT_CRC_DEFAULT_INIT;
- for (int i = 0; i < length; i++)
- {
- byte c = buffer[i];
- for (ushort j = 0x80; j != 0; j >>= 1)
- {
- ushort crc_bit = (ushort)((crc_rc & CRC_HIGHEST_BIT) != 0 ? 1 : 0);
- crc_rc <<= 1;
- if ((c & j) != 0)
- {
- crc_bit = (ushort)((crc_bit == 0) ? 1 : 0);
- }
- if (crc_bit != 0)
- {
- crc_rc ^= CRC_POLYNOM16;
- }
- }
- }
- return (ushort)((crc_rc ^ CRC_CRCXOR16) & CRC_MASK);
- }
- #endregion
- }
- public class HengshanPayTerminalHanlderGroupConfigV1
- {
- public string PumpIds { get; set; }
- public List<PumpSubAddress> PumpSubAddresses { get; set; }
- }
- public class HengshanPayTerminalHanlderGroupConfigV2
- {
- public string PumpIds { get; set; }
- public List<PumpSubAddress> PumpSubAddresses { get; set; }
- public List<PumpNozzleLogicId> PumpNozzleLogicIds { get; set; }
- public List<PumpSiteNozzleNo> PumpSiteNozzleNos { get; set; }
- public List<NozzleLogicId> NozzleLogicIds { get; set; }
- }
- public class PumpSubAddress
- {
- public byte PumpId { get; set; }
- public byte SubAddress { get; set; }
- }
- public class PumpNozzleLogicId
- {
- public byte PumpId { get; set; }
- public string LogicIds { get; set; }
- }
- public class PumpSiteNozzleNo
- {
- public byte PumpId { get; set; }
- public string SiteNozzleNos { get; set; }
- }
- public class NozzleLogicId
- {
- public byte NozzleNo { get; set; }
- public byte LogicId { get; set; }
- }
- }
|