From 45686b0663d5810059e36f540177b56619ea7253 Mon Sep 17 00:00:00 2001 From: Miguel Date: Mon, 21 Jul 2025 14:37:02 +0200 Subject: [PATCH] =?UTF-8?q?Actualizaci=C3=B3n=20de=20los=20archivos=20de?= =?UTF-8?q?=20configuraci=C3=B3n=20y=20estado=20del=20sistema=20para=20ref?= =?UTF-8?q?lejar=20los=20cambios=20recientes=20en=20las=20sesiones=20de=20?= =?UTF-8?q?plot.=20Se=20a=C3=B1adieron=20nuevos=20eventos=20en=20applicati?= =?UTF-8?q?on=5Fevents.json=20para=20la=20creaci=C3=B3n=20y=20eliminaci?= =?UTF-8?q?=C3=B3n=20de=20sesiones=20de=20plot,=20as=C3=AD=20como=20mejora?= =?UTF-8?q?s=20en=20el=20auto-scaling=20de=20los=20gr=C3=A1ficos.=20Se=20i?= =?UTF-8?q?mplementaron=20nuevas=20funcionalidades=20en=20PlotManager=20pa?= =?UTF-8?q?ra=20la=20gesti=C3=B3n=20de=20estado=20de=20las=20sesiones=20y?= =?UTF-8?q?=20se=20mejor=C3=B3=20la=20inicializaci=C3=B3n=20del=20sistema?= =?UTF-8?q?=20en=20main.js.=20Adem=C3=A1s,=20se=20ajustaron=20las=20fechas?= =?UTF-8?q?=20de=20=C3=BAltima=20actualizaci=C3=B3n=20en=20los=20archivos?= =?UTF-8?q?=20correspondientes.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- application_events.json | 139 +++++++++++++++++++++++++++++++++++++++- core/plot_manager.py | 40 ++++++++++-- plc_datasets.json | 2 +- plot_sessions.json | 25 ++++++-- static/js/main.js | 6 ++ static/js/plotting.js | 120 ++++++++++++++++++++++++++++++---- system_state.json | 2 +- 7 files changed, 304 insertions(+), 30 deletions(-) diff --git a/application_events.json b/application_events.json index 354ed45..9cffc2a 100644 --- a/application_events.json +++ b/application_events.json @@ -3930,8 +3930,143 @@ "DAR" ] } + }, + { + "timestamp": "2025-07-21T12:30:37.610610", + "level": "info", + "event_type": "plot_session_removed", + "message": "Plot session 'Condix' removed", + "details": { + "session_id": "plot_6" + } + }, + { + "timestamp": "2025-07-21T12:31:11.408598", + "level": "info", + "event_type": "plot_session_created", + "message": "Plot session 'Conducibilita Prodotto' created", + "details": { + "session_id": "plot_7", + "variables": [ + "CTS306_PV" + ], + "time_window": 10, + "trigger_variable": null + } + }, + { + "timestamp": "2025-07-21T12:31:55.488256", + "level": "info", + "event_type": "plot_session_removed", + "message": "Plot session 'Conducibilita Prodotto' removed", + "details": { + "session_id": "plot_7" + } + }, + { + "timestamp": "2025-07-21T12:31:55.815522", + "level": "info", + "event_type": "plot_session_created", + "message": "Plot session 'Conducibilita Prodotto' created", + "details": { + "session_id": "plot_8", + "variables": [ + "UR29_Brix" + ], + "time_window": 10, + "trigger_variable": null + } + }, + { + "timestamp": "2025-07-21T14:31:42.617158", + "level": "info", + "event_type": "application_started", + "message": "Application initialization completed successfully", + "details": {} + }, + { + "timestamp": "2025-07-21T14:31:42.642798", + "level": "info", + "event_type": "dataset_activated", + "message": "Dataset activated: DAR", + "details": { + "dataset_id": "dar", + "variables_count": 6, + "streaming_count": 4, + "prefix": "dar" + } + }, + { + "timestamp": "2025-07-21T14:31:42.648366", + "level": "info", + "event_type": "csv_recording_started", + "message": "CSV recording started: 1 datasets activated", + "details": { + "activated_datasets": 1, + "total_datasets": 2 + } + }, + { + "timestamp": "2025-07-21T14:32:46.681050", + "level": "info", + "event_type": "plot_session_removed", + "message": "Plot session 'Conducibilita Prodotto' removed", + "details": { + "session_id": "plot_8" + } + }, + { + "timestamp": "2025-07-21T14:32:47.005241", + "level": "info", + "event_type": "plot_session_created", + "message": "Plot session 'Conducibilita Prodotto' created", + "details": { + "session_id": "plot_9", + "variables": [ + "UR29_Brix" + ], + "time_window": 10, + "trigger_variable": null + } + }, + { + "timestamp": "2025-07-21T14:33:22.614591", + "level": "info", + "event_type": "plot_session_created", + "message": "Plot session 'Brix' created", + "details": { + "session_id": "plot_10", + "variables": [ + "UR62_Brix" + ], + "time_window": 60, + "trigger_variable": null + } + }, + { + "timestamp": "2025-07-21T14:34:57.247233", + "level": "info", + "event_type": "plot_session_removed", + "message": "Plot session 'Brix' removed", + "details": { + "session_id": "plot_10" + } + }, + { + "timestamp": "2025-07-21T14:35:23.267756", + "level": "info", + "event_type": "plot_session_created", + "message": "Plot session 'Brix' created", + "details": { + "session_id": "plot_11", + "variables": [ + "UR29_Brix" + ], + "time_window": 60, + "trigger_variable": null + } } ], - "last_updated": "2025-07-21T12:29:22.723751", - "total_entries": 372 + "last_updated": "2025-07-21T14:35:23.267756", + "total_entries": 384 } \ No newline at end of file diff --git a/core/plot_manager.py b/core/plot_manager.py index 3a0c58d..abb7aed 100644 --- a/core/plot_manager.py +++ b/core/plot_manager.py @@ -141,7 +141,7 @@ class PlotSession: } ) - # Calcular Y min/max automático si no están definidos + # Calcular Y min/max automático si no están definidos - MEJORADO y_min = self.y_min y_max = self.y_max @@ -149,14 +149,42 @@ class PlotSession: all_values = [] for var_data in self.data.values(): all_values.extend( - [point[1] for point in var_data if point[1] is not None] + [ + point[1] + for point in var_data + if point[1] is not None and isinstance(point[1], (int, float)) + ] ) if all_values: + min_val = min(all_values) + max_val = max(all_values) + + # Calcular rango para auto-scaling mejorado + range_val = max_val - min_val + + # Si el rango es muy pequeño (valores muy similares), usar un rango mínimo + if range_val < 0.001: # Valores prácticamente iguales + center = (min_val + max_val) / 2 + # Usar un rango mínimo basado en el valor o 1.0 si es 0 + min_range = max(abs(center) * 0.1, 1.0) + if y_min is None: + y_min = center - min_range + if y_max is None: + y_max = center + min_range + else: + # Auto-scaling normal con margen del 10% + margin = range_val * 0.1 + if y_min is None: + y_min = min_val - margin + if y_max is None: + y_max = max_val + margin + else: + # No hay datos - usar rango por defecto if y_min is None: - y_min = min(all_values) - (max(all_values) - min(all_values)) * 0.1 + y_min = 0 if y_max is None: - y_max = max(all_values) + (max(all_values) - min(all_values)) * 0.1 + y_max = 100 return { "session_id": self.session_id, @@ -260,10 +288,10 @@ class PlotManager: ) del self.sessions[session_id] - + # Guardar automáticamente después de eliminar self.save_plots() - + return True return False diff --git a/plc_datasets.json b/plc_datasets.json index 7ffcffa..f344d93 100644 --- a/plc_datasets.json +++ b/plc_datasets.json @@ -70,5 +70,5 @@ ], "current_dataset_id": "dar", "version": "1.0", - "last_update": "2025-07-21T12:29:22.704454" + "last_update": "2025-07-21T14:31:42.640749" } \ No newline at end of file diff --git a/plot_sessions.json b/plot_sessions.json index dafc043..b6fb05d 100644 --- a/plot_sessions.json +++ b/plot_sessions.json @@ -1,9 +1,22 @@ { "plots": { - "plot_6": { - "name": "Condix", + "plot_9": { + "name": "Conducibilita Prodotto", "variables": [ - "UR62_Brix" + "UR29_Brix" + ], + "time_window": 10, + "y_min": null, + "y_max": null, + "trigger_variable": null, + "trigger_enabled": false, + "trigger_on_true": true, + "session_id": "plot_9" + }, + "plot_11": { + "name": "Brix", + "variables": [ + "UR29_Brix" ], "time_window": 60, "y_min": null, @@ -11,10 +24,10 @@ "trigger_variable": null, "trigger_enabled": false, "trigger_on_true": true, - "session_id": "plot_6" + "session_id": "plot_11" } }, - "session_counter": 7, - "last_saved": "2025-07-21T11:26:04.418899", + "session_counter": 12, + "last_saved": "2025-07-21T14:35:23.266756", "version": "1.0" } \ No newline at end of file diff --git a/static/js/main.js b/static/js/main.js index 9c02dce..8f23ce6 100644 --- a/static/js/main.js +++ b/static/js/main.js @@ -24,6 +24,12 @@ document.addEventListener('DOMContentLoaded', function () { initCsvListeners(); initEventListeners(); + // 🔑 NUEVO: Inicializar plotManager si existe + if (typeof PlotManager !== 'undefined' && !window.plotManager) { + window.plotManager = new PlotManager(); + console.log('📈 PlotManager initialized from main.js'); + } + // Configurar actualizaciones periódicas como respaldo setInterval(updateStatus, 30000); // Cada 30 segundos como respaldo setInterval(refreshEventLog, 10000); // Cada 10 segundos diff --git a/static/js/plotting.js b/static/js/plotting.js index 31c5a39..40be0b0 100644 --- a/static/js/plotting.js +++ b/static/js/plotting.js @@ -7,6 +7,7 @@ class PlotManager { constructor() { this.sessions = new Map(); // session_id -> Chart instance this.updateInterval = null; + this.statusUpdateInterval = null; // 🔑 NUEVO: Para updates de status this.isInitialized = false; // Colores para las variables @@ -48,8 +49,32 @@ class PlotManager { if (data.sessions) { for (const session of data.sessions) { - // Cargar todas las sesiones (activas e inactivas) y crear tabs - await this.createPlotSessionTab(session.session_id, session); + // 🔑 NUEVO: Obtener configuración completa para cada sesión + try { + const configResponse = await fetch(`/api/plots/${session.session_id}/config`); + const configData = await configResponse.json(); + + let sessionInfo = session; + + // Si tenemos la configuración completa, usar esa información + if (configData.success && configData.config) { + sessionInfo = { + ...session, + is_active: configData.config.is_active, + is_paused: configData.config.is_paused, + variables_count: configData.config.variables ? configData.config.variables.length : session.variables_count, + trigger_enabled: configData.config.trigger_enabled, + trigger_variable: configData.config.trigger_variable, + trigger_on_true: configData.config.trigger_on_true + }; + } + + await this.createPlotSessionTab(session.session_id, sessionInfo); + } catch (configError) { + console.error(`Error loading config for session ${session.session_id}:`, configError); + // Usar información básica si falla la carga de configuración + await this.createPlotSessionTab(session.session_id, session); + } } } } catch (error) { @@ -62,6 +87,11 @@ class PlotManager { this.updateInterval = setInterval(() => { this.updateAllSessions(); }, 500); + + // 🔑 NUEVO: Actualizar status cada 2 segundos para mantener sincronización + this.statusUpdateInterval = setInterval(() => { + this.updateAllSessionsStatus(); + }, 2000); } stopAutoUpdate() { @@ -69,6 +99,12 @@ class PlotManager { clearInterval(this.updateInterval); this.updateInterval = null; } + + // 🔑 NUEVO: Detener también el update de status + if (this.statusUpdateInterval) { + clearInterval(this.statusUpdateInterval); + this.statusUpdateInterval = null; + } } async updateAllSessions() { @@ -79,6 +115,15 @@ class PlotManager { } } + // 🔑 NUEVO: Actualizar status de todas las sesiones + async updateAllSessionsStatus() { + const activeSessions = Array.from(this.sessions.keys()); + + for (const sessionId of activeSessions) { + await this.updateSessionStatus(sessionId); + } + } + async updateSessionData(sessionId) { try { const response = await fetch(`/api/plots/${sessionId}/data`); @@ -200,10 +245,14 @@ class PlotManager { // Actualizar datasets chart.data.datasets = plotData.datasets; - // Actualizar escalas Y si están definidas - if (plotData.y_min !== undefined || plotData.y_max !== undefined) { + // Actualizar escalas Y - mejore el auto-scaling + if (plotData.y_min !== undefined && plotData.y_max !== undefined) { chart.options.scales.y.min = plotData.y_min; chart.options.scales.y.max = plotData.y_max; + } else { + // Auto-scaling mejorado cuando no hay límites definidos + chart.options.scales.y.min = undefined; + chart.options.scales.y.max = undefined; } // Actualizar contador de puntos @@ -212,6 +261,18 @@ class PlotManager { pointsElement.textContent = plotData.data_points_count || 0; } + // 🔑 NUEVO: Actualizar status visual del plot + if (plotData.is_active !== undefined || plotData.is_paused !== undefined) { + this.updatePlotStats(sessionId, { + variables_count: plotData.datasets ? plotData.datasets.length : 0, + is_active: plotData.is_active, + is_paused: plotData.is_paused, + trigger_enabled: plotData.trigger_enabled, + trigger_variable: plotData.trigger_variable, + trigger_on_true: plotData.trigger_on_true + }); + } + // Actualizar chart chart.update('none'); } @@ -229,13 +290,11 @@ class PlotManager { const result = await response.json(); if (result.success) { - // Actualizar UI según la acción - if (action === 'stop') { - this.removeChart(sessionId); - } else { - // Actualizar datos inmediatamente - await this.updateSessionData(sessionId); - } + // 🔑 NUEVO: Actualizar status inmediatamente después de la acción + await this.updateSessionStatus(sessionId); + + // Actualizar datos inmediatamente + await this.updateSessionData(sessionId); showNotification(result.message, 'success'); } else { @@ -247,6 +306,20 @@ class PlotManager { } } + // 🔑 NUEVO: Método para actualizar solo el status del plot + async updateSessionStatus(sessionId) { + try { + const response = await fetch(`/api/plots/${sessionId}/config`); + const data = await response.json(); + + if (data.success && data.config) { + this.updatePlotStats(sessionId, data.config); + } + } catch (error) { + console.error(`Error updating session status ${sessionId}:`, error); + } + } + async removePlot(sessionId) { try { const response = await fetch(`/api/plots/${sessionId}`, { @@ -377,17 +450,29 @@ class PlotManager { const result = await response.json(); if (result.success) { - // Crear tab dinámico y chart - const sessionConfig = { + // 🔑 NUEVO: Obtener configuración real del backend en lugar de hardcodear + const configResponse = await fetch(`/api/plots/${result.session_id}/config`); + const configData = await configResponse.json(); + + let sessionConfig = { name: config.name, variables_count: config.variables.length, trigger_enabled: config.trigger_enabled, trigger_variable: config.trigger_variable, trigger_on_true: config.trigger_on_true, - is_active: false, // Por defecto stopped + is_active: false, // Por defecto stopped hasta que se obtenga del backend is_paused: false }; + // Si tenemos la configuración real del backend, usarla + if (configData.success && configData.config) { + sessionConfig = { + ...sessionConfig, + is_active: configData.config.is_active, + is_paused: configData.config.is_paused + }; + } + await this.createPlotSessionTab(result.session_id, sessionConfig); // Cambiar al sub-tab del nuevo plot @@ -395,6 +480,13 @@ class PlotManager { tabManager.switchSubTab(`plot-${result.session_id}`); } + // 🔑 NUEVO: Auto-start el plot para mejor experiencia de usuario + // Si el plot está stopped (por defecto), hacer start automáticamente + if (!sessionConfig.is_active) { + console.log(`Auto-starting plot session: ${result.session_id}`); + await this.controlPlot(result.session_id, 'start'); + } + showNotification(result.message, 'success'); return result.session_id; } else { diff --git a/system_state.json b/system_state.json index 7486606..47003de 100644 --- a/system_state.json +++ b/system_state.json @@ -7,5 +7,5 @@ ] }, "auto_recovery_enabled": true, - "last_update": "2025-07-21T12:29:22.714509" + "last_update": "2025-07-21T14:31:42.656469" } \ No newline at end of file