From bc0727a931790108c019d75d0075b1b3591bee67 Mon Sep 17 00:00:00 2001 From: Fredrik Osterlind Date: Mon, 4 Jun 2012 16:14:05 +0200 Subject: [PATCH] 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) --- .../contiki_tests/ip_cooja_telnet_ping.csc | 4 +- .../contiki_tests/ip_sky_telnet_ping.csc | 4 +- tools/cooja/java/se/sics/cooja/GUI.java | 48 +-- .../cooja/java/se/sics/cooja/Simulation.java | 149 ++++----- .../se/sics/cooja/plugins/ScriptRunner.java | 14 +- .../se/sics/cooja/plugins/SimControl.java | 296 ++++++------------ 6 files changed, 207 insertions(+), 308 deletions(-) diff --git a/tools/cooja/contiki_tests/ip_cooja_telnet_ping.csc b/tools/cooja/contiki_tests/ip_cooja_telnet_ping.csc index f7ea2dd87..6894ca492 100644 --- a/tools/cooja/contiki_tests/ip_cooja_telnet_ping.csc +++ b/tools/cooja/contiki_tests/ip_cooja_telnet_ping.csc @@ -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; diff --git a/tools/cooja/contiki_tests/ip_sky_telnet_ping.csc b/tools/cooja/contiki_tests/ip_sky_telnet_ping.csc index a98f47a94..380739d75 100644 --- a/tools/cooja/contiki_tests/ip_sky_telnet_ping.csc +++ b/tools/cooja/contiki_tests/ip_sky_telnet_ping.csc @@ -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(); diff --git a/tools/cooja/java/se/sics/cooja/GUI.java b/tools/cooja/java/se/sics/cooja/GUI.java index 973733994..29ef99eb2 100644 --- a/tools/cooja/java/se/sics/cooja/GUI.java +++ b/tools/cooja/java/se/sics/cooja/GUI.java @@ -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 { diff --git a/tools/cooja/java/se/sics/cooja/Simulation.java b/tools/cooja/java/se/sics/cooja/Simulation.java index f2b0f0e82..4b21878c1 100644 --- a/tools/cooja/java/se/sics/cooja/Simulation.java +++ b/tools/cooja/java/se/sics/cooja/Simulation.java @@ -63,8 +63,13 @@ public class Simulation extends Observable implements Runnable { private Vector moteTypes = new Vector(); - 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; } - } diff --git a/tools/cooja/java/se/sics/cooja/plugins/ScriptRunner.java b/tools/cooja/java/se/sics/cooja/plugins/ScriptRunner.java index 9e8a53e0f..67ff28c34 100644 --- a/tools/cooja/java/se/sics/cooja/plugins/ScriptRunner.java +++ b/tools/cooja/java/se/sics/cooja/plugins/ScriptRunner.java @@ -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; diff --git a/tools/cooja/java/se/sics/cooja/plugins/SimControl.java b/tools/cooja/java/se/sics/cooja/plugins/SimControl.java index 091cb812e..483acdf6b 100644 --- a/tools/cooja/java/se/sics/cooja/plugins/SimControl.java +++ b/tools/cooja/java/se/sics/cooja/plugins/SimControl.java @@ -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 { "

The keyboard shortcut for starting and pausing the simulation is Ctrl+S. " + "

Step runs the simulation for one millisecond. " + "

Reload reloads and restarts the simulation. " + - "

Writing simulation time in milliseconds in the Stop at field causes the simulation to pause at the given time. " + - "

Simulation speed is controlled via the bottom slider. " + - "If the slider value is zero, simulation runs at full speed. " + - "

Setting the slider to Real time, simulation speed is capped to not run faster than real time. " + - "The Real time slider value is to the right of No simulation delay: click on the slider button and press the right arrow key on the keyboard. "; + "

Simulation speed is controlled via the Speed limit menu."; } }