/// <summary> /// Runs polling loop this many times at a minimum. /// Due to timing limitations, this is a minimum run count /// and their may be 1 or more extra loops executed. /// </summary> /// <param name="loops">Loops to run</param> /// <returns></returns> public T RunIdleFor(int loops) { // Setup a semaphore to wait for this many polling loops var sem = new EmulationLoopSemaphore(_token) { SignalAt = loops }; _emulator.OnPollResponseSent += sem.LoopCallback; sem.OnLoopCalled += (sender, args) => { // Always be idle _emulator.CurrentState = Rs232State.Idling; }; // Create a new validator so we have perfect control of the state var validator = new ApexValidator(Config); validator.StartPollingLoop(); // Wait for signal sem.Gate.WaitOne(); // Cleanup validator.StopPollingLoop(); _emulator.OnPollResponseSent -= sem.LoopCallback; return(_emulator); }
/// <summary> /// Every n loops, issue the specified credit value /// </summary> /// <param name="loops">Count of loops between credits</param> /// <param name="count">Run this many times. -1 to loop forever.</param> /// <param name="creditIndices">Credits to issue</param> public T CreditEveryNLoops(int loops, int count, params byte[] creditIndices) { // Create a new validator so we have perfect control of the state var validator = new ApexValidator(Config); // Setup a semaphore to wait for this many polling loops var sem = new EmulationLoopSemaphore(_token) { SignalAt = loops * count }; // Start in idling state _emulator.CurrentState = Rs232State.Idling; _emulator.CurrentEvents = Rs232Event.None; var next = 0; _emulator.OnPollResponseSent += sem.LoopCallback; sem.OnLoopCalled += (sender, args) => { switch (_emulator.CurrentState) { case Rs232State.None: _emulator.CurrentState = Rs232State.Idling; _emulator.CurrentEvents = Rs232Event.None; break; case Rs232State.Idling: if (sem.Iterations % loops == 0) { _emulator.CurrentState = Rs232State.Accepting; _emulator.CurrentEvents = Rs232Event.None; } break; case Rs232State.Accepting: _emulator.CurrentState = Rs232State.Stacking; _emulator.CurrentEvents = Rs232Event.None; break; case Rs232State.Stacking: _emulator.CurrentState = Rs232State.Idling; _emulator.CurrentEvents = Rs232Event.Stacked; _emulator.Credit = creditIndices[next++ % creditIndices.Length]; break; } }; validator.StartPollingLoop(); // Wait for signal sem.Gate.WaitOne(); // Cleanup validator.StopPollingLoop(); _emulator.OnPollResponseSent -= sem.LoopCallback; return(_emulator); }
/// <summary> /// Run a real bill validator /// </summary> /// <param name="logger">Logger attaches to validator</param> /// <param name="args">Program arguments</param> private static void RunValidator(ILogger logger, string[] args) { var portName = args.FirstOrDefault(); if (string.IsNullOrEmpty(portName)) { Console.WriteLine("Usage: rs232validator.cli.exe portName"); return; } var config = Rs232Config.UsbRs232Config(portName, logger); var validator = new ApexValidator(config); validator.OnLostConnection += (sender, eventArgs) => { config.Logger?.Error($"[APP] Lost connection to acceptor"); }; validator.OnBillInEscrow += (sender, i) => { // For USA this index represent $20. This example will always return a $20 // Alternatively you could set the Rs232Config mask to 0x5F to disable a 20. if (i == 5) { config.Logger.Info($"[APP] Issuing a return command for this {BillValues[i]}"); validator.Return(); } else { config.Logger.Info($"[APP] Issuing stack command for this {BillValues[i]}"); validator.Stack(); } }; validator.OnCreditIndexReported += (sender, i) => { config.Logger.Info($"[APP] Credit issued: {BillValues[i]}"); }; validator.OnStateChanged += (sender, state) => { config.Logger.Info($"[APP] State changed from {state.OldState} to {state.NewState}"); }; validator.OnEventReported += (sender, evt) => { config.Logger.Info($"[APP] Event(s) reported: {evt}"); }; validator.OnCashBoxRemoved += (sender, eventArgs) => { config.Logger.Info("[APP] Cash box removed"); }; validator.OnCashBoxAttached += (sender, eventArgs) => { config.Logger.Info("[APP] Cash box attached"); }; if (!validator.StartPollingLoop()) { config.Logger.Error("[APP] Failed to start RS232 main loop"); return; } config.Logger.Info("[APP] Validator is now running. CTRL+C to Exit"); while (true) { Thread.Sleep(TimeSpan.FromMilliseconds(100)); if (!validator.IsUnresponsive) { continue; } config.Logger?.Error("[APP] validator failed to start. Quitting now"); validator.StopPollingLoop(); break; } }
private static void Main(string[] args) { var portName = args.FirstOrDefault(); if (string.IsNullOrEmpty(portName)) { Console.WriteLine("Usage: app portName"); return; } var logger = new ConsoleLogger { Level = 3 }; var config = Rs232Config.UsbRs232Config(portName, logger); var validator = new ApexValidator(config); validator.OnLostConnection += (sender, eventArgs) => { config.Logger?.Error($"[APP] Lost connection to acceptor"); }; validator.OnCreditIndexReported += (sender, i) => { config.Logger.Info($"[APP] Credit issued: {BillValues[i]}"); }; validator.OnStateChanged += (sender, state) => { config.Logger.Info($"[APP] State changed from {state.OldState} to {state.NewState}"); }; validator.OnEventReported += (sender, evt) => { config.Logger.Info($"[APP] Event(s) reported: {evt}"); }; validator.OnCashBoxRemoved += (sender, eventArgs) => { config.Logger.Info("[APP] Cash box removed"); }; validator.OnCashBoxAttached += (sender, eventArgs) => { config.Logger.Info("[APP] Cash box attached"); }; if (!validator.StartPollingLoop()) { config.Logger.Error("[APP] Failed to start RS232 main loop"); return; } config.Logger.Info("[APP] Validator is now running. CTRL+C to Exit"); while (true) { Thread.Sleep(TimeSpan.FromMilliseconds(100)); if (!validator.IsUnresponsive) { continue; } config.Logger?.Error("[APP] validator failed to start. Quitting now"); validator.StopPollingLoop(); break; } }