Отладка css
This commit is contained in:
@@ -51,12 +51,22 @@ func (a *API) HandleLatest(w http.ResponseWriter, r *http.Request) {
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
func (a *API) HandleHistory(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
raw := a.buf.GetLast(100)
|
||||
|
||||
out := make([]int, len(raw))
|
||||
for i, v := range raw {
|
||||
out[i] = int(v)
|
||||
}
|
||||
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"bytes": a.buf.GetLast(100),
|
||||
"bytes": out,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// === FIFO READER ===
|
||||
func startPipeReader(ctx context.Context, buf *adapter.RingBuffer, path string) {
|
||||
go func() {
|
||||
@@ -119,19 +129,23 @@ func main() {
|
||||
buf: buf,
|
||||
startTime: time.Now(),
|
||||
}
|
||||
fs := http.FileServer(http.Dir("/home/user/temp/golang/web"))
|
||||
|
||||
http.Handle("/api/", http.StripPrefix("/api", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.URL.Path {
|
||||
case "/latest":
|
||||
cors(api.HandleLatest)(w, r)
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
switch r.URL.Path {
|
||||
case "/latest":
|
||||
cors(api.HandleLatest)(w, r)
|
||||
case "/history":
|
||||
cors(api.HandleHistory)(w, r)
|
||||
case "/health":
|
||||
cors(api.HandleHealth)(w, r)
|
||||
}
|
||||
default:
|
||||
http.Error(w, "not found", 404)
|
||||
}
|
||||
})))
|
||||
|
||||
http.Handle("/", fs)
|
||||
http.Handle("/", http.FileServer(http.Dir("/home/user/temp/golang/web")))
|
||||
|
||||
log.Println("server :8080")
|
||||
log.Fatal(http.ListenAndServe(":8080", nil))
|
||||
|
||||
151
web/css/style.css
Normal file
151
web/css/style.css
Normal file
@@ -0,0 +1,151 @@
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: monospace;
|
||||
background: #050805;
|
||||
color: #00ff88;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
font-size: 22px;
|
||||
margin: 0 0 15px;
|
||||
}
|
||||
|
||||
/* ===== GRID ===== */
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
/* Desktop */
|
||||
@media (min-width: 900px) {
|
||||
.grid {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
|
||||
.wide {
|
||||
grid-column: span 2;
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== CARD ===== */
|
||||
|
||||
.card {
|
||||
border: 1px solid #00ff88;
|
||||
border-radius: 10px;
|
||||
padding: 14px;
|
||||
background: rgba(0,255,136,0.05);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.card h3 {
|
||||
margin: 0 0 10px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
/* ===== STATUS ===== */
|
||||
|
||||
#status {
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
#meta {
|
||||
font-size: 14px;
|
||||
opacity: 0.8;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
/* ===== LATEST ===== */
|
||||
|
||||
#latest {
|
||||
font-size: 36px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
/* ===== BITS ===== */
|
||||
|
||||
#bits {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.bit {
|
||||
height: 52px;
|
||||
border: 1px solid #00ff88;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 22px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.bit.on {
|
||||
background: #00ff88;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
/* Tablet/Desktop */
|
||||
@media (min-width: 700px) {
|
||||
#bits {
|
||||
grid-template-columns: repeat(8, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== HISTORY ===== */
|
||||
|
||||
#history {
|
||||
font-size: 13px;
|
||||
line-height: 1.35;
|
||||
max-height: 420px;
|
||||
overflow-y: auto;
|
||||
white-space: nowrap;
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
/* Desktop */
|
||||
@media (min-width: 900px) {
|
||||
#history {
|
||||
font-size: 12px;
|
||||
max-height: 520px;
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== PHONE ===== */
|
||||
|
||||
@media (max-width: 500px) {
|
||||
|
||||
h1 {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
#status {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
#latest {
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
.bit {
|
||||
height: 44px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
#history {
|
||||
font-size: 16px;
|
||||
line-height: 1.5;
|
||||
max-height: 55vh;
|
||||
}
|
||||
}
|
||||
38
web/dashboard.html
Normal file
38
web/dashboard.html
Normal file
@@ -0,0 +1,38 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>GPIO Dashboard</title>
|
||||
|
||||
<link rel="stylesheet" href="/css/style.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<h1>GPIO REALTIME DASHBOARD</h1>
|
||||
|
||||
<div class="grid">
|
||||
|
||||
<div class="card">
|
||||
<h3>STATUS</h3>
|
||||
<div id="status">...</div>
|
||||
<div id="meta"></div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h3>LATEST SIGNAL</h3>
|
||||
<div id="latest">--</div>
|
||||
<div id="bits"></div>
|
||||
</div>
|
||||
|
||||
<div class="card wide">
|
||||
<h3>LIVE STREAM (last 32)</h3>
|
||||
<div id="history"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script src="/js/app.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
120
web/index.html
120
web/index.html
@@ -1,125 +1,9 @@
|
||||
<!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>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="refresh" content="0; url=/dashboard.html">
|
||||
</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>
|
||||
|
||||
58
web/js/app.js
Normal file
58
web/js/app.js
Normal file
@@ -0,0 +1,58 @@
|
||||
function renderBits(byte) {
|
||||
const bits = document.getElementById("bits");
|
||||
bits.innerHTML = "";
|
||||
|
||||
byte = Number(byte || 0);
|
||||
|
||||
for (let i = 7; i >= 0; i--) {
|
||||
const v = (byte >> i) & 1;
|
||||
|
||||
const d = document.createElement("div");
|
||||
d.className = "bit" + (v ? " on" : "");
|
||||
d.textContent = v;
|
||||
bits.appendChild(d);
|
||||
}
|
||||
}
|
||||
|
||||
async function update() {
|
||||
try {
|
||||
const r1 = await fetch("/api/health");
|
||||
const r2 = await fetch("/api/history");
|
||||
|
||||
const h = await r1.json();
|
||||
const hist = await r2.json();
|
||||
|
||||
console.log("health:", h);
|
||||
console.log("history:", hist);
|
||||
|
||||
document.getElementById("status").textContent = h.status || "unknown";
|
||||
|
||||
const filled = h.stats?.filled ?? 0;
|
||||
const uptime = Math.round(h.uptime_sec || 0);
|
||||
|
||||
document.getElementById("meta").textContent =
|
||||
"uptime: " + uptime + "s | filled: " + filled;
|
||||
|
||||
const latest = Number(h.latest || 0);
|
||||
|
||||
document.getElementById("latest").textContent = latest;
|
||||
|
||||
renderBits(latest);
|
||||
|
||||
const arr = Array.isArray(hist.bytes) ? hist.bytes : [];
|
||||
|
||||
document.getElementById("history").innerHTML =
|
||||
arr.map((b, i) =>
|
||||
"[" + i + "] " +
|
||||
(Number(b) & 15).toString(2).padStart(4, "0") +
|
||||
" = " + (Number(b) & 15)
|
||||
).join("<br>");
|
||||
|
||||
} catch (e) {
|
||||
console.error("UPDATE ERROR:", e);
|
||||
document.getElementById("status").textContent = "SERVER DOWN";
|
||||
}
|
||||
}
|
||||
|
||||
setInterval(update, 500);
|
||||
update();
|
||||
Reference in New Issue
Block a user