blob: 3c68f3e739bab0c1eb45ecff751645172204fa12 [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),
Simon Hunt3a6eec02015-02-09 21:16:43 -0800326 allLinkTypes = 'direct indirect optical tunnel';
Simon Hunt1894d792015-02-04 17:09:20 -0800327
Simon Hunta142dd22015-02-12 22:07:51 -0800328 function restyleLinkElement(ldata, immediate) {
Simon Hunt1894d792015-02-04 17:09:20 -0800329 // this fn's job is to look at raw links and decide what svg classes
330 // need to be applied to the line element in the DOM
331 var th = ts.theme(),
332 el = ldata.el,
333 type = ldata.type(),
334 lw = ldata.linkWidth(),
Simon Hunta142dd22015-02-12 22:07:51 -0800335 online = ldata.online(),
336 delay = immediate ? 0 : 1000;
Simon Hunt1894d792015-02-04 17:09:20 -0800337
Thomas Vachuskacb5016f2015-05-18 14:11:43 -0700338 // FIXME: understand why el is sometimes undefined on addLink events...
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700339 // Investigated:
340 // el is undefined when it's a reverse link that is being added.
341 // updateLinks (which sets ldata.el) isn't called before this is called.
342 // Calling _updateLinks in addLinkUpdate fixes it, but there might be
343 // a more efficient way to fix it.
344 if (el && !el.empty()) {
Thomas Vachuskacb5016f2015-05-18 14:11:43 -0700345 el.classed('link', true);
346 el.classed('inactive', !online);
347 el.classed(allLinkTypes, false);
348 if (type) {
349 el.classed(type, true);
350 }
351 el.transition()
352 .duration(delay)
353 .attr('stroke-width', linkScale(lw))
354 .attr('stroke', linkConfig[th].baseColor);
Simon Hunt1894d792015-02-04 17:09:20 -0800355 }
Simon Hunt1894d792015-02-04 17:09:20 -0800356 }
357
Simon Hunt1894d792015-02-04 17:09:20 -0800358 function removeLinkElement(d) {
359 var idx = fs.find(d.key, network.links, 'key'),
360 removed;
361 if (idx >=0) {
362 // remove from links array
363 removed = network.links.splice(idx, 1);
364 // remove from lookup cache
365 delete lu[removed[0].key];
366 updateLinks();
Simon Hunta17fa672015-08-19 18:42:22 -0700367 fResume();
Simon Hunt1894d792015-02-04 17:09:20 -0800368 }
369 }
370
371 function removeHostElement(d, upd) {
372 // first, remove associated hostLink...
373 removeLinkElement(d.linkData);
374
375 // remove hostLink bindings
376 delete lu[d.ingress];
377 delete lu[d.egress];
378
379 // remove from lookup cache
380 delete lu[d.id];
381 // remove from nodes array
382 var idx = fs.find(d.id, network.nodes);
383 network.nodes.splice(idx, 1);
384
385 // remove from SVG
386 // NOTE: upd is false if we were called from removeDeviceElement()
387 if (upd) {
388 updateNodes();
Simon Hunta17fa672015-08-19 18:42:22 -0700389 fResume();
Simon Hunt1894d792015-02-04 17:09:20 -0800390 }
391 }
392
393 function removeDeviceElement(d) {
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700394 var id = d.id,
395 idx;
Simon Hunt1894d792015-02-04 17:09:20 -0800396 // first, remove associated hosts and links..
Simon Huntdc6adea2015-02-09 22:29:36 -0800397 tms.findAttachedHosts(id).forEach(removeHostElement);
398 tms.findAttachedLinks(id).forEach(removeLinkElement);
Simon Hunt1894d792015-02-04 17:09:20 -0800399
400 // remove from lookup cache
401 delete lu[id];
402 // remove from nodes array
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700403 idx = fs.find(id, network.nodes);
404 if (idx > -1) {
405 network.nodes.splice(idx, 1);
406 }
Simon Hunt1894d792015-02-04 17:09:20 -0800407
408 if (!network.nodes.length) {
Simon Huntdc6adea2015-02-09 22:29:36 -0800409 uplink.showNoDevs(true);
Simon Hunt1894d792015-02-04 17:09:20 -0800410 }
411
412 // remove from SVG
413 updateNodes();
Simon Hunta17fa672015-08-19 18:42:22 -0700414 fResume();
Simon Hunt1894d792015-02-04 17:09:20 -0800415 }
416
Simon Hunt5724fb42015-02-05 16:59:40 -0800417 function updateHostVisibility() {
Simon Hunt18bf9822015-02-12 17:35:45 -0800418 sus.visible(nodeG.selectAll('.host'), showHosts);
419 sus.visible(linkG.selectAll('.hostLink'), showHosts);
Simon Hunt8eb4d3a2015-02-23 18:23:29 -0800420 sus.visible(linkLabelG.selectAll('.hostLinkLabel'), showHosts);
Simon Hunt5724fb42015-02-05 16:59:40 -0800421 }
422
423 function updateOfflineVisibility(dev) {
424 function updDev(d, show) {
Simon Hunt8eb4d3a2015-02-23 18:23:29 -0800425 var b;
Simon Hunt18bf9822015-02-12 17:35:45 -0800426 sus.visible(d.el, show);
Simon Hunt5724fb42015-02-05 16:59:40 -0800427
Simon Huntdc6adea2015-02-09 22:29:36 -0800428 tms.findAttachedLinks(d.id).forEach(function (link) {
Simon Hunt5724fb42015-02-05 16:59:40 -0800429 b = show && ((link.type() !== 'hostLink') || showHosts);
Simon Hunt18bf9822015-02-12 17:35:45 -0800430 sus.visible(link.el, b);
Simon Hunt5724fb42015-02-05 16:59:40 -0800431 });
Simon Huntdc6adea2015-02-09 22:29:36 -0800432 tms.findAttachedHosts(d.id).forEach(function (host) {
Simon Hunt5724fb42015-02-05 16:59:40 -0800433 b = show && showHosts;
Simon Hunt18bf9822015-02-12 17:35:45 -0800434 sus.visible(host.el, b);
Simon Hunt5724fb42015-02-05 16:59:40 -0800435 });
436 }
437
438 if (dev) {
439 // updating a specific device that just toggled off/on-line
440 updDev(dev, dev.online || showOffline);
441 } else {
442 // updating all offline devices
Simon Huntdc6adea2015-02-09 22:29:36 -0800443 tms.findDevices(true).forEach(function (d) {
Simon Hunt5724fb42015-02-05 16:59:40 -0800444 updDev(d, showOffline);
445 });
446 }
447 }
448
Simon Hunt1894d792015-02-04 17:09:20 -0800449
Simon Hunt445e8152015-02-06 13:00:12 -0800450 function sendUpdateMeta(d, clearPos) {
Simon Huntac4c6f72015-02-03 19:50:53 -0800451 var metaUi = {},
452 ll;
453
Simon Hunt445e8152015-02-06 13:00:12 -0800454 // if we are not clearing the position data (unpinning),
455 // attach the x, y, longitude, latitude...
456 if (!clearPos) {
Simon Hunt3a6eec02015-02-09 21:16:43 -0800457 ll = tms.lngLatFromCoord([d.x, d.y]);
Simon Huntdc6adea2015-02-09 22:29:36 -0800458 metaUi = {x: d.x, y: d.y, lng: ll[0], lat: ll[1]};
Simon Hunt1894d792015-02-04 17:09:20 -0800459 }
460 d.metaUi = metaUi;
Simon Hunt237676b52015-03-10 19:04:26 -0700461 wss.sendEvent('updateMeta', {
Simon Hunt1894d792015-02-04 17:09:20 -0800462 id: d.id,
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700463 class: d.class,
Simon Hunt1894d792015-02-04 17:09:20 -0800464 memento: metaUi
465 });
Simon Huntac4c6f72015-02-03 19:50:53 -0800466 }
467
Simon Hunt1894d792015-02-04 17:09:20 -0800468
Simon Huntac4c6f72015-02-03 19:50:53 -0800469 function mkSvgClass(d) {
470 return d.fixed ? d.svgClass + ' fixed' : d.svgClass;
471 }
472
Simon Hunt5724fb42015-02-05 16:59:40 -0800473 function vis(b) {
474 return b ? 'visible' : 'hidden';
475 }
476
Simon Huntfcbde892015-04-16 12:05:28 -0700477 function toggleHosts(x) {
478 var kev = (x === 'keyev'),
479 on = kev ? !showHosts : !!x;
480
481 showHosts = on;
Simon Hunt5724fb42015-02-05 16:59:40 -0800482 updateHostVisibility();
Simon Huntfcbde892015-04-16 12:05:28 -0700483 flash.flash('Hosts ' + vis(on));
484 return on;
Simon Hunt5724fb42015-02-05 16:59:40 -0800485 }
486
Simon Huntfcbde892015-04-16 12:05:28 -0700487 function toggleOffline(x) {
488 var kev = (x === 'keyev'),
489 on = kev ? !showOffline : !!x;
490
491 showOffline = on;
Simon Hunt5724fb42015-02-05 16:59:40 -0800492 updateOfflineVisibility();
Simon Huntfcbde892015-04-16 12:05:28 -0700493 flash.flash('Offline devices ' + vis(on));
494 return on;
Simon Hunt5724fb42015-02-05 16:59:40 -0800495 }
496
497 function cycleDeviceLabels() {
Bri Prebilic Cole9cf1a8d2015-04-21 13:15:29 -0700498 flash.flash(td3.incDevLabIndex());
Simon Huntdc6adea2015-02-09 22:29:36 -0800499 tms.findDevices().forEach(function (d) {
Simon Hunta4242de2015-02-24 17:11:55 -0800500 td3.updateDeviceLabel(d);
Simon Hunt1c367112015-02-05 18:02:46 -0800501 });
Simon Hunt5724fb42015-02-05 16:59:40 -0800502 }
503
Simon Hunt445e8152015-02-06 13:00:12 -0800504 function unpin() {
Simon Hunt08f841d02015-02-10 14:39:20 -0800505 var hov = tss.hovered();
506 if (hov) {
507 sendUpdateMeta(hov, true);
508 hov.fixed = false;
509 hov.el.classed('fixed', false);
Simon Hunt445e8152015-02-06 13:00:12 -0800510 fResume();
511 }
512 }
513
Simon Hunta142dd22015-02-12 22:07:51 -0800514 function showMastership(masterId) {
515 if (!masterId) {
516 restoreLayerState();
517 } else {
518 showMastershipFor(masterId);
519 }
520 }
521
522 function restoreLayerState() {
523 // NOTE: this level of indirection required, for when we have
524 // the layer filter functionality re-implemented
525 suppressLayers(false);
526 }
527
528 function showMastershipFor(id) {
529 suppressLayers(true);
530 node.each(function (n) {
531 if (n.master === id) {
Simon Hunt743a8492015-08-25 16:18:19 -0700532 n.el.classed('suppressedmax', false);
Simon Hunta142dd22015-02-12 22:07:51 -0800533 }
534 });
535 }
536
Simon Hunt743a8492015-08-25 16:18:19 -0700537 function supAmt(less) {
538 return less ? "suppressed" : "suppressedmax";
539 }
540
541 function suppressLayers(b, less) {
542 var cls = supAmt(less);
543 node.classed(cls, b);
544 link.classed(cls, b);
545 }
546
547 function unsuppressNode(id, less) {
548 var cls = supAmt(less);
549 node.each(function (n) {
550 if (n.id === id) {
551 n.el.classed(cls, false);
552 }
553 });
554 }
555
Simon Hunt94f7dae2015-08-26 17:40:59 -0700556 function unsuppressLink(key, less) {
Simon Hunt743a8492015-08-25 16:18:19 -0700557 var cls = supAmt(less);
558 link.each(function (n) {
Simon Hunt94f7dae2015-08-26 17:40:59 -0700559 if (n.key === key) {
Simon Hunt743a8492015-08-25 16:18:19 -0700560 n.el.classed(cls, false);
561 }
562 });
Simon Hunta142dd22015-02-12 22:07:51 -0800563 }
Simon Hunt445e8152015-02-06 13:00:12 -0800564
Simon Hunt86b7c882015-04-02 23:06:08 -0700565 function showBadLinks() {
566 var badLinks = tms.findBadLinks();
567 flash.flash('Bad Links: ' + badLinks.length);
568 $log.debug('Bad Link List (' + badLinks.length + '):');
569 badLinks.forEach(function (d) {
570 $log.debug('bad link: (' + d.bad + ') ' + d.key, d);
571 if (d.el) {
572 d.el.attr('stroke-width', linkScale(2.8))
573 .attr('stroke', 'red');
574 }
575 });
576 // back to normal after 2 seconds...
577 $timeout(updateLinks, 2000);
578 }
579
Simon Hunt5724fb42015-02-05 16:59:40 -0800580 // ==========================================
581
Simon Huntac4c6f72015-02-03 19:50:53 -0800582 function updateNodes() {
Thomas Vachuska1a989c12015-06-09 18:29:22 -0700583 if (fNodesTimer) {
584 $timeout.cancel(fNodesTimer);
585 }
586 fNodesTimer = $timeout(_updateNodes, 150);
587 }
588
Simon Hunta17fa672015-08-19 18:42:22 -0700589 // IMPLEMENTATION NOTE: _updateNodes() should NOT stop, start, or resume
590 // the force layout; that needs to be determined and implemented elsewhere
Thomas Vachuska1a989c12015-06-09 18:29:22 -0700591 function _updateNodes() {
Simon Hunt1894d792015-02-04 17:09:20 -0800592 // select all the nodes in the layout:
Simon Huntac4c6f72015-02-03 19:50:53 -0800593 node = nodeG.selectAll('.node')
594 .data(network.nodes, function (d) { return d.id; });
595
Simon Hunt1894d792015-02-04 17:09:20 -0800596 // operate on existing nodes:
Simon Hunta4242de2015-02-24 17:11:55 -0800597 node.filter('.device').each(td3.deviceExisting);
598 node.filter('.host').each(td3.hostExisting);
Simon Huntac4c6f72015-02-03 19:50:53 -0800599
600 // operate on entering nodes:
601 var entering = node.enter()
602 .append('g')
603 .attr({
604 id: function (d) { return sus.safeId(d.id); },
605 class: mkSvgClass,
Simon Hunta17fa672015-08-19 18:42:22 -0700606 transform: function (d) {
607 // Need to guard against NaN here ??
608 return sus.translate(d.x, d.y);
609 },
Simon Huntac4c6f72015-02-03 19:50:53 -0800610 opacity: 0
611 })
612 .call(drag)
Simon Hunt08f841d02015-02-10 14:39:20 -0800613 .on('mouseover', tss.nodeMouseOver)
614 .on('mouseout', tss.nodeMouseOut)
Simon Huntac4c6f72015-02-03 19:50:53 -0800615 .transition()
616 .attr('opacity', 1);
617
Simon Hunt1894d792015-02-04 17:09:20 -0800618 // augment entering nodes:
Simon Hunta4242de2015-02-24 17:11:55 -0800619 entering.filter('.device').each(td3.deviceEnter);
620 entering.filter('.host').each(td3.hostEnter);
Simon Huntac4c6f72015-02-03 19:50:53 -0800621
Simon Hunt51056592015-02-03 21:48:07 -0800622 // operate on both existing and new nodes:
Simon Hunta4242de2015-02-24 17:11:55 -0800623 td3.updateDeviceColors();
Simon Huntac4c6f72015-02-03 19:50:53 -0800624
625 // operate on exiting nodes:
626 // Note that the node is removed after 2 seconds.
627 // Sub element animations should be shorter than 2 seconds.
628 var exiting = node.exit()
629 .transition()
630 .duration(2000)
631 .style('opacity', 0)
632 .remove();
633
Simon Hunt1894d792015-02-04 17:09:20 -0800634 // exiting node specifics:
Simon Hunta4242de2015-02-24 17:11:55 -0800635 exiting.filter('.host').each(td3.hostExit);
636 exiting.filter('.device').each(td3.deviceExit);
Simon Huntac4c6f72015-02-03 19:50:53 -0800637 }
638
Simon Hunt51056592015-02-03 21:48:07 -0800639 // ==========================
Simon Hunt1894d792015-02-04 17:09:20 -0800640
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700641 function getDefaultPos(link) {
642 return {
643 x1: link.source.x,
644 y1: link.source.y,
645 x2: link.target.x,
646 y2: link.target.y
647 };
648 }
649
650 // returns amount of adjustment along the normal for given link
651 function amt(numLinks, linkIdx) {
652 var gap = 6;
653 return (linkIdx - ((numLinks - 1) / 2)) * gap;
654 }
655
656 function calcMovement(d, amt, flipped) {
657 var pos = getDefaultPos(d),
658 mult = flipped ? -amt : amt,
659 dx = pos.x2 - pos.x1,
660 dy = pos.y2 - pos.y1,
661 length = Math.sqrt((dx * dx) + (dy * dy));
662
663 return {
664 x1: pos.x1 + (mult * dy / length),
665 y1: pos.y1 + (mult * -dx / length),
666 x2: pos.x2 + (mult * dy / length),
667 y2: pos.y2 + (mult * -dx / length)
668 };
669 }
670
671 function calcPosition() {
672 var lines = this,
673 linkSrcId;
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700674 linkNums = [];
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700675 lines.each(function (d) {
676 if (d.type() === 'hostLink') {
677 d.position = getDefaultPos(d);
678 }
679 });
680
681 function normalizeLinkSrc(link) {
682 // ensure source device is consistent across set of links
683 // temporary measure until link modeling is refactored
684 if (!linkSrcId) {
685 linkSrcId = link.source.id;
686 return false;
687 }
688
689 return link.source.id !== linkSrcId;
690 }
691
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700692 angular.forEach(network.linksByDevice, function (linkArr, key) {
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700693 var numLinks = linkArr.length,
694 link;
695
696 if (numLinks === 1) {
697 link = linkArr[0];
698 link.position = getDefaultPos(link);
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700699 link.position.multiLink = false;
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700700 } else if (numLinks >= 5) {
701 // this code is inefficient, in the future the way links
702 // are modeled will be changed
703 angular.forEach(linkArr, function (link) {
704 link.position = getDefaultPos(link);
705 link.position.multiLink = true;
706 });
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700707 linkNums.push({
708 id: key,
709 num: numLinks,
710 linkCoords: linkArr[0].position
711 });
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700712 } else {
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700713 linkSrcId = null;
714 angular.forEach(linkArr, function (link, index) {
715 var offsetAmt = amt(numLinks, index),
716 needToFlip = normalizeLinkSrc(link);
717 link.position = calcMovement(link, offsetAmt, needToFlip);
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700718 link.position.multiLink = false;
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700719 });
720 }
721 });
722 }
723
Simon Hunt1894d792015-02-04 17:09:20 -0800724 function updateLinks() {
Thomas Vachuska1a989c12015-06-09 18:29:22 -0700725 if (fLinksTimer) {
726 $timeout.cancel(fLinksTimer);
727 }
728 fLinksTimer = $timeout(_updateLinks, 150);
729 }
730
Simon Hunta17fa672015-08-19 18:42:22 -0700731 // IMPLEMENTATION NOTE: _updateLinks() should NOT stop, start, or resume
732 // the force layout; that needs to be determined and implemented elsewhere
Thomas Vachuska1a989c12015-06-09 18:29:22 -0700733 function _updateLinks() {
Simon Hunt1894d792015-02-04 17:09:20 -0800734 var th = ts.theme();
735
736 link = linkG.selectAll('.link')
737 .data(network.links, function (d) { return d.key; });
738
739 // operate on existing links:
Simon Huntd5264122015-02-25 10:17:43 -0800740 link.each(function (d) {
741 // this is supposed to be an existing link, but we have observed
742 // occasions (where links are deleted and added rapidly?) where
743 // the DOM element has not been defined. So protect against that...
744 if (d.el) {
745 restyleLinkElement(d, true);
746 }
747 });
Simon Hunt1894d792015-02-04 17:09:20 -0800748
749 // operate on entering links:
750 var entering = link.enter()
751 .append('line')
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700752 .call(calcPosition)
Simon Hunt1894d792015-02-04 17:09:20 -0800753 .attr({
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700754 x1: function (d) { return d.position.x1; },
755 y1: function (d) { return d.position.y1; },
756 x2: function (d) { return d.position.x2; },
757 y2: function (d) { return d.position.y2; },
Simon Hunt1894d792015-02-04 17:09:20 -0800758 stroke: linkConfig[th].inColor,
759 'stroke-width': linkConfig.inWidth
760 });
761
762 // augment links
Simon Hunta4242de2015-02-24 17:11:55 -0800763 entering.each(td3.linkEntering);
Simon Hunt1894d792015-02-04 17:09:20 -0800764
765 // operate on both existing and new links:
766 //link.each(...)
767
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700768 // add labels for how many links are in a thick line
769 td3.applyNumLinkLabels(linkNums, numLinkLblsG);
770
Simon Hunt1894d792015-02-04 17:09:20 -0800771 // apply or remove labels
Simon Hunta4242de2015-02-24 17:11:55 -0800772 td3.applyLinkLabels();
Simon Hunt1894d792015-02-04 17:09:20 -0800773
774 // operate on exiting links:
775 link.exit()
776 .attr('stroke-dasharray', '3 3')
Simon Hunt5724fb42015-02-05 16:59:40 -0800777 .attr('stroke', linkConfig[th].outColor)
Simon Hunt1894d792015-02-04 17:09:20 -0800778 .style('opacity', 0.5)
779 .transition()
780 .duration(1500)
781 .attr({
782 'stroke-dasharray': '3 12',
Simon Hunt1894d792015-02-04 17:09:20 -0800783 'stroke-width': linkConfig.outWidth
784 })
785 .style('opacity', 0.0)
786 .remove();
Simon Hunt1894d792015-02-04 17:09:20 -0800787 }
788
Simon Huntac4c6f72015-02-03 19:50:53 -0800789
790 // ==========================
Simon Hunt737c89f2015-01-28 12:23:19 -0800791 // force layout tick function
Simon Hunt737c89f2015-01-28 12:23:19 -0800792
Simon Hunt5724fb42015-02-05 16:59:40 -0800793 function fResume() {
Simon Huntc3c5b672015-02-20 11:32:13 -0800794 if (!tos.isOblique()) {
Simon Hunt5724fb42015-02-05 16:59:40 -0800795 force.resume();
796 }
797 }
798
799 function fStart() {
Simon Huntc3c5b672015-02-20 11:32:13 -0800800 if (!tos.isOblique()) {
Simon Hunta17fa672015-08-19 18:42:22 -0700801 if (fTimer) {
802 $timeout.cancel(fTimer);
803 }
804 fTimer = $timeout(function () {
805 $log.debug("Starting force-layout");
806 force.start();
807 }, 200);
Simon Hunt5724fb42015-02-05 16:59:40 -0800808 }
809 }
810
811 var tickStuff = {
812 nodeAttr: {
Simon Hunta17fa672015-08-19 18:42:22 -0700813 transform: function (d) {
814 var dx = isNaN(d.x) ? 0 : d.x,
815 dy = isNaN(d.y) ? 0 : d.y;
816 return sus.translate(dx, dy);
817 }
Simon Hunt5724fb42015-02-05 16:59:40 -0800818 },
819 linkAttr: {
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700820 x1: function (d) { return d.position.x1; },
821 y1: function (d) { return d.position.y1; },
822 x2: function (d) { return d.position.x2; },
823 y2: function (d) { return d.position.y2; }
Simon Hunt5724fb42015-02-05 16:59:40 -0800824 },
825 linkLabelAttr: {
826 transform: function (d) {
Simon Huntdc6adea2015-02-09 22:29:36 -0800827 var lnk = tms.findLinkById(d.key);
Simon Hunt5724fb42015-02-05 16:59:40 -0800828 if (lnk) {
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700829 return td3.transformLabel(lnk.position);
Simon Hunt5724fb42015-02-05 16:59:40 -0800830 }
831 }
832 }
833 };
834
835 function tick() {
Simon Hunt3ab20282015-02-26 20:32:19 -0800836 // guard against null (which can happen when our view pages out)...
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700837 if (node && node.size()) {
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700838 node.attr(tickStuff.nodeAttr);
839 }
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700840 if (link && link.size()) {
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -0700841 link.call(calcPosition)
842 .attr(tickStuff.linkAttr);
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700843 td3.applyNumLinkLabels(linkNums, numLinkLblsG);
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700844 }
Bri Prebilic Cole8d003782015-07-31 15:33:06 -0700845 if (linkLabel && linkLabel.size()) {
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700846 linkLabel.attr(tickStuff.linkLabelAttr);
847 }
Simon Hunt737c89f2015-01-28 12:23:19 -0800848 }
849
850
Simon Huntac4c6f72015-02-03 19:50:53 -0800851 // ==========================
852 // === MOUSE GESTURE HANDLERS
853
Simon Hunt205099e2015-02-07 13:12:01 -0800854 function zoomingOrPanning(ev) {
855 return ev.metaKey || ev.altKey;
Simon Hunt445e8152015-02-06 13:00:12 -0800856 }
857
858 function atDragEnd(d) {
859 // once we've finished moving, pin the node in position
860 d.fixed = true;
861 d3.select(this).classed('fixed', true);
862 sendUpdateMeta(d);
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700863 tss.clickConsumed(true);
Simon Hunt445e8152015-02-06 13:00:12 -0800864 }
865
866 // predicate that indicates when dragging is active
867 function dragEnabled() {
868 var ev = d3.event.sourceEvent;
869 // nodeLock means we aren't allowing nodes to be dragged...
Simon Hunt205099e2015-02-07 13:12:01 -0800870 return !nodeLock && !zoomingOrPanning(ev);
Simon Hunt445e8152015-02-06 13:00:12 -0800871 }
872
873 // predicate that indicates when clicking is active
874 function clickEnabled() {
875 return true;
876 }
Simon Hunt737c89f2015-01-28 12:23:19 -0800877
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700878 // =============================================
879 // function entry points for overlay module
Simon Huntf542d842015-02-11 16:20:33 -0800880
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700881 // TODO: find an automatic way of tracking via the "showHighlights" events
Simon Hunte50829c2015-06-09 08:39:28 -0700882 var allTrafficClasses = 'primary secondary optical animated ' +
883 'port-traffic-Kbps port-traffic-Mbps port-traffic-Gbps ' +
884 'port-traffic-Gbps-choked';
Simon Huntf542d842015-02-11 16:20:33 -0800885
886 function clearLinkTrafficStyle() {
887 link.style('stroke-width', null)
888 .classed(allTrafficClasses, false);
889 }
890
891 function removeLinkLabels() {
892 network.links.forEach(function (d) {
893 d.label = '';
894 });
895 }
Simon Hunt737c89f2015-01-28 12:23:19 -0800896
Simon Hunte9343f32015-10-21 18:07:46 -0700897 function clearNodeDeco() {
898 node.selectAll('g.badge').remove();
899 }
900
901 function removeNodeBadges() {
902 network.nodes.forEach(function (d) {
903 d.badge = null;
904 });
905 }
906
Simon Hunta4242de2015-02-24 17:11:55 -0800907 function updateLinkLabelModel() {
908 // create the backing data for showing labels..
909 var data = [];
910 link.each(function (d) {
911 if (d.label) {
912 data.push({
913 id: 'lab-' + d.key,
914 key: d.key,
915 label: d.label,
916 ldata: d
917 });
918 }
919 });
920
921 linkLabel = linkLabelG.selectAll('.linkLabel')
922 .data(data, function (d) { return d.id; });
923 }
924
Simon Hunt737c89f2015-01-28 12:23:19 -0800925 // ==========================
Simon Huntac4c6f72015-02-03 19:50:53 -0800926 // Module definition
Simon Hunt737c89f2015-01-28 12:23:19 -0800927
Simon Huntdc6adea2015-02-09 22:29:36 -0800928 function mkModelApi(uplink) {
929 return {
930 projection: uplink.projection,
931 network: network,
932 restyleLinkElement: restyleLinkElement,
933 removeLinkElement: removeLinkElement
934 };
935 }
936
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700937 function mkD3Api() {
Simon Hunta4242de2015-02-24 17:11:55 -0800938 return {
939 node: function () { return node; },
940 link: function () { return link; },
941 linkLabel: function () { return linkLabel; },
942 instVisible: function () { return tis.isVisible(); },
943 posNode: tms.positionNode,
944 showHosts: function () { return showHosts; },
945 restyleLinkElement: restyleLinkElement,
Bri Prebilic Cole80401762015-07-16 11:36:18 -0700946 updateLinkLabelModel: updateLinkLabelModel,
947 linkConfig: function () { return linkConfig; }
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700948 };
Simon Hunta4242de2015-02-24 17:11:55 -0800949 }
950
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700951 function mkSelectApi() {
Simon Hunt08f841d02015-02-10 14:39:20 -0800952 return {
953 node: function () { return node; },
954 zoomingOrPanning: zoomingOrPanning,
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700955 updateDeviceColors: td3.updateDeviceColors,
956 deselectLink: tls.deselectLink
Simon Hunt08f841d02015-02-10 14:39:20 -0800957 };
958 }
959
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700960 function mkTrafficApi() {
961 return {
962 hovered: tss.hovered,
963 somethingSelected: tss.somethingSelected,
964 selectOrder: tss.selectOrder
965 };
966 }
967
968 function mkOverlayApi() {
Simon Huntf542d842015-02-11 16:20:33 -0800969 return {
Simon Hunte9343f32015-10-21 18:07:46 -0700970 clearNodeDeco: clearNodeDeco,
971 removeNodeBadges: removeNodeBadges,
Simon Huntf542d842015-02-11 16:20:33 -0800972 clearLinkTrafficStyle: clearLinkTrafficStyle,
973 removeLinkLabels: removeLinkLabels,
Simon Hunt743a8492015-08-25 16:18:19 -0700974 findLinkById: tms.findLinkById,
Simon Hunt94f7dae2015-08-26 17:40:59 -0700975 findNodeById: nodeById,
Simon Huntf542d842015-02-11 16:20:33 -0800976 updateLinks: updateLinks,
Simon Hunt743a8492015-08-25 16:18:19 -0700977 updateNodes: updateNodes,
978 supLayers: suppressLayers,
979 unsupNode: unsuppressNode,
980 unsupLink: unsuppressLink
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -0700981 };
Simon Huntf542d842015-02-11 16:20:33 -0800982 }
983
Simon Huntc3c5b672015-02-20 11:32:13 -0800984 function mkObliqueApi(uplink, fltr) {
Simon Hunt96f88c62015-02-19 17:57:25 -0800985 return {
Simon Huntc3c5b672015-02-20 11:32:13 -0800986 force: function() { return force; },
987 zoomLayer: uplink.zoomLayer,
988 nodeGBBox: function() { return nodeG.node().getBBox(); },
Simon Hunt96f88c62015-02-19 17:57:25 -0800989 node: function () { return node; },
Simon Huntc3c5b672015-02-20 11:32:13 -0800990 link: function () { return link; },
991 linkLabel: function () { return linkLabel; },
992 nodes: function () { return network.nodes; },
993 tickStuff: tickStuff,
994 nodeLock: function (b) {
995 var old = nodeLock;
996 nodeLock = b;
997 return old;
998 },
999 opacifyMap: uplink.opacifyMap,
Bri Prebilic Cole038aedd2015-07-13 15:25:16 -07001000 inLayer: fltr.inLayer,
Bri Prebilic Cole80401762015-07-16 11:36:18 -07001001 calcLinkPos: calcPosition,
1002 applyNumLinkLabels: function () {
1003 td3.applyNumLinkLabels(linkNums, numLinkLblsG);
1004 }
Simon Hunt96f88c62015-02-19 17:57:25 -08001005 };
1006 }
1007
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001008 function mkFilterApi() {
Simon Hunteb0fa052015-02-17 19:20:28 -08001009 return {
1010 node: function () { return node; },
1011 link: function () { return link; }
1012 };
1013 }
1014
Simon Hunt9e2104c2015-02-26 10:48:59 -08001015 function mkLinkApi(svg, uplink) {
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001016 return {
1017 svg: svg,
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001018 zoomer: uplink.zoomer(),
1019 network: network,
Simon Hunt1a5301e2015-02-25 15:31:25 -08001020 portLabelG: function () { return portLabelG; },
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001021 showHosts: function () { return showHosts; }
1022 };
1023 }
1024
Simon Hunt737c89f2015-01-28 12:23:19 -08001025 angular.module('ovTopo')
1026 .factory('TopoForceService',
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -07001027 ['$log', '$timeout', 'FnService', 'SvgUtilService',
Simon Hunt86b7c882015-04-02 23:06:08 -07001028 'ThemeService', 'FlashService', 'WebSocketService',
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001029 'TopoOverlayService', 'TopoInstService', 'TopoModelService',
Simon Hunta4242de2015-02-24 17:11:55 -08001030 'TopoD3Service', 'TopoSelectService', 'TopoTrafficService',
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001031 'TopoObliqueService', 'TopoFilterService', 'TopoLinkService',
Simon Hunt737c89f2015-01-28 12:23:19 -08001032
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001033 function (_$log_, _$timeout_, _fs_, _sus_, _ts_, _flash_, _wss_, _tov_,
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001034 _tis_, _tms_, _td3_, _tss_, _tts_, _tos_, _fltr_, _tls_) {
Simon Hunt737c89f2015-01-28 12:23:19 -08001035 $log = _$log_;
Simon Hunt86b7c882015-04-02 23:06:08 -07001036 $timeout = _$timeout_;
Simon Hunt1894d792015-02-04 17:09:20 -08001037 fs = _fs_;
Simon Hunt737c89f2015-01-28 12:23:19 -08001038 sus = _sus_;
Simon Huntac4c6f72015-02-03 19:50:53 -08001039 ts = _ts_;
Simon Hunt5724fb42015-02-05 16:59:40 -08001040 flash = _flash_;
Simon Hunt237676b52015-03-10 19:04:26 -07001041 wss = _wss_;
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001042 tov = _tov_;
Simon Huntac4c6f72015-02-03 19:50:53 -08001043 tis = _tis_;
Simon Hunt3a6eec02015-02-09 21:16:43 -08001044 tms = _tms_;
Simon Hunta4242de2015-02-24 17:11:55 -08001045 td3 = _td3_;
Simon Hunt08f841d02015-02-10 14:39:20 -08001046 tss = _tss_;
Simon Huntf542d842015-02-11 16:20:33 -08001047 tts = _tts_;
Simon Hunt96f88c62015-02-19 17:57:25 -08001048 tos = _tos_;
Simon Hunteb0fa052015-02-17 19:20:28 -08001049 fltr = _fltr_;
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001050 tls = _tls_;
Simon Hunt737c89f2015-01-28 12:23:19 -08001051
Simon Hunta142dd22015-02-12 22:07:51 -08001052 var themeListener = ts.addListener(function () {
1053 updateLinks();
1054 updateNodes();
1055 });
1056
Simon Hunt737c89f2015-01-28 12:23:19 -08001057 // forceG is the SVG group to display the force layout in
Simon Huntdc6adea2015-02-09 22:29:36 -08001058 // uplink is the api from the main topo source file
Simon Hunt3a6eec02015-02-09 21:16:43 -08001059 // dim is the initial dimensions of the SVG as [w,h]
Simon Hunt737c89f2015-01-28 12:23:19 -08001060 // opts are, well, optional :)
Simon Hunt3ab20282015-02-26 20:32:19 -08001061 function initForce(_svg_, forceG, _uplink_, _dim_, opts) {
Simon Hunt1894d792015-02-04 17:09:20 -08001062 uplink = _uplink_;
Simon Hunt3a6eec02015-02-09 21:16:43 -08001063 dim = _dim_;
Simon Hunt3ab20282015-02-26 20:32:19 -08001064 svg = _svg_;
1065
1066 lu = network.lookup;
1067 rlk = network.revLinkToKey;
Simon Hunt3a6eec02015-02-09 21:16:43 -08001068
1069 $log.debug('initForce().. dim = ' + dim);
1070
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001071 tov.setApi(mkOverlayApi(), tss);
Simon Huntdc6adea2015-02-09 22:29:36 -08001072 tms.initModel(mkModelApi(uplink), dim);
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001073 td3.initD3(mkD3Api());
1074 tss.initSelect(mkSelectApi());
1075 tts.initTraffic(mkTrafficApi());
Simon Huntc3c5b672015-02-20 11:32:13 -08001076 tos.initOblique(mkObliqueApi(uplink, fltr));
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001077 fltr.initFilter(mkFilterApi());
Simon Hunt9e2104c2015-02-26 10:48:59 -08001078 tls.initLink(mkLinkApi(svg, uplink), td3);
Simon Hunta11b4eb2015-01-28 16:20:50 -08001079
Simon Hunt737c89f2015-01-28 12:23:19 -08001080 settings = angular.extend({}, defaultSettings, opts);
1081
1082 linkG = forceG.append('g').attr('id', 'topo-links');
1083 linkLabelG = forceG.append('g').attr('id', 'topo-linkLabels');
Bri Prebilic Cole80401762015-07-16 11:36:18 -07001084 numLinkLblsG = forceG.append('g').attr('id', 'topo-numLinkLabels');
Simon Hunt737c89f2015-01-28 12:23:19 -08001085 nodeG = forceG.append('g').attr('id', 'topo-nodes');
Simon Hunt1a5301e2015-02-25 15:31:25 -08001086 portLabelG = forceG.append('g').attr('id', 'topo-portLabels');
Simon Hunt737c89f2015-01-28 12:23:19 -08001087
1088 link = linkG.selectAll('.link');
1089 linkLabel = linkLabelG.selectAll('.linkLabel');
1090 node = nodeG.selectAll('.node');
1091
1092 force = d3.layout.force()
Simon Hunt3a6eec02015-02-09 21:16:43 -08001093 .size(dim)
Simon Hunt737c89f2015-01-28 12:23:19 -08001094 .nodes(network.nodes)
1095 .links(network.links)
1096 .gravity(settings.gravity)
1097 .friction(settings.friction)
1098 .charge(settings.charge._def_)
1099 .linkDistance(settings.linkDistance._def_)
1100 .linkStrength(settings.linkStrength._def_)
1101 .on('tick', tick);
1102
1103 drag = sus.createDragBehavior(force,
Simon Hunt08f841d02015-02-10 14:39:20 -08001104 tss.selectObject, atDragEnd, dragEnabled, clickEnabled);
Simon Hunt737c89f2015-01-28 12:23:19 -08001105 }
1106
Simon Hunt3a6eec02015-02-09 21:16:43 -08001107 function newDim(_dim_) {
1108 dim = _dim_;
1109 force.size(dim);
1110 tms.newDim(dim);
Simon Hunt737c89f2015-01-28 12:23:19 -08001111 }
1112
Simon Hunt3a6eec02015-02-09 21:16:43 -08001113 function destroyForce() {
Simon Hunt3ab20282015-02-26 20:32:19 -08001114 force.stop();
1115
Simon Huntfb8ea1f2015-02-24 21:38:09 -08001116 tls.destroyLink();
Simon Hunt96f88c62015-02-19 17:57:25 -08001117 tos.destroyOblique();
Simon Huntf542d842015-02-11 16:20:33 -08001118 tts.destroyTraffic();
1119 tss.destroySelect();
Simon Hunta4242de2015-02-24 17:11:55 -08001120 td3.destroyD3();
Simon Huntf542d842015-02-11 16:20:33 -08001121 tms.destroyModel();
Simon Hunt8d22c4b2015-08-06 16:24:43 -07001122 // note: no need to destroy overlay service
Simon Hunta142dd22015-02-12 22:07:51 -08001123 ts.removeListener(themeListener);
1124 themeListener = null;
Simon Hunt3ab20282015-02-26 20:32:19 -08001125
1126 // clean up the DOM
1127 svg.selectAll('g').remove();
1128 svg.selectAll('defs').remove();
1129
1130 // clean up internal state
1131 network.nodes = [];
1132 network.links = [];
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -07001133 network.linksByDevice = {};
Simon Hunt3ab20282015-02-26 20:32:19 -08001134 network.lookup = {};
1135 network.revLinkToKey = {};
1136
Bri Prebilic Cole80401762015-07-16 11:36:18 -07001137 linkNums = [];
1138
1139 linkG = linkLabelG = numLinkLblsG = nodeG = portLabelG = null;
Simon Hunt3ab20282015-02-26 20:32:19 -08001140 link = linkLabel = node = null;
1141 force = drag = null;
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -07001142
1143 // clean up $timeout promises
Simon Hunta17fa672015-08-19 18:42:22 -07001144 if (fTimer) {
1145 $timeout.cancel(fTimer);
1146 }
Bri Prebilic Coleaeeb33e2015-07-09 15:15:54 -07001147 if (fNodesTimer) {
1148 $timeout.cancel(fNodesTimer);
1149 }
1150 if (fLinksTimer) {
1151 $timeout.cancel(fLinksTimer);
1152 }
Simon Hunt3a6eec02015-02-09 21:16:43 -08001153 }
1154
Simon Hunt737c89f2015-01-28 12:23:19 -08001155 return {
1156 initForce: initForce,
Simon Hunt3a6eec02015-02-09 21:16:43 -08001157 newDim: newDim,
1158 destroyForce: destroyForce,
Simon Huntac4c6f72015-02-03 19:50:53 -08001159
Simon Hunta4242de2015-02-24 17:11:55 -08001160 updateDeviceColors: td3.updateDeviceColors,
Simon Hunt5724fb42015-02-05 16:59:40 -08001161 toggleHosts: toggleHosts,
Simon Hunt9e2104c2015-02-26 10:48:59 -08001162 togglePorts: tls.togglePorts,
Simon Hunt5724fb42015-02-05 16:59:40 -08001163 toggleOffline: toggleOffline,
1164 cycleDeviceLabels: cycleDeviceLabels,
Simon Hunt445e8152015-02-06 13:00:12 -08001165 unpin: unpin,
Simon Hunta142dd22015-02-12 22:07:51 -08001166 showMastership: showMastership,
Simon Hunt86b7c882015-04-02 23:06:08 -07001167 showBadLinks: showBadLinks,
Simon Huntac4c6f72015-02-03 19:50:53 -08001168
1169 addDevice: addDevice,
Simon Hunt1894d792015-02-04 17:09:20 -08001170 updateDevice: updateDevice,
1171 removeDevice: removeDevice,
1172 addHost: addHost,
1173 updateHost: updateHost,
Simon Hunt95d56fd2015-11-12 11:06:44 -08001174 moveHost: moveHost,
Simon Hunt1894d792015-02-04 17:09:20 -08001175 removeHost: removeHost,
1176 addLink: addLink,
1177 updateLink: updateLink,
Simon Hunt4a6b54b2015-10-27 22:08:25 -07001178 removeLink: removeLink,
1179 topoStartDone: topoStartDone
Simon Hunt737c89f2015-01-28 12:23:19 -08001180 };
1181 }]);
1182}());