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 { /// /// Handler that communicates directly with the Hengshan Payment Terminal for card handling and pump handling via serial port. /// [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, IDeviceHandler { #region Fields private string pumpIds; private string pumpSubAddresses; private string pumpNozzles; private string pumpSiteNozzleNos; private string nozzleLogicIds; private IContext _context; private List pumpHandlers = new List(); public Queue queue = new Queue(); public Queue commonQueue = new Queue(); private object syncObj = new object(); private ConcurrentDictionary statusDict = new ConcurrentDictionary(); public ConcurrentDictionary PumpStatusDict => statusDict; private Dictionary pumpIdSubAddressDict; public Dictionary> PumpNozzlesDict { get; private set; } public Dictionary NozzleLogicIdDict { get; private set; } public Dictionary> PumpSiteNozzleNoDict { get; private set; } //public MysqlDbContext MysqlDbContext { get; private set; } public StationInfo stationInfo { get; set; } public Dictionary stationPayment = new Dictionary(); public List nozzleInfoList { get; private set; } public TcpClient? client { get; set; } public int? serverPort { get; set; } //private readonly ConcurrentDictionary> _tcsDictionary = new ConcurrentDictionary>(); //private TaskCompletionSource checkDisConnectTask = new TaskCompletionSource(); private byte frame = 0x00; private object lockFrame = new object(); private IHttpClientUtil httpClientUtil; //记录油枪状态,key-枪号,value:01:离线 02:锁枪 03:空闲 04:提枪 06:开始加油 08:加油中 private ConcurrentDictionary nozzleStatusDic = new ConcurrentDictionary(); #endregion #region Logger private static NLog.Logger logger = NLog.LogManager.LoadConfiguration("NLog.config").GetLogger("IPosPlusApp"); #endregion #region Constructor //private static List 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 parameters) { logger.Info("OnFdcServerInit called"); if (parameters.ContainsKey("LastPriceChange")) { // nozzle logical id:rawPrice var lastPriceChanges = parameters["LastPriceChange"] as Dictionary; foreach (var priceChange in lastPriceChanges) { } } } #region Event handler public event EventHandler OnTerminalMessageReceived; public event EventHandler OnTotalizerReceived; public event EventHandler OnFuelPriceChangeRequested; public event EventHandler OnTerminalFuelPriceDownloadRequested; public event EventHandler OnCheckCommandReceived; public event EventHandler OnLockUnlockCompleted; #endregion #region Properties public List AssociatedPumpIds { get; private set; } public IContext 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 GetPumpIdList(string pumpIds) { var pumpIdList = new List(); 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.Parse(pumpIds) }; } else { throw new ArgumentException("Pump id list not specified!"); } } private Dictionary InitializePumpSubAddressMapping() { var dict = new Dictionary(); 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> ParsePumpNozzlesList(string pumpNozzles) { Dictionary> pumpNozzlesDict = new Dictionary>(); if (!string.IsNullOrEmpty(pumpNozzles) && pumpNozzles.Contains(';')) { var arr = pumpNozzles.Split(';'); foreach (var subMapping in arr) { var pair = new KeyValuePair(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 { pair.Value }); } else { List 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.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> ParsePumpSiteNozzleNoList(string pumpSiteNozzleNos) { Dictionary> pumpSiteNozzleNoDict = new Dictionary>(); if (!string.IsNullOrEmpty(pumpSiteNozzleNos) && pumpSiteNozzleNos.Contains(';')) { var arr = pumpSiteNozzleNos.Split(';'); foreach (var subMapping in arr) { var pair = new KeyValuePair>( 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.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 InitializeNozzleLogicIdMapping(string nozzleLogicIds) { var dict = new Dictionary(); 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 GetNozzleListForPump(int pumpId) { List 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 IEnumerable.GetEnumerator() { return pumpHandlers.GetEnumerator(); } #endregion #region IHandler implementation public void Init(IContext 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 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 WriteAsync(CommonMessage request, Func responseCapture, int timeout) { var resp = await _context.Outgoing.WriteAsync(request, responseCapture, timeout); return resp; } #endregion #region IEnumerable implementation public IEnumerator 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 二维码加油机相关方法 /// /// 获取站点信息 /// 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(); } else { nozzleInfoList = mysqlDbContext.NozzleInfos.Where(nozzle => nozzle.MachineId == fccMachineInfo.Id).Select(n => new DetailsNozzleInfoOutput(n)).ToList(); } } /// /// 接收到MQTT /// /// public async void OnReceiveMqttMessage(string message) { MqttRequest? mqttRequest = JsonConvert.DeserializeObject(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(mqttRequest.data); await SendAuthorizationAsync(mqttAuthorizationRequest); break; } case MQTT_TYPE.UNAUTHORIZATION: { MqttUnAhorizationRequest? mqttUnAhorizationRequest = JsonConvert.DeserializeObject(mqttRequest.data); await SendUnAuthorizartion(mqttUnAhorizationRequest); break; } case MQTT_TYPE.PAID: { MqttPaidRequest? mqttPaidRequest = JsonConvert.DeserializeObject(mqttRequest.data); await SendActuallyPaid(mqttPaidRequest, mqttRequest.UserName, mqttRequest.UserPhoneNumber); break; } case MQTT_TYPE.REFUND: { MqttRefundRequest? mqttRefundRequest = JsonConvert.DeserializeObject(mqttRequest.data); await OnRecieveOrderRefund(mqttRefundRequest); break; } } } /// /// 发送二维码信息给油机 /// /// 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 list = new List(); //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); } /// /// 发送实付金额给油机 /// /// 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); } /// /// 发送授权请求给油机 /// /// /// 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(); } /// /// 发送取消授权请求给油机 /// /// 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)}"); } /// /// 接收到云端发送订单退款信息 /// /// /// 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(); //} //public void OnTcpDisconnect() //{ // this.client = null; // ErrorMessage errorMessage = new ErrorMessage() // { // IsError = true, // TheErrorType = CommonMessage.ErrorType.DISCONNECT, // ErrorMessage = $"the client is disconnet" // }; // checkDisConnectTask.SetResult(errorMessage); //} /// /// 发送消息到油机,3秒的超时,重试三次 /// /// 发送的消息类型,用于日志记录 /// 发送的消息key,用于存储 TaskCompletionSource /// 实际发送消息 /// private async Task SendMessageToMaichine(string sendTag, Func 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" }; } ///// ///// 发送消息到油机,3秒的超时,重试三次 ///// ///// 发送的消息类型,用于日志记录 ///// 发送的消息key,用于存储 TaskCompletionSource ///// 实际发送消息 ///// //private async Task 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()); // logger.Info($"{sendTag}: add request {sendKey} to dic is {isAdd}"); // Write(sendMessage); // TaskCompletionSource? value; // TaskCompletionSource tcs; // if (_tcsDictionary.TryGetValue(sendKey, out value)) // { // tcs = value; // } // else // { // tcs = new TaskCompletionSource(); // } // 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)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" // }; //} ///// ///// 发送消息到油机,3秒的超时,重试三次 ///// ///// 发送的消息类型,用于日志记录 ///// 发送的消息key,用于存储 TaskCompletionSource ///// 实际发送消息 ///// ///// //private async Task 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()); // 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? value; // TaskCompletionSource tcs; // if(_tcsDictionary.TryGetValue(sendKey, out value)) // { // tcs = value; // } else // { // tcs = new TaskCompletionSource(); // } // 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)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" // }; //} /// /// 添加或修改订单 /// /// 接收到油机的订单信息 /// 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? response = JsonConvert.DeserializeObject>(responseStr); logger.Info($"reveice create transaction response:{JsonConvert.SerializeObject(response)}"); fccOrderInfo.CloundOrderId = response?.data?.Id; fccOrderInfo.UploadState = response?.data == null ? 0 : 1; mysqlDbContext.SaveChanges(); } /// /// 发送油枪状态给云端 /// /// private async void SendNozzleStatus(HeartBeatMessage heartBeatMessage) { //提取出状态有变化的油枪,打包成要发送至云端的数据,添加到列表 List sendNozzleStatus = new List(); 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? response = JsonConvert.DeserializeObject>(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}"); } } /// /// 获取发送帧号 /// /// /// 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; } ///// ///// 传入有效数据,拼接为要发送给油机包 ///// ///// ///// //public byte[] content2data(byte[] content, byte? sendFrame) //{ // List list = new List(); // //目标地址,源地址,帧号 // 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 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 addFA(List list) //{ // List result = new List(); // foreach (byte b in list) // { // if (b == 0xFA) // { // result.Add(0xFA); // result.Add(0xFA); // } // else // { // result.Add(b); // } // } // return result; //} ///// ///// 将数值转为byte[] ///// ///// 数值 ///// 数组长度,不够高位补0 ///// ///// //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); ; //} ///// ///// 将时间转为 BCD ///// ///// ///// //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 PumpSubAddresses { get; set; } } public class HengshanPayTerminalHanlderGroupConfigV2 { public string PumpIds { get; set; } public List PumpSubAddresses { get; set; } public List PumpNozzleLogicIds { get; set; } public List PumpSiteNozzleNos { get; set; } public List 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; } } }