123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446 |
- using System;
- using System.Collections.Generic;
- using System.Collections.ObjectModel;
- using System.Configuration;
- using System.IO.Ports;
- using System.Linq;
- using System.Text;
- using System.Threading;
- using Timer = System.Timers.Timer;
- using System.Collections;
- using Edge.Core.Processor;using Edge.Core.IndustryStandardInterface.Pump;
- using Wayne.FDCPOSLibrary;
- using System.Xml;
- using System.Threading.Tasks;
- using Microsoft.Extensions.Logging.Abstractions;
- using Microsoft.Extensions.Logging;
- using Microsoft.Extensions.DependencyInjection;
- using Edge.Core.Database.Models;
- using LanTian_Sinopec_PumpIcCardReader.MessageEntity.Outgoing;
- using Edge.Core.UniversalApi;
- namespace LanTian_Sinopec_PumpIcCardReader
- {
- /// <summary>
- /// PC主动
- /// </summary>
- public class Handler : IFdcPumpController, IDeviceHandler<byte[], KaJiLianDongV11MessageTemplateBase>
- {
- private IServiceProvider services;
- static ILogger logger = NullLogger.Instance;
- private LogicalDeviceState lastLogicalDeviceState = LogicalDeviceState.FDC_OFFLINE;
- private DateTime lastLogicalDeviceStateReceivedTime;
- // by seconds, change this value need change the correlated deviceOfflineCountdownTimer's interval as well
- public const int lastLogicalDeviceStateExpiredTime = 6;
- private List<LogicalNozzle> nozzles = new List<LogicalNozzle>();
- private System.Timers.Timer deviceOfflineCountdownTimer;
- private bool isOnFdcServerInitCalled = false;
- private int pumpId;
- protected IContext<byte[], KaJiLianDongV11MessageTemplateBase> context;
- private byte rotateMsgSeqNo_Seed = 0;
- /// <summary>
- /// 0<= value <=63
- /// </summary>
- private byte NextMsgSeqNo
- {
- get
- {
- var cur = this.rotateMsgSeqNo_Seed;
- if (cur > 63) cur = this.rotateMsgSeqNo_Seed = 0;
- this.rotateMsgSeqNo_Seed++;
- return cur;
- }
- }
- public event EventHandler<FdcPumpControllerOnStateChangeEventArg> OnStateChange;
- /// <summary>
- /// fired on fueling process is on going, the fuel amount should keep changing.
- /// </summary>
- public event EventHandler<FdcTransactionDoneEventArg> OnCurrentFuellingStatusChange;
- public IEnumerable<LogicalNozzle> Nozzles => this.nozzles;
- /// <summary>
- ///
- /// </summary>
- /// <param name="pumpId">the ic card also be treated as a logical pump</param>
- /// <param name="terminalPhysicalAddress">加液机的通讯终端的逻辑编号POS-P, 通讯终端出厂时POS-P 设为0xFF</param>
- /// <param name="fcPhysicalAddress">PC 机的地址范围:0xE0~0xF9</param>
- /// <param name="services"></param>
- public Handler(int pumpId,
- int terminalPhysicalAddress, int fcPhysicalAddress, IServiceProvider services)
- {
- this.services = services;
- var loggerFactory = services.GetRequiredService<ILoggerFactory>();
- logger = loggerFactory.CreateLogger("PumpHandler");
- this.pumpId = pumpId;
- this.terminalPhysicalAddress = (byte)terminalPhysicalAddress;
- this.fcPhysicalAddress = (byte)fcPhysicalAddress;
- //对于这个品牌,一个IC卡键盘仅可控制一把枪
- this.nozzles.Add(new LogicalNozzle(pumpId, (byte)terminalPhysicalAddress, 1, null));
- }
- public void Init(IContext<byte[], KaJiLianDongV11MessageTemplateBase> context)
- {
- this.context = context;
- var timeWindowWithActivePollingOutgoing =
- this.context.Outgoing as TimeWindowWithActivePollingOutgoing<byte[], KaJiLianDongV11MessageTemplateBase>;
- timeWindowWithActivePollingOutgoing.PollingMsgProducer = () =>
- {
- return new PcGenericInquiryRequest()
- {
- SourceAddress = this.fcPhysicalAddress,
- TargetAddress = this.terminalPhysicalAddress,
- MessageSequenceNumber = this.NextMsgSeqNo,
- PC_TIME = DateTime.Now,
- };
- };
- }
- public virtual async Task Process(IContext<byte[], KaJiLianDongV11MessageTemplateBase> context)
- {
- this.context = context;
- this.lastLogicalDeviceStateReceivedTime = DateTime.Now;
- if (this.lastLogicalDeviceState == LogicalDeviceState.FDC_OFFLINE)
- {
- logger.LogInformation("Pump: " + this.pumpId + ", " + "Recevied an IcCard Msg in FDC_OFFLINE state, " +
- "indicates the underlying connection is established, switch to FDC_READY");
- this.lastLogicalDeviceState = LogicalDeviceState.FDC_READY;
- this.OnStateChange?.Invoke(this, new FdcPumpControllerOnStateChangeEventArg(LogicalDeviceState.FDC_READY));
- }
- if (context.Incoming.Message is PumpRealTimeStateEvent pumpRealTimeStateResponse)
- {
- if (pumpRealTimeStateResponse.NozzleOperatingStates == null)
- {
- if (this.lastLogicalDeviceState != LogicalDeviceState.FDC_READY)
- {
- this.lastLogicalDeviceState = LogicalDeviceState.FDC_READY;
- this.OnStateChange?.Invoke(this, new FdcPumpControllerOnStateChangeEventArg(
- LogicalDeviceState.FDC_READY,
- this.nozzles.FirstOrDefault()));
- }
- }
- if (pumpRealTimeStateResponse.NozzleOperatingStates != null
- && pumpRealTimeStateResponse.NozzleOperatingStates.Any(n =>
- n.St状态字 == NozzleOperatingState.PumpStateChangeCode.抬枪或加油中))
- {
- this.nozzles.First().RealPriceOnPhysicalPump =
- pumpRealTimeStateResponse.NozzleOperatingStates.First().PRC价格;
- if (this.lastLogicalDeviceState != LogicalDeviceState.FDC_FUELLING)
- {
- this.lastLogicalDeviceState = LogicalDeviceState.FDC_FUELLING;
- this.OnCurrentFuellingStatusChange?.Invoke(this, new FdcTransactionDoneEventArg(new FdcTransaction()
- {
- Nozzle = this.nozzles.First(),
- Amount = pumpRealTimeStateResponse.NozzleOperatingStates.First().AMN数额,
- Volumn = pumpRealTimeStateResponse.NozzleOperatingStates.First().VOL升数,
- Price = pumpRealTimeStateResponse.NozzleOperatingStates.First().PRC价格,
- Finished = false,
- }));
- }
- }
- }
- else if (context.Incoming.Message is PumpNotifyTransactionDoneRequest pumpNotifyTransactionDoneRequest)
- {
- this.nozzles.First().RealPriceOnPhysicalPump =
- pumpNotifyTransactionDoneRequest.PRC_成交价格;
- this.OnCurrentFuellingStatusChange?.Invoke(this, new FdcTransactionDoneEventArg(new FdcTransaction()
- {
- Nozzle = this.nozzles.First(),
- Amount = pumpNotifyTransactionDoneRequest.AMN数额,
- Volumn = pumpNotifyTransactionDoneRequest.VOL_升数,
- Price = pumpNotifyTransactionDoneRequest.PRC_成交价格,
- SequenceNumberGeneratedOnPhysicalPump = pumpNotifyTransactionDoneRequest.POS_TTC,
- //AmountTotalizer = ,
- VolumeTotalizer = pumpNotifyTransactionDoneRequest.V_TOT_升累计,
- Finished = true,
- }));
- #warning should send a request to external module to validate the T-MAC
- this.context.Outgoing.Write(new PumpNotifyTransactionDoneResponse()
- {
- SourceAddress = this.fcPhysicalAddress,
- TargetAddress = this.terminalPhysicalAddress,
- MessageSequenceNumber = this.NextMsgSeqNo,
- Result = PumpNotifyTransactionDoneResponse.ResultEnum.正确
- });
- }
- else if (context.Incoming.Message is PumpAskDataDownloadRequest pumpAskDataDownloadRequest)
- {
- #warning should query cloud side to get the BL_LEN_长度
- this.context.Outgoing.Write(new PumpAskDataDownloadResponse()
- {
- SourceAddress = this.fcPhysicalAddress,
- TargetAddress = this.terminalPhysicalAddress,
- MessageSequenceNumber = this.NextMsgSeqNo,
- BL_LEN_长度 = 0,
- Content_内容 = pumpAskDataDownloadRequest.Content_内容,
- });
- }
- else if (context.Incoming.Message is PumpStartDataDownloadRequest pumpStartDataDownloadRequest)
- {
- #warning should query cloud side to get the real data for downloading to pump
- this.context.Outgoing.Write(new PumpStartDataDownloadResponse()
- {
- SourceAddress = this.fcPhysicalAddress,
- TargetAddress = this.terminalPhysicalAddress,
- MessageSequenceNumber = this.NextMsgSeqNo,
- Content_内容 = pumpStartDataDownloadRequest.Content_内容,
- S_OFFSET_段偏移 = pumpStartDataDownloadRequest.S_OFFSET_段偏移,
- SEG_段数_segs = 0,
- DATA_Content_数据内容 = new List<byte>() { 0x00 }
- });
- }
- else if (context.Incoming.Message is PumpInquiryGrayCardRecordRequest pumpInquiryGrayCardRecordRequest)
- {
- #warning should query cloud side to get the real data
- this.context.Outgoing.Write(new PumpInquiryGrayCardRecordResponse()
- {
- SourceAddress = this.fcPhysicalAddress,
- TargetAddress = this.terminalPhysicalAddress,
- MessageSequenceNumber = this.NextMsgSeqNo,
- M_FLAG_匹配标志 = PumpInquiryGrayCardRecordResponse.PumpInquiryGrayCardRecordResult.匹配,
- ASN卡应用号 = pumpInquiryGrayCardRecordRequest.ASN卡应用号,
- BAL_余额 = 999911,
- AMN_交易额 = 183,
- AMN_优惠价 = 123,
- AMN_折扣 = 45,
- AMN_优惠后金额 = 97,
- CTC_交易计数器 = 765,
- DS_扣款来源 = 0,
- TIME_日期及时间 = DateTime.Now,
- GMAC电子签名 = 23192394,
- PSAM_TID_PSAM编号 = new List<byte>() { 0x01, 0x02 },
- PSAM_TTC = 654
- });
- }
- else if (context.Incoming.Message is PumpInquiryCreditCardRecordRequest pumpInquiryCreditCardRecordRequest)
- {
- #warning should query cloud side to get the real data
- this.context.Outgoing.Write(new PumpInquiryCreditCardRecordResponse()
- {
- SourceAddress = this.fcPhysicalAddress,
- TargetAddress = this.terminalPhysicalAddress,
- MessageSequenceNumber = this.NextMsgSeqNo,
- S_DATA_记帐卡状态 = PumpInquiryCreditCardRecordResponse.CardStateEnum.可以加油_无里程限制,
- });
- }
- else if (context.Incoming.Message is PumpInquiryBlackAndWhiteListRequest pumpInquiryBlackAndWhiteListRequest)
- {
- #warning should query cloud side to get the real data
- this.context.Outgoing.Write(new PumpInquiryBlackAndWhiteListResponse()
- {
- SourceAddress = this.fcPhysicalAddress,
- TargetAddress = this.terminalPhysicalAddress,
- MessageSequenceNumber = this.NextMsgSeqNo,
- M_FLAG_匹配标志 = PumpInquiryBlackAndWhiteListResponse.ResultEnum.不匹配,
- ASN卡应用号 = pumpInquiryBlackAndWhiteListRequest.ASN卡应用号,
- });
- }
- else if (context.Incoming.Message is PumpInternalErrorRequest pumpInternalErrorRequest)
- {
- #warning may need notify someone that error happened??
- this.context.Outgoing.Write(new PumpInternalErrorResponse()
- {
- SourceAddress = this.fcPhysicalAddress,
- TargetAddress = this.terminalPhysicalAddress,
- MessageSequenceNumber = this.NextMsgSeqNo,
- COMMAND_通讯的命令字 = 0,
- FLAG_成功标志 = 0,
- });
- }
- else
- {
- if (logger.IsEnabled(LogLevel.Debug))
- logger.LogDebug("Incoming an unknown message, will do nothing and ignore.");
- }
- }
- public string Name => this.GetType().FullName;
- public Guid Id => new Guid();
- /// <summary>
- /// Gets the Identification of the pump for the system. Is the logical number of the pump
- /// </summary>
- public int PumpId => this.pumpId;
- /// <summary>
- /// PC 机的地址范围:0xE0~0xF9
- /// </summary>
- private byte fcPhysicalAddress;
- /// <summary>
- /// 加液机的通讯终端的逻辑编号POS-P, 通讯终端出厂时POS-P 设为0xFF
- /// </summary>
- private byte terminalPhysicalAddress;
- public int PumpPhysicalId => this.terminalPhysicalAddress;
- public int AmountDecimalDigits => 2;
- public int VolumeDecimalDigits => 2;
- public int PriceDecimalDigits => 2;
- public int VolumeTotalizerDecimalDigits => 2;
- [UniversalApi]
- public virtual async Task<LogicalDeviceState> QueryStatusAsync()
- {
- return this.lastLogicalDeviceState;
- }
- /// <summary>
- ///
- /// </summary>
- /// <returns>MoneyTotalizer:VolumnTotalizer</returns>
- [UniversalApi]
- public async Task<Tuple<int, int>> QueryTotalizerAsync(byte logicalNozzleId)
- {
- var response = await this.context.Outgoing.WriteAsync(new PcReadPumpAccumulatorRequest()
- {
- SourceAddress = this.fcPhysicalAddress,
- TargetAddress = this.terminalPhysicalAddress,
- MessageSequenceNumber = this.NextMsgSeqNo,
- }, (_, testResponse) => testResponse is PcReadPumpAccumulatorResponse, 4000) as PcReadPumpAccumulatorResponse;
- if (response == null) return new Tuple<int, int>(-1, -1);
- var volTotalizerValue = response?.MZN_单个油枪.FirstOrDefault()?.V_TOT_升累计;
- return new Tuple<int, int>(-1, volTotalizerValue ?? -1);
- }
- [UniversalApi]
- public async Task<PcReadPumpInfoResponse> PC机读取加液机信息Async()
- {
- var response = await this.context.Outgoing.WriteAsync(new PcReadPumpInfoRequest()
- {
- SourceAddress = this.fcPhysicalAddress,
- TargetAddress = this.terminalPhysicalAddress,
- MessageSequenceNumber = this.NextMsgSeqNo,
- }, (_, testResponse) => testResponse is PcReadPumpInfoResponse, 4000) as PcReadPumpInfoResponse;
- return response;
- }
- [UniversalApi]
- public async Task<KaJiLianDongV11MessageTemplateBase> PC机主动读取加液记录Async(int pos_TTC_终端交易号)
- {
- var response = await this.context.Outgoing.WriteAsync(new PcReadTransactionRequest(pos_TTC_终端交易号)
- {
- SourceAddress = this.fcPhysicalAddress,
- TargetAddress = this.terminalPhysicalAddress,
- MessageSequenceNumber = this.NextMsgSeqNo,
- }, (_, testResponse) => testResponse is PumpNotifyTransactionDoneRequest || testResponse is PcReadTransactionButDoesNotFindResponse, 4000);
- return response;
- }
- public virtual async Task<bool> ChangeFuelPriceAsync(int newPriceWithoutDecimalPoint, byte logicalNozzleId)
- {
- throw new NotImplementedException();
- }
- /// <summary>
- ///
- /// </summary>
- /// <param name="logicalNozzleId">useless for this type of pump, it always one pump one nozzle</param>
- /// <returns></returns>
- public virtual async Task<bool> AuthorizeAsync(byte logicalNozzleId)
- {
- throw new NotImplementedException();
- }
- /// <summary>
- ///
- /// </summary>
- /// <param name="moneyAmount"></param>
- /// <param name="logicalNozzleId">useless for this type of pump, it always one pump one nozzle</param>
- /// <returns></returns>
- public virtual async Task<bool> AuthorizeWithAmountAsync(int moneyAmountWithoutDecimalPoint, byte logicalNozzleId)
- {
- throw new NotImplementedException();
- }
- /// <summary>
- ///
- /// </summary>
- /// <param name="volumn"></param>
- /// <param name="logicalNozzleId">useless for this type of pump, it always one pump one nozzle</param>
- /// <returns></returns>
- public virtual async Task<bool> AuthorizeWithVolumeAsync(int volumnWithoutDecimalPoint, byte logicalNozzleId)
- {
- throw new NotImplementedException();
- }
- public virtual async Task<bool> FuelingRoundUpByAmountAsync(int amount)
- {
- throw new NotImplementedException();
- }
- #region not implemented
- public async Task<bool> UnAuthorizeAsync(byte logicalNozzleId)
- {
- throw new NotImplementedException();
- }
- public async Task<bool> SuspendFuellingAsync()
- {
- throw new NotImplementedException();
- }
- public async Task<bool> ResumeFuellingAsync()
- {
- throw new NotImplementedException();
- }
- public async Task<bool> FuelingRoundUpByVolumeAsync(int volume)
- { throw new NotImplementedException(); }
- #endregion
- /// <summary>
- /// </summary>
- protected Dictionary<byte, FuelSaleTransaction> logicalNozzleIdToLastFuelSaleTrxMapping = new Dictionary<byte, FuelSaleTransaction>();
- public void OnFdcServerInit(Dictionary<string, object> parameters)
- {
- if (parameters.ContainsKey("LastPriceChange"))
- {
- }
- /* Load Last sale(from db) for void the case of FC accidently disconnect from Pump in fueling,
- and may cause a fueling trx gone from FC control */
- if (parameters.ContainsKey("LastFuelSaleTrx"))
- {
- // nozzle logical id:lastSale
- //var lastFuelSaleTrxes = parameters["LastFuelSaleTrx"] as Dictionary<byte, FuelSaleTransaction>;
- //foreach (var lastFuelSaleTrx in lastFuelSaleTrxes)
- //{
- // logger.Info("Pump: " + this.pumpId + ", OnFdcServerInit, load last fuel sale " +
- // "on logical nozzle: " + lastFuelSaleTrx.Key + " with value: " + lastFuelSaleTrx.Value);
- // this.logicalNozzleIdToLastFuelSaleTrxMapping.Remove(lastFuelSaleTrx.Key);
- // this.logicalNozzleIdToLastFuelSaleTrxMapping.Add(lastFuelSaleTrx.Key, lastFuelSaleTrx.Value);
- //}
- }
- }
- public async Task<bool> LockNozzleAsync(byte logicalNozzleId)
- {
- return false;
- }
- public async Task<bool> UnlockNozzleAsync(byte logicalNozzleId)
- {
- return false;
- }
- }
- }
|