more refactoring of topology code
diff --git a/web/ons-demo/js/topology.js b/web/ons-demo/js/topology.js
index b7a9112..e934972 100644
--- a/web/ons-demo/js/topology.js
+++ b/web/ons-demo/js/topology.js
@@ -4,298 +4,12 @@
flow related topology is in flows.js
***************************************************************************************************/
-function createLinkMap(links) {
- var linkMap = {};
- links.forEach(function (link) {
- var srcDPID = link['src-switch'];
- var dstDPID = link['dst-switch'];
-
- var srcMap = linkMap[srcDPID] || {};
-
- srcMap[dstDPID] = link;
-
- linkMap[srcDPID] = srcMap;
- });
- return linkMap;
-}
-
-
-// removes links from the pending list that are now in the model
-function reconcilePendingLinks(model) {
- var links = [];
- model.links.forEach(function (link) {
- links.push(link);
- delete pendingLinks[makeLinkKey(link)]
- })
- var linkId;
- for (linkId in pendingLinks) {
- links.push(pendingLinks[linkId]);
- }
- return links
-}
-
-
-function createTopologyView() {
-
- window.addEventListener('resize', function () {
- // this is too slow. instead detect first resize event and hide the paths that have explicit matrix applied
- // either that or is it possible to position the paths so they get the automatic transform as well?
-// updateTopology();
- });
-
- var svg = d3.select('#svg-container').append('svg:svg');
-
- svg.append("svg:defs").append("svg:marker")
- .attr("id", "arrow")
- .attr("viewBox", "0 -5 10 10")
- .attr("refX", -1)
- .attr("markerWidth", 5)
- .attr("markerHeight", 5)
- .attr("orient", "auto")
- .append("svg:path")
- .attr("d", "M0,-3L10,0L0,3");
-
- return svg.append('svg:svg').attr('id', 'viewBox').attr('viewBox', '0 0 1000 1000').attr('preserveAspectRatio', 'none').
- attr('id', 'viewbox').append('svg:g').attr('transform', 'translate(500 500)');
-}
-
-// d3.xml("assets/map.svg", "image/svg+xml", function(xml) {
-// var importedNode = document.importNode(xml.documentElement, true);
-// var paths = importedNode.querySelectorAll('path');
-// var i;
-// for (i=0; i < paths.length; i+=1) {
-// svg.append('svg:path')
-// .attr('class', 'state')
-// .attr('d', d3.select(paths.item(i)).attr('d'))
-// .attr('transform', 'translate(-500 -500)scale(1 1.7)')
-// }
-// });
-
-
-var widths = {
- edge: 6,
- aggregation: 12,
- core: 18
-}
-
-function createRingsFromModel(model) {
- var rings = [{
- radius: 3,
- width: widths.edge,
- switches: model.edgeSwitches,
- className: 'edge',
- angles: []
- }, {
- radius: 2.25,
- width: widths.aggregation,
- switches: model.aggregationSwitches,
- className: 'aggregation',
- angles: []
- }, {
- radius: 0.75,
- width: widths.core,
- switches: model.coreSwitches,
- className: 'core',
- angles: []
- }];
-
-
- var aggRanges = {};
-
- // arrange edge switches at equal increments
- var k = 360 / rings[0].switches.length;
- rings[0].switches.forEach(function (s, i) {
- var angle = k * i;
-
- rings[0].angles[i] = angle;
-
- // record the angle for the agg switch layout
- var dpid = s.dpid.split(':');
- dpid[7] = '01'; // the last component of the agg switch is always '01'
- var aggdpid = dpid.join(':');
- var aggRange = aggRanges[aggdpid];
- if (!aggRange) {
- aggRange = aggRanges[aggdpid] = {};
- aggRange.min = aggRange.max = angle;
- } else {
- aggRange.max = angle;
- }
- });
-
- // arrange aggregation switches to "fan out" to edge switches
- k = 360 / rings[1].switches.length;
- rings[1].switches.forEach(function (s, i) {
-// rings[1].angles[i] = k * i;
- var range = aggRanges[s.dpid];
-
- rings[1].angles[i] = (range.min + range.max)/2;
- });
-
- // find the association between core switches and aggregation switches
- var aggregationSwitchMap = {};
- model.aggregationSwitches.forEach(function (s, i) {
- aggregationSwitchMap[s.dpid] = i;
- });
-
- // put core switches next to linked aggregation switches
- k = 360 / rings[2].switches.length;
- rings[2].switches.forEach(function (s, i) {
-// rings[2].angles[i] = k * i;
- var associatedAggregationSwitches = model.configuration.association[s.dpid];
- // TODO: go between if there are multiple
- var index = aggregationSwitchMap[associatedAggregationSwitches[0]];
-
- rings[2].angles[i] = rings[1].angles[index];
- });
-
- // TODO: construct this form initially rather than converting. it works better because
- // it allows binding by dpid
- var testRings = [];
- rings.forEach(function (ring) {
- var testRing = [];
- ring.switches.forEach(function (s, i) {
- var testSwitch = {
- dpid: s.dpid,
- state: s.state,
- radius: ring.radius,
- width: ring.width,
- className: ring.className,
- angle: ring.angles[i],
- controller: s.controller
- };
- testRing.push(testSwitch);
- });
-
-
- testRings.push(testRing);
- });
-
-
-// return rings;
- return testRings;
-}
+(function () {
updateTopology = function() {
// DRAW THE SWITCHES
- var rings = svg.selectAll('.ring').data(createRingsFromModel(model));
-
- var links = reconcilePendingLinks(model);
- var linkMap = createLinkMap(links);
-
- function mouseOverSwitch(data) {
-
- d3.event.preventDefault();
-
- d3.select(document.getElementById(data.dpid + '-label')).classed('nolabel', false);
-
- if (data.highlighted) {
- return;
- }
-
- // only highlight valid link or flow destination by checking for class of existing highlighted circle
- var highlighted = svg.selectAll('.highlight')[0];
- if (highlighted.length == 1) {
- var s = d3.select(highlighted[0]).select('circle');
- // only allow links
- // edge->edge (flow)
- // aggregation->core
- // core->core
- if (data.className == 'edge' && !s.classed('edge') ||
- data.className == 'core' && !s.classed('core') && !s.classed('aggregation') ||
- data.className == 'aggregation' && !s.classed('core')) {
- return;
- }
-
- // don't highlight if there's already a link or flow
- // var map = linkMap[data.dpid];
- // console.log(map);
- // console.log(s.data()[0].dpid);
- // console.log(map[s.data()[0].dpid]);
- // if (map && map[s.data()[0].dpid]) {
- // return;
- // }
-
- // the second highlighted switch is the target for a link or flow
- data.target = true;
- }
-
- var node = d3.select(document.getElementById(data.dpid));
- node.classed('highlight', true).select('circle').transition().duration(100).attr("r", widths.core);
- data.highlighted = true;
- node.moveToFront();
- }
-
- function mouseOutSwitch(data) {
- d3.select(document.getElementById(data.dpid + '-label')).classed('nolabel', true);
-
- if (data.mouseDown)
- return;
-
- var node = d3.select(document.getElementById(data.dpid));
- node.classed('highlight', false).select('circle').transition().duration(100).attr("r", widths[data.className]);
- data.highlighted = false;
- data.target = false;
- }
-
- function mouseDownSwitch(data) {
- mouseOverSwitch(data);
- data.mouseDown = true;
- d3.select('#topology').classed('linking', true);
-
- d3.select('svg')
- .append('svg:path')
- .attr('id', 'linkVector')
- .attr('d', function () {
- var s = d3.select(document.getElementById(data.dpid));
-
- var pt = document.querySelector('svg').createSVGPoint();
- pt.x = s.attr('x');
- pt.y = s.attr('y');
- pt = pt.matrixTransform(s[0][0].getCTM());
-
- return line([pt, pt]);
- });
-
-
- if (data.className === 'core') {
- d3.selectAll('.edge').classed('nodrop', true);
- }
- if (data.className === 'edge') {
- d3.selectAll('.core').classed('nodrop', true);
- d3.selectAll('.aggregation').classed('nodrop', true);
- }
- if (data.className === 'aggregation') {
- d3.selectAll('.edge').classed('nodrop', true);
- d3.selectAll('.aggregation').classed('nodrop', true);
- }
- }
-
- function mouseUpSwitch(data) {
- if (data.mouseDown) {
- data.mouseDown = false;
- d3.select('#topology').classed('linking', false);
- d3.event.stopPropagation();
- d3.selectAll('.nodrop').classed('nodrop', false);
- }
- }
-
- function doubleClickSwitch(data) {
- var circle = d3.select(document.getElementById(data.dpid)).select('circle');
- if (data.state == 'ACTIVE') {
- var prompt = 'Deactivate ' + data.dpid + '?';
- if (confirm(prompt)) {
- switchDown(data);
- setPending(circle);
- }
- } else {
- var prompt = 'Activate ' + data.dpid + '?';
- if (confirm(prompt)) {
- switchUp(data);
- setPending(circle);
- }
- }
- }
+ var rings = svg.selectAll('.ring').data(createTopologyModel(model));
function ringEnter(data, i) {
if (!data.length) {
@@ -382,7 +96,7 @@
// Now setup the labels
// This is done separately because SVG draws in node order and we want the labels
// always on top
- var labelRings = svg.selectAll('.labelRing').data(createRingsFromModel(model));
+ var labelRings = svg.selectAll('.labelRing').data(createTopologyModel(model));
d3.select(document.body).on('mousemove', function () {
if (!d3.select('#topology').classed('linking')) {
@@ -426,132 +140,6 @@
}
});
- d3.select(document.body).on('mouseup', function () {
- function clearHighlight() {
- svg.selectAll('circle').each(function (data) {
- data.mouseDown = false;
- d3.select('#topology').classed('linking', false);
- mouseOutSwitch(data);
- });
- d3.select('#linkVector').remove();
- };
-
- d3.selectAll('.nodrop').classed('nodrop', false);
-
- function removeLink(link) {
- var path1 = document.getElementById(link['src-switch'] + '=>' + link['dst-switch']);
- var path2 = document.getElementById(link['dst-switch'] + '=>' + link['src-switch']);
-
- if (path1) {
- setPending(d3.select(path1));
- }
- if (path2) {
- setPending(d3.select(path2));
- }
-
- linkDown(link);
- }
-
-
- var highlighted = svg.selectAll('.highlight')[0];
- if (highlighted.length == 2) {
- var s1Data = highlighted[0].__data__;
- var s2Data = highlighted[1].__data__;
-
- var srcData, dstData;
- if (s1Data.target) {
- dstData = s1Data;
- srcData = s2Data;
- } else {
- dstData = s2Data;
- srcData = s1Data;
- }
-
- if (s1Data.className == 'edge' && s2Data.className == 'edge') {
- var prompt = 'Create flow from ' + srcData.dpid + ' to ' + dstData.dpid + '?';
- if (confirm(prompt)) {
- addFlow(srcData, dstData);
-
- var flow = {
- dataPath: {
- srcPort: {
- dpid: {
- value: srcData.dpid
- }
- },
- dstPort: {
- dpid: {
- value: dstData.dpid
- }
- }
- },
- srcDpid: srcData.dpid,
- dstDpid: dstData.dpid,
- createPending: true
- };
-
- selectFlow(flow);
-
- setTimeout(function () {
- deselectFlowIfCreatePending(flow);
- }, pendingTimeout);
- }
- } else {
- var map = linkMap[srcData.dpid];
- if (map && map[dstData.dpid]) {
- var prompt = 'Remove link between ' + srcData.dpid + ' and ' + dstData.dpid + '?';
- if (confirm(prompt)) {
- removeLink(map[dstData.dpid]);
- }
- } else {
- map = linkMap[dstData.dpid];
- if (map && map[srcData.dpid]) {
- var prompt = 'Remove link between ' + dstData.dpid + ' and ' + srcData.dpid + '?';
- if (confirm(prompt)) {
- removeLink(map[srcData.dpid]);
- }
- } else {
- var prompt = 'Create link between ' + srcData.dpid + ' and ' + dstData.dpid + '?';
- if (confirm(prompt)) {
- var link1 = {
- 'src-switch': srcData.dpid,
- 'src-port': 1,
- 'dst-switch': dstData.dpid,
- 'dst-port': 1,
- pending: true
- };
- pendingLinks[makeLinkKey(link1)] = link1;
- var link2 = {
- 'src-switch': dstData.dpid,
- 'src-port': 1,
- 'dst-switch': srcData.dpid,
- 'dst-port': 1,
- pending: true
- };
- pendingLinks[makeLinkKey(link2)] = link2;
- updateTopology();
-
- linkUp(link1);
-
- // remove the pending links after 10s
- setTimeout(function () {
- delete pendingLinks[makeLinkKey(link1)];
- delete pendingLinks[makeLinkKey(link2)];
-
- updateTopology();
- }, pendingTimeout);
- }
- }
- }
- }
-
- clearHighlight();
- } else {
- clearHighlight();
- }
-
- });
-
function labelRingEnter(data) {
if (!data.length) {
return;
@@ -633,15 +221,15 @@
// DRAW THE LINKS
// key on link dpids since these will come/go during demo
- var links = d3.select('svg').selectAll('.link').data(links, function (d) {
+ var linkLines = d3.select('svg').selectAll('.link').data(links, function (d) {
return d['src-switch']+'->'+d['dst-switch'];
});
// add new links
- links.enter().append("svg:path")
+ linkLines.enter().append("svg:path")
.attr("class", "link");
- links.attr('id', function (d) {
+ linkLines.attr('id', function (d) {
return makeLinkKey(d);
})
.attr("d", function (d) {
@@ -671,5 +259,7 @@
// remove old links
- links.exit().remove();
-}
\ No newline at end of file
+ linkLines.exit().remove();
+}
+
+})();