Commit 241dc003 authored by Tobias Meuser's avatar Tobias Meuser Committed by Jose Ignacio Monreal Bailey
Browse files

First working version of monitoring

parent 85482a86
......@@ -2,17 +2,17 @@
* Copyright (c) 2005-2011 KOM - Multimedia Communications Lab
*
* This file is part of PeerfactSim.KOM.
*
*
* PeerfactSim.KOM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
*
* PeerfactSim.KOM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with PeerfactSim.KOM. If not, see <http://www.gnu.org/licenses/>.
*
......@@ -40,11 +40,11 @@ import de.tudarmstadt.maki.simonstrator.api.component.core.TimeComponent;
* generated events in order to provide valid experiments. The duration of each
* experiment is controlled by the scheduler and the parameters defined by the
* Application.
*
*
* @author Sebastian Kaune
*/
public class Scheduler implements EventHandler,
SchedulerComponent, TimeComponent {
SchedulerComponent, TimeComponent {
// Flag to allow the compiler to remove the unneeded debug code
private static final boolean DEBUG_CODE = false;
......@@ -96,7 +96,7 @@ public class Scheduler implements EventHandler,
/**
* Constructs a new scheduler instance using a calendar queue. If desired,
* status events about the progress of the simulation will be plotted.
*
*
* @param statusEvent
* the flag which speficies if status events will be plotted
*/
......@@ -158,7 +158,7 @@ public class Scheduler implements EventHandler,
/**
* Wake up thread safe and insert event immediately.
*
*
* @param content
* the content
* @param handler
......@@ -198,17 +198,17 @@ public class Scheduler implements EventHandler,
}
Monitor.log(Scheduler.class, Level.INFO,
"Simulated realtime: " + Time.getFormattedTime()
+ " - End of simulation.\n Scheduler processed in total "
+ this.processedEventCounter + " events with "
+ this.eventQueue.size()
+ " unprocessed events still in queue");
+ " - End of simulation.\n Scheduler processed in total "
+ this.processedEventCounter + " events with "
+ this.eventQueue.size()
+ " unprocessed events still in queue");
}
/**
* Sets the end time at which the simulation framework will finish at the
* latest the simulation , irrespective if there are still unprocessed
* events in the event queue.
*
*
* @param endTime
* point in time at which the simulator will finish at the latest
*/
......@@ -223,7 +223,7 @@ public class Scheduler implements EventHandler,
/**
* Process the next event from the event queue.
*
*
* @return whether an event was processed
*/
synchronized private boolean processNextEvent() {
......@@ -252,9 +252,9 @@ public class Scheduler implements EventHandler,
}
assert (nextEvent.getSimulationTime() >= currentTime) : "Next event: "
+ nextEvent.getSimulationTime()
+ ", but current "
+ currentTime + ".";
+ nextEvent.getSimulationTime()
+ ", but current "
+ currentTime + ".";
if (nextEvent.getSimulationTime() <= newRoundsCurrentTime) {
......@@ -273,7 +273,7 @@ public class Scheduler implements EventHandler,
if (realEvent.schedulerType == TYPE_END)
return false;
synchronized (this.eventQueue) {
if (this.eventQueue.peek().getSimulationTime() < newRoundsCurrentTime) {
unhandledRegularEventsInPast = true;
......@@ -310,9 +310,9 @@ public class Scheduler implements EventHandler,
}
assert (peekedEvent.getSimulationTime() >= currentTime) : "Next event: "
+ peekedEvent.getSimulationTime()
+ ", but current "
+ currentTime + ".";
+ peekedEvent.getSimulationTime()
+ ", but current "
+ currentTime + ".";
if (peekedEvent.getSimulationTime() > currentTime) {
......@@ -326,12 +326,12 @@ public class Scheduler implements EventHandler,
/*
* ATTENTION: Magic happening here!
*
*
* Correct for uncertainties of going to real sleep.
*/
final long SLEEP_CORRECTION_FACTOR = 400;
final long realTime_errorMs = System.currentTimeMillis()
- 0 /* Starttime */
- 0 /* Starttime */
- (currentTime / Time.MILLISECOND);
final long realTime_correctedTimeToWaitMs = realTime_timeToWaitMs
- (realTime_errorMs / SLEEP_CORRECTION_FACTOR);
......@@ -379,8 +379,8 @@ public class Scheduler implements EventHandler,
final SchedulerEvent realEvent = this.eventQueue.remove();
assert (realEvent.getSimulationTime() >= currentTime) : "Next event: "
+ realEvent.getSimulationTime() + ", but current "
+ currentTime + ".";
+ realEvent.getSimulationTime() + ", but current "
+ currentTime + ".";
if (timeSkew > 0) {
/*
......@@ -392,7 +392,7 @@ public class Scheduler implements EventHandler,
if (timeToWait > 0) {
Monitor.log(Scheduler.class, Level.DEBUG,
"Scheduler sleeping: " + timeToWait / 1000
+ " milliseconds");
+ " milliseconds");
try {
Thread.sleep((long) ((timeToWait / 1000) / timeSkew));
......@@ -437,7 +437,7 @@ public class Scheduler implements EventHandler,
/**
* Return whether event queue is empty.
*
*
* @return whether event queue is empty.
*/
public boolean isEmpty() {
......@@ -446,16 +446,17 @@ public class Scheduler implements EventHandler,
/**
* Returns the current time of the scheduler
*
*
* @return current scheduler time
*/
@Override
public long getCurrentTime() {
return currentTime;
}
/**
* Returns the end time of the scheduler
*
*
* @return
*/
public long getEndTime() {
......@@ -475,7 +476,7 @@ public class Scheduler implements EventHandler,
/**
* Method for JUnit tests in order to verify the correctness
*
*
* @return number of events in event queue.
*/
public int getEventQueueSize() {
......@@ -483,7 +484,7 @@ public class Scheduler implements EventHandler,
}
protected static final class SchedulerEvent implements
Comparable<SchedulerEvent> {
Comparable<SchedulerEvent> {
protected final int schedulerType;
......@@ -494,9 +495,9 @@ public class Scheduler implements EventHandler,
protected final Object data;
protected long simTime;
protected long globalOrderIdx;
protected static long globalOrderCounter = 0;
protected SchedulerEvent(Object data, long simTime,
......@@ -518,6 +519,7 @@ public class Scheduler implements EventHandler,
return simTime;
}
@Override
public int compareTo(SchedulerEvent o) {
int comp = Double.compare(this.simTime, o.simTime);
return comp == 0 ? Double.compare(this.globalOrderIdx, o.globalOrderIdx) : comp;
......@@ -530,7 +532,7 @@ public class Scheduler implements EventHandler,
/**
* Returns wheather the scheduler is running in realTime mode or not.
*
*
* @return the real time
*/
public boolean getRealTime() {
......@@ -540,7 +542,7 @@ public class Scheduler implements EventHandler,
/**
* a flag for slowing down the simulation down to real time. This flag only
* makes sense for simulations which run faster than real time
*
*
* @param realTime
* flag for switching the scheduler to real time mode
*/
......@@ -556,7 +558,7 @@ public class Scheduler implements EventHandler,
/**
* method for setting the time skew. A time skew of 100 means, that the
* simulation runs 100 times faster than real time
*
*
* @param timeSkew
* the time skew
*/
......@@ -567,10 +569,14 @@ public class Scheduler implements EventHandler,
}
}
public double getTimeSkew() {
return timeSkew;
}
/**
* Sets the simulation speed lock, changes in setRealTime or setTimeSkew
* will not apply while the lock is set.
*
*
* @param locked
* the new simulation speed locked
*/
......@@ -593,7 +599,7 @@ public class Scheduler implements EventHandler,
/**
* The comparator
*
*
* @author Bjoern Richerzhagen
* @version 1.0, 05.03.2012
*/
......
......@@ -2,17 +2,17 @@
* Copyright (c) 2005-2011 KOM - Multimedia Communications Lab
*
* This file is part of PeerfactSim.KOM.
*
*
* PeerfactSim.KOM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
*
* PeerfactSim.KOM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with PeerfactSim.KOM. If not, see <http://www.gnu.org/licenses/>.
*
......@@ -56,11 +56,11 @@ import de.tudarmstadt.maki.simonstrator.api.component.core.RandomGeneratorCompon
/**
* Concrete implementation of a simulator which can be used to run a simulation
* by calling the main method in the SimulatorRunner class.
*
*
* @author Sebastian Kaune
* @author Konstantin Pussep
* @version 3.0, 11/29/2007
*
*
*/
public class Simulator implements RandomGeneratorComponent, GlobalComponent {
......@@ -68,7 +68,7 @@ public class Simulator implements RandomGeneratorComponent, GlobalComponent {
* These constant should be ALWAYS used for virtual time calculations.
*/
public final static double NANOSECOND_UNIT = 1E-3d;
/**
* These constant should be ALWAYS used for virtual time calculations.
*/
......@@ -130,7 +130,7 @@ public class Simulator implements RandomGeneratorComponent, GlobalComponent {
/**
* This class is singleton, so use getInstance() method to obtain a
* reference to it.
*
*
*/
private Simulator() {
singleton = this;
......@@ -145,7 +145,7 @@ public class Simulator implements RandomGeneratorComponent, GlobalComponent {
/**
* Returns the single instance of the SimulationFramework
*
*
* @return the SimulationFramework
*/
public static Simulator getInstance() {
......@@ -158,10 +158,10 @@ public class Simulator implements RandomGeneratorComponent, GlobalComponent {
scheduler.reset();
}
/**
/**
* Set the scenario (protocol stack, network topology etc.) which will be
* used to run the simulation.
*
*
* @param scenario
* simulation scenario to be used
*/
......@@ -172,7 +172,7 @@ public class Simulator implements RandomGeneratorComponent, GlobalComponent {
/**
* Returns the scenario used to run the simulation.
*
*
* @return
*/
public Scenario getScenario() {
......@@ -181,7 +181,7 @@ public class Simulator implements RandomGeneratorComponent, GlobalComponent {
/**
* Only for internal use
*
*
* @return
*/
public static Scheduler getScheduler() {
......@@ -195,35 +195,35 @@ public class Simulator implements RandomGeneratorComponent, GlobalComponent {
/**
* This method will run the simulation using the previously set scenario
* data.
*
*
*/
public void start(boolean throwExceptions) {
checkRunning();
Monitor.log(Simulator.class, Level.INFO, "Prepare Scenario ...");
this.scenario.prepare();
Monitor.log(Simulator.class, Level.INFO, "Prepare Scenario ..."
+ getSeed());
/* Real World Starting Time:
* Block till we're allowed to start.
* Block till we're allowed to start.
*/
if( realWorldStartTime != null ) {
try {
//final Date now = new Date();
final Date now = NTPClient.getDate();
final long waitFor = realWorldStartTime.getTime() - now.getTime();
Thread.sleep(waitFor);
} catch (InterruptedException e) {
//
}
}
startTime = System.currentTimeMillis();
Monitor.log(Simulator.class, Level.INFO, "Simulation started...");
this.running = true;
......@@ -282,53 +282,53 @@ public class Simulator implements RandomGeneratorComponent, GlobalComponent {
/**
* Configure simulation from an XML file.
*
*
* @param configFile
* XML file with the configuration data.
* @param variables
* the variables which are specified in the XML file with the
* configuarion data.
*/
public void configure(String configFile, Map<String, String> variables, Map<String, String> modifiedVariables, List<String> variations) {
// TODO create a class, that contains general informations of the
// simulation, which can be accessed from every component during a
// simulation. This can be seen as an alternative to implementing the
// Composable interface
if (configFile.endsWith(".xml")) {
this.defaultConfigurator = new DefaultConfigurator(configFile);
defaultConfigurator.setVariables(variables);
} else {
SimCfgConfigurator simCfgConfigurator = new SimCfgConfigurator(configFile);
simCfgConfigurator.applyVariations(variations);
simCfgConfigurator.setVariables(modifiedVariables);
public void configure(String configFile, Map<String, String> variables, Map<String, String> modifiedVariables, List<String> variations) {
// TODO create a class, that contains general informations of the
// simulation, which can be accessed from every component during a
// simulation. This can be seen as an alternative to implementing the
// Composable interface
if (configFile.endsWith(".xml")) {
this.defaultConfigurator = new DefaultConfigurator(configFile);
defaultConfigurator.setVariables(variables);
} else {
SimCfgConfigurator simCfgConfigurator = new SimCfgConfigurator(configFile);
simCfgConfigurator.applyVariations(variations);
simCfgConfigurator.setVariables(modifiedVariables);
this.defaultConfigurator = simCfgConfigurator;
}
this.defaultConfigurator = simCfgConfigurator;
}
this.defaultConfigurator.register(Configurator.CORE, this);
this.defaultConfigurator.register(Configurator.CORE, this);
this.defaultConfigurator.configureAll();
// The next steps are only required for the xml version.
// SimCfg will execute them in the configurator
if (configFile.endsWith(".xml")) {
ScenarioFactory scenarioBuilder = (ScenarioFactory) this.defaultConfigurator.getConfigurable(Configurator.SCENARIO_TAG);
// The next steps are only required for the xml version.
// SimCfg will execute them in the configurator
if (configFile.endsWith(".xml")) {
ScenarioFactory scenarioBuilder = (ScenarioFactory) this.defaultConfigurator.getConfigurable(Configurator.SCENARIO_TAG);
if (scenarioBuilder == null)
throw new ConfigurationException(
"No scenario builder specified in the configuration file. Nothing to do.");
if (scenarioBuilder == null)
throw new ConfigurationException(
"No scenario builder specified in the configuration file. Nothing to do.");
Scenario scenario = scenarioBuilder.createScenario();
setScenario(scenario);
}
Scenario scenario = scenarioBuilder.createScenario();
setScenario(scenario);
}
}
/**
* Returns the seed used within a simulation run.
*
*
* @return the predefined seed
*
*
*/
public static long getSeed() {
return seed;
......@@ -337,7 +337,7 @@ public class Simulator implements RandomGeneratorComponent, GlobalComponent {
/**
* This method sets the seed of the global random generator which can be
* obtained using the static getRandom()-method.
*
*
* @param seed
* the seed to configure the global random generator
*/
......@@ -356,53 +356,53 @@ public class Simulator implements RandomGeneratorComponent, GlobalComponent {
throw new IllegalStateException("Simulator is already running.");
}
/**
* This method provides a newly created random generator, configured
* with the specified seed of the simulator. This method should be preferred
* over the deprecated @{link getRandom}.
*
* <b>Notes:</b>
* Doing so will prevent components from influencing each other, thus creating
* reproducible results while allowing different compositions of components.
*
* The method allows the use of an object as source. This is to allow the use in
* different situations:
* 1. Random generators belonging to a single component should pass the class
* object of that component to this method.
* 2. Components that are always loaded during a simulation can use the
* Simulator.class object for a system wide random generator like {@link #getRandom()}
* provided.
* 3. Utility classes that are loaded by many different components should use
* a reference to themselves to avoid transitive dependencies on the same random
* generator throughout different components.
* This approach will provide way to to configure different seeds for specific class
* objects. This will allow component specific seeds and, e.g., the transfer of movement
* behavior from one configured scenario to another.
*
* @param source The source that requires the random generator (component class object is preferred)
* @param salt Added to the default seed, should be bound
* @return The random generator for the given source
*/
/**
* This method provides a newly created random generator, configured
* with the specified seed of the simulator. This method should be preferred
* over the deprecated @{link getRandom}.
*
* <b>Notes:</b>
* Doing so will prevent components from influencing each other, thus creating
* reproducible results while allowing different compositions of components.
*
* The method allows the use of an object as source. This is to allow the use in
* different situations:
* 1. Random generators belonging to a single component should pass the class
* object of that component to this method.
* 2. Components that are always loaded during a simulation can use the
* Simulator.class object for a system wide random generator like {@link #getRandom()}
* provided.
* 3. Utility classes that are loaded by many different components should use
* a reference to themselves to avoid transitive dependencies on the same random
* generator throughout different components.
* This approach will provide way to to configure different seeds for specific class
* objects. This will allow component specific seeds and, e.g., the transfer of movement
* behavior from one configured scenario to another.
*
* @param source The source that requires the random generator (component class object is preferred)
* @param salt Added to the default seed, should be bound
* @return The random generator for the given source
*/
@Override
public Random getRandom(Object source) {
if (randomGenerators.containsKey(source)) {
return randomGenerators.get(source);
} else {
if (randomGenerators.containsKey(source)) {
return randomGenerators.get(source);
} else {
long thisSeed = source.toString().hashCode() + 31
* seed;
Monitor.log(Simulator.class, Level.INFO,
"Created a new Random Source for %s with seed %d", source,
thisSeed);
Random randomGenerator = new Random(thisSeed);
randomGenerators.put(source, randomGenerator);
return randomGenerator;
}
}
randomGenerators.put(source, randomGenerator);
return randomGenerator;
}
}
/**
* Returns the current simulation unit value.
*
*
* @return the current simulation unit value
*/
public static long getCurrentTime() {
......@@ -411,17 +411,17 @@ public class Simulator implements RandomGeneratorComponent, GlobalComponent {
/**
* Returns the start time of the simulation.
*
*
* @return
*/
public static long getStartTime() {
return singleton.startTime;
}
/**
* Returns the end time of the simulation.
*
*
* @return
*/
public static long getEndTime() {
......@@ -432,7 +432,7 @@ public class Simulator implements RandomGeneratorComponent, GlobalComponent {
* Sets the end time at which the simulation framework will finish at the
* latest the simulation , irrespective if there are still unprocessed
* events in the event queue.
*
*
* @param endTime
* point in time at which the simular will finish at the latest
*/
......@@ -450,7 +450,7 @@ public class Simulator implements RandomGeneratorComponent, GlobalComponent {
* in turn will save the state in a static variable. Is this flag set
* to true the MeasurementDAO will return from every method before
* doing any work.
*
*
* @param inactive
*/
public void setDatabaseInactive(boolean inactive) {
......@@ -463,25 +463,25 @@ public class Simulator implements RandomGeneratorComponent, GlobalComponent {
}
}
/**
/**
* Overrides the database name provided in the persistence.xml.
*
*
* As the DAO class only provides static methods and is not a singleton it
* cannot be accessed through the config file otherwise.
*
*
* @param database
* @deprecated use the MetricOutputDAO instead to do all relevant
* configuration!
*/
@Deprecated
public void setDatabase(String database) {
DAO.database = database;
}
public void setDatabase(String database) {
DAO.database = database;
}
/**
* Can be used to format the absolute simulation time (current, past or
* future) into human-readable format: (h:m:s:ms).
*
*
* @param time
* - absolute simulation time like the one obtained via
* getCurrentTime();
......@@ -494,7 +494,7 @@ public class Simulator implements RandomGeneratorComponent, GlobalComponent {
/**
* Specifies how often the scheduler will printout the current simulation
* time.
*
*
* @param time
*/
public void setStatusInterval(long time) {
......@@ -512,38 +512,38 @@ public class Simulator implements RandomGeneratorComponent, GlobalComponent {
public void setSimulationSpeedLocked(boolean locked) {
scheduler.setSimulationSpeedLocked(locked);
}
public static Configurator getConfigurator() {
return defaultConfigurator;
}
/**
* Sets the real world start time.
*
* @param realWorldStartTime the new real world start time
*/
public void setRealWorldStartTime(String realWorldStartTime) {
final Date now = new Date();
SimpleDateFormat formatter = new SimpleDateFormat("HH:mm:ss z dd.MM.yyyy", Locale.GERMANY);
try {
final Date startTime = formatter.parse(realWorldStartTime);
if( now.after(startTime) ) {
throw new AssertionError(
"Simulator was supposed to run in the past. Check config for realWorldStartTime.");
}
}
this.realWorldStartTime = startTime;
} catch (ParseException e) {
Monitor.log(
Simulator.class,
Level.WARN,
"Could not parse realWorldStartTime. Please check configuration file. Starting *NOW*");
}
}
public void addObserver(SimulatorObserver observer) {
......
......@@ -826,10 +826,8 @@ public class TraciSimulationController implements VehicleController, SimulationS
List<RoadNetworkLane> lanes = _roadNetwork.getEdge(pEdgeID).getLanes();
if (lanes.size() > 0) {
List<Location> laneShape = getLaneShape(lanes.get(0).getLaneID());
for (Location location : laneShape) {
if (0 <= location.getLongitude() && location.getLongitude() <= _endX - _startX && 0 <= location.getLatitude() && location.getLatitude() <= _endY - _startY) {
return true;
}
if (laneShape.size() > 1) {
return true;
}
}
return false;
......
......@@ -20,6 +20,7 @@
package de.tud.kom.p2psim.impl.topology.views.visualization.ui;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
......@@ -28,12 +29,16 @@ import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.JSpinner;
import javax.swing.JToggleButton;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingConstants;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
......@@ -44,6 +49,9 @@ import de.tud.kom.p2psim.impl.topology.views.visualization.ComponentVisManager;
import de.tud.kom.p2psim.impl.topology.views.visualization.ComponentVisManager.VisInfo;
import de.tud.kom.p2psim.impl.topology.views.visualization.ComponentVisManager.VisualizationListener;
import de.tud.kom.p2psim.impl.util.guirunner.Config;
import de.tudarmstadt.maki.simonstrator.api.Event;
import de.tudarmstadt.maki.simonstrator.api.EventHandler;
import de.tudarmstadt.maki.simonstrator.api.Time;
/**
* Menu-Bar containing means to alter the simulation speed, pause and un-pause
......@@ -70,6 +78,8 @@ public class SimControlPanel extends JMenuBar implements ActionListener, ChangeL
private ComponentVisManager visManager;
private JPanel runUntil;
public SimControlPanel(ComponentVisManager visManager) {
// super("Control");
this.visManager = visManager;
......@@ -86,6 +96,8 @@ public class SimControlPanel extends JMenuBar implements ActionListener, ChangeL
this.add(getSpeedLabel());
this.add(Box.createHorizontalStrut(10));
this.add(getPlayPauseButton());
this.add(Box.createHorizontalStrut(10));
this.add(getRunUntil());
this.add(Box.createHorizontalGlue());
}
......@@ -162,6 +174,53 @@ public class SimControlPanel extends JMenuBar implements ActionListener, ChangeL
return speedslider;
}
protected JPanel getRunUntil() {
if (runUntil == null) {
runUntil = new JPanel();
runUntil.setBackground(new Color(0, 0, 0, 0));
JButton button = new JButton("Run until");
runUntil.add(button);
JSpinner spinner = new JSpinner(new SpinnerNumberModel(0, 0, Simulator.getEndTime() / Time.MINUTE, 1));
runUntil.add(spinner);
runUntil.add(new JLabel("min"));
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent pE) {
int value = (int)((double) spinner.getValue());
boolean wasPaused = isSimulatorPaused;
if (Time.getCurrentTime() < value * Time.MINUTE) {
if (wasPaused) {
unpauseSimulation();
}
double timeSkew = Simulator.getScheduler().getTimeSkew();
Simulator.getScheduler().setTimeSkew(0);
Event.scheduleWithDelay(value * Time.MINUTE - Time.getCurrentTime(), new EventHandler() {
@Override
public void eventOccurred(Object pContent, int pType) {
if (Simulator.getScheduler().getTimeSkew() == 0) {
Simulator.getScheduler().setTimeSkew(timeSkew);
}
if (wasPaused) {
pauseSimulation();
}
}
}, null, 0);
}
}
});
}
return runUntil;
}
protected JLabel getSpeedLabel() {
if (speedlabel == null) {
speedlabel = new JLabel("max");
......
......@@ -27,6 +27,7 @@ import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
......@@ -112,7 +113,7 @@ implements VisualizationComponent {
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
boolean first = true;
for (NodeVis vis : nodes) {
for (NodeVis vis : new ArrayList<>(nodes)) {
vis.draw(g2);
if (first) {
first = false;
......@@ -238,7 +239,7 @@ implements VisualizationComponent {
g2.setStroke(new BasicStroke(3));
for (int color = 0; color < segments; color++) {
if (!activeLayers[color]) {
if (activeLayers.length <= color || !activeLayers[color]) {
continue;
}
g2.setColor(Color.DARK_GRAY);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment