ONOS-3129 : GUI Intents to Topo :: part 2 - pushing data through topo view and having the intent path show up.

Change-Id: Ie626a46e189d2b704d683e0f58762a68cd3d3a7d
diff --git a/core/api/src/main/java/org/onosproject/ui/table/cell/AppIdFormatter.java b/core/api/src/main/java/org/onosproject/ui/table/cell/AppIdFormatter.java
index 0e1c248..f7947a7 100644
--- a/core/api/src/main/java/org/onosproject/ui/table/cell/AppIdFormatter.java
+++ b/core/api/src/main/java/org/onosproject/ui/table/cell/AppIdFormatter.java
@@ -27,6 +27,7 @@
     // non-instantiable
     private AppIdFormatter() { }
 
+    // NOTE: do not change this format; we parse it on the client side.
     @Override
     protected String nonNullFormat(Object value) {
         ApplicationId appId = (ApplicationId) value;
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 292a5f9..82b7a78 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
@@ -27,6 +27,7 @@
 import org.onosproject.cluster.ControllerNode;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
+import org.onosproject.core.DefaultApplicationId;
 import org.onosproject.event.Event;
 import org.onosproject.mastership.MastershipAdminService;
 import org.onosproject.mastership.MastershipEvent;
@@ -49,11 +50,14 @@
 import org.onosproject.net.host.HostEvent;
 import org.onosproject.net.host.HostListener;
 import org.onosproject.net.intent.HostToHostIntent;
+import org.onosproject.net.intent.Intent;
 import org.onosproject.net.intent.IntentEvent;
 import org.onosproject.net.intent.IntentListener;
+import org.onosproject.net.intent.Key;
 import org.onosproject.net.intent.MultiPointToSinglePointIntent;
 import org.onosproject.net.link.LinkEvent;
 import org.onosproject.net.link.LinkListener;
+import org.onosproject.ui.JsonUtils;
 import org.onosproject.ui.RequestHandler;
 import org.onosproject.ui.UiConnection;
 import org.onosproject.ui.impl.TrafficMonitor.Mode;
@@ -77,7 +81,9 @@
 import static org.onosproject.cluster.ClusterEvent.Type.INSTANCE_ADDED;
 import static org.onosproject.net.DeviceId.deviceId;
 import static org.onosproject.net.HostId.hostId;
-import static org.onosproject.net.device.DeviceEvent.Type.*;
+import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_ADDED;
+import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_UPDATED;
+import static org.onosproject.net.device.DeviceEvent.Type.PORT_STATS_UPDATED;
 import static org.onosproject.net.host.HostEvent.Type.HOST_ADDED;
 import static org.onosproject.net.link.LinkEvent.Type.LINK_ADDED;
 import static org.onosproject.ui.JsonUtils.envelope;
@@ -98,6 +104,7 @@
     private static final String REQ_NEXT_INTENT = "requestNextRelatedIntent";
     private static final String REQ_PREV_INTENT = "requestPrevRelatedIntent";
     private static final String REQ_SEL_INTENT_TRAFFIC = "requestSelectedIntentTraffic";
+    private static final String SEL_INTENT = "selectIntent";
     private static final String REQ_ALL_FLOW_TRAFFIC = "requestAllFlowTraffic";
     private static final String REQ_ALL_PORT_TRAFFIC = "requestAllPortTraffic";
     private static final String REQ_DEV_LINK_FLOWS = "requestDeviceLinkFlows";
@@ -118,9 +125,13 @@
     private static final String SPRITE_LIST_RESPONSE = "spriteListResponse";
     private static final String SPRITE_DATA_RESPONSE = "spriteDataResponse";
     private static final String UPDATE_INSTANCE = "updateInstance";
+    private static final String TOPO_START_DONE = "topoStartDone";
 
     // fields
     private static final String ID = "id";
+    private static final String KEY = "key";
+    private static final String APP_ID = "appId";
+    private static final String APP_NAME = "appName";
     private static final String DEVICE = "device";
     private static final String HOST = "host";
     private static final String CLASS = "class";
@@ -136,7 +147,7 @@
     private static final String DEACTIVATE = "deactivate";
 
 
-    private static final String APP_ID = "org.onosproject.gui";
+    private static final String MY_APP_ID = "org.onosproject.gui";
 
     private static final long TRAFFIC_PERIOD = 5000;
     private static final long SUMMARY_PERIOD = 30000;
@@ -177,7 +188,7 @@
     @Override
     public void init(UiConnection connection, ServiceDirectory directory) {
         super.init(connection, directory);
-        appId = directory.get(CoreService.class).registerApplication(APP_ID);
+        appId = directory.get(CoreService.class).registerApplication(MY_APP_ID);
         traffic = new TrafficMonitor(TRAFFIC_PERIOD, servicesBundle, this);
     }
 
@@ -214,6 +225,7 @@
                 new ReqNextIntent(),
                 new ReqPrevIntent(),
                 new ReqSelectedIntentTraffic(),
+                new SelIntent(),
 
                 new CancelTraffic()
         );
