diff --git a/cmd/server/web/css/style.css b/cmd/server/web/css/style.css index 4662c86..2c2f5ec 100644 --- a/cmd/server/web/css/style.css +++ b/cmd/server/web/css/style.css @@ -145,7 +145,7 @@ h1 { flex: 1; border-radius: 2px; min-width: 5px; - border: 1.5px solid #00ff88; + border: 1.5px solid #e6852d; background-color: transparent; transition: background-color 0.3s, border-color 0.3s; } @@ -164,8 +164,8 @@ h1 { .sig-bar.active-1, .sig-bar.active-2, .sig-bar.active-3 { - background-color: #00ff88; - border-color: #00ff88; + background-color: #e6852d; + border-color: #c46b1f; } /* Нет связи - сигнальные бары */ @@ -186,7 +186,7 @@ h1 { #signalChart { width: 100%; background: #050805; - border: 1px solid #00ff88; + border: 1px solid #e6852d; border-radius: 8px; } diff --git a/cmd/server/web/dashboard.html b/cmd/server/web/dashboard.html index eb9db6b..1cc9c5e 100644 --- a/cmd/server/web/dashboard.html +++ b/cmd/server/web/dashboard.html @@ -60,7 +60,7 @@
-

ГРАФИК СИГНАЛА (последние 1000)

+

ГРАФИК СИГНАЛА

diff --git a/cmd/server/web/js/chart.js b/cmd/server/web/js/chart.js index 47b7149..c803a7b 100644 --- a/cmd/server/web/js/chart.js +++ b/cmd/server/web/js/chart.js @@ -3,7 +3,7 @@ export function drawChart(canvas, history) { if (!canvas || !history?.length) return; const rect = canvas.getBoundingClientRect(); - const displayHeight = rect.height || 120; + const displayHeight = rect.height || 200; // увеличил для подписей const displayWidth = rect.width || 800; canvas.width = displayWidth; @@ -13,22 +13,145 @@ export function drawChart(canvas, history) { const w = canvas.width; const h = canvas.height; - ctx.clearRect(0, 0, w, h); + // ===== ОТСТУПЫ ДЛЯ ПОДПИСЕЙ ===== + const padLeft = 50; // место под значения Y слева + const padRight = 20; // отступ справа + const padTop = 15; // отступ сверху + const padBottom = 30; // отступ снизу для подписей X - const stepX = w / history.length; - const scaleY = h / 63; + const plotW = w - padLeft - padRight; + const plotH = h - padTop - padBottom; - ctx.strokeStyle = "#00ff88"; - ctx.lineWidth = 2; + // ===== ФОН ГРАФИКА ===== + ctx.fillStyle = "#0a0f0a"; + ctx.fillRect(0, 0, w, h); + + // ===== СЕТКА ===== + const maxVal = 63; + const gridLines = 4; // 4 линии + 0 = 5 уровней: 0, 16, 32, 48, 64 + + 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), padLeft - 8, y + 4); + } + + // Вертикальные линии (временные метки) + const timeMarks = 5; + ctx.setLineDash([4, 4]); + for (let i = 0; i <= timeMarks; i++) { + const x = padLeft + (plotW / timeMarks) * i; + ctx.beginPath(); + ctx.moveTo(x, padTop); + ctx.lineTo(x, padTop + plotH); + ctx.stroke(); + + // Подпись снизу + const pct = Math.round((i / timeMarks) * 100); + ctx.fillStyle = "#008844"; + ctx.font = "9px monospace"; + ctx.textAlign = "center"; + ctx.fillText(`${100 - pct}%`, x, padTop + plotH + 16); + } + + ctx.setLineDash([]); // сброс пунктира + + // ===== ПОДПИСИ ОСЕЙ ===== + ctx.fillStyle = "#00ff88"; + ctx.font = "bold 12px monospace"; + + // Y + ctx.save(); + ctx.translate(12, padTop + plotH / 2); + ctx.rotate(-Math.PI / 2); + ctx.textAlign = "center"; + ctx.fillText("ЗНАЧЕНИЕ (0–63)", 0, 0); + ctx.restore(); + + // X + ctx.textAlign = "center"; + ctx.fillText("ВРЕМЯ ← новые", w / 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; + } + + // Линия графика + ctx.strokeStyle = "#e6852d"; + ctx.lineWidth = 1.5; + ctx.shadowColor = "#e6852d"; + ctx.shadowBlur = 3; + ctx.beginPath(); + const stepX = plotW / Math.max(history.length - 1, 1); + for (let i = 0; i < history.length; i++) { - const x = i * stepX; - const y = h - history[i] * scaleY; + const x = padLeft + i * stepX; + const y = padTop + plotH - (history[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 lastIdx = history.length - 1; + const lastX = padLeft + lastIdx * stepX; + const lastY = padTop + plotH - (history[lastIdx] / maxVal) * plotH; + const lastVal = history[lastIdx]; + + // Кружок на последней точке + ctx.fillStyle = "#c46b1f"; + ctx.beginPath(); + ctx.arc(lastX, lastY, 4, 0, Math.PI * 2); + ctx.fill(); + + // Подпись значения над точкой + ctx.fillStyle = "#00ff88"; + ctx.font = "bold 12px monospace"; + ctx.textAlign = "left"; + ctx.fillText(`${lastVal}`, lastX + 8, lastY - 8); + + // Карточка статистики в правом верхнем углу + const avg = Math.round(history.reduce((a, b) => a + b, 0) / history.length); + const peak = Math.max(...history); + const min = Math.min(...history); + + ctx.fillStyle = "rgba(0,0,0,0.7)"; + ctx.fillRect(w - 140, padTop, 130, 55); + + ctx.fillStyle = "#00ff88"; + ctx.font = "10px monospace"; + ctx.textAlign = "left"; + ctx.fillText(`Пик: ${peak}`, w - 130, padTop + 14); + ctx.fillText(`Сред: ${avg}`, w - 130, padTop + 28); + ctx.fillText(`Мин: ${min}`, w - 130, padTop + 42); + ctx.fillText(`Всего: ${history.length}`, w - 130, padTop + 56); + } } \ No newline at end of file