blob: 4afa6e892e1741e301a3de3bf3aa7ba89e81582a [file] [log] [blame]
Paul Greyson127d7fb2013-03-25 23:39:20 -07001/*global d3, document∆*/
Paul Greyson740bdaf2013-03-18 16:10:48 -07002
Paul Greyson6d9ed862013-03-23 17:37:15 -07003d3.selection.prototype.moveToFront = function() {
4 return this.each(function(){
5 this.parentNode.appendChild(this);
6 });
7};
Paul Greysond1a22d92013-03-19 12:15:19 -07008
Paul Greyson127d7fb2013-03-25 23:39:20 -07009var line = d3.svg.line()
10 .x(function(d) {
11 return d.x;
12 })
13 .y(function(d) {
14 return d.y;
15 });
16
17var svg, selectedFlowsView;
18
Paul Greysond1a22d92013-03-19 12:15:19 -070019var colors = [
Paul Greyson3e142162013-03-19 13:56:17 -070020 'color1',
21 'color2',
22 'color3',
23 'color4',
24 'color5',
25 'color6',
26 'color7',
27 'color8',
28 'color9',
29 'color10',
30 'color11',
Paul Greyson127d7fb2013-03-25 23:39:20 -070031 'color12'
32];
Paul Greyson01a5dff2013-03-19 15:50:14 -070033colors.reverse();
Paul Greysond1a22d92013-03-19 12:15:19 -070034
35var controllerColorMap = {};
36
37
38
Paul Greyson740bdaf2013-03-18 16:10:48 -070039function createTopologyView() {
Paul Greysonb367de22013-03-23 11:09:11 -070040 var svg = d3.select('#svg-container').append('svg:svg');
41
42 svg.append("svg:defs").append("svg:marker")
43 .attr("id", "arrow")
44 .attr("viewBox", "0 -5 10 10")
45 .attr("refX", -1)
46 .attr("markerWidth", 5)
47 .attr("markerHeight", 5)
48 .attr("orient", "auto")
49 .append("svg:path")
Paul Greyson45303ac2013-03-23 16:44:01 -070050 .attr("d", "M0,-3L10,0L0,3");
Paul Greysonb367de22013-03-23 11:09:11 -070051
52 return svg.append('svg:svg').attr('id', 'viewBox').attr('viewBox', '0 0 1000 1000').attr('preserveAspectRatio', 'none').
Paul Greyson952ccb62013-03-18 22:22:08 -070053 attr('id', 'viewbox').append('svg:g').attr('transform', 'translate(500 500)');
Paul Greyson740bdaf2013-03-18 16:10:48 -070054}
55
Paul Greyson127d7fb2013-03-25 23:39:20 -070056var selectedFlowsData = [
57 {selected: false, flow: null},
58 {selected: false, flow: null},
59 {selected: false, flow: null},
60 {selected: false, flow: null},
61 {selected: false, flow: null}
62];
63
64function drawFlows() {
65 // DRAW THE FLOWS
66 var flows = d3.select('svg').selectAll('.flow').data(selectedFlowsData, function (d) {
67 return d.flow ? d.flow.flowId.value : null;
68 });
69
70 flows.enter().append("svg:path")
71 .attr('class', 'flow')
72 .attr('d', function (d) {
73 if (!d.flow) {
74 return;
75 }
76 var pts = [];
77 d.flow.dataPath.flowEntries.forEach(function (flowEntry) {
78 var s = d3.select(document.getElementById(flowEntry.dpid.value));
79 var pt = document.querySelector('svg').createSVGPoint();
80 pt.x = s.attr('x');
81 pt.y = s.attr('y');
82 pt = pt.matrixTransform(s[0][0].getCTM());
83 pts.push(pt);
84 });
85 return line(pts);
86 })
87 .attr('stroke-dasharray', '10, 10')
88 .append('svg:animate')
89 .attr('attributeName', 'stroke-dashoffset')
90 .attr('attributeType', 'xml')
91 .attr('from', '500')
92 .attr('to', '-500')
93 .attr('dur', '20s')
94 .attr('repeatCount', 'indefinite');
95
96 flows.style('visibility', function (d) {
97 if (d) {
98 return d.selected ? '' : 'hidden';
99 }
100 })
101
102 flows.select('animate').attr('from', function (d) {
103 if (d.flow) {
104 if (d.selected) {
105 return '500';
106 } else {
107 return '-500';
108 }
109 }
110 });
111}
112
113function updateFlowView() {
114 selectedFlowsView.data(selectedFlowsData);
115
116 selectedFlowsView.classed('selected', function (d) {
117 if (d.flow) {
118 return d.selected;
119 }
120 });
121
122 selectedFlowsView.select('.flowId')
123 .text(function (d) {
124 if (d.flow) {
125 return d.flow.flowId.value;
126 }
127 });
128
129 selectedFlowsView.select('.srcDPID')
130 .text(function (d) {
131 if (d.flow) {
132 return d.flow.dataPath.srcPort.dpid.value;
133 }
134 });
135
136 selectedFlowsView.select('.dstDPID')
137 .text(function (d) {
138 if (d.flow) {
139 return d.flow.dataPath.dstPort.dpid.value;
140 }
141 });
142}
143
144function createFlowView() {
145 function rowEnter(d, i) {
146 var row = d3.select(this);
147
148 row.on('click', function () {
149 selectedFlowsData[i].selected = !selectedFlowsData[i].selected;
150 updateFlowView();
151 drawFlows();
152 });
153
154 row.append('div')
155 .classed('flowIndex', true)
156 .text(function () {
157 return i+1;
158 });
159
160 row.append('div')
161 .classed('flowId', true);
162
163 row.append('div')
164 .classed('srcDPID', true);
165
166 row.append('div')
167 .classed('dstDPID', true);
168
169 row.append('div')
170 .classed('iperf', true);
171 }
172
173 var flows = d3.select('#selectedFlows')
174 .selectAll('.selectedFlow')
175 .data(selectedFlowsData)
176 .enter()
177 .append('div')
178 .classed('selectedFlow', true)
179 .each(rowEnter);
180
181
182 return flows;
183}
184
Paul Greysond1a22d92013-03-19 12:15:19 -0700185function updateHeader(model) {
Paul Greysonb48943b2013-03-19 13:27:57 -0700186 d3.select('#lastUpdate').text(new Date());
Paul Greyson952ccb62013-03-18 22:22:08 -0700187 d3.select('#activeSwitches').text(model.edgeSwitches.length + model.aggregationSwitches.length + model.coreSwitches.length);
188 d3.select('#activeFlows').text(model.flows.length);
189}
190
191function toRadians (angle) {
192 return angle * (Math.PI / 180);
Paul Greyson740bdaf2013-03-18 16:10:48 -0700193}
194
Paul Greysonc17278a2013-03-23 10:17:12 -0700195function createRingsFromModel(model) {
Paul Greyson740bdaf2013-03-18 16:10:48 -0700196 var rings = [{
197 radius: 3,
Paul Greysonde7fad52013-03-19 12:47:32 -0700198 width: 6,
Paul Greyson952ccb62013-03-18 22:22:08 -0700199 switches: model.edgeSwitches,
Paul Greysonde7fad52013-03-19 12:47:32 -0700200 className: 'edge',
201 angles: []
Paul Greyson740bdaf2013-03-18 16:10:48 -0700202 }, {
Paul Greysond1a22d92013-03-19 12:15:19 -0700203 radius: 2.25,
Paul Greysonde7fad52013-03-19 12:47:32 -0700204 width: 12,
Paul Greyson952ccb62013-03-18 22:22:08 -0700205 switches: model.aggregationSwitches,
Paul Greysonde7fad52013-03-19 12:47:32 -0700206 className: 'aggregation',
207 angles: []
Paul Greyson740bdaf2013-03-18 16:10:48 -0700208 }, {
Paul Greyson127d7fb2013-03-25 23:39:20 -0700209 radius: 0.75,
Paul Greysonde7fad52013-03-19 12:47:32 -0700210 width: 18,
Paul Greyson952ccb62013-03-18 22:22:08 -0700211 switches: model.coreSwitches,
Paul Greysonde7fad52013-03-19 12:47:32 -0700212 className: 'core',
213 angles: []
Paul Greyson740bdaf2013-03-18 16:10:48 -0700214 }];
215
Paul Greysonde7fad52013-03-19 12:47:32 -0700216
217 var aggRanges = {};
218
219 // arrange edge switches at equal increments
220 var k = 360 / rings[0].switches.length;
221 rings[0].switches.forEach(function (s, i) {
222 var angle = k * i;
223
224 rings[0].angles[i] = angle;
225
226 // record the angle for the agg switch layout
227 var dpid = s.dpid.split(':');
Paul Greyson832d2202013-03-21 13:27:56 -0700228 dpid[7] = '01'; // the last component of the agg switch is always '01'
Paul Greysonde7fad52013-03-19 12:47:32 -0700229 var aggdpid = dpid.join(':');
230 var aggRange = aggRanges[aggdpid];
231 if (!aggRange) {
232 aggRange = aggRanges[aggdpid] = {};
233 aggRange.min = aggRange.max = angle;
234 } else {
235 aggRange.max = angle;
236 }
Paul Greysonde7fad52013-03-19 12:47:32 -0700237 });
238
239 // arrange aggregation switches to "fan out" to edge switches
240 k = 360 / rings[1].switches.length;
241 rings[1].switches.forEach(function (s, i) {
242// rings[1].angles[i] = k * i;
243 var range = aggRanges[s.dpid];
244
Paul Greyson832d2202013-03-21 13:27:56 -0700245 rings[1].angles[i] = (range.min + range.max)/2;
Paul Greysonde7fad52013-03-19 12:47:32 -0700246 });
247
Paul Greyson3f890b62013-03-22 17:39:36 -0700248 // find the association between core switches and aggregation switches
249 var aggregationSwitchMap = {};
250 model.aggregationSwitches.forEach(function (s, i) {
Paul Greysonc17278a2013-03-23 10:17:12 -0700251 aggregationSwitchMap[s.dpid] = i;
Paul Greyson3f890b62013-03-22 17:39:36 -0700252 });
253
Paul Greyson3f890b62013-03-22 17:39:36 -0700254 // put core switches next to linked aggregation switches
Paul Greysonde7fad52013-03-19 12:47:32 -0700255 k = 360 / rings[2].switches.length;
256 rings[2].switches.forEach(function (s, i) {
Paul Greyson3f890b62013-03-22 17:39:36 -0700257// rings[2].angles[i] = k * i;
Paul Greysonc17278a2013-03-23 10:17:12 -0700258 var associatedAggregationSwitches = model.configuration.association[s.dpid];
259 // TODO: go between if there are multiple
260 var index = aggregationSwitchMap[associatedAggregationSwitches[0]];
261
262 rings[2].angles[i] = rings[1].angles[index];
Paul Greysonde7fad52013-03-19 12:47:32 -0700263 });
264
Paul Greyson644d92a2013-03-23 18:00:40 -0700265 // TODO: construct this form initially rather than converting. it works better because
266 // it allows binding by dpid
267 var testRings = [];
268 rings.forEach(function (ring) {
269 var testRing = [];
270 ring.switches.forEach(function (s, i) {
271 var testSwitch = {
272 dpid: s.dpid,
273 state: s.state,
274 radius: ring.radius,
275 width: ring.width,
276 className: ring.className,
277 angle: ring.angles[i],
278 controller: s.controller
Paul Greyson127d7fb2013-03-25 23:39:20 -0700279 };
Paul Greyson644d92a2013-03-23 18:00:40 -0700280 testRing.push(testSwitch);
281 });
Paul Greyson6d9ed862013-03-23 17:37:15 -0700282
283
Paul Greyson644d92a2013-03-23 18:00:40 -0700284 testRings.push(testRing);
285 });
Paul Greyson6d9ed862013-03-23 17:37:15 -0700286
287
Paul Greyson644d92a2013-03-23 18:00:40 -0700288// return rings;
289 return testRings;
Paul Greysonc17278a2013-03-23 10:17:12 -0700290}
291
292function updateTopology(svg, model) {
293
294 // DRAW THE SWITCHES
295 var rings = svg.selectAll('.ring').data(createRingsFromModel(model));
296
Paul Greyson740bdaf2013-03-18 16:10:48 -0700297 function ringEnter(data, i) {
Paul Greyson644d92a2013-03-23 18:00:40 -0700298 if (!data.length) {
Paul Greyson740bdaf2013-03-18 16:10:48 -0700299 return;
300 }
301
Paul Greysonc17278a2013-03-23 10:17:12 -0700302 // create the nodes
Paul Greysonf9edc1a2013-03-19 13:22:06 -0700303 var nodes = d3.select(this).selectAll("g")
Paul Greyson644d92a2013-03-23 18:00:40 -0700304 .data(data, function (data) {
305 return data.dpid;
306 })
Paul Greyson740bdaf2013-03-18 16:10:48 -0700307 .enter().append("svg:g")
Paul Greysonf9edc1a2013-03-19 13:22:06 -0700308 .classed('nolabel', true)
Paul Greyson644d92a2013-03-23 18:00:40 -0700309 .attr("id", function (data, i) {
310 return data.dpid;
Paul Greyson23b0cd32013-03-18 23:45:48 -0700311 })
Paul Greyson644d92a2013-03-23 18:00:40 -0700312 .attr("transform", function(data, i) {
313 return "rotate(" + data.angle+ ")translate(" + data.radius * 150 + ")rotate(" + (-data.angle) + ")";
Paul Greysonf9edc1a2013-03-19 13:22:06 -0700314 });
315
Paul Greysonc17278a2013-03-23 10:17:12 -0700316 // add the cirles representing the switches
Paul Greysonf9edc1a2013-03-19 13:22:06 -0700317 nodes.append("svg:circle")
Paul Greyson644d92a2013-03-23 18:00:40 -0700318 .attr("transform", function(data, i) {
Paul Greysond1a22d92013-03-19 12:15:19 -0700319 var m = document.querySelector('#viewbox').getTransformToElement().inverse();
Paul Greysonf8f43172013-03-18 23:00:30 -0700320 if (data.scale) {
321 m = m.scale(data.scale);
322 }
323 return "matrix( " + m.a + " " + m.b + " " + m.c + " " + m.d + " " + m.e + " " + m.f + " )";
Paul Greyson952ccb62013-03-18 22:22:08 -0700324 })
Paul Greyson644d92a2013-03-23 18:00:40 -0700325 .attr("x", function (data) {
326 return -data.width / 2;
327 })
328 .attr("y", function (data) {
329 return -data.width / 2;
330 })
331 .attr("r", function (data) {
332 return data.width;
Paul Greyson127d7fb2013-03-25 23:39:20 -0700333 });
Paul Greysonf9edc1a2013-03-19 13:22:06 -0700334
Paul Greysonc17278a2013-03-23 10:17:12 -0700335 // setup the mouseover behaviors
Paul Greysonf9edc1a2013-03-19 13:22:06 -0700336 function showLabel(data, index) {
Paul Greyson644d92a2013-03-23 18:00:40 -0700337 d3.select(document.getElementById(data.dpid + '-label')).classed('nolabel', false);
Paul Greysonf9edc1a2013-03-19 13:22:06 -0700338 }
339
340 function hideLabel(data, index) {
Paul Greyson644d92a2013-03-23 18:00:40 -0700341 d3.select(document.getElementById(data.dpid + '-label')).classed('nolabel', true);
Paul Greysonf9edc1a2013-03-19 13:22:06 -0700342 }
343
344 nodes.on('mouseover', showLabel);
345 nodes.on('mouseout', hideLabel);
Paul Greyson740bdaf2013-03-18 16:10:48 -0700346 }
347
Paul Greysonc17278a2013-03-23 10:17:12 -0700348 // append switches
349 rings.enter().append("svg:g")
Paul Greyson740bdaf2013-03-18 16:10:48 -0700350 .attr("class", "ring")
351 .each(ringEnter);
Paul Greysonf8f43172013-03-18 23:00:30 -0700352
Paul Greysonf9edc1a2013-03-19 13:22:06 -0700353
Paul Greysonc17278a2013-03-23 10:17:12 -0700354 function ringUpdate(data, i) {
Paul Greyson127d7fb2013-03-25 23:39:20 -0700355 var nodes = d3.select(this).selectAll("g")
Paul Greyson644d92a2013-03-23 18:00:40 -0700356 .data(data, function (data) {
357 return data.dpid;
Paul Greyson127d7fb2013-03-25 23:39:20 -0700358 });
Paul Greyson644d92a2013-03-23 18:00:40 -0700359 nodes.select('circle').attr('class', function (data, i) {
Paul Greyson127d7fb2013-03-25 23:39:20 -0700360 if (data.state === 'ACTIVE') {
Paul Greyson644d92a2013-03-23 18:00:40 -0700361 return data.className + ' ' + controllerColorMap[data.controller];
Paul Greysonc17278a2013-03-23 10:17:12 -0700362 } else {
363 return data.className + ' ' + 'colorInactive';
364 }
Paul Greyson127d7fb2013-03-25 23:39:20 -0700365 });
Paul Greysonc17278a2013-03-23 10:17:12 -0700366 }
367
368 // update switches
369 rings.each(ringUpdate);
370
Paul Greyson968d1b42013-03-23 16:58:41 -0700371
372 // Now setup the labels
373 // This is done separately because SVG draws in node order and we want the labels
374 // always on top
375 var labelRings = svg.selectAll('.labelRing').data(createRingsFromModel(model));
376
Paul Greyson9066ab02013-03-23 18:15:41 -0700377 function labelRingEnter(data) {
Paul Greyson644d92a2013-03-23 18:00:40 -0700378 if (!data.length) {
Paul Greyson968d1b42013-03-23 16:58:41 -0700379 return;
380 }
381
382 // create the nodes
383 var nodes = d3.select(this).selectAll("g")
Paul Greyson644d92a2013-03-23 18:00:40 -0700384 .data(data, function (data) {
385 return data.dpid;
386 })
Paul Greyson968d1b42013-03-23 16:58:41 -0700387 .enter().append("svg:g")
388 .classed('nolabel', true)
Paul Greyson9066ab02013-03-23 18:15:41 -0700389 .attr("id", function (data) {
Paul Greyson644d92a2013-03-23 18:00:40 -0700390 return data.dpid + '-label';
Paul Greyson968d1b42013-03-23 16:58:41 -0700391 })
Paul Greyson644d92a2013-03-23 18:00:40 -0700392 .attr("transform", function(data, i) {
393 return "rotate(" + data.angle+ ")translate(" + data.radius * 150 + ")rotate(" + (-data.angle) + ")";
Paul Greyson968d1b42013-03-23 16:58:41 -0700394 });
395
396 // add the text nodes which show on mouse over
397 nodes.append("svg:text")
Paul Greyson127d7fb2013-03-25 23:39:20 -0700398 .text(function (data) {return data.dpid;})
Paul Greyson9066ab02013-03-23 18:15:41 -0700399 .attr("x", function (data) {
400 if (data.angle <= 90 || data.angle >= 270 && data.angle <= 360) {
401 if (data.className == 'edge') {
Paul Greyson1eb2dd12013-03-23 18:22:00 -0700402 return - data.width*3 - 4;
Paul Greyson9066ab02013-03-23 18:15:41 -0700403 } else {
Paul Greyson1eb2dd12013-03-23 18:22:00 -0700404 return - data.width - 4;
Paul Greyson9066ab02013-03-23 18:15:41 -0700405 }
406 } else {
407 if (data.className == 'edge') {
408 return data.width*3 + 4;
409 } else {
410 return data.width + 4;
411 }
412 }
413 })
414 .attr("y", function (data) {
415 var y;
416 if (data.angle <= 90 || data.angle >= 270 && data.angle <= 360) {
417 if (data.className == 'edge') {
418 y = data.width*3/2 + 4;
419 } else {
420 y = data.width/2 + 4;
421 }
422 } else {
423 if (data.className == 'edge') {
424 y = data.width*3/2 + 4;
425 } else {
426 y = data.width/2 + 4;
427 }
428 }
429 return y - 6;
430 })
431 .attr("text-anchor", function (data) {
432 if (data.angle <= 90 || data.angle >= 270 && data.angle <= 360) {
433 return "end";
434 } else {
435 return "start";
436 }
437 })
438 .attr("transform", function(data) {
Paul Greyson968d1b42013-03-23 16:58:41 -0700439 var m = document.querySelector('#viewbox').getTransformToElement().inverse();
440 if (data.scale) {
441 m = m.scale(data.scale);
442 }
443 return "matrix( " + m.a + " " + m.b + " " + m.c + " " + m.d + " " + m.e + " " + m.f + " )";
444 })
445 }
446
447 labelRings.enter().append("svg:g")
448 .attr("class", "textRing")
449 .each(labelRingEnter);
450
451
Paul Greysonc17278a2013-03-23 10:17:12 -0700452 // switches should not change during operation of the ui so no
453 // rings.exit()
454
455
Paul Greysonf9edc1a2013-03-19 13:22:06 -0700456 // do mouseover zoom on edge nodes
Paul Greyson6d9ed862013-03-23 17:37:15 -0700457 function zoom(data, index) {
Paul Greyson644d92a2013-03-23 18:00:40 -0700458 var g = d3.select(document.getElementById(data.dpid)).select('circle');
459 g.transition().duration(100).attr("r", data.width*3);
Paul Greyson6d9ed862013-03-23 17:37:15 -0700460 // TODO: this doesn't work because the data binding is by index
Paul Greyson644d92a2013-03-23 18:00:40 -0700461 d3.select(this.parentNode).moveToFront();
Paul Greyson6d9ed862013-03-23 17:37:15 -0700462 }
Paul Greysonf8f43172013-03-18 23:00:30 -0700463
Paul Greyson6d9ed862013-03-23 17:37:15 -0700464 svg.selectAll('.edge').on('mouseover', zoom);
465 svg.selectAll('.edge').on('mousedown', zoom);
Paul Greysond1a22d92013-03-19 12:15:19 -0700466
Paul Greyson6d9ed862013-03-23 17:37:15 -0700467 function unzoom(data, index) {
Paul Greyson644d92a2013-03-23 18:00:40 -0700468 var g = d3.select(document.getElementById(data.dpid)).select('circle');
469 g.transition().duration(100).attr("r", data.width);
Paul Greyson6d9ed862013-03-23 17:37:15 -0700470 }
471 svg.selectAll('.edge').on('mouseout', unzoom);
Paul Greysonf9edc1a2013-03-19 13:22:06 -0700472
473
Paul Greysond1a22d92013-03-19 12:15:19 -0700474 // DRAW THE LINKS
Paul Greysond1a22d92013-03-19 12:15:19 -0700475
Paul Greysonc17278a2013-03-23 10:17:12 -0700476 // key on link dpids since these will come/go during demo
Paul Greysonb367de22013-03-23 11:09:11 -0700477 var links = d3.select('svg').selectAll('.link').data(model.links, function (d) {
Paul Greysonc17278a2013-03-23 10:17:12 -0700478 return d['src-switch']+'->'+d['dst-switch'];
479 });
480
481 // add new links
Paul Greysonb367de22013-03-23 11:09:11 -0700482 links.enter().append("svg:path")
483 .attr("class", "link")
484 .attr("d", function (d) {
Paul Greysonc17278a2013-03-23 10:17:12 -0700485
Paul Greysond1a22d92013-03-19 12:15:19 -0700486 var src = d3.select(document.getElementById(d['src-switch']));
487 var dst = d3.select(document.getElementById(d['dst-switch']));
488
489 var srcPt = document.querySelector('svg').createSVGPoint();
490 srcPt.x = src.attr('x');
Paul Greysonb367de22013-03-23 11:09:11 -0700491 srcPt.y = src.attr('y');
492 srcPt = srcPt.matrixTransform(src[0][0].getCTM());
Paul Greysond1a22d92013-03-19 12:15:19 -0700493
494 var dstPt = document.querySelector('svg').createSVGPoint();
495 dstPt.x = dst.attr('x');
Paul Greysonb367de22013-03-23 11:09:11 -0700496 dstPt.y = dst.attr('y'); // tmp: make up and down links distinguishable
497 dstPt = dstPt.matrixTransform(dst[0][0].getCTM());
Paul Greysond1a22d92013-03-19 12:15:19 -0700498
Paul Greysonb367de22013-03-23 11:09:11 -0700499 var midPt = document.querySelector('svg').createSVGPoint();
500 midPt.x = (srcPt.x + dstPt.x)/2;
501 midPt.y = (srcPt.y + dstPt.y)/2;
502
503 return line([srcPt, midPt, dstPt]);
504 })
505 .attr("marker-mid", function(d) { return "url(#arrow)"; });
Paul Greysonc17278a2013-03-23 10:17:12 -0700506
507 // remove old links
508 links.exit().remove();
Paul Greyson644d92a2013-03-23 18:00:40 -0700509
Paul Greyson127d7fb2013-03-25 23:39:20 -0700510
511 drawFlows();
Paul Greysond1a22d92013-03-19 12:15:19 -0700512}
513
514function updateControllers(model) {
515 var controllers = d3.select('#controllerList').selectAll('.controller').data(model.controllers);
Paul Greyson3e142162013-03-19 13:56:17 -0700516 controllers.enter().append('div')
Paul Greysone262a292013-03-23 10:35:23 -0700517 .each(function (c) {
518 controllerColorMap[c] = colors.pop();
519 d3.select(document.body).classed(controllerColorMap[c] + '-selected', true);
520 })
521 .text(function (d) {
522 return d;
523 });
Paul Greysonbcd3c772013-03-21 13:16:44 -0700524
Paul Greysone262a292013-03-23 10:35:23 -0700525 controllers.attr('class', function (d) {
Paul Greysoneed36352013-03-23 11:19:11 -0700526 var color = 'colorInactive';
Paul Greysonbcd3c772013-03-21 13:16:44 -0700527 if (model.activeControllers.indexOf(d) != -1) {
528 color = controllerColorMap[d];
Paul Greysond1a22d92013-03-19 12:15:19 -0700529 }
Paul Greysonbcd3c772013-03-21 13:16:44 -0700530 var className = 'controller ' + color;
531 return className;
Paul Greysond1a22d92013-03-19 12:15:19 -0700532 });
Paul Greysond1a22d92013-03-19 12:15:19 -0700533
Paul Greysone262a292013-03-23 10:35:23 -0700534 // this should never be needed
535 // controllers.exit().remove();
Paul Greysond1a22d92013-03-19 12:15:19 -0700536
Paul Greyson3e142162013-03-19 13:56:17 -0700537 controllers.on('click', function (c, index) {
Paul Greysonc3e21a02013-03-21 13:56:05 -0700538 var allSelected = true;
539 for (var key in controllerColorMap) {
540 if (!d3.select(document.body).classed(controllerColorMap[key] + '-selected')) {
541 allSelected = false;
542 break;
543 }
544 }
545 if (allSelected) {
546 for (var key in controllerColorMap) {
547 d3.select(document.body).classed(controllerColorMap[key] + '-selected', key == c)
548 }
549 } else {
550 for (var key in controllerColorMap) {
551 d3.select(document.body).classed(controllerColorMap[key] + '-selected', true)
552 }
553 }
554
555 // var selected = d3.select(document.body).classed(controllerColorMap[c] + '-selected');
556 // d3.select(document.body).classed(controllerColorMap[c] + '-selected', !selected);
Paul Greysond1a22d92013-03-19 12:15:19 -0700557 });
Paul Greyson740bdaf2013-03-18 16:10:48 -0700558}
559
Paul Greysonb48943b2013-03-19 13:27:57 -0700560var oldModel;
Paul Greyson127d7fb2013-03-25 23:39:20 -0700561function sync(svg, selectedFlowsView) {
Paul Greysonbcd3c772013-03-21 13:16:44 -0700562 var d = Date.now();
Paul Greysonb48943b2013-03-19 13:27:57 -0700563 updateModel(function (newModel) {
Paul Greysonbcd3c772013-03-21 13:16:44 -0700564 console.log('Update time: ' + (Date.now() - d)/1000 + 's');
Paul Greyson740bdaf2013-03-18 16:10:48 -0700565
Paul Greysona4e0f542013-03-23 18:24:44 -0700566 if (!oldModel || JSON.stringify(oldModel) != JSON.stringify(newModel)) {
Paul Greysonb48943b2013-03-19 13:27:57 -0700567 updateControllers(newModel);
Paul Greyson127d7fb2013-03-25 23:39:20 -0700568
569 // fake flows right now
570 var i;
571 for (i = 0; i < newModel.flows.length; i+=1) {
572 var selected = selectedFlowsData[i] ? selectedFlowsData[i].selected : false;
573 selectedFlowsData[i].flow = newModel.flows[i];
574 selectedFlowsData[i].selected = selected;
575 }
576
577 updateFlowView(newModel);
Paul Greysonb48943b2013-03-19 13:27:57 -0700578 updateTopology(svg, newModel);
579 } else {
580 console.log('no change');
581 }
582 updateHeader(newModel);
583
584 oldModel = newModel;
Paul Greyson740bdaf2013-03-18 16:10:48 -0700585
586 // do it again in 1s
587 setTimeout(function () {
Paul Greysona36a9232013-03-22 22:41:27 -0700588 sync(svg)
Paul Greysond1a22d92013-03-19 12:15:19 -0700589 }, 1000);
Paul Greyson6f86d1e2013-03-18 14:40:39 -0700590 });
591}
Paul Greyson740bdaf2013-03-18 16:10:48 -0700592
Paul Greyson38d8bde2013-03-22 22:07:35 -0700593svg = createTopologyView();
Paul Greyson127d7fb2013-03-25 23:39:20 -0700594selectedFlowsView = createFlowView();
Paul Greyson38d8bde2013-03-22 22:07:35 -0700595// workaround for Chrome v25 bug
596// if executed immediately, the view box transform logic doesn't work properly
597// fixed in Chrome v27
598setTimeout(function () {
599 // workaround for another Chrome v25 bug
600 // viewbox transform stuff doesn't work in combination with browser zoom
Paul Greysonc17278a2013-03-23 10:17:12 -0700601 // also works in Chrome v27
Paul Greyson38d8bde2013-03-22 22:07:35 -0700602 d3.select('#svg-container').style('zoom', window.document.body.clientWidth/window.document.width);
Paul Greyson127d7fb2013-03-25 23:39:20 -0700603 sync(svg, selectedFlowsView);
Paul Greyson38d8bde2013-03-22 22:07:35 -0700604}, 100);