@@ -242,6 +254,7 @@
             sendAllDevices();
             sendAllLinks();
             sendAllHosts();
+            sendTopoStartDone();
         }
     }
 
@@ -527,6 +540,31 @@
         }
     }
 
+    private final class SelIntent extends RequestHandler {
+        private SelIntent() {
+            super(SEL_INTENT);
+        }
+
+        @Override
+        public void process(long sid, ObjectNode payload) {
+            int appId = Integer.parseInt(string(payload, APP_ID));
+            String appName = string(payload, APP_NAME);
+            ApplicationId applicId = new DefaultApplicationId(appId, appName);
+            long intentKey = Long.decode(string(payload, KEY));
+
+            Key key = Key.of(intentKey, applicId);
+            log.debug("Attempting to select intent key={}", key);
+
+            Intent intent = intentService.getIntent(key);
+            if (intent == null) {
+                log.debug("no such intent found!");
+            } else {
+                log.debug("starting to monitor intent {}", key);
+                traffic.monitor(intent);
+            }
+        }
+    }
+
     private final class CancelTraffic extends RequestHandler {
         private CancelTraffic() {
             super(CANCEL_TRAFFIC);
@@ -626,6 +664,9 @@
         return hostIds;
     }
 
+    private void sendTopoStartDone() {
+        sendMessage(JsonUtils.envelope(TOPO_START_DONE, objectNode()));
+    }
 
     private synchronized void startSummaryMonitoring() {
         stopSummaryMonitoring();
diff --git a/web/gui/src/main/webapp/app/view/intent/intent.js b/web/gui/src/main/webapp/app/view/intent/intent.js
index cec9062..3ed9cf2 100644
--- a/web/gui/src/main/webapp/app/view/intent/intent.js
+++ b/web/gui/src/main/webapp/app/view/intent/intent.js
@@ -23,12 +23,21 @@
 
     angular.module('ovIntent', [])
         .controller('OvIntentCtrl',
-        ['$log', '$scope', 'TableBuilderService',
+        ['$log', '$scope', 'TableBuilderService', 'NavService',
 
-        function ($log, $scope, tbs) {
+        function ($log, $scope, tbs, ns) {
 
             function selCb($event, row) {
                 $log.debug('Got a click on:', row);
+                var m = /(\d+)\s:\s(.*)/.exec(row.appId),
+                    id = m ? m[1] : null,
+                    name = m ? m[2] : null;
+
+                $scope.intentData = m ? {
+                    intentAppId: id,
+                    intentAppName: name,
+                    intentKey: row.key
+                } : null;
             }
 
             tbs.buildTable({
@@ -41,8 +50,9 @@
             $scope.topoTip = 'Show selected intent on topology view';
 
             $scope.showIntent = function () {
-                // TODO: navigate to topology view with selected intent context
-                $log.debug("+++ showIntent +++", $scope.selId);
+                var d = $scope.intentData;
+                $log.debug("+++ showIntent +++", d);
+                ns.navTo('topo', d);
             };
 
             $log.log('OvIntentCtrl has been created');
diff --git a/web/gui/src/main/webapp/app/view/topo/topo.js b/web/gui/src/main/webapp/app/view/topo/topo.js
index 0dfd628..7ddfd13 100644
--- a/web/gui/src/main/webapp/app/view/topo/topo.js
+++ b/web/gui/src/main/webapp/app/view/topo/topo.js
@@ -412,6 +412,12 @@
         flash.enable(true);
     }
 
+    function topoStartDone() {
+        var d = $scope.intentData;
+        if (d) {
+            tts.selectIntent(d);
+        }
+    }
 
     // --- Controller Definition -----------------------------------------
 
@@ -430,7 +436,8 @@
                   _zs_, _gs_, _ms_, _sus_, _flash_, _wss_, _ps_, _tes_, _tfs_,
                   _tps_, _tis_, _tss_, _tls_, _tts_, _tos_, _fltr_, _ttbs_, tspr,
                   _ttip_, _tov_) {
-            var projection,
+            var params = $loc.search(),
+                projection,
                 dim,
                 uplink = {
                     // provides function calls back into this space
@@ -438,7 +445,8 @@
                     projection: function () { return projection; },
                     zoomLayer: function () { return zoomLayer; },
                     zoomer: function () { return zoomer; },
-                    opacifyMap: opacifyMap
+                    opacifyMap: opacifyMap,
+                    topoStartDone: topoStartDone
                 };
 
             $scope = _$scope_;
@@ -469,6 +477,14 @@
             ttip = _ttip_;
             tov = _tov_;
 
+            if (params.intentKey && params.intentAppId && params.intentAppName) {
+                $scope.intentData = {
+                    key: params.intentKey,
+                    appId: params.intentAppId,
+                    appName: params.intentAppName
+                };
+            }
+
             $scope.notifyResize = function () {
                 svgResized(fs.windowSize(mast.mastHeight()));
             };
diff --git a/web/gui/src/main/webapp/app/view/topo/topoEvent.js b/web/gui/src/main/webapp/app/view/topo/topoEvent.js
index 5fd38bf..2957629 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoEvent.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoEvent.js
@@ -60,6 +60,8 @@
             updateLink: tfs,
             removeLink: tfs,
 
+            topoStartDone: tfs,
+
             spriteListResponse: tspr,
             spriteDataResponse: tspr
         };
diff --git a/web/gui/src/main/webapp/app/view/topo/topoForce.js b/web/gui/src/main/webapp/app/view/topo/topoForce.js
index f00b87f..844d7dc 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoForce.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoForce.js
@@ -240,6 +240,11 @@
         }
     }
 
+    function topoStartDone(data) {
+        // called when the initial barrage of data has been sent from server
+        uplink.topoStartDone();
+    }
+
     // ========================
 
     function nodeById(id) {
@@ -1140,7 +1145,8 @@
                 removeHost: removeHost,
                 addLink: addLink,
                 updateLink: updateLink,
-                removeLink: removeLink
+                removeLink: removeLink,
+                topoStartDone: topoStartDone
             };
         }]);
 }());
