GUI traffic visualization work on server-side.

Change-Id: I15564fc8484464858adf57fe22462f4951d09d09
diff --git a/web/gui/src/main/java/org/onlab/onos/gui/TopologyMessages.java b/web/gui/src/main/java/org/onlab/onos/gui/TopologyMessages.java
index e7bc37e..c87a7f6 100644
--- a/web/gui/src/main/java/org/onlab/onos/gui/TopologyMessages.java
+++ b/web/gui/src/main/java/org/onlab/onos/gui/TopologyMessages.java
@@ -43,7 +43,6 @@
 import org.onlab.onos.net.intent.ConnectivityIntent;
 import org.onlab.onos.net.intent.Intent;
 import org.onlab.onos.net.intent.IntentService;
-import org.onlab.onos.net.intent.IntentState;
 import org.onlab.onos.net.intent.LinkCollectionIntent;
 import org.onlab.onos.net.intent.PathIntent;
 import org.onlab.onos.net.link.LinkEvent;
@@ -232,7 +231,7 @@
         ObjectNode payload = mapper.createObjectNode()
                 .put("id", compactLinkString(link))
                 .put("type", link.type().toString().toLowerCase())
-                .put("online", true) // TODO: add link state field
+                .put("online", true) // link.state()) TODO: add link state field
                 .put("linkWidth", 2)
                 .put("src", link.src().deviceId().toString())
                 .put("srcPort", link.src().port().toString())
@@ -370,18 +369,18 @@
 
 
     // Produces JSON message to trigger traffic visualization
