blob: 74fe8a20ef403edc25ca7a55260d3b62434c043c [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 Burrows6deb4ce2016-08-26 16:06:23 +010025 var $log;
Steven Burrowsdfa52b02016-09-02 13:50:43 +010026 var Collection, Model, ts, sus;
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),
35 allLinkTypes = 'direct indirect optical tunnel UiDeviceLink',
36 allLinkSubTypes = 'inactive not-permitted';
37
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',
62 weight: 1,
63 srcPort: this.get('srcPort'),
64 tgtPort: this.get('dstPort'),
65 position: {
66 x1: 0,
67 y1: 0,
68 x2: 0,
69 y2: 0
70 }
71 // functions to aggregate dual link state
Steven Burrows9edc7e02016-08-29 11:52:07 +010072 // extra: link.extra
Steven Burrowsec1f45c2016-08-08 16:14:41 +010073 });
74
75 this.set(attrs);
76 }
77
Steven Burrows9edc7e02016-08-29 11:52:07 +010078 function rectAroundText(el) {
79 var text = el.select('text'),
80 box = text.node().getBBox();
81
82 // translate the bbox so that it is centered on [x,y]
83 box.x = -box.width / 2;
84 box.y = -box.height / 2;
85
86 // add padding
87 box.x -= 4;
88 box.width += 8;
89 return box;
90 }
91
Steven Burrowsdfa52b02016-09-02 13:50:43 +010092 function isLinkOnline(node) {
93 return (node.get('nodeType') === 'region') ? true : node.get('online');
94 }
95
Steven Burrowsec1f45c2016-08-08 16:14:41 +010096 function linkEndPoints(srcId, dstId) {
97
Steven Burrowsdfa52b02016-09-02 13:50:43 +010098 var sourceNode = this.region.findNodeById(srcId);
99 var targetNode = this.region.findNodeById(dstId);
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100100
Steven Burrows6deb4ce2016-08-26 16:06:23 +0100101 if (!sourceNode || !targetNode) {
102 $log.error('Node(s) not on map for link:' + srcId + ':' + dstId);
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100103 // logicError('Node(s) not on map for link:\n' + sMiss + dMiss);
Steven Burrows6deb4ce2016-08-26 16:06:23 +0100104 return null;
105 }
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100106
107 this.source = sourceNode.toJSON();
108 this.target = targetNode.toJSON();
109
110 return {
111 source: sourceNode,
112 target: targetNode
113 };
114 }
115
116 function createLinkCollection(data, _region) {
117
118 var LinkModel = Model.extend({
119 region: _region,
120 createLink: createLink,
121 linkEndPoints: linkEndPoints,
122 type: function () {
123 return this.get('type');
124 },
125 expected: function () {
Steven Burrows9edc7e02016-08-29 11:52:07 +0100126 // TODO: original code is: (s && s.expected) && (t && t.expected);
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100127 return true;
128 },
129 online: function () {
Steven Burrows9edc7e02016-08-29 11:52:07 +0100130
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100131 var source = this.get('source'),
132 target = this.get('target'),
133 sourceOnline = isLinkOnline(source),
134 targetOnline = isLinkOnline(target);
135
136 return (sourceOnline) && (targetOnline);
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100137 },
Steven Burrows9edc7e02016-08-29 11:52:07 +0100138 enhance: function () {
139 var data = [],
140 point;
141
142 angular.forEach(this.collection.models, function (link) {
143 link.unenhance();
144 });
145
146 this.el.classed('enhanced', true);
147 point = this.locatePortLabel();
148 angular.extend(point, {
149 id: 'topo-port-tgt',
150 num: this.get('portB')
151 });
152 data.push(point);
153
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100154 if (this.get('portA')) {
155 point = this.locatePortLabel(1);
156 angular.extend(point, {
157 id: 'topo-port-src',
158 num: this.get('portA')
159 });
160 data.push(point);
161 }
162
163 var entering = d3.select('#topo-portLabels')
164 .selectAll('.portLabel')
Steven Burrows9edc7e02016-08-29 11:52:07 +0100165 .data(data).enter().append('g')
166 .classed('portLabel', true)
167 .attr('id', function (d) { return d.id; });
168
169 entering.each(function (d) {
170 var el = d3.select(this),
171 rect = el.append('rect'),
172 text = el.append('text').text(d.num);
173
174 rect.attr(rectAroundText(el))
175 .attr('rx', 2)
176 .attr('ry', 2);
177
178 text.attr('dy', linkLabelOffset)
179 .attr('text-anchor', 'middle');
180
181 el.attr('transform', sus.translate(d.x, d.y));
182 });
183 },
184 unenhance: function () {
185 this.el.classed('enhanced', false);
186 d3.select('#topo-portLabels').selectAll('.portLabel').remove();
187 },
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100188 locatePortLabel: function (src) {
Steven Burrows9edc7e02016-08-29 11:52:07 +0100189 var offset = 32,
190 pos = this.get('position'),
191 nearX = src ? pos.x1 : pos.x2,
192 nearY = src ? pos.y1 : pos.y2,
193 farX = src ? pos.x2 : pos.x1,
194 farY = src ? pos.y2 : pos.y1;
195
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100196 function dist(x, y) {
197 return Math.sqrt(x * x + y * y);
198 }
Steven Burrows9edc7e02016-08-29 11:52:07 +0100199
200 var dx = farX - nearX,
201 dy = farY - nearY,
202 k = offset / dist(dx, dy);
203
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100204 return { x: k * dx + nearX, y: k * dy + nearY };
Steven Burrows9edc7e02016-08-29 11:52:07 +0100205 },
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100206 restyleLinkElement: function (immediate) {
207 // this fn's job is to look at raw links and decide what svg classes
208 // need to be applied to the line element in the DOM
209 var th = ts.theme(),
210 el = this.el,
211 type = this.get('type'),
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100212 online = this.online(),
213 modeCls = this.expected() ? 'inactive' : 'not-permitted',
Steven Burrows6deb4ce2016-08-26 16:06:23 +0100214 lw = 1.2,
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100215 delay = immediate ? 0 : 1000;
216
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100217 // NOTE: understand why el is sometimes undefined on addLink events...
218 // Investigated:
219 // el is undefined when it's a reverse link that is being added.
220 // updateLinks (which sets ldata.el) isn't called before this is called.
221 // Calling _updateLinks in addLinkUpdate fixes it, but there might be
222 // a more efficient way to fix it.
223 if (el && !el.empty()) {
224 el.classed('link', true);
225 el.classed(allLinkSubTypes, false);
226 el.classed(modeCls, !online);
227 el.classed(allLinkTypes, false);
228 if (type) {
229 el.classed(type, true);
230 }
231 el.transition()
232 .duration(delay)
233 .attr('stroke-width', linkScale(lw))
234 .attr('stroke', linkConfig[th].baseColor);
235 }
236 },
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100237 onEnter: function (el) {
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100238 var link = d3.select(el);
Steven Burrows9edc7e02016-08-29 11:52:07 +0100239
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100240 this.el = link;
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100241 this.restyleLinkElement();
242
243 if (this.get('type') === 'hostLink') {
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100244 // sus.visible(link, api.showHosts());
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100245 }
246 }
247 });
Steven Burrows57e24e92016-08-04 18:38:24 +0100248
249 var LinkCollection = Collection.extend({
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100250 model: LinkModel
Steven Burrows57e24e92016-08-04 18:38:24 +0100251 });
252
253 return new LinkCollection(data);
254 }
255
256 angular.module('ovTopo2')
257 .factory('Topo2LinkService',
Steven Burrowsdfa52b02016-09-02 13:50:43 +0100258 ['$log', 'Topo2Collection', 'Topo2Model',
259 'ThemeService', 'SvgUtilService',
Steven Burrows57e24e92016-08-04 18:38:24 +0100260
Steven Burrows9edc7e02016-08-29 11:52:07 +0100261 function (_$log_, _Collection_, _Model_, _ts_, _sus_) {
Steven Burrows57e24e92016-08-04 18:38:24 +0100262
Steven Burrows6deb4ce2016-08-26 16:06:23 +0100263 $log = _$log_;
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100264 ts = _ts_;
Steven Burrows9edc7e02016-08-29 11:52:07 +0100265 sus = _sus_;
Steven Burrows57e24e92016-08-04 18:38:24 +0100266 Collection = _Collection_;
Steven Burrowsec1f45c2016-08-08 16:14:41 +0100267 Model = _Model_;
Steven Burrows57e24e92016-08-04 18:38:24 +0100268
269 return {
270 createLinkCollection: createLinkCollection
271 };
272 }
273 ]);
274
275})();