import type { PingProbe, Server } from "@/lib/data" /** Длина ряда на графике дашборда (ось времени). */ export const DASH_LATENCY_POINTS = 60 /** Растягивает/сэмплирует ряд до `targetLen` (линейная интерполяция по индексам). */ export function padSeriesToLength(values: number[], targetLen: number): number[] { if (targetLen <= 0) return [] if (values.length === 0) return Array(targetLen).fill(0) if (values.length === targetLen) return [...values] if (values.length > targetLen) return values.slice(values.length - targetLen) if (values.length === 1) return Array(targetLen).fill(values[0] ?? 0) const out: number[] = [] for (let i = 0; i < targetLen; i++) { const t = (i / (targetLen - 1)) * (values.length - 1) const j = Math.floor(t) const f = t - j const a = values[j] ?? 0 const b = values[Math.min(j + 1, values.length - 1)] ?? a out.push(Math.round(a + f * (b - a))) } return out } export interface DashboardLatencyChartInput { series: Record labels: Record } /** * Средний RTT по всем включённым пробам с одного источника (srcServerId). * Ключ серии — стабильный id вида `src-`; подпись — площадка или имя сервера. * Ограничение числа линий — чтобы график оставался читаемым. */ export function buildLatencySeriesByProbeSource( probes: PingProbe[], servers: Server[], opts?: { maxServers?: number; points?: number }, ): DashboardLatencyChartInput { const maxServers = opts?.maxServers ?? 8 const points = opts?.points ?? DASH_LATENCY_POINTS const bySrv = new Map() for (const p of probes) { if (!p.enabled || !p.series?.length) continue const sid = p.srcServerId if (!bySrv.has(sid)) bySrv.set(sid, []) bySrv.get(sid)!.push(p) } const sortedIds = [...bySrv.keys()].sort((a, b) => { const na = servers.find((s) => s.id === a)?.name ?? a const nb = servers.find((s) => s.id === b)?.name ?? b return na.localeCompare(nb, "ru") }).slice(0, maxServers) const series: Record = {} const labels: Record = {} for (const sid of sortedIds) { const plist = bySrv.get(sid)! const innerLen = plist.reduce((m, p) => Math.max(m, p.series.length), 0) if (innerLen === 0) continue const averaged = Array.from({ length: innerLen }, (_, i) => { const vals = plist .map((p) => p.series[i]) .filter((v): v is number => typeof v === "number" && Number.isFinite(v)) if (!vals.length) return 0 return Math.round(vals.reduce((x, y) => x + y, 0) / vals.length) }) const key = `src-${sid}` series[key] = padSeriesToLength(averaged, points) const srv = servers.find((s) => s.id === sid) const site = srv?.site?.trim() labels[key] = site && site !== "—" ? site.slice(0, 16) : (srv?.name ?? `#${sid}`).slice(0, 16) } return { series, labels } }