GUI -- Topology - PortStats traffic: enhanced to change link style based on values (KBps, MBps, GBps).

Change-Id: I67a62ef85a292db431f3d32eefefd81d4e564794
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 c19e8bc..b006b1e 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
@@ -120,6 +120,7 @@
     private static final double MB = 1024 * KB;
     private static final double GB = 1024 * MB;
 
+    // TODO: change GB to Gb (when we compute bits/second)
     private static final String GB_UNIT = "GB";
     private static final String MB_UNIT = "MB";
     private static final String KB_UNIT = "KB";
@@ -763,6 +764,7 @@
 
     // Poor-mans formatting to get the labels with byte counts looking nice.
     private String formatBytes(long bytes) {
+        // TODO: multiply everything by 8 to compute bits/second
         String unit;
         double value;
         if (bytes > GB) {
diff --git a/web/gui/src/main/webapp/app/fw/util/fn.js b/web/gui/src/main/webapp/app/fw/util/fn.js
index 56e105b..6c7bb2a 100644
--- a/web/gui/src/main/webapp/app/fw/util/fn.js
+++ b/web/gui/src/main/webapp/app/fw/util/fn.js
@@ -176,6 +176,17 @@
         return Number(elem.style(prop).replace(/px$/, ''));
     }
 
+    function endsWith(str, suffix) {
+        return str.indexOf(suffix, str.length - suffix.length) !== -1;
+    }
+
+    function parseBitRate(str) {
+        return Number(str.replace(/,/, '')
+                        .replace(/\s+.bps/i, '')
+                        .replace(/\.\d*/, ''));
+    }
+
+
     angular.module('onosUtil')
         .factory('FnService', ['$window', function (_$window_) {
             $window = _$window_;
@@ -196,7 +207,9 @@
                 isEmptyObject: isEmptyObject,
                 cap: cap,
                 noPx: noPx,
-                noPxStyle: noPxStyle
+                noPxStyle: noPxStyle,
+                endsWith: endsWith,
+                parseBitRate: parseBitRate
             };
     }]);
 
diff --git a/web/gui/src/main/webapp/app/view/topo/topo.css b/web/gui/src/main/webapp/app/view/topo/topo.css
index d6452b7..81212f3 100644
--- a/web/gui/src/main/webapp/app/view/topo/topo.css
+++ b/web/gui/src/main/webapp/app/view/topo/topo.css
@@ -458,6 +458,45 @@
     stroke: rgba(121,231,158,0.5);
 }
 
+/* Port traffic color visualization for KBps, MBps, and GBps */
+
+.light #ov-topo svg .link.secondary.port-traffic-KBps {
+    stroke: rgb(0,153,51);
+    stroke-width: 5.0;
+}
+.dark #ov-topo svg .link.secondary.port-traffic-KBps {
+    stroke: rgb(98, 153, 118);
+    stroke-width: 5.0;
+}
+
+.light #ov-topo svg .link.secondary.port-traffic-MBps {
+    stroke: rgb(128,145,27);
+    stroke-width: 6.5;
+}
+.dark #ov-topo svg .link.secondary.port-traffic-MBps {
+    stroke: rgb(91, 109, 54);
+    stroke-width: 6.5;
+}
+
+.light #ov-topo svg .link.secondary.port-traffic-GBps {
+    stroke: rgb(255, 137, 3);
+    stroke-width: 8.0;
+}
+.dark #ov-topo svg .link.secondary.port-traffic-GBps {
+    stroke: rgb(174, 119, 55);
+    stroke-width: 8.0;
+}
+
+.light #ov-topo svg .link.secondary.port-traffic-GBps-choked {
+    stroke: rgb(183, 30, 21);
+    stroke-width: 8.0;
+}
+.dark #ov-topo svg .link.secondary.port-traffic-GBps-choked {
+    stroke: rgb(127, 40, 39);
+    stroke-width: 8.0;
+}
+
+
 #ov-topo svg .link.primary {
     stroke-width: 4px;
 }
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 0ed005e..80d0b4b 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoTraffic.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoTraffic.js
@@ -59,13 +59,31 @@
         //  labels to them, if they are defined.
         paths.forEach(function (p) {
             var n = p.links.length,
-                i, ldata;
+                i, ldata, lab, units, magnitude, portcls;
 
             for (i=0; i<n; i++) {
                 ldata = api.findLinkById(p.links[i]);
-                if (ldata && ldata.el) {
+                lab = p.labels[i];
+
+                if (ldata && !ldata.el.empty()) {
                     ldata.el.classed(p.class, true);
-                    ldata.label = p.labels[i];
+                    ldata.label = lab;
+
+                    // TODO: change this to 'bps' when we measure bits/sec
+                    if (fs.endsWith(lab, 'Bps')) {
+                        // inject additional styling for port-based traffic
+                        units = lab.substring(lab.length-4);
+                        portcls = 'port-traffic-' + units;
+
+                        // for GBps
+                        if (units.substring(0,1) === 'G') {
+                            magnitude = fs.parseBitRate(lab);
+                            if (magnitude >= 9) {
+                                portcls += '-choked'
+                            }
+                        }
+                        ldata.el.classed(portcls, true);
+                    }
                 }
             }
         });
diff --git a/web/gui/src/main/webapp/tests/app/fw/util/fn-spec.js b/web/gui/src/main/webapp/tests/app/fw/util/fn-spec.js
index d04b3c2..c9f3bef 100644
--- a/web/gui/src/main/webapp/tests/app/fw/util/fn-spec.js
+++ b/web/gui/src/main/webapp/tests/app/fw/util/fn-spec.js
@@ -214,7 +214,7 @@
             'isF', 'isA', 'isS', 'isO', 'contains',
             'areFunctions', 'areFunctionsNonStrict', 'windowSize', 'isMobile',
             'find', 'inArray', 'removeFromArray', 'isEmptyObject', 'cap',
-            'noPx', 'noPxStyle'
+            'noPx', 'noPxStyle', 'endsWith', 'parseBitRate'
         ])).toBeTruthy();
     });
 
@@ -413,4 +413,34 @@
         d3.select('#fooElem').remove();
     });
 
+    // === Tests for endsWith()
+    it('should return true if string ends with foo', function () {
+        expect(fs.endsWith("barfoo", "foo")).toBe(true);
+    });
+
+    it('should return false if string doesnt end with foo', function () {
+        expect(fs.endsWith("barfood", "foo")).toBe(false);
+    });
+
+    // === Tests for parseBitRate()
+    it('should return 5 - a', function () {
+        expect(fs.parseBitRate('5.47 KBps')).toBe(5);
+    });
+
+    it('should return 5 - b', function () {
+        expect(fs.parseBitRate('5. KBps')).toBe(5);
+    });
+
+    it('should return 5 - c', function () {
+        expect(fs.parseBitRate('5 KBps')).toBe(5);
+    });
+
+    it('should return 5 - d', function () {
+        expect(fs.parseBitRate('5 Kbps')).toBe(5);
+    });
+
+    it('should return 2001', function () {
+        expect(fs.parseBitRate('2,001.59 Gbps')).toBe(2001);
+    });
 });
+