126 lines
2.5 KiB
HTML
126 lines
2.5 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>GPIO Monitor Dashboard</title>
|
|
|
|
<style>
|
|
body {
|
|
font-family: monospace;
|
|
background: #0b0f0c;
|
|
color: #00ff88;
|
|
padding: 20px;
|
|
}
|
|
|
|
.card {
|
|
border: 1px solid #00ff88;
|
|
padding: 12px;
|
|
margin-bottom: 12px;
|
|
border-radius: 8px;
|
|
}
|
|
|
|
.status-ok { color: #00ff88; }
|
|
.status-bad { color: #ff4444; }
|
|
|
|
.bits {
|
|
display: flex;
|
|
gap: 8px;
|
|
margin-top: 10px;
|
|
}
|
|
|
|
.bit {
|
|
width: 40px;
|
|
height: 40px;
|
|
border: 1px solid #00ff88;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.bit.on {
|
|
background: #00ff88;
|
|
color: black;
|
|
}
|
|
|
|
.small {
|
|
opacity: 0.7;
|
|
font-size: 12px;
|
|
}
|
|
</style>
|
|
</head>
|
|
|
|
<body>
|
|
|
|
<h1>GPIO Monitor</h1>
|
|
|
|
<div class="card">
|
|
<div>PIPE STATUS: <span id="pipeStatus">...</span></div>
|
|
<div>DATA FLOW: <span id="dataFlow">...</span></div>
|
|
<div class="small" id="meta"></div>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<div>LATEST BYTE: <span id="latest">--</span></div>
|
|
<div class="bits" id="bits"></div>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<div>HISTORY (last 16 bytes)</div>
|
|
<div id="history"></div>
|
|
</div>
|
|
|
|
<script>
|
|
|
|
function renderBits(byte) {
|
|
const bits = document.getElementById("bits");
|
|
bits.innerHTML = "";
|
|
|
|
for (let i = 7; i >= 0; i--) {
|
|
const v = (byte >> i) & 1;
|
|
const div = document.createElement("div");
|
|
div.className = "bit" + (v ? " on" : "");
|
|
div.textContent = v;
|
|
bits.appendChild(div);
|
|
}
|
|
}
|
|
|
|
async function update() {
|
|
try {
|
|
const r = await fetch("/api/health");
|
|
const h = await r.json();
|
|
|
|
const pipeStatus = document.getElementById("pipeStatus");
|
|
const dataFlow = document.getElementById("dataFlow");
|
|
|
|
pipeStatus.textContent = h.status;
|
|
pipeStatus.className = h.status === "ok" ? "status-ok" : "status-bad";
|
|
|
|
dataFlow.textContent = h.last_data_ms + " ms idle";
|
|
|
|
document.getElementById("meta").textContent =
|
|
"uptime: " + Math.round(h.uptime_sec) + "s | filled: " + h.stats.filled;
|
|
|
|
const latest = h.latest;
|
|
|
|
document.getElementById("latest").textContent = latest;
|
|
renderBits(latest);
|
|
|
|
const r2 = await fetch("/api/history");
|
|
const hist = await r2.json();
|
|
|
|
document.getElementById("history").innerHTML =
|
|
hist.bytes.map(b => "0x" + b.toString(16).padStart(2,"0")).join(" ");
|
|
|
|
} catch(e) {
|
|
document.getElementById("pipeStatus").textContent = "SERVER DOWN";
|
|
}
|
|
}
|
|
|
|
setInterval(update, 500);
|
|
update();
|
|
|
|
</script>
|
|
|
|
</body>
|
|
</html>
|