blob: 592599a11e9fac3955ea977fc71b2cc1f5208a61 [file] [log] [blame]
// Simulation.java
package org.eclipse.stem.jobs.simulation;
/*******************************************************************************
* Copyright (c) 2006, 2008 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Preferences;
import org.eclipse.core.runtime.Preferences.IPropertyChangeListener;
import org.eclipse.core.runtime.Preferences.PropertyChangeEvent;
import org.eclipse.core.runtime.Status;
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.stem.core.Constants;
import org.eclipse.stem.core.CorePlugin;
import org.eclipse.stem.core.graph.GraphPartitioner;
import org.eclipse.stem.core.model.STEMTime;
import org.eclipse.stem.core.scenario.Scenario;
import org.eclipse.stem.core.scenario.ScenarioInitializationException;
import org.eclipse.stem.core.scenario.impl.ScenarioImpl;
import org.eclipse.stem.core.scenario.provider.ScenarioItemProviderAdapterFactory;
import org.eclipse.stem.core.sequencer.Sequencer;
import org.eclipse.stem.core.solver.Solver;
import org.eclipse.stem.jobs.Activator;
import org.eclipse.stem.jobs.DisplaySafeExecutor;
import org.eclipse.stem.jobs.execution.Executable;
import org.eclipse.stem.jobs.preferences.PreferenceConstants;
import org.eclipse.stem.jobs.preferences.SimulationManagementPreferencePage;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
/**
* This class implements the main simulation logic of the STEM system. It runs
* in the background as a separate {@link Job} in eclipse.
*/
public class Simulation extends Executable implements ISimulation, IPropertyChangeListener {
/**
* The collection of {@link ISimulationListener}'s waiting to be told about
* {@link Simulation}'s events.
*/
// I think this should really be a {@link CopyOnWriteArrayList} like {@link
// #listenersSync}
private final List<ISimulationListener> listeners = new CopyOnWriteArrayList<ISimulationListener>();
/**
* The collection of {@link IBatchManagerListenerSync}'s waiting to be told
* about {@link BatchManagerEvent}'s
*/
private final List<ISimulationListenerSync> listenersSync = new CopyOnWriteArrayList<ISimulationListenerSync>();
private final List<ISimulationListener> listenersHeadless = new CopyOnWriteArrayList<ISimulationListener>();
private final List<ISimulationListenerSync> listenersSyncHeadless = new CopyOnWriteArrayList<ISimulationListenerSync>();
/**
* If <code>true</code> then the {@link Simulation} will sleep for a
* specified time period at the end of each simulation cycle.
*/
public boolean simulationSleep = SimulationManagementPreferencePage.DEFAULT_SIMULATION_SLEEP;
/**
* If {@link simulationSleep} is <code>true</code>, then this is the number
* of milliseconds at the end of each cycle that the {@link Simulation}
* should sleep.
*/
private int sleepMilliseconds = SimulationManagementPreferencePage.MIN_SIMULATION_SLEEP_MILLISECONDS;
/**
* The current state of the {@link Simulation}.
*/
private SimulationState simulationState;
/**
* This is the {@link Scenario} being simulated. It contains all of the
* {@link SimulationState} information.
*/
private Scenario scenario = null;
private Adapter adapter = null;
private GraphPartitioner partitioner;
/**
* This flag controls the execution of the {@link Simulation}. If it is
* <code>false</code> the {@link Simulation} stops running (sleeps) on the
* next cycle.
*
* @see #pause()
*/
private boolean keepRunning = true;
/**
* This flag controls the state of the {@link Simulation}. If
* <code>true</code> then the {@link Simulation} is stopped if it is running
* and the {@link Scenario} is reset to its initial state. The
* {@link Simulation} does NOT resume running after the reset.
*
* @see #resetSimulation()
*/
private boolean reset = false;
/**
* This flag indicates that the {@link Simulation} should complete a single
* step (cycle) and then pause.
*/
private boolean stepping = false;
/**
* If <code>true</code> then the {@link Simulation} is stopping
*/
private boolean stopping = false;
ScenarioItemProviderAdapterFactory scenarioItemProviderAdapterFactory = new ScenarioItemProviderAdapterFactory();
/**
* Constructor
*
* @param title
* the title of the {@link Simulation}.
* @param sequenceNumber
* the sequence number of the {@link Simulation}
*/
public Simulation(final String title, final int sequenceNumber) {
super(title == null ? "" : title, sequenceNumber); //$NON-NLS-1$
simulationState = SimulationState.PAUSED;
} // Simulation
/**
* Constructor
*
* @param scenario
* the {@link Scenario} to simulate
* @param sequenceNumber
* the sequence number of the {@link Simulation}
*/
public Simulation(final Scenario scenario, final int sequenceNumber, GraphPartitioner p) {
this(scenario.produceTitle(), sequenceNumber);
this.scenario = scenario;
this.partitioner = p;
setPreferences();
Activator.getDefault().getPluginPreferences().addPropertyChangeListener(this);
} // Simulation
/**
* Set the preferences.
*
* @see #simulationSleep
* @see #sleepMilliseconds
*/
protected void setPreferences() {
final Preferences preferences = Activator.getDefault()
.getPluginPreferences();
simulationSleep = preferences
.getBoolean(PreferenceConstants.SIMULATION_SLEEP_BOOLEAN);
sleepMilliseconds = preferences
.getInt(PreferenceConstants.SIMULATION_SLEEP_MILLISECONDS_INTEGER);
ScenarioImpl.reportEachUnresolvedIdentifiable = preferences
.getBoolean(PreferenceConstants.REPORT_EACH_UNRESOLVED_IDENTIFIABLE_BOOLEAN);
ScenarioImpl.reportDanglingAirTransportEdges = preferences
.getBoolean(PreferenceConstants.REPORT_DANGLING_AIR_TRANPORT_EDGES_BOOLEAN);
ScenarioImpl.reportNumberofUnresolvedIdentifiables = preferences
.getBoolean(PreferenceConstants.REPORT_NUMBER_OF_UNRESOLVED_IDENTIFIABLES_BOOLEAN);
} // setPerferences
/**
* @return the state of the {@link Simulation}
*/
public final SimulationState getSimulationState() {
return simulationState;
} // getSimulationState
/**
* @param simulationState
* the {@link SimulationState} to set
*/
private final void setSimulationState(final SimulationState simulationState) {
this.simulationState = simulationState;
fireSimulationChanged(simulationState);
} // setSimulationState
boolean firstStep = true;
/**
* @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
*/
@Override
protected IStatus run(final IProgressMonitor monitor) {
IStatus retValue = Status.OK_STATUS;
try {
setSimulationState(SimulationState.RUNNING);
keepRunning = true;
// Did we get a request to reset?
if (reset) {
// Yes
scenario.reset();
reset = false;
}
assert scenario.sane();
monitor.beginTask(scenario.produceTitle(), TOTAL_WORK);
final Sequencer sequencer = scenario.getSequencer();
// Figure out how much work has been performed for this scenario
// already.
// $ANALYSIS-IGNORE
monitor.worked((int) sequencer.getWorkComplete());
final Simulation self = this;
/* adapter = new AdapterImpl() {
@Override
public void notifyChanged(Notification msg)
{
Scenario scenario = (Scenario)msg.getNotifier();
switch(msg.getFeatureID(Scenario.class)) {
case ScenarioPackage.SCENARIO__PROGRESS:
SimulationEvent event = new SimulationEvent(self, SimulationState.RUNNING, scenario.getProgress());
self.fireSimulationChangedEvent(event);
break;
}
}
};
scenario.eAdapters().add(adapter);
*/
//ScenarioItemProvider sip = (ScenarioItemProvider)scenarioItemProviderAdapterFactory.adapt(this, ScenarioItemProvider.class);
//scenarioItemProviderAdapterFactory.addListener(this); // ugh
// Does the sequencer say we've finished before we've started?
if (!sequencer.isTimeToStop()) {
// No
while (keepRunning && !reset) {
final STEMTime currentTime = sequencer.getCurrentTime();
monitor.subTask(currentTime.toString());
// Attempt one step (cycle) in the simulation
if(scenario.getCanonicalGraph() == null)
scenario.initialize();
if(scenario.getSolver() == null) {
Solver [] solvers = this.getSolvers();
// Use the default finite difference when not available
for(Solver s:solvers)
if(s.getClass().getName().equals("org.eclipse.stem.solvers.fd.impl.FiniteDifferenceImpl"))
{scenario.setSolver(s);break;}
}
// Make sure the decorators are set on the solver
scenario.getSolver().setCanonicalGraph(scenario.getCanonicalGraph());
if(firstStep) {
scenario.getSolver().initialize(scenario.getSequencer().getNextTime(), partitioner);
setSimulationState(SimulationState.COMPLETED_CYCLE); // So that log is written with initial values
firstStep = false;
}
boolean success = scenario.step();
if(!success) {keepRunning = false;retValue = Status.CANCEL_STATUS;}
assert scenario.sane();
// To sleep, per chance to dream?
if (simulationSleep) {
// Yes
try {
Thread.sleep(sleepMilliseconds);
} catch(InterruptedException ie) {
ie.printStackTrace(); // Unlikley
}
}
monitor.worked(sequencer.getWorkIncrement());
// We stop when the sequencer tells us it is time
if (sequencer.isTimeToStop()) {
keepRunning = false;
retValue = Status.OK_STATUS;
}
// Or, if things are canceled
else if (monitor.isCanceled()) {
keepRunning = false;
retValue = Status.CANCEL_STATUS;
}
// Or, if we're stepping
else if (stepping) {
keepRunning = false;
}
setSimulationState(SimulationState.COMPLETED_CYCLE);
} // while keepRunning
// Was it time to stop?
if (sequencer.isTimeToStop() && retValue == Status.OK_STATUS) {
// Yes
setSimulationState(SimulationState.COMPLETED_SEQUENCE);
}
} // if NOT time to stop before we start
else {
// Yes
// The sequencer says we've finished before we started
Activator.logInformation(MessageFormat.format(Messages
.getString("Sim.Time_Error"), sequencer
.getCurrentTime().toString(), sequencer.getEndTime()
.toString()), null);
} // else
// Did we get a request to reset?
if (reset) {
// Yes
scenario.reset();
reset = false;
setSimulationState(SimulationState.RESET);
}
monitor.done();
} catch (final ScenarioInitializationException sie) {
// Problem. We're out of here.
handleException(sie, true);
keepRunning = false;
stopping = true;
} // catch RuntimeException
// Are we stopping or just pausing?
setSimulationState(stopping ? SimulationState.STOPPED
: SimulationState.PAUSED);
return retValue;
} // run
/**
* Start running the {@link Simulation}.
*/
public final void run() {
stepping = false;
schedule();
} // run
/**
* Pause the {@link Simulation}
*/
public final void pause() {
keepRunning = false;
} // pause
/**
* Reset the {@link Simulation}.
*/
public final void reset() throws ScenarioInitializationException {
reset = true;
stepping = false;
// Is the simulation currently paused?
if (getSimulationState().equals(SimulationState.PAUSED)) {
// Yes
scenario.reset();
reset = false;
setSimulationState(SimulationState.RESET);
setSimulationState(SimulationState.PAUSED);
} // if
} // reset
/**
* Step the {@link Simulation} one step/cycle if it hasn't already ended
*/
public final void step() {
stepping = true;
schedule();
} // stepSimulation
/**
* Stop the {@link Simulation}.
*/
public final void stop() {
stopping = true;
keepRunning = false;
// We need to set our state here, which will notify our listeners,
// because we may not be scheduled and so the run(IProgressMonitor)
// method may not be executing and so would not set the state to STOPPED
// (and thus notify listeners)
setSimulationState(SimulationState.STOPPED);
} // stepSimulation
/**
* @see org.eclipse.stem.jobs.execution.IExecutable#isRunning()
*/
public boolean isRunning() {
return !simulationState.equals(SimulationState.PAUSED);
}
/**
* @return the {@link Scenario}
*/
public final Scenario getScenario() {
return this.scenario;
}
/**
* @param scenario
* the {@link Scenario} to set
*/
protected final void setScenario(final Scenario scenario) {
this.scenario = scenario;
}
/**
* @see org.eclipse.stem.jobs.simulation.ISimulation#addSimulationListener(org.eclipse.stem.jobs.simulation.ISimulationListener)
*/
public void addSimulationListener(final ISimulationListener listener) {
addSimulationListener(listener, false);
} // addSimulationListener
public void addSimulationListener(final ISimulationListener listener, final boolean headlessSafe) {
if (headlessSafe) {
if (!listenersHeadless.contains(listener)) {
listenersHeadless.add(listener);
}
} else {
if (!listeners.contains(listener)) {
listeners.add(listener);
}
}
}
public void addSimulationListenerSync(final ISimulationListenerSync listener, final boolean headlessSafe) {
if (headlessSafe) {
if (!listenersSyncHeadless.contains(listener)) {
listenersSyncHeadless.add(listener);
}
} else {
if (!listenersSync.contains(listener)) {
listenersSync.add(listener);
}
}
}
/**
* @see org.eclipse.stem.jobs.simulation.ISimulation#addSimulationListenerSync(org.eclipse.stem.jobs.simulation.ISimulationListenerSync)
*/
public void addSimulationListenerSync(final ISimulationListenerSync listener) {
addSimulationListenerSync(listener, false);
} // addSimulationListenerSync
/**
* @see org.eclipse.stem.jobs.simulation.ISimulation#removeSimulationListener(org.eclipse.stem.jobs.simulation.ISimulationListener)
*/
public void removeSimulationListener(final ISimulationListener listener) {
listeners.remove(listener);
listenersHeadless.remove(listener);
} // removeSimulationListener
/**
* @see org.eclipse.stem.jobs.simulation.ISimulation#removeSimulationListenerSync(org.eclipse.stem.jobs.simulation.ISimulationListenerSync)
*/
public void removeSimulationListenerSync(
final ISimulationListenerSync listener) {
listenersSync.remove(listener);
listenersSyncHeadless.remove(listener);
} // removeSimulationListenerSync
/**
* Tell the listeners about the change in the {@link Simulation}'s state
*
* @param simulationState
* the new {@link SimulationState} of the {@link Simulation}
*/
private void fireSimulationChanged(final SimulationState simulationState) {
final SimulationEvent event = new SimulationEvent(this, simulationState);
fireSimulationChangedEvent(event);
} // fireSimulationManagerChanged
/**
* Tell the listeners about the change in the {@link Simulation}'s state
*
* @param simulationState
* the new {@link SimulationState} of the {@link Simulation}
*/
void fireSimulationChangedEvent(final SimulationEvent event)
{
for (final ISimulationListener listener : listeners) {
executeAsyncEvent(listener, event, false);
} // for
for (final ISimulationListener listener : listenersHeadless) {
executeAsyncEvent(listener, event, true);
} // for
for (final ISimulationListenerSync listener : listenersSync) {
executeSyncEvent(listener, event, false);
} // for
for (final ISimulationListenerSync listener : listenersSyncHeadless) {
executeSyncEvent(listener, event, true);
} // for
} // fireSimulationManagerChanged
/**
* @param listener
* @param event
* @param headlessSafe
*/
private void executeAsyncEvent(final ISimulationListener listener, final SimulationEvent event, final boolean headlessSafe)
{
DisplaySafeExecutor.executeAsync(new Runnable() {
public void run()
{
listener.simulationChanged(event);
}
}, headlessSafe);
}
/**
* @param listener
* @param event
* @param headlessSafe
*/
private void executeSyncEvent(final ISimulationListenerSync listener, final SimulationEvent event, final boolean headlessSafe)
{
DisplaySafeExecutor.executeSync(new Runnable() {
public void run()
{
listener.simulationChangedSync(event);
}
}, headlessSafe);
}
/**
* @see org.eclipse.core.runtime.Preferences.IPropertyChangeListener#propertyChange(org.eclipse.core.runtime.Preferences.PropertyChangeEvent)
*/
public void propertyChange(@SuppressWarnings("unused") final PropertyChangeEvent event) {
setPreferences();
} // propertyChange
/**
* @return the title of the {@link Scenario}
*/
@Override
public String toString() {
return scenario.produceTitle();
}
/**
* interruptRequested. Return true if this listener requests
* that a decorator stops updating labels
*
* @return boolean True if stop
*/
public boolean interruptRequested() {
return (!this.keepRunning && stopping);
}
public void destroy() {
List<ISimulationListener> tempList = new ArrayList<ISimulationListener>();
tempList.addAll(listeners);
for (ISimulationListener listener:tempList) {
this.removeSimulationListener(listener);
}
listeners.clear();
List<ISimulationListenerSync> tempListSync = new ArrayList<ISimulationListenerSync>();
tempListSync.addAll(listenersSync);
for (ISimulationListenerSync listener:tempListSync) {
this.removeSimulationListenerSync(listener);
}
listenersSync.clear();
tempList.clear();
tempListSync.clear();
scenario.eAdapters().remove(adapter);
Activator.getDefault().getPluginPreferences().removePropertyChangeListener(this);
}
private org.eclipse.stem.core.solver.Solver [] getSolvers() {
Solver [] solvers;
final IExtensionRegistry registry = Platform.getExtensionRegistry();
final IConfigurationElement[] solverConfigElements = registry
.getConfigurationElementsFor(org.eclipse.stem.core.Constants.ID_SOLVER_EXTENSION_POINT);
final List<Solver> temp = new ArrayList<Solver>();
//solvers = new Solver[solverConfigElements.length];
for (int i = 0; i < solverConfigElements.length; i++) {
final IConfigurationElement element = solverConfigElements[i];
// Does the element specify the class of the disease model?
if (element.getName().equals(Constants.SOLVER_ELEMENT)) {
// Yes
try {
temp.add((Solver) element
.createExecutableExtension("class")); //$NON-NLS-1$
} catch (final Exception e) {
CorePlugin.logError(
"Can't create solver", e); //$NON-NLS-1$
}
} // if
} // for each configuration element
solvers = temp.toArray(new Solver[] {});
return solvers;
}
/**
* Do the processing required to handle a {@link Exception}
*
* @param scenario
* the {@link Scenario} that caused the {@link Exception}
* @param name
* the name to use in error messages that identifies the source
* of the {@link Scenario}
* @param promptUser
* if <code>true</code> then present the user with a dialog box
* explaining the message.
* @param e
* the {@link Exception}
*/
static public void handleException(final ScenarioInitializationException se,
final boolean promptUser) {
Activator.logError(se.getErrorMessage(), se.getOriginalException());
// Prompt the user?
if (promptUser) {
// Yes
try {
Display d = DisplaySafeExecutor.safeGetDefaultDisplay();
if (d != null) {
d.syncExec(new Runnable() {
public void run() {
try {
final IWorkbenchWindow window = PlatformUI
.getWorkbench().getActiveWorkbenchWindow();
final IStatus warning = new Status(IStatus.WARNING,
Activator.PLUGIN_ID, 1, se.getErrorMessage(), null);
ErrorDialog.openError(window.getShell(), null, null,
warning);
} catch(Exception e) {
// If we get this exception, it is because we're not running in
// eclipse.
}
} // run
});
}
} catch (final Error ncdfe) {
// Empty
} // catch
} // if
} // handleRuntimeException
} // Simulation