blob: 6d992f40bfc9f6ee3b87c890b7304b673b4bf3c7 [file] [log] [blame]
Simon Hunt737c89f2015-01-28 12:23:19 -08001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
Simon Hunt737c89f2015-01-28 12:23:19 -08003 *
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: {
Simon Huntf44d7262016-06-14 14:46:56 -070032 baseColor: '#939598',
Simon Hunt1894d792015-02-04 17:09:20 -080033 inColor: '#66f',
Simon Hunt3a6eec02015-02-09 21:16:43 -080034 outColor: '#f00'
Simon Hunt1894d792015-02-04 17:09:20 -080035 },
36 dark: {
Simon Huntf44d7262016-06-14 14:46:56 -070037 // TODO : theme
38 baseColor: '#939598',
Simon Hunt1894d792015-02-04 17:09:20 -080039 inColor: '#66f',
Simon Huntf44d7262016-06-14 14:46:56 -070040 outColor: '#f00'
Simon Hunt1894d792015-02-04 17:09:20 -080041 },
42 inWidth: 12,
43 outWidth: 10
44 };
45
Simon Hunt737c89f2015-01-28 12:23:19 -080046 // internal state
Simon Huntac4c6f72015-02-03 19:50:53 -080047 var settings, // merged default settings and options
Simon Hunt737c89f2015-01-28 12:23:19 -080048 force, // force layout object
49 drag, // drag behavior handler
50 network = {
51 nodes: [],
52 links: [],
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -070053 linksByDevice: {},
Simon Hunt737c89f2015-01-28 12:23:19 -080054 lookup: {},
55 revLinkToKey: {}
Simon Huntac4c6f72015-02-03 19:50:53 -080056 },
Simon Hunt3ab20282015-02-26 20:32:19 -080057 lu, // shorthand for lookup
58 rlk, // shorthand for revLinktoKey
Simon Hunta142dd22015-02-12 22:07:51 -080059 showHosts = false, // whether hosts are displayed
Simon Hunt5724fb42015-02-05 16:59:40 -080060 showOffline = true, // whether offline devices are displayed
Simon Hunt445e8152015-02-06 13:00:12 -080061 nodeLock = false, // whether nodes can be dragged or not (locked)
Simon Hunta17fa672015-08-19 18:42:22 -070062 fTimer, // timer for delayed force layout
Thomas Vachuska1a989c12015-06-09 18:29:22 -070063 fNodesTimer, // timer for delayed nodes update
64 fLinksTimer, // timer for delayed links update
Bri Prebilic Cole80401762015-07-16 11:36:18 -070065 dim, // the dimensions of the force layout [w,h]
66 linkNums = []; // array of link number labels
Simon Hunt737c89f2015-01-28 12:23:19 -080067
68 // SVG elements;
Bri Prebilic Cole80401762015-07-16 11:36:18 -070069 var linkG, linkLabelG, numLinkLblsG, portLabelG, nodeG;
Simon Hunt737c89f2015-01-28 12:23:19 -080070
71 // D3 selections;
72 var link, linkLabel, node;
73
74 // default settings for force layout
75 var defaultSettings = {
76 gravity: 0.4,
77 friction: 0.7,
78 charge: {
79 // note: key is node.class
80 device: -8000,
81 host: -5000,
82 _def_: -12000
83 },
84 linkDistance: {
85 // note: key is link.type
86 direct: 100,
87 optical: 120,
88 hostLink: 3,
89 _def_: 50
90 },
91 linkStrength: {
92 // note: key is link.type
93 // range: {0.0 ... 1.0}
94 //direct: 1.0,
95 //optical: 1.0,
96 //hostLink: 1.0,
97 _def_: 1.0
98 }
99 };
100
101
Simon Huntac4c6f72015-02-03 19:50:53 -0800102 // ==========================
103 // === EVENT HANDLERS
104
105 function addDevice(data) {
106 var id = data.id,
107 d;
108
Simon Hunt1894d792015-02-04 17:09:20 -0800109 uplink.showNoDevs(false);
Simon Huntac4c6f72015-02-03 19:50:53 -0800110
111 // although this is an add device event, if we already have the
112 // device, treat it as an update instead..
Simon Hunt1894d792015-02-04 17:09:20 -0800113 if (lu[id]) {
Simon Huntac4c6f72015-02-03 19:50:53 -0800114 updateDevice(data);
115 return;
116 }
117
Simon Hunt3a6eec02015-02-09 21:16:43 -0800118 d = tms.createDeviceNode(data);
Simon Huntac4c6f72015-02-03 19:50:53 -0800119 network.nodes.push(d);
Simon Hunt1894d792015-02-04 17:09:20 -0800120 lu[id] = d;
Simon Huntac4c6f72015-02-03 19:50:53 -0800121 updateNodes();
Simon Hunta17fa672015-08-19 18:42:22 -0700122 fStart();
Simon Huntac4c6f72015-02-03 19:50:53 -0800123 }
124
125 function updateDevice(data) {
126 var id = data.id,
Simon Hunt1894d792015-02-04 17:09:20 -0800127 d = lu[id],
Simon Huntac4c6f72015-02-03 19:50:53 -0800128 wasOnline;
129
130 if (d) {
131 wasOnline = d.online;
132 angular.extend(d, data);
Simon Hunt3a6eec02015-02-09 21:16:43 -0800133 if (tms.positionNode(d, true)) {
Simon Hunt445e8152015-02-06 13:00:12 -0800134 sendUpdateMeta(d);
Simon Huntac4c6f72015-02-03 19:50:53 -0800135 }
136 updateNodes();
137 if (wasOnline !== d.online) {
Simon Huntdc6adea2015-02-09 22:29:36 -0800138 tms.findAttachedLinks(d.id).forEach(restyleLinkElement);
Simon Hunt5724fb42015-02-05 16:59:40 -0800139 updateOfflineVisibility(d);
Simon Huntac4c6f72015-02-03 19:50:53 -0800140 }
Simon Huntac4c6f72015-02-03 19:50:53 -0800141 }
142 }
143
Simon Hunt1894d792015-02-04 17:09:20 -0800144 function removeDevice(data) {
145 var id = data.id,
146 d = lu[id];
147 if (d) {
148 removeDeviceElement(d);
Simon Hunt1894d792015-02-04 17:09:20 -0800149 }
150 }
151
152 function addHost(data) {
153 var id = data.id,
154 d, lnk;
155
156 // although this is an add host event, if we already have the
157 // host, treat it as an update instead..
158 if (lu[id]) {
159 updateHost(data);
160 return;
161 }
162
Simon Hunt3a6eec02015-02-09 21:16:43 -0800163 d = tms.createHostNode(data);
Simon Hunt1894d792015-02-04 17:09:20 -0800164 network.nodes.push(d);
165 lu[id] = d;
Simon Hunt1894d792015-02-04 17:09:20 -0800166 updateNodes();
167
Simon Hunt3a6eec02015-02-09 21:16:43 -0800168 lnk = tms.createHostLink(data);
Simon Hunt1894d792015-02-04 17:09:20 -0800169 if (lnk) {
Simon Hunt1894d792015-02-04 17:09:20 -0800170 d.linkData = lnk; // cache ref on its host
171 network.links.push(lnk);
172 lu[d.ingress] = lnk;
173 lu[d.egress] = lnk;
174 updateLinks();
175 }
Simon Hunta17fa672015-08-19 18:42:22 -0700176 fStart();
Simon Hunt1894d792015-02-04 17:09:20 -0800177 }
178
179 function updateHost(data) {
180 var id = data.id,
181 d = lu[id];
182 if (d) {
183 angular.extend(d, data);
Simon Hunt3a6eec02015-02-09 21:16:43 -0800184 if (tms.positionNode(d, true)) {
Simon Hunt445e8152015-02-06 13:00:12 -0800185 sendUpdateMeta(d);
Simon Hunt1894d792015-02-04 17:09:20 -0800186 }
187 updateNodes();
Simon Hunt1894d792015-02-04 17:09:20 -0800188 }
189 }
190
Simon Hunt95d56fd2015-11-12 11:06:44 -0800191 function moveHost(data) {
192 var id = data.id,
193 d = lu[id],
194 lnk;
195 if (d) {
196 // first remove the old host link
197 removeLinkElement(d.linkData);
198
199 // merge new data
200 angular.extend(d, data);
201 if (tms.positionNode(d, true)) {
202 sendUpdateMeta(d);
203 }
204
205 // now create a new host link
206 lnk = tms.createHostLink(data);
207 if (lnk) {
208 d.linkData = lnk;
209 network.links.push(lnk);
210 lu[d.ingress] = lnk;
211 lu[d.egress] = lnk;
212 }
213
214 updateNodes();
215 updateLinks();
216 fResume();
217 }
218 }
219
Simon Hunt1894d792015-02-04 17:09:20 -0800220 function removeHost(data) {
221 var id = data.id,
222 d = lu[id];
223 if (d) {
224 removeHostElement(d, true);
Simon Hunt1894d792015-02-04 17:09:20 -0800225 }
226 }
227
228 function addLink(data) {
Simon Huntdc6adea2015-02-09 22:29:36 -0800229 var result = tms.findLink(data, 'add'),
Simon Hunt1894d792015-02-04 17:09:20 -0800230 bad = result.badLogic,
231 d = result.ldata;
232
233 if (bad) {
Simon Hunteb18f522016-01-28 19:22:23 -0800234 $log.debug(bad + ': ' + link.id);
Simon Hunt1894d792015-02-04 17:09:20 -0800235 return;
236 }
237
238 if (d) {
239 // we already have a backing store link for src/dst nodes
240 addLinkUpdate(d, data);
241 return;
242 }
243
244 // no backing store link yet
Simon Hunt3a6eec02015-02-09 21:16:43 -0800245 d = tms.createLink(data);
Simon Hunt1894d792015-02-04 17:09:20 -0800246 if (d) {
247 network.links.push(d);
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700248 aggregateLink(d, data);
Simon Hunt1894d792015-02-04 17:09:20 -0800249 lu[d.key] = d;
250 updateLinks();
Simon Hunta17fa672015-08-19 18:42:22 -0700251 fStart();
Simon Hunt1894d792015-02-04 17:09:20 -0800252 }
253 }
254
255 function updateLink(data) {
Simon Huntdc6adea2015-02-09 22:29:36 -0800256 var result = tms.findLink(data, 'update'),
Simon Hunt1894d792015-02-04 17:09:20 -0800257 bad = result.badLogic;
258 if (bad) {
Simon Hunteb18f522016-01-28 19:22:23 -0800259 $log.debug(bad + ': ' + link.id);
Simon Hunt1894d792015-02-04 17:09:20 -0800260 return;
261 }
Simon Hunteb18f522016-01-28 19:22:23 -0800262 result.updateWith(data);
Simon Hunt1894d792015-02-04 17:09:20 -0800263 }
264
265 function removeLink(data) {
Simon Hunta4242de2015-02-24 17:11:55 -0800266 var result = tms.findLink(data, 'remove');
267
268 if (!result.badLogic) {
269 result.removeRawLink();
Simon Hunt1894d792015-02-04 17:09:20 -0800270 }
Simon Hunt1894d792015-02-04 17:09:20 -0800271 }
272
Simon Hunt4a6b54b2015-10-27 22:08:25 -0700273 function topoStartDone(data) {
274 // called when the initial barrage of data has been sent from server
275 uplink.topoStartDone();
276 }
277
Simon Hunt1894d792015-02-04 17:09:20 -0800278 // ========================
279
Simon Hunt94f7dae2015-08-26 17:40:59 -0700280 function nodeById(id) {
281 return lu[id];
282 }
283
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700284 function makeNodeKey(node1, node2) {
285 return node1 + '-' + node2;
286 }
287
288 function findNodePair(key, keyRev) {
289 if (network.linksByDevice[key]) {
290 return key;
291 } else if (network.linksByDevice[keyRev]) {
292 return keyRev;
293 } else {
294 return false;
295 }
296 }
297
298 function aggregateLink(ldata, link) {
299 var key = makeNodeKey(link.src, link.dst),
300 keyRev = makeNodeKey(link.dst, link.src),
301 found = findNodePair(key, keyRev);
302
303 if (found) {
304 network.linksByDevice[found].push(ldata);
305 ldata.devicePair = found;
306 } else {
307 network.linksByDevice[key] = [ ldata ];
308 ldata.devicePair = key;
309 }
310 }
311
Simon Hunt1894d792015-02-04 17:09:20 -0800312 function addLinkUpdate(ldata, link) {
313 // add link event, but we already have the reverse link installed
314 ldata.fromTarget = link;
Simon Huntdc6adea2015-02-09 22:29:36 -0800315 rlk[link.id] = ldata.key;
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700316 // possible solution to el being undefined in restyleLinkElement:
317 //_updateLinks();
Simon Hunt1894d792015-02-04 17:09:20 -0800318 restyleLinkElement(ldata);
319 }
320
Simon Hunt1894d792015-02-04 17:09:20 -0800321
322 var widthRatio = 1.4,
323 linkScale = d3.scale.linear()
324 .domain([1, 12])
325 .range([widthRatio, 12 * widthRatio])
Simon Hunt5724fb42015-02-05 16:59:40 -0800326 .clamp(true),
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800327 allLinkTypes = 'direct indirect optical tunnel',
328 allLinkSubTypes = 'inactive not-permitted';
Simon Hunt1894d792015-02-04 17:09:20 -0800329
Simon Hunta142dd22015-02-12 22:07:51 -0800330 function restyleLinkElement(ldata, immediate) {
Simon Hunt1894d792015-02-04 17:09:20 -0800331 // this fn's job is to look at raw links and decide what svg classes
332 // need to be applied to the line element in the DOM
333 var th = ts.theme(),
334 el = ldata.el,
335 type = ldata.type(),
336 lw = ldata.linkWidth(),
Simon Hunta142dd22015-02-12 22:07:51 -0800337 online = ldata.online(),
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800338 modeCls = ldata.expected() ? 'inactive' : 'not-permitted',
Simon Hunta142dd22015-02-12 22:07:51 -0800339 delay = immediate ? 0 : 1000;
Simon Hunt1894d792015-02-04 17:09:20 -0800340
Simon Huntf44d7262016-06-14 14:46:56 -0700341 // NOTE: understand why el is sometimes undefined on addLink events...
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700342 // Investigated:
343 // el is undefined when it's a reverse link that is being added.
344 // updateLinks (which sets ldata.el) isn't called before this is called.
345 // Calling _updateLinks in addLinkUpdate fixes it, but there might be
346 // a more efficient way to fix it.
347 if (el && !el.empty()) {
Thomas Vachuskacb5016f2015-05-18 14:11:43 -0700348 el.classed('link', true);
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800349 el.classed(allLinkSubTypes, false);
350 el.classed(modeCls, !online);
Thomas Vachuskacb5016f2015-05-18 14:11:43 -0700351 el.classed(allLinkTypes, false);
352 if (type) {
353 el.classed(type, true);
354 }
355 el.transition()
356 .duration(delay)
357 .attr('stroke-width', linkScale(lw))
358 .attr('stroke', linkConfig[th].baseColor);
Simon Hunt1894d792015-02-04 17:09:20 -0800359 }
Simon Hunt1894d792015-02-04 17:09:20 -0800360 }
361
Simon Hunt1894d792015-02-04 17:09:20 -0800362 function removeLinkElement(d) {
363 var idx = fs.find(d.key, network.links, 'key'),
364 removed;
365 if (idx >=0) {
366 // remove from links array
367 removed = network.links.splice(idx, 1);
368 // remove from lookup cache
369 delete lu[removed[0].key];
370 updateLinks();
Simon Hunta17fa672015-08-19 18:42:22 -0700371 fResume();
Simon Hunt1894d792015-02-04 17:09:20 -0800372 }
373 }
374
375 function removeHostElement(d, upd) {
376 // first, remove associated hostLink...
377 removeLinkElement(d.linkData);
378
379 // remove hostLink bindings
380 delete lu[d.ingress];
381 delete lu[d.egress];
382
383 // remove from lookup cache
384 delete lu[d.id];
385 // remove from nodes array
386 var idx = fs.find(d.id, network.nodes);
387 network.nodes.splice(idx, 1);
388
389 // remove from SVG
390 // NOTE: upd is false if we were called from removeDeviceElement()
391 if (upd) {
392 updateNodes();
Simon Hunta17fa672015-08-19 18:42:22 -0700393 fResume();
Simon Hunt1894d792015-02-04 17:09:20 -0800394 }
395 }
396
397 function removeDeviceElement(d) {
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700398 var id = d.id,
399 idx;
Simon Hunt1894d792015-02-04 17:09:20 -0800400 // first, remove associated hosts and links..
Simon Huntdc6adea2015-02-09 22:29:36 -0800401 tms.findAttachedHosts(id).forEach(removeHostElement);
402 tms.findAttachedLinks(id).forEach(removeLinkElement);
Simon Hunt1894d792015-02-04 17:09:20 -0800403
404 // remove from lookup cache
405 delete lu[id];
406 // remove from nodes array
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700407 idx = fs.find(id, network.nodes);
408 if (idx > -1) {
409 network.nodes.splice(idx, 1);
410 }
Simon Hunt1894d792015-02-04 17:09:20 -0800411
412 if (!network.nodes.length) {
Simon Huntdc6adea2015-02-09 22:29:36 -0800413 uplink.showNoDevs(true);
Simon Hunt1894d792015-02-04 17:09:20 -0800414 }
415
416 // remove from SVG
417 updateNodes();
Simon Hunta17fa672015-08-19 18:42:22 -0700418 fResume();
Simon Hunt1894d792015-02-04 17:09:20 -0800419 }
420
Simon Hunt5724fb42015-02-05 16:59:40 -0800421 function updateHostVisibility() {
Simon Hunt18bf9822015-02-12 17:35:45 -0800422 sus.visible(nodeG.selectAll('.host'), showHosts);
423 sus.visible(linkG.selectAll('.hostLink'), showHosts);
Simon Hunt8eb4d3a2015-02-23 18:23:29 -0800424 sus.visible(linkLabelG.selectAll('.hostLinkLabel'), showHosts);
Simon Hunt5724fb42015-02-05 16:59:40 -0800425 }
426
427 function updateOfflineVisibility(dev) {
428 function updDev(d, show) {
Simon Hunt8eb4d3a2015-02-23 18:23:29 -0800429 var b;
Simon Hunt18bf9822015-02-12 17:35:45 -0800430 sus.visible(d.el, show);
Simon Hunt5724fb42015-02-05 16:59:40 -0800431
Simon Huntdc6adea2015-02-09 22:29:36 -0800432 tms.findAttachedLinks(d.id).forEach(function (link) {
Simon Hunt5724fb42015-02-05 16:59:40 -0800433 b = show && ((link.type() !== 'hostLink') || showHosts);
Simon Hunt18bf9822015-02-12 17:35:45 -0800434 sus.visible(link.el, b);
Simon Hunt5724fb42015-02-05 16:59:40 -0800435 });
Simon Huntdc6adea2015-02-09 22:29:36 -0800436 tms.findAttachedHosts(d.id).forEach(function (host) {
Simon Hunt5724fb42015-02-05 16:59:40 -0800437 b = show && showHosts;
Simon Hunt18bf9822015-02-12 17:35:45 -0800438 sus.visible(host.el, b);
Simon Hunt5724fb42015-02-05 16:59:40 -0800439 });
440 }
441
442 if (dev) {
443 // updating a specific device that just toggled off/on-line
444 updDev(dev, dev.online || showOffline);
445 } else {
446 // updating all offline devices
Simon Huntdc6adea2015-02-09 22:29:36 -0800447 tms.findDevices(true).forEach(function (d) {
Simon Hunt5724fb42015-02-05 16:59:40 -0800448 updDev(d, showOffline);
449 });
450 }
451 }
452
Simon Hunt1894d792015-02-04 17:09:20 -0800453
Simon Hunt445e8152015-02-06 13:00:12 -0800454 function sendUpdateMeta(d, clearPos) {
Simon Huntac4c6f72015-02-03 19:50:53 -0800455 var metaUi = {},
456 ll;
457
Simon Hunt445e8152015-02-06 13:00:12 -0800458 // if we are not clearing the position data (unpinning),
Simon Huntfd7106c2016-02-09 15:05:26 -0800459 // attach the x, y, (and equivalent longitude, latitude)...
Simon Hunt445e8152015-02-06 13:00:12 -0800460 if (!clearPos) {
Simon Hunt3a6eec02015-02-09 21:16:43 -0800461 ll = tms.lngLatFromCoord([d.x, d.y]);
Simon Huntfd7106c2016-02-09 15:05:26 -0800462 metaUi = {
463 x: d.x,
464 y: d.y,
465 equivLoc: {
466 lng: ll[0],
467 lat: ll[1]
468 }
469 };
Simon Hunt1894d792015-02-04 17:09:20 -0800470 }
471 d.metaUi = metaUi;
Simon Hunt237676b52015-03-10 19:04:26 -0700472 wss.sendEvent('updateMeta', {
Simon Hunt1894d792015-02-04 17:09:20 -0800473 id: d.id,
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700474 class: d.class,
Simon Hunt1894d792015-02-04 17:09:20 -0800475 memento: metaUi
476 });
Simon Huntac4c6f72015-02-03 19:50:53 -0800477 }
478
Simon Hunt1894d792015-02-04 17:09:20 -0800479
Simon Huntac4c6f72015-02-03 19:50:53 -0800480 function mkSvgClass(d) {
481 return d.fixed ? d.svgClass + ' fixed' : d.svgClass;
482 }
483
Simon Hunt5724fb42015-02-05 16:59:40 -0800484 function vis(b) {
485 return b ? 'visible' : 'hidden';
486 }
487
Simon Huntfcbde892015-04-16 12:05:28 -0700488 function toggleHosts(x) {
489 var kev = (x === 'keyev'),
490 on = kev ? !showHosts : !!x;
491
492 showHosts = on;
Simon Hunt5724fb42015-02-05 16:59:40 -0800493 updateHostVisibility();
Simon Huntfcbde892015-04-16 12:05:28 -0700494 flash.flash('Hosts ' + vis(on));
495 return on;
Simon Hunt5724fb42015-02-05 16:59:40 -0800496 }
497
Simon Huntfcbde892015-04-16 12:05:28 -0700498 function toggleOffline(x) {
499 var kev = (x === 'keyev'),
500 on = kev ? !showOffline : !!x;
501
502 showOffline = on;
Simon Hunt5724fb42015-02-05 16:59:40 -0800503 updateOfflineVisibility();
Simon Huntfcbde892015-04-16 12:05:28 -0700504 flash.flash('Offline devices ' + vis(on));
505 return on;
Simon Hunt5724fb42015-02-05 16:59:40 -0800506 }
507
508 function cycleDeviceLabels() {
Bri Prebilic Cole9cf1a8d2015-04-21 13:15:29 -0700509 flash.flash(td3.incDevLabIndex());
Simon Huntdc6adea2015-02-09 22:29:36 -0800510 tms.findDevices().forEach(function (d) {
Simon Hunta4242de2015-02-24 17:11:55 -0800511 td3.updateDeviceLabel(d);
Simon Hunt1c367112015-02-05 18:02:46 -0800512 });
Simon Hunt5724fb42015-02-05 16:59:40 -0800513 }
514
Simon Hunt445e8152015-02-06 13:00:12 -0800515 function unpin() {
Simon Hunt08f841d02015-02-10 14:39:20 -0800516 var hov = tss.hovered();
517 if (hov) {
518 sendUpdateMeta(hov, true);
519 hov.fixed = false;
520 hov.el.classed('fixed', false);
Simon Hunt445e8152015-02-06 13:00:12 -0800521 fResume();
522 }
523 }
524
Simon Hunta142dd22015-02-12 22:07:51 -0800525 function showMastership(masterId) {
526 if (!masterId) {
527 restoreLayerState();
528 } else {
529 showMastershipFor(masterId);
530 }
531 }
532
533 function restoreLayerState() {
534 // NOTE: this level of indirection required, for when we have
535 // the layer filter functionality re-implemented
536 suppressLayers(false);
537 }
538
539 function showMastershipFor(id) {
540 suppressLayers(true);
541 node.each(function (n) {
542 if (n.master === id) {
Simon Hunt743a8492015-08-25 16:18:19 -0700543 n.el.classed('suppressedmax', false);
Simon Hunta142dd22015-02-12 22:07:51 -0800544 }
545 });
546 }
547
Simon Hunt743a8492015-08-25 16:18:19 -0700548 function supAmt(less) {
549 return less ? "suppressed" : "suppressedmax";
550 }
551
552 function suppressLayers(b, less) {
553 var cls = supAmt(less);
554 node.classed(cls, b);
555 link.classed(cls, b);
556 }
557
558 function unsuppressNode(id, less) {
559 var cls = supAmt(less);
560 node.each(function (n) {
561 if (n.id === id) {
562 n.el.classed(cls, false);
563 }
564 });
565 }
566
Simon Hunt94f7dae2015-08-26 17:40:59 -0700567 function unsuppressLink(key, less) {
Simon Hunt743a8492015-08-25 16:18:19 -0700568 var cls = supAmt(less);
569 link.each(function (n) {
Simon Hunt94f7dae2015-08-26 17:40:59 -0700570 if (n.key === key) {
Simon Hunt743a8492015-08-25 16:18:19 -0700571 n.el.classed(cls, false);
572 }
573 });
Simon Hunta142dd22015-02-12 22:07:51 -0800574 }
Simon Hunt445e8152015-02-06 13:00:12 -0800575
Simon Hunt86b7c882015-04-02 23:06:08 -0700576 function showBadLinks() {
577 var badLinks = tms.findBadLinks();
578 flash.flash('Bad Links: ' + badLinks.length);
579 $log.debug('Bad Link List (' + badLinks.length + '):');
580 badLinks.forEach(function (d) {
581 $log.debug('bad link: (' + d.bad + ') ' + d.key, d);
582 if (d.el) {
583 d.el.attr('stroke-width', linkScale(2.8))
584 .attr('stroke', 'red');
585 }
586 });
587 // back to normal after 2 seconds...
588 $timeout(updateLinks, 2000);
589 }
590
Simon Huntfd7106c2016-02-09 15:05:26 -0800591 function resetAllLocations() {
592 tms.resetAllLocations();
593 updateNodes();
594 tick(); // force nodes to be redrawn in their new locations
595 flash.flash('Reset Node Locations');
596 }
597
Simon Hunt5724fb42015-02-05 16:59:40 -0800598 // ==========================================
599
Simon Huntac4c6f72015-02-03 19:50:53 -0800600 function updateNodes() {
Thomas Vachuska1a989c12015-06-09 18:29:22 -0700601 if (fNodesTimer) {
602 $timeout.cancel(fNodesTimer);
603 }
604 fNodesTimer = $timeout(_updateNodes, 150);
605 }
606
Simon Hunta17fa672015-08-19 18:42:22 -0700607 // IMPLEMENTATION NOTE: _updateNodes() should NOT stop, start, or resume
608 // the force layout; that needs to be determined and implemented elsewhere
Thomas Vachuska1a989c12015-06-09 18:29:22 -0700609 function _updateNodes() {
Simon Hunt1894d792015-02-04 17:09:20 -0800610 // select all the nodes in the layout:
Simon Huntac4c6f72015-02-03 19:50:53 -0800611 node = nodeG.selectAll('.node')
612 .data(network.nodes, function (d) { return d.id; });
613
Simon Hunt1894d792015-02-04 17:09:20 -0800614 // operate on existing nodes:
Simon Hunta4242de2015-02-24 17:11:55 -0800615 node.filter('.device').each(td3.deviceExisting);
616 node.filter('.host').each(td3.hostExisting);
Simon Huntac4c6f72015-02-03 19:50:53 -0800617
618 // operate on entering nodes:
619 var entering = node.enter()
620 .append('g')
621 .attr({
622 id: function (d) { return sus.safeId(d.id); },
623 class: mkSvgClass,
Simon Hunta17fa672015-08-19 18:42:22 -0700624 transform: function (d) {
625 // Need to guard against NaN here ??
626 return sus.translate(d.x, d.y);
627 },
Simon Huntac4c6f72015-02-03 19:50:53 -0800628 opacity: 0
629 })
630 .call(drag)
Simon Hunt08f841d02015-02-10 14:39:20 -0800631 .on('mouseover', tss.nodeMouseOver)
632 .on('mouseout', tss.nodeMouseOut)
Simon Huntac4c6f72015-02-03 19:50:53 -0800633 .transition()
634 .attr('opacity', 1);
635
Simon Hunt1894d792015-02-04 17:09:20 -0800636 // augment entering nodes:
Simon Hunta4242de2015-02-24 17:11:55 -0800637 entering.filter('.device').each(td3.deviceEnter);
638 entering.filter('.host').each(td3.hostEnter);
Simon Huntac4c6f72015-02-03 19:50:53 -0800639
Simon Hunt51056592015-02-03 21:48:07 -0800640 // operate on both existing and new nodes:
Simon Hunta4242de2015-02-24 17:11:55 -0800641 td3.updateDeviceColors();
Simon Huntac4c6f72015-02-03 19:50:53 -0800642
643 // operate on exiting nodes:
644 // Note that the node is removed after 2 seconds.
645 // Sub element animations should be shorter than 2 seconds.
646 var exiting = node.exit()
647 .transition()
648 .duration(2000)
649 .style('opacity', 0)
650 .remove();
651
Simon Hunt1894d792015-02-04 17:09:20 -0800652 // exiting node specifics:
Simon Hunta4242de2015-02-24 17:11:55 -0800653 exiting.filter('.host').each(td3.hostExit);
654 exiting.filter('.device').each(td3.deviceExit);
Simon Huntac4c6f72015-02-03 19:50:53 -0800655 }
656
Simon Hunt51056592015-02-03 21:48:07 -0800657 // ==========================
Simon Hunt1894d792015-02-04 17:09:20 -0800658
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700659 function getDefaultPos(link) {
660 return {
661 x1: link.source.x,
662 y1: link.source.y,
663 x2: link.target.x,
664 y2: link.target.y
665 };
666 }
667
668 // returns amount of adjustment along the normal for given link
669 function amt(numLinks, linkIdx) {
670 var gap = 6;
671 return (linkIdx - ((numLinks - 1) / 2)) * gap;
672 }
673
674 function calcMovement(d, amt, flipped) {
675 var pos = getDefaultPos(d),
676 mult = flipped ? -amt : amt,
677 dx = pos.x2 - pos.x1,
678 dy = pos.y2 - pos.y1,
679 length = Math.sqrt((dx * dx) + (dy * dy));
680
681 return {
682 x1: pos.x1 + (mult * dy / length),
683 y1: pos.y1 + (mult * -dx / length),
684 x2: pos.x2 + (mult * dy / length),
685 y2: pos.y2 + (mult * -dx / length)
686 };
687 }
688
689 function calcPosition() {
690 var lines = this,
691 linkSrcId;
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700692 linkNums = [];
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700693 lines.each(function (d) {
694 if (d.type() === 'hostLink') {
695 d.position = getDefaultPos(d);
696 }
697 });
698
699 function normalizeLinkSrc(link) {
700 // ensure source device is consistent across set of links
701 // temporary measure until link modeling is refactored
702 if (!linkSrcId) {
703 linkSrcId = link.source.id;
704 return false;
705 }
706
707 return link.source.id !== linkSrcId;
708 }
709
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700710 angular.forEach(network.linksByDevice, function (linkArr, key) {
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700711 var numLinks = linkArr.length,
712 link;
713
714 if (numLinks === 1) {
715 link = linkArr[0];
716 link.position = getDefaultPos(link);
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700717 link.position.multiLink = false;
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700718 } else if (numLinks >= 5) {
719 // this code is inefficient, in the future the way links
720 // are modeled will be changed
721 angular.forEach(linkArr, function (link) {
722 link.position = getDefaultPos(link);
723 link.position.multiLink = true;
724 });
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700725 linkNums.push({
726 id: key,
727 num: numLinks,
728 linkCoords: linkArr[0].position
729 });
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700730 } else {
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700731 linkSrcId = null;
732 angular.forEach(linkArr, function (link, index) {
733 var offsetAmt = amt(numLinks, index),
734 needToFlip = normalizeLinkSrc(link);
735 link.position = calcMovement(link, offsetAmt, needToFlip);
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700736 link.position.multiLink = false;
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700737 });
738 }
739 });
740 }
741
Simon Hunt1894d792015-02-04 17:09:20 -0800742 function updateLinks() {
Thomas Vachuska1a989c12015-06-09 18:29:22 -0700743 if (fLinksTimer) {
744 $timeout.cancel(fLinksTimer);
745 }
746 fLinksTimer = $timeout(_updateLinks, 150);
747 }
748
Simon Hunta17fa672015-08-19 18:42:22 -0700749 // IMPLEMENTATION NOTE: _updateLinks() should NOT stop, start, or resume
750 // the force layout; that needs to be determined and implemented elsewhere
Thomas Vachuska1a989c12015-06-09 18:29:22 -0700751 function _updateLinks() {
Simon Hunt1894d792015-02-04 17:09:20 -0800752 var th = ts.theme();
753
754 link = linkG.selectAll('.link')
755 .data(network.links, function (d) { return d.key; });
756
757 // operate on existing links:
Simon Huntd5264122015-02-25 10:17:43 -0800758 link.each(function (d) {
759 // this is supposed to be an existing link, but we have observed
760 // occasions (where links are deleted and added rapidly?) where
761 // the DOM element has not been defined. So protect against that...
762 if (d.el) {
763 restyleLinkElement(d, true);
764 }
765 });
Simon Hunt1894d792015-02-04 17:09:20 -0800766
767 // operate on entering links:
768 var entering = link.enter()
769 .append('line')
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700770 .call(calcPosition)
Simon Hunt1894d792015-02-04 17:09:20 -0800771 .attr({
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700772 x1: function (d) { return d.position.x1; },
773 y1: function (d) { return d.position.y1; },
774 x2: function (d) { return d.position.x2; },
775 y2: function (d) { return d.position.y2; },
Simon Hunt1894d792015-02-04 17:09:20 -0800776 stroke: linkConfig[th].inColor,
777 'stroke-width': linkConfig.inWidth
778 });
779
780 // augment links
Simon Hunta4242de2015-02-24 17:11:55 -0800781 entering.each(td3.linkEntering);
Simon Hunt1894d792015-02-04 17:09:20 -0800782
783 // operate on both existing and new links:
784 //link.each(...)
785
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700786 // add labels for how many links are in a thick line
787 td3.applyNumLinkLabels(linkNums, numLinkLblsG);
788
Simon Hunt1894d792015-02-04 17:09:20 -0800789 // apply or remove labels
Simon Hunta4242de2015-02-24 17:11:55 -0800790 td3.applyLinkLabels();
Simon Hunt1894d792015-02-04 17:09:20 -0800791
792 // operate on exiting links:
793 link.exit()
794 .attr('stroke-dasharray', '3 3')
Simon Hunt5724fb42015-02-05 16:59:40 -0800795 .attr('stroke', linkConfig[th].outColor)
Simon Hunt1894d792015-02-04 17:09:20 -0800796 .style('opacity', 0.5)
797 .transition()
798 .duration(1500)
799 .attr({
800 'stroke-dasharray': '3 12',
Simon Hunt1894d792015-02-04 17:09:20 -0800801 'stroke-width': linkConfig.outWidth
802 })
803 .style('opacity', 0.0)
804 .remove();
Simon Hunt1894d792015-02-04 17:09:20 -0800805 }
806
Simon Huntac4c6f72015-02-03 19:50:53 -0800807
808 // ==========================
Simon Hunt737c89f2015-01-28 12:23:19 -0800809 // force layout tick function
Simon Hunt737c89f2015-01-28 12:23:19 -0800810
Simon Hunt5724fb42015-02-05 16:59:40 -0800811 function fResume() {
Simon Huntc3c5b672015-02-20 11:32:13 -0800812 if (!tos.isOblique()) {
Simon Hunt5724fb42015-02-05 16:59:40 -0800813 force.resume();
814 }
815 }
816
817 function fStart() {
Simon Huntc3c5b672015-02-20 11:32:13 -0800818 if (!tos.isOblique()) {
Simon Hunta17fa672015-08-19 18:42:22 -0700819 if (fTimer) {
820 $timeout.cancel(fTimer);
821 }
822 fTimer = $timeout(function () {
823 $log.debug("Starting force-layout");
824 force.start();
825 }, 200);
Simon Hunt5724fb42015-02-05 16:59:40 -0800826 }
827 }
828
829 var tickStuff = {
830 nodeAttr: {
Simon Hunta17fa672015-08-19 18:42:22 -0700831 transform: function (d) {
832 var dx = isNaN(d.x) ? 0 : d.x,
833 dy = isNaN(d.y) ? 0 : d.y;
834 return sus.translate(dx, dy);
835 }
Simon Hunt5724fb42015-02-05 16:59:40 -0800836 },
837 linkAttr: {
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700838 x1: function (d) { return d.position.x1; },
839 y1: function (d) { return d.position.y1; },
840 x2: function (d) { return d.position.x2; },
841 y2: function (d) { return d.position.y2; }
Simon Hunt5724fb42015-02-05 16:59:40 -0800842 },
843 linkLabelAttr: {
844 transform: function (d) {
Simon Huntdc6adea2015-02-09 22:29:36 -0800845 var lnk = tms.findLinkById(d.key);
Simon Hunt5724fb42015-02-05 16:59:40 -0800846 if (lnk) {
Carmelo Casconed01eda62016-08-02 10:19:15 -0700847 return td3.transformLabel(lnk.position, d.key);
Simon Hunt5724fb42015-02-05 16:59:40 -0800848 }
849 }
850 }
851 };
852
853 function tick() {
Simon Hunt3ab20282015-02-26 20:32:19 -0800854 // guard against null (which can happen when our view pages out)...
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700855 if (node && node.size()) {
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700856 node.attr(tickStuff.nodeAttr);
857 }
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700858 if (link && link.size()) {
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700859 link.call(calcPosition)
860 .attr(tickStuff.linkAttr);
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700861 td3.applyNumLinkLabels(linkNums, numLinkLblsG);
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700862 }
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700863 if (linkLabel && linkLabel.size()) {
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700864 linkLabel.attr(tickStuff.linkLabelAttr);
865 }
Simon Hunt737c89f2015-01-28 12:23:19 -0800866 }
867
868
Simon Huntac4c6f72015-02-03 19:50:53 -0800869 // ==========================
870 // === MOUSE GESTURE HANDLERS
871
Simon Hunt205099e2015-02-07 13:12:01 -0800872 function zoomingOrPanning(ev) {
873 return ev.metaKey || ev.altKey;
Simon Hunt445e8152015-02-06 13:00:12 -0800874 }
875
876 function atDragEnd(d) {
877 // once we've finished moving, pin the node in position
878 d.fixed = true;
879 d3.select(this).classed('fixed', true);
880 sendUpdateMeta(d);
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700881 tss.clickConsumed(true);
Simon Hunt445e8152015-02-06 13:00:12 -0800882 }
883
884 // predicate that indicates when dragging is active
885 function dragEnabled() {
886 var ev = d3.event.sourceEvent;
887 // nodeLock means we aren't allowing nodes to be dragged...
Simon Hunt205099e2015-02-07 13:12:01 -0800888 return !nodeLock && !zoomingOrPanning(ev);
Simon Hunt445e8152015-02-06 13:00:12 -0800889 }
890
891 // predicate that indicates when clicking is active
892 function clickEnabled() {
893 return true;
894 }
Simon Hunt737c89f2015-01-28 12:23:19 -0800895
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700896 // =============================================
897 // function entry points for overlay module
Simon Huntf542d842015-02-11 16:20:33 -0800898
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700899 // TODO: find an automatic way of tracking via the "showHighlights" events
Simon Hunte50829c2015-06-09 08:39:28 -0700900 var allTrafficClasses = 'primary secondary optical animated ' +
901 'port-traffic-Kbps port-traffic-Mbps port-traffic-Gbps ' +
902 'port-traffic-Gbps-choked';
Simon Huntf542d842015-02-11 16:20:33 -0800903
904 function clearLinkTrafficStyle() {
905 link.style('stroke-width', null)
906 .classed(allTrafficClasses, false);
907 }
908
909 function removeLinkLabels() {
910 network.links.forEach(function (d) {
911 d.label = '';
912 });
913 }
Simon Hunt737c89f2015-01-28 12:23:19 -0800914
Simon Hunte9343f32015-10-21 18:07:46 -0700915 function clearNodeDeco() {
916 node.selectAll('g.badge').remove();
917 }
918
919 function removeNodeBadges() {
920 network.nodes.forEach(function (d) {
921 d.badge = null;
922 });
923 }
924
Simon Hunta4242de2015-02-24 17:11:55 -0800925 function updateLinkLabelModel() {
926 // create the backing data for showing labels..
927 var data = [];
928 link.each(function (d) {
929 if (d.label) {
930 data.push({
931 id: 'lab-' + d.key,
932 key: d.key,
933 label: d.label,
934 ldata: d
935 });
936 }
937 });
938
939 linkLabel = linkLabelG.selectAll('.linkLabel')
940 .data(data, function (d) { return d.id; });
941 }
942
Simon Hunt737c89f2015-01-28 12:23:19 -0800943 // ==========================
Simon Huntac4c6f72015-02-03 19:50:53 -0800944 // Module definition
Simon Hunt737c89f2015-01-28 12:23:19 -0800945
Simon Huntdc6adea2015-02-09 22:29:36 -0800946 function mkModelApi(uplink) {
947 return {
948 projection: uplink.projection,
949 network: network,
950 restyleLinkElement: restyleLinkElement,
951 removeLinkElement: removeLinkElement
952 };
953 }
954
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700955 function mkD3Api() {
Simon Hunta4242de2015-02-24 17:11:55 -0800956 return {
957 node: function () { return node; },
958 link: function () { return link; },
959 linkLabel: function () { return linkLabel; },
960 instVisible: function () { return tis.isVisible(); },
961 posNode: tms.positionNode,
962 showHosts: function () { return showHosts; },
963 restyleLinkElement: restyleLinkElement,
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700964 updateLinkLabelModel: updateLinkLabelModel,
965 linkConfig: function () { return linkConfig; }
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700966 };
Simon Hunta4242de2015-02-24 17:11:55 -0800967 }
968
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700969 function mkSelectApi() {
Simon Hunt08f841d02015-02-10 14:39:20 -0800970 return {
971 node: function () { return node; },
972 zoomingOrPanning: zoomingOrPanning,
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700973 updateDeviceColors: td3.updateDeviceColors,
974 deselectLink: tls.deselectLink
Simon Hunt08f841d02015-02-10 14:39:20 -0800975 };
976 }
977
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700978 function mkTrafficApi() {
979 return {
980 hovered: tss.hovered,
981 somethingSelected: tss.somethingSelected,
982 selectOrder: tss.selectOrder
983 };
984 }
985
986 function mkOverlayApi() {
Simon Huntf542d842015-02-11 16:20:33 -0800987 return {
Simon Hunte9343f32015-10-21 18:07:46 -0700988 clearNodeDeco: clearNodeDeco,
989 removeNodeBadges: removeNodeBadges,
Simon Huntf542d842015-02-11 16:20:33 -0800990 clearLinkTrafficStyle: clearLinkTrafficStyle,
991 removeLinkLabels: removeLinkLabels,
Simon Hunt743a8492015-08-25 16:18:19 -0700992 findLinkById: tms.findLinkById,
Simon Hunt94f7dae2015-08-26 17:40:59 -0700993 findNodeById: nodeById,
Simon Huntf542d842015-02-11 16:20:33 -0800994 updateLinks: updateLinks,
Simon Hunt743a8492015-08-25 16:18:19 -0700995 updateNodes: updateNodes,
996 supLayers: suppressLayers,
997 unsupNode: unsuppressNode,
998 unsupLink: unsuppressLink
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700999 };
Simon Huntf542d842015-02-11 16:20:33 -08001000 }
1001
Simon Huntc3c5b672015-02-20 11:32:13 -08001002 function mkObliqueApi(uplink, fltr) {
Simon Hunt96f88c62015-02-19 17:57:25 -08001003 return {
Simon Huntc3c5b672015-02-20 11:32:13 -08001004 force: function() { return force; },
1005 zoomLayer: uplink.zoomLayer,
1006 nodeGBBox: function() { return nodeG.node().getBBox(); },
Simon Hunt96f88c62015-02-19 17:57:25 -08001007 node: function () { return node; },
Simon Huntc3c5b672015-02-20 11:32:13 -08001008 link: function () { return link; },
1009 linkLabel: function () { return linkLabel; },
1010 nodes: function () { return network.nodes; },
1011 tickStuff: tickStuff,
1012 nodeLock: function (b) {
1013 var old = nodeLock;
1014 nodeLock = b;
1015 return old;
1016 },
1017 opacifyMap: uplink.opacifyMap,
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -07001018 inLayer: fltr.inLayer,
Bri Prebilic Cole80401762015-07-16 11:36:18 -07001019 calcLinkPos: calcPosition,
1020 applyNumLinkLabels: function () {
1021 td3.applyNumLinkLabels(linkNums, numLinkLblsG);
1022 }
Simon Hunt96f88c62015-02-19 17:57:25 -08001023 };
1024 }
1025
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001026 function mkFilterApi() {
Simon Hunteb0fa052015-02-17 19:20:28 -08001027 return {
1028 node: function () { return node; },
1029 link: function () { return link; }
1030 };
1031 }
1032
Simon Hunt9e2104c2015-02-26 10:48:59 -08001033 function mkLinkApi(svg, uplink) {
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001034 return {
1035 svg: svg,
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001036 zoomer: uplink.zoomer(),
1037 network: network,
Simon Hunt1a5301e2015-02-25 15:31:25 -08001038 portLabelG: function () { return portLabelG; },
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001039 showHosts: function () { return showHosts; }
1040 };
1041 }
1042
Simon Huntf51bf462016-06-29 16:22:57 -07001043 function updateLinksAndNodes() {
1044 updateLinks();
1045 updateNodes();
1046 }
Steven Burrowsec1f45c2016-08-08 16:14:41 +01001047
Simon Hunt737c89f2015-01-28 12:23:19 -08001048 angular.module('ovTopo')
1049 .factory('TopoForceService',
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -07001050 ['$log', '$timeout', 'FnService', 'SvgUtilService',
Simon Hunt86b7c882015-04-02 23:06:08 -07001051 'ThemeService', 'FlashService', 'WebSocketService',
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001052 'TopoOverlayService', 'TopoInstService', 'TopoModelService',
Simon Hunta4242de2015-02-24 17:11:55 -08001053 'TopoD3Service', 'TopoSelectService', 'TopoTrafficService',
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001054 'TopoObliqueService', 'TopoFilterService', 'TopoLinkService',
Simon Hunt737c89f2015-01-28 12:23:19 -08001055
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001056 function (_$log_, _$timeout_, _fs_, _sus_, _ts_, _flash_, _wss_, _tov_,
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001057 _tis_, _tms_, _td3_, _tss_, _tts_, _tos_, _fltr_, _tls_) {
Simon Hunt737c89f2015-01-28 12:23:19 -08001058 $log = _$log_;
Simon Hunt86b7c882015-04-02 23:06:08 -07001059 $timeout = _$timeout_;
Simon Hunt1894d792015-02-04 17:09:20 -08001060 fs = _fs_;
Simon Hunt737c89f2015-01-28 12:23:19 -08001061 sus = _sus_;
Simon Huntac4c6f72015-02-03 19:50:53 -08001062 ts = _ts_;
Simon Hunt5724fb42015-02-05 16:59:40 -08001063 flash = _flash_;
Simon Hunt237676b52015-03-10 19:04:26 -07001064 wss = _wss_;
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001065 tov = _tov_;
Simon Huntac4c6f72015-02-03 19:50:53 -08001066 tis = _tis_;
Simon Hunt3a6eec02015-02-09 21:16:43 -08001067 tms = _tms_;
Simon Hunta4242de2015-02-24 17:11:55 -08001068 td3 = _td3_;
Simon Hunt08f841d02015-02-10 14:39:20 -08001069 tss = _tss_;
Simon Huntf542d842015-02-11 16:20:33 -08001070 tts = _tts_;
Simon Hunt96f88c62015-02-19 17:57:25 -08001071 tos = _tos_;
Simon Hunteb0fa052015-02-17 19:20:28 -08001072 fltr = _fltr_;
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001073 tls = _tls_;
Simon Hunt737c89f2015-01-28 12:23:19 -08001074
Simon Huntf51bf462016-06-29 16:22:57 -07001075 ts.addListener(updateLinksAndNodes);
Simon Hunta142dd22015-02-12 22:07:51 -08001076
Simon Hunt737c89f2015-01-28 12:23:19 -08001077 // forceG is the SVG group to display the force layout in
Simon Huntdc6adea2015-02-09 22:29:36 -08001078 // uplink is the api from the main topo source file
Simon Hunt3a6eec02015-02-09 21:16:43 -08001079 // dim is the initial dimensions of the SVG as [w,h]
Simon Hunt737c89f2015-01-28 12:23:19 -08001080 // opts are, well, optional :)
Simon Hunt3ab20282015-02-26 20:32:19 -08001081 function initForce(_svg_, forceG, _uplink_, _dim_, opts) {
Simon Hunt1894d792015-02-04 17:09:20 -08001082 uplink = _uplink_;
Simon Hunt3a6eec02015-02-09 21:16:43 -08001083 dim = _dim_;
Simon Hunt3ab20282015-02-26 20:32:19 -08001084 svg = _svg_;
1085
1086 lu = network.lookup;
1087 rlk = network.revLinkToKey;
Simon Hunt3a6eec02015-02-09 21:16:43 -08001088
1089 $log.debug('initForce().. dim = ' + dim);
1090
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001091 tov.setApi(mkOverlayApi(), tss);
Simon Huntdc6adea2015-02-09 22:29:36 -08001092 tms.initModel(mkModelApi(uplink), dim);
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001093 td3.initD3(mkD3Api());
1094 tss.initSelect(mkSelectApi());
1095 tts.initTraffic(mkTrafficApi());
Simon Huntc3c5b672015-02-20 11:32:13 -08001096 tos.initOblique(mkObliqueApi(uplink, fltr));
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001097 fltr.initFilter(mkFilterApi());
Simon Hunt9e2104c2015-02-26 10:48:59 -08001098 tls.initLink(mkLinkApi(svg, uplink), td3);
Simon Hunta11b4eb2015-01-28 16:20:50 -08001099
Simon Hunt737c89f2015-01-28 12:23:19 -08001100 settings = angular.extend({}, defaultSettings, opts);
1101
1102 linkG = forceG.append('g').attr('id', 'topo-links');
1103 linkLabelG = forceG.append('g').attr('id', 'topo-linkLabels');
Bri Prebilic Cole80401762015-07-16 11:36:18 -07001104 numLinkLblsG = forceG.append('g').attr('id', 'topo-numLinkLabels');
Simon Hunt737c89f2015-01-28 12:23:19 -08001105 nodeG = forceG.append('g').attr('id', 'topo-nodes');
Simon Hunt1a5301e2015-02-25 15:31:25 -08001106 portLabelG = forceG.append('g').attr('id', 'topo-portLabels');
Simon Hunt737c89f2015-01-28 12:23:19 -08001107
1108 link = linkG.selectAll('.link');
1109 linkLabel = linkLabelG.selectAll('.linkLabel');
1110 node = nodeG.selectAll('.node');
1111
1112 force = d3.layout.force()
Simon Hunt3a6eec02015-02-09 21:16:43 -08001113 .size(dim)
Simon Hunt737c89f2015-01-28 12:23:19 -08001114 .nodes(network.nodes)
1115 .links(network.links)
1116 .gravity(settings.gravity)
1117 .friction(settings.friction)
1118 .charge(settings.charge._def_)
1119 .linkDistance(settings.linkDistance._def_)
1120 .linkStrength(settings.linkStrength._def_)
1121 .on('tick', tick);
1122
1123 drag = sus.createDragBehavior(force,
Simon Hunt08f841d02015-02-10 14:39:20 -08001124 tss.selectObject, atDragEnd, dragEnabled, clickEnabled);
Simon Hunt737c89f2015-01-28 12:23:19 -08001125 }
1126
Simon Hunt3a6eec02015-02-09 21:16:43 -08001127 function newDim(_dim_) {
1128 dim = _dim_;
1129 force.size(dim);
1130 tms.newDim(dim);
Simon Hunt737c89f2015-01-28 12:23:19 -08001131 }
1132
Simon Hunt3a6eec02015-02-09 21:16:43 -08001133 function destroyForce() {
Simon Hunt3ab20282015-02-26 20:32:19 -08001134 force.stop();
1135
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001136 tls.destroyLink();
Simon Hunt96f88c62015-02-19 17:57:25 -08001137 tos.destroyOblique();
Simon Huntf542d842015-02-11 16:20:33 -08001138 tts.destroyTraffic();
1139 tss.destroySelect();
Simon Hunta4242de2015-02-24 17:11:55 -08001140 td3.destroyD3();
Simon Huntf542d842015-02-11 16:20:33 -08001141 tms.destroyModel();
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001142 // note: no need to destroy overlay service
Simon Huntf51bf462016-06-29 16:22:57 -07001143 ts.removeListener(updateLinksAndNodes);
Simon Hunt3ab20282015-02-26 20:32:19 -08001144
1145 // clean up the DOM
1146 svg.selectAll('g').remove();
1147 svg.selectAll('defs').remove();
1148
1149 // clean up internal state
1150 network.nodes = [];
1151 network.links = [];
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -07001152 network.linksByDevice = {};
Simon Hunt3ab20282015-02-26 20:32:19 -08001153 network.lookup = {};
1154 network.revLinkToKey = {};
1155
Bri Prebilic Cole80401762015-07-16 11:36:18 -07001156 linkNums = [];
1157
1158 linkG = linkLabelG = numLinkLblsG = nodeG = portLabelG = null;
Simon Hunt3ab20282015-02-26 20:32:19 -08001159 link = linkLabel = node = null;
1160 force = drag = null;
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -07001161
1162 // clean up $timeout promises
Simon Hunta17fa672015-08-19 18:42:22 -07001163 if (fTimer) {
1164 $timeout.cancel(fTimer);
1165 }
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -07001166 if (fNodesTimer) {
1167 $timeout.cancel(fNodesTimer);
1168 }
1169 if (fLinksTimer) {
1170 $timeout.cancel(fLinksTimer);
1171 }
Simon Hunt3a6eec02015-02-09 21:16:43 -08001172 }
1173
Simon Hunt737c89f2015-01-28 12:23:19 -08001174 return {
1175 initForce: initForce,
Simon Hunt3a6eec02015-02-09 21:16:43 -08001176 newDim: newDim,
1177 destroyForce: destroyForce,
Simon Huntac4c6f72015-02-03 19:50:53 -08001178
Simon Hunta4242de2015-02-24 17:11:55 -08001179 updateDeviceColors: td3.updateDeviceColors,
Simon Hunt5724fb42015-02-05 16:59:40 -08001180 toggleHosts: toggleHosts,
Simon Hunt9e2104c2015-02-26 10:48:59 -08001181 togglePorts: tls.togglePorts,
Simon Hunt5724fb42015-02-05 16:59:40 -08001182 toggleOffline: toggleOffline,
1183 cycleDeviceLabels: cycleDeviceLabels,
Simon Hunt445e8152015-02-06 13:00:12 -08001184 unpin: unpin,
Simon Hunta142dd22015-02-12 22:07:51 -08001185 showMastership: showMastership,
Simon Hunt86b7c882015-04-02 23:06:08 -07001186 showBadLinks: showBadLinks,
Simon Huntac4c6f72015-02-03 19:50:53 -08001187
Simon Huntfd7106c2016-02-09 15:05:26 -08001188 resetAllLocations: resetAllLocations,
Simon Huntac4c6f72015-02-03 19:50:53 -08001189 addDevice: addDevice,
Simon Hunt1894d792015-02-04 17:09:20 -08001190 updateDevice: updateDevice,
1191 removeDevice: removeDevice,
1192 addHost: addHost,
1193 updateHost: updateHost,
Simon Hunt95d56fd2015-11-12 11:06:44 -08001194 moveHost: moveHost,
Simon Hunt1894d792015-02-04 17:09:20 -08001195 removeHost: removeHost,
1196 addLink: addLink,
1197 updateLink: updateLink,
Simon Hunt4a6b54b2015-10-27 22:08:25 -07001198 removeLink: removeLink,
1199 topoStartDone: topoStartDone
Simon Hunt737c89f2015-01-28 12:23:19 -08001200 };
1201 }]);
1202}());