blob: c02ef51d266f42e240a7d084b39400524ecfcc3d [file] [log] [blame]
Simon Hunt08f841d02015-02-10 14:39:20 -08001/*
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 ONOS GUI -- Topology Selection Module.
19 Defines behavior when selecting nodes.
20 */
21
22(function () {
23 'use strict';
24
25 // injected refs
Simon Huntfb940112015-07-29 18:36:35 -070026 var $log, fs, wss, tov, tps, tts, ns;
Simon Hunt08f841d02015-02-10 14:39:20 -080027
28 // api to topoForce
29 var api;
30 /*
31 node() // get ref to D3 selection of nodes
32 zoomingOrPanning( ev )
33 updateDeviceColors( [dev] )
Simon Hunt0c6b2d32015-03-26 17:46:29 -070034 deselectLink()
Simon Hunt08f841d02015-02-10 14:39:20 -080035 */
36
37 // internal state
38 var hovered, // the node over which the mouse is hovering
39 selections = {}, // currently selected nodes (by id)
40 selectOrder = [], // the order in which we made selections
Simon Hunt0c6b2d32015-03-26 17:46:29 -070041 consumeClick = false; // used to coordinate with SVG click handler
Simon Hunt08f841d02015-02-10 14:39:20 -080042
Bri Prebilic Cole8f07f0d2015-04-23 13:28:43 -070043 // constants
Bri Prebilic Cole17c6d0a2015-07-16 14:56:40 -070044 var devPath = 'device',
45 flowPath = 'flow',
Bri Prebilic Cole5c60d6e2015-05-12 09:11:03 -070046 portPath ='port',
47 groupPath = 'group';
Bri Prebilic Cole8f07f0d2015-04-23 13:28:43 -070048
Simon Hunt08f841d02015-02-10 14:39:20 -080049 // ==========================
50
51 function nSel() {
52 return selectOrder.length;
53 }
54 function getSel(idx) {
55 return selections[selectOrder[idx]];
56 }
57 function allSelectionsClass(cls) {
58 for (var i=0, n=nSel(); i<n; i++) {
59 if (getSel(i).obj.class !== cls) {
60 return false;
61 }
62 }
63 return true;
64 }
65
66 // ==========================
67
68 function nodeMouseOver(m) {
69 if (!m.dragStarted) {
Simon Hunt36a58c62015-04-08 11:00:07 -070070 //$log.debug("MouseOver()...", m);
Simon Hunt08f841d02015-02-10 14:39:20 -080071 if (hovered != m) {
72 hovered = m;
Simon Huntf542d842015-02-11 16:20:33 -080073 tts.requestTrafficForMode();
Simon Hunt08f841d02015-02-10 14:39:20 -080074 }
75 }
76 }
77
78 function nodeMouseOut(m) {
79 if (!m.dragStarted) {
80 if (hovered) {
81 hovered = null;
Simon Huntf542d842015-02-11 16:20:33 -080082 tts.requestTrafficForMode();
Simon Hunt08f841d02015-02-10 14:39:20 -080083 }
Simon Hunt36a58c62015-04-08 11:00:07 -070084 //$log.debug("MouseOut()...", m);
Simon Hunt08f841d02015-02-10 14:39:20 -080085 }
86 }
87
88 // ==========================
89
90 function selectObject(obj) {
91 var el = this,
Simon Hunt5aac2fc2015-06-09 12:34:07 -070092 nodeEv = el && el.tagName === 'g',
93 ev = d3.event.sourceEvent || {},
Simon Hunt08f841d02015-02-10 14:39:20 -080094 n;
95
96 if (api.zoomingOrPanning(ev)) {
97 return;
98 }
99
Simon Hunt5aac2fc2015-06-09 12:34:07 -0700100 if (nodeEv) {
Simon Hunt08f841d02015-02-10 14:39:20 -0800101 n = d3.select(el);
102 } else {
103 api.node().each(function (d) {
104 if (d == obj) {
105 n = d3.select(el = this);
106 }
107 });
108 }
109 if (!n) return;
110
Simon Hunt5aac2fc2015-06-09 12:34:07 -0700111 if (nodeEv) {
112 consumeClick = true;
113 }
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700114 api.deselectLink();
115
Simon Hunt08f841d02015-02-10 14:39:20 -0800116 if (ev.shiftKey && n.classed('selected')) {
117 deselectObject(obj.id);
118 updateDetail();
119 return;
120 }
121
122 if (!ev.shiftKey) {
123 deselectAll();
124 }
125
126 selections[obj.id] = { obj: obj, el: el };
127 selectOrder.push(obj.id);
128
129 n.classed('selected', true);
130 api.updateDeviceColors(obj);
131 updateDetail();
Simon Hunt08f841d02015-02-10 14:39:20 -0800132 }
133
134 function deselectObject(id) {
135 var obj = selections[id];
136 if (obj) {
137 d3.select(obj.el).classed('selected', false);
138 delete selections[id];
139 fs.removeFromArray(id, selectOrder);
140 api.updateDeviceColors(obj.obj);
141 }
Simon Hunt08f841d02015-02-10 14:39:20 -0800142 }
143
144 function deselectAll() {
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700145 var something = (selectOrder.length > 0);
146
Simon Hunt08f841d02015-02-10 14:39:20 -0800147 // deselect all nodes in the network...
148 api.node().classed('selected', false);
149 selections = {};
150 selectOrder = [];
151 api.updateDeviceColors();
152 updateDetail();
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700153
154 // return true if something was selected
155 return something;
Simon Hunt08f841d02015-02-10 14:39:20 -0800156 }
157
158 // === -----------------------------------------------------
159
160 function requestDetails() {
161 var data = getSel(0).obj;
Simon Hunt237676b52015-03-10 19:04:26 -0700162 wss.sendEvent('requestDetails', {
Simon Hunt08f841d02015-02-10 14:39:20 -0800163 id: data.id,
164 class: data.class
165 });
166 }
167
168 // === -----------------------------------------------------
169
170 function updateDetail() {
171 var nSel = selectOrder.length;
172 if (!nSel) {
173 emptySelect();
174 } else if (nSel === 1) {
175 singleSelect();
176 } else {
177 multiSelect();
178 }
179 }
180
181 function emptySelect() {
Simon Huntf542d842015-02-11 16:20:33 -0800182 tts.cancelTraffic();
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700183 tps.displayNothing();
Simon Hunt08f841d02015-02-10 14:39:20 -0800184 }
185
186 function singleSelect() {
187 // NOTE: detail is shown from 'showDetails' event callback
188 requestDetails();
Simon Huntf542d842015-02-11 16:20:33 -0800189 tts.cancelTraffic();
190 tts.requestTrafficForMode();
Simon Hunt08f841d02015-02-10 14:39:20 -0800191 }
192
193 function multiSelect() {
Simon Hunt08f841d02015-02-10 14:39:20 -0800194 // display the selected nodes in the detail panel
195 tps.displayMulti(selectOrder);
196
197 // always add the 'show traffic' action
Bri Prebilic Colef5e48b12015-04-21 14:52:36 -0700198 tps.addAction({
199 id: '-mult-rel-traf-btn',
200 gid: 'allTraffic',
201 cb: tts.showRelatedIntentsAction,
202 tt: 'Show Related Traffic'
203 });
Simon Hunt08f841d02015-02-10 14:39:20 -0800204
205 // add other actions, based on what is selected...
206 if (nSel() === 2 && allSelectionsClass('host')) {
Bri Prebilic Colef5e48b12015-04-21 14:52:36 -0700207 tps.addAction({
208 id: 'host-flow-btn',
209 gid: 'endstation',
210 cb: tts.addHostIntentAction,
211 tt: 'Create Host-to-Host Flow'
212 });
Simon Hunt08f841d02015-02-10 14:39:20 -0800213 } else if (nSel() >= 2 && allSelectionsClass('host')) {
Bri Prebilic Colef5e48b12015-04-21 14:52:36 -0700214 tps.addAction({
215 id: 'mult-src-flow-btn',
216 gid: 'flows',
217 cb: tts.addMultiSourceIntentAction,
218 tt: 'Create Multi-Source Flow'
219 });
Simon Hunt08f841d02015-02-10 14:39:20 -0800220 }
221
Simon Huntf542d842015-02-11 16:20:33 -0800222 tts.cancelTraffic();
223 tts.requestTrafficForMode();
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700224 tps.displaySomething();
Simon Hunt08f841d02015-02-10 14:39:20 -0800225 }
226
227
228 // === -----------------------------------------------------
229 // Event Handlers
230
231 function showDetails(data) {
Simon Huntfb940112015-07-29 18:36:35 -0700232 var buttons = fs.isA(data.buttons);
233
Simon Hunt08f841d02015-02-10 14:39:20 -0800234 // display the data for the single selected node
235 tps.displaySingle(data);
236
Simon Huntfb940112015-07-29 18:36:35 -0700237 // TODO: use server-side-button-descriptors to add buttons
238
Simon Hunt08f841d02015-02-10 14:39:20 -0800239 // always add the 'show traffic' action
Bri Prebilic Colef5e48b12015-04-21 14:52:36 -0700240 tps.addAction({
241 id: '-sin-rel-traf-btn',
242 gid: 'intentTraffic',
243 cb: tts.showRelatedIntentsAction,
244 tt: 'Show Related Traffic'
245 });
Simon Hunt08f841d02015-02-10 14:39:20 -0800246
247 // add other actions, based on what is selected...
248 if (data.type === 'switch') {
Bri Prebilic Colef5e48b12015-04-21 14:52:36 -0700249 tps.addAction({
250 id: 'sin-dev-flows-btn',
251 gid: 'flows',
252 cb: tts.showDeviceLinkFlowsAction,
253 tt: 'Show Device Flows'
254 });
Simon Hunt08f841d02015-02-10 14:39:20 -0800255 }
Simon Huntfb940112015-07-29 18:36:35 -0700256
257 // TODO: for now, install overlay buttons here
258 if (buttons) {
259 tov.installButtons(buttons, tps.addAction, data);
260 }
261
262
Bri Prebilic Cole8f07f0d2015-04-23 13:28:43 -0700263 // TODO: have the server return explicit class and ID of each node
264 // for now, we assume the node is a device if it has a URI
265 if ((data.props).hasOwnProperty('URI')) {
266 tps.addAction({
Bri Prebilic Cole17c6d0a2015-07-16 14:56:40 -0700267 id: 'device-table-btn',
268 gid: data.type,
269 cb: function () {
270 ns.navTo(devPath, { devId: data.props['URI'] });
271 },
272 tt: 'Show device view'
273 });
274 tps.addAction({
Bri Prebilic Cole8f07f0d2015-04-23 13:28:43 -0700275 id: 'flows-table-btn',
Bri Prebilic Colecdc188d2015-04-24 16:40:11 -0700276 gid: 'flowTable',
Bri Prebilic Cole8f07f0d2015-04-23 13:28:43 -0700277 cb: function () {
Bri Prebilic Colecdc188d2015-04-24 16:40:11 -0700278 ns.navTo(flowPath, { devId: data.props['URI'] });
Bri Prebilic Cole8f07f0d2015-04-23 13:28:43 -0700279 },
Bri Prebilic Coleac829e42015-05-05 13:42:06 -0700280 tt: 'Show flow view for this device'
281 });
282 tps.addAction({
283 id: 'ports-table-btn',
Bri Prebilic Cole9467a232015-05-06 16:59:05 -0700284 gid: 'portTable',
Bri Prebilic Coleac829e42015-05-05 13:42:06 -0700285 cb: function () {
286 ns.navTo(portPath, { devId: data.props['URI'] });
287 },
288 tt: 'Show port view for this device'
Bri Prebilic Cole8f07f0d2015-04-23 13:28:43 -0700289 });
Bri Prebilic Cole5c60d6e2015-05-12 09:11:03 -0700290 tps.addAction({
291 id: 'groups-table-btn',
292 gid: 'groupTable',
293 cb: function () {
294 ns.navTo(groupPath, { devId: data.props['URI'] });
295 },
296 tt: 'Show group view for this device'
297 });
Bri Prebilic Cole8f07f0d2015-04-23 13:28:43 -0700298 }
Simon Hunt08f841d02015-02-10 14:39:20 -0800299
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700300 tps.displaySomething();
Simon Hunt6036b192015-02-11 11:20:26 -0800301 }
302
Simon Huntf542d842015-02-11 16:20:33 -0800303 function validateSelectionContext() {
304 if (!hovered && !nSel()) {
305 tts.cancelTraffic();
306 return false;
307 }
308 return true;
Simon Hunt08f841d02015-02-10 14:39:20 -0800309 }
Simon Hunt08f841d02015-02-10 14:39:20 -0800310
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700311 function clickConsumed(x) {
312 var cc = consumeClick;
313 consumeClick = !!x;
314 return cc;
315 }
316
Simon Hunt08f841d02015-02-10 14:39:20 -0800317 // === -----------------------------------------------------
318 // === MODULE DEFINITION ===
319
320 angular.module('ovTopo')
Simon Hunt75ec9692015-02-11 16:40:36 -0800321 .factory('TopoSelectService',
Simon Huntfb940112015-07-29 18:36:35 -0700322 ['$log', 'FnService', 'WebSocketService', 'TopoOverlayService',
Bri Prebilic Cole8f07f0d2015-04-23 13:28:43 -0700323 'TopoPanelService', 'TopoTrafficService', 'NavService',
Simon Hunt08f841d02015-02-10 14:39:20 -0800324
Simon Huntfb940112015-07-29 18:36:35 -0700325 function (_$log_, _fs_, _wss_, _tov_, _tps_, _tts_, _ns_) {
Simon Hunt6036b192015-02-11 11:20:26 -0800326 $log = _$log_;
327 fs = _fs_;
Simon Hunt237676b52015-03-10 19:04:26 -0700328 wss = _wss_;
Simon Huntfb940112015-07-29 18:36:35 -0700329 tov = _tov_;
Simon Hunt6036b192015-02-11 11:20:26 -0800330 tps = _tps_;
Simon Huntf542d842015-02-11 16:20:33 -0800331 tts = _tts_;
Bri Prebilic Cole8f07f0d2015-04-23 13:28:43 -0700332 ns = _ns_;
Simon Hunt08f841d02015-02-10 14:39:20 -0800333
Simon Hunt6036b192015-02-11 11:20:26 -0800334 function initSelect(_api_) {
335 api = _api_;
336 }
Simon Hunt08f841d02015-02-10 14:39:20 -0800337
Simon Hunt6036b192015-02-11 11:20:26 -0800338 function destroySelect() { }
Simon Hunt08f841d02015-02-10 14:39:20 -0800339
Simon Hunt6036b192015-02-11 11:20:26 -0800340 return {
341 initSelect: initSelect,
342 destroySelect: destroySelect,
Simon Hunt08f841d02015-02-10 14:39:20 -0800343
Simon Hunt6036b192015-02-11 11:20:26 -0800344 showDetails: showDetails,
Simon Hunt08f841d02015-02-10 14:39:20 -0800345
Simon Hunt6036b192015-02-11 11:20:26 -0800346 nodeMouseOver: nodeMouseOver,
347 nodeMouseOut: nodeMouseOut,
348 selectObject: selectObject,
349 deselectObject: deselectObject,
350 deselectAll: deselectAll,
Simon Huntf542d842015-02-11 16:20:33 -0800351
Simon Hunta0eb0a82015-02-11 12:30:06 -0800352 hovered: function () { return hovered; },
Simon Huntf542d842015-02-11 16:20:33 -0800353 selectOrder: function () { return selectOrder; },
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700354 validateSelectionContext: validateSelectionContext,
355
356 clickConsumed: clickConsumed
Simon Hunt6036b192015-02-11 11:20:26 -0800357 };
358 }]);
Simon Hunt08f841d02015-02-10 14:39:20 -0800359}());