Simon Hunt

GUI -- Added updateDevice event handling.

 - Display offline devices as grey.
 - Tracing web socket messages (for now, in console; in future, to trace view).
 - Captured sample events for use with test scenarios - both from and to the server.
 - Added description to scenario file.

Change-Id: I7825b32d63496ebea2ab5789519fb0c6af6c5257
Showing 36 changed files with 592 additions and 102 deletions
...@@ -44,6 +44,7 @@ ...@@ -44,6 +44,7 @@
44 <!-- This is where contributed stylesheets get INJECTED --> 44 <!-- This is where contributed stylesheets get INJECTED -->
45 <!-- TODO: replace with template marker and inject refs server-side --> 45 <!-- TODO: replace with template marker and inject refs server-side -->
46 <link rel="stylesheet" href="topo2.css"> 46 <link rel="stylesheet" href="topo2.css">
47 + <link rel="stylesheet" href="webSockTrace.css">
47 48
48 49
49 <!-- General library modules included here--> 50 <!-- General library modules included here-->
...@@ -97,6 +98,7 @@ ...@@ -97,6 +98,7 @@
97 98
98 <!-- Contributed (application) views injected here --> 99 <!-- Contributed (application) views injected here -->
99 <!-- TODO: replace with template marker and inject refs server-side --> 100 <!-- TODO: replace with template marker and inject refs server-side -->
101 + <script src="webSockTrace.js"></script>
100 <script src="topo2.js"></script> 102 <script src="topo2.js"></script>
101 103
102 <!-- finally, build the UI--> 104 <!-- finally, build the UI-->
......
1 +{
2 + "event": "addDevice",
3 + "payload": {
4 + "id": "of:0000000000000003",
5 + "type": "switch",
6 + "online": true,
7 + "labels": [
8 + "of:0000000000000003",
9 + "3",
10 + "",
11 + null
12 + ],
13 + "props": {}
14 + }
15 +}
1 +{
2 + "event": "addHost",
3 + "payload": {
4 + "id": "6A:40:24:F7:9C:2C/-1",
5 + "ingress": "6A:40:24:F7:9C:2C/-1/0-of:0000000000000003/2",
6 + "egress": "of:0000000000000003/2-6A:40:24:F7:9C:2C/-1/0",
7 + "cp": {
8 + "device": "of:0000000000000003",
9 + "port": 2
10 + },
11 + "labels": [
12 + "unknown",
13 + "6A:40:24:F7:9C:2C"
14 + ],
15 + "props": {}
16 + }
17 +}
1 +{
2 + "event": "addLink",
3 + "payload": {
4 + "id": "of:0000000000000007/4-of:0000000000000006/1",
5 + "type": "direct",
6 + "linkWidth": 2,
7 + "src": "of:0000000000000007",
8 + "srcPort": "4",
9 + "dst": "of:0000000000000006",
10 + "dstPort": "1"
11 + }
12 +}
1 +{
2 + "__comments__": [
3 + "fabricated event",
4 + "not sure if this is the actual format",
5 + "but we really only care about 'id' being in the payload"
6 + ],
7 + "event": "removeDevice",
8 + "payload": {
9 + "id": "of:0000000000000002",
10 + "type": "switch",
11 + "online": true,
12 + "labels": [
13 + "of:0000000000000002",
14 + "2",
15 + "",
16 + null
17 + ],
18 + "props": {}
19 + }
20 +}
1 +{
2 + "__comments__": [
3 + "fabricated event",
4 + "not sure if this is the actual format",
5 + "but we really only care about 'id' being in the payload"
6 + ],
7 + "event": "removeHost",
8 + "payload": {
9 + "id": "6A:40:24:F7:9C:2C/-1",
10 + "ingress": "6A:40:24:F7:9C:2C/-1/0-of:0000000000000003/2",
11 + "egress": "of:0000000000000003/2-6A:40:24:F7:9C:2C/-1/0",
12 + "cp": {
13 + "device": "of:0000000000000003",
14 + "port": 2
15 + },
16 + "labels": [
17 + "unknown",
18 + "6A:40:24:F7:9C:2C"
19 + ],
20 + "props": {}
21 + }
22 +}
1 +{
2 + "event": "removeLink",
3 + "payload": {
4 + "id": "of:0000000000000001/1-of:0000000000000002/4",
5 + "type": "direct",
6 + "linkWidth": 2,
7 + "src": "of:0000000000000001",
8 + "srcPort": "1",
9 + "dst": "of:0000000000000002",
10 + "dstPort": "4"
11 + }
12 +}
1 +{
2 + "event": "showPath",
3 + "sid": 15,
4 + "payload": {
5 + "links": [
6 + "62:4F:65:BF:FF:B3/-1/0-of:000000000000000b/1",
7 + "of:000000000000000b/4-of:000000000000000a/1",
8 + "of:000000000000000a/4-of:0000000000000001/3",
9 + "of:0000000000000001/1-of:0000000000000002/4",
10 + "of:0000000000000002/1-of:0000000000000003/4",
11 + "of:0000000000000003/1-CA:4B:EE:A4:B0:33/-1/0"
12 + ],
13 + "intentId": "0x52a914f9"
14 + }
15 +}
1 +{
2 + "event": "updateDevice",
3 + "payload": {
4 + "id": "of:0000000000000002",
5 + "type": "switch",
6 + "online": true,
7 + "labels": [
8 + "of:0000000000000002",
9 + "2",
10 + "",
11 + null
12 + ],
13 + "props": {}
14 + }
15 +}
1 +{
2 + "event": "updateDevice",
3 + "payload": {
4 + "id": "of:0000000000000002",
5 + "type": "switch",
6 + "online": false,
7 + "labels": [
8 + "of:0000000000000002",
9 + "2",
10 + "",
11 + null
12 + ],
13 + "props": {}
14 + }
15 +}
1 +{
2 + "event": "updateHost",
3 + "payload": {
4 + "id": "AA:C2:74:3F:B8:06/-1",
5 + "ingress": "AA:C2:74:3F:B8:06/-1/0-of:0000000000000005/3",
6 + "egress": "of:0000000000000005/3-AA:C2:74:3F:B8:06/-1/0",
7 + "cp": {
8 + "device": "of:0000000000000005",
9 + "port": 3
10 + },
11 + "labels": [
12 + "10.0.0.9",
13 + "AA:C2:74:3F:B8:06"
14 + ],
15 + "props":{}
16 + }
17 +}
1 +{
2 + "event": "updateLink",
3 + "payload": {
4 + "id": "of:0000000000000002/4-of:0000000000000001/1",
5 + "type": "direct",
6 + "linkWidth": 2,
7 + "src": "of:0000000000000002",
8 + "srcPort": "4",
9 + "dst": "of:0000000000000001",
10 + "dstPort": "1"
11 + }
12 +}
1 +{
2 + "event": "requestPath",
3 + "sid": 15,
4 + "payload": {
5 + "one": "62:4F:65:BF:FF:B3/-1",
6 + "two": "CA:4B:EE:A4:B0:33/-1"
7 + }
8 +}
1 +{
2 + "event": "updateMeta",
3 + "sid": 11,
4 + "payload": {
5 + "id": "62:4F:65:BF:FF:B3/-1",
6 + "class": "host",
7 + "x": 197,
8 + "y": 177
9 + }
10 +}
1 { 1 {
2 - "event": "addLink", 2 + "event": "updateDevice",
3 "payload": { 3 "payload": {
4 - "id": "of:0000ffffffff0003/21-of:0000ffffffff0008/20", 4 + "id": "of:0000ffffffff0008",
5 - "type": "direct", 5 + "type": "switch",
6 - "linkWidth": 2, 6 + "online": true,
7 - "src": "of:0000ffffffff0003", 7 + "labels": [
8 - "srcPort": "21", 8 + "0000ffffffff0008",
9 - "dst": "of:0000ffffffff0008", 9 + "FF:FF:FF:FF:00:08",
10 - "dstPort": "20", 10 + "sw-8-yo",
11 - "props" : { 11 + ""
12 - "BW": "70 G" 12 + ],
13 + "metaUi": {
14 + "x": 400,
15 + "y": 280
13 } 16 }
14 } 17 }
15 } 18 }
......
1 { 1 {
2 - "event": "addHost", 2 + "event": "updateDevice",
3 "payload": { 3 "payload": {
4 - "id": "0E:2A:69:30:13:86/-1", 4 + "id": "of:0000ffffffff0003",
5 - "ingress": "0E:2A:69:30:13:86/-1/0-of:0000ffffffff0003/2", 5 + "type": "switch",
6 - "egress": "of:0000ffffffff0003/2-0E:2A:69:30:13:86/-1/0", 6 + "online": true,
7 - "cp": {
8 - "device": "of:0000ffffffff0003",
9 - "port": 2
10 - },
11 "labels": [ 7 "labels": [
12 - "unknown", 8 + "0000ffffffff0003",
13 - "0E:2A:69:30:13:86" 9 + "FF:FF:FF:FF:00:03",
10 + "sw-3-yo",
11 + ""
14 ], 12 ],
15 - "props": {} 13 + "metaUi": {
14 + "x": 800,
15 + "y": 280
16 + }
16 } 17 }
17 } 18 }
......
1 { 1 {
2 - "event": "addHost", 2 + "event": "addLink",
3 "payload": { 3 "payload": {
4 - "id": "A6:96:E5:03:52:5F/-1", 4 + "id": "of:0000ffffffff0003/21-of:0000ffffffff0008/20",
5 - "ingress": "A6:96:E5:03:52:5F/-1/0-of:0000ffffffff0008/1", 5 + "type": "direct",
6 - "egress": "of:0000ffffffff0008/1-A6:96:E5:03:52:5F/-1/0", 6 + "linkWidth": 2,
7 - "cp": { 7 + "src": "of:0000ffffffff0003",
8 - "device": "of:0000ffffffff0008", 8 + "srcPort": "21",
9 - "port": 1 9 + "dst": "of:0000ffffffff0008",
10 - }, 10 + "dstPort": "20",
11 - "labels": [ 11 + "props" : {
12 - "unknown", 12 + "BW": "70 G"
13 - "A6:96:E5:03:52:5F" 13 + }
14 - ],
15 - "props": {}
16 } 14 }
17 } 15 }
......
1 { 1 {
2 - "event": "updateHost", 2 + "event": "addHost",
3 "payload": { 3 "payload": {
4 "id": "0E:2A:69:30:13:86/-1", 4 "id": "0E:2A:69:30:13:86/-1",
5 "ingress": "0E:2A:69:30:13:86/-1/0-of:0000ffffffff0003/2", 5 "ingress": "0E:2A:69:30:13:86/-1/0-of:0000ffffffff0003/2",
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
9 "port": 2 9 "port": 2
10 }, 10 },
11 "labels": [ 11 "labels": [
12 - "10.0.0.13", 12 + "unknown",
13 "0E:2A:69:30:13:86" 13 "0E:2A:69:30:13:86"
14 ], 14 ],
15 "props": {} 15 "props": {}
......
1 { 1 {
2 - "event": "updateHost", 2 + "event": "addHost",
3 "payload": { 3 "payload": {
4 "id": "A6:96:E5:03:52:5F/-1", 4 "id": "A6:96:E5:03:52:5F/-1",
5 "ingress": "A6:96:E5:03:52:5F/-1/0-of:0000ffffffff0008/1", 5 "ingress": "A6:96:E5:03:52:5F/-1/0-of:0000ffffffff0008/1",
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
9 "port": 1 9 "port": 1
10 }, 10 },
11 "labels": [ 11 "labels": [
12 - "10.0.0.17", 12 + "unknown",
13 "A6:96:E5:03:52:5F" 13 "A6:96:E5:03:52:5F"
14 ], 14 ],
15 "props": {} 15 "props": {}
......
1 +{
2 + "event": "updateHost",
3 + "payload": {
4 + "id": "0E:2A:69:30:13:86/-1",
5 + "ingress": "0E:2A:69:30:13:86/-1/0-of:0000ffffffff0003/2",
6 + "egress": "of:0000ffffffff0003/2-0E:2A:69:30:13:86/-1/0",
7 + "cp": {
8 + "device": "of:0000ffffffff0003",
9 + "port": 2
10 + },
11 + "labels": [
12 + "10.0.0.13",
13 + "0E:2A:69:30:13:86"
14 + ],
15 + "props": {}
16 + }
17 +}
1 +{
2 + "event": "updateHost",
3 + "payload": {
4 + "id": "A6:96:E5:03:52:5F/-1",
5 + "ingress": "A6:96:E5:03:52:5F/-1/0-of:0000ffffffff0008/1",
6 + "egress": "of:0000ffffffff0008/1-A6:96:E5:03:52:5F/-1/0",
7 + "cp": {
8 + "device": "of:0000ffffffff0008",
9 + "port": 1
10 + },
11 + "labels": [
12 + "10.0.0.17",
13 + "A6:96:E5:03:52:5F"
14 + ],
15 + "props": {}
16 + }
17 +}
...@@ -6,5 +6,17 @@ ...@@ -6,5 +6,17 @@
6 "title": "Simple Startup Scenario", 6 "title": "Simple Startup Scenario",
7 "params": { 7 "params": {
8 "lastAuto": 0 8 "lastAuto": 0
9 - } 9 + },
10 + "description": [
11 + "1. add device [8] (offline)",
12 + "2. add device [3] (offline)",
13 + "3. update device [8] (online)",
14 + "4. update device [3] (online)",
15 + "5. add link [3] --> [8]",
16 + "6. add host (to [3])",
17 + "7. add host (to [8])",
18 + "8. update host[3] (IP now 10.0.0.13)",
19 + "9. update host[8] (IP now 10.0.0.17)",
20 + ""
21 + ]
10 } 22 }
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -33,7 +33,8 @@ ...@@ -33,7 +33,8 @@
33 var uiApi, 33 var uiApi,
34 viewApi, 34 viewApi,
35 navApi, 35 navApi,
36 - libApi; 36 + libApi,
37 + exported = {};
37 38
38 var defaultOptions = { 39 var defaultOptions = {
39 trace: false, 40 trace: false,
...@@ -658,6 +659,7 @@ ...@@ -658,6 +659,7 @@
658 return makeUid(this, id); 659 return makeUid(this, id);
659 }, 660 },
660 661
662 + // TODO : add exportApi and importApi methods
661 // TODO : implement custom dialogs 663 // TODO : implement custom dialogs
662 664
663 // Consider enhancing alert mechanism to handle multiples 665 // Consider enhancing alert mechanism to handle multiples
...@@ -737,6 +739,7 @@ ...@@ -737,6 +739,7 @@
737 // .......................................................... 739 // ..........................................................
738 // View API 740 // View API
739 741
742 + // TODO: deprecated
740 viewApi = { 743 viewApi = {
741 /** @api view empty( ) 744 /** @api view empty( )
742 * Empties the current view. 745 * Empties the current view.
...@@ -802,7 +805,8 @@ ...@@ -802,7 +805,8 @@
802 lib: libApi, 805 lib: libApi,
803 //view: viewApi, 806 //view: viewApi,
804 nav: navApi, 807 nav: navApi,
805 - buildUi: buildOnosUi 808 + buildUi: buildOnosUi,
809 + exported: exported
806 }; 810 };
807 }; 811 };
808 812
......
...@@ -41,11 +41,16 @@ ...@@ -41,11 +41,16 @@
41 stroke: #ccc; 41 stroke: #ccc;
42 } 42 }
43 43
44 -#topo svg .node.device.switch { 44 +/* note: device is offline without the 'online' class */
45 +#topo svg .node.device {
46 + fill: #777;
47 +}
48 +
49 +#topo svg .node.device.switch.online {
45 fill: #17f; 50 fill: #17f;
46 } 51 }
47 52
48 -#topo svg .node.device.roadm { 53 +#topo svg .node.device.roadm.online {
49 fill: #03c; 54 fill: #03c;
50 } 55 }
51 56
...@@ -53,12 +58,17 @@ ...@@ -53,12 +58,17 @@
53 fill: #846; 58 fill: #846;
54 } 59 }
55 60
61 +/* note: device is offline without the 'online' class */
56 #topo svg .node.device text { 62 #topo svg .node.device text {
57 - fill: white; 63 + fill: #aaa;
58 font: 10pt sans-serif; 64 font: 10pt sans-serif;
59 pointer-events: none; 65 pointer-events: none;
60 } 66 }
61 67
68 +#topo svg .node.device.online text {
69 + fill: white;
70 +}
71 +
62 #topo svg .node.host text { 72 #topo svg .node.host text {
63 fill: #846; 73 fill: #846;
64 font: 9pt sans-serif; 74 font: 9pt sans-serif;
......
...@@ -24,7 +24,8 @@ ...@@ -24,7 +24,8 @@
24 'use strict'; 24 'use strict';
25 25
26 // shorter names for library APIs 26 // shorter names for library APIs
27 - var d3u = onos.lib.d3util; 27 + var d3u = onos.lib.d3util,
28 + trace;
28 29
29 // configuration data 30 // configuration data
30 var config = { 31 var config = {
...@@ -241,8 +242,8 @@ ...@@ -241,8 +242,8 @@
241 } 242 }
242 243
243 function handleUiEvent(data) { 244 function handleUiEvent(data) {
244 - testDebug('handleUiEvent(): ' + data.event); 245 + scenario.view.alert('UI Tx: ' + data.event + '\n\n' +
245 - // TODO: 246 + JSON.stringify(data));
246 } 247 }
247 248
248 function injectStartupEvents(view) { 249 function injectStartupEvents(view) {
...@@ -259,32 +260,44 @@ ...@@ -259,32 +260,44 @@
259 bgImg.style('visibility', (vis === 'hidden') ? 'visible' : 'hidden'); 260 bgImg.style('visibility', (vis === 'hidden') ? 'visible' : 'hidden');
260 } 261 }
261 262
262 - function cycleLabels() { 263 + function updateDeviceLabel(d) {
263 - deviceLabelIndex = (deviceLabelIndex === network.deviceLabelCount - 1) ? 0 : deviceLabelIndex + 1; 264 + var label = niceLabel(deviceLabel(d)),
265 + node = d.el,
266 + box;
264 267
265 - network.nodes.forEach(function (d) { 268 + node.select('text')
266 - if (d.class !== 'device') { return; } 269 + .text(label)
270 + .style('opacity', 0)
271 + .transition()
272 + .style('opacity', 1);
267 273
268 - var label = niceLabel(deviceLabel(d)), 274 + box = adjustRectToFitText(node);
269 - node = d.el,
270 - box;
271 275
272 - node.select('text') 276 + node.select('rect')
273 - .text(label) 277 + .transition()
274 - .style('opacity', 0) 278 + .attr(box);
275 - .transition()
276 - .style('opacity', 1);
277 279
278 - box = adjustRectToFitText(node); 280 + node.select('image')
281 + .transition()
282 + .attr('x', box.x + config.icons.xoff)
283 + .attr('y', box.y + config.icons.yoff);
284 + }
279 285
280 - node.select('rect') 286 + function updateHostLabel(d) {
281 - .transition() 287 + var label = hostLabel(d),
282 - .attr(box); 288 + host = d.el;
289 +
290 + host.select('text').text(label);
291 + }
292 +
293 + function cycleLabels() {
294 + deviceLabelIndex = (deviceLabelIndex === network.deviceLabelCount - 1)
295 + ? 0 : deviceLabelIndex + 1;
283 296
284 - node.select('image') 297 + network.nodes.forEach(function (d) {
285 - .transition() 298 + if (d.class === 'device') {
286 - .attr('x', box.x + config.icons.xoff) 299 + updateDeviceLabel(d);
287 - .attr('y', box.y + config.icons.yoff); 300 + }
288 }); 301 });
289 } 302 }
290 303
...@@ -348,15 +361,20 @@ ...@@ -348,15 +361,20 @@
348 // ============================== 361 // ==============================
349 // Event handlers for server-pushed events 362 // Event handlers for server-pushed events
350 363
364 + function logicError(msg) {
365 + // TODO, report logic error to server, via websock, so it can be logged
366 + network.view.alert('Logic Error:\n\n' + msg);
367 + }
368 +
351 var eventDispatch = { 369 var eventDispatch = {
352 addDevice: addDevice, 370 addDevice: addDevice,
353 - updateDevice: stillToImplement,
354 - removeDevice: stillToImplement,
355 addLink: addLink, 371 addLink: addLink,
356 - updateLink: stillToImplement,
357 - removeLink: stillToImplement,
358 addHost: addHost, 372 addHost: addHost,
373 + updateDevice: updateDevice,
374 + updateLink: stillToImplement,
359 updateHost: updateHost, 375 updateHost: updateHost,
376 + removeDevice: stillToImplement,
377 + removeLink: stillToImplement,
360 removeHost: stillToImplement, 378 removeHost: stillToImplement,
361 showPath: showPath 379 showPath: showPath
362 }; 380 };
...@@ -364,8 +382,6 @@ ...@@ -364,8 +382,6 @@
364 function addDevice(data) { 382 function addDevice(data) {
365 var device = data.payload, 383 var device = data.payload,
366 nodeData = createDeviceNode(device); 384 nodeData = createDeviceNode(device);
367 - note('addDevice', device.id);
368 -
369 network.nodes.push(nodeData); 385 network.nodes.push(nodeData);
370 network.lookup[nodeData.id] = nodeData; 386 network.lookup[nodeData.id] = nodeData;
371 updateNodes(); 387 updateNodes();
...@@ -375,10 +391,7 @@ ...@@ -375,10 +391,7 @@
375 function addLink(data) { 391 function addLink(data) {
376 var link = data.payload, 392 var link = data.payload,
377 lnk = createLink(link); 393 lnk = createLink(link);
378 -
379 if (lnk) { 394 if (lnk) {
380 - note('addLink', link.id);
381 -
382 network.links.push(lnk); 395 network.links.push(lnk);
383 network.lookup[lnk.id] = lnk; 396 network.lookup[lnk.id] = lnk;
384 updateLinks(); 397 updateLinks();
...@@ -390,8 +403,6 @@ ...@@ -390,8 +403,6 @@
390 var host = data.payload, 403 var host = data.payload,
391 node = createHostNode(host), 404 node = createHostNode(host),
392 lnk; 405 lnk;
393 - note('addHost', node.id);
394 -
395 network.nodes.push(node); 406 network.nodes.push(node);
396 network.lookup[host.id] = node; 407 network.lookup[host.id] = node;
397 updateNodes(); 408 updateNodes();
...@@ -406,13 +417,28 @@ ...@@ -406,13 +417,28 @@
406 network.force.start(); 417 network.force.start();
407 } 418 }
408 419
420 + function updateDevice(data) {
421 + var device = data.payload,
422 + id = device.id,
423 + nodeData = network.lookup[id];
424 + if (nodeData) {
425 + $.extend(nodeData, device);
426 + updateDeviceState(nodeData);
427 + } else {
428 + logicError('updateDevice lookup fail. ID = "' + id + '"');
429 + }
430 + }
431 +
409 function updateHost(data) { 432 function updateHost(data) {
410 var host = data.payload, 433 var host = data.payload,
411 - hostData = network.lookup[host.id]; 434 + id = host.id,
412 - note('updateHost', host.id); 435 + hostData = network.lookup[id];
413 - 436 + if (hostData) {
414 - $.extend(hostData, host); 437 + $.extend(hostData, host);
415 - updateNodes(); 438 + updateHostState(hostData);
439 + } else {
440 + logicError('updateHost lookup fail. ID = "' + id + '"');
441 + }
416 } 442 }
417 443
418 function showPath(data) { 444 function showPath(data) {
...@@ -466,9 +492,8 @@ ...@@ -466,9 +492,8 @@
466 lnk; 492 lnk;
467 493
468 if (!dstNode) { 494 if (!dstNode) {
469 - // TODO: send warning message back to server on websocket 495 + logicError('switch not on map for link\n\n' +
470 - network.view.alert('switch not on map for link\n\n' + 496 + 'src = ' + src + '\ndst = ' + dst);
471 - 'src = ' + src + '\ndst = ' + dst);
472 return null; 497 return null;
473 } 498 }
474 499
...@@ -500,9 +525,8 @@ ...@@ -500,9 +525,8 @@
500 dstNode = network.lookup[dst]; 525 dstNode = network.lookup[dst];
501 526
502 if (!(srcNode && dstNode)) { 527 if (!(srcNode && dstNode)) {
503 - // TODO: send warning message back to server on websocket 528 + logicError('nodes not on map for link\n\n' +
504 - network.view.alert('nodes not on map for link\n\n' + 529 + 'src = ' + src + '\ndst = ' + dst);
505 - 'src = ' + src + '\ndst = ' + dst);
506 return null; 530 return null;
507 } 531 }
508 532
...@@ -578,11 +602,12 @@ ...@@ -578,11 +602,12 @@
578 function createDeviceNode(device) { 602 function createDeviceNode(device) {
579 // start with the object as is 603 // start with the object as is
580 var node = device, 604 var node = device,
581 - type = device.type; 605 + type = device.type,
606 + svgCls = type ? 'node device ' + type : 'node device';
582 607
583 // Augment as needed... 608 // Augment as needed...
584 node.class = 'device'; 609 node.class = 'device';
585 - node.svgClass = type ? 'node device ' + type : 'node device'; 610 + node.svgClass = device.online ? svgCls + ' online' : svgCls;
586 positionNode(node); 611 positionNode(node);
587 612
588 // cache label array length 613 // cache label array length
...@@ -669,15 +694,24 @@ ...@@ -669,15 +694,24 @@
669 return (label && label.trim()) ? label : '.'; 694 return (label && label.trim()) ? label : '.';
670 } 695 }
671 696
697 + function updateDeviceState(nodeData) {
698 + nodeData.el.classed('online', nodeData.online);
699 + updateDeviceLabel(nodeData);
700 + // TODO: review what else might need to be updated
701 + }
702 +
703 + function updateHostState(hostData) {
704 + updateHostLabel(hostData);
705 + // TODO: review what else might need to be updated
706 + }
707 +
708 +
672 function updateNodes() { 709 function updateNodes() {
673 node = nodeG.selectAll('.node') 710 node = nodeG.selectAll('.node')
674 .data(network.nodes, function (d) { return d.id; }); 711 .data(network.nodes, function (d) { return d.id; });
675 712
676 // operate on existing nodes, if necessary 713 // operate on existing nodes, if necessary
677 // update host labels 714 // update host labels
678 - node.filter('.host').select('text')
679 - .text(hostLabel);
680 -
681 //node .foo() .bar() ... 715 //node .foo() .bar() ...
682 716
683 // operate on entering nodes: 717 // operate on entering nodes:
...@@ -828,7 +862,7 @@ ...@@ -828,7 +862,7 @@
828 862
829 webSock.ws.onmessage = function(m) { 863 webSock.ws.onmessage = function(m) {
830 if (m.data) { 864 if (m.data) {
831 - console.log(m.data); 865 + wsTraceRx(m.data);
832 handleServerEvent(JSON.parse(m.data)); 866 handleServerEvent(JSON.parse(m.data));
833 } 867 }
834 }; 868 };
...@@ -858,11 +892,28 @@ ...@@ -858,11 +892,28 @@
858 892
859 function sendMessage(evType, payload) { 893 function sendMessage(evType, payload) {
860 var toSend = { 894 var toSend = {
861 - event: evType, 895 + event: evType,
862 - sid: ++sid, 896 + sid: ++sid,
863 - payload: payload 897 + payload: payload
864 - }; 898 + },
865 - webSock.send(JSON.stringify(toSend)); 899 + asText = JSON.stringify(toSend);
900 + wsTraceTx(asText);
901 + webSock.send(asText);
902 + }
903 +
904 + function wsTraceTx(msg) {
905 + wsTrace('tx', msg);
906 + }
907 + function wsTraceRx(msg) {
908 + wsTrace('rx', msg);
909 + }
910 + function wsTrace(rxtx, msg) {
911 +
912 + console.log('[' + rxtx + '] ' + msg);
913 + // TODO: integrate with trace view
914 + //if (trace) {
915 + // trace.output(rxtx, msg);
916 + //}
866 } 917 }
867 918
868 919
...@@ -944,12 +995,19 @@ ...@@ -944,12 +995,19 @@
944 sc.evNumber = 0; 995 sc.evNumber = 0;
945 996
946 d3.json(urlSc, function(err, data) { 997 d3.json(urlSc, function(err, data) {
947 - var p = data && data.params || {}; 998 + var p = data && data.params || {},
999 + desc = data && data.description || null,
1000 + intro;
1001 +
948 if (err) { 1002 if (err) {
949 view.alert('No scenario found:\n\n' + urlSc + '\n\n' + err); 1003 view.alert('No scenario found:\n\n' + urlSc + '\n\n' + err);
950 } else { 1004 } else {
951 sc.params = p; 1005 sc.params = p;
952 - view.alert("Scenario loaded: " + ctx + '\n\n' + data.title); 1006 + intro = "Scenario loaded: " + ctx + '\n\n' + data.title;
1007 + if (desc) {
1008 + intro += '\n\n ' + desc.join('\n ');
1009 + }
1010 + view.alert(intro);
953 } 1011 }
954 }); 1012 });
955 1013
...@@ -967,6 +1025,9 @@ ...@@ -967,6 +1025,9 @@
967 fpad = fcfg.pad, 1025 fpad = fcfg.pad,
968 forceDim = [w - 2*fpad, h - 2*fpad]; 1026 forceDim = [w - 2*fpad, h - 2*fpad];
969 1027
1028 + // TODO: set trace api
1029 + //trace = onos.exported.webSockTrace;
1030 +
970 // NOTE: view.$div is a D3 selection of the view's div 1031 // NOTE: view.$div is a D3 selection of the view's div
971 svg = view.$div.append('svg'); 1032 svg = view.$div.append('svg');
972 setSize(svg, view); 1033 setSize(svg, view);
......
1 +/*
2 + * Copyright 2014 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 -- Web Socket Trace -- CSS file
19 +
20 + @author Simon Hunt
21 + */
22 +
23 +#webSockTrace .toolbar {
24 + height: 36px;
25 + padding: 4px;
26 + vertical-align: baseline;
27 + font-size: 12pt;
28 + margin-top: 6px;
29 +}
30 +
31 +/* theme-related */
32 +#webSockTrace .toolbar {
33 + background-color: #448;
34 + color: #fff;
35 +}
36 +
37 +#webSockTrace .output {
38 + overflow-y: scroll;
39 +}
40 +
41 +/* theme-related */
42 +#webSockTrace .output {
43 + background-color: #eef;
44 + color: #226;
45 +}
46 +
47 +#webSockTrace .output p {
48 + margin: 2px 8px;
49 + font-size: 10pt;
50 + padding-left: 6px;
51 +}
52 +
53 +/* theme-related */
54 +#webSockTrace .output p.tx {
55 + color: magenta;
56 +}
57 +#webSockTrace .output p.rx {
58 + color: blue;
59 +}
60 +
61 +
62 +#webSockTrace .output p.subtitle {
63 + margin: 6px 8px;
64 + padding-left: 2px;
65 + font-size: 12pt;
66 + font-weight: bold;
67 + font-style: italic;
68 +}
69 +
70 +/* theme-related */
71 +#webSockTrace .output p.subtitle {
72 + color: #626;
73 +}
1 +/*
2 + * Copyright 2014 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 + View that traces messages across the websocket.
19 +
20 + @author Simon Hunt
21 + */
22 +
23 +(function (onos) {
24 + 'use strict';
25 +
26 + var v,
27 + $d,
28 + tb,
29 + out,
30 + which = 'tx',
31 + keyDispatch = {
32 + space: function () {
33 + output(which, "Simon woz 'ere... " + which);
34 + which = (which === 'tx') ? 'rx' : 'tx';
35 + }
36 + };
37 +
38 +
39 + function addHeader() {
40 + tb = $d.append('div')
41 + .attr('class', 'toolbar');
42 + tb.append('span').text('Web Socket Trace');
43 + }
44 +
45 + function addOutput() {
46 + out = $d.append('div')
47 + .attr('class', 'output');
48 + }
49 +
50 + function subtitle(msg) {
51 + out.append('p').attr('class', 'subtitle').text(msg);
52 + }
53 +
54 + function output(rxtx, msg) {
55 + out.append('p').attr('class', rxtx).text(msg);
56 + }
57 +
58 + // invoked only the first time the view is loaded
59 + function preload(view, ctx, flags) {
60 + // NOTE: view.$div is a D3 selection of the view's div
61 + v = view;
62 + $d = v.$div;
63 + addHeader();
64 + addOutput();
65 +
66 +
67 + // hack for now, to allow topo access to our API
68 + // TODO: add 'exportApi' and 'importApi' to views.
69 + onos.exported.webSockTrace = {
70 + subtitle: subtitle,
71 + output: output
72 + };
73 + }
74 +
75 + // invoked just prior to loading the view
76 + function reset(view, ctx, flags) {
77 +
78 + }
79 +
80 + // invoked when the view is loaded
81 + function load(view, ctx, flags) {
82 + resize(view, ctx, flags);
83 + view.setKeys(keyDispatch);
84 + subtitle('Waiting for messages...');
85 + }
86 +
87 + // invoked when the view is resized
88 + function resize(view, ctx, flags) {
89 + var h = view.height();
90 + out.style('height', h + 'px');
91 +
92 + }
93 +
94 + // == register the view here, with links to lifecycle callbacks
95 +
96 + onos.ui.addView('webSockTrace', {
97 + preload: preload,
98 + load: load,
99 + resize: resize
100 + });
101 +
102 +}(ONOS));