blob: 6f77bede3aee2bf432c19b87d761c1ace26811f1 [file] [log] [blame]
Simon Hunt737c89f2015-01-28 12:23:19 -08001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
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,
Andrea Campanella732ea832017-02-06 09:25:59 -080027 tis, tms, td3, tss, tts, tos, fltr, tls, uplink, svg, tpis;
Simon Huntac4c6f72015-02-03 19:50:53 -080028
Simon Hunte2d9dc72017-08-10 15:21:04 -070029 // function to be replaced by the localization bundle function
30 var topoLion = function (x) {
31 return '#tfs#' + x + '#';
32 };
33
Simon Huntac4c6f72015-02-03 19:50:53 -080034 // configuration
Simon Hunt1894d792015-02-04 17:09:20 -080035 var linkConfig = {
36 light: {
Simon Huntf44d7262016-06-14 14:46:56 -070037 baseColor: '#939598',
Simon Hunt1894d792015-02-04 17:09:20 -080038 inColor: '#66f',
Steven Burrows1c2a9682017-07-14 16:52:46 +010039 outColor: '#f00',
Simon Hunt1894d792015-02-04 17:09:20 -080040 },
41 dark: {
Simon Huntf44d7262016-06-14 14:46:56 -070042 // TODO : theme
43 baseColor: '#939598',
Simon Hunt1894d792015-02-04 17:09:20 -080044 inColor: '#66f',
Steven Burrows1c2a9682017-07-14 16:52:46 +010045 outColor: '#f00',
Simon Hunt1894d792015-02-04 17:09:20 -080046 },
47 inWidth: 12,
Steven Burrows1c2a9682017-07-14 16:52:46 +010048 outWidth: 10,
Simon Hunt1894d792015-02-04 17:09:20 -080049 };
50
Simon Hunt737c89f2015-01-28 12:23:19 -080051 // internal state
Steven Burrows1c2a9682017-07-14 16:52:46 +010052 var settings, // merged default settings and options
53 force, // force layout object
54 drag, // drag behavior handler
Simon Hunt737c89f2015-01-28 12:23:19 -080055 network = {
56 nodes: [],
57 links: [],
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -070058 linksByDevice: {},
Simon Hunt737c89f2015-01-28 12:23:19 -080059 lookup: {},
Steven Burrows1c2a9682017-07-14 16:52:46 +010060 revLinkToKey: {},
Simon Huntac4c6f72015-02-03 19:50:53 -080061 },
Steven Burrows1c2a9682017-07-14 16:52:46 +010062 lu, // shorthand for lookup
63 rlk, // shorthand for revLinktoKey
64 showHosts = false, // whether hosts are displayed
65 showOffline = true, // whether offline devices are displayed
66 nodeLock = false, // whether nodes can be dragged or not (locked)
67 fTimer, // timer for delayed force layout
68 fNodesTimer, // timer for delayed nodes update
69 fLinksTimer, // timer for delayed links update
70 dim, // the dimensions of the force layout [w,h]
71 linkNums = [], // array of link number labels
72 devIconDim = 36, // node target dimension
73 devIconDimMin = 20, // node minimum dimension when zoomed out
74 devIconDimMax = 40, // node maximum dimension when zoomed in
Steven Burrowsf17f0ab2017-04-11 11:03:58 -070075 portLabelDim = 30;
Simon Hunt737c89f2015-01-28 12:23:19 -080076
77 // SVG elements;
Bri Prebilic Cole80401762015-07-16 11:36:18 -070078 var linkG, linkLabelG, numLinkLblsG, portLabelG, nodeG;
Simon Hunt737c89f2015-01-28 12:23:19 -080079
80 // D3 selections;
81 var link, linkLabel, node;
82
83 // default settings for force layout
84 var defaultSettings = {
85 gravity: 0.4,
86 friction: 0.7,
87 charge: {
88 // note: key is node.class
89 device: -8000,
90 host: -5000,
Steven Burrows1c2a9682017-07-14 16:52:46 +010091 _def_: -12000,
Simon Hunt737c89f2015-01-28 12:23:19 -080092 },
93 linkDistance: {
94 // note: key is link.type
95 direct: 100,
96 optical: 120,
97 hostLink: 3,
Steven Burrows1c2a9682017-07-14 16:52:46 +010098 _def_: 50,
Simon Hunt737c89f2015-01-28 12:23:19 -080099 },
100 linkStrength: {
101 // note: key is link.type
102 // range: {0.0 ... 1.0}
Steven Burrows1c2a9682017-07-14 16:52:46 +0100103 // direct: 1.0,
104 // optical: 1.0,
105 // hostLink: 1.0,
106 _def_: 1.0,
107 },
Simon Hunt737c89f2015-01-28 12:23:19 -0800108 };
109
110
Simon Huntac4c6f72015-02-03 19:50:53 -0800111 // ==========================
112 // === EVENT HANDLERS
113
114 function addDevice(data) {
115 var id = data.id,
116 d;
117
Simon Hunt1894d792015-02-04 17:09:20 -0800118 uplink.showNoDevs(false);
Simon Huntac4c6f72015-02-03 19:50:53 -0800119
120 // although this is an add device event, if we already have the
121 // device, treat it as an update instead..
Simon Hunt1894d792015-02-04 17:09:20 -0800122 if (lu[id]) {
Simon Huntac4c6f72015-02-03 19:50:53 -0800123 updateDevice(data);
124 return;
125 }
126
Simon Hunt3a6eec02015-02-09 21:16:43 -0800127 d = tms.createDeviceNode(data);
Simon Huntac4c6f72015-02-03 19:50:53 -0800128 network.nodes.push(d);
Simon Hunt1894d792015-02-04 17:09:20 -0800129 lu[id] = d;
Simon Huntac4c6f72015-02-03 19:50:53 -0800130 updateNodes();
Simon Hunta17fa672015-08-19 18:42:22 -0700131 fStart();
Simon Huntac4c6f72015-02-03 19:50:53 -0800132 }
133
134 function updateDevice(data) {
135 var id = data.id,
Simon Hunt1894d792015-02-04 17:09:20 -0800136 d = lu[id],
Simon Huntac4c6f72015-02-03 19:50:53 -0800137 wasOnline;
138
139 if (d) {
140 wasOnline = d.online;
141 angular.extend(d, data);
Simon Hunt3a6eec02015-02-09 21:16:43 -0800142 if (tms.positionNode(d, true)) {
Simon Hunt445e8152015-02-06 13:00:12 -0800143 sendUpdateMeta(d);
Simon Huntac4c6f72015-02-03 19:50:53 -0800144 }
145 updateNodes();
146 if (wasOnline !== d.online) {
Simon Huntdc6adea2015-02-09 22:29:36 -0800147 tms.findAttachedLinks(d.id).forEach(restyleLinkElement);
Simon Hunt5724fb42015-02-05 16:59:40 -0800148 updateOfflineVisibility(d);
Simon Huntac4c6f72015-02-03 19:50:53 -0800149 }
Simon Huntac4c6f72015-02-03 19:50:53 -0800150 }
151 }
152
Simon Hunt1894d792015-02-04 17:09:20 -0800153 function removeDevice(data) {
154 var id = data.id,
155 d = lu[id];
156 if (d) {
157 removeDeviceElement(d);
Simon Hunt1894d792015-02-04 17:09:20 -0800158 }
159 }
160
161 function addHost(data) {
162 var id = data.id,
Simon Hunt12c79ed2017-09-12 11:58:44 -0700163 d;
Simon Hunt1894d792015-02-04 17:09:20 -0800164
165 // although this is an add host event, if we already have the
166 // host, treat it as an update instead..
167 if (lu[id]) {
168 updateHost(data);
169 return;
170 }
171
Simon Hunt3a6eec02015-02-09 21:16:43 -0800172 d = tms.createHostNode(data);
Simon Hunt1894d792015-02-04 17:09:20 -0800173 network.nodes.push(d);
174 lu[id] = d;
Simon Hunt1894d792015-02-04 17:09:20 -0800175 updateNodes();
176
Simon Hunt12c79ed2017-09-12 11:58:44 -0700177 // need to handle possible multiple links (multi-homed host)
Simon Hunt7df764f2017-09-14 21:26:14 -0700178 createHostLinks(data.allCps, d);
Simon Hunt12c79ed2017-09-12 11:58:44 -0700179
180 if (d.links.length) {
Simon Hunt1894d792015-02-04 17:09:20 -0800181 updateLinks();
182 }
Simon Hunta17fa672015-08-19 18:42:22 -0700183 fStart();
Simon Hunt1894d792015-02-04 17:09:20 -0800184 }
185
186 function updateHost(data) {
187 var id = data.id,
188 d = lu[id];
189 if (d) {
190 angular.extend(d, data);
Simon Hunt3a6eec02015-02-09 21:16:43 -0800191 if (tms.positionNode(d, true)) {
Simon Hunt445e8152015-02-06 13:00:12 -0800192 sendUpdateMeta(d);
Simon Hunt1894d792015-02-04 17:09:20 -0800193 }
194 updateNodes();
Simon Hunt1894d792015-02-04 17:09:20 -0800195 }
196 }
197
Simon Hunt7df764f2017-09-14 21:26:14 -0700198 function createHostLinks(cps, model) {
199 model.links = [];
200 cps.forEach(function (cp) {
201 var linkData = {
202 key: model.id + '/0-' + cp.device + '/' + cp.port,
203 dst: cp.device,
204 dstPort: cp.port,
205 };
206 model.links.push(linkData);
207
208 var lnk = tms.createHostLink(model.id, cp.device, cp.port);
209 if (lnk) {
210 network.links.push(lnk);
211 lu[linkData.key] = lnk;
212 }
213 });
214 }
215
Simon Hunt95d56fd2015-11-12 11:06:44 -0800216 function moveHost(data) {
217 var id = data.id,
Simon Hunt7df764f2017-09-14 21:26:14 -0700218 d = lu[id];
Simon Hunt12c79ed2017-09-12 11:58:44 -0700219
Simon Hunt95d56fd2015-11-12 11:06:44 -0800220 if (d) {
Simon Hunt7df764f2017-09-14 21:26:14 -0700221 removeAllLinkElements(d.links);
Simon Hunt95d56fd2015-11-12 11:06:44 -0800222
223 // merge new data
224 angular.extend(d, data);
225 if (tms.positionNode(d, true)) {
226 sendUpdateMeta(d);
227 }
228
Simon Hunt7df764f2017-09-14 21:26:14 -0700229 // now create new host link(s)
230 createHostLinks(data.allCps, d);
Simon Hunt95d56fd2015-11-12 11:06:44 -0800231
232 updateNodes();
233 updateLinks();
234 fResume();
235 }
236 }
237
Simon Hunt1894d792015-02-04 17:09:20 -0800238 function removeHost(data) {
239 var id = data.id,
240 d = lu[id];
241 if (d) {
242 removeHostElement(d, true);
Simon Hunt1894d792015-02-04 17:09:20 -0800243 }
244 }
245
246 function addLink(data) {
Simon Huntdc6adea2015-02-09 22:29:36 -0800247 var result = tms.findLink(data, 'add'),
Simon Hunt1894d792015-02-04 17:09:20 -0800248 bad = result.badLogic,
249 d = result.ldata;
250
251 if (bad) {
Simon Hunteb18f522016-01-28 19:22:23 -0800252 $log.debug(bad + ': ' + link.id);
Simon Hunt1894d792015-02-04 17:09:20 -0800253 return;
254 }
255
256 if (d) {
257 // we already have a backing store link for src/dst nodes
258 addLinkUpdate(d, data);
259 return;
260 }
261
262 // no backing store link yet
Simon Hunt3a6eec02015-02-09 21:16:43 -0800263 d = tms.createLink(data);
Simon Hunt1894d792015-02-04 17:09:20 -0800264 if (d) {
265 network.links.push(d);
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700266 aggregateLink(d, data);
Simon Hunt1894d792015-02-04 17:09:20 -0800267 lu[d.key] = d;
268 updateLinks();
Simon Hunta17fa672015-08-19 18:42:22 -0700269 fStart();
Simon Hunt1894d792015-02-04 17:09:20 -0800270 }
271 }
272
273 function updateLink(data) {
Simon Huntdc6adea2015-02-09 22:29:36 -0800274 var result = tms.findLink(data, 'update'),
Simon Hunt1894d792015-02-04 17:09:20 -0800275 bad = result.badLogic;
276 if (bad) {
Simon Hunteb18f522016-01-28 19:22:23 -0800277 $log.debug(bad + ': ' + link.id);
Simon Hunt1894d792015-02-04 17:09:20 -0800278 return;
279 }
Simon Hunteb18f522016-01-28 19:22:23 -0800280 result.updateWith(data);
Simon Hunt1894d792015-02-04 17:09:20 -0800281 }
282
283 function removeLink(data) {
Simon Hunta4242de2015-02-24 17:11:55 -0800284 var result = tms.findLink(data, 'remove');
285
286 if (!result.badLogic) {
287 result.removeRawLink();
Simon Hunt1894d792015-02-04 17:09:20 -0800288 }
Simon Hunt1894d792015-02-04 17:09:20 -0800289 }
290
Simon Hunt4a6b54b2015-10-27 22:08:25 -0700291 function topoStartDone(data) {
292 // called when the initial barrage of data has been sent from server
293 uplink.topoStartDone();
294 }
295
Simon Hunt1894d792015-02-04 17:09:20 -0800296 // ========================
297
Simon Hunt94f7dae2015-08-26 17:40:59 -0700298 function nodeById(id) {
299 return lu[id];
300 }
301
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700302 function makeNodeKey(node1, node2) {
303 return node1 + '-' + node2;
304 }
305
306 function findNodePair(key, keyRev) {
307 if (network.linksByDevice[key]) {
308 return key;
309 } else if (network.linksByDevice[keyRev]) {
310 return keyRev;
311 } else {
312 return false;
313 }
314 }
315
316 function aggregateLink(ldata, link) {
317 var key = makeNodeKey(link.src, link.dst),
318 keyRev = makeNodeKey(link.dst, link.src),
319 found = findNodePair(key, keyRev);
320
321 if (found) {
322 network.linksByDevice[found].push(ldata);
323 ldata.devicePair = found;
324 } else {
Steven Burrows1c2a9682017-07-14 16:52:46 +0100325 network.linksByDevice[key] = [ldata];
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700326 ldata.devicePair = key;
327 }
328 }
329
Simon Hunt1894d792015-02-04 17:09:20 -0800330 function addLinkUpdate(ldata, link) {
331 // add link event, but we already have the reverse link installed
332 ldata.fromTarget = link;
Simon Huntdc6adea2015-02-09 22:29:36 -0800333 rlk[link.id] = ldata.key;
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700334 // possible solution to el being undefined in restyleLinkElement:
Steven Burrows1c2a9682017-07-14 16:52:46 +0100335 // _updateLinks();
Simon Hunt1894d792015-02-04 17:09:20 -0800336 restyleLinkElement(ldata);
337 }
338
Simon Hunt1894d792015-02-04 17:09:20 -0800339
340 var widthRatio = 1.4,
341 linkScale = d3.scale.linear()
342 .domain([1, 12])
343 .range([widthRatio, 12 * widthRatio])
Simon Hunt5724fb42015-02-05 16:59:40 -0800344 .clamp(true),
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800345 allLinkTypes = 'direct indirect optical tunnel',
346 allLinkSubTypes = 'inactive not-permitted';
Simon Hunt1894d792015-02-04 17:09:20 -0800347
Simon Hunta142dd22015-02-12 22:07:51 -0800348 function restyleLinkElement(ldata, immediate) {
Simon Hunt1894d792015-02-04 17:09:20 -0800349 // this fn's job is to look at raw links and decide what svg classes
350 // need to be applied to the line element in the DOM
351 var th = ts.theme(),
352 el = ldata.el,
353 type = ldata.type(),
354 lw = ldata.linkWidth(),
Simon Hunta142dd22015-02-12 22:07:51 -0800355 online = ldata.online(),
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800356 modeCls = ldata.expected() ? 'inactive' : 'not-permitted',
Simon Hunta142dd22015-02-12 22:07:51 -0800357 delay = immediate ? 0 : 1000;
Simon Hunt1894d792015-02-04 17:09:20 -0800358
Simon Huntf44d7262016-06-14 14:46:56 -0700359 // NOTE: understand why el is sometimes undefined on addLink events...
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700360 // Investigated:
361 // el is undefined when it's a reverse link that is being added.
362 // updateLinks (which sets ldata.el) isn't called before this is called.
363 // Calling _updateLinks in addLinkUpdate fixes it, but there might be
364 // a more efficient way to fix it.
365 if (el && !el.empty()) {
Thomas Vachuskacb5016f2015-05-18 14:11:43 -0700366 el.classed('link', true);
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800367 el.classed(allLinkSubTypes, false);
368 el.classed(modeCls, !online);
Thomas Vachuskacb5016f2015-05-18 14:11:43 -0700369 el.classed(allLinkTypes, false);
370 if (type) {
371 el.classed(type, true);
372 }
373 el.transition()
374 .duration(delay)
375 .attr('stroke-width', linkScale(lw))
376 .attr('stroke', linkConfig[th].baseColor);
Simon Hunt1894d792015-02-04 17:09:20 -0800377 }
Simon Hunt1894d792015-02-04 17:09:20 -0800378 }
379
Simon Hunt7df764f2017-09-14 21:26:14 -0700380 function removeAllLinkElements(links) {
381 links.forEach(function (lnk) {
382 removeLinkElement(lnk);
383 });
384 }
385
Simon Hunt1894d792015-02-04 17:09:20 -0800386 function removeLinkElement(d) {
387 var idx = fs.find(d.key, network.links, 'key'),
388 removed;
389 if (idx >=0) {
390 // remove from links array
391 removed = network.links.splice(idx, 1);
392 // remove from lookup cache
393 delete lu[removed[0].key];
394 updateLinks();
Simon Hunta17fa672015-08-19 18:42:22 -0700395 fResume();
Simon Hunt1894d792015-02-04 17:09:20 -0800396 }
397 }
398
399 function removeHostElement(d, upd) {
Simon Hunt1fb00552017-09-15 09:21:14 -0700400 // first, remove associated hostLink(s)...
401 removeAllLinkElements(d.links);
Simon Hunt1894d792015-02-04 17:09:20 -0800402
403 // remove from lookup cache
404 delete lu[d.id];
405 // remove from nodes array
406 var idx = fs.find(d.id, network.nodes);
407 network.nodes.splice(idx, 1);
408
409 // remove from SVG
410 // NOTE: upd is false if we were called from removeDeviceElement()
411 if (upd) {
412 updateNodes();
Simon Hunta17fa672015-08-19 18:42:22 -0700413 fResume();
Simon Hunt1894d792015-02-04 17:09:20 -0800414 }
415 }
416
417 function removeDeviceElement(d) {
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700418 var id = d.id,
419 idx;
Simon Hunt1894d792015-02-04 17:09:20 -0800420 // first, remove associated hosts and links..
Simon Huntdc6adea2015-02-09 22:29:36 -0800421 tms.findAttachedHosts(id).forEach(removeHostElement);
422 tms.findAttachedLinks(id).forEach(removeLinkElement);
Simon Hunt1894d792015-02-04 17:09:20 -0800423
424 // remove from lookup cache
425 delete lu[id];
426 // remove from nodes array
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700427 idx = fs.find(id, network.nodes);
428 if (idx > -1) {
429 network.nodes.splice(idx, 1);
430 }
Simon Hunt1894d792015-02-04 17:09:20 -0800431
432 if (!network.nodes.length) {
Simon Huntdc6adea2015-02-09 22:29:36 -0800433 uplink.showNoDevs(true);
Simon Hunt1894d792015-02-04 17:09:20 -0800434 }
435
436 // remove from SVG
437 updateNodes();
Simon Hunta17fa672015-08-19 18:42:22 -0700438 fResume();
Simon Hunt1894d792015-02-04 17:09:20 -0800439 }
440
Simon Hunt5724fb42015-02-05 16:59:40 -0800441 function updateHostVisibility() {
Simon Hunt18bf9822015-02-12 17:35:45 -0800442 sus.visible(nodeG.selectAll('.host'), showHosts);
443 sus.visible(linkG.selectAll('.hostLink'), showHosts);
Simon Hunt8eb4d3a2015-02-23 18:23:29 -0800444 sus.visible(linkLabelG.selectAll('.hostLinkLabel'), showHosts);
Simon Hunt5724fb42015-02-05 16:59:40 -0800445 }
446
447 function updateOfflineVisibility(dev) {
448 function updDev(d, show) {
Simon Hunt8eb4d3a2015-02-23 18:23:29 -0800449 var b;
Simon Hunt18bf9822015-02-12 17:35:45 -0800450 sus.visible(d.el, show);
Simon Hunt5724fb42015-02-05 16:59:40 -0800451
Simon Huntdc6adea2015-02-09 22:29:36 -0800452 tms.findAttachedLinks(d.id).forEach(function (link) {
Simon Hunt5724fb42015-02-05 16:59:40 -0800453 b = show && ((link.type() !== 'hostLink') || showHosts);
Simon Hunt18bf9822015-02-12 17:35:45 -0800454 sus.visible(link.el, b);
Simon Hunt5724fb42015-02-05 16:59:40 -0800455 });
Simon Huntdc6adea2015-02-09 22:29:36 -0800456 tms.findAttachedHosts(d.id).forEach(function (host) {
Simon Hunt5724fb42015-02-05 16:59:40 -0800457 b = show && showHosts;
Simon Hunt18bf9822015-02-12 17:35:45 -0800458 sus.visible(host.el, b);
Simon Hunt5724fb42015-02-05 16:59:40 -0800459 });
460 }
461
462 if (dev) {
463 // updating a specific device that just toggled off/on-line
464 updDev(dev, dev.online || showOffline);
465 } else {
466 // updating all offline devices
Simon Huntdc6adea2015-02-09 22:29:36 -0800467 tms.findDevices(true).forEach(function (d) {
Simon Hunt5724fb42015-02-05 16:59:40 -0800468 updDev(d, showOffline);
469 });
470 }
471 }
472
Simon Hunt1894d792015-02-04 17:09:20 -0800473
Simon Hunt445e8152015-02-06 13:00:12 -0800474 function sendUpdateMeta(d, clearPos) {
Simon Huntac4c6f72015-02-03 19:50:53 -0800475 var metaUi = {},
476 ll;
477
Simon Hunt445e8152015-02-06 13:00:12 -0800478 // if we are not clearing the position data (unpinning),
Simon Huntfd7106c2016-02-09 15:05:26 -0800479 // attach the x, y, (and equivalent longitude, latitude)...
Simon Hunt445e8152015-02-06 13:00:12 -0800480 if (!clearPos) {
Simon Hunt3a6eec02015-02-09 21:16:43 -0800481 ll = tms.lngLatFromCoord([d.x, d.y]);
Simon Huntfd7106c2016-02-09 15:05:26 -0800482 metaUi = {
483 x: d.x,
484 y: d.y,
485 equivLoc: {
486 lng: ll[0],
Steven Burrows1c2a9682017-07-14 16:52:46 +0100487 lat: ll[1],
488 },
Simon Huntfd7106c2016-02-09 15:05:26 -0800489 };
Simon Hunt1894d792015-02-04 17:09:20 -0800490 }
491 d.metaUi = metaUi;
Simon Hunt237676b52015-03-10 19:04:26 -0700492 wss.sendEvent('updateMeta', {
Simon Hunt1894d792015-02-04 17:09:20 -0800493 id: d.id,
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700494 class: d.class,
Steven Burrows1c2a9682017-07-14 16:52:46 +0100495 memento: metaUi,
Simon Hunt1894d792015-02-04 17:09:20 -0800496 });
Simon Huntac4c6f72015-02-03 19:50:53 -0800497 }
498
Simon Hunt1894d792015-02-04 17:09:20 -0800499
Simon Huntac4c6f72015-02-03 19:50:53 -0800500 function mkSvgClass(d) {
501 return d.fixed ? d.svgClass + ' fixed' : d.svgClass;
502 }
503
Simon Hunt5724fb42015-02-05 16:59:40 -0800504 function vis(b) {
Simon Hunt1603c692017-08-10 19:53:35 -0700505 return topoLion(b ? 'visible' : 'hidden');
Simon Hunt5724fb42015-02-05 16:59:40 -0800506 }
507
Simon Huntfcbde892015-04-16 12:05:28 -0700508 function toggleHosts(x) {
509 var kev = (x === 'keyev'),
510 on = kev ? !showHosts : !!x;
511
512 showHosts = on;
Simon Hunt5724fb42015-02-05 16:59:40 -0800513 updateHostVisibility();
Simon Hunte2d9dc72017-08-10 15:21:04 -0700514 flash.flash(topoLion('hosts') + ' ' + vis(on));
Simon Huntfcbde892015-04-16 12:05:28 -0700515 return on;
Simon Hunt5724fb42015-02-05 16:59:40 -0800516 }
517
Simon Huntfcbde892015-04-16 12:05:28 -0700518 function toggleOffline(x) {
519 var kev = (x === 'keyev'),
520 on = kev ? !showOffline : !!x;
521
522 showOffline = on;
Simon Hunt5724fb42015-02-05 16:59:40 -0800523 updateOfflineVisibility();
Simon Hunte2d9dc72017-08-10 15:21:04 -0700524 flash.flash(topoLion('fl_offline_devices') + ' ' + vis(on));
Simon Huntfcbde892015-04-16 12:05:28 -0700525 return on;
Simon Hunt5724fb42015-02-05 16:59:40 -0800526 }
527
528 function cycleDeviceLabels() {
Bri Prebilic Cole9cf1a8d2015-04-21 13:15:29 -0700529 flash.flash(td3.incDevLabIndex());
Simon Huntdc6adea2015-02-09 22:29:36 -0800530 tms.findDevices().forEach(function (d) {
Simon Hunta4242de2015-02-24 17:11:55 -0800531 td3.updateDeviceLabel(d);
Simon Hunt1c367112015-02-05 18:02:46 -0800532 });
Simon Hunt5724fb42015-02-05 16:59:40 -0800533 }
534
Simon Hunt10618f62017-06-15 19:30:52 -0700535 function cycleHostLabels() {
536 flash.flash(td3.incHostLabIndex());
537 tms.findHosts().forEach(function (d) {
538 td3.updateHostLabel(d);
539 });
540 }
541
Simon Hunt445e8152015-02-06 13:00:12 -0800542 function unpin() {
Simon Hunt08f841d02015-02-10 14:39:20 -0800543 var hov = tss.hovered();
544 if (hov) {
545 sendUpdateMeta(hov, true);
546 hov.fixed = false;
547 hov.el.classed('fixed', false);
Simon Hunt445e8152015-02-06 13:00:12 -0800548 fResume();
549 }
550 }
551
Simon Hunta142dd22015-02-12 22:07:51 -0800552 function showMastership(masterId) {
553 if (!masterId) {
554 restoreLayerState();
555 } else {
556 showMastershipFor(masterId);
557 }
558 }
559
560 function restoreLayerState() {
561 // NOTE: this level of indirection required, for when we have
562 // the layer filter functionality re-implemented
563 suppressLayers(false);
564 }
565
566 function showMastershipFor(id) {
567 suppressLayers(true);
568 node.each(function (n) {
569 if (n.master === id) {
Simon Hunt743a8492015-08-25 16:18:19 -0700570 n.el.classed('suppressedmax', false);
Simon Hunta142dd22015-02-12 22:07:51 -0800571 }
572 });
573 }
574
Simon Hunt743a8492015-08-25 16:18:19 -0700575 function supAmt(less) {
Steven Burrows1c2a9682017-07-14 16:52:46 +0100576 return less ? 'suppressed' : 'suppressedmax';
Simon Hunt743a8492015-08-25 16:18:19 -0700577 }
578
579 function suppressLayers(b, less) {
580 var cls = supAmt(less);
581 node.classed(cls, b);
582 link.classed(cls, b);
583 }
584
585 function unsuppressNode(id, less) {
586 var cls = supAmt(less);
587 node.each(function (n) {
588 if (n.id === id) {
589 n.el.classed(cls, false);
590 }
591 });
592 }
593
Simon Hunt94f7dae2015-08-26 17:40:59 -0700594 function unsuppressLink(key, less) {
Simon Hunt743a8492015-08-25 16:18:19 -0700595 var cls = supAmt(less);
596 link.each(function (n) {
Simon Hunt94f7dae2015-08-26 17:40:59 -0700597 if (n.key === key) {
Simon Hunt743a8492015-08-25 16:18:19 -0700598 n.el.classed(cls, false);
599 }
600 });
Simon Hunta142dd22015-02-12 22:07:51 -0800601 }
Simon Hunt445e8152015-02-06 13:00:12 -0800602
Simon Hunt86b7c882015-04-02 23:06:08 -0700603 function showBadLinks() {
604 var badLinks = tms.findBadLinks();
Simon Hunte2d9dc72017-08-10 15:21:04 -0700605 flash.flash(topoLion('fl_bad_links') + ': ' + badLinks.length);
Simon Hunt86b7c882015-04-02 23:06:08 -0700606 $log.debug('Bad Link List (' + badLinks.length + '):');
607 badLinks.forEach(function (d) {
608 $log.debug('bad link: (' + d.bad + ') ' + d.key, d);
609 if (d.el) {
610 d.el.attr('stroke-width', linkScale(2.8))
611 .attr('stroke', 'red');
612 }
613 });
614 // back to normal after 2 seconds...
615 $timeout(updateLinks, 2000);
616 }
617
Steven Burrowsf17f0ab2017-04-11 11:03:58 -0700618 function deviceScale() {
619 var scale = uplink.zoomer().scale(),
620 dim = devIconDim,
621 multiplier = 1;
622
623 if (dim * scale < devIconDimMin) {
624 multiplier = devIconDimMin / (dim * scale);
625 } else if (dim * scale > devIconDimMax) {
626 multiplier = devIconDimMax / (dim * scale);
627 }
628
629 return multiplier;
630 }
631
632 function linkWidthScale(scale) {
633 var scale = uplink.zoomer().scale();
634 return linkScale(widthRatio) / scale;
635 }
636
637 function portLabelScale(scale) {
638 var scale = uplink.zoomer().scale();
639 return portLabelDim / (portLabelDim * scale);
640 }
641
642 function setNodeScale(scale) {
643 // Scale the network nodes
644 _.each(network.nodes, function (node) {
645 if (node.class === 'host') {
646 node.el.selectAll('g').style('transform', 'scale(' + deviceScale(scale) + ')');
647 node.el.selectAll('text').style('transform', 'scale(' + deviceScale(scale) + ')');
648 return;
649 }
650 node.el.selectAll('*')
651 .style('transform', 'scale(' + deviceScale(scale) + ')');
652 });
653
654 // Scale the network links
655 _.each(network.links, function (link) {
656 link.el.style('stroke-width', linkWidthScale(scale) + 'px');
657 });
658
659 d3.select('#topo-portLabels')
660 .selectAll('.portLabel')
661 .selectAll('*')
662 .style('transform', 'scale(' + portLabelScale(scale) + ')');
663 }
664
Simon Huntfd7106c2016-02-09 15:05:26 -0800665 function resetAllLocations() {
666 tms.resetAllLocations();
667 updateNodes();
668 tick(); // force nodes to be redrawn in their new locations
Simon Hunte2d9dc72017-08-10 15:21:04 -0700669 flash.flash(topoLion('fl_reset_node_locations'));
Simon Huntfd7106c2016-02-09 15:05:26 -0800670 }
671
Simon Hunt5724fb42015-02-05 16:59:40 -0800672 // ==========================================
673
Simon Huntac4c6f72015-02-03 19:50:53 -0800674 function updateNodes() {
Thomas Vachuska1a989c12015-06-09 18:29:22 -0700675 if (fNodesTimer) {
676 $timeout.cancel(fNodesTimer);
677 }
678 fNodesTimer = $timeout(_updateNodes, 150);
679 }
680
Simon Hunta17fa672015-08-19 18:42:22 -0700681 // IMPLEMENTATION NOTE: _updateNodes() should NOT stop, start, or resume
682 // the force layout; that needs to be determined and implemented elsewhere
Thomas Vachuska1a989c12015-06-09 18:29:22 -0700683 function _updateNodes() {
Simon Hunt1894d792015-02-04 17:09:20 -0800684 // select all the nodes in the layout:
Simon Huntac4c6f72015-02-03 19:50:53 -0800685 node = nodeG.selectAll('.node')
686 .data(network.nodes, function (d) { return d.id; });
687
Simon Hunt1894d792015-02-04 17:09:20 -0800688 // operate on existing nodes:
Simon Hunta4242de2015-02-24 17:11:55 -0800689 node.filter('.device').each(td3.deviceExisting);
690 node.filter('.host').each(td3.hostExisting);
Simon Huntac4c6f72015-02-03 19:50:53 -0800691
692 // operate on entering nodes:
693 var entering = node.enter()
694 .append('g')
695 .attr({
696 id: function (d) { return sus.safeId(d.id); },
697 class: mkSvgClass,
Simon Hunta17fa672015-08-19 18:42:22 -0700698 transform: function (d) {
699 // Need to guard against NaN here ??
700 return sus.translate(d.x, d.y);
701 },
Steven Burrows1c2a9682017-07-14 16:52:46 +0100702 opacity: 0,
Simon Huntac4c6f72015-02-03 19:50:53 -0800703 })
704 .call(drag)
Simon Hunt08f841d02015-02-10 14:39:20 -0800705 .on('mouseover', tss.nodeMouseOver)
706 .on('mouseout', tss.nodeMouseOut)
Simon Huntac4c6f72015-02-03 19:50:53 -0800707 .transition()
708 .attr('opacity', 1);
709
Simon Hunt1894d792015-02-04 17:09:20 -0800710 // augment entering nodes:
Simon Hunta4242de2015-02-24 17:11:55 -0800711 entering.filter('.device').each(td3.deviceEnter);
712 entering.filter('.host').each(td3.hostEnter);
Simon Huntac4c6f72015-02-03 19:50:53 -0800713
Simon Hunt51056592015-02-03 21:48:07 -0800714 // operate on both existing and new nodes:
Simon Hunta4242de2015-02-24 17:11:55 -0800715 td3.updateDeviceColors();
Simon Huntac4c6f72015-02-03 19:50:53 -0800716
717 // operate on exiting nodes:
718 // Note that the node is removed after 2 seconds.
719 // Sub element animations should be shorter than 2 seconds.
720 var exiting = node.exit()
721 .transition()
722 .duration(2000)
723 .style('opacity', 0)
724 .remove();
725
Simon Hunt1894d792015-02-04 17:09:20 -0800726 // exiting node specifics:
Simon Hunta4242de2015-02-24 17:11:55 -0800727 exiting.filter('.host').each(td3.hostExit);
728 exiting.filter('.device').each(td3.deviceExit);
Simon Huntac4c6f72015-02-03 19:50:53 -0800729 }
730
Simon Hunt51056592015-02-03 21:48:07 -0800731 // ==========================
Simon Hunt1894d792015-02-04 17:09:20 -0800732
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700733 function getDefaultPos(link) {
734 return {
735 x1: link.source.x,
736 y1: link.source.y,
737 x2: link.target.x,
Steven Burrows1c2a9682017-07-14 16:52:46 +0100738 y2: link.target.y,
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700739 };
740 }
741
742 // returns amount of adjustment along the normal for given link
743 function amt(numLinks, linkIdx) {
744 var gap = 6;
745 return (linkIdx - ((numLinks - 1) / 2)) * gap;
746 }
747
748 function calcMovement(d, amt, flipped) {
749 var pos = getDefaultPos(d),
750 mult = flipped ? -amt : amt,
751 dx = pos.x2 - pos.x1,
752 dy = pos.y2 - pos.y1,
753 length = Math.sqrt((dx * dx) + (dy * dy));
754
755 return {
756 x1: pos.x1 + (mult * dy / length),
757 y1: pos.y1 + (mult * -dx / length),
758 x2: pos.x2 + (mult * dy / length),
Steven Burrows1c2a9682017-07-14 16:52:46 +0100759 y2: pos.y2 + (mult * -dx / length),
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700760 };
761 }
762
763 function calcPosition() {
764 var lines = this,
765 linkSrcId;
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700766 linkNums = [];
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700767 lines.each(function (d) {
768 if (d.type() === 'hostLink') {
769 d.position = getDefaultPos(d);
770 }
771 });
772
773 function normalizeLinkSrc(link) {
774 // ensure source device is consistent across set of links
775 // temporary measure until link modeling is refactored
776 if (!linkSrcId) {
777 linkSrcId = link.source.id;
778 return false;
779 }
780
781 return link.source.id !== linkSrcId;
782 }
783
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700784 angular.forEach(network.linksByDevice, function (linkArr, key) {
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700785 var numLinks = linkArr.length,
786 link;
787
788 if (numLinks === 1) {
789 link = linkArr[0];
790 link.position = getDefaultPos(link);
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700791 link.position.multiLink = false;
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700792 } else if (numLinks >= 5) {
793 // this code is inefficient, in the future the way links
794 // are modeled will be changed
795 angular.forEach(linkArr, function (link) {
796 link.position = getDefaultPos(link);
797 link.position.multiLink = true;
798 });
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700799 linkNums.push({
800 id: key,
801 num: numLinks,
Steven Burrows1c2a9682017-07-14 16:52:46 +0100802 linkCoords: linkArr[0].position,
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700803 });
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700804 } else {
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700805 linkSrcId = null;
806 angular.forEach(linkArr, function (link, index) {
807 var offsetAmt = amt(numLinks, index),
808 needToFlip = normalizeLinkSrc(link);
809 link.position = calcMovement(link, offsetAmt, needToFlip);
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700810 link.position.multiLink = false;
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700811 });
812 }
813 });
814 }
815
Simon Hunt1894d792015-02-04 17:09:20 -0800816 function updateLinks() {
Thomas Vachuska1a989c12015-06-09 18:29:22 -0700817 if (fLinksTimer) {
818 $timeout.cancel(fLinksTimer);
819 }
820 fLinksTimer = $timeout(_updateLinks, 150);
821 }
822
Simon Hunta17fa672015-08-19 18:42:22 -0700823 // IMPLEMENTATION NOTE: _updateLinks() should NOT stop, start, or resume
824 // the force layout; that needs to be determined and implemented elsewhere
Thomas Vachuska1a989c12015-06-09 18:29:22 -0700825 function _updateLinks() {
Simon Hunt1894d792015-02-04 17:09:20 -0800826 var th = ts.theme();
827
828 link = linkG.selectAll('.link')
829 .data(network.links, function (d) { return d.key; });
830
831 // operate on existing links:
Simon Huntd5264122015-02-25 10:17:43 -0800832 link.each(function (d) {
833 // this is supposed to be an existing link, but we have observed
834 // occasions (where links are deleted and added rapidly?) where
835 // the DOM element has not been defined. So protect against that...
836 if (d.el) {
837 restyleLinkElement(d, true);
838 }
839 });
Simon Hunt1894d792015-02-04 17:09:20 -0800840
841 // operate on entering links:
842 var entering = link.enter()
843 .append('line')
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700844 .call(calcPosition)
Simon Hunt1894d792015-02-04 17:09:20 -0800845 .attr({
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700846 x1: function (d) { return d.position.x1; },
847 y1: function (d) { return d.position.y1; },
848 x2: function (d) { return d.position.x2; },
849 y2: function (d) { return d.position.y2; },
Simon Hunt1894d792015-02-04 17:09:20 -0800850 stroke: linkConfig[th].inColor,
Steven Burrows1c2a9682017-07-14 16:52:46 +0100851 'stroke-width': linkConfig.inWidth,
Simon Hunt1894d792015-02-04 17:09:20 -0800852 });
853
854 // augment links
Simon Hunta4242de2015-02-24 17:11:55 -0800855 entering.each(td3.linkEntering);
Simon Hunt1894d792015-02-04 17:09:20 -0800856
857 // operate on both existing and new links:
Steven Burrows1c2a9682017-07-14 16:52:46 +0100858 // link.each(...)
Simon Hunt1894d792015-02-04 17:09:20 -0800859
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700860 // add labels for how many links are in a thick line
861 td3.applyNumLinkLabels(linkNums, numLinkLblsG);
862
Simon Hunt1894d792015-02-04 17:09:20 -0800863 // apply or remove labels
Simon Hunta4242de2015-02-24 17:11:55 -0800864 td3.applyLinkLabels();
Simon Hunt1894d792015-02-04 17:09:20 -0800865
866 // operate on exiting links:
867 link.exit()
868 .attr('stroke-dasharray', '3 3')
Simon Hunt5724fb42015-02-05 16:59:40 -0800869 .attr('stroke', linkConfig[th].outColor)
Simon Hunt1894d792015-02-04 17:09:20 -0800870 .style('opacity', 0.5)
871 .transition()
872 .duration(1500)
873 .attr({
874 'stroke-dasharray': '3 12',
Steven Burrows1c2a9682017-07-14 16:52:46 +0100875 'stroke-width': linkConfig.outWidth,
Simon Hunt1894d792015-02-04 17:09:20 -0800876 })
877 .style('opacity', 0.0)
878 .remove();
Simon Hunt1894d792015-02-04 17:09:20 -0800879 }
880
Simon Huntac4c6f72015-02-03 19:50:53 -0800881
882 // ==========================
Simon Hunt737c89f2015-01-28 12:23:19 -0800883 // force layout tick function
Simon Hunt737c89f2015-01-28 12:23:19 -0800884
Simon Hunt5724fb42015-02-05 16:59:40 -0800885 function fResume() {
Simon Huntc3c5b672015-02-20 11:32:13 -0800886 if (!tos.isOblique()) {
Simon Hunt5724fb42015-02-05 16:59:40 -0800887 force.resume();
888 }
889 }
890
891 function fStart() {
Simon Huntc3c5b672015-02-20 11:32:13 -0800892 if (!tos.isOblique()) {
Simon Hunta17fa672015-08-19 18:42:22 -0700893 if (fTimer) {
894 $timeout.cancel(fTimer);
895 }
896 fTimer = $timeout(function () {
Steven Burrows1c2a9682017-07-14 16:52:46 +0100897 $log.debug('Starting force-layout');
Simon Hunta17fa672015-08-19 18:42:22 -0700898 force.start();
899 }, 200);
Simon Hunt5724fb42015-02-05 16:59:40 -0800900 }
901 }
902
903 var tickStuff = {
904 nodeAttr: {
Simon Hunta17fa672015-08-19 18:42:22 -0700905 transform: function (d) {
906 var dx = isNaN(d.x) ? 0 : d.x,
907 dy = isNaN(d.y) ? 0 : d.y;
908 return sus.translate(dx, dy);
Steven Burrows1c2a9682017-07-14 16:52:46 +0100909 },
Simon Hunt5724fb42015-02-05 16:59:40 -0800910 },
911 linkAttr: {
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700912 x1: function (d) { return d.position.x1; },
913 y1: function (d) { return d.position.y1; },
914 x2: function (d) { return d.position.x2; },
Steven Burrows1c2a9682017-07-14 16:52:46 +0100915 y2: function (d) { return d.position.y2; },
Simon Hunt5724fb42015-02-05 16:59:40 -0800916 },
917 linkLabelAttr: {
918 transform: function (d) {
Simon Huntdc6adea2015-02-09 22:29:36 -0800919 var lnk = tms.findLinkById(d.key);
Simon Hunt5724fb42015-02-05 16:59:40 -0800920 if (lnk) {
Carmelo Casconed01eda62016-08-02 10:19:15 -0700921 return td3.transformLabel(lnk.position, d.key);
Simon Hunt5724fb42015-02-05 16:59:40 -0800922 }
Steven Burrows1c2a9682017-07-14 16:52:46 +0100923 },
924 },
Simon Hunt5724fb42015-02-05 16:59:40 -0800925 };
926
927 function tick() {
Simon Hunt3ab20282015-02-26 20:32:19 -0800928 // guard against null (which can happen when our view pages out)...
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700929 if (node && node.size()) {
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700930 node.attr(tickStuff.nodeAttr);
931 }
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700932 if (link && link.size()) {
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700933 link.call(calcPosition)
934 .attr(tickStuff.linkAttr);
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700935 td3.applyNumLinkLabels(linkNums, numLinkLblsG);
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700936 }
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700937 if (linkLabel && linkLabel.size()) {
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700938 linkLabel.attr(tickStuff.linkLabelAttr);
939 }
Simon Hunt737c89f2015-01-28 12:23:19 -0800940 }
941
942
Simon Huntac4c6f72015-02-03 19:50:53 -0800943 // ==========================
944 // === MOUSE GESTURE HANDLERS
945
Simon Hunt205099e2015-02-07 13:12:01 -0800946 function zoomingOrPanning(ev) {
947 return ev.metaKey || ev.altKey;
Simon Hunt445e8152015-02-06 13:00:12 -0800948 }
949
950 function atDragEnd(d) {
951 // once we've finished moving, pin the node in position
952 d.fixed = true;
953 d3.select(this).classed('fixed', true);
954 sendUpdateMeta(d);
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700955 tss.clickConsumed(true);
Simon Hunt445e8152015-02-06 13:00:12 -0800956 }
957
958 // predicate that indicates when dragging is active
959 function dragEnabled() {
960 var ev = d3.event.sourceEvent;
961 // nodeLock means we aren't allowing nodes to be dragged...
Simon Hunt205099e2015-02-07 13:12:01 -0800962 return !nodeLock && !zoomingOrPanning(ev);
Simon Hunt445e8152015-02-06 13:00:12 -0800963 }
964
965 // predicate that indicates when clicking is active
966 function clickEnabled() {
967 return true;
968 }
Simon Hunt737c89f2015-01-28 12:23:19 -0800969
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700970 // =============================================
971 // function entry points for overlay module
Simon Huntf542d842015-02-11 16:20:33 -0800972
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700973 // TODO: find an automatic way of tracking via the "showHighlights" events
Simon Hunte50829c2015-06-09 08:39:28 -0700974 var allTrafficClasses = 'primary secondary optical animated ' +
Simon Hunt21281fd2017-03-30 22:28:28 -0700975 'port-traffic-green port-traffic-yellow port-traffic-orange ' +
976 'port-traffic-red';
Simon Huntf542d842015-02-11 16:20:33 -0800977
978 function clearLinkTrafficStyle() {
979 link.style('stroke-width', null)
980 .classed(allTrafficClasses, false);
981 }
982
983 function removeLinkLabels() {
984 network.links.forEach(function (d) {
985 d.label = '';
986 });
987 }
Simon Hunt737c89f2015-01-28 12:23:19 -0800988
Simon Hunte9343f32015-10-21 18:07:46 -0700989 function clearNodeDeco() {
990 node.selectAll('g.badge').remove();
991 }
992
993 function removeNodeBadges() {
994 network.nodes.forEach(function (d) {
995 d.badge = null;
996 });
997 }
998
Simon Hunta4242de2015-02-24 17:11:55 -0800999 function updateLinkLabelModel() {
1000 // create the backing data for showing labels..
1001 var data = [];
1002 link.each(function (d) {
1003 if (d.label) {
1004 data.push({
1005 id: 'lab-' + d.key,
1006 key: d.key,
1007 label: d.label,
Steven Burrows1c2a9682017-07-14 16:52:46 +01001008 ldata: d,
Simon Hunta4242de2015-02-24 17:11:55 -08001009 });
1010 }
1011 });
1012
1013 linkLabel = linkLabelG.selectAll('.linkLabel')
1014 .data(data, function (d) { return d.id; });
1015 }
1016
Simon Hunt737c89f2015-01-28 12:23:19 -08001017 // ==========================
Simon Huntac4c6f72015-02-03 19:50:53 -08001018 // Module definition
Simon Hunt737c89f2015-01-28 12:23:19 -08001019
Simon Huntdc6adea2015-02-09 22:29:36 -08001020 function mkModelApi(uplink) {
1021 return {
1022 projection: uplink.projection,
1023 network: network,
1024 restyleLinkElement: restyleLinkElement,
Steven Burrows1c2a9682017-07-14 16:52:46 +01001025 removeLinkElement: removeLinkElement,
Simon Huntdc6adea2015-02-09 22:29:36 -08001026 };
1027 }
1028
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001029 function mkD3Api() {
Simon Hunta4242de2015-02-24 17:11:55 -08001030 return {
1031 node: function () { return node; },
1032 link: function () { return link; },
1033 linkLabel: function () { return linkLabel; },
1034 instVisible: function () { return tis.isVisible(); },
1035 posNode: tms.positionNode,
1036 showHosts: function () { return showHosts; },
1037 restyleLinkElement: restyleLinkElement,
Bri Prebilic Cole80401762015-07-16 11:36:18 -07001038 updateLinkLabelModel: updateLinkLabelModel,
Steven Burrowsf17f0ab2017-04-11 11:03:58 -07001039 linkConfig: function () { return linkConfig; },
1040 deviceScale: deviceScale,
Steven Burrows1c2a9682017-07-14 16:52:46 +01001041 linkWidthScale: linkWidthScale,
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -07001042 };
Simon Hunta4242de2015-02-24 17:11:55 -08001043 }
1044
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001045 function mkSelectApi() {
Simon Hunt08f841d02015-02-10 14:39:20 -08001046 return {
1047 node: function () { return node; },
1048 zoomingOrPanning: zoomingOrPanning,
Simon Hunt0c6b2d32015-03-26 17:46:29 -07001049 updateDeviceColors: td3.updateDeviceColors,
Steven Burrows1c2a9682017-07-14 16:52:46 +01001050 deselectAllLinks: tls.deselectAllLinks,
Simon Hunt08f841d02015-02-10 14:39:20 -08001051 };
1052 }
1053
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001054 function mkTrafficApi() {
1055 return {
1056 hovered: tss.hovered,
1057 somethingSelected: tss.somethingSelected,
Steven Burrows1c2a9682017-07-14 16:52:46 +01001058 selectOrder: tss.selectOrder,
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001059 };
1060 }
1061
1062 function mkOverlayApi() {
Simon Huntf542d842015-02-11 16:20:33 -08001063 return {
Simon Hunte9343f32015-10-21 18:07:46 -07001064 clearNodeDeco: clearNodeDeco,
1065 removeNodeBadges: removeNodeBadges,
Simon Huntf542d842015-02-11 16:20:33 -08001066 clearLinkTrafficStyle: clearLinkTrafficStyle,
1067 removeLinkLabels: removeLinkLabels,
Simon Hunt743a8492015-08-25 16:18:19 -07001068 findLinkById: tms.findLinkById,
Simon Hunt94f7dae2015-08-26 17:40:59 -07001069 findNodeById: nodeById,
Simon Huntf542d842015-02-11 16:20:33 -08001070 updateLinks: updateLinks,
Simon Hunt743a8492015-08-25 16:18:19 -07001071 updateNodes: updateNodes,
1072 supLayers: suppressLayers,
1073 unsupNode: unsuppressNode,
Steven Burrows1c2a9682017-07-14 16:52:46 +01001074 unsupLink: unsuppressLink,
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -07001075 };
Simon Huntf542d842015-02-11 16:20:33 -08001076 }
1077
Simon Huntc3c5b672015-02-20 11:32:13 -08001078 function mkObliqueApi(uplink, fltr) {
Simon Hunt96f88c62015-02-19 17:57:25 -08001079 return {
Steven Burrows1c2a9682017-07-14 16:52:46 +01001080 force: function () { return force; },
Simon Huntc3c5b672015-02-20 11:32:13 -08001081 zoomLayer: uplink.zoomLayer,
Steven Burrows1c2a9682017-07-14 16:52:46 +01001082 nodeGBBox: function () { return nodeG.node().getBBox(); },
Simon Hunt96f88c62015-02-19 17:57:25 -08001083 node: function () { return node; },
Simon Huntc3c5b672015-02-20 11:32:13 -08001084 link: function () { return link; },
1085 linkLabel: function () { return linkLabel; },
1086 nodes: function () { return network.nodes; },
1087 tickStuff: tickStuff,
1088 nodeLock: function (b) {
1089 var old = nodeLock;
1090 nodeLock = b;
1091 return old;
1092 },
1093 opacifyMap: uplink.opacifyMap,
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -07001094 inLayer: fltr.inLayer,
Bri Prebilic Cole80401762015-07-16 11:36:18 -07001095 calcLinkPos: calcPosition,
1096 applyNumLinkLabels: function () {
1097 td3.applyNumLinkLabels(linkNums, numLinkLblsG);
Steven Burrows1c2a9682017-07-14 16:52:46 +01001098 },
Simon Hunt96f88c62015-02-19 17:57:25 -08001099 };
1100 }
1101
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001102 function mkFilterApi() {
Simon Hunteb0fa052015-02-17 19:20:28 -08001103 return {
1104 node: function () { return node; },
Steven Burrows1c2a9682017-07-14 16:52:46 +01001105 link: function () { return link; },
Simon Hunteb0fa052015-02-17 19:20:28 -08001106 };
1107 }
1108
Simon Hunt9e2104c2015-02-26 10:48:59 -08001109 function mkLinkApi(svg, uplink) {
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001110 return {
1111 svg: svg,
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001112 zoomer: uplink.zoomer(),
1113 network: network,
Simon Hunt1a5301e2015-02-25 15:31:25 -08001114 portLabelG: function () { return portLabelG; },
Steven Burrows1c2a9682017-07-14 16:52:46 +01001115 showHosts: function () { return showHosts; },
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001116 };
1117 }
1118
Simon Huntf51bf462016-06-29 16:22:57 -07001119 function updateLinksAndNodes() {
1120 updateLinks();
1121 updateNodes();
1122 }
Steven Burrowsec1f45c2016-08-08 16:14:41 +01001123
Simon Hunte2d9dc72017-08-10 15:21:04 -07001124 // invoked after the localization bundle has been received from the server
1125 function setLionBundle(bundle) {
1126 topoLion = bundle;
1127 td3.setLionBundle(bundle);
1128 fltr.setLionBundle(bundle);
1129 tls.setLionBundle(bundle);
Simon Hunt1603c692017-08-10 19:53:35 -07001130 tos.setLionBundle(bundle);
1131 tov.setLionBundle(bundle);
Simon Huntcaed0412017-08-12 13:49:17 -07001132 tss.setLionBundle(bundle);
Simon Hunte2d9dc72017-08-10 15:21:04 -07001133 }
1134
Simon Hunt737c89f2015-01-28 12:23:19 -08001135 angular.module('ovTopo')
1136 .factory('TopoForceService',
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -07001137 ['$log', '$timeout', 'FnService', 'SvgUtilService',
Simon Hunt86b7c882015-04-02 23:06:08 -07001138 'ThemeService', 'FlashService', 'WebSocketService',
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001139 'TopoOverlayService', 'TopoInstService', 'TopoModelService',
Simon Hunta4242de2015-02-24 17:11:55 -08001140 'TopoD3Service', 'TopoSelectService', 'TopoTrafficService',
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001141 'TopoObliqueService', 'TopoFilterService', 'TopoLinkService',
Andrea Campanella732ea832017-02-06 09:25:59 -08001142 'TopoProtectedIntentsService',
Simon Hunt737c89f2015-01-28 12:23:19 -08001143
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001144 function (_$log_, _$timeout_, _fs_, _sus_, _ts_, _flash_, _wss_, _tov_,
Andrea Campanella732ea832017-02-06 09:25:59 -08001145 _tis_, _tms_, _td3_, _tss_, _tts_, _tos_, _fltr_, _tls_, _tpis_) {
Simon Hunt737c89f2015-01-28 12:23:19 -08001146 $log = _$log_;
Simon Hunt86b7c882015-04-02 23:06:08 -07001147 $timeout = _$timeout_;
Simon Hunt1894d792015-02-04 17:09:20 -08001148 fs = _fs_;
Simon Hunt737c89f2015-01-28 12:23:19 -08001149 sus = _sus_;
Simon Huntac4c6f72015-02-03 19:50:53 -08001150 ts = _ts_;
Simon Hunt5724fb42015-02-05 16:59:40 -08001151 flash = _flash_;
Simon Hunt237676b52015-03-10 19:04:26 -07001152 wss = _wss_;
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001153 tov = _tov_;
Simon Huntac4c6f72015-02-03 19:50:53 -08001154 tis = _tis_;
Simon Hunt3a6eec02015-02-09 21:16:43 -08001155 tms = _tms_;
Simon Hunta4242de2015-02-24 17:11:55 -08001156 td3 = _td3_;
Simon Hunt08f841d02015-02-10 14:39:20 -08001157 tss = _tss_;
Simon Huntf542d842015-02-11 16:20:33 -08001158 tts = _tts_;
Simon Hunt96f88c62015-02-19 17:57:25 -08001159 tos = _tos_;
Simon Hunteb0fa052015-02-17 19:20:28 -08001160 fltr = _fltr_;
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001161 tls = _tls_;
Andrea Campanella732ea832017-02-06 09:25:59 -08001162 tpis = _tpis_;
Simon Hunt737c89f2015-01-28 12:23:19 -08001163
Simon Huntf51bf462016-06-29 16:22:57 -07001164 ts.addListener(updateLinksAndNodes);
Simon Hunta142dd22015-02-12 22:07:51 -08001165
Simon Hunt737c89f2015-01-28 12:23:19 -08001166 // forceG is the SVG group to display the force layout in
Simon Huntdc6adea2015-02-09 22:29:36 -08001167 // uplink is the api from the main topo source file
Simon Hunt3a6eec02015-02-09 21:16:43 -08001168 // dim is the initial dimensions of the SVG as [w,h]
Simon Hunt737c89f2015-01-28 12:23:19 -08001169 // opts are, well, optional :)
Simon Hunt3ab20282015-02-26 20:32:19 -08001170 function initForce(_svg_, forceG, _uplink_, _dim_, opts) {
Simon Hunt1894d792015-02-04 17:09:20 -08001171 uplink = _uplink_;
Simon Hunt3a6eec02015-02-09 21:16:43 -08001172 dim = _dim_;
Simon Hunt3ab20282015-02-26 20:32:19 -08001173 svg = _svg_;
1174
1175 lu = network.lookup;
1176 rlk = network.revLinkToKey;
Simon Hunt3a6eec02015-02-09 21:16:43 -08001177
1178 $log.debug('initForce().. dim = ' + dim);
1179
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001180 tov.setApi(mkOverlayApi(), tss);
Simon Huntdc6adea2015-02-09 22:29:36 -08001181 tms.initModel(mkModelApi(uplink), dim);
Steven Burrowsf17f0ab2017-04-11 11:03:58 -07001182 td3.initD3(mkD3Api(), uplink.zoomer());
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001183 tss.initSelect(mkSelectApi());
1184 tts.initTraffic(mkTrafficApi());
Andrea Campanella732ea832017-02-06 09:25:59 -08001185 tpis.initProtectedIntents(mkTrafficApi());
Simon Huntc3c5b672015-02-20 11:32:13 -08001186 tos.initOblique(mkObliqueApi(uplink, fltr));
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001187 fltr.initFilter(mkFilterApi());
Simon Hunt9e2104c2015-02-26 10:48:59 -08001188 tls.initLink(mkLinkApi(svg, uplink), td3);
Simon Hunta11b4eb2015-01-28 16:20:50 -08001189
Simon Hunt737c89f2015-01-28 12:23:19 -08001190 settings = angular.extend({}, defaultSettings, opts);
1191
1192 linkG = forceG.append('g').attr('id', 'topo-links');
1193 linkLabelG = forceG.append('g').attr('id', 'topo-linkLabels');
Bri Prebilic Cole80401762015-07-16 11:36:18 -07001194 numLinkLblsG = forceG.append('g').attr('id', 'topo-numLinkLabels');
Simon Hunt737c89f2015-01-28 12:23:19 -08001195 nodeG = forceG.append('g').attr('id', 'topo-nodes');
Simon Hunt1a5301e2015-02-25 15:31:25 -08001196 portLabelG = forceG.append('g').attr('id', 'topo-portLabels');
Simon Hunt737c89f2015-01-28 12:23:19 -08001197
1198 link = linkG.selectAll('.link');
1199 linkLabel = linkLabelG.selectAll('.linkLabel');
1200 node = nodeG.selectAll('.node');
1201
1202 force = d3.layout.force()
Simon Hunt3a6eec02015-02-09 21:16:43 -08001203 .size(dim)
Simon Hunt737c89f2015-01-28 12:23:19 -08001204 .nodes(network.nodes)
1205 .links(network.links)
1206 .gravity(settings.gravity)
1207 .friction(settings.friction)
1208 .charge(settings.charge._def_)
1209 .linkDistance(settings.linkDistance._def_)
1210 .linkStrength(settings.linkStrength._def_)
1211 .on('tick', tick);
1212
1213 drag = sus.createDragBehavior(force,
Simon Hunt08f841d02015-02-10 14:39:20 -08001214 tss.selectObject, atDragEnd, dragEnabled, clickEnabled);
Simon Hunt737c89f2015-01-28 12:23:19 -08001215 }
1216
Simon Hunt3a6eec02015-02-09 21:16:43 -08001217 function newDim(_dim_) {
1218 dim = _dim_;
1219 force.size(dim);
1220 tms.newDim(dim);
Simon Hunt737c89f2015-01-28 12:23:19 -08001221 }
1222
Simon Hunt3a6eec02015-02-09 21:16:43 -08001223 function destroyForce() {
Simon Hunt3ab20282015-02-26 20:32:19 -08001224 force.stop();
1225
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001226 tls.destroyLink();
Simon Hunt96f88c62015-02-19 17:57:25 -08001227 tos.destroyOblique();
Simon Huntf542d842015-02-11 16:20:33 -08001228 tts.destroyTraffic();
Andrea Campanella732ea832017-02-06 09:25:59 -08001229 tpis.destroyProtectedIntents();
Simon Huntf542d842015-02-11 16:20:33 -08001230 tss.destroySelect();
Simon Hunta4242de2015-02-24 17:11:55 -08001231 td3.destroyD3();
Simon Huntf542d842015-02-11 16:20:33 -08001232 tms.destroyModel();
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001233 // note: no need to destroy overlay service
Simon Huntf51bf462016-06-29 16:22:57 -07001234 ts.removeListener(updateLinksAndNodes);
Simon Hunt3ab20282015-02-26 20:32:19 -08001235
1236 // clean up the DOM
1237 svg.selectAll('g').remove();
1238 svg.selectAll('defs').remove();
1239
1240 // clean up internal state
1241 network.nodes = [];
1242 network.links = [];
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -07001243 network.linksByDevice = {};
Simon Hunt3ab20282015-02-26 20:32:19 -08001244 network.lookup = {};
1245 network.revLinkToKey = {};
1246
Bri Prebilic Cole80401762015-07-16 11:36:18 -07001247 linkNums = [];
1248
1249 linkG = linkLabelG = numLinkLblsG = nodeG = portLabelG = null;
Simon Hunt3ab20282015-02-26 20:32:19 -08001250 link = linkLabel = node = null;
1251 force = drag = null;
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -07001252
1253 // clean up $timeout promises
Simon Hunta17fa672015-08-19 18:42:22 -07001254 if (fTimer) {
1255 $timeout.cancel(fTimer);
1256 }
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -07001257 if (fNodesTimer) {
1258 $timeout.cancel(fNodesTimer);
1259 }
1260 if (fLinksTimer) {
1261 $timeout.cancel(fLinksTimer);
1262 }
Simon Hunt3a6eec02015-02-09 21:16:43 -08001263 }
1264
Simon Hunt737c89f2015-01-28 12:23:19 -08001265 return {
1266 initForce: initForce,
Simon Hunt3a6eec02015-02-09 21:16:43 -08001267 newDim: newDim,
1268 destroyForce: destroyForce,
Simon Huntac4c6f72015-02-03 19:50:53 -08001269
Simon Hunta4242de2015-02-24 17:11:55 -08001270 updateDeviceColors: td3.updateDeviceColors,
Simon Hunt5724fb42015-02-05 16:59:40 -08001271 toggleHosts: toggleHosts,
Simon Hunt9e2104c2015-02-26 10:48:59 -08001272 togglePorts: tls.togglePorts,
Simon Hunt5724fb42015-02-05 16:59:40 -08001273 toggleOffline: toggleOffline,
1274 cycleDeviceLabels: cycleDeviceLabels,
Simon Hunt10618f62017-06-15 19:30:52 -07001275 cycleHostLabels: cycleHostLabels,
Simon Hunt445e8152015-02-06 13:00:12 -08001276 unpin: unpin,
Simon Hunta142dd22015-02-12 22:07:51 -08001277 showMastership: showMastership,
Simon Hunt86b7c882015-04-02 23:06:08 -07001278 showBadLinks: showBadLinks,
Steven Burrowsf17f0ab2017-04-11 11:03:58 -07001279 setNodeScale: setNodeScale,
Simon Huntac4c6f72015-02-03 19:50:53 -08001280
Simon Huntfd7106c2016-02-09 15:05:26 -08001281 resetAllLocations: resetAllLocations,
Simon Huntac4c6f72015-02-03 19:50:53 -08001282 addDevice: addDevice,
Simon Hunt1894d792015-02-04 17:09:20 -08001283 updateDevice: updateDevice,
1284 removeDevice: removeDevice,
1285 addHost: addHost,
1286 updateHost: updateHost,
Simon Hunt95d56fd2015-11-12 11:06:44 -08001287 moveHost: moveHost,
Simon Hunt1894d792015-02-04 17:09:20 -08001288 removeHost: removeHost,
1289 addLink: addLink,
1290 updateLink: updateLink,
Simon Hunt4a6b54b2015-10-27 22:08:25 -07001291 removeLink: removeLink,
Steven Burrows1c2a9682017-07-14 16:52:46 +01001292 topoStartDone: topoStartDone,
Simon Hunte2d9dc72017-08-10 15:21:04 -07001293
1294 setLionBundle: setLionBundle,
Simon Hunt737c89f2015-01-28 12:23:19 -08001295 };
1296 }]);
1297}());