// app.js (оркестратор) import { fetchHealth, fetchHistory } from "./data.js"; import { state, smooth, addSignals, resetBufferTracking } from "./state.js"; import { initDOM, setStatus, setMeta, setBars, setSignal, DOM } from "./ui.js"; import { initAccordion } from "./accordion.js"; import { drawChart } from "./chart.js"; import { initResize } from "./layout.js"; import { initCamera } from "./camera.js"; let lastChart = 0; let connectionLost = false; let consecutiveErrors = 0; const MAX_ERRORS = 3; let pollTimer = null; // ================= INIT ================= window.addEventListener("DOMContentLoaded", () => { initDOM(); initAccordion(); initResize(); initCamera(); }); document.addEventListener("keydown", (e) => { switch(e.key.toLowerCase()) { case 'c': // Переключение камеры const camBtn = document.getElementById('camToggle'); if (camBtn) camBtn.click(); break; case 'f': // На весь экран для левой панели if (leftPanel) { leftPanel.style.width = "100%"; if (rightPanel) rightPanel.style.display = "none"; } break; case 'escape': if (rightPanel) rightPanel.style.display = "flex"; applyRatio(0.6); // 60% левая, 40% правая break; } }); // ================= UPDATE ================= async function update() { try { const [h, hist] = await Promise.all([ fetchHealth(), fetchHistory() ]); consecutiveErrors = 0; if (connectionLost) { console.log("[app] Соединение восстановлено"); } // ===== STATUS ===== const isOnline = h.pipe_alive && h.has_data; if (isOnline) { setStatus("На связи"); connectionLost = false; } else { setStatus("Нет связи", true); if (!connectionLost) { resetBufferTracking(); } connectionLost = true; } const arr = Array.isArray(hist.bytes) ? hist.bytes : []; const raw = arr[arr.length - 1] ?? 0; // ===== ДОБАВЛЯЕМ ТОЛЬКО НОВЫЕ БАЙТЫ ===== if (arr.length > 0 && isOnline) { const bufferSize = h.stats?.filled ?? 0; addSignals(arr, bufferSize); } // ===== DECODE ===== const value = raw & 0x3F; const level = (raw >> 6) & 0x3; const percent = (value / 63) * 100; // ===== UI ===== if (isOnline) { setBars(smooth(percent)); setSignal(level); } else { setBars(0, true); setSignal(0, true); } if (DOM.lastByte) { DOM.lastByte.textContent = isOnline ? value : "--"; } // ===== META ===== const uptime = Math.round(h.uptime_sec || 0); const filled = h.stats?.filled ?? 0; const Bps = Math.round(h.stats?.bytes_per_sec ?? 0); setMeta( `Работа: ${uptime}s | Буфер: ${filled} | Приём: ${Bps} B/s` ); // ===== HISTORY ===== if (DOM.history) { DOM.history.innerHTML = arr .map((b, i) => `[${i}] ${(b & 0xFF).toString(2).padStart(8, "0")} = ${b & 0xFF}` ) .join("
"); } // ===== CHART ===== const now = performance.now(); if (now - lastChart > 100 && DOM.canvas) { drawChart(DOM.canvas, state.signalHistory); lastChart = now; } } catch (e) { consecutiveErrors++; if (consecutiveErrors === 1) { console.warn("[app] Сервер недоступен, ждём..."); } setStatus("СЕРВЕР НЕДОСТУПЕН", true); setBars(0, true); setSignal(0, true); resetBufferTracking(); if (DOM.lastByte) { DOM.lastByte.textContent = "--"; } if (consecutiveErrors >= MAX_ERRORS) { clearInterval(pollTimer); pollTimer = setInterval(update, 2000); console.log("[app] Замедлили опрос до 2с"); } } } // ================= RECONNECT LOGIC ================= function startPolling() { if (pollTimer) clearInterval(pollTimer); if (consecutiveErrors > 0) { console.log("[app] Попытка переподключения..."); update(); } pollTimer = setInterval(update, 500); } window.addEventListener("focus", () => { if (consecutiveErrors >= MAX_ERRORS) { console.log("[app] Окно в фокусе, ускоряем опрос"); consecutiveErrors = 0; startPolling(); } }); // ================= START ================= startPolling(); update();