// app.js (оркестратор) import { fetchHealth, fetchHistory } from "./data.js"; import { state, addSignals, resetBufferTracking, updatePeakHolder, forceResetPeak, getPeakInfo } 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: number = 0; let connectionLost: boolean = false; let consecutiveErrors: number = 0; const MAX_ERRORS: number = 3; let pollTimer: number | null = null; // ================= INIT ================= window.addEventListener("DOMContentLoaded", () => { initDOM(); initAccordion(); initResize(); initCamera(); }); document.addEventListener("keydown", (e: KeyboardEvent) => { const leftPanel = document.getElementById("leftPanel"); const rightPanel = document.getElementById("rightPanel"); 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"; if (leftPanel && rightPanel) { const total = window.innerWidth; const leftWidth = total * 0.6; leftPanel.style.flex = `0 0 ${leftWidth}px`; rightPanel.style.flex = `0 0 ${total - leftWidth}px`; } break; } }); // ================= UPDATE ================= async function update(): Promise { try { const [health, history] = await Promise.all([ fetchHealth(), fetchHistory() ]); consecutiveErrors = 0; if (connectionLost) { console.log("[app] Соединение восстановлено"); } const isOnline = health.pipe_alive && health.has_data; if (isOnline) { setStatus("На связи"); connectionLost = false; } else { setStatus("Нет связи", true); if (!connectionLost) resetBufferTracking(); connectionLost = true; } const arr = Array.isArray(history.bytes) ? history.bytes : []; const raw = arr[arr.length - 1] ?? 0; if (arr.length > 0 && isOnline) { const bufferSize = health.stats?.filled ?? 0; addSignals(arr, bufferSize); } const value = raw & 0x3F; const level = (raw >> 6) & 0x3; const percent = (value / 63) * 100; if (isOnline) { const displayValue = updatePeakHolder(percent); setBars(displayValue); setSignal(level); } else { // Сервер НЕ на связи - сбрасываем индикаторы в 0 и делаем серыми setBars(0, true); // true = режим "нет соединения" setSignal(0, true); // true = режим "нет соединения" } if (DOM.lastByte) { DOM.lastByte.textContent = isOnline ? value.toString() : "--"; } const uptime = Math.round(health.uptime_sec || 0); const filled = health.stats?.filled ?? 0; const Bps = Math.round(health.stats?.bytes_per_sec ?? 0); setMeta(`Работа: ${uptime}s | Буфер: ${filled} | Приём: ${Bps} B/s`); if (DOM.history) { DOM.history.innerHTML = arr .map((b, i) => `[${i}] ${(b & 0xFF).toString(2).padStart(8, "0")} = ${b & 0xFF}`) .join("
"); } 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 && pollTimer) { clearInterval(pollTimer); pollTimer = window.setInterval(update, 2000); console.log("[app] Замедлили опрос до 2с"); } } } function startPolling(): void { if (pollTimer) clearInterval(pollTimer); if (consecutiveErrors > 0) update(); pollTimer = window.setInterval(update, 500); } window.addEventListener("focus", () => { if (consecutiveErrors >= MAX_ERRORS) { console.log("[app] Окно в фокусе, ускоряем опрос"); consecutiveErrors = 0; startPolling(); } }); startPolling(); update();