new feature: ability to show log listener's output in the timeline, using the mote output plugin's currently

active filter
This commit is contained in:
Fredrik Osterlind 2013-08-14 12:47:44 +02:00
parent 4811d7f378
commit eb1a147d7d

View file

@ -31,7 +31,6 @@
package se.sics.cooja.plugins; package se.sics.cooja.plugins;
import java.awt.Color; import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Font; import java.awt.Font;
import java.awt.FontMetrics; import java.awt.FontMetrics;
@ -55,7 +54,6 @@ import java.util.Observer;
import javax.swing.AbstractAction; import javax.swing.AbstractAction;
import javax.swing.Action; import javax.swing.Action;
import javax.swing.Box;
import javax.swing.JCheckBox; import javax.swing.JCheckBox;
import javax.swing.JCheckBoxMenuItem; import javax.swing.JCheckBoxMenuItem;
import javax.swing.JComboBox; import javax.swing.JComboBox;
@ -70,7 +68,6 @@ import javax.swing.JPanel;
import javax.swing.JPopupMenu; import javax.swing.JPopupMenu;
import javax.swing.JScrollPane; import javax.swing.JScrollPane;
import javax.swing.JSlider; import javax.swing.JSlider;
import javax.swing.JSplitPane;
import javax.swing.JToolTip; import javax.swing.JToolTip;
import javax.swing.KeyStroke; import javax.swing.KeyStroke;
import javax.swing.Popup; import javax.swing.Popup;
@ -89,7 +86,8 @@ import se.sics.cooja.HasQuickHelp;
import se.sics.cooja.Mote; import se.sics.cooja.Mote;
import se.sics.cooja.Plugin; import se.sics.cooja.Plugin;
import se.sics.cooja.PluginType; import se.sics.cooja.PluginType;
import se.sics.cooja.SimEventCentral.MoteCountListener; import se.sics.cooja.SimEventCentral.LogOutputEvent;
import se.sics.cooja.SimEventCentral.LogOutputListener;
import se.sics.cooja.Simulation; import se.sics.cooja.Simulation;
import se.sics.cooja.VisPlugin; import se.sics.cooja.VisPlugin;
import se.sics.cooja.Watchpoint; import se.sics.cooja.Watchpoint;
@ -132,13 +130,14 @@ public class TimeLine extends VisPlugin implements HasQuickHelp {
private int paintedMoteHeight = EVENT_PIXEL_HEIGHT; private int paintedMoteHeight = EVENT_PIXEL_HEIGHT;
private Simulation simulation; private Simulation simulation;
private MoteCountListener newMotesListener; private LogOutputListener newMotesListener;
/* Expermental features: Use currently active plugin to filter Timeline Log outputs */
private LogListener logEventFilterPlugin = null;
private JScrollPane timelineScrollPane; private JScrollPane timelineScrollPane;
private MoteRuler timelineMoteRuler; private MoteRuler timelineMoteRuler;
private JComponent timeline; private JComponent timeline;
private Box eventCheckboxes;
private JSplitPane splitPane;
private Observer moteHighlightObserver = null; private Observer moteHighlightObserver = null;
private ArrayList<Mote> highlightedMotes = new ArrayList<Mote>(); private ArrayList<Mote> highlightedMotes = new ArrayList<Mote>();
@ -150,13 +149,20 @@ public class TimeLine extends VisPlugin implements HasQuickHelp {
private boolean showRadioRXTX = true; private boolean showRadioRXTX = true;
private boolean showRadioChannels = false; private boolean showRadioChannels = false;
private boolean showRadioHW = true; private boolean showRadioOnoff = true;
private boolean showLEDs = true; private boolean showLeds = true;
private boolean showLogOutputs = false; private boolean showLogOutputs = false;
private boolean showWatchpoints = false; private boolean showWatchpoints = false;
private Point popupLocation = null; private Point popupLocation = null;
private JCheckBox showWatchpointsCheckBox;
private JCheckBox showLogsCheckBox;
private JCheckBox showLedsCheckBox;
private JCheckBox showRadioOnoffCheckbox;
private JCheckBox showRadioChannelsCheckbox;
private JCheckBox showRadioTXRXCheckbox;
/** /**
* @param simulation Simulation * @param simulation Simulation
* @param gui GUI * @param gui GUI
@ -195,149 +201,85 @@ public class TimeLine extends VisPlugin implements HasQuickHelp {
return executionDetails; return executionDetails;
} }
}); });
viewMenu.add(new JCheckBoxMenuItem(radioChannelsAction) {
private static final long serialVersionUID = 6830282466652559714L;
public boolean isSelected() {
return radioChannels;
}
});
fileMenu.add(new JMenuItem(saveDataAction)); fileMenu.add(new JMenuItem(saveDataAction));
fileMenu.add(new JMenuItem(statisticsAction)); fileMenu.add(new JMenuItem(statisticsAction));
editMenu.add(new JMenuItem(clearAction)); editMenu.add(new JMenuItem(clearAction));
JCheckBox eventCheckBox; showRadioTXRXCheckbox = createEventCheckbox("Radio traffic", "Show radio transmissions, receptions, and collisions");
eventCheckBox = createEventCheckbox("Radio traffic", "Show radio transmissions, receptions, and collisions"); showRadioTXRXCheckbox.setName("showRadioRXTX");
eventCheckBox.setSelected(showRadioRXTX); showRadioTXRXCheckbox.addActionListener(new ActionListener() {
eventCheckBox.setName("showRadioRXTX");
eventCheckBox.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
showRadioRXTX = ((JCheckBox) e.getSource()).isSelected(); showRadioRXTX = ((JCheckBox) e.getSource()).isSelected();
recalculateMoteHeight(); recalculateMoteHeight();
} }
}); });
eventsMenu.add(eventCheckBox); eventsMenu.add(showRadioTXRXCheckbox);
eventCheckBox = createEventCheckbox("Radio channel", "Show different radio channels"); showRadioOnoffCheckbox = createEventCheckbox("Radio on/off", "Show radio hardware state");
eventCheckBox.setSelected(showRadioChannels); showRadioOnoffCheckbox.setSelected(showRadioOnoff);
eventCheckBox.setName("showRadioChannels"); showRadioOnoffCheckbox.setName("showRadioHW");
eventCheckBox.addActionListener(new ActionListener() { showRadioOnoffCheckbox.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
showRadioOnoff = ((JCheckBox) e.getSource()).isSelected();
recalculateMoteHeight();
}
});
eventsMenu.add(showRadioOnoffCheckbox);
showRadioChannelsCheckbox = createEventCheckbox("Radio channel", "Show different radio channels");
showRadioChannelsCheckbox.setSelected(showRadioChannels);
showRadioChannelsCheckbox.setName("showRadioChannels");
showRadioChannelsCheckbox.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
showRadioChannels = ((JCheckBox) e.getSource()).isSelected(); showRadioChannels = ((JCheckBox) e.getSource()).isSelected();
recalculateMoteHeight(); recalculateMoteHeight();
} }
}); });
/*eventCheckboxes.add(eventCheckBox);*/ eventsMenu.add(showRadioChannelsCheckbox);
eventsMenu.add(eventCheckBox); showLedsCheckBox = createEventCheckbox("LEDs", "Show LED state");
eventCheckBox = createEventCheckbox("Radio state", "Show radio hardware state"); showLedsCheckBox.setSelected(showLeds);
eventCheckBox.setSelected(showRadioHW); showLedsCheckBox.setName("showLEDs");
eventCheckBox.setName("showRadioHW"); showLedsCheckBox.addActionListener(new ActionListener() {
eventCheckBox.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
showRadioHW = ((JCheckBox) e.getSource()).isSelected(); showLeds = ((JCheckBox) e.getSource()).isSelected();
recalculateMoteHeight(); recalculateMoteHeight();
} }
}); });
eventsMenu.add(eventCheckBox); eventsMenu.add(showLedsCheckBox);
eventCheckBox = createEventCheckbox("LEDs", "Show LED state"); showLogsCheckBox = createEventCheckbox("Log output", "Show mote log output, such as printf()'s");
eventCheckBox.setSelected(showLEDs); showLogsCheckBox.setSelected(showLogOutputs);
eventCheckBox.setName("showLEDs"); showLogsCheckBox.setName("showLogOutput");
eventCheckBox.addActionListener(new ActionListener() { showLogsCheckBox.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
showLEDs = ((JCheckBox) e.getSource()).isSelected();
recalculateMoteHeight();
}
});
eventsMenu.add(eventCheckBox);
eventCheckBox = createEventCheckbox("Log output", "Show mote log output, such as by printf()'s");
eventCheckBox.setSelected(showLogOutputs);
eventCheckBox.setName("showLogOutput");
eventCheckBox.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
showLogOutputs = ((JCheckBox) e.getSource()).isSelected(); showLogOutputs = ((JCheckBox) e.getSource()).isSelected();
/* Check whether there is an active log listener that is used to filter logs */
logEventFilterPlugin = (LogListener) simulation.getGUI().getPlugin(
LogListener.class.getName());
if (showLogOutputs) {
if (logEventFilterPlugin != null) {
logger.info("Filtering shown log outputs by use of " + GUI.getDescriptionOf(LogListener.class) + " plugin");
} else {
logger.info("No active " + GUI.getDescriptionOf(LogListener.class) + " plugin, not filtering log outputs");
}
}
recalculateMoteHeight(); recalculateMoteHeight();
} }
}); });
/*eventCheckboxes.add(eventCheckBox);*/ eventsMenu.add(showLogsCheckBox);
eventCheckBox = createEventCheckbox("Watchpoints", "Show code watchpoints (for MSPSim-based motes)"); showWatchpointsCheckBox = createEventCheckbox("Watchpoints", "Show code watchpoints (for emulated motes)");
eventCheckBox.setSelected(showWatchpoints); showWatchpointsCheckBox.setSelected(showWatchpoints);
eventCheckBox.setName("showWatchpoints"); showWatchpointsCheckBox.setName("showWatchpoints");
eventCheckBox.addActionListener(new ActionListener() { showWatchpointsCheckBox.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
showWatchpoints = ((JCheckBox) e.getSource()).isSelected(); showWatchpoints = ((JCheckBox) e.getSource()).isSelected();
recalculateMoteHeight(); recalculateMoteHeight();
} }
}); });
eventsMenu.add(eventCheckBox); eventsMenu.add(showWatchpointsCheckBox);
/* Box: events to observe */ /* Box: events to observe */
eventCheckboxes = Box.createVerticalBox();
/*
eventCheckboxes.add(new JButton(addMoteAction));
eventCheckboxes.add(new JSeparator());
JCheckBox eventCheckBox;
eventCheckBox = createEventCheckbox("Radio RX/TX", "Show radio transmissions, receptions, and collisions");
eventCheckBox.setSelected(showRadioRXTX);
eventCheckBox.setName("showRadioRXTX");
eventCheckBox.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
showRadioRXTX = ((JCheckBox) e.getSource()).isSelected();
recalculateMoteHeight();
}
});
eventCheckboxes.add(eventCheckBox);
eventCheckBox = createEventCheckbox("Radio channels", "Show different radio channels");
eventCheckBox.setSelected(showRadioChannels);
eventCheckBox.setName("showRadioChannels");
eventCheckBox.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
showRadioChannels = ((JCheckBox) e.getSource()).isSelected();
recalculateMoteHeight();
}
});
eventCheckBox = createEventCheckbox("Radio ON/OFF", "Show radio hardware state");
eventCheckBox.setSelected(showRadioHW);
eventCheckBox.setName("showRadioHW");
eventCheckBox.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
showRadioHW = ((JCheckBox) e.getSource()).isSelected();
recalculateMoteHeight();
}
});
eventCheckboxes.add(eventCheckBox);
eventCheckBox = createEventCheckbox("LEDs", "Show LED state");
eventCheckBox.setSelected(showLEDs);
eventCheckBox.setName("showLEDs");
eventCheckBox.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
showLEDs = ((JCheckBox) e.getSource()).isSelected();
recalculateMoteHeight();
}
});
eventCheckboxes.add(eventCheckBox);
eventCheckBox = createEventCheckbox("Log output", "Show mote log output, such as by printf()'s");
eventCheckBox.setSelected(showLogOutputs);
eventCheckBox.setName("showLogOutput");
eventCheckBox.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
showLogOutputs = ((JCheckBox) e.getSource()).isSelected();
recalculateMoteHeight();
}
});
eventCheckBox = createEventCheckbox("Watchpoints", "Show code watchpoints (for MSPSim-based motes)");
eventCheckBox.setSelected(showWatchpoints);
eventCheckBox.setName("showWatchpoints");
eventCheckBox.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
showWatchpoints = ((JCheckBox) e.getSource()).isSelected();
recalculateMoteHeight();
}
});
eventCheckboxes.add(eventCheckBox);
*/
/* Panel: timeline canvas w. scroll pane and add mote button */ /* Panel: timeline canvas w. scroll pane and add mote button */
timeline = new Timeline(); timeline = new Timeline();
timelineScrollPane = new JScrollPane( timelineScrollPane = new JScrollPane(
@ -350,13 +292,6 @@ public class TimeLine extends VisPlugin implements HasQuickHelp {
timelineScrollPane.setRowHeaderView(timelineMoteRuler); timelineScrollPane.setRowHeaderView(timelineMoteRuler);
timelineScrollPane.setBackground(Color.WHITE); timelineScrollPane.setBackground(Color.WHITE);
splitPane = new JSplitPane(
JSplitPane.HORIZONTAL_SPLIT,
new JScrollPane(eventCheckboxes),
timelineScrollPane
);
splitPane.setOneTouchExpandable(true);
/* Zoom in/out via keyboard*/ /* Zoom in/out via keyboard*/
getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_PLUS, KeyEvent.CTRL_DOWN_MASK), "zoomIn"); getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_PLUS, KeyEvent.CTRL_DOWN_MASK), "zoomIn");
getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_EQUALS, KeyEvent.SHIFT_DOWN_MASK | KeyEvent.CTRL_DOWN_MASK), "zoomIn"); getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_EQUALS, KeyEvent.SHIFT_DOWN_MASK | KeyEvent.CTRL_DOWN_MASK), "zoomIn");
@ -374,14 +309,30 @@ public class TimeLine extends VisPlugin implements HasQuickHelp {
numberMotesWasUpdated(); numberMotesWasUpdated();
/* Automatically add/delete motes */ /* Automatically add/delete motes.
simulation.getEventCentral().addMoteCountListener(newMotesListener = new MoteCountListener() { * This listener also observes mote log outputs. */
simulation.getEventCentral().addLogOutputListener(newMotesListener = new LogOutputListener() {
public void moteWasAdded(Mote mote) { public void moteWasAdded(Mote mote) {
addMote(mote); addMote(mote);
} }
public void moteWasRemoved(Mote mote) { public void moteWasRemoved(Mote mote) {
removeMote(mote); removeMote(mote);
} }
public void removedLogOutput(LogOutputEvent ev) {
}
public void newLogOutput(LogOutputEvent ev) {
/* Log output */
Mote mote = ev.getMote();
LogEvent logEvent = new LogEvent(ev);
/* TODO Optimize */
for (MoteEvents moteEvents: allMoteEvents) {
if (moteEvents.mote == mote) {
moteEvents.addLog(logEvent);
break;
}
}
}
}); });
for (Mote m: simulation.getMotes()) { for (Mote m: simulation.getMotes()) {
addMote(m); addMote(m);
@ -427,6 +378,17 @@ public class TimeLine extends VisPlugin implements HasQuickHelp {
this.setSize(gui.getDesktopPane().getWidth(), 166); this.setSize(gui.getDesktopPane().getWidth(), 166);
} }
public void startPlugin() {
super.startPlugin();
showWatchpointsCheckBox.setSelected(showWatchpoints);
showLogsCheckBox.setSelected(showLogOutputs);
showLedsCheckBox.setSelected(showLeds);
showRadioOnoffCheckbox.setSelected(showRadioOnoff);
showRadioChannelsCheckbox.setSelected(showRadioChannels);
showRadioTXRXCheckbox.setSelected(showRadioRXTX);
}
private JCheckBox createEventCheckbox(String text, String tooltip) { private JCheckBox createEventCheckbox(String text, String tooltip) {
JCheckBox checkBox = new JCheckBox(text, true); JCheckBox checkBox = new JCheckBox(text, true);
checkBox.setToolTipText(tooltip); checkBox.setToolTipText(tooltip);
@ -503,7 +465,7 @@ public class TimeLine extends VisPlugin implements HasQuickHelp {
private static final long serialVersionUID = 7546685285707302865L; private static final long serialVersionUID = 7546685285707302865L;
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
JComboBox source = new JComboBox(); JComboBox<Object> source = new JComboBox<Object>();
source.addItem("All motes"); source.addItem("All motes");
for (Mote m: simulation.getMotes()) { for (Mote m: simulation.getMotes()) {
source.addItem(m); source.addItem(m);
@ -876,8 +838,6 @@ public class TimeLine extends VisPlugin implements HasQuickHelp {
} }
} }
/* TODO Radio channels */
if (radioHW) { if (radioHW) {
for (MoteEvent ev: moteEvents.radioHWEvents) { for (MoteEvent ev: moteEvents.radioHWEvents) {
if (!(ev instanceof RadioHWEvent)) continue; if (!(ev instanceof RadioHWEvent)) continue;
@ -923,8 +883,6 @@ public class TimeLine extends VisPlugin implements HasQuickHelp {
} }
} }
/* TODO Watchpoints */
output.append(stats.toString(logs, leds, radioHW, radioRXTX)); output.append(stats.toString(logs, leds, radioHW, radioRXTX));
} }
@ -976,7 +934,7 @@ public class TimeLine extends VisPlugin implements HasQuickHelp {
}); });
} }
private Action radioLoggerAction = new AbstractAction("Show in Radio Logger") { private Action radioLoggerAction = new AbstractAction("Show in " + GUI.getDescriptionOf(RadioLogger.class)) {
private static final long serialVersionUID = 7690116136861949864L; private static final long serialVersionUID = 7690116136861949864L;
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
if (popupLocation == null) { if (popupLocation == null) {
@ -996,7 +954,7 @@ public class TimeLine extends VisPlugin implements HasQuickHelp {
} }
} }
}; };
private Action logListenerAction = new AbstractAction("Show in Log Listener") { private Action logListenerAction = new AbstractAction("Show in " + GUI.getDescriptionOf(LogListener.class)) {
private static final long serialVersionUID = -8626118368774023257L; private static final long serialVersionUID = -8626118368774023257L;
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
if (popupLocation == null) { if (popupLocation == null) {
@ -1017,7 +975,7 @@ public class TimeLine extends VisPlugin implements HasQuickHelp {
} }
}; };
private Action showInAllAction = new AbstractAction("Show in log listener and radio logger") { private Action showInAllAction = new AbstractAction("Show in " + GUI.getDescriptionOf(LogListener.class) + " and " + GUI.getDescriptionOf(RadioLogger.class)) {
private static final long serialVersionUID = -2458733078524773995L; private static final long serialVersionUID = -2458733078524773995L;
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
logListenerAction.actionPerformed(null); logListenerAction.actionPerformed(null);
@ -1026,20 +984,12 @@ public class TimeLine extends VisPlugin implements HasQuickHelp {
}; };
private boolean executionDetails = false; private boolean executionDetails = false;
private boolean radioChannels = false;
private Action executionDetailsAction = new AbstractAction("Show execution details in tooltips") { private Action executionDetailsAction = new AbstractAction("Show execution details in tooltips") {
private static final long serialVersionUID = -8626118368774023257L; private static final long serialVersionUID = -8626118368774023257L;
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
executionDetails = !executionDetails; 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() { private void numberMotesWasUpdated() {
/* Plugin title */ /* Plugin title */
@ -1101,9 +1051,6 @@ public class TimeLine extends VisPlugin implements HasQuickHelp {
} }
private void addMoteObservers(final 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 */
/* LEDs */ /* LEDs */
final LED moteLEDs = mote.getInterfaces().getLED(); final LED moteLEDs = mote.getInterfaces().getLED();
if (moteLEDs != null) { if (moteLEDs != null) {
@ -1131,69 +1078,59 @@ public class TimeLine extends VisPlugin implements HasQuickHelp {
activeMoteObservers.add(new MoteObservation(mote, moteLEDs, observer)); activeMoteObservers.add(new MoteObservation(mote, moteLEDs, observer));
} }
/* Radio HW, RXTX */ /* Radio OnOff, RXTX, and channels */
final Radio moteRadio = mote.getInterfaces().getRadio(); final Radio moteRadio = mote.getInterfaces().getRadio();
if (moteRadio != null) { if (moteRadio != null) {
RadioChannelEvent startupChannel = new RadioChannelEvent(
simulation.getSimulationTime(), moteRadio.getChannel(), moteRadio.isRadioOn());
moteEvents.addRadioChannel(startupChannel);
RadioHWEvent startupHW = new RadioHWEvent( RadioHWEvent startupHW = new RadioHWEvent(
simulation.getSimulationTime(), moteRadio.isRadioOn()); simulation.getSimulationTime(), moteRadio.isRadioOn());
if (radioChannels) {
startupHW.channel = moteRadio.getChannel();
}
moteEvents.addRadioHW(startupHW); moteEvents.addRadioHW(startupHW);
RadioRXTXEvent startupRXTX = new RadioRXTXEvent( RadioRXTXEvent startupRXTX = new RadioRXTXEvent(
simulation.getSimulationTime(), RXTXRadioEvent.IDLE); simulation.getSimulationTime(), RXTXRadioEvent.IDLE);
moteEvents.addRadioRXTX(startupRXTX); moteEvents.addRadioRXTX(startupRXTX);
Observer observer = new Observer() { Observer observer = new Observer() {
int lastChannel = -1; int lastChannel = -1;
public void update(Observable o, Object arg) { public void update(Observable o, Object arg) {
/* Radio HW events */ RadioEvent radioEv = moteRadio.getLastEvent();
if (radioChannels && moteRadio.getLastEvent() == RadioEvent.UNKNOWN) {
int nowChannel = moteRadio.getChannel();
if (nowChannel == lastChannel) {
return;
}
lastChannel = nowChannel;
String details = null;
if (executionDetails && mote instanceof AbstractEmulatedMote) {
details = ((AbstractEmulatedMote) mote).getExecutionDetails();
if (details != null) {
details = "<br>" + details.replace("\n", "<br>");
}
}
/* Radio channel */
int nowChannel = moteRadio.getChannel();
if (nowChannel != lastChannel) {
lastChannel = nowChannel;
RadioChannelEvent ev = new RadioChannelEvent(
simulation.getSimulationTime(), nowChannel, moteRadio.isRadioOn());
moteEvents.addRadioChannel(ev);
ev.details = details;
}
if (radioEv == RadioEvent.HW_ON ||
radioEv == RadioEvent.HW_OFF) {
RadioHWEvent ev = new RadioHWEvent( RadioHWEvent ev = new RadioHWEvent(
simulation.getSimulationTime(), moteRadio.isRadioOn()); simulation.getSimulationTime(), moteRadio.isRadioOn());
if (radioChannels) {
ev.channel = moteRadio.getChannel();
}
moteEvents.addRadioHW(ev); moteEvents.addRadioHW(ev);
if (executionDetails && mote instanceof AbstractEmulatedMote) { ev.details = details;
String details = ((AbstractEmulatedMote) mote).getExecutionDetails();
if (details != null) {
details = "<br>" + details.replace("\n", "<br>");
ev.details = details;
}
}
return;
}
if (moteRadio.getLastEvent() == RadioEvent.HW_ON || /* Also create another channel event here */
moteRadio.getLastEvent() == RadioEvent.HW_OFF) { lastChannel = nowChannel;
RadioHWEvent ev = new RadioHWEvent( RadioChannelEvent ev2 = new RadioChannelEvent(
simulation.getSimulationTime(), moteRadio.isRadioOn()); simulation.getSimulationTime(), nowChannel, moteRadio.isRadioOn());
if (radioChannels) { ev2.details = details;
ev.channel = moteRadio.getChannel(); moteEvents.addRadioChannel(ev2);
}
moteEvents.addRadioHW(ev);
if (executionDetails && mote instanceof AbstractEmulatedMote) {
String details = ((AbstractEmulatedMote) mote).getExecutionDetails();
if (details != null) {
details = "<br>" + details.replace("\n", "<br>");
ev.details = details;
}
}
return;
} }
/* Radio RXTX events */ /* Radio RXTX events */
RadioEvent radioEv = moteRadio.getLastEvent();
if (radioEv == RadioEvent.TRANSMISSION_STARTED || if (radioEv == RadioEvent.TRANSMISSION_STARTED ||
radioEv == RadioEvent.TRANSMISSION_FINISHED || radioEv == RadioEvent.TRANSMISSION_FINISHED ||
radioEv == RadioEvent.RECEPTION_STARTED || radioEv == RadioEvent.RECEPTION_STARTED ||
@ -1221,15 +1158,7 @@ public class TimeLine extends VisPlugin implements HasQuickHelp {
moteEvents.addRadioRXTX(ev); moteEvents.addRadioRXTX(ev);
if (executionDetails && mote instanceof AbstractEmulatedMote) { ev.details = details;
String details = ((AbstractEmulatedMote) mote).getExecutionDetails();
if (details != null) {
details = "<br>" + details.replace("\n", "<br>");
ev.details = details;
}
}
return;
} }
} }
@ -1239,7 +1168,7 @@ public class TimeLine extends VisPlugin implements HasQuickHelp {
activeMoteObservers.add(new MoteObservation(mote, moteRadio, observer)); activeMoteObservers.add(new MoteObservation(mote, moteRadio, observer));
} }
/* XXX Experimental: Watchpoints */ /* Watchpoints */
if (mote instanceof WatchpointMote) { if (mote instanceof WatchpointMote) {
final WatchpointMote watchpointMote = ((WatchpointMote)mote); final WatchpointMote watchpointMote = ((WatchpointMote)mote);
WatchpointListener listener = new WatchpointListener() { WatchpointListener listener = new WatchpointListener() {
@ -1317,10 +1246,10 @@ public class TimeLine extends VisPlugin implements HasQuickHelp {
if (showRadioChannels) { if (showRadioChannels) {
h += EVENT_PIXEL_HEIGHT; h += EVENT_PIXEL_HEIGHT;
} }
if (showRadioHW) { if (showRadioOnoff) {
h += EVENT_PIXEL_HEIGHT; h += EVENT_PIXEL_HEIGHT;
} }
if (showLEDs) { if (showLeds) {
h += 3*LED_PIXEL_HEIGHT; h += 3*LED_PIXEL_HEIGHT;
} }
if (showLogOutputs) { if (showLogOutputs) {
@ -1378,11 +1307,11 @@ public class TimeLine extends VisPlugin implements HasQuickHelp {
element = new Element("showRadioChannels"); element = new Element("showRadioChannels");
config.add(element); config.add(element);
} }
if (showRadioHW) { if (showRadioOnoff) {
element = new Element("showRadioHW"); element = new Element("showRadioHW");
config.add(element); config.add(element);
} }
if (showLEDs) { if (showLeds) {
element = new Element("showLEDs"); element = new Element("showLEDs");
config.add(element); config.add(element);
} }
@ -1399,14 +1328,6 @@ public class TimeLine extends VisPlugin implements HasQuickHelp {
element = new Element("executionDetails"); element = new Element("executionDetails");
config.add(element); config.add(element);
} }
if (radioChannels) {
element = new Element("radioChannels");
config.add(element);
}
element = new Element("split");
element.addContent("" + splitPane.getDividerLocation());
config.add(element);
element = new Element("zoomfactor"); element = new Element("zoomfactor");
element.addContent("" + currentPixelDivisor); element.addContent("" + currentPixelDivisor);
@ -1418,13 +1339,12 @@ public class TimeLine extends VisPlugin implements HasQuickHelp {
public boolean setConfigXML(Collection<Element> configXML, boolean visAvailable) { public boolean setConfigXML(Collection<Element> configXML, boolean visAvailable) {
showRadioRXTX = false; showRadioRXTX = false;
showRadioChannels = false; showRadioChannels = false;
showRadioHW = false; showRadioOnoff = false;
showLEDs = false; showLeds = false;
showLogOutputs = false; showLogOutputs = false;
showWatchpoints = false; showWatchpoints = false;
executionDetails = false; executionDetails = false;
radioChannels = false;
/* Remove already registered motes */ /* Remove already registered motes */
MoteEvents[] allMoteEventsArr = allMoteEvents.toArray(new MoteEvents[0]); MoteEvents[] allMoteEventsArr = allMoteEvents.toArray(new MoteEvents[0]);
@ -1442,19 +1362,16 @@ public class TimeLine extends VisPlugin implements HasQuickHelp {
} else if ("showRadioChannels".equals(name)) { } else if ("showRadioChannels".equals(name)) {
showRadioChannels = true; showRadioChannels = true;
} else if ("showRadioHW".equals(name)) { } else if ("showRadioHW".equals(name)) {
showRadioHW = true; showRadioOnoff = true;
} else if ("showLEDs".equals(name)) { } else if ("showLEDs".equals(name)) {
showLEDs = true; showLeds = true;
} else if ("showLogOutput".equals(name)) { } else if ("showLogOutput".equals(name)) {
showLogOutputs = true; showLogOutputs = true;
} else if ("showWatchpoints".equals(name)) { } else if ("showWatchpoints".equals(name)) {
showWatchpoints = true; showWatchpoints = true;
} else if ("executionDetails".equals(name)) { } else if ("executionDetails".equals(name)) {
executionDetails = true; 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)) { } else if ("zoom".equals(name)) {
/* NB: Historically this is a one-based not zero-based index */ /* NB: Historically this is a one-based not zero-based index */
final int zl = Integer.parseInt(element.getText())-1; final int zl = Integer.parseInt(element.getText())-1;
@ -1466,22 +1383,6 @@ public class TimeLine extends VisPlugin implements HasQuickHelp {
} }
} }
/* XXX HACK: Update checkboxes according to config */
for (Component c: eventCheckboxes.getComponents()) {
if (c.getName() == "showRadioRXTX") {
((JCheckBox)c).setSelected(showRadioRXTX);
} else if (c.getName() == "showRadioChannels") {
((JCheckBox)c).setSelected(showRadioChannels);
} else if (c.getName() == "showRadioHW") {
((JCheckBox)c).setSelected(showRadioHW);
} else if (c.getName() == "showLEDs") {
((JCheckBox)c).setSelected(showLEDs);
} else if (c.getName() == "showLogOutput") {
((JCheckBox)c).setSelected(showLogOutputs);
} else if (c.getName() == "showWatchpoints") {
((JCheckBox)c).setSelected(showWatchpoints);
}
}
recalculateMoteHeight(); recalculateMoteHeight();
return true; return true;
@ -1505,28 +1406,9 @@ public class TimeLine extends VisPlugin implements HasQuickHelp {
/* Popup menu */ /* Popup menu */
final JPopupMenu popupMenu = new JPopupMenu(); final JPopupMenu popupMenu = new JPopupMenu();
/* popupMenu.add(new JMenuItem(addMoteAction));
popupMenu.addSeparator();
popupMenu.add(new JMenuItem(zoomInAction));
popupMenu.add(new JMenuItem(zoomOutAction));
popupMenu.add(new JMenuItem(zoomSliderAction));
popupMenu.addSeparator();
popupMenu.add(new JMenuItem(saveDataAction));
popupMenu.add(new JMenuItem(statisticsAction));
popupMenu.add(new JMenuItem(clearAction));
popupMenu.addSeparator();
*/
/* JMenu focusMenu = new JMenu("Show in");*/
popupMenu.add(new JMenuItem(showInAllAction)); popupMenu.add(new JMenuItem(showInAllAction));
/* focusMenu.addSeparator(); */
popupMenu.add(new JMenuItem(logListenerAction)); popupMenu.add(new JMenuItem(logListenerAction));
popupMenu.add(new JMenuItem(radioLoggerAction)); popupMenu.add(new JMenuItem(radioLoggerAction));
/* popupMenu.add(focusMenu);*/
JMenu advancedMenu = new JMenu("Advanced"); JMenu advancedMenu = new JMenu("Advanced");
advancedMenu.add(new JCheckBoxMenuItem(executionDetailsAction) { advancedMenu.add(new JCheckBoxMenuItem(executionDetailsAction) {
@ -1535,13 +1417,6 @@ public class TimeLine extends VisPlugin implements HasQuickHelp {
return executionDetails; return executionDetails;
} }
}); });
advancedMenu.add(new JCheckBoxMenuItem(radioChannelsAction) {
private static final long serialVersionUID = 6830282466652559714L;
public boolean isSelected() {
return radioChannels;
}
});
/* popupMenu.add(advancedMenu);*/
addMouseListener(new MouseAdapter() { addMouseListener(new MouseAdapter() {
long lastClick = -1; long lastClick = -1;
@ -1741,7 +1616,7 @@ public class TimeLine extends VisPlugin implements HasQuickHelp {
/*logger.info("Painting interval: " + intervalStart + " -> " + intervalEnd);*/ /*logger.info("Painting interval: " + intervalStart + " -> " + intervalEnd);*/
if (bounds.x > Integer.MAX_VALUE - 1000) { if (bounds.x > Integer.MAX_VALUE - 1000) {
/* TODO Strange bounds */ /* Strange bounds */
return; return;
} }
@ -1779,14 +1654,14 @@ public class TimeLine extends VisPlugin implements HasQuickHelp {
} }
lineHeightOffset += EVENT_PIXEL_HEIGHT; lineHeightOffset += EVENT_PIXEL_HEIGHT;
} }
if (showRadioHW) { if (showRadioOnoff) {
MoteEvent firstEvent = getFirstIntervalEvent(allMoteEvents.get(mIndex).radioHWEvents, intervalStart); MoteEvent firstEvent = getFirstIntervalEvent(allMoteEvents.get(mIndex).radioHWEvents, intervalStart);
if (firstEvent != null) { if (firstEvent != null) {
firstEvent.paintInterval(g, lineHeightOffset, intervalEnd); firstEvent.paintInterval(g, lineHeightOffset, intervalEnd);
} }
lineHeightOffset += EVENT_PIXEL_HEIGHT; lineHeightOffset += EVENT_PIXEL_HEIGHT;
} }
if (showLEDs) { if (showLeds) {
MoteEvent firstEvent = getFirstIntervalEvent(allMoteEvents.get(mIndex).ledEvents, intervalStart); MoteEvent firstEvent = getFirstIntervalEvent(allMoteEvents.get(mIndex).ledEvents, intervalStart);
if (firstEvent != null) { if (firstEvent != null) {
firstEvent.paintInterval(g, lineHeightOffset, intervalEnd); firstEvent.paintInterval(g, lineHeightOffset, intervalEnd);
@ -1931,13 +1806,13 @@ public class TimeLine extends VisPlugin implements HasQuickHelp {
} }
evMatched++; evMatched++;
} }
if (showRadioHW) { if (showRadioOnoff) {
if (evMatched == evMouse) { if (evMatched == evMouse) {
events = allMoteEvents.get(mote).radioHWEvents; events = allMoteEvents.get(mote).radioHWEvents;
} }
evMatched++; evMatched++;
} }
if (showLEDs) { if (showLeds) {
if (evMatched == evMouse) { if (evMatched == evMouse) {
events = allMoteEvents.get(mote).ledEvents; events = allMoteEvents.get(mote).ledEvents;
} }
@ -2167,16 +2042,7 @@ public class TimeLine extends VisPlugin implements HasQuickHelp {
} }
} }
} }
class RadioChannelEvent extends MoteEvent {
public RadioChannelEvent(long time) {
super(time);
}
public Color getEventColor() {
return Color.GRAY; /* TODO Implement me */
}
}
/* TODO Which colors? */
private final static Color[] CHANNEL_COLORS = new Color[] { private final static Color[] CHANNEL_COLORS = new Color[] {
Color.decode("0x008080"), Color.decode("0x808080"), Color.decode("0xC00000"), Color.decode("0x008080"), Color.decode("0x808080"), Color.decode("0xC00000"),
Color.decode("0x000020"), Color.decode("0x202000"), Color.decode("0x200020"), Color.decode("0x000020"), Color.decode("0x202000"), Color.decode("0x200020"),
@ -2190,28 +2056,47 @@ public class TimeLine extends VisPlugin implements HasQuickHelp {
Color.decode("0x00FF00"), Color.decode("0x0000FF"), Color.decode("0xFFFF00"), Color.decode("0x00FF00"), Color.decode("0x0000FF"), Color.decode("0xFFFF00"),
Color.decode("0xFF00FF"), Color.decode("0x808000"), Color.decode("0x800080"), Color.decode("0xFF00FF"), Color.decode("0x808000"), Color.decode("0x800080"),
}; };
class RadioChannelEvent extends MoteEvent {
int channel;
boolean radioOn;
public RadioChannelEvent(long time, int channel, boolean radioOn) {
super(time);
this.channel = channel;
this.radioOn = radioOn;
}
public Color getEventColor() {
if (channel >= 0) {
if (!radioOn) {
return null;
}
Color c = CHANNEL_COLORS[channel % CHANNEL_COLORS.length];
return c;
}
return null;
}
public String toString() {
String str = "Radio channel " + channel + "<br>";
return str;
}
}
class RadioHWEvent extends MoteEvent { class RadioHWEvent extends MoteEvent {
boolean on; boolean on;
int channel = -1;
public RadioHWEvent(long time, boolean on) { public RadioHWEvent(long time, boolean on) {
super(time); super(time);
this.on = on; this.on = on;
} }
public RadioHWEvent(long time, boolean on, int channel) { public RadioHWEvent(long time, boolean on, int channel) {
this(time, on); this(time, on);
this.channel = channel;
} }
public Color getEventColor() { public Color getEventColor() {
if (on && radioChannels && channel >= 0 && channel < CHANNEL_COLORS.length) { if (on) {
return CHANNEL_COLORS[channel]; return Color.GRAY;
} }
return on?Color.GRAY:null; return null;
} }
public String toString() { public String toString() {
String str = "Radio HW was turned " + (on?"on":"off") + " at time " + time + "<br>"; String str = "Radio HW was turned " + (on?"on":"off") + "<br>";
if (channel > 0) {
str += "Radio channel: " + channel;
}
return str; return str;
} }
} }
@ -2298,11 +2183,56 @@ public class TimeLine extends VisPlugin implements HasQuickHelp {
} }
} }
class LogEvent extends MoteEvent { class LogEvent extends MoteEvent {
public LogEvent(long time) { LogOutputEvent logEvent;
super(time); public LogEvent(LogOutputEvent ev) {
super(ev.getTime());
this.logEvent = ev;
} }
public Color getEventColor() { public Color getEventColor() {
return Color.GRAY; /* TODO Implement me */ if (logEventFilterPlugin != null) {
/* Ask log listener for event color to use */
return logEventFilterPlugin.getColorOfEntry(logEvent);
}
return Color.GRAY;
}
/* Default paint method */
public void paintInterval(Graphics g, int lineHeightOffset, long end) {
LogEvent ev = this;
while (ev != null && ev.time < end) {
/* Ask active log listener whether this should be filtered */
if (logEventFilterPlugin != null) {
boolean show = logEventFilterPlugin.filterWouldAccept(ev.logEvent);
if (!show) {
/* Skip painting event */
ev = (LogEvent) ev.next;
continue;
}
}
Color color = ev.getEventColor();
if (color == null) {
/* Skip painting event */
ev = (LogEvent) ev.next;
continue;
}
g.setColor(color);
g.fillRect(
(int)(ev.time/currentPixelDivisor), lineHeightOffset,
4, EVENT_PIXEL_HEIGHT
);
g.setColor(Color.BLACK);
g.fillRect(
(int)(ev.time/currentPixelDivisor), lineHeightOffset,
1, EVENT_PIXEL_HEIGHT
);
ev = (LogEvent) ev.next;
}
}
public String toString() {
return "Mote " + logEvent.getMote() + " says:<br>" + logEvent.getMessage() + "<br>";
} }
} }
class WatchpointEvent extends MoteEvent { class WatchpointEvent extends MoteEvent {
@ -2434,7 +2364,6 @@ public class TimeLine extends VisPlugin implements HasQuickHelp {
} }
lastRadioChannelEvent = ev; lastRadioChannelEvent = ev;
/* TODO XXX Requires MSPSim changes */
radioChannelEvents.add(ev); radioChannelEvents.add(ev);
} }
public void addRadioHW(RadioHWEvent ev) { public void addRadioHW(RadioHWEvent ev) {
@ -2530,19 +2459,23 @@ public class TimeLine extends VisPlugin implements HasQuickHelp {
"<b>Timeline</b>" + "<b>Timeline</b>" +
"<p>The timeline shows simulation events over time. " + "<p>The timeline shows simulation events over time. " +
"The timeline can be used to inspect activities of individual nodes as well as interactions between nodes." + "The timeline can be used to inspect activities of individual nodes as well as interactions between nodes." +
"<p>For each mote, simulation events are shown on a colored line. Different colors correspond to different events. For more information about a particular event, hover the mouse above it." + "<p>For each mote, simulation events are shown on a colored line. Different colors correspond to different events. For more information about a particular event, mouse click it." +
"<p>The <i>Events</i> menu control what event types are shown in the timeline. " + "<p>The <i>Events</i> menu control what event types are shown in the timeline. " +
"Currently, four event types are supported (see below). " + "Currently, six event types are supported (see below). " +
"<p>All motes are by default shown in the timeline. Motes can be removed from the timeline by right-clicking the node ID on the left." + "<p>All motes are by default shown in the timeline. Motes can be removed from the timeline by right-clicking the node ID on the left." +
"<p>To display a vertical time marker on the timeline, press and hold the mouse on the time ruler (top)." + "<p>To display a vertical time marker on the timeline, press and hold the mouse on the time ruler (top)." +
"<p>For more options for a given event, right-click the mouse for a popup menu." + "<p>For more options for a given event, right-click the mouse for a popup menu." +
"<p><b>Radio traffic</b>" + "<p><b>Radio traffic</b>" +
"<br>Shows radio traffic events. Transmissions are painted blue, receptions are green, and interfered radios are red." + "<br>Shows radio traffic events. Transmissions are painted blue, receptions are green, and interfered radios are red." +
"<p><b>Radio state</b>" + "<p><b>Radio channel</b>" +
"<br>Shows the current radio channel by colors." +
"<p><b>Radio on/off</b>" +
"<br>Shows whether the mote radio is on or off. When gray, the radio is on." + "<br>Shows whether the mote radio is on or off. When gray, the radio is on." +
"<p><b>LEDs</b>" + "<p><b>LEDs</b>" +
"<br>Shows LED state: red, green, and blue. (Assumes all mote types have exactly three LEDs.)" + "<br>Shows LED state: red, green, and blue. (Assumes all mote types have exactly three LEDs.)" +
"<p><b>Log outputs</b>" +
"<br>Shows log outputs, as also shown in " + GUI.getDescriptionOf(LogListener.class) +
"<p><b>Watchpoints</b>" + "<p><b>Watchpoints</b>" +
"<br>Shows triggered watchpoints, currently only supported by MSPSim-based motes. To add watchpoints, use the Msp Code Watcher plugin."; "<br>Shows triggered watchpoints, currently only supported by emulated motes. To add watchpoints, use the Msp Code Watcher plugin.";
} }
} }