blob: b6359ca8ea1284cdaf963f3c0eb57bbdc619dd86 [file] [log] [blame]
package org.eclipse.stem.ui.views;
/*******************************************************************************
* Copyright (c) 2006 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 org.eclipse.jface.resource.ColorRegistry;
import org.eclipse.jface.resource.FontRegistry;
import org.eclipse.jface.resource.ImageRegistry;
import org.eclipse.stem.core.model.STEMTime;
import org.eclipse.stem.core.scenario.Scenario;
import org.eclipse.stem.core.sequencer.Sequencer;
import org.eclipse.stem.jobs.execution.IExecutable;
import org.eclipse.stem.jobs.simulation.ISimulation;
import org.eclipse.stem.jobs.simulation.ISimulationListener;
import org.eclipse.stem.jobs.simulation.SimulationEvent;
import org.eclipse.stem.jobs.simulation.SimulationState;
import org.eclipse.stem.ui.Activator;
import org.eclipse.stem.ui.ISharedColors;
import org.eclipse.stem.ui.ISharedFonts;
import org.eclipse.stem.ui.ISharedImages;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.ControlListener;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.ProgressBar;
import org.eclipse.swt.widgets.Text;
/**
* This class is an SWT control that displays the status/state of an ISimulation
* and allows the ISimulation to be managed.
*
*/
public class SimulationControl extends Composite implements
ISimulationListener, ControlListener {
/**
* This is the active simulation job to which this class is attached & has
* control upon.
*/
protected ISimulation simulation = null;
/**
* Boolean used to keep track of resettings : We need this variable because
* sometimes after a reset there is still one or two complete_cycles from
* old simulation run (before resetting) that should not be processed.
*
*/
private boolean reset = false;
/**
* The Image registry manages images, creates them on-demand, and disposes
* them when the associated display is disposed.
*/
private static ImageRegistry imageRegistry = null;
/**
* The color registry manages SWT colors, creates them on-demand, and takes
* care of disposal.
*/
private static ColorRegistry colorRegistry = null;
/**
* JFace font registry
*/
private static FontRegistry fontRegistry = null;
/**
* WIDGETS for simulation control
*/
/**
* The top level container for the simulation control
*/
private Composite mainContainer = null;
private Composite btnContainer = null;
private Text simulationProgressText;
private Text simulationStateText;
private Text simulationTimeText;
private ProgressBar pb = null;
private Text simulationTitleText = null;
/**
* Symbolic constant indicating no work has been done
*/
private static int BEGIN_WORK = 0;
/**
* Boolean variable used to indicate whether we should build a progress bar
* using SWT.INDETERMINATE
*/
private boolean UNKNOWN_DURATION = false;
/**
* Some constants we need to set preferred sizes of SWT controls and
* containers.
*/
private static final int STANDARD_LENGTH = 100;
private static final int STANDARD_HEIGHT = 22;
private static final int CONTAINER_HEIGHT = 225;
private static final int MARGIN = 10;
/**
* Constructor
*
* @param parent
* @param simulation
*
*/
public SimulationControl(Composite parent, ISimulation simulation) {
super(parent, SWT.NONE);
// First, register as a listener of simulation manager events
simulation.addSimulationListener(this);
// Sets the active simulation we are going to control
this.simulation = simulation;
Sequencer sequencer = simulation.getScenario().getSequencer();
// Check to see if we have to create an indeterminate progress bar.
UNKNOWN_DURATION =
(sequencer.getDuration() == Sequencer.UNKNOWN_DURATION)
? true : false;
// Set the image registry
if (imageRegistry == null) {
Activator activator = Activator.getDefault();
imageRegistry = activator.getImageRegistry();
}
// Set the color registry
if (colorRegistry == null) {
Activator activator = Activator.getDefault();
colorRegistry = activator.getColorRegistry();
}
// Set the font registry
if (fontRegistry == null) {
Activator activator = Activator.getDefault();
fontRegistry = activator.getFontRegistry();
}
// Set the layout data for the simulation control
// The parent has been set to GridLayout() previously.
this.setLayoutData(new GridData(SWT.FILL, SWT.NONE, true, false));
// Now create all the widgets
createContents(this);
parent.redraw();
}
/**
* Compute the new bounds for the simulation control after resizing.
*
* @param event
*/
public void controlResized(ControlEvent event) {
final int MULTIPLIER = 5;
// Get the client area boundaries.
Rectangle clientBounds = null;
if (this.getParent() != null) {
if (this.getParent().getParent() != null) {
clientBounds = this.getParent().getParent().getClientArea();
}
}
// Make sure we don't get an NPE
if (clientBounds == null) {
return;
}
int width = clientBounds.width- (MARGIN * MULTIPLIER);
int height = STANDARD_HEIGHT;
////////////////////////////////////////////
// JHK This code was removed because the controls did not display well on the MAC.
// Swiching from a grid layout to flow layout seems to fix the problem.
// the folowing is no longer needed.
//
// Compute the new displaying bounds.
/*Rectangle pbBounds = this.computeTrim(pb.getBounds().x,
pb.getBounds().y, width,height);
Rectangle titleBounds = this.computeTrim(
simulationTitleText.getBounds().x,
simulationTitleText.getBounds().y, width, height);
Rectangle stateBounds = this.computeTrim(
simulationStateText.getBounds().x,
simulationStateText.getBounds().y, width, height);
Rectangle timeBounds = this.computeTrim(
simulationTimeText.getBounds().x,
simulationTimeText.getBounds().y, width, height);
Rectangle progressBounds = this.computeTrim(
simulationProgressText.getBounds().x,
simulationProgressText.getBounds().y, width, height);*/
Rectangle mainBounds = this.computeTrim(clientBounds.x, clientBounds.y,
clientBounds.width - MARGIN, CONTAINER_HEIGHT);
Rectangle btnBounds = this.computeTrim(btnContainer.getBounds().x,
btnContainer.getBounds().y,width,height+2*MARGIN);
// Set the new boundaries.
// pb.setBounds(pbBounds);
// simulationTitleText.setBounds(titleBounds);
// simulationStateText.setBounds(stateBounds);
// simulationTimeText.setBounds(timeBounds);
// simulationProgressText.setBounds(progressBounds);
btnContainer.setBounds(btnBounds);
mainContainer.setBounds(mainBounds);
// Redraw the widgets.
mainContainer.redraw();
}
/**
* Event handler method for moved window events.
*
* @param event
*/
public void controlMoved(ControlEvent event) {
// DO NOTHING
}
/**
* Reset the progress bar to initial state
*
*/
protected void resetProgressBar() {
if (null == pb || pb.isDisposed() ) {
return;
}
// if (UNKNOWN_DURATION) {
// return;
// }
pb.setMinimum(BEGIN_WORK);
pb.setMaximum(IExecutable.TOTAL_WORK);
pb.setSelection(BEGIN_WORK);
pb.redraw();
}
/**
* Update simulation state label whenever simulation changes state
*
* @param state
* of simulation
*/
protected void updateStateLabel(SimulationState state) {
if (null == simulationStateText || simulationStateText.isDisposed()) {
return;
}
Rectangle bounds = simulationStateText.getBounds();
if (state.equals(SimulationState.RUNNING) ||
state.equals(SimulationState.COMPLETED_CYCLE) ) {
simulationStateText.setText(Messages.getString("SCtrl.Running")); //$NON-NLS-1$
simulationStateText.setBackground(colorRegistry
.get(ISharedColors.GREEN));
} else if (state.equals(SimulationState.PAUSED)) {
simulationStateText.setText(Messages.getString("SCtrl.Paused")); //$NON-NLS-1$
simulationStateText.setBackground(colorRegistry
.get(ISharedColors.YELLOW));
} else if (state.equals(SimulationState.RESET)) {
simulationStateText.setText(Messages.getString("SCtrl.Reset")); //$NON-NLS-1$
simulationStateText.setBackground(colorRegistry
.get(ISharedColors.ORANGE));
pb.setSelection(0);
}
simulationStateText.setBounds(bounds);
simulationStateText.redraw();
}
/**
* Update progress bar and progress Label
* If no duration then use sequencer cycle count to
* (modulo 100) to reflect the progress.
* If there is a duration then use the work completed
* value.
*
* @param workCompleted
* @param state Simulation state (running, paused ...)
*/
protected void updateProgressLabel(int workCompleted, SimulationState state) {
Sequencer sequencer = simulation.getScenario().getSequencer();
int current = sequencer.getCycle() ;
Rectangle bounds = simulationProgressText.getBounds();
if (UNKNOWN_DURATION == true) {
// WE HAVE UNKNOWN DURATION
simulationProgressText.setText(
Messages.getString("SCtrl.Progress") +
" : unknown duration"); //$NON-NLS-1$ //$NON-NLS-2$
current = current%100;
} else if (workCompleted <= IExecutable.TOTAL_WORK) {
simulationProgressText.setText(
Messages.getString("SCtrl.Progress") +
" : " + workCompleted + "%"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
simulationProgressText.setBounds(bounds);
simulationProgressText.redraw();
// pb.setSelection(current);
}
/**
* Update simulation time label
*
* @param state Simulation state
*/
protected void updateTimeLabel(SimulationState state) {
if(simulationTimeText.isDisposed()) return;
Rectangle bounds = simulationTimeText.getBounds();
// Get the current time :
Sequencer sequencer = simulation.getScenario().getSequencer();
STEMTime time = sequencer.getCurrentTime();
if ((state.name().equals("RUNNING") == false) //$NON-NLS-1$
&& (state.name().equals("COMPLETED_CYCLE") == false)) { //$NON-NLS-1$
// DONT UPDATE UNLESS STATE IS RUNNING OR COMPLETED_CYCLE
//return;
}
if (sequencer.getCycle() > 0 && reset == true) {
// DO NOTHING : ignore spurious completed cycles after a reset.
reset = false;
return;
}
String timeLabel= "[" + sequencer.getCycle() + "] " +
Messages.getString("SCtrl.Time") +
" : " + time.toString();
//$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
simulationTimeText.setText(timeLabel);
simulationTimeText.setToolTipText(timeLabel);
simulationTimeText.setBounds(bounds);
simulationTimeText.redraw();
}
/**
* @return the simulation to which this control is attached or associated
*/
public ISimulation getSimulation() {
return simulation;
}
/**
* This is where we create & initialize the components that go into the
* simulation control.
*
* <pre>
* - Title of Simulation
* - Progress status
* - State (Running Paused)
* - progress bar
* - Control buttons
*
* @param parent SimulationControl Composite (ourselves)
*
*/
private void createContents(Composite parent) {
// Create the top level container for the SWT Widgets of the simulation
// control
mainContainer = new Composite(parent, SWT.SMOOTH | SWT.BORDER);
// Set Layout data for MainContainer within SimulationControl
mainContainer.setLayoutData(getMainContainerGridData());
// Set the layout manager for the top level container
GridLayout gridLayout = new GridLayout();
gridLayout.numColumns = 1;
mainContainer.setLayout(new FormLayout());
simulationTitleText = new Text(mainContainer, SWT.MULTI | SWT.WRAP);
simulationTitleText.setText(simulation.getNameWithSequenceNumber());
simulationTitleText.setToolTipText(simulation.getName());
simulationTitleText
.setBackground(colorRegistry.get(ISharedColors.GRAY));
simulationTitleText.setFont(fontRegistry.get(ISharedFonts.DEFAULT));
simulationTitleText.setEditable(false);
//
FormData sttFormData = new FormData();
sttFormData.left = new FormAttachment(0, 0);
sttFormData.right = new FormAttachment(100, 0);
sttFormData.top = new FormAttachment(1, 0);
simulationTitleText.setLayoutData(sttFormData);
simulationProgressText = new Text(mainContainer, SWT.SMOOTH
| SWT.BORDER);
simulationProgressText.setBackground(colorRegistry
.get(ISharedColors.GREEN));
simulationProgressText.setEditable(false);
simulationProgressText.setFont(fontRegistry.get(ISharedFonts.DEFAULT));
//
FormData sptFormData = new FormData();
sptFormData.left = new FormAttachment(0, 0);
sptFormData.right = new FormAttachment(100, 0);
sptFormData.top = new FormAttachment(simulationTitleText, 0);
simulationProgressText.setLayoutData(sptFormData);
simulationStateText = new Text(mainContainer, SWT.SMOOTH | SWT.BORDER);
simulationStateText.setBackground(colorRegistry
.get(ISharedColors.GREEN));
simulationStateText.setEditable(false);
simulationStateText.setFont(fontRegistry.get(ISharedFonts.DEFAULT));
FormData sstFormData = new FormData();
sstFormData.left = new FormAttachment(0, 0);
sstFormData.right = new FormAttachment(100, 0);
sstFormData.top = new FormAttachment(simulationProgressText, 0);
simulationStateText.setLayoutData(sstFormData);
pb = new ProgressBar(mainContainer, SWT.SMOOTH | SWT.BORDER);
pb.setFont(fontRegistry.get(ISharedFonts.DEFAULT));
FormData pbFormData = new FormData();
pbFormData.left = new FormAttachment(0, 0);
pbFormData.right = new FormAttachment(100, 0);
pbFormData.top = new FormAttachment(simulationStateText, 0);
pb.setLayoutData(pbFormData);
simulationTimeText = new Text(mainContainer, SWT.SMOOTH | SWT.BORDER);
simulationTimeText.setBackground(colorRegistry
.get(ISharedColors.GREEN));
simulationTimeText.setEditable(false);
simulationTimeText.setFont(fontRegistry.get(ISharedFonts.DEFAULT));
FormData stmtFormData = new FormData();
stmtFormData.left = new FormAttachment(0, 0);
stmtFormData.right = new FormAttachment(100, 0);
stmtFormData.top = new FormAttachment(pb, 0);
simulationTimeText.setLayoutData(stmtFormData);
Composite controlButtons = createControlButtons(mainContainer);
FormData controlsFormData = new FormData();
controlsFormData.left = new FormAttachment(0, 0);
controlsFormData.right = new FormAttachment(80, 0);
controlsFormData.top = new FormAttachment(simulationTimeText, 0);
controlButtons.setLayoutData(controlsFormData);
resetProgressBar(); // Set and display progress bar's initial value
updateStateLabel(simulation.getSimulationState());
updateProgressLabel(BEGIN_WORK, simulation.getSimulationState());
updateTimeLabel(simulation.getSimulationState());
//mainContainer.redraw();
}
/**
* Create a new grid data object with default values.
*
* @return the new grid data object
*/
protected GridData getDefaultGridData() {
GridData gridData = new GridData();
gridData.grabExcessHorizontalSpace = false;
gridData.grabExcessVerticalSpace = false;
gridData.heightHint = STANDARD_HEIGHT;
gridData.minimumHeight = STANDARD_HEIGHT;
gridData.widthHint = STANDARD_LENGTH;
gridData.minimumWidth = STANDARD_LENGTH;
gridData.horizontalAlignment = SWT.LEFT;
return gridData;
}
/**
* Create a new grid data customized for a text field.
*
* @return gridData the new grid data object
*/
protected GridData getTextGridData() {
GridData gridData = new GridData(SWT.FILL, SWT.NONE, true, false);
gridData.grabExcessHorizontalSpace = true;
gridData.grabExcessVerticalSpace = false;
gridData.heightHint = STANDARD_HEIGHT;
gridData.minimumHeight = STANDARD_HEIGHT-2;
gridData.widthHint = STANDARD_LENGTH;
gridData.minimumWidth = STANDARD_LENGTH;
gridData.horizontalAlignment = SWT.FILL;
return gridData;
}
/**
* Create a new grid data customized for a progress bar.
*
* @return gridData the new grid data object
*/
protected GridData getProgressBarGridData() {
GridData gridData = new GridData(SWT.FILL, SWT.NONE, true, false);
gridData.grabExcessHorizontalSpace = true;
gridData.grabExcessVerticalSpace = false;
gridData.horizontalAlignment = SWT.FILL;
return gridData;
}
/**
* Create a new grid data customized for the main container of the
* simulation control.
*
* @return gridData the new grid data object
*/
protected GridData getMainContainerGridData() {
GridData gridData = new GridData();
gridData.grabExcessVerticalSpace = false;
gridData.grabExcessHorizontalSpace = true;
// Set the preferred size.
gridData.widthHint = STANDARD_LENGTH;
gridData.minimumWidth = STANDARD_LENGTH;
//gridData.heightHint = STANDARD_HEIGHT;
//gridData.minimumHeight = STANDARD_HEIGHT;
gridData.heightHint = CONTAINER_HEIGHT;
gridData.minimumHeight = CONTAINER_HEIGHT;
return gridData;
}
/**
* Create a new grid data customized for the push buttons.
*
* @return gridData the new grid data object
*/
protected GridData getButtonGridData() {
GridData gridData = new GridData();
gridData.grabExcessVerticalSpace = false;
gridData.grabExcessHorizontalSpace = true;
gridData.horizontalAlignment = SWT.FILL;
gridData.verticalAlignment = SWT.CENTER;
gridData.heightHint = STANDARD_HEIGHT-2+MARGIN;
gridData.minimumHeight = STANDARD_HEIGHT-2;
return gridData;
}
/**
* Creates the array of SWT push buttons.
* btnContainer is a new Composite that will contain
* a row of 5 buttons.
* @param parent mainContainer
*/
protected Composite createControlButtons(Composite parent) {
final int OFFSET = 16;
btnContainer = new Composite(parent,0);
//btnContainer.setSize(STANDARD_LENGTH, STANDARD_HEIGHT+OFFSET);
GridLayout gridLayout = new GridLayout();
gridLayout.numColumns = 5;
gridLayout.makeColumnsEqualWidth = true;
btnContainer.setLayout(gridLayout);
// set gridData for btnContainer within mainContainer
GridData gridData = new GridData();
gridData.grabExcessHorizontalSpace = false;
gridData.horizontalAlignment = GridData.FILL;
gridData.verticalAlignment = GridData.FILL;
gridData.grabExcessVerticalSpace = true;
gridData.heightHint = STANDARD_HEIGHT + OFFSET+MARGIN;
gridData.minimumHeight = STANDARD_HEIGHT+ OFFSET+MARGIN;
gridData.widthHint = STANDARD_LENGTH;
gridData.minimumWidth = STANDARD_LENGTH;
btnContainer.setLayoutData(gridData);
// create the simulation control buttons
Button run = new Button(btnContainer, SWT.PUSH);
Button pause = new Button(btnContainer, SWT.PUSH);
Button reset = new Button(btnContainer, SWT.PUSH);
Button step = new Button(btnContainer, SWT.PUSH);
Button stop = new Button(btnContainer, SWT.PUSH);
// set alignment and background color
run.setBackground(parent.getDisplay().getSystemColor(SWT.COLOR_GRAY));
run.setToolTipText(Messages.getString("SCtrl.StartSim")); //$NON-NLS-1$
run.setLayoutData(getButtonGridData());
pause
.setBackground(parent.getDisplay().getSystemColor(
SWT.COLOR_GRAY));
pause.setToolTipText(Messages.getString("SCtrl.PauseSim")); //$NON-NLS-1$
pause.setLayoutData(getButtonGridData());
reset
.setBackground(parent.getDisplay().getSystemColor(
SWT.COLOR_GRAY));
reset.setToolTipText(Messages.getString("SCtrl.RestoreSim")); //$NON-NLS-1$
reset.setLayoutData(getButtonGridData());
step.setBackground(parent.getDisplay().getSystemColor(SWT.COLOR_GRAY));
step.setToolTipText(Messages.getString("SCtrl.StepSim")); //$NON-NLS-1$
step.setLayoutData(getButtonGridData());
stop.setBackground(parent.getDisplay().getSystemColor(SWT.COLOR_GRAY));
stop.setToolTipText(Messages.getString("SCtrl.StopSim")); //$NON-NLS-1$
stop.setLayoutData(getButtonGridData());
run.setImage(imageRegistry.get(ISharedImages.RUN_ICON));
pause.setImage(imageRegistry.get(ISharedImages.PAUSE_ICON));
reset.setImage(imageRegistry.get(ISharedImages.RESET_ICON));
step.setImage(imageRegistry.get(ISharedImages.STEP_ICON));
stop.setImage(imageRegistry.get(ISharedImages.STOP_ICON));
// set event listeners for buttons
run.addListener(SWT.Selection, new Listener() {
public void handleEvent(Event e) {
switch (e.type) {
case SWT.Selection:
run();
break;
}
}
});
pause.addListener(SWT.Selection, new Listener() {
public void handleEvent(Event e) {
switch (e.type) {
case SWT.Selection:
pause();
break;
}
}
});
reset.addListener(SWT.Selection, new Listener() {
public void handleEvent(Event e) {
switch (e.type) {
case SWT.Selection:
reset();
break;
}
}
});
step.addListener(SWT.Selection, new Listener() {
public void handleEvent(Event e) {
switch (e.type) {
case SWT.Selection:
step();
break;
}
}
});
stop.addListener(SWT.Selection, new Listener() {
public void handleEvent(Event e) {
switch (e.type) {
case SWT.Selection:
stop();
break;
}
}
});
return btnContainer;
}
/**
* @see org.eclipse.swt.widgets.Widget#dispose()
*/
@Override
public void dispose() {
// Nullify all our references.
if (simulation != null) {
simulation.removeSimulationListener(this);
simulation = null;
}
// Force disposal of the main widget for the simulation control.
if (mainContainer != null) {
mainContainer.dispose();
}
} // dispose
int lastWorkCompleted = -1;
/**
* This is where the simulation control hears about changes in the
* simulation it is managing. It is really only interested in all events
* generated by the active simulation
*
* @see org.eclipse.stem.jobs.simulation.ISimulationListener#simulationChanged(org.eclipse.stem.jobs.simulation.SimulationEvent)
*/
public void simulationChanged(SimulationEvent event) {
// Get the current simulation state
final SimulationState simulationState = event.getSimulationState();
switch (simulationState) {
case STOPPED:
// We're done, we'll get an event from the SimulationManager telling
// us that the Simulation is no longer around.
return;
case COMPLETED_CYCLE:
if(simulation != null) {
Scenario scenario = simulation.getScenario();
// If sequencer is null, then we have disposed the widget.
if (scenario == null) {
// Widget disposed, do nothing.
return;
}
Sequencer sequencer = scenario.getSequencer();
if (sequencer == null) {
// Widget disposed, do nothing.
return;
}
final int workComplete = (int) sequencer.getWorkComplete();
if (workComplete < IExecutable.TOTAL_WORK) {
// Has the widget been disposed ?
if (pb.isDisposed() == true) {
// Yes, do nothing.
return;
}
if(lastWorkCompleted == workComplete) break;
lastWorkCompleted = workComplete;
pb.setSelection(workComplete);
updateProgressLabel(workComplete, simulationState);
}
}
break;
case RUNNING:
break;
case COMPLETED_SEQUENCE:
case PAUSED:
case RESET:
// Nothing
break;
} // switch simulation state
updateStateLabel(simulationState);
updateTimeLabel(simulationState);
} // simulationChanged
/**
* Reset the time label
*
*/
protected void resetTimeLabel() {
final int ZERO = 0;
simulationTimeText
.setText("[ " + ZERO + " ] " + Messages.getString("SCtrl.Time") + " : " + ZERO); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
simulationTimeText.redraw();
}
/**
* Run the simulation
*
*/
public void run() {
simulation.run();
}
/**
* Pause the simulation
*
*/
public void pause() {
simulation.pause();
}
/**
* Reset the simulation and the controls.
*
*/
public void reset() {
// Reset the simulation
simulation.reset();
// Turn flag for resets ON :
reset = true;
// Now reset the widget :
resetTimeLabel();
}
/**
* Step the simulation
*
*/
public void step() {
simulation.step();
}
/**
* Stop the simulation
*/
public void stop() {
simulation.stop();
}
} // SimulationControl