blob: f00b87fae4b062960dac7b146f7fcdfd912e95ed [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)
Simon Hunta17fa672015-08-19 18:42:22 -070061 fTimer, // timer for delayed force layout
Thomas Vachuska1a989c12015-06-09 18:29:22 -070062 fNodesTimer, // timer for delayed nodes update
63 fLinksTimer, // timer for delayed links update
Bri Prebilic Cole80401762015-07-16 11:36:18 -070064 dim, // the dimensions of the force layout [w,h]
65 linkNums = []; // array of link number labels
Simon Hunt737c89f2015-01-28 12:23:19 -080066
67 // SVG elements;
Bri Prebilic Cole80401762015-07-16 11:36:18 -070068 var linkG, linkLabelG, numLinkLblsG, portLabelG, nodeG;
Simon Hunt737c89f2015-01-28 12:23:19 -080069
70 // D3 selections;
71 var link, linkLabel, node;
72
73 // default settings for force layout
74 var defaultSettings = {
75 gravity: 0.4,
76 friction: 0.7,
77 charge: {
78 // note: key is node.class
79 device: -8000,
80 host: -5000,
81 _def_: -12000
82 },
83 linkDistance: {
84 // note: key is link.type
85 direct: 100,
86 optical: 120,
87 hostLink: 3,
88 _def_: 50
89 },
90 linkStrength: {
91 // note: key is link.type
92 // range: {0.0 ... 1.0}
93 //direct: 1.0,
94 //optical: 1.0,
95 //hostLink: 1.0,
96 _def_: 1.0
97 }
98 };
99
100
Simon Huntac4c6f72015-02-03 19:50:53 -0800101 // ==========================
102 // === EVENT HANDLERS
103
104 function addDevice(data) {
105 var id = data.id,
106 d;
107
Simon Hunt1894d792015-02-04 17:09:20 -0800108 uplink.showNoDevs(false);
Simon Huntac4c6f72015-02-03 19:50:53 -0800109
110 // although this is an add device event, if we already have the
111 // device, treat it as an update instead..
Simon Hunt1894d792015-02-04 17:09:20 -0800112 if (lu[id]) {
Simon Huntac4c6f72015-02-03 19:50:53 -0800113 updateDevice(data);
114 return;
115 }
116
Simon Hunt3a6eec02015-02-09 21:16:43 -0800117 d = tms.createDeviceNode(data);
Simon Huntac4c6f72015-02-03 19:50:53 -0800118 network.nodes.push(d);
Simon Hunt1894d792015-02-04 17:09:20 -0800119 lu[id] = d;
Simon Huntac4c6f72015-02-03 19:50:53 -0800120 updateNodes();
Simon Hunta17fa672015-08-19 18:42:22 -0700121 fStart();
Simon Huntac4c6f72015-02-03 19:50:53 -0800122 }
123
124 function updateDevice(data) {
125 var id = data.id,
Simon Hunt1894d792015-02-04 17:09:20 -0800126 d = lu[id],
Simon Huntac4c6f72015-02-03 19:50:53 -0800127 wasOnline;
128
129 if (d) {
130 wasOnline = d.online;
131 angular.extend(d, data);
Simon Hunt3a6eec02015-02-09 21:16:43 -0800132 if (tms.positionNode(d, true)) {
Simon Hunt445e8152015-02-06 13:00:12 -0800133 sendUpdateMeta(d);
Simon Huntac4c6f72015-02-03 19:50:53 -0800134 }
135 updateNodes();
136 if (wasOnline !== d.online) {
Simon Huntdc6adea2015-02-09 22:29:36 -0800137 tms.findAttachedLinks(d.id).forEach(restyleLinkElement);
Simon Hunt5724fb42015-02-05 16:59:40 -0800138 updateOfflineVisibility(d);
Simon Huntac4c6f72015-02-03 19:50:53 -0800139 }
Simon Huntac4c6f72015-02-03 19:50:53 -0800140 }
141 }
142
Simon Hunt1894d792015-02-04 17:09:20 -0800143 function removeDevice(data) {
144 var id = data.id,
145 d = lu[id];
146 if (d) {
147 removeDeviceElement(d);
Simon Hunt1894d792015-02-04 17:09:20 -0800148 }
149 }
150
151 function addHost(data) {
152 var id = data.id,
153 d, lnk;
154
155 // although this is an add host event, if we already have the
156 // host, treat it as an update instead..
157 if (lu[id]) {
158 updateHost(data);
159 return;
160 }
161
Simon Hunt3a6eec02015-02-09 21:16:43 -0800162 d = tms.createHostNode(data);
Simon Hunt1894d792015-02-04 17:09:20 -0800163 network.nodes.push(d);
164 lu[id] = d;
Simon Hunt1894d792015-02-04 17:09:20 -0800165 updateNodes();
166
Simon Hunt3a6eec02015-02-09 21:16:43 -0800167 lnk = tms.createHostLink(data);
Simon Hunt1894d792015-02-04 17:09:20 -0800168 if (lnk) {
Simon Hunt1894d792015-02-04 17:09:20 -0800169 d.linkData = lnk; // cache ref on its host
170 network.links.push(lnk);
171 lu[d.ingress] = lnk;
172 lu[d.egress] = lnk;
173 updateLinks();
174 }
Simon Hunta17fa672015-08-19 18:42:22 -0700175 fStart();
Simon Hunt1894d792015-02-04 17:09:20 -0800176 }
177
178 function updateHost(data) {
179 var id = data.id,
180 d = lu[id];
181 if (d) {
182 angular.extend(d, data);
Simon Hunt3a6eec02015-02-09 21:16:43 -0800183 if (tms.positionNode(d, true)) {
Simon Hunt445e8152015-02-06 13:00:12 -0800184 sendUpdateMeta(d);
Simon Hunt1894d792015-02-04 17:09:20 -0800185 }
186 updateNodes();
Simon Hunt1894d792015-02-04 17:09:20 -0800187 }
188 }
189
190 function removeHost(data) {
191 var id = data.id,
192 d = lu[id];
193 if (d) {
194 removeHostElement(d, true);
Simon Hunt1894d792015-02-04 17:09:20 -0800195 }
196 }
197
198 function addLink(data) {
Simon Huntdc6adea2015-02-09 22:29:36 -0800199 var result = tms.findLink(data, 'add'),
Simon Hunt1894d792015-02-04 17:09:20 -0800200 bad = result.badLogic,
201 d = result.ldata;
202
203 if (bad) {
204 //logicError(bad + ': ' + link.id);
205 return;
206 }
207
208 if (d) {
209 // we already have a backing store link for src/dst nodes
210 addLinkUpdate(d, data);
211 return;
212 }
213
214 // no backing store link yet
Simon Hunt3a6eec02015-02-09 21:16:43 -0800215 d = tms.createLink(data);
Simon Hunt1894d792015-02-04 17:09:20 -0800216 if (d) {
217 network.links.push(d);
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700218 aggregateLink(d, data);
Simon Hunt1894d792015-02-04 17:09:20 -0800219 lu[d.key] = d;
220 updateLinks();
Simon Hunta17fa672015-08-19 18:42:22 -0700221 fStart();
Simon Hunt1894d792015-02-04 17:09:20 -0800222 }
223 }
224
225 function updateLink(data) {
Simon Huntdc6adea2015-02-09 22:29:36 -0800226 var result = tms.findLink(data, 'update'),
Simon Hunt1894d792015-02-04 17:09:20 -0800227 bad = result.badLogic;
228 if (bad) {
229 //logicError(bad + ': ' + link.id);
230 return;
231 }
232 result.updateWith(link);
233 }
234
235 function removeLink(data) {
Simon Hunta4242de2015-02-24 17:11:55 -0800236 var result = tms.findLink(data, 'remove');
237
238 if (!result.badLogic) {
239 result.removeRawLink();
Simon Hunt1894d792015-02-04 17:09:20 -0800240 }
Simon Hunt1894d792015-02-04 17:09:20 -0800241 }
242
243 // ========================
244
Simon Hunt94f7dae2015-08-26 17:40:59 -0700245 function nodeById(id) {
246 return lu[id];
247 }
248
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700249 function makeNodeKey(node1, node2) {
250 return node1 + '-' + node2;
251 }
252
253 function findNodePair(key, keyRev) {
254 if (network.linksByDevice[key]) {
255 return key;
256 } else if (network.linksByDevice[keyRev]) {
257 return keyRev;
258 } else {
259 return false;
260 }
261 }
262
263 function aggregateLink(ldata, link) {
264 var key = makeNodeKey(link.src, link.dst),
265 keyRev = makeNodeKey(link.dst, link.src),
266 found = findNodePair(key, keyRev);
267
268 if (found) {
269 network.linksByDevice[found].push(ldata);
270 ldata.devicePair = found;
271 } else {
272 network.linksByDevice[key] = [ ldata ];
273 ldata.devicePair = key;
274 }
275 }
276
Simon Hunt1894d792015-02-04 17:09:20 -0800277 function addLinkUpdate(ldata, link) {
278 // add link event, but we already have the reverse link installed
279 ldata.fromTarget = link;
Simon Huntdc6adea2015-02-09 22:29:36 -0800280 rlk[link.id] = ldata.key;
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700281 // possible solution to el being undefined in restyleLinkElement:
282 //_updateLinks();
Simon Hunt1894d792015-02-04 17:09:20 -0800283 restyleLinkElement(ldata);
284 }
285
Simon Hunt1894d792015-02-04 17:09:20 -0800286
287 var widthRatio = 1.4,
288 linkScale = d3.scale.linear()
289 .domain([1, 12])
290 .range([widthRatio, 12 * widthRatio])
Simon Hunt5724fb42015-02-05 16:59:40 -0800291 .clamp(true),
Simon Hunt3a6eec02015-02-09 21:16:43 -0800292 allLinkTypes = 'direct indirect optical tunnel';
Simon Hunt1894d792015-02-04 17:09:20 -0800293
Simon Hunta142dd22015-02-12 22:07:51 -0800294 function restyleLinkElement(ldata, immediate) {
Simon Hunt1894d792015-02-04 17:09:20 -0800295 // this fn's job is to look at raw links and decide what svg classes
296 // need to be applied to the line element in the DOM
297 var th = ts.theme(),
298 el = ldata.el,
299 type = ldata.type(),
300 lw = ldata.linkWidth(),
Simon Hunta142dd22015-02-12 22:07:51 -0800301 online = ldata.online(),
302 delay = immediate ? 0 : 1000;
Simon Hunt1894d792015-02-04 17:09:20 -0800303
Thomas Vachuskacb5016f2015-05-18 14:11:43 -0700304 // FIXME: understand why el is sometimes undefined on addLink events...
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700305 // Investigated:
306 // el is undefined when it's a reverse link that is being added.
307 // updateLinks (which sets ldata.el) isn't called before this is called.
308 // Calling _updateLinks in addLinkUpdate fixes it, but there might be
309 // a more efficient way to fix it.
310 if (el && !el.empty()) {
Thomas Vachuskacb5016f2015-05-18 14:11:43 -0700311 el.classed('link', true);
312 el.classed('inactive', !online);
313 el.classed(allLinkTypes, false);
314 if (type) {
315 el.classed(type, true);
316 }
317 el.transition()
318 .duration(delay)
319 .attr('stroke-width', linkScale(lw))
320 .attr('stroke', linkConfig[th].baseColor);
Simon Hunt1894d792015-02-04 17:09:20 -0800321 }
Simon Hunt1894d792015-02-04 17:09:20 -0800322 }
323
Simon Hunt1894d792015-02-04 17:09:20 -0800324 function removeLinkElement(d) {
325 var idx = fs.find(d.key, network.links, 'key'),
326 removed;
327 if (idx >=0) {
328 // remove from links array
329 removed = network.links.splice(idx, 1);
330 // remove from lookup cache
331 delete lu[removed[0].key];
332 updateLinks();
Simon Hunta17fa672015-08-19 18:42:22 -0700333 fResume();
Simon Hunt1894d792015-02-04 17:09:20 -0800334 }
335 }
336
337 function removeHostElement(d, upd) {
338 // first, remove associated hostLink...
339 removeLinkElement(d.linkData);
340
341 // remove hostLink bindings
342 delete lu[d.ingress];
343 delete lu[d.egress];
344
345 // remove from lookup cache
346 delete lu[d.id];
347 // remove from nodes array
348 var idx = fs.find(d.id, network.nodes);
349 network.nodes.splice(idx, 1);
350
351 // remove from SVG
352 // NOTE: upd is false if we were called from removeDeviceElement()
353 if (upd) {
354 updateNodes();
Simon Hunta17fa672015-08-19 18:42:22 -0700355 fResume();
Simon Hunt1894d792015-02-04 17:09:20 -0800356 }
357 }
358
359 function removeDeviceElement(d) {
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700360 var id = d.id,
361 idx;
Simon Hunt1894d792015-02-04 17:09:20 -0800362 // first, remove associated hosts and links..
Simon Huntdc6adea2015-02-09 22:29:36 -0800363 tms.findAttachedHosts(id).forEach(removeHostElement);
364 tms.findAttachedLinks(id).forEach(removeLinkElement);
Simon Hunt1894d792015-02-04 17:09:20 -0800365
366 // remove from lookup cache
367 delete lu[id];
368 // remove from nodes array
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700369 idx = fs.find(id, network.nodes);
370 if (idx > -1) {
371 network.nodes.splice(idx, 1);
372 }
Simon Hunt1894d792015-02-04 17:09:20 -0800373
374 if (!network.nodes.length) {
Simon Huntdc6adea2015-02-09 22:29:36 -0800375 uplink.showNoDevs(true);
Simon Hunt1894d792015-02-04 17:09:20 -0800376 }
377
378 // remove from SVG
379 updateNodes();
Simon Hunta17fa672015-08-19 18:42:22 -0700380 fResume();
Simon Hunt1894d792015-02-04 17:09:20 -0800381 }
382
Simon Hunt5724fb42015-02-05 16:59:40 -0800383 function updateHostVisibility() {
Simon Hunt18bf9822015-02-12 17:35:45 -0800384 sus.visible(nodeG.selectAll('.host'), showHosts);
385 sus.visible(linkG.selectAll('.hostLink'), showHosts);
Simon Hunt8eb4d3a2015-02-23 18:23:29 -0800386 sus.visible(linkLabelG.selectAll('.hostLinkLabel'), showHosts);
Simon Hunt5724fb42015-02-05 16:59:40 -0800387 }
388
389 function updateOfflineVisibility(dev) {
390 function updDev(d, show) {
Simon Hunt8eb4d3a2015-02-23 18:23:29 -0800391 var b;
Simon Hunt18bf9822015-02-12 17:35:45 -0800392 sus.visible(d.el, show);
Simon Hunt5724fb42015-02-05 16:59:40 -0800393
Simon Huntdc6adea2015-02-09 22:29:36 -0800394 tms.findAttachedLinks(d.id).forEach(function (link) {
Simon Hunt5724fb42015-02-05 16:59:40 -0800395 b = show && ((link.type() !== 'hostLink') || showHosts);
Simon Hunt18bf9822015-02-12 17:35:45 -0800396 sus.visible(link.el, b);
Simon Hunt5724fb42015-02-05 16:59:40 -0800397 });
Simon Huntdc6adea2015-02-09 22:29:36 -0800398 tms.findAttachedHosts(d.id).forEach(function (host) {
Simon Hunt5724fb42015-02-05 16:59:40 -0800399 b = show && showHosts;
Simon Hunt18bf9822015-02-12 17:35:45 -0800400 sus.visible(host.el, b);
Simon Hunt5724fb42015-02-05 16:59:40 -0800401 });
402 }
403
404 if (dev) {
405 // updating a specific device that just toggled off/on-line
406 updDev(dev, dev.online || showOffline);
407 } else {
408 // updating all offline devices
Simon Huntdc6adea2015-02-09 22:29:36 -0800409 tms.findDevices(true).forEach(function (d) {
Simon Hunt5724fb42015-02-05 16:59:40 -0800410 updDev(d, showOffline);
411 });
412 }
413 }
414
Simon Hunt1894d792015-02-04 17:09:20 -0800415
Simon Hunt445e8152015-02-06 13:00:12 -0800416 function sendUpdateMeta(d, clearPos) {
Simon Huntac4c6f72015-02-03 19:50:53 -0800417 var metaUi = {},
418 ll;
419
Simon Hunt445e8152015-02-06 13:00:12 -0800420 // if we are not clearing the position data (unpinning),
421 // attach the x, y, longitude, latitude...
422 if (!clearPos) {
Simon Hunt3a6eec02015-02-09 21:16:43 -0800423 ll = tms.lngLatFromCoord([d.x, d.y]);
Simon Huntdc6adea2015-02-09 22:29:36 -0800424 metaUi = {x: d.x, y: d.y, lng: ll[0], lat: ll[1]};
Simon Hunt1894d792015-02-04 17:09:20 -0800425 }
426 d.metaUi = metaUi;
Simon Hunt237676b52015-03-10 19:04:26 -0700427 wss.sendEvent('updateMeta', {
Simon Hunt1894d792015-02-04 17:09:20 -0800428 id: d.id,
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700429 class: d.class,
Simon Hunt1894d792015-02-04 17:09:20 -0800430 memento: metaUi
431 });
Simon Huntac4c6f72015-02-03 19:50:53 -0800432 }
433
Simon Hunt1894d792015-02-04 17:09:20 -0800434
Simon Huntac4c6f72015-02-03 19:50:53 -0800435 function mkSvgClass(d) {
436 return d.fixed ? d.svgClass + ' fixed' : d.svgClass;
437 }
438
Simon Hunt5724fb42015-02-05 16:59:40 -0800439 function vis(b) {
440 return b ? 'visible' : 'hidden';
441 }
442
Simon Huntfcbde892015-04-16 12:05:28 -0700443 function toggleHosts(x) {
444 var kev = (x === 'keyev'),
445 on = kev ? !showHosts : !!x;
446
447 showHosts = on;
Simon Hunt5724fb42015-02-05 16:59:40 -0800448 updateHostVisibility();
Simon Huntfcbde892015-04-16 12:05:28 -0700449 flash.flash('Hosts ' + vis(on));
450 return on;
Simon Hunt5724fb42015-02-05 16:59:40 -0800451 }
452
Simon Huntfcbde892015-04-16 12:05:28 -0700453 function toggleOffline(x) {
454 var kev = (x === 'keyev'),
455 on = kev ? !showOffline : !!x;
456
457 showOffline = on;
Simon Hunt5724fb42015-02-05 16:59:40 -0800458 updateOfflineVisibility();
Simon Huntfcbde892015-04-16 12:05:28 -0700459 flash.flash('Offline devices ' + vis(on));
460 return on;
Simon Hunt5724fb42015-02-05 16:59:40 -0800461 }
462
463 function cycleDeviceLabels() {
Bri Prebilic Cole9cf1a8d2015-04-21 13:15:29 -0700464 flash.flash(td3.incDevLabIndex());
Simon Huntdc6adea2015-02-09 22:29:36 -0800465 tms.findDevices().forEach(function (d) {
Simon Hunta4242de2015-02-24 17:11:55 -0800466 td3.updateDeviceLabel(d);
Simon Hunt1c367112015-02-05 18:02:46 -0800467 });
Simon Hunt5724fb42015-02-05 16:59:40 -0800468 }
469
Simon Hunt445e8152015-02-06 13:00:12 -0800470 function unpin() {
Simon Hunt08f841d02015-02-10 14:39:20 -0800471 var hov = tss.hovered();
472 if (hov) {
473 sendUpdateMeta(hov, true);
474 hov.fixed = false;
475 hov.el.classed('fixed', false);
Simon Hunt445e8152015-02-06 13:00:12 -0800476 fResume();
477 }
478 }
479
Simon Hunta142dd22015-02-12 22:07:51 -0800480 function showMastership(masterId) {
481 if (!masterId) {
482 restoreLayerState();
483 } else {
484 showMastershipFor(masterId);
485 }
486 }
487
488 function restoreLayerState() {
489 // NOTE: this level of indirection required, for when we have
490 // the layer filter functionality re-implemented
491 suppressLayers(false);
492 }
493
494 function showMastershipFor(id) {
495 suppressLayers(true);
496 node.each(function (n) {
497 if (n.master === id) {
Simon Hunt743a8492015-08-25 16:18:19 -0700498 n.el.classed('suppressedmax', false);
Simon Hunta142dd22015-02-12 22:07:51 -0800499 }
500 });
501 }
502
Simon Hunt743a8492015-08-25 16:18:19 -0700503 function supAmt(less) {
504 return less ? "suppressed" : "suppressedmax";
505 }
506
507 function suppressLayers(b, less) {
508 var cls = supAmt(less);
509 node.classed(cls, b);
510 link.classed(cls, b);
511 }
512
513 function unsuppressNode(id, less) {
514 var cls = supAmt(less);
515 node.each(function (n) {
516 if (n.id === id) {
517 n.el.classed(cls, false);
518 }
519 });
520 }
521
Simon Hunt94f7dae2015-08-26 17:40:59 -0700522 function unsuppressLink(key, less) {
Simon Hunt743a8492015-08-25 16:18:19 -0700523 var cls = supAmt(less);
524 link.each(function (n) {
Simon Hunt94f7dae2015-08-26 17:40:59 -0700525 if (n.key === key) {
Simon Hunt743a8492015-08-25 16:18:19 -0700526 n.el.classed(cls, false);
527 }
528 });
Simon Hunta142dd22015-02-12 22:07:51 -0800529 }
Simon Hunt445e8152015-02-06 13:00:12 -0800530
Simon Hunt86b7c882015-04-02 23:06:08 -0700531 function showBadLinks() {
532 var badLinks = tms.findBadLinks();
533 flash.flash('Bad Links: ' + badLinks.length);
534 $log.debug('Bad Link List (' + badLinks.length + '):');
535 badLinks.forEach(function (d) {
536 $log.debug('bad link: (' + d.bad + ') ' + d.key, d);
537 if (d.el) {
538 d.el.attr('stroke-width', linkScale(2.8))
539 .attr('stroke', 'red');
540 }
541 });
542 // back to normal after 2 seconds...
543 $timeout(updateLinks, 2000);
544 }
545
Simon Hunt5724fb42015-02-05 16:59:40 -0800546 // ==========================================
547
Simon Huntac4c6f72015-02-03 19:50:53 -0800548 function updateNodes() {
Thomas Vachuska1a989c12015-06-09 18:29:22 -0700549 if (fNodesTimer) {
550 $timeout.cancel(fNodesTimer);
551 }
552 fNodesTimer = $timeout(_updateNodes, 150);
553 }
554
Simon Hunta17fa672015-08-19 18:42:22 -0700555 // IMPLEMENTATION NOTE: _updateNodes() should NOT stop, start, or resume
556 // the force layout; that needs to be determined and implemented elsewhere
Thomas Vachuska1a989c12015-06-09 18:29:22 -0700557 function _updateNodes() {
Simon Hunt1894d792015-02-04 17:09:20 -0800558 // select all the nodes in the layout:
Simon Huntac4c6f72015-02-03 19:50:53 -0800559 node = nodeG.selectAll('.node')
560 .data(network.nodes, function (d) { return d.id; });
561
Simon Hunt1894d792015-02-04 17:09:20 -0800562 // operate on existing nodes:
Simon Hunta4242de2015-02-24 17:11:55 -0800563 node.filter('.device').each(td3.deviceExisting);
564 node.filter('.host').each(td3.hostExisting);
Simon Huntac4c6f72015-02-03 19:50:53 -0800565
566 // operate on entering nodes:
567 var entering = node.enter()
568 .append('g')
569 .attr({
570 id: function (d) { return sus.safeId(d.id); },
571 class: mkSvgClass,
Simon Hunta17fa672015-08-19 18:42:22 -0700572 transform: function (d) {
573 // Need to guard against NaN here ??
574 return sus.translate(d.x, d.y);
575 },
Simon Huntac4c6f72015-02-03 19:50:53 -0800576 opacity: 0
577 })
578 .call(drag)
Simon Hunt08f841d02015-02-10 14:39:20 -0800579 .on('mouseover', tss.nodeMouseOver)
580 .on('mouseout', tss.nodeMouseOut)
Simon Huntac4c6f72015-02-03 19:50:53 -0800581 .transition()
582 .attr('opacity', 1);
583
Simon Hunt1894d792015-02-04 17:09:20 -0800584 // augment entering nodes:
Simon Hunta4242de2015-02-24 17:11:55 -0800585 entering.filter('.device').each(td3.deviceEnter);
586 entering.filter('.host').each(td3.hostEnter);
Simon Huntac4c6f72015-02-03 19:50:53 -0800587
Simon Hunt51056592015-02-03 21:48:07 -0800588 // operate on both existing and new nodes:
Simon Hunta4242de2015-02-24 17:11:55 -0800589 td3.updateDeviceColors();
Simon Huntac4c6f72015-02-03 19:50:53 -0800590
591 // operate on exiting nodes:
592 // Note that the node is removed after 2 seconds.
593 // Sub element animations should be shorter than 2 seconds.
594 var exiting = node.exit()
595 .transition()
596 .duration(2000)
597 .style('opacity', 0)
598 .remove();
599
Simon Hunt1894d792015-02-04 17:09:20 -0800600 // exiting node specifics:
Simon Hunta4242de2015-02-24 17:11:55 -0800601 exiting.filter('.host').each(td3.hostExit);
602 exiting.filter('.device').each(td3.deviceExit);
Simon Huntac4c6f72015-02-03 19:50:53 -0800603 }
604
Simon Hunt51056592015-02-03 21:48:07 -0800605 // ==========================
Simon Hunt1894d792015-02-04 17:09:20 -0800606
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700607 function getDefaultPos(link) {
608 return {
609 x1: link.source.x,
610 y1: link.source.y,
611 x2: link.target.x,
612 y2: link.target.y
613 };
614 }
615
616 // returns amount of adjustment along the normal for given link
617 function amt(numLinks, linkIdx) {
618 var gap = 6;
619 return (linkIdx - ((numLinks - 1) / 2)) * gap;
620 }
621
622 function calcMovement(d, amt, flipped) {
623 var pos = getDefaultPos(d),
624 mult = flipped ? -amt : amt,
625 dx = pos.x2 - pos.x1,
626 dy = pos.y2 - pos.y1,
627 length = Math.sqrt((dx * dx) + (dy * dy));
628
629 return {
630 x1: pos.x1 + (mult * dy / length),
631 y1: pos.y1 + (mult * -dx / length),
632 x2: pos.x2 + (mult * dy / length),
633 y2: pos.y2 + (mult * -dx / length)
634 };
635 }
636
637 function calcPosition() {
638 var lines = this,
639 linkSrcId;
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700640 linkNums = [];
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700641 lines.each(function (d) {
642 if (d.type() === 'hostLink') {
643 d.position = getDefaultPos(d);
644 }
645 });
646
647 function normalizeLinkSrc(link) {
648 // ensure source device is consistent across set of links
649 // temporary measure until link modeling is refactored
650 if (!linkSrcId) {
651 linkSrcId = link.source.id;
652 return false;
653 }
654
655 return link.source.id !== linkSrcId;
656 }
657
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700658 angular.forEach(network.linksByDevice, function (linkArr, key) {
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700659 var numLinks = linkArr.length,
660 link;
661
662 if (numLinks === 1) {
663 link = linkArr[0];
664 link.position = getDefaultPos(link);
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700665 link.position.multiLink = false;
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700666 } else if (numLinks >= 5) {
667 // this code is inefficient, in the future the way links
668 // are modeled will be changed
669 angular.forEach(linkArr, function (link) {
670 link.position = getDefaultPos(link);
671 link.position.multiLink = true;
672 });
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700673 linkNums.push({
674 id: key,
675 num: numLinks,
676 linkCoords: linkArr[0].position
677 });
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700678 } else {
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700679 linkSrcId = null;
680 angular.forEach(linkArr, function (link, index) {
681 var offsetAmt = amt(numLinks, index),
682 needToFlip = normalizeLinkSrc(link);
683 link.position = calcMovement(link, offsetAmt, needToFlip);
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700684 link.position.multiLink = false;
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700685 });
686 }
687 });
688 }
689
Simon Hunt1894d792015-02-04 17:09:20 -0800690 function updateLinks() {
Thomas Vachuska1a989c12015-06-09 18:29:22 -0700691 if (fLinksTimer) {
692 $timeout.cancel(fLinksTimer);
693 }
694 fLinksTimer = $timeout(_updateLinks, 150);
695 }
696
Simon Hunta17fa672015-08-19 18:42:22 -0700697 // IMPLEMENTATION NOTE: _updateLinks() should NOT stop, start, or resume
698 // the force layout; that needs to be determined and implemented elsewhere
Thomas Vachuska1a989c12015-06-09 18:29:22 -0700699 function _updateLinks() {
Simon Hunt1894d792015-02-04 17:09:20 -0800700 var th = ts.theme();
701
702 link = linkG.selectAll('.link')
703 .data(network.links, function (d) { return d.key; });
704
705 // operate on existing links:
Simon Huntd5264122015-02-25 10:17:43 -0800706 link.each(function (d) {
707 // this is supposed to be an existing link, but we have observed
708 // occasions (where links are deleted and added rapidly?) where
709 // the DOM element has not been defined. So protect against that...
710 if (d.el) {
711 restyleLinkElement(d, true);
712 }
713 });
Simon Hunt1894d792015-02-04 17:09:20 -0800714
715 // operate on entering links:
716 var entering = link.enter()
717 .append('line')
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700718 .call(calcPosition)
Simon Hunt1894d792015-02-04 17:09:20 -0800719 .attr({
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700720 x1: function (d) { return d.position.x1; },
721 y1: function (d) { return d.position.y1; },
722 x2: function (d) { return d.position.x2; },
723 y2: function (d) { return d.position.y2; },
Simon Hunt1894d792015-02-04 17:09:20 -0800724 stroke: linkConfig[th].inColor,
725 'stroke-width': linkConfig.inWidth
726 });
727
728 // augment links
Simon Hunta4242de2015-02-24 17:11:55 -0800729 entering.each(td3.linkEntering);
Simon Hunt1894d792015-02-04 17:09:20 -0800730
731 // operate on both existing and new links:
732 //link.each(...)
733
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700734 // add labels for how many links are in a thick line
735 td3.applyNumLinkLabels(linkNums, numLinkLblsG);
736
Simon Hunt1894d792015-02-04 17:09:20 -0800737 // apply or remove labels
Simon Hunta4242de2015-02-24 17:11:55 -0800738 td3.applyLinkLabels();
Simon Hunt1894d792015-02-04 17:09:20 -0800739
740 // operate on exiting links:
741 link.exit()
742 .attr('stroke-dasharray', '3 3')
Simon Hunt5724fb42015-02-05 16:59:40 -0800743 .attr('stroke', linkConfig[th].outColor)
Simon Hunt1894d792015-02-04 17:09:20 -0800744 .style('opacity', 0.5)
745 .transition()
746 .duration(1500)
747 .attr({
748 'stroke-dasharray': '3 12',
Simon Hunt1894d792015-02-04 17:09:20 -0800749 'stroke-width': linkConfig.outWidth
750 })
751 .style('opacity', 0.0)
752 .remove();
Simon Hunt1894d792015-02-04 17:09:20 -0800753 }
754
Simon Huntac4c6f72015-02-03 19:50:53 -0800755
756 // ==========================
Simon Hunt737c89f2015-01-28 12:23:19 -0800757 // force layout tick function
Simon Hunt737c89f2015-01-28 12:23:19 -0800758
Simon Hunt5724fb42015-02-05 16:59:40 -0800759 function fResume() {
Simon Huntc3c5b672015-02-20 11:32:13 -0800760 if (!tos.isOblique()) {
Simon Hunt5724fb42015-02-05 16:59:40 -0800761 force.resume();
762 }
763 }
764
765 function fStart() {
Simon Huntc3c5b672015-02-20 11:32:13 -0800766 if (!tos.isOblique()) {
Simon Hunta17fa672015-08-19 18:42:22 -0700767 if (fTimer) {
768 $timeout.cancel(fTimer);
769 }
770 fTimer = $timeout(function () {
771 $log.debug("Starting force-layout");
772 force.start();
773 }, 200);
Simon Hunt5724fb42015-02-05 16:59:40 -0800774 }
775 }
776
777 var tickStuff = {
778 nodeAttr: {
Simon Hunta17fa672015-08-19 18:42:22 -0700779 transform: function (d) {
780 var dx = isNaN(d.x) ? 0 : d.x,
781 dy = isNaN(d.y) ? 0 : d.y;
782 return sus.translate(dx, dy);
783 }
Simon Hunt5724fb42015-02-05 16:59:40 -0800784 },
785 linkAttr: {
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700786 x1: function (d) { return d.position.x1; },
787 y1: function (d) { return d.position.y1; },
788 x2: function (d) { return d.position.x2; },
789 y2: function (d) { return d.position.y2; }
Simon Hunt5724fb42015-02-05 16:59:40 -0800790 },
791 linkLabelAttr: {
792 transform: function (d) {
Simon Huntdc6adea2015-02-09 22:29:36 -0800793 var lnk = tms.findLinkById(d.key);
Simon Hunt5724fb42015-02-05 16:59:40 -0800794 if (lnk) {
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700795 return td3.transformLabel(lnk.position);
Simon Hunt5724fb42015-02-05 16:59:40 -0800796 }
797 }
798 }
799 };
800
801 function tick() {
Simon Hunt3ab20282015-02-26 20:32:19 -0800802 // guard against null (which can happen when our view pages out)...
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700803 if (node && node.size()) {
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700804 node.attr(tickStuff.nodeAttr);
805 }
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700806 if (link && link.size()) {
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700807 link.call(calcPosition)
808 .attr(tickStuff.linkAttr);
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700809 td3.applyNumLinkLabels(linkNums, numLinkLblsG);
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700810 }
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700811 if (linkLabel && linkLabel.size()) {
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700812 linkLabel.attr(tickStuff.linkLabelAttr);
813 }
Simon Hunt737c89f2015-01-28 12:23:19 -0800814 }
815
816
Simon Huntac4c6f72015-02-03 19:50:53 -0800817 // ==========================
818 // === MOUSE GESTURE HANDLERS
819
Simon Hunt205099e2015-02-07 13:12:01 -0800820 function zoomingOrPanning(ev) {
821 return ev.metaKey || ev.altKey;
Simon Hunt445e8152015-02-06 13:00:12 -0800822 }
823
824 function atDragEnd(d) {
825 // once we've finished moving, pin the node in position
826 d.fixed = true;
827 d3.select(this).classed('fixed', true);
828 sendUpdateMeta(d);
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700829 tss.clickConsumed(true);
Simon Hunt445e8152015-02-06 13:00:12 -0800830 }
831
832 // predicate that indicates when dragging is active
833 function dragEnabled() {
834 var ev = d3.event.sourceEvent;
835 // nodeLock means we aren't allowing nodes to be dragged...
Simon Hunt205099e2015-02-07 13:12:01 -0800836 return !nodeLock && !zoomingOrPanning(ev);
Simon Hunt445e8152015-02-06 13:00:12 -0800837 }
838
839 // predicate that indicates when clicking is active
840 function clickEnabled() {
841 return true;
842 }
Simon Hunt737c89f2015-01-28 12:23:19 -0800843
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700844 // =============================================
845 // function entry points for overlay module
Simon Huntf542d842015-02-11 16:20:33 -0800846
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700847 // TODO: find an automatic way of tracking via the "showHighlights" events
Simon Hunte50829c2015-06-09 08:39:28 -0700848 var allTrafficClasses = 'primary secondary optical animated ' +
849 'port-traffic-Kbps port-traffic-Mbps port-traffic-Gbps ' +
850 'port-traffic-Gbps-choked';
Simon Huntf542d842015-02-11 16:20:33 -0800851
852 function clearLinkTrafficStyle() {
853 link.style('stroke-width', null)
854 .classed(allTrafficClasses, false);
855 }
856
857 function removeLinkLabels() {
858 network.links.forEach(function (d) {
859 d.label = '';
860 });
861 }
Simon Hunt737c89f2015-01-28 12:23:19 -0800862
Simon Hunte9343f32015-10-21 18:07:46 -0700863 function clearNodeDeco() {
864 node.selectAll('g.badge').remove();
865 }
866
867 function removeNodeBadges() {
868 network.nodes.forEach(function (d) {
869 d.badge = null;
870 });
871 }
872
Simon Hunta4242de2015-02-24 17:11:55 -0800873 function updateLinkLabelModel() {
874 // create the backing data for showing labels..
875 var data = [];
876 link.each(function (d) {
877 if (d.label) {
878 data.push({
879 id: 'lab-' + d.key,
880 key: d.key,
881 label: d.label,
882 ldata: d
883 });
884 }
885 });
886
887 linkLabel = linkLabelG.selectAll('.linkLabel')
888 .data(data, function (d) { return d.id; });
889 }
890
Simon Hunt737c89f2015-01-28 12:23:19 -0800891 // ==========================
Simon Huntac4c6f72015-02-03 19:50:53 -0800892 // Module definition
Simon Hunt737c89f2015-01-28 12:23:19 -0800893
Simon Huntdc6adea2015-02-09 22:29:36 -0800894 function mkModelApi(uplink) {
895 return {
896 projection: uplink.projection,
897 network: network,
898 restyleLinkElement: restyleLinkElement,
899 removeLinkElement: removeLinkElement
900 };
901 }
902
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700903 function mkD3Api() {
Simon Hunta4242de2015-02-24 17:11:55 -0800904 return {
905 node: function () { return node; },
906 link: function () { return link; },
907 linkLabel: function () { return linkLabel; },
908 instVisible: function () { return tis.isVisible(); },
909 posNode: tms.positionNode,
910 showHosts: function () { return showHosts; },
911 restyleLinkElement: restyleLinkElement,
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700912 updateLinkLabelModel: updateLinkLabelModel,
913 linkConfig: function () { return linkConfig; }
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700914 };
Simon Hunta4242de2015-02-24 17:11:55 -0800915 }
916
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700917 function mkSelectApi() {
Simon Hunt08f841d02015-02-10 14:39:20 -0800918 return {
919 node: function () { return node; },
920 zoomingOrPanning: zoomingOrPanning,
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700921 updateDeviceColors: td3.updateDeviceColors,
922 deselectLink: tls.deselectLink
Simon Hunt08f841d02015-02-10 14:39:20 -0800923 };
924 }
925
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700926 function mkTrafficApi() {
927 return {
928 hovered: tss.hovered,
929 somethingSelected: tss.somethingSelected,
930 selectOrder: tss.selectOrder
931 };
932 }
933
934 function mkOverlayApi() {
Simon Huntf542d842015-02-11 16:20:33 -0800935 return {
Simon Hunte9343f32015-10-21 18:07:46 -0700936 clearNodeDeco: clearNodeDeco,
937 removeNodeBadges: removeNodeBadges,
Simon Huntf542d842015-02-11 16:20:33 -0800938 clearLinkTrafficStyle: clearLinkTrafficStyle,
939 removeLinkLabels: removeLinkLabels,
Simon Hunt743a8492015-08-25 16:18:19 -0700940 findLinkById: tms.findLinkById,
Simon Hunt94f7dae2015-08-26 17:40:59 -0700941 findNodeById: nodeById,
Simon Huntf542d842015-02-11 16:20:33 -0800942 updateLinks: updateLinks,
Simon Hunt743a8492015-08-25 16:18:19 -0700943 updateNodes: updateNodes,
944 supLayers: suppressLayers,
945 unsupNode: unsuppressNode,
946 unsupLink: unsuppressLink
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700947 };
Simon Huntf542d842015-02-11 16:20:33 -0800948 }
949
Simon Huntc3c5b672015-02-20 11:32:13 -0800950 function mkObliqueApi(uplink, fltr) {
Simon Hunt96f88c62015-02-19 17:57:25 -0800951 return {
Simon Huntc3c5b672015-02-20 11:32:13 -0800952 force: function() { return force; },
953 zoomLayer: uplink.zoomLayer,
954 nodeGBBox: function() { return nodeG.node().getBBox(); },
Simon Hunt96f88c62015-02-19 17:57:25 -0800955 node: function () { return node; },
Simon Huntc3c5b672015-02-20 11:32:13 -0800956 link: function () { return link; },
957 linkLabel: function () { return linkLabel; },
958 nodes: function () { return network.nodes; },
959 tickStuff: tickStuff,
960 nodeLock: function (b) {
961 var old = nodeLock;
962 nodeLock = b;
963 return old;
964 },
965 opacifyMap: uplink.opacifyMap,
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700966 inLayer: fltr.inLayer,
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700967 calcLinkPos: calcPosition,
968 applyNumLinkLabels: function () {
969 td3.applyNumLinkLabels(linkNums, numLinkLblsG);
970 }
Simon Hunt96f88c62015-02-19 17:57:25 -0800971 };
972 }
973
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700974 function mkFilterApi() {
Simon Hunteb0fa052015-02-17 19:20:28 -0800975 return {
976 node: function () { return node; },
977 link: function () { return link; }
978 };
979 }
980
Simon Hunt9e2104c2015-02-26 10:48:59 -0800981 function mkLinkApi(svg, uplink) {
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800982 return {
983 svg: svg,
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800984 zoomer: uplink.zoomer(),
985 network: network,
Simon Hunt1a5301e2015-02-25 15:31:25 -0800986 portLabelG: function () { return portLabelG; },
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800987 showHosts: function () { return showHosts; }
988 };
989 }
990
Simon Hunt737c89f2015-01-28 12:23:19 -0800991 angular.module('ovTopo')
992 .factory('TopoForceService',
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700993 ['$log', '$timeout', 'FnService', 'SvgUtilService',
Simon Hunt86b7c882015-04-02 23:06:08 -0700994 'ThemeService', 'FlashService', 'WebSocketService',
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700995 'TopoOverlayService', 'TopoInstService', 'TopoModelService',
Simon Hunta4242de2015-02-24 17:11:55 -0800996 'TopoD3Service', 'TopoSelectService', 'TopoTrafficService',
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800997 'TopoObliqueService', 'TopoFilterService', 'TopoLinkService',
Simon Hunt737c89f2015-01-28 12:23:19 -0800998
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700999 function (_$log_, _$timeout_, _fs_, _sus_, _ts_, _flash_, _wss_, _tov_,
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001000 _tis_, _tms_, _td3_, _tss_, _tts_, _tos_, _fltr_, _tls_) {
Simon Hunt737c89f2015-01-28 12:23:19 -08001001 $log = _$log_;
Simon Hunt86b7c882015-04-02 23:06:08 -07001002 $timeout = _$timeout_;
Simon Hunt1894d792015-02-04 17:09:20 -08001003 fs = _fs_;
Simon Hunt737c89f2015-01-28 12:23:19 -08001004 sus = _sus_;
Simon Huntac4c6f72015-02-03 19:50:53 -08001005 ts = _ts_;
Simon Hunt5724fb42015-02-05 16:59:40 -08001006 flash = _flash_;
Simon Hunt237676b52015-03-10 19:04:26 -07001007 wss = _wss_;
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001008 tov = _tov_;
Simon Huntac4c6f72015-02-03 19:50:53 -08001009 tis = _tis_;
Simon Hunt3a6eec02015-02-09 21:16:43 -08001010 tms = _tms_;
Simon Hunta4242de2015-02-24 17:11:55 -08001011 td3 = _td3_;
Simon Hunt08f841d02015-02-10 14:39:20 -08001012 tss = _tss_;
Simon Huntf542d842015-02-11 16:20:33 -08001013 tts = _tts_;
Simon Hunt96f88c62015-02-19 17:57:25 -08001014 tos = _tos_;
Simon Hunteb0fa052015-02-17 19:20:28 -08001015 fltr = _fltr_;
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001016 tls = _tls_;
Simon Hunt737c89f2015-01-28 12:23:19 -08001017
Simon Hunta142dd22015-02-12 22:07:51 -08001018 var themeListener = ts.addListener(function () {
1019 updateLinks();
1020 updateNodes();
1021 });
1022
Simon Hunt737c89f2015-01-28 12:23:19 -08001023 // forceG is the SVG group to display the force layout in
Simon Huntdc6adea2015-02-09 22:29:36 -08001024 // uplink is the api from the main topo source file
Simon Hunt3a6eec02015-02-09 21:16:43 -08001025 // dim is the initial dimensions of the SVG as [w,h]
Simon Hunt737c89f2015-01-28 12:23:19 -08001026 // opts are, well, optional :)
Simon Hunt3ab20282015-02-26 20:32:19 -08001027 function initForce(_svg_, forceG, _uplink_, _dim_, opts) {
Simon Hunt1894d792015-02-04 17:09:20 -08001028 uplink = _uplink_;
Simon Hunt3a6eec02015-02-09 21:16:43 -08001029 dim = _dim_;
Simon Hunt3ab20282015-02-26 20:32:19 -08001030 svg = _svg_;
1031
1032 lu = network.lookup;
1033 rlk = network.revLinkToKey;
Simon Hunt3a6eec02015-02-09 21:16:43 -08001034
1035 $log.debug('initForce().. dim = ' + dim);
1036
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001037 tov.setApi(mkOverlayApi(), tss);
Simon Huntdc6adea2015-02-09 22:29:36 -08001038 tms.initModel(mkModelApi(uplink), dim);
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001039 td3.initD3(mkD3Api());
1040 tss.initSelect(mkSelectApi());
1041 tts.initTraffic(mkTrafficApi());
Simon Huntc3c5b672015-02-20 11:32:13 -08001042 tos.initOblique(mkObliqueApi(uplink, fltr));
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001043 fltr.initFilter(mkFilterApi());
Simon Hunt9e2104c2015-02-26 10:48:59 -08001044 tls.initLink(mkLinkApi(svg, uplink), td3);
Simon Hunta11b4eb2015-01-28 16:20:50 -08001045
Simon Hunt737c89f2015-01-28 12:23:19 -08001046 settings = angular.extend({}, defaultSettings, opts);
1047
1048 linkG = forceG.append('g').attr('id', 'topo-links');
1049 linkLabelG = forceG.append('g').attr('id', 'topo-linkLabels');
Bri Prebilic Cole80401762015-07-16 11:36:18 -07001050 numLinkLblsG = forceG.append('g').attr('id', 'topo-numLinkLabels');
Simon Hunt737c89f2015-01-28 12:23:19 -08001051 nodeG = forceG.append('g').attr('id', 'topo-nodes');
Simon Hunt1a5301e2015-02-25 15:31:25 -08001052 portLabelG = forceG.append('g').attr('id', 'topo-portLabels');
Simon Hunt737c89f2015-01-28 12:23:19 -08001053
1054 link = linkG.selectAll('.link');
1055 linkLabel = linkLabelG.selectAll('.linkLabel');
1056 node = nodeG.selectAll('.node');
1057
1058 force = d3.layout.force()
Simon Hunt3a6eec02015-02-09 21:16:43 -08001059 .size(dim)
Simon Hunt737c89f2015-01-28 12:23:19 -08001060 .nodes(network.nodes)
1061 .links(network.links)
1062 .gravity(settings.gravity)
1063 .friction(settings.friction)
1064 .charge(settings.charge._def_)
1065 .linkDistance(settings.linkDistance._def_)
1066 .linkStrength(settings.linkStrength._def_)
1067 .on('tick', tick);
1068
1069 drag = sus.createDragBehavior(force,
Simon Hunt08f841d02015-02-10 14:39:20 -08001070 tss.selectObject, atDragEnd, dragEnabled, clickEnabled);
Simon Hunt737c89f2015-01-28 12:23:19 -08001071 }
1072
Simon Hunt3a6eec02015-02-09 21:16:43 -08001073 function newDim(_dim_) {
1074 dim = _dim_;
1075 force.size(dim);
1076 tms.newDim(dim);
Simon Hunt737c89f2015-01-28 12:23:19 -08001077 }
1078
Simon Hunt3a6eec02015-02-09 21:16:43 -08001079 function destroyForce() {
Simon Hunt3ab20282015-02-26 20:32:19 -08001080 force.stop();
1081
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001082 tls.destroyLink();
Simon Hunt96f88c62015-02-19 17:57:25 -08001083 tos.destroyOblique();
Simon Huntf542d842015-02-11 16:20:33 -08001084 tts.destroyTraffic();
1085 tss.destroySelect();
Simon Hunta4242de2015-02-24 17:11:55 -08001086 td3.destroyD3();
Simon Huntf542d842015-02-11 16:20:33 -08001087 tms.destroyModel();
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001088 // note: no need to destroy overlay service
Simon Hunta142dd22015-02-12 22:07:51 -08001089 ts.removeListener(themeListener);
1090 themeListener = null;
Simon Hunt3ab20282015-02-26 20:32:19 -08001091
1092 // clean up the DOM
1093 svg.selectAll('g').remove();
1094 svg.selectAll('defs').remove();
1095
1096 // clean up internal state
1097 network.nodes = [];
1098 network.links = [];
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -07001099 network.linksByDevice = {};
Simon Hunt3ab20282015-02-26 20:32:19 -08001100 network.lookup = {};
1101 network.revLinkToKey = {};
1102
Bri Prebilic Cole80401762015-07-16 11:36:18 -07001103 linkNums = [];
1104
1105 linkG = linkLabelG = numLinkLblsG = nodeG = portLabelG = null;
Simon Hunt3ab20282015-02-26 20:32:19 -08001106 link = linkLabel = node = null;
1107 force = drag = null;
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -07001108
1109 // clean up $timeout promises
Simon Hunta17fa672015-08-19 18:42:22 -07001110 if (fTimer) {
1111 $timeout.cancel(fTimer);
1112 }
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -07001113 if (fNodesTimer) {
1114 $timeout.cancel(fNodesTimer);
1115 }
1116 if (fLinksTimer) {
1117 $timeout.cancel(fLinksTimer);
1118 }
Simon Hunt3a6eec02015-02-09 21:16:43 -08001119 }
1120
Simon Hunt737c89f2015-01-28 12:23:19 -08001121 return {
1122 initForce: initForce,
Simon Hunt3a6eec02015-02-09 21:16:43 -08001123 newDim: newDim,
1124 destroyForce: destroyForce,
Simon Huntac4c6f72015-02-03 19:50:53 -08001125
Simon Hunta4242de2015-02-24 17:11:55 -08001126 updateDeviceColors: td3.updateDeviceColors,
Simon Hunt5724fb42015-02-05 16:59:40 -08001127 toggleHosts: toggleHosts,
Simon Hunt9e2104c2015-02-26 10:48:59 -08001128 togglePorts: tls.togglePorts,
Simon Hunt5724fb42015-02-05 16:59:40 -08001129 toggleOffline: toggleOffline,
1130 cycleDeviceLabels: cycleDeviceLabels,
Simon Hunt445e8152015-02-06 13:00:12 -08001131 unpin: unpin,
Simon Hunta142dd22015-02-12 22:07:51 -08001132 showMastership: showMastership,
Simon Hunt86b7c882015-04-02 23:06:08 -07001133 showBadLinks: showBadLinks,
Simon Huntac4c6f72015-02-03 19:50:53 -08001134
1135 addDevice: addDevice,
Simon Hunt1894d792015-02-04 17:09:20 -08001136 updateDevice: updateDevice,
1137 removeDevice: removeDevice,
1138 addHost: addHost,
1139 updateHost: updateHost,
1140 removeHost: removeHost,
1141 addLink: addLink,
1142 updateLink: updateLink,
1143 removeLink: removeLink
Simon Hunt737c89f2015-01-28 12:23:19 -08001144 };
1145 }]);
1146}());