package com.plectix.simulator.controller;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import com.plectix.simulator.util.ExponentialMovingAverage;
public class SimulatorProgressMonitor implements SimulatorCallableListener {
private static final int TIME_WINDOW = 10;
private boolean aborted= false;
private long startTimestamp;
private int numberOfRuns;
private double doneCount = 0;
private ExponentialMovingAverage exponentialMovingAverage = new ExponentialMovingAverage(TIME_WINDOW);
private List<SimulatorFutureTask> futureTaskList = null;
private SimulatorCallableListener listener = null;
private ExecutorCompletionService<SimulatorResultsData> executorCompletionService = null;
//**********************************************************************************************************
/**
* @param simulatorFactoryInterface
* @param simulationInputDataList
* @param listener
* @param executorCompletionService
*/
public SimulatorProgressMonitor(SimulatorFactoryInterface simulatorFactoryInterface,
List<SimulatorInputData> simulationInputDataList,
SimulatorCallableListener listener,
ExecutorCompletionService<SimulatorResultsData> executorCompletionService) {
super();
if (simulatorFactoryInterface == null || simulationInputDataList == null || executorCompletionService == null) {
throw new RuntimeException("Unexpected null objects");
}
this.listener = listener;
this.executorCompletionService = executorCompletionService;
this.numberOfRuns = simulationInputDataList.size();
futureTaskList = new ArrayList<SimulatorFutureTask>(numberOfRuns);
startTimestamp = System.currentTimeMillis();
for (SimulatorInputData simulatorInputData : simulationInputDataList) {
SimulatorCallable simulatorCallable = new SimulatorCallable(simulatorFactoryInterface.createSimulator(), simulatorInputData, this);
SimulatorFutureTask futureTask = new SimulatorFutureTask(simulatorCallable);
executorCompletionService.submit(futureTask, futureTask.getSimulator().getSimulatorResultsData());
futureTaskList.add(futureTask);
}
}
//**********************************************************************************************************
/**
* Retrieves and removes the Future representing the next completed task,
* waiting if necessary up to the specified wait time if none are yet present.
*
* @param timeout how long to wait before giving up, in units of <code>unit</code>
* @param unit a <code>TimeUnit</code> determining how to interpret the <code>timeout</code> parameter
*
* @return the Future representing the next completed task or <code>null</code>
* if the specified waiting time elapses before one is present
*
* @throws InterruptedException - if interrupted while waiting
*/
public Future<SimulatorResultsData> poll(long timeout, TimeUnit unit) throws InterruptedException {
return executorCompletionService.poll(timeout, unit);
}
//**********************************************************************************************************
/**
* Aborts all jobs.
*/
public void abort() {
for (SimulatorFutureTask f : futureTaskList) {
f.cancel(true);
}
aborted = true;
}
//**********************************************************************************************************
/**
* Returns [0-1], -1 for unknown
*/
public double getAmountComplete() {
synchronized (exponentialMovingAverage) {
return doneCount / numberOfRuns;
}
}
//**********************************************************************************************************
/**
* Expected Time Of Completion for this job in milliseconds, Returns 0 for unknown
*/
public long getExpectedTimeOfCompletion() {
synchronized (exponentialMovingAverage) {
return (long) exponentialMovingAverage.getCurrentValue();
}
}
//**********************************************************************************************************
/**
* Returns the start time of the process
*/
public long getStartTimestamp() {
return startTimestamp;
}
//**********************************************************************************************************
/**
* Returns current status, null for none
*/
public String getStatus() {
synchronized (exponentialMovingAverage) {
if (doneCount < numberOfRuns) {
if (aborted) {
return "Aborting...";
}
return "Run no: " + doneCount;
} else {
if (aborted) {
return "Aborted!";
}
return "Finished Simulation";
}
}
}
//**********************************************************************************************************
/**
*
*/
public void finished(SimulatorCallable simulatorCallable) {
synchronized (exponentialMovingAverage) {
doneCount++;
exponentialMovingAverage.addValue(startTimestamp + numberOfRuns*(System.currentTimeMillis()-startTimestamp)/doneCount);
}
if (listener != null) {
listener.finished(simulatorCallable);
}
}
}