From 39aeb2f15077ed6bda667c2973bc43b84de96fe8 Mon Sep 17 00:00:00 2001 From: fros4943 Date: Thu, 27 Aug 2009 13:59:47 +0000 Subject: [PATCH] visualizer skin updates: broke out traffic visualization from the udgm radio medium skin (the stand-alone traffic skin can be used with any radio medium), added a simple background grid skin and a mote position skin --- .../plugins/skins/AddressVisualizerSkin.java | 8 +- .../plugins/skins/GridVisualizerSkin.java | 125 +++++++ .../cooja/plugins/skins/IDVisualizerSkin.java | 10 +- .../plugins/skins/LEDVisualizerSkin.java | 10 +- .../plugins/skins/LogVisualizerSkin.java | 8 +- .../plugins/skins/PositionVisualizerSkin.java | 157 ++++++++ .../plugins/skins/TrafficVisualizerSkin.java | 300 ++++++++++++++++ .../plugins/skins/UDGMVisualizerSkin.java | 336 ++++++------------ 8 files changed, 722 insertions(+), 232 deletions(-) create mode 100644 tools/cooja/java/se/sics/cooja/plugins/skins/GridVisualizerSkin.java create mode 100644 tools/cooja/java/se/sics/cooja/plugins/skins/PositionVisualizerSkin.java create mode 100644 tools/cooja/java/se/sics/cooja/plugins/skins/TrafficVisualizerSkin.java diff --git a/tools/cooja/java/se/sics/cooja/plugins/skins/AddressVisualizerSkin.java b/tools/cooja/java/se/sics/cooja/plugins/skins/AddressVisualizerSkin.java index 8ed22b1f5..ac57ca7bb 100644 --- a/tools/cooja/java/se/sics/cooja/plugins/skins/AddressVisualizerSkin.java +++ b/tools/cooja/java/se/sics/cooja/plugins/skins/AddressVisualizerSkin.java @@ -26,7 +26,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: AddressVisualizerSkin.java,v 1.1 2009/04/20 16:15:47 fros4943 Exp $ + * $Id: AddressVisualizerSkin.java,v 1.2 2009/08/27 13:59:47 fros4943 Exp $ */ package se.sics.cooja.plugins.skins; @@ -112,8 +112,12 @@ public class AddressVisualizerSkin implements VisualizerSkin { return null; } - public void paintSkin(Graphics g) { + public void paintBeforeMotes(Graphics g) { + } + + public void paintAfterMotes(Graphics g) { FontMetrics fm = g.getFontMetrics(); + g.setColor(Color.BLACK); /* Paint last output below motes */ Mote[] allMotes = simulation.getMotes(); diff --git a/tools/cooja/java/se/sics/cooja/plugins/skins/GridVisualizerSkin.java b/tools/cooja/java/se/sics/cooja/plugins/skins/GridVisualizerSkin.java new file mode 100644 index 000000000..aeec2d078 --- /dev/null +++ b/tools/cooja/java/se/sics/cooja/plugins/skins/GridVisualizerSkin.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2009, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: GridVisualizerSkin.java,v 1.1 2009/08/27 13:59:48 fros4943 Exp $ + */ + +package se.sics.cooja.plugins.skins; + +import java.awt.Color; +import java.awt.Graphics; + +import org.apache.log4j.Logger; + +import se.sics.cooja.ClassDescription; +import se.sics.cooja.Mote; +import se.sics.cooja.Simulation; +import se.sics.cooja.interfaces.Position; +import se.sics.cooja.plugins.Visualizer; +import se.sics.cooja.plugins.VisualizerSkin; + +/** + * Background grid visualizer skin. + * + * @author Fredrik Osterlind + */ +@ClassDescription("10m background grid") +public class GridVisualizerSkin implements VisualizerSkin { + private static Logger logger = Logger.getLogger(GridVisualizerSkin.class); + + private Visualizer visualizer = null; + + public void setActive(Simulation simulation, Visualizer vis) { + this.visualizer = vis; + } + + public void setInactive() { + } + + public Color[] getColorOf(Mote mote) { + return null; + } + + public void paintBeforeMotes(Graphics g) { + + /* Background grid every 10 meters */ + Position upperLeft = + visualizer.transformPixelToPosition(-10, -10); + upperLeft.setCoordinates( + ((int)(upperLeft.getXCoordinate()/10))*10, + ((int)(upperLeft.getYCoordinate()/10))*10, + 0); + Position lowerRight = + visualizer.transformPixelToPosition(visualizer.getWidth(), visualizer.getHeight()); + lowerRight.setCoordinates( + ((int)(lowerRight.getXCoordinate()/10))*10, + ((int)(lowerRight.getYCoordinate()/10))*10, + 0); + + if ((lowerRight.getXCoordinate() - upperLeft.getXCoordinate())/10.0 < 200 && + (lowerRight.getYCoordinate() - upperLeft.getYCoordinate())/10.0 < 200) { + /* X axis */ + for (double x = upperLeft.getXCoordinate(); x <= lowerRight.getXCoordinate(); x += 10.0) { + int pixel = visualizer.transformPositionToPixel(x, 0, 0).x; + if (x % 100 == 0) { + g.setColor(Color.GRAY); + } else { + g.setColor(Color.LIGHT_GRAY); + } + g.drawLine( + pixel, + 0, + pixel, + visualizer.getHeight() + ); + } + /* Y axis */ + for (double y = upperLeft.getYCoordinate(); y <= lowerRight.getYCoordinate(); y += 10.0) { + int pixel = visualizer.transformPositionToPixel(0, y, 0).y; + if (y % 100 == 0) { + g.setColor(Color.GRAY); + } else { + g.setColor(Color.LIGHT_GRAY); + } + g.drawLine( + 0, + pixel, + visualizer.getWidth(), + pixel + ); + } + } + } + + public void paintAfterMotes(Graphics g) { + } + + public Visualizer getVisualizer() { + return visualizer; + } +} diff --git a/tools/cooja/java/se/sics/cooja/plugins/skins/IDVisualizerSkin.java b/tools/cooja/java/se/sics/cooja/plugins/skins/IDVisualizerSkin.java index 2b9ea32f5..db2bcac62 100644 --- a/tools/cooja/java/se/sics/cooja/plugins/skins/IDVisualizerSkin.java +++ b/tools/cooja/java/se/sics/cooja/plugins/skins/IDVisualizerSkin.java @@ -26,7 +26,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: IDVisualizerSkin.java,v 1.1 2009/04/14 15:40:26 fros4943 Exp $ + * $Id: IDVisualizerSkin.java,v 1.2 2009/08/27 13:59:47 fros4943 Exp $ */ package se.sics.cooja.plugins.skins; @@ -70,9 +70,13 @@ public class IDVisualizerSkin implements VisualizerSkin { return null; } - public void paintSkin(Graphics g) { - FontMetrics fm = g.getFontMetrics(); + public void paintBeforeMotes(Graphics g) { + } + public void paintAfterMotes(Graphics g) { + FontMetrics fm = g.getFontMetrics(); + g.setColor(Color.BLACK); + /* Paint ID inside each mote */ Mote[] allMotes = simulation.getMotes(); for (Mote mote: allMotes) { diff --git a/tools/cooja/java/se/sics/cooja/plugins/skins/LEDVisualizerSkin.java b/tools/cooja/java/se/sics/cooja/plugins/skins/LEDVisualizerSkin.java index 93b94d66d..ec19d4a00 100644 --- a/tools/cooja/java/se/sics/cooja/plugins/skins/LEDVisualizerSkin.java +++ b/tools/cooja/java/se/sics/cooja/plugins/skins/LEDVisualizerSkin.java @@ -26,7 +26,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: LEDVisualizerSkin.java,v 1.3 2009/04/20 16:16:25 fros4943 Exp $ + * $Id: LEDVisualizerSkin.java,v 1.4 2009/08/27 13:59:48 fros4943 Exp $ */ package se.sics.cooja.plugins.skins; @@ -103,7 +103,10 @@ public class LEDVisualizerSkin implements VisualizerSkin { return null; } - public void paintSkin(Graphics g) { + public void paintBeforeMotes(Graphics g) { + } + + public void paintAfterMotes(Graphics g) { /* Paint LEDs left of each mote */ Mote[] allMotes = simulation.getMotes(); for (Mote mote: allMotes) { @@ -140,10 +143,7 @@ public class LEDVisualizerSkin implements VisualizerSkin { } else { g.drawRect(x, y, 7, 4); } - } - - g.setColor(Color.BLACK); } public Visualizer getVisualizer() { diff --git a/tools/cooja/java/se/sics/cooja/plugins/skins/LogVisualizerSkin.java b/tools/cooja/java/se/sics/cooja/plugins/skins/LogVisualizerSkin.java index 1fe771df8..0a411b2e9 100644 --- a/tools/cooja/java/se/sics/cooja/plugins/skins/LogVisualizerSkin.java +++ b/tools/cooja/java/se/sics/cooja/plugins/skins/LogVisualizerSkin.java @@ -26,7 +26,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: LogVisualizerSkin.java,v 1.2 2009/04/14 15:40:26 fros4943 Exp $ + * $Id: LogVisualizerSkin.java,v 1.3 2009/08/27 13:59:47 fros4943 Exp $ */ package se.sics.cooja.plugins.skins; @@ -103,8 +103,12 @@ public class LogVisualizerSkin implements VisualizerSkin { return null; } - public void paintSkin(Graphics g) { + public void paintBeforeMotes(Graphics g) { + } + + public void paintAfterMotes(Graphics g) { FontMetrics fm = g.getFontMetrics(); + g.setColor(Color.BLACK); /* Paint last output below motes */ Mote[] allMotes = simulation.getMotes(); diff --git a/tools/cooja/java/se/sics/cooja/plugins/skins/PositionVisualizerSkin.java b/tools/cooja/java/se/sics/cooja/plugins/skins/PositionVisualizerSkin.java new file mode 100644 index 000000000..2c9160c06 --- /dev/null +++ b/tools/cooja/java/se/sics/cooja/plugins/skins/PositionVisualizerSkin.java @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2009, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: PositionVisualizerSkin.java,v 1.1 2009/08/27 13:59:47 fros4943 Exp $ + */ + +package se.sics.cooja.plugins.skins; + +import java.awt.Color; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Point; +import java.util.Observable; +import java.util.Observer; + +import org.apache.log4j.Logger; + +import se.sics.cooja.ClassDescription; +import se.sics.cooja.Mote; +import se.sics.cooja.Simulation; +import se.sics.cooja.SimEventCentral.MoteCountListener; +import se.sics.cooja.interfaces.Position; +import se.sics.cooja.plugins.Visualizer; +import se.sics.cooja.plugins.VisualizerSkin; + +/** + * Visualizer skin for mote positions. + * + * Paints the three dimensional mote position on the right-hand side of the mote. + * + * @author Fredrik Osterlind + */ +@ClassDescription("Positions") +public class PositionVisualizerSkin implements VisualizerSkin { + private static Logger logger = Logger.getLogger(PositionVisualizerSkin.class); + + private Simulation simulation = null; + private Visualizer visualizer = null; + + private Observer positionObserver = new Observer() { + public void update(Observable obs, Object obj) { + visualizer.repaint(); + } + }; + private MoteCountListener simObserver = new MoteCountListener() { + public void moteWasAdded(Mote mote) { + Position p = mote.getInterfaces().getPosition(); + if (p != null) { + p.addObserver(positionObserver); + } + } + public void moteWasRemoved(Mote mote) { + Position p = mote.getInterfaces().getPosition(); + if (p != null) { + p.deleteObserver(positionObserver); + } + } + }; + + public void setActive(Simulation simulation, Visualizer vis) { + this.simulation = simulation; + this.visualizer = vis; + + simulation.getEventCentral().addMoteCountListener(simObserver); + for (Mote m: simulation.getMotes()) { + simObserver.moteWasAdded(m); + } + } + + public void setInactive() { + simulation.getEventCentral().removeMoteCountListener(simObserver); + for (Mote m: simulation.getMotes()) { + simObserver.moteWasRemoved(m); + } + } + + public Color[] getColorOf(Mote mote) { + return null; + } + + public void paintBeforeMotes(Graphics g) { + } + + public void paintAfterMotes(Graphics g) { + FontMetrics fm = g.getFontMetrics(); + g.setColor(Color.BLACK); + + /* Paint last output below motes */ + Mote[] allMotes = simulation.getMotes(); + for (Mote mote: allMotes) { + Position pos = mote.getInterfaces().getPosition(); + Point pixel = visualizer.transformPositionToPixel(pos); + + String msg = ""; + String[] parts; + + /* X */ + parts = String.valueOf(pos.getXCoordinate()).split("\\."); + if (parts[0].length() >= 4) { + msg += parts[0]; + } else { + msg += String.valueOf(pos.getXCoordinate()).substring(0, 5); + } + + /* Y */ + msg += ", "; + parts = String.valueOf(pos.getYCoordinate()).split("\\."); + if (parts[0].length() >= 4) { + msg += parts[0]; + } else { + msg += String.valueOf(pos.getYCoordinate()).substring(0, 5); + } + + /* Z */ + if (pos.getZCoordinate() != 0) { + msg += ", "; + parts = String.valueOf(pos.getXCoordinate()).split("\\."); + if (parts[0].length() >= 4) { + msg += parts[0]; + } else { + msg += String.valueOf(pos.getXCoordinate()).substring(0, 5); + } + } + + g.drawString(msg, pixel.x + Visualizer.MOTE_RADIUS + 4, pixel.y + 4); + } + } + + public Visualizer getVisualizer() { + return visualizer; + } +} diff --git a/tools/cooja/java/se/sics/cooja/plugins/skins/TrafficVisualizerSkin.java b/tools/cooja/java/se/sics/cooja/plugins/skins/TrafficVisualizerSkin.java new file mode 100644 index 000000000..a0ce4f933 --- /dev/null +++ b/tools/cooja/java/se/sics/cooja/plugins/skins/TrafficVisualizerSkin.java @@ -0,0 +1,300 @@ +/* + * Copyright (c) 2009, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: TrafficVisualizerSkin.java,v 1.1 2009/08/27 13:59:48 fros4943 Exp $ + */ + +package se.sics.cooja.plugins.skins; + +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Point; +import java.util.ArrayDeque; +import java.util.Observable; +import java.util.Observer; + +import javax.swing.Box; +import javax.swing.JLabel; + +import org.apache.log4j.Logger; + +import se.sics.cooja.ClassDescription; +import se.sics.cooja.Mote; +import se.sics.cooja.RadioConnection; +import se.sics.cooja.Simulation; +import se.sics.cooja.SimEventCentral.MoteCountListener; +import se.sics.cooja.interfaces.Position; +import se.sics.cooja.interfaces.Radio; +import se.sics.cooja.plugins.Visualizer; +import se.sics.cooja.plugins.VisualizerSkin; +import se.sics.cooja.plugins.Visualizer.SimulationMenuAction; +import se.sics.cooja.radiomediums.AbstractRadioMedium; + +/** + * Radio traffic visualizer skin. + * + * Transmitting motes are painted blue. Receiving motes are painted green. + * Interfered motes are painted red. Motes without radios are painted gray. All + * other motes are painted white. + * + * In contrast to the {@link UDGMVisualizerSkin}, this skin listens to all mote + * radios, not to the radio medium. The radio traffic skin also displays a + * history. + * + * @see UDGMVisualizerSkin + * @author Fredrik Osterlind + */ +@ClassDescription("Radio traffic") +public class TrafficVisualizerSkin implements VisualizerSkin { + private static Logger logger = Logger.getLogger(TrafficVisualizerSkin.class); + + private static final boolean DRAW_ARROWS = true; + private static final Color COLOR_HISTORY = new Color(100, 100, 100, 100); + + private Simulation simulation = null; + private Visualizer visualizer = null; + + private Box counters; + + private final static int HISTORY_SIZE = 8; + private boolean showHistory = false; + private ArrayDeque history = new ArrayDeque(); + + private AbstractRadioMedium radioMedium; + private Observer radioObserver, radioMediumObserver; + + public void setActive(Simulation simulation, Visualizer vis) { + if (!(simulation.getRadioMedium() instanceof AbstractRadioMedium)) { + logger.fatal("Radio medium type not supported: " + simulation.getRadioMedium()); + return; + } + this.radioMedium = (AbstractRadioMedium) simulation.getRadioMedium(); + this.simulation = simulation; + this.visualizer = vis; + + final JLabel txCounter = new JLabel("TX: " + radioMedium.COUNTER_TX); + final JLabel rxCounter = new JLabel("RX: " + radioMedium.COUNTER_RX); + final JLabel interferedCounter = new JLabel("INT: " + radioMedium.COUNTER_INTERFERED); + + counters = Box.createHorizontalBox(); + counters.add(txCounter); + counters.add(Box.createHorizontalStrut(10)); + counters.add(rxCounter); + counters.add(Box.createHorizontalStrut(10)); + counters.add(interferedCounter); + + visualizer.getCurrentCanvas().add(counters); + + /* Start observing radio medium and radios */ + radioMedium.addRadioMediumObserver(radioMediumObserver = new Observer() { + public void update(Observable obs, Object obj) { + txCounter.setText("TX: " + radioMedium.COUNTER_TX); + rxCounter.setText("RX: " + radioMedium.COUNTER_RX); + interferedCounter.setText("INT: " + + radioMedium.COUNTER_INTERFERED); + + if (showHistory) { + RadioConnection[] past = radioMedium.getLastTickConnections(); + for (RadioConnection con: past) { + history.add(con); + } + while (history.size() > HISTORY_SIZE) { + history.removeFirst(); + } + } + visualizer.repaint(); + } + }); + radioObserver = new Observer() { + public void update(Observable o, Object arg) { + visualizer.repaint(); + } + }; + simulation.getEventCentral().addMoteCountListener(new MoteCountListener() { + public void moteWasAdded(Mote mote) { + Radio r = mote.getInterfaces().getRadio(); + if (r != null) { + r.addObserver(radioObserver); + } + } + public void moteWasRemoved(Mote mote) { + Radio r = mote.getInterfaces().getRadio(); + if (r != null) { + r.deleteObserver(radioObserver); + } + history.clear(); + } + }); + for (Mote mote: simulation.getMotes()) { + Radio r = mote.getInterfaces().getRadio(); + if (r != null) { + r.addObserver(radioObserver); + } + } + + /* Register menu actions */ + visualizer.registerSimulationMenuAction(ToggleHistoryAction.class); + } + + public void setInactive() { + if (simulation == null) { + /* Skin was never activated */ + return; + } + + visualizer.getCurrentCanvas().remove(counters); + + /* Stop observing radio medium and radios */ + radioMedium.deleteRadioMediumObserver(radioMediumObserver); + for (Mote mote: simulation.getMotes()) { + Radio r = mote.getInterfaces().getRadio(); + if (r != null) { + r.addObserver(radioObserver); + } + } + + /* Unregister menu actions */ + visualizer.unregisterSimulationMenuAction(ToggleHistoryAction.class); + } + + public Color[] getColorOf(Mote mote) { + if (simulation == null) { + /* Skin was never activated */ + return null; + } + + Radio moteRadio = mote.getInterfaces().getRadio(); + if (moteRadio == null) { + return null; + } + + if (!moteRadio.isReceiverOn()) { + return new Color[] { Color.GRAY }; + } + + if (moteRadio.isTransmitting()) { + return new Color[] { Color.BLUE }; + } + + if (moteRadio.isInterfered()) { + return new Color[] { Color.RED }; + } + + if (moteRadio.isReceiving()) { + return new Color[] { Color.GREEN }; + } + + return null; + } + + public void paintBeforeMotes(Graphics g) { + } + + public void paintAfterMotes(Graphics g) { + if (simulation == null) { + /* Skin was never activated */ + return; + } + + if (showHistory) { + /* Paint history in gray */ + RadioConnection[] historyArr = history.toArray(new RadioConnection[0]); + for (RadioConnection conn : historyArr) { + g.setColor(COLOR_HISTORY); + Radio source = conn.getSource(); + Point sourcePoint = visualizer.transformPositionToPixel(source.getPosition()); + for (Radio destRadio : conn.getDestinations()) { + Position destPos = destRadio.getPosition(); + Point destPoint = visualizer.transformPositionToPixel(destPos); + g.drawLine(sourcePoint.x, sourcePoint.y, destPoint.x, destPoint.y); + } + } + } + + /* Paint active connections in black */ + RadioConnection[] conns = radioMedium.getActiveConnections(); + if (conns != null) { + g.setColor(Color.BLACK); + for (RadioConnection conn : conns) { + Radio source = conn.getSource(); + Point sourcePoint = visualizer.transformPositionToPixel(source.getPosition()); + for (Radio destRadio : conn.getDestinations()) { + if (destRadio == null) { + continue; + } + Position destPos = destRadio.getPosition(); + Point destPoint = visualizer.transformPositionToPixel(destPos); + g.drawLine(sourcePoint.x, sourcePoint.y, destPoint.x, destPoint.y); + + /* Draw arrows */ + if (DRAW_ARROWS) { + Point centerPoint = new Point( + destPoint.x/2 + sourcePoint.x/2, + destPoint.y/2 + sourcePoint.y/2 + ); + int startAngle = (int) (-180 * Math.atan2(destPoint.y - sourcePoint.y, destPoint.x - sourcePoint.x)/Math.PI - 90); + g.drawArc(centerPoint.x-5, centerPoint.y-5, 10, 10, startAngle, 180); + } + } + } + } + } + + public static class ToggleHistoryAction implements SimulationMenuAction { + public boolean isEnabled(Visualizer visualizer, Simulation simulation) { + return true; + } + + public String getDescription(Visualizer visualizer, Simulation simulation) { + VisualizerSkin[] skins = visualizer.getCurrentSkins(); + boolean showing = false; + for (VisualizerSkin skin: skins) { + if (skin instanceof TrafficVisualizerSkin) { + showing = ((TrafficVisualizerSkin)skin).showHistory; + } + } + if (showing) { + return "Hide traffic history"; + } + return "Show traffic history"; + } + + public void doAction(Visualizer visualizer, Simulation simulation) { + VisualizerSkin[] skins = visualizer.getCurrentSkins(); + for (VisualizerSkin skin: skins) { + if (skin instanceof TrafficVisualizerSkin) { + ((TrafficVisualizerSkin)skin).showHistory = !((TrafficVisualizerSkin)skin).showHistory; + visualizer.repaint(); + } + } + } + }; + + public Visualizer getVisualizer() { + return visualizer; + } +} diff --git a/tools/cooja/java/se/sics/cooja/plugins/skins/UDGMVisualizerSkin.java b/tools/cooja/java/se/sics/cooja/plugins/skins/UDGMVisualizerSkin.java index a7bb17484..964f8f2d5 100644 --- a/tools/cooja/java/se/sics/cooja/plugins/skins/UDGMVisualizerSkin.java +++ b/tools/cooja/java/se/sics/cooja/plugins/skins/UDGMVisualizerSkin.java @@ -26,20 +26,20 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: UDGMVisualizerSkin.java,v 1.6 2009/04/20 18:09:54 fros4943 Exp $ + * $Id: UDGMVisualizerSkin.java,v 1.7 2009/08/27 13:59:47 fros4943 Exp $ */ package se.sics.cooja.plugins.skins; import java.awt.Color; import java.awt.Graphics; -import java.awt.Graphics2D; import java.awt.Point; +import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; -import java.util.Observable; -import java.util.Observer; +import javax.swing.BorderFactory; +import javax.swing.Box; import javax.swing.JLabel; import javax.swing.JSpinner; import javax.swing.SpinnerNumberModel; @@ -50,7 +50,6 @@ import org.apache.log4j.Logger; import se.sics.cooja.ClassDescription; import se.sics.cooja.Mote; -import se.sics.cooja.RadioConnection; import se.sics.cooja.Simulation; import se.sics.cooja.interfaces.Position; import se.sics.cooja.interfaces.Radio; @@ -60,17 +59,15 @@ import se.sics.cooja.plugins.Visualizer.SimulationMenuAction; import se.sics.cooja.radiomediums.UDGM; /** - * Visualizer skin for Unit Disk Graph radio medium. - * - * Allows a user to change the collective TX/interference ranges, - * and the TX/RX success ratio. - * - * Transmitting motes are painted blue. XXXXXXXX - * Receiving motes are painted green. - * Interfered motes are painted red. - * Motes without radios are painted gray. - * All other motes are painted white. - * + * Visualizer skin for configuring the Unit Disk Graph radio medium (UDGM). + * + * Allows a user to change the collective TX/interference ranges, and the TX/RX + * success ratio. + * + * To also see radio traffic, this skin can be combined with {@link + * TrafficVisualizerSkin()}. + * + * @see TrafficVisualizerSkin * @see UDGM * @author Fredrik Osterlind */ @@ -78,38 +75,27 @@ import se.sics.cooja.radiomediums.UDGM; public class UDGMVisualizerSkin implements VisualizerSkin { private static Logger logger = Logger.getLogger(UDGMVisualizerSkin.class); - private static final boolean DRAW_ARROWS = true; - + private static Color COLOR_TX = new Color(0, 255, 0, 100); + private static Color COLOR_INT = new Color(50, 50, 50, 100); + private Simulation simulation = null; private Visualizer visualizer = null; private UDGM radioMedium = null; - private Observer radioMediumObserver; - - private JLabel txCounter = null; - private JLabel rxCounter = null; - private JLabel interferedCounter = null; - - private JSpinner txRangeSpinner = null; - private JSpinner interferenceRangeSpinner = null; - private JSpinner successRatioTxSpinner = null; - private JSpinner successRatioRxSpinner = null; private Mote selectedMote = null; - private MouseListener selectMoteMouseListener = new MouseListener() { - public void mouseExited(MouseEvent e) { } - public void mouseEntered(MouseEvent e) { } - public void mouseReleased(MouseEvent e) { } - public void mouseClicked(MouseEvent e) { } + private Box top, ratioRX, ratioTX, rangeTX, rangeINT; + private MouseListener selectMoteMouseListener = new MouseAdapter() { public void mousePressed(MouseEvent e) { Mote[] motes = visualizer.findMotesAtPosition(e.getX(), e.getY()); if (motes == null || motes.length == 0) { selectedMote = null; - txRangeSpinner.setVisible(false); - interferenceRangeSpinner.setVisible(false); - successRatioTxSpinner.setVisible(false); - successRatioRxSpinner.setVisible(false); + rangeTX.setVisible(false); + rangeINT.setVisible(false); + ratioRX.setVisible(false); + ratioTX.setVisible(false); + top.setVisible(false); visualizer.repaint(); return; } @@ -152,16 +138,16 @@ public class UDGMVisualizerSkin implements VisualizerSkin { successRatioRxModel.setMaximum(new Double(1.0)); JSpinner.NumberEditor editor; - txRangeSpinner = new JSpinner(transmissionModel); + final JSpinner txRangeSpinner = new JSpinner(transmissionModel); editor = new JSpinner.NumberEditor(txRangeSpinner, "0m"); txRangeSpinner.setEditor(editor); - interferenceRangeSpinner = new JSpinner(interferenceModel); + final JSpinner interferenceRangeSpinner = new JSpinner(interferenceModel); editor = new JSpinner.NumberEditor(interferenceRangeSpinner, "0m"); interferenceRangeSpinner.setEditor(editor); - successRatioTxSpinner = new JSpinner(successRatioTxModel); + final JSpinner successRatioTxSpinner = new JSpinner(successRatioTxModel); editor = new JSpinner.NumberEditor(successRatioTxSpinner, "0.0%"); successRatioTxSpinner.setEditor(editor); - successRatioRxSpinner = new JSpinner(successRatioRxModel); + final JSpinner successRatioRxSpinner = new JSpinner(successRatioRxModel); editor = new JSpinner.NumberEditor(successRatioRxSpinner, "0.0%"); successRatioRxSpinner.setEditor(editor); @@ -206,11 +192,6 @@ public class UDGMVisualizerSkin implements VisualizerSkin { } }); - txRangeSpinner.setVisible(false); - interferenceRangeSpinner.setVisible(false); - successRatioTxSpinner.setVisible(false); - successRatioRxSpinner.setVisible(false); - /* Register mouse listener */ visualizer.getCurrentCanvas().addMouseListener(selectMoteMouseListener); @@ -218,35 +199,43 @@ public class UDGMVisualizerSkin implements VisualizerSkin { visualizer.registerSimulationMenuAction(RangeMenuAction.class); visualizer.registerSimulationMenuAction(SuccessRatioMenuAction.class); - /* Add (currently invisible) spinners */ - txCounter = new JLabel("TX: " + radioMedium.COUNTER_TX); - rxCounter = new JLabel("RX: " + radioMedium.COUNTER_RX); - interferedCounter = new JLabel("INT: " + + radioMedium.COUNTER_INTERFERED); - visualizer.getCurrentCanvas().add(txCounter); - visualizer.getCurrentCanvas().add(rxCounter); - visualizer.getCurrentCanvas().add(interferedCounter); + /* UI components */ + top = Box.createVerticalBox(); + top.setBorder(BorderFactory.createCompoundBorder( + BorderFactory.createLineBorder(Color.BLACK), + BorderFactory.createEmptyBorder(0, 3, 0, 3))); + top.setOpaque(true); + top.setBackground(Color.LIGHT_GRAY); - visualizer.getCurrentCanvas().add(txRangeSpinner); - visualizer.getCurrentCanvas().add(interferenceRangeSpinner); - visualizer.getCurrentCanvas().add(successRatioTxSpinner); - visualizer.getCurrentCanvas().add(successRatioRxSpinner); + rangeTX = Box.createHorizontalBox(); + rangeTX.add(new JLabel("TX range:")); + rangeTX.add(Box.createHorizontalStrut(5)); + rangeTX.add(txRangeSpinner); + rangeINT = Box.createHorizontalBox(); + rangeINT.add(new JLabel("INT range:")); + rangeINT.add(Box.createHorizontalStrut(5)); + rangeINT.add(interferenceRangeSpinner); + ratioTX = Box.createHorizontalBox(); + ratioTX.add(new JLabel("TX ratio:")); + ratioTX.add(Box.createHorizontalStrut(5)); + ratioTX.add(successRatioTxSpinner); + ratioRX = Box.createHorizontalBox(); + ratioRX.add(new JLabel("RX ratio:")); + ratioRX.add(Box.createHorizontalStrut(5)); + ratioRX.add(successRatioRxSpinner); - /* Start observing radio medium */ - radioMedium.addRadioMediumObserver(radioMediumObserver = new Observer() { - public void update(Observable obs, Object obj) { - if (txCounter != null) { - txCounter.setText("TX: " + radioMedium.COUNTER_TX); - } - if (rxCounter != null) { - rxCounter.setText("RX: " + radioMedium.COUNTER_RX); - } - if (interferedCounter != null) { - interferedCounter.setText("INT: " + + radioMedium.COUNTER_INTERFERED); - } + rangeTX.setVisible(false); + rangeINT.setVisible(false); + ratioTX.setVisible(false); + ratioRX.setVisible(false); + top.setVisible(false); - visualizer.repaint(); - } - }); + top.add(rangeTX); + top.add(rangeINT); + top.add(ratioTX); + top.add(ratioRX); + + visualizer.getCurrentCanvas().add(top); } public void setInactive() { @@ -258,20 +247,8 @@ public class UDGMVisualizerSkin implements VisualizerSkin { /* Remove mouse listener */ visualizer.getCurrentCanvas().removeMouseListener(selectMoteMouseListener); - /* Stop observing radio medium */ - radioMedium.deleteRadioMediumObserver(radioMediumObserver); - radioMediumObserver = null; - radioMedium = null; - - visualizer.getCurrentCanvas().remove(txCounter); - visualizer.getCurrentCanvas().remove(rxCounter); - visualizer.getCurrentCanvas().remove(interferedCounter); - - /* Remove spinners */ - visualizer.getCurrentCanvas().remove(txRangeSpinner); - visualizer.getCurrentCanvas().remove(interferenceRangeSpinner); - visualizer.getCurrentCanvas().remove(successRatioTxSpinner); - visualizer.getCurrentCanvas().remove(successRatioRxSpinner); + /* Remove spinners etc */ + visualizer.getCurrentCanvas().remove(top); /* Unregister menu actions */ visualizer.unregisterSimulationMenuAction(RangeMenuAction.class); @@ -279,155 +256,72 @@ public class UDGMVisualizerSkin implements VisualizerSkin { } public Color[] getColorOf(Mote mote) { - if (simulation == null) { - /* Skin was never activated */ - return null; - } - - if (mote.getState() == Mote.State.DEAD) { - return null; - } - - Radio moteRadio = mote.getInterfaces().getRadio(); - if (moteRadio == null) { - return null; - } - - if (selectedMote != null && mote == selectedMote) { + if (mote == selectedMote) { return new Color[] { Color.CYAN }; } - - if (!moteRadio.isReceiverOn()) { - return new Color[] { Color.GRAY }; - } - - if (moteRadio.isTransmitting()) { - return new Color[] { Color.BLUE }; - } - - if (moteRadio.isInterfered()) { - return new Color[] { Color.RED }; - } - - if (moteRadio.isReceiving()) { - return new Color[] { Color.GREEN }; - } - return null; } - private static Color COLOR_INTERFERENCE = new Color( - Color.DARK_GRAY.getRed(), - Color.DARK_GRAY.getGreen(), - Color.DARK_GRAY.getBlue(), - 100 - ); - private static Color COLOR_TX = new Color( - Color.GREEN.getRed(), - Color.GREEN.getGreen(), - Color.GREEN.getBlue(), - 100 - ); - public void paintSkin(Graphics g) { - if (simulation == null) { - /* Skin was never activated */ + public void paintBeforeMotes(Graphics g) { + if (simulation == null + || selectedMote == null + || selectedMote.getInterfaces().getRadio() == null) { return; } - /* Paint transmission and interference range for select mote */ - if (selectedMote != null) { - Graphics2D g2 = (Graphics2D) g; - Position motePos = selectedMote.getInterfaces().getPosition(); + /* Paint transmission and interference range for selected mote */ + Position motePos = selectedMote.getInterfaces().getPosition(); - Point pixelCoord = visualizer.transformPositionToPixel(motePos); - int x = pixelCoord.x; - int y = pixelCoord.y; + Point pixelCoord = visualizer.transformPositionToPixel(motePos); + int x = pixelCoord.x; + int y = pixelCoord.y; - // Fetch current output power indicator (scale with as percent) - if (selectedMote.getInterfaces().getRadio() != null) { - Radio selectedRadio = selectedMote.getInterfaces().getRadio(); - double moteInterferenceRange = - radioMedium.INTERFERENCE_RANGE - * ((double) selectedRadio.getCurrentOutputPowerIndicator() - / (double) selectedRadio.getOutputPowerIndicatorMax()); - double moteTransmissionRange = - radioMedium.TRANSMITTING_RANGE - * ((double) selectedRadio.getCurrentOutputPowerIndicator() - / (double) selectedRadio.getOutputPowerIndicatorMax()); + // Fetch current output power indicator (scale with as percent) + Radio selectedRadio = selectedMote.getInterfaces().getRadio(); + double moteInterferenceRange = + radioMedium.INTERFERENCE_RANGE + * ((double) selectedRadio.getCurrentOutputPowerIndicator() + / (double) selectedRadio.getOutputPowerIndicatorMax()); + double moteTransmissionRange = + radioMedium.TRANSMITTING_RANGE + * ((double) selectedRadio.getCurrentOutputPowerIndicator() + / (double) selectedRadio.getOutputPowerIndicatorMax()); - Point translatedZero = visualizer.transformPositionToPixel(0.0, 0.0, 0.0); - Point translatedInterference = visualizer.transformPositionToPixel(moteInterferenceRange, moteInterferenceRange, 0.0); - Point translatedTransmission = visualizer.transformPositionToPixel(moteTransmissionRange, moteTransmissionRange, 0.0); + Point translatedZero = visualizer.transformPositionToPixel(0.0, 0.0, 0.0); + Point translatedInterference = visualizer.transformPositionToPixel(moteInterferenceRange, moteInterferenceRange, 0.0); + Point translatedTransmission = visualizer.transformPositionToPixel(moteTransmissionRange, moteTransmissionRange, 0.0); - translatedInterference.x = Math.abs(translatedInterference.x - translatedZero.x); - translatedInterference.y = Math.abs(translatedInterference.y - translatedZero.y); - translatedTransmission.x = Math.abs(translatedTransmission.x - translatedZero.x); - translatedTransmission.y = Math.abs(translatedTransmission.y - translatedZero.y); + translatedInterference.x = Math.abs(translatedInterference.x - translatedZero.x); + translatedInterference.y = Math.abs(translatedInterference.y - translatedZero.y); + translatedTransmission.x = Math.abs(translatedTransmission.x - translatedZero.x); + translatedTransmission.y = Math.abs(translatedTransmission.y - translatedZero.y); - /* Interference range */ - g.setColor(COLOR_INTERFERENCE); - g.fillOval( - x - translatedInterference.x, - y - translatedInterference.y, - 2 * translatedInterference.x, - 2 * translatedInterference.y); + /* Interference range */ + g.setColor(COLOR_INT); + g.fillOval( + x - translatedInterference.x, + y - translatedInterference.y, + 2 * translatedInterference.x, + 2 * translatedInterference.y); - /* Transmission range */ - g.setColor(COLOR_TX); - g.fillOval( - x - translatedTransmission.x, - y - translatedTransmission.y, - 2 * translatedTransmission.x, - 2 * translatedTransmission.y); - } - } + /* Transmission range */ + g.setColor(COLOR_TX); + g.fillOval( + x - translatedTransmission.x, + y - translatedTransmission.y, + 2 * translatedTransmission.x, + 2 * translatedTransmission.y); + } - /* Paint active connections in black */ - RadioConnection[] conns = radioMedium.getActiveConnections(); - if (conns != null) { - g.setColor(Color.BLACK); - for (RadioConnection conn : conns) { - Radio source = conn.getSource(); - Point sourcePoint = visualizer.transformPositionToPixel(source.getPosition()); - for (Radio destRadio : conn.getDestinations()) { - Position destPos = destRadio.getPosition(); - Point destPoint = visualizer.transformPositionToPixel(destPos); - g.drawLine(sourcePoint.x, sourcePoint.y, destPoint.x, destPoint.y); - - /* Draw arrows */ - if (DRAW_ARROWS) { - Point centerPoint = new Point( - destPoint.x/2 + sourcePoint.x/2, - destPoint.y/2 + sourcePoint.y/2 - ); - int startAngle = (int) (-180 * Math.atan2(destPoint.y - sourcePoint.y, destPoint.x - sourcePoint.x)/Math.PI - 90); - g.drawArc(centerPoint.x-5, centerPoint.y-5, 10, 10, startAngle, 180); - } - } - } - } - - /* Paint past connections in gray */ - /*conns = radioMedium.getLastTickConnections(); - if (conns != null) { - g.setColor(Color.GRAY); - for (RadioConnection conn : conns) { - Point sourcePoint = visualizer.transformPositionToPixel(conn.getSource().getPosition()); - for (Radio dest : conn.getDestinations()) { - Position destPos = dest.getPosition(); - Point destPoint = visualizer.transformPositionToPixel(destPos); - g.drawLine(sourcePoint.x, sourcePoint.y, destPoint.x, destPoint.y); - } - } - }*/ + public void paintAfterMotes(Graphics g) { } public static class RangeMenuAction implements SimulationMenuAction { - public boolean isEnabled(Simulation simulation) { + public boolean isEnabled(Visualizer visualizer, Simulation simulation) { return true; } - public String getDescription(Simulation simulation) { + public String getDescription(Visualizer visualizer, Simulation simulation) { return "Change transmission ranges"; } @@ -435,8 +329,9 @@ public class UDGMVisualizerSkin implements VisualizerSkin { VisualizerSkin[] skins = visualizer.getCurrentSkins(); for (VisualizerSkin skin: skins) { if (skin instanceof UDGMVisualizerSkin) { - ((UDGMVisualizerSkin)skin).txRangeSpinner.setVisible(true); - ((UDGMVisualizerSkin)skin).interferenceRangeSpinner.setVisible(true); + ((UDGMVisualizerSkin)skin).rangeTX.setVisible(true); + ((UDGMVisualizerSkin)skin).rangeINT.setVisible(true); + ((UDGMVisualizerSkin)skin).top.setVisible(true); visualizer.repaint(); } } @@ -444,11 +339,11 @@ public class UDGMVisualizerSkin implements VisualizerSkin { }; public static class SuccessRatioMenuAction implements SimulationMenuAction { - public boolean isEnabled(Simulation simulation) { + public boolean isEnabled(Visualizer visualizer, Simulation simulation) { return true; } - public String getDescription(Simulation simulation) { + public String getDescription(Visualizer visualizer, Simulation simulation) { return "Change TX/RX success ratio"; } @@ -456,8 +351,9 @@ public class UDGMVisualizerSkin implements VisualizerSkin { VisualizerSkin[] skins = visualizer.getCurrentSkins(); for (VisualizerSkin skin: skins) { if (skin instanceof UDGMVisualizerSkin) { - ((UDGMVisualizerSkin)skin).successRatioTxSpinner.setVisible(true); - ((UDGMVisualizerSkin)skin).successRatioRxSpinner.setVisible(true); + ((UDGMVisualizerSkin)skin).ratioTX.setVisible(true); + ((UDGMVisualizerSkin)skin).ratioRX.setVisible(true); + ((UDGMVisualizerSkin)skin).top.setVisible(true); visualizer.repaint(); } }