Simon Hunt
Committed by Gerrit Code Review

GUI -- Migrating the add/update device functionality to the Topology View. (WIP)

- still a lot of work to do.

Change-Id: I0453b7e2ec20a8a8149fd9d6440a13a3d43fbfd6
...@@ -70,3 +70,7 @@ svg.embeddedIcon .icon rect { ...@@ -70,3 +70,7 @@ svg.embeddedIcon .icon rect {
70 .dark svg.embeddedIcon .icon rect { 70 .dark svg.embeddedIcon .icon rect {
71 stroke: #ccc; 71 stroke: #ccc;
72 } 72 }
73 +
74 +svg .svgIcon {
75 + fill-rule: evenodd;
76 +}
......
...@@ -26,8 +26,8 @@ ...@@ -26,8 +26,8 @@
26 cornerSize = vboxSize / 10, 26 cornerSize = vboxSize / 10,
27 viewBox = '0 0 ' + vboxSize + ' ' + vboxSize; 27 viewBox = '0 0 ' + vboxSize + ' ' + vboxSize;
28 28
29 - // maps icon id to the glyph id it uses. 29 + // Maps icon ID to the glyph ID it uses.
30 - // note: icon id maps to a CSS class for styling that icon 30 + // NOTE: icon ID maps to a CSS class for styling that icon
31 var glyphMapping = { 31 var glyphMapping = {
32 deviceOnline: 'checkMark', 32 deviceOnline: 'checkMark',
33 deviceOffline: 'xMark', 33 deviceOffline: 'xMark',
...@@ -36,6 +36,8 @@ ...@@ -36,6 +36,8 @@
36 tableColSortNone: '-' 36 tableColSortNone: '-'
37 }; 37 };
38 38
39 +
40 +
39 function ensureIconLibDefs() { 41 function ensureIconLibDefs() {
40 var body = d3.select('body'), 42 var body = d3.select('body'),
41 svg = body.select('svg#IconLibDefs'), 43 svg = body.select('svg#IconLibDefs'),
...@@ -48,13 +50,6 @@ ...@@ -48,13 +50,6 @@
48 return svg.select('defs'); 50 return svg.select('defs');
49 } 51 }
50 52
51 - angular.module('onosSvg')
52 - .factory('IconService', ['$log', 'FnService', 'GlyphService',
53 - function (_$log_, _fs_, _gs_) {
54 - $log = _$log_;
55 - fs = _fs_;
56 - gs = _gs_;
57 -
58 // div is a D3 selection of the <DIV> element into which icon should load 53 // div is a D3 selection of the <DIV> element into which icon should load
59 // iconCls is the CSS class used to identify the icon 54 // iconCls is the CSS class used to identify the icon
60 // size is dimension of icon in pixels. Defaults to 20. 55 // size is dimension of icon in pixels. Defaults to 20.
...@@ -103,9 +98,73 @@ ...@@ -103,9 +98,73 @@
103 loadIcon(div, iconCls, size, true); 98 loadIcon(div, iconCls, size, true);
104 } 99 }
105 100
101 +
102 + // configuration for device and host icons in the topology view
103 + var config = {
104 + device: {
105 + dim: 36,
106 + rx: 4
107 + },
108 + host: {
109 + radius: {
110 + noGlyph: 9,
111 + withGlyph: 14
112 + },
113 + glyphed: {
114 + endstation: 1,
115 + bgpSpeaker: 1,
116 + router: 1
117 + }
118 + }
119 + };
120 +
121 +
122 + // Adds a device icon to the specified element, using the given glyph.
123 + // Returns the D3 selection of the icon.
124 + function addDeviceIcon(elem, glyphId) {
125 + var cfg = config.device,
126 + g = elem.append('g')
127 + .attr('class', 'svgIcon deviceIcon');
128 +
129 + g.append('rect').attr({
130 + x: 0,
131 + y: 0,
132 + rx: cfg.rx,
133 + width: cfg.dim,
134 + height: cfg.dim
135 + });
136 +
137 + g.append('use').attr({
138 + 'xlink:href': '#' + glyphId,
139 + width: cfg.dim,
140 + height: cfg.dim
141 + });
142 +
143 + g.dim = cfg.dim;
144 + return g;
145 + }
146 +
147 + function addHostIcon(elem, glyphId) {
148 + // TODO:
149 + }
150 +
151 +
152 + // =========================
153 + // === DEFINE THE MODULE
154 +
155 + angular.module('onosSvg')
156 + .factory('IconService', ['$log', 'FnService', 'GlyphService',
157 + function (_$log_, _fs_, _gs_) {
158 + $log = _$log_;
159 + fs = _fs_;
160 + gs = _gs_;
161 +
106 return { 162 return {
107 loadIcon: loadIcon, 163 loadIcon: loadIcon,
108 - loadEmbeddedIcon: loadEmbeddedIcon 164 + loadEmbeddedIcon: loadEmbeddedIcon,
165 + addDeviceIcon: addDeviceIcon,
166 + addHostIcon: addHostIcon,
167 + iconConfig: function () { return config; }
109 }; 168 };
110 }]); 169 }]);
111 170
......
...@@ -22,28 +22,25 @@ ...@@ -22,28 +22,25 @@
22 The Map Service provides a simple API for loading geographical maps into 22 The Map Service provides a simple API for loading geographical maps into
23 an SVG layer. For example, as a background to the Topology View. 23 an SVG layer. For example, as a background to the Topology View.
24 24
25 - e.g. var ok = MapService.loadMapInto(svgLayer, '*continental-us'); 25 + e.g. var promise = MapService.loadMapInto(svgLayer, '*continental-us');
26 26
27 The Map Service makes use of the GeoDataService to load the required data 27 The Map Service makes use of the GeoDataService to load the required data
28 from the server and to create the appropriate geographical projection. 28 from the server and to create the appropriate geographical projection.
29 29
30 + A promise is returned to the caller, which is resolved with the
31 + map projection once created.
30 */ 32 */
31 33
32 (function () { 34 (function () {
33 'use strict'; 35 'use strict';
34 36
35 // injected references 37 // injected references
36 - var $log, fs, gds; 38 + var $log, $q, fs, gds;
37 -
38 - angular.module('onosSvg')
39 - .factory('MapService', ['$log', 'FnService', 'GeoDataService',
40 - function (_$log_, _fs_, _gds_) {
41 - $log = _$log_;
42 - fs = _fs_;
43 - gds = _gds_;
44 39
45 function loadMapInto(mapLayer, id, opts) { 40 function loadMapInto(mapLayer, id, opts) {
46 - var promise = gds.fetchTopoData(id); 41 + var promise = gds.fetchTopoData(id),
42 + deferredProjection = $q.defer();
43 +
47 if (!promise) { 44 if (!promise) {
48 $log.warn('Failed to load map: ' + id); 45 $log.warn('Failed to load map: ' + id);
49 return false; 46 return false;
...@@ -52,15 +49,26 @@ ...@@ -52,15 +49,26 @@
52 promise.then(function () { 49 promise.then(function () {
53 var gen = gds.createPathGenerator(promise.topodata, opts); 50 var gen = gds.createPathGenerator(promise.topodata, opts);
54 51
52 + deferredProjection.resolve(gen.settings.projection);
53 +
55 mapLayer.selectAll('path') 54 mapLayer.selectAll('path')
56 .data(gen.geodata.features) 55 .data(gen.geodata.features)
57 .enter() 56 .enter()
58 .append('path') 57 .append('path')
59 .attr('d', gen.pathgen); 58 .attr('d', gen.pathgen);
60 }); 59 });
61 - return true; 60 + return deferredProjection.promise;
62 } 61 }
63 62
63 +
64 + angular.module('onosSvg')
65 + .factory('MapService', ['$log', '$q', 'FnService', 'GeoDataService',
66 + function (_$log_, _$q_, _fs_, _gds_) {
67 + $log = _$log_;
68 + $q = _$q_;
69 + fs = _fs_;
70 + gds = _gds_;
71 +
64 return { 72 return {
65 loadMapInto: loadMapInto 73 loadMapInto: loadMapInto
66 }; 74 };
......
...@@ -240,13 +240,18 @@ ...@@ -240,13 +240,18 @@
240 el.style('visibility', (b ? 'visible' : 'hidden')); 240 el.style('visibility', (b ? 'visible' : 'hidden'));
241 } 241 }
242 242
243 + function safeId(s) {
244 + return s.replace(/[^a-z0-9]/gi, '-');
245 + }
246 +
243 return { 247 return {
244 createDragBehavior: createDragBehavior, 248 createDragBehavior: createDragBehavior,
245 loadGlow: loadGlow, 249 loadGlow: loadGlow,
246 cat7: cat7, 250 cat7: cat7,
247 translate: translate, 251 translate: translate,
248 stripPx: stripPx, 252 stripPx: stripPx,
249 - makeVisible: makeVisible 253 + makeVisible: makeVisible,
254 + safeId: safeId
250 }; 255 };
251 }]); 256 }]);
252 }()); 257 }());
......
...@@ -245,3 +245,94 @@ ...@@ -245,3 +245,94 @@
245 /* TODO: add blue glow */ 245 /* TODO: add blue glow */
246 /*filter: url(#blue-glow);*/ 246 /*filter: url(#blue-glow);*/
247 } 247 }
248 +
249 +
250 +/* --- Topo Nodes --- */
251 +
252 +#ov-topo svg .node {
253 + cursor: pointer;
254 +}
255 +
256 +#ov-topo svg .node.selected rect,
257 +#ov-topo svg .node.selected circle {
258 + fill: #f90;
259 + /* TODO: add blue glow filter */
260 + /*filter: url(#blue-glow);*/
261 +}
262 +
263 +#ov-topo svg .node text {
264 + pointer-events: none;
265 +}
266 +
267 +/* Device Nodes */
268 +
269 +#ov-topo svg .node.device {
270 +}
271 +
272 +#ov-topo svg .node.device rect {
273 + stroke-width: 1.5;
274 +}
275 +
276 +#ov-topo svg .node.device.fixed rect {
277 + stroke-width: 1.5;
278 + stroke: #ccc;
279 +}
280 +
281 +/* note: device is offline without the 'online' class */
282 +#ov-topo svg .node.device {
283 + fill: #777;
284 +}
285 +
286 +#ov-topo svg .node.device.online {
287 + fill: #6e7fa3;
288 +}
289 +
290 +/* note: device is offline without the 'online' class */
291 +#ov-topo svg .node.device text {
292 + fill: #bbb;
293 + font: 10pt sans-serif;
294 +}
295 +
296 +#ov-topo svg .node.device.online text {
297 + fill: white;
298 +}
299 +
300 +#ov-topo svg .node.device .svgIcon rect {
301 + fill: #aaa;
302 +}
303 +#ov-topo svg .node.device .svgIcon use {
304 + fill: #777;
305 +}
306 +#ov-topo svg .node.device.selected .svgIcon rect {
307 + fill: #f90;
308 +}
309 +#ov-topo svg .node.device.online .svgIcon rect {
310 + fill: #ccc;
311 +}
312 +#ov-topo svg .node.device.online .svgIcon use {
313 + fill: #000;
314 +}
315 +#ov-topo svg .node.device.online.selected .svgIcon rect {
316 + fill: #f90;
317 +}
318 +
319 +
320 +/* Host Nodes */
321 +
322 +#ov-topo svg .node.host {
323 + stroke: #000;
324 +}
325 +
326 +#ov-topo svg .node.host text {
327 + fill: #846;
328 + stroke: none;
329 + font: 9pt sans-serif;
330 +}
331 +
332 +svg .node.host circle {
333 + stroke: #000;
334 + fill: #edb;
335 +}
336 +
337 +
338 +
......
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
28 ]; 28 ];
29 29
30 // references to injected services etc. 30 // references to injected services etc.
31 - var $log, fs, ks, zs, gs, ms, sus, tfs; 31 + var $log, fs, ks, zs, gs, ms, sus, tfs, tis;
32 32
33 // DOM elements 33 // DOM elements
34 var ovtopo, svg, defs, zoomLayer, mapG, forceG, noDevsLayer; 34 var ovtopo, svg, defs, zoomLayer, mapG, forceG, noDevsLayer;
...@@ -41,20 +41,61 @@ ...@@ -41,20 +41,61 @@
41 // --- Short Cut Keys ------------------------------------------------ 41 // --- Short Cut Keys ------------------------------------------------
42 42
43 var keyBindings = { 43 var keyBindings = {
44 - W: [logWarning, '(temp) log a warning'], 44 + //O: [toggleSummary, 'Toggle ONOS summary pane'],
45 - E: [logError, '(temp) log an error'], 45 + I: [toggleInstances, 'Toggle ONOS instances pane'],
46 - R: [resetZoom, 'Reset pan / zoom'] 46 + //D: [toggleDetails, 'Disable / enable details pane'],
47 +
48 + //H: [toggleHosts, 'Toggle host visibility'],
49 + //M: [toggleOffline, 'Toggle offline visibility'],
50 + //B: [toggleBg, 'Toggle background image'],
51 + //P: togglePorts,
52 +
53 + //X: [toggleNodeLock, 'Lock / unlock node positions'],
54 + //Z: [toggleOblique, 'Toggle oblique view (Experimental)'],
55 + L: [cycleLabels, 'Cycle device labels'],
56 + //U: [unpin, 'Unpin node (hover mouse over)'],
57 + R: [resetZoom, 'Reset pan / zoom'],
58 +
59 + //V: [showRelatedIntentsAction, 'Show all related intents'],
60 + //rightArrow: [showNextIntentAction, 'Show next related intent'],
61 + //leftArrow: [showPrevIntentAction, 'Show previous related intent'],
62 + //W: [showSelectedIntentTrafficAction, 'Monitor traffic of selected intent'],
63 + //A: [showAllTrafficAction, 'Monitor all traffic'],
64 + //F: [showDeviceLinkFlowsAction, 'Show device link flows'],
65 +
66 + //E: [equalizeMasters, 'Equalize mastership roles'],
67 +
68 + //esc: handleEscape,
69 +
70 + _helpFormat: [
71 + ['O', 'I', 'D', '-', 'H', 'M', 'B', 'P' ],
72 + ['X', 'Z', 'L', 'U', 'R' ],
73 + ['V', 'rightArrow', 'leftArrow', 'W', 'A', 'F', '-', 'E' ]
74 + ]
75 +
47 }; 76 };
48 77
49 - // ----------------- 78 + // mouse gestures
50 - // these functions are necessarily temporary examples.... 79 + var gestures = [
51 - function logWarning() { 80 + ['click', 'Select the item and show details'],
52 - $log.warn('You have been warned!'); 81 + ['shift-click', 'Toggle selection state'],
82 + ['drag', 'Reposition (and pin) device / host'],
83 + ['cmd-scroll', 'Zoom in / out'],
84 + ['cmd-drag', 'Pan']
85 + ];
86 +
87 + function toggleInstances() {
88 + if (tis.isVisible()) {
89 + tis.hide();
90 + } else {
91 + tis.show();
53 } 92 }
54 - function logError() { 93 + tfs.updateDeviceColors();
55 - $log.error('You are erroneous!'); 94 + }
95 +
96 + function cycleLabels() {
97 + $log.debug('Cycle Labels.....');
56 } 98 }
57 - // -----------------
58 99
59 function resetZoom() { 100 function resetZoom() {
60 zoomer.reset(); 101 zoomer.reset();
...@@ -83,7 +124,6 @@ ...@@ -83,7 +124,6 @@
83 function zoomCallback() { 124 function zoomCallback() {
84 var tr = zoomer.translate(), 125 var tr = zoomer.translate(),
85 sc = zoomer.scale(); 126 sc = zoomer.scale();
86 - $log.log('ZOOM: translate = ' + tr + ', scale = ' + sc);
87 127
88 // keep the map lines constant width while zooming 128 // keep the map lines constant width while zooming
89 mapG.style('stroke-width', (2.0 / sc) + 'px'); 129 mapG.style('stroke-width', (2.0 / sc) + 'px');
...@@ -150,16 +190,19 @@ ...@@ -150,16 +190,19 @@
150 190
151 function setUpMap() { 191 function setUpMap() {
152 mapG = zoomLayer.append('g').attr('id', 'topo-map'); 192 mapG = zoomLayer.append('g').attr('id', 'topo-map');
153 - //ms.loadMapInto(map, '*continental_us', {mapFillScale:0.5}); 193 +
154 - ms.loadMapInto(mapG, '*continental_us');
155 //showCallibrationPoints(); 194 //showCallibrationPoints();
195 + //return ms.loadMapInto(map, '*continental_us', {mapFillScale:0.5});
196 +
197 + // returns a promise for the projection...
198 + return ms.loadMapInto(mapG, '*continental_us');
156 } 199 }
157 200
158 // --- Force Layout -------------------------------------------------- 201 // --- Force Layout --------------------------------------------------
159 202
160 - function setUpForce() { 203 + function setUpForce(xlink) {
161 forceG = zoomLayer.append('g').attr('id', 'topo-force'); 204 forceG = zoomLayer.append('g').attr('id', 'topo-force');
162 - tfs.initForce(forceG, svg.attr('width'), svg.attr('height')); 205 + tfs.initForce(forceG, xlink, svg.attr('width'), svg.attr('height'));
163 } 206 }
164 207
165 // --- Controller Definition ----------------------------------------- 208 // --- Controller Definition -----------------------------------------
...@@ -174,8 +217,12 @@ ...@@ -174,8 +217,12 @@
174 'TopoInstService', 217 'TopoInstService',
175 218
176 function ($scope, _$log_, $loc, $timeout, _fs_, mast, 219 function ($scope, _$log_, $loc, $timeout, _fs_, mast,
177 - _ks_, _zs_, _gs_, _ms_, _sus_, tes, _tfs_, tps, tis) { 220 + _ks_, _zs_, _gs_, _ms_, _sus_, tes, _tfs_, tps, _tis_) {
178 - var self = this; 221 + var self = this,
222 + xlink = {
223 + showNoDevs: showNoDevs
224 + };
225 +
179 $log = _$log_; 226 $log = _$log_;
180 fs = _fs_; 227 fs = _fs_;
181 ks = _ks_; 228 ks = _ks_;
...@@ -184,6 +231,7 @@ ...@@ -184,6 +231,7 @@
184 ms = _ms_; 231 ms = _ms_;
185 sus = _sus_; 232 sus = _sus_;
186 tfs = _tfs_; 233 tfs = _tfs_;
234 + tis = _tis_;
187 235
188 self.notifyResize = function () { 236 self.notifyResize = function () {
189 svgResized(fs.windowSize(mast.mastHeight())); 237 svgResized(fs.windowSize(mast.mastHeight()));
...@@ -207,8 +255,8 @@ ...@@ -207,8 +255,8 @@
207 setUpDefs(); 255 setUpDefs();
208 setUpZoom(); 256 setUpZoom();
209 setUpNoDevs(); 257 setUpNoDevs();
210 - setUpMap(); 258 + xlink.projectionPromise = setUpMap();
211 - setUpForce(); 259 + setUpForce(xlink);
212 260
213 tis.initInst(); 261 tis.initInst();
214 tps.initPanels(); 262 tps.initPanels();
......
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
23 'use strict'; 23 'use strict';
24 24
25 // injected refs 25 // injected refs
26 - var $log, wss, wes, tps, tis; 26 + var $log, wss, wes, tps, tis, tfs;
27 27
28 // internal state 28 // internal state
29 var wsock; 29 var wsock;
...@@ -32,7 +32,9 @@ ...@@ -32,7 +32,9 @@
32 showSummary: showSummary, 32 showSummary: showSummary,
33 addInstance: addInstance, 33 addInstance: addInstance,
34 updateInstance: updateInstance, 34 updateInstance: updateInstance,
35 - removeInstance: removeInstance 35 + removeInstance: removeInstance,
36 + addDevice: addDevice,
37 + updateDevice: updateDevice
36 // TODO: implement remaining handlers.. 38 // TODO: implement remaining handlers..
37 39
38 }; 40 };
...@@ -63,6 +65,16 @@ ...@@ -63,6 +65,16 @@
63 tis.removeInstance(ev.payload); 65 tis.removeInstance(ev.payload);
64 } 66 }
65 67
68 + function addDevice(ev) {
69 + $log.debug(' **** Add Device **** ', ev.payload);
70 + tfs.addDevice(ev.payload);
71 + }
72 +
73 + function updateDevice(ev) {
74 + $log.debug(' **** Update Device **** ', ev.payload);
75 + tfs.updateDevice(ev.payload);
76 + }
77 +
66 // ========================== 78 // ==========================
67 79
68 var dispatcher = { 80 var dispatcher = {
...@@ -100,14 +112,15 @@ ...@@ -100,14 +112,15 @@
100 angular.module('ovTopo') 112 angular.module('ovTopo')
101 .factory('TopoEventService', 113 .factory('TopoEventService',
102 ['$log', '$location', 'WebSocketService', 'WsEventService', 114 ['$log', '$location', 'WebSocketService', 'WsEventService',
103 - 'TopoPanelService', 'TopoInstService', 115 + 'TopoPanelService', 'TopoInstService', 'TopoForceService',
104 116
105 - function (_$log_, $loc, _wss_, _wes_, _tps_, _tis_) { 117 + function (_$log_, $loc, _wss_, _wes_, _tps_, _tis_, _tfs_) {
106 $log = _$log_; 118 $log = _$log_;
107 wss = _wss_; 119 wss = _wss_;
108 wes = _wes_; 120 wes = _wes_;
109 tps = _tps_; 121 tps = _tps_;
110 tis = _tis_; 122 tis = _tis_;
123 + tfs = _tfs_;
111 124
112 function bindDispatcher(TopoDomElementsPassedHere) { 125 function bindDispatcher(TopoDomElementsPassedHere) {
113 // TODO: store refs to topo DOM elements... 126 // TODO: store refs to topo DOM elements...
......
...@@ -325,7 +325,10 @@ ...@@ -325,7 +325,10 @@
325 destroyInst: destroyInst, 325 destroyInst: destroyInst,
326 addInstance: addInstance, 326 addInstance: addInstance,
327 updateInstance: updateInstance, 327 updateInstance: updateInstance,
328 - removeInstance: removeInstance 328 + removeInstance: removeInstance,
329 + isVisible: function () { return oiBox.isVisible(); },
330 + show: function () { oiBox.show(); },
331 + hide: function () { oiBox.hide(); }
329 }; 332 };
330 }]); 333 }]);
331 }()); 334 }());
......