blob: d9057576f6857f817e9c2415ee8e38f3bf9ea6d3 [file] [log] [blame]
Steven Burrows57e24e92016-08-04 18:38:24 +01001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
Steven Burrows57e24e92016-08-04 18:38:24 +01003 *
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/*
18 ONOS GUI -- Topology Links Module.
19 Module that holds the links for a region
20 */
21
22(function () {
23 'use strict';
24
Steven Burrowsca1a39c2017-05-08 17:31:08 -040025 var $log, Collection, Model, ts, sus, t2zs, t2vs, t2lps,
26 fn, ps, t2mss, t2ts;
Steven Burrows9edc7e02016-08-29 11:52:07 +010027
28 var linkLabelOffset = '0.35em';
Steven Burrows57e24e92016-08-04 18:38:24 +010029
Steven Burrowsec1f45c2016-08-08 16:14:41 +010030 var widthRatio = 1.4,
31 linkScale = d3.scale.linear()
32 .domain([1, 12])
33 .range([widthRatio, 12 * widthRatio])
34 .clamp(true),
Steven Burrowsa3fca812016-10-14 15:11:04 -050035 labelDim = 30;
Steven Burrowsec1f45c2016-08-08 16:14:41 +010036
37 // configuration
38 var linkConfig = {
39 light: {
40 baseColor: '#939598',
41 inColor: '#66f',
Steven Burrows1c2a9682017-07-14 16:52:46 +010042 outColor: '#f00',
Steven Burrowsec1f45c2016-08-08 16:14:41 +010043 },
44 dark: {
45 // TODO : theme
46 baseColor: '#939598',
47 inColor: '#66f',
Steven Burrows1c2a9682017-07-14 16:52:46 +010048 outColor: '#f00',
Steven Burrowsec1f45c2016-08-08 16:14:41 +010049 },
50 inWidth: 12,
Steven Burrows1c2a9682017-07-14 16:52:46 +010051 outWidth: 10,
Steven Burrowsec1f45c2016-08-08 16:14:41 +010052 };
53
Steven Burrowsec1f45c2016-08-08 16:14:41 +010054 function createLink() {
55
56 var linkPoints = this.linkEndPoints(this.get('epA'), this.get('epB'));
Steven Burrowsec1f45c2016-08-08 16:14:41 +010057
58 var attrs = angular.extend({}, linkPoints, {
59 key: this.get('id'),
60 class: 'link',
Steven Burrowsec1f45c2016-08-08 16:14:41 +010061 position: {
62 x1: 0,
63 y1: 0,
64 x2: 0,
Steven Burrows1c2a9682017-07-14 16:52:46 +010065 y2: 0,
66 },
Steven Burrowsec1f45c2016-08-08 16:14:41 +010067 // functions to aggregate dual link state
Steven Burrows9edc7e02016-08-29 11:52:07 +010068 // extra: link.extra
Steven Burrowsec1f45c2016-08-08 16:14:41 +010069 });
70
71 this.set(attrs);
72 }
73
Steven Burrows9edc7e02016-08-29 11:52:07 +010074 function rectAroundText(el) {
75 var text = el.select('text'),
76 box = text.node().getBBox();
77
78 // translate the bbox so that it is centered on [x,y]
79 box.x = -box.width / 2;
80 box.y = -box.height / 2;
81
82 // add padding
83 box.x -= 4;
84 box.width += 8;
85 return box;
86 }
87
Steven Burrowsdfa52b02016-09-02 13:50:43 +010088 function isLinkOnline(node) {
89 return (node.get('nodeType') === 'region') ? true : node.get('online');
90 }
91
Steven Burrowsec1f45c2016-08-08 16:14:41 +010092 function linkEndPoints(srcId, dstId) {
93
Steven Burrowsaf96a212016-12-28 12:57:02 +000094 var findNodeById = this.region.model.findNodeById.bind(this.region),
95 allNodes = this.region.model.nodes(),
96 sourceNode = findNodeById(this, srcId),
97 targetNode = findNodeById(this, dstId);
Steven Burrowsec1f45c2016-08-08 16:14:41 +010098
Steven Burrows6deb4ce2016-08-26 16:06:23 +010099 if (!sourceNode || !targetNode) {
Steven Burrows8f45ce22016-10-27 20:04:14 -0500100 $log.error('Node(s) not on map for link:' + srcId + '~' + dstId);
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100101 // logicError('Node(s) not on map for link:\n' + sMiss + dMiss);
Steven Burrows6deb4ce2016-08-26 16:06:23 +0100102 return null;
103 }
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100104
Steven Burrowsa3fca812016-10-14 15:11:04 -0500105 this.source = allNodes.indexOf(sourceNode);
106 this.target = allNodes.indexOf(targetNode);
107 this.sourceNode = sourceNode;
108 this.targetNode = targetNode;
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100109
110 return {
111 source: sourceNode,
Steven Burrows1c2a9682017-07-14 16:52:46 +0100112 target: targetNode,
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100113 };
114 }
115
116 function createLinkCollection(data, _region) {
117
118 var LinkModel = Model.extend({
Steven Burrows68d6f952017-03-10 13:53:35 +0000119 initialize: function () {
120 this.super = this.constructor.__super__;
121 this.super.initialize.apply(this, arguments);
122 this.createLink();
123 },
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100124 region: _region,
125 createLink: createLink,
126 linkEndPoints: linkEndPoints,
127 type: function () {
128 return this.get('type');
129 },
Steven Burrows86af4352016-11-16 18:19:12 -0600130 svgClassName: function () {
131 return fn.classNames('link',
132 this.nodeType,
133 this.get('type'),
134 {
135 enhanced: this.get('enhanced'),
Steven Burrowsc515e602017-04-13 11:17:40 -0700136 selected: this.get('selected'),
Steven Burrows1c2a9682017-07-14 16:52:46 +0100137 suppressedmax: this.get('mastership'),
Steven Burrowsca1a39c2017-05-08 17:31:08 -0400138 },
139 (this.linkLabel) ? this.linkLabel.linkLabelCSSClass() : null
Steven Burrows86af4352016-11-16 18:19:12 -0600140 );
141 },
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100142 expected: function () {
Steven Burrows9edc7e02016-08-29 11:52:07 +0100143 // TODO: original code is: (s && s.expected) && (t && t.expected);
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100144 return true;
145 },
146 online: function () {
Steven Burrows9edc7e02016-08-29 11:52:07 +0100147
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100148 var source = this.get('source'),
149 target = this.get('target'),
150 sourceOnline = isLinkOnline(source),
151 targetOnline = isLinkOnline(target);
152
153 return (sourceOnline) && (targetOnline);
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100154 },
Steven Burrows86af4352016-11-16 18:19:12 -0600155 onChange: function () {
156 // Update class names when the model changes
157 if (this.el) {
158 this.el.attr('class', this.svgClassName());
Steven Burrowsca1a39c2017-05-08 17:31:08 -0400159 this.setScale();
Steven Burrows86af4352016-11-16 18:19:12 -0600160 }
161 },
Steven Burrows9edc7e02016-08-29 11:52:07 +0100162 enhance: function () {
163 var data = [],
164 point;
165
Steven Burrows42d7a6c2017-11-01 15:54:57 +0000166 var hostsVisible = ps.getPrefs('topo2_prefs')['hosts'];
167 if (!hostsVisible && this.get('type') === 'UiEdgeLink') {
168 return;
169 }
170
Steven Burrows1c5c8612016-10-05 13:45:13 -0500171 if (showPort()) {
Steven Burrowsaf96a212016-12-28 12:57:02 +0000172 this.set('enhanced', true);
Steven Burrows1c5c8612016-10-05 13:45:13 -0500173 point = this.locatePortLabel();
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100174 angular.extend(point, {
Simon Hunt95f4b422017-03-03 13:49:05 -0800175 id: 'topo2-port-tgt',
Steven Burrows1c2a9682017-07-14 16:52:46 +0100176 num: this.get('portB'),
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100177 });
178 data.push(point);
Steven Burrows1c5c8612016-10-05 13:45:13 -0500179
180 if (this.get('portA')) {
181 point = this.locatePortLabel(1);
182 angular.extend(point, {
Simon Hunt95f4b422017-03-03 13:49:05 -0800183 id: 'topo2-port-src',
Steven Burrows1c2a9682017-07-14 16:52:46 +0100184 num: this.get('portA'),
Steven Burrows1c5c8612016-10-05 13:45:13 -0500185 });
186 data.push(point);
187 }
188
Steven Burrowsbd402842017-03-08 21:30:38 +0000189 var entering = d3.select('.topo2-portLabels')
Steven Burrows1c5c8612016-10-05 13:45:13 -0500190 .selectAll('.portLabel')
Steven Burrowsa3fca812016-10-14 15:11:04 -0500191 .data(data)
192 .enter().append('g')
Steven Burrows1c5c8612016-10-05 13:45:13 -0500193 .classed('portLabel', true)
Steven Burrowsaf96a212016-12-28 12:57:02 +0000194 .attr('id', function (d) { return d.id; });
Steven Burrows1c5c8612016-10-05 13:45:13 -0500195
196 entering.each(function (d) {
197 var el = d3.select(this),
198 rect = el.append('rect'),
199 text = el.append('text').text(d.num);
200
201 var rectSize = rectAroundText(el);
202
203 rect.attr(rectSize)
204 .attr('rx', 2)
205 .attr('ry', 2);
206
207 text.attr('dy', linkLabelOffset)
208 .attr('text-anchor', 'middle');
209
210 el.attr('transform', sus.translate(d.x, d.y));
Steven Burrowsa3fca812016-10-14 15:11:04 -0500211
Steven Burrows1c5c8612016-10-05 13:45:13 -0500212 });
Steven Burrowsa3fca812016-10-14 15:11:04 -0500213
214 this.setScale();
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100215 }
Steven Burrows9edc7e02016-08-29 11:52:07 +0100216 },
217 unenhance: function () {
Steven Burrows86af4352016-11-16 18:19:12 -0600218 this.set('enhanced', false);
Steven Burrowsbd402842017-03-08 21:30:38 +0000219 d3.select('.topo2-portLabels').selectAll('.portLabel').remove();
Steven Burrows0bc66652017-04-20 13:04:10 -0400220 this.setScale();
Steven Burrows9edc7e02016-08-29 11:52:07 +0100221 },
Steven Burrows32ce1d92017-04-13 13:18:44 -0700222 amt: function (numLinks, index) {
Steven Burrows9ce0e402017-04-20 16:39:17 -0400223 var bbox = this.get('source').el.node().getBBox(),
224 gap = bbox.width / 4;
Steven Burrows32ce1d92017-04-13 13:18:44 -0700225 return (index - ((numLinks - 1) / 2)) * gap;
226 },
227 defaultPosition: function () {
228 return {
229 x1: this.get('source').x,
230 y1: this.get('source').y,
231 x2: this.get('target').x,
Steven Burrows1c2a9682017-07-14 16:52:46 +0100232 y2: this.get('target').y,
233 };
Steven Burrows32ce1d92017-04-13 13:18:44 -0700234 },
235 calcMovement: function (amt, flipped) {
236 var pos = this.defaultPosition(),
237 mult = flipped ? -amt : amt,
238 dx = pos.x2 - pos.x1,
239 dy = pos.y2 - pos.y1,
240 length = Math.sqrt((dx * dx) + (dy * dy));
241
242 return {
243 x1: pos.x1 + (mult * dy / length),
244 y1: pos.y1 + (mult * -dx / length),
245 x2: pos.x2 + (mult * dy / length),
Steven Burrows1c2a9682017-07-14 16:52:46 +0100246 y2: pos.y2 + (mult * -dx / length),
Steven Burrows32ce1d92017-04-13 13:18:44 -0700247 };
248 },
Steven Burrowsa31c5b02017-04-12 10:45:08 -0700249 setPosition: function () {
Steven Burrows32ce1d92017-04-13 13:18:44 -0700250 var multiline = this.get('multiline');
251 if (multiline) {
252 var offsetAmt = this.amt(multiline.deviceLinks, multiline.index);
253 this.set('position', this.calcMovement(offsetAmt));
254 } else {
255 this.set('position', this.defaultPosition());
256 }
Steven Burrowsa31c5b02017-04-12 10:45:08 -0700257
258 if (this.get('enhanced')) {
259 this.updatePortPosition();
260 }
Steven Burrows9ce0e402017-04-20 16:39:17 -0400261
262 if (this.el) {
263 this.el.attr(this.get('position'));
264 }
265
Steven Burrowsca1a39c2017-05-08 17:31:08 -0400266 if (this.linkLabel) {
267 this.linkLabel.setPosition();
268 }
Steven Burrowsa31c5b02017-04-12 10:45:08 -0700269 },
270 updatePortPosition: function () {
271 var sourcePos = this.locatePortLabel(1),
272 targetPos = this.locatePortLabel();
273 d3.select('#topo2-port-src')
274 .attr('transform', sus.translate(sourcePos.x, sourcePos.y));
275 d3.select('#topo2-port-tgt')
276 .attr('transform', sus.translate(targetPos.x, targetPos.y));
277 },
Steven Burrows1aa4f582016-12-13 15:05:41 -0500278 getSelected: function () {
279 return this.collection.filter(function (m) {
280 return m.get('selected');
281 });
282 },
Steven Burrows86af4352016-11-16 18:19:12 -0600283 select: function () {
Steven Burrows5fa057e2017-03-15 17:07:56 +0000284 this.set({ 'selected': true });
Steven Burrows1aa4f582016-12-13 15:05:41 -0500285 return this.getSelected();
286 },
287 deselect: function () {
Steven Burrows5fa057e2017-03-15 17:07:56 +0000288 this.set({
289 'selected': false,
Steven Burrows1c2a9682017-07-14 16:52:46 +0100290 'enhanced': false,
Steven Burrows5fa057e2017-03-15 17:07:56 +0000291 });
Steven Burrows86af4352016-11-16 18:19:12 -0600292 },
293 showDetails: function () {
Steven Burrows1aa4f582016-12-13 15:05:41 -0500294 var selected = this.getSelected();
Steven Burrows86af4352016-11-16 18:19:12 -0600295
296 if (selected) {
297 t2lps.displayLink(this);
298 } else {
299 t2lps.hide();
300 }
301 },
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100302 locatePortLabel: function (src) {
Steven Burrowsa3fca812016-10-14 15:11:04 -0500303 var offset = 32 / (labelDim * t2zs.scale()),
304 sourceX = this.get('position').x1,
305 sourceY = this.get('position').y1,
306 targetX = this.get('position').x2,
307 targetY = this.get('position').y2,
308 nearX = src ? sourceX : targetX,
309 nearY = src ? sourceY : targetY,
310 farX = src ? targetX : sourceX,
311 farY = src ? targetY : sourceY;
Steven Burrows9edc7e02016-08-29 11:52:07 +0100312
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100313 function dist(x, y) {
314 return Math.sqrt(x * x + y * y);
315 }
Steven Burrows9edc7e02016-08-29 11:52:07 +0100316
317 var dx = farX - nearX,
318 dy = farY - nearY,
Steven Burrows0bc66652017-04-20 13:04:10 -0400319 k = (32 * offset) / dist(dx, dy);
Steven Burrows9edc7e02016-08-29 11:52:07 +0100320
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100321 return { x: k * dx + nearX, y: k * dy + nearY };
Steven Burrows9edc7e02016-08-29 11:52:07 +0100322 },
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100323 onEnter: function (el) {
Steven Burrows448468c2017-04-13 16:09:30 -0700324 var link = d3.select(el),
325 th = ts.theme(),
326 delay = 1000;
Steven Burrows9edc7e02016-08-29 11:52:07 +0100327
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100328 this.el = link;
Steven Burrows448468c2017-04-13 16:09:30 -0700329
330 link.transition()
331 .duration(delay)
332 .attr('stroke', linkConfig[th].baseColor);
333
Steven Burrowsaea509d2017-04-12 14:17:47 -0700334 this.setVisibility();
Steven Burrows448468c2017-04-13 16:09:30 -0700335 this.setScale();
Steven Burrows0616e802016-10-06 21:45:07 -0500336 },
Steven Burrowsca1a39c2017-05-08 17:31:08 -0400337 linkWidth: function () {
338 var width = widthRatio;
339 if (this.get('enhanced')) { width = 3.5; }
340 if (this.linkLabel) {
341 var scale = d3.scale.ordinal()
342 .rangeRoundPoints([4, 8]),
343 label = this.linkLabel.get('label').split(' ');
344
345 switch (t2ts.selectedTrafficOverlay()) {
346 case 'flowStatsBytes':
347 scale.domain(['KB', 'MB', 'GB']);
348 width = scale(label[1]);
349 break;
350 case 'portStatsBitSec':
Steven Burrows2b29ca42017-05-12 10:58:05 -0400351 scale.domain(['Kbps', 'Mbps', 'Gbps']);
Steven Burrowsca1a39c2017-05-08 17:31:08 -0400352 width = scale(label[1]);
353 break;
354 case 'portStatsPktSec':
355 scale = d3.scale.linear()
356 .domain([1, 10, 100, 1000, 10000])
357 .range(d3.range(3.5, 9))
358 .clamp(true);
359 width = scale(parseInt(label[0]));
360 }
361 }
362
363 return width;
364 },
Steven Burrows0616e802016-10-06 21:45:07 -0500365 setScale: function () {
Steven Burrowse8a455a2017-03-16 16:58:59 +0000366
367 if (!this.el) return;
368
Steven Burrowsca1a39c2017-05-08 17:31:08 -0400369 var linkWidthRatio = this.linkWidth();
Steven Burrows0bc66652017-04-20 13:04:10 -0400370
371 var width = linkScale(linkWidthRatio) / t2zs.scale();
Steven Burrows448468c2017-04-13 16:09:30 -0700372 this.el.attr('stroke-width', width + 'px');
Steven Burrowsa3fca812016-10-14 15:11:04 -0500373
374 var labelScale = labelDim / (labelDim * t2zs.scale());
375
Steven Burrows8909c5c2017-04-10 09:56:52 -0700376 d3.select('.topo2-portLabels')
Steven Burrowsa3fca812016-10-14 15:11:04 -0500377 .selectAll('.portLabel')
378 .selectAll('*')
379 .style('transform', 'scale(' + labelScale + ')');
380
Steven Burrows9ce0e402017-04-20 16:39:17 -0400381 this.setPosition();
382
Steven Burrowsca1a39c2017-05-08 17:31:08 -0400383 if (this.linkLabel) {
384 this.linkLabel.setScale();
385 }
Steven Burrows1c5c8612016-10-05 13:45:13 -0500386 },
387 update: function () {
Steven Burrows86af4352016-11-16 18:19:12 -0600388 if (this.get('enhanced')) {
Steven Burrows1c5c8612016-10-05 13:45:13 -0500389 this.enhance();
390 }
Steven Burrowsb15a3942017-01-17 17:25:04 +0000391 },
Steven Burrowsaea509d2017-04-12 14:17:47 -0700392 setVisibility: function () {
393
394 if (this.get('type') !== 'UiEdgeLink') {
395 return;
396 }
397
398 var visible = ps.getPrefs('topo2_prefs')['hosts'];
399 this.el.style('visibility', visible ? 'visible' : 'hidden');
400 },
Steven Burrowsc515e602017-04-13 11:17:40 -0700401 displayMastership: function () {
Steven Burrows1c2a9682017-07-14 16:52:46 +0100402 this.set({ mastership: t2mss.mastership() !== null });
Steven Burrowsc515e602017-04-13 11:17:40 -0700403 },
Steven Burrowsb15a3942017-01-17 17:25:04 +0000404 remove: function () {
405
406 var width = linkScale(widthRatio) / t2zs.scale();
407
408 this.el.transition()
409 .duration(300)
410 .attr('stroke', '#ff0000')
411 .style('stroke-width', width * 4)
412 .transition()
413 .delay(1000)
414 .style('opacity', 0);
Steven Burrows1c2a9682017-07-14 16:52:46 +0100415 },
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100416 });
Steven Burrows57e24e92016-08-04 18:38:24 +0100417
418 var LinkCollection = Collection.extend({
Steven Burrows1c2a9682017-07-14 16:52:46 +0100419 model: LinkModel,
Steven Burrows57e24e92016-08-04 18:38:24 +0100420 });
421
422 return new LinkCollection(data);
423 }
424
Steven Burrows1c5c8612016-10-05 13:45:13 -0500425 function showPort() {
426 return t2vs.getPortHighlighting();
427 }
428
Steven Burrows57e24e92016-08-04 18:38:24 +0100429 angular.module('ovTopo2')
Steven Burrowsaf96a212016-12-28 12:57:02 +0000430 .factory('Topo2LinkService', [
431 '$log', 'Topo2Collection', 'Topo2Model',
Steven Burrows0616e802016-10-06 21:45:07 -0500432 'ThemeService', 'SvgUtilService', 'Topo2ZoomService',
Steven Burrowsaea509d2017-04-12 14:17:47 -0700433 'Topo2ViewService', 'Topo2LinkPanelService', 'FnService', 'PrefsService',
Steven Burrowsca1a39c2017-05-08 17:31:08 -0400434 'Topo2MastershipService', 'Topo2TrafficService',
Steven Burrowsaf96a212016-12-28 12:57:02 +0000435 function (_$log_, _c_, _Model_, _ts_, _sus_,
Steven Burrowsca1a39c2017-05-08 17:31:08 -0400436 _t2zs_, _t2vs_, _t2lps_, _fn_, _ps_, _t2mss_, _t2ts_) {
Steven Burrows57e24e92016-08-04 18:38:24 +0100437
Steven Burrowsaf96a212016-12-28 12:57:02 +0000438 $log = _$log_;
439 ts = _ts_;
440 sus = _sus_;
441 t2zs = _t2zs_;
442 t2vs = _t2vs_;
443 Collection = _c_;
444 Model = _Model_;
445 t2lps = _t2lps_;
446 fn = _fn_;
Steven Burrowsaea509d2017-04-12 14:17:47 -0700447 ps = _ps_;
Steven Burrowsc515e602017-04-13 11:17:40 -0700448 t2mss = _t2mss_;
Steven Burrowsca1a39c2017-05-08 17:31:08 -0400449 t2ts = _t2ts_;
Steven Burrows57e24e92016-08-04 18:38:24 +0100450
Steven Burrowsaf96a212016-12-28 12:57:02 +0000451 return {
Steven Burrows1c2a9682017-07-14 16:52:46 +0100452 createLinkCollection: createLinkCollection,
Steven Burrowsaf96a212016-12-28 12:57:02 +0000453 };
Steven Burrows1c2a9682017-07-14 16:52:46 +0100454 },
Steven Burrowsaf96a212016-12-28 12:57:02 +0000455 ]);
Steven Burrows57e24e92016-08-04 18:38:24 +0100456})();