blob: 41c8e1e30d1d371b06b3c227dedaac527a5c9678 [file] [log] [blame]
Simon Hunt72e44bf2015-07-21 21:34:20 -07001/*
2 * Copyright 2015 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/*
19 ONOS GUI -- Topology Overlay Module.
20
21 Provides overlay capabilities, allowing ONOS apps to provide additional
22 custom data/behavior for the topology view.
23
24 */
25
26(function () {
27 'use strict';
28
29 // constants
30 var tos = 'TopoOverlayService: ';
31
32 // injected refs
Simon Hunt8d22c4b2015-08-06 16:24:43 -070033 var $log, fs, gs, wss, ns, tss, tps, api;
Simon Hunt72e44bf2015-07-21 21:34:20 -070034
35 // internal state
Simon Hunte05cae42015-07-23 17:35:24 -070036 var overlays = {},
37 current = null;
Simon Hunt72e44bf2015-07-21 21:34:20 -070038
39 function error(fn, msg) {
40 $log.error(tos + fn + '(): ' + msg);
41 }
42
43 function warn(fn, msg) {
44 $log.warn(tos + fn + '(): ' + msg);
45 }
46
Simon Huntfb940112015-07-29 18:36:35 -070047 function mkGlyphId(oid, gid) {
48 return (gid[0] === '*') ? oid + '-' + gid.slice(1) : gid;
49 }
Simon Hunt72e44bf2015-07-21 21:34:20 -070050
Simon Huntfb940112015-07-29 18:36:35 -070051 function handleGlyphs(o) {
52 var gdata = fs.isO(o.glyphs),
53 oid = o.overlayId,
54 gid = o.glyphId || 'unknown',
55 data = {},
56 note = [];
57
58 o._glyphId = mkGlyphId(oid, gid);
59
60 o.mkGid = function (g) {
61 return mkGlyphId(oid, g);
62 };
63 o.mkId = function (s) {
64 return oid + '-' + s;
65 };
66
67 // process glyphs if defined
68 if (gdata) {
69 angular.forEach(gdata, function (value, key) {
70 var fullkey = oid + '-' + key;
71 data['_' + fullkey] = value.vb;
72 data[fullkey] = value.d;
73 note.push('*' + key);
74 });
75 gs.registerGlyphs(data);
76 $log.debug('registered overlay glyphs:', oid, note);
Simon Hunt72e44bf2015-07-21 21:34:20 -070077 }
78 }
79
80 function register(overlay) {
81 var r = 'register',
82 over = fs.isO(overlay),
Simon Hunt8d22c4b2015-08-06 16:24:43 -070083 kb = over ? fs.isO(overlay.keyBindings) : null,
Simon Hunt72e44bf2015-07-21 21:34:20 -070084 id = over ? over.overlayId : '';
85
86 if (!id) {
87 return error(r, 'not a recognized overlay');
88 }
89 if (overlays[id]) {
90 return warn(r, 'already registered: "' + id + '"');
91 }
92 overlays[id] = overlay;
Simon Huntfb940112015-07-29 18:36:35 -070093 handleGlyphs(overlay);
Simon Hunt8d22c4b2015-08-06 16:24:43 -070094
95 if (kb) {
96 if (!fs.isA(kb._keyOrder)) {
97 warn(r, 'no _keyOrder array defined on keyBindings');
98 } else {
99 kb._keyOrder.forEach(function (k) {
100 if (k !== '-' && !kb[k]) {
101 warn(r, 'no "' + k + '" property defined on keyBindings');
102 }
103 });
104 }
105 }
106
Simon Hunt72e44bf2015-07-21 21:34:20 -0700107 $log.debug(tos + 'registered overlay: ' + id, overlay);
108 }
109
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700110 // TODO: remove this redundant code.......
Simon Hunt72e44bf2015-07-21 21:34:20 -0700111 // NOTE: unregister needs to be called if an app is ever
112 // deactivated/uninstalled via the applications view
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700113/*
Simon Hunt72e44bf2015-07-21 21:34:20 -0700114 function unregister(overlay) {
115 var u = 'unregister',
116 over = fs.isO(overlay),
117 id = over ? over.overlayId : '';
118
119 if (!id) {
120 return error(u, 'not a recognized overlay');
121 }
122 if (!overlays[id]) {
123 return warn(u, 'not registered: "' + id + "'")
124 }
125 delete overlays[id];
126 $log.debug(tos + 'unregistered overlay: ' + id);
Simon Hunt72e44bf2015-07-21 21:34:20 -0700127 }
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700128*/
Simon Hunt72e44bf2015-07-21 21:34:20 -0700129
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700130
131 // returns the list of overlay identifiers
Simon Hunt72e44bf2015-07-21 21:34:20 -0700132 function list() {
133 return d3.map(overlays).keys();
134 }
135
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700136 // add a radio button for each registered overlay
137 function augmentRbset(rset, switchFn) {
138 angular.forEach(overlays, function (ov) {
139 rset.push({
140 gid: ov._glyphId,
141 tooltip: (ov.tooltip || '(no tooltip)'),
142 cb: function () {
143 tbSelection(ov.overlayId, switchFn);
144 }
145 });
146 });
Simon Hunt72e44bf2015-07-21 21:34:20 -0700147 }
148
Simon Hunte05cae42015-07-23 17:35:24 -0700149 // an overlay was selected via toolbar radio button press from user
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700150 function tbSelection(id, switchFn) {
Simon Hunte05cae42015-07-23 17:35:24 -0700151 var same = current && current.overlayId === id,
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700152 payload = {},
153 actions;
Simon Hunte05cae42015-07-23 17:35:24 -0700154
155 function doop(op) {
156 var oid = current.overlayId;
157 $log.debug('Overlay:', op, oid);
158 current[op]();
159 payload[op] = oid;
160 }
161
162 if (!same) {
163 current && doop('deactivate');
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700164 current = overlays[id];
Simon Hunte05cae42015-07-23 17:35:24 -0700165 current && doop('activate');
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700166 actions = current && fs.isO(current.keyBindings);
167 switchFn(id, actions);
168
Simon Hunte05cae42015-07-23 17:35:24 -0700169 wss.sendEvent('topoSelectOverlay', payload);
Simon Hunt0af1ec32015-07-24 12:17:55 -0700170
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700171 // Ensure summary and details panels are updated immediately..
Simon Hunt0af1ec32015-07-24 12:17:55 -0700172 wss.sendEvent('requestSummary');
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700173 tss.updateDetail();
Simon Hunte05cae42015-07-23 17:35:24 -0700174 }
175 }
176
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700177 var coreButtons = {
178 showDeviceView: {
179 gid: 'switch',
180 tt: 'Show Device View',
181 path: 'device'
182 },
183 showFlowView: {
184 gid: 'flowTable',
185 tt: 'Show Flow View for this Device',
186 path: 'flow'
187 },
188 showPortView: {
189 gid: 'portTable',
190 tt: 'Show Port View for this Device',
191 path: 'port'
192 },
193 showGroupView: {
194 gid: 'groupTable',
195 tt: 'Show Group View for this Device',
196 path: 'group'
197 }
Simon Hunt3a0598f2015-08-04 19:59:04 -0700198 };
Simon Huntfb940112015-07-29 18:36:35 -0700199
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700200 // retrieves a button definition from the current overlay and generates
201 // a button descriptor to be added to the panel, with the data baked in
202 function _getButtonDef(id, data) {
203 var btns = current && current.buttons,
204 b = btns && btns[id],
205 cb = fs.isF(b.cb),
206 f = cb ? function () { cb(data); } : function () {};
207
208 return b ? {
209 id: current.mkId(id),
210 gid: current.mkGid(b.gid),
211 tt: b.tt,
212 cb: f
213 } : null;
214 }
215
Simon Hunt3a0598f2015-08-04 19:59:04 -0700216 // install core buttons, and include any additional from the current overlay
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700217 function installButtons(buttons, data, devId) {
218 buttons.forEach(function (id) {
219 var btn = coreButtons[id],
220 gid = btn && btn.gid,
221 tt = btn && btn.tt,
222 path = btn && btn.path;
Simon Hunt3a0598f2015-08-04 19:59:04 -0700223
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700224 if (btn) {
225 tps.addAction({
226 id: 'core-' + id,
227 gid: gid,
228 tt: tt,
229 cb: function () { ns.navTo(path, {devId: devId }); }
230 });
231 } else if (btn = _getButtonDef(id, data)) {
232 tps.addAction(btn);
Simon Hunt3a0598f2015-08-04 19:59:04 -0700233 }
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700234 });
235 }
Simon Hunt3a0598f2015-08-04 19:59:04 -0700236
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700237 function addDetailButton(id) {
238 var b = _getButtonDef(id);
239 if (b) {
240 tps.addAction({
241 id: current.mkId(id),
242 gid: current.mkGid(b.gid),
243 cb: b.cb,
244 tt: b.tt
245 });
246 }
247 }
Simon Hunt3a0598f2015-08-04 19:59:04 -0700248
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700249
250 // === -----------------------------------------------------
251 // Hooks for overlays
252
253 function _hook(x) {
254 var h = current && current.hooks;
255 return h && fs.isF(h[x]);
256 }
257
258 function escapeHook() {
259 var eh = _hook('escape');
260 return eh ? eh() : false;
261 }
262
263 function emptySelectHook() {
264 var cb = _hook('empty');
265 cb && cb();
266 }
267
268 function singleSelectHook(data) {
269 var cb = _hook('single');
270 cb && cb(data);
271 }
272
273 function multiSelectHook(selectOrder) {
274 var cb = _hook('multi');
275 cb && cb(selectOrder);
276 }
277
278 // === -----------------------------------------------------
279 // Event (from server) Handlers
280
281 function setApi(_api_, _tss_) {
282 api = _api_;
283 tss = _tss_;
284 }
285
286 // TODO: refactor this (currently using showTraffic data structure)
287 function showHighlights(data) {
288 /*
289 API to topoForce
290 clearLinkTrafficStyle()
291 removeLinkLabels()
292 updateLinks()
293 findLinkById( id )
294 */
295
296 var paths = data.paths;
297
298 api.clearLinkTrafficStyle();
299 api.removeLinkLabels();
300
301 // Now highlight all links in the paths payload, and attach
302 // labels to them, if they are defined.
303 paths.forEach(function (p) {
304 var n = p.links.length,
305 i, ldata, lab, units, magnitude, portcls;
306
307 for (i=0; i<n; i++) {
308 ldata = api.findLinkById(p.links[i]);
309 lab = p.labels[i];
310
311 if (ldata && !ldata.el.empty()) {
312 ldata.el.classed(p.class, true);
313 ldata.label = lab;
314
315 if (fs.endsWith(lab, 'bps')) {
316 // inject additional styling for port-based traffic
317 units = lab.substring(lab.length-4);
318 portcls = 'port-traffic-' + units;
319
320 // for GBps
321 if (units.substring(0,1) === 'G') {
322 magnitude = fs.parseBitRate(lab);
323 if (magnitude >= 9) {
324 portcls += '-choked'
325 }
326 }
327 ldata.el.classed(portcls, true);
328 }
329 }
330 }
Simon Hunt3a0598f2015-08-04 19:59:04 -0700331 });
Simon Huntfb940112015-07-29 18:36:35 -0700332
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700333 api.updateLinks();
Simon Huntfb940112015-07-29 18:36:35 -0700334 }
335
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700336 // ========================================================================
337
Simon Hunt72e44bf2015-07-21 21:34:20 -0700338 angular.module('ovTopo')
339 .factory('TopoOverlayService',
Simon Hunt3a0598f2015-08-04 19:59:04 -0700340 ['$log', 'FnService', 'GlyphService', 'WebSocketService', 'NavService',
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700341 'TopoPanelService',
Simon Hunt72e44bf2015-07-21 21:34:20 -0700342
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700343 function (_$log_, _fs_, _gs_, _wss_, _ns_, _tps_) {
Simon Hunt72e44bf2015-07-21 21:34:20 -0700344 $log = _$log_;
345 fs = _fs_;
346 gs = _gs_;
Simon Hunte05cae42015-07-23 17:35:24 -0700347 wss = _wss_;
Simon Hunt3a0598f2015-08-04 19:59:04 -0700348 ns = _ns_;
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700349 tps = _tps_;
Simon Hunt72e44bf2015-07-21 21:34:20 -0700350
351 return {
352 register: register,
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700353 //unregister: unregister,
354 setApi: setApi,
Simon Hunt72e44bf2015-07-21 21:34:20 -0700355 list: list,
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700356 augmentRbset: augmentRbset,
357 mkGlyphId: mkGlyphId,
Simon Huntfb940112015-07-29 18:36:35 -0700358 tbSelection: tbSelection,
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700359 installButtons: installButtons,
360 addDetailButton: addDetailButton,
361 hooks: {
362 escape: escapeHook,
363 emptySelect: emptySelectHook,
364 singleSelect: singleSelectHook,
365 multiSelect: multiSelectHook
366 },
367
368 showHighlights: showHighlights
Simon Hunt72e44bf2015-07-21 21:34:20 -0700369 }
370 }]);
371
372}());