blob: ef9f633c4f97ee48310f2f6eec173b8c395aa076 [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) {
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100106 console.log(data);
Simon Huntac4c6f72015-02-03 19:50:53 -0800107 var id = data.id,
108 d;
109
Simon Hunt1894d792015-02-04 17:09:20 -0800110 uplink.showNoDevs(false);
Simon Huntac4c6f72015-02-03 19:50:53 -0800111
112 // although this is an add device event, if we already have the
113 // device, treat it as an update instead..
Simon Hunt1894d792015-02-04 17:09:20 -0800114 if (lu[id]) {
Simon Huntac4c6f72015-02-03 19:50:53 -0800115 updateDevice(data);
116 return;
117 }
118
Simon Hunt3a6eec02015-02-09 21:16:43 -0800119 d = tms.createDeviceNode(data);
Simon Huntac4c6f72015-02-03 19:50:53 -0800120 network.nodes.push(d);
Simon Hunt1894d792015-02-04 17:09:20 -0800121 lu[id] = d;
Simon Huntac4c6f72015-02-03 19:50:53 -0800122 updateNodes();
Simon Hunta17fa672015-08-19 18:42:22 -0700123 fStart();
Simon Huntac4c6f72015-02-03 19:50:53 -0800124 }
125
126 function updateDevice(data) {
127 var id = data.id,
Simon Hunt1894d792015-02-04 17:09:20 -0800128 d = lu[id],
Simon Huntac4c6f72015-02-03 19:50:53 -0800129 wasOnline;
130
131 if (d) {
132 wasOnline = d.online;
133 angular.extend(d, data);
Simon Hunt3a6eec02015-02-09 21:16:43 -0800134 if (tms.positionNode(d, true)) {
Simon Hunt445e8152015-02-06 13:00:12 -0800135 sendUpdateMeta(d);
Simon Huntac4c6f72015-02-03 19:50:53 -0800136 }
137 updateNodes();
138 if (wasOnline !== d.online) {
Simon Huntdc6adea2015-02-09 22:29:36 -0800139 tms.findAttachedLinks(d.id).forEach(restyleLinkElement);
Simon Hunt5724fb42015-02-05 16:59:40 -0800140 updateOfflineVisibility(d);
Simon Huntac4c6f72015-02-03 19:50:53 -0800141 }
Simon Huntac4c6f72015-02-03 19:50:53 -0800142 }
143 }
144
Simon Hunt1894d792015-02-04 17:09:20 -0800145 function removeDevice(data) {
146 var id = data.id,
147 d = lu[id];
148 if (d) {
149 removeDeviceElement(d);
Simon Hunt1894d792015-02-04 17:09:20 -0800150 }
151 }
152
153 function addHost(data) {
154 var id = data.id,
155 d, lnk;
156
157 // although this is an add host event, if we already have the
158 // host, treat it as an update instead..
159 if (lu[id]) {
160 updateHost(data);
161 return;
162 }
163
Simon Hunt3a6eec02015-02-09 21:16:43 -0800164 d = tms.createHostNode(data);
Simon Hunt1894d792015-02-04 17:09:20 -0800165 network.nodes.push(d);
166 lu[id] = d;
Simon Hunt1894d792015-02-04 17:09:20 -0800167 updateNodes();
168
Simon Hunt3a6eec02015-02-09 21:16:43 -0800169 lnk = tms.createHostLink(data);
Simon Hunt1894d792015-02-04 17:09:20 -0800170 if (lnk) {
Simon Hunt1894d792015-02-04 17:09:20 -0800171 d.linkData = lnk; // cache ref on its host
172 network.links.push(lnk);
173 lu[d.ingress] = lnk;
174 lu[d.egress] = lnk;
175 updateLinks();
176 }
Simon Hunta17fa672015-08-19 18:42:22 -0700177 fStart();
Simon Hunt1894d792015-02-04 17:09:20 -0800178 }
179
180 function updateHost(data) {
181 var id = data.id,
182 d = lu[id];
183 if (d) {
184 angular.extend(d, data);
Simon Hunt3a6eec02015-02-09 21:16:43 -0800185 if (tms.positionNode(d, true)) {
Simon Hunt445e8152015-02-06 13:00:12 -0800186 sendUpdateMeta(d);
Simon Hunt1894d792015-02-04 17:09:20 -0800187 }
188 updateNodes();
Simon Hunt1894d792015-02-04 17:09:20 -0800189 }
190 }
191
Simon Hunt95d56fd2015-11-12 11:06:44 -0800192 function moveHost(data) {
193 var id = data.id,
194 d = lu[id],
195 lnk;
196 if (d) {
197 // first remove the old host link
198 removeLinkElement(d.linkData);
199
200 // merge new data
201 angular.extend(d, data);
202 if (tms.positionNode(d, true)) {
203 sendUpdateMeta(d);
204 }
205
206 // now create a new host link
207 lnk = tms.createHostLink(data);
208 if (lnk) {
209 d.linkData = lnk;
210 network.links.push(lnk);
211 lu[d.ingress] = lnk;
212 lu[d.egress] = lnk;
213 }
214
215 updateNodes();
216 updateLinks();
217 fResume();
218 }
219 }
220
Simon Hunt1894d792015-02-04 17:09:20 -0800221 function removeHost(data) {
222 var id = data.id,
223 d = lu[id];
224 if (d) {
225 removeHostElement(d, true);
Simon Hunt1894d792015-02-04 17:09:20 -0800226 }
227 }
228
229 function addLink(data) {
Simon Huntdc6adea2015-02-09 22:29:36 -0800230 var result = tms.findLink(data, 'add'),
Simon Hunt1894d792015-02-04 17:09:20 -0800231 bad = result.badLogic,
232 d = result.ldata;
233
234 if (bad) {
Simon Hunteb18f522016-01-28 19:22:23 -0800235 $log.debug(bad + ': ' + link.id);
Simon Hunt1894d792015-02-04 17:09:20 -0800236 return;
237 }
238
239 if (d) {
240 // we already have a backing store link for src/dst nodes
241 addLinkUpdate(d, data);
242 return;
243 }
244
245 // no backing store link yet
Simon Hunt3a6eec02015-02-09 21:16:43 -0800246 d = tms.createLink(data);
Simon Hunt1894d792015-02-04 17:09:20 -0800247 if (d) {
248 network.links.push(d);
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700249 aggregateLink(d, data);
Simon Hunt1894d792015-02-04 17:09:20 -0800250 lu[d.key] = d;
251 updateLinks();
Simon Hunta17fa672015-08-19 18:42:22 -0700252 fStart();
Simon Hunt1894d792015-02-04 17:09:20 -0800253 }
254 }
255
256 function updateLink(data) {
Simon Huntdc6adea2015-02-09 22:29:36 -0800257 var result = tms.findLink(data, 'update'),
Simon Hunt1894d792015-02-04 17:09:20 -0800258 bad = result.badLogic;
259 if (bad) {
Simon Hunteb18f522016-01-28 19:22:23 -0800260 $log.debug(bad + ': ' + link.id);
Simon Hunt1894d792015-02-04 17:09:20 -0800261 return;
262 }
Simon Hunteb18f522016-01-28 19:22:23 -0800263 result.updateWith(data);
Simon Hunt1894d792015-02-04 17:09:20 -0800264 }
265
266 function removeLink(data) {
Simon Hunta4242de2015-02-24 17:11:55 -0800267 var result = tms.findLink(data, 'remove');
268
269 if (!result.badLogic) {
270 result.removeRawLink();
Simon Hunt1894d792015-02-04 17:09:20 -0800271 }
Simon Hunt1894d792015-02-04 17:09:20 -0800272 }
273
Simon Hunt4a6b54b2015-10-27 22:08:25 -0700274 function topoStartDone(data) {
275 // called when the initial barrage of data has been sent from server
276 uplink.topoStartDone();
277 }
278
Simon Hunt1894d792015-02-04 17:09:20 -0800279 // ========================
280
Simon Hunt94f7dae2015-08-26 17:40:59 -0700281 function nodeById(id) {
282 return lu[id];
283 }
284
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700285 function makeNodeKey(node1, node2) {
286 return node1 + '-' + node2;
287 }
288
289 function findNodePair(key, keyRev) {
290 if (network.linksByDevice[key]) {
291 return key;
292 } else if (network.linksByDevice[keyRev]) {
293 return keyRev;
294 } else {
295 return false;
296 }
297 }
298
299 function aggregateLink(ldata, link) {
300 var key = makeNodeKey(link.src, link.dst),
301 keyRev = makeNodeKey(link.dst, link.src),
302 found = findNodePair(key, keyRev);
303
304 if (found) {
305 network.linksByDevice[found].push(ldata);
306 ldata.devicePair = found;
307 } else {
308 network.linksByDevice[key] = [ ldata ];
309 ldata.devicePair = key;
310 }
311 }
312
Simon Hunt1894d792015-02-04 17:09:20 -0800313 function addLinkUpdate(ldata, link) {
314 // add link event, but we already have the reverse link installed
315 ldata.fromTarget = link;
Simon Huntdc6adea2015-02-09 22:29:36 -0800316 rlk[link.id] = ldata.key;
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700317 // possible solution to el being undefined in restyleLinkElement:
318 //_updateLinks();
Simon Hunt1894d792015-02-04 17:09:20 -0800319 restyleLinkElement(ldata);
320 }
321
Simon Hunt1894d792015-02-04 17:09:20 -0800322
323 var widthRatio = 1.4,
324 linkScale = d3.scale.linear()
325 .domain([1, 12])
326 .range([widthRatio, 12 * widthRatio])
Simon Hunt5724fb42015-02-05 16:59:40 -0800327 .clamp(true),
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800328 allLinkTypes = 'direct indirect optical tunnel',
329 allLinkSubTypes = 'inactive not-permitted';
Simon Hunt1894d792015-02-04 17:09:20 -0800330
Simon Hunta142dd22015-02-12 22:07:51 -0800331 function restyleLinkElement(ldata, immediate) {
Simon Hunt1894d792015-02-04 17:09:20 -0800332 // this fn's job is to look at raw links and decide what svg classes
333 // need to be applied to the line element in the DOM
334 var th = ts.theme(),
335 el = ldata.el,
336 type = ldata.type(),
337 lw = ldata.linkWidth(),
Simon Hunta142dd22015-02-12 22:07:51 -0800338 online = ldata.online(),
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800339 modeCls = ldata.expected() ? 'inactive' : 'not-permitted',
Simon Hunta142dd22015-02-12 22:07:51 -0800340 delay = immediate ? 0 : 1000;
Simon Hunt1894d792015-02-04 17:09:20 -0800341
Simon Huntf44d7262016-06-14 14:46:56 -0700342 // NOTE: understand why el is sometimes undefined on addLink events...
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700343 // Investigated:
344 // el is undefined when it's a reverse link that is being added.
345 // updateLinks (which sets ldata.el) isn't called before this is called.
346 // Calling _updateLinks in addLinkUpdate fixes it, but there might be
347 // a more efficient way to fix it.
348 if (el && !el.empty()) {
Thomas Vachuskacb5016f2015-05-18 14:11:43 -0700349 el.classed('link', true);
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800350 el.classed(allLinkSubTypes, false);
351 el.classed(modeCls, !online);
Thomas Vachuskacb5016f2015-05-18 14:11:43 -0700352 el.classed(allLinkTypes, false);
353 if (type) {
354 el.classed(type, true);
355 }
356 el.transition()
357 .duration(delay)
358 .attr('stroke-width', linkScale(lw))
359 .attr('stroke', linkConfig[th].baseColor);
Simon Hunt1894d792015-02-04 17:09:20 -0800360 }
Simon Hunt1894d792015-02-04 17:09:20 -0800361 }
362
Simon Hunt1894d792015-02-04 17:09:20 -0800363 function removeLinkElement(d) {
364 var idx = fs.find(d.key, network.links, 'key'),
365 removed;
366 if (idx >=0) {
367 // remove from links array
368 removed = network.links.splice(idx, 1);
369 // remove from lookup cache
370 delete lu[removed[0].key];
371 updateLinks();
Simon Hunta17fa672015-08-19 18:42:22 -0700372 fResume();
Simon Hunt1894d792015-02-04 17:09:20 -0800373 }
374 }
375
376 function removeHostElement(d, upd) {
377 // first, remove associated hostLink...
378 removeLinkElement(d.linkData);
379
380 // remove hostLink bindings
381 delete lu[d.ingress];
382 delete lu[d.egress];
383
384 // remove from lookup cache
385 delete lu[d.id];
386 // remove from nodes array
387 var idx = fs.find(d.id, network.nodes);
388 network.nodes.splice(idx, 1);
389
390 // remove from SVG
391 // NOTE: upd is false if we were called from removeDeviceElement()
392 if (upd) {
393 updateNodes();
Simon Hunta17fa672015-08-19 18:42:22 -0700394 fResume();
Simon Hunt1894d792015-02-04 17:09:20 -0800395 }
396 }
397
398 function removeDeviceElement(d) {
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700399 var id = d.id,
400 idx;
Simon Hunt1894d792015-02-04 17:09:20 -0800401 // first, remove associated hosts and links..
Simon Huntdc6adea2015-02-09 22:29:36 -0800402 tms.findAttachedHosts(id).forEach(removeHostElement);
403 tms.findAttachedLinks(id).forEach(removeLinkElement);
Simon Hunt1894d792015-02-04 17:09:20 -0800404
405 // remove from lookup cache
406 delete lu[id];
407 // remove from nodes array
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700408 idx = fs.find(id, network.nodes);
409 if (idx > -1) {
410 network.nodes.splice(idx, 1);
411 }
Simon Hunt1894d792015-02-04 17:09:20 -0800412
413 if (!network.nodes.length) {
Simon Huntdc6adea2015-02-09 22:29:36 -0800414 uplink.showNoDevs(true);
Simon Hunt1894d792015-02-04 17:09:20 -0800415 }
416
417 // remove from SVG
418 updateNodes();
Simon Hunta17fa672015-08-19 18:42:22 -0700419 fResume();
Simon Hunt1894d792015-02-04 17:09:20 -0800420 }
421
Simon Hunt5724fb42015-02-05 16:59:40 -0800422 function updateHostVisibility() {
Simon Hunt18bf9822015-02-12 17:35:45 -0800423 sus.visible(nodeG.selectAll('.host'), showHosts);
424 sus.visible(linkG.selectAll('.hostLink'), showHosts);
Simon Hunt8eb4d3a2015-02-23 18:23:29 -0800425 sus.visible(linkLabelG.selectAll('.hostLinkLabel'), showHosts);
Simon Hunt5724fb42015-02-05 16:59:40 -0800426 }
427
428 function updateOfflineVisibility(dev) {
429 function updDev(d, show) {
Simon Hunt8eb4d3a2015-02-23 18:23:29 -0800430 var b;
Simon Hunt18bf9822015-02-12 17:35:45 -0800431 sus.visible(d.el, show);
Simon Hunt5724fb42015-02-05 16:59:40 -0800432
Simon Huntdc6adea2015-02-09 22:29:36 -0800433 tms.findAttachedLinks(d.id).forEach(function (link) {
Simon Hunt5724fb42015-02-05 16:59:40 -0800434 b = show && ((link.type() !== 'hostLink') || showHosts);
Simon Hunt18bf9822015-02-12 17:35:45 -0800435 sus.visible(link.el, b);
Simon Hunt5724fb42015-02-05 16:59:40 -0800436 });
Simon Huntdc6adea2015-02-09 22:29:36 -0800437 tms.findAttachedHosts(d.id).forEach(function (host) {
Simon Hunt5724fb42015-02-05 16:59:40 -0800438 b = show && showHosts;
Simon Hunt18bf9822015-02-12 17:35:45 -0800439 sus.visible(host.el, b);
Simon Hunt5724fb42015-02-05 16:59:40 -0800440 });
441 }
442
443 if (dev) {
444 // updating a specific device that just toggled off/on-line
445 updDev(dev, dev.online || showOffline);
446 } else {
447 // updating all offline devices
Simon Huntdc6adea2015-02-09 22:29:36 -0800448 tms.findDevices(true).forEach(function (d) {
Simon Hunt5724fb42015-02-05 16:59:40 -0800449 updDev(d, showOffline);
450 });
451 }
452 }
453
Simon Hunt1894d792015-02-04 17:09:20 -0800454
Simon Hunt445e8152015-02-06 13:00:12 -0800455 function sendUpdateMeta(d, clearPos) {
Simon Huntac4c6f72015-02-03 19:50:53 -0800456 var metaUi = {},
457 ll;
458
Simon Hunt445e8152015-02-06 13:00:12 -0800459 // if we are not clearing the position data (unpinning),
Simon Huntfd7106c2016-02-09 15:05:26 -0800460 // attach the x, y, (and equivalent longitude, latitude)...
Simon Hunt445e8152015-02-06 13:00:12 -0800461 if (!clearPos) {
Simon Hunt3a6eec02015-02-09 21:16:43 -0800462 ll = tms.lngLatFromCoord([d.x, d.y]);
Simon Huntfd7106c2016-02-09 15:05:26 -0800463 metaUi = {
464 x: d.x,
465 y: d.y,
466 equivLoc: {
467 lng: ll[0],
468 lat: ll[1]
469 }
470 };
Simon Hunt1894d792015-02-04 17:09:20 -0800471 }
472 d.metaUi = metaUi;
Simon Hunt237676b52015-03-10 19:04:26 -0700473 wss.sendEvent('updateMeta', {
Simon Hunt1894d792015-02-04 17:09:20 -0800474 id: d.id,
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700475 class: d.class,
Simon Hunt1894d792015-02-04 17:09:20 -0800476 memento: metaUi
477 });
Simon Huntac4c6f72015-02-03 19:50:53 -0800478 }
479
Simon Hunt1894d792015-02-04 17:09:20 -0800480
Simon Huntac4c6f72015-02-03 19:50:53 -0800481 function mkSvgClass(d) {
482 return d.fixed ? d.svgClass + ' fixed' : d.svgClass;
483 }
484
Simon Hunt5724fb42015-02-05 16:59:40 -0800485 function vis(b) {
486 return b ? 'visible' : 'hidden';
487 }
488
Simon Huntfcbde892015-04-16 12:05:28 -0700489 function toggleHosts(x) {
490 var kev = (x === 'keyev'),
491 on = kev ? !showHosts : !!x;
492
493 showHosts = on;
Simon Hunt5724fb42015-02-05 16:59:40 -0800494 updateHostVisibility();
Simon Huntfcbde892015-04-16 12:05:28 -0700495 flash.flash('Hosts ' + vis(on));
496 return on;
Simon Hunt5724fb42015-02-05 16:59:40 -0800497 }
498
Simon Huntfcbde892015-04-16 12:05:28 -0700499 function toggleOffline(x) {
500 var kev = (x === 'keyev'),
501 on = kev ? !showOffline : !!x;
502
503 showOffline = on;
Simon Hunt5724fb42015-02-05 16:59:40 -0800504 updateOfflineVisibility();
Simon Huntfcbde892015-04-16 12:05:28 -0700505 flash.flash('Offline devices ' + vis(on));
506 return on;
Simon Hunt5724fb42015-02-05 16:59:40 -0800507 }
508
509 function cycleDeviceLabels() {
Bri Prebilic Cole9cf1a8d2015-04-21 13:15:29 -0700510 flash.flash(td3.incDevLabIndex());
Simon Huntdc6adea2015-02-09 22:29:36 -0800511 tms.findDevices().forEach(function (d) {
Simon Hunta4242de2015-02-24 17:11:55 -0800512 td3.updateDeviceLabel(d);
Simon Hunt1c367112015-02-05 18:02:46 -0800513 });
Simon Hunt5724fb42015-02-05 16:59:40 -0800514 }
515
Simon Hunt445e8152015-02-06 13:00:12 -0800516 function unpin() {
Simon Hunt08f841d02015-02-10 14:39:20 -0800517 var hov = tss.hovered();
518 if (hov) {
519 sendUpdateMeta(hov, true);
520 hov.fixed = false;
521 hov.el.classed('fixed', false);
Simon Hunt445e8152015-02-06 13:00:12 -0800522 fResume();
523 }
524 }
525
Simon Hunta142dd22015-02-12 22:07:51 -0800526 function showMastership(masterId) {
527 if (!masterId) {
528 restoreLayerState();
529 } else {
530 showMastershipFor(masterId);
531 }
532 }
533
534 function restoreLayerState() {
535 // NOTE: this level of indirection required, for when we have
536 // the layer filter functionality re-implemented
537 suppressLayers(false);
538 }
539
540 function showMastershipFor(id) {
541 suppressLayers(true);
542 node.each(function (n) {
543 if (n.master === id) {
Simon Hunt743a8492015-08-25 16:18:19 -0700544 n.el.classed('suppressedmax', false);
Simon Hunta142dd22015-02-12 22:07:51 -0800545 }
546 });
547 }
548
Simon Hunt743a8492015-08-25 16:18:19 -0700549 function supAmt(less) {
550 return less ? "suppressed" : "suppressedmax";
551 }
552
553 function suppressLayers(b, less) {
554 var cls = supAmt(less);
555 node.classed(cls, b);
556 link.classed(cls, b);
557 }
558
559 function unsuppressNode(id, less) {
560 var cls = supAmt(less);
561 node.each(function (n) {
562 if (n.id === id) {
563 n.el.classed(cls, false);
564 }
565 });
566 }
567
Simon Hunt94f7dae2015-08-26 17:40:59 -0700568 function unsuppressLink(key, less) {
Simon Hunt743a8492015-08-25 16:18:19 -0700569 var cls = supAmt(less);
570 link.each(function (n) {
Simon Hunt94f7dae2015-08-26 17:40:59 -0700571 if (n.key === key) {
Simon Hunt743a8492015-08-25 16:18:19 -0700572 n.el.classed(cls, false);
573 }
574 });
Simon Hunta142dd22015-02-12 22:07:51 -0800575 }
Simon Hunt445e8152015-02-06 13:00:12 -0800576
Simon Hunt86b7c882015-04-02 23:06:08 -0700577 function showBadLinks() {
578 var badLinks = tms.findBadLinks();
579 flash.flash('Bad Links: ' + badLinks.length);
580 $log.debug('Bad Link List (' + badLinks.length + '):');
581 badLinks.forEach(function (d) {
582 $log.debug('bad link: (' + d.bad + ') ' + d.key, d);
583 if (d.el) {
584 d.el.attr('stroke-width', linkScale(2.8))
585 .attr('stroke', 'red');
586 }
587 });
588 // back to normal after 2 seconds...
589 $timeout(updateLinks, 2000);
590 }
591
Simon Huntfd7106c2016-02-09 15:05:26 -0800592 function resetAllLocations() {
593 tms.resetAllLocations();
594 updateNodes();
595 tick(); // force nodes to be redrawn in their new locations
596 flash.flash('Reset Node Locations');
597 }
598
Simon Hunt5724fb42015-02-05 16:59:40 -0800599 // ==========================================
600
Simon Huntac4c6f72015-02-03 19:50:53 -0800601 function updateNodes() {
Thomas Vachuska1a989c12015-06-09 18:29:22 -0700602 if (fNodesTimer) {
603 $timeout.cancel(fNodesTimer);
604 }
605 fNodesTimer = $timeout(_updateNodes, 150);
606 }
607
Simon Hunta17fa672015-08-19 18:42:22 -0700608 // IMPLEMENTATION NOTE: _updateNodes() should NOT stop, start, or resume
609 // the force layout; that needs to be determined and implemented elsewhere
Thomas Vachuska1a989c12015-06-09 18:29:22 -0700610 function _updateNodes() {
Simon Hunt1894d792015-02-04 17:09:20 -0800611 // select all the nodes in the layout:
Simon Huntac4c6f72015-02-03 19:50:53 -0800612 node = nodeG.selectAll('.node')
613 .data(network.nodes, function (d) { return d.id; });
614
Simon Hunt1894d792015-02-04 17:09:20 -0800615 // operate on existing nodes:
Simon Hunta4242de2015-02-24 17:11:55 -0800616 node.filter('.device').each(td3.deviceExisting);
617 node.filter('.host').each(td3.hostExisting);
Simon Huntac4c6f72015-02-03 19:50:53 -0800618
619 // operate on entering nodes:
620 var entering = node.enter()
621 .append('g')
622 .attr({
623 id: function (d) { return sus.safeId(d.id); },
624 class: mkSvgClass,
Simon Hunta17fa672015-08-19 18:42:22 -0700625 transform: function (d) {
626 // Need to guard against NaN here ??
627 return sus.translate(d.x, d.y);
628 },
Simon Huntac4c6f72015-02-03 19:50:53 -0800629 opacity: 0
630 })
631 .call(drag)
Simon Hunt08f841d02015-02-10 14:39:20 -0800632 .on('mouseover', tss.nodeMouseOver)
633 .on('mouseout', tss.nodeMouseOut)
Simon Huntac4c6f72015-02-03 19:50:53 -0800634 .transition()
635 .attr('opacity', 1);
636
Simon Hunt1894d792015-02-04 17:09:20 -0800637 // augment entering nodes:
Simon Hunta4242de2015-02-24 17:11:55 -0800638 entering.filter('.device').each(td3.deviceEnter);
639 entering.filter('.host').each(td3.hostEnter);
Simon Huntac4c6f72015-02-03 19:50:53 -0800640
Simon Hunt51056592015-02-03 21:48:07 -0800641 // operate on both existing and new nodes:
Simon Hunta4242de2015-02-24 17:11:55 -0800642 td3.updateDeviceColors();
Simon Huntac4c6f72015-02-03 19:50:53 -0800643
644 // operate on exiting nodes:
645 // Note that the node is removed after 2 seconds.
646 // Sub element animations should be shorter than 2 seconds.
647 var exiting = node.exit()
648 .transition()
649 .duration(2000)
650 .style('opacity', 0)
651 .remove();
652
Simon Hunt1894d792015-02-04 17:09:20 -0800653 // exiting node specifics:
Simon Hunta4242de2015-02-24 17:11:55 -0800654 exiting.filter('.host').each(td3.hostExit);
655 exiting.filter('.device').each(td3.deviceExit);
Simon Huntac4c6f72015-02-03 19:50:53 -0800656 }
657
Simon Hunt51056592015-02-03 21:48:07 -0800658 // ==========================
Simon Hunt1894d792015-02-04 17:09:20 -0800659
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700660 function getDefaultPos(link) {
661 return {
662 x1: link.source.x,
663 y1: link.source.y,
664 x2: link.target.x,
665 y2: link.target.y
666 };
667 }
668
669 // returns amount of adjustment along the normal for given link
670 function amt(numLinks, linkIdx) {
671 var gap = 6;
672 return (linkIdx - ((numLinks - 1) / 2)) * gap;
673 }
674
675 function calcMovement(d, amt, flipped) {
676 var pos = getDefaultPos(d),
677 mult = flipped ? -amt : amt,
678 dx = pos.x2 - pos.x1,
679 dy = pos.y2 - pos.y1,
680 length = Math.sqrt((dx * dx) + (dy * dy));
681
682 return {
683 x1: pos.x1 + (mult * dy / length),
684 y1: pos.y1 + (mult * -dx / length),
685 x2: pos.x2 + (mult * dy / length),
686 y2: pos.y2 + (mult * -dx / length)
687 };
688 }
689
690 function calcPosition() {
691 var lines = this,
692 linkSrcId;
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700693 linkNums = [];
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700694 lines.each(function (d) {
695 if (d.type() === 'hostLink') {
696 d.position = getDefaultPos(d);
697 }
698 });
699
700 function normalizeLinkSrc(link) {
701 // ensure source device is consistent across set of links
702 // temporary measure until link modeling is refactored
703 if (!linkSrcId) {
704 linkSrcId = link.source.id;
705 return false;
706 }
707
708 return link.source.id !== linkSrcId;
709 }
710
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700711 angular.forEach(network.linksByDevice, function (linkArr, key) {
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700712 var numLinks = linkArr.length,
713 link;
714
715 if (numLinks === 1) {
716 link = linkArr[0];
717 link.position = getDefaultPos(link);
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700718 link.position.multiLink = false;
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700719 } else if (numLinks >= 5) {
720 // this code is inefficient, in the future the way links
721 // are modeled will be changed
722 angular.forEach(linkArr, function (link) {
723 link.position = getDefaultPos(link);
724 link.position.multiLink = true;
725 });
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700726 linkNums.push({
727 id: key,
728 num: numLinks,
729 linkCoords: linkArr[0].position
730 });
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700731 } else {
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700732 linkSrcId = null;
733 angular.forEach(linkArr, function (link, index) {
734 var offsetAmt = amt(numLinks, index),
735 needToFlip = normalizeLinkSrc(link);
736 link.position = calcMovement(link, offsetAmt, needToFlip);
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700737 link.position.multiLink = false;
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700738 });
739 }
740 });
741 }
742
Simon Hunt1894d792015-02-04 17:09:20 -0800743 function updateLinks() {
Thomas Vachuska1a989c12015-06-09 18:29:22 -0700744 if (fLinksTimer) {
745 $timeout.cancel(fLinksTimer);
746 }
747 fLinksTimer = $timeout(_updateLinks, 150);
748 }
749
Simon Hunta17fa672015-08-19 18:42:22 -0700750 // IMPLEMENTATION NOTE: _updateLinks() should NOT stop, start, or resume
751 // the force layout; that needs to be determined and implemented elsewhere
Thomas Vachuska1a989c12015-06-09 18:29:22 -0700752 function _updateLinks() {
Simon Hunt1894d792015-02-04 17:09:20 -0800753 var th = ts.theme();
754
755 link = linkG.selectAll('.link')
756 .data(network.links, function (d) { return d.key; });
757
758 // operate on existing links:
Simon Huntd5264122015-02-25 10:17:43 -0800759 link.each(function (d) {
760 // this is supposed to be an existing link, but we have observed
761 // occasions (where links are deleted and added rapidly?) where
762 // the DOM element has not been defined. So protect against that...
763 if (d.el) {
764 restyleLinkElement(d, true);
765 }
766 });
Simon Hunt1894d792015-02-04 17:09:20 -0800767
768 // operate on entering links:
769 var entering = link.enter()
770 .append('line')
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700771 .call(calcPosition)
Simon Hunt1894d792015-02-04 17:09:20 -0800772 .attr({
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700773 x1: function (d) { return d.position.x1; },
774 y1: function (d) { return d.position.y1; },
775 x2: function (d) { return d.position.x2; },
776 y2: function (d) { return d.position.y2; },
Simon Hunt1894d792015-02-04 17:09:20 -0800777 stroke: linkConfig[th].inColor,
778 'stroke-width': linkConfig.inWidth
779 });
780
781 // augment links
Simon Hunta4242de2015-02-24 17:11:55 -0800782 entering.each(td3.linkEntering);
Simon Hunt1894d792015-02-04 17:09:20 -0800783
784 // operate on both existing and new links:
785 //link.each(...)
786
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700787 // add labels for how many links are in a thick line
788 td3.applyNumLinkLabels(linkNums, numLinkLblsG);
789
Simon Hunt1894d792015-02-04 17:09:20 -0800790 // apply or remove labels
Simon Hunta4242de2015-02-24 17:11:55 -0800791 td3.applyLinkLabels();
Simon Hunt1894d792015-02-04 17:09:20 -0800792
793 // operate on exiting links:
794 link.exit()
795 .attr('stroke-dasharray', '3 3')
Simon Hunt5724fb42015-02-05 16:59:40 -0800796 .attr('stroke', linkConfig[th].outColor)
Simon Hunt1894d792015-02-04 17:09:20 -0800797 .style('opacity', 0.5)
798 .transition()
799 .duration(1500)
800 .attr({
801 'stroke-dasharray': '3 12',
Simon Hunt1894d792015-02-04 17:09:20 -0800802 'stroke-width': linkConfig.outWidth
803 })
804 .style('opacity', 0.0)
805 .remove();
Simon Hunt1894d792015-02-04 17:09:20 -0800806 }
807
Simon Huntac4c6f72015-02-03 19:50:53 -0800808
809 // ==========================
Simon Hunt737c89f2015-01-28 12:23:19 -0800810 // force layout tick function
Simon Hunt737c89f2015-01-28 12:23:19 -0800811
Simon Hunt5724fb42015-02-05 16:59:40 -0800812 function fResume() {
Simon Huntc3c5b672015-02-20 11:32:13 -0800813 if (!tos.isOblique()) {
Simon Hunt5724fb42015-02-05 16:59:40 -0800814 force.resume();
815 }
816 }
817
818 function fStart() {
Simon Huntc3c5b672015-02-20 11:32:13 -0800819 if (!tos.isOblique()) {
Simon Hunta17fa672015-08-19 18:42:22 -0700820 if (fTimer) {
821 $timeout.cancel(fTimer);
822 }
823 fTimer = $timeout(function () {
824 $log.debug("Starting force-layout");
825 force.start();
826 }, 200);
Simon Hunt5724fb42015-02-05 16:59:40 -0800827 }
828 }
829
830 var tickStuff = {
831 nodeAttr: {
Simon Hunta17fa672015-08-19 18:42:22 -0700832 transform: function (d) {
833 var dx = isNaN(d.x) ? 0 : d.x,
834 dy = isNaN(d.y) ? 0 : d.y;
835 return sus.translate(dx, dy);
836 }
Simon Hunt5724fb42015-02-05 16:59:40 -0800837 },
838 linkAttr: {
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700839 x1: function (d) { return d.position.x1; },
840 y1: function (d) { return d.position.y1; },
841 x2: function (d) { return d.position.x2; },
842 y2: function (d) { return d.position.y2; }
Simon Hunt5724fb42015-02-05 16:59:40 -0800843 },
844 linkLabelAttr: {
845 transform: function (d) {
Simon Huntdc6adea2015-02-09 22:29:36 -0800846 var lnk = tms.findLinkById(d.key);
Simon Hunt5724fb42015-02-05 16:59:40 -0800847 if (lnk) {
Carmelo Casconed01eda62016-08-02 10:19:15 -0700848 return td3.transformLabel(lnk.position, d.key);
Simon Hunt5724fb42015-02-05 16:59:40 -0800849 }
850 }
851 }
852 };
853
854 function tick() {
Simon Hunt3ab20282015-02-26 20:32:19 -0800855 // guard against null (which can happen when our view pages out)...
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700856 if (node && node.size()) {
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700857 node.attr(tickStuff.nodeAttr);
858 }
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700859 if (link && link.size()) {
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700860 link.call(calcPosition)
861 .attr(tickStuff.linkAttr);
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700862 td3.applyNumLinkLabels(linkNums, numLinkLblsG);
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700863 }
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700864 if (linkLabel && linkLabel.size()) {
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700865 linkLabel.attr(tickStuff.linkLabelAttr);
866 }
Simon Hunt737c89f2015-01-28 12:23:19 -0800867 }
868
869
Simon Huntac4c6f72015-02-03 19:50:53 -0800870 // ==========================
871 // === MOUSE GESTURE HANDLERS
872
Simon Hunt205099e2015-02-07 13:12:01 -0800873 function zoomingOrPanning(ev) {
874 return ev.metaKey || ev.altKey;
Simon Hunt445e8152015-02-06 13:00:12 -0800875 }
876
877 function atDragEnd(d) {
878 // once we've finished moving, pin the node in position
879 d.fixed = true;
880 d3.select(this).classed('fixed', true);
881 sendUpdateMeta(d);
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700882 tss.clickConsumed(true);
Simon Hunt445e8152015-02-06 13:00:12 -0800883 }
884
885 // predicate that indicates when dragging is active
886 function dragEnabled() {
887 var ev = d3.event.sourceEvent;
888 // nodeLock means we aren't allowing nodes to be dragged...
Simon Hunt205099e2015-02-07 13:12:01 -0800889 return !nodeLock && !zoomingOrPanning(ev);
Simon Hunt445e8152015-02-06 13:00:12 -0800890 }
891
892 // predicate that indicates when clicking is active
893 function clickEnabled() {
894 return true;
895 }
Simon Hunt737c89f2015-01-28 12:23:19 -0800896
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700897 // =============================================
898 // function entry points for overlay module
Simon Huntf542d842015-02-11 16:20:33 -0800899
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700900 // TODO: find an automatic way of tracking via the "showHighlights" events
Simon Hunte50829c2015-06-09 08:39:28 -0700901 var allTrafficClasses = 'primary secondary optical animated ' +
902 'port-traffic-Kbps port-traffic-Mbps port-traffic-Gbps ' +
903 'port-traffic-Gbps-choked';
Simon Huntf542d842015-02-11 16:20:33 -0800904
905 function clearLinkTrafficStyle() {
906 link.style('stroke-width', null)
907 .classed(allTrafficClasses, false);
908 }
909
910 function removeLinkLabels() {
911 network.links.forEach(function (d) {
912 d.label = '';
913 });
914 }
Simon Hunt737c89f2015-01-28 12:23:19 -0800915
Simon Hunte9343f32015-10-21 18:07:46 -0700916 function clearNodeDeco() {
917 node.selectAll('g.badge').remove();
918 }
919
920 function removeNodeBadges() {
921 network.nodes.forEach(function (d) {
922 d.badge = null;
923 });
924 }
925
Simon Hunta4242de2015-02-24 17:11:55 -0800926 function updateLinkLabelModel() {
927 // create the backing data for showing labels..
928 var data = [];
929 link.each(function (d) {
930 if (d.label) {
931 data.push({
932 id: 'lab-' + d.key,
933 key: d.key,
934 label: d.label,
935 ldata: d
936 });
937 }
938 });
939
940 linkLabel = linkLabelG.selectAll('.linkLabel')
941 .data(data, function (d) { return d.id; });
942 }
943
Simon Hunt737c89f2015-01-28 12:23:19 -0800944 // ==========================
Simon Huntac4c6f72015-02-03 19:50:53 -0800945 // Module definition
Simon Hunt737c89f2015-01-28 12:23:19 -0800946
Simon Huntdc6adea2015-02-09 22:29:36 -0800947 function mkModelApi(uplink) {
948 return {
949 projection: uplink.projection,
950 network: network,
951 restyleLinkElement: restyleLinkElement,
952 removeLinkElement: removeLinkElement
953 };
954 }
955
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700956 function mkD3Api() {
Simon Hunta4242de2015-02-24 17:11:55 -0800957 return {
958 node: function () { return node; },
959 link: function () { return link; },
960 linkLabel: function () { return linkLabel; },
961 instVisible: function () { return tis.isVisible(); },
962 posNode: tms.positionNode,
963 showHosts: function () { return showHosts; },
964 restyleLinkElement: restyleLinkElement,
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700965 updateLinkLabelModel: updateLinkLabelModel,
966 linkConfig: function () { return linkConfig; }
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700967 };
Simon Hunta4242de2015-02-24 17:11:55 -0800968 }
969
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700970 function mkSelectApi() {
Simon Hunt08f841d02015-02-10 14:39:20 -0800971 return {
972 node: function () { return node; },
973 zoomingOrPanning: zoomingOrPanning,
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700974 updateDeviceColors: td3.updateDeviceColors,
975 deselectLink: tls.deselectLink
Simon Hunt08f841d02015-02-10 14:39:20 -0800976 };
977 }
978
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700979 function mkTrafficApi() {
980 return {
981 hovered: tss.hovered,
982 somethingSelected: tss.somethingSelected,
983 selectOrder: tss.selectOrder
984 };
985 }
986
987 function mkOverlayApi() {
Simon Huntf542d842015-02-11 16:20:33 -0800988 return {
Simon Hunte9343f32015-10-21 18:07:46 -0700989 clearNodeDeco: clearNodeDeco,
990 removeNodeBadges: removeNodeBadges,
Simon Huntf542d842015-02-11 16:20:33 -0800991 clearLinkTrafficStyle: clearLinkTrafficStyle,
992 removeLinkLabels: removeLinkLabels,
Simon Hunt743a8492015-08-25 16:18:19 -0700993 findLinkById: tms.findLinkById,
Simon Hunt94f7dae2015-08-26 17:40:59 -0700994 findNodeById: nodeById,
Simon Huntf542d842015-02-11 16:20:33 -0800995 updateLinks: updateLinks,
Simon Hunt743a8492015-08-25 16:18:19 -0700996 updateNodes: updateNodes,
997 supLayers: suppressLayers,
998 unsupNode: unsuppressNode,
999 unsupLink: unsuppressLink
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -07001000 };
Simon Huntf542d842015-02-11 16:20:33 -08001001 }
1002
Simon Huntc3c5b672015-02-20 11:32:13 -08001003 function mkObliqueApi(uplink, fltr) {
Simon Hunt96f88c62015-02-19 17:57:25 -08001004 return {
Simon Huntc3c5b672015-02-20 11:32:13 -08001005 force: function() { return force; },
1006 zoomLayer: uplink.zoomLayer,
1007 nodeGBBox: function() { return nodeG.node().getBBox(); },
Simon Hunt96f88c62015-02-19 17:57:25 -08001008 node: function () { return node; },
Simon Huntc3c5b672015-02-20 11:32:13 -08001009 link: function () { return link; },
1010 linkLabel: function () { return linkLabel; },
1011 nodes: function () { return network.nodes; },
1012 tickStuff: tickStuff,
1013 nodeLock: function (b) {
1014 var old = nodeLock;
1015 nodeLock = b;
1016 return old;
1017 },
1018 opacifyMap: uplink.opacifyMap,
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -07001019 inLayer: fltr.inLayer,
Bri Prebilic Cole80401762015-07-16 11:36:18 -07001020 calcLinkPos: calcPosition,
1021 applyNumLinkLabels: function () {
1022 td3.applyNumLinkLabels(linkNums, numLinkLblsG);
1023 }
Simon Hunt96f88c62015-02-19 17:57:25 -08001024 };
1025 }
1026
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001027 function mkFilterApi() {
Simon Hunteb0fa052015-02-17 19:20:28 -08001028 return {
1029 node: function () { return node; },
1030 link: function () { return link; }
1031 };
1032 }
1033
Simon Hunt9e2104c2015-02-26 10:48:59 -08001034 function mkLinkApi(svg, uplink) {
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001035 return {
1036 svg: svg,
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001037 zoomer: uplink.zoomer(),
1038 network: network,
Simon Hunt1a5301e2015-02-25 15:31:25 -08001039 portLabelG: function () { return portLabelG; },
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001040 showHosts: function () { return showHosts; }
1041 };
1042 }
1043
Simon Huntf51bf462016-06-29 16:22:57 -07001044 function updateLinksAndNodes() {
1045 updateLinks();
1046 updateNodes();
1047 }
Steven Burrowsec1f45c2016-08-08 16:14:41 +01001048
Simon Hunt737c89f2015-01-28 12:23:19 -08001049 angular.module('ovTopo')
1050 .factory('TopoForceService',
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -07001051 ['$log', '$timeout', 'FnService', 'SvgUtilService',
Simon Hunt86b7c882015-04-02 23:06:08 -07001052 'ThemeService', 'FlashService', 'WebSocketService',
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001053 'TopoOverlayService', 'TopoInstService', 'TopoModelService',
Simon Hunta4242de2015-02-24 17:11:55 -08001054 'TopoD3Service', 'TopoSelectService', 'TopoTrafficService',
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001055 'TopoObliqueService', 'TopoFilterService', 'TopoLinkService',
Simon Hunt737c89f2015-01-28 12:23:19 -08001056
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001057 function (_$log_, _$timeout_, _fs_, _sus_, _ts_, _flash_, _wss_, _tov_,
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001058 _tis_, _tms_, _td3_, _tss_, _tts_, _tos_, _fltr_, _tls_) {
Simon Hunt737c89f2015-01-28 12:23:19 -08001059 $log = _$log_;
Simon Hunt86b7c882015-04-02 23:06:08 -07001060 $timeout = _$timeout_;
Simon Hunt1894d792015-02-04 17:09:20 -08001061 fs = _fs_;
Simon Hunt737c89f2015-01-28 12:23:19 -08001062 sus = _sus_;
Simon Huntac4c6f72015-02-03 19:50:53 -08001063 ts = _ts_;
Simon Hunt5724fb42015-02-05 16:59:40 -08001064 flash = _flash_;
Simon Hunt237676b52015-03-10 19:04:26 -07001065 wss = _wss_;
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001066 tov = _tov_;
Simon Huntac4c6f72015-02-03 19:50:53 -08001067 tis = _tis_;
Simon Hunt3a6eec02015-02-09 21:16:43 -08001068 tms = _tms_;
Simon Hunta4242de2015-02-24 17:11:55 -08001069 td3 = _td3_;
Simon Hunt08f841d02015-02-10 14:39:20 -08001070 tss = _tss_;
Simon Huntf542d842015-02-11 16:20:33 -08001071 tts = _tts_;
Simon Hunt96f88c62015-02-19 17:57:25 -08001072 tos = _tos_;
Simon Hunteb0fa052015-02-17 19:20:28 -08001073 fltr = _fltr_;
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001074 tls = _tls_;
Simon Hunt737c89f2015-01-28 12:23:19 -08001075
Simon Huntf51bf462016-06-29 16:22:57 -07001076 ts.addListener(updateLinksAndNodes);
Simon Hunta142dd22015-02-12 22:07:51 -08001077
Simon Hunt737c89f2015-01-28 12:23:19 -08001078 // forceG is the SVG group to display the force layout in
Simon Huntdc6adea2015-02-09 22:29:36 -08001079 // uplink is the api from the main topo source file
Simon Hunt3a6eec02015-02-09 21:16:43 -08001080 // dim is the initial dimensions of the SVG as [w,h]
Simon Hunt737c89f2015-01-28 12:23:19 -08001081 // opts are, well, optional :)
Simon Hunt3ab20282015-02-26 20:32:19 -08001082 function initForce(_svg_, forceG, _uplink_, _dim_, opts) {
Simon Hunt1894d792015-02-04 17:09:20 -08001083 uplink = _uplink_;
Simon Hunt3a6eec02015-02-09 21:16:43 -08001084 dim = _dim_;
Simon Hunt3ab20282015-02-26 20:32:19 -08001085 svg = _svg_;
1086
1087 lu = network.lookup;
1088 rlk = network.revLinkToKey;
Simon Hunt3a6eec02015-02-09 21:16:43 -08001089
1090 $log.debug('initForce().. dim = ' + dim);
1091
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001092 tov.setApi(mkOverlayApi(), tss);
Simon Huntdc6adea2015-02-09 22:29:36 -08001093 tms.initModel(mkModelApi(uplink), dim);
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001094 td3.initD3(mkD3Api());
1095 tss.initSelect(mkSelectApi());
1096 tts.initTraffic(mkTrafficApi());
Simon Huntc3c5b672015-02-20 11:32:13 -08001097 tos.initOblique(mkObliqueApi(uplink, fltr));
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001098 fltr.initFilter(mkFilterApi());
Simon Hunt9e2104c2015-02-26 10:48:59 -08001099 tls.initLink(mkLinkApi(svg, uplink), td3);
Simon Hunta11b4eb2015-01-28 16:20:50 -08001100
Simon Hunt737c89f2015-01-28 12:23:19 -08001101 settings = angular.extend({}, defaultSettings, opts);
1102
1103 linkG = forceG.append('g').attr('id', 'topo-links');
1104 linkLabelG = forceG.append('g').attr('id', 'topo-linkLabels');
Bri Prebilic Cole80401762015-07-16 11:36:18 -07001105 numLinkLblsG = forceG.append('g').attr('id', 'topo-numLinkLabels');
Simon Hunt737c89f2015-01-28 12:23:19 -08001106 nodeG = forceG.append('g').attr('id', 'topo-nodes');
Simon Hunt1a5301e2015-02-25 15:31:25 -08001107 portLabelG = forceG.append('g').attr('id', 'topo-portLabels');
Simon Hunt737c89f2015-01-28 12:23:19 -08001108
1109 link = linkG.selectAll('.link');
1110 linkLabel = linkLabelG.selectAll('.linkLabel');
1111 node = nodeG.selectAll('.node');
1112
1113 force = d3.layout.force()
Simon Hunt3a6eec02015-02-09 21:16:43 -08001114 .size(dim)
Simon Hunt737c89f2015-01-28 12:23:19 -08001115 .nodes(network.nodes)
1116 .links(network.links)
1117 .gravity(settings.gravity)
1118 .friction(settings.friction)
1119 .charge(settings.charge._def_)
1120 .linkDistance(settings.linkDistance._def_)
1121 .linkStrength(settings.linkStrength._def_)
1122 .on('tick', tick);
1123
1124 drag = sus.createDragBehavior(force,
Simon Hunt08f841d02015-02-10 14:39:20 -08001125 tss.selectObject, atDragEnd, dragEnabled, clickEnabled);
Simon Hunt737c89f2015-01-28 12:23:19 -08001126 }
1127
Simon Hunt3a6eec02015-02-09 21:16:43 -08001128 function newDim(_dim_) {
1129 dim = _dim_;
1130 force.size(dim);
1131 tms.newDim(dim);
Simon Hunt737c89f2015-01-28 12:23:19 -08001132 }
1133
Simon Hunt3a6eec02015-02-09 21:16:43 -08001134 function destroyForce() {
Simon Hunt3ab20282015-02-26 20:32:19 -08001135 force.stop();
1136
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001137 tls.destroyLink();
Simon Hunt96f88c62015-02-19 17:57:25 -08001138 tos.destroyOblique();
Simon Huntf542d842015-02-11 16:20:33 -08001139 tts.destroyTraffic();
1140 tss.destroySelect();
Simon Hunta4242de2015-02-24 17:11:55 -08001141 td3.destroyD3();
Simon Huntf542d842015-02-11 16:20:33 -08001142 tms.destroyModel();
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001143 // note: no need to destroy overlay service
Simon Huntf51bf462016-06-29 16:22:57 -07001144 ts.removeListener(updateLinksAndNodes);
Simon Hunt3ab20282015-02-26 20:32:19 -08001145
1146 // clean up the DOM
1147 svg.selectAll('g').remove();
1148 svg.selectAll('defs').remove();
1149
1150 // clean up internal state
1151 network.nodes = [];
1152 network.links = [];
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -07001153 network.linksByDevice = {};
Simon Hunt3ab20282015-02-26 20:32:19 -08001154 network.lookup = {};
1155 network.revLinkToKey = {};
1156
Bri Prebilic Cole80401762015-07-16 11:36:18 -07001157 linkNums = [];
1158
1159 linkG = linkLabelG = numLinkLblsG = nodeG = portLabelG = null;
Simon Hunt3ab20282015-02-26 20:32:19 -08001160 link = linkLabel = node = null;
1161 force = drag = null;
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -07001162
1163 // clean up $timeout promises
Simon Hunta17fa672015-08-19 18:42:22 -07001164 if (fTimer) {
1165 $timeout.cancel(fTimer);
1166 }
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -07001167 if (fNodesTimer) {
1168 $timeout.cancel(fNodesTimer);
1169 }
1170 if (fLinksTimer) {
1171 $timeout.cancel(fLinksTimer);
1172 }
Simon Hunt3a6eec02015-02-09 21:16:43 -08001173 }
1174
Simon Hunt737c89f2015-01-28 12:23:19 -08001175 return {
1176 initForce: initForce,
Simon Hunt3a6eec02015-02-09 21:16:43 -08001177 newDim: newDim,
1178 destroyForce: destroyForce,
Simon Huntac4c6f72015-02-03 19:50:53 -08001179
Simon Hunta4242de2015-02-24 17:11:55 -08001180 updateDeviceColors: td3.updateDeviceColors,
Simon Hunt5724fb42015-02-05 16:59:40 -08001181 toggleHosts: toggleHosts,
Simon Hunt9e2104c2015-02-26 10:48:59 -08001182 togglePorts: tls.togglePorts,
Simon Hunt5724fb42015-02-05 16:59:40 -08001183 toggleOffline: toggleOffline,
1184 cycleDeviceLabels: cycleDeviceLabels,
Simon Hunt445e8152015-02-06 13:00:12 -08001185 unpin: unpin,
Simon Hunta142dd22015-02-12 22:07:51 -08001186 showMastership: showMastership,
Simon Hunt86b7c882015-04-02 23:06:08 -07001187 showBadLinks: showBadLinks,
Simon Huntac4c6f72015-02-03 19:50:53 -08001188
Simon Huntfd7106c2016-02-09 15:05:26 -08001189 resetAllLocations: resetAllLocations,
Simon Huntac4c6f72015-02-03 19:50:53 -08001190 addDevice: addDevice,
Simon Hunt1894d792015-02-04 17:09:20 -08001191 updateDevice: updateDevice,
1192 removeDevice: removeDevice,
1193 addHost: addHost,
1194 updateHost: updateHost,
Simon Hunt95d56fd2015-11-12 11:06:44 -08001195 moveHost: moveHost,
Simon Hunt1894d792015-02-04 17:09:20 -08001196 removeHost: removeHost,
1197 addLink: addLink,
1198 updateLink: updateLink,
Simon Hunt4a6b54b2015-10-27 22:08:25 -07001199 removeLink: removeLink,
1200 topoStartDone: topoStartDone
Simon Hunt737c89f2015-01-28 12:23:19 -08001201 };
1202 }]);
1203}());