blob: 0595393e9127b143bc17458e629197323a42466a [file] [log] [blame]
Simon Hunt737c89f2015-01-28 12:23:19 -08001/*
2 * Copyright 2015 Open Networking Laboratory
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/*
Simon Hunt3a6eec02015-02-09 21:16:43 -080018 ONOS GUI -- Topology Force Module.
19 Visualization of the topology in an SVG layer, using a D3 Force Layout.
Simon Hunt737c89f2015-01-28 12:23:19 -080020 */
21
22(function () {
23 'use strict';
24
25 // injected refs
Simon Hunt8d22c4b2015-08-06 16:24:43 -070026 var $log, $timeout, fs, sus, ts, flash, wss, tov,
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -070027 tis, tms, td3, tss, tts, tos, fltr, tls, uplink, svg;
Simon Huntac4c6f72015-02-03 19:50:53 -080028
29 // configuration
Simon Hunt1894d792015-02-04 17:09:20 -080030 var linkConfig = {
31 light: {
32 baseColor: '#666',
33 inColor: '#66f',
Simon Hunt3a6eec02015-02-09 21:16:43 -080034 outColor: '#f00'
Simon Hunt1894d792015-02-04 17:09:20 -080035 },
36 dark: {
Simon Hunt5724fb42015-02-05 16:59:40 -080037 baseColor: '#aaa',
Simon Hunt1894d792015-02-04 17:09:20 -080038 inColor: '#66f',
Simon Hunt5724fb42015-02-05 16:59:40 -080039 outColor: '#f66'
Simon Hunt1894d792015-02-04 17:09:20 -080040 },
41 inWidth: 12,
42 outWidth: 10
43 };
44
Simon Hunt737c89f2015-01-28 12:23:19 -080045 // internal state
Simon Huntac4c6f72015-02-03 19:50:53 -080046 var settings, // merged default settings and options
Simon Hunt737c89f2015-01-28 12:23:19 -080047 force, // force layout object
48 drag, // drag behavior handler
49 network = {
50 nodes: [],
51 links: [],
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -070052 linksByDevice: {},
Simon Hunt737c89f2015-01-28 12:23:19 -080053 lookup: {},
54 revLinkToKey: {}
Simon Huntac4c6f72015-02-03 19:50:53 -080055 },
Simon Hunt3ab20282015-02-26 20:32:19 -080056 lu, // shorthand for lookup
57 rlk, // shorthand for revLinktoKey
Simon Hunta142dd22015-02-12 22:07:51 -080058 showHosts = false, // whether hosts are displayed
Simon Hunt5724fb42015-02-05 16:59:40 -080059 showOffline = true, // whether offline devices are displayed
Simon Hunt445e8152015-02-06 13:00:12 -080060 nodeLock = false, // whether nodes can be dragged or not (locked)
Thomas Vachuska1a989c12015-06-09 18:29:22 -070061 fNodesTimer, // timer for delayed nodes update
62 fLinksTimer, // timer for delayed links update
Bri Prebilic Cole80401762015-07-16 11:36:18 -070063 dim, // the dimensions of the force layout [w,h]
64 linkNums = []; // array of link number labels
Simon Hunt737c89f2015-01-28 12:23:19 -080065
66 // SVG elements;
Bri Prebilic Cole80401762015-07-16 11:36:18 -070067 var linkG, linkLabelG, numLinkLblsG, portLabelG, nodeG;
Simon Hunt737c89f2015-01-28 12:23:19 -080068
69 // D3 selections;
70 var link, linkLabel, node;
71
72 // default settings for force layout
73 var defaultSettings = {
74 gravity: 0.4,
75 friction: 0.7,
76 charge: {
77 // note: key is node.class
78 device: -8000,
79 host: -5000,
80 _def_: -12000
81 },
82 linkDistance: {
83 // note: key is link.type
84 direct: 100,
85 optical: 120,
86 hostLink: 3,
87 _def_: 50
88 },
89 linkStrength: {
90 // note: key is link.type
91 // range: {0.0 ... 1.0}
92 //direct: 1.0,
93 //optical: 1.0,
94 //hostLink: 1.0,
95 _def_: 1.0
96 }
97 };
98
99
Simon Huntac4c6f72015-02-03 19:50:53 -0800100 // ==========================
101 // === EVENT HANDLERS
102
103 function addDevice(data) {
104 var id = data.id,
105 d;
106
Simon Hunt1894d792015-02-04 17:09:20 -0800107 uplink.showNoDevs(false);
Simon Huntac4c6f72015-02-03 19:50:53 -0800108
109 // although this is an add device event, if we already have the
110 // device, treat it as an update instead..
Simon Hunt1894d792015-02-04 17:09:20 -0800111 if (lu[id]) {
Simon Huntac4c6f72015-02-03 19:50:53 -0800112 updateDevice(data);
113 return;
114 }
115
Simon Hunt3a6eec02015-02-09 21:16:43 -0800116 d = tms.createDeviceNode(data);
Simon Huntac4c6f72015-02-03 19:50:53 -0800117 network.nodes.push(d);
Simon Hunt1894d792015-02-04 17:09:20 -0800118 lu[id] = d;
Simon Huntac4c6f72015-02-03 19:50:53 -0800119 updateNodes();
Simon Huntac4c6f72015-02-03 19:50:53 -0800120 }
121
122 function updateDevice(data) {
123 var id = data.id,
Simon Hunt1894d792015-02-04 17:09:20 -0800124 d = lu[id],
Simon Huntac4c6f72015-02-03 19:50:53 -0800125 wasOnline;
126
127 if (d) {
128 wasOnline = d.online;
129 angular.extend(d, data);
Simon Hunt3a6eec02015-02-09 21:16:43 -0800130 if (tms.positionNode(d, true)) {
Simon Hunt445e8152015-02-06 13:00:12 -0800131 sendUpdateMeta(d);
Simon Huntac4c6f72015-02-03 19:50:53 -0800132 }
133 updateNodes();
134 if (wasOnline !== d.online) {
Simon Huntdc6adea2015-02-09 22:29:36 -0800135 tms.findAttachedLinks(d.id).forEach(restyleLinkElement);
Simon Hunt5724fb42015-02-05 16:59:40 -0800136 updateOfflineVisibility(d);
Simon Huntac4c6f72015-02-03 19:50:53 -0800137 }
Simon Huntac4c6f72015-02-03 19:50:53 -0800138 }
139 }
140
Simon Hunt1894d792015-02-04 17:09:20 -0800141 function removeDevice(data) {
142 var id = data.id,
143 d = lu[id];
144 if (d) {
145 removeDeviceElement(d);
Simon Hunt1894d792015-02-04 17:09:20 -0800146 }
147 }
148
149 function addHost(data) {
150 var id = data.id,
151 d, lnk;
152
153 // although this is an add host event, if we already have the
154 // host, treat it as an update instead..
155 if (lu[id]) {
156 updateHost(data);
157 return;
158 }
159
Simon Hunt3a6eec02015-02-09 21:16:43 -0800160 d = tms.createHostNode(data);
Simon Hunt1894d792015-02-04 17:09:20 -0800161 network.nodes.push(d);
162 lu[id] = d;
Simon Hunt1894d792015-02-04 17:09:20 -0800163 updateNodes();
164
Simon Hunt3a6eec02015-02-09 21:16:43 -0800165 lnk = tms.createHostLink(data);
Simon Hunt1894d792015-02-04 17:09:20 -0800166 if (lnk) {
Simon Hunt1894d792015-02-04 17:09:20 -0800167 d.linkData = lnk; // cache ref on its host
168 network.links.push(lnk);
169 lu[d.ingress] = lnk;
170 lu[d.egress] = lnk;
171 updateLinks();
172 }
Simon Hunt1894d792015-02-04 17:09:20 -0800173 }
174
175 function updateHost(data) {
176 var id = data.id,
177 d = lu[id];
178 if (d) {
179 angular.extend(d, data);
Simon Hunt3a6eec02015-02-09 21:16:43 -0800180 if (tms.positionNode(d, true)) {
Simon Hunt445e8152015-02-06 13:00:12 -0800181 sendUpdateMeta(d);
Simon Hunt1894d792015-02-04 17:09:20 -0800182 }
183 updateNodes();
Simon Hunt1894d792015-02-04 17:09:20 -0800184 }
185 }
186
187 function removeHost(data) {
188 var id = data.id,
189 d = lu[id];
190 if (d) {
191 removeHostElement(d, true);
Simon Hunt1894d792015-02-04 17:09:20 -0800192 }
193 }
194
195 function addLink(data) {
Simon Huntdc6adea2015-02-09 22:29:36 -0800196 var result = tms.findLink(data, 'add'),
Simon Hunt1894d792015-02-04 17:09:20 -0800197 bad = result.badLogic,
198 d = result.ldata;
199
200 if (bad) {
201 //logicError(bad + ': ' + link.id);
202 return;
203 }
204
205 if (d) {
206 // we already have a backing store link for src/dst nodes
207 addLinkUpdate(d, data);
208 return;
209 }
210
211 // no backing store link yet
Simon Hunt3a6eec02015-02-09 21:16:43 -0800212 d = tms.createLink(data);
Simon Hunt1894d792015-02-04 17:09:20 -0800213 if (d) {
214 network.links.push(d);
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700215 aggregateLink(d, data);
Simon Hunt1894d792015-02-04 17:09:20 -0800216 lu[d.key] = d;
217 updateLinks();
Simon Hunt1894d792015-02-04 17:09:20 -0800218 }
219 }
220
221 function updateLink(data) {
Simon Huntdc6adea2015-02-09 22:29:36 -0800222 var result = tms.findLink(data, 'update'),
Simon Hunt1894d792015-02-04 17:09:20 -0800223 bad = result.badLogic;
224 if (bad) {
225 //logicError(bad + ': ' + link.id);
226 return;
227 }
228 result.updateWith(link);
229 }
230
231 function removeLink(data) {
Simon Hunta4242de2015-02-24 17:11:55 -0800232 var result = tms.findLink(data, 'remove');
233
234 if (!result.badLogic) {
235 result.removeRawLink();
Simon Hunt1894d792015-02-04 17:09:20 -0800236 }
Simon Hunt1894d792015-02-04 17:09:20 -0800237 }
238
239 // ========================
240
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700241 function makeNodeKey(node1, node2) {
242 return node1 + '-' + node2;
243 }
244
245 function findNodePair(key, keyRev) {
246 if (network.linksByDevice[key]) {
247 return key;
248 } else if (network.linksByDevice[keyRev]) {
249 return keyRev;
250 } else {
251 return false;
252 }
253 }
254
255 function aggregateLink(ldata, link) {
256 var key = makeNodeKey(link.src, link.dst),
257 keyRev = makeNodeKey(link.dst, link.src),
258 found = findNodePair(key, keyRev);
259
260 if (found) {
261 network.linksByDevice[found].push(ldata);
262 ldata.devicePair = found;
263 } else {
264 network.linksByDevice[key] = [ ldata ];
265 ldata.devicePair = key;
266 }
267 }
268
Simon Hunt1894d792015-02-04 17:09:20 -0800269 function addLinkUpdate(ldata, link) {
270 // add link event, but we already have the reverse link installed
271 ldata.fromTarget = link;
Simon Huntdc6adea2015-02-09 22:29:36 -0800272 rlk[link.id] = ldata.key;
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700273 // possible solution to el being undefined in restyleLinkElement:
274 //_updateLinks();
Simon Hunt1894d792015-02-04 17:09:20 -0800275 restyleLinkElement(ldata);
276 }
277
Simon Hunt1894d792015-02-04 17:09:20 -0800278
279 var widthRatio = 1.4,
280 linkScale = d3.scale.linear()
281 .domain([1, 12])
282 .range([widthRatio, 12 * widthRatio])
Simon Hunt5724fb42015-02-05 16:59:40 -0800283 .clamp(true),
Simon Hunt3a6eec02015-02-09 21:16:43 -0800284 allLinkTypes = 'direct indirect optical tunnel';
Simon Hunt1894d792015-02-04 17:09:20 -0800285
Simon Hunta142dd22015-02-12 22:07:51 -0800286 function restyleLinkElement(ldata, immediate) {
Simon Hunt1894d792015-02-04 17:09:20 -0800287 // this fn's job is to look at raw links and decide what svg classes
288 // need to be applied to the line element in the DOM
289 var th = ts.theme(),
290 el = ldata.el,
291 type = ldata.type(),
292 lw = ldata.linkWidth(),
Simon Hunta142dd22015-02-12 22:07:51 -0800293 online = ldata.online(),
294 delay = immediate ? 0 : 1000;
Simon Hunt1894d792015-02-04 17:09:20 -0800295
Thomas Vachuskacb5016f2015-05-18 14:11:43 -0700296 // FIXME: understand why el is sometimes undefined on addLink events...
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700297 // Investigated:
298 // el is undefined when it's a reverse link that is being added.
299 // updateLinks (which sets ldata.el) isn't called before this is called.
300 // Calling _updateLinks in addLinkUpdate fixes it, but there might be
301 // a more efficient way to fix it.
302 if (el && !el.empty()) {
Thomas Vachuskacb5016f2015-05-18 14:11:43 -0700303 el.classed('link', true);
304 el.classed('inactive', !online);
305 el.classed(allLinkTypes, false);
306 if (type) {
307 el.classed(type, true);
308 }
309 el.transition()
310 .duration(delay)
311 .attr('stroke-width', linkScale(lw))
312 .attr('stroke', linkConfig[th].baseColor);
Simon Hunt1894d792015-02-04 17:09:20 -0800313 }
Simon Hunt1894d792015-02-04 17:09:20 -0800314 }
315
Simon Hunt1894d792015-02-04 17:09:20 -0800316 function removeLinkElement(d) {
317 var idx = fs.find(d.key, network.links, 'key'),
318 removed;
319 if (idx >=0) {
320 // remove from links array
321 removed = network.links.splice(idx, 1);
322 // remove from lookup cache
323 delete lu[removed[0].key];
324 updateLinks();
Simon Hunt1894d792015-02-04 17:09:20 -0800325 }
326 }
327
328 function removeHostElement(d, upd) {
329 // first, remove associated hostLink...
330 removeLinkElement(d.linkData);
331
332 // remove hostLink bindings
333 delete lu[d.ingress];
334 delete lu[d.egress];
335
336 // remove from lookup cache
337 delete lu[d.id];
338 // remove from nodes array
339 var idx = fs.find(d.id, network.nodes);
340 network.nodes.splice(idx, 1);
341
342 // remove from SVG
343 // NOTE: upd is false if we were called from removeDeviceElement()
344 if (upd) {
345 updateNodes();
Simon Hunt1894d792015-02-04 17:09:20 -0800346 }
347 }
348
349 function removeDeviceElement(d) {
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700350 var id = d.id,
351 idx;
Simon Hunt1894d792015-02-04 17:09:20 -0800352 // first, remove associated hosts and links..
Simon Huntdc6adea2015-02-09 22:29:36 -0800353 tms.findAttachedHosts(id).forEach(removeHostElement);
354 tms.findAttachedLinks(id).forEach(removeLinkElement);
Simon Hunt1894d792015-02-04 17:09:20 -0800355
356 // remove from lookup cache
357 delete lu[id];
358 // remove from nodes array
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700359 idx = fs.find(id, network.nodes);
360 if (idx > -1) {
361 network.nodes.splice(idx, 1);
362 }
Simon Hunt1894d792015-02-04 17:09:20 -0800363
364 if (!network.nodes.length) {
Simon Huntdc6adea2015-02-09 22:29:36 -0800365 uplink.showNoDevs(true);
Simon Hunt1894d792015-02-04 17:09:20 -0800366 }
367
368 // remove from SVG
369 updateNodes();
Simon Hunt1894d792015-02-04 17:09:20 -0800370 }
371
Simon Hunt5724fb42015-02-05 16:59:40 -0800372 function updateHostVisibility() {
Simon Hunt18bf9822015-02-12 17:35:45 -0800373 sus.visible(nodeG.selectAll('.host'), showHosts);
374 sus.visible(linkG.selectAll('.hostLink'), showHosts);
Simon Hunt8eb4d3a2015-02-23 18:23:29 -0800375 sus.visible(linkLabelG.selectAll('.hostLinkLabel'), showHosts);
Simon Hunt5724fb42015-02-05 16:59:40 -0800376 }
377
378 function updateOfflineVisibility(dev) {
379 function updDev(d, show) {
Simon Hunt8eb4d3a2015-02-23 18:23:29 -0800380 var b;
Simon Hunt18bf9822015-02-12 17:35:45 -0800381 sus.visible(d.el, show);
Simon Hunt5724fb42015-02-05 16:59:40 -0800382
Simon Huntdc6adea2015-02-09 22:29:36 -0800383 tms.findAttachedLinks(d.id).forEach(function (link) {
Simon Hunt5724fb42015-02-05 16:59:40 -0800384 b = show && ((link.type() !== 'hostLink') || showHosts);
Simon Hunt18bf9822015-02-12 17:35:45 -0800385 sus.visible(link.el, b);
Simon Hunt5724fb42015-02-05 16:59:40 -0800386 });
Simon Huntdc6adea2015-02-09 22:29:36 -0800387 tms.findAttachedHosts(d.id).forEach(function (host) {
Simon Hunt5724fb42015-02-05 16:59:40 -0800388 b = show && showHosts;
Simon Hunt18bf9822015-02-12 17:35:45 -0800389 sus.visible(host.el, b);
Simon Hunt5724fb42015-02-05 16:59:40 -0800390 });
391 }
392
393 if (dev) {
394 // updating a specific device that just toggled off/on-line
395 updDev(dev, dev.online || showOffline);
396 } else {
397 // updating all offline devices
Simon Huntdc6adea2015-02-09 22:29:36 -0800398 tms.findDevices(true).forEach(function (d) {
Simon Hunt5724fb42015-02-05 16:59:40 -0800399 updDev(d, showOffline);
400 });
401 }
402 }
403
Simon Hunt1894d792015-02-04 17:09:20 -0800404
Simon Hunt445e8152015-02-06 13:00:12 -0800405 function sendUpdateMeta(d, clearPos) {
Simon Huntac4c6f72015-02-03 19:50:53 -0800406 var metaUi = {},
407 ll;
408
Simon Hunt445e8152015-02-06 13:00:12 -0800409 // if we are not clearing the position data (unpinning),
410 // attach the x, y, longitude, latitude...
411 if (!clearPos) {
Simon Hunt3a6eec02015-02-09 21:16:43 -0800412 ll = tms.lngLatFromCoord([d.x, d.y]);
Simon Huntdc6adea2015-02-09 22:29:36 -0800413 metaUi = {x: d.x, y: d.y, lng: ll[0], lat: ll[1]};
Simon Hunt1894d792015-02-04 17:09:20 -0800414 }
415 d.metaUi = metaUi;
Simon Hunt237676b52015-03-10 19:04:26 -0700416 wss.sendEvent('updateMeta', {
Simon Hunt1894d792015-02-04 17:09:20 -0800417 id: d.id,
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700418 class: d.class,
Simon Hunt1894d792015-02-04 17:09:20 -0800419 memento: metaUi
420 });
Simon Huntac4c6f72015-02-03 19:50:53 -0800421 }
422
Simon Hunt1894d792015-02-04 17:09:20 -0800423
Simon Huntac4c6f72015-02-03 19:50:53 -0800424 function mkSvgClass(d) {
425 return d.fixed ? d.svgClass + ' fixed' : d.svgClass;
426 }
427
Simon Hunt5724fb42015-02-05 16:59:40 -0800428 function vis(b) {
429 return b ? 'visible' : 'hidden';
430 }
431
Simon Huntfcbde892015-04-16 12:05:28 -0700432 function toggleHosts(x) {
433 var kev = (x === 'keyev'),
434 on = kev ? !showHosts : !!x;
435
436 showHosts = on;
Simon Hunt5724fb42015-02-05 16:59:40 -0800437 updateHostVisibility();
Simon Huntfcbde892015-04-16 12:05:28 -0700438 flash.flash('Hosts ' + vis(on));
439 return on;
Simon Hunt5724fb42015-02-05 16:59:40 -0800440 }
441
Simon Huntfcbde892015-04-16 12:05:28 -0700442 function toggleOffline(x) {
443 var kev = (x === 'keyev'),
444 on = kev ? !showOffline : !!x;
445
446 showOffline = on;
Simon Hunt5724fb42015-02-05 16:59:40 -0800447 updateOfflineVisibility();
Simon Huntfcbde892015-04-16 12:05:28 -0700448 flash.flash('Offline devices ' + vis(on));
449 return on;
Simon Hunt5724fb42015-02-05 16:59:40 -0800450 }
451
452 function cycleDeviceLabels() {
Bri Prebilic Cole9cf1a8d2015-04-21 13:15:29 -0700453 flash.flash(td3.incDevLabIndex());
Simon Huntdc6adea2015-02-09 22:29:36 -0800454 tms.findDevices().forEach(function (d) {
Simon Hunta4242de2015-02-24 17:11:55 -0800455 td3.updateDeviceLabel(d);
Simon Hunt1c367112015-02-05 18:02:46 -0800456 });
Simon Hunt5724fb42015-02-05 16:59:40 -0800457 }
458
Simon Hunt445e8152015-02-06 13:00:12 -0800459 function unpin() {
Simon Hunt08f841d02015-02-10 14:39:20 -0800460 var hov = tss.hovered();
461 if (hov) {
462 sendUpdateMeta(hov, true);
463 hov.fixed = false;
464 hov.el.classed('fixed', false);
Simon Hunt445e8152015-02-06 13:00:12 -0800465 fResume();
466 }
467 }
468
Simon Hunta142dd22015-02-12 22:07:51 -0800469 function showMastership(masterId) {
470 if (!masterId) {
471 restoreLayerState();
472 } else {
473 showMastershipFor(masterId);
474 }
475 }
476
477 function restoreLayerState() {
478 // NOTE: this level of indirection required, for when we have
479 // the layer filter functionality re-implemented
480 suppressLayers(false);
481 }
482
483 function showMastershipFor(id) {
484 suppressLayers(true);
485 node.each(function (n) {
486 if (n.master === id) {
487 n.el.classed('suppressed', false);
488 }
489 });
490 }
491
492 function suppressLayers(b) {
493 node.classed('suppressed', b);
494 link.classed('suppressed', b);
495// d3.selectAll('svg .port').classed('inactive', b);
496// d3.selectAll('svg .portText').classed('inactive', b);
497 }
Simon Hunt445e8152015-02-06 13:00:12 -0800498
Simon Hunt86b7c882015-04-02 23:06:08 -0700499 function showBadLinks() {
500 var badLinks = tms.findBadLinks();
501 flash.flash('Bad Links: ' + badLinks.length);
502 $log.debug('Bad Link List (' + badLinks.length + '):');
503 badLinks.forEach(function (d) {
504 $log.debug('bad link: (' + d.bad + ') ' + d.key, d);
505 if (d.el) {
506 d.el.attr('stroke-width', linkScale(2.8))
507 .attr('stroke', 'red');
508 }
509 });
510 // back to normal after 2 seconds...
511 $timeout(updateLinks, 2000);
512 }
513
Simon Hunt5724fb42015-02-05 16:59:40 -0800514 // ==========================================
515
Simon Huntac4c6f72015-02-03 19:50:53 -0800516 function updateNodes() {
Thomas Vachuska1a989c12015-06-09 18:29:22 -0700517 if (fNodesTimer) {
518 $timeout.cancel(fNodesTimer);
519 }
520 fNodesTimer = $timeout(_updateNodes, 150);
521 }
522
523 function _updateNodes() {
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700524 force.stop();
Simon Hunt1894d792015-02-04 17:09:20 -0800525 // select all the nodes in the layout:
Simon Huntac4c6f72015-02-03 19:50:53 -0800526 node = nodeG.selectAll('.node')
527 .data(network.nodes, function (d) { return d.id; });
528
Simon Hunt1894d792015-02-04 17:09:20 -0800529 // operate on existing nodes:
Simon Hunta4242de2015-02-24 17:11:55 -0800530 node.filter('.device').each(td3.deviceExisting);
531 node.filter('.host').each(td3.hostExisting);
Simon Huntac4c6f72015-02-03 19:50:53 -0800532
533 // operate on entering nodes:
534 var entering = node.enter()
535 .append('g')
536 .attr({
537 id: function (d) { return sus.safeId(d.id); },
538 class: mkSvgClass,
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700539 transform: function (d) { return sus.translate(d.x, d.y); },
Simon Huntac4c6f72015-02-03 19:50:53 -0800540 opacity: 0
541 })
542 .call(drag)
Simon Hunt08f841d02015-02-10 14:39:20 -0800543 .on('mouseover', tss.nodeMouseOver)
544 .on('mouseout', tss.nodeMouseOut)
Simon Huntac4c6f72015-02-03 19:50:53 -0800545 .transition()
546 .attr('opacity', 1);
547
Simon Hunt1894d792015-02-04 17:09:20 -0800548 // augment entering nodes:
Simon Hunta4242de2015-02-24 17:11:55 -0800549 entering.filter('.device').each(td3.deviceEnter);
550 entering.filter('.host').each(td3.hostEnter);
Simon Huntac4c6f72015-02-03 19:50:53 -0800551
Simon Hunt51056592015-02-03 21:48:07 -0800552 // operate on both existing and new nodes:
Simon Hunta4242de2015-02-24 17:11:55 -0800553 td3.updateDeviceColors();
Simon Huntac4c6f72015-02-03 19:50:53 -0800554
555 // operate on exiting nodes:
556 // Note that the node is removed after 2 seconds.
557 // Sub element animations should be shorter than 2 seconds.
558 var exiting = node.exit()
559 .transition()
560 .duration(2000)
561 .style('opacity', 0)
562 .remove();
563
Simon Hunt1894d792015-02-04 17:09:20 -0800564 // exiting node specifics:
Simon Hunta4242de2015-02-24 17:11:55 -0800565 exiting.filter('.host').each(td3.hostExit);
566 exiting.filter('.device').each(td3.deviceExit);
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700567 fStart();
Simon Huntac4c6f72015-02-03 19:50:53 -0800568 }
569
Simon Hunt51056592015-02-03 21:48:07 -0800570 // ==========================
Simon Hunt1894d792015-02-04 17:09:20 -0800571
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700572 function getDefaultPos(link) {
573 return {
574 x1: link.source.x,
575 y1: link.source.y,
576 x2: link.target.x,
577 y2: link.target.y
578 };
579 }
580
581 // returns amount of adjustment along the normal for given link
582 function amt(numLinks, linkIdx) {
583 var gap = 6;
584 return (linkIdx - ((numLinks - 1) / 2)) * gap;
585 }
586
587 function calcMovement(d, amt, flipped) {
588 var pos = getDefaultPos(d),
589 mult = flipped ? -amt : amt,
590 dx = pos.x2 - pos.x1,
591 dy = pos.y2 - pos.y1,
592 length = Math.sqrt((dx * dx) + (dy * dy));
593
594 return {
595 x1: pos.x1 + (mult * dy / length),
596 y1: pos.y1 + (mult * -dx / length),
597 x2: pos.x2 + (mult * dy / length),
598 y2: pos.y2 + (mult * -dx / length)
599 };
600 }
601
602 function calcPosition() {
603 var lines = this,
604 linkSrcId;
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700605 linkNums = [];
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700606 lines.each(function (d) {
607 if (d.type() === 'hostLink') {
608 d.position = getDefaultPos(d);
609 }
610 });
611
612 function normalizeLinkSrc(link) {
613 // ensure source device is consistent across set of links
614 // temporary measure until link modeling is refactored
615 if (!linkSrcId) {
616 linkSrcId = link.source.id;
617 return false;
618 }
619
620 return link.source.id !== linkSrcId;
621 }
622
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700623 angular.forEach(network.linksByDevice, function (linkArr, key) {
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700624 var numLinks = linkArr.length,
625 link;
626
627 if (numLinks === 1) {
628 link = linkArr[0];
629 link.position = getDefaultPos(link);
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700630 link.position.multiLink = false;
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700631 } else if (numLinks >= 5) {
632 // this code is inefficient, in the future the way links
633 // are modeled will be changed
634 angular.forEach(linkArr, function (link) {
635 link.position = getDefaultPos(link);
636 link.position.multiLink = true;
637 });
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700638 linkNums.push({
639 id: key,
640 num: numLinks,
641 linkCoords: linkArr[0].position
642 });
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700643 } else {
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700644 linkSrcId = null;
645 angular.forEach(linkArr, function (link, index) {
646 var offsetAmt = amt(numLinks, index),
647 needToFlip = normalizeLinkSrc(link);
648 link.position = calcMovement(link, offsetAmt, needToFlip);
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700649 link.position.multiLink = false;
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700650 });
651 }
652 });
653 }
654
Simon Hunt1894d792015-02-04 17:09:20 -0800655 function updateLinks() {
Thomas Vachuska1a989c12015-06-09 18:29:22 -0700656 if (fLinksTimer) {
657 $timeout.cancel(fLinksTimer);
658 }
659 fLinksTimer = $timeout(_updateLinks, 150);
660 }
661
662 function _updateLinks() {
Simon Hunt1894d792015-02-04 17:09:20 -0800663 var th = ts.theme();
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700664 force.stop();
Simon Hunt1894d792015-02-04 17:09:20 -0800665
666 link = linkG.selectAll('.link')
667 .data(network.links, function (d) { return d.key; });
668
669 // operate on existing links:
Simon Huntd5264122015-02-25 10:17:43 -0800670 link.each(function (d) {
671 // this is supposed to be an existing link, but we have observed
672 // occasions (where links are deleted and added rapidly?) where
673 // the DOM element has not been defined. So protect against that...
674 if (d.el) {
675 restyleLinkElement(d, true);
676 }
677 });
Simon Hunt1894d792015-02-04 17:09:20 -0800678
679 // operate on entering links:
680 var entering = link.enter()
681 .append('line')
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700682 .call(calcPosition)
Simon Hunt1894d792015-02-04 17:09:20 -0800683 .attr({
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700684 x1: function (d) { return d.position.x1; },
685 y1: function (d) { return d.position.y1; },
686 x2: function (d) { return d.position.x2; },
687 y2: function (d) { return d.position.y2; },
Simon Hunt1894d792015-02-04 17:09:20 -0800688 stroke: linkConfig[th].inColor,
689 'stroke-width': linkConfig.inWidth
690 });
691
692 // augment links
Simon Hunta4242de2015-02-24 17:11:55 -0800693 entering.each(td3.linkEntering);
Simon Hunt1894d792015-02-04 17:09:20 -0800694
695 // operate on both existing and new links:
696 //link.each(...)
697
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700698 // add labels for how many links are in a thick line
699 td3.applyNumLinkLabels(linkNums, numLinkLblsG);
700
Simon Hunt1894d792015-02-04 17:09:20 -0800701 // apply or remove labels
Simon Hunta4242de2015-02-24 17:11:55 -0800702 td3.applyLinkLabels();
Simon Hunt1894d792015-02-04 17:09:20 -0800703
704 // operate on exiting links:
705 link.exit()
706 .attr('stroke-dasharray', '3 3')
Simon Hunt5724fb42015-02-05 16:59:40 -0800707 .attr('stroke', linkConfig[th].outColor)
Simon Hunt1894d792015-02-04 17:09:20 -0800708 .style('opacity', 0.5)
709 .transition()
710 .duration(1500)
711 .attr({
712 'stroke-dasharray': '3 12',
Simon Hunt1894d792015-02-04 17:09:20 -0800713 'stroke-width': linkConfig.outWidth
714 })
715 .style('opacity', 0.0)
716 .remove();
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700717 fStart();
Simon Hunt1894d792015-02-04 17:09:20 -0800718 }
719
Simon Huntac4c6f72015-02-03 19:50:53 -0800720
721 // ==========================
Simon Hunt737c89f2015-01-28 12:23:19 -0800722 // force layout tick function
Simon Hunt737c89f2015-01-28 12:23:19 -0800723
Simon Hunt5724fb42015-02-05 16:59:40 -0800724 function fResume() {
Simon Huntc3c5b672015-02-20 11:32:13 -0800725 if (!tos.isOblique()) {
Simon Hunt5724fb42015-02-05 16:59:40 -0800726 force.resume();
727 }
728 }
729
730 function fStart() {
Simon Huntc3c5b672015-02-20 11:32:13 -0800731 if (!tos.isOblique()) {
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700732 $log.debug("Starting force-layout");
733 force.start();
Simon Hunt5724fb42015-02-05 16:59:40 -0800734 }
735 }
736
737 var tickStuff = {
738 nodeAttr: {
739 transform: function (d) { return sus.translate(d.x, d.y); }
740 },
741 linkAttr: {
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700742 x1: function (d) { return d.position.x1; },
743 y1: function (d) { return d.position.y1; },
744 x2: function (d) { return d.position.x2; },
745 y2: function (d) { return d.position.y2; }
Simon Hunt5724fb42015-02-05 16:59:40 -0800746 },
747 linkLabelAttr: {
748 transform: function (d) {
Simon Huntdc6adea2015-02-09 22:29:36 -0800749 var lnk = tms.findLinkById(d.key);
Simon Hunt5724fb42015-02-05 16:59:40 -0800750 if (lnk) {
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700751 return td3.transformLabel(lnk.position);
Simon Hunt5724fb42015-02-05 16:59:40 -0800752 }
753 }
754 }
755 };
756
757 function tick() {
Simon Hunt3ab20282015-02-26 20:32:19 -0800758 // guard against null (which can happen when our view pages out)...
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700759 if (node && node.size()) {
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700760 node.attr(tickStuff.nodeAttr);
761 }
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700762 if (link && link.size()) {
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700763 link.call(calcPosition)
764 .attr(tickStuff.linkAttr);
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700765 td3.applyNumLinkLabels(linkNums, numLinkLblsG);
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700766 }
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700767 if (linkLabel && linkLabel.size()) {
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700768 linkLabel.attr(tickStuff.linkLabelAttr);
769 }
Simon Hunt737c89f2015-01-28 12:23:19 -0800770 }
771
772
Simon Huntac4c6f72015-02-03 19:50:53 -0800773 // ==========================
774 // === MOUSE GESTURE HANDLERS
775
Simon Hunt205099e2015-02-07 13:12:01 -0800776 function zoomingOrPanning(ev) {
777 return ev.metaKey || ev.altKey;
Simon Hunt445e8152015-02-06 13:00:12 -0800778 }
779
780 function atDragEnd(d) {
781 // once we've finished moving, pin the node in position
782 d.fixed = true;
783 d3.select(this).classed('fixed', true);
784 sendUpdateMeta(d);
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700785 tss.clickConsumed(true);
Simon Hunt445e8152015-02-06 13:00:12 -0800786 }
787
788 // predicate that indicates when dragging is active
789 function dragEnabled() {
790 var ev = d3.event.sourceEvent;
791 // nodeLock means we aren't allowing nodes to be dragged...
Simon Hunt205099e2015-02-07 13:12:01 -0800792 return !nodeLock && !zoomingOrPanning(ev);
Simon Hunt445e8152015-02-06 13:00:12 -0800793 }
794
795 // predicate that indicates when clicking is active
796 function clickEnabled() {
797 return true;
798 }
Simon Hunt737c89f2015-01-28 12:23:19 -0800799
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700800 // =============================================
801 // function entry points for overlay module
Simon Huntf542d842015-02-11 16:20:33 -0800802
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700803 // TODO: find an automatic way of tracking via the "showHighlights" events
Simon Hunte50829c2015-06-09 08:39:28 -0700804 var allTrafficClasses = 'primary secondary optical animated ' +
805 'port-traffic-Kbps port-traffic-Mbps port-traffic-Gbps ' +
806 'port-traffic-Gbps-choked';
Simon Huntf542d842015-02-11 16:20:33 -0800807
808 function clearLinkTrafficStyle() {
809 link.style('stroke-width', null)
810 .classed(allTrafficClasses, false);
811 }
812
813 function removeLinkLabels() {
814 network.links.forEach(function (d) {
815 d.label = '';
816 });
817 }
Simon Hunt737c89f2015-01-28 12:23:19 -0800818
Simon Hunta4242de2015-02-24 17:11:55 -0800819 function updateLinkLabelModel() {
820 // create the backing data for showing labels..
821 var data = [];
822 link.each(function (d) {
823 if (d.label) {
824 data.push({
825 id: 'lab-' + d.key,
826 key: d.key,
827 label: d.label,
828 ldata: d
829 });
830 }
831 });
832
833 linkLabel = linkLabelG.selectAll('.linkLabel')
834 .data(data, function (d) { return d.id; });
835 }
836
Simon Hunt737c89f2015-01-28 12:23:19 -0800837 // ==========================
Simon Huntac4c6f72015-02-03 19:50:53 -0800838 // Module definition
Simon Hunt737c89f2015-01-28 12:23:19 -0800839
Simon Huntdc6adea2015-02-09 22:29:36 -0800840 function mkModelApi(uplink) {
841 return {
842 projection: uplink.projection,
843 network: network,
844 restyleLinkElement: restyleLinkElement,
845 removeLinkElement: removeLinkElement
846 };
847 }
848
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700849 function mkD3Api() {
Simon Hunta4242de2015-02-24 17:11:55 -0800850 return {
851 node: function () { return node; },
852 link: function () { return link; },
853 linkLabel: function () { return linkLabel; },
854 instVisible: function () { return tis.isVisible(); },
855 posNode: tms.positionNode,
856 showHosts: function () { return showHosts; },
857 restyleLinkElement: restyleLinkElement,
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700858 updateLinkLabelModel: updateLinkLabelModel,
859 linkConfig: function () { return linkConfig; }
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700860 };
Simon Hunta4242de2015-02-24 17:11:55 -0800861 }
862
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700863 function mkSelectApi() {
Simon Hunt08f841d02015-02-10 14:39:20 -0800864 return {
865 node: function () { return node; },
866 zoomingOrPanning: zoomingOrPanning,
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700867 updateDeviceColors: td3.updateDeviceColors,
868 deselectLink: tls.deselectLink
Simon Hunt08f841d02015-02-10 14:39:20 -0800869 };
870 }
871
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700872 function mkTrafficApi() {
873 return {
874 hovered: tss.hovered,
875 somethingSelected: tss.somethingSelected,
876 selectOrder: tss.selectOrder
877 };
878 }
879
880 function mkOverlayApi() {
Simon Huntf542d842015-02-11 16:20:33 -0800881 return {
882 clearLinkTrafficStyle: clearLinkTrafficStyle,
883 removeLinkLabels: removeLinkLabels,
884 updateLinks: updateLinks,
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700885 findLinkById: tms.findLinkById
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700886 };
Simon Huntf542d842015-02-11 16:20:33 -0800887 }
888
Simon Huntc3c5b672015-02-20 11:32:13 -0800889 function mkObliqueApi(uplink, fltr) {
Simon Hunt96f88c62015-02-19 17:57:25 -0800890 return {
Simon Huntc3c5b672015-02-20 11:32:13 -0800891 force: function() { return force; },
892 zoomLayer: uplink.zoomLayer,
893 nodeGBBox: function() { return nodeG.node().getBBox(); },
Simon Hunt96f88c62015-02-19 17:57:25 -0800894 node: function () { return node; },
Simon Huntc3c5b672015-02-20 11:32:13 -0800895 link: function () { return link; },
896 linkLabel: function () { return linkLabel; },
897 nodes: function () { return network.nodes; },
898 tickStuff: tickStuff,
899 nodeLock: function (b) {
900 var old = nodeLock;
901 nodeLock = b;
902 return old;
903 },
904 opacifyMap: uplink.opacifyMap,
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700905 inLayer: fltr.inLayer,
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700906 calcLinkPos: calcPosition,
907 applyNumLinkLabels: function () {
908 td3.applyNumLinkLabels(linkNums, numLinkLblsG);
909 }
Simon Hunt96f88c62015-02-19 17:57:25 -0800910 };
911 }
912
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700913 function mkFilterApi() {
Simon Hunteb0fa052015-02-17 19:20:28 -0800914 return {
915 node: function () { return node; },
916 link: function () { return link; }
917 };
918 }
919
Simon Hunt9e2104c2015-02-26 10:48:59 -0800920 function mkLinkApi(svg, uplink) {
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800921 return {
922 svg: svg,
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800923 zoomer: uplink.zoomer(),
924 network: network,
Simon Hunt1a5301e2015-02-25 15:31:25 -0800925 portLabelG: function () { return portLabelG; },
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800926 showHosts: function () { return showHosts; }
927 };
928 }
929
Simon Hunt737c89f2015-01-28 12:23:19 -0800930 angular.module('ovTopo')
931 .factory('TopoForceService',
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700932 ['$log', '$timeout', 'FnService', 'SvgUtilService',
Simon Hunt86b7c882015-04-02 23:06:08 -0700933 'ThemeService', 'FlashService', 'WebSocketService',
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700934 'TopoOverlayService', 'TopoInstService', 'TopoModelService',
Simon Hunta4242de2015-02-24 17:11:55 -0800935 'TopoD3Service', 'TopoSelectService', 'TopoTrafficService',
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800936 'TopoObliqueService', 'TopoFilterService', 'TopoLinkService',
Simon Hunt737c89f2015-01-28 12:23:19 -0800937
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700938 function (_$log_, _$timeout_, _fs_, _sus_, _ts_, _flash_, _wss_, _tov_,
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800939 _tis_, _tms_, _td3_, _tss_, _tts_, _tos_, _fltr_, _tls_) {
Simon Hunt737c89f2015-01-28 12:23:19 -0800940 $log = _$log_;
Simon Hunt86b7c882015-04-02 23:06:08 -0700941 $timeout = _$timeout_;
Simon Hunt1894d792015-02-04 17:09:20 -0800942 fs = _fs_;
Simon Hunt737c89f2015-01-28 12:23:19 -0800943 sus = _sus_;
Simon Huntac4c6f72015-02-03 19:50:53 -0800944 ts = _ts_;
Simon Hunt5724fb42015-02-05 16:59:40 -0800945 flash = _flash_;
Simon Hunt237676b52015-03-10 19:04:26 -0700946 wss = _wss_;
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700947 tov = _tov_;
Simon Huntac4c6f72015-02-03 19:50:53 -0800948 tis = _tis_;
Simon Hunt3a6eec02015-02-09 21:16:43 -0800949 tms = _tms_;
Simon Hunta4242de2015-02-24 17:11:55 -0800950 td3 = _td3_;
Simon Hunt08f841d02015-02-10 14:39:20 -0800951 tss = _tss_;
Simon Huntf542d842015-02-11 16:20:33 -0800952 tts = _tts_;
Simon Hunt96f88c62015-02-19 17:57:25 -0800953 tos = _tos_;
Simon Hunteb0fa052015-02-17 19:20:28 -0800954 fltr = _fltr_;
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800955 tls = _tls_;
Simon Hunt737c89f2015-01-28 12:23:19 -0800956
Simon Hunta142dd22015-02-12 22:07:51 -0800957 var themeListener = ts.addListener(function () {
958 updateLinks();
959 updateNodes();
960 });
961
Simon Hunt737c89f2015-01-28 12:23:19 -0800962 // forceG is the SVG group to display the force layout in
Simon Huntdc6adea2015-02-09 22:29:36 -0800963 // uplink is the api from the main topo source file
Simon Hunt3a6eec02015-02-09 21:16:43 -0800964 // dim is the initial dimensions of the SVG as [w,h]
Simon Hunt737c89f2015-01-28 12:23:19 -0800965 // opts are, well, optional :)
Simon Hunt3ab20282015-02-26 20:32:19 -0800966 function initForce(_svg_, forceG, _uplink_, _dim_, opts) {
Simon Hunt1894d792015-02-04 17:09:20 -0800967 uplink = _uplink_;
Simon Hunt3a6eec02015-02-09 21:16:43 -0800968 dim = _dim_;
Simon Hunt3ab20282015-02-26 20:32:19 -0800969 svg = _svg_;
970
971 lu = network.lookup;
972 rlk = network.revLinkToKey;
Simon Hunt3a6eec02015-02-09 21:16:43 -0800973
974 $log.debug('initForce().. dim = ' + dim);
975
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700976 tov.setApi(mkOverlayApi(), tss);
Simon Huntdc6adea2015-02-09 22:29:36 -0800977 tms.initModel(mkModelApi(uplink), dim);
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700978 td3.initD3(mkD3Api());
979 tss.initSelect(mkSelectApi());
980 tts.initTraffic(mkTrafficApi());
Simon Huntc3c5b672015-02-20 11:32:13 -0800981 tos.initOblique(mkObliqueApi(uplink, fltr));
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700982 fltr.initFilter(mkFilterApi());
Simon Hunt9e2104c2015-02-26 10:48:59 -0800983 tls.initLink(mkLinkApi(svg, uplink), td3);
Simon Hunta11b4eb2015-01-28 16:20:50 -0800984
Simon Hunt737c89f2015-01-28 12:23:19 -0800985 settings = angular.extend({}, defaultSettings, opts);
986
987 linkG = forceG.append('g').attr('id', 'topo-links');
988 linkLabelG = forceG.append('g').attr('id', 'topo-linkLabels');
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700989 numLinkLblsG = forceG.append('g').attr('id', 'topo-numLinkLabels');
Simon Hunt737c89f2015-01-28 12:23:19 -0800990 nodeG = forceG.append('g').attr('id', 'topo-nodes');
Simon Hunt1a5301e2015-02-25 15:31:25 -0800991 portLabelG = forceG.append('g').attr('id', 'topo-portLabels');
Simon Hunt737c89f2015-01-28 12:23:19 -0800992
993 link = linkG.selectAll('.link');
994 linkLabel = linkLabelG.selectAll('.linkLabel');
995 node = nodeG.selectAll('.node');
996
997 force = d3.layout.force()
Simon Hunt3a6eec02015-02-09 21:16:43 -0800998 .size(dim)
Simon Hunt737c89f2015-01-28 12:23:19 -0800999 .nodes(network.nodes)
1000 .links(network.links)
1001 .gravity(settings.gravity)
1002 .friction(settings.friction)
1003 .charge(settings.charge._def_)
1004 .linkDistance(settings.linkDistance._def_)
1005 .linkStrength(settings.linkStrength._def_)
1006 .on('tick', tick);
1007
1008 drag = sus.createDragBehavior(force,
Simon Hunt08f841d02015-02-10 14:39:20 -08001009 tss.selectObject, atDragEnd, dragEnabled, clickEnabled);
Simon Hunt737c89f2015-01-28 12:23:19 -08001010 }
1011
Simon Hunt3a6eec02015-02-09 21:16:43 -08001012 function newDim(_dim_) {
1013 dim = _dim_;
1014 force.size(dim);
1015 tms.newDim(dim);
Simon Hunt737c89f2015-01-28 12:23:19 -08001016 }
1017
Simon Hunt3a6eec02015-02-09 21:16:43 -08001018 function destroyForce() {
Simon Hunt3ab20282015-02-26 20:32:19 -08001019 force.stop();
1020
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001021 tls.destroyLink();
Simon Hunt96f88c62015-02-19 17:57:25 -08001022 tos.destroyOblique();
Simon Huntf542d842015-02-11 16:20:33 -08001023 tts.destroyTraffic();
1024 tss.destroySelect();
Simon Hunta4242de2015-02-24 17:11:55 -08001025 td3.destroyD3();
Simon Huntf542d842015-02-11 16:20:33 -08001026 tms.destroyModel();
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001027 // note: no need to destroy overlay service
Simon Hunta142dd22015-02-12 22:07:51 -08001028 ts.removeListener(themeListener);
1029 themeListener = null;
Simon Hunt3ab20282015-02-26 20:32:19 -08001030
1031 // clean up the DOM
1032 svg.selectAll('g').remove();
1033 svg.selectAll('defs').remove();
1034
1035 // clean up internal state
1036 network.nodes = [];
1037 network.links = [];
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -07001038 network.linksByDevice = {};
Simon Hunt3ab20282015-02-26 20:32:19 -08001039 network.lookup = {};
1040 network.revLinkToKey = {};
1041
Bri Prebilic Cole80401762015-07-16 11:36:18 -07001042 linkNums = [];
1043
1044 linkG = linkLabelG = numLinkLblsG = nodeG = portLabelG = null;
Simon Hunt3ab20282015-02-26 20:32:19 -08001045 link = linkLabel = node = null;
1046 force = drag = null;
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -07001047
1048 // clean up $timeout promises
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -07001049 if (fNodesTimer) {
1050 $timeout.cancel(fNodesTimer);
1051 }
1052 if (fLinksTimer) {
1053 $timeout.cancel(fLinksTimer);
1054 }
Simon Hunt3a6eec02015-02-09 21:16:43 -08001055 }
1056
Simon Hunt737c89f2015-01-28 12:23:19 -08001057 return {
1058 initForce: initForce,
Simon Hunt3a6eec02015-02-09 21:16:43 -08001059 newDim: newDim,
1060 destroyForce: destroyForce,
Simon Huntac4c6f72015-02-03 19:50:53 -08001061
Simon Hunta4242de2015-02-24 17:11:55 -08001062 updateDeviceColors: td3.updateDeviceColors,
Simon Hunt5724fb42015-02-05 16:59:40 -08001063 toggleHosts: toggleHosts,
Simon Hunt9e2104c2015-02-26 10:48:59 -08001064 togglePorts: tls.togglePorts,
Simon Hunt5724fb42015-02-05 16:59:40 -08001065 toggleOffline: toggleOffline,
1066 cycleDeviceLabels: cycleDeviceLabels,
Simon Hunt445e8152015-02-06 13:00:12 -08001067 unpin: unpin,
Simon Hunta142dd22015-02-12 22:07:51 -08001068 showMastership: showMastership,
Simon Hunt86b7c882015-04-02 23:06:08 -07001069 showBadLinks: showBadLinks,
Simon Huntac4c6f72015-02-03 19:50:53 -08001070
1071 addDevice: addDevice,
Simon Hunt1894d792015-02-04 17:09:20 -08001072 updateDevice: updateDevice,
1073 removeDevice: removeDevice,
1074 addHost: addHost,
1075 updateHost: updateHost,
1076 removeHost: removeHost,
1077 addLink: addLink,
1078 updateLink: updateLink,
1079 removeLink: removeLink
Simon Hunt737c89f2015-01-28 12:23:19 -08001080 };
1081 }]);
1082}());