Simon Hunt

GUI -- Re-instated Packet / Optical Layer filters.

- Adding ONOS instance fly-in pane. Note: still WIP.
- refactored onos.ui.addFloatingPanel to allow TL vs. TR.
- added instance pane to topo view.
- implemented addInstance() event.
- refactored event tracing.
- added instances test scenario.

Change-Id: I58d9769afa8aee9079ec778496cbc47bef329608
1 +{
2 + "event": "addLink",
3 + "payload": {
4 + "id": "of:0000ffffffff0003/4-of:0000ffffffffff03/1",
5 + "type": "pktopt",
6 + "linkWidth": 2,
7 + "src": "of:0000ffffffff0003",
8 + "srcPort": "4",
9 + "dst": "of:0000ffffffffff03",
10 + "dstPort": "1",
11 + "props" : {
12 + "BW": "90 Gb"
13 + }
14 + }
15 +}
1 +{
2 + "event": "addLink",
3 + "payload": {
4 + "id": "of:0000ffffffff0003/9-of:0000ffffffff0007/2",
5 + "type": "direct",
6 + "linkWidth": 2,
7 + "src": "of:0000ffffffff0003",
8 + "srcPort": "9",
9 + "dst": "of:0000ffffffff0007",
10 + "dstPort": "2",
11 + "props" : {
12 + "BW": "120 Gb"
13 + }
14 + }
15 +}
1 +{
2 + "event": "addLink",
3 + "payload": {
4 + "id": "of:0000ffffffff0008/2-of:0000ffffffff0003/1",
5 + "type": "direct",
6 + "linkWidth": 2,
7 + "src": "of:0000ffffffff0008",
8 + "srcPort": "2",
9 + "dst": "of:0000ffffffff0003",
10 + "dstPort": "1",
11 + "props" : {
12 + "BW": "70 Gb"
13 + }
14 + }
15 +}
1 +{
2 + "event": "addLink",
3 + "payload": {
4 + "id": "of:0000ffffffff0008/4-of:0000ffffffff0007/1",
5 + "type": "direct",
6 + "linkWidth": 2,
7 + "src": "of:0000ffffffff0008",
8 + "srcPort": "4",
9 + "dst": "of:0000ffffffff0007",
10 + "dstPort": "1",
11 + "props" : {
12 + "BW": "90 Gb"
13 + }
14 + }
15 +}
1 +{
2 + "event": "addLink",
3 + "payload": {
4 + "id": "of:0000ffffffffff08/4-of:0000ffffffffff03/1",
5 + "type": "optical",
6 + "linkWidth": 2,
7 + "src": "of:0000ffffffffff08",
8 + "srcPort": "4",
9 + "dst": "of:0000ffffffffff03",
10 + "dstPort": "1",
11 + "props" : {
12 + "BW": "90 Gb"
13 + }
14 + }
15 +}
1 +{
2 + "event": "addHost",
3 + "payload": {
4 + "id": "0E:2A:69:30:13:aa/-1",
5 + "ingress": "0E:2A:69:30:13:aa/-1/0-of:0000ffffffff0008/101",
6 + "egress": "of:0000ffffffff0008/101-0E:2A:69:30:13:aa/-1/0",
7 + "cp": {
8 + "device": "of:0000ffffffff0008",
9 + "port": 101
10 + },
11 + "labels": [
12 + "12.13.14.15",
13 + "0E:2A:69:30:13:aa"
14 + ],
15 + "props": {}
16 + }
17 +}
1 +{
2 + "event": "addHost",
3 + "payload": {
4 + "id": "0E:2A:69:30:13:88/-1",
5 + "ingress": "0E:2A:69:30:13:88/-1/0-of:0000ffffffff0007/101",
6 + "egress": "of:0000ffffffff0007/101-0E:2A:69:30:13:86/-1/0",
7 + "cp": {
8 + "device": "of:0000ffffffff0007",
9 + "port": 101
10 + },
11 + "labels": [
12 + "4.5.7.6",
13 + "0E:2A:69:30:13:88"
14 + ],
15 + "props": {}
16 + }
17 +}
1 +{
2 + "event": "addHost",
3 + "payload": {
4 + "id": "0E:2A:69:30:13:86/-1",
5 + "ingress": "0E:2A:69:30:13:86/-1/0-of:0000ffffffff0003/101",
6 + "egress": "of:0000ffffffff0003/101-0E:2A:69:30:13:86/-1/0",
7 + "cp": {
8 + "device": "of:0000ffffffff0003",
9 + "port": 101
10 + },
11 + "labels": [
12 + "1.2.3.4",
13 + "0E:2A:69:30:13:86"
14 + ],
15 + "props": {}
16 + }
17 +}
1 +{
2 + "event": "addInstance",
3 + "payload": {
4 + "id": "local",
5 + "online": true,
6 + "labels": [
7 + "local",
8 + "127.0.0.1"
9 + ]
10 + }
11 +}
1 +{
2 + "event": "addInstance",
3 + "payload": {
4 + "id": "onos-2",
5 + "online": true,
6 + "labels": [
7 + "onos-2",
8 + "192.168.2.2"
9 + ]
10 + }
11 +}
1 +{
2 + "event": "addInstance",
3 + "payload": {
4 + "id": "onos-3",
5 + "online": false,
6 + "labels": [
7 + "onos-3",
8 + "192.168.3.3"
9 + ]
10 + }
11 +}
1 +{
2 + "event": "addDevice",
3 + "payload": {
4 + "id": "of:0000ffffffff0008",
5 + "type": "switch",
6 + "online": true,
7 + "master": "onos-3",
8 + "labels": [
9 + "0000ffffffff0008",
10 + "FF:FF:FF:FF:00:08",
11 + "sw-8"
12 + ],
13 + "metaUi": {
14 + "x": 734,
15 + "y": 477
16 + }
17 + }
18 +}
1 +{
2 + "event": "addDevice",
3 + "payload": {
4 + "id": "of:0000ffffffff0003",
5 + "type": "switch",
6 + "online": true,
7 + "master": "local",
8 + "labels": [
9 + "0000ffffffff0003",
10 + "FF:FF:FF:FF:00:03",
11 + "sw-3"
12 + ],
13 + "metaUi": {
14 + "x": 282,
15 + "y": 503
16 + }
17 + }
18 +}
1 +{
2 + "event": "addDevice",
3 + "payload": {
4 + "id": "of:0000ffffffff0007",
5 + "type": "switch",
6 + "online": true,
7 + "master": "onos-2",
8 + "labels": [
9 + "0000ffffffff0007",
10 + "FF:FF:FF:FF:00:07",
11 + "sw-7"
12 + ],
13 + "metaUi": {
14 + "x": 530,
15 + "y": 330
16 + }
17 + }
18 +}
1 +{
2 + "event": "addDevice",
3 + "payload": {
4 + "id": "of:0000ffffffffff08",
5 + "type": "roadm",
6 + "online": true,
7 + "master": "onos-3",
8 + "labels": [
9 + "0000ffffffffff08",
10 + "FF:FF:FF:FF:FF:08",
11 + "opt-8"
12 + ],
13 + "metaUi": {
14 + "x": 734,
15 + "y": 577
16 + }
17 + }
18 +}
1 +{
2 + "event": "addDevice",
3 + "payload": {
4 + "id": "of:0000ffffffffff03",
5 + "type": "roadm",
6 + "online": true,
7 + "master": "local",
8 + "labels": [
9 + "0000ffffffffff03",
10 + "FF:FF:FF:FF:FF:03",
11 + "opt-3"
12 + ],
13 + "metaUi": {
14 + "x": 282,
15 + "y": 603
16 + }
17 + }
18 +}
1 +{
2 + "event": "addLink",
3 + "payload": {
4 + "id": "of:0000ffffffff0008/4-of:0000ffffffffff08/1",
5 + "type": "pktopt",
6 + "linkWidth": 2,
7 + "src": "of:0000ffffffff0008",
8 + "srcPort": "4",
9 + "dst": "of:0000ffffffffff08",
10 + "dstPort": "1",
11 + "props" : {
12 + "BW": "90 Gb"
13 + }
14 + }
15 +}
1 +{
2 + "comments": [
3 + "Developing ONOS instance visualization"
4 + ],
5 + "title": "ONOS Instance Scenario",
6 + "params": {
7 + "lastAuto": 17
8 + },
9 + "description": [
10 + "Visualizing ONOS instances.",
11 + "",
12 + "Press 'S' to load initial events.",
13 + "",
14 + "Press spacebar to complete the scenario..."
15 + ]
16 +}
...@@ -699,6 +699,16 @@ ...@@ -699,6 +699,16 @@
699 // .......................................................... 699 // ..........................................................
700 // UI API 700 // UI API
701 701
702 + var fpConfig = {
703 + TR: {
704 + side: 'right'
705 +
706 + },
707 + TL: {
708 + side: 'left'
709 + }
710 + };
711 +
702 uiApi = { 712 uiApi = {
703 addLib: function (libName, api) { 713 addLib: function (libName, api) {
704 // TODO: validation of args 714 // TODO: validation of args
...@@ -713,6 +723,7 @@ ...@@ -713,6 +723,7 @@
713 */ 723 */
714 addFloatingPanel: function (id, position) { 724 addFloatingPanel: function (id, position) {
715 var pos = position || 'TR', 725 var pos = position || 'TR',
726 + cfg = fpConfig[pos],
716 el, 727 el,
717 fp; 728 fp;
718 729
...@@ -723,22 +734,37 @@ ...@@ -723,22 +734,37 @@
723 734
724 el = $floatPanels.append('div') 735 el = $floatPanels.append('div')
725 .attr('id', id) 736 .attr('id', id)
726 - .attr('class', 'fpanel'); 737 + .attr('class', 'fpanel')
738 + .style('opacity', 0);
739 +
740 + // has to be called after el is set.
741 + el.style(cfg.side, pxHide());
742 +
743 + function pxShow() {
744 + return '20px';
745 + }
746 + function pxHide() {
747 + return (-20 - widthVal()) + 'px';
748 + }
749 + function widthVal() {
750 + return el.style('width').replace(/px$/, '');
751 + }
727 752
728 fp = { 753 fp = {
729 id: id, 754 id: id,
730 el: el, 755 el: el,
731 pos: pos, 756 pos: pos,
757 +
732 show: function () { 758 show: function () {
733 console.log('show pane: ' + id); 759 console.log('show pane: ' + id);
734 el.transition().duration(750) 760 el.transition().duration(750)
735 - .style('right', '20px') 761 + .style(cfg.side, pxShow())
736 .style('opacity', 1); 762 .style('opacity', 1);
737 }, 763 },
738 hide: function () { 764 hide: function () {
739 console.log('hide pane: ' + id); 765 console.log('hide pane: ' + id);
740 el.transition().duration(750) 766 el.transition().duration(750)
741 - .style('right', '-320px') 767 + .style(cfg.side, pxHide())
742 .style('opacity', 0); 768 .style('opacity', 0);
743 }, 769 },
744 empty: function () { 770 empty: function () {
...@@ -746,6 +772,12 @@ ...@@ -746,6 +772,12 @@
746 }, 772 },
747 append: function (what) { 773 append: function (what) {
748 return el.append(what); 774 return el.append(what);
775 + },
776 + width: function (w) {
777 + if (w === undefined) {
778 + return widthVal();
779 + }
780 + el.style('width', w);
749 } 781 }
750 }; 782 };
751 fpanels[id] = fp; 783 fpanels[id] = fp;
......
...@@ -197,6 +197,39 @@ ...@@ -197,6 +197,39 @@
197 border: 0; 197 border: 0;
198 } 198 }
199 199
200 +/* ONOS instance stuff */
201 +
202 +#topo-oibox {
203 + width: 80px;
204 +}
205 +
206 +#topo-oibox .onosInst {
207 + margin: 6px 0;
208 + padding: 3px;
209 + width: 80%;
210 + left: 10%;
211 + height: 40px;
212 + cursor: pointer;
213 +
214 + /* theme-related */
215 + color: #444;
216 + background-color: #ccc;
217 + border: 2px dashed #aaa;
218 +}
219 +
220 +
221 +#topo-oibox .onosInst.online {
222 + /* theme-related */
223 + color: #113;
224 + background-color: #bbf;
225 + border: 2px solid #555;
226 +}
227 +
228 +#topo svg .suppressed,
229 +#topo-oibox .suppressed {
230 + opacity: 0.2;
231 +}
232 +
200 /* Web Socket Closed Mask (starts hidden) */ 233 /* Web Socket Closed Mask (starts hidden) */
201 234
202 #topo-mask { 235 #topo-mask {
......
...@@ -155,11 +155,14 @@ ...@@ -155,11 +155,14 @@
155 sid = 0, 155 sid = 0,
156 deviceLabelIndex = 0, 156 deviceLabelIndex = 0,
157 hostLabelIndex = 0, 157 hostLabelIndex = 0,
158 - detailPane,
159 - selectOrder = [],
160 selections = {}, 158 selections = {},
159 + selectOrder = [],
161 hovered = null, 160 hovered = null,
161 + detailPane,
162 antTimer = null, 162 antTimer = null,
163 + onosInstances = {},
164 + onosOrder = [],
165 + oiBox,
163 166
164 viewMode = 'showAll', 167 viewMode = 'showAll',
165 portLabelsOn = false; 168 portLabelsOn = false;
...@@ -198,11 +201,19 @@ ...@@ -198,11 +201,19 @@
198 } 201 }
199 } 202 }
200 203
204 + function evTrace(data) {
205 + fnTrace(data.event, data.payload.id);
206 + }
207 +
201 // ============================== 208 // ==============================
202 // Key Callbacks 209 // Key Callbacks
203 210
204 function testMe(view) { 211 function testMe(view) {
205 - view.alert('test'); 212 + //view.alert('test');
213 + detailPane.show();
214 + setTimeout(detailPane.hide, 2000);
215 + oiBox.show();
216 + setTimeout(oiBox.hide, 2000);
206 } 217 }
207 218
208 function abortIfLive() { 219 function abortIfLive() {
...@@ -303,25 +314,61 @@ ...@@ -303,25 +314,61 @@
303 // ============================== 314 // ==============================
304 // Radio Button Callbacks 315 // Radio Button Callbacks
305 316
317 + var layerLookup = {
318 + host: {
319 + endstation: 'pkt', // default, if host event does not define type
320 + bgpSpeaker: 'pkt'
321 + },
322 + device: {
323 + switch: 'pkt',
324 + roadm: 'opt'
325 + },
326 + link: {
327 + hostLink: 'pkt',
328 + direct: 'pkt',
329 + optical: 'opt'
330 + }
331 + };
332 +
333 + function inLayer(d, layer) {
334 + var look = layerLookup[d.class],
335 + lyr = look && look[d.type];
336 + return lyr === layer;
337 + }
338 +
339 + function unsuppressLayer(which) {
340 + node.each(function (d) {
341 + var node = d.el;
342 + if (inLayer(d, which)) {
343 + node.classed('suppressed', false);
344 + }
345 + });
346 +
347 + link.each(function (d) {
348 + var link = d.el;
349 + if (inLayer(d, which)) {
350 + link.classed('suppressed', false);
351 + }
352 + });
353 + }
354 +
306 function showAllLayers() { 355 function showAllLayers() {
307 -// network.node.classed('inactive', false); 356 + node.classed('suppressed', false);
308 -// network.link.classed('inactive', false); 357 + link.classed('suppressed', false);
309 // d3.selectAll('svg .port').classed('inactive', false); 358 // d3.selectAll('svg .port').classed('inactive', false);
310 // d3.selectAll('svg .portText').classed('inactive', false); 359 // d3.selectAll('svg .portText').classed('inactive', false);
311 - // TODO ...
312 - network.view.alert('showAllLayers() callback');
313 } 360 }
314 361
315 function showPacketLayer() { 362 function showPacketLayer() {
316 - showAllLayers(); 363 + node.classed('suppressed', true);
317 - // TODO ... 364 + link.classed('suppressed', true);
318 - network.view.alert('showPacketLayer() callback'); 365 + unsuppressLayer('pkt');
319 } 366 }
320 367
321 function showOpticalLayer() { 368 function showOpticalLayer() {
322 - showAllLayers(); 369 + node.classed('suppressed', true);
323 - // TODO ... 370 + link.classed('suppressed', true);
324 - network.view.alert('showOpticalLayer() callback'); 371 + unsuppressLayer('opt');
325 } 372 }
326 373
327 // ============================== 374 // ==============================
...@@ -351,7 +398,7 @@ ...@@ -351,7 +398,7 @@
351 } 398 }
352 399
353 var eventDispatch = { 400 var eventDispatch = {
354 - addInstance: stillToImplement, 401 + addInstance: addInstance,
355 addDevice: addDevice, 402 addDevice: addDevice,
356 addLink: addLink, 403 addLink: addLink,
357 addHost: addHost, 404 addHost: addHost,
...@@ -371,8 +418,21 @@ ...@@ -371,8 +418,21 @@
371 showTraffic: showTraffic 418 showTraffic: showTraffic
372 }; 419 };
373 420
421 + function addInstance(data) {
422 + evTrace(data);
423 + var inst = data.payload,
424 + id = inst.id;
425 + if (onosInstances[id]) {
426 + logicError('ONOS instance already added: ' + id);
427 + return;
428 + }
429 + onosInstances[id] = inst;
430 + onosOrder.push(inst);
431 + updateInstances();
432 + }
433 +
374 function addDevice(data) { 434 function addDevice(data) {
375 - fnTrace('addDevice', data.payload.id); 435 + evTrace(data);
376 var device = data.payload, 436 var device = data.payload,
377 nodeData = createDeviceNode(device); 437 nodeData = createDeviceNode(device);
378 network.nodes.push(nodeData); 438 network.nodes.push(nodeData);
...@@ -382,7 +442,7 @@ ...@@ -382,7 +442,7 @@
382 } 442 }
383 443
384 function addLink(data) { 444 function addLink(data) {
385 - fnTrace('addLink', data.payload.id); 445 + evTrace(data);
386 var link = data.payload, 446 var link = data.payload,
387 lnk = createLink(link); 447 lnk = createLink(link);
388 if (lnk) { 448 if (lnk) {
...@@ -394,7 +454,7 @@ ...@@ -394,7 +454,7 @@
394 } 454 }
395 455
396 function addHost(data) { 456 function addHost(data) {
397 - fnTrace('addHost', data.payload.id); 457 + evTrace(data);
398 var host = data.payload, 458 var host = data.payload,
399 node = createHostNode(host), 459 node = createHostNode(host),
400 lnk; 460 lnk;
...@@ -415,7 +475,7 @@ ...@@ -415,7 +475,7 @@
415 475
416 // TODO: fold updateX(...) methods into one base method; remove duplication 476 // TODO: fold updateX(...) methods into one base method; remove duplication
417 function updateDevice(data) { 477 function updateDevice(data) {
418 - fnTrace('updateDevice', data.payload.id); 478 + evTrace(data);
419 var device = data.payload, 479 var device = data.payload,
420 id = device.id, 480 id = device.id,
421 nodeData = network.lookup[id]; 481 nodeData = network.lookup[id];
...@@ -428,7 +488,7 @@ ...@@ -428,7 +488,7 @@
428 } 488 }
429 489
430 function updateLink(data) { 490 function updateLink(data) {
431 - fnTrace('updateLink', data.payload.id); 491 + evTrace(data);
432 var link = data.payload, 492 var link = data.payload,
433 id = link.id, 493 id = link.id,
434 linkData = network.lookup[id]; 494 linkData = network.lookup[id];
...@@ -441,7 +501,7 @@ ...@@ -441,7 +501,7 @@
441 } 501 }
442 502
443 function updateHost(data) { 503 function updateHost(data) {
444 - fnTrace('updateHost', data.payload.id); 504 + evTrace(data);
445 var host = data.payload, 505 var host = data.payload,
446 id = host.id, 506 id = host.id,
447 hostData = network.lookup[id]; 507 hostData = network.lookup[id];
...@@ -455,7 +515,7 @@ ...@@ -455,7 +515,7 @@
455 515
456 // TODO: fold removeX(...) methods into base method - remove dup code 516 // TODO: fold removeX(...) methods into base method - remove dup code
457 function removeLink(data) { 517 function removeLink(data) {
458 - fnTrace('removeLink', data.payload.id); 518 + evTrace(data);
459 var link = data.payload, 519 var link = data.payload,
460 id = link.id, 520 id = link.id,
461 linkData = network.lookup[id]; 521 linkData = network.lookup[id];
...@@ -467,7 +527,7 @@ ...@@ -467,7 +527,7 @@
467 } 527 }
468 528
469 function removeHost(data) { 529 function removeHost(data) {
470 - fnTrace('removeHost', data.payload.id); 530 + evTrace(data);
471 var host = data.payload, 531 var host = data.payload,
472 id = host.id, 532 id = host.id,
473 hostData = network.lookup[id]; 533 hostData = network.lookup[id];
...@@ -479,14 +539,14 @@ ...@@ -479,14 +539,14 @@
479 } 539 }
480 540
481 function showDetails(data) { 541 function showDetails(data) {
482 - fnTrace('showDetails', data.payload.id); 542 + evTrace(data);
483 populateDetails(data.payload); 543 populateDetails(data.payload);
484 detailPane.show(); 544 detailPane.show();
485 } 545 }
486 546
487 function showPath(data) { 547 function showPath(data) {
488 // TODO: review - making sure we are handling the payload correctly. 548 // TODO: review - making sure we are handling the payload correctly.
489 - fnTrace('showPath', data.payload.id); 549 + evTrace(data);
490 var links = data.payload.links, 550 var links = data.payload.links,
491 s = [ data.event + "\n" + links.length ]; 551 s = [ data.event + "\n" + links.length ];
492 links.forEach(function (d, i) { 552 links.forEach(function (d, i) {
...@@ -503,7 +563,7 @@ ...@@ -503,7 +563,7 @@
503 } 563 }
504 564
505 function showTraffic(data) { 565 function showTraffic(data) {
506 - fnTrace('showTraffic', data.payload.id); 566 + evTrace(data);
507 var paths = data.payload.paths; 567 var paths = data.payload.paths;
508 568
509 // Revert any links hilighted previously. 569 // Revert any links hilighted previously.
...@@ -602,6 +662,30 @@ ...@@ -602,6 +662,30 @@
602 662
603 663
604 // ============================== 664 // ==============================
665 + // onos instance panel functions
666 +
667 + function updateInstances() {
668 + var onoses = oiBox.el.selectAll('.onosInst')
669 + .data(onosOrder, function (d) { return d.id; });
670 +
671 + // operate on existing onoses if necessary
672 +
673 + var entering = onoses.enter()
674 + .append('div')
675 + .attr('class', 'onosInst')
676 + .classed('online', function (d) { return d.online; })
677 + .text(function (d) { return d.id; });
678 +
679 + // operate on existing + new onoses here
680 +
681 + // the departed...
682 + var exiting = onoses.exit()
683 + .transition()
684 + .style('opacity', 0)
685 + .remove();
686 + }
687 +
688 + // ==============================
605 // force layout modification functions 689 // force layout modification functions
606 690
607 function translate(x, y) { 691 function translate(x, y) {
...@@ -760,11 +844,9 @@ ...@@ -760,11 +844,9 @@
760 // Augment as needed... 844 // Augment as needed...
761 node.class = 'host'; 845 node.class = 'host';
762 if (!node.type) { 846 if (!node.type) {
763 - // TODO: perhaps type would be: {phone, tablet, laptop, endstation} ?
764 node.type = 'endstation'; 847 node.type = 'endstation';
765 } 848 }
766 node.svgClass = 'node host'; 849 node.svgClass = 'node host';
767 - // TODO: consider placing near its switch, if [x,y] not defined
768 positionNode(node); 850 positionNode(node);
769 851
770 // cache label array length 852 // cache label array length
...@@ -1352,21 +1434,21 @@ ...@@ -1352,21 +1434,21 @@
1352 function addSingleSelectActions() { 1434 function addSingleSelectActions() {
1353 detailPane.append('hr'); 1435 detailPane.append('hr');
1354 // always want to allow 'show traffic' 1436 // always want to allow 'show traffic'
1355 - addAction('Show Traffic', showTrafficAction); 1437 + addAction(detailPane, 'Show Traffic', showTrafficAction);
1356 } 1438 }
1357 1439
1358 function addMultiSelectActions() { 1440 function addMultiSelectActions() {
1359 detailPane.append('hr'); 1441 detailPane.append('hr');
1360 // always want to allow 'show traffic' 1442 // always want to allow 'show traffic'
1361 - addAction('Show Traffic', showTrafficAction); 1443 + addAction(detailPane, 'Show Traffic', showTrafficAction);
1362 // if exactly two hosts are selected, also want 'add host intent' 1444 // if exactly two hosts are selected, also want 'add host intent'
1363 if (nSel() === 2 && allSelectionsClass('host')) { 1445 if (nSel() === 2 && allSelectionsClass('host')) {
1364 - addAction('Add Host Intent', addIntentAction); 1446 + addAction(detailPane, 'Add Host Intent', addIntentAction);
1365 } 1447 }
1366 } 1448 }
1367 1449
1368 - function addAction(text, cb) { 1450 + function addAction(panel, text, cb) {
1369 - detailPane.append('div') 1451 + panel.append('div')
1370 .classed('actionBtn', true) 1452 .classed('actionBtn', true)
1371 .text(text) 1453 .text(text)
1372 .on('click', cb); 1454 .on('click', cb);
...@@ -1441,40 +1523,53 @@ ...@@ -1441,40 +1523,53 @@
1441 // TODO: toggle button (and other widgets in the masthead) should be provided 1523 // TODO: toggle button (and other widgets in the masthead) should be provided
1442 // by the framework; not generated by the view. 1524 // by the framework; not generated by the view.
1443 1525
1444 - var showTrafficOnHover, 1526 + var showInstances,
1445 - doPanZoom; 1527 + doPanZoom,
1528 + showTrafficOnHover;
1446 1529
1447 function addButtonBar(view) { 1530 function addButtonBar(view) {
1448 var bb = d3.select('#mast') 1531 var bb = d3.select('#mast')
1449 .append('span').classed('right', true).attr('id', 'bb'); 1532 .append('span').classed('right', true).attr('id', 'bb');
1450 1533
1451 - doPanZoom = bb.append('span') 1534 + function mkTogBtn(text, cb) {
1452 - .classed('btn', true) 1535 + return bb.append('span')
1453 - .text('Pan/Zoom') 1536 + .classed('btn', true)
1454 - .on('click', togglePanZoom); 1537 + .text(text)
1538 + .on('click', cb);
1539 + }
1455 1540
1456 - showTrafficOnHover = bb.append('span') 1541 + showInstances = mkTogBtn('Show Instances', toggleInst);
1457 - .classed('btn', true) 1542 + doPanZoom = mkTogBtn('Pan/Zoom', togglePanZoom);
1458 - .text('Show traffic on hover') 1543 + showTrafficOnHover = mkTogBtn('Show traffic on hover', toggleTrafficHover);
1459 - .on('click', toggleTrafficHover);
1460 } 1544 }
1461 1545
1462 - function toggleTrafficHover() { 1546 + function instShown() {
1463 - showTrafficOnHover.classed('active', !trafficHover()); 1547 + return showInstances.classed('active');
1464 } 1548 }
1465 - 1549 + function toggleInst() {
1466 - function trafficHover() { 1550 + showInstances.classed('active', !instShown());
1467 - return showTrafficOnHover.classed('active'); 1551 + if (instShown()) {
1552 + oiBox.show();
1553 + } else {
1554 + oiBox.hide();
1555 + }
1468 } 1556 }
1469 1557
1558 + function panZoom() {
1559 + return doPanZoom.classed('active');
1560 + }
1470 function togglePanZoom() { 1561 function togglePanZoom() {
1471 doPanZoom.classed('active', !panZoom()); 1562 doPanZoom.classed('active', !panZoom());
1472 } 1563 }
1473 1564
1474 - function panZoom() { 1565 + function trafficHover() {
1475 - return doPanZoom.classed('active'); 1566 + return showTrafficOnHover.classed('active');
1567 + }
1568 + function toggleTrafficHover() {
1569 + showTrafficOnHover.classed('active', !trafficHover());
1476 } 1570 }
1477 1571
1572 +
1478 // ============================== 1573 // ==============================
1479 // View life-cycle callbacks 1574 // View life-cycle callbacks
1480 1575
...@@ -1485,16 +1580,12 @@ ...@@ -1485,16 +1580,12 @@
1485 fpad = fcfg.pad, 1580 fpad = fcfg.pad,
1486 forceDim = [w - 2*fpad, h - 2*fpad]; 1581 forceDim = [w - 2*fpad, h - 2*fpad];
1487 1582
1488 - // TODO: set trace api
1489 - //trace = onos.exported.webSockTrace;
1490 -
1491 // NOTE: view.$div is a D3 selection of the view's div 1583 // NOTE: view.$div is a D3 selection of the view's div
1492 var viewBox = '0 0 ' + config.logicalSize + ' ' + config.logicalSize; 1584 var viewBox = '0 0 ' + config.logicalSize + ' ' + config.logicalSize;
1493 svg = view.$div.append('svg').attr('viewBox', viewBox); 1585 svg = view.$div.append('svg').attr('viewBox', viewBox);
1494 setSize(svg, view); 1586 setSize(svg, view);
1495 1587
1496 zoomPanContainer = svg.append('g').attr('id', 'zoomPanContainer'); 1588 zoomPanContainer = svg.append('g').attr('id', 'zoomPanContainer');
1497 -
1498 setupZoomPan(); 1589 setupZoomPan();
1499 1590
1500 // add blue glow filter to svg layer 1591 // add blue glow filter to svg layer
...@@ -1566,6 +1657,7 @@ ...@@ -1566,6 +1657,7 @@
1566 selectCb, atDragEnd, panZoom); 1657 selectCb, atDragEnd, panZoom);
1567 1658
1568 // create mask layer for when we lose connection to server. 1659 // create mask layer for when we lose connection to server.
1660 + // TODO: this should be part of the framework
1569 mask = view.$div.append('div').attr('id','topo-mask'); 1661 mask = view.$div.append('div').attr('id','topo-mask');
1570 para(mask, 'Oops!'); 1662 para(mask, 'Oops!');
1571 para(mask, 'Web-socket connection to server closed...'); 1663 para(mask, 'Web-socket connection to server closed...');
...@@ -1707,5 +1799,6 @@ ...@@ -1707,5 +1799,6 @@
1707 }); 1799 });
1708 1800
1709 detailPane = onos.ui.addFloatingPanel('topo-detail'); 1801 detailPane = onos.ui.addFloatingPanel('topo-detail');
1802 + oiBox = onos.ui.addFloatingPanel('topo-oibox', 'TL');
1710 1803
1711 }(ONOS)); 1804 }(ONOS));
......