luci-base, luci-mod-status: convert realtime stats to client side views
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
This commit is contained in:
parent
a2f43983b6
commit
cfb5af89e1
10 changed files with 1369 additions and 1477 deletions
|
@ -43,9 +43,10 @@
|
|||
"ubus": {
|
||||
"file": [ "list", "read", "stat" ],
|
||||
"iwinfo": [ "assoclist", "freqlist", "txpowerlist", "countrylist" ],
|
||||
"luci": [ "getDUIDHints", "getInitList", "getLocaltime", "getTimezones", "getLEDs", "getUSBDevices", "getSwconfigFeatures", "getSwconfigPortState", "getBlockDevices", "getMountPoints" ],
|
||||
"luci": [ "getConntrackList", "getDUIDHints", "getInitList", "getLocaltime", "getRealtimeStats", "getTimezones", "getLEDs", "getUSBDevices", "getSwconfigFeatures", "getSwconfigPortState", "getBlockDevices", "getMountPoints" ],
|
||||
"luci-rpc": [ "getBoardJSON", "getDHCPLeases", "getDSLStatus", "getHostHints", "getNetworkDevices", "getWirelessDevices" ],
|
||||
"network.interface": [ "dump" ],
|
||||
"network.rrdns": [ "lookup" ],
|
||||
"network": [ "get_proto_handlers" ],
|
||||
"system": [ "board", "info", "validate_firmware_image" ],
|
||||
"uci": [ "changes", "get" ]
|
||||
|
|
|
@ -0,0 +1,311 @@
|
|||
'use strict';
|
||||
'require rpc';
|
||||
'require network';
|
||||
|
||||
var callLuciRealtimeStats = rpc.declare({
|
||||
object: 'luci',
|
||||
method: 'getRealtimeStats',
|
||||
params: [ 'mode', 'device' ],
|
||||
expect: { result: [] }
|
||||
});
|
||||
|
||||
var graphPolls = [],
|
||||
pollInterval = 3;
|
||||
|
||||
Math.log2 = Math.log2 || function(x) { return Math.log(x) * Math.LOG2E; };
|
||||
|
||||
function rate(n, br) {
|
||||
n = (n || 0).toFixed(2);
|
||||
return [ '%1024.2mbit/s'.format(n * 8), br ? E('br') : ' ', '(%1024.2mB/s)'.format(n) ]
|
||||
}
|
||||
|
||||
return L.view.extend({
|
||||
load: function() {
|
||||
return Promise.all([
|
||||
this.loadSVG(L.resource('bandwidth.svg')),
|
||||
network.getDevices()
|
||||
]);
|
||||
},
|
||||
|
||||
updateGraph: function(ifname, svg, lines, cb) {
|
||||
var G = svg.firstElementChild;
|
||||
|
||||
var view = document.querySelector('#view');
|
||||
|
||||
var width = view.offsetWidth - 2;
|
||||
var height = 300 - 2;
|
||||
var step = 5;
|
||||
|
||||
var data_wanted = Math.floor(width / step);
|
||||
|
||||
var data_values = [],
|
||||
line_elements = [];
|
||||
|
||||
for (var i = 0; i < lines.length; i++)
|
||||
if (lines[i] != null)
|
||||
data_values.push([]);
|
||||
|
||||
var info = {
|
||||
line_current: [],
|
||||
line_average: [],
|
||||
line_peak: []
|
||||
};
|
||||
|
||||
/* prefill datasets */
|
||||
for (var i = 0; i < data_values.length; i++)
|
||||
for (var j = 0; j < data_wanted; j++)
|
||||
data_values[i][j] = 0;
|
||||
|
||||
/* plot horizontal time interval lines */
|
||||
for (var i = width % (step * 60); i < width; i += step * 60) {
|
||||
var line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
|
||||
line.setAttribute('x1', i);
|
||||
line.setAttribute('y1', 0);
|
||||
line.setAttribute('x2', i);
|
||||
line.setAttribute('y2', '100%');
|
||||
line.setAttribute('style', 'stroke:black;stroke-width:0.1');
|
||||
|
||||
var text = document.createElementNS('http://www.w3.org/2000/svg', 'text');
|
||||
text.setAttribute('x', i + 5);
|
||||
text.setAttribute('y', 15);
|
||||
text.setAttribute('style', 'fill:#eee; font-size:9pt; font-family:sans-serif; text-shadow:1px 1px 1px #000');
|
||||
text.appendChild(document.createTextNode(Math.round((width - i) / step / 60) + 'm'));
|
||||
|
||||
G.appendChild(line);
|
||||
G.appendChild(text);
|
||||
}
|
||||
|
||||
info.interval = pollInterval;
|
||||
info.timeframe = data_wanted / 60;
|
||||
|
||||
graphPolls.push({
|
||||
ifname: ifname,
|
||||
svg: svg,
|
||||
lines: lines,
|
||||
cb: cb,
|
||||
info: info,
|
||||
width: width,
|
||||
height: height,
|
||||
step: step,
|
||||
values: data_values,
|
||||
timestamp: 0,
|
||||
fill: 1
|
||||
});
|
||||
},
|
||||
|
||||
pollData: function() {
|
||||
L.Poll.add(L.bind(function() {
|
||||
var tasks = [];
|
||||
|
||||
for (var i = 0; i < graphPolls.length; i++) {
|
||||
var ctx = graphPolls[i];
|
||||
tasks.push(L.resolveDefault(callLuciRealtimeStats('interface', ctx.ifname), []));
|
||||
}
|
||||
|
||||
return Promise.all(tasks).then(L.bind(function(datasets) {
|
||||
for (var gi = 0; gi < graphPolls.length; gi++) {
|
||||
var ctx = graphPolls[gi],
|
||||
data = datasets[gi],
|
||||
values = ctx.values,
|
||||
lines = ctx.lines,
|
||||
info = ctx.info;
|
||||
|
||||
var data_scale = 0;
|
||||
var data_wanted = Math.floor(ctx.width / ctx.step);
|
||||
var last_timestamp = NaN;
|
||||
|
||||
for (var i = 0, di = 0; di < lines.length; di++) {
|
||||
if (lines[di] == null)
|
||||
continue;
|
||||
|
||||
var multiply = (lines[di].multiply != null) ? lines[di].multiply : 1,
|
||||
offset = (lines[di].offset != null) ? lines[di].offset : 0;
|
||||
|
||||
for (var j = ctx.timestamp ? 0 : 1; j < data.length; j++) {
|
||||
/* skip overlapping entries */
|
||||
if (data[j][0] <= ctx.timestamp)
|
||||
continue;
|
||||
|
||||
if (i == 0) {
|
||||
ctx.fill++;
|
||||
last_timestamp = data[j][0];
|
||||
}
|
||||
|
||||
if (lines[di].counter) {
|
||||
/* normalize difference against time interval */
|
||||
if (j > 0) {
|
||||
var time_delta = data[j][0] - data[j - 1][0];
|
||||
if (time_delta) {
|
||||
info.line_current[i] = (data[j][di + 1] * multiply - data[j - 1][di + 1] * multiply) / time_delta;
|
||||
info.line_current[i] -= Math.min(info.line_current[i], offset);
|
||||
values[i].push(info.line_current[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
info.line_current[i] = data[j][di + 1] * multiply;
|
||||
info.line_current[i] -= Math.min(info.line_current[i], offset);
|
||||
values[i].push(info.line_current[i]);
|
||||
}
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
/* cut off outdated entries */
|
||||
ctx.fill = Math.min(ctx.fill, data_wanted);
|
||||
|
||||
for (var i = 0; i < values.length; i++) {
|
||||
var len = values[i].length;
|
||||
values[i] = values[i].slice(len - data_wanted, len);
|
||||
|
||||
/* find peaks, averages */
|
||||
info.line_peak[i] = NaN;
|
||||
info.line_average[i] = 0;
|
||||
|
||||
for (var j = 0; j < values[i].length; j++) {
|
||||
info.line_peak[i] = isNaN(info.line_peak[i]) ? values[i][j] : Math.max(info.line_peak[i], values[i][j]);
|
||||
info.line_average[i] += values[i][j];
|
||||
}
|
||||
|
||||
info.line_average[i] = info.line_average[i] / ctx.fill;
|
||||
}
|
||||
|
||||
info.peak = Math.max.apply(Math, info.line_peak);
|
||||
|
||||
/* remember current timestamp, calculate horizontal scale */
|
||||
if (!isNaN(last_timestamp))
|
||||
ctx.timestamp = last_timestamp;
|
||||
|
||||
var size = Math.floor(Math.log2(info.peak)),
|
||||
div = Math.pow(2, size - (size % 10)),
|
||||
mult = info.peak / div,
|
||||
mult = (mult < 5) ? 2 : ((mult < 50) ? 10 : ((mult < 500) ? 100 : 1000));
|
||||
|
||||
info.peak = info.peak + (mult * div) - (info.peak % (mult * div));
|
||||
|
||||
data_scale = ctx.height / info.peak;
|
||||
|
||||
/* plot data */
|
||||
for (var i = 0, di = 0; di < lines.length; di++) {
|
||||
if (lines[di] == null)
|
||||
continue;
|
||||
|
||||
var el = ctx.svg.firstElementChild.getElementById(lines[di].line),
|
||||
pt = '0,' + ctx.height,
|
||||
y = 0;
|
||||
|
||||
if (!el)
|
||||
continue;
|
||||
|
||||
for (var j = 0; j < values[i].length; j++) {
|
||||
var x = j * ctx.step;
|
||||
|
||||
y = ctx.height - Math.floor(values[i][j] * data_scale);
|
||||
//y -= Math.floor(y % (1 / data_scale));
|
||||
|
||||
pt += ' ' + x + ',' + y;
|
||||
}
|
||||
|
||||
pt += ' ' + ctx.width + ',' + y + ' ' + ctx.width + ',' + ctx.height;
|
||||
|
||||
el.setAttribute('points', pt);
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
info.label_25 = 0.25 * info.peak;
|
||||
info.label_50 = 0.50 * info.peak;
|
||||
info.label_75 = 0.75 * info.peak;
|
||||
|
||||
if (typeof(ctx.cb) == 'function')
|
||||
ctx.cb(ctx.svg, info);
|
||||
}
|
||||
}, this));
|
||||
}, this), pollInterval);
|
||||
},
|
||||
|
||||
loadSVG: function(src) {
|
||||
return L.Request.get(src).then(function(response) {
|
||||
if (!response.ok)
|
||||
throw new Error(response.statusText);
|
||||
|
||||
return E('div', {
|
||||
'style': 'width:100%;height:300px;border:1px solid #000;background:#fff'
|
||||
}, response.text());
|
||||
});
|
||||
},
|
||||
|
||||
render: function(data) {
|
||||
var svg = data[0],
|
||||
devs = data[1];
|
||||
|
||||
var v = E('div', {}, E('div'));
|
||||
|
||||
for (var i = 0; i < devs.length; i++) {
|
||||
var ifname = devs[i].getName();
|
||||
|
||||
if (!ifname)
|
||||
continue;
|
||||
|
||||
var csvg = svg.cloneNode(true);
|
||||
|
||||
v.firstElementChild.appendChild(E('div', { 'data-tab': ifname, 'data-tab-title': ifname }, [
|
||||
csvg,
|
||||
E('div', { 'class': 'right' }, E('small', { 'id': 'scale' }, '-')),
|
||||
E('br'),
|
||||
|
||||
E('div', { 'class': 'table', 'style': 'width:100%;table-layout:fixed' }, [
|
||||
E('div', { 'class': 'tr' }, [
|
||||
E('div', { 'class': 'td right top' }, E('strong', { 'style': 'border-bottom:2px solid blue' }, [ _('Inbound:') ])),
|
||||
E('div', { 'class': 'td', 'id': 'rx_bw_cur' }, rate(0, true)),
|
||||
|
||||
E('div', { 'class': 'td right top' }, E('strong', {}, [ _('Average:') ])),
|
||||
E('div', { 'class': 'td', 'id': 'rx_bw_avg' }, rate(0, true)),
|
||||
|
||||
E('div', { 'class': 'td right top' }, E('strong', {}, [ _('Peak:') ])),
|
||||
E('div', { 'class': 'td', 'id': 'rx_bw_peak' }, rate(0, true))
|
||||
]),
|
||||
E('div', { 'class': 'tr' }, [
|
||||
E('div', { 'class': 'td right top' }, E('strong', { 'style': 'border-bottom:2px solid green' }, [ _('Outbound:') ])),
|
||||
E('div', { 'class': 'td', 'id': 'tx_bw_cur' }, rate(0, true)),
|
||||
|
||||
E('div', { 'class': 'td right top' }, E('strong', {}, [ _('Average:') ])),
|
||||
E('div', { 'class': 'td', 'id': 'tx_bw_avg' }, rate(0, true)),
|
||||
|
||||
E('div', { 'class': 'td right top' }, E('strong', {}, [ _('Peak:') ])),
|
||||
E('div', { 'class': 'td', 'id': 'tx_bw_peak' }, rate(0, true))
|
||||
])
|
||||
])
|
||||
]));
|
||||
|
||||
this.updateGraph(ifname, csvg, [ { line: 'rx', counter: true }, null, { line: 'tx', counter: true } ], function(svg, info) {
|
||||
var G = svg.firstElementChild, tab = svg.parentNode;
|
||||
|
||||
G.getElementById('label_25').firstChild.data = rate(info.label_25).join('');
|
||||
G.getElementById('label_50').firstChild.data = rate(info.label_50).join('');
|
||||
G.getElementById('label_75').firstChild.data = rate(info.label_75).join('');
|
||||
|
||||
tab.querySelector('#scale').firstChild.data = _('(%d minute window, %d second interval)').format(info.timeframe, info.interval);
|
||||
|
||||
L.dom.content(tab.querySelector('#rx_bw_cur'), rate(info.line_current[0], true));
|
||||
L.dom.content(tab.querySelector('#rx_bw_avg'), rate(info.line_average[0], true));
|
||||
L.dom.content(tab.querySelector('#rx_bw_peak'), rate(info.line_peak[0], true));
|
||||
|
||||
L.dom.content(tab.querySelector('#tx_bw_cur'), rate(info.line_current[1], true));
|
||||
L.dom.content(tab.querySelector('#tx_bw_avg'), rate(info.line_average[1], true));
|
||||
L.dom.content(tab.querySelector('#tx_bw_peak'), rate(info.line_peak[1], true));
|
||||
});
|
||||
}
|
||||
|
||||
L.ui.tabs.initTabGroup(v.firstElementChild.childNodes);
|
||||
|
||||
this.pollData();
|
||||
|
||||
return v;
|
||||
},
|
||||
|
||||
handleSaveApply: null,
|
||||
handleSave: null,
|
||||
handleReset: null
|
||||
});
|
|
@ -0,0 +1,423 @@
|
|||
'use strict';
|
||||
'require rpc';
|
||||
|
||||
var callLuciRealtimeStats = rpc.declare({
|
||||
object: 'luci',
|
||||
method: 'getRealtimeStats',
|
||||
params: [ 'mode', 'device' ],
|
||||
expect: { result: [] }
|
||||
});
|
||||
|
||||
var callLuciConntrackList = rpc.declare({
|
||||
object: 'luci',
|
||||
method: 'getConntrackList',
|
||||
expect: { result: [] }
|
||||
});
|
||||
|
||||
var callNetworkRrdnsLookup = rpc.declare({
|
||||
object: 'network.rrdns',
|
||||
method: 'lookup',
|
||||
params: [ 'addrs', 'timeout', 'limit' ],
|
||||
expect: { '': {} }
|
||||
});
|
||||
|
||||
var graphPolls = [],
|
||||
pollInterval = 3,
|
||||
dns_cache = {},
|
||||
enableLookups = false;
|
||||
|
||||
var recheck_lookup_queue = {};
|
||||
|
||||
Math.log2 = Math.log2 || function(x) { return Math.log(x) * Math.LOG2E; };
|
||||
|
||||
return L.view.extend({
|
||||
load: function() {
|
||||
return Promise.all([
|
||||
this.loadSVG(L.resource('connections.svg'))
|
||||
]);
|
||||
},
|
||||
|
||||
updateGraph: function(svg, lines, cb) {
|
||||
var G = svg.firstElementChild;
|
||||
|
||||
var view = document.querySelector('#view');
|
||||
|
||||
var width = view.offsetWidth - 2;
|
||||
var height = 300 - 2;
|
||||
var step = 5;
|
||||
|
||||
var data_wanted = Math.floor(width / step);
|
||||
|
||||
var data_values = [],
|
||||
line_elements = [];
|
||||
|
||||
for (var i = 0; i < lines.length; i++)
|
||||
if (lines[i] != null)
|
||||
data_values.push([]);
|
||||
|
||||
var info = {
|
||||
line_current: [],
|
||||
line_average: [],
|
||||
line_peak: []
|
||||
};
|
||||
|
||||
/* prefill datasets */
|
||||
for (var i = 0; i < data_values.length; i++)
|
||||
for (var j = 0; j < data_wanted; j++)
|
||||
data_values[i][j] = 0;
|
||||
|
||||
/* plot horizontal time interval lines */
|
||||
for (var i = width % (step * 60); i < width; i += step * 60) {
|
||||
var line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
|
||||
line.setAttribute('x1', i);
|
||||
line.setAttribute('y1', 0);
|
||||
line.setAttribute('x2', i);
|
||||
line.setAttribute('y2', '100%');
|
||||
line.setAttribute('style', 'stroke:black;stroke-width:0.1');
|
||||
|
||||
var text = document.createElementNS('http://www.w3.org/2000/svg', 'text');
|
||||
text.setAttribute('x', i + 5);
|
||||
text.setAttribute('y', 15);
|
||||
text.setAttribute('style', 'fill:#eee; font-size:9pt; font-family:sans-serif; text-shadow:1px 1px 1px #000');
|
||||
text.appendChild(document.createTextNode(Math.round((width - i) / step / 60) + 'm'));
|
||||
|
||||
G.appendChild(line);
|
||||
G.appendChild(text);
|
||||
}
|
||||
|
||||
info.interval = pollInterval;
|
||||
info.timeframe = data_wanted / 60;
|
||||
|
||||
graphPolls.push({
|
||||
svg: svg,
|
||||
lines: lines,
|
||||
cb: cb,
|
||||
info: info,
|
||||
width: width,
|
||||
height: height,
|
||||
step: step,
|
||||
values: data_values,
|
||||
timestamp: 0,
|
||||
fill: 1
|
||||
});
|
||||
},
|
||||
|
||||
updateConntrack: function(conn) {
|
||||
var lookup_queue = [ ];
|
||||
var rows = [];
|
||||
|
||||
conn.sort(function(a, b) {
|
||||
return b.bytes - a.bytes;
|
||||
});
|
||||
|
||||
for (var i = 0; i < conn.length; i++)
|
||||
{
|
||||
var c = conn[i];
|
||||
|
||||
if ((c.src == '127.0.0.1' && c.dst == '127.0.0.1') ||
|
||||
(c.src == '::1' && c.dst == '::1'))
|
||||
continue;
|
||||
|
||||
if (!dns_cache[c.src] && lookup_queue.indexOf(c.src) == -1)
|
||||
lookup_queue.push(c.src);
|
||||
|
||||
if (!dns_cache[c.dst] && lookup_queue.indexOf(c.dst) == -1)
|
||||
lookup_queue.push(c.dst);
|
||||
|
||||
var src = dns_cache[c.src] || (c.layer3 == 'ipv6' ? '[' + c.src + ']' : c.src);
|
||||
var dst = dns_cache[c.dst] || (c.layer3 == 'ipv6' ? '[' + c.dst + ']' : c.dst);
|
||||
|
||||
rows.push([
|
||||
c.layer3.toUpperCase(),
|
||||
c.layer4.toUpperCase(),
|
||||
c.hasOwnProperty('sport') ? (src + ':' + c.sport) : src,
|
||||
c.hasOwnProperty('dport') ? (dst + ':' + c.dport) : dst,
|
||||
'%1024.2mB (%d %s)'.format(c.bytes, c.packets, _('Pkts.'))
|
||||
]);
|
||||
}
|
||||
|
||||
cbi_update_table('#connections', rows, E('em', _('No information available')));
|
||||
|
||||
if (enableLookups && lookup_queue.length > 0) {
|
||||
var reduced_lookup_queue = lookup_queue;
|
||||
|
||||
if (lookup_queue.length > 100)
|
||||
reduced_lookup_queue = lookup_queue.slice(0, 100);
|
||||
|
||||
callNetworkRrdnsLookup(reduced_lookup_queue, 5000, 1000).then(function(replies) {
|
||||
for (var index in reduced_lookup_queue) {
|
||||
var address = reduced_lookup_queue[index];
|
||||
|
||||
if (!address)
|
||||
continue;
|
||||
|
||||
if (replies[address]) {
|
||||
dns_cache[address] = replies[address];
|
||||
lookup_queue.splice(reduced_lookup_queue.indexOf(address), 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (recheck_lookup_queue[address] > 2) {
|
||||
dns_cache[address] = (address.match(/:/)) ? '[' + address + ']' : address;
|
||||
lookup_queue.splice(index, 1);
|
||||
}
|
||||
else {
|
||||
recheck_lookup_queue[address] = (recheck_lookup_queue[address] || 0) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
var btn = document.querySelector('.btn.toggle-lookups');
|
||||
if (btn) {
|
||||
btn.firstChild.data = enableLookups ? _('Disable DNS lookups') : _('Enable DNS lookups');
|
||||
btn.classList.remove('spinning');
|
||||
btn.disabled = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
pollData: function() {
|
||||
L.Poll.add(L.bind(function() {
|
||||
var tasks = [
|
||||
L.resolveDefault(callLuciConntrackList(), [])
|
||||
];
|
||||
|
||||
for (var i = 0; i < graphPolls.length; i++) {
|
||||
var ctx = graphPolls[i];
|
||||
tasks.push(L.resolveDefault(callLuciRealtimeStats('conntrack'), []));
|
||||
}
|
||||
|
||||
return Promise.all(tasks).then(L.bind(function(datasets) {
|
||||
this.updateConntrack(datasets[0]);
|
||||
|
||||
for (var gi = 0; gi < graphPolls.length; gi++) {
|
||||
var ctx = graphPolls[gi],
|
||||
data = datasets[gi + 1],
|
||||
values = ctx.values,
|
||||
lines = ctx.lines,
|
||||
info = ctx.info;
|
||||
|
||||
var data_scale = 0;
|
||||
var data_wanted = Math.floor(ctx.width / ctx.step);
|
||||
var last_timestamp = NaN;
|
||||
|
||||
for (var i = 0, di = 0; di < lines.length; di++) {
|
||||
if (lines[di] == null)
|
||||
continue;
|
||||
|
||||
var multiply = (lines[di].multiply != null) ? lines[di].multiply : 1,
|
||||
offset = (lines[di].offset != null) ? lines[di].offset : 0;
|
||||
|
||||
for (var j = ctx.timestamp ? 0 : 1; j < data.length; j++) {
|
||||
/* skip overlapping entries */
|
||||
if (data[j][0] <= ctx.timestamp)
|
||||
continue;
|
||||
|
||||
if (i == 0) {
|
||||
ctx.fill++;
|
||||
last_timestamp = data[j][0];
|
||||
}
|
||||
|
||||
info.line_current[i] = data[j][di + 1] * multiply;
|
||||
info.line_current[i] -= Math.min(info.line_current[i], offset);
|
||||
values[i].push(info.line_current[i]);
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
/* cut off outdated entries */
|
||||
ctx.fill = Math.min(ctx.fill, data_wanted);
|
||||
|
||||
for (var i = 0; i < values.length; i++) {
|
||||
var len = values[i].length;
|
||||
values[i] = values[i].slice(len - data_wanted, len);
|
||||
|
||||
/* find peaks, averages */
|
||||
info.line_peak[i] = NaN;
|
||||
info.line_average[i] = 0;
|
||||
|
||||
for (var j = 0; j < values[i].length; j++) {
|
||||
info.line_peak[i] = isNaN(info.line_peak[i]) ? values[i][j] : Math.max(info.line_peak[i], values[i][j]);
|
||||
info.line_average[i] += values[i][j];
|
||||
}
|
||||
|
||||
info.line_average[i] = info.line_average[i] / ctx.fill;
|
||||
}
|
||||
|
||||
info.peak = Math.max.apply(Math, info.line_peak);
|
||||
|
||||
/* remember current timestamp, calculate horizontal scale */
|
||||
if (!isNaN(last_timestamp))
|
||||
ctx.timestamp = last_timestamp;
|
||||
|
||||
var size = Math.floor(Math.log2(info.peak)),
|
||||
div = Math.pow(2, size - (size % 10)),
|
||||
mult = info.peak / div,
|
||||
mult = (mult < 5) ? 2 : ((mult < 50) ? 10 : ((mult < 500) ? 100 : 1000));
|
||||
|
||||
info.peak = info.peak + (mult * div) - (info.peak % (mult * div));
|
||||
|
||||
data_scale = ctx.height / info.peak;
|
||||
|
||||
/* plot data */
|
||||
for (var i = 0, di = 0; di < lines.length; di++) {
|
||||
if (lines[di] == null)
|
||||
continue;
|
||||
|
||||
var el = ctx.svg.firstElementChild.getElementById(lines[di].line),
|
||||
pt = '0,' + ctx.height,
|
||||
y = 0;
|
||||
|
||||
if (!el)
|
||||
continue;
|
||||
|
||||
for (var j = 0; j < values[i].length; j++) {
|
||||
var x = j * ctx.step;
|
||||
|
||||
y = ctx.height - Math.floor(values[i][j] * data_scale);
|
||||
//y -= Math.floor(y % (1 / data_scale));
|
||||
|
||||
pt += ' ' + x + ',' + y;
|
||||
}
|
||||
|
||||
pt += ' ' + ctx.width + ',' + y + ' ' + ctx.width + ',' + ctx.height;
|
||||
|
||||
el.setAttribute('points', pt);
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
info.label_25 = 0.25 * info.peak;
|
||||
info.label_50 = 0.50 * info.peak;
|
||||
info.label_75 = 0.75 * info.peak;
|
||||
|
||||
if (typeof(ctx.cb) == 'function')
|
||||
ctx.cb(ctx.svg, info);
|
||||
}
|
||||
}, this));
|
||||
}, this), pollInterval);
|
||||
},
|
||||
|
||||
loadSVG: function(src) {
|
||||
return L.Request.get(src).then(function(response) {
|
||||
if (!response.ok)
|
||||
throw new Error(response.statusText);
|
||||
|
||||
return E('div', {
|
||||
'style': 'width:100%;height:300px;border:1px solid #000;background:#fff'
|
||||
}, response.text());
|
||||
});
|
||||
},
|
||||
|
||||
render: function(data) {
|
||||
var svg = data[0];
|
||||
|
||||
var v = E([], [
|
||||
svg,
|
||||
E('div', { 'class': 'right' }, E('small', { 'id': 'scale' }, '-')),
|
||||
E('br'),
|
||||
|
||||
E('div', { 'class': 'table', 'style': 'width:100%;table-layout:fixed' }, [
|
||||
E('div', { 'class': 'tr' }, [
|
||||
E('div', { 'class': 'td right top' }, E('strong', { 'style': 'border-bottom:2px solid blue' }, [ _('UDP:') ])),
|
||||
E('div', { 'class': 'td', 'id': 'lb_udp_cur' }, [ '0' ]),
|
||||
|
||||
E('div', { 'class': 'td right top' }, E('strong', {}, [ _('Average:') ])),
|
||||
E('div', { 'class': 'td', 'id': 'lb_udp_avg' }, [ '0' ]),
|
||||
|
||||
E('div', { 'class': 'td right top' }, E('strong', {}, [ _('Peak:') ])),
|
||||
E('div', { 'class': 'td', 'id': 'lb_udp_peak' }, [ '0' ])
|
||||
]),
|
||||
E('div', { 'class': 'tr' }, [
|
||||
E('div', { 'class': 'td right top' }, E('strong', { 'style': 'border-bottom:2px solid green' }, [ _('TCP:') ])),
|
||||
E('div', { 'class': 'td', 'id': 'lb_tcp_cur' }, [ '0' ]),
|
||||
|
||||
E('div', { 'class': 'td right top' }, E('strong', {}, [ _('Average:') ])),
|
||||
E('div', { 'class': 'td', 'id': 'lb_tcp_avg' }, [ '0' ]),
|
||||
|
||||
E('div', { 'class': 'td right top' }, E('strong', {}, [ _('Peak:') ])),
|
||||
E('div', { 'class': 'td', 'id': 'lb_tcp_peak' }, [ '0' ])
|
||||
]),
|
||||
E('div', { 'class': 'tr' }, [
|
||||
E('div', { 'class': 'td right top' }, E('strong', { 'style': 'border-bottom:2px solid red' }, [ _('Other:') ])),
|
||||
E('div', { 'class': 'td', 'id': 'lb_otr_cur' }, [ '0' ]),
|
||||
|
||||
E('div', { 'class': 'td right top' }, E('strong', {}, [ _('Average:') ])),
|
||||
E('div', { 'class': 'td', 'id': 'lb_otr_avg' }, [ '0' ]),
|
||||
|
||||
E('div', { 'class': 'td right top' }, E('strong', {}, [ _('Peak:') ])),
|
||||
E('div', { 'class': 'td', 'id': 'lb_otr_peak' }, [ '0' ])
|
||||
])
|
||||
]),
|
||||
|
||||
E('div', { 'class': 'right' }, [
|
||||
E('button', {
|
||||
'class': 'btn toggle-lookups',
|
||||
'click': function(ev) {
|
||||
if (!enableLookups) {
|
||||
ev.currentTarget.classList.add('spinning');
|
||||
ev.currentTarget.disabled = true;
|
||||
enableLookups = true;
|
||||
}
|
||||
else {
|
||||
ev.currentTarget.firstChild.data = _('Enable DNS lookups');
|
||||
enableLookups = false;
|
||||
}
|
||||
|
||||
this.blur();
|
||||
}
|
||||
}, [ enableLookups ? _('Disable DNS lookups') : _('Enable DNS lookups') ])
|
||||
]),
|
||||
|
||||
E('br'),
|
||||
|
||||
E('div', { 'class': 'cbi-section-node' }, [
|
||||
E('div', { 'class': 'table', 'id': 'connections' }, [
|
||||
E('div', { 'class': 'tr table-titles' }, [
|
||||
E('div', { 'class': 'th col-2 hide-xs' }, [ _('Network') ]),
|
||||
E('div', { 'class': 'th col-2' }, [ _('Protocol') ]),
|
||||
E('div', { 'class': 'th col-7' }, [ _('Source') ]),
|
||||
E('div', { 'class': 'th col-7' }, [ _('Destination') ]),
|
||||
E('div', { 'class': 'th col-4' }, [ _('Transfer') ])
|
||||
]),
|
||||
E('div', { 'class': 'tr placeholder' }, [
|
||||
E('div', { 'class': 'td' }, [
|
||||
E('em', {}, [ _('Collecting data...') ])
|
||||
])
|
||||
])
|
||||
])
|
||||
])
|
||||
]);
|
||||
|
||||
this.updateGraph(svg, [ { line: 'udp' }, { line: 'tcp' }, { line: 'other' } ], function(svg, info) {
|
||||
var G = svg.firstElementChild, tab = svg.parentNode;
|
||||
|
||||
G.getElementById('label_25').firstChild.data = '%d'.format(info.label_25);
|
||||
G.getElementById('label_50').firstChild.data = '%d'.format(info.label_50);
|
||||
G.getElementById('label_75').firstChild.data = '%d'.format(info.label_75);
|
||||
|
||||
tab.querySelector('#scale').firstChild.data = _('(%d minute window, %d second interval)').format(info.timeframe, info.interval);
|
||||
|
||||
tab.querySelector('#lb_udp_cur').firstChild.data = '%d'.format(info.line_current[0]);
|
||||
tab.querySelector('#lb_udp_avg').firstChild.data = '%d'.format(info.line_average[0]);
|
||||
tab.querySelector('#lb_udp_peak').firstChild.data = '%d'.format(info.line_peak[0]);
|
||||
|
||||
tab.querySelector('#lb_tcp_cur').firstChild.data = '%d'.format(info.line_current[1]);
|
||||
tab.querySelector('#lb_tcp_avg').firstChild.data = '%d'.format(info.line_average[1]);
|
||||
tab.querySelector('#lb_tcp_peak').firstChild.data = '%d'.format(info.line_peak[1]);
|
||||
|
||||
tab.querySelector('#lb_otr_cur').firstChild.data = '%d'.format(info.line_current[2]);
|
||||
tab.querySelector('#lb_otr_avg').firstChild.data = '%d'.format(info.line_average[2]);
|
||||
tab.querySelector('#lb_otr_peak').firstChild.data = '%d'.format(info.line_peak[2]);
|
||||
});
|
||||
|
||||
this.pollData();
|
||||
|
||||
return v;
|
||||
},
|
||||
|
||||
handleSaveApply: null,
|
||||
handleSave: null,
|
||||
handleReset: null
|
||||
});
|
|
@ -0,0 +1,290 @@
|
|||
'use strict';
|
||||
'require rpc';
|
||||
|
||||
var callLuciRealtimeStats = rpc.declare({
|
||||
object: 'luci',
|
||||
method: 'getRealtimeStats',
|
||||
params: [ 'mode', 'device' ],
|
||||
expect: { result: [] }
|
||||
});
|
||||
|
||||
var graphPolls = [],
|
||||
pollInterval = 3;
|
||||
|
||||
Math.log2 = Math.log2 || function(x) { return Math.log(x) * Math.LOG2E; };
|
||||
|
||||
return L.view.extend({
|
||||
load: function() {
|
||||
return Promise.all([
|
||||
this.loadSVG(L.resource('load.svg'))
|
||||
]);
|
||||
},
|
||||
|
||||
updateGraph: function(svg, lines, cb) {
|
||||
var G = svg.firstElementChild;
|
||||
|
||||
var view = document.querySelector('#view');
|
||||
|
||||
var width = view.offsetWidth - 2;
|
||||
var height = 300 - 2;
|
||||
var step = 5;
|
||||
|
||||
var data_wanted = Math.floor(width / step);
|
||||
|
||||
var data_values = [],
|
||||
line_elements = [];
|
||||
|
||||
for (var i = 0; i < lines.length; i++)
|
||||
if (lines[i] != null)
|
||||
data_values.push([]);
|
||||
|
||||
var info = {
|
||||
line_current: [],
|
||||
line_average: [],
|
||||
line_peak: []
|
||||
};
|
||||
|
||||
/* prefill datasets */
|
||||
for (var i = 0; i < data_values.length; i++)
|
||||
for (var j = 0; j < data_wanted; j++)
|
||||
data_values[i][j] = 0;
|
||||
|
||||
/* plot horizontal time interval lines */
|
||||
for (var i = width % (step * 60); i < width; i += step * 60) {
|
||||
var line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
|
||||
line.setAttribute('x1', i);
|
||||
line.setAttribute('y1', 0);
|
||||
line.setAttribute('x2', i);
|
||||
line.setAttribute('y2', '100%');
|
||||
line.setAttribute('style', 'stroke:black;stroke-width:0.1');
|
||||
|
||||
var text = document.createElementNS('http://www.w3.org/2000/svg', 'text');
|
||||
text.setAttribute('x', i + 5);
|
||||
text.setAttribute('y', 15);
|
||||
text.setAttribute('style', 'fill:#eee; font-size:9pt; font-family:sans-serif; text-shadow:1px 1px 1px #000');
|
||||
text.appendChild(document.createTextNode(Math.round((width - i) / step / 60) + 'm'));
|
||||
|
||||
G.appendChild(line);
|
||||
G.appendChild(text);
|
||||
}
|
||||
|
||||
info.interval = pollInterval;
|
||||
info.timeframe = data_wanted / 60;
|
||||
|
||||
graphPolls.push({
|
||||
svg: svg,
|
||||
lines: lines,
|
||||
cb: cb,
|
||||
info: info,
|
||||
width: width,
|
||||
height: height,
|
||||
step: step,
|
||||
values: data_values,
|
||||
timestamp: 0,
|
||||
fill: 1
|
||||
});
|
||||
},
|
||||
|
||||
pollData: function() {
|
||||
L.Poll.add(L.bind(function() {
|
||||
var tasks = [];
|
||||
|
||||
for (var i = 0; i < graphPolls.length; i++) {
|
||||
var ctx = graphPolls[i];
|
||||
tasks.push(L.resolveDefault(callLuciRealtimeStats('load'), []));
|
||||
}
|
||||
|
||||
return Promise.all(tasks).then(L.bind(function(datasets) {
|
||||
for (var gi = 0; gi < graphPolls.length; gi++) {
|
||||
var ctx = graphPolls[gi],
|
||||
data = datasets[gi],
|
||||
values = ctx.values,
|
||||
lines = ctx.lines,
|
||||
info = ctx.info;
|
||||
|
||||
var data_scale = 0;
|
||||
var data_wanted = Math.floor(ctx.width / ctx.step);
|
||||
var last_timestamp = NaN;
|
||||
|
||||
for (var i = 0, di = 0; di < lines.length; di++) {
|
||||
if (lines[di] == null)
|
||||
continue;
|
||||
|
||||
var multiply = (lines[di].multiply != null) ? lines[di].multiply : 1,
|
||||
offset = (lines[di].offset != null) ? lines[di].offset : 0;
|
||||
|
||||
for (var j = ctx.timestamp ? 0 : 1; j < data.length; j++) {
|
||||
/* skip overlapping entries */
|
||||
if (data[j][0] <= ctx.timestamp)
|
||||
continue;
|
||||
|
||||
if (i == 0) {
|
||||
ctx.fill++;
|
||||
last_timestamp = data[j][0];
|
||||
}
|
||||
|
||||
info.line_current[i] = data[j][di + 1] * multiply;
|
||||
info.line_current[i] -= Math.min(info.line_current[i], offset);
|
||||
values[i].push(info.line_current[i]);
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
/* cut off outdated entries */
|
||||
ctx.fill = Math.min(ctx.fill, data_wanted);
|
||||
|
||||
for (var i = 0; i < values.length; i++) {
|
||||
var len = values[i].length;
|
||||
values[i] = values[i].slice(len - data_wanted, len);
|
||||
|
||||
/* find peaks, averages */
|
||||
info.line_peak[i] = NaN;
|
||||
info.line_average[i] = 0;
|
||||
|
||||
for (var j = 0; j < values[i].length; j++) {
|
||||
info.line_peak[i] = isNaN(info.line_peak[i]) ? values[i][j] : Math.max(info.line_peak[i], values[i][j]);
|
||||
info.line_average[i] += values[i][j];
|
||||
}
|
||||
|
||||
info.line_average[i] = info.line_average[i] / ctx.fill;
|
||||
}
|
||||
|
||||
info.peak = Math.max.apply(Math, info.line_peak);
|
||||
|
||||
/* remember current timestamp, calculate horizontal scale */
|
||||
if (!isNaN(last_timestamp))
|
||||
ctx.timestamp = last_timestamp;
|
||||
|
||||
var size = Math.floor(Math.log2(info.peak)),
|
||||
div = Math.pow(2, size - (size % 10)),
|
||||
mult = info.peak / div,
|
||||
mult = (mult < 5) ? 2 : ((mult < 50) ? 10 : ((mult < 500) ? 100 : 1000));
|
||||
|
||||
info.peak = info.peak + (mult * div) - (info.peak % (mult * div));
|
||||
|
||||
data_scale = ctx.height / info.peak;
|
||||
|
||||
/* plot data */
|
||||
for (var i = 0, di = 0; di < lines.length; di++) {
|
||||
if (lines[di] == null)
|
||||
continue;
|
||||
|
||||
var el = ctx.svg.firstElementChild.getElementById(lines[di].line),
|
||||
pt = '0,' + ctx.height,
|
||||
y = 0;
|
||||
|
||||
if (!el)
|
||||
continue;
|
||||
|
||||
for (var j = 0; j < values[i].length; j++) {
|
||||
var x = j * ctx.step;
|
||||
|
||||
y = ctx.height - Math.floor(values[i][j] * data_scale);
|
||||
//y -= Math.floor(y % (1 / data_scale));
|
||||
|
||||
pt += ' ' + x + ',' + y;
|
||||
}
|
||||
|
||||
pt += ' ' + ctx.width + ',' + y + ' ' + ctx.width + ',' + ctx.height;
|
||||
|
||||
el.setAttribute('points', pt);
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
info.label_25 = 0.25 * info.peak;
|
||||
info.label_50 = 0.50 * info.peak;
|
||||
info.label_75 = 0.75 * info.peak;
|
||||
|
||||
if (typeof(ctx.cb) == 'function')
|
||||
ctx.cb(ctx.svg, info);
|
||||
}
|
||||
}, this));
|
||||
}, this), pollInterval);
|
||||
},
|
||||
|
||||
loadSVG: function(src) {
|
||||
return L.Request.get(src).then(function(response) {
|
||||
if (!response.ok)
|
||||
throw new Error(response.statusText);
|
||||
|
||||
return E('div', {
|
||||
'style': 'width:100%;height:300px;border:1px solid #000;background:#fff'
|
||||
}, response.text());
|
||||
});
|
||||
},
|
||||
|
||||
render: function(data) {
|
||||
var svg = data[0];
|
||||
|
||||
var v = E([], [
|
||||
svg,
|
||||
E('div', { 'class': 'right' }, E('small', { 'id': 'scale' }, '-')),
|
||||
E('br'),
|
||||
|
||||
E('div', { 'class': 'table', 'style': 'width:100%;table-layout:fixed' }, [
|
||||
E('div', { 'class': 'tr' }, [
|
||||
E('div', { 'class': 'td right top' }, E('strong', { 'style': 'border-bottom:2px solid #f00' }, [ _('1 Minute Load:') ])),
|
||||
E('div', { 'class': 'td', 'id': 'lb_load01_cur' }, [ '0.00' ]),
|
||||
|
||||
E('div', { 'class': 'td right top' }, E('strong', {}, [ _('Average:') ])),
|
||||
E('div', { 'class': 'td', 'id': 'lb_load01_avg' }, [ '0.00' ]),
|
||||
|
||||
E('div', { 'class': 'td right top' }, E('strong', {}, [ _('Peak:') ])),
|
||||
E('div', { 'class': 'td', 'id': 'lb_load01_peak' }, [ '0.00' ])
|
||||
]),
|
||||
E('div', { 'class': 'tr' }, [
|
||||
E('div', { 'class': 'td right top' }, E('strong', { 'style': 'border-bottom:2px solid #f60' }, [ _('5 Minute Load:') ])),
|
||||
E('div', { 'class': 'td', 'id': 'lb_load05_cur' }, [ '0.00' ]),
|
||||
|
||||
E('div', { 'class': 'td right top' }, E('strong', {}, [ _('Average:') ])),
|
||||
E('div', { 'class': 'td', 'id': 'lb_load05_avg' }, [ '0.00' ]),
|
||||
|
||||
E('div', { 'class': 'td right top' }, E('strong', {}, [ _('Peak:') ])),
|
||||
E('div', { 'class': 'td', 'id': 'lb_load05_peak' }, [ '0.00' ])
|
||||
]),
|
||||
E('div', { 'class': 'tr' }, [
|
||||
E('div', { 'class': 'td right top' }, E('strong', { 'style': 'border-bottom:2px solid #fa0' }, [ _('15 Minute Load:') ])),
|
||||
E('div', { 'class': 'td', 'id': 'lb_load15_cur' }, [ '0.00' ]),
|
||||
|
||||
E('div', { 'class': 'td right top' }, E('strong', {}, [ _('Average:') ])),
|
||||
E('div', { 'class': 'td', 'id': 'lb_load15_avg' }, [ '0.00' ]),
|
||||
|
||||
E('div', { 'class': 'td right top' }, E('strong', {}, [ _('Peak:') ])),
|
||||
E('div', { 'class': 'td', 'id': 'lb_load15_peak' }, [ '0.00' ])
|
||||
])
|
||||
])
|
||||
]);
|
||||
|
||||
this.updateGraph(svg, [ { line: 'load01' }, { line: 'load05' }, { line: 'load15' } ], function(svg, info) {
|
||||
var G = svg.firstElementChild, tab = svg.parentNode;
|
||||
|
||||
G.getElementById('label_25').firstChild.data = '%.2f'.format(info.label_25 / 100);
|
||||
G.getElementById('label_50').firstChild.data = '%.2f'.format(info.label_50 / 100);
|
||||
G.getElementById('label_75').firstChild.data = '%.2f'.format(info.label_75 / 100);
|
||||
|
||||
tab.querySelector('#scale').firstChild.data = _('(%d minute window, %d second interval)').format(info.timeframe, info.interval);
|
||||
|
||||
tab.querySelector('#lb_load01_cur').firstChild.data = '%.2f'.format(info.line_current[0] / 100);
|
||||
tab.querySelector('#lb_load01_avg').firstChild.data = '%.2f'.format(info.line_average[0] / 100);
|
||||
tab.querySelector('#lb_load01_peak').firstChild.data = '%.2f'.format(info.line_peak[0] / 100);
|
||||
|
||||
tab.querySelector('#lb_load05_cur').firstChild.data = '%.2f'.format(info.line_current[1] / 100);
|
||||
tab.querySelector('#lb_load05_avg').firstChild.data = '%.2f'.format(info.line_average[1] / 100);
|
||||
tab.querySelector('#lb_load05_peak').firstChild.data = '%.2f'.format(info.line_peak[1] / 100);
|
||||
|
||||
tab.querySelector('#lb_load15_cur').firstChild.data = '%.2f'.format(info.line_current[2] / 100);
|
||||
tab.querySelector('#lb_load15_avg').firstChild.data = '%.2f'.format(info.line_average[2] / 100);
|
||||
tab.querySelector('#lb_load15_peak').firstChild.data = '%.2f'.format(info.line_peak[2] / 100);
|
||||
});
|
||||
|
||||
this.pollData();
|
||||
|
||||
return v;
|
||||
},
|
||||
|
||||
handleSaveApply: null,
|
||||
handleSave: null,
|
||||
handleReset: null
|
||||
});
|
|
@ -0,0 +1,339 @@
|
|||
'use strict';
|
||||
'require rpc';
|
||||
'require network';
|
||||
|
||||
var callLuciRealtimeStats = rpc.declare({
|
||||
object: 'luci',
|
||||
method: 'getRealtimeStats',
|
||||
params: [ 'mode', 'device' ],
|
||||
expect: { result: [] }
|
||||
});
|
||||
|
||||
var graphPolls = [],
|
||||
pollInterval = 3;
|
||||
|
||||
Math.log2 = Math.log2 || function(x) { return Math.log(x) * Math.LOG2E; };
|
||||
|
||||
return L.view.extend({
|
||||
load: function() {
|
||||
return Promise.all([
|
||||
this.loadSVG(L.resource('wireless.svg')),
|
||||
this.loadSVG(L.resource('wifirate.svg')),
|
||||
network.getWifiDevices().then(function(radios) {
|
||||
var tasks = [], all_networks = [];
|
||||
|
||||
for (var i = 0; i < radios.length; i++)
|
||||
tasks.push(radios[i].getWifiNetworks().then(function(networks) {
|
||||
all_networks.push.apply(all_networks, networks);
|
||||
}));
|
||||
|
||||
return Promise.all(tasks).then(function() {
|
||||
return all_networks;
|
||||
});
|
||||
})
|
||||
]);
|
||||
},
|
||||
|
||||
updateGraph: function(ifname, svg, lines, cb) {
|
||||
var G = svg.firstElementChild;
|
||||
|
||||
var view = document.querySelector('#view');
|
||||
|
||||
var width = view.offsetWidth - 2;
|
||||
var height = 300 - 2;
|
||||
var step = 5;
|
||||
|
||||
var data_wanted = Math.floor(width / step);
|
||||
|
||||
var data_values = [],
|
||||
line_elements = [];
|
||||
|
||||
for (var i = 0; i < lines.length; i++)
|
||||
if (lines[i] != null)
|
||||
data_values.push([]);
|
||||
|
||||
var info = {
|
||||
line_current: [],
|
||||
line_average: [],
|
||||
line_peak: []
|
||||
};
|
||||
|
||||
/* prefill datasets */
|
||||
for (var i = 0; i < data_values.length; i++)
|
||||
for (var j = 0; j < data_wanted; j++)
|
||||
data_values[i][j] = 0;
|
||||
|
||||
/* plot horizontal time interval lines */
|
||||
for (var i = width % (step * 60); i < width; i += step * 60) {
|
||||
var line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
|
||||
line.setAttribute('x1', i);
|
||||
line.setAttribute('y1', 0);
|
||||
line.setAttribute('x2', i);
|
||||
line.setAttribute('y2', '100%');
|
||||
line.setAttribute('style', 'stroke:black;stroke-width:0.1');
|
||||
|
||||
var text = document.createElementNS('http://www.w3.org/2000/svg', 'text');
|
||||
text.setAttribute('x', i + 5);
|
||||
text.setAttribute('y', 15);
|
||||
text.setAttribute('style', 'fill:#eee; font-size:9pt; font-family:sans-serif; text-shadow:1px 1px 1px #000');
|
||||
text.appendChild(document.createTextNode(Math.round((width - i) / step / 60) + 'm'));
|
||||
|
||||
G.appendChild(line);
|
||||
G.appendChild(text);
|
||||
}
|
||||
|
||||
info.interval = pollInterval;
|
||||
info.timeframe = data_wanted / 60;
|
||||
|
||||
graphPolls.push({
|
||||
ifname: ifname,
|
||||
svg: svg,
|
||||
lines: lines,
|
||||
cb: cb,
|
||||
info: info,
|
||||
width: width,
|
||||
height: height,
|
||||
step: step,
|
||||
values: data_values,
|
||||
timestamp: 0,
|
||||
fill: 1
|
||||
});
|
||||
},
|
||||
|
||||
pollData: function() {
|
||||
L.Poll.add(L.bind(function() {
|
||||
var tasks = [];
|
||||
|
||||
for (var i = 0; i < graphPolls.length; i++) {
|
||||
var ctx = graphPolls[i];
|
||||
tasks.push(L.resolveDefault(callLuciRealtimeStats('wireless', ctx.ifname), []));
|
||||
}
|
||||
|
||||
return Promise.all(tasks).then(L.bind(function(datasets) {
|
||||
for (var gi = 0; gi < graphPolls.length; gi++) {
|
||||
var ctx = graphPolls[gi],
|
||||
data = datasets[gi],
|
||||
values = ctx.values,
|
||||
lines = ctx.lines,
|
||||
info = ctx.info;
|
||||
|
||||
var data_scale = 0;
|
||||
var data_wanted = Math.floor(ctx.width / ctx.step);
|
||||
var last_timestamp = NaN;
|
||||
|
||||
for (var i = 0, di = 0; di < lines.length; di++) {
|
||||
if (lines[di] == null)
|
||||
continue;
|
||||
|
||||
var multiply = (lines[di].multiply != null) ? lines[di].multiply : 1,
|
||||
offset = (lines[di].offset != null) ? lines[di].offset : 0;
|
||||
|
||||
for (var j = ctx.timestamp ? 0 : 1; j < data.length; j++) {
|
||||
/* skip overlapping entries */
|
||||
if (data[j][0] <= ctx.timestamp)
|
||||
continue;
|
||||
|
||||
if (i == 0) {
|
||||
ctx.fill++;
|
||||
last_timestamp = data[j][0];
|
||||
}
|
||||
|
||||
info.line_current[i] = data[j][di + 1] * multiply;
|
||||
info.line_current[i] -= Math.min(info.line_current[i], offset);
|
||||
values[i].push(info.line_current[i]);
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
/* cut off outdated entries */
|
||||
ctx.fill = Math.min(ctx.fill, data_wanted);
|
||||
|
||||
for (var i = 0; i < values.length; i++) {
|
||||
var len = values[i].length;
|
||||
values[i] = values[i].slice(len - data_wanted, len);
|
||||
|
||||
/* find peaks, averages */
|
||||
info.line_peak[i] = NaN;
|
||||
info.line_average[i] = 0;
|
||||
|
||||
for (var j = 0; j < values[i].length; j++) {
|
||||
info.line_peak[i] = isNaN(info.line_peak[i]) ? values[i][j] : Math.max(info.line_peak[i], values[i][j]);
|
||||
info.line_average[i] += values[i][j];
|
||||
}
|
||||
|
||||
info.line_average[i] = info.line_average[i] / ctx.fill;
|
||||
}
|
||||
|
||||
info.peak = Math.max.apply(Math, info.line_peak);
|
||||
|
||||
/* remember current timestamp, calculate horizontal scale */
|
||||
if (!isNaN(last_timestamp))
|
||||
ctx.timestamp = last_timestamp;
|
||||
|
||||
var size = Math.floor(Math.log2(info.peak)),
|
||||
div = Math.pow(2, size - (size % 10)),
|
||||
mult = info.peak / div,
|
||||
mult = (mult < 5) ? 2 : ((mult < 50) ? 10 : ((mult < 500) ? 100 : 1000));
|
||||
|
||||
info.peak = info.peak + (mult * div) - (info.peak % (mult * div));
|
||||
|
||||
data_scale = ctx.height / info.peak;
|
||||
|
||||
/* plot data */
|
||||
for (var i = 0, di = 0; di < lines.length; di++) {
|
||||
if (lines[di] == null)
|
||||
continue;
|
||||
|
||||
var el = ctx.svg.firstElementChild.getElementById(lines[di].line),
|
||||
pt = '0,' + ctx.height,
|
||||
y = 0;
|
||||
|
||||
if (!el)
|
||||
continue;
|
||||
|
||||
for (var j = 0; j < values[i].length; j++) {
|
||||
var x = j * ctx.step;
|
||||
|
||||
y = ctx.height - Math.floor(values[i][j] * data_scale);
|
||||
//y -= Math.floor(y % (1 / data_scale));
|
||||
|
||||
pt += ' ' + x + ',' + y;
|
||||
}
|
||||
|
||||
pt += ' ' + ctx.width + ',' + y + ' ' + ctx.width + ',' + ctx.height;
|
||||
|
||||
el.setAttribute('points', pt);
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
info.label_25 = 0.25 * info.peak;
|
||||
info.label_50 = 0.50 * info.peak;
|
||||
info.label_75 = 0.75 * info.peak;
|
||||
|
||||
if (typeof(ctx.cb) == 'function')
|
||||
ctx.cb(ctx.svg, info);
|
||||
}
|
||||
}, this));
|
||||
}, this), pollInterval);
|
||||
},
|
||||
|
||||
loadSVG: function(src) {
|
||||
return L.Request.get(src).then(function(response) {
|
||||
if (!response.ok)
|
||||
throw new Error(response.statusText);
|
||||
|
||||
return E('div', {
|
||||
'style': 'width:100%;height:300px;border:1px solid #000;background:#fff'
|
||||
}, response.text());
|
||||
});
|
||||
},
|
||||
|
||||
render: function(data) {
|
||||
var svg1 = data[0],
|
||||
svg2 = data[1],
|
||||
wifidevs = data[2];
|
||||
|
||||
var v = E('div', {}, E('div'));
|
||||
|
||||
for (var i = 0; i < wifidevs.length; i++) {
|
||||
var ifname = wifidevs[i].getIfname();
|
||||
|
||||
if (!ifname)
|
||||
continue;
|
||||
|
||||
var csvg1 = svg1.cloneNode(true),
|
||||
csvg2 = svg2.cloneNode(true);
|
||||
|
||||
v.firstElementChild.appendChild(E('div', { 'data-tab': ifname, 'data-tab-title': ifname }, [
|
||||
csvg1,
|
||||
E('div', { 'class': 'right' }, E('small', { 'id': 'scale' }, '-')),
|
||||
E('br'),
|
||||
|
||||
E('div', { 'class': 'table', 'style': 'width:100%;table-layout:fixed' }, [
|
||||
E('div', { 'class': 'tr' }, [
|
||||
E('div', { 'class': 'td right top' }, E('strong', { 'style': 'border-bottom:2px solid blue' }, [ _('Signal:') ])),
|
||||
E('div', { 'class': 'td', 'id': 'rssi_bw_cur' }, [ '0 ' + _('dBm') ]),
|
||||
|
||||
E('div', { 'class': 'td right top' }, E('strong', {}, [ _('Average:') ])),
|
||||
E('div', { 'class': 'td', 'id': 'rssi_bw_avg' }, [ '0 ' + _('dBm') ]),
|
||||
|
||||
E('div', { 'class': 'td right top' }, E('strong', {}, [ _('Peak:') ])),
|
||||
E('div', { 'class': 'td', 'id': 'rssi_bw_peak' }, [ '0 ' + _('dBm') ])
|
||||
]),
|
||||
E('div', { 'class': 'tr' }, [
|
||||
E('div', { 'class': 'td right top' }, E('strong', { 'style': 'border-bottom:2px solid red' }, [ _('Noise:') ])),
|
||||
E('div', { 'class': 'td', 'id': 'noise_bw_cur' }, [ '0 ' + _('dBm') ]),
|
||||
|
||||
E('div', { 'class': 'td right top' }, E('strong', {}, [ _('Average:') ])),
|
||||
E('div', { 'class': 'td', 'id': 'noise_bw_avg' }, [ '0 ' + _('dBm') ]),
|
||||
|
||||
E('div', { 'class': 'td right top' }, E('strong', {}, [ _('Peak:') ])),
|
||||
E('div', { 'class': 'td', 'id': 'noise_bw_peak' }, [ '0 ' + _('dBm') ])
|
||||
])
|
||||
]),
|
||||
E('br'),
|
||||
|
||||
csvg2,
|
||||
E('div', { 'class': 'right' }, E('small', { 'id': 'scale2' }, '-')),
|
||||
E('br'),
|
||||
|
||||
E('div', { 'class': 'table', 'style': 'width:100%;table-layout:fixed' }, [
|
||||
E('div', { 'class': 'tr' }, [
|
||||
E('div', { 'class': 'td right top' }, E('strong', { 'style': 'border-bottom:2px solid green' }, [ _('Phy Rate:') ])),
|
||||
E('div', { 'class': 'td', 'id': 'rate_bw_cur' }, [ '0 MBit/s' ]),
|
||||
|
||||
E('div', { 'class': 'td right top' }, E('strong', {}, [ _('Average:') ])),
|
||||
E('div', { 'class': 'td', 'id': 'rate_bw_avg' }, [ '0 MBit/s' ]),
|
||||
|
||||
E('div', { 'class': 'td right top' }, E('strong', {}, [ _('Peak:') ])),
|
||||
E('div', { 'class': 'td', 'id': 'rate_bw_peak' }, [ '0 MBit/s' ])
|
||||
])
|
||||
])
|
||||
]));
|
||||
|
||||
this.updateGraph(ifname, csvg1, [ null, { line: 'rssi', offset: 155 }, { line: 'noise', offset: 155 } ], function(svg, info) {
|
||||
var G = svg.firstElementChild, tab = svg.parentNode;
|
||||
|
||||
G.getElementById('label_25').firstChild.data = '%d %s'.format(info.label_25 - 100, _('dBm'));
|
||||
G.getElementById('label_50').firstChild.data = '%d %s'.format(info.label_50 - 100, _('dBm'));
|
||||
G.getElementById('label_75').firstChild.data = '%d %s'.format(info.label_75 - 100, _('dBm'));
|
||||
|
||||
tab.querySelector('#scale').firstChild.data = _('(%d minute window, %d second interval)').format(info.timeframe, info.interval);
|
||||
|
||||
tab.querySelector('#rssi_bw_cur').firstChild.data = '%d %s'.format(info.line_current[0] - 100, _('dBm'));
|
||||
tab.querySelector('#rssi_bw_avg').firstChild.data = '%d %s'.format(info.line_average[0] - 100, _('dBm'));
|
||||
tab.querySelector('#rssi_bw_peak').firstChild.data = '%d %s'.format(info.line_peak[0] - 100, _('dBm'));
|
||||
|
||||
tab.querySelector('#noise_bw_cur').firstChild.data = '%d %s'.format(info.line_current[1] - 100, _('dBm'));
|
||||
tab.querySelector('#noise_bw_avg').firstChild.data = '%d %s'.format(info.line_average[1] - 100, _('dBm'));
|
||||
tab.querySelector('#noise_bw_peak').firstChild.data = '%d %s'.format(info.line_peak[1] - 100, _('dBm'));
|
||||
});
|
||||
|
||||
this.updateGraph(ifname, csvg2, [ { line: 'rate', multiply: 0.001 } ], function(svg, info) {
|
||||
var G = svg.firstElementChild, tab = svg.parentNode;
|
||||
|
||||
G.getElementById('label_25').firstChild.data = '%.2f %s'.format(info.label_25, _('MBit/s'));
|
||||
G.getElementById('label_50').firstChild.data = '%.2f %s'.format(info.label_50, _('MBit/s'));
|
||||
G.getElementById('label_75').firstChild.data = '%.2f %s'.format(info.label_75, _('MBit/s'));
|
||||
|
||||
tab.querySelector('#scale2').firstChild.data = _('(%d minute window, %d second interval)').format(info.timeframe, info.interval);
|
||||
|
||||
tab.querySelector('#rate_bw_cur').firstChild.data = '%d %s'.format(info.line_current[0], _('Mbit/s'));
|
||||
tab.querySelector('#rate_bw_avg').firstChild.data = '%d %s'.format(info.line_average[0], _('Mbit/s'));
|
||||
tab.querySelector('#rate_bw_peak').firstChild.data = '%d %s'.format(info.line_peak[0], _('Mbit/s'));
|
||||
});
|
||||
}
|
||||
|
||||
L.ui.tabs.initTabGroup(v.firstElementChild.childNodes);
|
||||
|
||||
this.pollData();
|
||||
|
||||
return v;
|
||||
},
|
||||
|
||||
handleSaveApply: null,
|
||||
handleSave: null,
|
||||
handleReset: null
|
||||
});
|
|
@ -20,22 +20,10 @@ function index()
|
|||
|
||||
entry({"admin", "status", "realtime"}, alias("admin", "status", "realtime", "load"), _("Realtime Graphs"), 7)
|
||||
|
||||
entry({"admin", "status", "realtime", "load"}, template("admin_status/load"), _("Load"), 1).leaf = true
|
||||
entry({"admin", "status", "realtime", "load_status"}, call("action_load")).leaf = true
|
||||
|
||||
entry({"admin", "status", "realtime", "bandwidth"}, template("admin_status/bandwidth"), _("Traffic"), 2).leaf = true
|
||||
entry({"admin", "status", "realtime", "bandwidth_status"}, call("action_bandwidth")).leaf = true
|
||||
|
||||
page = entry({"admin", "status", "realtime", "wireless"}, template("admin_status/wireless"), _("Wireless"), 3)
|
||||
page.uci_depends = { wireless = true }
|
||||
page.leaf = true
|
||||
|
||||
page = entry({"admin", "status", "realtime", "wireless_status"}, call("action_wireless"))
|
||||
page.uci_depends = { wireless = true }
|
||||
page.leaf = true
|
||||
|
||||
entry({"admin", "status", "realtime", "connections"}, template("admin_status/connections"), _("Connections"), 4).leaf = true
|
||||
entry({"admin", "status", "realtime", "connections_status"}, call("action_connections")).leaf = true
|
||||
entry({"admin", "status", "realtime", "load"}, view("status/load"), _("Load"), 1)
|
||||
entry({"admin", "status", "realtime", "bandwidth"}, view("status/bandwidth"), _("Traffic"), 2)
|
||||
entry({"admin", "status", "realtime", "wireless"}, view("status/wireless"), _("Wireless"), 3).uci_depends = { wireless = true }
|
||||
entry({"admin", "status", "realtime", "connections"}, view("status/connections"), _("Connections"), 4)
|
||||
|
||||
entry({"admin", "status", "nameinfo"}, call("action_nameinfo")).leaf = true
|
||||
end
|
||||
|
@ -84,97 +72,3 @@ function action_iptables()
|
|||
|
||||
luci.http.redirect(luci.dispatcher.build_url("admin/status/iptables"))
|
||||
end
|
||||
|
||||
function action_bandwidth(iface)
|
||||
luci.http.prepare_content("application/json")
|
||||
|
||||
local bwc = io.popen("luci-bwc -i %s 2>/dev/null"
|
||||
% luci.util.shellquote(iface))
|
||||
|
||||
if bwc then
|
||||
luci.http.write("[")
|
||||
|
||||
while true do
|
||||
local ln = bwc:read("*l")
|
||||
if not ln then break end
|
||||
luci.http.write(ln)
|
||||
end
|
||||
|
||||
luci.http.write("]")
|
||||
bwc:close()
|
||||
end
|
||||
end
|
||||
|
||||
function action_wireless(iface)
|
||||
luci.http.prepare_content("application/json")
|
||||
|
||||
local bwc = io.popen("luci-bwc -r %s 2>/dev/null"
|
||||
% luci.util.shellquote(iface))
|
||||
|
||||
if bwc then
|
||||
luci.http.write("[")
|
||||
|
||||
while true do
|
||||
local ln = bwc:read("*l")
|
||||
if not ln then break end
|
||||
luci.http.write(ln)
|
||||
end
|
||||
|
||||
luci.http.write("]")
|
||||
bwc:close()
|
||||
end
|
||||
end
|
||||
|
||||
function action_load()
|
||||
luci.http.prepare_content("application/json")
|
||||
|
||||
local bwc = io.popen("luci-bwc -l 2>/dev/null")
|
||||
if bwc then
|
||||
luci.http.write("[")
|
||||
|
||||
while true do
|
||||
local ln = bwc:read("*l")
|
||||
if not ln then break end
|
||||
luci.http.write(ln)
|
||||
end
|
||||
|
||||
luci.http.write("]")
|
||||
bwc:close()
|
||||
end
|
||||
end
|
||||
|
||||
function action_connections()
|
||||
local sys = require "luci.sys"
|
||||
|
||||
luci.http.prepare_content("application/json")
|
||||
|
||||
luci.http.write('{ "connections": ')
|
||||
luci.http.write_json(sys.net.conntrack())
|
||||
|
||||
local bwc = io.popen("luci-bwc -c 2>/dev/null")
|
||||
if bwc then
|
||||
luci.http.write(', "statistics": [')
|
||||
|
||||
while true do
|
||||
local ln = bwc:read("*l")
|
||||
if not ln then break end
|
||||
luci.http.write(ln)
|
||||
end
|
||||
|
||||
luci.http.write("]")
|
||||
bwc:close()
|
||||
end
|
||||
|
||||
luci.http.write(" }")
|
||||
end
|
||||
|
||||
function action_nameinfo(...)
|
||||
local util = require "luci.util"
|
||||
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json(util.ubus("network.rrdns", "lookup", {
|
||||
addrs = { ... },
|
||||
timeout = 5000,
|
||||
limit = 1000
|
||||
}) or { })
|
||||
end
|
||||
|
|
|
@ -1,308 +0,0 @@
|
|||
<%#
|
||||
Copyright 2010-2018 Jo-Philipp Wich <jo@mein.io>
|
||||
Licensed to the public under the Apache License 2.0.
|
||||
-%>
|
||||
|
||||
<%-
|
||||
local ntm = require "luci.model.network".init()
|
||||
|
||||
local dev
|
||||
local devices = { }
|
||||
for _, dev in luci.util.vspairs(luci.sys.net.devices()) do
|
||||
if dev ~= "lo" and not ntm:ignore_interface(dev) then
|
||||
devices[#devices+1] = dev
|
||||
end
|
||||
end
|
||||
|
||||
local curdev = luci.http.formvalue("dev") or devices[1]
|
||||
-%>
|
||||
|
||||
<%+header%>
|
||||
|
||||
<script type="text/javascript">//<![CDATA[
|
||||
var bwxhr = new XHR();
|
||||
|
||||
var G;
|
||||
var TIME = 0;
|
||||
var RXB = 1;
|
||||
var RXP = 2;
|
||||
var TXB = 3;
|
||||
var TXP = 4;
|
||||
|
||||
var width = 760;
|
||||
var height = 300;
|
||||
var step = 5;
|
||||
|
||||
var data_wanted = Math.floor(width / step);
|
||||
var data_fill = 1;
|
||||
var data_stamp = 0;
|
||||
|
||||
var data_rx = [ ];
|
||||
var data_tx = [ ];
|
||||
|
||||
var line_rx;
|
||||
var line_tx;
|
||||
|
||||
var label_25;
|
||||
var label_50;
|
||||
var label_75;
|
||||
|
||||
var label_rx_cur;
|
||||
var label_rx_avg;
|
||||
var label_rx_peak;
|
||||
|
||||
var label_tx_cur;
|
||||
var label_tx_avg;
|
||||
var label_tx_peak;
|
||||
|
||||
var label_scale;
|
||||
|
||||
|
||||
Math.log2 = Math.log2 || function(x) { return Math.log(x) * Math.LOG2E; };
|
||||
|
||||
function bandwidth_label(bytes, br)
|
||||
{
|
||||
var uby = '<%:kB/s%>';
|
||||
var kby = (bytes / 1024);
|
||||
|
||||
if (kby >= 1024)
|
||||
{
|
||||
uby = '<%:MB/s%>';
|
||||
kby = kby / 1024;
|
||||
}
|
||||
|
||||
var ubi = '<%:kbit/s%>';
|
||||
var kbi = (bytes * 8 / 1024);
|
||||
|
||||
if (kbi >= 1024)
|
||||
{
|
||||
ubi = '<%:Mbit/s%>';
|
||||
kbi = kbi / 1024;
|
||||
}
|
||||
|
||||
return String.format("%f %s%s(%f %s)",
|
||||
kbi.toFixed(2), ubi,
|
||||
br ? '<br />' : ' ',
|
||||
kby.toFixed(2), uby
|
||||
);
|
||||
}
|
||||
|
||||
/* wait for SVG */
|
||||
window.setTimeout(
|
||||
function() {
|
||||
var svg = document.getElementById('bwsvg');
|
||||
|
||||
try {
|
||||
G = svg.getSVGDocument
|
||||
? svg.getSVGDocument() : svg.contentDocument;
|
||||
}
|
||||
catch(e) {
|
||||
G = document.embeds['bwsvg'].getSVGDocument();
|
||||
}
|
||||
|
||||
if (!G)
|
||||
{
|
||||
window.setTimeout(arguments.callee, 1000);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* find sizes */
|
||||
width = svg.offsetWidth - 2;
|
||||
height = svg.offsetHeight - 2;
|
||||
data_wanted = Math.ceil(width / step);
|
||||
|
||||
/* prefill datasets */
|
||||
for (var i = 0; i < data_wanted; i++)
|
||||
{
|
||||
data_rx[i] = 0;
|
||||
data_tx[i] = 0;
|
||||
}
|
||||
|
||||
/* find svg elements */
|
||||
line_rx = G.getElementById('rx');
|
||||
line_tx = G.getElementById('tx');
|
||||
|
||||
label_25 = G.getElementById('label_25');
|
||||
label_50 = G.getElementById('label_50');
|
||||
label_75 = G.getElementById('label_75');
|
||||
|
||||
label_rx_cur = document.getElementById('rx_bw_cur');
|
||||
label_rx_avg = document.getElementById('rx_bw_avg');
|
||||
label_rx_peak = document.getElementById('rx_bw_peak');
|
||||
|
||||
label_tx_cur = document.getElementById('tx_bw_cur');
|
||||
label_tx_avg = document.getElementById('tx_bw_avg');
|
||||
label_tx_peak = document.getElementById('tx_bw_peak');
|
||||
|
||||
label_scale = document.getElementById('scale');
|
||||
|
||||
|
||||
/* plot horizontal time interval lines */
|
||||
for (var i = width % (step * 60); i < width; i += step * 60)
|
||||
{
|
||||
var line = G.createElementNS('http://www.w3.org/2000/svg', 'line');
|
||||
line.setAttribute('x1', i);
|
||||
line.setAttribute('y1', 0);
|
||||
line.setAttribute('x2', i);
|
||||
line.setAttribute('y2', '100%');
|
||||
line.setAttribute('style', 'stroke:black;stroke-width:0.1');
|
||||
|
||||
var text = G.createElementNS('http://www.w3.org/2000/svg', 'text');
|
||||
text.setAttribute('x', i + 5);
|
||||
text.setAttribute('y', 15);
|
||||
text.setAttribute('style', 'fill:#eee; font-size:9pt; font-family:sans-serif; text-shadow:1px 1px 1px #000');
|
||||
text.appendChild(G.createTextNode(Math.round((width - i) / step / 60) + 'm'));
|
||||
|
||||
label_25.parentNode.appendChild(line);
|
||||
label_25.parentNode.appendChild(text);
|
||||
}
|
||||
|
||||
label_scale.innerHTML = String.format('<%:(%d minute window, %d second interval)%>', data_wanted / 60, 3);
|
||||
|
||||
/* render datasets, start update interval */
|
||||
XHR.poll(3, '<%=build_url("admin/status/realtime/bandwidth_status", curdev)%>', null,
|
||||
function(x, data)
|
||||
{
|
||||
var data_max = 0;
|
||||
var data_scale = 0;
|
||||
|
||||
var data_rx_avg = 0;
|
||||
var data_tx_avg = 0;
|
||||
|
||||
var data_rx_peak = 0;
|
||||
var data_tx_peak = 0;
|
||||
|
||||
for (var i = data_stamp ? 0 : 1; i < data.length; i++)
|
||||
{
|
||||
/* skip overlapping entries */
|
||||
if (data[i][TIME] <= data_stamp)
|
||||
continue;
|
||||
|
||||
data_fill++;
|
||||
|
||||
/* normalize difference against time interval */
|
||||
if (i > 0)
|
||||
{
|
||||
var time_delta = data[i][TIME] - data[i-1][TIME];
|
||||
if (time_delta)
|
||||
{
|
||||
data_rx.push((data[i][RXB] - data[i-1][RXB]) / time_delta);
|
||||
data_tx.push((data[i][TXB] - data[i-1][TXB]) / time_delta);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* cut off outdated entries */
|
||||
data_rx = data_rx.slice(data_rx.length - data_wanted, data_rx.length);
|
||||
data_tx = data_tx.slice(data_tx.length - data_wanted, data_tx.length);
|
||||
data_fill = Math.min(data_fill, data_wanted);
|
||||
|
||||
/* find peak */
|
||||
for (var i = 0; i < data_rx.length; i++)
|
||||
{
|
||||
data_max = Math.max(data_max, data_rx[i]);
|
||||
data_max = Math.max(data_max, data_tx[i]);
|
||||
|
||||
data_rx_peak = Math.max(data_rx_peak, data_rx[i]);
|
||||
data_tx_peak = Math.max(data_tx_peak, data_tx[i]);
|
||||
|
||||
data_rx_avg += data_rx[i];
|
||||
data_tx_avg += data_tx[i];
|
||||
}
|
||||
|
||||
data_rx_avg = (data_rx_avg / data_fill);
|
||||
data_tx_avg = (data_tx_avg / data_fill);
|
||||
|
||||
var size = Math.floor(Math.log2(data_max)),
|
||||
div = Math.pow(2, size - (size % 10)),
|
||||
mult = data_max / div,
|
||||
mult = (mult < 5) ? 2 : ((mult < 50) ? 10 : ((mult < 500) ? 100 : 1000));
|
||||
|
||||
data_max = data_max + (mult * div) - (data_max % (mult * div));
|
||||
|
||||
/* remember current timestamp, calculate horizontal scale */
|
||||
data_stamp = data[data.length-1][TIME];
|
||||
data_scale = height / data_max;
|
||||
|
||||
/* plot data */
|
||||
var pt_rx = '0,' + height;
|
||||
var pt_tx = '0,' + height;
|
||||
|
||||
var y_rx = 0;
|
||||
var y_tx = 0;
|
||||
|
||||
for (var i = 0; i < data_rx.length; i++)
|
||||
{
|
||||
var x = i * step;
|
||||
|
||||
y_rx = height - Math.floor(data_rx[i] * data_scale);
|
||||
y_tx = height - Math.floor(data_tx[i] * data_scale);
|
||||
|
||||
pt_rx += ' ' + x + ',' + y_rx;
|
||||
pt_tx += ' ' + x + ',' + y_tx;
|
||||
}
|
||||
|
||||
pt_rx += ' ' + width + ',' + y_rx + ' ' + width + ',' + height;
|
||||
pt_tx += ' ' + width + ',' + y_tx + ' ' + width + ',' + height;
|
||||
|
||||
|
||||
line_rx.setAttribute('points', pt_rx);
|
||||
line_tx.setAttribute('points', pt_tx);
|
||||
|
||||
label_25.firstChild.data = bandwidth_label(0.25 * data_max);
|
||||
label_50.firstChild.data = bandwidth_label(0.50 * data_max);
|
||||
label_75.firstChild.data = bandwidth_label(0.75 * data_max);
|
||||
|
||||
label_rx_cur.innerHTML = bandwidth_label(data_rx[data_rx.length-1], true);
|
||||
label_tx_cur.innerHTML = bandwidth_label(data_tx[data_tx.length-1], true);
|
||||
|
||||
label_rx_avg.innerHTML = bandwidth_label(data_rx_avg, true);
|
||||
label_tx_avg.innerHTML = bandwidth_label(data_tx_avg, true);
|
||||
|
||||
label_rx_peak.innerHTML = bandwidth_label(data_rx_peak, true);
|
||||
label_tx_peak.innerHTML = bandwidth_label(data_tx_peak, true);
|
||||
}
|
||||
);
|
||||
|
||||
XHR.run();
|
||||
}
|
||||
}, 1000
|
||||
);
|
||||
//]]></script>
|
||||
|
||||
<h2 name="content"><%:Realtime Traffic%></h2>
|
||||
|
||||
<ul class="cbi-tabmenu">
|
||||
<% for _, dev in ipairs(devices) do %>
|
||||
<li class="cbi-tab<%= dev == curdev and "" or "-disabled" %>"><a href="?dev=<%=pcdata(dev)%>"><%=pcdata(dev)%></a></li>
|
||||
<% end %>
|
||||
</ul>
|
||||
|
||||
<embed id="bwsvg" style="width:100%; height:300px; border:1px solid #000000; background-color:#FFFFFF" src="<%=resource%>/bandwidth.svg" />
|
||||
<div style="text-align:right"><small id="scale">-</small></div>
|
||||
<br />
|
||||
|
||||
<div class="table" style="width:100%; table-layout:fixed" cellspacing="5">
|
||||
<div class="tr">
|
||||
<div class="td" style="text-align:right; vertical-align:top"><strong style="border-bottom:2px solid blue"><%:Inbound:%></strong></div>
|
||||
<div class="td" id="rx_bw_cur">0 <%:kbit/s%><br />(0 <%:kB/s%>)</div>
|
||||
|
||||
<div class="td" style="text-align:right; vertical-align:top"><strong><%:Average:%></strong></div>
|
||||
<div class="td" id="rx_bw_avg">0 <%:kbit/s%><br />(0 <%:kB/s%>)</div>
|
||||
|
||||
<div class="td" style="text-align:right; vertical-align:top"><strong><%:Peak:%></strong></div>
|
||||
<div class="td" id="rx_bw_peak">0 <%:kbit/s%><br />(0 <%:kB/s%>)</div>
|
||||
</div>
|
||||
<div class="tr">
|
||||
<div class="td" style="text-align:right; vertical-align:top"><strong style="border-bottom:2px solid green"><%:Outbound:%></strong></div>
|
||||
<div class="td" id="tx_bw_cur">0 <%:kbit/s%><br />(0 <%:kB/s%>)</div>
|
||||
|
||||
<div class="td" style="text-align:right; vertical-align:top"><strong><%:Average:%></strong></div>
|
||||
<div class="td" id="tx_bw_avg">0 <%:kbit/s%><br />(0 <%:kB/s%>)</div>
|
||||
|
||||
<div class="td" style="text-align:right; vertical-align:top"><strong><%:Peak:%></strong></div>
|
||||
<div class="td" id="tx_bw_peak">0 <%:kbit/s%><br />(0 <%:kB/s%>)</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%+footer%>
|
|
@ -1,405 +0,0 @@
|
|||
<%#
|
||||
Copyright 2010-2018 Jo-Philipp Wich <jo@mein.io>
|
||||
Licensed to the public under the Apache License 2.0.
|
||||
-%>
|
||||
|
||||
<%+header%>
|
||||
|
||||
<script type="text/javascript">//<![CDATA[
|
||||
var bwxhr = new XHR();
|
||||
|
||||
var G;
|
||||
var TIME = 0;
|
||||
var UDP = 1;
|
||||
var TCP = 2;
|
||||
var OTHER = 3;
|
||||
|
||||
var width = 760;
|
||||
var height = 300;
|
||||
var step = 5;
|
||||
|
||||
var data_wanted = Math.floor(width / step);
|
||||
var data_fill = 1;
|
||||
var data_stamp = 0;
|
||||
|
||||
var data_udp = [ ];
|
||||
var data_tcp = [ ];
|
||||
var data_otr = [ ];
|
||||
|
||||
var line_udp;
|
||||
var line_tcp;
|
||||
|
||||
var label_25;
|
||||
var label_50;
|
||||
var label_75;
|
||||
|
||||
var label_udp_cur;
|
||||
var label_udp_avg;
|
||||
var label_udp_peak;
|
||||
|
||||
var label_tcp_cur;
|
||||
var label_tcp_avg;
|
||||
var label_tcp_peak;
|
||||
|
||||
var label_otr_cur;
|
||||
var label_otr_avg;
|
||||
var label_otr_peak;
|
||||
|
||||
var label_scale;
|
||||
|
||||
var conn_table;
|
||||
|
||||
var dns_cache = { };
|
||||
|
||||
|
||||
/* wait for SVG */
|
||||
window.setTimeout(
|
||||
function() {
|
||||
var svg = document.getElementById('bwsvg');
|
||||
|
||||
try {
|
||||
G = svg.getSVGDocument
|
||||
? svg.getSVGDocument() : svg.contentDocument;
|
||||
}
|
||||
catch(e) {
|
||||
G = document.embeds['bwsvg'].getSVGDocument();
|
||||
}
|
||||
|
||||
if (!G)
|
||||
{
|
||||
window.setTimeout(arguments.callee, 1000);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* find sizes */
|
||||
width = svg.offsetWidth - 2;
|
||||
height = svg.offsetHeight - 2;
|
||||
data_wanted = Math.ceil(width / step);
|
||||
|
||||
/* prefill datasets */
|
||||
for (var i = 0; i < data_wanted; i++)
|
||||
{
|
||||
data_udp[i] = 0;
|
||||
data_tcp[i] = 0;
|
||||
data_otr[i] = 0;
|
||||
}
|
||||
|
||||
/* find svg elements */
|
||||
line_udp = G.getElementById('udp');
|
||||
line_tcp = G.getElementById('tcp');
|
||||
line_otr = G.getElementById('other');
|
||||
|
||||
label_25 = G.getElementById('label_25');
|
||||
label_50 = G.getElementById('label_50');
|
||||
label_75 = G.getElementById('label_75');
|
||||
|
||||
label_udp_cur = document.getElementById('lb_udp_cur');
|
||||
label_udp_avg = document.getElementById('lb_udp_avg');
|
||||
label_udp_peak = document.getElementById('lb_udp_peak');
|
||||
|
||||
label_tcp_cur = document.getElementById('lb_tcp_cur');
|
||||
label_tcp_avg = document.getElementById('lb_tcp_avg');
|
||||
label_tcp_peak = document.getElementById('lb_tcp_peak');
|
||||
|
||||
label_otr_cur = document.getElementById('lb_otr_cur');
|
||||
label_otr_avg = document.getElementById('lb_otr_avg');
|
||||
label_otr_peak = document.getElementById('lb_otr_peak');
|
||||
|
||||
label_scale = document.getElementById('scale');
|
||||
|
||||
conn_table = document.getElementById('connections');
|
||||
|
||||
|
||||
/* plot horizontal time interval lines */
|
||||
for (var i = width % (step * 60); i < width; i += step * 60)
|
||||
{
|
||||
var line = G.createElementNS('http://www.w3.org/2000/svg', 'line');
|
||||
line.setAttribute('x1', i);
|
||||
line.setAttribute('y1', 0);
|
||||
line.setAttribute('x2', i);
|
||||
line.setAttribute('y2', '100%');
|
||||
line.setAttribute('style', 'stroke:black;stroke-width:0.1');
|
||||
|
||||
var text = G.createElementNS('http://www.w3.org/2000/svg', 'text');
|
||||
text.setAttribute('x', i + 5);
|
||||
text.setAttribute('y', 15);
|
||||
text.setAttribute('style', 'fill:#eee; font-size:9pt; font-family:sans-serif; text-shadow:1px 1px 1px #000');
|
||||
text.appendChild(G.createTextNode(Math.round((width - i) / step / 60) + 'm'));
|
||||
|
||||
label_25.parentNode.appendChild(line);
|
||||
label_25.parentNode.appendChild(text);
|
||||
}
|
||||
|
||||
label_scale.innerHTML = String.format('<%:(%d minute window, %d second interval)%>', data_wanted / 60, 3);
|
||||
|
||||
var recheck_lookup_queue = {};
|
||||
|
||||
/* render datasets, start update interval */
|
||||
XHR.poll(3, '<%=build_url("admin/status/realtime/connections_status")%>', null,
|
||||
function(x, json)
|
||||
{
|
||||
|
||||
if (!json.connections)
|
||||
return;
|
||||
|
||||
var conn = json.connections;
|
||||
|
||||
var lookup_queue = [ ];
|
||||
var rows = [];
|
||||
|
||||
conn.sort(function(a, b) {
|
||||
return b.bytes - a.bytes;
|
||||
});
|
||||
|
||||
for (var i = 0; i < conn.length; i++)
|
||||
{
|
||||
var c = conn[i];
|
||||
|
||||
if ((c.src == '127.0.0.1' && c.dst == '127.0.0.1') ||
|
||||
(c.src == '::1' && c.dst == '::1'))
|
||||
continue;
|
||||
|
||||
if (!dns_cache[c.src] && lookup_queue.indexOf(c.src) == -1)
|
||||
lookup_queue.push(c.src);
|
||||
|
||||
if (!dns_cache[c.dst] && lookup_queue.indexOf(c.dst) == -1)
|
||||
lookup_queue.push(c.dst);
|
||||
|
||||
var src = dns_cache[c.src] || (c.layer3 == 'ipv6' ? '[' + c.src + ']' : c.src);
|
||||
var dst = dns_cache[c.dst] || (c.layer3 == 'ipv6' ? '[' + c.dst + ']' : c.dst);
|
||||
|
||||
rows.push([
|
||||
c.layer3.toUpperCase(),
|
||||
c.layer4.toUpperCase(),
|
||||
c.hasOwnProperty('sport') ? (src + ':' + c.sport) : src,
|
||||
c.hasOwnProperty('dport') ? (dst + ':' + c.dport) : dst,
|
||||
'%1024.2mB (%d <%:Pkts.%>)'.format(c.bytes, c.packets)
|
||||
]);
|
||||
}
|
||||
|
||||
cbi_update_table(conn_table, rows, '<em><%:No information available%></em>');
|
||||
|
||||
if (lookup_queue.length > 0) {
|
||||
var reduced_lookup_queue = lookup_queue;
|
||||
|
||||
if (lookup_queue.length > 100)
|
||||
reduced_lookup_queue = lookup_queue.slice(0, 100);
|
||||
|
||||
XHR.get('<%=build_url("admin/status/nameinfo")%>/' + reduced_lookup_queue.join('/'), null,
|
||||
function(x, json) {
|
||||
if (!json)
|
||||
return;
|
||||
|
||||
for (var index in reduced_lookup_queue) {
|
||||
var address = reduced_lookup_queue[index];
|
||||
|
||||
if (!address)
|
||||
continue;
|
||||
|
||||
if (json[address]) {
|
||||
dns_cache[address] = json[address];
|
||||
lookup_queue.splice(reduced_lookup_queue.indexOf(address),1);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(recheck_lookup_queue[address] > 2) {
|
||||
dns_cache[address] = (address.match(/:/)) ? '[' + address + ']' : address;
|
||||
lookup_queue.splice(index,1);
|
||||
} else {
|
||||
recheck_lookup_queue[address] != null ? recheck_lookup_queue[address]++ : recheck_lookup_queue[address] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
var data = json.statistics;
|
||||
|
||||
var data_max = 0;
|
||||
var data_scale = 0;
|
||||
|
||||
var data_udp_avg = 0;
|
||||
var data_tcp_avg = 0;
|
||||
var data_otr_avg = 0;
|
||||
|
||||
var data_udp_peak = 0;
|
||||
var data_tcp_peak = 0;
|
||||
var data_otr_peak = 0;
|
||||
|
||||
for (var i = data_stamp ? 0 : 1; i < data.length; i++)
|
||||
{
|
||||
/* skip overlapping entries */
|
||||
if (data[i][TIME] <= data_stamp)
|
||||
continue;
|
||||
|
||||
data_fill++;
|
||||
|
||||
data_udp.push(data[i][UDP]);
|
||||
data_tcp.push(data[i][TCP]);
|
||||
data_otr.push(data[i][OTHER]);
|
||||
}
|
||||
|
||||
/* cut off outdated entries */
|
||||
data_fill = Math.min(data_fill, data_wanted);
|
||||
data_udp = data_udp.slice(data_udp.length - data_wanted, data_udp.length);
|
||||
data_tcp = data_tcp.slice(data_tcp.length - data_wanted, data_tcp.length);
|
||||
data_otr = data_otr.slice(data_otr.length - data_wanted, data_otr.length);
|
||||
|
||||
/* find peak */
|
||||
for (var i = 0; i < data_udp.length; i++)
|
||||
{
|
||||
data_max = Math.max(data_max, data_udp[i]);
|
||||
data_max = Math.max(data_max, data_tcp[i]);
|
||||
data_max = Math.max(data_max, data_otr[i]);
|
||||
|
||||
data_udp_peak = Math.max(data_udp_peak, data_udp[i]);
|
||||
data_tcp_peak = Math.max(data_tcp_peak, data_tcp[i]);
|
||||
data_otr_peak = Math.max(data_otr_peak, data_otr[i]);
|
||||
|
||||
data_udp_avg += data_udp[i];
|
||||
data_tcp_avg += data_tcp[i];
|
||||
data_otr_avg += data_otr[i];
|
||||
}
|
||||
|
||||
data_udp_avg = data_udp_avg / data_fill;
|
||||
data_tcp_avg = data_tcp_avg / data_fill;
|
||||
data_otr_avg = data_otr_avg / data_fill;
|
||||
|
||||
/* remember current timestamp, calculate horizontal scale */
|
||||
data_stamp = data[data.length-1][TIME];
|
||||
data_scale = height / (data_max * 1.1);
|
||||
|
||||
|
||||
/* plot data */
|
||||
var pt_udp = '0,' + height;
|
||||
var pt_tcp = '0,' + height;
|
||||
var pt_otr = '0,' + height;
|
||||
|
||||
var y_udp = 0;
|
||||
var y_tcp = 0;
|
||||
var y_otr = 0;
|
||||
|
||||
for (var i = 0; i < data_udp.length; i++)
|
||||
{
|
||||
var x = i * step;
|
||||
|
||||
y_udp = height - Math.floor(data_udp[i] * data_scale);
|
||||
y_tcp = height - Math.floor(data_tcp[i] * data_scale);
|
||||
y_otr = height - Math.floor(data_otr[i] * data_scale);
|
||||
|
||||
pt_udp += ' ' + x + ',' + y_udp;
|
||||
pt_tcp += ' ' + x + ',' + y_tcp;
|
||||
pt_otr += ' ' + x + ',' + y_otr;
|
||||
}
|
||||
|
||||
pt_udp += ' ' + width + ',' + y_udp + ' ' + width + ',' + height;
|
||||
pt_tcp += ' ' + width + ',' + y_tcp + ' ' + width + ',' + height;
|
||||
pt_otr += ' ' + width + ',' + y_otr + ' ' + width + ',' + height;
|
||||
|
||||
|
||||
var order = [
|
||||
[ line_udp, data_udp[data_udp.length-1] ],
|
||||
[ line_tcp, data_tcp[data_tcp.length-1] ],
|
||||
[ line_otr, data_otr[data_otr.length-1] ]
|
||||
];
|
||||
|
||||
order.sort(function(a, b) { return b[1] - a[1] });
|
||||
|
||||
for (var i = 0; i < order.length; i++)
|
||||
order[i][0].parentNode.appendChild(order[i][0]);
|
||||
|
||||
|
||||
line_udp.setAttribute('points', pt_udp);
|
||||
line_tcp.setAttribute('points', pt_tcp);
|
||||
line_otr.setAttribute('points', pt_otr);
|
||||
|
||||
label_25.firstChild.data = Math.floor(1.1 * 0.25 * data_max);
|
||||
label_50.firstChild.data = Math.floor(1.1 * 0.50 * data_max);
|
||||
label_75.firstChild.data = Math.floor(1.1 * 0.75 * data_max);
|
||||
|
||||
label_udp_cur.innerHTML = Math.floor(data_udp[data_udp.length-1]);
|
||||
label_tcp_cur.innerHTML = Math.floor(data_tcp[data_tcp.length-1]);
|
||||
label_otr_cur.innerHTML = Math.floor(data_otr[data_otr.length-1]);
|
||||
|
||||
label_udp_avg.innerHTML = Math.floor(data_udp_avg);
|
||||
label_tcp_avg.innerHTML = Math.floor(data_tcp_avg);
|
||||
label_otr_avg.innerHTML = Math.floor(data_otr_avg);
|
||||
|
||||
label_udp_peak.innerHTML = Math.floor(data_udp_peak);
|
||||
label_tcp_peak.innerHTML = Math.floor(data_tcp_peak);
|
||||
label_otr_peak.innerHTML = Math.floor(data_otr_peak);
|
||||
}
|
||||
);
|
||||
|
||||
XHR.run();
|
||||
}
|
||||
}, 1000
|
||||
);
|
||||
//]]></script>
|
||||
|
||||
<h2 name="content"><%:Realtime Connections%></h2>
|
||||
|
||||
<div class="cbi-map-descr"><%:This page gives an overview over currently active network connections.%></div>
|
||||
|
||||
<fieldset class="cbi-section" id="cbi-table-table">
|
||||
<legend><%:Active Connections%></legend>
|
||||
|
||||
<embed id="bwsvg" style="width:100%; height:300px; border:1px solid #000000; background-color:#FFFFFF" src="<%=resource%>/connections.svg" />
|
||||
<div style="text-align:right"><small id="scale">-</small></div>
|
||||
<br />
|
||||
|
||||
<div class="table">
|
||||
<div class="tr">
|
||||
<div class="td" style="text-align:right; vertical-align:top"><strong style="border-bottom:2px solid blue"><%:UDP:%></strong></div>
|
||||
<div class="td" id="lb_udp_cur">0</div>
|
||||
|
||||
<div class="td" style="text-align:right; vertical-align:top"><strong><%:Average:%></strong></div>
|
||||
<div class="td" id="lb_udp_avg">0</div>
|
||||
|
||||
<div class="td" style="text-align:right; vertical-align:top"><strong><%:Peak:%></strong></div>
|
||||
<div class="td" id="lb_udp_peak">0</div>
|
||||
</div>
|
||||
<div class="tr">
|
||||
<div class="td" style="text-align:right; vertical-align:top"><strong style="border-bottom:2px solid green"><%:TCP:%></strong></div>
|
||||
<div class="td" id="lb_tcp_cur">0</div>
|
||||
|
||||
<div class="td" style="text-align:right; vertical-align:top"><strong><%:Average:%></strong></div>
|
||||
<div class="td" id="lb_tcp_avg">0</div>
|
||||
|
||||
<div class="td" style="text-align:right; vertical-align:top"><strong><%:Peak:%></strong></div>
|
||||
<div class="td" id="lb_tcp_peak">0</div>
|
||||
</div>
|
||||
<div class="tr">
|
||||
<div class="td" style="text-align:right; vertical-align:top"><strong style="border-bottom:2px solid red"><%:Other:%></strong></div>
|
||||
<div class="td" id="lb_otr_cur">0</div>
|
||||
|
||||
<div class="td" style="text-align:right; vertical-align:top"><strong><%:Average:%></strong></div>
|
||||
<div class="td" id="lb_otr_avg">0</div>
|
||||
|
||||
<div class="td" style="text-align:right; vertical-align:top"><strong><%:Peak:%></strong></div>
|
||||
<div class="td" id="lb_otr_peak">0</div>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
|
||||
<div class="cbi-section-node">
|
||||
<div class="table" id="connections">
|
||||
<div class="tr table-titles">
|
||||
<div class="th col-2 hide-xs"><%:Network%></div>
|
||||
<div class="th col-2"><%:Protocol%></div>
|
||||
<div class="th col-7"><%:Source%></div>
|
||||
<div class="th col-7"><%:Destination%></div>
|
||||
<div class="th col-4"><%:Transfer%></div>
|
||||
</div>
|
||||
|
||||
<div class="tr placeholder">
|
||||
<div class="td">
|
||||
<em><%:Collecting data...%></em>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<%+footer%>
|
|
@ -1,283 +0,0 @@
|
|||
<%#
|
||||
Copyright 2010-2018 Jo-Philipp Wich <jo@mein.io>
|
||||
Licensed to the public under the Apache License 2.0.
|
||||
-%>
|
||||
|
||||
<%+header%>
|
||||
|
||||
<script type="text/javascript">//<![CDATA[
|
||||
var bwxhr = new XHR();
|
||||
|
||||
var G;
|
||||
var TIME = 0;
|
||||
var L01 = 1;
|
||||
var L05 = 2;
|
||||
var L15 = 3;
|
||||
|
||||
var width = 760;
|
||||
var height = 300;
|
||||
var step = 5;
|
||||
|
||||
var data_wanted = Math.floor(width / step);
|
||||
var data_fill = 1;
|
||||
var data_stamp = 0;
|
||||
|
||||
var data_01 = [ ];
|
||||
var data_05 = [ ];
|
||||
var data_15 = [ ];
|
||||
|
||||
var line_01;
|
||||
var line_05;
|
||||
var line_15;
|
||||
|
||||
var label_25;
|
||||
var label_050;
|
||||
var label_75;
|
||||
|
||||
var label_01_cur;
|
||||
var label_01_avg;
|
||||
var label_01_peak;
|
||||
|
||||
var label_05_cur;
|
||||
var label_05_avg;
|
||||
var label_05_peak;
|
||||
|
||||
var label_15_cur;
|
||||
var label_15_avg;
|
||||
var label_15_peak;
|
||||
|
||||
var label_scale;
|
||||
|
||||
|
||||
/* wait for SVG */
|
||||
window.setTimeout(
|
||||
function() {
|
||||
var svg = document.getElementById('bwsvg');
|
||||
|
||||
try {
|
||||
G = svg.getSVGDocument
|
||||
? svg.getSVGDocument() : svg.contentDocument;
|
||||
}
|
||||
catch(e) {
|
||||
G = document.embeds['bwsvg'].getSVGDocument();
|
||||
}
|
||||
|
||||
if (!G)
|
||||
{
|
||||
window.setTimeout(arguments.callee, 1000);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* find sizes */
|
||||
width = svg.offsetWidth - 2;
|
||||
height = svg.offsetHeight - 2;
|
||||
data_wanted = Math.ceil(width / step);
|
||||
|
||||
/* prefill datasets */
|
||||
for (var i = 0; i < data_wanted; i++)
|
||||
{
|
||||
data_01[i] = 0;
|
||||
data_05[i] = 0;
|
||||
data_15[i] = 0;
|
||||
}
|
||||
|
||||
/* find svg elements */
|
||||
line_01 = G.getElementById('load01');
|
||||
line_05 = G.getElementById('load05');
|
||||
line_15 = G.getElementById('load15');
|
||||
|
||||
label_25 = G.getElementById('label_25');
|
||||
label_50 = G.getElementById('label_50');
|
||||
label_75 = G.getElementById('label_75');
|
||||
|
||||
label_01_cur = document.getElementById('lb_load01_cur');
|
||||
label_01_avg = document.getElementById('lb_load01_avg');
|
||||
label_01_peak = document.getElementById('lb_load01_peak');
|
||||
|
||||
label_05_cur = document.getElementById('lb_load05_cur');
|
||||
label_05_avg = document.getElementById('lb_load05_avg');
|
||||
label_05_peak = document.getElementById('lb_load05_peak');
|
||||
|
||||
label_15_cur = document.getElementById('lb_load15_cur');
|
||||
label_15_avg = document.getElementById('lb_load15_avg');
|
||||
label_15_peak = document.getElementById('lb_load15_peak');
|
||||
|
||||
label_scale = document.getElementById('scale');
|
||||
|
||||
|
||||
/* plot horizontal time interval lines */
|
||||
for (var i = width % (step * 60); i < width; i += step * 60)
|
||||
{
|
||||
var line = G.createElementNS('http://www.w3.org/2000/svg', 'line');
|
||||
line.setAttribute('x1', i);
|
||||
line.setAttribute('y1', 0);
|
||||
line.setAttribute('x2', i);
|
||||
line.setAttribute('y2', '100%');
|
||||
line.setAttribute('style', 'stroke:black;stroke-width:0.1');
|
||||
|
||||
var text = G.createElementNS('http://www.w3.org/2000/svg', 'text');
|
||||
text.setAttribute('x', i + 5);
|
||||
text.setAttribute('y', 15);
|
||||
text.setAttribute('style', 'fill:#eee; font-size:9pt; font-family:sans-serif; text-shadow:1px 1px 1px #000');
|
||||
text.appendChild(G.createTextNode(Math.round((width - i) / step / 60) + 'm'));
|
||||
|
||||
label_25.parentNode.appendChild(line);
|
||||
label_25.parentNode.appendChild(text);
|
||||
}
|
||||
|
||||
label_scale.innerHTML = String.format('<%:(%d minute window, %d second interval)%>', data_wanted / 60, 3);
|
||||
|
||||
/* render datasets, start update interval */
|
||||
XHR.poll(3, '<%=build_url("admin/status/realtime/load_status")%>', null,
|
||||
function(x, data)
|
||||
{
|
||||
var data_max = 0;
|
||||
var data_scale = 0;
|
||||
|
||||
var data_01_avg = 0;
|
||||
var data_05_avg = 0;
|
||||
var data_15_avg = 0;
|
||||
|
||||
var data_01_peak = 0;
|
||||
var data_05_peak = 0;
|
||||
var data_15_peak = 0;
|
||||
|
||||
for (var i = data_stamp ? 0 : 1; i < data.length; i++)
|
||||
{
|
||||
/* skip overlapping entries */
|
||||
if (data[i][TIME] <= data_stamp)
|
||||
continue;
|
||||
|
||||
data_fill++;
|
||||
|
||||
data_01.push(data[i][L01]);
|
||||
data_05.push(data[i][L05]);
|
||||
data_15.push(data[i][L15]);
|
||||
}
|
||||
|
||||
/* cut off outdated entries */
|
||||
data_fill = Math.min(data_fill, data_wanted);
|
||||
data_01 = data_01.slice(data_01.length - data_wanted, data_01.length);
|
||||
data_05 = data_05.slice(data_05.length - data_wanted, data_05.length);
|
||||
data_15 = data_15.slice(data_15.length - data_wanted, data_15.length);
|
||||
|
||||
/* find peak */
|
||||
for (var i = 0; i < data_01.length; i++)
|
||||
{
|
||||
data_max = Math.max(data_max, data_01[i]);
|
||||
data_max = Math.max(data_max, data_05[i]);
|
||||
data_max = Math.max(data_max, data_15[i]);
|
||||
|
||||
data_01_peak = Math.max(data_01_peak, data_01[i]);
|
||||
data_05_peak = Math.max(data_05_peak, data_05[i]);
|
||||
data_15_peak = Math.max(data_15_peak, data_15[i]);
|
||||
|
||||
data_01_avg += data_01[i];
|
||||
data_05_avg += data_05[i];
|
||||
data_15_avg += data_15[i];
|
||||
}
|
||||
|
||||
data_01_avg = data_01_avg / data_fill;
|
||||
data_05_avg = data_05_avg / data_fill;
|
||||
data_15_avg = data_15_avg / data_fill;
|
||||
|
||||
/* remember current timestamp, calculate horizontal scale */
|
||||
data_stamp = data[data.length-1][TIME];
|
||||
data_scale = height / (data_max * 1.1);
|
||||
|
||||
|
||||
/* plot data */
|
||||
var pt_01 = '0,' + height;
|
||||
var pt_05 = '0,' + height;
|
||||
var pt_15 = '0,' + height;
|
||||
|
||||
var y_01 = 0;
|
||||
var y_05 = 0;
|
||||
var y_15 = 0;
|
||||
|
||||
for (var i = 0; i < data_01.length; i++)
|
||||
{
|
||||
var x = i * step;
|
||||
|
||||
y_01 = height - Math.floor(data_01[i] * data_scale);
|
||||
y_05 = height - Math.floor(data_05[i] * data_scale);
|
||||
y_15 = height - Math.floor(data_15[i] * data_scale);
|
||||
|
||||
pt_01 += ' ' + x + ',' + y_01;
|
||||
pt_05 += ' ' + x + ',' + y_05;
|
||||
pt_15 += ' ' + x + ',' + y_15;
|
||||
}
|
||||
|
||||
pt_01 += ' ' + width + ',' + y_01 + ' ' + width + ',' + height;
|
||||
pt_05 += ' ' + width + ',' + y_05 + ' ' + width + ',' + height;
|
||||
pt_15 += ' ' + width + ',' + y_15 + ' ' + width + ',' + height;
|
||||
|
||||
|
||||
line_01.setAttribute('points', pt_01);
|
||||
line_05.setAttribute('points', pt_05);
|
||||
line_15.setAttribute('points', pt_15);
|
||||
|
||||
label_25.firstChild.data = (1.1 * 0.25 * data_max / 100).toFixed(2);
|
||||
label_50.firstChild.data = (1.1 * 0.50 * data_max / 100).toFixed(2);
|
||||
label_75.firstChild.data = (1.1 * 0.75 * data_max / 100).toFixed(2);
|
||||
|
||||
label_01_cur.innerHTML = (data_01[data_01.length-1] / 100).toFixed(2);
|
||||
label_05_cur.innerHTML = (data_05[data_05.length-1] / 100).toFixed(2);
|
||||
label_15_cur.innerHTML = (data_15[data_15.length-1] / 100).toFixed(2);
|
||||
|
||||
label_01_avg.innerHTML = (data_01_avg / 100).toFixed(2);
|
||||
label_05_avg.innerHTML = (data_05_avg / 100).toFixed(2);
|
||||
label_15_avg.innerHTML = (data_15_avg / 100).toFixed(2);
|
||||
|
||||
label_01_peak.innerHTML = (data_01_peak / 100).toFixed(2);
|
||||
label_05_peak.innerHTML = (data_05_peak / 100).toFixed(2);
|
||||
label_15_peak.innerHTML = (data_15_peak / 100).toFixed(2);
|
||||
}
|
||||
);
|
||||
|
||||
XHR.run();
|
||||
}
|
||||
}, 1000
|
||||
);
|
||||
//]]></script>
|
||||
|
||||
<h2 name="content"><%:Realtime Load%></h2>
|
||||
|
||||
<embed id="bwsvg" style="width:100%; height:300px; border:1px solid #000000; background-color:#FFFFFF" src="<%=resource%>/load.svg" />
|
||||
<div style="text-align:right"><small id="scale">-</small></div>
|
||||
<br />
|
||||
|
||||
<div class="table" style="width:100%; table-layout:fixed" cellspacing="5">
|
||||
<div class="tr">
|
||||
<div class="td" style="text-align:right; vertical-align:top"><strong style="border-bottom:2px solid #ff0000; white-space:nowrap"><%:1 Minute Load:%></strong></div>
|
||||
<div class="td" id="lb_load01_cur">0</div>
|
||||
|
||||
<div class="td" style="text-align:right; vertical-align:top"><strong><%:Average:%></strong></div>
|
||||
<div class="td" id="lb_load01_avg">0</div>
|
||||
|
||||
<div class="td" style="text-align:right; vertical-align:top"><strong><%:Peak:%></strong></div>
|
||||
<div class="td" id="lb_load01_peak">0</div>
|
||||
</div>
|
||||
<div class="tr">
|
||||
<div class="td" style="text-align:right; vertical-align:top"><strong style="border-bottom:2px solid #ff6600; white-space:nowrap"><%:5 Minute Load:%></strong></div>
|
||||
<div class="td" id="lb_load05_cur">0</div>
|
||||
|
||||
<div class="td" style="text-align:right; vertical-align:top"><strong><%:Average:%></strong></div>
|
||||
<div class="td" id="lb_load05_avg">0</div>
|
||||
|
||||
<div class="td" style="text-align:right; vertical-align:top"><strong><%:Peak:%></strong></div>
|
||||
<div class="td" id="lb_load05_peak">0</div>
|
||||
</div>
|
||||
<div class="tr">
|
||||
<div class="td" style="text-align:right; vertical-align:top"><strong style="border-bottom:2px solid #ffaa00; white-space:nowrap"><%:15 Minute Load:%></strong></div>
|
||||
<div class="td" id="lb_load15_cur">0</div>
|
||||
|
||||
<div class="td" style="text-align:right; vertical-align:top"><strong><%:Average:%></strong></div>
|
||||
<div class="td" id="lb_load15_avg">0</div>
|
||||
|
||||
<div class="td" style="text-align:right; vertical-align:top"><strong><%:Peak:%></strong></div>
|
||||
<div class="td" id="lb_load15_peak">0</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%+footer%>
|
|
@ -1,370 +0,0 @@
|
|||
<%#
|
||||
Copyright 2011-2018 Jo-Philipp Wich <jo@mein.io>
|
||||
Licensed to the public under the Apache License 2.0.
|
||||
-%>
|
||||
|
||||
<%-
|
||||
local ntm = require "luci.model.network".init()
|
||||
|
||||
local dev
|
||||
local devices = { }
|
||||
for _, dev in luci.util.vspairs(luci.sys.net.devices()) do
|
||||
if dev:match("^wlan%d") or dev:match("^ath%d") or dev:match("^wl%d") then
|
||||
devices[#devices+1] = dev
|
||||
end
|
||||
end
|
||||
|
||||
local curdev = luci.http.formvalue("dev") or devices[1]
|
||||
-%>
|
||||
|
||||
<%+header%>
|
||||
|
||||
<script type="text/javascript">//<![CDATA[
|
||||
var bwxhr = new XHR();
|
||||
|
||||
var G, G2;
|
||||
var TIME = 0;
|
||||
var RATE = 1;
|
||||
var RSSI = 2;
|
||||
var NOISE = 3;
|
||||
|
||||
var width = 760;
|
||||
var height = 300;
|
||||
var step = 5;
|
||||
|
||||
var data_wanted = Math.floor(width / step);
|
||||
var data_fill = 1;
|
||||
var data_stamp = 0;
|
||||
|
||||
var data_rssi = [ ];
|
||||
var data_noise = [ ];
|
||||
var data_rate = [ ];
|
||||
|
||||
var line_rssi;
|
||||
var line_noise;
|
||||
var line_rate;
|
||||
|
||||
var label_25, label_25_2;
|
||||
var label_50, label_50_2;
|
||||
var label_75, label_75_2;
|
||||
|
||||
var label_rssi_cur;
|
||||
var label_rssi_avg;
|
||||
var label_rssi_peak;
|
||||
|
||||
var label_noise_cur;
|
||||
var label_noise_avg;
|
||||
var label_noise_peak;
|
||||
|
||||
var label_rate_cur;
|
||||
var label_rate_avg;
|
||||
var label_rate_peak;
|
||||
|
||||
var label_scale;
|
||||
var label_scale_2;
|
||||
|
||||
|
||||
/* wait for SVG */
|
||||
window.setTimeout(
|
||||
function() {
|
||||
var svg = document.getElementById('iwsvg');
|
||||
var svg2 = document.getElementById('iwsvg2');
|
||||
|
||||
try {
|
||||
G = svg.getSVGDocument
|
||||
? svg.getSVGDocument() : svg.contentDocument;
|
||||
G2 = svg2.getSVGDocument
|
||||
? svg2.getSVGDocument() : svg2.contentDocument;
|
||||
}
|
||||
catch(e) {
|
||||
G = document.embeds['iwsvg'].getSVGDocument();
|
||||
G2 = document.embeds['iwsvg2'].getSVGDocument();
|
||||
}
|
||||
|
||||
if (!G || !G2)
|
||||
{
|
||||
window.setTimeout(arguments.callee, 1000);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* find sizes */
|
||||
width = svg.offsetWidth - 2;
|
||||
height = svg.offsetHeight - 2;
|
||||
data_wanted = Math.ceil(width / step);
|
||||
|
||||
/* prefill datasets */
|
||||
for (var i = 0; i < data_wanted; i++)
|
||||
{
|
||||
data_rssi[i] = 0;
|
||||
data_noise[i] = 0;
|
||||
data_rate[i] = 0;
|
||||
}
|
||||
|
||||
/* find svg elements */
|
||||
line_rssi = G.getElementById('rssi');
|
||||
line_noise = G.getElementById('noise');
|
||||
line_rate = G2.getElementById('rate');
|
||||
|
||||
label_25 = G.getElementById('label_25');
|
||||
label_50 = G.getElementById('label_50');
|
||||
label_75 = G.getElementById('label_75');
|
||||
label_25_2 = G2.getElementById('label_25');
|
||||
label_50_2 = G2.getElementById('label_50');
|
||||
label_75_2 = G2.getElementById('label_75');
|
||||
|
||||
label_rssi_cur = document.getElementById('rssi_bw_cur');
|
||||
label_rssi_avg = document.getElementById('rssi_bw_avg');
|
||||
label_rssi_peak = document.getElementById('rssi_bw_peak');
|
||||
|
||||
label_noise_cur = document.getElementById('noise_bw_cur');
|
||||
label_noise_avg = document.getElementById('noise_bw_avg');
|
||||
label_noise_peak = document.getElementById('noise_bw_peak');
|
||||
|
||||
label_rate_cur = document.getElementById('rate_bw_cur');
|
||||
label_rate_avg = document.getElementById('rate_bw_avg');
|
||||
label_rate_peak = document.getElementById('rate_bw_peak');
|
||||
|
||||
label_scale = document.getElementById('scale');
|
||||
label_scale_2 = document.getElementById('scale2');
|
||||
|
||||
|
||||
/* plot horizontal time interval lines */
|
||||
for (var i = width % (step * 60); i < width; i += step * 60)
|
||||
{
|
||||
var line = G.createElementNS('http://www.w3.org/2000/svg', 'line');
|
||||
line.setAttribute('x1', i);
|
||||
line.setAttribute('y1', 0);
|
||||
line.setAttribute('x2', i);
|
||||
line.setAttribute('y2', '100%');
|
||||
line.setAttribute('style', 'stroke:black;stroke-width:0.1');
|
||||
|
||||
var text = G.createElementNS('http://www.w3.org/2000/svg', 'text');
|
||||
text.setAttribute('x', i + 5);
|
||||
text.setAttribute('y', 15);
|
||||
text.setAttribute('style', 'fill:#eee; font-size:9pt; font-family:sans-serif; text-shadow:1px 1px 1px #000');
|
||||
text.appendChild(G.createTextNode(Math.round((width - i) / step / 60) + 'm'));
|
||||
|
||||
label_25.parentNode.appendChild(line);
|
||||
label_25.parentNode.appendChild(text);
|
||||
|
||||
|
||||
var line2 = G2.createElementNS('http://www.w3.org/2000/svg', 'line');
|
||||
line2.setAttribute('x1', i);
|
||||
line2.setAttribute('y1', 0);
|
||||
line2.setAttribute('x2', i);
|
||||
line2.setAttribute('y2', '100%');
|
||||
line2.setAttribute('style', 'stroke:black;stroke-width:0.1');
|
||||
|
||||
var text2 = G2.createElementNS('http://www.w3.org/2000/svg', 'text');
|
||||
text2.setAttribute('x', i + 5);
|
||||
text2.setAttribute('y', 15);
|
||||
text2.setAttribute('style', 'fill:#eee; font-size:9pt; font-family:sans-serif; text-shadow:1px 1px 1px #000');
|
||||
text2.appendChild(G.createTextNode(Math.round((width - i) / step / 60) + 'm'));
|
||||
|
||||
label_25_2.parentNode.appendChild(line2);
|
||||
label_25_2.parentNode.appendChild(text2);
|
||||
}
|
||||
|
||||
label_scale.innerHTML = String.format('<%:(%d minute window, %d second interval)%>', data_wanted / 60, 3);
|
||||
label_scale_2.innerHTML = String.format('<%:(%d minute window, %d second interval)%>', data_wanted / 60, 3);
|
||||
|
||||
/* render datasets, start update interval */
|
||||
XHR.poll(3, '<%=build_url("admin/status/realtime/wireless_status", curdev)%>', null,
|
||||
function(x, data)
|
||||
{
|
||||
var noise_floor = 255;
|
||||
var rate_floor = 60000;
|
||||
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
noise_floor = Math.min(noise_floor, data[i][NOISE]);
|
||||
rate_floor = Math.min(rate_floor, data[i][RATE]);
|
||||
}
|
||||
|
||||
noise_floor -= 5;
|
||||
|
||||
var data_max = 0;
|
||||
var data_scale = 0;
|
||||
var data_max_2 = 0;
|
||||
var data_scale_2 = 0;
|
||||
|
||||
var data_rssi_avg = 0;
|
||||
var data_noise_avg = 0;
|
||||
var data_rate_avg = 0;
|
||||
|
||||
var data_rssi_peak = 0;
|
||||
var data_noise_peak = 0;
|
||||
var data_rate_peak = 0;
|
||||
|
||||
for (var i = data_stamp ? 0 : 1; i < data.length; i++)
|
||||
{
|
||||
/* skip overlapping entries */
|
||||
if (data[i][TIME] <= data_stamp)
|
||||
continue;
|
||||
|
||||
data_fill++;
|
||||
|
||||
data_rssi.push(data[i][RSSI] - noise_floor);
|
||||
data_noise.push(data[i][NOISE] - noise_floor);
|
||||
data_rate.push(Math.floor(data[i][RATE] / 1000));
|
||||
}
|
||||
|
||||
/* cut off outdated entries */
|
||||
data_fill = Math.min(data_fill, data_wanted);
|
||||
data_rssi = data_rssi.slice(data_rssi.length - data_wanted, data_rssi.length);
|
||||
data_noise = data_noise.slice(data_noise.length - data_wanted, data_noise.length);
|
||||
data_rate = data_rate.slice(data_rate.length - data_wanted, data_rate.length);
|
||||
|
||||
/* find peak */
|
||||
for (var i = 0; i < data_rssi.length; i++)
|
||||
{
|
||||
data_max = Math.max(data_max, data_rssi[i]);
|
||||
data_max_2 = Math.max(data_max_2, data_rate[i]);
|
||||
|
||||
data_rssi_peak = Math.max(data_rssi_peak, data_rssi[i]);
|
||||
data_noise_peak = Math.max(data_noise_peak, data_noise[i]);
|
||||
data_rate_peak = Math.max(data_rate_peak, data_rate[i]);
|
||||
|
||||
data_rssi_avg += data_rssi[i];
|
||||
data_noise_avg += data_noise[i];
|
||||
data_rate_avg += data_rate[i];
|
||||
}
|
||||
|
||||
data_rssi_avg = data_rssi_avg / data_fill;
|
||||
data_noise_avg = data_noise_avg / data_fill;
|
||||
data_rate_avg = data_rate_avg / data_fill;
|
||||
|
||||
/* remember current timestamp, calculate horizontal scale */
|
||||
data_stamp = data[data.length-1][TIME];
|
||||
data_scale = (height / (data_max * 1.1)).toFixed(1);
|
||||
data_scale_2 = (height / (data_max_2 * 1.1)).toFixed(1);
|
||||
|
||||
/* plot data */
|
||||
var pt_rssi = '0,' + height;
|
||||
var pt_noise = '0,' + height;
|
||||
var pt_rate = '0,' + height;
|
||||
|
||||
var y_rssi = 0;
|
||||
var y_noise = 0;
|
||||
var y_rate = 0;
|
||||
|
||||
for (var i = 0; i < data_rssi.length; i++)
|
||||
{
|
||||
var x = i * step;
|
||||
|
||||
y_rssi = height - Math.floor(data_rssi[i] * data_scale);
|
||||
y_noise = height - Math.floor(data_noise[i] * data_scale);
|
||||
y_rate = height - Math.floor(data_rate[i] * data_scale_2);
|
||||
|
||||
y_rssi -= Math.floor(y_rssi % (1/data_scale));
|
||||
y_noise -= Math.floor(y_noise % (1/data_scale));
|
||||
|
||||
pt_rssi += ' ' + x + ',' + y_rssi;
|
||||
pt_noise += ' ' + x + ',' + y_noise;
|
||||
pt_rate += ' ' + x + ',' + y_rate;
|
||||
}
|
||||
|
||||
pt_rssi += ' ' + width + ',' + y_rssi + ' ' + width + ',' + height;
|
||||
pt_noise += ' ' + width + ',' + y_noise + ' ' + width + ',' + height;
|
||||
pt_rate += ' ' + width + ',' + y_rate + ' ' + width + ',' + height;
|
||||
|
||||
line_rssi.setAttribute('points', pt_rssi);
|
||||
line_noise.setAttribute('points', pt_noise);
|
||||
line_rate.setAttribute('points', pt_rate);
|
||||
|
||||
function wireless_label(dbm, noise)
|
||||
{
|
||||
if (noise)
|
||||
return String.format("%d <%:dBm%> (SNR %d <%:dB%>)", noise_floor + dbm - 255, dbm - noise);
|
||||
else
|
||||
return String.format("%d <%:dBm%>", noise_floor + dbm - 255);
|
||||
}
|
||||
|
||||
function rate_label(mbit)
|
||||
{
|
||||
return String.format("%d <%:Mbit/s%>", mbit);
|
||||
}
|
||||
|
||||
label_25.firstChild.data = wireless_label(1.1 * 0.25 * data_max);
|
||||
label_50.firstChild.data = wireless_label(1.1 * 0.50 * data_max);
|
||||
label_75.firstChild.data = wireless_label(1.1 * 0.75 * data_max);
|
||||
|
||||
label_25_2.firstChild.data = rate_label(1.1 * 0.25 * data_max_2);
|
||||
label_50_2.firstChild.data = rate_label(1.1 * 0.50 * data_max_2);
|
||||
label_75_2.firstChild.data = rate_label(1.1 * 0.75 * data_max_2);
|
||||
|
||||
label_rssi_cur.innerHTML = wireless_label(data_rssi[data_rssi.length-1], data_noise[data_noise.length-1]).nobr();
|
||||
label_noise_cur.innerHTML = wireless_label(data_noise[data_noise.length-1]).nobr();
|
||||
|
||||
label_rssi_avg.innerHTML = wireless_label(data_rssi_avg, data_noise_avg).nobr();
|
||||
label_noise_avg.innerHTML = wireless_label(data_noise_avg).nobr();
|
||||
|
||||
label_rssi_peak.innerHTML = wireless_label(data_rssi_peak, data_noise_peak).nobr();
|
||||
label_noise_peak.innerHTML = wireless_label(data_noise_peak).nobr();
|
||||
|
||||
label_rate_cur.innerHTML = rate_label(data_rate[data_rate.length-1]);
|
||||
label_rate_avg.innerHTML = rate_label(data_rate_avg);
|
||||
label_rate_peak.innerHTML = rate_label(data_rate_peak);
|
||||
}
|
||||
);
|
||||
|
||||
XHR.run();
|
||||
}
|
||||
}, 1000
|
||||
);
|
||||
//]]></script>
|
||||
|
||||
<h2 name="content"><%:Realtime Wireless%></h2>
|
||||
|
||||
<ul class="cbi-tabmenu">
|
||||
<% for _, dev in ipairs(devices) do %>
|
||||
<li class="cbi-tab<%= dev == curdev and "" or "-disabled" %>"><a href="?dev=<%=pcdata(dev)%>"><%=pcdata(dev)%></a></li>
|
||||
<% end %>
|
||||
</ul>
|
||||
|
||||
<embed id="iwsvg" style="width:100%; height:300px; border:1px solid #000000; background-color:#FFFFFF" src="<%=resource%>/wireless.svg" />
|
||||
<div style="text-align:right"><small id="scale">-</small></div>
|
||||
<br />
|
||||
|
||||
<div class="table" style="width:100%; table-layout:fixed" cellspacing="5">
|
||||
<div class="tr">
|
||||
<div class="td" style="text-align:right; vertical-align:top"><strong style="border-bottom:2px solid blue"><%:Signal:%></strong></div>
|
||||
<div class="td" id="rssi_bw_cur">0 <%:dBm%></div>
|
||||
|
||||
<div class="td" style="text-align:right; vertical-align:top"><strong><%:Average:%></strong></div>
|
||||
<div class="td" id="rssi_bw_avg">0 <%:dBm%></div>
|
||||
|
||||
<div class="td" style="text-align:right; vertical-align:top"><strong><%:Peak:%></strong></div>
|
||||
<div class="td" id="rssi_bw_peak">0 <%:dBm%></div>
|
||||
</div>
|
||||
<div class="tr">
|
||||
<div class="td" style="text-align:right; vertical-align:top"><strong style="border-bottom:2px solid red"><%:Noise:%></strong></div>
|
||||
<div class="td" id="noise_bw_cur">0 <%:dBm%></div>
|
||||
|
||||
<div class="td" style="text-align:right; vertical-align:top"><strong><%:Average:%></strong></div>
|
||||
<div class="td" id="noise_bw_avg">0 <%:dBm%></div>
|
||||
|
||||
<div class="td" style="text-align:right; vertical-align:top"><strong><%:Peak:%></strong></div>
|
||||
<div class="td" id="noise_bw_peak">0 <%:dBm%></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
<embed id="iwsvg2" style="width:100%; height:300px; border:1px solid #000000; background-color:#FFFFFF" src="<%=resource%>/wifirate.svg" />
|
||||
<div style="text-align:right"><small id="scale2">-</small></div>
|
||||
<br />
|
||||
|
||||
<div class="table" style="width:100%; table-layout:fixed" cellspacing="5">
|
||||
<div class="tr">
|
||||
<div class="td" style="text-align:right; vertical-align:top"><strong style="border-bottom:2px solid green"><%:Phy Rate:%></strong></div>
|
||||
<div class="td" id="rate_bw_cur">0 MBit/s</div>
|
||||
|
||||
<div class="td" style="text-align:right; vertical-align:top"><strong><%:Average:%></strong></div>
|
||||
<div class="td" id="rate_bw_avg">0 MBit/s</div>
|
||||
|
||||
<div class="td" style="text-align:right; vertical-align:top"><strong><%:Peak:%></strong></div>
|
||||
<div class="td" id="rate_bw_peak">0 MBit/s</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%+footer%>
|
Loading…
Reference in a new issue