-    protected ObjectNode trafficMessage(Set<Intent> intents, long sid) {
+    protected ObjectNode trafficMessage(long sid, TrafficClass... trafficClasses) {
         ObjectNode payload = mapper.createObjectNode();
         ArrayNode paths = mapper.createArrayNode();
         payload.set("paths", paths);
 
-        for (Intent intent : intents) {
-            List<Intent> installables = intentService.getInstallableIntents(intent.id());
-            IntentState state = intentService.getIntentState(intent.id());
-            String type = state == IntentState.FAILED ? "inactive" : "active";
-            for (Intent installable : installables) {
-                if (installable instanceof ConnectivityIntent) {
-                    addPathTraffic(paths, type, (ConnectivityIntent) installable);
+        for (TrafficClass trafficClass : trafficClasses) {
+            for (Intent intent : trafficClass.intents) {
+                List<Intent> installables = intentService.getInstallableIntents(intent.id());
+                for (Intent installable : installables) {
+                    if (installable instanceof ConnectivityIntent) {
+                        addPathTraffic(paths, trafficClass.type, (ConnectivityIntent) installable);
+                    }
                 }
             }
         }
@@ -452,4 +451,15 @@
         }
     }
 
+    // Auxiliary carrier of data for requesting traffic message.
+    protected class TrafficClass {
+        public final String type;
+        public final Set<Intent> intents;
+
+        TrafficClass(String type, Set<Intent> intents) {
+            this.type = type;
+            this.intents = intents;
+        }
+    }
+
 }
diff --git a/web/gui/src/main/java/org/onlab/onos/gui/TopologyWebSocket.java b/web/gui/src/main/java/org/onlab/onos/gui/TopologyWebSocket.java
index 9e2623f..cf8144b 100644
--- a/web/gui/src/main/java/org/onlab/onos/gui/TopologyWebSocket.java
+++ b/web/gui/src/main/java/org/onlab/onos/gui/TopologyWebSocket.java
@@ -56,6 +56,7 @@
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
+import static com.google.common.base.Strings.isNullOrEmpty;
 import static org.onlab.onos.cluster.ClusterEvent.Type.INSTANCE_ADDED;
 import static org.onlab.onos.net.DeviceId.deviceId;
 import static org.onlab.onos.net.HostId.hostId;
@@ -252,38 +253,44 @@
     private void requestTraffic(ObjectNode event) {
         ObjectNode payload = payload(event);
         long sid = number(event, "sid");
-        Set<Intent> intents = findPathIntents(payload);
 
-        // Add all those intents to the list of monitored intents & flows.
-        intentsToMonitor.clear();
-        for (Intent intent : intents) {
-            intentsToMonitor.put(intent, sid);
+        // Get the set of selected hosts and their intents.
+        Set<Host> hosts = getHosts((ArrayNode) payload.path("ids"));
+        Set<Intent> intents = findPathIntents(hosts);
+
+        // If there is a hover node, include it in the hosts and find intents.
+        String hover = string(payload, "hover");
+        Set<Intent> hoverIntents;
+        if (!isNullOrEmpty(hover)) {
+            addHost(hosts, hostId(hover));
+            hoverIntents = findPathIntents(hosts);
+            intents.removeAll(hoverIntents);
+
+            // Send an initial message to highlight all links of all monitored intents.
+            sendMessage(trafficMessage(sid,
+                                       new TrafficClass("primary", hoverIntents),
+                                       new TrafficClass("secondary", intents)));
+
+        } else {
+            // Send an initial message to highlight all links of all monitored intents.
+            sendMessage(trafficMessage(sid, new TrafficClass("primary", intents)));
+
+            // Add all those intents to the list of monitored intents & flows.
+            intentsToMonitor.clear();
+            for (Intent intent : intents) {
+                intentsToMonitor.put(intent, sid);
+            }
         }
-
-        // Send an initial message to highlight all links of all monitored intents.
-        sendMessage(trafficMessage(intents, sid));
     }
 
     // Cancels sending traffic messages.
     private void cancelTraffic(ObjectNode event) {
-        ObjectNode payload = payload(event);
-        long sid = number(event, "sid");
-        Set<Intent> intents = findPathIntents(payload);
-
-        // Remove all those intents from the list of monitored intents & flows.
-        intentsToMonitor.clear(); // TODO: remove when ready
-        for (Intent intent : intents) {
-            intentsToMonitor.remove(intent.id());
-        }
-        sendMessage(trafficMessage(intents, sid));
+        sendMessage(trafficMessage(number(event, "sid")));
     }
 
     // Finds all path (host-to-host or point-to-point) intents that pertains
-    // to the hosts indicated in the given event payload.
-    private Set<Intent> findPathIntents(ObjectNode payload) {
-        // Get the list of selected hosts.
-        Set<Host> hosts = getHosts((ArrayNode) payload.path("ids"));
-
+    // to the given hosts.
+    private Set<Intent> findPathIntents(Set<Host> hosts) {
         // Derive from this the set of edge connect points.
         Set<ConnectPoint> edgePoints = getEdgePoints(hosts);
 
@@ -359,19 +366,25 @@
     // Produces a set of all host ids listed in the specified JSON array.
     private Set<Host> getHosts(ArrayNode array) {
         Set<Host> hosts = new HashSet<>();
-        for (JsonNode node : array) {
-            try {
-                Host host = hostService.getHost(hostId(node.asText()));
-                if (host != null) {
-                    hosts.add(host);
+        if (array != null) {
+            for (JsonNode node : array) {
+                try {
+                    addHost(hosts, hostId(node.asText()));
+                } catch (IllegalArgumentException e) {
+                    log.debug("Skipping ID {}", node.asText());
                 }
-            } catch (IllegalArgumentException e) {
-                log.debug("Skipping ID {}", node.asText());
             }
         }
         return hosts;
     }
 
+    private void addHost(Set<Host> hosts, HostId hostId) {
+        Host host = hostService.getHost(hostId);
+        if (host != null) {
+            hosts.add(host);
+        }
+    }
+
     // Produces a set of edge points from the specified set of hosts.
     private Set<ConnectPoint> getEdgePoints(Set<Host> hosts) {
         Set<ConnectPoint> edgePoints = new HashSet<>();
@@ -460,7 +473,8 @@
                     ObjectNode payload = pathMessage(path, "host")
                             .put("intentId", intent.id().toString());
                     sendMessage(envelope("showPath", sid, payload));
-                    sendMessage(trafficMessage(intentsToMonitor.keySet(), sid));
+                    TrafficClass tc = new TrafficClass("animated", intentsToMonitor.keySet());
+                    sendMessage(trafficMessage(sid, tc));
                 }
             }
         }