blob: 8508f852b16ae98041d5ca51837cbe5dbbe875c0 [file] [log] [blame]
Simon Hunt737c89f2015-01-28 12:23:19 -08001/*
2 * Copyright 2015 Open Networking Laboratory
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/*
Simon Hunt3a6eec02015-02-09 21:16:43 -080018 ONOS GUI -- Topology Force Module.
19 Visualization of the topology in an SVG layer, using a D3 Force Layout.
Simon Hunt737c89f2015-01-28 12:23:19 -080020 */
21
22(function () {
23 'use strict';
24
25 // injected refs
Simon Hunt8d22c4b2015-08-06 16:24:43 -070026 var $log, $timeout, fs, sus, ts, flash, wss, tov,
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -070027 tis, tms, td3, tss, tts, tos, fltr, tls, uplink, svg;
Simon Huntac4c6f72015-02-03 19:50:53 -080028
29 // configuration
Simon Hunt1894d792015-02-04 17:09:20 -080030 var linkConfig = {
31 light: {
32 baseColor: '#666',
33 inColor: '#66f',
Simon Hunt3a6eec02015-02-09 21:16:43 -080034 outColor: '#f00'
Simon Hunt1894d792015-02-04 17:09:20 -080035 },
36 dark: {
Simon Hunt5724fb42015-02-05 16:59:40 -080037 baseColor: '#aaa',
Simon Hunt1894d792015-02-04 17:09:20 -080038 inColor: '#66f',
Simon Hunt5724fb42015-02-05 16:59:40 -080039 outColor: '#f66'
Simon Hunt1894d792015-02-04 17:09:20 -080040 },
41 inWidth: 12,
42 outWidth: 10
43 };
44
Simon Hunt737c89f2015-01-28 12:23:19 -080045 // internal state
Simon Huntac4c6f72015-02-03 19:50:53 -080046 var settings, // merged default settings and options
Simon Hunt737c89f2015-01-28 12:23:19 -080047 force, // force layout object
48 drag, // drag behavior handler
49 network = {
50 nodes: [],
51 links: [],
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -070052 linksByDevice: {},
Simon Hunt737c89f2015-01-28 12:23:19 -080053 lookup: {},
54 revLinkToKey: {}
Simon Huntac4c6f72015-02-03 19:50:53 -080055 },
Simon Hunt3ab20282015-02-26 20:32:19 -080056 lu, // shorthand for lookup
57 rlk, // shorthand for revLinktoKey
Simon Hunta142dd22015-02-12 22:07:51 -080058 showHosts = false, // whether hosts are displayed
Simon Hunt5724fb42015-02-05 16:59:40 -080059 showOffline = true, // whether offline devices are displayed
Simon Hunt445e8152015-02-06 13:00:12 -080060 nodeLock = false, // whether nodes can be dragged or not (locked)
Simon Hunta17fa672015-08-19 18:42:22 -070061 fTimer, // timer for delayed force layout
Thomas Vachuska1a989c12015-06-09 18:29:22 -070062 fNodesTimer, // timer for delayed nodes update
63 fLinksTimer, // timer for delayed links update
Bri Prebilic Cole80401762015-07-16 11:36:18 -070064 dim, // the dimensions of the force layout [w,h]
65 linkNums = []; // array of link number labels
Simon Hunt737c89f2015-01-28 12:23:19 -080066
67 // SVG elements;
Bri Prebilic Cole80401762015-07-16 11:36:18 -070068 var linkG, linkLabelG, numLinkLblsG, portLabelG, nodeG;
Simon Hunt737c89f2015-01-28 12:23:19 -080069
70 // D3 selections;
71 var link, linkLabel, node;
72
73 // default settings for force layout
74 var defaultSettings = {
75 gravity: 0.4,
76 friction: 0.7,
77 charge: {
78 // note: key is node.class
79 device: -8000,
80 host: -5000,
81 _def_: -12000
82 },
83 linkDistance: {
84 // note: key is link.type
85 direct: 100,
86 optical: 120,
87 hostLink: 3,
88 _def_: 50
89 },
90 linkStrength: {
91 // note: key is link.type
92 // range: {0.0 ... 1.0}
93 //direct: 1.0,
94 //optical: 1.0,
95 //hostLink: 1.0,
96 _def_: 1.0
97 }
98 };
99
100
Simon Huntac4c6f72015-02-03 19:50:53 -0800101 // ==========================
102 // === EVENT HANDLERS
103
104 function addDevice(data) {
105 var id = data.id,
106 d;
107
Simon Hunt1894d792015-02-04 17:09:20 -0800108 uplink.showNoDevs(false);
Simon Huntac4c6f72015-02-03 19:50:53 -0800109
110 // although this is an add device event, if we already have the
111 // device, treat it as an update instead..
Simon Hunt1894d792015-02-04 17:09:20 -0800112 if (lu[id]) {
Simon Huntac4c6f72015-02-03 19:50:53 -0800113 updateDevice(data);
114 return;
115 }
116
Simon Hunt3a6eec02015-02-09 21:16:43 -0800117 d = tms.createDeviceNode(data);
Simon Huntac4c6f72015-02-03 19:50:53 -0800118 network.nodes.push(d);
Simon Hunt1894d792015-02-04 17:09:20 -0800119 lu[id] = d;
Simon Huntac4c6f72015-02-03 19:50:53 -0800120 updateNodes();
Simon Hunta17fa672015-08-19 18:42:22 -0700121 fStart();
Simon Huntac4c6f72015-02-03 19:50:53 -0800122 }
123
124 function updateDevice(data) {
125 var id = data.id,
Simon Hunt1894d792015-02-04 17:09:20 -0800126 d = lu[id],
Simon Huntac4c6f72015-02-03 19:50:53 -0800127 wasOnline;
128
129 if (d) {
130 wasOnline = d.online;
131 angular.extend(d, data);
Simon Hunt3a6eec02015-02-09 21:16:43 -0800132 if (tms.positionNode(d, true)) {
Simon Hunt445e8152015-02-06 13:00:12 -0800133 sendUpdateMeta(d);
Simon Huntac4c6f72015-02-03 19:50:53 -0800134 }
135 updateNodes();
136 if (wasOnline !== d.online) {
Simon Huntdc6adea2015-02-09 22:29:36 -0800137 tms.findAttachedLinks(d.id).forEach(restyleLinkElement);
Simon Hunt5724fb42015-02-05 16:59:40 -0800138 updateOfflineVisibility(d);
Simon Huntac4c6f72015-02-03 19:50:53 -0800139 }
Simon Huntac4c6f72015-02-03 19:50:53 -0800140 }
141 }
142
Simon Hunt1894d792015-02-04 17:09:20 -0800143 function removeDevice(data) {
144 var id = data.id,
145 d = lu[id];
146 if (d) {
147 removeDeviceElement(d);
Simon Hunt1894d792015-02-04 17:09:20 -0800148 }
149 }
150
151 function addHost(data) {
152 var id = data.id,
153 d, lnk;
154
155 // although this is an add host event, if we already have the
156 // host, treat it as an update instead..
157 if (lu[id]) {
158 updateHost(data);
159 return;
160 }
161
Simon Hunt3a6eec02015-02-09 21:16:43 -0800162 d = tms.createHostNode(data);
Simon Hunt1894d792015-02-04 17:09:20 -0800163 network.nodes.push(d);
164 lu[id] = d;
Simon Hunt1894d792015-02-04 17:09:20 -0800165 updateNodes();
166
Simon Hunt3a6eec02015-02-09 21:16:43 -0800167 lnk = tms.createHostLink(data);
Simon Hunt1894d792015-02-04 17:09:20 -0800168 if (lnk) {
Simon Hunt1894d792015-02-04 17:09:20 -0800169 d.linkData = lnk; // cache ref on its host
170 network.links.push(lnk);
171 lu[d.ingress] = lnk;
172 lu[d.egress] = lnk;
173 updateLinks();
174 }
Simon Hunta17fa672015-08-19 18:42:22 -0700175 fStart();
Simon Hunt1894d792015-02-04 17:09:20 -0800176 }
177
178 function updateHost(data) {
179 var id = data.id,
180 d = lu[id];
181 if (d) {
182 angular.extend(d, data);
Simon Hunt3a6eec02015-02-09 21:16:43 -0800183 if (tms.positionNode(d, true)) {
Simon Hunt445e8152015-02-06 13:00:12 -0800184 sendUpdateMeta(d);
Simon Hunt1894d792015-02-04 17:09:20 -0800185 }
186 updateNodes();
Simon Hunt1894d792015-02-04 17:09:20 -0800187 }
188 }
189
Simon Hunt95d56fd2015-11-12 11:06:44 -0800190 function moveHost(data) {
191 var id = data.id,
192 d = lu[id],
193 lnk;
194 if (d) {
195 // first remove the old host link
196 removeLinkElement(d.linkData);
197
198 // merge new data
199 angular.extend(d, data);
200 if (tms.positionNode(d, true)) {
201 sendUpdateMeta(d);
202 }
203
204 // now create a new host link
205 lnk = tms.createHostLink(data);
206 if (lnk) {
207 d.linkData = lnk;
208 network.links.push(lnk);
209 lu[d.ingress] = lnk;
210 lu[d.egress] = lnk;
211 }
212
213 updateNodes();
214 updateLinks();
215 fResume();
216 }
217 }
218
Simon Hunt1894d792015-02-04 17:09:20 -0800219 function removeHost(data) {
220 var id = data.id,
221 d = lu[id];
222 if (d) {
223 removeHostElement(d, true);
Simon Hunt1894d792015-02-04 17:09:20 -0800224 }
225 }
226
227 function addLink(data) {
Simon Huntdc6adea2015-02-09 22:29:36 -0800228 var result = tms.findLink(data, 'add'),
Simon Hunt1894d792015-02-04 17:09:20 -0800229 bad = result.badLogic,
230 d = result.ldata;
231
232 if (bad) {
Simon Hunteb18f522016-01-28 19:22:23 -0800233 $log.debug(bad + ': ' + link.id);
Simon Hunt1894d792015-02-04 17:09:20 -0800234 return;
235 }
236
237 if (d) {
238 // we already have a backing store link for src/dst nodes
239 addLinkUpdate(d, data);
240 return;
241 }
242
243 // no backing store link yet
Simon Hunt3a6eec02015-02-09 21:16:43 -0800244 d = tms.createLink(data);
Simon Hunt1894d792015-02-04 17:09:20 -0800245 if (d) {
246 network.links.push(d);
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700247 aggregateLink(d, data);
Simon Hunt1894d792015-02-04 17:09:20 -0800248 lu[d.key] = d;
249 updateLinks();
Simon Hunta17fa672015-08-19 18:42:22 -0700250 fStart();
Simon Hunt1894d792015-02-04 17:09:20 -0800251 }
252 }
253
254 function updateLink(data) {
Simon Huntdc6adea2015-02-09 22:29:36 -0800255 var result = tms.findLink(data, 'update'),
Simon Hunt1894d792015-02-04 17:09:20 -0800256 bad = result.badLogic;
257 if (bad) {
Simon Hunteb18f522016-01-28 19:22:23 -0800258 $log.debug(bad + ': ' + link.id);
Simon Hunt1894d792015-02-04 17:09:20 -0800259 return;
260 }
Simon Hunteb18f522016-01-28 19:22:23 -0800261 result.updateWith(data);
Simon Hunt1894d792015-02-04 17:09:20 -0800262 }
263
264 function removeLink(data) {
Simon Hunta4242de2015-02-24 17:11:55 -0800265 var result = tms.findLink(data, 'remove');
266
267 if (!result.badLogic) {
268 result.removeRawLink();
Simon Hunt1894d792015-02-04 17:09:20 -0800269 }
Simon Hunt1894d792015-02-04 17:09:20 -0800270 }
271
Simon Hunt4a6b54b2015-10-27 22:08:25 -0700272 function topoStartDone(data) {
273 // called when the initial barrage of data has been sent from server
274 uplink.topoStartDone();
275 }
276
Simon Hunt1894d792015-02-04 17:09:20 -0800277 // ========================
278
Simon Hunt94f7dae2015-08-26 17:40:59 -0700279 function nodeById(id) {
280 return lu[id];
281 }
282
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700283 function makeNodeKey(node1, node2) {
284 return node1 + '-' + node2;
285 }
286
287 function findNodePair(key, keyRev) {
288 if (network.linksByDevice[key]) {
289 return key;
290 } else if (network.linksByDevice[keyRev]) {
291 return keyRev;
292 } else {
293 return false;
294 }
295 }
296
297 function aggregateLink(ldata, link) {
298 var key = makeNodeKey(link.src, link.dst),
299 keyRev = makeNodeKey(link.dst, link.src),
300 found = findNodePair(key, keyRev);
301
302 if (found) {
303 network.linksByDevice[found].push(ldata);
304 ldata.devicePair = found;
305 } else {
306 network.linksByDevice[key] = [ ldata ];
307 ldata.devicePair = key;
308 }
309 }
310
Simon Hunt1894d792015-02-04 17:09:20 -0800311 function addLinkUpdate(ldata, link) {
312 // add link event, but we already have the reverse link installed
313 ldata.fromTarget = link;
Simon Huntdc6adea2015-02-09 22:29:36 -0800314 rlk[link.id] = ldata.key;
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700315 // possible solution to el being undefined in restyleLinkElement:
316 //_updateLinks();
Simon Hunt1894d792015-02-04 17:09:20 -0800317 restyleLinkElement(ldata);
318 }
319
Simon Hunt1894d792015-02-04 17:09:20 -0800320
321 var widthRatio = 1.4,
322 linkScale = d3.scale.linear()
323 .domain([1, 12])
324 .range([widthRatio, 12 * widthRatio])
Simon Hunt5724fb42015-02-05 16:59:40 -0800325 .clamp(true),
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800326 allLinkTypes = 'direct indirect optical tunnel',
327 allLinkSubTypes = 'inactive not-permitted';
Simon Hunt1894d792015-02-04 17:09:20 -0800328
Simon Hunta142dd22015-02-12 22:07:51 -0800329 function restyleLinkElement(ldata, immediate) {
Simon Hunt1894d792015-02-04 17:09:20 -0800330 // this fn's job is to look at raw links and decide what svg classes
331 // need to be applied to the line element in the DOM
332 var th = ts.theme(),
333 el = ldata.el,
334 type = ldata.type(),
335 lw = ldata.linkWidth(),
Simon Hunta142dd22015-02-12 22:07:51 -0800336 online = ldata.online(),
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800337 modeCls = ldata.expected() ? 'inactive' : 'not-permitted',
Simon Hunta142dd22015-02-12 22:07:51 -0800338 delay = immediate ? 0 : 1000;
Simon Hunt1894d792015-02-04 17:09:20 -0800339
Thomas Vachuskacb5016f2015-05-18 14:11:43 -0700340 // FIXME: understand why el is sometimes undefined on addLink events...
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700341 // Investigated:
342 // el is undefined when it's a reverse link that is being added.
343 // updateLinks (which sets ldata.el) isn't called before this is called.
344 // Calling _updateLinks in addLinkUpdate fixes it, but there might be
345 // a more efficient way to fix it.
346 if (el && !el.empty()) {
Thomas Vachuskacb5016f2015-05-18 14:11:43 -0700347 el.classed('link', true);
Ray Milkeyb7f0f642016-01-22 16:08:14 -0800348 el.classed(allLinkSubTypes, false);
349 el.classed(modeCls, !online);
Thomas Vachuskacb5016f2015-05-18 14:11:43 -0700350 el.classed(allLinkTypes, false);
351 if (type) {
352 el.classed(type, true);
353 }
354 el.transition()
355 .duration(delay)
356 .attr('stroke-width', linkScale(lw))
357 .attr('stroke', linkConfig[th].baseColor);
Simon Hunt1894d792015-02-04 17:09:20 -0800358 }
Simon Hunt1894d792015-02-04 17:09:20 -0800359 }
360
Simon Hunt1894d792015-02-04 17:09:20 -0800361 function removeLinkElement(d) {
362 var idx = fs.find(d.key, network.links, 'key'),
363 removed;
364 if (idx >=0) {
365 // remove from links array
366 removed = network.links.splice(idx, 1);
367 // remove from lookup cache
368 delete lu[removed[0].key];
369 updateLinks();
Simon Hunta17fa672015-08-19 18:42:22 -0700370 fResume();
Simon Hunt1894d792015-02-04 17:09:20 -0800371 }
372 }
373
374 function removeHostElement(d, upd) {
375 // first, remove associated hostLink...
376 removeLinkElement(d.linkData);
377
378 // remove hostLink bindings
379 delete lu[d.ingress];
380 delete lu[d.egress];
381
382 // remove from lookup cache
383 delete lu[d.id];
384 // remove from nodes array
385 var idx = fs.find(d.id, network.nodes);
386 network.nodes.splice(idx, 1);
387
388 // remove from SVG
389 // NOTE: upd is false if we were called from removeDeviceElement()
390 if (upd) {
391 updateNodes();
Simon Hunta17fa672015-08-19 18:42:22 -0700392 fResume();
Simon Hunt1894d792015-02-04 17:09:20 -0800393 }
394 }
395
396 function removeDeviceElement(d) {
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700397 var id = d.id,
398 idx;
Simon Hunt1894d792015-02-04 17:09:20 -0800399 // first, remove associated hosts and links..
Simon Huntdc6adea2015-02-09 22:29:36 -0800400 tms.findAttachedHosts(id).forEach(removeHostElement);
401 tms.findAttachedLinks(id).forEach(removeLinkElement);
Simon Hunt1894d792015-02-04 17:09:20 -0800402
403 // remove from lookup cache
404 delete lu[id];
405 // remove from nodes array
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700406 idx = fs.find(id, network.nodes);
407 if (idx > -1) {
408 network.nodes.splice(idx, 1);
409 }
Simon Hunt1894d792015-02-04 17:09:20 -0800410
411 if (!network.nodes.length) {
Simon Huntdc6adea2015-02-09 22:29:36 -0800412 uplink.showNoDevs(true);
Simon Hunt1894d792015-02-04 17:09:20 -0800413 }
414
415 // remove from SVG
416 updateNodes();
Simon Hunta17fa672015-08-19 18:42:22 -0700417 fResume();
Simon Hunt1894d792015-02-04 17:09:20 -0800418 }
419
Simon Hunt5724fb42015-02-05 16:59:40 -0800420 function updateHostVisibility() {
Simon Hunt18bf9822015-02-12 17:35:45 -0800421 sus.visible(nodeG.selectAll('.host'), showHosts);
422 sus.visible(linkG.selectAll('.hostLink'), showHosts);
Simon Hunt8eb4d3a2015-02-23 18:23:29 -0800423 sus.visible(linkLabelG.selectAll('.hostLinkLabel'), showHosts);
Simon Hunt5724fb42015-02-05 16:59:40 -0800424 }
425
426 function updateOfflineVisibility(dev) {
427 function updDev(d, show) {
Simon Hunt8eb4d3a2015-02-23 18:23:29 -0800428 var b;
Simon Hunt18bf9822015-02-12 17:35:45 -0800429 sus.visible(d.el, show);
Simon Hunt5724fb42015-02-05 16:59:40 -0800430
Simon Huntdc6adea2015-02-09 22:29:36 -0800431 tms.findAttachedLinks(d.id).forEach(function (link) {
Simon Hunt5724fb42015-02-05 16:59:40 -0800432 b = show && ((link.type() !== 'hostLink') || showHosts);
Simon Hunt18bf9822015-02-12 17:35:45 -0800433 sus.visible(link.el, b);
Simon Hunt5724fb42015-02-05 16:59:40 -0800434 });
Simon Huntdc6adea2015-02-09 22:29:36 -0800435 tms.findAttachedHosts(d.id).forEach(function (host) {
Simon Hunt5724fb42015-02-05 16:59:40 -0800436 b = show && showHosts;
Simon Hunt18bf9822015-02-12 17:35:45 -0800437 sus.visible(host.el, b);
Simon Hunt5724fb42015-02-05 16:59:40 -0800438 });
439 }
440
441 if (dev) {
442 // updating a specific device that just toggled off/on-line
443 updDev(dev, dev.online || showOffline);
444 } else {
445 // updating all offline devices
Simon Huntdc6adea2015-02-09 22:29:36 -0800446 tms.findDevices(true).forEach(function (d) {
Simon Hunt5724fb42015-02-05 16:59:40 -0800447 updDev(d, showOffline);
448 });
449 }
450 }
451
Simon Hunt1894d792015-02-04 17:09:20 -0800452
Simon Hunt445e8152015-02-06 13:00:12 -0800453 function sendUpdateMeta(d, clearPos) {
Simon Huntac4c6f72015-02-03 19:50:53 -0800454 var metaUi = {},
455 ll;
456
Simon Hunt445e8152015-02-06 13:00:12 -0800457 // if we are not clearing the position data (unpinning),
Simon Huntfd7106c2016-02-09 15:05:26 -0800458 // attach the x, y, (and equivalent longitude, latitude)...
Simon Hunt445e8152015-02-06 13:00:12 -0800459 if (!clearPos) {
Simon Hunt3a6eec02015-02-09 21:16:43 -0800460 ll = tms.lngLatFromCoord([d.x, d.y]);
Simon Huntfd7106c2016-02-09 15:05:26 -0800461 metaUi = {
462 x: d.x,
463 y: d.y,
464 equivLoc: {
465 lng: ll[0],
466 lat: ll[1]
467 }
468 };
Simon Hunt1894d792015-02-04 17:09:20 -0800469 }
470 d.metaUi = metaUi;
Simon Hunt237676b52015-03-10 19:04:26 -0700471 wss.sendEvent('updateMeta', {
Simon Hunt1894d792015-02-04 17:09:20 -0800472 id: d.id,
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700473 class: d.class,
Simon Hunt1894d792015-02-04 17:09:20 -0800474 memento: metaUi
475 });
Simon Huntac4c6f72015-02-03 19:50:53 -0800476 }
477
Simon Hunt1894d792015-02-04 17:09:20 -0800478
Simon Huntac4c6f72015-02-03 19:50:53 -0800479 function mkSvgClass(d) {
480 return d.fixed ? d.svgClass + ' fixed' : d.svgClass;
481 }
482
Simon Hunt5724fb42015-02-05 16:59:40 -0800483 function vis(b) {
484 return b ? 'visible' : 'hidden';
485 }
486
Simon Huntfcbde892015-04-16 12:05:28 -0700487 function toggleHosts(x) {
488 var kev = (x === 'keyev'),
489 on = kev ? !showHosts : !!x;
490
491 showHosts = on;
Simon Hunt5724fb42015-02-05 16:59:40 -0800492 updateHostVisibility();
Simon Huntfcbde892015-04-16 12:05:28 -0700493 flash.flash('Hosts ' + vis(on));
494 return on;
Simon Hunt5724fb42015-02-05 16:59:40 -0800495 }
496
Simon Huntfcbde892015-04-16 12:05:28 -0700497 function toggleOffline(x) {
498 var kev = (x === 'keyev'),
499 on = kev ? !showOffline : !!x;
500
501 showOffline = on;
Simon Hunt5724fb42015-02-05 16:59:40 -0800502 updateOfflineVisibility();
Simon Huntfcbde892015-04-16 12:05:28 -0700503 flash.flash('Offline devices ' + vis(on));
504 return on;
Simon Hunt5724fb42015-02-05 16:59:40 -0800505 }
506
507 function cycleDeviceLabels() {
Bri Prebilic Cole9cf1a8d2015-04-21 13:15:29 -0700508 flash.flash(td3.incDevLabIndex());
Simon Huntdc6adea2015-02-09 22:29:36 -0800509 tms.findDevices().forEach(function (d) {
Simon Hunta4242de2015-02-24 17:11:55 -0800510 td3.updateDeviceLabel(d);
Simon Hunt1c367112015-02-05 18:02:46 -0800511 });
Simon Hunt5724fb42015-02-05 16:59:40 -0800512 }
513
Simon Hunt445e8152015-02-06 13:00:12 -0800514 function unpin() {
Simon Hunt08f841d02015-02-10 14:39:20 -0800515 var hov = tss.hovered();
516 if (hov) {
517 sendUpdateMeta(hov, true);
518 hov.fixed = false;
519 hov.el.classed('fixed', false);
Simon Hunt445e8152015-02-06 13:00:12 -0800520 fResume();
521 }
522 }
523
Simon Hunta142dd22015-02-12 22:07:51 -0800524 function showMastership(masterId) {
525 if (!masterId) {
526 restoreLayerState();
527 } else {
528 showMastershipFor(masterId);
529 }
530 }
531
532 function restoreLayerState() {
533 // NOTE: this level of indirection required, for when we have
534 // the layer filter functionality re-implemented
535 suppressLayers(false);
536 }
537
538 function showMastershipFor(id) {
539 suppressLayers(true);
540 node.each(function (n) {
541 if (n.master === id) {
Simon Hunt743a8492015-08-25 16:18:19 -0700542 n.el.classed('suppressedmax', false);
Simon Hunta142dd22015-02-12 22:07:51 -0800543 }
544 });
545 }
546
Simon Hunt743a8492015-08-25 16:18:19 -0700547 function supAmt(less) {
548 return less ? "suppressed" : "suppressedmax";
549 }
550
551 function suppressLayers(b, less) {
552 var cls = supAmt(less);
553 node.classed(cls, b);
554 link.classed(cls, b);
555 }
556
557 function unsuppressNode(id, less) {
558 var cls = supAmt(less);
559 node.each(function (n) {
560 if (n.id === id) {
561 n.el.classed(cls, false);
562 }
563 });
564 }
565
Simon Hunt94f7dae2015-08-26 17:40:59 -0700566 function unsuppressLink(key, less) {
Simon Hunt743a8492015-08-25 16:18:19 -0700567 var cls = supAmt(less);
568 link.each(function (n) {
Simon Hunt94f7dae2015-08-26 17:40:59 -0700569 if (n.key === key) {
Simon Hunt743a8492015-08-25 16:18:19 -0700570 n.el.classed(cls, false);
571 }
572 });
Simon Hunta142dd22015-02-12 22:07:51 -0800573 }
Simon Hunt445e8152015-02-06 13:00:12 -0800574
Simon Hunt86b7c882015-04-02 23:06:08 -0700575 function showBadLinks() {
576 var badLinks = tms.findBadLinks();
577 flash.flash('Bad Links: ' + badLinks.length);
578 $log.debug('Bad Link List (' + badLinks.length + '):');
579 badLinks.forEach(function (d) {
580 $log.debug('bad link: (' + d.bad + ') ' + d.key, d);
581 if (d.el) {
582 d.el.attr('stroke-width', linkScale(2.8))
583 .attr('stroke', 'red');
584 }
585 });
586 // back to normal after 2 seconds...
587 $timeout(updateLinks, 2000);
588 }
589
Simon Huntfd7106c2016-02-09 15:05:26 -0800590 function resetAllLocations() {
591 tms.resetAllLocations();
592 updateNodes();
593 tick(); // force nodes to be redrawn in their new locations
594 flash.flash('Reset Node Locations');
595 }
596
Simon Hunt5724fb42015-02-05 16:59:40 -0800597 // ==========================================
598
Simon Huntac4c6f72015-02-03 19:50:53 -0800599 function updateNodes() {
Thomas Vachuska1a989c12015-06-09 18:29:22 -0700600 if (fNodesTimer) {
601 $timeout.cancel(fNodesTimer);
602 }
603 fNodesTimer = $timeout(_updateNodes, 150);
604 }
605
Simon Hunta17fa672015-08-19 18:42:22 -0700606 // IMPLEMENTATION NOTE: _updateNodes() should NOT stop, start, or resume
607 // the force layout; that needs to be determined and implemented elsewhere
Thomas Vachuska1a989c12015-06-09 18:29:22 -0700608 function _updateNodes() {
Simon Hunt1894d792015-02-04 17:09:20 -0800609 // select all the nodes in the layout:
Simon Huntac4c6f72015-02-03 19:50:53 -0800610 node = nodeG.selectAll('.node')
611 .data(network.nodes, function (d) { return d.id; });
612
Simon Hunt1894d792015-02-04 17:09:20 -0800613 // operate on existing nodes:
Simon Hunta4242de2015-02-24 17:11:55 -0800614 node.filter('.device').each(td3.deviceExisting);
615 node.filter('.host').each(td3.hostExisting);
Simon Huntac4c6f72015-02-03 19:50:53 -0800616
617 // operate on entering nodes:
618 var entering = node.enter()
619 .append('g')
620 .attr({
621 id: function (d) { return sus.safeId(d.id); },
622 class: mkSvgClass,
Simon Hunta17fa672015-08-19 18:42:22 -0700623 transform: function (d) {
624 // Need to guard against NaN here ??
625 return sus.translate(d.x, d.y);
626 },
Simon Huntac4c6f72015-02-03 19:50:53 -0800627 opacity: 0
628 })
629 .call(drag)
Simon Hunt08f841d02015-02-10 14:39:20 -0800630 .on('mouseover', tss.nodeMouseOver)
631 .on('mouseout', tss.nodeMouseOut)
Simon Huntac4c6f72015-02-03 19:50:53 -0800632 .transition()
633 .attr('opacity', 1);
634
Simon Hunt1894d792015-02-04 17:09:20 -0800635 // augment entering nodes:
Simon Hunta4242de2015-02-24 17:11:55 -0800636 entering.filter('.device').each(td3.deviceEnter);
637 entering.filter('.host').each(td3.hostEnter);
Simon Huntac4c6f72015-02-03 19:50:53 -0800638
Simon Hunt51056592015-02-03 21:48:07 -0800639 // operate on both existing and new nodes:
Simon Hunta4242de2015-02-24 17:11:55 -0800640 td3.updateDeviceColors();
Simon Huntac4c6f72015-02-03 19:50:53 -0800641
642 // operate on exiting nodes:
643 // Note that the node is removed after 2 seconds.
644 // Sub element animations should be shorter than 2 seconds.
645 var exiting = node.exit()
646 .transition()
647 .duration(2000)
648 .style('opacity', 0)
649 .remove();
650
Simon Hunt1894d792015-02-04 17:09:20 -0800651 // exiting node specifics:
Simon Hunta4242de2015-02-24 17:11:55 -0800652 exiting.filter('.host').each(td3.hostExit);
653 exiting.filter('.device').each(td3.deviceExit);
Simon Huntac4c6f72015-02-03 19:50:53 -0800654 }
655
Simon Hunt51056592015-02-03 21:48:07 -0800656 // ==========================
Simon Hunt1894d792015-02-04 17:09:20 -0800657
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700658 function getDefaultPos(link) {
659 return {
660 x1: link.source.x,
661 y1: link.source.y,
662 x2: link.target.x,
663 y2: link.target.y
664 };
665 }
666
667 // returns amount of adjustment along the normal for given link
668 function amt(numLinks, linkIdx) {
669 var gap = 6;
670 return (linkIdx - ((numLinks - 1) / 2)) * gap;
671 }
672
673 function calcMovement(d, amt, flipped) {
674 var pos = getDefaultPos(d),
675 mult = flipped ? -amt : amt,
676 dx = pos.x2 - pos.x1,
677 dy = pos.y2 - pos.y1,
678 length = Math.sqrt((dx * dx) + (dy * dy));
679
680 return {
681 x1: pos.x1 + (mult * dy / length),
682 y1: pos.y1 + (mult * -dx / length),
683 x2: pos.x2 + (mult * dy / length),
684 y2: pos.y2 + (mult * -dx / length)
685 };
686 }
687
688 function calcPosition() {
689 var lines = this,
690 linkSrcId;
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700691 linkNums = [];
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700692 lines.each(function (d) {
693 if (d.type() === 'hostLink') {
694 d.position = getDefaultPos(d);
695 }
696 });
697
698 function normalizeLinkSrc(link) {
699 // ensure source device is consistent across set of links
700 // temporary measure until link modeling is refactored
701 if (!linkSrcId) {
702 linkSrcId = link.source.id;
703 return false;
704 }
705
706 return link.source.id !== linkSrcId;
707 }
708
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700709 angular.forEach(network.linksByDevice, function (linkArr, key) {
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700710 var numLinks = linkArr.length,
711 link;
712
713 if (numLinks === 1) {
714 link = linkArr[0];
715 link.position = getDefaultPos(link);
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700716 link.position.multiLink = false;
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700717 } else if (numLinks >= 5) {
718 // this code is inefficient, in the future the way links
719 // are modeled will be changed
720 angular.forEach(linkArr, function (link) {
721 link.position = getDefaultPos(link);
722 link.position.multiLink = true;
723 });
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700724 linkNums.push({
725 id: key,
726 num: numLinks,
727 linkCoords: linkArr[0].position
728 });
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700729 } else {
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700730 linkSrcId = null;
731 angular.forEach(linkArr, function (link, index) {
732 var offsetAmt = amt(numLinks, index),
733 needToFlip = normalizeLinkSrc(link);
734 link.position = calcMovement(link, offsetAmt, needToFlip);
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700735 link.position.multiLink = false;
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700736 });
737 }
738 });
739 }
740
Simon Hunt1894d792015-02-04 17:09:20 -0800741 function updateLinks() {
Thomas Vachuska1a989c12015-06-09 18:29:22 -0700742 if (fLinksTimer) {
743 $timeout.cancel(fLinksTimer);
744 }
745 fLinksTimer = $timeout(_updateLinks, 150);
746 }
747
Simon Hunta17fa672015-08-19 18:42:22 -0700748 // IMPLEMENTATION NOTE: _updateLinks() should NOT stop, start, or resume
749 // the force layout; that needs to be determined and implemented elsewhere
Thomas Vachuska1a989c12015-06-09 18:29:22 -0700750 function _updateLinks() {
Simon Hunt1894d792015-02-04 17:09:20 -0800751 var th = ts.theme();
752
753 link = linkG.selectAll('.link')
754 .data(network.links, function (d) { return d.key; });
755
756 // operate on existing links:
Simon Huntd5264122015-02-25 10:17:43 -0800757 link.each(function (d) {
758 // this is supposed to be an existing link, but we have observed
759 // occasions (where links are deleted and added rapidly?) where
760 // the DOM element has not been defined. So protect against that...
761 if (d.el) {
762 restyleLinkElement(d, true);
763 }
764 });
Simon Hunt1894d792015-02-04 17:09:20 -0800765
766 // operate on entering links:
767 var entering = link.enter()
768 .append('line')
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700769 .call(calcPosition)
Simon Hunt1894d792015-02-04 17:09:20 -0800770 .attr({
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700771 x1: function (d) { return d.position.x1; },
772 y1: function (d) { return d.position.y1; },
773 x2: function (d) { return d.position.x2; },
774 y2: function (d) { return d.position.y2; },
Simon Hunt1894d792015-02-04 17:09:20 -0800775 stroke: linkConfig[th].inColor,
776 'stroke-width': linkConfig.inWidth
777 });
778
779 // augment links
Simon Hunta4242de2015-02-24 17:11:55 -0800780 entering.each(td3.linkEntering);
Simon Hunt1894d792015-02-04 17:09:20 -0800781
782 // operate on both existing and new links:
783 //link.each(...)
784
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700785 // add labels for how many links are in a thick line
786 td3.applyNumLinkLabels(linkNums, numLinkLblsG);
787
Simon Hunt1894d792015-02-04 17:09:20 -0800788 // apply or remove labels
Simon Hunta4242de2015-02-24 17:11:55 -0800789 td3.applyLinkLabels();
Simon Hunt1894d792015-02-04 17:09:20 -0800790
791 // operate on exiting links:
792 link.exit()
793 .attr('stroke-dasharray', '3 3')
Simon Hunt5724fb42015-02-05 16:59:40 -0800794 .attr('stroke', linkConfig[th].outColor)
Simon Hunt1894d792015-02-04 17:09:20 -0800795 .style('opacity', 0.5)
796 .transition()
797 .duration(1500)
798 .attr({
799 'stroke-dasharray': '3 12',
Simon Hunt1894d792015-02-04 17:09:20 -0800800 'stroke-width': linkConfig.outWidth
801 })
802 .style('opacity', 0.0)
803 .remove();
Simon Hunt1894d792015-02-04 17:09:20 -0800804 }
805
Simon Huntac4c6f72015-02-03 19:50:53 -0800806
807 // ==========================
Simon Hunt737c89f2015-01-28 12:23:19 -0800808 // force layout tick function
Simon Hunt737c89f2015-01-28 12:23:19 -0800809
Simon Hunt5724fb42015-02-05 16:59:40 -0800810 function fResume() {
Simon Huntc3c5b672015-02-20 11:32:13 -0800811 if (!tos.isOblique()) {
Simon Hunt5724fb42015-02-05 16:59:40 -0800812 force.resume();
813 }
814 }
815
816 function fStart() {
Simon Huntc3c5b672015-02-20 11:32:13 -0800817 if (!tos.isOblique()) {
Simon Hunta17fa672015-08-19 18:42:22 -0700818 if (fTimer) {
819 $timeout.cancel(fTimer);
820 }
821 fTimer = $timeout(function () {
822 $log.debug("Starting force-layout");
823 force.start();
824 }, 200);
Simon Hunt5724fb42015-02-05 16:59:40 -0800825 }
826 }
827
828 var tickStuff = {
829 nodeAttr: {
Simon Hunta17fa672015-08-19 18:42:22 -0700830 transform: function (d) {
831 var dx = isNaN(d.x) ? 0 : d.x,
832 dy = isNaN(d.y) ? 0 : d.y;
833 return sus.translate(dx, dy);
834 }
Simon Hunt5724fb42015-02-05 16:59:40 -0800835 },
836 linkAttr: {
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700837 x1: function (d) { return d.position.x1; },
838 y1: function (d) { return d.position.y1; },
839 x2: function (d) { return d.position.x2; },
840 y2: function (d) { return d.position.y2; }
Simon Hunt5724fb42015-02-05 16:59:40 -0800841 },
842 linkLabelAttr: {
843 transform: function (d) {
Simon Huntdc6adea2015-02-09 22:29:36 -0800844 var lnk = tms.findLinkById(d.key);
Simon Hunt5724fb42015-02-05 16:59:40 -0800845 if (lnk) {
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700846 return td3.transformLabel(lnk.position);
Simon Hunt5724fb42015-02-05 16:59:40 -0800847 }
848 }
849 }
850 };
851
852 function tick() {
Simon Hunt3ab20282015-02-26 20:32:19 -0800853 // guard against null (which can happen when our view pages out)...
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700854 if (node && node.size()) {
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700855 node.attr(tickStuff.nodeAttr);
856 }
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700857 if (link && link.size()) {
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700858 link.call(calcPosition)
859 .attr(tickStuff.linkAttr);
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700860 td3.applyNumLinkLabels(linkNums, numLinkLblsG);
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700861 }
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700862 if (linkLabel && linkLabel.size()) {
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700863 linkLabel.attr(tickStuff.linkLabelAttr);
864 }
Simon Hunt737c89f2015-01-28 12:23:19 -0800865 }
866
867
Simon Huntac4c6f72015-02-03 19:50:53 -0800868 // ==========================
869 // === MOUSE GESTURE HANDLERS
870
Simon Hunt205099e2015-02-07 13:12:01 -0800871 function zoomingOrPanning(ev) {
872 return ev.metaKey || ev.altKey;
Simon Hunt445e8152015-02-06 13:00:12 -0800873 }
874
875 function atDragEnd(d) {
876 // once we've finished moving, pin the node in position
877 d.fixed = true;
878 d3.select(this).classed('fixed', true);
879 sendUpdateMeta(d);
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700880 tss.clickConsumed(true);
Simon Hunt445e8152015-02-06 13:00:12 -0800881 }
882
883 // predicate that indicates when dragging is active
884 function dragEnabled() {
885 var ev = d3.event.sourceEvent;
886 // nodeLock means we aren't allowing nodes to be dragged...
Simon Hunt205099e2015-02-07 13:12:01 -0800887 return !nodeLock && !zoomingOrPanning(ev);
Simon Hunt445e8152015-02-06 13:00:12 -0800888 }
889
890 // predicate that indicates when clicking is active
891 function clickEnabled() {
892 return true;
893 }
Simon Hunt737c89f2015-01-28 12:23:19 -0800894
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700895 // =============================================
896 // function entry points for overlay module
Simon Huntf542d842015-02-11 16:20:33 -0800897
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700898 // TODO: find an automatic way of tracking via the "showHighlights" events
Simon Hunte50829c2015-06-09 08:39:28 -0700899 var allTrafficClasses = 'primary secondary optical animated ' +
900 'port-traffic-Kbps port-traffic-Mbps port-traffic-Gbps ' +
901 'port-traffic-Gbps-choked';
Simon Huntf542d842015-02-11 16:20:33 -0800902
903 function clearLinkTrafficStyle() {
904 link.style('stroke-width', null)
905 .classed(allTrafficClasses, false);
906 }
907
908 function removeLinkLabels() {
909 network.links.forEach(function (d) {
910 d.label = '';
911 });
912 }
Simon Hunt737c89f2015-01-28 12:23:19 -0800913
Simon Hunte9343f32015-10-21 18:07:46 -0700914 function clearNodeDeco() {
915 node.selectAll('g.badge').remove();
916 }
917
918 function removeNodeBadges() {
919 network.nodes.forEach(function (d) {
920 d.badge = null;
921 });
922 }
923
Simon Hunta4242de2015-02-24 17:11:55 -0800924 function updateLinkLabelModel() {
925 // create the backing data for showing labels..
926 var data = [];
927 link.each(function (d) {
928 if (d.label) {
929 data.push({
930 id: 'lab-' + d.key,
931 key: d.key,
932 label: d.label,
933 ldata: d
934 });
935 }
936 });
937
938 linkLabel = linkLabelG.selectAll('.linkLabel')
939 .data(data, function (d) { return d.id; });
940 }
941
Simon Hunt737c89f2015-01-28 12:23:19 -0800942 // ==========================
Simon Huntac4c6f72015-02-03 19:50:53 -0800943 // Module definition
Simon Hunt737c89f2015-01-28 12:23:19 -0800944
Simon Huntdc6adea2015-02-09 22:29:36 -0800945 function mkModelApi(uplink) {
946 return {
947 projection: uplink.projection,
948 network: network,
949 restyleLinkElement: restyleLinkElement,
950 removeLinkElement: removeLinkElement
951 };
952 }
953
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700954 function mkD3Api() {
Simon Hunta4242de2015-02-24 17:11:55 -0800955 return {
956 node: function () { return node; },
957 link: function () { return link; },
958 linkLabel: function () { return linkLabel; },
959 instVisible: function () { return tis.isVisible(); },
960 posNode: tms.positionNode,
961 showHosts: function () { return showHosts; },
962 restyleLinkElement: restyleLinkElement,
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700963 updateLinkLabelModel: updateLinkLabelModel,
964 linkConfig: function () { return linkConfig; }
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700965 };
Simon Hunta4242de2015-02-24 17:11:55 -0800966 }
967
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700968 function mkSelectApi() {
Simon Hunt08f841d02015-02-10 14:39:20 -0800969 return {
970 node: function () { return node; },
971 zoomingOrPanning: zoomingOrPanning,
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700972 updateDeviceColors: td3.updateDeviceColors,
973 deselectLink: tls.deselectLink
Simon Hunt08f841d02015-02-10 14:39:20 -0800974 };
975 }
976
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700977 function mkTrafficApi() {
978 return {
979 hovered: tss.hovered,
980 somethingSelected: tss.somethingSelected,
981 selectOrder: tss.selectOrder
982 };
983 }
984
985 function mkOverlayApi() {
Simon Huntf542d842015-02-11 16:20:33 -0800986 return {
Simon Hunte9343f32015-10-21 18:07:46 -0700987 clearNodeDeco: clearNodeDeco,
988 removeNodeBadges: removeNodeBadges,
Simon Huntf542d842015-02-11 16:20:33 -0800989 clearLinkTrafficStyle: clearLinkTrafficStyle,
990 removeLinkLabels: removeLinkLabels,
Simon Hunt743a8492015-08-25 16:18:19 -0700991 findLinkById: tms.findLinkById,
Simon Hunt94f7dae2015-08-26 17:40:59 -0700992 findNodeById: nodeById,
Simon Huntf542d842015-02-11 16:20:33 -0800993 updateLinks: updateLinks,
Simon Hunt743a8492015-08-25 16:18:19 -0700994 updateNodes: updateNodes,
995 supLayers: suppressLayers,
996 unsupNode: unsuppressNode,
997 unsupLink: unsuppressLink
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700998 };
Simon Huntf542d842015-02-11 16:20:33 -0800999 }
1000
Simon Huntc3c5b672015-02-20 11:32:13 -08001001 function mkObliqueApi(uplink, fltr) {
Simon Hunt96f88c62015-02-19 17:57:25 -08001002 return {
Simon Huntc3c5b672015-02-20 11:32:13 -08001003 force: function() { return force; },
1004 zoomLayer: uplink.zoomLayer,
1005 nodeGBBox: function() { return nodeG.node().getBBox(); },
Simon Hunt96f88c62015-02-19 17:57:25 -08001006 node: function () { return node; },
Simon Huntc3c5b672015-02-20 11:32:13 -08001007 link: function () { return link; },
1008 linkLabel: function () { return linkLabel; },
1009 nodes: function () { return network.nodes; },
1010 tickStuff: tickStuff,
1011 nodeLock: function (b) {
1012 var old = nodeLock;
1013 nodeLock = b;
1014 return old;
1015 },
1016 opacifyMap: uplink.opacifyMap,
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -07001017 inLayer: fltr.inLayer,
Bri Prebilic Cole80401762015-07-16 11:36:18 -07001018 calcLinkPos: calcPosition,
1019 applyNumLinkLabels: function () {
1020 td3.applyNumLinkLabels(linkNums, numLinkLblsG);
1021 }
Simon Hunt96f88c62015-02-19 17:57:25 -08001022 };
1023 }
1024
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001025 function mkFilterApi() {
Simon Hunteb0fa052015-02-17 19:20:28 -08001026 return {
1027 node: function () { return node; },
1028 link: function () { return link; }
1029 };
1030 }
1031
Simon Hunt9e2104c2015-02-26 10:48:59 -08001032 function mkLinkApi(svg, uplink) {
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001033 return {
1034 svg: svg,
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001035 zoomer: uplink.zoomer(),
1036 network: network,
Simon Hunt1a5301e2015-02-25 15:31:25 -08001037 portLabelG: function () { return portLabelG; },
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001038 showHosts: function () { return showHosts; }
1039 };
1040 }
1041
Simon Hunt737c89f2015-01-28 12:23:19 -08001042 angular.module('ovTopo')
1043 .factory('TopoForceService',
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -07001044 ['$log', '$timeout', 'FnService', 'SvgUtilService',
Simon Hunt86b7c882015-04-02 23:06:08 -07001045 'ThemeService', 'FlashService', 'WebSocketService',
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001046 'TopoOverlayService', 'TopoInstService', 'TopoModelService',
Simon Hunta4242de2015-02-24 17:11:55 -08001047 'TopoD3Service', 'TopoSelectService', 'TopoTrafficService',
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001048 'TopoObliqueService', 'TopoFilterService', 'TopoLinkService',
Simon Hunt737c89f2015-01-28 12:23:19 -08001049
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001050 function (_$log_, _$timeout_, _fs_, _sus_, _ts_, _flash_, _wss_, _tov_,
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001051 _tis_, _tms_, _td3_, _tss_, _tts_, _tos_, _fltr_, _tls_) {
Simon Hunt737c89f2015-01-28 12:23:19 -08001052 $log = _$log_;
Simon Hunt86b7c882015-04-02 23:06:08 -07001053 $timeout = _$timeout_;
Simon Hunt1894d792015-02-04 17:09:20 -08001054 fs = _fs_;
Simon Hunt737c89f2015-01-28 12:23:19 -08001055 sus = _sus_;
Simon Huntac4c6f72015-02-03 19:50:53 -08001056 ts = _ts_;
Simon Hunt5724fb42015-02-05 16:59:40 -08001057 flash = _flash_;
Simon Hunt237676b52015-03-10 19:04:26 -07001058 wss = _wss_;
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001059 tov = _tov_;
Simon Huntac4c6f72015-02-03 19:50:53 -08001060 tis = _tis_;
Simon Hunt3a6eec02015-02-09 21:16:43 -08001061 tms = _tms_;
Simon Hunta4242de2015-02-24 17:11:55 -08001062 td3 = _td3_;
Simon Hunt08f841d02015-02-10 14:39:20 -08001063 tss = _tss_;
Simon Huntf542d842015-02-11 16:20:33 -08001064 tts = _tts_;
Simon Hunt96f88c62015-02-19 17:57:25 -08001065 tos = _tos_;
Simon Hunteb0fa052015-02-17 19:20:28 -08001066 fltr = _fltr_;
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001067 tls = _tls_;
Simon Hunt737c89f2015-01-28 12:23:19 -08001068
Simon Hunta142dd22015-02-12 22:07:51 -08001069 var themeListener = ts.addListener(function () {
1070 updateLinks();
1071 updateNodes();
1072 });
1073
Simon Hunt737c89f2015-01-28 12:23:19 -08001074 // forceG is the SVG group to display the force layout in
Simon Huntdc6adea2015-02-09 22:29:36 -08001075 // uplink is the api from the main topo source file
Simon Hunt3a6eec02015-02-09 21:16:43 -08001076 // dim is the initial dimensions of the SVG as [w,h]
Simon Hunt737c89f2015-01-28 12:23:19 -08001077 // opts are, well, optional :)
Simon Hunt3ab20282015-02-26 20:32:19 -08001078 function initForce(_svg_, forceG, _uplink_, _dim_, opts) {
Simon Hunt1894d792015-02-04 17:09:20 -08001079 uplink = _uplink_;
Simon Hunt3a6eec02015-02-09 21:16:43 -08001080 dim = _dim_;
Simon Hunt3ab20282015-02-26 20:32:19 -08001081 svg = _svg_;
1082
1083 lu = network.lookup;
1084 rlk = network.revLinkToKey;
Simon Hunt3a6eec02015-02-09 21:16:43 -08001085
1086 $log.debug('initForce().. dim = ' + dim);
1087
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001088 tov.setApi(mkOverlayApi(), tss);
Simon Huntdc6adea2015-02-09 22:29:36 -08001089 tms.initModel(mkModelApi(uplink), dim);
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001090 td3.initD3(mkD3Api());
1091 tss.initSelect(mkSelectApi());
1092 tts.initTraffic(mkTrafficApi());
Simon Huntc3c5b672015-02-20 11:32:13 -08001093 tos.initOblique(mkObliqueApi(uplink, fltr));
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001094 fltr.initFilter(mkFilterApi());
Simon Hunt9e2104c2015-02-26 10:48:59 -08001095 tls.initLink(mkLinkApi(svg, uplink), td3);
Simon Hunta11b4eb2015-01-28 16:20:50 -08001096
Simon Hunt737c89f2015-01-28 12:23:19 -08001097 settings = angular.extend({}, defaultSettings, opts);
1098
1099 linkG = forceG.append('g').attr('id', 'topo-links');
1100 linkLabelG = forceG.append('g').attr('id', 'topo-linkLabels');
Bri Prebilic Cole80401762015-07-16 11:36:18 -07001101 numLinkLblsG = forceG.append('g').attr('id', 'topo-numLinkLabels');
Simon Hunt737c89f2015-01-28 12:23:19 -08001102 nodeG = forceG.append('g').attr('id', 'topo-nodes');
Simon Hunt1a5301e2015-02-25 15:31:25 -08001103 portLabelG = forceG.append('g').attr('id', 'topo-portLabels');
Simon Hunt737c89f2015-01-28 12:23:19 -08001104
1105 link = linkG.selectAll('.link');
1106 linkLabel = linkLabelG.selectAll('.linkLabel');
1107 node = nodeG.selectAll('.node');
1108
1109 force = d3.layout.force()
Simon Hunt3a6eec02015-02-09 21:16:43 -08001110 .size(dim)
Simon Hunt737c89f2015-01-28 12:23:19 -08001111 .nodes(network.nodes)
1112 .links(network.links)
1113 .gravity(settings.gravity)
1114 .friction(settings.friction)
1115 .charge(settings.charge._def_)
1116 .linkDistance(settings.linkDistance._def_)
1117 .linkStrength(settings.linkStrength._def_)
1118 .on('tick', tick);
1119
1120 drag = sus.createDragBehavior(force,
Simon Hunt08f841d02015-02-10 14:39:20 -08001121 tss.selectObject, atDragEnd, dragEnabled, clickEnabled);
Simon Hunt737c89f2015-01-28 12:23:19 -08001122 }
1123
Simon Hunt3a6eec02015-02-09 21:16:43 -08001124 function newDim(_dim_) {
1125 dim = _dim_;
1126 force.size(dim);
1127 tms.newDim(dim);
Simon Hunt737c89f2015-01-28 12:23:19 -08001128 }
1129
Simon Hunt3a6eec02015-02-09 21:16:43 -08001130 function destroyForce() {
Simon Hunt3ab20282015-02-26 20:32:19 -08001131 force.stop();
1132
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001133 tls.destroyLink();
Simon Hunt96f88c62015-02-19 17:57:25 -08001134 tos.destroyOblique();
Simon Huntf542d842015-02-11 16:20:33 -08001135 tts.destroyTraffic();
1136 tss.destroySelect();
Simon Hunta4242de2015-02-24 17:11:55 -08001137 td3.destroyD3();
Simon Huntf542d842015-02-11 16:20:33 -08001138 tms.destroyModel();
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001139 // note: no need to destroy overlay service
Simon Hunta142dd22015-02-12 22:07:51 -08001140 ts.removeListener(themeListener);
1141 themeListener = null;
Simon Hunt3ab20282015-02-26 20:32:19 -08001142
1143 // clean up the DOM
1144 svg.selectAll('g').remove();
1145 svg.selectAll('defs').remove();
1146
1147 // clean up internal state
1148 network.nodes = [];
1149 network.links = [];
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -07001150 network.linksByDevice = {};
Simon Hunt3ab20282015-02-26 20:32:19 -08001151 network.lookup = {};
1152 network.revLinkToKey = {};
1153
Bri Prebilic Cole80401762015-07-16 11:36:18 -07001154 linkNums = [];
1155
1156 linkG = linkLabelG = numLinkLblsG = nodeG = portLabelG = null;
Simon Hunt3ab20282015-02-26 20:32:19 -08001157 link = linkLabel = node = null;
1158 force = drag = null;
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -07001159
1160 // clean up $timeout promises
Simon Hunta17fa672015-08-19 18:42:22 -07001161 if (fTimer) {
1162 $timeout.cancel(fTimer);
1163 }
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -07001164 if (fNodesTimer) {
1165 $timeout.cancel(fNodesTimer);
1166 }
1167 if (fLinksTimer) {
1168 $timeout.cancel(fLinksTimer);
1169 }
Simon Hunt3a6eec02015-02-09 21:16:43 -08001170 }
1171
Simon Hunt737c89f2015-01-28 12:23:19 -08001172 return {
1173 initForce: initForce,
Simon Hunt3a6eec02015-02-09 21:16:43 -08001174 newDim: newDim,
1175 destroyForce: destroyForce,
Simon Huntac4c6f72015-02-03 19:50:53 -08001176
Simon Hunta4242de2015-02-24 17:11:55 -08001177 updateDeviceColors: td3.updateDeviceColors,
Simon Hunt5724fb42015-02-05 16:59:40 -08001178 toggleHosts: toggleHosts,
Simon Hunt9e2104c2015-02-26 10:48:59 -08001179 togglePorts: tls.togglePorts,
Simon Hunt5724fb42015-02-05 16:59:40 -08001180 toggleOffline: toggleOffline,
1181 cycleDeviceLabels: cycleDeviceLabels,
Simon Hunt445e8152015-02-06 13:00:12 -08001182 unpin: unpin,
Simon Hunta142dd22015-02-12 22:07:51 -08001183 showMastership: showMastership,
Simon Hunt86b7c882015-04-02 23:06:08 -07001184 showBadLinks: showBadLinks,
Simon Huntac4c6f72015-02-03 19:50:53 -08001185
Simon Huntfd7106c2016-02-09 15:05:26 -08001186 resetAllLocations: resetAllLocations,
Simon Huntac4c6f72015-02-03 19:50:53 -08001187 addDevice: addDevice,
Simon Hunt1894d792015-02-04 17:09:20 -08001188 updateDevice: updateDevice,
1189 removeDevice: removeDevice,
1190 addHost: addHost,
1191 updateHost: updateHost,
Simon Hunt95d56fd2015-11-12 11:06:44 -08001192 moveHost: moveHost,
Simon Hunt1894d792015-02-04 17:09:20 -08001193 removeHost: removeHost,
1194 addLink: addLink,
1195 updateLink: updateLink,
Simon Hunt4a6b54b2015-10-27 22:08:25 -07001196 removeLink: removeLink,
1197 topoStartDone: topoStartDone
Simon Hunt737c89f2015-01-28 12:23:19 -08001198 };
1199 }]);
1200}());