* 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:
parent
d27a2dfb5b
commit
0a9e9dea59
1 changed files with 162 additions and 64 deletions
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue