Polishes bmx6 graph. Adds colors by network and gives color to links based on link quality.
This commit is contained in:
parent
9646815e7d
commit
1bc988c09c
2 changed files with 239 additions and 66 deletions
|
@ -29,11 +29,6 @@ the file called "COPYING".
|
|||
|
||||
<%+header%>
|
||||
|
||||
<script type="text/javascript" src="<%=resource%>/bmx6/js/raphael-min.js"></script>
|
||||
<script type="text/javascript" src="<%=resource%>/bmx6/js/dracula_graffle.js"></script>
|
||||
<script type="text/javascript" src="<%=resource%>/bmx6/js/jquery-1.4.2.min.js"></script>
|
||||
<script type="text/javascript" src="<%=resource%>/bmx6/js/dracula_graph.js"></script>
|
||||
|
||||
<button id="redraw" onclick="redraw();">  redraw  </button>
|
||||
|
||||
<div id="wait" style="text-align: center">
|
||||
|
@ -45,66 +40,10 @@ the file called "COPYING".
|
|||
|
||||
<div id="canvas" style="min-width:800px; min-height:800px"></div>
|
||||
|
||||
<script type="text/javascript">//<![CDATA[
|
||||
var redraw;
|
||||
|
||||
XHR.get('<%=luci.dispatcher.build_url(unpack(location))%>', null,
|
||||
function(x, data)
|
||||
{
|
||||
var g = new Graph();
|
||||
var seen = { };
|
||||
|
||||
for (var i = 0; i < (data.length); i++)
|
||||
{
|
||||
// node->node
|
||||
if (data[i].name)
|
||||
{
|
||||
for (var j = 0; j < (data[i].links.length); j++)
|
||||
{
|
||||
var key = (data[i].name < data[i].links[j].name)
|
||||
? data[i].name + '|' + data[i].links[j].name
|
||||
: data[i].links[j].name + '|' + data[i].name;
|
||||
|
||||
var rxRate = data[i].links[j].rxRate;
|
||||
var txRate = data[i].links[j].txRate;
|
||||
|
||||
if (!seen[key] && rxRate>0 && txRate>0)
|
||||
{
|
||||
g.addEdge(data[i].name, data[i].links[j].name,
|
||||
{ label: rxRate + '/' + txRate,
|
||||
directed: false, stroke: '#aaaaaa', fill: '#ffffff',
|
||||
'label-style': { 'font-size': 8 }});
|
||||
seen[key] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
//g.addEdge(data[i].router, data[i].neighbor,
|
||||
// { label: data[i].label, directed: true, stroke: '#aaaaaa' });
|
||||
// node->leaf
|
||||
//else if (data[i].router && data[i].gateway)
|
||||
// g.addEdge(data[i].router, data[i].gateway,
|
||||
// { label: 'leaf', stroke: '#cccccc' });
|
||||
}
|
||||
|
||||
var canvas = document.getElementById('canvas');
|
||||
|
||||
var layouter = new Graph.Layout.Spring(g);
|
||||
layouter.layout();
|
||||
|
||||
var divwait = document.getElementById("wait");
|
||||
divwait.parentNode.removeChild(divwait);
|
||||
|
||||
var renderer = new Graph.Renderer.Raphael(canvas.id, g, canvas.offsetWidth, canvas.offsetHeight);
|
||||
renderer.draw();
|
||||
|
||||
redraw = function() {
|
||||
layouter.layout();
|
||||
renderer.draw();
|
||||
}
|
||||
|
||||
}
|
||||
);
|
||||
//]]></script>
|
||||
|
||||
<script type="text/javascript" src="<%=resource%>/bmx6/js/raphael-min.js"></script>
|
||||
<script type="text/javascript" src="<%=resource%>/bmx6/js/dracula_graffle.js"></script>
|
||||
<script type="text/javascript" src="<%=resource%>/bmx6/js/jquery-1.4.2.min.js"></script>
|
||||
<script type="text/javascript" src="<%=resource%>/bmx6/js/dracula_graph.js"></script>
|
||||
<script type="text/javascript" src="<%=resource%>/bmx6/js/bmx6-graph.js"></script>
|
||||
|
||||
<%+footer%>
|
||||
|
|
|
@ -0,0 +1,234 @@
|
|||
var graph, canvas, layouter, renderer, divwait, nodes, announcements, nodesIndex, palette, localInfo;
|
||||
document.addEventListener( "DOMContentLoaded", init, false);
|
||||
|
||||
/**
|
||||
* Returns an index of nodes by name
|
||||
*/
|
||||
function createNodeIndex(nodes) {
|
||||
var inode, index = {};
|
||||
|
||||
for (inode in nodes)
|
||||
index[nodes[inode].name] = nodes[inode];
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates to have announcements in nodes list
|
||||
*/
|
||||
function processNodeAnnouncements(nodes, announcements) {
|
||||
var iannouncement, remoteNode, announcement;
|
||||
nodesIndex = createNodeIndex(nodes);
|
||||
|
||||
for(iannouncement in announcements) {
|
||||
announcement = announcements[iannouncement];
|
||||
if (announcement.remoteName == '---' ) continue;
|
||||
if (!( announcement.remoteName in nodesIndex )) {
|
||||
newNode = {
|
||||
name: announcement.remoteName,
|
||||
links: []
|
||||
};
|
||||
nodes.push(newNode);
|
||||
nodesIndex[newNode.name] = newNode;
|
||||
};
|
||||
|
||||
remoteNode = nodesIndex[announcement.remoteName];
|
||||
if (!( 'announcements' in remoteNode )) remoteNode.announcements = [];
|
||||
remoteNode.announcements.push(announcement);
|
||||
};
|
||||
}
|
||||
|
||||
function init() {
|
||||
palette = generatePalette(200);
|
||||
|
||||
graph = new Graph();
|
||||
canvas = document.getElementById('canvas');
|
||||
layouter = new Graph.Layout.Spring(graph);
|
||||
renderer = new Graph.Renderer.Raphael(canvas.id, graph, canvas.offsetWidth, canvas.offsetHeight);
|
||||
|
||||
divwait = document.getElementById("wait");
|
||||
|
||||
XHR.get('/cgi-bin/luci/status/bmx6/topology', null, function(nodesRequest, nodesData) {
|
||||
nodes = nodesData;
|
||||
|
||||
XHR.get('/cgi-bin/bmx6-info?$myself&', null, function(myselfRequest, myselfData) {
|
||||
if (myselfData)
|
||||
localAnnouncements = [
|
||||
{remoteName: myselfData.myself.hostname, advNet: myselfData.myself.net4},
|
||||
{remoteName: myselfData.myself.hostname, advNet: myselfData.myself.net6}
|
||||
];
|
||||
|
||||
XHR.get('/cgi-bin/bmx6-info?$tunnels=&', null, function(tunnelsRequest, tunnelsData) {
|
||||
var iAnnouncement;
|
||||
|
||||
announcements = tunnelsData.tunnels;
|
||||
for(iAnnouncement in localAnnouncements) {
|
||||
announcements.push(localAnnouncements[iAnnouncement])
|
||||
};
|
||||
|
||||
processNodeAnnouncements(nodes, announcements);
|
||||
|
||||
divwait.parentNode.removeChild(divwait);
|
||||
draw(nodes);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function hashCode(str) {
|
||||
var hash = 0;
|
||||
if (str.length == 0) return hash;
|
||||
for (i = 0; i < str.length; i++) {
|
||||
char = str.charCodeAt(i);
|
||||
hash = ((hash<<5)-hash)+char;
|
||||
hash = hash & hash; // Convert to 32bit integer
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
function generatePalette(size) {
|
||||
var i, arr = [];
|
||||
Raphael.getColor(); // just to remove the grey one
|
||||
for(i = 0; i < size; i++) {
|
||||
arr.push(Raphael.getColor())
|
||||
}
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
function getFillFromHash(hash) {
|
||||
return palette[Math.abs(hash % palette.length)];
|
||||
}
|
||||
|
||||
function hashAnnouncementsNames(announcementsNames) {
|
||||
return hashCode(announcementsNames.sort().join('-'));
|
||||
}
|
||||
|
||||
function getNodeAnnouncements(networkNode) {
|
||||
return networkNode.announcements;
|
||||
}
|
||||
|
||||
function nodeRenderer(raphael, node) {
|
||||
var nodeFill, renderedNode, options;
|
||||
options = {
|
||||
'fill': 'announcements' in node.networkNode ? getFillFromHash(
|
||||
hashAnnouncementsNames(
|
||||
getNodeAnnouncements(node.networkNode).map(function(ann) {return ann.advNet;})
|
||||
)
|
||||
) : '#bfbfbf',
|
||||
'stroke-width': 1,
|
||||
|
||||
};
|
||||
|
||||
renderedNode = raphael.set();
|
||||
|
||||
renderedNode.push(raphael.ellipse(node.point[0], node.point[1], 30, 20).attr({"fill": options['fill'], "stroke-width": options['stroke-width']}));
|
||||
renderedNode.push(raphael.text(node.point[0], node.point[1] + 30, node.networkNode.name).attr({}));
|
||||
|
||||
renderedNode.items.forEach(function(el) {
|
||||
var announcements, tooltip = raphael.set();
|
||||
tooltip.push(raphael.rect(-60, -60, 120, 60).attr({"fill": "#fec", "stroke-width": 1, r : "9px"}));
|
||||
|
||||
announcements = getNodeAnnouncements(node.networkNode);
|
||||
if (announcements) {
|
||||
|
||||
announcements = announcements.map(function(ann) {return ann.advNet});
|
||||
tooltip.push(raphael.text(0, -40, 'announcements\n' + announcements.join('\n')).attr({}));
|
||||
};
|
||||
|
||||
el.tooltip(tooltip);
|
||||
});
|
||||
|
||||
return renderedNode;
|
||||
}
|
||||
|
||||
function genericNodeRenderer(raphael, node) {
|
||||
var renderedNode;
|
||||
|
||||
renderedNode = raphael.set();
|
||||
|
||||
renderedNode.push(raphael.ellipse(node.point[0], node.point[1], 30, 20).attr({"fill": '#bfbfbf', "stroke-width": 1}));
|
||||
renderedNode.push(raphael.text(node.point[0], node.point[1] + 30, node.networkNode.name).attr({}));
|
||||
|
||||
return renderedNode;
|
||||
}
|
||||
|
||||
function redraw() {
|
||||
layouter.layout();
|
||||
renderer.draw();
|
||||
}
|
||||
|
||||
function interpolateColor(minColor,maxColor,maxDepth,depth){
|
||||
|
||||
function d2h(d) {return d.toString(16);}
|
||||
function h2d(h) {return parseInt(h,16);}
|
||||
|
||||
if(depth == 0){
|
||||
return minColor;
|
||||
}
|
||||
if(depth == maxDepth){
|
||||
return maxColor;
|
||||
}
|
||||
|
||||
var color = "#";
|
||||
|
||||
for(var i=1; i <= 6; i+=2){
|
||||
var minVal = new Number(h2d(minColor.substr(i,2)));
|
||||
var maxVal = new Number(h2d(maxColor.substr(i,2)));
|
||||
var nVal = minVal + (maxVal-minVal) * (depth/maxDepth);
|
||||
var val = d2h(Math.floor(nVal));
|
||||
while(val.length < 2){
|
||||
val = "0"+val;
|
||||
}
|
||||
color += val;
|
||||
}
|
||||
return color;
|
||||
}
|
||||
function draw(nodes) {
|
||||
var node, neighbourNode, seenKey, rxRate, txRate, seen, i, j, currentName, linkQuality;
|
||||
|
||||
seen = { };
|
||||
|
||||
for (i = 0; i < (nodes.length); i++) {
|
||||
node = nodes[i];
|
||||
graph.addNode(node.name, {
|
||||
networkNode: node,
|
||||
render: nodeRenderer
|
||||
});
|
||||
};
|
||||
|
||||
for (i = 0; i < (nodes.length); i++) {
|
||||
node = nodes[i];
|
||||
|
||||
if (! node.name) continue;
|
||||
|
||||
currentName = node.name;
|
||||
|
||||
for (j = 0; j < (node.links.length); j++) {
|
||||
neighbourNode = node.links[j];
|
||||
|
||||
graph.addNode(neighbourNode.name, {render: genericNodeRenderer, networkNode: neighbourNode});
|
||||
|
||||
seenKey = (node.name < neighbourNode.name) ? node.name + '|' + neighbourNode.name : neighbourNode.name + '|' + node.name;
|
||||
|
||||
rxRate = neighbourNode.rxRate;
|
||||
txRate = neighbourNode.txRate;
|
||||
|
||||
if (!seen[seenKey] && rxRate > 0 && txRate > 0) {
|
||||
linkQuality = ( rxRate + txRate ) / 2;
|
||||
|
||||
graph.addEdge(node.name, neighbourNode.name, {
|
||||
'label': rxRate + '/' + txRate,
|
||||
'directed': false,
|
||||
'stroke': interpolateColor('FF0000','00FF00', 5, 5 * ( linkQuality - 1 )/100),
|
||||
'fill': interpolateColor('FF0000','00FF00', 5, 5 * ( linkQuality - 1 )/100),
|
||||
'label-style': { 'font-size': 8 }
|
||||
});
|
||||
|
||||
seen[seenKey] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
redraw();
|
||||
}
|
Loading…
Reference in a new issue