Topo2: Implemented Link Labels
JIRA-Tasks; ONOS-6387
Change-Id: I6d0292846349d73d6d274ae806d14736b2d3eb7c
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2-theme.css b/web/gui/src/main/webapp/app/view/topo2/topo2-theme.css
index a9ba89e..34422f7 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2-theme.css
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2-theme.css
@@ -293,35 +293,45 @@
/* TODO: Review for not-permitted links */
#ov-topo2 svg .link.not-permitted {
stroke: rgb(255,0,0);
- stroke-width: 5.0;
stroke-dasharray: 8 4;
}
#ov-topo2 svg .link.secondary {
- stroke-width: 3px;
stroke: rgba(0,153,51,0.5);
}
+#ov-topo2 svg .link.secondary.port-traffic-green {
+ stroke: rgb(0,153,51);
+}
+
+#ov-topo2 svg .link.secondary.port-traffic-yellow {
+ stroke: rgb(128,145,27);
+}
+
+#ov-topo2 svg .link.secondary.port-traffic-orange {
+ stroke: rgb(255, 137, 3);
+}
+
+#ov-topo2 svg .link.secondary.port-traffic-red {
+ stroke: rgb(183, 30, 21);
+}
+
/* Port traffic color visualization for Kbps, Mbps, and Gbps */
#ov-topo2 svg .link.secondary.port-traffic-Kbps {
stroke: rgb(0,153,51);
- stroke-width: 5.0;
}
#ov-topo2 svg .link.secondary.port-traffic-Mbps {
stroke: rgb(128,145,27);
- stroke-width: 6.5;
}
#ov-topo2 svg .link.secondary.port-traffic-Gbps {
stroke: rgb(255, 137, 3);
- stroke-width: 8.0;
}
#ov-topo2 svg .link.secondary.port-traffic-Gbps-choked {
stroke: rgb(183, 30, 21);
- stroke-width: 8.0;
}
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2Collection.js b/web/gui/src/main/webapp/app/view/topo2/topo2Collection.js
index c3662e6..1166fb8 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2Collection.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Collection.js
@@ -48,12 +48,14 @@
addModel: function (data) {
if (Object.getPrototypeOf(data) !== Object.prototype) {
this.models.push(data);
+ data.collection = this;
this._byId[data.get('id')] = data;
return data;
}
var CollectionModel = this.model;
var model = new CollectionModel(data, this);
+ model.collection = this;
this.models.push(model);
this._byId[data.id] = model;
@@ -96,6 +98,12 @@
filter: function (comparator) {
return _.filter(this.models, comparator);
},
+ empty: function () {
+ _.map(this.models, function (m) {
+ m.remove();
+ });
+ this._reset();
+ },
_reset: function () {
this._byId = [];
this.models = [];
@@ -111,7 +119,6 @@
.factory('Topo2Collection',
['Topo2Model', 'FnService',
function (_Model_, fn) {
-
Collection.extend = fn.extend;
Model = _Model_;
return Collection;
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2Event.js b/web/gui/src/main/webapp/app/view/topo2/topo2Event.js
index a03dab4..e8b8eac 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2Event.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Event.js
@@ -27,7 +27,7 @@
'use strict';
// injected refs
- var $log, wss, t2fs;
+ var $log, wss, t2fs, t2ovs;
// internal state
var handlerMap,
@@ -42,7 +42,8 @@
topo2CurrentRegion: t2fs,
topo2PeerRegions: t2fs,
- topo2UiModelEvent: t2fs
+ topo2UiModelEvent: t2fs,
+ topo2Highlights: t2ovs.showHighlights,
// Add further event names / module references as needed
};
@@ -83,12 +84,13 @@
angular.module('ovTopo2')
.factory('Topo2EventService', [
- '$log', 'WebSocketService', 'Topo2ForceService',
+ '$log', 'WebSocketService', 'Topo2ForceService', 'Topo2OverlayService',
- function (_$log_, _wss_, _t2fs_) {
+ function (_$log_, _wss_, _t2fs_, _t2ovs_) {
$log = _$log_;
wss = _wss_;
t2fs = _t2fs_;
+ t2ovs = _t2ovs_;
// deferred creation of handler map, so module references are good
createHandlerMap();
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2Label.js b/web/gui/src/main/webapp/app/view/topo2/topo2Label.js
index 8a47424..28f134c 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2Label.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Label.js
@@ -50,26 +50,39 @@
_iconG: {},
_labelG: {},
- initialize: function (data, node, options) {
+ initialize: function (data, node) {
this.parent = node;
- this.options = options || {};
-
t2zs.addZoomEventListener(this.setScale.bind(this));
+ this.beforeRender();
this.render();
+ this.afterRender();
},
- onChange: function (property, value, options) {
+ onChange: function (property) {
if (property === 'x' || property === 'y') {
this._position();
}
+
+ if (property === 'label') {
+ var width = this._labelG.text.node().getBBox().width + 20,
+ height = this._labelG.text.node().getBBox().height + 10;
+
+ this._labelG.text.text(this.get('label'));
+ this._labelG.rect.attr({
+ width: width,
+ height: height
+ }).style({
+ transform: sus.translate(-(width/2) + 'px', -(height/2) + 'px')
+ });
+ }
},
setPosition: function () {},
setScale: function () {},
applyStyles: function () {
- var styles = _.extend({}, defaultStyles, this.get('styles'));
+ var styles = _.extend({}, defaultStyles, this.get('styles') || {});
- if (this.get('text')) {
+ if (this.get('label')) {
this._labelG.text.style(styles.label.text);
this._labelG.rect.style(styles.label.rect);
}
@@ -79,21 +92,17 @@
this._iconG.rect.style(styles.icon.rect);
}
},
-
_position: function () {
this.el.style('transform', sus.translate(this.get('x') + 'px',
this.get('y') + 'px'));
},
- labelDimensions: function () {
- return this.content.node().getBBox();
- },
renderText: function () {
this._labelG.el = this.content.append('g')
.attr('class', 'label-group');
this._labelG.rect = this._labelG.el.append('rect');
this._labelG.text = this._labelG.el.append('text')
- .text(this.get('text'))
+ .text(this.get('label'))
.attr('y', '0.4em')
.style('text-anchor', 'middle');
@@ -127,6 +136,7 @@
transform: sus.translate(iconX, iconY)
});
},
+ beforeRender: function () {},
render: function () {
this.el = this.parent.append('g')
.attr('class', 'topo2-label')
@@ -146,6 +156,10 @@
this.applyStyles();
this.setPosition();
this.setScale();
+ },
+ afterRender: function () {},
+ remove: function () {
+ this.el.remove();
}
});
}
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2LabelCollection.js b/web/gui/src/main/webapp/app/view/topo2/topo2LabelCollection.js
index 895f491..ba05bb5 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2LabelCollection.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2LabelCollection.js
@@ -19,7 +19,6 @@
A collection of any type of label (Topo2Label, Topo2Badge, Topo2LinkLabel)
*/
-
(function () {
var instance;
@@ -32,12 +31,18 @@
var LabelCollection = Collection.extend({
initialize: function () {
instance = this;
+ },
+ addLabel: function (Model, label, targetNode, options) {
+ if (this._byId[label.id]) {
+ this.get(label.id).set(label);
+ } else {
+ var lab = new Model(label, targetNode, options)
+ this.add(lab);
+ }
}
});
return instance || new LabelCollection();
}
]);
-
-
-})();
\ No newline at end of file
+})();
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2Link.js b/web/gui/src/main/webapp/app/view/topo2/topo2Link.js
index 77892ab..eb0da8f 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2Link.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Link.js
@@ -22,7 +22,8 @@
(function () {
'use strict';
- var $log, Collection, Model, ts, sus, t2zs, t2vs, t2lps, fn, ps, t2mss;
+ var $log, Collection, Model, ts, sus, t2zs, t2vs, t2lps,
+ fn, ps, t2mss, t2ts;
var linkLabelOffset = '0.35em';
@@ -134,7 +135,8 @@
enhanced: this.get('enhanced'),
selected: this.get('selected'),
suppressedmax: this.get('mastership')
- }
+ },
+ (this.linkLabel) ? this.linkLabel.linkLabelCSSClass() : null
);
},
expected: function () {
@@ -154,6 +156,7 @@
// Update class names when the model changes
if (this.el) {
this.el.attr('class', this.svgClassName());
+ this.setScale();
}
},
enhance: function () {
@@ -239,7 +242,6 @@
};
},
setPosition: function () {
-
var multiline = this.get('multiline');
if (multiline) {
var offsetAmt = this.amt(multiline.deviceLinks, multiline.index);
@@ -256,6 +258,9 @@
this.el.attr(this.get('position'));
}
+ if (this.linkLabel) {
+ this.linkLabel.setPosition();
+ }
},
updatePortPosition: function () {
var sourcePos = this.locatePortLabel(1),
@@ -324,11 +329,39 @@
this.setVisibility();
this.setScale();
},
+ linkWidth: function () {
+ var width = widthRatio;
+ if (this.get('enhanced')) { width = 3.5; }
+ if (this.linkLabel) {
+ var scale = d3.scale.ordinal()
+ .rangeRoundPoints([4, 8]),
+ label = this.linkLabel.get('label').split(' ');
+
+ switch (t2ts.selectedTrafficOverlay()) {
+ case 'flowStatsBytes':
+ scale.domain(['KB', 'MB', 'GB']);
+ width = scale(label[1]);
+ break;
+ case 'portStatsBitSec':
+ scale.domain(['Kbps', 'Mbps', 'Gbps'])
+ width = scale(label[1]);
+ break;
+ case 'portStatsPktSec':
+ scale = d3.scale.linear()
+ .domain([1, 10, 100, 1000, 10000])
+ .range(d3.range(3.5, 9))
+ .clamp(true);
+ width = scale(parseInt(label[0]));
+ }
+ }
+
+ return width;
+ },
setScale: function () {
if (!this.el) return;
- var linkWidthRatio = this.get('enhanced') ? 3.5 : widthRatio;
+ var linkWidthRatio = this.linkWidth();
var width = linkScale(linkWidthRatio) / t2zs.scale();
this.el.attr('stroke-width', width + 'px');
@@ -342,6 +375,9 @@
this.setPosition();
+ if (this.linkLabel) {
+ this.linkLabel.setScale();
+ }
},
update: function () {
if (this.get('enhanced')) {
@@ -390,9 +426,9 @@
'$log', 'Topo2Collection', 'Topo2Model',
'ThemeService', 'SvgUtilService', 'Topo2ZoomService',
'Topo2ViewService', 'Topo2LinkPanelService', 'FnService', 'PrefsService',
- 'Topo2MastershipService',
+ 'Topo2MastershipService', 'Topo2TrafficService',
function (_$log_, _c_, _Model_, _ts_, _sus_,
- _t2zs_, _t2vs_, _t2lps_, _fn_, _ps_, _t2mss_) {
+ _t2zs_, _t2vs_, _t2lps_, _fn_, _ps_, _t2mss_, _t2ts_) {
$log = _$log_;
ts = _ts_;
@@ -405,6 +441,7 @@
fn = _fn_;
ps = _ps_;
t2mss = _t2mss_;
+ t2ts = _t2ts_;
return {
createLinkCollection: createLinkCollection
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2LinkLabel.js b/web/gui/src/main/webapp/app/view/topo2/topo2LinkLabel.js
index 6e6d7fa..66b06e5 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2LinkLabel.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2LinkLabel.js
@@ -30,8 +30,21 @@
className: 'topo2-linklabel',
maxHeight: 30,
minHeight: 20,
+ initialize: function (label, dom, options) {
+ this.link = options.link;
+ this.parent = dom;
+ this.super = this.constructor.__super__;
+ this.super.initialize.apply(this, arguments);
+ },
+ onChange: function () {
+ this.link.onChange();
+ this.constructor.__super__.onChange.apply(this, arguments);
+ },
+ linkLabelCSSClass: function () {
+ return this.get('css') || '';
+ },
setPosition: function () {
- var link = this.options.link;
+ var link = this.link;
this.set({
x: (link.source.x + link.target.x) / 2,
y: (link.source.y + link.target.y) / 2
@@ -40,6 +53,14 @@
setScale: function () {
this.content.style('transform',
'scale(' + t2zs.adjustmentScale(20, 30) + ')');
+ },
+ beforeRender: function () {
+ this.link.linkLabel = this;
+ },
+ remove: function () {
+ this.link.linkLabel = null;
+ this.link.onChange();
+ this.constructor.__super__.remove.apply(this, arguments);
}
});
}
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2Model.js b/web/gui/src/main/webapp/app/view/topo2/topo2Model.js
index 31ec0f6..efcd201 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2Model.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Model.js
@@ -23,13 +23,8 @@
'use strict';
function Model(attributes, collection) {
-
- var attrs = attributes || {};
this.attributes = {};
-
- attrs = angular.extend({}, attrs);
- this.set(attrs, { silent: true });
- this.collection = collection;
+ this.set(angular.extend({}, attributes || {}), { silent: true });
this.initialize.apply(this, arguments);
}
@@ -116,6 +111,11 @@
},
toJSON: function (options) {
return angular.copy(this.attributes);
+ },
+ remove: function () {
+ if (this.collection) {
+ this.collection.remove(this);
+ }
}
};
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2Overlay.js b/web/gui/src/main/webapp/app/view/topo2/topo2Overlay.js
index 808ee69..1227c30 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2Overlay.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Overlay.js
@@ -22,7 +22,7 @@
var t2os = 'Topo2OverlayService: ';
// injected refs
- var $log, $timeout, fs, gs, wss, t2kcs, api;
+ var $log, $timeout, fs, gs, wss, t2kcs, t2rs, t2lc, api, LinkLabel;
// internal state
var overlays = {},
@@ -134,27 +134,20 @@
}
function showHighlights(data) {
- function doHighlight() {
- _showHighlights(data);
- }
-
- // note: this allows the server-side event to add a manual delay
- // before invoking the highlight... this was (originally) to
- // allow for the re-creation of the DOM model, before trying
- // to reference elements. For Topo2, there may be a better
- // solution, making this piece of code redundant. Steven??
-
- if (data.delay) {
- $timeout(doHighlight, data.delay);
- } else {
- doHighlight();
- }
-
- }
-
- function _showHighlights(data) {
- // TODO: implement the highlighting .. see topoOverlay.js for example
$log.info('+++ TOPO 2 +++ show highlights', data);
+ t2lc.empty();
+ var linkLabelsDOM = d3.select('.topo2-linkLabels');
+ _.each(data.links, function (link) {
+ // TODO: Inconsistent host id's (currentRegion and LinkLabel)
+ var id = link.id.replace('/None/0', '/None').replace('-', '~'),
+ lab = t2rs.getLink(id);
+ // TODO: There's a bug in backend where link id is in reverse
+ if (lab) {
+ t2lc.addLabel(LinkLabel, link, linkLabelsDOM, {
+ link: lab
+ });
+ }
+ });
}
// ========================================================================
@@ -162,15 +155,20 @@
angular.module('ovTopo2')
.factory('Topo2OverlayService', [
'$log', '$timeout', 'FnService', 'GlyphService', 'WebSocketService',
- 'Topo2KeyCommandService',
+ 'Topo2KeyCommandService', 'Topo2RegionService', 'Topo2LabelCollection',
+ 'Topo2LinkLabel',
- function (_$log_, _$timeout_, _fs_, _gs_, _wss_, _t2kcs_) {
+ function (_$log_, _$timeout_, _fs_, _gs_, _wss_, _t2kcs_, _t2rs_,
+ _t2lc_, _t2ll_) {
$log = _$log_;
$timeout = _$timeout_;
fs = _fs_;
gs = _gs_;
wss = _wss_;
t2kcs = _t2kcs_;
+ t2rs = _t2rs_;
+ t2lc = _t2lc_;
+ LinkLabel = _t2ll_;
return {
register: register,
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2Traffic.js b/web/gui/src/main/webapp/app/view/topo2/topo2Traffic.js
index 6ca4d5e..1cb703f 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2Traffic.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Traffic.js
@@ -35,6 +35,7 @@
// internal state
var mode = null,
+ currentIndex = 0,
allIndex = 0;
// === -----------------------------------------------------
@@ -59,9 +60,14 @@
trafficType: allTrafficTypes[allIndex]
});
flash.flash(allTrafficMsgs[allIndex]);
+ currentIndex = allIndex;
allIndex = (allIndex + 1) % 3;
}
+ function selectedTrafficOverlay() {
+ return allTrafficTypes[currentIndex];
+ }
+
// === -----------------------------------------------------
angular.module('ovTopo2')
@@ -80,7 +86,8 @@
// invoked from toolbar overlay buttons or keystrokes
cancelTraffic: cancelTraffic,
- showAllTraffic: showAllTraffic
+ showAllTraffic: showAllTraffic,
+ selectedTrafficOverlay: selectedTrafficOverlay
}
}
]);