diff --git a/web/index.html b/web/index.html index ac776e9..0056901 100644 --- a/web/index.html +++ b/web/index.html @@ -3,10 +3,16 @@ Webstat - + + -
+
+
diff --git a/web/main.mjs b/web/main.mjs new file mode 100644 index 0000000..4eae58c --- /dev/null +++ b/web/main.mjs @@ -0,0 +1,32 @@ +import * as WebStat from "./webstat.mjs" + +const Millisecond = 1 +const Second = 1000 * Millisecond +const Minute = 60 * Second + +class App { + constructor() { + this.stat = new WebStat.Stat() + this.areaChart = new WebStat.AreaChart(document.querySelector("#chart")) + this.pieChart = new WebStat.PieChart(document.querySelector("#pie")) + + setInterval(()=>this.update(), 2 * Second) + } + + async update() { + this.stat.update() + this.areaChart.update(this.stat) + this.pieChart.update(this.stat) + } + +} + +function init() { + new App() +} + +if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", init) +} else { + init() +} diff --git a/web/webstat.mjs b/web/webstat.mjs index eae42b5..dc6d8b7 100644 --- a/web/webstat.mjs +++ b/web/webstat.mjs @@ -1,10 +1,6 @@ //import Chart from "https://esm.run/chart.js@4.2.1/auto" import Chart from "https://cdn.jsdelivr.net/npm/chart.js@4.2.1/auto/+esm" -const Millisecond = 1 -const Second = 1000 * Millisecond -const Minute = 60 * Second - function qpush(arr, val, len) { arr.push(val) if (arr.length > len) { @@ -13,36 +9,53 @@ function qpush(arr, val, len) { } class Stat { - constructor(text) { - let lines = text.split("\n") + constructor() { + this.Stat = {} + this.LastStat = {} + } + + async update() { + let resp = await fetch("proc/stat") + let stext = await resp.text() + let lines = stext.split("\n") + this.Date = new Date(resp.headers.get("Date")) + this.LastStat = this.Stat + this.Stat = {} for (let line of lines) { let parts = line.split(/\s+/) let key = parts.shift() let vals = parts.map(Number) - if (key.startsWith("cpu")) { - this[key] = { - user: vals[0], - nice: vals[1], - sys: vals[2], - idle: vals[3], - wait: vals[4], - irq: vals[5], - softirq: vals[6], - steal: vals[7], - guest: vals[8], - guestNice: vals[9], - } - } else { - this[key] = vals - } + this.Stat[key] = vals } } + + cpu(n="") { + let key = `cpu${n}` + let vals = this.Stat[key] + let prev = this.LastStat[key] + if (!vals || !prev) { + return {} + } + let total = vals.reduce((a,b)=>a+b) - prev.reduce((a,b)=>a+b) + return { + user: (vals[0] - prev[0]) / total, + nice: (vals[1] - prev[1]) / total, + sys: (vals[2] - prev[2]) / total, + idle: (vals[3] - prev[3]) / total, + wait: (vals[4] - prev[4]) / total, + irq: (vals[5] - prev[5]) / total, + softirq: (vals[6] - prev[6]) / total, + steal: (vals[7] - prev[7]) / total, + guest: (vals[8] - prev[8]) / total, + guestNice: (vals[9] - prev[9]) / total, + } + } } -class StatUpdater { - constructor(interval, width=60) { +class AreaChart { + constructor(element, width=60) { this.width = width - this.canvas = document.querySelector("#stats") + this.canvas = element.appendChild(document.createElement("canvas")) this.data ={ labels: [], datasets: [] @@ -78,44 +91,63 @@ class StatUpdater { } } ) - - setInterval(() => this.update(), interval) - this.update() } - async update() { - let now = Date.now() - let resp = await fetch("proc/stat") - let stext = await resp.text() - let stat = new Stat(stext) - if (this.last) { - let user = stat.cpu.user - this.last.cpu.user - let nice = stat.cpu.nice - this.last.cpu.nice - let sys = stat.cpu.sys - this.last.cpu.sys - let idle = stat.cpu.idle - this.last.cpu.idle - let wait = stat.cpu.wait - this.last.cpu.wait - let total = user + nice + sys + idle + wait - - qpush(this.data.labels, now, this.width) - qpush(this.datasets.user, user/total, this.width) - qpush(this.datasets.nice, nice/total, this.width) - qpush(this.datasets.sys, sys/total, this.width) - qpush(this.datasets.idle, idle/total, this.width) - qpush(this.datasets.wait, wait/total, this.width) - - //this.data.datasets[0].label= `user: ${user/total*100}` - } - this.last = stat + async update(stat) { + let now = stat.Date + let cpu = stat.cpu() + qpush(this.data.labels, now, this.width) + qpush(this.datasets.user, cpu.user, this.width) + qpush(this.datasets.nice, cpu.nice, this.width) + qpush(this.datasets.sys, cpu.sys, this.width) + qpush(this.datasets.idle, cpu.idle, this.width) + qpush(this.datasets.wait, cpu.wait, this.width) this.chart.update() } } -function init() { - new StatUpdater(2*Second) +class PieChart { + constructor(element) { + this.canvas = element.appendChild(document.createElement("canvas")) + this.data ={ + labels: ["user", "nice", "sys", "idle", "wait"], + datasets: [] + } + this.chart = new Chart( + this.canvas, + { + type: "pie", + data: this.data, + options: { + animation: false, + borderWidth: 0, + backgroundColor: [ + "red", + "green", + "orange", + "rgba(0, 64, 0, 0.2)", + "magenta", + ], + plugins: { + legend: { display: false }, + }, + }, + } + ) + } + + async update(stat) { + let cpu = stat.cpu() + this.data.datasets = [{ + label: "Current", + data: [cpu.user, cpu.nice, cpu.sys, cpu.idle, cpu.wait], + }] + this.chart.update() + } } -if (document.readyState === "loading") { - document.addEventListener("DOMContentLoaded", init) -} else { - init() -} +export { + Stat, + AreaChart, + PieChart, +} \ No newline at end of file