ONOS-2385 -- Bug fixes for removing individual links on the Topo View. 5 or more links between devices have a label indicating how many there are between each one.
Change-Id: I301ca6da8c453b54e16980a47e09dfd9f2f80f8b
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 c76d9d5..bbbea31 100644
--- a/web/gui/src/main/webapp/app/view/topo/topo.css
+++ b/web/gui/src/main/webapp/app/view/topo/topo.css
@@ -579,6 +579,26 @@
fill: #eee;
}
+/* Number of Links Labels */
+#ov-topo line.numLinkHash {
+ stroke-width: 3;
+}
+
+#ov-topo text.numLinkText {
+ font-size: 15pt;
+}
+
+#ov-topo text.numLinkText {
+ text-anchor: middle;
+}
+
+.light #ov-topo text.numLinkText {
+ fill: #444;
+}
+.dark #ov-topo text.numLinkText {
+ fill: #eee;
+}
+
/* ------------------------------------------------- */
/* Sprite Layer */
diff --git a/web/gui/src/main/webapp/app/view/topo/topoD3.js b/web/gui/src/main/webapp/app/view/topo/topoD3.js
index 4dcf3f0..d29748b 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoD3.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoD3.js
@@ -429,6 +429,100 @@
});
}
+ function labelPoint(linkPos) {
+ var lengthUpLine = 1 / 3,
+ dx = linkPos.x2 - linkPos.x1,
+ dy = linkPos.y2 - linkPos.y1,
+ movedX = dx * lengthUpLine,
+ movedY = dy * lengthUpLine;
+
+ return {
+ x: movedX,
+ y: movedY
+ };
+ }
+
+ function calcGroupPos(linkPos) {
+ var moved = labelPoint(linkPos);
+ return sus.translate(linkPos.x1 + moved.x, linkPos.y1 + moved.y);
+ }
+
+ // calculates where on the link that the hash line for 5+ label appears
+ function hashAttrs(linkPos) {
+ var hashLength = 25,
+ halfLength = hashLength / 2,
+ dx = linkPos.x2 - linkPos.x1,
+ dy = linkPos.y2 - linkPos.y1,
+ length = Math.sqrt((dx * dx) + (dy * dy)),
+ moveAmtX = (dx / length) * halfLength,
+ moveAmtY = (dy / length) * halfLength,
+ mid = labelPoint(linkPos),
+ angle = Math.atan(dy / dx) + 45;
+
+ return {
+ x1: mid.x - moveAmtX,
+ y1: mid.y - moveAmtY,
+ x2: mid.x + moveAmtX,
+ y2: mid.y + moveAmtY,
+ stroke: api.linkConfig()[ts.theme()].baseColor,
+ transform: 'rotate(' + angle + ',' + mid.x + ',' + mid.y + ')'
+ };
+ }
+
+ function textLabelPos(linkPos) {
+ var point = labelPoint(linkPos),
+ dist = 20;
+ return {
+ x: point.x + dist,
+ y: point.y + dist
+ };
+ }
+
+ function applyNumLinkLabels(data, lblsG) {
+ var labels = lblsG.selectAll('g.numLinkLabel')
+ .data(data, function (d) { return 'pair-' + d.id; }),
+ entering;
+
+ // update existing labels
+ labels.each(function (d) {
+ var el = d3.select(this);
+
+ el.attr({
+ transform: function (d) { return calcGroupPos(d.linkCoords); }
+ });
+ el.select('line')
+ .attr(hashAttrs(d.linkCoords));
+ el.select('text')
+ .attr(textLabelPos(d.linkCoords))
+ .text(d.num);
+ });
+
+ // add new labels
+ entering = labels
+ .enter()
+ .append('g')
+ .attr({
+ transform: function (d) { return calcGroupPos(d.linkCoords); },
+ id: function (d) { return 'pair-' + d.id; }
+ })
+ .classed('numLinkLabel', true);
+
+ entering.each(function (d) {
+ var el = d3.select(this);
+
+ el.append('line')
+ .classed('numLinkHash', true)
+ .attr(hashAttrs(d.linkCoords));
+ el.append('text')
+ .classed('numLinkText', true)
+ .attr(textLabelPos(d.linkCoords))
+ .text(d.num);
+ });
+
+ // remove old labels
+ labels.exit().remove();
+ }
+
// ==========================
// Module definition
@@ -475,7 +569,8 @@
linkEntering: linkEntering,
applyLinkLabels: applyLinkLabels,
transformLabel: transformLabel,
- applyPortLabels: applyPortLabels
+ applyPortLabels: applyPortLabels,
+ applyNumLinkLabels: applyNumLinkLabels
};
}]);
}());
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 48309f0..5fb6841 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoForce.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoForce.js
@@ -61,10 +61,11 @@
fTimer, // timer for delayed force layout
fNodesTimer, // timer for delayed nodes update
fLinksTimer, // timer for delayed links update
- dim; // the dimensions of the force layout [w,h]
+ dim, // the dimensions of the force layout [w,h]
+ linkNums = []; // array of link number labels
// SVG elements;
- var linkG, linkLabelG, portLabelG, nodeG;
+ var linkG, linkLabelG, numLinkLblsG, portLabelG, nodeG;
// D3 selections;
var link, linkLabel, node;
@@ -608,6 +609,7 @@
function calcPosition() {
var lines = this,
linkSrcId;
+ linkNums = [];
lines.each(function (d) {
if (d.type() === 'hostLink') {
d.position = getDefaultPos(d);
@@ -625,13 +627,14 @@
return link.source.id !== linkSrcId;
}
- angular.forEach(network.linksByDevice, function (linkArr) {
+ angular.forEach(network.linksByDevice, function (linkArr, key) {
var numLinks = linkArr.length,
link;
if (numLinks === 1) {
link = linkArr[0];
link.position = getDefaultPos(link);
+ link.position.multiLink = false;
} else if (numLinks >= 5) {
// this code is inefficient, in the future the way links
// are modeled will be changed
@@ -639,13 +642,18 @@
link.position = getDefaultPos(link);
link.position.multiLink = true;
});
+ linkNums.push({
+ id: key,
+ num: numLinks,
+ linkCoords: linkArr[0].position
+ });
} else {
- // calculate position of links
linkSrcId = null;
angular.forEach(linkArr, function (link, index) {
var offsetAmt = amt(numLinks, index),
needToFlip = normalizeLinkSrc(link);
link.position = calcMovement(link, offsetAmt, needToFlip);
+ link.position.multiLink = false;
});
}
});
@@ -696,6 +704,9 @@
// operate on both existing and new links:
//link.each(...)
+ // add labels for how many links are in a thick line
+ td3.applyNumLinkLabels(linkNums, numLinkLblsG);
+
// apply or remove labels
td3.applyLinkLabels();
@@ -764,6 +775,7 @@
if (link) {
link.call(calcPosition)
.attr(tickStuff.linkAttr);
+ td3.applyNumLinkLabels(linkNums, numLinkLblsG);
}
if (linkLabel) {
linkLabel.attr(tickStuff.linkLabelAttr);
@@ -855,7 +867,8 @@
posNode: tms.positionNode,
showHosts: function () { return showHosts; },
restyleLinkElement: restyleLinkElement,
- updateLinkLabelModel: updateLinkLabelModel
+ updateLinkLabelModel: updateLinkLabelModel,
+ linkConfig: function () { return linkConfig; }
};
}
@@ -897,7 +910,10 @@
},
opacifyMap: uplink.opacifyMap,
inLayer: fltr.inLayer,
- calcLinkPos: calcPosition
+ calcLinkPos: calcPosition,
+ applyNumLinkLabels: function () {
+ td3.applyNumLinkLabels(linkNums, numLinkLblsG);
+ }
};
}
@@ -975,6 +991,7 @@
linkG = forceG.append('g').attr('id', 'topo-links');
linkLabelG = forceG.append('g').attr('id', 'topo-linkLabels');
+ numLinkLblsG = forceG.append('g').attr('id', 'topo-numLinkLabels');
nodeG = forceG.append('g').attr('id', 'topo-nodes');
portLabelG = forceG.append('g').attr('id', 'topo-portLabels');
@@ -1026,7 +1043,9 @@
network.lookup = {};
network.revLinkToKey = {};
- linkG = linkLabelG = nodeG = portLabelG = null;
+ linkNums = [];
+
+ linkG = linkLabelG = numLinkLblsG = nodeG = portLabelG = null;
link = linkLabel = node = null;
force = drag = null;
diff --git a/web/gui/src/main/webapp/app/view/topo/topoModel.js b/web/gui/src/main/webapp/app/view/topo/topoModel.js
index c42e3ec..fb98fc2 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoModel.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoModel.js
@@ -301,7 +301,10 @@
// remove link out of aggregate linksByDevice list
var linksForDevPair = linksByDevice[ldata.devicePair],
rmvIdx = fs.find(ldata.key, linksForDevPair, 'key');
- linksForDevPair.splice(rmvIdx, 1);
+ if (rmvIdx >= 0) {
+ linksForDevPair.splice(rmvIdx, 1);
+ }
+ ldata.position.multilink = linksForDevPair.length >= 5;
if (link) {
// remove fromSource
diff --git a/web/gui/src/main/webapp/app/view/topo/topoOblique.js b/web/gui/src/main/webapp/app/view/topo/topoOblique.js
index 326271a..e84b117 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoOblique.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoOblique.js
@@ -157,7 +157,8 @@
api.link().transition()
.duration(time)
.call(api.calcLinkPos)
- .attr(api.tickStuff.linkAttr);
+ .attr(api.tickStuff.linkAttr)
+ .call(api.applyNumLinkLabels);
api.linkLabel().transition()
.duration(time)
.attr(api.tickStuff.linkLabelAttr);