using Edge.Core.IndustryStandardInterface.Pump.Fdc;
using Edge.Core.IndustryStandardInterface.Pump;
using HengshanPaymentTerminal;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using HengshanPaymentTerminal.MessageEntity.Outgoing;
namespace HengshanPaymentTerminal
{
///
/// A neutral pump handler that represents a Hengshan pump regardless of its protocol (Hengshan or TQC).
/// Actuall this is a fake pump handler since it doesn't have direct control over the pumps.
///
public class HengshanPumpHandler : IFdcPumpController
{
#region Fields
//The parent of the pump handler.
private HengshanPayTermHandler terminalHandler;
private LogicalDeviceState state = LogicalDeviceState.FDC_READY;
//private SpsManager spsManager;
private byte frameSqNo;
private object syncObj = new object();
private bool totalizerRequested = false;
private byte totalizerRequestedNozzleLogicId = 0;
private bool lockPumpRequested = false;
private bool unlockPumpRequested = false;
private LogicalNozzle nozzle;
private List nozzleList;
private List siteNozzleNoList;
#endregion
#region Logger
private static NLog.Logger logger = NLog.LogManager.LoadConfiguration("NLog.config").GetLogger("IPosPlusApp");
#endregion
#region Constructor
public HengshanPumpHandler(HengshanPayTermHandler terminalHandler, string name, int pumpId, List nozzles, List siteNozzleNoList)
{
this.terminalHandler = terminalHandler;
this.terminalHandler.OnCheckCommandReceived += TerminalHandler_OnCheckCommandReceived;
Name = name;
PumpId = pumpId;
nozzle = new LogicalNozzle(pumpId, Convert.ToByte(pumpId), 1, null);
//spsManager = new SpsManager();
this.siteNozzleNoList = siteNozzleNoList;
nozzleList = new List();
foreach (var nozzleNo in nozzles)
{
var logicalNozzle = new LogicalNozzle(PumpId, Convert.ToByte(nozzleNo), Convert.ToByte(nozzleNo), null);
nozzleList.Add(logicalNozzle);
}
}
#endregion
#region Properties
public string Name { get; }
public int PumpId { get; }
public int PumpPhysicalId => 1;
public IEnumerable Nozzles
{
get
{
//For Hengshan pumps, there is only one logical nozzle per pump, no real price set.
return nozzleList;//return new List { nozzle };
}
}
//Decimal digit settings, China domestic standard
//小数点后位数设定,中国国内标准,一般为2位
public int AmountDecimalDigits => 2;
public int VolumeDecimalDigits => 2;
public int PriceDecimalDigits => 2;
public int VolumeTotalizerDecimalDigits => 2;
public byte FrameSqNo
{
get
{
lock (syncObj)
{
return frameSqNo;
}
}
set
{
lock (syncObj)
{
frameSqNo = value;
}
}
}
#endregion
#region Events
public event EventHandler OnStateChange;
public event EventHandler OnCurrentFuellingStatusChange;
public event EventHandler OnFuelPriceChangeRequested;
#endregion
#region Event handlers
private byte GetSiteNozzleNoByLogicId(byte logicId)
{
foreach (var nozzleNo in siteNozzleNoList)
{
if (terminalHandler.NozzleLogicIdDict[nozzleNo] == logicId)
return Convert.ToByte(nozzleNo);
}
return Convert.ToByte(PumpId);
}
private void TerminalHandler_OnCheckCommandReceived(object sender, CheckCommandEventArgs e)
{
if (totalizerRequested
&& e.CheckCommandRequest.SourceAddress == terminalHandler.GetSubAddressForPump(PumpId))
{
var request = new ReadVolumeTotal
{
Prefix = 0xFA,
SourceAddress = Convert.ToByte(terminalHandler.GetSubAddressForPump(PumpId)),
DestinationAddress = Convert.ToByte(terminalHandler.GetSubAddressForPump(PumpId)),
FrameSqNoByte = e.CheckCommandRequest.FrameSqNoByte,
Handle = (byte)MessageEntity.Command.ReadVolumeTotalizer,
NozzleNo = GetSiteNozzleNoByLogicId(totalizerRequestedNozzleLogicId)//Convert.ToByte(PumpId)
};
terminalHandler.PendMessage(request);
Log("Request to terminal to get totalizer, pending!");
}
else if ((lockPumpRequested || unlockPumpRequested)
&& e.CheckCommandRequest.SourceAddress == terminalHandler.GetSubAddressForPump(PumpId))
{
LockOrUnlockPumpRequest request = new LockOrUnlockPumpRequest
{
Prefix = 0xFA,
SourceAddress = Convert.ToByte(terminalHandler.GetSubAddressForPump(PumpId)),
DestinationAddress = Convert.ToByte(terminalHandler.GetSubAddressForPump(PumpId)),
Handle = Convert.ToByte(MessageEntity.Command.LockOrUnlockPump),
FrameSqNoByte = e.CheckCommandRequest.FrameSqNoByte,
FPCode = EncodeFPCodeString(PumpId, PumpId),
OperationType = lockPumpRequested ? Support.LockUnlockOperation.Lock : Support.LockUnlockOperation.Unlock
};
terminalHandler.PendMessage(request);
var currentPump = terminalHandler.PumpStatusDict.GetValueOrDefault(PumpId);
if (currentPump == null)
Log("Could not get the pump state holder");
else
Log($"Current pump state holder, pump no: {currentPump.PumpNo}");
currentPump.OperationType = request.OperationType;
Log($"Current pump state holder, operation type set to: {request.OperationType}");
//terminalHandler.Write(request);
if (lockPumpRequested)
Log($"PumpNo: {PumpId}, Request to lock pump, pending!");
if (unlockPumpRequested)
Log($"PumpNo: {PumpId}, Request to unlock pump, pending!");
}
}
public void FirePumpStateChange(LogicalDeviceState state, byte nozzeLogicId)
{
var logicalNozzle = new LogicalNozzle(PumpId, Convert.ToByte(PumpPhysicalId), nozzeLogicId, null);
this.state = state;
if (state == LogicalDeviceState.FDC_READY)
OnStateChange?.Invoke(this, new FdcPumpControllerOnStateChangeEventArg(LogicalDeviceState.FDC_READY));
else
OnStateChange?.Invoke(this, new FdcPumpControllerOnStateChangeEventArg(state, logicalNozzle));
}
public void FireNozzleStateChange(LogicalDeviceState? state)
{
nozzle = new LogicalNozzle(PumpId, Convert.ToByte(PumpPhysicalId), 1, null);
if (state.HasValue)
nozzle.LogicalState = state;
else
nozzle.LogicalState = null;
OnStateChange?.Invoke(this, new FdcPumpControllerOnStateChangeEventArg(LogicalDeviceState.FDC_READY, nozzle));
}
//2023-08-22 状态变更
public void FireFuelingStatusChange(FdcTransaction fuelingTransaction)
{
OnCurrentFuellingStatusChange?.Invoke(this, new FdcTransactionDoneEventArg(fuelingTransaction));
}
#endregion
#region Pump interactions
public LogicalDeviceState QueryStatus()
{
var stateHolder = terminalHandler.PumpStatusDict.GetValueOrDefault(PumpId);
if (stateHolder != null)
{
if (stateHolder.State.DispenserState == 3)
{
return LogicalDeviceState.FDC_READY;
}
else if (stateHolder.State.DispenserState == 2)
{
return LogicalDeviceState.FDC_LOCKED;
}
}
return LogicalDeviceState.FDC_READY;
}
///
/// locks a nozzle
///
///
///
public Task LockNozzleAsync(byte logicalNozzleId)
{
var stateHolder = terminalHandler.PumpStatusDict.GetValueOrDefault(PumpId);
if (stateHolder != null)
{
if (stateHolder.State.DispenserState == 2)
{
// If the pump is already locked, reply with success immediately.
return Task.FromResult(true);
}
else if (stateHolder.State.DispenserState != 3)
{
// Avoid handling locking when pump is fueling or in other states.
return Task.FromResult(false);
}
}
TaskCompletionSource tcs = new TaskCompletionSource();
EventHandler eventHandler = null;
eventHandler += (o, e) =>
{
Log($"Locking pump, result received, success? {e.Result}");
lockPumpRequested = false;
tcs.SetResult(e.Result);
if (e.Result)
{
FireNozzleStateChange(LogicalDeviceState.FDC_LOCKED);
}
terminalHandler.OnLockUnlockCompleted -= eventHandler;
var currentPump = terminalHandler.PumpStatusDict.GetValueOrDefault(PumpId);
if (currentPump != null)
{
currentPump.OperationType = Support.LockUnlockOperation.None;
}
};
terminalHandler.OnLockUnlockCompleted += eventHandler;
lockPumpRequested = true;
return tcs.Task;
}
///
/// unlock a locked nozzle
///
///
///
public Task UnlockNozzleAsync(byte logicalNozzleId)
{
var stateHolder = terminalHandler.PumpStatusDict.GetValueOrDefault(PumpId);
if (stateHolder != null)
{
if (stateHolder.State.DispenserState == 3)
{
// Need to do nothing when pump is not locked.
return Task.FromResult(true);
}
}
TaskCompletionSource tcs = new TaskCompletionSource();
EventHandler eventHandler = null;
eventHandler += (o, e) =>
{
Log($"Unlocking pump, result received, success? {e.Result}");
unlockPumpRequested = false;
tcs.SetResult(e.Result);
if (e.Result)
{
FireNozzleStateChange(null);
}
terminalHandler.OnLockUnlockCompleted -= eventHandler;
var currentPump = terminalHandler.PumpStatusDict.GetValueOrDefault(PumpId);
if (currentPump != null)
{
currentPump.OperationType = Support.LockUnlockOperation.None;
}
};
terminalHandler.OnLockUnlockCompleted += eventHandler;
unlockPumpRequested = true;
return tcs.Task;
}
#endregion
#region FDC Server Init
public void OnFdcServerInit(Dictionary parameters)
{
}
public Task QueryStatusAsync()
{
return Task.FromResult(state);
}
public void QueryTotalizerAsync(int pumpId, byte logicalNozzleId)
{
Log($"Query totalizer internally...");
var request = new ReadVolumeTotal
{
Prefix = 0xFA,
SourceAddress = Convert.ToByte(pumpId),
DestinationAddress = Convert.ToByte(pumpId),
FrameSqNoByte = frameSqNo,
Handle = (byte)MessageEntity.Command.ReadVolumeTotalizer,
NozzleNo = logicalNozzleId
};
terminalHandler.Write(request);
}
public Task> QueryTotalizerAsync(byte logicalNozzleId)
{
Log($"LogicalNozzle {logicalNozzleId} querying totalizer...");
TaskCompletionSource> tcs = new TaskCompletionSource>();
EventHandler eventHandler = null;
eventHandler += (o, e) =>
{
Log($"Totalizer data for nozzle: {e.NozzleNo}, money acc: {e.Totalizer.Item1}, volume acc: {e.Totalizer.Item2}");
totalizerRequested = false; //Reset the value so that no more request will be sent.
if (e.NozzleNo == GetSiteNozzleNoByLogicId(logicalNozzleId))
{
Log("Totalizer data ready");
tcs.SetResult(e.Totalizer);
terminalHandler.OnTotalizerReceived -= eventHandler;
}
totalizerRequestedNozzleLogicId = 0;
};
terminalHandler.OnTotalizerReceived += eventHandler;
totalizerRequestedNozzleLogicId = logicalNozzleId;
totalizerRequested = true;
return tcs.Task;
}
public Task ChangeFuelPriceAsync(int newPriceWithoutDecimalPoint, byte logicalNozzleId)
{
logger.Info($"Change price received, Pump {PumpId}, LogicalNozzle {logicalNozzleId}, Price {newPriceWithoutDecimalPoint}");
//Let the iPOS App handle the fuel price change.
OnFuelPriceChangeRequested?.Invoke(this, new FuelPriceChangeRequestEventArgs
{
NozzleId = logicalNozzleId,
PumpId = (byte)PumpId,
Price = newPriceWithoutDecimalPoint
});
Log($"application handled fuel price change");
return Task.FromResult(true);
//TaskCompletionSource tcs = new TaskCompletionSource();
//EventHandler eventHandler = null;
//eventHandler += (o, e) =>
//{
// Log("Terminal requested fuel price");
// tcs.SetResult(e.Requested);
// terminalHandler.OnTerminalFuelPriceDownloadRequested -= eventHandler;
//};
//terminalHandler.OnTerminalFuelPriceDownloadRequested += eventHandler;
//return tcs.Task;
}
public Task AuthorizeAsync(byte logicalNozzleId)
{
return Task.FromResult(false);
}
public Task UnAuthorizeAsync(byte logicalNozzleId)
{
return Task.FromResult(false);
}
public Task AuthorizeWithAmountAsync(int moneyAmountWithoutDecimalPoint, byte logicalNozzleId)
{
return Task.FromResult(false);
}
public Task AuthorizeWithVolumeAsync(int volumnWithoutDecimalPoint, byte logicalNozzleId)
{
return Task.FromResult(false);
}
public Task FuelingRoundUpByAmountAsync(int amount)
{
return Task.FromResult(false);
}
public Task FuelingRoundUpByVolumeAsync(int volume)
{
return Task.FromResult(false);
}
public Task SuspendFuellingAsync()
{
return Task.FromResult(false);
}
public Task ResumeFuellingAsync()
{
return Task.FromResult(false);
}
#endregion
private string EncodeFPCodeString(int nozzleId, int pumpId)
{
return nozzleId.ToString("X").PadLeft(2, '0') + pumpId.ToString("X").PadLeft(2, '0');
}
#region Log
private void Log(string message)
{
logger.Info($"Pump Handler: {PumpId}, {message}");
}
#endregion
}
}