blob: bcd315d5e47fca62a9b58f6a94cfe1a3df5185bd [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 srcPort: this.get('srcPort'),
63 tgtPort: this.get('dstPort'),
64 position: {
65 x1: 0,
66 y1: 0,
67 x2: 0,
68 y2: 0
69 }
70 // functions to aggregate dual link state
Steven Burrows9edc7e02016-08-29 11:52:07 +010071 // extra: link.extra
Steven Burrowsec1f45c2016-08-08 16:14:41 +010072 });
73
74 this.set(attrs);
75 }
76
Steven Burrows9edc7e02016-08-29 11:52:07 +010077 function rectAroundText(el) {
78 var text = el.select('text'),
79 box = text.node().getBBox();
80
81 // translate the bbox so that it is centered on [x,y]
82 box.x = -box.width / 2;
83 box.y = -box.height / 2;
84
85 // add padding
86 box.x -= 4;
87 box.width += 8;
88 return box;
89 }
90
Steven Burrowsdfa52b02016-09-02 13:50:43 +010091 function isLinkOnline(node) {
92 return (node.get('nodeType') === 'region') ? true : node.get('online');
93 }
94
Steven Burrowsec1f45c2016-08-08 16:14:41 +010095 function linkEndPoints(srcId, dstId) {
96
Steven Burrowsaf96a212016-12-28 12:57:02 +000097 var findNodeById = this.region.model.findNodeById.bind(this.region),
98 allNodes = this.region.model.nodes(),
99 sourceNode = findNodeById(this, srcId),
100 targetNode = findNodeById(this, dstId);
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100101
Steven Burrows6deb4ce2016-08-26 16:06:23 +0100102 if (!sourceNode || !targetNode) {
Steven Burrows8f45ce22016-10-27 20:04:14 -0500103 $log.error('Node(s) not on map for link:' + srcId + '~' + dstId);
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100104 // logicError('Node(s) not on map for link:\n' + sMiss + dMiss);
Steven Burrows6deb4ce2016-08-26 16:06:23 +0100105 return null;
106 }
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100107
Steven Burrowsa3fca812016-10-14 15:11:04 -0500108 this.source = allNodes.indexOf(sourceNode);
109 this.target = allNodes.indexOf(targetNode);
110 this.sourceNode = sourceNode;
111 this.targetNode = targetNode;
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100112
113 return {
114 source: sourceNode,
115 target: targetNode
116 };
117 }
118
119 function createLinkCollection(data, _region) {
120
121 var LinkModel = Model.extend({
122 region: _region,
123 createLink: createLink,
124 linkEndPoints: linkEndPoints,
125 type: function () {
126 return this.get('type');
127 },
Steven Burrows86af4352016-11-16 18:19:12 -0600128 svgClassName: function () {
129 return fn.classNames('link',
130 this.nodeType,
131 this.get('type'),
132 {
133 enhanced: this.get('enhanced'),
134 selected: this.get('selected')
135 }
136 );
137 },
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100138 expected: function () {
Steven Burrows9edc7e02016-08-29 11:52:07 +0100139 // TODO: original code is: (s && s.expected) && (t && t.expected);
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100140 return true;
141 },
142 online: function () {
Steven Burrows9edc7e02016-08-29 11:52:07 +0100143
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100144 var source = this.get('source'),
145 target = this.get('target'),
146 sourceOnline = isLinkOnline(source),
147 targetOnline = isLinkOnline(target);
148
149 return (sourceOnline) && (targetOnline);
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100150 },
Steven Burrows86af4352016-11-16 18:19:12 -0600151 onChange: function () {
152 // Update class names when the model changes
153 if (this.el) {
154 this.el.attr('class', this.svgClassName());
155 }
156 },
Steven Burrows9edc7e02016-08-29 11:52:07 +0100157 enhance: function () {
158 var data = [],
159 point;
160
Steven Burrows1c5c8612016-10-05 13:45:13 -0500161 if (showPort()) {
Steven Burrowsaf96a212016-12-28 12:57:02 +0000162 this.set('enhanced', true);
Steven Burrows1c5c8612016-10-05 13:45:13 -0500163 point = this.locatePortLabel();
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100164 angular.extend(point, {
Steven Burrows1c5c8612016-10-05 13:45:13 -0500165 id: 'topo-port-tgt',
166 num: this.get('portB')
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100167 });
168 data.push(point);
Steven Burrows1c5c8612016-10-05 13:45:13 -0500169
170 if (this.get('portA')) {
171 point = this.locatePortLabel(1);
172 angular.extend(point, {
173 id: 'topo-port-src',
174 num: this.get('portA')
175 });
176 data.push(point);
177 }
178
179 var entering = d3.select('#topo-portLabels')
180 .selectAll('.portLabel')
Steven Burrowsa3fca812016-10-14 15:11:04 -0500181 .data(data)
182 .enter().append('g')
Steven Burrows1c5c8612016-10-05 13:45:13 -0500183 .classed('portLabel', true)
Steven Burrowsaf96a212016-12-28 12:57:02 +0000184 .attr('id', function (d) { return d.id; });
Steven Burrows1c5c8612016-10-05 13:45:13 -0500185
186 entering.each(function (d) {
187 var el = d3.select(this),
188 rect = el.append('rect'),
189 text = el.append('text').text(d.num);
190
191 var rectSize = rectAroundText(el);
192
193 rect.attr(rectSize)
194 .attr('rx', 2)
195 .attr('ry', 2);
196
197 text.attr('dy', linkLabelOffset)
198 .attr('text-anchor', 'middle');
199
200 el.attr('transform', sus.translate(d.x, d.y));
Steven Burrowsa3fca812016-10-14 15:11:04 -0500201
Steven Burrows1c5c8612016-10-05 13:45:13 -0500202 });
Steven Burrowsa3fca812016-10-14 15:11:04 -0500203
204 this.setScale();
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100205 }
Steven Burrows9edc7e02016-08-29 11:52:07 +0100206 },
207 unenhance: function () {
Steven Burrows86af4352016-11-16 18:19:12 -0600208 this.set('enhanced', false);
Steven Burrows9edc7e02016-08-29 11:52:07 +0100209 d3.select('#topo-portLabels').selectAll('.portLabel').remove();
210 },
Steven Burrows1aa4f582016-12-13 15:05:41 -0500211 getSelected: function () {
212 return this.collection.filter(function (m) {
213 return m.get('selected');
214 });
215 },
Steven Burrows86af4352016-11-16 18:19:12 -0600216 select: function () {
Steven Burrows1aa4f582016-12-13 15:05:41 -0500217
Steven Burrows86af4352016-11-16 18:19:12 -0600218 // TODO: if single selection clear selected devices, hosts, sub-regions
219 var s = Boolean(this.get('selected'));
220 // Clear all selected Items
221 _.each(this.collection.models, function (m) {
222 m.set('selected', false);
223 });
224
225 this.set('selected', !s);
Steven Burrows1aa4f582016-12-13 15:05:41 -0500226 this.showDetails();
Steven Burrows86af4352016-11-16 18:19:12 -0600227
Steven Burrows1aa4f582016-12-13 15:05:41 -0500228 return this.getSelected();
229 },
230 deselect: function () {
231 this.set('selected', false);
232 this.set('enhanced', false);
Steven Burrows86af4352016-11-16 18:19:12 -0600233 },
234 showDetails: function () {
Steven Burrows1aa4f582016-12-13 15:05:41 -0500235 var selected = this.getSelected();
Steven Burrows86af4352016-11-16 18:19:12 -0600236
237 if (selected) {
238 t2lps.displayLink(this);
239 } else {
240 t2lps.hide();
241 }
242 },
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100243 locatePortLabel: function (src) {
Steven Burrowsa3fca812016-10-14 15:11:04 -0500244
245 var offset = 32 / (labelDim * t2zs.scale()),
246 sourceX = this.get('position').x1,
247 sourceY = this.get('position').y1,
248 targetX = this.get('position').x2,
249 targetY = this.get('position').y2,
250 nearX = src ? sourceX : targetX,
251 nearY = src ? sourceY : targetY,
252 farX = src ? targetX : sourceX,
253 farY = src ? targetY : sourceY;
Steven Burrows9edc7e02016-08-29 11:52:07 +0100254
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100255 function dist(x, y) {
256 return Math.sqrt(x * x + y * y);
257 }
Steven Burrows9edc7e02016-08-29 11:52:07 +0100258
259 var dx = farX - nearX,
260 dy = farY - nearY,
Steven Burrowsa3fca812016-10-14 15:11:04 -0500261 k = (32 * offset) / dist(dx, dy);
Steven Burrows9edc7e02016-08-29 11:52:07 +0100262
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100263 return { x: k * dx + nearX, y: k * dy + nearY };
Steven Burrows9edc7e02016-08-29 11:52:07 +0100264 },
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100265 restyleLinkElement: function (immediate) {
266 // this fn's job is to look at raw links and decide what svg classes
267 // need to be applied to the line element in the DOM
268 var th = ts.theme(),
269 el = this.el,
270 type = this.get('type'),
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100271 online = this.online(),
Steven Burrows583f4be2016-11-04 14:06:50 +0100272 modeCls = this.expected() ? '-inactive' : 'not-permitted',
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100273 delay = immediate ? 0 : 1000;
274
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100275 // NOTE: understand why el is sometimes undefined on addLink events...
276 // Investigated:
277 // el is undefined when it's a reverse link that is being added.
278 // updateLinks (which sets ldata.el) isn't called before this is called.
279 // Calling _updateLinks in addLinkUpdate fixes it, but there might be
280 // a more efficient way to fix it.
281 if (el && !el.empty()) {
282 el.classed('link', true);
283 el.classed(allLinkSubTypes, false);
284 el.classed(modeCls, !online);
285 el.classed(allLinkTypes, false);
286 if (type) {
287 el.classed(type, true);
288 }
289 el.transition()
290 .duration(delay)
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100291 .attr('stroke', linkConfig[th].baseColor);
Steven Burrowsa3fca812016-10-14 15:11:04 -0500292
293 this.setScale();
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100294 }
295 },
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100296 onEnter: function (el) {
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100297 var link = d3.select(el);
Steven Burrows9edc7e02016-08-29 11:52:07 +0100298
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100299 this.el = link;
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100300 this.restyleLinkElement();
301
302 if (this.get('type') === 'hostLink') {
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100303 // sus.visible(link, api.showHosts());
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100304 }
Steven Burrows0616e802016-10-06 21:45:07 -0500305 },
306 setScale: function () {
Steven Burrowsa3fca812016-10-14 15:11:04 -0500307 var width = linkScale(widthRatio) / t2zs.scale();
Steven Burrows0616e802016-10-06 21:45:07 -0500308 this.el.style('stroke-width', width + 'px');
Steven Burrowsa3fca812016-10-14 15:11:04 -0500309
310 var labelScale = labelDim / (labelDim * t2zs.scale());
311
312 d3.select('#topo-portLabels')
313 .selectAll('.portLabel')
314 .selectAll('*')
315 .style('transform', 'scale(' + labelScale + ')');
316
Steven Burrows1c5c8612016-10-05 13:45:13 -0500317 },
318 update: function () {
Steven Burrows86af4352016-11-16 18:19:12 -0600319 if (this.get('enhanced')) {
Steven Burrows1c5c8612016-10-05 13:45:13 -0500320 this.enhance();
321 }
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100322 }
323 });
Steven Burrows57e24e92016-08-04 18:38:24 +0100324
325 var LinkCollection = Collection.extend({
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100326 model: LinkModel
Steven Burrows57e24e92016-08-04 18:38:24 +0100327 });
328
329 return new LinkCollection(data);
330 }
331
Steven Burrows1c5c8612016-10-05 13:45:13 -0500332 function showPort() {
333 return t2vs.getPortHighlighting();
334 }
335
Steven Burrows57e24e92016-08-04 18:38:24 +0100336 angular.module('ovTopo2')
Steven Burrowsaf96a212016-12-28 12:57:02 +0000337 .factory('Topo2LinkService', [
338 '$log', 'Topo2Collection', 'Topo2Model',
Steven Burrows0616e802016-10-06 21:45:07 -0500339 'ThemeService', 'SvgUtilService', 'Topo2ZoomService',
Steven Burrows86af4352016-11-16 18:19:12 -0600340 'Topo2ViewService', 'Topo2LinkPanelService', 'FnService',
Steven Burrowsaf96a212016-12-28 12:57:02 +0000341 function (_$log_, _c_, _Model_, _ts_, _sus_,
342 _t2zs_, _t2vs_, _t2lps_, _fn_) {
Steven Burrows57e24e92016-08-04 18:38:24 +0100343
Steven Burrowsaf96a212016-12-28 12:57:02 +0000344 $log = _$log_;
345 ts = _ts_;
346 sus = _sus_;
347 t2zs = _t2zs_;
348 t2vs = _t2vs_;
349 Collection = _c_;
350 Model = _Model_;
351 t2lps = _t2lps_;
352 fn = _fn_;
Steven Burrows57e24e92016-08-04 18:38:24 +0100353
Steven Burrowsaf96a212016-12-28 12:57:02 +0000354 return {
355 createLinkCollection: createLinkCollection
356 };
357 }
358 ]);
Steven Burrows57e24e92016-08-04 18:38:24 +0100359})();