* observing motes' radio states ("is transmitting") instead of radio events ("started transmitting").

previously the last radio event was logged in the Timeline, which could be very confusing when a radio was interfered while transmitting a packet: it looked like the radio stopped transmitting. (this behavior did not, however, affect the simulation output but only the timeline)

* added Radio Logger menu option shortcut, for quickly moving from the Timeline to the Radio Logger plugin
* minor ui fixes regarding event tooltips: tooltip info appear at mouse clicks
This commit is contained in:
fros4943 2009-11-25 15:39:57 +00:00
parent d27a2dfb5b
commit 0a9e9dea59
1 changed files with 162 additions and 64 deletions

View File

@ -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.16 2009/11/23 15:25:13 fros4943 Exp $
* $Id: TimeLine.java,v 1.17 2009/11/25 15:39:57 fros4943 Exp $
*/
package se.sics.cooja.plugins;
@ -67,6 +67,9 @@ import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JToolTip;
import javax.swing.Popup;
import javax.swing.PopupFactory;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
@ -275,7 +278,7 @@ public class TimeLine extends VisPlugin {
removeMote(m);
}
};
private Action addMoteAction = new AbstractAction() {
private Action addMoteAction = new AbstractAction("Add motes to timeline") {
public void actionPerformed(ActionEvent e) {
JComboBox source = new JComboBox();
@ -309,7 +312,7 @@ public class TimeLine extends VisPlugin {
}
};
private Action zoomInAction = new AbstractAction() {
private Action zoomInAction = new AbstractAction("Zoom in") {
public void actionPerformed(ActionEvent e) {
Rectangle r = timeline.getVisibleRect();
int pixelX = r.x + r.width/2;
@ -342,7 +345,7 @@ public class TimeLine extends VisPlugin {
}
};
private Action zoomOutAction = new AbstractAction() {
private Action zoomOutAction = new AbstractAction("Zoom out") {
public void actionPerformed(ActionEvent e) {
Rectangle r = timeline.getVisibleRect();
int pixelX = r.x + r.width/2;
@ -377,7 +380,7 @@ public class TimeLine extends VisPlugin {
/**
* Save logged raw data to file for post-processing.
*/
private Action saveDataAction = new AbstractAction() {
private Action saveDataAction = new AbstractAction("Save raw data to file") {
public void actionPerformed(ActionEvent e) {
JFileChooser fc = new JFileChooser();
int returnVal = fc.showSaveDialog(GUI.getTopParentContainer());
@ -447,31 +450,32 @@ public class TimeLine extends VisPlugin {
long onTimeRedLED = 0, onTimeGreenLED = 0, onTimeBlueLED = 0;
int nrLogs = 0;
long radioOn = 0;
int nrRX = 0, nrTX = 0, nrCollisions = 0;
long onTimeRX = 0, onTimeTX = 0, onTimeInterfered = 0;
public String toString() {
return
"Mote: " + mote + "\n" +
"Mote: " + (mote!=null?mote:"ALL") + "\n" +
"LED red ontime:\t" + onTimeRedLED + "us = " + 100.0*((double)onTimeRedLED/simulation.getSimulationTime()) + "%\n" +
"LED green ontime:\t" + onTimeGreenLED + "us = " + 100.0*((double)onTimeGreenLED/simulation.getSimulationTime()) + "%\n" +
"LED blue ontime:\t" + onTimeBlueLED + "us = " + 100.0*((double)onTimeBlueLED/simulation.getSimulationTime()) + "%\n" +
"Log messages: " + nrLogs + "\n" +
"Radio ontime:\t" + radioOn + "us = " + 100.0*((double)radioOn/simulation.getSimulationTime()) + "%\n" +
"Radio RX: " + nrRX + "\n" +
"Radio TX: " + nrTX + "\n" +
"Radio collisions: " + nrCollisions + "\n"
;
"Radio RX time:\t" + onTimeRX + "us = " + 100.0*((double)onTimeRX/simulation.getSimulationTime()) + "%\n" +
"Radio TX time:\t" + onTimeTX + "us = " + 100.0*((double)onTimeTX/simulation.getSimulationTime()) + "%\n" +
"Radio interfered time:\t" + onTimeInterfered + "us = " + 100.0*((double)onTimeInterfered/simulation.getSimulationTime()) + "%\n";
}
}
private Action statisticsAction = new AbstractAction() {
private Action statisticsAction = new AbstractAction("Print statistics to console") {
public void actionPerformed(ActionEvent e) {
if (simulation.isRunning()) {
simulation.stopSimulation();
}
/* Process all events (per mote basis) */
ArrayList<MoteStatistics> allStats = new ArrayList<MoteStatistics>();
for (MoteEvents moteEvents: allMoteEvents) {
MoteStatistics stats = new MoteStatistics();
allStats.add(stats);
stats.mote = moteEvents.mote;
for (MoteEvent ev: moteEvents.ledEvents) {
@ -532,15 +536,28 @@ public class TimeLine extends VisPlugin {
for (MoteEvent ev: moteEvents.radioRXTXEvents) {
if (!(ev instanceof RadioRXTXEvent)) continue;
RadioRXTXEvent rxtxEvent = (RadioRXTXEvent) ev;
if (rxtxEvent.state == RadioEvent.TRANSMISSION_STARTED) {
stats.nrTX++;
if (rxtxEvent.state == RXTXRadioEvent.IDLE) {
continue;
}
if (rxtxEvent.state == RadioEvent.RECEPTION_STARTED) {
stats.nrRX++;
long diff;
if (rxtxEvent.next == null) {
diff = (simulation.getSimulationTime() - rxtxEvent.time);
} else {
diff = (rxtxEvent.next.time - rxtxEvent.time);
}
if (rxtxEvent.state == RadioEvent.RECEPTION_INTERFERED) {
stats.nrCollisions++;
if (rxtxEvent.state == RXTXRadioEvent.TRANSMITTING) {
stats.onTimeTX += diff;
continue;
}
if (rxtxEvent.state == RXTXRadioEvent.INTERFERED) {
stats.onTimeInterfered += diff;
continue;
}
if (rxtxEvent.state == RXTXRadioEvent.RECEIVING) {
stats.onTimeRX += diff;
continue;
}
}
@ -549,6 +566,56 @@ public class TimeLine extends VisPlugin {
logger.info(stats.toString());
}
/* Summary */
MoteStatistics all = new MoteStatistics();
for (MoteStatistics stats: allStats) {
all.onTimeRedLED += stats.onTimeRedLED;
all.onTimeGreenLED += stats.onTimeGreenLED;
all.onTimeBlueLED += stats.onTimeBlueLED;
all.radioOn += stats.radioOn;
all.onTimeRX += stats.onTimeRX;
all.onTimeTX += stats.onTimeTX;
all.onTimeInterfered += stats.onTimeInterfered;
}
logger.info("SUMMARY");
logger.info(all.toString());
}
};
public void trySelectTime(final long time) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
/* Visible rectangle */
int newX = (int) (time / currentPixelDivisor);
int w = timeline.getVisibleRect().width;
Rectangle r = new Rectangle(
newX - w/2, 0,
w, 1
);
timeline.scrollRectToVisible(r);
/* Time ruler */
mousePixelPositionX = newX;
mouseDownPixelPositionX = newX;
mousePixelPositionY = timeline.getHeight();
repaint();
}
});
}
private Action radioLoggerAction = new AbstractAction("to Radio Logger") {
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;
}
/* Select simulation time */
plugin.trySelectTime(popupLocation.x*currentPixelDivisor);
}
};
@ -649,7 +716,7 @@ public class TimeLine extends VisPlugin {
simulation.getSimulationTime(), moteRadio.isReceiverOn());
moteEvents.addRadioHW(startupHW);
RadioRXTXEvent startupRXTX = new RadioRXTXEvent(
simulation.getSimulationTime(), RadioEvent.UNKNOWN);
simulation.getSimulationTime(), RXTXRadioEvent.IDLE);
moteEvents.addRadioRXTX(startupRXTX);
Observer observer = new Observer() {
public void update(Observable o, Object arg) {
@ -664,14 +731,29 @@ public class TimeLine extends VisPlugin {
}
/* Radio RXTX events */
if (moteRadio.getLastEvent() == RadioEvent.TRANSMISSION_STARTED ||
moteRadio.getLastEvent() == RadioEvent.TRANSMISSION_FINISHED ||
moteRadio.getLastEvent() == RadioEvent.RECEPTION_STARTED ||
moteRadio.getLastEvent() == RadioEvent.RECEPTION_INTERFERED ||
moteRadio.getLastEvent() == RadioEvent.RECEPTION_FINISHED) {
RadioRXTXEvent ev = new RadioRXTXEvent(
simulation.getSimulationTime(), moteRadio.getLastEvent());
RadioEvent radioEv = moteRadio.getLastEvent();
if (radioEv == RadioEvent.TRANSMISSION_STARTED ||
radioEv == RadioEvent.TRANSMISSION_FINISHED ||
radioEv == RadioEvent.RECEPTION_STARTED ||
radioEv == RadioEvent.RECEPTION_INTERFERED ||
radioEv == RadioEvent.RECEPTION_FINISHED) {
RadioRXTXEvent ev;
/* Override events, instead show state */
if (moteRadio.isTransmitting()) {
ev = new RadioRXTXEvent(
simulation.getSimulationTime(), RXTXRadioEvent.TRANSMITTING);
} else if (moteRadio.isInterfered()) {
ev = new RadioRXTXEvent(
simulation.getSimulationTime(), RXTXRadioEvent.INTERFERED);
} else if (moteRadio.isReceiving()) {
ev = new RadioRXTXEvent(
simulation.getSimulationTime(), RXTXRadioEvent.RECEIVING);
} else {
ev = new RadioRXTXEvent(
simulation.getSimulationTime(), RXTXRadioEvent.IDLE);
}
moteEvents.addRadioRXTX(ev);
return;
}
@ -900,10 +982,10 @@ public class TimeLine extends VisPlugin {
return true;
}
private int mousePixelPositionX = -1;
private int mousePixelPositionY = -1;
private int mouseDownPixelPositionX = -1;
class Timeline extends JComponent {
private int mousePixelPositionX = -1;
private int mousePixelPositionY = -1;
private int mouseDownPixelPositionX = -1;
public Timeline() {
setLayout(null);
@ -915,29 +997,22 @@ public class TimeLine extends VisPlugin {
/* Popup menu */
final JPopupMenu popupMenu = new JPopupMenu();
JMenuItem addMoteItem = new JMenuItem(addMoteAction);
addMoteItem.setText("Add motes to timeline");
popupMenu.add(addMoteItem);
popupMenu.add(new JMenuItem(addMoteAction));
popupMenu.addSeparator();
JMenuItem zoomInItem = new JMenuItem(zoomInAction);
zoomInItem.setText("Zoom in");
popupMenu.add(zoomInItem);
JMenuItem zoomOutItem = new JMenuItem(zoomOutAction);
zoomOutItem.setText("Zoom out");
popupMenu.add(zoomOutItem);
popupMenu.add(new JMenuItem(zoomInAction));
popupMenu.add(new JMenuItem(zoomOutAction));
popupMenu.addSeparator();
JMenuItem saveItem = new JMenuItem(saveDataAction);
saveItem.setText("Save raw data to file");
popupMenu.add(saveItem);
popupMenu.add(new JMenuItem(saveDataAction));
popupMenu.add(new JMenuItem(statisticsAction));
JMenuItem statisticsItem = new JMenuItem(statisticsAction);
statisticsItem.setText("Print statistics to console");
popupMenu.add(statisticsItem);
popupMenu.addSeparator();
popupMenu.add(new JMenuItem(radioLoggerAction));
addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
@ -962,6 +1037,7 @@ public class TimeLine extends VisPlugin {
}
private MouseAdapter mouseAdapter = new MouseAdapter() {
private Popup popUpToolTip = null;
public void mouseDragged(MouseEvent e) {
super.mouseDragged(e);
if (mousePixelPositionX >= 0) {
@ -971,14 +1047,31 @@ public class TimeLine extends VisPlugin {
}
}
public void mousePressed(MouseEvent e) {
if (popUpToolTip != null) {
popUpToolTip.hide();
popUpToolTip = null;
}
if (e.getPoint().getY() < FIRST_MOTE_PIXEL_OFFSET) {
mousePixelPositionX = e.getX();
mouseDownPixelPositionX = e.getX();
mousePixelPositionY = e.getY();
repaint();
} else {
/* Trigger tooltip */
JToolTip t = timeline.createToolTip();
t.setTipText(timeline.getToolTipText(e));
if (t.getTipText() == null || t.getTipText().equals("")) {
return;
}
popUpToolTip = PopupFactory.getSharedInstance().getPopup(timeline, t, e.getXOnScreen(), e.getYOnScreen());
popUpToolTip.show();
}
}
public void mouseReleased(MouseEvent e) {
if (popUpToolTip != null) {
popUpToolTip.hide();
popUpToolTip = null;
}
super.mouseReleased(e);
mousePixelPositionX = -1;
repaint();
@ -1354,36 +1447,41 @@ public class TimeLine extends VisPlugin {
return "No events has been captured yet";
}
}
public enum RXTXRadioEvent {
IDLE, RECEIVING, TRANSMITTING, INTERFERED
}
class RadioRXTXEvent extends MoteEvent {
RadioEvent state = null;
public RadioRXTXEvent(long time, RadioEvent ev) {
RXTXRadioEvent state = null;
public RadioRXTXEvent(long time, RXTXRadioEvent ev) {
super(time);
this.state = ev;
}
public Color getEventColor() {
if (state == RadioEvent.TRANSMISSION_STARTED) {
if (state == RXTXRadioEvent.IDLE) {
return null;
} else if (state == RXTXRadioEvent.TRANSMITTING) {
return Color.BLUE;
} else if (state == RadioEvent.RECEPTION_STARTED) {
} else if (state == RXTXRadioEvent.RECEIVING) {
return Color.GREEN;
} else if (state == RadioEvent.RECEPTION_INTERFERED) {
} else if (state == RXTXRadioEvent.INTERFERED) {
return Color.RED;
} else {
logger.fatal("Unknown RXTX event");
return null;
}
}
public String toString() {
if (state == RadioEvent.TRANSMISSION_STARTED) {
return "Radio TX started at " + time + "<br>";
} else if (state == RadioEvent.TRANSMISSION_FINISHED) {
return "Radio TX finished at " + time + "<br>";
} else if (state == RadioEvent.RECEPTION_STARTED) {
return "Radio RX started at " + time + "<br>";
} else if (state == RadioEvent.RECEPTION_FINISHED) {
return "Radio RX finished at " + time + "<br>";
} else if (state == RadioEvent.RECEPTION_INTERFERED) {
return "Radio reception was interfered at " + time + "<br>";
if (state == RXTXRadioEvent.IDLE) {
return "Radio idle from " + time + "<br>";
} else if (state == RXTXRadioEvent.TRANSMITTING) {
return "Radio transmitting from " + time + "<br>";
} else if (state == RXTXRadioEvent.RECEIVING) {
return "Radio receiving from " + time + "<br>";
} else if (state == RXTXRadioEvent.INTERFERED) {
return "Radio interfered from " + time + "<br>";
} else {
return "Unknown event<br>";
}
return "Unknown event<br>";
}
}
class RadioChannelEvent extends MoteEvent {