GUI2 Topo Added base common base for Topo2ViewMessageHandler and TopologyViewMessageHandlerBase

Change-Id: I6818b764a3dbafb134786c4f6cdddd051b0c88ba
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewMessageHandler.java b/web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewMessageHandler.java
index 5f30bf8..455b0f3 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewMessageHandler.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewMessageHandler.java
@@ -743,7 +743,8 @@
     //=======================================================================
 
     // Converts highlights to JSON format and sends the message to the client
-    void sendHighlights(Highlights highlights) {
+    @Override
+    public void sendHighlights(Highlights highlights) {
         sendMessage(highlightsMessage(highlights));
     }
 
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewMessageHandlerBase.java b/web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewMessageHandlerBase.java
index 9e62410..93f41a6 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewMessageHandlerBase.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewMessageHandlerBase.java
@@ -50,7 +50,7 @@
 import org.onosproject.net.topology.Topology;
 import org.onosproject.ui.JsonUtils;
 import org.onosproject.ui.UiConnection;
-import org.onosproject.ui.UiMessageHandler;
+import org.onosproject.ui.impl.topo.TopoologyTrafficMessageHandlerAbstract;
 import org.onosproject.ui.impl.topo.util.ServicesBundle;
 import org.onosproject.ui.lion.LionBundle;
 import org.onosproject.ui.topo.PropertyPanel;
@@ -77,7 +77,7 @@
 /**
  * Facility for creating messages bound for the topology viewer.
  */
-public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler {
+public abstract class TopologyViewMessageHandlerBase extends TopoologyTrafficMessageHandlerAbstract {
 
     private static final String NO_GEO_VALUE = "0.0";
     private static final String DASH = "-";
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/TrafficMonitor.java b/web/gui/src/main/java/org/onosproject/ui/impl/TrafficMonitor.java
index f45eb8d..9d9b3c3 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/TrafficMonitor.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/TrafficMonitor.java
@@ -17,57 +17,31 @@
 
 package org.onosproject.ui.impl;
 
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Lists;
 import org.onosproject.net.Device;
 import org.onosproject.net.DeviceId;
-import org.onosproject.net.ElementId;
 import org.onosproject.net.Host;
-import org.onosproject.net.HostId;
 import org.onosproject.net.Link;
 import org.onosproject.net.PortNumber;
-import org.onosproject.net.device.DeviceService;
 import org.onosproject.net.flow.FlowEntry;
 import org.onosproject.net.flow.TrafficTreatment;
 import org.onosproject.net.flow.instructions.Instruction;
 import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
-import org.onosproject.net.intent.FlowObjectiveIntent;
-import org.onosproject.net.intent.FlowRuleIntent;
-import org.onosproject.net.intent.HostToHostIntent;
-import org.onosproject.net.intent.Intent;
-import org.onosproject.net.intent.LinkCollectionIntent;
-import org.onosproject.net.intent.OpticalConnectivityIntent;
-import org.onosproject.net.intent.OpticalPathIntent;
-import org.onosproject.net.intent.PathIntent;
-import org.onosproject.net.link.LinkService;
-import org.onosproject.ui.impl.topo.util.IntentSelection;
 import org.onosproject.ui.impl.topo.util.ServicesBundle;
-import org.onosproject.ui.impl.topo.util.TopoIntentFilter;
 import org.onosproject.ui.impl.topo.util.TrafficLink;
 import org.onosproject.ui.impl.topo.util.TrafficLink.StatsType;
 import org.onosproject.ui.impl.topo.util.TrafficLinkMap;
-import org.onosproject.ui.topo.DeviceHighlight;
 import org.onosproject.ui.topo.Highlights;
-import org.onosproject.ui.topo.Highlights.Amount;
-import org.onosproject.ui.topo.HostHighlight;
-import org.onosproject.ui.topo.LinkHighlight.Flavor;
-import org.onosproject.ui.topo.NodeHighlight;
-import org.onosproject.ui.topo.NodeSelection;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.stream.Collectors;
 
 import static org.onosproject.net.DefaultEdgeLink.createEdgeLinks;
-import static org.onosproject.net.DefaultEdgeLink.createEdgeLink;
 import static org.onosproject.ui.impl.TrafficMonitorBase.Mode.RELATED_INTENTS;
 import static org.onosproject.ui.impl.TrafficMonitorBase.Mode.SELECTED_INTENT;
 
@@ -80,10 +54,6 @@
             LoggerFactory.getLogger(TrafficMonitor.class);
 
     private final TopologyViewMessageHandler msgHandler;
-    private final TopoIntentFilter intentFilter;
-
-    private NodeSelection selectedNodes = null;
-    private IntentSelection selectedIntents = null;
 
 
     /**
@@ -95,10 +65,9 @@
      */
     public TrafficMonitor(long trafficPeriod, ServicesBundle servicesBundle,
                           TopologyViewMessageHandler msgHandler) {
-        super(trafficPeriod, servicesBundle);
+        super(trafficPeriod, servicesBundle, msgHandler);
         this.msgHandler = msgHandler;
 
-        intentFilter = new TopoIntentFilter(servicesBundle);
     }
 
     // =======================================================================
@@ -106,77 +75,9 @@
 
     // monitor(Mode) is now implemented in the super class
 
-    /**
-     * Monitor for traffic data to be sent back to the web client, under
-     * the given mode, using the given selection of devices and hosts.
-     * In the case of "device link flows", this causes a background traffic
-     * task to be scheduled to repeatedly compute and transmit the appropriate
-     * traffic data to the client. In the case of "related intents", no
-     * repeating task is scheduled.
-     * <p>
-     * The monitoring mode is expected to be one of:
-     * <ul>
-     * <li>DEV_LINK_FLOWS</li>
-     * <li>RELATED_INTENTS</li>
-     * </ul>
-     *
-     * @param mode          monitoring mode
-     * @param nodeSelection how to select a node
-     */
-    public synchronized void monitor(Mode mode, NodeSelection nodeSelection) {
-        log.debug("monitor: {} -- {}", mode, nodeSelection);
-        this.mode = mode;
-        this.selectedNodes = nodeSelection;
-
-        switch (mode) {
-            case DEV_LINK_FLOWS:
-                // only care about devices (not hosts)
-                if (selectedNodes.devicesWithHover().isEmpty()) {
-                    clearAll();
-                } else {
-                    scheduleTask();
-                    sendDeviceLinkFlows();
-                }
-                break;
-
-            case RELATED_INTENTS:
-                if (selectedNodes.none()) {
-                    clearAll();
-                } else {
-                    selectedIntents = new IntentSelection(selectedNodes, intentFilter);
-                    if (selectedIntents.none()) {
-                        clearAll();
-                    } else {
-                        sendSelectedIntents();
-                    }
-                }
-                break;
-
-            default:
-                log.debug("Unexpected call to monitor({}, {})", mode, nodeSelection);
-                clearAll();
-                break;
-        }
-    }
-
     // TODO: move this out to the "h2h/multi-intent app"
 
     /**
-     * Monitor for traffic data to be sent back to the web client, for the
-     * given intent.
-     *
-     * @param intent the intent to monitor
-     */
-    public synchronized void monitor(Intent intent) {
-        log.debug("monitor intent: {}", intent.id());
-        selectedNodes = null;
-        selectedIntents = new IntentSelection(intent);
-        mode = SELECTED_INTENT;
-        scheduleTask();
-        sendSelectedIntentTraffic();
-    }
-
-    /**
      * Selects the next intent in the select group (if there is one),
      * and sends highlighting data back to the web client to display
      * which path is selected.
@@ -261,12 +162,6 @@
         selectedIntents = null;
     }
 
-
-    private void sendSelectedIntents() {
-        log.debug("sendSelectedIntents: {}", selectedIntents);
-        msgHandler.sendHighlights(intentGroup());
-    }
-
     // =======================================================================
     // === Generate messages in JSON object node format
 
@@ -298,56 +193,6 @@
         return highlights;
     }
 
-    private Highlights intentGroup() {
-        Highlights highlights = new Highlights();
-
-        if (selectedIntents != null && !selectedIntents.none()) {
-            // If 'all' intents are selected, they will all have primary
-            // highlighting; otherwise, the specifically selected intent will
-            // have primary highlighting, and the remainder will have secondary
-            // highlighting.
-            Set<Intent> primary;
-            Set<Intent> secondary;
-            int count = selectedIntents.size();
-
-            Set<Intent> allBut = new HashSet<>(selectedIntents.intents());
-            Intent current;
-
-            if (selectedIntents.all()) {
-                primary = allBut;
-                secondary = Collections.emptySet();
-                log.debug("Highlight all intents ({})", count);
-            } else {
-                current = selectedIntents.current();
-                primary = new HashSet<>();
-                primary.add(current);
-                allBut.remove(current);
-                secondary = allBut;
-                log.debug("Highlight intent: {} ([{}] of {})",
-                          current.id(), selectedIntents.index(), count);
-            }
-
-            highlightIntentLinks(highlights, primary, secondary);
-        }
-        return highlights;
-    }
-
-    private Highlights intentTraffic() {
-        Highlights highlights = new Highlights();
-
-        if (selectedIntents != null && selectedIntents.single()) {
-            Intent current = selectedIntents.current();
-            Set<Intent> primary = new HashSet<>();
-            primary.add(current);
-            log.debug("Highlight traffic for intent: {} ([{}] of {})",
-                      current.id(), selectedIntents.index(), selectedIntents.size());
-
-            highlightIntentLinksWithTraffic(highlights, primary);
-            highlights.subdueAllElse(Amount.MINIMALLY);
-        }
-        return highlights;
-    }
-
     // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
     // Counts all flow entries that egress on the links of the given device.
@@ -392,137 +237,4 @@
         return count;
     }
 
-    private void highlightIntentLinks(Highlights highlights,
-                                      Set<Intent> primary, Set<Intent> secondary) {
-        TrafficLinkMap linkMap = new TrafficLinkMap();
-        // NOTE: highlight secondary first, then primary, so that links shared
-        //       by intents are colored correctly ("last man wins")
-        createTrafficLinks(highlights, linkMap, secondary, Flavor.SECONDARY_HIGHLIGHT, false);
-        createTrafficLinks(highlights, linkMap, primary, Flavor.PRIMARY_HIGHLIGHT, false);
-        colorLinks(highlights, linkMap);
-    }
-
-    private void highlightIntentLinksWithTraffic(Highlights highlights,
-                                                 Set<Intent> primary) {
-        TrafficLinkMap linkMap = new TrafficLinkMap();
-        createTrafficLinks(highlights, linkMap, primary, Flavor.PRIMARY_HIGHLIGHT, true);
-        colorLinks(highlights, linkMap);
-    }
-
-    private void createTrafficLinks(Highlights highlights,
-                                    TrafficLinkMap linkMap, Set<Intent> intents,
-                                    Flavor flavor, boolean showTraffic) {
-        for (Intent intent : intents) {
-            List<Intent> installables = services.intent()
-                    .getInstallableIntents(intent.key());
-            Iterable<Link> links = null;
-            if (installables != null) {
-                for (Intent installable : installables) {
-
-                    if (installable instanceof PathIntent) {
-                        links = ((PathIntent) installable).path().links();
-                    } else if (installable instanceof FlowRuleIntent) {
-                        Collection<Link> l = new ArrayList<>();
-                        l.addAll(linkResources(installable));
-                        // Add cross connect links
-                        if (intent instanceof OpticalConnectivityIntent) {
-                            OpticalConnectivityIntent ocIntent = (OpticalConnectivityIntent) intent;
-                            LinkService linkService = services.link();
-                            DeviceService deviceService = services.device();
-                            l.addAll(linkService.getDeviceIngressLinks(ocIntent.getSrc().deviceId()).stream()
-                                    .filter(i ->
-                                            deviceService.getDevice(i.src().deviceId()).type() == Device.Type.SWITCH)
-                                    .collect(Collectors.toList()));
-                            l.addAll(linkService.getDeviceEgressLinks(ocIntent.getDst().deviceId()).stream()
-                                    .filter(e ->
-                                            deviceService.getDevice(e.dst().deviceId()).type() == Device.Type.SWITCH)
-                                    .collect(Collectors.toList()));
-                        }
-                        links = l;
-                    } else if (installable instanceof FlowObjectiveIntent) {
-                        links = linkResources(installable);
-                    } else if (installable instanceof LinkCollectionIntent) {
-                        links = ((LinkCollectionIntent) installable).links();
-                    } else if (installable instanceof OpticalPathIntent) {
-                        links = ((OpticalPathIntent) installable).path().links();
-                    }
-
-                    if (links == null) {
-                        links = Lists.newArrayList();
-                    }
-
-                    links = addEdgeLinksIfNeeded(intent, Lists.newArrayList(links));
-
-                    boolean isOptical = intent instanceof OpticalConnectivityIntent;
-                    processLinks(linkMap, links, flavor, isOptical, showTraffic);
-                    updateHighlights(highlights, links);
-                }
-            }
-        }
-    }
-
-    private Iterable<Link> addEdgeLinksIfNeeded(Intent parentIntent,
-                                                Collection<Link> links) {
-        if (parentIntent instanceof HostToHostIntent) {
-            links = new HashSet<>(links);
-            HostToHostIntent h2h = (HostToHostIntent) parentIntent;
-            Host h1 = services.host().getHost(h2h.one());
-            Host h2 = services.host().getHost(h2h.two());
-            links.add(createEdgeLink(h1, true));
-            links.add(createEdgeLink(h2, true));
-        }
-        return links;
-    }
-
-    private void updateHighlights(Highlights highlights, Iterable<Link> links) {
-        for (Link link : links) {
-            ensureNodePresent(highlights, link.src().elementId());
-            ensureNodePresent(highlights, link.dst().elementId());
-        }
-    }
-
-    private void ensureNodePresent(Highlights highlights, ElementId eid) {
-        String id = eid.toString();
-        NodeHighlight nh = highlights.getNode(id);
-        if (nh == null) {
-            if (eid instanceof DeviceId) {
-                nh = new DeviceHighlight(id);
-                highlights.add((DeviceHighlight) nh);
-            } else if (eid instanceof HostId) {
-                nh = new HostHighlight(id);
-                highlights.add((HostHighlight) nh);
-            }
-        }
-    }
-
-    // Extracts links from the specified flow rule intent resources
-    private Collection<Link> linkResources(Intent installable) {
-        ImmutableList.Builder<Link> builder = ImmutableList.builder();
-        installable.resources().stream().filter(r -> r instanceof Link)
-                .forEach(r -> builder.add((Link) r));
-        return builder.build();
-    }
-
-    private void processLinks(TrafficLinkMap linkMap, Iterable<Link> links,
-                              Flavor flavor, boolean isOptical,
-                              boolean showTraffic) {
-        if (links != null) {
-            for (Link link : links) {
-                TrafficLink tlink = linkMap.add(link);
-                tlink.tagFlavor(flavor);
-                tlink.optical(isOptical);
-                if (showTraffic) {
-                    tlink.addLoad(getLinkFlowLoad(link));
-                    tlink.antMarch(true);
-                }
-            }
-        }
-    }
-
-    private void colorLinks(Highlights highlights, TrafficLinkMap linkMap) {
-        for (TrafficLink tlink : linkMap.biLinks()) {
-            highlights.add(tlink.highlight(StatsType.TAGGED));
-        }
-    }
-
 }
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/TrafficMonitorBase.java b/web/gui/src/main/java/org/onosproject/ui/impl/TrafficMonitorBase.java
index 43c3fdb..2f14ac4 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/TrafficMonitorBase.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/TrafficMonitorBase.java
@@ -17,39 +17,262 @@
 
 package org.onosproject.ui.impl;
 
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import org.onosproject.net.Device;
+import org.onosproject.net.ElementId;
+import org.onosproject.net.Host;
+import org.onosproject.net.HostId;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.intent.FlowObjectiveIntent;
+import org.onosproject.net.intent.FlowRuleIntent;
+import org.onosproject.net.intent.HostToHostIntent;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.LinkCollectionIntent;
+import org.onosproject.net.intent.OpticalConnectivityIntent;
+import org.onosproject.net.intent.OpticalPathIntent;
+import org.onosproject.net.intent.PathIntent;
+import org.onosproject.net.link.LinkService;
 import org.onosproject.net.statistic.PortStatisticsService.MetricType;
 import org.onosproject.net.DefaultEdgeLink;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.Link;
 import org.onosproject.net.statistic.Load;
+import org.onosproject.ui.impl.topo.TopoologyTrafficMessageHandlerAbstract;
+import org.onosproject.ui.impl.topo.util.IntentSelection;
 import org.onosproject.ui.impl.topo.util.ServicesBundle;
+import org.onosproject.ui.impl.topo.util.TopoIntentFilter;
 import org.onosproject.ui.impl.topo.util.TrafficLink;
 import org.onosproject.ui.impl.topo.util.TrafficLinkMap;
 import org.onosproject.ui.topo.AbstractTopoMonitor;
+import org.onosproject.ui.topo.DeviceHighlight;
 import org.onosproject.ui.topo.Highlights;
+import org.onosproject.ui.topo.HostHighlight;
+import org.onosproject.ui.topo.LinkHighlight;
+import org.onosproject.ui.topo.NodeHighlight;
+import org.onosproject.ui.topo.NodeSelection;
 import org.onosproject.ui.topo.TopoUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
 import java.util.Timer;
 import java.util.TimerTask;
+import java.util.stream.Collectors;
 
+import static org.onosproject.net.DefaultEdgeLink.createEdgeLink;
 import static org.onosproject.net.statistic.PortStatisticsService.MetricType.BYTES;
 import static org.onosproject.net.statistic.PortStatisticsService.MetricType.PACKETS;
 import static org.onosproject.net.DefaultEdgeLink.createEdgeLinks;
 import static org.onosproject.ui.impl.TrafficMonitorBase.Mode.IDLE;
+import static org.onosproject.ui.impl.TrafficMonitorBase.Mode.SELECTED_INTENT;
 
 /**
  * Base superclass for traffic monitor (both 'classic' and 'topo2' versions).
  */
 public abstract class TrafficMonitorBase extends AbstractTopoMonitor {
 
-    private final Logger log = LoggerFactory.getLogger(getClass());
+    protected final Logger log = LoggerFactory.getLogger(getClass());
 
     // 4 Kilo Bytes as threshold
     protected static final double BPS_THRESHOLD = 4 * TopoUtils.N_KILO;
+    protected final TopoIntentFilter intentFilter;
+    protected IntentSelection selectedIntents = null;
+    protected final TopoologyTrafficMessageHandlerAbstract msgHandler;
+    protected NodeSelection selectedNodes = null;
+
+    protected void sendSelectedIntents() {
+        log.debug("sendSelectedIntents: {}", selectedIntents);
+        msgHandler.sendHighlights(intentGroup());
+    }
+
+    protected void ensureNodePresent(Highlights highlights, ElementId eid) {
+        String id = eid.toString();
+        NodeHighlight nh = highlights.getNode(id);
+        if (nh == null) {
+            if (eid instanceof DeviceId) {
+                nh = new DeviceHighlight(id);
+                highlights.add((DeviceHighlight) nh);
+            } else if (eid instanceof HostId) {
+                nh = new HostHighlight(id);
+                highlights.add((HostHighlight) nh);
+            }
+        }
+    }
+
+    protected void colorLinks(Highlights highlights, TrafficLinkMap linkMap) {
+        for (TrafficLink tlink : linkMap.biLinks()) {
+            highlights.add(tlink.highlight(TrafficLink.StatsType.TAGGED));
+        }
+    }
+
+    protected void processLinks(TrafficLinkMap linkMap, Iterable<Link> links,
+                                LinkHighlight.Flavor flavor, boolean isOptical,
+                                boolean showTraffic) {
+        if (links != null) {
+            for (Link link : links) {
+                TrafficLink tlink = linkMap.add(link);
+                tlink.tagFlavor(flavor);
+                tlink.optical(isOptical);
+                if (showTraffic) {
+                    tlink.addLoad(getLinkFlowLoad(link));
+                    tlink.antMarch(true);
+                }
+            }
+        }
+    }
+
+    protected void updateHighlights(Highlights highlights, Iterable<Link> links) {
+        for (Link link : links) {
+            ensureNodePresent(highlights, link.src().elementId());
+            ensureNodePresent(highlights, link.dst().elementId());
+        }
+    }
+
+    protected Iterable<Link> addEdgeLinksIfNeeded(Intent parentIntent,
+                                                  Collection<Link> links) {
+        if (parentIntent instanceof HostToHostIntent) {
+            links = new HashSet<>(links);
+            HostToHostIntent h2h = (HostToHostIntent) parentIntent;
+            Host h1 = services.host().getHost(h2h.one());
+            Host h2 = services.host().getHost(h2h.two());
+            links.add(createEdgeLink(h1, true));
+            links.add(createEdgeLink(h2, true));
+        }
+        return links;
+    }
+
+    // Extracts links from the specified flow rule intent resources
+    protected Collection<Link> linkResources(Intent installable) {
+        ImmutableList.Builder<Link> builder = ImmutableList.builder();
+        installable.resources().stream().filter(r -> r instanceof Link)
+                .forEach(r -> builder.add((Link) r));
+        return builder.build();
+    }
+
+    protected void createTrafficLinks(Highlights highlights,
+                                      TrafficLinkMap linkMap, Set<Intent> intents,
+                                      LinkHighlight.Flavor flavor, boolean showTraffic) {
+        for (Intent intent : intents) {
+            List<Intent> installables = services.intent()
+                    .getInstallableIntents(intent.key());
+            Iterable<Link> links = null;
+            if (installables != null) {
+                for (Intent installable : installables) {
+
+                    if (installable instanceof PathIntent) {
+                        links = ((PathIntent) installable).path().links();
+                    } else if (installable instanceof FlowRuleIntent) {
+                        Collection<Link> l = new ArrayList<>();
+                        l.addAll(linkResources(installable));
+                        // Add cross connect links
+                        if (intent instanceof OpticalConnectivityIntent) {
+                            OpticalConnectivityIntent ocIntent = (OpticalConnectivityIntent) intent;
+                            LinkService linkService = services.link();
+                            DeviceService deviceService = services.device();
+                            l.addAll(linkService.getDeviceIngressLinks(ocIntent.getSrc().deviceId()).stream()
+                                    .filter(i ->
+                                            deviceService.getDevice(i.src().deviceId()).type() == Device.Type.SWITCH)
+                                    .collect(Collectors.toList()));
+                            l.addAll(linkService.getDeviceEgressLinks(ocIntent.getDst().deviceId()).stream()
+                                    .filter(e ->
+                                            deviceService.getDevice(e.dst().deviceId()).type() == Device.Type.SWITCH)
+                                    .collect(Collectors.toList()));
+                        }
+                        links = l;
+                    } else if (installable instanceof FlowObjectiveIntent) {
+                        links = linkResources(installable);
+                    } else if (installable instanceof LinkCollectionIntent) {
+                        links = ((LinkCollectionIntent) installable).links();
+                    } else if (installable instanceof OpticalPathIntent) {
+                        links = ((OpticalPathIntent) installable).path().links();
+                    }
+
+                    if (links == null) {
+                        links = Lists.newArrayList();
+                    }
+
+                    links = addEdgeLinksIfNeeded(intent, Lists.newArrayList(links));
+
+                    boolean isOptical = intent instanceof OpticalConnectivityIntent;
+                    processLinks(linkMap, links, flavor, isOptical, showTraffic);
+                    updateHighlights(highlights, links);
+                }
+            }
+        }
+    }
+
+    protected void highlightIntentLinks(Highlights highlights,
+                                        Set<Intent> primary, Set<Intent> secondary) {
+        TrafficLinkMap linkMap = new TrafficLinkMap();
+        // NOTE: highlight secondary first, then primary, so that links shared
+        //       by intents are colored correctly ("last man wins")
+        createTrafficLinks(highlights, linkMap, secondary, LinkHighlight.Flavor.SECONDARY_HIGHLIGHT, false);
+        createTrafficLinks(highlights, linkMap, primary, LinkHighlight.Flavor.PRIMARY_HIGHLIGHT, false);
+        colorLinks(highlights, linkMap);
+    }
+
+    protected Highlights intentGroup() {
+        Highlights highlights = new Highlights();
+
+        if (selectedIntents != null && !selectedIntents.none()) {
+            // If 'all' intents are selected, they will all have primary
+            // highlighting; otherwise, the specifically selected intent will
+            // have primary highlighting, and the remainder will have secondary
+            // highlighting.
+            Set<Intent> primary;
+            Set<Intent> secondary;
+            int count = selectedIntents.size();
+
+            Set<Intent> allBut = new HashSet<>(selectedIntents.intents());
+            Intent current;
+
+            if (selectedIntents.all()) {
+                primary = allBut;
+                secondary = Collections.emptySet();
+                log.debug("Highlight all intents ({})", count);
+            } else {
+                current = selectedIntents.current();
+                primary = new HashSet<>();
+                primary.add(current);
+                allBut.remove(current);
+                secondary = allBut;
+                log.debug("Highlight intent: {} ([{}] of {})",
+                                         current.id(), selectedIntents.index(), count);
+            }
+
+            highlightIntentLinks(highlights, primary, secondary);
+        }
+        return highlights;
+    }
+
+    protected Highlights intentTraffic() {
+        Highlights highlights = new Highlights();
+
+        if (selectedIntents != null && selectedIntents.single()) {
+            Intent current = selectedIntents.current();
+            Set<Intent> primary = new HashSet<>();
+            primary.add(current);
+            log.debug("Highlight traffic for intent: {} ([{}] of {})",
+                                     current.id(), selectedIntents.index(), selectedIntents.size());
+
+            highlightIntentLinksWithTraffic(highlights, primary);
+            highlights.subdueAllElse(Highlights.Amount.MINIMALLY);
+        }
+        return highlights;
+    }
+
+    private void highlightIntentLinksWithTraffic(Highlights highlights,
+                                                 Set<Intent> primary) {
+        TrafficLinkMap linkMap = new TrafficLinkMap();
+        createTrafficLinks(highlights, linkMap, primary, LinkHighlight.Flavor.PRIMARY_HIGHLIGHT, true);
+        colorLinks(highlights, linkMap);
+    }
 
 
     /**
@@ -89,12 +312,16 @@
      *
      * @param trafficPeriod  traffic task period in ms
      * @param servicesBundle bundle of services
+     * @param msgHandler Traffic Message handler
      */
     protected TrafficMonitorBase(long trafficPeriod,
-                                 ServicesBundle servicesBundle) {
+                                 ServicesBundle servicesBundle,
+                                 TopoologyTrafficMessageHandlerAbstract msgHandler) {
         this.trafficPeriod = trafficPeriod;
         this.services = servicesBundle;
+        this.msgHandler = msgHandler;
         timer = new Timer("uiTopo-" + getClass().getSimpleName());
+        intentFilter = new TopoIntentFilter(servicesBundle);
     }
 
     /**
@@ -149,6 +376,74 @@
     }
 
     /**
+     * Monitor for traffic data to be sent back to the web client, under
+     * the given mode, using the given selection of devices and hosts.
+     * In the case of "device link flows", this causes a background traffic
+     * task to be scheduled to repeatedly compute and transmit the appropriate
+     * traffic data to the client. In the case of "related intents", no
+     * repeating task is scheduled.
+     * <p>
+     * The monitoring mode is expected to be one of:
+     * <ul>
+     * <li>DEV_LINK_FLOWS</li>
+     * <li>RELATED_INTENTS</li>
+     * </ul>
+     *
+     * @param mode          monitoring mode
+     * @param nodeSelection how to select a node
+     */
+    public synchronized void monitor(Mode mode, NodeSelection nodeSelection) {
+        log.debug("monitor: {} -- {}", mode, nodeSelection);
+        this.mode = mode;
+        this.selectedNodes = nodeSelection;
+
+        switch (mode) {
+            case DEV_LINK_FLOWS:
+                // only care about devices (not hosts)
+                if (selectedNodes.devicesWithHover().isEmpty()) {
+                    clearAll();
+                } else {
+                    scheduleTask();
+                    sendDeviceLinkFlows();
+                }
+                break;
+
+            case RELATED_INTENTS:
+                if (selectedNodes.none()) {
+                    clearAll();
+                } else {
+                    selectedIntents = new IntentSelection(selectedNodes, intentFilter);
+                    if (selectedIntents.none()) {
+                        clearAll();
+                    } else {
+                        sendSelectedIntents();
+                    }
+                }
+                break;
+
+            default:
+                log.debug("Unexpected call to monitor({}, {})", mode, nodeSelection);
+                clearAll();
+                break;
+        }
+    }
+
+    /**
+     * Monitor for traffic data to be sent back to the web client, for the
+     * given intent.
+     *
+     * @param intent the intent to monitor
+     */
+    public synchronized void monitor(Intent intent) {
+        log.debug("monitor intent: {}", intent.id());
+        selectedNodes = null;
+        selectedIntents = new IntentSelection(intent);
+        mode = SELECTED_INTENT;
+        scheduleTask();
+        sendSelectedIntentTraffic();
+    }
+
+    /**
      * Subclass should compile and send appropriate highlights data showing
      * flow traffic (bytes on links).
      */
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/UiWebSocket.java b/web/gui/src/main/java/org/onosproject/ui/impl/UiWebSocket.java
index 608031c..041dce2 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/UiWebSocket.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/UiWebSocket.java
@@ -40,6 +40,7 @@
 import org.onosproject.ui.impl.topo.Topo2OverlayCache;
 import org.onosproject.ui.impl.topo.Topo2TrafficMessageHandler;
 import org.onosproject.ui.impl.topo.Topo2ViewMessageHandler;
+import org.onosproject.ui.impl.topo.Traffic2Overlay;
 import org.onosproject.ui.impl.topo.UiTopoSession;
 import org.onosproject.ui.impl.topo.model.UiSharedTopologyModel;
 import org.onosproject.ui.lion.LionBundle;
@@ -323,8 +324,10 @@
 
         handlerCrossConnects(handlerInstances);
 
-        log.debug("#handlers = {}, #overlays = {}",
-                  handlers.size(), overlayCache.size());
+        overlay2Cache.switchOverlay(null, Traffic2Overlay.OVERLAY_ID);
+
+        log.debug("#handlers = {}, #overlays = Topo: {}, Topo2: {}",
+                  handlers.size(), overlayCache.size(), overlay2Cache.size());
     }
 
     private Map<String, LionBundle> generateLionMap(UiExtensionService service) {
@@ -404,6 +407,7 @@
                     handlers.get(Topo2TrafficMessageHandler.class);
             if (topo2traffic != null) {
                 topo2mh.setTrafficHandler(topo2traffic);
+                topo2traffic.setOverlayCache(overlay2Cache);
             } else {
                 log.error("No topo2 traffic handler found");
             }
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2TrafficMessageHandler.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2TrafficMessageHandler.java
index 586ff8c..5f5d84d 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2TrafficMessageHandler.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2TrafficMessageHandler.java
@@ -17,36 +17,56 @@
 
 package org.onosproject.ui.impl.topo;
 
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.google.common.collect.ImmutableSet;
 import org.onlab.osgi.ServiceDirectory;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.FilteredConnectPoint;
+import org.onosproject.net.Host;
+import org.onosproject.net.HostId;
+import org.onosproject.net.HostLocation;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.intent.HostToHostIntent;
+import org.onosproject.net.intent.MultiPointToSinglePointIntent;
 import org.onosproject.ui.RequestHandler;
 import org.onosproject.ui.UiConnection;
-import org.onosproject.ui.UiMessageHandler;
 import org.onosproject.ui.impl.TrafficMonitorBase.Mode;
 import org.onosproject.ui.impl.UiWebSocket;
 import org.onosproject.ui.impl.topo.util.ServicesBundle;
 import org.onosproject.ui.model.topo.UiLinkId;
 import org.onosproject.ui.model.topo.UiSynthLink;
 import org.onosproject.ui.topo.Highlights;
+import org.onosproject.ui.topo.NodeSelection;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.util.Collection;
+import java.util.HashSet;
 import java.util.Map;
+import java.util.Set;
 
+import static org.onosproject.net.HostId.hostId;
 import static org.onosproject.ui.topo.TopoJson.topo2HighlightsMessage;
 
 /**
  * Server-side component to handle messages pertaining to topo-2 traffic.
  */
-public class Topo2TrafficMessageHandler extends UiMessageHandler {
+public class Topo2TrafficMessageHandler extends TopoologyTrafficMessageHandlerAbstract {
 
     private final Logger log = LoggerFactory.getLogger(getClass());
 
     // === Inbound event identifiers
     private static final String REQUEST_ALL_TRAFFIC = "topo2RequestAllTraffic";
     private static final String CANCEL_TRAFFIC = "topo2CancelTraffic";
+    private static final String ADD_HOST_INTENT = "topo2AddHostIntent";
+    private static final String ADD_MULTI_SRC_INTENT = "topo2AddMultiSourceIntent";
+    private static final String REQ_RELATED_INTENTS = "topo2RequestRelatedIntents";
 
     // field values
     private static final String TRAFFIC_TYPE = "trafficType";
@@ -54,21 +74,29 @@
     private static final String PORT_STATS_BIT_SEC = "portStatsBitSec";
     private static final String PORT_STATS_PKT_SEC = "portStatsPktSec";
 
+    // fields
+    private static final String ONE = "one";
+    private static final String TWO = "two";
+    private static final String SRC = "src";
+    private static final String DST = "dst";
+
     // configuration parameters
     private static final long TRAFFIC_PERIOD = 5000;
 
     protected ServicesBundle services;
-
+    private static final String MY_APP_ID = "org.onosproject.gui";
+    private ApplicationId appId;
     private UiTopoSession topoSession;
-    private Traffic2Monitor traffic;
+    private Topo2OverlayCache overlay2Cache;
+    private Traffic2Monitor traffic2;
 
 
     @Override
     public void init(UiConnection connection, ServiceDirectory directory) {
         super.init(connection, directory);
-
+        appId = directory.get(CoreService.class).registerApplication(MY_APP_ID);
         services = new ServicesBundle(directory);
-        traffic = new Traffic2Monitor(TRAFFIC_PERIOD, services, this);
+        traffic2 = new Traffic2Monitor(TRAFFIC_PERIOD, services, this);
         topoSession = ((UiWebSocket) connection).topoSession();
     }
 
@@ -76,15 +104,26 @@
     protected Collection<RequestHandler> createRequestHandlers() {
         return ImmutableSet.of(
                 new Topo2AllTraffic(),
-                new Topo2CancelTraffic()
+                new Topo2CancelTraffic(),
+                new Topo2AddHostIntent(),
+                new Topo2AddMultiSourceIntent()
         );
     }
 
     /**
+     * Injects the topology overlay cache.
+     *
+     * @param overlay2Cache injected cache
+     */
+    public void setOverlayCache(Topo2OverlayCache overlay2Cache) {
+        this.overlay2Cache = overlay2Cache;
+    }
+
+    /**
      * Shuts down the background traffic monitoring task.
      */
     void ceaseAndDesist() {
-        traffic.stopMonitoring();
+        traffic2.stopMonitoring();
     }
 
     /**
@@ -92,7 +131,8 @@
      *
      * @param highlights the highlights for transmission
      */
-    void sendHighlights(Highlights highlights) {
+    @Override
+    public void sendHighlights(Highlights highlights) {
         sendMessage(topo2HighlightsMessage(highlights));
     }
 
@@ -121,15 +161,15 @@
 
             switch (mode) {
                 case FLOW_STATS_BYTES:
-                    traffic.monitor(Mode.ALL_FLOW_TRAFFIC_BYTES);
+                    traffic2.monitor(Mode.ALL_FLOW_TRAFFIC_BYTES);
                     break;
 
                 case PORT_STATS_BIT_SEC:
-                    traffic.monitor(Mode.ALL_PORT_TRAFFIC_BIT_PS);
+                    traffic2.monitor(Mode.ALL_PORT_TRAFFIC_BIT_PS);
                     break;
 
                 case PORT_STATS_PKT_SEC:
-                    traffic.monitor(Mode.ALL_PORT_TRAFFIC_PKT_PS);
+                    traffic2.monitor(Mode.ALL_PORT_TRAFFIC_PKT_PS);
                     break;
 
                 default:
@@ -147,9 +187,105 @@
         @Override
         public void process(ObjectNode payload) {
             log.debug("CANCEL TRAFFIC");
-            traffic.stopMonitoring();
+            traffic2.stopMonitoring();
         }
     }
+
+    private final class Topo2AddHostIntent extends RequestHandler {
+        private Topo2AddHostIntent() {
+            super(ADD_HOST_INTENT);
+        }
+
+        @Override
+        public void process(ObjectNode payload) {
+            // TODO: add protection against device ids and non-existent hosts.
+            HostId one = hostId(string(payload, ONE));
+            HostId two = hostId(string(payload, TWO));
+
+            HostToHostIntent intent = HostToHostIntent.builder()
+                    .appId(appId)
+                    .one(one)
+                    .two(two)
+                    .build();
+
+            services.intent().submit(intent);
+            if (overlay2Cache.isActive(Traffic2Overlay.OVERLAY_ID)) {
+                traffic2.monitor(intent);
+            }
+        }
+    }
+
+    private final class Topo2AddMultiSourceIntent extends RequestHandler {
+        private Topo2AddMultiSourceIntent() {
+            super(ADD_MULTI_SRC_INTENT);
+        }
+
+        @Override
+        public void process(ObjectNode payload) {
+            // TODO: add protection against device ids and non-existent hosts.
+            Set<HostId> src = getHostIds((ArrayNode) payload.path(SRC));
+            HostId dst = hostId(string(payload, DST));
+            Host dstHost = services.host().getHost(dst);
+
+            Set<FilteredConnectPoint> ingressPoints = getHostLocations(src);
+
+            // FIXME: clearly, this is not enough
+            TrafficSelector selector = DefaultTrafficSelector.builder()
+                    .matchEthDst(dstHost.mac()).build();
+            TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment();
+
+            MultiPointToSinglePointIntent intent =
+                    MultiPointToSinglePointIntent.builder()
+                            .appId(appId)
+                            .selector(selector)
+                            .treatment(treatment)
+                            .filteredIngressPoints(ingressPoints)
+                            .filteredEgressPoint(new FilteredConnectPoint(dstHost.location()))
+                            .build();
+
+            services.intent().submit(intent);
+            if (overlay2Cache.isActive(Traffic2Overlay.OVERLAY_ID)) {
+                traffic2.monitor(intent);
+            }
+        }
+    }
+
+    private final class ReqRelatedIntents extends RequestHandler {
+        private ReqRelatedIntents() {
+            super(REQ_RELATED_INTENTS);
+        }
+
+        @Override
+        public void process(ObjectNode payload) {
+            traffic2.monitor(Mode.RELATED_INTENTS, makeNodeSelection(payload));
+        }
+    }
+
+    // Produces a list of host ids from the specified JSON array.
+    private Set<HostId> getHostIds(ArrayNode ids) {
+        Set<HostId> hostIds = new HashSet<>();
+        for (JsonNode id : ids) {
+            hostIds.add(hostId(id.asText()));
+        }
+        return hostIds;
+    }
+
+    private Set<FilteredConnectPoint> getHostLocations(Set<HostId> hostIds) {
+        Set<FilteredConnectPoint> points = new HashSet<>();
+        for (HostId hostId : hostIds) {
+            points.add(new FilteredConnectPoint(getHostLocation(hostId)));
+        }
+        return points;
+    }
+
+    private HostLocation getHostLocation(HostId hostId) {
+        return services.host().getHost(hostId).location();
+    }
+
+    private NodeSelection makeNodeSelection(ObjectNode payload) {
+        return new NodeSelection(payload, services.device(), services.host(),
+                                 services.link());
+    }
 }
 
 
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2ViewMessageHandler.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2ViewMessageHandler.java
index 5fbb444..057a050 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2ViewMessageHandler.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2ViewMessageHandler.java
@@ -67,7 +67,11 @@
     private static final String CURRENT_REGION = "topo2CurrentRegion";
     private static final String PEER_REGIONS = "topo2PeerRegions";
     private static final String OVERLAYS = "topo2Overlays";
+    private static final String TOPO_SELECT_OVERLAY = "topoSelectOverlay";
 
+    // fields
+    private static final String ACTIVATE = "activate";
+    private static final String DEACTIVATE = "deactivate";
 
     private UiTopoSession topoSession;
     private Topo2Jsonifier t2json;
@@ -114,6 +118,19 @@
         );
     }
 
+    private final class TopoSelectOverlay extends RequestHandler {
+        private TopoSelectOverlay() {
+            super(TOPO_SELECT_OVERLAY);
+        }
+
+        @Override
+        public void process(ObjectNode payload) {
+            String deact = string(payload, DEACTIVATE);
+            String act = string(payload, ACTIVATE);
+            overlay2Cache.switchOverlay(deact, act);
+        }
+    }
+
     // ==================================================================
 
     private ObjectNode mkLayoutMessage(UiTopoLayout currentLayout) {
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/TopoologyTrafficMessageHandlerAbstract.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/TopoologyTrafficMessageHandlerAbstract.java
new file mode 100644
index 0000000..900f4df
--- /dev/null
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/TopoologyTrafficMessageHandlerAbstract.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.ui.impl.topo;
+
+import org.onosproject.ui.UiMessageHandler;
+import org.onosproject.ui.topo.Highlights;
+
+/**
+ * Base superclass for traffic message handler (both 'classic' and 'topo2' versions).
+ */
+public abstract class TopoologyTrafficMessageHandlerAbstract extends UiMessageHandler {
+    public abstract void sendHighlights(Highlights highlights);
+}
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/Traffic2Monitor.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/Traffic2Monitor.java
index 4e78525..d0d40bd 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/topo/Traffic2Monitor.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/Traffic2Monitor.java
@@ -54,7 +54,7 @@
      */
     public Traffic2Monitor(long trafficPeriod, ServicesBundle servicesBundle,
                            Topo2TrafficMessageHandler msgHandler) {
-        super(trafficPeriod, servicesBundle);
+        super(trafficPeriod, servicesBundle, msgHandler);
         this.msgHandler = msgHandler;
     }
 
@@ -90,7 +90,8 @@
 
     @Override
     protected void sendSelectedIntentTraffic() {
-        log.debug("sendSelectedIntentTraffic: NOT IMPLEMENTED YET");
+        log.debug("sendSelectedIntentTraffic: {}", selectedIntents);
+        msgHandler.sendHighlights(intentTraffic());
     }
 
     @Override
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/Traffic2Overlay.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/Traffic2Overlay.java
index 26692fe..057a5987 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/topo/Traffic2Overlay.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/Traffic2Overlay.java
@@ -29,7 +29,7 @@
     private final Logger log = LoggerFactory.getLogger(getClass());
 
     // NOTE: this must match the ID defined in topo2TrafficOverlay.js
-    private static final String OVERLAY_ID = "traffic-2-overlay";
+    public static final String OVERLAY_ID = "traffic-2-overlay";
 
     /**
      * Creates a traffic overlay instance.
diff --git a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/details/details.component.ts b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/details/details.component.ts
index 21ad66e..79118b8 100644
--- a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/details/details.component.ts
+++ b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/panel/details/details.component.ts
@@ -318,13 +318,13 @@
         const ids: string[] = [];
         Object.values(this.showDetails.propValues).forEach((v) => ids.push(v));
         if (path === 'relatedIntents' && this.showDetails.id === 'multiple') {
-            this.wss.sendEvent('requestRelatedIntents', {
+            this.wss.sendEvent('topo2RequestRelatedIntents', {
                 'ids': ids,
                 'hover': ''
             });
 
         } else if (path === 'create_h2h_flow' && this.showDetails.id === 'multiple') {
-            this.wss.sendEvent('addHostIntent', {
+            this.wss.sendEvent('topo2AddHostIntent', {
                 'one': ids[0],
                 'two': ids[1],
                 'ids': ids
@@ -332,7 +332,7 @@
 
         } else if (path === 'create_msrc_flow' && this.showDetails.id === 'multiple') {
             // Should only happen when there are 3 or more ids
-            this.wss.sendEvent('addMultiSourceIntent', {
+            this.wss.sendEvent('topo2AddMultiSourceIntent', {
                 'src': ids.slice(0, ids.length - 1),
                 'dst': ids[ids.length - 1],
                 'ids': ids