// ===== SMOOTH STATE ===== let lastValue = 0; // ===== ACCORDION ===== const historyHeader = document.getElementById("history-header"); const historyBody = document.getElementById("history-body"); let accordionOpen = false; if (historyBody && historyHeader) { historyBody.classList.add("collapsed"); historyHeader.addEventListener("click", () => { accordionOpen = !accordionOpen; historyHeader.textContent = (accordionOpen ? "▼" : "▶") + " ПРИНЯТЫЕ ДАННЫЕ"; historyBody.classList.toggle("collapsed", !accordionOpen); }); } // ===== PROGRESS ===== function setBar(id, value) { const el = document.getElementById(id); if (!el) return; value = Math.max(0, Math.min(100, value)); el.style.width = value + "%"; } // ===== SIGNAL ===== function setSignal(level) { const bars = document.querySelectorAll(".sig-bar"); if (!bars.length) return; bars.forEach(b => b.classList.remove("active-1", "active-2", "active-3", "dim")); bars.forEach(b => b.classList.add("dim")); if (level >= 1) { bars[0].classList.add("active-1"); bars[0].classList.remove("dim"); } if (level >= 2) { bars[1].classList.add("active-2"); bars[1].classList.remove("dim"); } if (level >= 3) { bars[2].classList.add("active-3"); bars[2].classList.remove("dim"); } } // ===== SMOOTH ===== function smoothBar(target) { const alpha = 0.2; lastValue = lastValue + alpha * (target - lastValue); return lastValue; } // ===== MEDIAN ===== function median(values) { if (!values || values.length === 0) return 0; const sorted = [...values].sort((a, b) => a - b); const mid = Math.floor(sorted.length / 2); return sorted.length % 2 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; } // ===== UPDATE LOOP ===== async function update() { try { const [r1, r2] = await Promise.all([ fetch("/api/health"), fetch("/api/history") ]); const h = await r1.json(); const hist = await r2.json(); // ===== STATUS ===== const statusEl = document.getElementById("status"); if (statusEl) statusEl.textContent = h.status || "неизвестно"; const filled = h.stats?.filled ?? 0; const uptime = Math.round(h.uptime_sec || 0); const bps = Math.round(h.stats?.bits_per_sec ?? 0); const Bps = Math.round(h.stats?.bytes_per_sec ?? 0); const meta = document.getElementById("meta"); if (meta) { meta.textContent = `работа: ${uptime}s | буфер: ${filled} | ${bps} bit/s (${Bps} B/s)`; } // ===== LATEST ===== const latest = Number(h.latest || 0); const latestEl = document.getElementById("latest"); if (latestEl) latestEl.textContent = latest; // optional bits view if (typeof renderBits === "function") { renderBits(latest); } // ===== HISTORY ===== const arr = Array.isArray(hist.bytes) ? hist.bytes : []; const historyEl = document.getElementById("history"); if (historyEl) { historyEl.innerHTML = arr .map((b, i) => `[${i}] ${(Number(b) & 0xFF).toString(2).padStart(8, "0")} = ${Number(b) & 0xFF}` ) .join("
"); } // ===== SIGNAL PARSING ===== const raw = arr.length ? (arr[arr.length - 1] & 0xFF) : 0; const barValue = raw & 0x3F; // 0–63 const signalLevel = (raw >> 6) & 0x3; // 0–3 const percent = (barValue / 63) * 100; const smooth = smoothBar(percent); setBar("bar1", smooth); setSignal(signalLevel); const lastByte = document.getElementById("last-byte"); if (lastByte) lastByte.textContent = barValue; } catch (e) { console.error("UPDATE ERROR:", e); const status = document.getElementById("status"); if (status) status.textContent = "СЕРВЕР НЕДОСТУПЕН"; } } // ===== START ===== setInterval(update, 500); update();