diff --git a/PROJECT_STATUS.md b/PROJECT_STATUS.md index a841e07..496f667 100644 --- a/PROJECT_STATUS.md +++ b/PROJECT_STATUS.md @@ -71,6 +71,8 @@ ventana de procesamiento en vez de bloquear un segundo por sensor. - Valores permitidos: 7, 30, 90, 365 o 0 para retencion indefinida. - La poda conserva los nombres actuales de archivos para no romper graficas ni exportaciones. +- Las graficas filtran visualmente el dia actual; los CSV mantienen todos los + registros disponibles dentro de la retencion configurada. ## Notificaciones diff --git a/README.md b/README.md index f99609d..7ab8ada 100644 --- a/README.md +++ b/README.md @@ -119,6 +119,10 @@ expone en `GET /api/alarms`. Se registran entradas a `WARNING`, `CRITICAL` y 90, 365 o 0 para conservar indefinidamente. El recolector poda filas antiguas de CSV sin cambiar los nombres que usa el dashboard. +Las graficas del dashboard muestran solo los datos del dia actual para evitar +saturacion visual. Los CSV conservan el periodo completo configurado por +retencion y siguen disponibles para exportacion. + ## Umbrales de alarma Los limites se guardan en `config/alarms.json`, se consultan con diff --git a/frontend/dashboard.js b/frontend/dashboard.js index 20f9bd4..0103e7e 100644 --- a/frontend/dashboard.js +++ b/frontend/dashboard.js @@ -1,6 +1,7 @@ const POLLING_INTERVAL_MS = 1000; const WARNING_MARGIN_RATIO = 0.1; const MIN_READING_MAX_AGE_MS = 15000; +const DAY_IN_MS = 24 * 60 * 60 * 1000; // Variables dinámicas para el control de históricos let currentHistoryInterval = 10000; @@ -718,6 +719,7 @@ function parseHistoricalCsv(csvText, valueKey) { return rows .map((line) => splitCsvLine(line)) .map((columns) => { + const rawTimestamp = columns[timestampIndex]; const rawValue = Number(columns[valueIndex]); if (!Number.isFinite(rawValue)) { @@ -725,13 +727,49 @@ function parseHistoricalCsv(csvText, valueKey) { } return { - label: formatTimestamp(columns[timestampIndex]), + timestampMs: parseTimestampMs(rawTimestamp), + label: formatTimestamp(rawTimestamp), value: rawValue }; }) .filter(Boolean); } +function parseTimestampMs(rawTimestamp) { + const numericTimestamp = Number(rawTimestamp); + + if (Number.isFinite(numericTimestamp)) { + if (numericTimestamp > 1000000000000) { + return numericTimestamp; + } + + if (numericTimestamp > 1000000000) { + return numericTimestamp * 1000; + } + } + + const parsedDate = new Date(rawTimestamp); + + if (!Number.isNaN(parsedDate.getTime())) { + return parsedDate.getTime(); + } + + return Number.NaN; +} + +function filterPointsForCurrentDay(points) { + const startOfToday = new Date(); + startOfToday.setHours(0, 0, 0, 0); + const startMs = startOfToday.getTime(); + const endMs = startMs + DAY_IN_MS; + + return points.filter((point) => + Number.isFinite(point.timestampMs) && + point.timestampMs >= startMs && + point.timestampMs < endMs + ); +} + function splitCsvLine(line) { return line .split(",") @@ -755,22 +793,10 @@ function formatTimestamp(rawTimestamp) { return ""; } - const numericTimestamp = Number(rawTimestamp); - - if (Number.isFinite(numericTimestamp)) { - if (numericTimestamp > 1000000000000) { - return new Date(numericTimestamp).toLocaleTimeString(); - } - - if (numericTimestamp > 1000000000) { - return new Date(numericTimestamp * 1000).toLocaleTimeString(); - } - } - - const parsedDate = new Date(rawTimestamp); + const parsedMs = parseTimestampMs(rawTimestamp); - if (!Number.isNaN(parsedDate.getTime())) { - return parsedDate.toLocaleTimeString(); + if (Number.isFinite(parsedMs)) { + return new Date(parsedMs).toLocaleTimeString(); } return rawTimestamp; @@ -893,7 +919,7 @@ async function updateHistoricalTrends() { historicalCharts.map(async (chartConfig) => ({ chartConfig, points: currentEnabledSensorIds.has(chartConfig.sensorId) - ? await readHistoricalData(chartConfig) + ? filterPointsForCurrentDay(await readHistoricalData(chartConfig)) : [] })) ); diff --git a/frontend/index.html b/frontend/index.html index b6f06ee..8e47bd0 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -145,7 +145,7 @@