/*
***************************************************************************************
* Copyright (C) 2006 EsperTech, Inc. All rights reserved. *
* http://www.espertech.com/esper *
* http://www.espertech.com *
* ---------------------------------------------------------------------------------- *
* The software in this package is published under the terms of the GPL license *
* a copy of which has been included with this distribution in the license.txt file. *
***************************************************************************************
*/
package com.espertech.esper.example.terminal.jse.simulate;
import com.espertech.esper.example.terminal.jse.event.*;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
/**
* The primary class used to perform a standalone simulation.
* <p/>
* We generate terminal events per batch. For each batch the following can happen
* - some terminal can be out of order straight
* - some other can issue low paper events straight
* - the working terminals are all used for checkin
* - 33% of checkin do not complete in this batch (but will in the next batch)
* - per checkin there is a chance that the operation is either cancelled or aborted due to a terminal switching
* to out of order
* <p/>
* At each batch all terminals are assumed to be repaired
*/
public class EventGenerator {
private static final int TERMINAL_COUNT = 100;
private static final int TERMINAL_OUTOFORDER_LIKELYHOOD_PER_CHECKIN = 10; //max 1000
private static final int TERMINAL_EVENT_LIKELYHOOD_PER_BATCH = 100; //max 100
private final Random random;
public EventGenerator() {
this.random = new Random();
}
public List<BaseTerminalEvent> generateBatch() {
List<BaseTerminalEvent> batch = new LinkedList<BaseTerminalEvent>();
// Sometimes generate a low paper or out-of-order event
List<String> terminalOutOfOrderIds = generateTerminalEvent(batch);
// Generate a couple of checkin, completed and cancelled events, and sometimes an out-of-order
generateCheckin(batch, terminalOutOfOrderIds);
return batch;
}
private void generateCheckin(List<BaseTerminalEvent> eventBatch, List<String> outOfOrderIds) {
// Generate unique terminal ids
String[] termIds = new String[TERMINAL_COUNT];
for (int i = 0; i < termIds.length; i++) {
termIds[i] = Integer.toString(i);
}
// Swap terminals to get a random ordering
randomize(termIds);
// Add a check-in event for each
for (String termId : termIds) {
if (outOfOrderIds.contains(termId)) {
continue;
}
Checkin checkin = new Checkin(new Terminal(termId));
eventBatch.add(checkin);
}
// Add a cancelled or completed or out-of-order for each
int completedCount = 0;
int cancelledCount = 0;
int outOfOrderCount = 0;
int slowCustomer = 0;
for (String termId : termIds) {
if (outOfOrderIds.contains(termId)) {
continue;
}
BaseTerminalEvent theEvent = null;
// With a x in 1000 chance send an OutOfOrder
if (random.nextInt(1000) < TERMINAL_OUTOFORDER_LIKELYHOOD_PER_CHECKIN) {
outOfOrderCount++;
theEvent = new OutOfOrder(new Terminal(termId));
System.out.println("\tGenerated a Checkin followed by " + theEvent.getType() + " event for terminal " + theEvent.getTerminal().getId());
} else if (random.nextInt(3) < 1) {
completedCount++;
theEvent = new Completed(new Terminal(termId));
} else if (random.nextInt(3) < 2) {
cancelledCount++;
theEvent = new Cancelled(new Terminal(termId));
} else {
slowCustomer++;
// 33% of customers are doing checking over 2 runs (reading all screens and talking etc)
// this means we 'll have pending Checkin events in the system
// that can later be completed, cancelled, and for which there can also be new Checkin events
// that is a customer waiting after another customer to finish using this Terminal
// By counting orphan Checkin events we can spot terminals for which there is a traffic jam or
// compute latency statistics etc.
//
// Note
// Further simulation should add a repair action that re-activate a terminal and keep tracks
// of the terminal status between each batch
}
if (theEvent != null) {
eventBatch.add(theEvent);
}
}
System.out.println("\t=> Generated " + termIds.length + " Checkin events followed by " +
completedCount + " Completed, " +
cancelledCount + " Cancelled, " +
outOfOrderCount + " OutOfOrder events" +
" (" + slowCustomer + " pending)");
}
private List<String> generateTerminalEvent(List<BaseTerminalEvent> eventBatch) {
List<String> outOfOrder = new ArrayList<String>();
if (random.nextInt(100) >= TERMINAL_EVENT_LIKELYHOOD_PER_BATCH) {
return outOfOrder;
}
BaseTerminalEvent theEvent = null;
if (random.nextBoolean()) {
theEvent = new LowPaper(getRandomTerminal());
} else {
Terminal terminal = getRandomTerminal();
theEvent = new OutOfOrder(terminal);
outOfOrder.add(terminal.getId());
}
eventBatch.add(theEvent);
System.out.println("\tGenerated " + theEvent.getType() + " event for terminal " + theEvent.getTerminal().getId());
return outOfOrder;
}
// Swap 100 values in the array
private void randomize(String[] values) {
for (int i = 0; i < values.length; i++) {
int pos1 = random.nextInt(values.length);
int pos2 = random.nextInt(values.length);
String temp = values[pos2];
values[pos2] = values[pos1];
values[pos1] = temp;
}
}
private Terminal getRandomTerminal() {
return new Terminal(getRandomTerminalId());
}
private String getRandomTerminalId() {
int id = random.nextInt(TERMINAL_COUNT);
return Integer.toString(id);
}
}