From fd627257f55df7e0e4658e12dcbe06f56860e5ba Mon Sep 17 00:00:00 2001 From: Maxim Date: Thu, 18 Jun 2026 11:01:56 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A0=D0=B5=D1=84=D0=B0=D0=BA=D1=82=D0=BE?= =?UTF-8?q?=D1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/server/web/dist/accordion.js | 18 ---- cmd/server/web/dist/app.js | 138 ---------------------------- cmd/server/web/dist/camera.js | 148 ------------------------------- cmd/server/web/dist/chart.js | 114 ------------------------ cmd/server/web/dist/data.js | 12 --- cmd/server/web/dist/layout.js | 79 ----------------- cmd/server/web/dist/state.js | 33 ------- cmd/server/web/dist/ui.js | 86 ------------------ 8 files changed, 628 deletions(-) delete mode 100644 cmd/server/web/dist/accordion.js delete mode 100644 cmd/server/web/dist/app.js delete mode 100644 cmd/server/web/dist/camera.js delete mode 100644 cmd/server/web/dist/chart.js delete mode 100644 cmd/server/web/dist/data.js delete mode 100644 cmd/server/web/dist/layout.js delete mode 100644 cmd/server/web/dist/state.js delete mode 100644 cmd/server/web/dist/ui.js diff --git a/cmd/server/web/dist/accordion.js b/cmd/server/web/dist/accordion.js deleted file mode 100644 index 529ebcd..0000000 --- a/cmd/server/web/dist/accordion.js +++ /dev/null @@ -1,18 +0,0 @@ -// accordion.ts -import { DOM } from "./ui.js"; -export function initAccordion() { - if (!DOM.historyHeader || !DOM.historyBody) - return; - let open = false; - DOM.historyBody.classList.add("collapsed"); - DOM.historyHeader.textContent = "▶ ПРИНЯТЫЕ ДАННЫЕ"; - DOM.historyHeader.addEventListener("click", () => { - open = !open; - if (DOM.historyHeader) { - DOM.historyHeader.textContent = (open ? "▼" : "▶") + " ПРИНЯТЫЕ ДАННЫЕ"; - } - if (DOM.historyBody) { - DOM.historyBody.classList.toggle("collapsed", !open); - } - }); -} diff --git a/cmd/server/web/dist/app.js b/cmd/server/web/dist/app.js deleted file mode 100644 index e985df7..0000000 --- a/cmd/server/web/dist/app.js +++ /dev/null @@ -1,138 +0,0 @@ -// 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) => { - 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() { - 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) { - setBars(smooth(percent)); - setSignal(level); - } - else { - setBars(0, true); - setSignal(0, 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() { - 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(); diff --git a/cmd/server/web/dist/camera.js b/cmd/server/web/dist/camera.js deleted file mode 100644 index c232fa1..0000000 --- a/cmd/server/web/dist/camera.js +++ /dev/null @@ -1,148 +0,0 @@ -// camera.ts -import { updateResizerVisibility } from './layout.js'; -let rightPanel = null; -let camImage = null; -let crosshair = null; -let isOpen = false; -let streamCheckInterval = null; -export function initCamera() { - rightPanel = document.getElementById("rightPanel"); - camImage = document.getElementById("camImage"); - crosshair = document.getElementById("crosshair"); - // Проверяем доступность потока - checkStreamAvailability(); - const btn = document.getElementById("camToggle"); - const streamUrl = "/api/cam"; - function open() { - if (isOpen) - return; - isOpen = true; - if (rightPanel) { - rightPanel.classList.remove("hidden"); - } - // Показываем перекрестие - if (crosshair) { - crosshair.style.display = "block"; - } - if (camImage) { - camImage.src = streamUrl; - } - if (btn) { - const textSpan = btn.querySelector('.cam-btn-text'); - if (textSpan) - textSpan.textContent = 'Скрыть'; - } - console.log("[camera] opened"); - updateResizerVisibility(); - // Запускаем мониторинг потока - startStreamMonitoring(); - } - function close() { - if (!isOpen) - return; - isOpen = false; - // Скрываем перекрестие - if (crosshair) { - crosshair.style.display = "none"; - } - // остановка stream - if (camImage) { - camImage.src = ""; - } - if (rightPanel) { - rightPanel.classList.add("hidden"); - } - if (btn) { - const textSpan = btn.querySelector('.cam-btn-text'); - if (textSpan) - textSpan.textContent = 'Камера'; - } - console.log("[camera] closed"); - updateResizerVisibility(); - // Останавливаем мониторинг - stopStreamMonitoring(); - } - function toggle() { - if (isOpen) { - close(); - } - else { - open(); - } - } - if (camImage) { - camImage.onload = () => { - console.log("[camera] stream started"); - if (crosshair) { - crosshair.style.opacity = '1'; - } - }; - camImage.onerror = (e) => { - console.error("[camera] stream error", e); - if (crosshair) { - crosshair.style.opacity = '0.3'; - } - }; - } - btn?.addEventListener("click", toggle); - // Проверка доступности камеры - async function checkStreamAvailability() { - try { - const response = await fetch('/api/stream'); - const data = await response.json(); - if (data.available && data.cam) { - console.log("[camera] stream available:", data.source); - // Автозапуск если камера доступна - if (!isOpen) { - open(); - } - } - else { - console.log("[camera] stream not available"); - if (crosshair) { - crosshair.style.display = 'none'; - } - } - } - catch (error) { - console.log("[camera] check failed:", error); - } - } - // Мониторинг состояния потока - function startStreamMonitoring() { - stopStreamMonitoring(); - streamCheckInterval = window.setInterval(() => { - if (isOpen && camImage) { - if (!camImage.complete || camImage.naturalWidth === 0) { - if (crosshair) { - crosshair.style.opacity = '0.3'; - } - } - else { - if (crosshair) { - crosshair.style.opacity = '1'; - } - } - } - }, 2000); - } - function stopStreamMonitoring() { - if (streamCheckInterval) { - clearInterval(streamCheckInterval); - streamCheckInterval = null; - } - } -} -// Экспортируем для возможности ручного управления -export function showCrosshair(show = true) { - const crosshairElem = document.getElementById("crosshair"); - if (crosshairElem) { - crosshairElem.style.display = show ? "block" : "none"; - } -} -export function setCrosshairOpacity(opacity) { - const crosshairElem = document.getElementById("crosshair"); - if (crosshairElem) { - crosshairElem.style.opacity = String(opacity); - } -} diff --git a/cmd/server/web/dist/chart.js b/cmd/server/web/dist/chart.js deleted file mode 100644 index 05839bf..0000000 --- a/cmd/server/web/dist/chart.js +++ /dev/null @@ -1,114 +0,0 @@ -// chart.js -export function drawChart(canvas, history) { - if (!canvas || !history?.length) - return; - const rect = canvas.getBoundingClientRect(); - const displayHeight = rect.height || 200; - const displayWidth = rect.width || 800; - canvas.width = displayWidth; - canvas.height = displayHeight; - const ctx = canvas.getContext("2d"); - if (!ctx) - return; - const w = canvas.width; - const h = canvas.height; - const padLeft = 45; - const padRight = 15; - const padTop = 10; - const padBottom = 25; - const plotW = w - padLeft - padRight; - const plotH = h - padTop - padBottom; - // Фон - ctx.fillStyle = "#0a0f0a"; - ctx.fillRect(0, 0, w, h); - // Сетка - const maxVal = 63; - const gridLines = 4; - ctx.strokeStyle = "#0d2a0d"; - ctx.lineWidth = 0.5; - ctx.setLineDash([4, 4]); - for (let i = 0; i <= gridLines; i++) { - const val = (maxVal / gridLines) * i; - const y = padTop + plotH - (val / maxVal) * plotH; - ctx.beginPath(); - ctx.moveTo(padLeft, y); - ctx.lineTo(w - padRight, y); - ctx.stroke(); - ctx.fillStyle = "#00ff88"; - ctx.font = "11px monospace"; - ctx.textAlign = "right"; - ctx.fillText(Math.round(val).toString(), padLeft - 8, y + 4); - } - ctx.setLineDash([]); - // Подписи осей - ctx.fillStyle = "#00ff88"; - ctx.font = "bold 11px monospace"; - ctx.save(); - ctx.translate(12, padTop + plotH / 2); - ctx.rotate(-Math.PI / 2); - ctx.textAlign = "center"; - ctx.fillText("ЗНАЧЕНИЕ", 0, 0); - ctx.restore(); - ctx.textAlign = "center"; - ctx.fillText("БУФЕР ПРИНЯТЫХ ДАННЫХ", padLeft + plotW / 2, h - 4); - const hasSignal = history.some(v => v > 0); - if (!hasSignal) { - ctx.fillStyle = "#ff4444"; - ctx.font = "14px monospace"; - ctx.textAlign = "center"; - ctx.fillText("Ожидание сигнала...", w / 2, h / 2); - return; - } - let plotData = history; - if (history.length > plotW) { - const step = history.length / plotW; - plotData = []; - for (let i = 0; i < plotW; i++) { - const idx = Math.floor(i * step); - plotData.push(history[idx]); - } - } - ctx.strokeStyle = "#e6852d"; - ctx.lineWidth = 1.5; - ctx.shadowColor = "#e6852d"; - ctx.shadowBlur = 3; - ctx.beginPath(); - const stepX = plotW / Math.max(plotData.length - 1, 1); - for (let i = 0; i < plotData.length; i++) { - const x = padLeft + i * stepX; - const y = padTop + plotH - (plotData[i] / maxVal) * plotH; - if (i === 0) - ctx.moveTo(x, y); - else - ctx.lineTo(x, y); - } - ctx.stroke(); - ctx.shadowBlur = 0; - if (history.length > 0) { - const lastVal = history[history.length - 1]; - const lastX = padLeft + (plotData.length - 1) * stepX; - const lastY = padTop + plotH - (lastVal / maxVal) * plotH; - ctx.fillStyle = "#c46b1f"; - ctx.beginPath(); - ctx.arc(lastX, lastY, 4, 0, Math.PI * 2); - ctx.fill(); - const labelText = `${lastVal}`; - const labelWidth = ctx.measureText(labelText).width; - ctx.fillStyle = "rgba(0,0,0,0.8)"; - ctx.fillRect(lastX - labelWidth / 2 - 4, lastY - 22, labelWidth + 8, 18); - ctx.fillStyle = "#00ff88"; - ctx.font = "bold 12px monospace"; - ctx.textAlign = "center"; - ctx.fillText(labelText, lastX, lastY - 8); - const peak = Math.max(...history); - const min = Math.min(...history); - ctx.fillStyle = "rgba(0,0,0,0.7)"; - ctx.fillRect(w - 120, padTop, 110, 42); - ctx.fillStyle = "#00ff88"; - ctx.font = "10px monospace"; - ctx.textAlign = "left"; - ctx.fillText(`Пик: ${peak}`, w - 110, padTop + 14); - ctx.fillText(`Мин: ${min}`, w - 110, padTop + 28); - ctx.fillText(`Точек: ${history.length}`, w - 110, padTop + 42); - } -} diff --git a/cmd/server/web/dist/data.js b/cmd/server/web/dist/data.js deleted file mode 100644 index c8b9144..0000000 --- a/cmd/server/web/dist/data.js +++ /dev/null @@ -1,12 +0,0 @@ -export async function fetchHealth() { - const r = await fetch("/api/health"); - if (!r.ok) - throw new Error(`HTTP ${r.status}`); - return r.json(); -} -export async function fetchHistory() { - const r = await fetch("/api/history"); - if (!r.ok) - throw new Error(`HTTP ${r.status}`); - return r.json(); -} diff --git a/cmd/server/web/dist/layout.js b/cmd/server/web/dist/layout.js deleted file mode 100644 index b78b95a..0000000 --- a/cmd/server/web/dist/layout.js +++ /dev/null @@ -1,79 +0,0 @@ -// layout.ts -let leftPanel = null; -let rightPanel = null; -let dragBar = null; -let isDragging = false; -let pendingX = null; -export function initResize() { - leftPanel = document.getElementById("leftPanel"); - rightPanel = document.getElementById("rightPanel"); - dragBar = document.getElementById("dragBar"); - if (!leftPanel || !rightPanel || !dragBar) - return; - const savedRatio = localStorage.getItem('layoutRatio'); - if (savedRatio) { - applyRatio(parseFloat(savedRatio)); - } - dragBar.addEventListener("mousedown", (e) => { - if (rightPanel.classList.contains("hidden")) - return; - isDragging = true; - document.body.style.cursor = "col-resize"; - e.preventDefault(); - }); - document.addEventListener("mouseup", () => { - if (!isDragging) - return; - isDragging = false; - document.body.style.cursor = "default"; - if (rightPanel && !rightPanel.classList.contains("hidden")) { - const total = window.innerWidth; - const leftWidth = leftPanel.getBoundingClientRect().width; - localStorage.setItem('layoutRatio', String(leftWidth / total)); - } - }); - document.addEventListener("mousemove", (e) => { - if (!isDragging) - return; - if (rightPanel && rightPanel.classList.contains("hidden")) - return; - pendingX = e.clientX; - requestAnimationFrame(() => { - if (pendingX === null) - return; - if (!leftPanel || !rightPanel || !dragBar) - return; - const total = window.innerWidth; - let leftWidth = pendingX; - const min = 300; - if (leftWidth < min) - leftWidth = min; - if (leftWidth > total - min) - leftWidth = total - min; - const rightWidth = total - leftWidth - dragBar.offsetWidth; - leftPanel.style.flex = `0 0 ${leftWidth}px`; - rightPanel.style.flex = `0 0 ${rightWidth}px`; - pendingX = null; - }); - }); -} -function applyRatio(ratio) { - const total = window.innerWidth; - const leftWidth = Math.floor(total * ratio); - const rightWidth = total - leftWidth; - if (leftPanel && rightPanel) { - leftPanel.style.flex = `0 0 ${leftWidth}px`; - rightPanel.style.flex = `0 0 ${rightWidth}px`; - } -} -export function updateResizerVisibility() { - if (!dragBar || !rightPanel || !leftPanel) - return; - if (rightPanel.classList.contains("hidden")) { - dragBar.classList.add("hidden"); - leftPanel.style.flex = "1"; - } - else { - dragBar.classList.remove("hidden"); - } -} diff --git a/cmd/server/web/dist/state.js b/cmd/server/web/dist/state.js deleted file mode 100644 index 90e1eec..0000000 --- a/cmd/server/web/dist/state.js +++ /dev/null @@ -1,33 +0,0 @@ -export const state = { - maxPoints: 1000, - signalHistory: [], - smoothValue: 0, - lastBufferSize: 0, -}; -export function smooth(target) { - state.smoothValue += 0.2 * (target - state.smoothValue); - return state.smoothValue; -} -export function addSignal(value) { - state.signalHistory.push(value); - if (state.signalHistory.length > state.maxPoints) { - state.signalHistory = state.signalHistory.slice(-state.maxPoints); - } -} -export function addSignals(values, bufferSize) { - if (!Array.isArray(values) || values.length === 0) - return; - const newBytes = bufferSize - state.lastBufferSize; - if (newBytes > 0 && newBytes <= values.length) { - const fresh = values.slice(-newBytes); - const decoded = fresh.map(b => b & 0x3F); - state.signalHistory.push(...decoded); - } - state.lastBufferSize = bufferSize; - if (state.signalHistory.length > state.maxPoints) { - state.signalHistory = state.signalHistory.slice(-state.maxPoints); - } -} -export function resetBufferTracking() { - state.lastBufferSize = 0; -} diff --git a/cmd/server/web/dist/ui.js b/cmd/server/web/dist/ui.js deleted file mode 100644 index 4203175..0000000 --- a/cmd/server/web/dist/ui.js +++ /dev/null @@ -1,86 +0,0 @@ -export const DOM = { - status: null, - meta: null, - latest: null, - lastByte: null, - history: null, - canvas: null, - bars: document.querySelectorAll(".sig-bar"), - barFill: null, - historyHeader: null, - historyBody: null, -}; -export function initDOM() { - DOM.status = document.getElementById("status"); - DOM.meta = document.getElementById("meta"); - DOM.latest = document.getElementById("latest"); - DOM.lastByte = document.getElementById("last-byte"); - DOM.history = document.getElementById("history"); - DOM.canvas = document.getElementById("signalChart"); - DOM.barFill = document.getElementById("bar1"); - DOM.historyHeader = document.getElementById("history-header"); - DOM.historyBody = document.getElementById("history-body"); - DOM.bars = document.querySelectorAll(".sig-bar"); -} -export function setStatus(v, isError = false) { - if (!DOM.status) - return; - DOM.status.textContent = v; - DOM.status.classList.remove("status-ok", "status-error", "status-lost"); - if (isError) { - if (v === "Нет связи") { - DOM.status.classList.add("status-lost"); - } - else { - DOM.status.classList.add("status-error"); - } - } - else { - DOM.status.classList.add("status-ok"); - } -} -export function setMeta(v) { - if (DOM.meta) - DOM.meta.textContent = v; -} -export function setBars(p, noConnection = false) { - const el = DOM.barFill; - if (!el) - return; - if (noConnection) { - el.style.width = "0%"; - el.classList.add("no-connection"); - } - else { - el.classList.remove("no-connection"); - el.style.width = `${Math.max(0, Math.min(100, p))}%`; - } -} -export function setSignal(level, noConnection = false) { - const bars = DOM.bars; - if (!bars || bars.length < 3) - return; - bars.forEach(bar => { - bar.classList.remove("active-1", "active-2", "active-3", "dim", "no-connection"); - if (noConnection) { - bar.classList.add("no-connection"); - } - else { - bar.classList.add("dim"); - } - }); - if (noConnection) - return; - 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"); - } -}