two major updates:

simulation time now uses microsecond resolution instead of earlier millisecond (allows for better simulation accuracy)
+
Contiki motes schedule their own events instead of the earlier approach of having the simulation loop register tick events common to all Contiki motes (faster simulation)

both changes require extensive changes throughout COOJA
This commit is contained in:
fros4943 2009-05-26 14:15:41 +00:00
parent 300f3396ac
commit 61c2ed2e1a

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2006, Swedish Institute of Computer Science. All rights * Copyright (c) 2009, Swedish Institute of Computer Science. All rights
* reserved. * reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -24,7 +24,7 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
* $Id: Simulation.java,v 1.46 2009/03/12 11:01:26 nifi Exp $ * $Id: Simulation.java,v 1.47 2009/05/26 14:15:41 fros4943 Exp $
*/ */
package se.sics.cooja; package se.sics.cooja;
@ -33,13 +33,12 @@ import java.util.*;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import org.jdom.*; import org.jdom.*;
import se.sics.cooja.contikimote.ContikiMote;
import se.sics.cooja.dialogs.*; import se.sics.cooja.dialogs.*;
/** /**
* A simulation consists of a number of motes and mote types. * A simulation consists of a number of motes and mote types.
* *
* The motes in the simulation are ticked every millisecond.
*
* A simulation is observable: * A simulation is observable:
* changed simulation state, added or deleted motes etc are observed. * changed simulation state, added or deleted motes etc are observed.
* To track mote changes, observe the mote (interfaces) itself. * To track mote changes, observe the mote (interfaces) itself.
@ -47,7 +46,10 @@ import se.sics.cooja.dialogs.*;
* @author Fredrik Osterlind * @author Fredrik Osterlind
*/ */
public class Simulation extends Observable implements Runnable { public class Simulation extends Observable implements Runnable {
public static final long MILLISECOND = 1000L;
/*private static long EVENT_COUNTER = 0;*/
private Vector<Mote> motes = new Vector<Mote>(); private Vector<Mote> motes = new Vector<Mote>();
private Vector<MoteType> moteTypes = new Vector<MoteType>(); private Vector<MoteType> moteTypes = new Vector<MoteType>();
@ -56,8 +58,6 @@ public class Simulation extends Observable implements Runnable {
private long currentSimulationTime = 0; private long currentSimulationTime = 0;
private int tickTime = 1;
private String title = null; private String title = null;
private RadioMedium currentRadioMedium = null; private RadioMedium currentRadioMedium = null;
@ -76,45 +76,57 @@ public class Simulation extends Observable implements Runnable {
private boolean randomSeedGenerated = false; private boolean randomSeedGenerated = false;
private int maxMoteStartupDelay = 1000; private long maxMoteStartupDelay = 1000*MILLISECOND;
private Random randomGenerator = new Random(); private Random randomGenerator = new Random();
// Tick observable private boolean hasMillisecondObservers = false;
private class TickObservable extends Observable { private MillisecondObservable millisecondObservable = new MillisecondObservable();
private void allTicksPerformed() { private class MillisecondObservable extends Observable {
private void newMillisecond(long time) {
setChanged(); setChanged();
notifyObservers(); notifyObservers(time);
} }
} }
private TickObservable tickObservable = new TickObservable(); private EventQueue eventQueue = new EventQueue();
/** /**
* Add tick observer. This observer is notified once every tick loop, that is, * Add millisecond observer.
* when all motes have been ticked. * This observer is notified once every simulated millisecond.
* *
* @see #deleteTickObserver(Observer) * @see #deleteMillisecondObserver(Observer)
* @param newObserver * @param newObserver Observer
* New observer
*/ */
public void addTickObserver(Observer newObserver) { public void addMillisecondObserver(Observer newObserver) {
tickObservable.addObserver(newObserver); millisecondObservable.addObserver(newObserver);
hasMillisecondObservers = true;
rescheduleEvents = true;
} }
/** /**
* Delete an existing tick observer. * Delete millisecond observer.
* *
* @see #addTickObserver(Observer) * @see #addMillisecondObserver(Observer)
* @param observer * @param observer Observer to delete
* Observer to delete
*/ */
public void deleteTickObserver(Observer observer) { public void deleteMillisecondObserver(Observer observer) {
tickObservable.deleteObserver(observer); millisecondObservable.deleteObserver(observer);
hasMillisecondObservers = millisecondObservable.countObservers() > 0;
rescheduleEvents = true;
} }
protected void scheduleEventUnsafe(TimeEvent e, long time) { /**
* Schedule events.
* This method is not thread-safe, and should only be invoked when the
* simulation is paused, or from inside the simulation loop.
*
* @see #scheduleEvent(TimeEvent, long)
* @param e Event
* @param time Execution time
*/
public void scheduleEventUnsafe(TimeEvent e, long time) {
eventQueue.addEvent(e, time); eventQueue.addEvent(e, time);
} }
@ -122,7 +134,7 @@ public class Simulation extends Observable implements Runnable {
* Schedule event to be handled by event loop. * Schedule event to be handled by event loop.
* *
* @param e Event * @param e Event
* @param time Simulated time * @param time Execution time
*/ */
public void scheduleEvent(TimeEvent e, long time) { public void scheduleEvent(TimeEvent e, long time) {
if (Thread.currentThread() == simulationThread) { if (Thread.currentThread() == simulationThread) {
@ -132,10 +144,8 @@ public class Simulation extends Observable implements Runnable {
} }
} }
private EventQueue eventQueue = new EventQueue();
private Mote[] emulatedMoteArray; private Mote[] emulatedMoteArray;
private TimeEvent tickemulatedMotesEvent = new TimeEvent(0) { private TimeEvent tickEmulatedMotesEvent = new TimeEvent(0) {
public void execute(long t) { public void execute(long t) {
/*logger.info("MSP motes tick at: " + t);*/ /*logger.info("MSP motes tick at: " + t);*/
if (emulatedMoteArray.length == 0) { if (emulatedMoteArray.length == 0) {
@ -154,26 +164,11 @@ public class Simulation extends Observable implements Runnable {
} }
} }
/* Reschedule MSP motes */ /* XXX Reschedule MSP motes (millisecond resolution) */
scheduleEventUnsafe(this, t+1); scheduleEventUnsafe(this, t+1000);
} }
}; public String toString() {
return "MSPSIM ALL";
private Mote[] moteArray;
private TimeEvent tickMotesEvent = new TimeEvent(0) {
public void execute(long t) {
/*logger.info("Contiki motes tick at: " + t);*/
if (moteArray.length == 0) {
return;
}
/* Tick Contiki motes */
for (Mote mote : moteArray) {
mote.tick(t);
}
/* Reschedule Contiki motes */
scheduleEventUnsafe(this, t+1);
} }
}; };
@ -185,26 +180,39 @@ public class Simulation extends Observable implements Runnable {
} }
try { Thread.sleep(delayTime); } catch (InterruptedException e) { } try { Thread.sleep(delayTime); } catch (InterruptedException e) { }
scheduleEventUnsafe(this, t+1); scheduleEventUnsafe(this, t+MILLISECOND);
}
public String toString() {
return "DELAY";
} }
}; };
private void recreateTickLists() { private TimeEvent millisecondEvent = new TimeEvent(0) {
public void execute(long t) {
if (!hasMillisecondObservers) {
return;
}
millisecondObservable.newMillisecond(getSimulationTime());
scheduleEventUnsafe(this, t+MILLISECOND);
}
public String toString() {
return "MILLISECOND: " + millisecondObservable.countObservers();
}
};
private void recreateMoteLists() {
/* Tick MSP motes separately */ /* Tick MSP motes separately */
ArrayList<Mote> emulatedMotes = new ArrayList<Mote>(); ArrayList<Mote> emulatedMotes = new ArrayList<Mote>();
ArrayList<Mote> contikiMotes = new ArrayList<Mote>();
for (Mote mote: motes) { for (Mote mote: motes) {
/* TODO: fixe an emulatedMote generic class */ /* TODO: fixe an emulatedMote generic class */
if (mote.getType().getClass().toString().contains(".mspmote.")) { if (mote.getType().getClass().toString().contains(".mspmote.")) {
emulatedMotes.add(mote); emulatedMotes.add(mote);
} else if (mote.getType().getClass().toString().contains(".avrmote.")) { } else if (mote.getType().getClass().toString().contains(".avrmote.")) {
emulatedMotes.add(mote); emulatedMotes.add(mote);
} else {
contikiMotes.add(mote);
} }
} }
emulatedMoteArray = emulatedMotes.toArray(new Mote[emulatedMotes.size()]); emulatedMoteArray = emulatedMotes.toArray(new Mote[emulatedMotes.size()]);
moteArray = contikiMotes.toArray(new Mote[contikiMotes.size()]);
} }
private boolean rescheduleEvents = false; private boolean rescheduleEvents = false;
@ -212,28 +220,27 @@ public class Simulation extends Observable implements Runnable {
long lastStartTime = System.currentTimeMillis(); long lastStartTime = System.currentTimeMillis();
logger.info("Simulation main loop started, system time: " + lastStartTime); logger.info("Simulation main loop started, system time: " + lastStartTime);
isRunning = true; isRunning = true;
/* Schedule tick events */ /* Schedule tick events */
scheduleEventUnsafe(tickMotesEvent, currentSimulationTime); scheduleEventUnsafe(tickEmulatedMotesEvent, currentSimulationTime);
scheduleEventUnsafe(tickemulatedMotesEvent, currentSimulationTime); scheduleEventUnsafe(delayEvent, currentSimulationTime - (currentSimulationTime % 1000) + 1000);
scheduleEventUnsafe(delayEvent, currentSimulationTime); scheduleEventUnsafe(millisecondEvent, currentSimulationTime - (currentSimulationTime % 1000) + 1000);
/* Simulation starting */ /* Simulation starting */
this.setChanged(); this.setChanged();
this.notifyObservers(this); this.notifyObservers(this);
recreateTickLists(); recreateMoteLists();
boolean increasedTime;
try { try {
TimeEvent nextEvent; TimeEvent nextEvent;
while (isRunning) { while (isRunning) {
if (rescheduleEvents) { if (rescheduleEvents) {
rescheduleEvents = false; rescheduleEvents = false;
scheduleEventUnsafe(tickMotesEvent, currentSimulationTime); scheduleEventUnsafe(tickEmulatedMotesEvent, currentSimulationTime);
scheduleEventUnsafe(tickemulatedMotesEvent, currentSimulationTime); scheduleEventUnsafe(delayEvent, currentSimulationTime - (currentSimulationTime % 1000) + 1000);
scheduleEventUnsafe(delayEvent, currentSimulationTime); scheduleEventUnsafe(millisecondEvent, currentSimulationTime - (currentSimulationTime % 1000) + 1000);
} }
nextEvent = eventQueue.popFirst(); nextEvent = eventQueue.popFirst();
@ -241,15 +248,10 @@ public class Simulation extends Observable implements Runnable {
throw new RuntimeException("No more events"); throw new RuntimeException("No more events");
} }
increasedTime = nextEvent.time > currentSimulationTime;
currentSimulationTime = nextEvent.time; currentSimulationTime = nextEvent.time;
/*logger.info("Executing event #" + EVENT_COUNTER++ + " @ " + currentSimulationTime + ": " + nextEvent);*/
nextEvent.execute(currentSimulationTime); nextEvent.execute(currentSimulationTime);
/* Notify tick observers */
if (increasedTime) {
tickObservable.allTicksPerformed();
}
if (stopSimulation) { if (stopSimulation) {
isRunning = false; isRunning = false;
} }
@ -303,8 +305,9 @@ public class Simulation extends Observable implements Runnable {
/* Wait until simulation stops */ /* Wait until simulation stops */
if (Thread.currentThread() != simulationThread) { if (Thread.currentThread() != simulationThread) {
try { try {
if (simulationThread != null) { Thread simThread = simulationThread;
simulationThread.join(); if (simThread != null) {
simThread.join();
} }
} catch (InterruptedException e) { } catch (InterruptedException e) {
} }
@ -316,14 +319,19 @@ public class Simulation extends Observable implements Runnable {
* Starts simulation if stopped, ticks all motes once, and finally stops * Starts simulation if stopped, ticks all motes once, and finally stops
* simulation again. * simulation again.
*/ */
public void tickSimulation() { public void stepMillisecondSimulation() {
addTickObserver(new Observer() { TimeEvent stopEvent = new TimeEvent(0) {
public void update(Observable obs, Object obj) { public void execute(long t) {
/* Stop simulation */
stopSimulation(); stopSimulation();
deleteTickObserver(this);
} }
}); };
startSimulation(); scheduleEvent(stopEvent, getSimulationTime()+Simulation.MILLISECOND);
/* Start simulation if not running */
if (!isRunning()) {
startSimulation();
}
} }
/** /**
@ -370,14 +378,14 @@ public class Simulation extends Observable implements Runnable {
/** /**
* @return Maximum mote startup delay * @return Maximum mote startup delay
*/ */
public int getDelayedMoteStartupTime() { public long getDelayedMoteStartupTime() {
return maxMoteStartupDelay; return maxMoteStartupDelay;
} }
/** /**
* @param maxMoteStartupDelay Maximum mote startup delay * @param maxMoteStartupDelay Maximum mote startup delay
*/ */
public void setDelayedMoteStartupTime(int maxMoteStartupDelay) { public void setDelayedMoteStartupTime(long maxMoteStartupDelay) {
this.maxMoteStartupDelay = Math.max(0, maxMoteStartupDelay); this.maxMoteStartupDelay = Math.max(0, maxMoteStartupDelay);
} }
@ -402,11 +410,6 @@ public class Simulation extends Observable implements Runnable {
element.setText(Integer.toString(delayTime)); element.setText(Integer.toString(delayTime));
config.add(element); config.add(element);
// Tick time
element = new Element("ticktime");
element.setText(Integer.toString(tickTime));
config.add(element);
// Random seed // Random seed
element = new Element("randomseed"); element = new Element("randomseed");
if (randomSeedGenerated) { if (randomSeedGenerated) {
@ -417,8 +420,8 @@ public class Simulation extends Observable implements Runnable {
config.add(element); config.add(element);
// Max mote startup delay // Max mote startup delay
element = new Element("motedelay"); element = new Element("motedelay_us");
element.setText(Integer.toString(maxMoteStartupDelay)); element.setText(Long.toString(maxMoteStartupDelay));
config.add(element); config.add(element);
// Radio Medium // Radio Medium
@ -487,11 +490,6 @@ public class Simulation extends Observable implements Runnable {
delayTime = Integer.parseInt(element.getText()); delayTime = Integer.parseInt(element.getText());
} }
// Tick time
if (element.getName().equals("ticktime")) {
tickTime = Integer.parseInt(element.getText());
}
// Random seed // Random seed
if (element.getName().equals("randomseed")) { if (element.getName().equals("randomseed")) {
if (element.getText().equals("generated")) { if (element.getText().equals("generated")) {
@ -504,6 +502,9 @@ public class Simulation extends Observable implements Runnable {
// Max mote startup delay // Max mote startup delay
if (element.getName().equals("motedelay")) { if (element.getName().equals("motedelay")) {
maxMoteStartupDelay = Integer.parseInt(element.getText())*MILLISECOND;
}
if (element.getName().equals("motedelay_us")) {
maxMoteStartupDelay = Integer.parseInt(element.getText()); maxMoteStartupDelay = Integer.parseInt(element.getText());
} }
@ -596,28 +597,39 @@ public class Simulation extends Observable implements Runnable {
* Mote to remove * Mote to remove
*/ */
public void removeMote(final Mote mote) { public void removeMote(final Mote mote) {
if (!isRunning()) {
/* Simulation is stopped, remove mote immediately */
motes.remove(mote);
currentRadioMedium.unregisterMote(mote, this);
myGUI.closeMotePlugins(mote);
this.setChanged();
this.notifyObservers(this);
return;
}
/* Simulation is running, remove mote in simulation loop */ /* Simulation is running, remove mote in simulation loop */
TimeEvent removeNewMoteEvent = new TimeEvent(0) { TimeEvent removeMoteEvent = new TimeEvent(0) {
public void execute(long t) { public void execute(long t) {
motes.remove(mote); motes.remove(mote);
currentRadioMedium.unregisterMote(mote, Simulation.this); currentRadioMedium.unregisterMote(mote, Simulation.this);
recreateTickLists();
/* Loop through all scheduled events.
* Delete all events associated with deleted mote. */
TimeEvent ev = eventQueue.peekFirst();
while (ev != null) {
if (ev instanceof MoteTimeEvent) {
if (((MoteTimeEvent)ev).getMote() == mote) {
ev.remove();
}
}
ev = ev.nextEvent;
}
recreateMoteLists();
Simulation.this.setChanged(); Simulation.this.setChanged();
Simulation.this.notifyObservers(this); Simulation.this.notifyObservers(this);
} }
}; };
scheduleEvent(removeNewMoteEvent, Simulation.this.getSimulationTime()); if (!isRunning()) {
/* Simulation is stopped, remove mote immediately */
removeMoteEvent.execute(0);
} else {
/* Schedule event */
scheduleEvent(removeMoteEvent, Simulation.this.getSimulationTime());
}
} }
/** /**
@ -629,7 +641,8 @@ public class Simulation extends Observable implements Runnable {
public void addMote(final Mote mote) { public void addMote(final Mote mote) {
if (maxMoteStartupDelay > 0 && mote.getInterfaces().getClock() != null) { if (maxMoteStartupDelay > 0 && mote.getInterfaces().getClock() != null) {
mote.getInterfaces().getClock().setDrift( mote.getInterfaces().getClock().setDrift(
-randomGenerator.nextInt(maxMoteStartupDelay) - getSimulationTime()
- randomGenerator.nextInt((int)maxMoteStartupDelay)
); );
} }
@ -647,10 +660,13 @@ public class Simulation extends Observable implements Runnable {
public void execute(long t) { public void execute(long t) {
motes.add(mote); motes.add(mote);
currentRadioMedium.registerMote(mote, Simulation.this); currentRadioMedium.registerMote(mote, Simulation.this);
recreateTickLists(); recreateMoteLists();
Simulation.this.setChanged(); Simulation.this.setChanged();
Simulation.this.notifyObservers(this); Simulation.this.notifyObservers(this);
} }
public String toString() {
return "ADD MOTE";
}
}; };
scheduleEvent(addNewMoteEvent, Simulation.this.getSimulationTime()); scheduleEvent(addNewMoteEvent, Simulation.this.getSimulationTime());
@ -727,11 +743,10 @@ public class Simulation extends Observable implements Runnable {
} }
/** /**
* Set delay time to delayTime. When all motes have been ticked, the * Set delay time in milliseconds.
* simulation waits for this time before ticking again. * The simulation loop sleeps this value every simulated millisecond.
* *
* @param delayTime * @param delayTime New delay time (ms)
* New delay time (ms)
*/ */
public void setDelayTime(int delayTime) { public void setDelayTime(int delayTime) {
this.delayTime = delayTime; this.delayTime = delayTime;
@ -767,27 +782,20 @@ public class Simulation extends Observable implements Runnable {
/** /**
* Returns current simulation time. * Returns current simulation time.
* *
* @return Simulation time (ms) * @return Simulation time (microseconds)
*/ */
public long getSimulationTime() { public long getSimulationTime() {
return currentSimulationTime; return currentSimulationTime;
} }
/** /**
* Set tick time to tickTime. The tick time is the simulated time every tick * Returns current simulation time rounded to milliseconds.
* takes. When all motes have been ticked, current simulation time is *
* increased with tickTime. Default tick time is 1 ms. * @see #getSimulationTime()
* * @return
* @see #getTickTime()
* @see #getTickTimeInSeconds()
* @param tickTime
* New tick time (ms)
*/ */
public void setTickTime(int tickTime) { public long getSimulationTimeMillis() {
this.tickTime = tickTime; return currentSimulationTime / MILLISECOND;
this.setChanged();
this.notifyObservers(this);
} }
/** /**
@ -826,26 +834,6 @@ public class Simulation extends Observable implements Runnable {
return currentRadioMedium; return currentRadioMedium;
} }
/**
* Get current tick time (ms).
*
* @see #setTickTime(int)
* @return Current tick time (ms)
*/
public int getTickTime() {
return tickTime;
}
/**
* Get current tick time (seconds).
*
* @see #setTickTime(int)
* @return Current tick time (seconds)
*/
public double getTickTimeInSeconds() {
return (tickTime) / 1000.0;
}
/** /**
* Return true is simulation is running. * Return true is simulation is running.
* *