blob: c7d1dca91a4d04eb5d84c28419cb114d59abd484 [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
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',
Steven Burrows1c2a9682017-07-14 16:52:46 +010034 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',
Steven Burrows1c2a9682017-07-14 16:52:46 +010040 outColor: '#f00',
Simon Hunt1894d792015-02-04 17:09:20 -080041 },
42 inWidth: 12,
Steven Burrows1c2a9682017-07-14 16:52:46 +010043 outWidth: 10,
Simon Hunt1894d792015-02-04 17:09:20 -080044 };
45
Simon Hunt737c89f2015-01-28 12:23:19 -080046 // internal state
Steven Burrows1c2a9682017-07-14 16:52:46 +010047 var settings, // merged default settings and options
48 force, // force layout object
49 drag, // drag behavior handler
Simon Hunt737c89f2015-01-28 12:23:19 -080050 network = {
51 nodes: [],
52 links: [],
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -070053 linksByDevice: {},
Simon Hunt737c89f2015-01-28 12:23:19 -080054 lookup: {},
Steven Burrows1c2a9682017-07-14 16:52:46 +010055 revLinkToKey: {},
Simon Huntac4c6f72015-02-03 19:50:53 -080056 },
Steven Burrows1c2a9682017-07-14 16:52:46 +010057 lu, // shorthand for lookup
58 rlk, // shorthand for revLinktoKey
59 showHosts = false, // whether hosts are displayed
60 showOffline = true, // whether offline devices are displayed
61 nodeLock = false, // whether nodes can be dragged or not (locked)
62 fTimer, // timer for delayed force layout
63 fNodesTimer, // timer for delayed nodes update
64 fLinksTimer, // timer for delayed links update
65 dim, // the dimensions of the force layout [w,h]
66 linkNums = [], // array of link number labels
67 devIconDim = 36, // node target dimension
68 devIconDimMin = 20, // node minimum dimension when zoomed out
69 devIconDimMax = 40, // node maximum dimension when zoomed in
Steven Burrowsf17f0ab2017-04-11 11:03:58 -070070 portLabelDim = 30;
Simon Hunt737c89f2015-01-28 12:23:19 -080071
72 // SVG elements;
Bri Prebilic Cole80401762015-07-16 11:36:18 -070073 var linkG, linkLabelG, numLinkLblsG, portLabelG, nodeG;
Simon Hunt737c89f2015-01-28 12:23:19 -080074
75 // D3 selections;
76 var link, linkLabel, node;
77
78 // default settings for force layout
79 var defaultSettings = {
80 gravity: 0.4,
81 friction: 0.7,
82 charge: {
83 // note: key is node.class
84 device: -8000,
85 host: -5000,
Steven Burrows1c2a9682017-07-14 16:52:46 +010086 _def_: -12000,
Simon Hunt737c89f2015-01-28 12:23:19 -080087 },
88 linkDistance: {
89 // note: key is link.type
90 direct: 100,
91 optical: 120,
92 hostLink: 3,
Steven Burrows1c2a9682017-07-14 16:52:46 +010093 _def_: 50,
Simon Hunt737c89f2015-01-28 12:23:19 -080094 },
95 linkStrength: {
96 // note: key is link.type
97 // range: {0.0 ... 1.0}
Steven Burrows1c2a9682017-07-14 16:52:46 +010098 // direct: 1.0,
99 // optical: 1.0,
100 // hostLink: 1.0,
101 _def_: 1.0,
102 },
Simon Hunt737c89f2015-01-28 12:23:19 -0800103 };
104
105
Simon Huntac4c6f72015-02-03 19:50:53 -0800106 // ==========================
107 // === EVENT HANDLERS
108
109 function addDevice(data) {
110 var id = data.id,
111 d;
112
Simon Hunt1894d792015-02-04 17:09:20 -0800113 uplink.showNoDevs(false);
Simon Huntac4c6f72015-02-03 19:50:53 -0800114
115 // although this is an add device event, if we already have the
116 // device, treat it as an update instead..
Simon Hunt1894d792015-02-04 17:09:20 -0800117 if (lu[id]) {
Simon Huntac4c6f72015-02-03 19:50:53 -0800118 updateDevice(data);
119 return;
120 }
121
Simon Hunt3a6eec02015-02-09 21:16:43 -0800122 d = tms.createDeviceNode(data);
Simon Huntac4c6f72015-02-03 19:50:53 -0800123 network.nodes.push(d);
Simon Hunt1894d792015-02-04 17:09:20 -0800124 lu[id] = d;
Simon Huntac4c6f72015-02-03 19:50:53 -0800125 updateNodes();
Simon Hunta17fa672015-08-19 18:42:22 -0700126 fStart();
Simon Huntac4c6f72015-02-03 19:50:53 -0800127 }
128
129 function updateDevice(data) {
130 var id = data.id,
Simon Hunt1894d792015-02-04 17:09:20 -0800131 d = lu[id],
Simon Huntac4c6f72015-02-03 19:50:53 -0800132 wasOnline;
133
134 if (d) {
135 wasOnline = d.online;
136 angular.extend(d, data);
Simon Hunt3a6eec02015-02-09 21:16:43 -0800137 if (tms.positionNode(d, true)) {
Simon Hunt445e8152015-02-06 13:00:12 -0800138 sendUpdateMeta(d);
Simon Huntac4c6f72015-02-03 19:50:53 -0800139 }
140 updateNodes();
141 if (wasOnline !== d.online) {
Simon Huntdc6adea2015-02-09 22:29:36 -0800142 tms.findAttachedLinks(d.id).forEach(restyleLinkElement);
Simon Hunt5724fb42015-02-05 16:59:40 -0800143 updateOfflineVisibility(d);
Simon Huntac4c6f72015-02-03 19:50:53 -0800144 }
Simon Huntac4c6f72015-02-03 19:50:53 -0800145 }
146 }
147
Simon Hunt1894d792015-02-04 17:09:20 -0800148 function removeDevice(data) {
149 var id = data.id,
150 d = lu[id];
151 if (d) {
152 removeDeviceElement(d);
Simon Hunt1894d792015-02-04 17:09:20 -0800153 }
154 }
155
156 function addHost(data) {
157 var id = data.id,
158 d, lnk;
159
160 // although this is an add host event, if we already have the
161 // host, treat it as an update instead..
162 if (lu[id]) {
163 updateHost(data);
164 return;
165 }
166
Simon Hunt3a6eec02015-02-09 21:16:43 -0800167 d = tms.createHostNode(data);
Simon Hunt1894d792015-02-04 17:09:20 -0800168 network.nodes.push(d);
169 lu[id] = d;
Simon Hunt1894d792015-02-04 17:09:20 -0800170 updateNodes();
171
Simon Hunt3a6eec02015-02-09 21:16:43 -0800172 lnk = tms.createHostLink(data);
Simon Hunt1894d792015-02-04 17:09:20 -0800173 if (lnk) {
Steven Burrows1c2a9682017-07-14 16:52:46 +0100174 d.linkData = lnk; // cache ref on its host
Simon Hunt1894d792015-02-04 17:09:20 -0800175 network.links.push(lnk);
176 lu[d.ingress] = lnk;
177 lu[d.egress] = lnk;
178 updateLinks();
179 }
Simon Hunta17fa672015-08-19 18:42:22 -0700180 fStart();
Simon Hunt1894d792015-02-04 17:09:20 -0800181 }
182
183 function updateHost(data) {
184 var id = data.id,
185 d = lu[id];
186 if (d) {
187 angular.extend(d, data);
Simon Hunt3a6eec02015-02-09 21:16:43 -0800188 if (tms.positionNode(d, true)) {
Simon Hunt445e8152015-02-06 13:00:12 -0800189 sendUpdateMeta(d);
Simon Hunt1894d792015-02-04 17:09:20 -0800190 }
191 updateNodes();
Simon Hunt1894d792015-02-04 17:09:20 -0800192 }
193 }
194
Simon Hunt95d56fd2015-11-12 11:06:44 -0800195 function moveHost(data) {
196 var id = data.id,
197 d = lu[id],
198 lnk;
199 if (d) {
200 // first remove the old host link
201 removeLinkElement(d.linkData);
202
203 // merge new data
204 angular.extend(d, data);
205 if (tms.positionNode(d, true)) {
206 sendUpdateMeta(d);
207 }
208
209 // now create a new host link
210 lnk = tms.createHostLink(data);
211 if (lnk) {
212 d.linkData = lnk;
213 network.links.push(lnk);
214 lu[d.ingress] = lnk;
215 lu[d.egress] = lnk;
216 }
217
218 updateNodes();
219 updateLinks();
220 fResume();
221 }
222 }
223
Simon Hunt1894d792015-02-04 17:09:20 -0800224 function removeHost(data) {
225 var id = data.id,
226 d = lu[id];
227 if (d) {
228 removeHostElement(d, true);
Simon Hunt1894d792015-02-04 17:09:20 -0800229 }
230 }
231
232 function addLink(data) {
Simon Huntdc6adea2015-02-09 22:29:36 -0800233 var result = tms.findLink(data, 'add'),
Simon Hunt1894d792015-02-04 17:09:20 -0800234 bad = result.badLogic,
235 d = result.ldata;
236
237 if (bad) {
Simon Hunteb18f522016-01-28 19:22:23 -0800238 $log.debug(bad + ': ' + link.id);
Simon Hunt1894d792015-02-04 17:09:20 -0800239 return;
240 }
241
242 if (d) {
243 // we already have a backing store link for src/dst nodes
244 addLinkUpdate(d, data);
245 return;
246 }
247
248 // no backing store link yet
Simon Hunt3a6eec02015-02-09 21:16:43 -0800249 d = tms.createLink(data);
Simon Hunt1894d792015-02-04 17:09:20 -0800250 if (d) {
251 network.links.push(d);
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700252 aggregateLink(d, data);
Simon Hunt1894d792015-02-04 17:09:20 -0800253 lu[d.key] = d;
254 updateLinks();
Simon Hunta17fa672015-08-19 18:42:22 -0700255 fStart();
Simon Hunt1894d792015-02-04 17:09:20 -0800256 }
257 }
258
259 function updateLink(data) {
Simon Huntdc6adea2015-02-09 22:29:36 -0800260 var result = tms.findLink(data, 'update'),
Simon Hunt1894d792015-02-04 17:09:20 -0800261 bad = result.badLogic;
262 if (bad) {
Simon Hunteb18f522016-01-28 19:22:23 -0800263 $log.debug(bad + ': ' + link.id);
Simon Hunt1894d792015-02-04 17:09:20 -0800264 return;
265 }
Simon Hunteb18f522016-01-28 19:22:23 -0800266 result.updateWith(data);
Simon Hunt1894d792015-02-04 17:09:20 -0800267 }
268
269 function removeLink(data) {
Simon Hunta4242de2015-02-24 17:11:55 -0800270 var result = tms.findLink(data, 'remove');
271
272 if (!result.badLogic) {
273 result.removeRawLink();
Simon Hunt1894d792015-02-04 17:09:20 -0800274 }
Simon Hunt1894d792015-02-04 17:09:20 -0800275 }
276
Simon Hunt4a6b54b2015-10-27 22:08:25 -0700277 function topoStartDone(data) {
278 // called when the initial barrage of data has been sent from server
279 uplink.topoStartDone();
280 }
281
Simon Hunt1894d792015-02-04 17:09:20 -0800282 // ========================
283
Simon Hunt94f7dae2015-08-26 17:40:59 -0700284 function nodeById(id) {
285 return lu[id];
286 }
287
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700288 function makeNodeKey(node1, node2) {
289 return node1 + '-' + node2;
290 }
291
292 function findNodePair(key, keyRev) {
293 if (network.linksByDevice[key]) {
294 return key;
295 } else if (network.linksByDevice[keyRev]) {
296 return keyRev;
297 } else {
298 return false;
299 }
300 }
301
302 function aggregateLink(ldata, link) {
303 var key = makeNodeKey(link.src, link.dst),
304 keyRev = makeNodeKey(link.dst, link.src),
305 found = findNodePair(key, keyRev);
306
307 if (found) {
308 network.linksByDevice[found].push(ldata);
309 ldata.devicePair = found;
310 } else {
Steven Burrows1c2a9682017-07-14 16:52:46 +0100311 network.linksByDevice[key] = [ldata];
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700312 ldata.devicePair = key;
313 }
314 }
315
Simon Hunt1894d792015-02-04 17:09:20 -0800316 function addLinkUpdate(ldata, link) {
317 // add link event, but we already have the reverse link installed
318 ldata.fromTarget = link;
Simon Huntdc6adea2015-02-09 22:29:36 -0800319 rlk[link.id] = ldata.key;
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700320 // possible solution to el being undefined in restyleLinkElement:
Steven Burrows1c2a9682017-07-14 16:52:46 +0100321 // _updateLinks();
Simon Hunt1894d792015-02-04 17:09:20 -0800322 restyleLinkElement(ldata);
323 }
324
Simon Hunt1894d792015-02-04 17:09:20 -0800325
326 var widthRatio = 1.4,
327 linkScale = d3.scale.linear()
328 .domain([1, 12])
329 .range([widthRatio, 12 * widthRatio])
Simon Hunt5724fb42015-02-05 16:59:40 -0800330 .clamp(true),
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800331 allLinkTypes = 'direct indirect optical tunnel',
332 allLinkSubTypes = 'inactive not-permitted';
Simon Hunt1894d792015-02-04 17:09:20 -0800333
Simon Hunta142dd22015-02-12 22:07:51 -0800334 function restyleLinkElement(ldata, immediate) {
Simon Hunt1894d792015-02-04 17:09:20 -0800335 // this fn's job is to look at raw links and decide what svg classes
336 // need to be applied to the line element in the DOM
337 var th = ts.theme(),
338 el = ldata.el,
339 type = ldata.type(),
340 lw = ldata.linkWidth(),
Simon Hunta142dd22015-02-12 22:07:51 -0800341 online = ldata.online(),
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800342 modeCls = ldata.expected() ? 'inactive' : 'not-permitted',
Simon Hunta142dd22015-02-12 22:07:51 -0800343 delay = immediate ? 0 : 1000;
Simon Hunt1894d792015-02-04 17:09:20 -0800344
Simon Huntf44d7262016-06-14 14:46:56 -0700345 // NOTE: understand why el is sometimes undefined on addLink events...
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700346 // Investigated:
347 // el is undefined when it's a reverse link that is being added.
348 // updateLinks (which sets ldata.el) isn't called before this is called.
349 // Calling _updateLinks in addLinkUpdate fixes it, but there might be
350 // a more efficient way to fix it.
351 if (el && !el.empty()) {
Thomas Vachuskacb5016f2015-05-18 14:11:43 -0700352 el.classed('link', true);
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800353 el.classed(allLinkSubTypes, false);
354 el.classed(modeCls, !online);
Thomas Vachuskacb5016f2015-05-18 14:11:43 -0700355 el.classed(allLinkTypes, false);
356 if (type) {
357 el.classed(type, true);
358 }
359 el.transition()
360 .duration(delay)
361 .attr('stroke-width', linkScale(lw))
362 .attr('stroke', linkConfig[th].baseColor);
Simon Hunt1894d792015-02-04 17:09:20 -0800363 }
Simon Hunt1894d792015-02-04 17:09:20 -0800364 }
365
Simon Hunt1894d792015-02-04 17:09:20 -0800366 function removeLinkElement(d) {
367 var idx = fs.find(d.key, network.links, 'key'),
368 removed;
369 if (idx >=0) {
370 // remove from links array
371 removed = network.links.splice(idx, 1);
372 // remove from lookup cache
373 delete lu[removed[0].key];
374 updateLinks();
Simon Hunta17fa672015-08-19 18:42:22 -0700375 fResume();
Simon Hunt1894d792015-02-04 17:09:20 -0800376 }
377 }
378
379 function removeHostElement(d, upd) {
380 // first, remove associated hostLink...
381 removeLinkElement(d.linkData);
382
383 // remove hostLink bindings
384 delete lu[d.ingress];
385 delete lu[d.egress];
386
387 // remove from lookup cache
388 delete lu[d.id];
389 // remove from nodes array
390 var idx = fs.find(d.id, network.nodes);
391 network.nodes.splice(idx, 1);
392
393 // remove from SVG
394 // NOTE: upd is false if we were called from removeDeviceElement()
395 if (upd) {
396 updateNodes();
Simon Hunta17fa672015-08-19 18:42:22 -0700397 fResume();
Simon Hunt1894d792015-02-04 17:09:20 -0800398 }
399 }
400
401 function removeDeviceElement(d) {
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700402 var id = d.id,
403 idx;
Simon Hunt1894d792015-02-04 17:09:20 -0800404 // first, remove associated hosts and links..
Simon Huntdc6adea2015-02-09 22:29:36 -0800405 tms.findAttachedHosts(id).forEach(removeHostElement);
406 tms.findAttachedLinks(id).forEach(removeLinkElement);
Simon Hunt1894d792015-02-04 17:09:20 -0800407
408 // remove from lookup cache
409 delete lu[id];
410 // remove from nodes array
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700411 idx = fs.find(id, network.nodes);
412 if (idx > -1) {
413 network.nodes.splice(idx, 1);
414 }
Simon Hunt1894d792015-02-04 17:09:20 -0800415
416 if (!network.nodes.length) {
Simon Huntdc6adea2015-02-09 22:29:36 -0800417 uplink.showNoDevs(true);
Simon Hunt1894d792015-02-04 17:09:20 -0800418 }
419
420 // remove from SVG
421 updateNodes();
Simon Hunta17fa672015-08-19 18:42:22 -0700422 fResume();
Simon Hunt1894d792015-02-04 17:09:20 -0800423 }
424
Simon Hunt5724fb42015-02-05 16:59:40 -0800425 function updateHostVisibility() {
Simon Hunt18bf9822015-02-12 17:35:45 -0800426 sus.visible(nodeG.selectAll('.host'), showHosts);
427 sus.visible(linkG.selectAll('.hostLink'), showHosts);
Simon Hunt8eb4d3a2015-02-23 18:23:29 -0800428 sus.visible(linkLabelG.selectAll('.hostLinkLabel'), showHosts);
Simon Hunt5724fb42015-02-05 16:59:40 -0800429 }
430
431 function updateOfflineVisibility(dev) {
432 function updDev(d, show) {
Simon Hunt8eb4d3a2015-02-23 18:23:29 -0800433 var b;
Simon Hunt18bf9822015-02-12 17:35:45 -0800434 sus.visible(d.el, show);
Simon Hunt5724fb42015-02-05 16:59:40 -0800435
Simon Huntdc6adea2015-02-09 22:29:36 -0800436 tms.findAttachedLinks(d.id).forEach(function (link) {
Simon Hunt5724fb42015-02-05 16:59:40 -0800437 b = show && ((link.type() !== 'hostLink') || showHosts);
Simon Hunt18bf9822015-02-12 17:35:45 -0800438 sus.visible(link.el, b);
Simon Hunt5724fb42015-02-05 16:59:40 -0800439 });
Simon Huntdc6adea2015-02-09 22:29:36 -0800440 tms.findAttachedHosts(d.id).forEach(function (host) {
Simon Hunt5724fb42015-02-05 16:59:40 -0800441 b = show && showHosts;
Simon Hunt18bf9822015-02-12 17:35:45 -0800442 sus.visible(host.el, b);
Simon Hunt5724fb42015-02-05 16:59:40 -0800443 });
444 }
445
446 if (dev) {
447 // updating a specific device that just toggled off/on-line
448 updDev(dev, dev.online || showOffline);
449 } else {
450 // updating all offline devices
Simon Huntdc6adea2015-02-09 22:29:36 -0800451 tms.findDevices(true).forEach(function (d) {
Simon Hunt5724fb42015-02-05 16:59:40 -0800452 updDev(d, showOffline);
453 });
454 }
455 }
456
Simon Hunt1894d792015-02-04 17:09:20 -0800457
Simon Hunt445e8152015-02-06 13:00:12 -0800458 function sendUpdateMeta(d, clearPos) {
Simon Huntac4c6f72015-02-03 19:50:53 -0800459 var metaUi = {},
460 ll;
461
Simon Hunt445e8152015-02-06 13:00:12 -0800462 // if we are not clearing the position data (unpinning),
Simon Huntfd7106c2016-02-09 15:05:26 -0800463 // attach the x, y, (and equivalent longitude, latitude)...
Simon Hunt445e8152015-02-06 13:00:12 -0800464 if (!clearPos) {
Simon Hunt3a6eec02015-02-09 21:16:43 -0800465 ll = tms.lngLatFromCoord([d.x, d.y]);
Simon Huntfd7106c2016-02-09 15:05:26 -0800466 metaUi = {
467 x: d.x,
468 y: d.y,
469 equivLoc: {
470 lng: ll[0],
Steven Burrows1c2a9682017-07-14 16:52:46 +0100471 lat: ll[1],
472 },
Simon Huntfd7106c2016-02-09 15:05:26 -0800473 };
Simon Hunt1894d792015-02-04 17:09:20 -0800474 }
475 d.metaUi = metaUi;
Simon Hunt237676b52015-03-10 19:04:26 -0700476 wss.sendEvent('updateMeta', {
Simon Hunt1894d792015-02-04 17:09:20 -0800477 id: d.id,
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700478 class: d.class,
Steven Burrows1c2a9682017-07-14 16:52:46 +0100479 memento: metaUi,
Simon Hunt1894d792015-02-04 17:09:20 -0800480 });
Simon Huntac4c6f72015-02-03 19:50:53 -0800481 }
482
Simon Hunt1894d792015-02-04 17:09:20 -0800483
Simon Huntac4c6f72015-02-03 19:50:53 -0800484 function mkSvgClass(d) {
485 return d.fixed ? d.svgClass + ' fixed' : d.svgClass;
486 }
487
Simon Hunt5724fb42015-02-05 16:59:40 -0800488 function vis(b) {
489 return b ? 'visible' : 'hidden';
490 }
491
Simon Huntfcbde892015-04-16 12:05:28 -0700492 function toggleHosts(x) {
493 var kev = (x === 'keyev'),
494 on = kev ? !showHosts : !!x;
495
496 showHosts = on;
Simon Hunt5724fb42015-02-05 16:59:40 -0800497 updateHostVisibility();
Simon Huntfcbde892015-04-16 12:05:28 -0700498 flash.flash('Hosts ' + vis(on));
499 return on;
Simon Hunt5724fb42015-02-05 16:59:40 -0800500 }
501
Simon Huntfcbde892015-04-16 12:05:28 -0700502 function toggleOffline(x) {
503 var kev = (x === 'keyev'),
504 on = kev ? !showOffline : !!x;
505
506 showOffline = on;
Simon Hunt5724fb42015-02-05 16:59:40 -0800507 updateOfflineVisibility();
Simon Huntfcbde892015-04-16 12:05:28 -0700508 flash.flash('Offline devices ' + vis(on));
509 return on;
Simon Hunt5724fb42015-02-05 16:59:40 -0800510 }
511
512 function cycleDeviceLabels() {
Bri Prebilic Cole9cf1a8d2015-04-21 13:15:29 -0700513 flash.flash(td3.incDevLabIndex());
Simon Huntdc6adea2015-02-09 22:29:36 -0800514 tms.findDevices().forEach(function (d) {
Simon Hunta4242de2015-02-24 17:11:55 -0800515 td3.updateDeviceLabel(d);
Simon Hunt1c367112015-02-05 18:02:46 -0800516 });
Simon Hunt5724fb42015-02-05 16:59:40 -0800517 }
518
Simon Hunt10618f62017-06-15 19:30:52 -0700519 function cycleHostLabels() {
520 flash.flash(td3.incHostLabIndex());
521 tms.findHosts().forEach(function (d) {
522 td3.updateHostLabel(d);
523 });
524 }
525
Simon Hunt445e8152015-02-06 13:00:12 -0800526 function unpin() {
Simon Hunt08f841d02015-02-10 14:39:20 -0800527 var hov = tss.hovered();
528 if (hov) {
529 sendUpdateMeta(hov, true);
530 hov.fixed = false;
531 hov.el.classed('fixed', false);
Simon Hunt445e8152015-02-06 13:00:12 -0800532 fResume();
533 }
534 }
535
Simon Hunta142dd22015-02-12 22:07:51 -0800536 function showMastership(masterId) {
537 if (!masterId) {
538 restoreLayerState();
539 } else {
540 showMastershipFor(masterId);
541 }
542 }
543
544 function restoreLayerState() {
545 // NOTE: this level of indirection required, for when we have
546 // the layer filter functionality re-implemented
547 suppressLayers(false);
548 }
549
550 function showMastershipFor(id) {
551 suppressLayers(true);
552 node.each(function (n) {
553 if (n.master === id) {
Simon Hunt743a8492015-08-25 16:18:19 -0700554 n.el.classed('suppressedmax', false);
Simon Hunta142dd22015-02-12 22:07:51 -0800555 }
556 });
557 }
558
Simon Hunt743a8492015-08-25 16:18:19 -0700559 function supAmt(less) {
Steven Burrows1c2a9682017-07-14 16:52:46 +0100560 return less ? 'suppressed' : 'suppressedmax';
Simon Hunt743a8492015-08-25 16:18:19 -0700561 }
562
563 function suppressLayers(b, less) {
564 var cls = supAmt(less);
565 node.classed(cls, b);
566 link.classed(cls, b);
567 }
568
569 function unsuppressNode(id, less) {
570 var cls = supAmt(less);
571 node.each(function (n) {
572 if (n.id === id) {
573 n.el.classed(cls, false);
574 }
575 });
576 }
577
Simon Hunt94f7dae2015-08-26 17:40:59 -0700578 function unsuppressLink(key, less) {
Simon Hunt743a8492015-08-25 16:18:19 -0700579 var cls = supAmt(less);
580 link.each(function (n) {
Simon Hunt94f7dae2015-08-26 17:40:59 -0700581 if (n.key === key) {
Simon Hunt743a8492015-08-25 16:18:19 -0700582 n.el.classed(cls, false);
583 }
584 });
Simon Hunta142dd22015-02-12 22:07:51 -0800585 }
Simon Hunt445e8152015-02-06 13:00:12 -0800586
Simon Hunt86b7c882015-04-02 23:06:08 -0700587 function showBadLinks() {
588 var badLinks = tms.findBadLinks();
589 flash.flash('Bad Links: ' + badLinks.length);
590 $log.debug('Bad Link List (' + badLinks.length + '):');
591 badLinks.forEach(function (d) {
592 $log.debug('bad link: (' + d.bad + ') ' + d.key, d);
593 if (d.el) {
594 d.el.attr('stroke-width', linkScale(2.8))
595 .attr('stroke', 'red');
596 }
597 });
598 // back to normal after 2 seconds...
599 $timeout(updateLinks, 2000);
600 }
601
Steven Burrowsf17f0ab2017-04-11 11:03:58 -0700602 function deviceScale() {
603 var scale = uplink.zoomer().scale(),
604 dim = devIconDim,
605 multiplier = 1;
606
607 if (dim * scale < devIconDimMin) {
608 multiplier = devIconDimMin / (dim * scale);
609 } else if (dim * scale > devIconDimMax) {
610 multiplier = devIconDimMax / (dim * scale);
611 }
612
613 return multiplier;
614 }
615
616 function linkWidthScale(scale) {
617 var scale = uplink.zoomer().scale();
618 return linkScale(widthRatio) / scale;
619 }
620
621 function portLabelScale(scale) {
622 var scale = uplink.zoomer().scale();
623 return portLabelDim / (portLabelDim * scale);
624 }
625
626 function setNodeScale(scale) {
627 // Scale the network nodes
628 _.each(network.nodes, function (node) {
629 if (node.class === 'host') {
630 node.el.selectAll('g').style('transform', 'scale(' + deviceScale(scale) + ')');
631 node.el.selectAll('text').style('transform', 'scale(' + deviceScale(scale) + ')');
632 return;
633 }
634 node.el.selectAll('*')
635 .style('transform', 'scale(' + deviceScale(scale) + ')');
636 });
637
638 // Scale the network links
639 _.each(network.links, function (link) {
640 link.el.style('stroke-width', linkWidthScale(scale) + 'px');
641 });
642
643 d3.select('#topo-portLabels')
644 .selectAll('.portLabel')
645 .selectAll('*')
646 .style('transform', 'scale(' + portLabelScale(scale) + ')');
647 }
648
Simon Huntfd7106c2016-02-09 15:05:26 -0800649 function resetAllLocations() {
650 tms.resetAllLocations();
651 updateNodes();
652 tick(); // force nodes to be redrawn in their new locations
653 flash.flash('Reset Node Locations');
654 }
655
Simon Hunt5724fb42015-02-05 16:59:40 -0800656 // ==========================================
657
Simon Huntac4c6f72015-02-03 19:50:53 -0800658 function updateNodes() {
Thomas Vachuska1a989c12015-06-09 18:29:22 -0700659 if (fNodesTimer) {
660 $timeout.cancel(fNodesTimer);
661 }
662 fNodesTimer = $timeout(_updateNodes, 150);
663 }
664
Simon Hunta17fa672015-08-19 18:42:22 -0700665 // IMPLEMENTATION NOTE: _updateNodes() should NOT stop, start, or resume
666 // the force layout; that needs to be determined and implemented elsewhere
Thomas Vachuska1a989c12015-06-09 18:29:22 -0700667 function _updateNodes() {
Simon Hunt1894d792015-02-04 17:09:20 -0800668 // select all the nodes in the layout:
Simon Huntac4c6f72015-02-03 19:50:53 -0800669 node = nodeG.selectAll('.node')
670 .data(network.nodes, function (d) { return d.id; });
671
Simon Hunt1894d792015-02-04 17:09:20 -0800672 // operate on existing nodes:
Simon Hunta4242de2015-02-24 17:11:55 -0800673 node.filter('.device').each(td3.deviceExisting);
674 node.filter('.host').each(td3.hostExisting);
Simon Huntac4c6f72015-02-03 19:50:53 -0800675
676 // operate on entering nodes:
677 var entering = node.enter()
678 .append('g')
679 .attr({
680 id: function (d) { return sus.safeId(d.id); },
681 class: mkSvgClass,
Simon Hunta17fa672015-08-19 18:42:22 -0700682 transform: function (d) {
683 // Need to guard against NaN here ??
684 return sus.translate(d.x, d.y);
685 },
Steven Burrows1c2a9682017-07-14 16:52:46 +0100686 opacity: 0,
Simon Huntac4c6f72015-02-03 19:50:53 -0800687 })
688 .call(drag)
Simon Hunt08f841d02015-02-10 14:39:20 -0800689 .on('mouseover', tss.nodeMouseOver)
690 .on('mouseout', tss.nodeMouseOut)
Simon Huntac4c6f72015-02-03 19:50:53 -0800691 .transition()
692 .attr('opacity', 1);
693
Simon Hunt1894d792015-02-04 17:09:20 -0800694 // augment entering nodes:
Simon Hunta4242de2015-02-24 17:11:55 -0800695 entering.filter('.device').each(td3.deviceEnter);
696 entering.filter('.host').each(td3.hostEnter);
Simon Huntac4c6f72015-02-03 19:50:53 -0800697
Simon Hunt51056592015-02-03 21:48:07 -0800698 // operate on both existing and new nodes:
Simon Hunta4242de2015-02-24 17:11:55 -0800699 td3.updateDeviceColors();
Simon Huntac4c6f72015-02-03 19:50:53 -0800700
701 // operate on exiting nodes:
702 // Note that the node is removed after 2 seconds.
703 // Sub element animations should be shorter than 2 seconds.
704 var exiting = node.exit()
705 .transition()
706 .duration(2000)
707 .style('opacity', 0)
708 .remove();
709
Simon Hunt1894d792015-02-04 17:09:20 -0800710 // exiting node specifics:
Simon Hunta4242de2015-02-24 17:11:55 -0800711 exiting.filter('.host').each(td3.hostExit);
712 exiting.filter('.device').each(td3.deviceExit);
Simon Huntac4c6f72015-02-03 19:50:53 -0800713 }
714
Simon Hunt51056592015-02-03 21:48:07 -0800715 // ==========================
Simon Hunt1894d792015-02-04 17:09:20 -0800716
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700717 function getDefaultPos(link) {
718 return {
719 x1: link.source.x,
720 y1: link.source.y,
721 x2: link.target.x,
Steven Burrows1c2a9682017-07-14 16:52:46 +0100722 y2: link.target.y,
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700723 };
724 }
725
726 // returns amount of adjustment along the normal for given link
727 function amt(numLinks, linkIdx) {
728 var gap = 6;
729 return (linkIdx - ((numLinks - 1) / 2)) * gap;
730 }
731
732 function calcMovement(d, amt, flipped) {
733 var pos = getDefaultPos(d),
734 mult = flipped ? -amt : amt,
735 dx = pos.x2 - pos.x1,
736 dy = pos.y2 - pos.y1,
737 length = Math.sqrt((dx * dx) + (dy * dy));
738
739 return {
740 x1: pos.x1 + (mult * dy / length),
741 y1: pos.y1 + (mult * -dx / length),
742 x2: pos.x2 + (mult * dy / length),
Steven Burrows1c2a9682017-07-14 16:52:46 +0100743 y2: pos.y2 + (mult * -dx / length),
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700744 };
745 }
746
747 function calcPosition() {
748 var lines = this,
749 linkSrcId;
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700750 linkNums = [];
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700751 lines.each(function (d) {
752 if (d.type() === 'hostLink') {
753 d.position = getDefaultPos(d);
754 }
755 });
756
757 function normalizeLinkSrc(link) {
758 // ensure source device is consistent across set of links
759 // temporary measure until link modeling is refactored
760 if (!linkSrcId) {
761 linkSrcId = link.source.id;
762 return false;
763 }
764
765 return link.source.id !== linkSrcId;
766 }
767
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700768 angular.forEach(network.linksByDevice, function (linkArr, key) {
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700769 var numLinks = linkArr.length,
770 link;
771
772 if (numLinks === 1) {
773 link = linkArr[0];
774 link.position = getDefaultPos(link);
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700775 link.position.multiLink = false;
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700776 } else if (numLinks >= 5) {
777 // this code is inefficient, in the future the way links
778 // are modeled will be changed
779 angular.forEach(linkArr, function (link) {
780 link.position = getDefaultPos(link);
781 link.position.multiLink = true;
782 });
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700783 linkNums.push({
784 id: key,
785 num: numLinks,
Steven Burrows1c2a9682017-07-14 16:52:46 +0100786 linkCoords: linkArr[0].position,
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700787 });
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700788 } else {
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700789 linkSrcId = null;
790 angular.forEach(linkArr, function (link, index) {
791 var offsetAmt = amt(numLinks, index),
792 needToFlip = normalizeLinkSrc(link);
793 link.position = calcMovement(link, offsetAmt, needToFlip);
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700794 link.position.multiLink = false;
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700795 });
796 }
797 });
798 }
799
Simon Hunt1894d792015-02-04 17:09:20 -0800800 function updateLinks() {
Thomas Vachuska1a989c12015-06-09 18:29:22 -0700801 if (fLinksTimer) {
802 $timeout.cancel(fLinksTimer);
803 }
804 fLinksTimer = $timeout(_updateLinks, 150);
805 }
806
Simon Hunta17fa672015-08-19 18:42:22 -0700807 // IMPLEMENTATION NOTE: _updateLinks() should NOT stop, start, or resume
808 // the force layout; that needs to be determined and implemented elsewhere
Thomas Vachuska1a989c12015-06-09 18:29:22 -0700809 function _updateLinks() {
Simon Hunt1894d792015-02-04 17:09:20 -0800810 var th = ts.theme();
811
812 link = linkG.selectAll('.link')
813 .data(network.links, function (d) { return d.key; });
814
815 // operate on existing links:
Simon Huntd5264122015-02-25 10:17:43 -0800816 link.each(function (d) {
817 // this is supposed to be an existing link, but we have observed
818 // occasions (where links are deleted and added rapidly?) where
819 // the DOM element has not been defined. So protect against that...
820 if (d.el) {
821 restyleLinkElement(d, true);
822 }
823 });
Simon Hunt1894d792015-02-04 17:09:20 -0800824
825 // operate on entering links:
826 var entering = link.enter()
827 .append('line')
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700828 .call(calcPosition)
Simon Hunt1894d792015-02-04 17:09:20 -0800829 .attr({
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700830 x1: function (d) { return d.position.x1; },
831 y1: function (d) { return d.position.y1; },
832 x2: function (d) { return d.position.x2; },
833 y2: function (d) { return d.position.y2; },
Simon Hunt1894d792015-02-04 17:09:20 -0800834 stroke: linkConfig[th].inColor,
Steven Burrows1c2a9682017-07-14 16:52:46 +0100835 'stroke-width': linkConfig.inWidth,
Simon Hunt1894d792015-02-04 17:09:20 -0800836 });
837
838 // augment links
Simon Hunta4242de2015-02-24 17:11:55 -0800839 entering.each(td3.linkEntering);
Simon Hunt1894d792015-02-04 17:09:20 -0800840
841 // operate on both existing and new links:
Steven Burrows1c2a9682017-07-14 16:52:46 +0100842 // link.each(...)
Simon Hunt1894d792015-02-04 17:09:20 -0800843
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700844 // add labels for how many links are in a thick line
845 td3.applyNumLinkLabels(linkNums, numLinkLblsG);
846
Simon Hunt1894d792015-02-04 17:09:20 -0800847 // apply or remove labels
Simon Hunta4242de2015-02-24 17:11:55 -0800848 td3.applyLinkLabels();
Simon Hunt1894d792015-02-04 17:09:20 -0800849
850 // operate on exiting links:
851 link.exit()
852 .attr('stroke-dasharray', '3 3')
Simon Hunt5724fb42015-02-05 16:59:40 -0800853 .attr('stroke', linkConfig[th].outColor)
Simon Hunt1894d792015-02-04 17:09:20 -0800854 .style('opacity', 0.5)
855 .transition()
856 .duration(1500)
857 .attr({
858 'stroke-dasharray': '3 12',
Steven Burrows1c2a9682017-07-14 16:52:46 +0100859 'stroke-width': linkConfig.outWidth,
Simon Hunt1894d792015-02-04 17:09:20 -0800860 })
861 .style('opacity', 0.0)
862 .remove();
Simon Hunt1894d792015-02-04 17:09:20 -0800863 }
864
Simon Huntac4c6f72015-02-03 19:50:53 -0800865
866 // ==========================
Simon Hunt737c89f2015-01-28 12:23:19 -0800867 // force layout tick function
Simon Hunt737c89f2015-01-28 12:23:19 -0800868
Simon Hunt5724fb42015-02-05 16:59:40 -0800869 function fResume() {
Simon Huntc3c5b672015-02-20 11:32:13 -0800870 if (!tos.isOblique()) {
Simon Hunt5724fb42015-02-05 16:59:40 -0800871 force.resume();
872 }
873 }
874
875 function fStart() {
Simon Huntc3c5b672015-02-20 11:32:13 -0800876 if (!tos.isOblique()) {
Simon Hunta17fa672015-08-19 18:42:22 -0700877 if (fTimer) {
878 $timeout.cancel(fTimer);
879 }
880 fTimer = $timeout(function () {
Steven Burrows1c2a9682017-07-14 16:52:46 +0100881 $log.debug('Starting force-layout');
Simon Hunta17fa672015-08-19 18:42:22 -0700882 force.start();
883 }, 200);
Simon Hunt5724fb42015-02-05 16:59:40 -0800884 }
885 }
886
887 var tickStuff = {
888 nodeAttr: {
Simon Hunta17fa672015-08-19 18:42:22 -0700889 transform: function (d) {
890 var dx = isNaN(d.x) ? 0 : d.x,
891 dy = isNaN(d.y) ? 0 : d.y;
892 return sus.translate(dx, dy);
Steven Burrows1c2a9682017-07-14 16:52:46 +0100893 },
Simon Hunt5724fb42015-02-05 16:59:40 -0800894 },
895 linkAttr: {
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700896 x1: function (d) { return d.position.x1; },
897 y1: function (d) { return d.position.y1; },
898 x2: function (d) { return d.position.x2; },
Steven Burrows1c2a9682017-07-14 16:52:46 +0100899 y2: function (d) { return d.position.y2; },
Simon Hunt5724fb42015-02-05 16:59:40 -0800900 },
901 linkLabelAttr: {
902 transform: function (d) {
Simon Huntdc6adea2015-02-09 22:29:36 -0800903 var lnk = tms.findLinkById(d.key);
Simon Hunt5724fb42015-02-05 16:59:40 -0800904 if (lnk) {
Carmelo Casconed01eda62016-08-02 10:19:15 -0700905 return td3.transformLabel(lnk.position, d.key);
Simon Hunt5724fb42015-02-05 16:59:40 -0800906 }
Steven Burrows1c2a9682017-07-14 16:52:46 +0100907 },
908 },
Simon Hunt5724fb42015-02-05 16:59:40 -0800909 };
910
911 function tick() {
Simon Hunt3ab20282015-02-26 20:32:19 -0800912 // guard against null (which can happen when our view pages out)...
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700913 if (node && node.size()) {
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700914 node.attr(tickStuff.nodeAttr);
915 }
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700916 if (link && link.size()) {
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700917 link.call(calcPosition)
918 .attr(tickStuff.linkAttr);
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700919 td3.applyNumLinkLabels(linkNums, numLinkLblsG);
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700920 }
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700921 if (linkLabel && linkLabel.size()) {
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700922 linkLabel.attr(tickStuff.linkLabelAttr);
923 }
Simon Hunt737c89f2015-01-28 12:23:19 -0800924 }
925
926
Simon Huntac4c6f72015-02-03 19:50:53 -0800927 // ==========================
928 // === MOUSE GESTURE HANDLERS
929
Simon Hunt205099e2015-02-07 13:12:01 -0800930 function zoomingOrPanning(ev) {
931 return ev.metaKey || ev.altKey;
Simon Hunt445e8152015-02-06 13:00:12 -0800932 }
933
934 function atDragEnd(d) {
935 // once we've finished moving, pin the node in position
936 d.fixed = true;
937 d3.select(this).classed('fixed', true);
938 sendUpdateMeta(d);
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700939 tss.clickConsumed(true);
Simon Hunt445e8152015-02-06 13:00:12 -0800940 }
941
942 // predicate that indicates when dragging is active
943 function dragEnabled() {
944 var ev = d3.event.sourceEvent;
945 // nodeLock means we aren't allowing nodes to be dragged...
Simon Hunt205099e2015-02-07 13:12:01 -0800946 return !nodeLock && !zoomingOrPanning(ev);
Simon Hunt445e8152015-02-06 13:00:12 -0800947 }
948
949 // predicate that indicates when clicking is active
950 function clickEnabled() {
951 return true;
952 }
Simon Hunt737c89f2015-01-28 12:23:19 -0800953
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700954 // =============================================
955 // function entry points for overlay module
Simon Huntf542d842015-02-11 16:20:33 -0800956
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700957 // TODO: find an automatic way of tracking via the "showHighlights" events
Simon Hunte50829c2015-06-09 08:39:28 -0700958 var allTrafficClasses = 'primary secondary optical animated ' +
Simon Hunt21281fd2017-03-30 22:28:28 -0700959 'port-traffic-green port-traffic-yellow port-traffic-orange ' +
960 'port-traffic-red';
Simon Huntf542d842015-02-11 16:20:33 -0800961
962 function clearLinkTrafficStyle() {
963 link.style('stroke-width', null)
964 .classed(allTrafficClasses, false);
965 }
966
967 function removeLinkLabels() {
968 network.links.forEach(function (d) {
969 d.label = '';
970 });
971 }
Simon Hunt737c89f2015-01-28 12:23:19 -0800972
Simon Hunte9343f32015-10-21 18:07:46 -0700973 function clearNodeDeco() {
974 node.selectAll('g.badge').remove();
975 }
976
977 function removeNodeBadges() {
978 network.nodes.forEach(function (d) {
979 d.badge = null;
980 });
981 }
982
Simon Hunta4242de2015-02-24 17:11:55 -0800983 function updateLinkLabelModel() {
984 // create the backing data for showing labels..
985 var data = [];
986 link.each(function (d) {
987 if (d.label) {
988 data.push({
989 id: 'lab-' + d.key,
990 key: d.key,
991 label: d.label,
Steven Burrows1c2a9682017-07-14 16:52:46 +0100992 ldata: d,
Simon Hunta4242de2015-02-24 17:11:55 -0800993 });
994 }
995 });
996
997 linkLabel = linkLabelG.selectAll('.linkLabel')
998 .data(data, function (d) { return d.id; });
999 }
1000
Simon Hunt737c89f2015-01-28 12:23:19 -08001001 // ==========================
Simon Huntac4c6f72015-02-03 19:50:53 -08001002 // Module definition
Simon Hunt737c89f2015-01-28 12:23:19 -08001003
Simon Huntdc6adea2015-02-09 22:29:36 -08001004 function mkModelApi(uplink) {
1005 return {
1006 projection: uplink.projection,
1007 network: network,
1008 restyleLinkElement: restyleLinkElement,
Steven Burrows1c2a9682017-07-14 16:52:46 +01001009 removeLinkElement: removeLinkElement,
Simon Huntdc6adea2015-02-09 22:29:36 -08001010 };
1011 }
1012
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001013 function mkD3Api() {
Simon Hunta4242de2015-02-24 17:11:55 -08001014 return {
1015 node: function () { return node; },
1016 link: function () { return link; },
1017 linkLabel: function () { return linkLabel; },
1018 instVisible: function () { return tis.isVisible(); },
1019 posNode: tms.positionNode,
1020 showHosts: function () { return showHosts; },
1021 restyleLinkElement: restyleLinkElement,
Bri Prebilic Cole80401762015-07-16 11:36:18 -07001022 updateLinkLabelModel: updateLinkLabelModel,
Steven Burrowsf17f0ab2017-04-11 11:03:58 -07001023 linkConfig: function () { return linkConfig; },
1024 deviceScale: deviceScale,
Steven Burrows1c2a9682017-07-14 16:52:46 +01001025 linkWidthScale: linkWidthScale,
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -07001026 };
Simon Hunta4242de2015-02-24 17:11:55 -08001027 }
1028
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001029 function mkSelectApi() {
Simon Hunt08f841d02015-02-10 14:39:20 -08001030 return {
1031 node: function () { return node; },
1032 zoomingOrPanning: zoomingOrPanning,
Simon Hunt0c6b2d32015-03-26 17:46:29 -07001033 updateDeviceColors: td3.updateDeviceColors,
Steven Burrows1c2a9682017-07-14 16:52:46 +01001034 deselectAllLinks: tls.deselectAllLinks,
Simon Hunt08f841d02015-02-10 14:39:20 -08001035 };
1036 }
1037
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001038 function mkTrafficApi() {
1039 return {
1040 hovered: tss.hovered,
1041 somethingSelected: tss.somethingSelected,
Steven Burrows1c2a9682017-07-14 16:52:46 +01001042 selectOrder: tss.selectOrder,
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001043 };
1044 }
1045
1046 function mkOverlayApi() {
Simon Huntf542d842015-02-11 16:20:33 -08001047 return {
Simon Hunte9343f32015-10-21 18:07:46 -07001048 clearNodeDeco: clearNodeDeco,
1049 removeNodeBadges: removeNodeBadges,
Simon Huntf542d842015-02-11 16:20:33 -08001050 clearLinkTrafficStyle: clearLinkTrafficStyle,
1051 removeLinkLabels: removeLinkLabels,
Simon Hunt743a8492015-08-25 16:18:19 -07001052 findLinkById: tms.findLinkById,
Simon Hunt94f7dae2015-08-26 17:40:59 -07001053 findNodeById: nodeById,
Simon Huntf542d842015-02-11 16:20:33 -08001054 updateLinks: updateLinks,
Simon Hunt743a8492015-08-25 16:18:19 -07001055 updateNodes: updateNodes,
1056 supLayers: suppressLayers,
1057 unsupNode: unsuppressNode,
Steven Burrows1c2a9682017-07-14 16:52:46 +01001058 unsupLink: unsuppressLink,
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -07001059 };
Simon Huntf542d842015-02-11 16:20:33 -08001060 }
1061
Simon Huntc3c5b672015-02-20 11:32:13 -08001062 function mkObliqueApi(uplink, fltr) {
Simon Hunt96f88c62015-02-19 17:57:25 -08001063 return {
Steven Burrows1c2a9682017-07-14 16:52:46 +01001064 force: function () { return force; },
Simon Huntc3c5b672015-02-20 11:32:13 -08001065 zoomLayer: uplink.zoomLayer,
Steven Burrows1c2a9682017-07-14 16:52:46 +01001066 nodeGBBox: function () { return nodeG.node().getBBox(); },
Simon Hunt96f88c62015-02-19 17:57:25 -08001067 node: function () { return node; },
Simon Huntc3c5b672015-02-20 11:32:13 -08001068 link: function () { return link; },
1069 linkLabel: function () { return linkLabel; },
1070 nodes: function () { return network.nodes; },
1071 tickStuff: tickStuff,
1072 nodeLock: function (b) {
1073 var old = nodeLock;
1074 nodeLock = b;
1075 return old;
1076 },
1077 opacifyMap: uplink.opacifyMap,
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -07001078 inLayer: fltr.inLayer,
Bri Prebilic Cole80401762015-07-16 11:36:18 -07001079 calcLinkPos: calcPosition,
1080 applyNumLinkLabels: function () {
1081 td3.applyNumLinkLabels(linkNums, numLinkLblsG);
Steven Burrows1c2a9682017-07-14 16:52:46 +01001082 },
Simon Hunt96f88c62015-02-19 17:57:25 -08001083 };
1084 }
1085
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001086 function mkFilterApi() {
Simon Hunteb0fa052015-02-17 19:20:28 -08001087 return {
1088 node: function () { return node; },
Steven Burrows1c2a9682017-07-14 16:52:46 +01001089 link: function () { return link; },
Simon Hunteb0fa052015-02-17 19:20:28 -08001090 };
1091 }
1092
Simon Hunt9e2104c2015-02-26 10:48:59 -08001093 function mkLinkApi(svg, uplink) {
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001094 return {
1095 svg: svg,
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001096 zoomer: uplink.zoomer(),
1097 network: network,
Simon Hunt1a5301e2015-02-25 15:31:25 -08001098 portLabelG: function () { return portLabelG; },
Steven Burrows1c2a9682017-07-14 16:52:46 +01001099 showHosts: function () { return showHosts; },
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001100 };
1101 }
1102
Simon Huntf51bf462016-06-29 16:22:57 -07001103 function updateLinksAndNodes() {
1104 updateLinks();
1105 updateNodes();
1106 }
Steven Burrowsec1f45c2016-08-08 16:14:41 +01001107
Simon Hunt737c89f2015-01-28 12:23:19 -08001108 angular.module('ovTopo')
1109 .factory('TopoForceService',
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -07001110 ['$log', '$timeout', 'FnService', 'SvgUtilService',
Simon Hunt86b7c882015-04-02 23:06:08 -07001111 'ThemeService', 'FlashService', 'WebSocketService',
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001112 'TopoOverlayService', 'TopoInstService', 'TopoModelService',
Simon Hunta4242de2015-02-24 17:11:55 -08001113 'TopoD3Service', 'TopoSelectService', 'TopoTrafficService',
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001114 'TopoObliqueService', 'TopoFilterService', 'TopoLinkService',
Andrea Campanella732ea832017-02-06 09:25:59 -08001115 'TopoProtectedIntentsService',
Simon Hunt737c89f2015-01-28 12:23:19 -08001116
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001117 function (_$log_, _$timeout_, _fs_, _sus_, _ts_, _flash_, _wss_, _tov_,
Andrea Campanella732ea832017-02-06 09:25:59 -08001118 _tis_, _tms_, _td3_, _tss_, _tts_, _tos_, _fltr_, _tls_, _tpis_) {
Simon Hunt737c89f2015-01-28 12:23:19 -08001119 $log = _$log_;
Simon Hunt86b7c882015-04-02 23:06:08 -07001120 $timeout = _$timeout_;
Simon Hunt1894d792015-02-04 17:09:20 -08001121 fs = _fs_;
Simon Hunt737c89f2015-01-28 12:23:19 -08001122 sus = _sus_;
Simon Huntac4c6f72015-02-03 19:50:53 -08001123 ts = _ts_;
Simon Hunt5724fb42015-02-05 16:59:40 -08001124 flash = _flash_;
Simon Hunt237676b52015-03-10 19:04:26 -07001125 wss = _wss_;
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001126 tov = _tov_;
Simon Huntac4c6f72015-02-03 19:50:53 -08001127 tis = _tis_;
Simon Hunt3a6eec02015-02-09 21:16:43 -08001128 tms = _tms_;
Simon Hunta4242de2015-02-24 17:11:55 -08001129 td3 = _td3_;
Simon Hunt08f841d02015-02-10 14:39:20 -08001130 tss = _tss_;
Simon Huntf542d842015-02-11 16:20:33 -08001131 tts = _tts_;
Simon Hunt96f88c62015-02-19 17:57:25 -08001132 tos = _tos_;
Simon Hunteb0fa052015-02-17 19:20:28 -08001133 fltr = _fltr_;
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001134 tls = _tls_;
Andrea Campanella732ea832017-02-06 09:25:59 -08001135 tpis = _tpis_;
Simon Hunt737c89f2015-01-28 12:23:19 -08001136
Simon Huntf51bf462016-06-29 16:22:57 -07001137 ts.addListener(updateLinksAndNodes);
Simon Hunta142dd22015-02-12 22:07:51 -08001138
Simon Hunt737c89f2015-01-28 12:23:19 -08001139 // forceG is the SVG group to display the force layout in
Simon Huntdc6adea2015-02-09 22:29:36 -08001140 // uplink is the api from the main topo source file
Simon Hunt3a6eec02015-02-09 21:16:43 -08001141 // dim is the initial dimensions of the SVG as [w,h]
Simon Hunt737c89f2015-01-28 12:23:19 -08001142 // opts are, well, optional :)
Simon Hunt3ab20282015-02-26 20:32:19 -08001143 function initForce(_svg_, forceG, _uplink_, _dim_, opts) {
Simon Hunt1894d792015-02-04 17:09:20 -08001144 uplink = _uplink_;
Simon Hunt3a6eec02015-02-09 21:16:43 -08001145 dim = _dim_;
Simon Hunt3ab20282015-02-26 20:32:19 -08001146 svg = _svg_;
1147
1148 lu = network.lookup;
1149 rlk = network.revLinkToKey;
Simon Hunt3a6eec02015-02-09 21:16:43 -08001150
1151 $log.debug('initForce().. dim = ' + dim);
1152
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001153 tov.setApi(mkOverlayApi(), tss);
Simon Huntdc6adea2015-02-09 22:29:36 -08001154 tms.initModel(mkModelApi(uplink), dim);
Steven Burrowsf17f0ab2017-04-11 11:03:58 -07001155 td3.initD3(mkD3Api(), uplink.zoomer());
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001156 tss.initSelect(mkSelectApi());
1157 tts.initTraffic(mkTrafficApi());
Andrea Campanella732ea832017-02-06 09:25:59 -08001158 tpis.initProtectedIntents(mkTrafficApi());
Simon Huntc3c5b672015-02-20 11:32:13 -08001159 tos.initOblique(mkObliqueApi(uplink, fltr));
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001160 fltr.initFilter(mkFilterApi());
Simon Hunt9e2104c2015-02-26 10:48:59 -08001161 tls.initLink(mkLinkApi(svg, uplink), td3);
Simon Hunta11b4eb2015-01-28 16:20:50 -08001162
Simon Hunt737c89f2015-01-28 12:23:19 -08001163 settings = angular.extend({}, defaultSettings, opts);
1164
1165 linkG = forceG.append('g').attr('id', 'topo-links');
1166 linkLabelG = forceG.append('g').attr('id', 'topo-linkLabels');
Bri Prebilic Cole80401762015-07-16 11:36:18 -07001167 numLinkLblsG = forceG.append('g').attr('id', 'topo-numLinkLabels');
Simon Hunt737c89f2015-01-28 12:23:19 -08001168 nodeG = forceG.append('g').attr('id', 'topo-nodes');
Simon Hunt1a5301e2015-02-25 15:31:25 -08001169 portLabelG = forceG.append('g').attr('id', 'topo-portLabels');
Simon Hunt737c89f2015-01-28 12:23:19 -08001170
1171 link = linkG.selectAll('.link');
1172 linkLabel = linkLabelG.selectAll('.linkLabel');
1173 node = nodeG.selectAll('.node');
1174
1175 force = d3.layout.force()
Simon Hunt3a6eec02015-02-09 21:16:43 -08001176 .size(dim)
Simon Hunt737c89f2015-01-28 12:23:19 -08001177 .nodes(network.nodes)
1178 .links(network.links)
1179 .gravity(settings.gravity)
1180 .friction(settings.friction)
1181 .charge(settings.charge._def_)
1182 .linkDistance(settings.linkDistance._def_)
1183 .linkStrength(settings.linkStrength._def_)
1184 .on('tick', tick);
1185
1186 drag = sus.createDragBehavior(force,
Simon Hunt08f841d02015-02-10 14:39:20 -08001187 tss.selectObject, atDragEnd, dragEnabled, clickEnabled);
Simon Hunt737c89f2015-01-28 12:23:19 -08001188 }
1189
Simon Hunt3a6eec02015-02-09 21:16:43 -08001190 function newDim(_dim_) {
1191 dim = _dim_;
1192 force.size(dim);
1193 tms.newDim(dim);
Simon Hunt737c89f2015-01-28 12:23:19 -08001194 }
1195
Simon Hunt3a6eec02015-02-09 21:16:43 -08001196 function destroyForce() {
Simon Hunt3ab20282015-02-26 20:32:19 -08001197 force.stop();
1198
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001199 tls.destroyLink();
Simon Hunt96f88c62015-02-19 17:57:25 -08001200 tos.destroyOblique();
Simon Huntf542d842015-02-11 16:20:33 -08001201 tts.destroyTraffic();
Andrea Campanella732ea832017-02-06 09:25:59 -08001202 tpis.destroyProtectedIntents();
Simon Huntf542d842015-02-11 16:20:33 -08001203 tss.destroySelect();
Simon Hunta4242de2015-02-24 17:11:55 -08001204 td3.destroyD3();
Simon Huntf542d842015-02-11 16:20:33 -08001205 tms.destroyModel();
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001206 // note: no need to destroy overlay service
Simon Huntf51bf462016-06-29 16:22:57 -07001207 ts.removeListener(updateLinksAndNodes);
Simon Hunt3ab20282015-02-26 20:32:19 -08001208
1209 // clean up the DOM
1210 svg.selectAll('g').remove();
1211 svg.selectAll('defs').remove();
1212
1213 // clean up internal state
1214 network.nodes = [];
1215 network.links = [];
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -07001216 network.linksByDevice = {};
Simon Hunt3ab20282015-02-26 20:32:19 -08001217 network.lookup = {};
1218 network.revLinkToKey = {};
1219
Bri Prebilic Cole80401762015-07-16 11:36:18 -07001220 linkNums = [];
1221
1222 linkG = linkLabelG = numLinkLblsG = nodeG = portLabelG = null;
Simon Hunt3ab20282015-02-26 20:32:19 -08001223 link = linkLabel = node = null;
1224 force = drag = null;
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -07001225
1226 // clean up $timeout promises
Simon Hunta17fa672015-08-19 18:42:22 -07001227 if (fTimer) {
1228 $timeout.cancel(fTimer);
1229 }
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -07001230 if (fNodesTimer) {
1231 $timeout.cancel(fNodesTimer);
1232 }
1233 if (fLinksTimer) {
1234 $timeout.cancel(fLinksTimer);
1235 }
Simon Hunt3a6eec02015-02-09 21:16:43 -08001236 }
1237
Simon Hunt737c89f2015-01-28 12:23:19 -08001238 return {
1239 initForce: initForce,
Simon Hunt3a6eec02015-02-09 21:16:43 -08001240 newDim: newDim,
1241 destroyForce: destroyForce,
Simon Huntac4c6f72015-02-03 19:50:53 -08001242
Simon Hunta4242de2015-02-24 17:11:55 -08001243 updateDeviceColors: td3.updateDeviceColors,
Simon Hunt5724fb42015-02-05 16:59:40 -08001244 toggleHosts: toggleHosts,
Simon Hunt9e2104c2015-02-26 10:48:59 -08001245 togglePorts: tls.togglePorts,
Simon Hunt5724fb42015-02-05 16:59:40 -08001246 toggleOffline: toggleOffline,
1247 cycleDeviceLabels: cycleDeviceLabels,
Simon Hunt10618f62017-06-15 19:30:52 -07001248 cycleHostLabels: cycleHostLabels,
Simon Hunt445e8152015-02-06 13:00:12 -08001249 unpin: unpin,
Simon Hunta142dd22015-02-12 22:07:51 -08001250 showMastership: showMastership,
Simon Hunt86b7c882015-04-02 23:06:08 -07001251 showBadLinks: showBadLinks,
Steven Burrowsf17f0ab2017-04-11 11:03:58 -07001252 setNodeScale: setNodeScale,
Simon Huntac4c6f72015-02-03 19:50:53 -08001253
Simon Huntfd7106c2016-02-09 15:05:26 -08001254 resetAllLocations: resetAllLocations,
Simon Huntac4c6f72015-02-03 19:50:53 -08001255 addDevice: addDevice,
Simon Hunt1894d792015-02-04 17:09:20 -08001256 updateDevice: updateDevice,
1257 removeDevice: removeDevice,
1258 addHost: addHost,
1259 updateHost: updateHost,
Simon Hunt95d56fd2015-11-12 11:06:44 -08001260 moveHost: moveHost,
Simon Hunt1894d792015-02-04 17:09:20 -08001261 removeHost: removeHost,
1262 addLink: addLink,
1263 updateLink: updateLink,
Simon Hunt4a6b54b2015-10-27 22:08:25 -07001264 removeLink: removeLink,
Steven Burrows1c2a9682017-07-14 16:52:46 +01001265 topoStartDone: topoStartDone,
Simon Hunt737c89f2015-01-28 12:23:19 -08001266 };
1267 }]);
1268}());