diff --git a/tools/cooja/java/se/sics/cooja/plugins/TimeLine.java b/tools/cooja/java/se/sics/cooja/plugins/TimeLine.java
index 7c1728738..762f4988c 100644
--- a/tools/cooja/java/se/sics/cooja/plugins/TimeLine.java
+++ b/tools/cooja/java/se/sics/cooja/plugins/TimeLine.java
@@ -26,7 +26,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: TimeLine.java,v 1.26 2010/05/21 08:46:44 fros4943 Exp $
+ * $Id: TimeLine.java,v 1.27 2010/08/13 10:23:20 fros4943 Exp $
*/
package se.sics.cooja.plugins;
@@ -52,17 +52,18 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Observable;
import java.util.Observer;
-import java.util.Vector;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JCheckBox;
+import javax.swing.JCheckBoxMenuItem;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
+import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
@@ -85,6 +86,7 @@ import org.jdom.Element;
import se.sics.cooja.ClassDescription;
import se.sics.cooja.GUI;
import se.sics.cooja.Mote;
+import se.sics.cooja.Plugin;
import se.sics.cooja.PluginType;
import se.sics.cooja.Simulation;
import se.sics.cooja.VisPlugin;
@@ -94,6 +96,7 @@ import se.sics.cooja.SimEventCentral.MoteCountListener;
import se.sics.cooja.interfaces.LED;
import se.sics.cooja.interfaces.Radio;
import se.sics.cooja.interfaces.Radio.RadioEvent;
+import se.sics.cooja.motes.AbstractEmulatedMote;
/**
* Shows events such as mote logs, LEDs, and radio transmissions, in a timeline.
@@ -747,39 +750,63 @@ public class TimeLine extends VisPlugin {
});
}
- private Action radioLoggerAction = new AbstractAction("to Radio Logger") {
+ private Action radioLoggerAction = new AbstractAction("in Radio Logger") {
private static final long serialVersionUID = 7690116136861949864L;
public void actionPerformed(ActionEvent e) {
- RadioLogger plugin = (RadioLogger) simulation.getGUI().getStartedPlugin(RadioLogger.class.getName());
- if (plugin == null) {
- logger.fatal("No Radio Logger plugin");
- return;
- }
if (popupLocation == null) {
return;
}
+ long time = (long) ((double)popupLocation.x*currentPixelDivisor);
- /* Select simulation time */
- plugin.trySelectTime((long) (popupLocation.x*currentPixelDivisor));
+ Plugin[] plugins = simulation.getGUI().getStartedPlugins();
+ for (Plugin p: plugins) {
+ if (!(p instanceof RadioLogger)) {
+ continue;
+ }
+
+ /* Select simulation time */
+ RadioLogger plugin = (RadioLogger) p;
+ plugin.trySelectTime(time);
+ }
}
};
- private Action logListenerAction = new AbstractAction("to Log Listener") {
+ private Action logListenerAction = new AbstractAction("in Log Listener") {
private static final long serialVersionUID = -8626118368774023257L;
public void actionPerformed(ActionEvent e) {
- LogListener plugin = (LogListener) simulation.getGUI().getStartedPlugin(LogListener.class.getName());
- if (plugin == null) {
- logger.fatal("No Log Listener plugin");
- return;
- }
if (popupLocation == null) {
return;
}
+ long time = (long) ((double)popupLocation.x*currentPixelDivisor);
- /* Select simulation time */
- plugin.trySelectTime((long) (popupLocation.x*currentPixelDivisor));
+ Plugin[] plugins = simulation.getGUI().getStartedPlugins();
+ for (Plugin p: plugins) {
+ if (!(p instanceof LogListener)) {
+ continue;
+ }
+
+ /* Select simulation time */
+ LogListener plugin = (LogListener) p;
+ plugin.trySelectTime(time);
+ }
}
};
-
+
+ private boolean executionDetails = false;
+ private boolean radioChannels = false;
+ private Action executionDetailsAction = new AbstractAction("Show execution details in tooltips") {
+ private static final long serialVersionUID = -8626118368774023257L;
+ public void actionPerformed(ActionEvent e) {
+ executionDetails = !executionDetails;
+ }
+ };
+ private Action radioChannelsAction = new AbstractAction("Color radio state by active radio channel") {
+ private static final long serialVersionUID = -8626118368774023257L;
+ public void actionPerformed(ActionEvent e) {
+ radioChannels = !radioChannels;
+ repaint();
+ }
+ };
+
private void numberMotesWasUpdated() {
/* Plugin title */
if (allMoteEvents.isEmpty()) {
@@ -839,7 +866,7 @@ public class TimeLine extends VisPlugin {
}
}
- private void addMoteObservers(Mote mote, final MoteEvents moteEvents) {
+ private void addMoteObservers(final Mote mote, final MoteEvents moteEvents) {
/* TODO Log: final Log moteLog = mote.getInterfaces().getLog(); */
/* TODO Unknown state event */
@@ -875,19 +902,59 @@ public class TimeLine extends VisPlugin {
if (moteRadio != null) {
RadioHWEvent startupHW = new RadioHWEvent(
simulation.getSimulationTime(), moteRadio.isReceiverOn());
+ if (radioChannels) {
+ startupHW.channel = moteRadio.getChannel();
+ }
moteEvents.addRadioHW(startupHW);
RadioRXTXEvent startupRXTX = new RadioRXTXEvent(
simulation.getSimulationTime(), RXTXRadioEvent.IDLE);
moteEvents.addRadioRXTX(startupRXTX);
Observer observer = new Observer() {
- public void update(Observable o, Object arg) {
+ int lastChannel = -1;
+ public void update(Observable o, Object arg) {
/* Radio HW events */
- if (moteRadio.getLastEvent() == RadioEvent.HW_ON ||
- moteRadio.getLastEvent() == RadioEvent.HW_OFF) {
+ if (radioChannels && moteRadio.getLastEvent() == RadioEvent.UNKNOWN) {
+ int nowChannel = moteRadio.getChannel();
+ if (nowChannel == lastChannel) {
+ return;
+ }
+ lastChannel = nowChannel;
+
RadioHWEvent ev = new RadioHWEvent(
- simulation.getSimulationTime(), moteRadio.getLastEvent()==RadioEvent.HW_ON);
+ simulation.getSimulationTime(), moteRadio.isReceiverOn());
+ if (radioChannels) {
+ ev.channel = moteRadio.getChannel();
+ }
moteEvents.addRadioHW(ev);
+
+ if (executionDetails && mote instanceof AbstractEmulatedMote) {
+ String details = ((AbstractEmulatedMote) mote).getExecutionDetails();
+ if (details != null) {
+ details = "
" + details.replace("\n", "
");
+ ev.details = details;
+ }
+ }
+ return;
+ }
+
+ if (moteRadio.getLastEvent() == RadioEvent.HW_ON ||
+ moteRadio.getLastEvent() == RadioEvent.HW_OFF) {
+ RadioHWEvent ev = new RadioHWEvent(
+ simulation.getSimulationTime(), moteRadio.isReceiverOn());
+ if (radioChannels) {
+ ev.channel = moteRadio.getChannel();
+ }
+
+ moteEvents.addRadioHW(ev);
+
+ if (executionDetails && mote instanceof AbstractEmulatedMote) {
+ String details = ((AbstractEmulatedMote) mote).getExecutionDetails();
+ if (details != null) {
+ details = "
" + details.replace("\n", "
");
+ ev.details = details;
+ }
+ }
return;
}
@@ -919,6 +986,15 @@ public class TimeLine extends VisPlugin {
}
moteEvents.addRadioRXTX(ev);
+
+ if (executionDetails && mote instanceof AbstractEmulatedMote) {
+ String details = ((AbstractEmulatedMote) mote).getExecutionDetails();
+ if (details != null) {
+ details = "
" + details.replace("\n", "
");
+ ev.details = details;
+ }
+ }
+
return;
}
@@ -1034,7 +1110,7 @@ public class TimeLine extends VisPlugin {
}
public Collection getConfigXML() {
- Vector config = new Vector();
+ ArrayList config = new ArrayList();
Element element;
/* Remember observed motes */
@@ -1075,6 +1151,15 @@ public class TimeLine extends VisPlugin {
config.add(element);
}
+ if (executionDetails) {
+ element = new Element("executionDetails");
+ config.add(element);
+ }
+ if (radioChannels) {
+ element = new Element("radioChannels");
+ config.add(element);
+ }
+
element = new Element("split");
element.addContent("" + splitPane.getDividerLocation());
config.add(element);
@@ -1094,6 +1179,9 @@ public class TimeLine extends VisPlugin {
showLogOutputs = false;
showWatchpoints = false;
+ executionDetails = false;
+ radioChannels = false;
+
/* Remove already registered motes */
MoteEvents[] allMoteEventsArr = allMoteEvents.toArray(new MoteEvents[0]);
for (MoteEvents moteEvents: allMoteEventsArr) {
@@ -1117,6 +1205,10 @@ public class TimeLine extends VisPlugin {
showLogOutputs = true;
} else if ("showWatchpoints".equals(name)) {
showWatchpoints = true;
+ } else if ("executionDetails".equals(name)) {
+ executionDetails = true;
+ } else if ("radioChannels".equals(name)) {
+ radioChannels = true;
} else if ("split".equals(name)) {
splitPane.setDividerLocation(Integer.parseInt(element.getText()));
} else if ("zoom".equals(name)) {
@@ -1180,8 +1272,25 @@ public class TimeLine extends VisPlugin {
popupMenu.addSeparator();
- popupMenu.add(new JMenuItem(radioLoggerAction));
- popupMenu.add(new JMenuItem(logListenerAction));
+ JMenu focusMenu = new JMenu("Focus");
+ focusMenu.add(new JMenuItem(logListenerAction));
+ focusMenu.add(new JMenuItem(radioLoggerAction));
+ popupMenu.add(focusMenu);
+
+ JMenu advancedMenu = new JMenu("Advanced");
+ advancedMenu.add(new JCheckBoxMenuItem(executionDetailsAction) {
+ private static final long serialVersionUID = 8314556794750277113L;
+ public boolean isSelected() {
+ return executionDetails;
+ }
+ });
+ advancedMenu.add(new JCheckBoxMenuItem(radioChannelsAction) {
+ private static final long serialVersionUID = 6830282466652559714L;
+ public boolean isSelected() {
+ return radioChannels;
+ }
+ });
+ popupMenu.add(advancedMenu);
addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
@@ -1232,7 +1341,17 @@ public class TimeLine extends VisPlugin {
forceRepaintAndFocus(zoomCenterTime, zoomCenter);
return;
}
+ if (e.isAltDown()) {
+ /* Pan with mouse */
+ if (zoomCenterTime < 0) {
+ return;
+ }
+ zoomCenter = (double) (e.getX() - timeline.getVisibleRect().x) / timeline.getVisibleRect().width;
+ forceRepaintAndFocus(zoomCenterTime, zoomCenter);
+ return;
+ }
+
if (mousePixelPositionX >= 0) {
mousePixelPositionX = e.getX();
mousePixelPositionY = e.getY();
@@ -1248,6 +1367,11 @@ public class TimeLine extends VisPlugin {
zoomCenter = (double) (e.getX() - timeline.getVisibleRect().x) / timeline.getVisibleRect().width;
return;
}
+ if (e.isAltDown()) {
+ /* Pan with mouse */
+ zoomCenterTime = (long) (e.getX()*currentPixelDivisor);
+ return;
+ }
if (popUpToolTip != null) {
popUpToolTip.hide();
@@ -1434,11 +1558,12 @@ public class TimeLine extends VisPlugin {
private void drawMouseTime(Graphics g, long start, long end) {
if (mousePixelPositionX >= 0) {
+ long time = (long) ((double)mousePixelPositionX*currentPixelDivisor);
+ long diff = (long) ((double)Math.abs(mouseDownPixelPositionX-mousePixelPositionX)*currentPixelDivisor);
String str =
- "Time (ms): " +
- ((double)mousePixelPositionX*currentPixelDivisor/Simulation.MILLISECOND) +
- " (" + Math.abs(((double)(mouseDownPixelPositionX - mousePixelPositionX)*currentPixelDivisor/Simulation.MILLISECOND)) + ")";
-
+ "Time (ms): " + (double)time/Simulation.MILLISECOND +
+ " (" + (double)diff/Simulation.MILLISECOND + ")";
+
int h = g.getFontMetrics().getHeight();
int w = g.getFontMetrics().stringWidth(str) + 6;
int y= mousePixelPositionY";
/* Time */
- long time = event.getPoint().x*(long)currentPixelDivisor;
- tooltip += "Time (ms): " + (double)(time/Simulation.MILLISECOND) + "
";
+ long time = (long) (event.getPoint().x*currentPixelDivisor);
+ tooltip += "Time (ms): " + (double)time/Simulation.MILLISECOND + "
";
/* Event */
ArrayList extends MoteEvent> events = null;
@@ -1529,6 +1654,10 @@ public class TimeLine extends VisPlugin {
MoteEvent ev = getFirstIntervalEvent(events, time);
if (ev != null && time >= ev.time) {
tooltip += ev + "
";
+
+ if (ev.details != null) {
+ tooltip += "Details:
" + ev.details;
+ }
}
}
@@ -1610,6 +1739,7 @@ public class TimeLine extends VisPlugin {
abstract class MoteEvent {
MoteEvent prev = null;
MoteEvent next = null;
+ String details = null;
long time;
public MoteEvent(long time) {
this.time = time;
@@ -1684,6 +1814,10 @@ public class TimeLine extends VisPlugin {
super(time);
this.state = ev;
}
+ public RadioRXTXEvent(long time, RXTXRadioEvent ev, String details) {
+ this(time, ev);
+ this.details = details;
+ }
public Color getEventColor() {
if (state == RXTXRadioEvent.IDLE) {
return null;
@@ -1702,7 +1836,7 @@ public class TimeLine extends VisPlugin {
if (state == RXTXRadioEvent.IDLE) {
return "Radio idle from " + time + "
";
} else if (state == RXTXRadioEvent.TRANSMITTING) {
- return "Radio transmitting from " + time + "
";
+ return "Radio transmitting from " + time + "
";
} else if (state == RXTXRadioEvent.RECEIVING) {
return "Radio receiving from " + time + "
";
} else if (state == RXTXRadioEvent.INTERFERED) {
@@ -1720,13 +1854,53 @@ public class TimeLine extends VisPlugin {
return Color.GRAY; /* TODO Implement me */
}
}
+ private final Color[] CHANNEL_COLORS = new Color[] {
+ new Color(200, 200, 200),
+ new Color(200, 200, 255),
+ new Color(200, 255, 200),
+ new Color(200, 255, 255),
+ new Color(255, 200, 200),
+ new Color(255, 255, 200),
+ new Color(255, 255, 255),
+ new Color(255, 220, 200),
+ new Color(220, 255, 220),
+ new Color(255, 200, 255),
+ new Color(200, 200, 200),
+ new Color(200, 200, 255),
+ new Color(200, 255, 200),
+ new Color(200, 255, 255),
+ new Color(255, 200, 200),
+ new Color(255, 255, 200),
+ new Color(255, 255, 255),
+ new Color(255, 220, 200),
+ new Color(220, 255, 220),
+ new Color(255, 200, 255),
+ new Color(200, 200, 200),
+ new Color(200, 200, 255),
+ new Color(200, 255, 200),
+ new Color(200, 255, 255),
+ new Color(255, 200, 200),
+ new Color(255, 255, 200),
+ new Color(255, 255, 255),
+ new Color(255, 220, 200),
+ new Color(220, 255, 220),
+ new Color(255, 200, 255),
+ };
class RadioHWEvent extends MoteEvent {
boolean on;
+ int channel = -1;
public RadioHWEvent(long time, boolean on) {
super(time);
this.on = on;
}
+ public RadioHWEvent(long time, boolean on, int channel) {
+ this(time, on);
+ this.channel = channel;
+ }
public Color getEventColor() {
+ if (on && radioChannels && channel >= 0 && channel < CHANNEL_COLORS.length) {
+ return CHANNEL_COLORS[channel];
+ }
return on?Color.GRAY:null;
}
public String toString() {