Simon Hunt

Another step forward with the UI.

- Added hosts to test data.
- Messed with colors and icons.
- Added more internal configuration values.
...@@ -10,17 +10,33 @@ ...@@ -10,17 +10,33 @@
10 var api = onos.api; 10 var api = onos.api;
11 11
12 var config = { 12 var config = {
13 + options: {
13 layering: false, 14 layering: false,
15 + collisionPrevention: true
16 + },
14 jsonUrl: 'network.json', 17 jsonUrl: 'network.json',
15 iconUrl: { 18 iconUrl: {
16 - pkt: 'pkt.png', 19 + logo: 'img/onos-logo.tiff',
17 - opt: 'opt.png' 20 + device: 'img/device.png',
21 + host: 'img/host.png',
22 + pkt: 'img/pkt.png',
23 + opt: 'img/opt.png'
18 }, 24 },
19 mastHeight: 32, 25 mastHeight: 32,
20 force: { 26 force: {
21 - linkDistance: 240, 27 + note: 'node.class or link.class is used to differentiate',
22 - linkStrength: 0.8, 28 + linkDistance: {
23 - charge: -400, 29 + infra: 240,
30 + host: 100
31 + },
32 + linkStrength: {
33 + infra: 1.0,
34 + host: 0.4
35 + },
36 + charge: {
37 + device: -800,
38 + host: -400
39 + },
24 ticksWithoutCollisions: 50, 40 ticksWithoutCollisions: 50,
25 marginLR: 20, 41 marginLR: 20,
26 marginTB: 20, 42 marginTB: 20,
...@@ -37,13 +53,22 @@ ...@@ -37,13 +53,22 @@
37 marginLR: 3, 53 marginLR: 3,
38 marginTB: 2 54 marginTB: 2
39 }, 55 },
56 + icons: {
57 + w: 32,
58 + h: 32,
59 + xoff: -12,
60 + yoff: -10
61 + },
40 constraints: { 62 constraints: {
41 ypos: { 63 ypos: {
42 - pkt: 0.3, 64 + host: 0.15,
43 - opt: 0.7 65 + switch: 0.3,
44 - } 66 + roadm: 0.7
45 } 67 }
46 }, 68 },
69 + hostLinkWidth: 1.0,
70 + mouseOutTimerDelayMs: 120
71 + },
47 view = {}, 72 view = {},
48 network = {}, 73 network = {},
49 selected = {}, 74 selected = {},
...@@ -104,14 +129,23 @@ ...@@ -104,14 +129,23 @@
104 var nw = network.forceWidth, 129 var nw = network.forceWidth,
105 nh = network.forceHeight; 130 nh = network.forceHeight;
106 131
107 - network.data.nodes.forEach(function(n) { 132 + function yPosConstraintForNode(n) {
133 + return config.constraints.ypos[n.type || 'host'];
134 + }
135 +
136 + // Note that both 'devices' and 'hosts' get mapped into the nodes array
137 +
138 + // first, the devices...
139 + network.data.devices.forEach(function(n) {
108 var ypc = yPosConstraintForNode(n), 140 var ypc = yPosConstraintForNode(n),
109 ix = Math.random() * 0.6 * nw + 0.2 * nw, 141 ix = Math.random() * 0.6 * nw + 0.2 * nw,
110 iy = ypc * nh, 142 iy = ypc * nh,
111 node = { 143 node = {
112 id: n.id, 144 id: n.id,
145 + labels: n.labels,
146 + class: 'device',
147 + icon: 'device',
113 type: n.type, 148 type: n.type,
114 - status: n.status,
115 x: ix, 149 x: ix,
116 y: iy, 150 y: iy,
117 constraint: { 151 constraint: {
...@@ -123,21 +157,61 @@ ...@@ -123,21 +157,61 @@
123 network.nodes.push(node); 157 network.nodes.push(node);
124 }); 158 });
125 159
126 - function yPosConstraintForNode(n) { 160 + // then, the hosts...
127 - return config.constraints.ypos[n.type] || 0.5; 161 + network.data.hosts.forEach(function(n) {
162 + var ypc = yPosConstraintForNode(n),
163 + ix = Math.random() * 0.6 * nw + 0.2 * nw,
164 + iy = ypc * nh,
165 + node = {
166 + id: n.id,
167 + labels: n.labels,
168 + class: 'host',
169 + icon: 'host',
170 + type: n.type,
171 + x: ix,
172 + y: iy,
173 + constraint: {
174 + weight: 0.7,
175 + y: iy
128 } 176 }
177 + };
178 + network.lookup[n.id] = node;
179 + network.nodes.push(node);
180 + });
129 181
130 182
183 + // now, process the explicit links...
131 network.data.links.forEach(function(n) { 184 network.data.links.forEach(function(n) {
132 var src = network.lookup[n.src], 185 var src = network.lookup[n.src],
133 dst = network.lookup[n.dst], 186 dst = network.lookup[n.dst],
134 id = src.id + "~" + dst.id; 187 id = src.id + "~" + dst.id;
135 188
136 var link = { 189 var link = {
190 + class: 'infra',
191 + id: id,
192 + type: n.type,
193 + width: n.linkWidth,
194 + source: src,
195 + target: dst,
196 + strength: config.force.linkStrength.infra
197 + };
198 + network.links.push(link);
199 + });
200 +
201 + // finally, infer host links...
202 + network.data.hosts.forEach(function(n) {
203 + var src = network.lookup[n.id],
204 + dst = network.lookup[n.cp.device],
205 + id = src.id + "~" + dst.id;
206 +
207 + var link = {
208 + class: 'host',
137 id: id, 209 id: id,
210 + type: 'hostLink',
211 + width: config.hostLinkWidth,
138 source: src, 212 source: src,
139 target: dst, 213 target: dst,
140 - strength: config.force.linkStrength 214 + strength: config.force.linkStrength.host
141 }; 215 };
142 network.links.push(link); 216 network.links.push(link);
143 }); 217 });
...@@ -145,13 +219,15 @@ ...@@ -145,13 +219,15 @@
145 219
146 function createLayout() { 220 function createLayout() {
147 221
222 + var cfg = config.force;
223 +
148 network.force = d3.layout.force() 224 network.force = d3.layout.force()
225 + .size([network.forceWidth, network.forceHeight])
149 .nodes(network.nodes) 226 .nodes(network.nodes)
150 .links(network.links) 227 .links(network.links)
151 - .linkStrength(function(d) { return d.strength; }) 228 + .linkStrength(function(d) { return cfg.linkStrength[d.class]; })
152 - .size([network.forceWidth, network.forceHeight]) 229 + .linkDistance(function(d) { return cfg.linkDistance[d.class]; })
153 - .linkDistance(config.force.linkDistance) 230 + .charge(function(d) { return cfg.charge[d.class]; })
154 - .charge(config.force.charge)
155 .on('tick', tick); 231 .on('tick', tick);
156 232
157 network.svg = d3.select('#view').append('svg') 233 network.svg = d3.select('#view').append('svg')
...@@ -205,9 +281,10 @@ ...@@ -205,9 +281,10 @@
205 network.link = network.svg.append('g').selectAll('.link') 281 network.link = network.svg.append('g').selectAll('.link')
206 .data(network.force.links(), function(d) {return d.id}) 282 .data(network.force.links(), function(d) {return d.id})
207 .enter().append('line') 283 .enter().append('line')
208 - .attr('class', 'link'); 284 + .attr('class', function(d) {return 'link ' + d.class});
285 +
209 286
210 - // TODO: drag behavior 287 + // == define node drag behavior...
211 network.draggedThreshold = d3.scale.linear() 288 network.draggedThreshold = d3.scale.linear()
212 .domain([0, 0.1]) 289 .domain([0, 0.1])
213 .range([5, 20]) 290 .range([5, 20])
...@@ -258,7 +335,11 @@ ...@@ -258,7 +335,11 @@
258 .data(network.force.nodes(), function(d) {return d.id}) 335 .data(network.force.nodes(), function(d) {return d.id})
259 .enter().append('g') 336 .enter().append('g')
260 .attr('class', function(d) { 337 .attr('class', function(d) {
261 - return 'node ' + d.type; 338 + var cls = 'node ' + d.class;
339 + if (d.type) {
340 + cls += ' ' + d.type;
341 + }
342 + return cls;
262 }) 343 })
263 .attr('transform', function(d) { 344 .attr('transform', function(d) {
264 return translate(d.x, d.y); 345 return translate(d.x, d.y);
...@@ -281,29 +362,32 @@ ...@@ -281,29 +362,32 @@
281 } 362 }
282 network.mouseoutTimeout = setTimeout(function() { 363 network.mouseoutTimeout = setTimeout(function() {
283 highlightObject(null); 364 highlightObject(null);
284 - }, 160); 365 + }, config.mouseOutTimerDelayMs);
285 } 366 }
286 }); 367 });
287 368
288 network.nodeRect = network.node.append('rect') 369 network.nodeRect = network.node.append('rect')
289 .attr('rx', 5) 370 .attr('rx', 5)
290 - .attr('ry', 5) 371 + .attr('ry', 5);
291 - .attr('width', 126) 372 + // note that width/height are adjusted to fit the label text
292 - .attr('height', 40);
293 373
294 network.node.each(function(d) { 374 network.node.each(function(d) {
295 var node = d3.select(this), 375 var node = d3.select(this),
296 rect = node.select('rect'), 376 rect = node.select('rect'),
297 - img = node.append('svg:image') 377 + icon = iconUrl(d),
298 - .attr('x', -16)
299 - .attr('y', -16)
300 - .attr('width', 32)
301 - .attr('height', 32)
302 - .attr('xlink:href', iconUrl(d)),
303 text = node.append('text') 378 text = node.append('text')
304 .text(d.id) 379 .text(d.id)
305 - .attr('dy', '1.1em'), 380 + .attr('dy', '1.1em');
306 - dummy; 381 +
382 + if (icon) {
383 + var cfg = config.icons;
384 + node.append('svg:image')
385 + .attr('width', cfg.w)
386 + .attr('height', cfg.h)
387 + .attr('xlink:href', icon);
388 + // note, icon relative positioning (x,y) is done after we have
389 + // adjusted the bounds of the rectangle...
390 + }
307 391
308 }); 392 });
309 393
...@@ -352,7 +436,8 @@ ...@@ -352,7 +436,8 @@
352 .attr('height', bounds.y2 - bounds.y1); 436 .attr('height', bounds.y2 - bounds.y1);
353 437
354 node.select('image') 438 node.select('image')
355 - .attr('x', bounds.x1); 439 + .attr('x', bounds.x1 + config.icons.xoff)
440 + .attr('y', bounds.y1 + config.icons.yoff);
356 441
357 d.extent = { 442 d.extent = {
358 left: bounds.x1 - lab.marginLR, 443 left: bounds.x1 - lab.marginLR,
...@@ -384,7 +469,7 @@ ...@@ -384,7 +469,7 @@
384 } 469 }
385 470
386 function iconUrl(d) { 471 function iconUrl(d) {
387 - return config.iconUrl[d.type]; 472 + return config.iconUrl[d.icon];
388 } 473 }
389 474
390 function translate(x, y) { 475 function translate(x, y) {
...@@ -440,7 +525,7 @@ ...@@ -440,7 +525,7 @@
440 function tick(e) { 525 function tick(e) {
441 network.numTicks++; 526 network.numTicks++;
442 527
443 - if (config.layering) { 528 + if (config.options.layering) {
444 // adjust the y-coord of each node, based on y-pos constraints 529 // adjust the y-coord of each node, based on y-pos constraints
445 network.nodes.forEach(function (n) { 530 network.nodes.forEach(function (n) {
446 var z = e.alpha * n.constraint.weight; 531 var z = e.alpha * n.constraint.weight;
...@@ -450,7 +535,7 @@ ...@@ -450,7 +535,7 @@
450 }); 535 });
451 } 536 }
452 537
453 - if (network.preventCollisions) { 538 + if (config.options.collisionPrevention && network.preventCollisions) {
454 preventCollisions(); 539 preventCollisions();
455 } 540 }
456 541
......
1 { 1 {
2 - "id": "network-v1",
3 "meta": { 2 "meta": {
4 - "__comment_1__": "This is sample data for developing the ONOS UI", 3 + "comments": [
5 - "foo": "bar", 4 + "This is sample data for developing the ONOS UI (network view)",
6 - "zoo": "goo" 5 + " in a standalone mode (no server required).",
6 + " Eventually, we will wire this up to live data",
7 + " from the server, via a websocket.",
8 + "",
9 + "Note that this is just a first-draft of the data --",
10 + " additional fields will be added when they are needed."
11 + ],
12 + "otherMetaData": "can go here..."
7 }, 13 },
8 - "nodes": [ 14 + "devices": [
9 { 15 {
10 - "id": "sample1", 16 + "id": "of:0000000000000001",
11 - "type": "opt", 17 + "labels": ["00:00:00:00:00:00:00:01", "of/::01", "opt-1"],
12 - "status": "good" 18 + "type": "roadm"
13 }, 19 },
14 { 20 {
15 - "id": "00:00:00:00:00:00:00:02", 21 + "id": "of:0000000000000002",
16 - "type": "opt", 22 + "labels": ["00:00:00:00:00:00:00:02", "of/::02", "opt-2"],
17 - "status": "good" 23 + "type": "roadm"
18 }, 24 },
19 { 25 {
20 - "id": "00:00:00:00:00:00:00:03", 26 + "id": "of:0000000000000003",
21 - "type": "opt", 27 + "labels": ["00:00:00:00:00:00:00:03", "of/::03", "opt-3"],
22 - "status": "good" 28 + "type": "roadm"
23 }, 29 },
24 { 30 {
25 - "id": "00:00:00:00:00:00:00:04", 31 + "id": "of:0000000000000004",
26 - "type": "opt", 32 + "labels": ["00:00:00:00:00:00:00:04", "of/::04", "opt-4"],
27 - "status": "good" 33 + "type": "roadm"
28 }, 34 },
29 { 35 {
30 - "id": "00:00:00:00:00:00:00:11", 36 + "id": "of:0000000000000011",
31 - "type": "pkt", 37 + "labels": ["00:00:00:00:00:00:00:11", "of/::11", "pkt-11"],
32 - "status": "good" 38 + "type": "switch"
33 }, 39 },
34 { 40 {
35 - "id": "00:00:00:00:00:00:00:12", 41 + "id": "of:0000000000000012",
36 - "type": "pkt", 42 + "labels": ["00:00:00:00:00:00:00:12", "of/::12", "pkt-12"],
37 - "status": "good" 43 + "type": "switch"
38 }, 44 },
39 { 45 {
40 - "id": "00:00:00:00:00:00:00:13", 46 + "id": "of:0000000000000013",
41 - "type": "pkt", 47 + "labels": ["00:00:00:00:00:00:00:13", "of/::13", "pkt-13"],
42 - "status": "good" 48 + "type": "switch"
43 } 49 }
44 ], 50 ],
51 + "linkNotes": [
52 + "even though we have 'directionality' (src/dst), each link is",
53 + " considered to be bi-directional. Note that links between hosts",
54 + " and edge switches are inferred from host information, and not",
55 + " explicitly represented here."
56 + ],
45 "links": [ 57 "links": [
46 - { "src": "sample1", "dst": "00:00:00:00:00:00:00:02" }, 58 + {
47 - { "src": "sample1", "dst": "00:00:00:00:00:00:00:03" }, 59 + "src": "of:0000000000000001",
48 - { "src": "sample1", "dst": "00:00:00:00:00:00:00:04" }, 60 + "dst": "of:0000000000000002",
49 - { "src": "00:00:00:00:00:00:00:02", "dst": "00:00:00:00:00:00:00:03" }, 61 + "type": "optical",
50 - { "src": "00:00:00:00:00:00:00:02", "dst": "00:00:00:00:00:00:00:04" }, 62 + "linkWidth": 1.5
51 - { "src": "00:00:00:00:00:00:00:03", "dst": "00:00:00:00:00:00:00:04" }, 63 + },
52 - { "src": "00:00:00:00:00:00:00:13", "dst": "00:00:00:00:00:00:00:03" }, 64 + {
53 - { "src": "00:00:00:00:00:00:00:12", "dst": "00:00:00:00:00:00:00:02" }, 65 + "src": "of:0000000000000001",
54 - { "src": "00:00:00:00:00:00:00:11", "dst": "sample1" } 66 + "dst": "of:0000000000000003",
67 + "type": "optical",
68 + "linkWidth": 1.5
69 + },
70 + {
71 + "src": "of:0000000000000001",
72 + "dst": "of:0000000000000004",
73 + "type": "optical",
74 + "linkWidth": 1.5
75 + },
76 + {
77 + "src": "of:0000000000000002",
78 + "dst": "of:0000000000000003",
79 + "type": "optical",
80 + "linkWidth": 1.5
81 + },
82 + {
83 + "src": "of:0000000000000002",
84 + "dst": "of:0000000000000004",
85 + "type": "optical",
86 + "linkWidth": 1.5
87 + },
88 + {
89 + "src": "of:0000000000000003",
90 + "dst": "of:0000000000000004",
91 + "type": "optical",
92 + "linkWidth": 1.5
93 + },
94 + {
95 + "src": "of:0000000000000013",
96 + "dst": "of:0000000000000003",
97 + "type": "direct",
98 + "linkWidth": 1.0
99 + },
100 + {
101 + "src": "of:0000000000000012",
102 + "dst": "of:0000000000000002",
103 + "type": "direct",
104 + "linkWidth": 1.0
105 + },
106 + {
107 + "src": "of:0000000000000011",
108 + "dst": "of:0000000000000001",
109 + "type": "direct",
110 + "linkWidth": 1.0
111 + }
112 + ],
113 + "hosts": [
114 + {
115 + "id": "00:60:d3:00:11:01/7",
116 + "labels": ["00:60:d3:00:11:01/7", "Host-11-A"],
117 + "cp" : {
118 + "device": "of:0000000000000011",
119 + "port": 6
120 + }
121 + },
122 + {
123 + "id": "00:60:d3:00:11:02/7",
124 + "labels": ["00:60:d3:00:11:02/7", "Host-11-B"],
125 + "cp" : {
126 + "device": "of:0000000000000011",
127 + "port": 8
128 + }
129 + },
130 + {
131 + "id": "00:60:d3:00:12:01/4",
132 + "labels": ["00:60:d3:00:12:01/4", "Host-12-C"],
133 + "cp" : {
134 + "device": "of:0000000000000012",
135 + "port": 12
136 + }
137 + },
138 + {
139 + "id": "00:60:d3:00:12:02/4",
140 + "labels": ["00:60:d3:00:12:02/4", "Host-12-D"],
141 + "cp" : {
142 + "device": "of:0000000000000012",
143 + "port": 13
144 + }
145 + },
146 + {
147 + "id": "00:60:d3:00:13:01/19",
148 + "labels": ["00:60:d3:00:13:01/19", "Host-13-E"],
149 + "cp" : {
150 + "device": "of:0000000000000013",
151 + "port": 7
152 + }
153 + },
154 + {
155 + "id": "00:60:d3:00:13:02/19",
156 + "labels": ["00:60:d3:00:13:02/19", "Host-13-F"],
157 + "cp" : {
158 + "device": "of:0000000000000013",
159 + "port": 8
160 + }
161 + }
55 ] 162 ]
56 } 163 }
......
...@@ -38,7 +38,7 @@ svg { ...@@ -38,7 +38,7 @@ svg {
38 * Network Graph elements ====================================== 38 * Network Graph elements ======================================
39 */ 39 */
40 40
41 -.link { 41 +svg .link {
42 fill: none; 42 fill: none;
43 stroke: #666; 43 stroke: #666;
44 stroke-width: 1.5px; 44 stroke-width: 1.5px;
...@@ -56,7 +56,7 @@ marker#end { ...@@ -56,7 +56,7 @@ marker#end {
56 stroke-width: 1.5px; 56 stroke-width: 1.5px;
57 } 57 }
58 58
59 -.node rect { 59 +svg .node rect {
60 stroke-width: 1.5px; 60 stroke-width: 1.5px;
61 61
62 transition: opacity 250ms; 62 transition: opacity 250ms;
...@@ -64,13 +64,15 @@ marker#end { ...@@ -64,13 +64,15 @@ marker#end {
64 -moz-transition: opacity 250ms; 64 -moz-transition: opacity 250ms;
65 } 65 }
66 66
67 -/*differentiate between packet and optical nodes*/ 67 +svg .node.device.roadm rect {
68 -svg .node.pkt rect { 68 + fill: #229;
69 - fill: #77a; 69 +}
70 +svg .node.device.switch rect {
71 + fill: #55f;
70 } 72 }
71 73
72 -svg .node.opt rect { 74 +svg .node.host rect {
73 - fill: #7a7; 75 + fill: #787;
74 } 76 }
75 77
76 svg .node text { 78 svg .node text {
...@@ -121,15 +123,13 @@ svg .legend .category text { ...@@ -121,15 +123,13 @@ svg .legend .category text {
121 #frame { 123 #frame {
122 width: 100%; 124 width: 100%;
123 height: 100%; 125 height: 100%;
124 - background-color: #cdf; 126 + background-color: #fff;
125 } 127 }
126 128
127 #mast { 129 #mast {
128 height: 32px; 130 height: 32px;
129 - background-color: #abe; 131 + padding: 6px;
132 + background-color: #ccc;
130 vertical-align: baseline; 133 vertical-align: baseline;
131 } 134 }
132 135
133 -#main {
134 - background-color: #99c;
135 -}
......