blob: d3f449cb6ae8fd021976c4230ef6995325438def [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 Burrows86af4352016-11-16 18:19:12 -060025 var $log, Collection, Model, ts, sus, t2zs, t2vs, t2lps, fn;
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 Burrows583f4be2016-11-04 14:06:50 +010034 allLinkTypes = 'direct optical tunnel UiDeviceLink',
35 allLinkSubTypes = 'not-permitted',
Steven Burrowsa3fca812016-10-14 15:11:04 -050036 labelDim = 30;
Steven Burrowsec1f45c2016-08-08 16:14:41 +010037
38 // configuration
39 var linkConfig = {
40 light: {
41 baseColor: '#939598',
42 inColor: '#66f',
43 outColor: '#f00'
44 },
45 dark: {
46 // TODO : theme
47 baseColor: '#939598',
48 inColor: '#66f',
49 outColor: '#f00'
50 },
51 inWidth: 12,
52 outWidth: 10
53 };
54
Steven Burrowsec1f45c2016-08-08 16:14:41 +010055 function createLink() {
56
57 var linkPoints = this.linkEndPoints(this.get('epA'), this.get('epB'));
Steven Burrowsec1f45c2016-08-08 16:14:41 +010058
59 var attrs = angular.extend({}, linkPoints, {
60 key: this.get('id'),
61 class: 'link',
Steven Burrowsec1f45c2016-08-08 16:14:41 +010062 position: {
63 x1: 0,
64 y1: 0,
65 x2: 0,
66 y2: 0
67 }
68 // functions to aggregate dual link state
Steven Burrows9edc7e02016-08-29 11:52:07 +010069 // extra: link.extra
Steven Burrowsec1f45c2016-08-08 16:14:41 +010070 });
71
72 this.set(attrs);
73 }
74
Steven Burrows9edc7e02016-08-29 11:52:07 +010075 function rectAroundText(el) {
76 var text = el.select('text'),
77 box = text.node().getBBox();
78
79 // translate the bbox so that it is centered on [x,y]
80 box.x = -box.width / 2;
81 box.y = -box.height / 2;
82
83 // add padding
84 box.x -= 4;
85 box.width += 8;
86 return box;
87 }
88
Steven Burrowsdfa52b02016-09-02 13:50:43 +010089 function isLinkOnline(node) {
90 return (node.get('nodeType') === 'region') ? true : node.get('online');
91 }
92
Steven Burrowsec1f45c2016-08-08 16:14:41 +010093 function linkEndPoints(srcId, dstId) {
94
Steven Burrowsaf96a212016-12-28 12:57:02 +000095 var findNodeById = this.region.model.findNodeById.bind(this.region),
96 allNodes = this.region.model.nodes(),
97 sourceNode = findNodeById(this, srcId),
98 targetNode = findNodeById(this, dstId);
Steven Burrowsec1f45c2016-08-08 16:14:41 +010099
Steven Burrows6deb4ce2016-08-26 16:06:23 +0100100 if (!sourceNode || !targetNode) {
Steven Burrows8f45ce22016-10-27 20:04:14 -0500101 $log.error('Node(s) not on map for link:' + srcId + '~' + dstId);
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100102 // logicError('Node(s) not on map for link:\n' + sMiss + dMiss);
Steven Burrows6deb4ce2016-08-26 16:06:23 +0100103 return null;
104 }
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100105
Steven Burrowsa3fca812016-10-14 15:11:04 -0500106 this.source = allNodes.indexOf(sourceNode);
107 this.target = allNodes.indexOf(targetNode);
108 this.sourceNode = sourceNode;
109 this.targetNode = targetNode;
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100110
111 return {
112 source: sourceNode,
113 target: targetNode
114 };
115 }
116
117 function createLinkCollection(data, _region) {
118
119 var LinkModel = Model.extend({
Steven Burrows68d6f952017-03-10 13:53:35 +0000120 initialize: function () {
121 this.super = this.constructor.__super__;
122 this.super.initialize.apply(this, arguments);
123 this.createLink();
124 },
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100125 region: _region,
126 createLink: createLink,
127 linkEndPoints: linkEndPoints,
128 type: function () {
129 return this.get('type');
130 },
Steven Burrows86af4352016-11-16 18:19:12 -0600131 svgClassName: function () {
132 return fn.classNames('link',
133 this.nodeType,
134 this.get('type'),
135 {
136 enhanced: this.get('enhanced'),
137 selected: this.get('selected')
138 }
139 );
140 },
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100141 expected: function () {
Steven Burrows9edc7e02016-08-29 11:52:07 +0100142 // TODO: original code is: (s && s.expected) && (t && t.expected);
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100143 return true;
144 },
145 online: function () {
Steven Burrows9edc7e02016-08-29 11:52:07 +0100146
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100147 var source = this.get('source'),
148 target = this.get('target'),
149 sourceOnline = isLinkOnline(source),
150 targetOnline = isLinkOnline(target);
151
152 return (sourceOnline) && (targetOnline);
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100153 },
Steven Burrows86af4352016-11-16 18:19:12 -0600154 onChange: function () {
155 // Update class names when the model changes
156 if (this.el) {
157 this.el.attr('class', this.svgClassName());
158 }
159 },
Steven Burrows9edc7e02016-08-29 11:52:07 +0100160 enhance: function () {
161 var data = [],
162 point;
163
Steven Burrows1c5c8612016-10-05 13:45:13 -0500164 if (showPort()) {
Steven Burrowsaf96a212016-12-28 12:57:02 +0000165 this.set('enhanced', true);
Steven Burrows1c5c8612016-10-05 13:45:13 -0500166 point = this.locatePortLabel();
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100167 angular.extend(point, {
Simon Hunt95f4b422017-03-03 13:49:05 -0800168 id: 'topo2-port-tgt',
Steven Burrows1c5c8612016-10-05 13:45:13 -0500169 num: this.get('portB')
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100170 });
171 data.push(point);
Steven Burrows1c5c8612016-10-05 13:45:13 -0500172
173 if (this.get('portA')) {
174 point = this.locatePortLabel(1);
175 angular.extend(point, {
Simon Hunt95f4b422017-03-03 13:49:05 -0800176 id: 'topo2-port-src',
Steven Burrows1c5c8612016-10-05 13:45:13 -0500177 num: this.get('portA')
178 });
179 data.push(point);
180 }
181
Steven Burrowsbd402842017-03-08 21:30:38 +0000182 var entering = d3.select('.topo2-portLabels')
Steven Burrows1c5c8612016-10-05 13:45:13 -0500183 .selectAll('.portLabel')
Steven Burrowsa3fca812016-10-14 15:11:04 -0500184 .data(data)
185 .enter().append('g')
Steven Burrows1c5c8612016-10-05 13:45:13 -0500186 .classed('portLabel', true)
Steven Burrowsaf96a212016-12-28 12:57:02 +0000187 .attr('id', function (d) { return d.id; });
Steven Burrows1c5c8612016-10-05 13:45:13 -0500188
189 entering.each(function (d) {
190 var el = d3.select(this),
191 rect = el.append('rect'),
192 text = el.append('text').text(d.num);
193
194 var rectSize = rectAroundText(el);
195
196 rect.attr(rectSize)
197 .attr('rx', 2)
198 .attr('ry', 2);
199
200 text.attr('dy', linkLabelOffset)
201 .attr('text-anchor', 'middle');
202
203 el.attr('transform', sus.translate(d.x, d.y));
Steven Burrowsa3fca812016-10-14 15:11:04 -0500204
Steven Burrows1c5c8612016-10-05 13:45:13 -0500205 });
Steven Burrowsa3fca812016-10-14 15:11:04 -0500206
207 this.setScale();
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100208 }
Steven Burrows9edc7e02016-08-29 11:52:07 +0100209 },
210 unenhance: function () {
Steven Burrows86af4352016-11-16 18:19:12 -0600211 this.set('enhanced', false);
Steven Burrowsbd402842017-03-08 21:30:38 +0000212 d3.select('.topo2-portLabels').selectAll('.portLabel').remove();
Steven Burrows9edc7e02016-08-29 11:52:07 +0100213 },
Steven Burrowsa31c5b02017-04-12 10:45:08 -0700214 setPosition: function () {
215 this.set({
216 position: {
217 x1: this.get('source').x,
218 y1: this.get('source').y,
219 x2: this.get('target').x,
220 y2: this.get('target').y
221 }
222 });
223
224 if (this.get('enhanced')) {
225 this.updatePortPosition();
226 }
227 },
228 updatePortPosition: function () {
229 var sourcePos = this.locatePortLabel(1),
230 targetPos = this.locatePortLabel();
231 d3.select('#topo2-port-src')
232 .attr('transform', sus.translate(sourcePos.x, sourcePos.y));
233 d3.select('#topo2-port-tgt')
234 .attr('transform', sus.translate(targetPos.x, targetPos.y));
235 },
Steven Burrows1aa4f582016-12-13 15:05:41 -0500236 getSelected: function () {
237 return this.collection.filter(function (m) {
238 return m.get('selected');
239 });
240 },
Steven Burrows86af4352016-11-16 18:19:12 -0600241 select: function () {
Steven Burrows5fa057e2017-03-15 17:07:56 +0000242 this.set({ 'selected': true });
Steven Burrows1aa4f582016-12-13 15:05:41 -0500243 return this.getSelected();
244 },
245 deselect: function () {
Steven Burrows5fa057e2017-03-15 17:07:56 +0000246 this.set({
247 'selected': false,
248 'enhanced': false
249 });
Steven Burrows86af4352016-11-16 18:19:12 -0600250 },
251 showDetails: function () {
Steven Burrows1aa4f582016-12-13 15:05:41 -0500252 var selected = this.getSelected();
Steven Burrows86af4352016-11-16 18:19:12 -0600253
254 if (selected) {
255 t2lps.displayLink(this);
256 } else {
257 t2lps.hide();
258 }
259 },
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100260 locatePortLabel: function (src) {
Steven Burrowsa3fca812016-10-14 15:11:04 -0500261
262 var offset = 32 / (labelDim * t2zs.scale()),
263 sourceX = this.get('position').x1,
264 sourceY = this.get('position').y1,
265 targetX = this.get('position').x2,
266 targetY = this.get('position').y2,
267 nearX = src ? sourceX : targetX,
268 nearY = src ? sourceY : targetY,
269 farX = src ? targetX : sourceX,
270 farY = src ? targetY : sourceY;
Steven Burrows9edc7e02016-08-29 11:52:07 +0100271
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100272 function dist(x, y) {
273 return Math.sqrt(x * x + y * y);
274 }
Steven Burrows9edc7e02016-08-29 11:52:07 +0100275
276 var dx = farX - nearX,
277 dy = farY - nearY,
Steven Burrowsa3fca812016-10-14 15:11:04 -0500278 k = (32 * offset) / dist(dx, dy);
Steven Burrows9edc7e02016-08-29 11:52:07 +0100279
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100280 return { x: k * dx + nearX, y: k * dy + nearY };
Steven Burrows9edc7e02016-08-29 11:52:07 +0100281 },
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100282 restyleLinkElement: function (immediate) {
283 // this fn's job is to look at raw links and decide what svg classes
284 // need to be applied to the line element in the DOM
285 var th = ts.theme(),
286 el = this.el,
287 type = this.get('type'),
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100288 online = this.online(),
Steven Burrows583f4be2016-11-04 14:06:50 +0100289 modeCls = this.expected() ? '-inactive' : 'not-permitted',
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100290 delay = immediate ? 0 : 1000;
291
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100292 // NOTE: understand why el is sometimes undefined on addLink events...
293 // Investigated:
294 // el is undefined when it's a reverse link that is being added.
295 // updateLinks (which sets ldata.el) isn't called before this is called.
296 // Calling _updateLinks in addLinkUpdate fixes it, but there might be
297 // a more efficient way to fix it.
298 if (el && !el.empty()) {
299 el.classed('link', true);
300 el.classed(allLinkSubTypes, false);
301 el.classed(modeCls, !online);
302 el.classed(allLinkTypes, false);
303 if (type) {
304 el.classed(type, true);
305 }
306 el.transition()
307 .duration(delay)
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100308 .attr('stroke', linkConfig[th].baseColor);
Steven Burrowsa3fca812016-10-14 15:11:04 -0500309
310 this.setScale();
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100311 }
312 },
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100313 onEnter: function (el) {
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100314 var link = d3.select(el);
Steven Burrows9edc7e02016-08-29 11:52:07 +0100315
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100316 this.el = link;
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100317 this.restyleLinkElement();
318
319 if (this.get('type') === 'hostLink') {
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100320 // sus.visible(link, api.showHosts());
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100321 }
Steven Burrows0616e802016-10-06 21:45:07 -0500322 },
323 setScale: function () {
Steven Burrowse8a455a2017-03-16 16:58:59 +0000324
325 if (!this.el) return;
326
Steven Burrowsa3fca812016-10-14 15:11:04 -0500327 var width = linkScale(widthRatio) / t2zs.scale();
Steven Burrows0616e802016-10-06 21:45:07 -0500328 this.el.style('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 },
343 remove: function () {
344
345 var width = linkScale(widthRatio) / t2zs.scale();
346
347 this.el.transition()
348 .duration(300)
349 .attr('stroke', '#ff0000')
350 .style('stroke-width', width * 4)
351 .transition()
352 .delay(1000)
353 .style('opacity', 0);
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100354 }
355 });
Steven Burrows57e24e92016-08-04 18:38:24 +0100356
357 var LinkCollection = Collection.extend({
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100358 model: LinkModel
Steven Burrows57e24e92016-08-04 18:38:24 +0100359 });
360
361 return new LinkCollection(data);
362 }
363
Steven Burrows1c5c8612016-10-05 13:45:13 -0500364 function showPort() {
365 return t2vs.getPortHighlighting();
366 }
367
Steven Burrows57e24e92016-08-04 18:38:24 +0100368 angular.module('ovTopo2')
Steven Burrowsaf96a212016-12-28 12:57:02 +0000369 .factory('Topo2LinkService', [
370 '$log', 'Topo2Collection', 'Topo2Model',
Steven Burrows0616e802016-10-06 21:45:07 -0500371 'ThemeService', 'SvgUtilService', 'Topo2ZoomService',
Steven Burrows86af4352016-11-16 18:19:12 -0600372 'Topo2ViewService', 'Topo2LinkPanelService', 'FnService',
Steven Burrowsaf96a212016-12-28 12:57:02 +0000373 function (_$log_, _c_, _Model_, _ts_, _sus_,
374 _t2zs_, _t2vs_, _t2lps_, _fn_) {
Steven Burrows57e24e92016-08-04 18:38:24 +0100375
Steven Burrowsaf96a212016-12-28 12:57:02 +0000376 $log = _$log_;
377 ts = _ts_;
378 sus = _sus_;
379 t2zs = _t2zs_;
380 t2vs = _t2vs_;
381 Collection = _c_;
382 Model = _Model_;
383 t2lps = _t2lps_;
384 fn = _fn_;
Steven Burrows57e24e92016-08-04 18:38:24 +0100385
Steven Burrowsaf96a212016-12-28 12:57:02 +0000386 return {
387 createLinkCollection: createLinkCollection
388 };
389 }
390 ]);
Steven Burrows57e24e92016-08-04 18:38:24 +0100391})();