update 2026-05-27 23:25:57

This commit is contained in:
action committed 2026-05-27 23:25:57 +08:00
1 parent 84f707b52b
commit d0159b49a5
759 files changed
+362365

No files matched your search

@@ -0,0 +1,18 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" version="1.1" xmlns="http://www.w3.org/2000/svg">
<polyline id="total_line" class="total" points="" />
<polyline id="user_line" class="user" points="" />
<polyline id="sys_line" class="sys" points="" />
<polyline id="sirq_line" class="sirq" points="" />
<line class="grid" x1="0" y1="25%" x2="100%" y2="25%" />
<text id="label_75" x="20" y="24%"> </text>
<line class="grid" x1="0" y1="50%" x2="100%" y2="50%" />
<text id="label_50" x="20" y="49%"> </text>
<line class="grid" x1="0" y1="75%" x2="100%" y2="75%" />
<text id="label_25" x="20" y="74%"> </text>
</svg>

After

Width:  |  Height:  |  Size: 747 B

@@ -0,0 +1,509 @@
'use strict';
'require dom';
'require fs';
'require poll';
'require request';
'require view';
'require ui';
document.head.append(E('style', {'type': 'text/css'},
`
:root {
--app-cpu-status-total: gray;
--app-cpu-status-user: blue;
--app-cpu-status-sys: red;
--app-cpu-status-sirq: green;
}
.svg_background {
width: 100%;
height: 300px;
border: 1px solid #000;
background: #fff';
}
[data-darkmode="true"] .svg_background {
background-color: var(--background-color-high) !important;
}
.graph_legend {
border-bottom: 2px solid;
}
.total {
border-color: var(--app-cpu-status-total);
}
.user{
border-color: var(--app-cpu-status-user);
}
.sys {
border-color: var(--app-cpu-status-sys);
}
.sirq {
border-color: var(--app-cpu-status-sirq);
}
svg line.grid {
stroke: black;
stroke-width: 0.1;
}
[data-darkmode="true"] svg line.grid {
stroke: #fff !important;
}
svg text {
fill: #eee;
font-size: 9pt;
font-family: sans-serif;
text-shadow: 1px 1px 1px #000;
}
svg #total_line {
fill: var(--app-cpu-status-total);
fill-opacity: 0.4;
stroke: var(--app-cpu-status-total);
stroke-width: 1;
}
svg #user_line {
fill: var(--app-cpu-status-user);
fill-opacity: 0.5;
stroke: var(--app-cpu-status-user);
stroke-width: 1;
}
svg #sys_line {
fill: var(--app-cpu-status-sys);
fill-opacity: 0.5;
stroke: var(--app-cpu-status-sys);
stroke-width: 1;
}
svg #sirq_line {
fill: var(--app-cpu-status-sirq);
fill-opacity: 0.5;
stroke: var(--app-cpu-status-sirq);
stroke-width: 1;
}
`));
Math.log2 = Math.log2 || (x => Math.log(x) * Math.LOG2E);
return view.extend({
pollInterval : 3,
dataBufferSize: 4,
CPUDevices : {},
graphPolls : [],
lastStatArray : null,
parseProcData(data) {
let cpu_stat_array = [];
let stat_items_array = data.trim().split('\n').filter(s => s.startsWith('cpu'));
for(let str of stat_items_array) {
let arr = str.split(/\s+/).slice(0, 8);
arr[0] = (arr[0] === 'cpu') ? -1 : arr[0].replace('cpu', '');
cpu_stat_array.push(arr.map(e => Number(e)));
};
cpu_stat_array.sort((a, b) => a[0] - b[0]);
return cpu_stat_array;
},
calcCPULoad(cpu_stat_array) {
let ret_array = [];
cpu_stat_array.forEach((c, i) => {
let loadUser = 0,
loadNice = 0,
loadSys = 0,
loadIdle = 0,
loadIo = 0,
loadIrq = 0,
loadSirq = 0,
loadAvg = 0;
if(this.lastStatArray !== null && this.lastStatArray.cpuNum === c.cpuNum) {
let user = c[1] - this.lastStatArray[i][1],
nice = c[2] - this.lastStatArray[i][2],
sys = c[3] - this.lastStatArray[i][3],
idle = c[4] - this.lastStatArray[i][4],
io = c[5] - this.lastStatArray[i][5],
irq = c[6] - this.lastStatArray[i][6],
sirq = c[7] - this.lastStatArray[i][7];
let sum = user + nice + sys + idle + io + irq + sirq;
loadUser = Number((100 * user / sum).toFixed(1));
loadNice = Number((100 * nice / sum).toFixed(1));
loadSys = Number((100 * sys / sum).toFixed(1));
loadIdle = Number((100 * idle / sum).toFixed(1));
loadIo = Number((100 * io / sum).toFixed(1));
loadIrq = Number((100 * irq / sum).toFixed(1));
loadSirq = Number((100 * sirq / sum).toFixed(1));
loadAvg = Math.round(100 * (user + nice + sys + io + irq + sirq) / sum);
};
ret_array.push(
{cpuNum: c[0], loadUser, loadNice, loadSys, loadIdle, loadIo, loadIrq, loadSirq, loadAvg});
});
this.lastStatArray = cpu_stat_array;
return ret_array;
},
getCPUData(cpu_data) {
return L.resolveDefault(fs.read('/proc/stat'), null).then(cpu_data => {
if(cpu_data) {
let cpu_stat_array = this.parseProcData(cpu_data);
// For single-core CPU
if(cpu_stat_array.length === 2) {
cpu_stat_array = cpu_stat_array.slice(0, 1);
};
let cpu_load_array = this.calcCPULoad(cpu_stat_array);
for(let i = 0; i < cpu_load_array.length; i++) {
let num = i;
let total = cpu_load_array[i].loadAvg;
let user = cpu_load_array[i].loadUser;
let sys = cpu_load_array[i].loadSys;
let sirq = cpu_load_array[i].loadSirq;
if(!(num in this.CPUDevices)) {
this.CPUDevices[num] = {
num : i,
name: String(cpu_load_array[i].cpuNum),
load: [],
};
};
let load_array = this.CPUDevices[num].load;
load_array.push([
new Date().getTime(),
total || 0,
user || 0,
sys || 0,
sirq || 0,
]);
if(load_array.length > this.dataBufferSize) {
load_array.shift();
};
};
};
return this.CPUDevices;
});
},
loadSVG(src) {
return request.get(src).then(response => {
if(!response.ok) {
throw new Error(response.statusText);
};
return E('div', {
'class': 'svg_background',
}, E(response.text()));
});
},
updateGraph(cpu_num, svg, peak, lines, cb) {
let G = svg.firstElementChild;
let view = document.querySelector('#view');
let width = view.offsetWidth - 2;
let height = 300 - 2;
let base_step = 5;
let time_interval = 60;
let time_interval_min = time_interval / 60
let step = base_step * this.pollInterval;
let data_wanted = Math.ceil(width / step);
let timeline_offset = width % step;
let data_values = [];
let line_elements = [];
for(let i = 0; i < lines.length; i++) {
if(lines[i] != null) {
data_values.push([]);
};
};
let info = {
line_current: [],
line_average: [],
line_peak : [],
};
/* prefill datasets */
for(let i = 0; i < data_values.length; i++) {
for(let j = 0; j < data_wanted; j++) {
data_values[i][j] = NaN;
};
};
/* plot horizontal time interval lines */
for(let i = width % (base_step * time_interval); i < width; i += base_step * time_interval) {
let x = i - (timeline_offset);
let line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
line.setAttribute('x1', x);
line.setAttribute('y1', 0);
line.setAttribute('x2', x);
line.setAttribute('y2', '100%');
line.setAttribute('class', 'grid');
let text = document.createElementNS('http://www.w3.org/2000/svg', 'text');
text.setAttribute('x', x + 5);
text.setAttribute('y', 15);
text.append(document.createTextNode(String((width - i) / base_step / time_interval) + 'm'));
G.append(line);
G.append(text);
};
info.interval = this.pollInterval;
info.timeframe = Math.floor(data_wanted / time_interval * this.pollInterval);
this.graphPolls.push({
cpu_num,
svg,
peak,
lines,
cb,
info,
width,
height,
step,
data_wanted,
values : data_values,
timestamp : 0,
fill : 1,
});
},
pollData() {
poll.add(L.bind(function() {
return this.getCPUData().then(L.bind(function(datasets) {
for(let gi = 0; gi < this.graphPolls.length; gi++) {
let ctx = this.graphPolls[gi];
if(!datasets[ctx.cpu_num]) {
continue;
};
let data = datasets[ctx.cpu_num].load;
if(!data) {
continue;
};
let values = ctx.values;
let lines = ctx.lines;
let info = ctx.info;
let peak = ctx.peak;
let data_scale = 0;
let data_wanted = ctx.data_wanted;
let last_timestamp = NaN;
for(let i = 0, di = 0; di < lines.length; di++) {
if(lines[di] == null) {
continue;
};
for(let j = ctx.timestamp ? 0 : 1; j < data.length; j++) {
/* skip overlapping and empty 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];
values[i].push(info.line_current[i]);
};
i++;
};
/* cut off outdated entries */
ctx.fill = Math.min(ctx.fill, data_wanted);
for(let i = 0; i < values.length; i++) {
let 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;
let nonempty = 0;
for(let 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_peak[i] = Number(info.line_peak[i].toFixed(1));
if(!isNaN(values[i][j])) {
nonempty++;
info.line_average[i] += values[i][j];
};
};
info.line_average[i] = info.line_average[i] / nonempty;
info.line_average[i] = Number(info.line_average[i].toFixed(1));
};
/* remember current timestamp, calculate horizontal scale */
if(!isNaN(last_timestamp)) {
ctx.timestamp = last_timestamp;
};
let size = Math.floor(Math.log2(info.peak));
let div = Math.pow(2, size - (size % 10));
data_scale = ctx.height / peak;
/* plot data */
for(let i = 0, di = 0; di < lines.length; di++) {
if(lines[di] == null) {
continue;
};
let el = ctx.svg.firstElementChild.getElementById(lines[di].line);
let pt = '0,' + ctx.height;
let y = 0;
if(!el) {
continue;
};
for(let j = 0; j < values[i].length; j++) {
let x = j * ctx.step;
y = ctx.height - Math.floor(values[i][j] * data_scale);
//y -= Math.floor(y % (1 / data_scale));
y = isNaN(y) ? ctx.height + 1 : y;
pt += ` ${x},${y}`;
};
pt += ` ${ctx.width},${y} ${ctx.width},${ctx.height}`;
el.setAttribute('points', pt);
i++;
};
info.label_25 = 0.25 * peak;
info.label_50 = 0.50 * peak;
info.label_75 = 0.75 * peak;
if(typeof(ctx.cb) == 'function') {
ctx.cb(ctx.svg, info);
};
};
}, this));
}, this), this.pollInterval);
},
load() {
return Promise.all([
this.loadSVG(L.resource('svg/cpu.svg')),
this.getCPUData(),
]);
},
render(data) {
let svg = data[0];
let cpus = data[1];
let map = E('div', { 'class': 'cbi-map', 'id': 'map' });
let tabs = E('div');
map.append(tabs);
for(let i of Object.values(cpus)) {
let cpu_num = i.num;
let cpu_name = (Number(i.name) >= 0 ) ? `${_('CPU')} ${i.name}` : _('Total');
let csvg = svg.cloneNode(true);
tabs.append(E('div', { 'class': 'cbi-section', 'data-tab': cpu_num, 'data-tab-title': cpu_name }, [
csvg,
E('div', { 'class': 'right' }, E('small', { 'data-graph': 'scale' }, '-')),
E('br'),
E('table', { 'class': 'table', 'style': 'width:100%;table-layout:fixed' }, [
E('tr', { 'class': 'tr' }, [
E('td', { 'class': 'td right top' }, E('strong', { 'class': 'graph_legend total' }, _('Total') + ':')),
E('td', { 'class': 'td', 'data-graph': 'total_cur' }, '-'),
E('td', { 'class': 'td right top' }, E('strong', {}, _('Average:'))),
E('td', { 'class': 'td', 'data-graph': 'total_avg' }, '-'),
E('td', { 'class': 'td right top' }, E('strong', {}, _('Peak:'))),
E('td', { 'class': 'td', 'data-graph': 'total_peak' }, '-'),
]),
E('tr', { 'class': 'tr' }, [
E('td', { 'class': 'td right top' }, E('strong', { 'class': 'graph_legend user' }, 'User' + ':')),
E('td', { 'class': 'td', 'data-graph': 'user_cur' }, '-'),
E('td', { 'class': 'td right top' }, E('strong', {}, _('Average:'))),
E('td', { 'class': 'td', 'data-graph': 'user_avg' }, '-'),
E('td', { 'class': 'td right top' }, E('strong', {}, _('Peak:'))),
E('td', { 'class': 'td', 'data-graph': 'user_peak' }, '-'),
]),
E('tr', { 'class': 'tr' }, [
E('td', { 'class': 'td right top' }, E('strong', { 'class': 'graph_legend sys' }, 'System' + ':')),
E('td', { 'class': 'td', 'data-graph': 'sys_cur' }, '-'),
E('td', { 'class': 'td right top' }, E('strong', {}, _('Average:'))),
E('td', { 'class': 'td', 'data-graph': 'sys_avg' }, '-'),
E('td', { 'class': 'td right top' }, E('strong', {}, _('Peak:'))),
E('td', { 'class': 'td', 'data-graph': 'sys_peak' }, '-'),
]),
E('tr', { 'class': 'tr' }, [
E('td', { 'class': 'td right top' }, E('strong', { 'class': 'graph_legend sirq' }, 'Sirq' + ':')),
E('td', { 'class': 'td', 'data-graph': 'sirq_cur' }, '-'),
E('td', { 'class': 'td right top' }, E('strong', {}, _('Average:'))),
E('td', { 'class': 'td', 'data-graph': 'sirq_avg' }, '-'),
E('td', { 'class': 'td right top' }, E('strong', {}, _('Peak:'))),
E('td', { 'class': 'td', 'data-graph': 'sirq_peak' }, '-'),
]),
]),
E('br'),
]));
this.updateGraph(
cpu_num,
csvg,
100,
[ { 'line': 'total_line' }, { 'line': 'user_line' }, { 'line': 'sys_line' }, { 'line': 'sirq_line' } ],
(svg, info) => {
let 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('[data-graph="scale"]').firstChild.data = _('(%d minute window, %d second interval)').format(info.timeframe, info.interval);
dom.content(tab.querySelector('[data-graph="total_cur"]'), info.line_current[0] + ' %');
dom.content(tab.querySelector('[data-graph="total_avg"]'), info.line_average[0] + ' %');
dom.content(tab.querySelector('[data-graph="total_peak"]'), info.line_peak[0] + ' %');
dom.content(tab.querySelector('[data-graph="user_cur"]'), info.line_current[1] + ' %');
dom.content(tab.querySelector('[data-graph="user_avg"]'), info.line_average[1] + ' %');
dom.content(tab.querySelector('[data-graph="user_peak"]'), info.line_peak[1] + ' %');
dom.content(tab.querySelector('[data-graph="sys_cur"]'), info.line_current[2] + ' %');
dom.content(tab.querySelector('[data-graph="sys_avg"]'), info.line_average[2] + ' %');
dom.content(tab.querySelector('[data-graph="sys_peak"]'), info.line_peak[2] + ' %');
dom.content(tab.querySelector('[data-graph="sirq_cur"]'), info.line_current[3] + ' %');
dom.content(tab.querySelector('[data-graph="sirq_avg"]'), info.line_average[3] + ' %');
dom.content(tab.querySelector('[data-graph="sirq_peak"]'), info.line_peak[3] + ' %');
}
);
};
ui.tabs.initTabGroup(tabs.childNodes);
this.pollData();
return E([], [
E('h2', _('CPU load')),
E('div', {'class': 'cbi-map-descr'},
_('This page displays the average CPU load at %d second interval.').format(this.pollInterval)),
map,
]);
},
handleSaveApply: null,
handleSave : null,
handleReset : null,
});
@@ -0,0 +1,363 @@
'use strict';
'require baseclass';
'require fs';
document.head.append(E('style', {'type': 'text/css'},
`
.cpu-status-view-mode-entry {
display: inline-block;
cursor: pointer;
margin: 2px 4px 2px 0 !important;
padding: 3px 5px;
border: 1px solid;
border-color: transparent;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
opacity: 0.7;
background-color: rgba(100 100 100 / 0.2);
}
.cpu-status-view-mode-entry:hover {
opacity: 0.9;
}
.cpu-status-view-mode-entry:active {
opacity: 1.0;
}
.cpu-status-view-mode-entry-checked {
border: 1px solid;
opacity: 1.0;
}
.cpu-status-view-mode-entry-checked:hover {
opacity: 1.0;
}
`));
return baseclass.extend({
title : _('CPU Load'),
viewName : 'cpu_status',
availableViewModes: {
0: [ 'allCPUs', _('Load') ],
1: [ 'allCPUsDetail', _('Detailed load') ],
2: [ 'eachCPU', _('Load of each CPU') ],
3: [ 'eachCPUDetail', _('Detailed load of each CPU') ],
},
viewMode : 0,
lastStatArray : null,
restoreSettingsFromLocalStorage() {
let viewModeLocal = localStorage.getItem(`luci-app-${this.viewName}-viewMode`);
if(viewModeLocal) {
this.viewMode = Number(viewModeLocal);
};
},
saveSettingsToLocalStorage(viewMode) {
if(this.viewMode != viewMode) {
localStorage.setItem(
`luci-app-${this.viewName}-viewMode`, String(viewMode));
};
},
parseProcData(data) {
let cpuStatArray = [];
let statItemsArray = data.trim().split('\n').filter(s => s.startsWith('cpu'));
for(let str of statItemsArray) {
let arr = str.split(/\s+/).slice(0, 8);
arr[0] = (arr[0] == 'cpu') ? Infinity : arr[0].replace('cpu', '');
cpuStatArray.push(arr.map(e => Number(e)));
};
cpuStatArray.sort((a, b) => a[0] - b[0]);
return cpuStatArray;
},
calcCPULoad(cpuStatArray) {
let retArray = [];
cpuStatArray.forEach((c, i) => {
let loadUser = 0,
loadNice = 0,
loadSys = 0,
loadIdle = 0,
loadIo = 0,
loadIrq = 0,
loadSirq = 0,
loadAvg = 0;
if(this.lastStatArray !== null && this.lastStatArray.cpuNum == c.cpuNum) {
let user = c[1] - this.lastStatArray[i][1],
nice = c[2] - this.lastStatArray[i][2],
sys = c[3] - this.lastStatArray[i][3],
idle = c[4] - this.lastStatArray[i][4],
io = c[5] - this.lastStatArray[i][5],
irq = c[6] - this.lastStatArray[i][6],
sirq = c[7] - this.lastStatArray[i][7];
let sum = user + nice + sys + idle + io + irq + sirq;
loadUser = Number((100 * user / sum).toFixed(1));
loadNice = Number((100 * nice / sum).toFixed(1));
loadSys = Number((100 * sys / sum).toFixed(1));
loadIdle = Number((100 * idle / sum).toFixed(1));
loadIo = Number((100 * io / sum).toFixed(1));
loadIrq = Number((100 * irq / sum).toFixed(1));
loadSirq = Number((100 * sirq / sum).toFixed(1));
loadAvg = Math.round(100 * (user + nice + sys + io + irq + sirq) / sum);
};
retArray.push(
{cpuNum: c[0], loadUser, loadNice, loadSys, loadIdle, loadIo, loadIrq, loadSirq, loadAvg});
});
return retArray;
},
allCPUs: {
makeTable() {
this.table = E('table', { 'class': 'table' });
},
append(cpuNum, cpuLoadObj, cpuLoadFlag) {
if(cpuNum == Infinity) {
this.table.append(
E('tr', { 'class': 'tr' }, [
E('td', { 'class': 'td left', 'width': '33%' },
_('Total load')),
E('td', { 'class': 'td' },
E('div', {
'class': 'cbi-progressbar',
'title': (cpuLoadFlag) ?
cpuLoadObj.loadAvg + '%' :
_('Calculating') + '...',
},
E('div', { 'style': 'width:' + cpuLoadObj.loadAvg + '%' })
)
),
])
);
};
},
},
allCPUsDetail: {
cpuTableTitles: [
_('All CPUs'),
_('Load'),
'user %',
'nice %',
'system %',
'idle %',
'iowait %',
'irq %',
'softirq %',
],
makeTable() {
this.table = E('table', { 'class': 'table' },
E('tr', { 'class': 'tr table-titles' }, [
E('th', { 'class': 'th left' }, this.cpuTableTitles[0]),
E('th', { 'class': 'th left' }, this.cpuTableTitles[1]),
E('th', { 'class': 'th center' }, this.cpuTableTitles[2]),
E('th', { 'class': 'th center' }, this.cpuTableTitles[3]),
E('th', { 'class': 'th center' }, this.cpuTableTitles[4]),
E('th', { 'class': 'th center' }, this.cpuTableTitles[5]),
E('th', { 'class': 'th center' }, this.cpuTableTitles[6]),
E('th', { 'class': 'th center' }, this.cpuTableTitles[7]),
E('th', { 'class': 'th center' }, this.cpuTableTitles[8]),
])
);
},
append(cpuNum, cpuLoadObj, cpuLoadFlag) {
if(cpuNum == Infinity) {
this.table.append(
E('tr', { 'class': 'tr' }, [
E('td', { 'class': 'td left', 'data-title': this.cpuTableTitles[0] },
_('Total load')),
E('td', { 'class': 'td left', 'data-title': this.cpuTableTitles[1] },
E('div', {
'class': 'cbi-progressbar',
'title': (cpuLoadFlag) ?
cpuLoadObj.loadAvg + '%' :
_('Calculating') + '...',
'style': 'min-width:8em !important',
},
E('div', { 'style': 'width:' + cpuLoadObj.loadAvg + '%' })
)
),
E('td', { 'class': 'td center',
'data-title': this.cpuTableTitles[2] }, cpuLoadObj.loadUser),
E('td', { 'class': 'td center',
'data-title': this.cpuTableTitles[3] }, cpuLoadObj.loadNice),
E('td', { 'class': 'td center',
'data-title': this.cpuTableTitles[4] }, cpuLoadObj.loadSys),
E('td', { 'class': 'td center',
'data-title': this.cpuTableTitles[5] }, cpuLoadObj.loadIdle),
E('td', { 'class': 'td center',
'data-title': this.cpuTableTitles[6] }, cpuLoadObj.loadIo),
E('td', { 'class': 'td center',
'data-title': this.cpuTableTitles[7] }, cpuLoadObj.loadIrq),
E('td', { 'class': 'td center',
'data-title': this.cpuTableTitles[8] }, cpuLoadObj.loadSirq),
])
);
};
},
},
eachCPU: {
makeTable() {
this.table = E('table', { 'class': 'table' });
},
append(cpuNum, cpuLoadObj, cpuLoadFlag) {
this.table.append(
E('tr', { 'class': 'tr' }, [
E('td', { 'class': 'td left', 'width': '33%' },
(cpuNum == Infinity) ? _('Total load') : _('CPU') + ' ' + cpuNum),
E('td', { 'class': 'td' },
E('div', {
'class': 'cbi-progressbar',
'title': (cpuLoadFlag) ?
cpuLoadObj.loadAvg + '%' :
_('Calculating') + '...',
},
E('div', { 'style': 'width:' + cpuLoadObj.loadAvg + '%' })
)
),
])
);
},
},
eachCPUDetail: {
cpuTableTitles: [
_('CPU'),
_('Load'),
'user %',
'nice %',
'system %',
'idle %',
'iowait %',
'irq %',
'softirq %',
],
makeTable() {
this.table = E('table', { 'class': 'table' },
E('tr', { 'class': 'tr table-titles' }, [
E('th', { 'class': 'th left' }, this.cpuTableTitles[0]),
E('th', { 'class': 'th left' }, this.cpuTableTitles[1]),
E('th', { 'class': 'th center' }, this.cpuTableTitles[2]),
E('th', { 'class': 'th center' }, this.cpuTableTitles[3]),
E('th', { 'class': 'th center' }, this.cpuTableTitles[4]),
E('th', { 'class': 'th center' }, this.cpuTableTitles[5]),
E('th', { 'class': 'th center' }, this.cpuTableTitles[6]),
E('th', { 'class': 'th center' }, this.cpuTableTitles[7]),
E('th', { 'class': 'th center' }, this.cpuTableTitles[8]),
])
);
},
append(cpuNum, cpuLoadObj, cpuLoadFlag) {
this.table.append(
E('tr', { 'class': 'tr' }, [
E('td', { 'class': 'td left', 'data-title': this.cpuTableTitles[0] },
(cpuNum == Infinity) ? _('Total load') : _('CPU') + ' ' + cpuNum),
E('td', { 'class': 'td left', 'data-title': this.cpuTableTitles[1] },
E('div', {
'class': 'cbi-progressbar',
'title': (cpuLoadFlag) ?
cpuLoadObj.loadAvg + '%' :
_('Calculating') + '...',
'style': 'min-width:8em !important',
},
E('div', { 'style': 'width:' + cpuLoadObj.loadAvg + '%' })
)
),
E('td', { 'class': 'td center',
'data-title': this.cpuTableTitles[2] }, cpuLoadObj.loadUser),
E('td', { 'class': 'td center',
'data-title': this.cpuTableTitles[3] }, cpuLoadObj.loadNice),
E('td', { 'class': 'td center',
'data-title': this.cpuTableTitles[4] }, cpuLoadObj.loadSys),
E('td', { 'class': 'td center',
'data-title': this.cpuTableTitles[5] }, cpuLoadObj.loadIdle),
E('td', { 'class': 'td center',
'data-title': this.cpuTableTitles[6] }, cpuLoadObj.loadIo),
E('td', { 'class': 'td center',
'data-title': this.cpuTableTitles[7] }, cpuLoadObj.loadIrq),
E('td', { 'class': 'td center',
'data-title': this.cpuTableTitles[8] }, cpuLoadObj.loadSirq),
])
);
},
},
makeCPUTable(cpuLoadArray) {
let currentView = (this.availableViewModes[this.viewMode] !== undefined) ?
this[this.availableViewModes[this.viewMode][0]] :
this[this.availableViewModes[0][0]];
currentView.makeTable();
cpuLoadArray.forEach((c, i) => {
currentView.append(c.cpuNum, c, (this.lastStatArray !== null));
});
return currentView.table;
},
load() {
this.restoreSettingsFromLocalStorage();
return L.resolveDefault(fs.read('/proc/stat'), null);
},
render(cpuData) {
if(!cpuData) return;
let cpuStatArray = this.parseProcData(cpuData);
// For single-core CPU
if(cpuStatArray.length == 2) {
cpuStatArray = cpuStatArray.slice(1, 2);
delete this.availableViewModes[2];
delete this.availableViewModes[3];
if(this.viewMode > 1) this.viewMode = 0;
};
let cpuLoadArray = this.calcCPULoad(cpuStatArray);
let cpuTable = this.makeCPUTable(cpuLoadArray);
let viewModeSelectFunc = value => {
this.saveSettingsToLocalStorage(value);
this.viewMode = value;
viewModeEntries.forEach(i => {
i.classList.remove('cpu-status-view-mode-entry-checked');
});
viewModeEntries[value].classList.add('cpu-status-view-mode-entry-checked');
let newTable = this.makeCPUTable(cpuLoadArray);
cpuTable.replaceWith(newTable);
cpuTable = newTable;
};
let viewModeSelectOnClick = ev => {
viewModeSelectFunc(ev.target.dataset.value);
ev.target.blur();
};
let viewModeEntries = [];
for(let [i, v] of Object.entries(this.availableViewModes)) {
viewModeEntries.push(
E('span', {
'class' : 'cpu-status-view-mode-entry',
'data-value': i,
'click' : viewModeSelectOnClick,
}, v[1])
);
};
viewModeSelectFunc(this.viewMode);
this.lastStatArray = cpuStatArray;
return E('div', { 'class': 'cbi-section' }, [
E('div', { 'style': 'margin-bottom:1em' },
viewModeEntries
),
cpuTable,
]);
},
});