package cz.agents.agentpolis.darptestbed.simmodel.agent.timer;
import java.util.ArrayList;
import java.util.List;
import org.apache.log4j.Logger;
import org.joda.time.Duration;
import cz.agents.agentpolis.darptestbed.global.Utils;
import cz.agents.agentpolis.darptestbed.simmodel.environment.model.TestbedModel;
import cz.agents.alite.common.event.Event;
import cz.agents.alite.common.event.EventHandler;
import cz.agents.alite.common.event.EventProcessor;
import cz.agents.alite.common.event.EventProcessorEventType;
/**
* The timer has a list of callbacks (e.g. agents) and an interval. Every time
* the interval elapses, the timer calls all the callbacks in its list. (It
* doesn't work with real time, but simulation time instead).
*
* @author Lukas Canda
*/
public class Timer {
private static final Logger LOGGER = Logger.getLogger(Timer.class);
private final String id;
/**
* Every time the interval elapses, the timer starts its activity (calling
* callbacks).
*/
protected long interval;
/**
* Objects, whose callback methods should be called regularly by this timer.
*/
protected List<TimerCallback> callbacks;
/**
* The total number of timers running in the simulation.
*/
protected int numberOfTimers;
/**
* Core processor for processing all simulation events.
*/
private final EventProcessor eventProcessor;
/**
* An event for the event processor.
*/
private EventHandler eventHandler;
/**
* A storage to save all data concerning taxi drivers and passengers
*/
protected final TestbedModel taxiModel;
/**
* Utils is a class that contains useful methods to measure distances etc.
* Here, we call it to update its map of distances now and then.
*/
private final Utils utils;
/**
* How many empty calls should the timer do before stopping the simulation.
* If this number is too low, then the simulation can stop in the middle,
* because nothing much is happening. If it is too big, the simulation can
* wait a lot of time after finishing.
*/
private final int NUMBER_OF_EMPTY_CALLS = 20;
/**
* This helps us stop at the right time (after running out of events for x
* times in a row)
*/
private int stopIn = NUMBER_OF_EMPTY_CALLS;
public Timer(String timerId, EventProcessor eventProcessor, TestbedModel taxiModel, Utils utils,
int numberOfTimers, long interval) {
if (interval <= 0) {
throw new RuntimeException("It is not possible to set up interval less or equal zero");
}
callbacks = new ArrayList<TimerCallback>();
this.id = timerId;
this.eventProcessor = eventProcessor;
this.taxiModel = taxiModel;
this.utils = utils;
this.interval = interval;
this.numberOfTimers = numberOfTimers;
}
public Timer(String timerId, EventProcessor eventProcessor, TestbedModel taxiModel, Utils utils,
int numberOfTimers, Duration interval) {
this(timerId, eventProcessor, taxiModel, utils, numberOfTimers, interval.getMillis());
}
/**
* The main method of the timer.
*/
public void start() {
eventHandler = new EventHandler() {
/**
* This is the code to be repeated over and over again (using the
* interval).
*/
@Override
public void handleEvent(Event event) {
long startTime = System.currentTimeMillis();
// the simulation is terminated when it doesn't contain any
// events except timers
if (eventProcessor.getCurrentTime() > interval
&& eventProcessor.getCurrentQueueLength() <= numberOfTimers) {
if (stopIn <= 0) {
eventProcessor.addEvent(EventProcessorEventType.STOP, null, null, null);
} else {
stopIn--;
}
} else {
stopIn = NUMBER_OF_EMPTY_CALLS;
}
eventProcessor.addEvent(eventHandler, interval);
//LOGGER.debug("timer " + id + " callbacks");
// // refresh map of distances to keep it updated
// utils.refreshPassenAndDistMap();
// callbacks
for (TimerCallback call : callbacks) {
call.timerCallback();
}
utils.logAlgRealTime(System.currentTimeMillis() - startTime);
}
@Override
public EventProcessor getEventProcessor() {
return null;
}
};
eventProcessor.addEvent(eventHandler, interval);
}
public void addCallback(TimerCallback callback) {
this.callbacks.add(callback);
}
public String getId() {
return id;
}
}