GUI2 Added intents to Traffic2 Monitor

Change-Id: I92744f214b96b6abcb09fac3afa9c497780065e5
diff --git a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/topology/topology.component.spec.ts b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/topology/topology.component.spec.ts
index c7b6c15..f38604f 100644
--- a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/topology/topology.component.spec.ts
+++ b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/topology/topology.component.spec.ts
@@ -104,7 +104,9 @@
     }
 }
 
-class MockTrafficService {}
+class MockTrafficService {
+    init(force: ForceSvgComponent) {}
+}
 
 class MockLayoutService {}
 
diff --git a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/topology/topology.component.ts b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/topology/topology.component.ts
index 8ce1a6e..97b8764 100644
--- a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/topology/topology.component.ts
+++ b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/topology/topology.component.ts
@@ -65,7 +65,7 @@
     RESETZOOM_BTN,
     SUMMARY_TOGGLE
 } from '../panel/toolbar/toolbar.component';
-import {TrafficService} from '../traffic.service';
+import {TrafficService, TrafficType} from '../traffic.service';
 import {ZoomableDirective} from '../layer/zoomable.directive';
 import {MapObject} from '../layer/maputils';
 import {LayoutService, LayoutType} from '../layout.service';
@@ -86,6 +86,7 @@
 const PREF_SUMMARY = 'summary';
 const PREF_TOOLBAR = 'toolbar';
 const PREF_PINNED = 'pinned';
