replaced Simulation's setDelayTime(ms) method with more intuitive setSpeedLimit(ratio)

updated Simulation Control tool to use new setSpeedLimit(), and removed unused "run until" function

updated references to old setDelayTime(ms)
This commit is contained in:
Fredrik Osterlind 2012-06-04 16:14:05 +02:00
parent 92765b384e
commit bc0727a931
6 changed files with 207 additions and 308 deletions

View file

@ -161,8 +161,8 @@ while (currentMote <= NR_MOTES) {
GENERATE_MSG(1000, "continue");
WAIT_UNTIL(msg.equals("continue"));
/* override simulation delay to realtime */
mote.getSimulation().setDelayTime(java.lang.Integer.MIN_VALUE);
/* override simulation speed limit to realtime */
mote.getSimulation().setSpeedLimit(1.0);
/* ping motes */
currentMote = 1;

View file

@ -133,8 +133,8 @@ pingOnceProcess = new java.lang.Runtime.getRuntime().exec(pingOnceCmd);
GENERATE_MSG(5000, "continue");
WAIT_UNTIL(msg.equals("continue"));
/* override simulation delay to realtime */
mote.getSimulation().setDelayTime(java.lang.Integer.MIN_VALUE);
/* override simulation speed limit to realtime */
mote.getSimulation().setSpeedLimit(1.0);
/* start ping process */
var runnableObj = new Object();

View file

@ -34,6 +34,7 @@ import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dialog;
import java.awt.Dialog.ModalityType;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.GraphicsDevice;
@ -41,7 +42,6 @@ import java.awt.GraphicsEnvironment;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Window;
import java.awt.Dialog.ModalityType;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
@ -109,8 +109,8 @@ import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.ToolTipManager;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.UIManager.LookAndFeelInfo;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.MenuEvent;
import javax.swing.event.MenuListener;
import javax.swing.filechooser.FileFilter;
@ -682,7 +682,7 @@ public class GUI extends Observable {
}
private JMenuBar createMenuBar() {
JMenuItem menuItem;
/* Prepare GUI actions */
@ -706,20 +706,20 @@ public class GUI extends Observable {
/* toolsMenu = new JMenu("Tools"); */
JMenu settingsMenu = new JMenu("Settings");
JMenu helpMenu = new JMenu("Help");
menuBar.add(fileMenu);
menuBar.add(simulationMenu);
menuBar.add(motesMenu);
menuBar.add(toolsMenu);
menuBar.add(settingsMenu);
menuBar.add(helpMenu);
fileMenu.setMnemonic(KeyEvent.VK_F);
simulationMenu.setMnemonic(KeyEvent.VK_S);
motesMenu.setMnemonic(KeyEvent.VK_M);
toolsMenu.setMnemonic(KeyEvent.VK_T);
helpMenu.setMnemonic(KeyEvent.VK_H);
/* File menu */
fileMenu.addMenuListener(new MenuListener() {
public void menuSelected(MenuEvent e) {
@ -732,7 +732,7 @@ public class GUI extends Observable {
public void menuCanceled(MenuEvent e) {
}
});
fileMenu.add(new JMenuItem(newSimulationAction));
menuOpenSimulation = new JMenu("Open simulation");
@ -865,9 +865,9 @@ public class GUI extends Observable {
public void menuCanceled(MenuEvent e) {
}
});
// Mote menu
motesMenu.addMenuListener(new MenuListener() {
@ -879,7 +879,7 @@ public class GUI extends Observable {
public void menuCanceled(MenuEvent e) {
}
});
// Mote types sub menu
menuMoteTypes = new JMenu("Add motes");
@ -889,10 +889,10 @@ public class GUI extends Observable {
// Clear menu
menuMoteTypes.removeAll();
if (mySimulation != null) {
// Recreate menu items
JMenuItem menuItem;
@ -904,13 +904,13 @@ public class GUI extends Observable {
menuItem.addActionListener(guiEventHandler);
menuMoteTypes.add(menuItem);
}
if(mySimulation.getMoteTypes().length > 0) {
menuMoteTypes.add(new JSeparator());
}
}
menuMoteTypes.add(menuMoteTypeClasses);
}
@ -928,10 +928,10 @@ public class GUI extends Observable {
menuItem.putClientProperty("class", MoteTypeInformation.class);
motesMenu.add(menuItem);
motesMenu.add(new JMenuItem(removeAllMotesAction));
// Tools menu
// Tools menu
toolsMenu.addMenuListener(new MenuListener() {
public void menuSelected(MenuEvent e) {
for (Component menuComponent: toolsMenu.getMenuComponents()) {
@ -1028,7 +1028,7 @@ public class GUI extends Observable {
settingsMenu.add(new JMenuItem(showBufferSettingsAction));
/* Help */
/* Help */
helpMenu.add(new JMenuItem(showGettingStartedAction));
helpMenu.add(new JMenuItem(showKeyboardShortcutsAction));
JCheckBoxMenuItem checkBox = new JCheckBoxMenuItem(showQuickHelpAction);
@ -1175,7 +1175,7 @@ public class GUI extends Observable {
ToolTipManager.sharedInstance().setDismissDelay(60000);
/* Nimbus */
/* Nimbus */
try {
String osName = System.getProperty("os.name").toLowerCase();
if (osName.startsWith("linux")) {
@ -1186,10 +1186,10 @@ public class GUI extends Observable {
break;
}
}
} catch (UnsupportedLookAndFeelException e) {
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
}
}
} else {
UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
}
@ -3211,7 +3211,7 @@ public class GUI extends Observable {
logger.fatal("Error: " + e.getMessage(), e);
System.exit(1);
}
sim.setDelayTime(0);
sim.setSpeedLimit(null);
sim.startSimulation();
} else {
logger.fatal("No test editor controlling simulation, aborting");
@ -4499,7 +4499,7 @@ public class GUI extends Observable {
super.setEnabled(newValue);
}
public boolean shouldBeEnabled() {
return getSimulation() != null;
return getSimulation() != null && getSimulation().isRunnable();
}
};
class StartPluginGUIAction extends GUIAction {

View file

@ -63,8 +63,13 @@ public class Simulation extends Observable implements Runnable {
private Vector<MoteType> moteTypes = new Vector<MoteType>();
private int delayTime=0, delayPeriod=1;
private long delayLastSim;
/* If true, run simulation at full speed */
private boolean speedLimitNone = true;
/* Limit simulation speed to maxSpeed; if maxSpeed is 1.0 simulation is run at real-time speed */
private double speedLimit;
/* Used to restrict simulation speed */
private long speedLimitLastSimtime;
private long speedLimitLastRealtime;
private long currentSimulationTime = 0;
@ -191,35 +196,32 @@ public class Simulation extends Observable implements Runnable {
private TimeEvent delayEvent = new TimeEvent(0) {
public void execute(long t) {
/* As fast as possible: no need to reschedule delay event */
if (delayTime == 0) {
if (speedLimitNone) {
/* As fast as possible: no need to reschedule delay event */
return;
}
/* Special case: real time */
if (delayPeriod == Integer.MIN_VALUE) {
delayLastSim++;
long tmp = System.currentTimeMillis();
if (delayLastSim > tmp) {
try {
Thread.sleep(delayLastSim-tmp);
} catch (InterruptedException e) {
}
long diffSimtime = (getSimulationTime() - speedLimitLastSimtime)/1000; /* ms */
long diffRealtime = System.currentTimeMillis() - speedLimitLastRealtime; /* ms */
long expectedDiffRealtime = (long) (diffSimtime/speedLimit);
long sleep = expectedDiffRealtime - diffRealtime;
if (sleep >= 0) {
/* Slow down simulation */
try {
Thread.sleep(sleep);
} catch (InterruptedException e) {
}
/* Reschedule us next millisecond */
scheduleEvent(this, t+MILLISECOND);
return;
} else {
/* Reduce slow-down: execute this delay event less often */
scheduleEvent(this, t-sleep*MILLISECOND);
}
/* Normal operation */
try {
Thread.sleep(delayTime);
} catch (InterruptedException e) {
/* Update counters every second */
if (diffRealtime > 1000) {
speedLimitLastRealtime = System.currentTimeMillis();
speedLimitLastSimtime = getSimulationTime();
}
/* Reschedule us next period */
scheduleEvent(this, t+delayPeriod*MILLISECOND);
}
public String toString() {
return "DELAY";
@ -249,7 +251,8 @@ public class Simulation extends Observable implements Runnable {
long lastStartTime = System.currentTimeMillis();
logger.info("Simulation main loop started, system time: " + lastStartTime);
isRunning = true;
delayLastSim = System.currentTimeMillis();
speedLimitLastRealtime = System.currentTimeMillis();
speedLimitLastSimtime = getSimulationTime();
/* Simulation starting */
this.setChanged();
@ -473,10 +476,12 @@ public class Simulation extends Observable implements Runnable {
element.setText(title);
config.add(element);
// Delay time
element = new Element("delaytime");
element.setText("" + getDelayTime());
config.add(element);
/* Max simulation speed */
if (!speedLimitNone) {
element = new Element("speedlimit");
element.setText("" + getSpeedLimit());
config.add(element);
}
// Random seed
element = new Element("randomseed");
@ -560,9 +565,14 @@ public class Simulation extends Observable implements Runnable {
title = element.getText();
}
// Delay time
if (element.getName().equals("delaytime")) {
setDelayTime(Integer.parseInt(element.getText()));
/* Max simulation speed */
if (element.getName().equals("speedlimit")) {
String text = element.getText();
if (text.equals("null")) {
setSpeedLimit(null);
} else {
setSpeedLimit(Double.parseDouble(text));
}
}
// Random seed
@ -942,42 +952,27 @@ public class Simulation extends Observable implements Runnable {
}
/**
* Set delay time (ms).
* The simulation loop delays given value every simulated millisecond.
* If the value is zero there is no delay.
* If the value is negative, the simulation loop delays 1ms every (-time) simulated milliseconds.
*
* Examples:
* time=0: no sleeping (simulation runs as fast as possible).
* time=10: simulation delays 10ms every simulated millisecond.
* time=-5: simulation delays 1ms every 5 simulated milliseconds.
*
* Special case:
* time=Integer.MIN_VALUE: simulation tries to execute at real time.
*
* @param time New delay time value
* Limit simulation speed to given ratio.
* This method may be called from outside the simulation thread.
* @param newSpeedLimit
*/
public void setDelayTime(int time) {
if (time == Integer.MIN_VALUE) {
/* Special case: real time */
delayTime = Integer.MIN_VALUE;
delayPeriod = Integer.MIN_VALUE;
delayLastSim = System.currentTimeMillis();
} else if (time < 0) {
delayTime = 1;
delayPeriod = -time;
} else {
delayTime = time;
delayPeriod = 1; /* minimum */
}
public void setSpeedLimit(final Double newSpeedLimit) {
invokeSimulationThread(new Runnable() {
public void run() {
if (!delayEvent.isScheduled()) {
scheduleEvent(
delayEvent,
currentSimulationTime - (currentSimulationTime % MILLISECOND) + MILLISECOND);
if (newSpeedLimit == null) {
speedLimitNone = true;
return;
}
speedLimitNone = false;
speedLimitLastRealtime = System.currentTimeMillis();
speedLimitLastSimtime = getSimulationTime();
speedLimit = newSpeedLimit.doubleValue();
if (delayEvent.isScheduled()) {
delayEvent.remove();
}
scheduleEvent(delayEvent, currentSimulationTime);
Simulation.this.setChanged();
Simulation.this.notifyObservers(this);
}
@ -985,23 +980,13 @@ public class Simulation extends Observable implements Runnable {
}
/**
* Returns current delay time value.
* Note that this value can be negative.
*
* @see #setDelayTime(int)
* @return Delay time value. May be negative, see {@link #setDelayTime(int)}
* @return Max simulation speed ratio. Returns null if no limit.
*/
public int getDelayTime() {
/* Special case: real time */
if (delayPeriod == Integer.MIN_VALUE) {
return Integer.MIN_VALUE;
public Double getSpeedLimit() {
if (speedLimitNone) {
return null;
}
if (delayPeriod > 1) {
return -delayPeriod;
}
return delayTime;
return new Double(speedLimit);
}
/**
@ -1087,9 +1072,12 @@ public class Simulation extends Observable implements Runnable {
* @return True if simulation is runnable
*/
public boolean isRunnable() {
return motes.size() > 0;
if (motes.isEmpty()) {
return false;
}
return isRunning || hasPollRequests || eventQueue.peekFirst() != null;
}
/**
* Get current simulation title (short description).
*
@ -1108,5 +1096,4 @@ public class Simulation extends Observable implements Runnable {
public void setTitle(String title) {
this.title = title;
}
}

View file

@ -132,16 +132,16 @@ public class ScriptRunner extends VisPlugin {
JMenu fileMenu = new JMenu("File");
JMenu editMenu = new JMenu("Edit");
JMenu runMenu = new JMenu("Run");
menuBar.add(fileMenu);
menuBar.add(editMenu);
menuBar.add(runMenu);
this.setJMenuBar(menuBar);
/* Examples popup menu */
JMenu examplesMenu = new JMenu("Load example script");
for (int i=0; i < EXAMPLE_SCRIPTS.length; i += 2) {
final String file = EXAMPLE_SCRIPTS[i];
JMenuItem exampleItem = new JMenuItem(EXAMPLE_SCRIPTS[i+1]);
@ -161,7 +161,7 @@ public class ScriptRunner extends VisPlugin {
}
fileMenu.add(examplesMenu);
{
/* Workaround to configure jsyntaxpane */
JEditorPane e = new JEditorPane();
@ -242,7 +242,7 @@ public class ScriptRunner extends VisPlugin {
JPanel buttonPanel = new JPanel(new BorderLayout());
/*buttonPanel.add(BorderLayout.CENTER, toggleButton);*/
/* buttonPanel.add(BorderLayout.EAST, runTestButton);*/
JPanel southPanel = new JPanel(new BorderLayout());
@ -663,7 +663,7 @@ public class ScriptRunner extends VisPlugin {
} catch (Exception e) {
logger.fatal("Error: " + e.getMessage(), e);
}
simulation.setDelayTime(0);
simulation.setSpeedLimit(null);
simulation.startSimulation();
}
return true;

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2009, Swedish Institute of Computer Science.
* Copyright (c) 2012, Swedish Institute of Computer Science.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -25,70 +25,53 @@
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: SimControl.java,v 1.18 2010/11/03 12:29:47 adamdunkels Exp $
*/
package se.sics.cooja.plugins;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.text.NumberFormat;
import java.util.Observable;
import java.util.Observer;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JFormattedTextField;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.apache.log4j.Logger;
import se.sics.cooja.ClassDescription;
import se.sics.cooja.GUI;
import se.sics.cooja.HasQuickHelp;
import se.sics.cooja.PluginType;
import se.sics.cooja.Simulation;
import se.sics.cooja.TimeEvent;
import se.sics.cooja.VisPlugin;
/**
* Control panel for starting and pausing the current simulation.
* Allows for configuring the simulation delay.
*
* @author Fredrik Osterlind
*/
@ClassDescription("Control Panel")
@ClassDescription("Simulation control...")
@PluginType(PluginType.SIM_STANDARD_PLUGIN)
public class SimControl extends VisPlugin implements HasQuickHelp {
private static final long serialVersionUID = 8452253637624664192L;
private static Logger logger = Logger.getLogger(SimControl.class);
private static final int LABEL_UPDATE_INTERVAL = 150;
private Simulation simulation;
private static final int SLIDE_MIN = -100;
private static final int SLIDE_MAX = 1000;
private static final int LABEL_UPDATE_INTERVAL = 100;
private JButton startButton, stopButton;
private JSlider sliderDelay;
private JLabel simulationTime, simulationSpeedup, delayLabel;
private JFormattedTextField stopTimeTextField;
private JLabel simulationTime, simulationSpeedup;
private Observer simObserver;
@ -101,7 +84,7 @@ public class SimControl extends VisPlugin implements HasQuickHelp {
* @param simulation Simulation to control
*/
public SimControl(Simulation simulation, GUI gui) {
super("Control Panel", gui);
super("Simulation control", gui);
this.simulation = simulation;
/* Update current time label when simulation is running */
@ -109,6 +92,54 @@ public class SimControl extends VisPlugin implements HasQuickHelp {
updateLabelTimer.start();
}
/* Menus */
JMenuBar menuBar = new JMenuBar();
JMenu runMenu = new JMenu("Run");
JMenu speedMenu = new JMenu("Speed limit");
menuBar.add(runMenu);
menuBar.add(speedMenu);
this.setJMenuBar(menuBar);
runMenu.add(new JMenuItem(startAction));
runMenu.add(new JMenuItem(stopAction));
runMenu.add(new JMenuItem(stepAction));
runMenu.add(new JMenuItem(reloadAction));
ButtonGroup speedlimitButtonGroup = new ButtonGroup();
JRadioButtonMenuItem limitMenuItemNo = new JRadioButtonMenuItem(
new ChangeMaxSpeedLimitAction("No speed limit", null));
speedlimitButtonGroup.add(limitMenuItemNo);
speedMenu.add(limitMenuItemNo);
JRadioButtonMenuItem limitMenuItem1 = new JRadioButtonMenuItem(
new ChangeMaxSpeedLimitAction("1%", 0.01));
speedlimitButtonGroup.add(limitMenuItem1);
speedMenu.add(limitMenuItem1);
JRadioButtonMenuItem limitMenuItem2 = new JRadioButtonMenuItem(
new ChangeMaxSpeedLimitAction("10%", 0.10));
speedlimitButtonGroup.add(limitMenuItem2);
speedMenu.add(limitMenuItem2);
JRadioButtonMenuItem limitMenuItem3 = new JRadioButtonMenuItem(
new ChangeMaxSpeedLimitAction("100%", 1.0));
speedlimitButtonGroup.add(limitMenuItem3);
speedMenu.add(limitMenuItem3);
JRadioButtonMenuItem limitMenuItem4 = new JRadioButtonMenuItem(
new ChangeMaxSpeedLimitAction("1000%", 10.0));
speedlimitButtonGroup.add(limitMenuItem4);
speedMenu.add(limitMenuItem4);
if (simulation.getSpeedLimit() == null) {
limitMenuItemNo.setSelected(true);
} else if (simulation.getSpeedLimit().doubleValue() == 0.01) {
limitMenuItem1.setSelected(true);
} else if (simulation.getSpeedLimit().doubleValue() == 0.10) {
limitMenuItem2.setSelected(true);
} else if (simulation.getSpeedLimit().doubleValue() == 1.0) {
limitMenuItem3.setSelected(true);
} else if (simulation.getSpeedLimit().doubleValue() == 10) {
limitMenuItem4.setSelected(true);
}
/* Container */
JPanel smallPanel;
JPanel controlPanel = new JPanel();
@ -129,51 +160,6 @@ public class SimControl extends VisPlugin implements HasQuickHelp {
smallPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
controlPanel.add(smallPanel);
/* Run until */
smallPanel = new JPanel();
smallPanel.setLayout(new BoxLayout(smallPanel, BoxLayout.X_AXIS));
smallPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 0, 5));
JLabel label = new JLabel("Stop at:");
smallPanel.add(label);
smallPanel.add(Box.createHorizontalStrut(10));
NumberFormat integerFormat = NumberFormat.getIntegerInstance();
stopTimeTextField = new JFormattedTextField(integerFormat);
stopTimeTextField.addPropertyChangeListener("value", new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent e) {
/* Remove already scheduled stop event */
if (stopEvent.isScheduled()) {
stopEvent.remove();
}
final long t = ((Number)e.getNewValue()).intValue()*Simulation.MILLISECOND;
if (t <= SimControl.this.simulation.getSimulationTime()) {
/* No simulation stop scheduled */
stopTimeTextField.setBackground(Color.LIGHT_GRAY);
stopTimeTextField.setToolTipText("Enter simulation time when to automatically pause");
} else {
/* Schedule simulation stop */
stopTimeTextField.setBackground(Color.WHITE);
stopTimeTextField.setToolTipText("Simulation will stop at time (us): " + t);
SimControl.this.simulation.invokeSimulationThread(new Runnable() {
public void run() {
if (stopEvent.isScheduled()) {
stopEvent.remove();
}
SimControl.this.simulation.scheduleEvent(stopEvent, t);
}
});
}
}
});
stopTimeTextField.setValue(simulation.getSimulationTimeMillis());
stopTimeTextField.setSize(100, stopTimeTextField.getHeight());
smallPanel.add(stopTimeTextField);
smallPanel.add(Box.createHorizontalGlue());
smallPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
controlPanel.add(smallPanel);
@ -182,7 +168,7 @@ public class SimControl extends VisPlugin implements HasQuickHelp {
smallPanel.setLayout(new BoxLayout(smallPanel, BoxLayout.X_AXIS));
smallPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 0, 5));
label = new JLabel("?");
JLabel label = new JLabel("?");
smallPanel.add(label);
simulationTime = label;
@ -201,41 +187,6 @@ public class SimControl extends VisPlugin implements HasQuickHelp {
smallPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
controlPanel.add(smallPanel);
/* Delay label */
smallPanel = new JPanel();
smallPanel.setLayout(new BoxLayout(smallPanel, BoxLayout.X_AXIS));
smallPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 0, 5));
label = new JLabel("?");
smallPanel.add(label);
delayLabel = label;
smallPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
controlPanel.add(smallPanel);
/* Delay slider */
smallPanel = new JPanel();
smallPanel.setLayout(new BoxLayout(smallPanel, BoxLayout.X_AXIS));
smallPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 10, 5));
sliderDelay = new JSlider(
JSlider.HORIZONTAL,
SLIDE_MIN,
SLIDE_MAX,
convertTimeToSlide(simulation.getDelayTime()));
sliderDelay.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e) {
SimControl.this.simulation.setDelayTime(
convertSlideToTime(sliderDelay.getValue()));
updateValues();
}
});
smallPanel.add(sliderDelay);
smallPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
controlPanel.add(smallPanel);
/* Observe current simulation */
simulation.addObserver(simObserver = new Observer() {
public void update(Observable obs, Object obj) {
@ -253,33 +204,31 @@ public class SimControl extends VisPlugin implements HasQuickHelp {
this.lastSystemTimeTimestamp = System.currentTimeMillis();
this.lastSimulationTimeTimestamp = 0;
/* XXX HACK: here we set the position and size of the window when it
* appears on a blank simulation screen. */
this.setLocation(400, 0);
this.setSize(280, 160);
}
private class ChangeMaxSpeedLimitAction extends AbstractAction {
private Double maxSpeed;
public ChangeMaxSpeedLimitAction(String name, Double maxSpeed) {
super(name);
this.maxSpeed = maxSpeed;
}
public void actionPerformed(ActionEvent e) {
simulation.setSpeedLimit(maxSpeed);
}
}
private void updateValues() {
/* Update simulation delay */
sliderDelay.setValue(convertTimeToSlide(simulation.getDelayTime()));
if (simulation.getDelayTime() == 0) {
delayLabel.setText("No simulation delay");
} else if (simulation.getDelayTime() == Integer.MIN_VALUE) {
delayLabel.setText("Real time");
} else if (simulation.getDelayTime() > 0) {
delayLabel.setText("Delay: " + simulation.getDelayTime() + " ms");
} else {
delayLabel.setText("Delay: 1/" + (-simulation.getDelayTime()) + " ms");
}
/* Update current time */
simulationTime.setText("Simulation time: "
+ simulation.getSimulationTimeMillis()
+ " ms");
simulationSpeedup.setText("Relative speed: ---");
simulationTime.setText(getTimeString());
simulationSpeedup.setText("Speed: ---");
if (simulation.isRunning() && !updateLabelTimer.isRunning()) {
updateLabelTimer.start();
}
if (!simulation.isRunning()) {
simulationTime.setToolTipText("Simulation time in microseconds: "
+ simulation.getSimulationTime());
}
/* Update control buttons */
if (simulation.isRunning()) {
@ -287,44 +236,34 @@ public class SimControl extends VisPlugin implements HasQuickHelp {
stopAction.setEnabled(true);
stepAction.setEnabled(false);
} else {
startAction.setEnabled(true);
stopAction.setEnabled(false);
stepAction.setEnabled(true);
if (!stopEvent.isScheduled()) {
stopTimeTextField.setValue(simulation.getSimulationTimeMillis());
if(simulation.isRunnable()) {
startAction.setEnabled(true);
stepAction.setEnabled(true);
} else {
startAction.setEnabled(false);
stepAction.setEnabled(false);
}
stopAction.setEnabled(false);
}
}
private int convertSlideToTime(int slide) {
if (slide == SLIDE_MIN) {
/* Special case: no delay */
return 0;
private static final long TIME_SECOND = 1000*Simulation.MILLISECOND;
private static final long TIME_MINUTE = 60*TIME_SECOND;
private static final long TIME_HOUR = 60*TIME_MINUTE;
public String getTimeString() {
long t = simulation.getSimulationTime();
long h = (t / TIME_HOUR);
t -= (t / TIME_HOUR)*TIME_HOUR;
long m = (t / TIME_MINUTE);
t -= (t / TIME_MINUTE)*TIME_MINUTE;
long s = (t / TIME_SECOND);
t -= (t / TIME_SECOND)*TIME_SECOND;
long ms = t / Simulation.MILLISECOND;
if (h > 0) {
return String.format("Time: %d:%02d:%02d.%03d", h,m,s,ms);
} else {
return String.format("Time: %02d:%02d.%03d", m,s,ms);
}
if (slide == SLIDE_MIN+1) {
/* Special case: real time */
return Integer.MIN_VALUE;
}
if (slide <= 0) {
return slide-2; /* Ignore special cases */
}
return slide;
}
private int convertTimeToSlide(int time) {
if (time == 0) {
/* Special case: no delay */
return SLIDE_MIN;
}
if (time == Integer.MIN_VALUE) {
/* Special case: real time */
return SLIDE_MIN+1;
}
if (time < 0) {
return time+2; /* Ignore special cases */
}
return time;
}
public void closePlugin() {
@ -333,46 +272,23 @@ public class SimControl extends VisPlugin implements HasQuickHelp {
simulation.deleteObserver(simObserver);
}
/* Remove stop event */
if (stopEvent.isScheduled()) {
stopEvent.remove();
}
/* Remove label update timer */
updateLabelTimer.stop();
}
private TimeEvent stopEvent = new TimeEvent(0) {
public void execute(long t) {
/* Stop simulation */
simulation.stopSimulation();
SwingUtilities.invokeLater(new Runnable() {
public void run() {
stopTimeTextField.setBackground(Color.LIGHT_GRAY);
stopTimeTextField.setToolTipText("Enter simulation time when to automatically pause");
stopTimeTextField.requestFocus();
}
});
}
};
private Timer updateLabelTimer = new Timer(LABEL_UPDATE_INTERVAL, new ActionListener() {
public void actionPerformed(ActionEvent e) {
simulationTime.setText("Simulation time: "
+ simulation.getSimulationTimeMillis()
+ " ms");
simulationTime.setText(getTimeString());
long systemTimeDiff = System.currentTimeMillis() - lastSystemTimeTimestamp;
if(systemTimeDiff > 1000) {
if (systemTimeDiff > 1000) {
long simulationTimeDiff = simulation.getSimulationTimeMillis() - lastSimulationTimeTimestamp;
lastSimulationTimeTimestamp = simulation.getSimulationTimeMillis();
lastSystemTimeTimestamp = System.currentTimeMillis();
// long String.format("%2.2f"
double speedup = (double)simulationTimeDiff / (double)systemTimeDiff;
simulationSpeedup.setText(String.format("Relative speed: %2.2f%%", 100 * speedup));
simulationSpeedup.setText(String.format("Speed: %2.2f%%", 100 * speedup));
}
/* Automatically stop if simulation is no longer running */
@ -413,10 +329,6 @@ public class SimControl extends VisPlugin implements HasQuickHelp {
"<p>The keyboard shortcut for starting and pausing the simulation is <i>Ctrl+S</i>. " +
"<p><i>Step</i> runs the simulation for one millisecond. " +
"<p><i>Reload</i> reloads and restarts the simulation. " +
"<p>Writing simulation time in milliseconds in the <i>Stop at</i> field causes the simulation to pause at the given time. " +
"<p>Simulation speed is controlled via the bottom slider. " +
"If the slider value is zero, simulation runs at full speed. " +
"<p>Setting the slider to <i>Real time</i>, simulation speed is capped to not run faster than real time. " +
"The <i>Real time</i> slider value is to the right of <i>No simulation delay</i>: click on the slider button and press the right arrow key on the keyboard. ";
"<p>Simulation speed is controlled via the Speed limit menu.";
}
}