succbone: init
This commit is contained in:
parent
4e31493f0a
commit
05d102ab9b
8 changed files with 591 additions and 0 deletions
247
succd/index.html
Normal file
247
succd/index.html
Normal file
|
@ -0,0 +1,247 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<title>succd</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<style>
|
||||
body {
|
||||
font-size: 14px;
|
||||
padding: 2em;
|
||||
}
|
||||
table {
|
||||
font-size: 40px;
|
||||
}
|
||||
th, td {
|
||||
background-color: #e8e8e8;
|
||||
padding: 0.4em;
|
||||
}
|
||||
th {
|
||||
font-weight: 100;
|
||||
text-align: right;
|
||||
font-size: 30px;
|
||||
}
|
||||
td {
|
||||
text-align: left;
|
||||
}
|
||||
td {
|
||||
font-weight: 800;
|
||||
}
|
||||
h2 {
|
||||
font-style: italic;
|
||||
font-weight: 100;
|
||||
}
|
||||
</style>
|
||||
|
||||
<h1>succd</h1>
|
||||
<h2>nothing more permanent than a temporary solution</h2>
|
||||
|
||||
<p style="margin-top: 5em;">
|
||||
<table>
|
||||
<tr>
|
||||
<th>Voltage</th>
|
||||
<td id="volts">{{.volts}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Pressure</th>
|
||||
<td id="mbar">{{.mbar}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Status</th>
|
||||
<td id="status">OK</td>
|
||||
</tr>
|
||||
</table>
|
||||
</p>
|
||||
<p style="margin-top: 2em;">
|
||||
<canvas id="graph" width="1024" height="512"></canvas>
|
||||
</p>
|
||||
|
||||
<p style="font-style: italic; font-size: 12px; margin-top: 5em;">
|
||||
{{.hostname}} | load: {{.load}} | <a href="/debug/pprof">pprof</a> | <a href="/metrics">metrics</a> | ws ping: <span id="ping">…</span>
|
||||
</p>
|
||||
|
||||
<script>
|
||||
|
||||
let historical = [];
|
||||
let canvas = null;
|
||||
|
||||
// Push a datapoint (in mbar) to the historical buffer, maintaining enough data
|
||||
// for ~10 minutes.
|
||||
let historicalPush = (v) => {
|
||||
historical.push({v: v, time: Date.now() / 1000});
|
||||
let len = historical.length;
|
||||
// TODO(q3k): trim based on recorded timestamp, not constant buffer size.
|
||||
let trim = len - 8192;
|
||||
if (trim > 0) {
|
||||
historical = historical.slice(trim);
|
||||
}
|
||||
};
|
||||
|
||||
// Draw the historical graph and schedule next draw in 100msec.
|
||||
let historicalDraw = (w, h) => {
|
||||
const now = Date.now() / 1000;
|
||||
|
||||
// TODO(q3k): better use canvas API to not have so much silly math around
|
||||
// coordinate calculation.
|
||||
|
||||
canvas.clearRect(0, 0, w, h);
|
||||
canvas.fillStyle = "#f0f0f0";
|
||||
canvas.fillRect(0, 0, w, h);
|
||||
|
||||
// Margins of the main graph window.
|
||||
const marginLeft = 64;
|
||||
const marginRight = 32;
|
||||
const marginTop = 32;
|
||||
const marginBottom = 32;
|
||||
|
||||
// Draw main graph window.
|
||||
canvas.fillStyle = "#f8f8f8";
|
||||
canvas.strokeStyle = "#444";
|
||||
canvas.lineWidth = 1;
|
||||
canvas.fillRect(marginLeft, marginTop, w-(marginLeft+marginRight), h-(marginTop+marginBottom));
|
||||
canvas.strokeRect(marginLeft, marginTop, w-(marginLeft+marginRight), h-(marginTop+marginBottom));
|
||||
|
||||
// Range of decades for Y value.
|
||||
const ymin = -4;
|
||||
const ymax = 4;
|
||||
// Pixels per decade.
|
||||
const yscale = (h - (marginTop+marginBottom)) / (ymax - ymin);
|
||||
|
||||
// For every decade...
|
||||
for (let i = ymin; i < ymax; i++) {
|
||||
const yoff = (i - ymin) * yscale + yscale / 2;
|
||||
const y = Math.floor(h - marginBottom - yoff) + 0.5;
|
||||
// Draw Y scale ticks.
|
||||
canvas.beginPath();
|
||||
canvas.moveTo(marginLeft-5, y);
|
||||
canvas.lineTo(marginLeft, y);
|
||||
canvas.strokeStyle = "#000";
|
||||
canvas.stroke();
|
||||
|
||||
// Draw Y grid.
|
||||
canvas.beginPath();
|
||||
canvas.moveTo(marginLeft, y);
|
||||
canvas.lineTo(w-marginRight-1, y);
|
||||
canvas.strokeStyle = "#ccc";
|
||||
canvas.stroke();
|
||||
|
||||
// Draw Y fine grid.
|
||||
if (i > ymin) {
|
||||
for (let j = 2; j < 10; j++) {
|
||||
let yy = y - Math.log10(j/10) * yscale;
|
||||
canvas.beginPath();
|
||||
canvas.moveTo(marginLeft, yy);
|
||||
canvas.lineTo(w-marginRight-1, yy);
|
||||
canvas.strokeStyle = "#eee";
|
||||
canvas.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
// Draw Y labels.
|
||||
canvas.font = "10px sans-serif";
|
||||
canvas.fillStyle = "#000";
|
||||
canvas.textAlign = "right";
|
||||
const text = `10^${i}`;
|
||||
canvas.fillText(text, marginLeft-10, y+5);
|
||||
}
|
||||
|
||||
// How much space to leave in front of the graph.
|
||||
const xhead = 10;
|
||||
|
||||
// Draw X labels..
|
||||
canvas.textAlign = "center";
|
||||
canvas.fillText("Now", w - marginRight - xhead, h - marginBottom + 15)
|
||||
canvas.fillText("-10min", marginLeft, h - marginBottom + 15)
|
||||
|
||||
const xmax = 60 * 10;
|
||||
const xscale = (w - (marginLeft+marginRight+xhead)) / (xmax);
|
||||
|
||||
// Clip to main window.
|
||||
canvas.save();
|
||||
canvas.beginPath();
|
||||
canvas.rect(marginLeft, marginTop, w-(marginLeft+marginRight), h-(marginTop+marginBottom+1));
|
||||
canvas.clip();
|
||||
|
||||
// Draw actual data line.
|
||||
let first = true;
|
||||
canvas.beginPath();
|
||||
historical.forEach((v) => {
|
||||
const time = v.time;
|
||||
const mbar = v.v;
|
||||
const elapsed = now-time;
|
||||
if (elapsed > xmax) {
|
||||
return;
|
||||
}
|
||||
|
||||
const x = (w - marginRight - xhead) - (elapsed * xscale);
|
||||
const yoff = (Math.log10(mbar) - ymin) * yscale + yscale / 2;
|
||||
const y = h - marginBottom - yoff;
|
||||
|
||||
if (first) {
|
||||
first = false;
|
||||
canvas.moveTo(x, y);
|
||||
} else {
|
||||
canvas.lineTo(x, y);
|
||||
}
|
||||
});
|
||||
canvas.strokeStyle = "#de1010";
|
||||
canvas.lineWidth = 1;
|
||||
canvas.stroke();
|
||||
canvas.restore();
|
||||
|
||||
setTimeout(() => { historicalDraw(w, h); }, 100);
|
||||
};
|
||||
|
||||
window.addEventListener("load", (_) => {
|
||||
console.log("s u c c");
|
||||
|
||||
let status = document.querySelector("#status");
|
||||
let volts = document.querySelector("#volts");
|
||||
let mbar = document.querySelector("#mbar");
|
||||
let ping = document.querySelector("#ping");
|
||||
canvas = document.querySelector("#graph").getContext("2d");
|
||||
|
||||
// TODO(q3k): unhardcode this and generally support scaling canvas.
|
||||
historicalDraw(1024, 512);
|
||||
|
||||
// Basic retry loop for connecting to WS.
|
||||
let loc = window.location;
|
||||
let wsloc = "";
|
||||
if (loc.protocol == "https:") {
|
||||
wsloc = "wss:";
|
||||
} else {
|
||||
wsloc = "ws:";
|
||||
}
|
||||
wsloc += "//" + loc.host + "/stream";
|
||||
console.log("Connecting to " + wsloc + "...");
|
||||
|
||||
let connected = false;
|
||||
let connect = () => {
|
||||
const socket = new WebSocket(wsloc);
|
||||
socket.addEventListener("open", (event) => {
|
||||
connected = true;
|
||||
console.log("Socket connected!");
|
||||
status.innerHTML = "Online";
|
||||
status.style = "background-color: #60f060;";
|
||||
});
|
||||
socket.addEventListener("message", (event) => {
|
||||
const data = JSON.parse(event.data);
|
||||
volts.innerHTML = data.Volts;
|
||||
mbar.innerHTML = data.Mbar;
|
||||
historicalPush(data.MbarFloat);
|
||||
ping.innerHTML = Date.now();
|
||||
});
|
||||
socket.addEventListener("close", (event) => {
|
||||
status.innerHTML = "Offline";
|
||||
status.style = "background-color: #f06060;";
|
||||
if (connected) {
|
||||
console.log("Socket dead, reconnecting...");
|
||||
}
|
||||
connected = false;
|
||||
setTimeout(connect, 1000);
|
||||
});
|
||||
socket.addEventListener("error", (event) => {
|
||||
socket.close();
|
||||
});
|
||||
};
|
||||
connect();
|
||||
});
|
||||
</script>
|
Loading…
Add table
Add a link
Reference in a new issue