diff --git a/tools/cooja/java/org/contikios/cooja/plugins/skins/TrafficVisualizerSkin.java b/tools/cooja/java/org/contikios/cooja/plugins/skins/TrafficVisualizerSkin.java index d632ae094..0cd05c319 100644 --- a/tools/cooja/java/org/contikios/cooja/plugins/skins/TrafficVisualizerSkin.java +++ b/tools/cooja/java/org/contikios/cooja/plugins/skins/TrafficVisualizerSkin.java @@ -33,7 +33,9 @@ import java.awt.Color; import java.awt.Graphics; import java.awt.Point; import java.awt.Polygon; -import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; import java.util.Observable; import java.util.Observer; @@ -60,54 +62,51 @@ import org.contikios.cooja.radiomediums.AbstractRadioMedium; @ClassDescription("Radio traffic") @SupportedArguments(radioMediums = {AbstractRadioMedium.class}) public class TrafficVisualizerSkin implements VisualizerSkin { - private static Logger logger = Logger.getLogger(TrafficVisualizerSkin.class); + private static final Logger logger = Logger.getLogger(TrafficVisualizerSkin.class); private final int MAX_HISTORY_SIZE = 200; + private final float TRANSMITTED_COLOR_RGB[] = Color.BLUE.getRGBColorComponents(null); + private final float UNTRANSMITTED_COLOR_RGB[] = Color.RED.getRGBColorComponents(null); private boolean active = false; private Simulation simulation = null; private Visualizer visualizer = null; private AbstractRadioMedium radioMedium = null; - private ArrayList historyList = new ArrayList(); - private RadioConnectionArrow[] history = null; + private final List historyList = new LinkedList<>(); private Observer radioMediumObserver = new Observer() { + @Override public void update(Observable obs, Object obj) { RadioConnection last = radioMedium.getLastConnection(); if (last != null && historyList.size() < MAX_HISTORY_SIZE) { - historyList.add(new RadioConnectionArrow(last)); - history = historyList.toArray(new RadioConnectionArrow[0]); - visualizer.repaint(500); + synchronized(historyList) { + historyList.add(new RadioConnectionArrow(last)); + visualizer.repaint(500); + } } } }; - private TimeEvent ageArrowsTimeEvent = new TimeEvent(0) { + + private final TimeEvent ageArrowsTimeEvent = new TimeEvent(0) { + @Override public void execute(long t) { if (!active) { return; } if (historyList.size() > 0) { - boolean hasOld = false; - /* Increase age */ - for (RadioConnectionArrow connArrow : historyList) { - connArrow.increaseAge(); - if(connArrow.getAge() >= connArrow.getMaxAge()) { - hasOld = true; - } - } - - /* Remove too old arrows */ - if (hasOld) { - RadioConnectionArrow[] historyArr = historyList.toArray(new RadioConnectionArrow[0]); - for (RadioConnectionArrow connArrow : historyArr) { - if(connArrow.getAge() >= connArrow.getMaxAge()) { - historyList.remove(connArrow); + synchronized (historyList) { + /* Increase age and remove too old arrows */ + Iterator iter = historyList.iterator(); + while (iter.hasNext()) { + RadioConnectionArrow rca = iter.next(); + /* Try to increase age and remove if max age was reached */ + if (!rca.increaseAge()) { + iter.remove(); } } - historyArr = historyList.toArray(new RadioConnectionArrow[0]); } visualizer.repaint(500); @@ -118,6 +117,7 @@ public class TrafficVisualizerSkin implements VisualizerSkin { } }; + @Override public void setActive(final Simulation simulation, Visualizer vis) { this.radioMedium = (AbstractRadioMedium) simulation.getRadioMedium(); this.simulation = simulation; @@ -125,9 +125,9 @@ public class TrafficVisualizerSkin implements VisualizerSkin { this.active = true; simulation.invokeSimulationThread(new Runnable() { + @Override public void run() { historyList.clear(); - history = null; /* Start observing radio medium for transmissions */ radioMedium.addRadioMediumObserver(radioMediumObserver); @@ -138,6 +138,7 @@ public class TrafficVisualizerSkin implements VisualizerSkin { }); } + @Override public void setInactive() { this.active = false; if (simulation == null) { @@ -149,11 +150,12 @@ public class TrafficVisualizerSkin implements VisualizerSkin { radioMedium.deleteRadioMediumObserver(radioMediumObserver); } + @Override public Color[] getColorOf(Mote mote) { return null; } - private Polygon arrowPoly = new Polygon(); + private final Polygon arrowPoly = new Polygon(); private void drawArrow(Graphics g, int xSource, int ySource, int xDest, int yDest, int delta) { double dx = xSource - xDest; double dy = ySource - yDest; @@ -183,52 +185,81 @@ public class TrafficVisualizerSkin implements VisualizerSkin { return (int)(0.5 + len * Math.sin(dir)); } + @Override public void paintBeforeMotes(Graphics g) { - RadioConnectionArrow[] historyCopy = history; - if (historyCopy == null) { - return; - } - for (RadioConnectionArrow connArrow : historyCopy) { - float colorHistoryIndex = (float)connArrow.getAge() / (float)connArrow.getMaxAge(); - g.setColor(new Color(colorHistoryIndex, colorHistoryIndex, 1.0f)); - Radio source = connArrow.getConnection().getSource(); - Point sourcePoint = visualizer.transformPositionToPixel(source.getPosition()); - for (Radio destRadio : connArrow.getConnection().getDestinations()) { - Position destPos = destRadio.getPosition(); - Point destPoint = visualizer.transformPositionToPixel(destPos); - drawArrow(g, sourcePoint.x, sourcePoint.y, destPoint.x, destPoint.y, 8); + synchronized (historyList) { + for (RadioConnectionArrow connArrow : historyList) { + float colorHistoryIndex = 1.0f - connArrow.getAge(); + Radio source = connArrow.getConnection().getSource(); + Point sourcePoint = visualizer.transformPositionToPixel(source.getPosition()); + /* If there is no destination, paint red circles to indicate untransmitted message */ + if (connArrow.getConnection().getDestinations().length == 0) { + g.setColor(new Color(UNTRANSMITTED_COLOR_RGB[0], UNTRANSMITTED_COLOR_RGB[1], UNTRANSMITTED_COLOR_RGB[2], colorHistoryIndex)); + g.drawOval(sourcePoint.x - 20, sourcePoint.y - 20, 40, 40); + g.drawOval(sourcePoint.x - 30, sourcePoint.y - 30, 60, 60); + continue; + } + g.setColor(new Color(TRANSMITTED_COLOR_RGB[0], TRANSMITTED_COLOR_RGB[1], TRANSMITTED_COLOR_RGB[2], colorHistoryIndex)); + for (Radio destRadio : connArrow.getConnection().getDestinations()) { + Position destPos = destRadio.getPosition(); + Point destPoint = visualizer.transformPositionToPixel(destPos); + drawArrow(g, sourcePoint.x, sourcePoint.y, destPoint.x, destPoint.y, 8); + } } } } + @Override public void paintAfterMotes(Graphics g) { } + @Override public Visualizer getVisualizer() { return visualizer; } private static class RadioConnectionArrow { - private RadioConnection conn; - private int age; + private static final int MAX_AGE = 10; + private final RadioConnection conn; + private int age; + RadioConnectionArrow(RadioConnection conn) { this.conn = conn; this.age = 0; } - public void increaseAge() { + + /** + * Increases age of radio connection if possible or indicates max age. + * + * @return true if max age was not reached yet, false, if max age was + * reached + */ + public boolean increaseAge() { if (age < MAX_AGE) { age++; + return true; + } else { + return false; } } - public int getAge() { - return age; + + /** + * Returns relative age of radio connection + * + * @return Relative age (0.0 - 1.0) + */ + public float getAge() { + return (float) age / (float) MAX_AGE; } + + /** + * Returns radio connection + * + * @return radio connection + */ public RadioConnection getConnection() { return conn; } - public int getMaxAge() { - return MAX_AGE; - } } }