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 @@
var api = onos.api;
var config = {
layering: false,
options: {
layering: false,
collisionPrevention: true
},
jsonUrl: 'network.json',
iconUrl: {
pkt: 'pkt.png',
opt: 'opt.png'
logo: 'img/onos-logo.tiff',
device: 'img/device.png',
host: 'img/host.png',
pkt: 'img/pkt.png',
opt: 'img/opt.png'
},
mastHeight: 32,
force: {
linkDistance: 240,
linkStrength: 0.8,
charge: -400,
note: 'node.class or link.class is used to differentiate',
linkDistance: {
infra: 240,
host: 100
},
linkStrength: {
infra: 1.0,
host: 0.4
},
charge: {
device: -800,
host: -400
},
ticksWithoutCollisions: 50,
marginLR: 20,
marginTB: 20,
......@@ -37,12 +53,21 @@
marginLR: 3,
marginTB: 2
},
icons: {
w: 32,
h: 32,
xoff: -12,
yoff: -10
},
constraints: {
ypos: {
pkt: 0.3,
opt: 0.7
host: 0.15,
switch: 0.3,
roadm: 0.7
}
}
},
hostLinkWidth: 1.0,
mouseOutTimerDelayMs: 120
},
view = {},
network = {},
......@@ -104,14 +129,23 @@
var nw = network.forceWidth,
nh = network.forceHeight;
network.data.nodes.forEach(function(n) {
function yPosConstraintForNode(n) {
return config.constraints.ypos[n.type || 'host'];
}
// Note that both 'devices' and 'hosts' get mapped into the nodes array
// first, the devices...
network.data.devices.forEach(function(n) {
var ypc = yPosConstraintForNode(n),
ix = Math.random() * 0.6 * nw + 0.2 * nw,
iy = ypc * nh,
node = {
id: n.id,
labels: n.labels,
class: 'device',
icon: 'device',
type: n.type,
status: n.status,
x: ix,
y: iy,
constraint: {
......@@ -123,21 +157,61 @@
network.nodes.push(node);
});
function yPosConstraintForNode(n) {
return config.constraints.ypos[n.type] || 0.5;
}
// then, the hosts...
network.data.hosts.forEach(function(n) {
var ypc = yPosConstraintForNode(n),
ix = Math.random() * 0.6 * nw + 0.2 * nw,
iy = ypc * nh,
node = {
id: n.id,
labels: n.labels,
class: 'host',
icon: 'host',
type: n.type,
x: ix,
y: iy,
constraint: {
weight: 0.7,
y: iy
}
};
network.lookup[n.id] = node;
network.nodes.push(node);
});
// now, process the explicit links...
network.data.links.forEach(function(n) {
var src = network.lookup[n.src],
dst = network.lookup[n.dst],
id = src.id + "~" + dst.id;
var link = {
class: 'infra',
id: id,
type: n.type,
width: n.linkWidth,
source: src,
target: dst,
strength: config.force.linkStrength
strength: config.force.linkStrength.infra
};
network.links.push(link);
});
// finally, infer host links...
network.data.hosts.forEach(function(n) {
var src = network.lookup[n.id],
dst = network.lookup[n.cp.device],
id = src.id + "~" + dst.id;
var link = {
class: 'host',
id: id,
type: 'hostLink',
width: config.hostLinkWidth,
source: src,
target: dst,
strength: config.force.linkStrength.host
};
network.links.push(link);
});
......@@ -145,13 +219,15 @@
function createLayout() {
var cfg = config.force;
network.force = d3.layout.force()
.size([network.forceWidth, network.forceHeight])
.nodes(network.nodes)
.links(network.links)
.linkStrength(function(d) { return d.strength; })
.size([network.forceWidth, network.forceHeight])
.linkDistance(config.force.linkDistance)
.charge(config.force.charge)
.linkStrength(function(d) { return cfg.linkStrength[d.class]; })
.linkDistance(function(d) { return cfg.linkDistance[d.class]; })
.charge(function(d) { return cfg.charge[d.class]; })
.on('tick', tick);
network.svg = d3.select('#view').append('svg')
......@@ -205,9 +281,10 @@
network.link = network.svg.append('g').selectAll('.link')
.data(network.force.links(), function(d) {return d.id})
.enter().append('line')
.attr('class', 'link');
.attr('class', function(d) {return 'link ' + d.class});
// TODO: drag behavior
// == define node drag behavior...
network.draggedThreshold = d3.scale.linear()
.domain([0, 0.1])
.range([5, 20])
......@@ -258,7 +335,11 @@
.data(network.force.nodes(), function(d) {return d.id})
.enter().append('g')
.attr('class', function(d) {
return 'node ' + d.type;
var cls = 'node ' + d.class;
if (d.type) {
cls += ' ' + d.type;
}
return cls;
})
.attr('transform', function(d) {
return translate(d.x, d.y);
......@@ -281,29 +362,32 @@
}
network.mouseoutTimeout = setTimeout(function() {
highlightObject(null);
}, 160);
}, config.mouseOutTimerDelayMs);
}
});
network.nodeRect = network.node.append('rect')
.attr('rx', 5)
.attr('ry', 5)
.attr('width', 126)
.attr('height', 40);
.attr('ry', 5);
// note that width/height are adjusted to fit the label text
network.node.each(function(d) {
var node = d3.select(this),
rect = node.select('rect'),
img = node.append('svg:image')
.attr('x', -16)
.attr('y', -16)
.attr('width', 32)
.attr('height', 32)
.attr('xlink:href', iconUrl(d)),
icon = iconUrl(d),
text = node.append('text')
.text(d.id)
.attr('dy', '1.1em'),
dummy;
.attr('dy', '1.1em');
if (icon) {
var cfg = config.icons;
node.append('svg:image')
.attr('width', cfg.w)
.attr('height', cfg.h)
.attr('xlink:href', icon);
// note, icon relative positioning (x,y) is done after we have
// adjusted the bounds of the rectangle...
}
});
......@@ -352,7 +436,8 @@
.attr('height', bounds.y2 - bounds.y1);
node.select('image')
.attr('x', bounds.x1);
.attr('x', bounds.x1 + config.icons.xoff)
.attr('y', bounds.y1 + config.icons.yoff);
d.extent = {
left: bounds.x1 - lab.marginLR,
......@@ -384,7 +469,7 @@
}
function iconUrl(d) {
return config.iconUrl[d.type];
return config.iconUrl[d.icon];
}
function translate(x, y) {
......@@ -440,7 +525,7 @@
function tick(e) {
network.numTicks++;
if (config.layering) {
if (config.options.layering) {
// adjust the y-coord of each node, based on y-pos constraints
network.nodes.forEach(function (n) {
var z = e.alpha * n.constraint.weight;
......@@ -450,7 +535,7 @@
});
}
if (network.preventCollisions) {
if (config.options.collisionPrevention && network.preventCollisions) {
preventCollisions();
}
......
{
"id": "network-v1",
"meta": {
"__comment_1__": "This is sample data for developing the ONOS UI",
"foo": "bar",
"zoo": "goo"
"comments": [
"This is sample data for developing the ONOS UI (network view)",
" in a standalone mode (no server required).",
" Eventually, we will wire this up to live data",
" from the server, via a websocket.",
"",
"Note that this is just a first-draft of the data --",
" additional fields will be added when they are needed."
],
"otherMetaData": "can go here..."
},
"nodes": [
"devices": [
{
"id": "sample1",
"type": "opt",
"status": "good"
"id": "of:0000000000000001",
"labels": ["00:00:00:00:00:00:00:01", "of/::01", "opt-1"],
"type": "roadm"
},
{
"id": "00:00:00:00:00:00:00:02",
"type": "opt",
"status": "good"
"id": "of:0000000000000002",
"labels": ["00:00:00:00:00:00:00:02", "of/::02", "opt-2"],
"type": "roadm"
},
{
"id": "00:00:00:00:00:00:00:03",
"type": "opt",
"status": "good"
"id": "of:0000000000000003",
"labels": ["00:00:00:00:00:00:00:03", "of/::03", "opt-3"],
"type": "roadm"
},
{
"id": "00:00:00:00:00:00:00:04",
"type": "opt",
"status": "good"
"id": "of:0000000000000004",
"labels": ["00:00:00:00:00:00:00:04", "of/::04", "opt-4"],
"type": "roadm"
},
{
"id": "00:00:00:00:00:00:00:11",
"type": "pkt",
"status": "good"
"id": "of:0000000000000011",
"labels": ["00:00:00:00:00:00:00:11", "of/::11", "pkt-11"],
"type": "switch"
},
{
"id": "00:00:00:00:00:00:00:12",
"type": "pkt",
"status": "good"
"id": "of:0000000000000012",
"labels": ["00:00:00:00:00:00:00:12", "of/::12", "pkt-12"],
"type": "switch"
},
{
"id": "00:00:00:00:00:00:00:13",
"type": "pkt",
"status": "good"
"id": "of:0000000000000013",
"labels": ["00:00:00:00:00:00:00:13", "of/::13", "pkt-13"],
"type": "switch"
}
],
"linkNotes": [
"even though we have 'directionality' (src/dst), each link is",
" considered to be bi-directional. Note that links between hosts",
" and edge switches are inferred from host information, and not",
" explicitly represented here."
],
"links": [
{ "src": "sample1", "dst": "00:00:00:00:00:00:00:02" },
{ "src": "sample1", "dst": "00:00:00:00:00:00:00:03" },
{ "src": "sample1", "dst": "00:00:00:00:00:00:00:04" },
{ "src": "00:00:00:00:00:00:00:02", "dst": "00:00:00:00:00:00:00:03" },
{ "src": "00:00:00:00:00:00:00:02", "dst": "00:00:00:00:00:00:00:04" },
{ "src": "00:00:00:00:00:00:00:03", "dst": "00:00:00:00:00:00:00:04" },
{ "src": "00:00:00:00:00:00:00:13", "dst": "00:00:00:00:00:00:00:03" },
{ "src": "00:00:00:00:00:00:00:12", "dst": "00:00:00:00:00:00:00:02" },
{ "src": "00:00:00:00:00:00:00:11", "dst": "sample1" }
{
"src": "of:0000000000000001",
"dst": "of:0000000000000002",
"type": "optical",
"linkWidth": 1.5
},
{
"src": "of:0000000000000001",
"dst": "of:0000000000000003",
"type": "optical",
"linkWidth": 1.5
},
{
"src": "of:0000000000000001",
"dst": "of:0000000000000004",
"type": "optical",
"linkWidth": 1.5
},
{
"src": "of:0000000000000002",
"dst": "of:0000000000000003",
"type": "optical",
"linkWidth": 1.5
},
{
"src": "of:0000000000000002",
"dst": "of:0000000000000004",
"type": "optical",
"linkWidth": 1.5
},
{
"src": "of:0000000000000003",
"dst": "of:0000000000000004",
"type": "optical",
"linkWidth": 1.5
},
{
"src": "of:0000000000000013",
"dst": "of:0000000000000003",
"type": "direct",
"linkWidth": 1.0
},
{
"src": "of:0000000000000012",
"dst": "of:0000000000000002",
"type": "direct",
"linkWidth": 1.0
},
{
"src": "of:0000000000000011",
"dst": "of:0000000000000001",
"type": "direct",
"linkWidth": 1.0
}
],
"hosts": [
{
"id": "00:60:d3:00:11:01/7",
"labels": ["00:60:d3:00:11:01/7", "Host-11-A"],
"cp" : {
"device": "of:0000000000000011",
"port": 6
}
},
{
"id": "00:60:d3:00:11:02/7",
"labels": ["00:60:d3:00:11:02/7", "Host-11-B"],
"cp" : {
"device": "of:0000000000000011",
"port": 8
}
},
{
"id": "00:60:d3:00:12:01/4",
"labels": ["00:60:d3:00:12:01/4", "Host-12-C"],
"cp" : {
"device": "of:0000000000000012",
"port": 12
}
},
{
"id": "00:60:d3:00:12:02/4",
"labels": ["00:60:d3:00:12:02/4", "Host-12-D"],
"cp" : {
"device": "of:0000000000000012",
"port": 13
}
},
{
"id": "00:60:d3:00:13:01/19",
"labels": ["00:60:d3:00:13:01/19", "Host-13-E"],
"cp" : {
"device": "of:0000000000000013",
"port": 7
}
},
{
"id": "00:60:d3:00:13:02/19",
"labels": ["00:60:d3:00:13:02/19", "Host-13-F"],
"cp" : {
"device": "of:0000000000000013",
"port": 8
}
}
]
}
......
......@@ -38,7 +38,7 @@ svg {
* Network Graph elements ======================================
*/
.link {
svg .link {
fill: none;
stroke: #666;
stroke-width: 1.5px;
......@@ -56,7 +56,7 @@ marker#end {
stroke-width: 1.5px;
}
.node rect {
svg .node rect {
stroke-width: 1.5px;
transition: opacity 250ms;
......@@ -64,13 +64,15 @@ marker#end {
-moz-transition: opacity 250ms;
}
/*differentiate between packet and optical nodes*/
svg .node.pkt rect {
fill: #77a;
svg .node.device.roadm rect {
fill: #229;
}
svg .node.device.switch rect {
fill: #55f;
}
svg .node.opt rect {
fill: #7a7;
svg .node.host rect {
fill: #787;
}
svg .node text {
......@@ -121,15 +123,13 @@ svg .legend .category text {
#frame {
width: 100%;
height: 100%;
background-color: #cdf;
background-color: #fff;
}
#mast {
height: 32px;
background-color: #abe;
padding: 6px;
background-color: #ccc;
vertical-align: baseline;
}
#main {
background-color: #99c;
}
......