blob: 844d7dc937afd2497a7a50c39160ff64b3aaa143 [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
Simon Hunt4a6b54b2015-10-27 22:08:25 -0700243 function topoStartDone(data) {
244 // called when the initial barrage of data has been sent from server
245 uplink.topoStartDone();
246 }
247
Simon Hunt1894d792015-02-04 17:09:20 -0800248 // ========================
249
Simon Hunt94f7dae2015-08-26 17:40:59 -0700250 function nodeById(id) {
251 return lu[id];
252 }
253
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700254 function makeNodeKey(node1, node2) {
255 return node1 + '-' + node2;
256 }
257
258 function findNodePair(key, keyRev) {
259 if (network.linksByDevice[key]) {
260 return key;
261 } else if (network.linksByDevice[keyRev]) {
262 return keyRev;
263 } else {
264 return false;
265 }
266 }
267
268 function aggregateLink(ldata, link) {
269 var key = makeNodeKey(link.src, link.dst),
270 keyRev = makeNodeKey(link.dst, link.src),
271 found = findNodePair(key, keyRev);
272
273 if (found) {
274 network.linksByDevice[found].push(ldata);
275 ldata.devicePair = found;
276 } else {
277 network.linksByDevice[key] = [ ldata ];
278 ldata.devicePair = key;
279 }
280 }
281
Simon Hunt1894d792015-02-04 17:09:20 -0800282 function addLinkUpdate(ldata, link) {
283 // add link event, but we already have the reverse link installed
284 ldata.fromTarget = link;
Simon Huntdc6adea2015-02-09 22:29:36 -0800285 rlk[link.id] = ldata.key;
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700286 // possible solution to el being undefined in restyleLinkElement:
287 //_updateLinks();
Simon Hunt1894d792015-02-04 17:09:20 -0800288 restyleLinkElement(ldata);
289 }
290
Simon Hunt1894d792015-02-04 17:09:20 -0800291
292 var widthRatio = 1.4,
293 linkScale = d3.scale.linear()
294 .domain([1, 12])
295 .range([widthRatio, 12 * widthRatio])
Simon Hunt5724fb42015-02-05 16:59:40 -0800296 .clamp(true),
Simon Hunt3a6eec02015-02-09 21:16:43 -0800297 allLinkTypes = 'direct indirect optical tunnel';
Simon Hunt1894d792015-02-04 17:09:20 -0800298
Simon Hunta142dd22015-02-12 22:07:51 -0800299 function restyleLinkElement(ldata, immediate) {
Simon Hunt1894d792015-02-04 17:09:20 -0800300 // this fn's job is to look at raw links and decide what svg classes
301 // need to be applied to the line element in the DOM
302 var th = ts.theme(),
303 el = ldata.el,
304 type = ldata.type(),
305 lw = ldata.linkWidth(),
Simon Hunta142dd22015-02-12 22:07:51 -0800306 online = ldata.online(),
307 delay = immediate ? 0 : 1000;
Simon Hunt1894d792015-02-04 17:09:20 -0800308
Thomas Vachuskacb5016f2015-05-18 14:11:43 -0700309 // FIXME: understand why el is sometimes undefined on addLink events...
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700310 // Investigated:
311 // el is undefined when it's a reverse link that is being added.
312 // updateLinks (which sets ldata.el) isn't called before this is called.
313 // Calling _updateLinks in addLinkUpdate fixes it, but there might be
314 // a more efficient way to fix it.
315 if (el && !el.empty()) {
Thomas Vachuskacb5016f2015-05-18 14:11:43 -0700316 el.classed('link', true);
317 el.classed('inactive', !online);
318 el.classed(allLinkTypes, false);
319 if (type) {
320 el.classed(type, true);
321 }
322 el.transition()
323 .duration(delay)
324 .attr('stroke-width', linkScale(lw))
325 .attr('stroke', linkConfig[th].baseColor);
Simon Hunt1894d792015-02-04 17:09:20 -0800326 }
Simon Hunt1894d792015-02-04 17:09:20 -0800327 }
328
Simon Hunt1894d792015-02-04 17:09:20 -0800329 function removeLinkElement(d) {
330 var idx = fs.find(d.key, network.links, 'key'),
331 removed;
332 if (idx >=0) {
333 // remove from links array
334 removed = network.links.splice(idx, 1);
335 // remove from lookup cache
336 delete lu[removed[0].key];
337 updateLinks();
Simon Hunta17fa672015-08-19 18:42:22 -0700338 fResume();
Simon Hunt1894d792015-02-04 17:09:20 -0800339 }
340 }
341
342 function removeHostElement(d, upd) {
343 // first, remove associated hostLink...
344 removeLinkElement(d.linkData);
345
346 // remove hostLink bindings
347 delete lu[d.ingress];
348 delete lu[d.egress];
349
350 // remove from lookup cache
351 delete lu[d.id];
352 // remove from nodes array
353 var idx = fs.find(d.id, network.nodes);
354 network.nodes.splice(idx, 1);
355
356 // remove from SVG
357 // NOTE: upd is false if we were called from removeDeviceElement()
358 if (upd) {
359 updateNodes();
Simon Hunta17fa672015-08-19 18:42:22 -0700360 fResume();
Simon Hunt1894d792015-02-04 17:09:20 -0800361 }
362 }
363
364 function removeDeviceElement(d) {
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700365 var id = d.id,
366 idx;
Simon Hunt1894d792015-02-04 17:09:20 -0800367 // first, remove associated hosts and links..
Simon Huntdc6adea2015-02-09 22:29:36 -0800368 tms.findAttachedHosts(id).forEach(removeHostElement);
369 tms.findAttachedLinks(id).forEach(removeLinkElement);
Simon Hunt1894d792015-02-04 17:09:20 -0800370
371 // remove from lookup cache
372 delete lu[id];
373 // remove from nodes array
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700374 idx = fs.find(id, network.nodes);
375 if (idx > -1) {
376 network.nodes.splice(idx, 1);
377 }
Simon Hunt1894d792015-02-04 17:09:20 -0800378
379 if (!network.nodes.length) {
Simon Huntdc6adea2015-02-09 22:29:36 -0800380 uplink.showNoDevs(true);
Simon Hunt1894d792015-02-04 17:09:20 -0800381 }
382
383 // remove from SVG
384 updateNodes();
Simon Hunta17fa672015-08-19 18:42:22 -0700385 fResume();
Simon Hunt1894d792015-02-04 17:09:20 -0800386 }
387
Simon Hunt5724fb42015-02-05 16:59:40 -0800388 function updateHostVisibility() {
Simon Hunt18bf9822015-02-12 17:35:45 -0800389 sus.visible(nodeG.selectAll('.host'), showHosts);
390 sus.visible(linkG.selectAll('.hostLink'), showHosts);
Simon Hunt8eb4d3a2015-02-23 18:23:29 -0800391 sus.visible(linkLabelG.selectAll('.hostLinkLabel'), showHosts);
Simon Hunt5724fb42015-02-05 16:59:40 -0800392 }
393
394 function updateOfflineVisibility(dev) {
395 function updDev(d, show) {
Simon Hunt8eb4d3a2015-02-23 18:23:29 -0800396 var b;
Simon Hunt18bf9822015-02-12 17:35:45 -0800397 sus.visible(d.el, show);
Simon Hunt5724fb42015-02-05 16:59:40 -0800398
Simon Huntdc6adea2015-02-09 22:29:36 -0800399 tms.findAttachedLinks(d.id).forEach(function (link) {
Simon Hunt5724fb42015-02-05 16:59:40 -0800400 b = show && ((link.type() !== 'hostLink') || showHosts);
Simon Hunt18bf9822015-02-12 17:35:45 -0800401 sus.visible(link.el, b);
Simon Hunt5724fb42015-02-05 16:59:40 -0800402 });
Simon Huntdc6adea2015-02-09 22:29:36 -0800403 tms.findAttachedHosts(d.id).forEach(function (host) {
Simon Hunt5724fb42015-02-05 16:59:40 -0800404 b = show && showHosts;
Simon Hunt18bf9822015-02-12 17:35:45 -0800405 sus.visible(host.el, b);
Simon Hunt5724fb42015-02-05 16:59:40 -0800406 });
407 }
408
409 if (dev) {
410 // updating a specific device that just toggled off/on-line
411 updDev(dev, dev.online || showOffline);
412 } else {
413 // updating all offline devices
Simon Huntdc6adea2015-02-09 22:29:36 -0800414 tms.findDevices(true).forEach(function (d) {
Simon Hunt5724fb42015-02-05 16:59:40 -0800415 updDev(d, showOffline);
416 });
417 }
418 }
419
Simon Hunt1894d792015-02-04 17:09:20 -0800420
Simon Hunt445e8152015-02-06 13:00:12 -0800421 function sendUpdateMeta(d, clearPos) {
Simon Huntac4c6f72015-02-03 19:50:53 -0800422 var metaUi = {},
423 ll;
424
Simon Hunt445e8152015-02-06 13:00:12 -0800425 // if we are not clearing the position data (unpinning),
426 // attach the x, y, longitude, latitude...
427 if (!clearPos) {
Simon Hunt3a6eec02015-02-09 21:16:43 -0800428 ll = tms.lngLatFromCoord([d.x, d.y]);
Simon Huntdc6adea2015-02-09 22:29:36 -0800429 metaUi = {x: d.x, y: d.y, lng: ll[0], lat: ll[1]};
Simon Hunt1894d792015-02-04 17:09:20 -0800430 }
431 d.metaUi = metaUi;
Simon Hunt237676b52015-03-10 19:04:26 -0700432 wss.sendEvent('updateMeta', {
Simon Hunt1894d792015-02-04 17:09:20 -0800433 id: d.id,
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700434 class: d.class,
Simon Hunt1894d792015-02-04 17:09:20 -0800435 memento: metaUi
436 });
Simon Huntac4c6f72015-02-03 19:50:53 -0800437 }
438
Simon Hunt1894d792015-02-04 17:09:20 -0800439
Simon Huntac4c6f72015-02-03 19:50:53 -0800440 function mkSvgClass(d) {
441 return d.fixed ? d.svgClass + ' fixed' : d.svgClass;
442 }
443
Simon Hunt5724fb42015-02-05 16:59:40 -0800444 function vis(b) {
445 return b ? 'visible' : 'hidden';
446 }
447
Simon Huntfcbde892015-04-16 12:05:28 -0700448 function toggleHosts(x) {
449 var kev = (x === 'keyev'),
450 on = kev ? !showHosts : !!x;
451
452 showHosts = on;
Simon Hunt5724fb42015-02-05 16:59:40 -0800453 updateHostVisibility();
Simon Huntfcbde892015-04-16 12:05:28 -0700454 flash.flash('Hosts ' + vis(on));
455 return on;
Simon Hunt5724fb42015-02-05 16:59:40 -0800456 }
457
Simon Huntfcbde892015-04-16 12:05:28 -0700458 function toggleOffline(x) {
459 var kev = (x === 'keyev'),
460 on = kev ? !showOffline : !!x;
461
462 showOffline = on;
Simon Hunt5724fb42015-02-05 16:59:40 -0800463 updateOfflineVisibility();
Simon Huntfcbde892015-04-16 12:05:28 -0700464 flash.flash('Offline devices ' + vis(on));
465 return on;
Simon Hunt5724fb42015-02-05 16:59:40 -0800466 }
467
468 function cycleDeviceLabels() {
Bri Prebilic Cole9cf1a8d2015-04-21 13:15:29 -0700469 flash.flash(td3.incDevLabIndex());
Simon Huntdc6adea2015-02-09 22:29:36 -0800470 tms.findDevices().forEach(function (d) {
Simon Hunta4242de2015-02-24 17:11:55 -0800471 td3.updateDeviceLabel(d);
Simon Hunt1c367112015-02-05 18:02:46 -0800472 });
Simon Hunt5724fb42015-02-05 16:59:40 -0800473 }
474
Simon Hunt445e8152015-02-06 13:00:12 -0800475 function unpin() {
Simon Hunt08f841d02015-02-10 14:39:20 -0800476 var hov = tss.hovered();
477 if (hov) {
478 sendUpdateMeta(hov, true);
479 hov.fixed = false;
480 hov.el.classed('fixed', false);
Simon Hunt445e8152015-02-06 13:00:12 -0800481 fResume();
482 }
483 }
484
Simon Hunta142dd22015-02-12 22:07:51 -0800485 function showMastership(masterId) {
486 if (!masterId) {
487 restoreLayerState();
488 } else {
489 showMastershipFor(masterId);
490 }
491 }
492
493 function restoreLayerState() {
494 // NOTE: this level of indirection required, for when we have
495 // the layer filter functionality re-implemented
496 suppressLayers(false);
497 }
498
499 function showMastershipFor(id) {
500 suppressLayers(true);
501 node.each(function (n) {
502 if (n.master === id) {
Simon Hunt743a8492015-08-25 16:18:19 -0700503 n.el.classed('suppressedmax', false);
Simon Hunta142dd22015-02-12 22:07:51 -0800504 }
505 });
506 }
507
Simon Hunt743a8492015-08-25 16:18:19 -0700508 function supAmt(less) {
509 return less ? "suppressed" : "suppressedmax";
510 }
511
512 function suppressLayers(b, less) {
513 var cls = supAmt(less);
514 node.classed(cls, b);
515 link.classed(cls, b);
516 }
517
518 function unsuppressNode(id, less) {
519 var cls = supAmt(less);
520 node.each(function (n) {
521 if (n.id === id) {
522 n.el.classed(cls, false);
523 }
524 });
525 }
526
Simon Hunt94f7dae2015-08-26 17:40:59 -0700527 function unsuppressLink(key, less) {
Simon Hunt743a8492015-08-25 16:18:19 -0700528 var cls = supAmt(less);
529 link.each(function (n) {
Simon Hunt94f7dae2015-08-26 17:40:59 -0700530 if (n.key === key) {
Simon Hunt743a8492015-08-25 16:18:19 -0700531 n.el.classed(cls, false);
532 }
533 });
Simon Hunta142dd22015-02-12 22:07:51 -0800534 }
Simon Hunt445e8152015-02-06 13:00:12 -0800535
Simon Hunt86b7c882015-04-02 23:06:08 -0700536 function showBadLinks() {
537 var badLinks = tms.findBadLinks();
538 flash.flash('Bad Links: ' + badLinks.length);
539 $log.debug('Bad Link List (' + badLinks.length + '):');
540 badLinks.forEach(function (d) {
541 $log.debug('bad link: (' + d.bad + ') ' + d.key, d);
542 if (d.el) {
543 d.el.attr('stroke-width', linkScale(2.8))
544 .attr('stroke', 'red');
545 }
546 });
547 // back to normal after 2 seconds...
548 $timeout(updateLinks, 2000);
549 }
550
Simon Hunt5724fb42015-02-05 16:59:40 -0800551 // ==========================================
552
Simon Huntac4c6f72015-02-03 19:50:53 -0800553 function updateNodes() {
Thomas Vachuska1a989c12015-06-09 18:29:22 -0700554 if (fNodesTimer) {
555 $timeout.cancel(fNodesTimer);
556 }
557 fNodesTimer = $timeout(_updateNodes, 150);
558 }
559
Simon Hunta17fa672015-08-19 18:42:22 -0700560 // IMPLEMENTATION NOTE: _updateNodes() should NOT stop, start, or resume
561 // the force layout; that needs to be determined and implemented elsewhere
Thomas Vachuska1a989c12015-06-09 18:29:22 -0700562 function _updateNodes() {
Simon Hunt1894d792015-02-04 17:09:20 -0800563 // select all the nodes in the layout:
Simon Huntac4c6f72015-02-03 19:50:53 -0800564 node = nodeG.selectAll('.node')
565 .data(network.nodes, function (d) { return d.id; });
566
Simon Hunt1894d792015-02-04 17:09:20 -0800567 // operate on existing nodes:
Simon Hunta4242de2015-02-24 17:11:55 -0800568 node.filter('.device').each(td3.deviceExisting);
569 node.filter('.host').each(td3.hostExisting);
Simon Huntac4c6f72015-02-03 19:50:53 -0800570
571 // operate on entering nodes:
572 var entering = node.enter()
573 .append('g')
574 .attr({
575 id: function (d) { return sus.safeId(d.id); },
576 class: mkSvgClass,
Simon Hunta17fa672015-08-19 18:42:22 -0700577 transform: function (d) {
578 // Need to guard against NaN here ??
579 return sus.translate(d.x, d.y);
580 },
Simon Huntac4c6f72015-02-03 19:50:53 -0800581 opacity: 0
582 })
583 .call(drag)
Simon Hunt08f841d02015-02-10 14:39:20 -0800584 .on('mouseover', tss.nodeMouseOver)
585 .on('mouseout', tss.nodeMouseOut)
Simon Huntac4c6f72015-02-03 19:50:53 -0800586 .transition()
587 .attr('opacity', 1);
588
Simon Hunt1894d792015-02-04 17:09:20 -0800589 // augment entering nodes:
Simon Hunta4242de2015-02-24 17:11:55 -0800590 entering.filter('.device').each(td3.deviceEnter);
591 entering.filter('.host').each(td3.hostEnter);
Simon Huntac4c6f72015-02-03 19:50:53 -0800592
Simon Hunt51056592015-02-03 21:48:07 -0800593 // operate on both existing and new nodes:
Simon Hunta4242de2015-02-24 17:11:55 -0800594 td3.updateDeviceColors();
Simon Huntac4c6f72015-02-03 19:50:53 -0800595
596 // operate on exiting nodes:
597 // Note that the node is removed after 2 seconds.
598 // Sub element animations should be shorter than 2 seconds.
599 var exiting = node.exit()
600 .transition()
601 .duration(2000)
602 .style('opacity', 0)
603 .remove();
604
Simon Hunt1894d792015-02-04 17:09:20 -0800605 // exiting node specifics:
Simon Hunta4242de2015-02-24 17:11:55 -0800606 exiting.filter('.host').each(td3.hostExit);
607 exiting.filter('.device').each(td3.deviceExit);
Simon Huntac4c6f72015-02-03 19:50:53 -0800608 }
609
Simon Hunt51056592015-02-03 21:48:07 -0800610 // ==========================
Simon Hunt1894d792015-02-04 17:09:20 -0800611
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700612 function getDefaultPos(link) {
613 return {
614 x1: link.source.x,
615 y1: link.source.y,
616 x2: link.target.x,
617 y2: link.target.y
618 };
619 }
620
621 // returns amount of adjustment along the normal for given link
622 function amt(numLinks, linkIdx) {
623 var gap = 6;
624 return (linkIdx - ((numLinks - 1) / 2)) * gap;
625 }
626
627 function calcMovement(d, amt, flipped) {
628 var pos = getDefaultPos(d),
629 mult = flipped ? -amt : amt,
630 dx = pos.x2 - pos.x1,
631 dy = pos.y2 - pos.y1,
632 length = Math.sqrt((dx * dx) + (dy * dy));
633
634 return {
635 x1: pos.x1 + (mult * dy / length),
636 y1: pos.y1 + (mult * -dx / length),
637 x2: pos.x2 + (mult * dy / length),
638 y2: pos.y2 + (mult * -dx / length)
639 };
640 }
641
642 function calcPosition() {
643 var lines = this,
644 linkSrcId;
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700645 linkNums = [];
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700646 lines.each(function (d) {
647 if (d.type() === 'hostLink') {
648 d.position = getDefaultPos(d);
649 }
650 });
651
652 function normalizeLinkSrc(link) {
653 // ensure source device is consistent across set of links
654 // temporary measure until link modeling is refactored
655 if (!linkSrcId) {
656 linkSrcId = link.source.id;
657 return false;
658 }
659
660 return link.source.id !== linkSrcId;
661 }
662
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700663 angular.forEach(network.linksByDevice, function (linkArr, key) {
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700664 var numLinks = linkArr.length,
665 link;
666
667 if (numLinks === 1) {
668 link = linkArr[0];
669 link.position = getDefaultPos(link);
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700670 link.position.multiLink = false;
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700671 } else if (numLinks >= 5) {
672 // this code is inefficient, in the future the way links
673 // are modeled will be changed
674 angular.forEach(linkArr, function (link) {
675 link.position = getDefaultPos(link);
676 link.position.multiLink = true;
677 });
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700678 linkNums.push({
679 id: key,
680 num: numLinks,
681 linkCoords: linkArr[0].position
682 });
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700683 } else {
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700684 linkSrcId = null;
685 angular.forEach(linkArr, function (link, index) {
686 var offsetAmt = amt(numLinks, index),
687 needToFlip = normalizeLinkSrc(link);
688 link.position = calcMovement(link, offsetAmt, needToFlip);
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700689 link.position.multiLink = false;
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700690 });
691 }
692 });
693 }
694
Simon Hunt1894d792015-02-04 17:09:20 -0800695 function updateLinks() {
Thomas Vachuska1a989c12015-06-09 18:29:22 -0700696 if (fLinksTimer) {
697 $timeout.cancel(fLinksTimer);
698 }
699 fLinksTimer = $timeout(_updateLinks, 150);
700 }
701
Simon Hunta17fa672015-08-19 18:42:22 -0700702 // IMPLEMENTATION NOTE: _updateLinks() should NOT stop, start, or resume
703 // the force layout; that needs to be determined and implemented elsewhere
Thomas Vachuska1a989c12015-06-09 18:29:22 -0700704 function _updateLinks() {
Simon Hunt1894d792015-02-04 17:09:20 -0800705 var th = ts.theme();
706
707 link = linkG.selectAll('.link')
708 .data(network.links, function (d) { return d.key; });
709
710 // operate on existing links:
Simon Huntd5264122015-02-25 10:17:43 -0800711 link.each(function (d) {
712 // this is supposed to be an existing link, but we have observed
713 // occasions (where links are deleted and added rapidly?) where
714 // the DOM element has not been defined. So protect against that...
715 if (d.el) {
716 restyleLinkElement(d, true);
717 }
718 });
Simon Hunt1894d792015-02-04 17:09:20 -0800719
720 // operate on entering links:
721 var entering = link.enter()
722 .append('line')
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700723 .call(calcPosition)
Simon Hunt1894d792015-02-04 17:09:20 -0800724 .attr({
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700725 x1: function (d) { return d.position.x1; },
726 y1: function (d) { return d.position.y1; },
727 x2: function (d) { return d.position.x2; },
728 y2: function (d) { return d.position.y2; },
Simon Hunt1894d792015-02-04 17:09:20 -0800729 stroke: linkConfig[th].inColor,
730 'stroke-width': linkConfig.inWidth
731 });
732
733 // augment links
Simon Hunta4242de2015-02-24 17:11:55 -0800734 entering.each(td3.linkEntering);
Simon Hunt1894d792015-02-04 17:09:20 -0800735
736 // operate on both existing and new links:
737 //link.each(...)
738
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700739 // add labels for how many links are in a thick line
740 td3.applyNumLinkLabels(linkNums, numLinkLblsG);
741
Simon Hunt1894d792015-02-04 17:09:20 -0800742 // apply or remove labels
Simon Hunta4242de2015-02-24 17:11:55 -0800743 td3.applyLinkLabels();
Simon Hunt1894d792015-02-04 17:09:20 -0800744
745 // operate on exiting links:
746 link.exit()
747 .attr('stroke-dasharray', '3 3')
Simon Hunt5724fb42015-02-05 16:59:40 -0800748 .attr('stroke', linkConfig[th].outColor)
Simon Hunt1894d792015-02-04 17:09:20 -0800749 .style('opacity', 0.5)
750 .transition()
751 .duration(1500)
752 .attr({
753 'stroke-dasharray': '3 12',
Simon Hunt1894d792015-02-04 17:09:20 -0800754 'stroke-width': linkConfig.outWidth
755 })
756 .style('opacity', 0.0)
757 .remove();
Simon Hunt1894d792015-02-04 17:09:20 -0800758 }
759
Simon Huntac4c6f72015-02-03 19:50:53 -0800760
761 // ==========================
Simon Hunt737c89f2015-01-28 12:23:19 -0800762 // force layout tick function
Simon Hunt737c89f2015-01-28 12:23:19 -0800763
Simon Hunt5724fb42015-02-05 16:59:40 -0800764 function fResume() {
Simon Huntc3c5b672015-02-20 11:32:13 -0800765 if (!tos.isOblique()) {
Simon Hunt5724fb42015-02-05 16:59:40 -0800766 force.resume();
767 }
768 }
769
770 function fStart() {
Simon Huntc3c5b672015-02-20 11:32:13 -0800771 if (!tos.isOblique()) {
Simon Hunta17fa672015-08-19 18:42:22 -0700772 if (fTimer) {
773 $timeout.cancel(fTimer);
774 }
775 fTimer = $timeout(function () {
776 $log.debug("Starting force-layout");
777 force.start();
778 }, 200);
Simon Hunt5724fb42015-02-05 16:59:40 -0800779 }
780 }
781
782 var tickStuff = {
783 nodeAttr: {
Simon Hunta17fa672015-08-19 18:42:22 -0700784 transform: function (d) {
785 var dx = isNaN(d.x) ? 0 : d.x,
786 dy = isNaN(d.y) ? 0 : d.y;
787 return sus.translate(dx, dy);
788 }
Simon Hunt5724fb42015-02-05 16:59:40 -0800789 },
790 linkAttr: {
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700791 x1: function (d) { return d.position.x1; },
792 y1: function (d) { return d.position.y1; },
793 x2: function (d) { return d.position.x2; },
794 y2: function (d) { return d.position.y2; }
Simon Hunt5724fb42015-02-05 16:59:40 -0800795 },
796 linkLabelAttr: {
797 transform: function (d) {
Simon Huntdc6adea2015-02-09 22:29:36 -0800798 var lnk = tms.findLinkById(d.key);
Simon Hunt5724fb42015-02-05 16:59:40 -0800799 if (lnk) {
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700800 return td3.transformLabel(lnk.position);
Simon Hunt5724fb42015-02-05 16:59:40 -0800801 }
802 }
803 }
804 };
805
806 function tick() {
Simon Hunt3ab20282015-02-26 20:32:19 -0800807 // guard against null (which can happen when our view pages out)...
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700808 if (node && node.size()) {
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700809 node.attr(tickStuff.nodeAttr);
810 }
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700811 if (link && link.size()) {
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700812 link.call(calcPosition)
813 .attr(tickStuff.linkAttr);
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700814 td3.applyNumLinkLabels(linkNums, numLinkLblsG);
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700815 }
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700816 if (linkLabel && linkLabel.size()) {
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700817 linkLabel.attr(tickStuff.linkLabelAttr);
818 }
Simon Hunt737c89f2015-01-28 12:23:19 -0800819 }
820
821
Simon Huntac4c6f72015-02-03 19:50:53 -0800822 // ==========================
823 // === MOUSE GESTURE HANDLERS
824
Simon Hunt205099e2015-02-07 13:12:01 -0800825 function zoomingOrPanning(ev) {
826 return ev.metaKey || ev.altKey;
Simon Hunt445e8152015-02-06 13:00:12 -0800827 }
828
829 function atDragEnd(d) {
830 // once we've finished moving, pin the node in position
831 d.fixed = true;
832 d3.select(this).classed('fixed', true);
833 sendUpdateMeta(d);
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700834 tss.clickConsumed(true);
Simon Hunt445e8152015-02-06 13:00:12 -0800835 }
836
837 // predicate that indicates when dragging is active
838 function dragEnabled() {
839 var ev = d3.event.sourceEvent;
840 // nodeLock means we aren't allowing nodes to be dragged...
Simon Hunt205099e2015-02-07 13:12:01 -0800841 return !nodeLock && !zoomingOrPanning(ev);
Simon Hunt445e8152015-02-06 13:00:12 -0800842 }
843
844 // predicate that indicates when clicking is active
845 function clickEnabled() {
846 return true;
847 }
Simon Hunt737c89f2015-01-28 12:23:19 -0800848
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700849 // =============================================
850 // function entry points for overlay module
Simon Huntf542d842015-02-11 16:20:33 -0800851
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700852 // TODO: find an automatic way of tracking via the "showHighlights" events
Simon Hunte50829c2015-06-09 08:39:28 -0700853 var allTrafficClasses = 'primary secondary optical animated ' +
854 'port-traffic-Kbps port-traffic-Mbps port-traffic-Gbps ' +
855 'port-traffic-Gbps-choked';
Simon Huntf542d842015-02-11 16:20:33 -0800856
857 function clearLinkTrafficStyle() {
858 link.style('stroke-width', null)
859 .classed(allTrafficClasses, false);
860 }
861
862 function removeLinkLabels() {
863 network.links.forEach(function (d) {
864 d.label = '';
865 });
866 }
Simon Hunt737c89f2015-01-28 12:23:19 -0800867
Simon Hunte9343f32015-10-21 18:07:46 -0700868 function clearNodeDeco() {
869 node.selectAll('g.badge').remove();
870 }
871
872 function removeNodeBadges() {
873 network.nodes.forEach(function (d) {
874 d.badge = null;
875 });
876 }
877
Simon Hunta4242de2015-02-24 17:11:55 -0800878 function updateLinkLabelModel() {
879 // create the backing data for showing labels..
880 var data = [];
881 link.each(function (d) {
882 if (d.label) {
883 data.push({
884 id: 'lab-' + d.key,
885 key: d.key,
886 label: d.label,
887 ldata: d
888 });
889 }
890 });
891
892 linkLabel = linkLabelG.selectAll('.linkLabel')
893 .data(data, function (d) { return d.id; });
894 }
895
Simon Hunt737c89f2015-01-28 12:23:19 -0800896 // ==========================
Simon Huntac4c6f72015-02-03 19:50:53 -0800897 // Module definition
Simon Hunt737c89f2015-01-28 12:23:19 -0800898
Simon Huntdc6adea2015-02-09 22:29:36 -0800899 function mkModelApi(uplink) {
900 return {
901 projection: uplink.projection,
902 network: network,
903 restyleLinkElement: restyleLinkElement,
904 removeLinkElement: removeLinkElement
905 };
906 }
907
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700908 function mkD3Api() {
Simon Hunta4242de2015-02-24 17:11:55 -0800909 return {
910 node: function () { return node; },
911 link: function () { return link; },
912 linkLabel: function () { return linkLabel; },
913 instVisible: function () { return tis.isVisible(); },
914 posNode: tms.positionNode,
915 showHosts: function () { return showHosts; },
916 restyleLinkElement: restyleLinkElement,
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700917 updateLinkLabelModel: updateLinkLabelModel,
918 linkConfig: function () { return linkConfig; }
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700919 };
Simon Hunta4242de2015-02-24 17:11:55 -0800920 }
921
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700922 function mkSelectApi() {
Simon Hunt08f841d02015-02-10 14:39:20 -0800923 return {
924 node: function () { return node; },
925 zoomingOrPanning: zoomingOrPanning,
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700926 updateDeviceColors: td3.updateDeviceColors,
927 deselectLink: tls.deselectLink
Simon Hunt08f841d02015-02-10 14:39:20 -0800928 };
929 }
930
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700931 function mkTrafficApi() {
932 return {
933 hovered: tss.hovered,
934 somethingSelected: tss.somethingSelected,
935 selectOrder: tss.selectOrder
936 };
937 }
938
939 function mkOverlayApi() {
Simon Huntf542d842015-02-11 16:20:33 -0800940 return {
Simon Hunte9343f32015-10-21 18:07:46 -0700941 clearNodeDeco: clearNodeDeco,
942 removeNodeBadges: removeNodeBadges,
Simon Huntf542d842015-02-11 16:20:33 -0800943 clearLinkTrafficStyle: clearLinkTrafficStyle,
944 removeLinkLabels: removeLinkLabels,
Simon Hunt743a8492015-08-25 16:18:19 -0700945 findLinkById: tms.findLinkById,
Simon Hunt94f7dae2015-08-26 17:40:59 -0700946 findNodeById: nodeById,
Simon Huntf542d842015-02-11 16:20:33 -0800947 updateLinks: updateLinks,
Simon Hunt743a8492015-08-25 16:18:19 -0700948 updateNodes: updateNodes,
949 supLayers: suppressLayers,
950 unsupNode: unsuppressNode,
951 unsupLink: unsuppressLink
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700952 };
Simon Huntf542d842015-02-11 16:20:33 -0800953 }
954
Simon Huntc3c5b672015-02-20 11:32:13 -0800955 function mkObliqueApi(uplink, fltr) {
Simon Hunt96f88c62015-02-19 17:57:25 -0800956 return {
Simon Huntc3c5b672015-02-20 11:32:13 -0800957 force: function() { return force; },
958 zoomLayer: uplink.zoomLayer,
959 nodeGBBox: function() { return nodeG.node().getBBox(); },
Simon Hunt96f88c62015-02-19 17:57:25 -0800960 node: function () { return node; },
Simon Huntc3c5b672015-02-20 11:32:13 -0800961 link: function () { return link; },
962 linkLabel: function () { return linkLabel; },
963 nodes: function () { return network.nodes; },
964 tickStuff: tickStuff,
965 nodeLock: function (b) {
966 var old = nodeLock;
967 nodeLock = b;
968 return old;
969 },
970 opacifyMap: uplink.opacifyMap,
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700971 inLayer: fltr.inLayer,
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700972 calcLinkPos: calcPosition,
973 applyNumLinkLabels: function () {
974 td3.applyNumLinkLabels(linkNums, numLinkLblsG);
975 }
Simon Hunt96f88c62015-02-19 17:57:25 -0800976 };
977 }
978
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700979 function mkFilterApi() {
Simon Hunteb0fa052015-02-17 19:20:28 -0800980 return {
981 node: function () { return node; },
982 link: function () { return link; }
983 };
984 }
985
Simon Hunt9e2104c2015-02-26 10:48:59 -0800986 function mkLinkApi(svg, uplink) {
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800987 return {
988 svg: svg,
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800989 zoomer: uplink.zoomer(),
990 network: network,
Simon Hunt1a5301e2015-02-25 15:31:25 -0800991 portLabelG: function () { return portLabelG; },
Simon Huntfb8ea1f2015-02-24 21:38:09 -0800992 showHosts: function () { return showHosts; }
993 };
994 }
995
Simon Hunt737c89f2015-01-28 12:23:19 -0800996 angular.module('ovTopo')
997 .factory('TopoForceService',
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700998 ['$log', '$timeout', 'FnService', 'SvgUtilService',
Simon Hunt86b7c882015-04-02 23:06:08 -0700999 'ThemeService', 'FlashService', 'WebSocketService',
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001000 'TopoOverlayService', 'TopoInstService', 'TopoModelService',
Simon Hunta4242de2015-02-24 17:11:55 -08001001 'TopoD3Service', 'TopoSelectService', 'TopoTrafficService',
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001002 'TopoObliqueService', 'TopoFilterService', 'TopoLinkService',
Simon Hunt737c89f2015-01-28 12:23:19 -08001003
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001004 function (_$log_, _$timeout_, _fs_, _sus_, _ts_, _flash_, _wss_, _tov_,
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001005 _tis_, _tms_, _td3_, _tss_, _tts_, _tos_, _fltr_, _tls_) {
Simon Hunt737c89f2015-01-28 12:23:19 -08001006 $log = _$log_;
Simon Hunt86b7c882015-04-02 23:06:08 -07001007 $timeout = _$timeout_;
Simon Hunt1894d792015-02-04 17:09:20 -08001008 fs = _fs_;
Simon Hunt737c89f2015-01-28 12:23:19 -08001009 sus = _sus_;
Simon Huntac4c6f72015-02-03 19:50:53 -08001010 ts = _ts_;
Simon Hunt5724fb42015-02-05 16:59:40 -08001011 flash = _flash_;
Simon Hunt237676b52015-03-10 19:04:26 -07001012 wss = _wss_;
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001013 tov = _tov_;
Simon Huntac4c6f72015-02-03 19:50:53 -08001014 tis = _tis_;
Simon Hunt3a6eec02015-02-09 21:16:43 -08001015 tms = _tms_;
Simon Hunta4242de2015-02-24 17:11:55 -08001016 td3 = _td3_;
Simon Hunt08f841d02015-02-10 14:39:20 -08001017 tss = _tss_;
Simon Huntf542d842015-02-11 16:20:33 -08001018 tts = _tts_;
Simon Hunt96f88c62015-02-19 17:57:25 -08001019 tos = _tos_;
Simon Hunteb0fa052015-02-17 19:20:28 -08001020 fltr = _fltr_;
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001021 tls = _tls_;
Simon Hunt737c89f2015-01-28 12:23:19 -08001022
Simon Hunta142dd22015-02-12 22:07:51 -08001023 var themeListener = ts.addListener(function () {
1024 updateLinks();
1025 updateNodes();
1026 });
1027
Simon Hunt737c89f2015-01-28 12:23:19 -08001028 // forceG is the SVG group to display the force layout in
Simon Huntdc6adea2015-02-09 22:29:36 -08001029 // uplink is the api from the main topo source file
Simon Hunt3a6eec02015-02-09 21:16:43 -08001030 // dim is the initial dimensions of the SVG as [w,h]
Simon Hunt737c89f2015-01-28 12:23:19 -08001031 // opts are, well, optional :)
Simon Hunt3ab20282015-02-26 20:32:19 -08001032 function initForce(_svg_, forceG, _uplink_, _dim_, opts) {
Simon Hunt1894d792015-02-04 17:09:20 -08001033 uplink = _uplink_;
Simon Hunt3a6eec02015-02-09 21:16:43 -08001034 dim = _dim_;
Simon Hunt3ab20282015-02-26 20:32:19 -08001035 svg = _svg_;
1036
1037 lu = network.lookup;
1038 rlk = network.revLinkToKey;
Simon Hunt3a6eec02015-02-09 21:16:43 -08001039
1040 $log.debug('initForce().. dim = ' + dim);
1041
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001042 tov.setApi(mkOverlayApi(), tss);
Simon Huntdc6adea2015-02-09 22:29:36 -08001043 tms.initModel(mkModelApi(uplink), dim);
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001044 td3.initD3(mkD3Api());
1045 tss.initSelect(mkSelectApi());
1046 tts.initTraffic(mkTrafficApi());
Simon Huntc3c5b672015-02-20 11:32:13 -08001047 tos.initOblique(mkObliqueApi(uplink, fltr));
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001048 fltr.initFilter(mkFilterApi());
Simon Hunt9e2104c2015-02-26 10:48:59 -08001049 tls.initLink(mkLinkApi(svg, uplink), td3);
Simon Hunta11b4eb2015-01-28 16:20:50 -08001050
Simon Hunt737c89f2015-01-28 12:23:19 -08001051 settings = angular.extend({}, defaultSettings, opts);
1052
1053 linkG = forceG.append('g').attr('id', 'topo-links');
1054 linkLabelG = forceG.append('g').attr('id', 'topo-linkLabels');
Bri Prebilic Cole80401762015-07-16 11:36:18 -07001055 numLinkLblsG = forceG.append('g').attr('id', 'topo-numLinkLabels');
Simon Hunt737c89f2015-01-28 12:23:19 -08001056 nodeG = forceG.append('g').attr('id', 'topo-nodes');
Simon Hunt1a5301e2015-02-25 15:31:25 -08001057 portLabelG = forceG.append('g').attr('id', 'topo-portLabels');
Simon Hunt737c89f2015-01-28 12:23:19 -08001058
1059 link = linkG.selectAll('.link');
1060 linkLabel = linkLabelG.selectAll('.linkLabel');
1061 node = nodeG.selectAll('.node');
1062
1063 force = d3.layout.force()
Simon Hunt3a6eec02015-02-09 21:16:43 -08001064 .size(dim)
Simon Hunt737c89f2015-01-28 12:23:19 -08001065 .nodes(network.nodes)
1066 .links(network.links)
1067 .gravity(settings.gravity)
1068 .friction(settings.friction)
1069 .charge(settings.charge._def_)
1070 .linkDistance(settings.linkDistance._def_)
1071 .linkStrength(settings.linkStrength._def_)
1072 .on('tick', tick);
1073
1074 drag = sus.createDragBehavior(force,
Simon Hunt08f841d02015-02-10 14:39:20 -08001075 tss.selectObject, atDragEnd, dragEnabled, clickEnabled);
Simon Hunt737c89f2015-01-28 12:23:19 -08001076 }
1077
Simon Hunt3a6eec02015-02-09 21:16:43 -08001078 function newDim(_dim_) {
1079 dim = _dim_;
1080 force.size(dim);
1081 tms.newDim(dim);
Simon Hunt737c89f2015-01-28 12:23:19 -08001082 }
1083
Simon Hunt3a6eec02015-02-09 21:16:43 -08001084 function destroyForce() {
Simon Hunt3ab20282015-02-26 20:32:19 -08001085 force.stop();
1086
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001087 tls.destroyLink();
Simon Hunt96f88c62015-02-19 17:57:25 -08001088 tos.destroyOblique();
Simon Huntf542d842015-02-11 16:20:33 -08001089 tts.destroyTraffic();
1090 tss.destroySelect();
Simon Hunta4242de2015-02-24 17:11:55 -08001091 td3.destroyD3();
Simon Huntf542d842015-02-11 16:20:33 -08001092 tms.destroyModel();
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001093 // note: no need to destroy overlay service
Simon Hunta142dd22015-02-12 22:07:51 -08001094 ts.removeListener(themeListener);
1095 themeListener = null;
Simon Hunt3ab20282015-02-26 20:32:19 -08001096
1097 // clean up the DOM
1098 svg.selectAll('g').remove();
1099 svg.selectAll('defs').remove();
1100
1101 // clean up internal state
1102 network.nodes = [];
1103 network.links = [];
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -07001104 network.linksByDevice = {};
Simon Hunt3ab20282015-02-26 20:32:19 -08001105 network.lookup = {};
1106 network.revLinkToKey = {};
1107
Bri Prebilic Cole80401762015-07-16 11:36:18 -07001108 linkNums = [];
1109
1110 linkG = linkLabelG = numLinkLblsG = nodeG = portLabelG = null;
Simon Hunt3ab20282015-02-26 20:32:19 -08001111 link = linkLabel = node = null;
1112 force = drag = null;
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -07001113
1114 // clean up $timeout promises
Simon Hunta17fa672015-08-19 18:42:22 -07001115 if (fTimer) {
1116 $timeout.cancel(fTimer);
1117 }
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -07001118 if (fNodesTimer) {
1119 $timeout.cancel(fNodesTimer);
1120 }
1121 if (fLinksTimer) {
1122 $timeout.cancel(fLinksTimer);
1123 }
Simon Hunt3a6eec02015-02-09 21:16:43 -08001124 }
1125
Simon Hunt737c89f2015-01-28 12:23:19 -08001126 return {
1127 initForce: initForce,
Simon Hunt3a6eec02015-02-09 21:16:43 -08001128 newDim: newDim,
1129 destroyForce: destroyForce,
Simon Huntac4c6f72015-02-03 19:50:53 -08001130
Simon Hunta4242de2015-02-24 17:11:55 -08001131 updateDeviceColors: td3.updateDeviceColors,
Simon Hunt5724fb42015-02-05 16:59:40 -08001132 toggleHosts: toggleHosts,
Simon Hunt9e2104c2015-02-26 10:48:59 -08001133 togglePorts: tls.togglePorts,
Simon Hunt5724fb42015-02-05 16:59:40 -08001134 toggleOffline: toggleOffline,
1135 cycleDeviceLabels: cycleDeviceLabels,
Simon Hunt445e8152015-02-06 13:00:12 -08001136 unpin: unpin,
Simon Hunta142dd22015-02-12 22:07:51 -08001137 showMastership: showMastership,
Simon Hunt86b7c882015-04-02 23:06:08 -07001138 showBadLinks: showBadLinks,
Simon Huntac4c6f72015-02-03 19:50:53 -08001139
1140 addDevice: addDevice,
Simon Hunt1894d792015-02-04 17:09:20 -08001141 updateDevice: updateDevice,
1142 removeDevice: removeDevice,
1143 addHost: addHost,
1144 updateHost: updateHost,
1145 removeHost: removeHost,
1146 addLink: addLink,
1147 updateLink: updateLink,
Simon Hunt4a6b54b2015-10-27 22:08:25 -07001148 removeLink: removeLink,
1149 topoStartDone: topoStartDone
Simon Hunt737c89f2015-01-28 12:23:19 -08001150 };
1151 }]);
1152}());