op-packages/luci-app-cpu-status/htdocs/luci-static/resources/view/status/include/18_cpu.js
github-actions[bot] 29852d0699 🏅 Sync 2026-04-14 23:55:21
2026-04-14 23:55:21 +08:00

364 lines
10 KiB
JavaScript

'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,
]);
},
});