diff --git a/tools/cooja/java/se/sics/cooja/plugins/TimeLine.java b/tools/cooja/java/se/sics/cooja/plugins/TimeLine.java index 43d60c9c3..5d257db04 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.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 allStats = new ArrayList(); 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 + "
"; - } else if (state == RadioEvent.TRANSMISSION_FINISHED) { - return "Radio TX finished at " + time + "
"; - } else if (state == RadioEvent.RECEPTION_STARTED) { - return "Radio RX started at " + time + "
"; - } else if (state == RadioEvent.RECEPTION_FINISHED) { - return "Radio RX finished at " + time + "
"; - } else if (state == RadioEvent.RECEPTION_INTERFERED) { - return "Radio reception was interfered at " + time + "
"; + if (state == RXTXRadioEvent.IDLE) { + return "Radio idle from " + time + "
"; + } else if (state == RXTXRadioEvent.TRANSMITTING) { + return "Radio transmitting from " + time + "
"; + } else if (state == RXTXRadioEvent.RECEIVING) { + return "Radio receiving from " + time + "
"; + } else if (state == RXTXRadioEvent.INTERFERED) { + return "Radio interfered from " + time + "
"; + } else { + return "Unknown event
"; } - return "Unknown event
"; } } class RadioChannelEvent extends MoteEvent {