+const PREF_TRAFFIC = 'traffic';
 
 const BACKGROUND_ELEMENTS = [
     'svg topo2',
@@ -111,6 +112,7 @@
     toolbar: number;
     grid: number;
     pinned: number;
+    traffic: number;
 }
 
 /**
@@ -166,7 +168,9 @@
         spr: 0,
         summary: 1,
         toolbar: 0,
-        grid: 0
+        grid: 0,
+        pinned: 0,
+        traffic: 2 // default to PORTSTATSPKTSEC, as it will iterate over to 0 on init
     };
 
     mapIdState: MapObject = <MapObject>{
@@ -260,6 +264,14 @@
         }
     }
 
+    private static trafficTypeFlashMessage(index: number): string {
+        switch (index) {
+            case 0: return 'tr_fl_fstats_bytes';
+            case 1: return 'tr_fl_pstats_bits';
+            case 2: return 'tr_fl_pstats_pkts';
+        }
+    }
+
     /**
      * Pass the list of Key Commands to the KeyService, and initialize the Topology
      * Service - which communicates with through the WebSocket to the ONOS server
@@ -276,6 +288,7 @@
         this.ps.addListener((data) => this.prefsUpdateHandler(data));
         this.prefsState = this.ps.getPrefs(TOPO2_PREFS, this.prefsState);
         this.mapIdState = this.ps.getPrefs(TOPO_MAPID_PREFS, this.mapIdState);
+        this.trs.init(this.force);
 
         this.log.debug('Topology component initialized');
     }
@@ -311,6 +324,7 @@
     ngOnDestroy() {
         this.ts.destroy();
         this.ps.removeListener((data) => this.prefsUpdateHandler(data));
+        this.trs.destroy();
         this.log.debug('Topology component destroyed');
     }
 
@@ -373,7 +387,7 @@
                 this.cancelTraffic();
                 break;
             case ALL_TRAFFIC:
-                this.monitorAllTraffic();
+                this.cycleTrafficTypeDisplay();
                 break;
             case QUICKHELP_BTN:
                 this.ks.quickHelpShown = true;
@@ -397,7 +411,7 @@
      */
     actionMap() {
         return {
-            A: [() => {this.monitorAllTraffic(); }, 'Monitor all traffic'],
+            A: [() => {this.cycleTrafficTypeDisplay(); }, 'Monitor all traffic'],
             B: [(token) => {this.toggleBackground(token); }, 'Toggle background'],
             D: [(token) => {this.toggleDetails(token); }, 'Toggle details panel'],
             E: [() => {this.equalizeMasters(); }, 'Equalize mastership roles'],
@@ -527,6 +541,15 @@
         this.log.debug('Cycling grid display', old, next);
     }
 
+    protected cycleTrafficTypeDisplay() {
+        const old: TrafficType.Enum = this.prefsState.traffic; // by number
+        const next = TrafficType.next(old);
+        this.flashMsg = this.lionFn(TopologyComponent.trafficTypeFlashMessage(next));
+        this.updatePrefsState(PREF_TRAFFIC, next);
+        this.trs.requestTraffic(next);
+        this.log.debug('Cycling traffic display', old, next);
+    }
+
     /**
      * When the button is clicked on the toolbar or the B key is pressed
      * 1) Find the inverse of the current state (held as 1 or 0)
@@ -674,15 +697,7 @@
     nodeSelected(nodesOrLink: UiElement[]) {
         this.details.ngOnChanges({'selectedNodes':
             new SimpleChange(undefined, nodesOrLink, true)});
-    }
-
-    /**
-     * Enable traffic monitoring
-     */
-    monitorAllTraffic() {
-        // TODO: Implement support for toggling between bits, packets and octets
-        this.flashMsg = this.lionFn('tr_fl_pstats_bits');
-        this.trs.init(this.force);
+        this.trs.cancelTraffic();
     }
 
     /**
@@ -690,7 +705,7 @@
      */
     cancelTraffic() {
         this.flashMsg = this.lionFn('fl_monitoring_canceled');
-        this.trs.destroy();
+        this.trs.cancelTraffic();
     }
 
     changeMap(map: MapObject) {
diff --git a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/traffic.service.ts b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/traffic.service.ts
index 6864414..74afe35 100644
--- a/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/traffic.service.ts
+++ b/web/gui2-topo-lib/projects/gui2-topo-lib/src/lib/traffic.service.ts
@@ -17,25 +17,42 @@
 import {LogService, WebSocketService} from 'gui2-fw-lib';
 import {ForceSvgComponent} from './layer/forcesvg/forcesvg.component';
 
-export enum TrafficType {
-    IDLE,
-    FLOWSTATSBYTES = 'flowStatsBytes',
-    PORTSTATSBITSEC = 'portStatsBitSec',
-    PORTSTATSPKTSEC = 'portStatsPktSec',
+export namespace TrafficType {
+    /**
+     * Toggle state for how the traffic should be displayed
+     */
+    export enum Enum { // Do not add an alias - they need to be number indexed
+        FLOWSTATSBYTES, // 0 flowStatsBytes
+        PORTSTATSBITSEC, // 1 portStatsBitSec
+        PORTSTATSPKTSEC // 2 portStatsPktSec
+    }
+
+    /**
+     * Add the method 'next()' to the TrafficType enum above
+     */
+    export function next(current: Enum) {
+        if (current === Enum.FLOWSTATSBYTES) {
+            return Enum.PORTSTATSBITSEC;
+        } else if (current === Enum.PORTSTATSBITSEC) {
+            return Enum.PORTSTATSPKTSEC;
+        } else if (current === Enum.PORTSTATSPKTSEC) {
+            return Enum.FLOWSTATSBYTES;
+        } else { // e.g. undefined
+            return Enum.PORTSTATSBITSEC;
+        }
+    }
+
+    export function literal(type: Enum) {
+        if (type === Enum.FLOWSTATSBYTES) {
+            return 'flowStatsBytes';
+        } else if (type === Enum.PORTSTATSBITSEC) {
+            return 'portStatsBitSec';
+        } else if (type === Enum.PORTSTATSPKTSEC) {
+            return 'portStatsPktSec';
+        }
+    }
 }
 
-const ALL_TRAFFIC_TYPES = [
-    TrafficType.FLOWSTATSBYTES,
-    TrafficType.PORTSTATSBITSEC,
-    TrafficType.PORTSTATSPKTSEC
-];
-
-const ALL_TRAFFIC_MSGS = [
-    'Flow Stats (bytes)',
-    'Port Stats (bits / second)',
-    'Port Stats (packets / second)',
-];
-
 /**
  * ONOS GUI -- Traffic Service Module.
  */
@@ -43,6 +60,7 @@
 export class TrafficService {
     private handlers: string[] = [];
     private openListener: any;
+    private trafficType: TrafficType.Enum;
 
     constructor(
         protected log: LogService,
@@ -64,26 +82,31 @@
         // in case we fail over to a new server,
         // listen for wsock-open events
         this.openListener = this.wss.addOpenListener(() => this.wsOpen);
-
-        // tell the server we are ready to receive topology events
-        this.wss.sendEvent('topo2RequestAllTraffic', {
-            trafficType: TrafficType.FLOWSTATSBYTES
-        });
-        this.log.debug('Topo2Traffic: Show All Traffic');
     }
 
     destroy() {
-        this.wss.sendEvent('topo2CancelTraffic', {});
         this.wss.unbindHandlers(this.handlers);
         this.handlers.pop();
-        this.log.debug('Traffic monitoring canceled');
     }
 
     wsOpen(host: string, url: string) {
         this.log.debug('topo2RequestAllTraffic: WSopen - cluster node:', host, 'URL:', url);
         // tell the server we are ready to receive topo events
         this.wss.sendEvent('topo2RequestAllTraffic', {
-            trafficType: TrafficType.FLOWSTATSBYTES
+            trafficType: TrafficType.literal(this.trafficType)
         });
     }
+
+    requestTraffic(trafficType: TrafficType.Enum) {
+        // tell the server we are ready to receive topology events
+        this.wss.sendEvent('topo2RequestAllTraffic', {
+            trafficType: TrafficType.literal(trafficType)
+        });
+        this.log.debug('Topo2Traffic: Show', trafficType);
+    }
+
+    cancelTraffic() {
+        this.wss.sendEvent('topo2CancelTraffic', {});
+        this.log.debug('Traffic monitoring canceled');
+    }
 }