blob: 37affcee3c703f278aad87e2ec1b9f7a1b61ceea [file] [log] [blame]
Steven Burrows57e24e92016-08-04 18:38:24 +01001/*
2 * Copyright 2016-present 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/*
18 ONOS GUI -- Topology Links Module.
19 Module that holds the links for a region
20 */
21
22(function () {
23 'use strict';
24
Steven Burrowsc515e602017-04-13 11:17:40 -070025 var $log, Collection, Model, ts, sus, t2zs, t2vs, t2lps, fn, ps, t2mss;
Steven Burrows9edc7e02016-08-29 11:52:07 +010026
27 var linkLabelOffset = '0.35em';
Steven Burrows57e24e92016-08-04 18:38:24 +010028
Steven Burrowsec1f45c2016-08-08 16:14:41 +010029 var widthRatio = 1.4,
30 linkScale = d3.scale.linear()
31 .domain([1, 12])
32 .range([widthRatio, 12 * widthRatio])
33 .clamp(true),
Steven Burrowsa3fca812016-10-14 15:11:04 -050034 labelDim = 30;
Steven Burrowsec1f45c2016-08-08 16:14:41 +010035
36 // configuration
37 var linkConfig = {
38 light: {
39 baseColor: '#939598',
40 inColor: '#66f',
41 outColor: '#f00'
42 },
43 dark: {
44 // TODO : theme
45 baseColor: '#939598',
46 inColor: '#66f',
47 outColor: '#f00'
48 },
49 inWidth: 12,
50 outWidth: 10
51 };
52
Steven Burrowsec1f45c2016-08-08 16:14:41 +010053 function createLink() {
54
55 var linkPoints = this.linkEndPoints(this.get('epA'), this.get('epB'));
Steven Burrowsec1f45c2016-08-08 16:14:41 +010056
57 var attrs = angular.extend({}, linkPoints, {
58 key: this.get('id'),
59 class: 'link',
Steven Burrowsec1f45c2016-08-08 16:14:41 +010060 position: {
61 x1: 0,
62 y1: 0,
63 x2: 0,
64 y2: 0
65 }
66 // functions to aggregate dual link state
Steven Burrows9edc7e02016-08-29 11:52:07 +010067 // extra: link.extra
Steven Burrowsec1f45c2016-08-08 16:14:41 +010068 });
69
70 this.set(attrs);
71 }
72
Steven Burrows9edc7e02016-08-29 11:52:07 +010073 function rectAroundText(el) {
74 var text = el.select('text'),
75 box = text.node().getBBox();
76
77 // translate the bbox so that it is centered on [x,y]
78 box.x = -box.width / 2;
79 box.y = -box.height / 2;
80
81 // add padding
82 box.x -= 4;
83 box.width += 8;
84 return box;
85 }
86
Steven Burrowsdfa52b02016-09-02 13:50:43 +010087 function isLinkOnline(node) {
88 return (node.get('nodeType') === 'region') ? true : node.get('online');
89 }
90
Steven Burrowsec1f45c2016-08-08 16:14:41 +010091 function linkEndPoints(srcId, dstId) {
92
Steven Burrowsaf96a212016-12-28 12:57:02 +000093 var findNodeById = this.region.model.findNodeById.bind(this.region),
94 allNodes = this.region.model.nodes(),
95 sourceNode = findNodeById(this, srcId),
96 targetNode = findNodeById(this, dstId);
Steven Burrowsec1f45c2016-08-08 16:14:41 +010097
Steven Burrows6deb4ce2016-08-26 16:06:23 +010098 if (!sourceNode || !targetNode) {
Steven Burrows8f45ce22016-10-27 20:04:14 -050099 $log.error('Node(s) not on map for link:' + srcId + '~' + dstId);
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100100 // logicError('Node(s) not on map for link:\n' + sMiss + dMiss);
Steven Burrows6deb4ce2016-08-26 16:06:23 +0100101 return null;
102 }
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100103
Steven Burrowsa3fca812016-10-14 15:11:04 -0500104 this.source = allNodes.indexOf(sourceNode);
105 this.target = allNodes.indexOf(targetNode);
106 this.sourceNode = sourceNode;
107 this.targetNode = targetNode;
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100108
109 return {
110 source: sourceNode,
111 target: targetNode
112 };
113 }
114
115 function createLinkCollection(data, _region) {
116
117 var LinkModel = Model.extend({
Steven Burrows68d6f952017-03-10 13:53:35 +0000118 initialize: function () {
119 this.super = this.constructor.__super__;
120 this.super.initialize.apply(this, arguments);
121 this.createLink();
122 },
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100123 region: _region,
124 createLink: createLink,
125 linkEndPoints: linkEndPoints,
126 type: function () {
127 return this.get('type');
128 },
Steven Burrows86af4352016-11-16 18:19:12 -0600129 svgClassName: function () {
130 return fn.classNames('link',
131 this.nodeType,
132 this.get('type'),
133 {
134 enhanced: this.get('enhanced'),
Steven Burrowsc515e602017-04-13 11:17:40 -0700135 selected: this.get('selected'),
136 suppressedmax: this.get('mastership')
Steven Burrows86af4352016-11-16 18:19:12 -0600137 }
138 );
139 },
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100140 expected: function () {
Steven Burrows9edc7e02016-08-29 11:52:07 +0100141 // TODO: original code is: (s && s.expected) && (t && t.expected);
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100142 return true;
143 },
144 online: function () {
Steven Burrows9edc7e02016-08-29 11:52:07 +0100145
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100146 var source = this.get('source'),
147 target = this.get('target'),
148 sourceOnline = isLinkOnline(source),
149 targetOnline = isLinkOnline(target);
150
151 return (sourceOnline) && (targetOnline);
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100152 },
Steven Burrows86af4352016-11-16 18:19:12 -0600153 onChange: function () {
154 // Update class names when the model changes
155 if (this.el) {
156 this.el.attr('class', this.svgClassName());
157 }
158 },
Steven Burrows9edc7e02016-08-29 11:52:07 +0100159 enhance: function () {
160 var data = [],
161 point;
162
Steven Burrows1c5c8612016-10-05 13:45:13 -0500163 if (showPort()) {
Steven Burrowsaf96a212016-12-28 12:57:02 +0000164 this.set('enhanced', true);
Steven Burrows1c5c8612016-10-05 13:45:13 -0500165 point = this.locatePortLabel();
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100166 angular.extend(point, {
Simon Hunt95f4b422017-03-03 13:49:05 -0800167 id: 'topo2-port-tgt',
Steven Burrows1c5c8612016-10-05 13:45:13 -0500168 num: this.get('portB')
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100169 });
170 data.push(point);
Steven Burrows1c5c8612016-10-05 13:45:13 -0500171
172 if (this.get('portA')) {
173 point = this.locatePortLabel(1);
174 angular.extend(point, {
Simon Hunt95f4b422017-03-03 13:49:05 -0800175 id: 'topo2-port-src',
Steven Burrows1c5c8612016-10-05 13:45:13 -0500176 num: this.get('portA')
177 });
178 data.push(point);
179 }
180
Steven Burrowsbd402842017-03-08 21:30:38 +0000181 var entering = d3.select('.topo2-portLabels')
Steven Burrows1c5c8612016-10-05 13:45:13 -0500182 .selectAll('.portLabel')
Steven Burrowsa3fca812016-10-14 15:11:04 -0500183 .data(data)
184 .enter().append('g')
Steven Burrows1c5c8612016-10-05 13:45:13 -0500185 .classed('portLabel', true)
Steven Burrowsaf96a212016-12-28 12:57:02 +0000186 .attr('id', function (d) { return d.id; });
Steven Burrows1c5c8612016-10-05 13:45:13 -0500187
188 entering.each(function (d) {
189 var el = d3.select(this),
190 rect = el.append('rect'),
191 text = el.append('text').text(d.num);
192
193 var rectSize = rectAroundText(el);
194
195 rect.attr(rectSize)
196 .attr('rx', 2)
197 .attr('ry', 2);
198
199 text.attr('dy', linkLabelOffset)
200 .attr('text-anchor', 'middle');
201
202 el.attr('transform', sus.translate(d.x, d.y));
Steven Burrowsa3fca812016-10-14 15:11:04 -0500203
Steven Burrows1c5c8612016-10-05 13:45:13 -0500204 });
Steven Burrowsa3fca812016-10-14 15:11:04 -0500205
206 this.setScale();
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100207 }
Steven Burrows9edc7e02016-08-29 11:52:07 +0100208 },
209 unenhance: function () {
Steven Burrows86af4352016-11-16 18:19:12 -0600210 this.set('enhanced', false);
Steven Burrowsbd402842017-03-08 21:30:38 +0000211 d3.select('.topo2-portLabels').selectAll('.portLabel').remove();
Steven Burrows0bc66652017-04-20 13:04:10 -0400212 this.setScale();
Steven Burrows9edc7e02016-08-29 11:52:07 +0100213 },
Steven Burrows32ce1d92017-04-13 13:18:44 -0700214 amt: function (numLinks, index) {
215 var gap = 6;
216 return (index - ((numLinks - 1) / 2)) * gap;
217 },
218 defaultPosition: function () {
219 return {
220 x1: this.get('source').x,
221 y1: this.get('source').y,
222 x2: this.get('target').x,
223 y2: this.get('target').y
224 }
225 },
226 calcMovement: function (amt, flipped) {
227 var pos = this.defaultPosition(),
228 mult = flipped ? -amt : amt,
229 dx = pos.x2 - pos.x1,
230 dy = pos.y2 - pos.y1,
231 length = Math.sqrt((dx * dx) + (dy * dy));
232
233 return {
234 x1: pos.x1 + (mult * dy / length),
235 y1: pos.y1 + (mult * -dx / length),
236 x2: pos.x2 + (mult * dy / length),
237 y2: pos.y2 + (mult * -dx / length)
238 };
239 },
Steven Burrowsa31c5b02017-04-12 10:45:08 -0700240 setPosition: function () {
Steven Burrows32ce1d92017-04-13 13:18:44 -0700241
242 var multiline = this.get('multiline');
243 if (multiline) {
244 var offsetAmt = this.amt(multiline.deviceLinks, multiline.index);
245 this.set('position', this.calcMovement(offsetAmt));
246 } else {
247 this.set('position', this.defaultPosition());
248 }
Steven Burrowsa31c5b02017-04-12 10:45:08 -0700249
250 if (this.get('enhanced')) {
251 this.updatePortPosition();
252 }
253 },
254 updatePortPosition: function () {
255 var sourcePos = this.locatePortLabel(1),
256 targetPos = this.locatePortLabel();
257 d3.select('#topo2-port-src')
258 .attr('transform', sus.translate(sourcePos.x, sourcePos.y));
259 d3.select('#topo2-port-tgt')
260 .attr('transform', sus.translate(targetPos.x, targetPos.y));
261 },
Steven Burrows1aa4f582016-12-13 15:05:41 -0500262 getSelected: function () {
263 return this.collection.filter(function (m) {
264 return m.get('selected');
265 });
266 },
Steven Burrows86af4352016-11-16 18:19:12 -0600267 select: function () {
Steven Burrows5fa057e2017-03-15 17:07:56 +0000268 this.set({ 'selected': true });
Steven Burrows1aa4f582016-12-13 15:05:41 -0500269 return this.getSelected();
270 },
271 deselect: function () {
Steven Burrows5fa057e2017-03-15 17:07:56 +0000272 this.set({
273 'selected': false,
274 'enhanced': false
275 });
Steven Burrows86af4352016-11-16 18:19:12 -0600276 },
277 showDetails: function () {
Steven Burrows1aa4f582016-12-13 15:05:41 -0500278 var selected = this.getSelected();
Steven Burrows86af4352016-11-16 18:19:12 -0600279
280 if (selected) {
281 t2lps.displayLink(this);
282 } else {
283 t2lps.hide();
284 }
285 },
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100286 locatePortLabel: function (src) {
Steven Burrowsa3fca812016-10-14 15:11:04 -0500287 var offset = 32 / (labelDim * t2zs.scale()),
288 sourceX = this.get('position').x1,
289 sourceY = this.get('position').y1,
290 targetX = this.get('position').x2,
291 targetY = this.get('position').y2,
292 nearX = src ? sourceX : targetX,
293 nearY = src ? sourceY : targetY,
294 farX = src ? targetX : sourceX,
295 farY = src ? targetY : sourceY;
Steven Burrows9edc7e02016-08-29 11:52:07 +0100296
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100297 function dist(x, y) {
298 return Math.sqrt(x * x + y * y);
299 }
Steven Burrows9edc7e02016-08-29 11:52:07 +0100300
301 var dx = farX - nearX,
302 dy = farY - nearY,
Steven Burrows0bc66652017-04-20 13:04:10 -0400303 k = (32 * offset) / dist(dx, dy);
Steven Burrows9edc7e02016-08-29 11:52:07 +0100304
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100305 return { x: k * dx + nearX, y: k * dy + nearY };
Steven Burrows9edc7e02016-08-29 11:52:07 +0100306 },
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100307 onEnter: function (el) {
Steven Burrows448468c2017-04-13 16:09:30 -0700308 var link = d3.select(el),
309 th = ts.theme(),
310 delay = 1000;
Steven Burrows9edc7e02016-08-29 11:52:07 +0100311
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100312 this.el = link;
Steven Burrows448468c2017-04-13 16:09:30 -0700313
314 link.transition()
315 .duration(delay)
316 .attr('stroke', linkConfig[th].baseColor);
317
Steven Burrowsaea509d2017-04-12 14:17:47 -0700318 this.setVisibility();
Steven Burrows448468c2017-04-13 16:09:30 -0700319 this.setScale();
Steven Burrows0616e802016-10-06 21:45:07 -0500320 },
321 setScale: function () {
Steven Burrowse8a455a2017-03-16 16:58:59 +0000322
323 if (!this.el) return;
324
Steven Burrows0bc66652017-04-20 13:04:10 -0400325 var linkWidthRatio = this.get('enhanced') ? 3.5 : widthRatio;
326
327 var width = linkScale(linkWidthRatio) / t2zs.scale();
Steven Burrows448468c2017-04-13 16:09:30 -0700328 this.el.attr('stroke-width', width + 'px');
Steven Burrowsa3fca812016-10-14 15:11:04 -0500329
330 var labelScale = labelDim / (labelDim * t2zs.scale());
331
Steven Burrows8909c5c2017-04-10 09:56:52 -0700332 d3.select('.topo2-portLabels')
Steven Burrowsa3fca812016-10-14 15:11:04 -0500333 .selectAll('.portLabel')
334 .selectAll('*')
335 .style('transform', 'scale(' + labelScale + ')');
336
Steven Burrows1c5c8612016-10-05 13:45:13 -0500337 },
338 update: function () {
Steven Burrows86af4352016-11-16 18:19:12 -0600339 if (this.get('enhanced')) {
Steven Burrows1c5c8612016-10-05 13:45:13 -0500340 this.enhance();
341 }
Steven Burrowsb15a3942017-01-17 17:25:04 +0000342 },
Steven Burrowsaea509d2017-04-12 14:17:47 -0700343 setVisibility: function () {
344
345 if (this.get('type') !== 'UiEdgeLink') {
346 return;
347 }
348
349 var visible = ps.getPrefs('topo2_prefs')['hosts'];
350 this.el.style('visibility', visible ? 'visible' : 'hidden');
351 },
Steven Burrowsc515e602017-04-13 11:17:40 -0700352 displayMastership: function () {
353 this.set({ mastership: t2mss.mastership() !== null});
354 },
Steven Burrowsb15a3942017-01-17 17:25:04 +0000355 remove: function () {
356
357 var width = linkScale(widthRatio) / t2zs.scale();
358
359 this.el.transition()
360 .duration(300)
361 .attr('stroke', '#ff0000')
362 .style('stroke-width', width * 4)
363 .transition()
364 .delay(1000)
365 .style('opacity', 0);
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100366 }
367 });
Steven Burrows57e24e92016-08-04 18:38:24 +0100368
369 var LinkCollection = Collection.extend({
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100370 model: LinkModel
Steven Burrows57e24e92016-08-04 18:38:24 +0100371 });
372
373 return new LinkCollection(data);
374 }
375
Steven Burrows1c5c8612016-10-05 13:45:13 -0500376 function showPort() {
377 return t2vs.getPortHighlighting();
378 }
379
Steven Burrows57e24e92016-08-04 18:38:24 +0100380 angular.module('ovTopo2')
Steven Burrowsaf96a212016-12-28 12:57:02 +0000381 .factory('Topo2LinkService', [
382 '$log', 'Topo2Collection', 'Topo2Model',
Steven Burrows0616e802016-10-06 21:45:07 -0500383 'ThemeService', 'SvgUtilService', 'Topo2ZoomService',
Steven Burrowsaea509d2017-04-12 14:17:47 -0700384 'Topo2ViewService', 'Topo2LinkPanelService', 'FnService', 'PrefsService',
Steven Burrowsc515e602017-04-13 11:17:40 -0700385 'Topo2MastershipService',
Steven Burrowsaf96a212016-12-28 12:57:02 +0000386 function (_$log_, _c_, _Model_, _ts_, _sus_,
Steven Burrowsc515e602017-04-13 11:17:40 -0700387 _t2zs_, _t2vs_, _t2lps_, _fn_, _ps_, _t2mss_) {
Steven Burrows57e24e92016-08-04 18:38:24 +0100388
Steven Burrowsaf96a212016-12-28 12:57:02 +0000389 $log = _$log_;
390 ts = _ts_;
391 sus = _sus_;
392 t2zs = _t2zs_;
393 t2vs = _t2vs_;
394 Collection = _c_;
395 Model = _Model_;
396 t2lps = _t2lps_;
397 fn = _fn_;
Steven Burrowsaea509d2017-04-12 14:17:47 -0700398 ps = _ps_;
Steven Burrowsc515e602017-04-13 11:17:40 -0700399 t2mss = _t2mss_;
Steven Burrows57e24e92016-08-04 18:38:24 +0100400
Steven Burrowsaf96a212016-12-28 12:57:02 +0000401 return {
402 createLinkCollection: createLinkCollection
403 };
404 }
405 ]);
Steven Burrows57e24e92016-08-04 18:38:24 +0100406})();