diff --git a/web/gui/src/main/webapp/app/view/topo/topoOverlay.js b/web/gui/src/main/webapp/app/view/topo/topoOverlay.js
index 9a3b435..2dee4c4 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoOverlay.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoOverlay.js
@@ -34,7 +34,8 @@
 
     // internal state
     var overlays = {},
-        current = null;
+        current = null,
+        reset = true;
 
     function error(fn, msg) {
         $log.error(tos + fn + '(): ' + msg);
@@ -144,7 +145,8 @@
             payload[op] = oid;
         }
 
-        if (!same) {
+        if (reset || !same) {
+            reset = false;
             current && doop('deactivate');
             current = overlays[id];
             current && doop('activate');
@@ -390,6 +392,7 @@
                 tbSelection: tbSelection,
                 installButtons: installButtons,
                 addDetailButton: addDetailButton,
+                resetOnToolbarDestroy: function () { reset = true; },
                 hooks: {
                     escape: escapeHook,
                     emptySelect: emptySelectHook,
diff --git a/web/gui/src/main/webapp/app/view/topo/topoToolbar.js b/web/gui/src/main/webapp/app/view/topo/topoToolbar.js
index 3928cd2..0628544 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoToolbar.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoToolbar.js
@@ -243,6 +243,7 @@
 
     function destroyToolbar() {
         tbs.destroyToolbar(name);
+        tov.resetOnToolbarDestroy();
     }
 
     // allows us to ensure the button states track key strokes
diff --git a/web/gui/src/main/webapp/app/view/topo/topoTraffic.js b/web/gui/src/main/webapp/app/view/topo/topoTraffic.js
index ca37936..ff690c4 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoTraffic.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoTraffic.js
@@ -152,6 +152,14 @@
         }
     }
 
+    // force the system to create a single intent selection
+    function selectIntent(data) {
+        trafficMode = 'intents';
+        hoverMode = null;
+        wss.sendEvent('selectIntent', data);
+        flash.flash('Selecting Intent ' + data.key);
+    }
+
 
     // === ------------------------------------------------------
     // action buttons on detail panel (multiple selection)
@@ -207,6 +215,7 @@
                 showPrevIntent: showPrevIntent,
                 showNextIntent: showNextIntent,
                 showSelectedIntentTraffic: showSelectedIntentTraffic,
+                selectIntent: selectIntent,
 
                 // invoked from mouseover/mouseout and selection change
                 requestTrafficForMode: requestTrafficForMode,