diff --git a/application_events.json b/application_events.json index 430b1ec..5226adf 100644 --- a/application_events.json +++ b/application_events.json @@ -7066,8 +7066,778 @@ "resumed_datasets": 1, "total_attempted": 1 } + }, + { + "timestamp": "2025-08-08T18:32:36.930843", + "level": "info", + "event_type": "datasets_resumed_after_reconnection", + "message": "Automatically resumed streaming for 1 datasets after PLC reconnection", + "details": { + "resumed_datasets": 1, + "total_attempted": 1 + } + }, + { + "timestamp": "2025-08-08T18:33:41.856796", + "level": "info", + "event_type": "datasets_resumed_after_reconnection", + "message": "Automatically resumed streaming for 1 datasets after PLC reconnection", + "details": { + "resumed_datasets": 1, + "total_attempted": 1 + } + }, + { + "timestamp": "2025-08-08T18:35:08.610009", + "level": "info", + "event_type": "datasets_resumed_after_reconnection", + "message": "Automatically resumed streaming for 1 datasets after PLC reconnection", + "details": { + "resumed_datasets": 1, + "total_attempted": 1 + } + }, + { + "timestamp": "2025-08-08T18:35:47.523446", + "level": "info", + "event_type": "datasets_resumed_after_reconnection", + "message": "Automatically resumed streaming for 1 datasets after PLC reconnection", + "details": { + "resumed_datasets": 1, + "total_attempted": 1 + } + }, + { + "timestamp": "2025-08-08T18:37:16.496562", + "level": "info", + "event_type": "datasets_resumed_after_reconnection", + "message": "Automatically resumed streaming for 1 datasets after PLC reconnection", + "details": { + "resumed_datasets": 1, + "total_attempted": 1 + } + }, + { + "timestamp": "2025-08-08T18:37:49.163502", + "level": "info", + "event_type": "datasets_resumed_after_reconnection", + "message": "Automatically resumed streaming for 1 datasets after PLC reconnection", + "details": { + "resumed_datasets": 1, + "total_attempted": 1 + } + }, + { + "timestamp": "2025-08-08T18:38:14.360918", + "level": "info", + "event_type": "datasets_resumed_after_reconnection", + "message": "Automatically resumed streaming for 1 datasets after PLC reconnection", + "details": { + "resumed_datasets": 1, + "total_attempted": 1 + } + }, + { + "timestamp": "2025-08-08T18:38:34.474496", + "level": "info", + "event_type": "datasets_resumed_after_reconnection", + "message": "Automatically resumed streaming for 1 datasets after PLC reconnection", + "details": { + "resumed_datasets": 1, + "total_attempted": 1 + } + }, + { + "timestamp": "2025-08-08T18:38:43.051794", + "level": "info", + "event_type": "datasets_resumed_after_reconnection", + "message": "Automatically resumed streaming for 1 datasets after PLC reconnection", + "details": { + "resumed_datasets": 1, + "total_attempted": 1 + } + }, + { + "timestamp": "2025-08-08T18:39:45.490511", + "level": "info", + "event_type": "datasets_resumed_after_reconnection", + "message": "Automatically resumed streaming for 1 datasets after PLC reconnection", + "details": { + "resumed_datasets": 1, + "total_attempted": 1 + } + }, + { + "timestamp": "2025-08-08T18:40:29.253027", + "level": "info", + "event_type": "datasets_resumed_after_reconnection", + "message": "Automatically resumed streaming for 1 datasets after PLC reconnection", + "details": { + "resumed_datasets": 1, + "total_attempted": 1 + } + }, + { + "timestamp": "2025-08-08T18:41:55.120326", + "level": "info", + "event_type": "datasets_resumed_after_reconnection", + "message": "Automatically resumed streaming for 1 datasets after PLC reconnection", + "details": { + "resumed_datasets": 1, + "total_attempted": 1 + } + }, + { + "timestamp": "2025-08-08T18:42:02.777019", + "level": "info", + "event_type": "application_started", + "message": "Application initialization completed successfully", + "details": {} + }, + { + "timestamp": "2025-08-08T18:42:08.483137", + "level": "info", + "event_type": "dataset_activated", + "message": "Dataset activated: DAR", + "details": { + "dataset_id": "DAR", + "variables_count": 2, + "streaming_count": 2, + "prefix": "gateway_phoenix" + } + }, + { + "timestamp": "2025-08-08T18:42:08.496321", + "level": "info", + "event_type": "csv_recording_started", + "message": "CSV recording started: 1 datasets activated", + "details": { + "activated_datasets": 1, + "total_datasets": 1 + } + }, + { + "timestamp": "2025-08-08T18:42:08.508057", + "level": "info", + "event_type": "plc_connection", + "message": "Successfully connected to PLC 10.1.33.11 and auto-started CSV recording for 1 datasets", + "details": { + "ip": "10.1.33.11", + "rack": 0, + "slot": 2, + "auto_started_recording": true, + "recording_datasets": 1, + "dataset_names": [ + "DAR" + ] + } + }, + { + "timestamp": "2025-08-08T18:42:30.275070", + "level": "info", + "event_type": "datasets_resumed_after_reconnection", + "message": "Automatically resumed streaming for 1 datasets after PLC reconnection", + "details": { + "resumed_datasets": 1, + "total_attempted": 1 + } + }, + { + "timestamp": "2025-08-08T18:42:57.450910", + "level": "info", + "event_type": "datasets_resumed_after_reconnection", + "message": "Automatically resumed streaming for 1 datasets after PLC reconnection", + "details": { + "resumed_datasets": 1, + "total_attempted": 1 + } + }, + { + "timestamp": "2025-08-08T18:43:28.802654", + "level": "info", + "event_type": "datasets_resumed_after_reconnection", + "message": "Automatically resumed streaming for 1 datasets after PLC reconnection", + "details": { + "resumed_datasets": 1, + "total_attempted": 1 + } + }, + { + "timestamp": "2025-08-08T18:43:57.553329", + "level": "info", + "event_type": "datasets_resumed_after_reconnection", + "message": "Automatically resumed streaming for 1 datasets after PLC reconnection", + "details": { + "resumed_datasets": 1, + "total_attempted": 1 + } + }, + { + "timestamp": "2025-08-08T18:44:32.188355", + "level": "info", + "event_type": "datasets_resumed_after_reconnection", + "message": "Automatically resumed streaming for 1 datasets after PLC reconnection", + "details": { + "resumed_datasets": 1, + "total_attempted": 1 + } + }, + { + "timestamp": "2025-08-08T18:46:03.356965", + "level": "info", + "event_type": "datasets_resumed_after_reconnection", + "message": "Automatically resumed streaming for 1 datasets after PLC reconnection", + "details": { + "resumed_datasets": 1, + "total_attempted": 1 + } + }, + { + "timestamp": "2025-08-08T18:47:01.606975", + "level": "info", + "event_type": "datasets_resumed_after_reconnection", + "message": "Automatically resumed streaming for 1 datasets after PLC reconnection", + "details": { + "resumed_datasets": 1, + "total_attempted": 1 + } + }, + { + "timestamp": "2025-08-08T18:47:07.809192", + "level": "info", + "event_type": "application_started", + "message": "Application initialization completed successfully", + "details": {} + }, + { + "timestamp": "2025-08-08T18:47:10.182054", + "level": "info", + "event_type": "dataset_activated", + "message": "Dataset activated: DAR", + "details": { + "dataset_id": "DAR", + "variables_count": 2, + "streaming_count": 2, + "prefix": "gateway_phoenix" + } + }, + { + "timestamp": "2025-08-08T18:47:10.195870", + "level": "info", + "event_type": "csv_recording_started", + "message": "CSV recording started: 1 datasets activated", + "details": { + "activated_datasets": 1, + "total_datasets": 1 + } + }, + { + "timestamp": "2025-08-08T18:47:10.203036", + "level": "info", + "event_type": "plc_connection", + "message": "Successfully connected to PLC 10.1.33.11 and auto-started CSV recording for 1 datasets", + "details": { + "ip": "10.1.33.11", + "rack": 0, + "slot": 2, + "auto_started_recording": true, + "recording_datasets": 1, + "dataset_names": [ + "DAR" + ] + } + }, + { + "timestamp": "2025-08-08T18:47:34.880687", + "level": "info", + "event_type": "datasets_resumed_after_reconnection", + "message": "Automatically resumed streaming for 1 datasets after PLC reconnection", + "details": { + "resumed_datasets": 1, + "total_attempted": 1 + } + }, + { + "timestamp": "2025-08-08T18:48:08.125088", + "level": "info", + "event_type": "datasets_resumed_after_reconnection", + "message": "Automatically resumed streaming for 1 datasets after PLC reconnection", + "details": { + "resumed_datasets": 1, + "total_attempted": 1 + } + }, + { + "timestamp": "2025-08-08T18:49:07.206807", + "level": "info", + "event_type": "datasets_resumed_after_reconnection", + "message": "Automatically resumed streaming for 1 datasets after PLC reconnection", + "details": { + "resumed_datasets": 1, + "total_attempted": 1 + } + }, + { + "timestamp": "2025-08-08T18:49:48.588727", + "level": "info", + "event_type": "datasets_resumed_after_reconnection", + "message": "Automatically resumed streaming for 1 datasets after PLC reconnection", + "details": { + "resumed_datasets": 1, + "total_attempted": 1 + } + }, + { + "timestamp": "2025-08-08T18:50:05.383732", + "level": "info", + "event_type": "datasets_resumed_after_reconnection", + "message": "Automatically resumed streaming for 1 datasets after PLC reconnection", + "details": { + "resumed_datasets": 1, + "total_attempted": 1 + } + }, + { + "timestamp": "2025-08-08T18:50:32.372196", + "level": "info", + "event_type": "datasets_resumed_after_reconnection", + "message": "Automatically resumed streaming for 1 datasets after PLC reconnection", + "details": { + "resumed_datasets": 1, + "total_attempted": 1 + } + }, + { + "timestamp": "2025-08-08T18:51:08.441123", + "level": "info", + "event_type": "datasets_resumed_after_reconnection", + "message": "Automatically resumed streaming for 1 datasets after PLC reconnection", + "details": { + "resumed_datasets": 1, + "total_attempted": 1 + } + }, + { + "timestamp": "2025-08-08T18:51:41.836369", + "level": "info", + "event_type": "datasets_resumed_after_reconnection", + "message": "Automatically resumed streaming for 1 datasets after PLC reconnection", + "details": { + "resumed_datasets": 1, + "total_attempted": 1 + } + }, + { + "timestamp": "2025-08-08T18:52:52.094018", + "level": "info", + "event_type": "datasets_resumed_after_reconnection", + "message": "Automatically resumed streaming for 1 datasets after PLC reconnection", + "details": { + "resumed_datasets": 1, + "total_attempted": 1 + } + }, + { + "timestamp": "2025-08-08T18:54:06.230888", + "level": "info", + "event_type": "datasets_resumed_after_reconnection", + "message": "Automatically resumed streaming for 1 datasets after PLC reconnection", + "details": { + "resumed_datasets": 1, + "total_attempted": 1 + } + }, + { + "timestamp": "2025-08-08T19:04:04.996795", + "level": "info", + "event_type": "application_started", + "message": "Application initialization completed successfully", + "details": {} + }, + { + "timestamp": "2025-08-08T19:04:12.344518", + "level": "error", + "event_type": "plc_connection_failed", + "message": "Failed to connect to PLC 10.1.33.11", + "details": { + "ip": "10.1.33.11", + "rack": 0, + "slot": 2, + "error": "b' TCP : Unreachable peer'" + } + }, + { + "timestamp": "2025-08-08T19:04:22.846057", + "level": "error", + "event_type": "plc_connection_failed", + "message": "Failed to connect to PLC 10.1.33.11", + "details": { + "ip": "10.1.33.11", + "rack": 0, + "slot": 2, + "error": "b' TCP : Unreachable peer'" + } + }, + { + "timestamp": "2025-08-08T19:04:52.850501", + "level": "error", + "event_type": "plc_connection_failed", + "message": "Failed to connect to PLC 10.1.33.11", + "details": { + "ip": "10.1.33.11", + "rack": 0, + "slot": 2, + "error": "b' TCP : Unreachable peer'" + } + }, + { + "timestamp": "2025-08-08T19:05:02.848724", + "level": "error", + "event_type": "plc_connection_failed", + "message": "Failed to connect to PLC 10.1.33.11", + "details": { + "ip": "10.1.33.11", + "rack": 0, + "slot": 2, + "error": "b' TCP : Unreachable peer'" + } + }, + { + "timestamp": "2025-08-08T19:05:07.349816", + "level": "error", + "event_type": "plc_connection_failed", + "message": "Failed to connect to PLC 10.1.33.11", + "details": { + "ip": "10.1.33.11", + "rack": 0, + "slot": 2, + "error": "b' TCP : Unreachable peer'" + } + }, + { + "timestamp": "2025-08-08T19:05:28.849056", + "level": "error", + "event_type": "plc_connection_failed", + "message": "Failed to connect to PLC 10.1.33.11", + "details": { + "ip": "10.1.33.11", + "rack": 0, + "slot": 2, + "error": "b' TCP : Unreachable peer'" + } + }, + { + "timestamp": "2025-08-08T19:05:33.354319", + "level": "error", + "event_type": "plc_connection_failed", + "message": "Failed to connect to PLC 10.1.33.11", + "details": { + "ip": "10.1.33.11", + "rack": 0, + "slot": 2, + "error": "b' TCP : Unreachable peer'" + } + }, + { + "timestamp": "2025-08-08T19:05:39.502498", + "level": "info", + "event_type": "dataset_activated", + "message": "Dataset activated: DAR", + "details": { + "dataset_id": "DAR", + "variables_count": 2, + "streaming_count": 2, + "prefix": "gateway_phoenix" + } + }, + { + "timestamp": "2025-08-08T19:05:39.521504", + "level": "info", + "event_type": "csv_recording_started", + "message": "CSV recording started: 1 datasets activated", + "details": { + "activated_datasets": 1, + "total_datasets": 1 + } + }, + { + "timestamp": "2025-08-08T19:05:39.540484", + "level": "info", + "event_type": "plc_connection", + "message": "Successfully connected to PLC 10.1.33.11 and auto-started CSV recording for 1 datasets", + "details": { + "ip": "10.1.33.11", + "rack": 0, + "slot": 2, + "auto_started_recording": true, + "recording_datasets": 1, + "dataset_names": [ + "DAR" + ] + } + }, + { + "timestamp": "2025-08-08T19:06:51.117994", + "level": "info", + "event_type": "datasets_resumed_after_reconnection", + "message": "Automatically resumed streaming for 1 datasets after PLC reconnection", + "details": { + "resumed_datasets": 1, + "total_attempted": 1 + } + }, + { + "timestamp": "2025-08-08T19:07:04.570720", + "level": "info", + "event_type": "datasets_resumed_after_reconnection", + "message": "Automatically resumed streaming for 1 datasets after PLC reconnection", + "details": { + "resumed_datasets": 1, + "total_attempted": 1 + } + }, + { + "timestamp": "2025-08-08T19:11:35.101004", + "level": "info", + "event_type": "datasets_resumed_after_reconnection", + "message": "Automatically resumed streaming for 1 datasets after PLC reconnection", + "details": { + "resumed_datasets": 1, + "total_attempted": 1 + } + }, + { + "timestamp": "2025-08-08T19:12:02.495213", + "level": "info", + "event_type": "datasets_resumed_after_reconnection", + "message": "Automatically resumed streaming for 1 datasets after PLC reconnection", + "details": { + "resumed_datasets": 1, + "total_attempted": 1 + } + }, + { + "timestamp": "2025-08-08T19:12:34.481986", + "level": "info", + "event_type": "datasets_resumed_after_reconnection", + "message": "Automatically resumed streaming for 1 datasets after PLC reconnection", + "details": { + "resumed_datasets": 1, + "total_attempted": 1 + } + }, + { + "timestamp": "2025-08-08T19:13:27.619818", + "level": "info", + "event_type": "datasets_resumed_after_reconnection", + "message": "Automatically resumed streaming for 1 datasets after PLC reconnection", + "details": { + "resumed_datasets": 1, + "total_attempted": 1 + } + }, + { + "timestamp": "2025-08-08T19:19:40.215752", + "level": "info", + "event_type": "application_started", + "message": "Application initialization completed successfully", + "details": {} + }, + { + "timestamp": "2025-08-08T19:19:40.280935", + "level": "info", + "event_type": "dataset_activated", + "message": "Dataset activated: DAR", + "details": { + "dataset_id": "DAR", + "variables_count": 2, + "streaming_count": 2, + "prefix": "gateway_phoenix" + } + }, + { + "timestamp": "2025-08-08T19:19:40.290333", + "level": "info", + "event_type": "csv_recording_started", + "message": "CSV recording started: 1 datasets activated", + "details": { + "activated_datasets": 1, + "total_datasets": 1 + } + }, + { + "timestamp": "2025-08-08T19:23:06.249068", + "level": "info", + "event_type": "dataset_csv_file_created", + "message": "New CSV file created after variable modification for dataset 'DAR': gateway_phoenix_19_23_06.csv", + "details": { + "dataset_id": "DAR", + "file_path": "records\\08-08-2025\\gateway_phoenix_19_23_06.csv", + "variables_count": 1, + "reason": "variable_modification" + } + }, + { + "timestamp": "2025-08-08T19:23:06.257427", + "level": "info", + "event_type": "variable_removed", + "message": "Variable removed from dataset 'DAR': UR29_Brix", + "details": { + "dataset_id": "DAR", + "name": "UR29_Brix", + "removed_config": { + "area": "db", + "offset": 40, + "type": "real", + "streaming": true, + "db": 2123 + } + } + }, + { + "timestamp": "2025-08-08T19:23:06.591207", + "level": "info", + "event_type": "dataset_csv_file_created", + "message": "New CSV file created after variable modification for dataset 'DAR': gateway_phoenix_19_23_06.csv", + "details": { + "dataset_id": "DAR", + "file_path": "records\\08-08-2025\\gateway_phoenix_19_23_06.csv", + "variables_count": 2, + "reason": "variable_modification" + } + }, + { + "timestamp": "2025-08-08T19:23:06.600691", + "level": "info", + "event_type": "variable_added", + "message": "Variable added to dataset 'DAR': UR29_Brix -> DB1011.1322 (real)", + "details": { + "dataset_id": "DAR", + "name": "UR29_Brix", + "area": "db", + "db": 1011, + "offset": 1322, + "bit": null, + "type": "real", + "streaming": false + } + }, + { + "timestamp": "2025-08-08T19:23:17.990119", + "level": "info", + "event_type": "dataset_csv_file_created", + "message": "New CSV file created after variable modification for dataset 'DAR': gateway_phoenix_19_23_17.csv", + "details": { + "dataset_id": "DAR", + "file_path": "records\\08-08-2025\\gateway_phoenix_19_23_17.csv", + "variables_count": 1, + "reason": "variable_modification" + } + }, + { + "timestamp": "2025-08-08T19:23:17.999136", + "level": "info", + "event_type": "variable_removed", + "message": "Variable removed from dataset 'DAR': UR29_ma", + "details": { + "dataset_id": "DAR", + "name": "UR29_ma", + "removed_config": { + "area": "db", + "offset": 36, + "type": "real", + "streaming": true, + "db": 2123 + } + } + }, + { + "timestamp": "2025-08-08T19:23:18.334515", + "level": "info", + "event_type": "dataset_csv_file_created", + "message": "New CSV file created after variable modification for dataset 'DAR': gateway_phoenix_19_23_18.csv", + "details": { + "dataset_id": "DAR", + "file_path": "records\\08-08-2025\\gateway_phoenix_19_23_18.csv", + "variables_count": 2, + "reason": "variable_modification" + } + }, + { + "timestamp": "2025-08-08T19:23:18.342533", + "level": "info", + "event_type": "variable_added", + "message": "Variable added to dataset 'DAR': UR29_ma -> DB1011.1296 (real)", + "details": { + "dataset_id": "DAR", + "name": "UR29_ma", + "area": "db", + "db": 1011, + "offset": 1296, + "bit": null, + "type": "real", + "streaming": false + } + }, + { + "timestamp": "2025-08-08T19:29:29.647644", + "level": "info", + "event_type": "application_started", + "message": "Application initialization completed successfully", + "details": {} + }, + { + "timestamp": "2025-08-08T19:29:29.730896", + "level": "info", + "event_type": "dataset_activated", + "message": "Dataset activated: DAR", + "details": { + "dataset_id": "DAR", + "variables_count": 2, + "streaming_count": 2, + "prefix": "gateway_phoenix" + } + }, + { + "timestamp": "2025-08-08T19:29:29.749218", + "level": "info", + "event_type": "csv_recording_started", + "message": "CSV recording started: 1 datasets activated", + "details": { + "activated_datasets": 1, + "total_datasets": 1 + } + }, + { + "timestamp": "2025-08-08T19:35:31.385846", + "level": "info", + "event_type": "application_started", + "message": "Application initialization completed successfully", + "details": {} + }, + { + "timestamp": "2025-08-08T19:35:31.438174", + "level": "info", + "event_type": "dataset_activated", + "message": "Dataset activated: DAR", + "details": { + "dataset_id": "DAR", + "variables_count": 2, + "streaming_count": 2, + "prefix": "gateway_phoenix" + } + }, + { + "timestamp": "2025-08-08T19:35:31.446174", + "level": "info", + "event_type": "csv_recording_started", + "message": "CSV recording started: 1 datasets activated", + "details": { + "activated_datasets": 1, + "total_datasets": 1 + } } ], - "last_updated": "2025-08-08T18:30:01.498578", - "total_entries": 671 + "last_updated": "2025-08-08T19:35:31.446174", + "total_entries": 742 } \ No newline at end of file diff --git a/plc_datasets.json b/plc_datasets.json index 5828eb1..ddbf3df 100644 --- a/plc_datasets.json +++ b/plc_datasets.json @@ -6,17 +6,17 @@ "variables": { "UR29_Brix": { "area": "db", - "offset": 40, + "offset": 1322, "type": "real", "streaming": true, - "db": 2123 + "db": 1011 }, "UR29_ma": { "area": "db", - "offset": 36, + "offset": 1296, "type": "real", "streaming": true, - "db": 2123 + "db": 1011 } }, "streaming_variables": [ @@ -33,5 +33,5 @@ ], "current_dataset_id": "DAR", "version": "1.0", - "last_update": "2025-08-08T17:13:42.321147" + "last_update": "2025-08-08T19:35:31.435189" } \ No newline at end of file diff --git a/static/js/plotting.js b/static/js/plotting.js index 7d90b86..7ffbb8b 100644 --- a/static/js/plotting.js +++ b/static/js/plotting.js @@ -19,7 +19,8 @@ class PlotManager { this.updateInterval = null; this.statusUpdateInterval = null; // 🔑 NUEVO: Para updates de status this.isInitialized = false; - this.refreshRates = new Map(); // Para mantener el refresh rate de cada sesión + // El render lo maneja el plugin a ~30 FPS; no exponemos refresh rate en UI + this.refreshRates = new Map(); // aún usado internamente para onRefresh // Colores para las variables this.colors = [ @@ -141,15 +142,7 @@ class PlotManager { -
- - - ms -
+
@@ -176,23 +169,7 @@ class PlotManager { createStreamingChart(sessionId, config) { const ctx = document.getElementById(`chart-${sessionId}`).getContext('2d'); - // Verificar si RealTimeScale está disponible - const hasRealTimeScale = Chart.registry.scales.realtime; - - let chartConfig; - - console.log(`🔍 Plot ${sessionId}: Checking streaming support...`); - console.log(`📊 Chart.registry.scales.realtime available:`, !!hasRealTimeScale); - - if (hasRealTimeScale) { - // Configuración con chartjs-plugin-streaming - console.log(`✅ Plot ${sessionId}: Using Real-time Streaming mode`); - chartConfig = this.createStreamingChartConfig(sessionId, config); - } else { - // Configuración fallback con time scale normal - console.log(`⚠️ Plot ${sessionId}: Using Manual Fallback mode`); - chartConfig = this.createFallbackChartConfig(sessionId, config); - } + let chartConfig = this.createStreamingChartConfig(sessionId, config); // Pre-poblar datasets en config antes de crear el Chart para evitar metas undefined if (Array.isArray(config.variables) && config.variables.length > 0) { @@ -214,8 +191,18 @@ class PlotManager { chartConfig.data.datasets = datasets; } - // Crear chart - const chart = new Chart(ctx, chartConfig); + // Crear chart intentando modo realtime; si falla, usar fallback time + let chart; + let isRealTimeMode = true; + try { + chart = new Chart(ctx, chartConfig); + console.log(`✅ Plot ${sessionId}: Real-time Streaming enabled`); + } catch (e) { + console.warn(`⚠️ Plot ${sessionId}: Real-time scale not available. Falling back to time scale.`, e); + chartConfig = this.createFallbackChartConfig(sessionId, config); + chart = new Chart(ctx, chartConfig); + isRealTimeMode = false; + } // Guardar en sessions this.sessions.set(sessionId, { @@ -223,14 +210,13 @@ class PlotManager { config: config, lastDataFetch: 0, datasetIndex: new Map(), // Mapeo de variable -> índice de dataset - isRealTimeMode: hasRealTimeScale, + isRealTimeMode: isRealTimeMode, lastPushedXByDataset: new Map() // índice de dataset -> último timestamp x añadido }); - // Inicializar refresh rate por defecto + // Fijar refresh de datos base (no de render). El render es 30 FPS en el plugin. if (!this.refreshRates.has(sessionId)) { - this.refreshRates.set(sessionId, 1000); // 1 segundo por defecto - console.log(`⏱️ Plot ${sessionId}: Default refresh rate set to 1000ms`); + this.refreshRates.set(sessionId, 1000); } // Inicializar índice de datasets (sin mutar chart aún) @@ -241,7 +227,7 @@ class PlotManager { } // Si no es modo realtime, iniciar intervalo manual - if (!hasRealTimeScale) { + if (!isRealTimeMode) { const refreshRate = this.refreshRates.get(sessionId) || 1000; console.log(`🔄 Plot ${sessionId}: Starting manual refresh with ${refreshRate}ms interval`); this.startManualRefresh(sessionId); @@ -270,7 +256,7 @@ class PlotManager { refresh: this.refreshRates.get(sessionId) || 1000, // Actualizar según configuración dinámica delay: ((this.refreshRates.get(sessionId) || 1000) * 2), // Margen temporal para evitar saltos frameRate: 30, - pause: !config.is_active, // Pausar si no está activo + pause: false, // No pausar inicialmente; el estado real se aplicará vía updateSessionStatus onRefresh: (chart) => { this.onStreamingRefresh(sessionId, chart); } @@ -475,13 +461,10 @@ class PlotManager { const plotData = await response.json(); - if (plotData && plotData.datasets && plotData.datasets.length > 0) { - // Procesar nuevos datos para el streaming - this.addNewDataToStreaming(sessionId, plotData, now); - - // Actualizar contador de puntos - this.updatePointsCounter(sessionId, plotData); - } + // 🔑 MODIFICADO: Llamar siempre a addNewDataToStreaming para asegurar el panning continuo. + // La función se encargará de manejar si hay datos nuevos o no. + this.addNewDataToStreaming(sessionId, plotData, now); + this.updatePointsCounter(sessionId, plotData); } catch (error) { console.error(`📈 Error in streaming refresh for ${sessionId}:`, error); @@ -499,38 +482,39 @@ class PlotManager { const chart = sessionData.chart; const isRealTimeMode = sessionData.isRealTimeMode; - let pointsAdded = 0; - plotData.datasets.forEach((backendDataset, datasetIndex) => { - if (!backendDataset.data || backendDataset.data.length === 0) { - return; + // En modo realtime, dejamos el panning continuo al plugin mediante 'delay'. + // Solo empujamos puntos cuando haya datos nuevos del backend, usando su timestamp real. + let pointsAdded = 0; + chart.data.datasets.forEach((chartDataset, datasetIndex) => { + const lastPushedX = sessionData.lastPushedXByDataset.get(datasetIndex) || 0; + + const backendDataset = plotData && plotData.datasets ? plotData.datasets[datasetIndex] : undefined; + if (!backendDataset || !backendDataset.data || backendDataset.data.length === 0) { + return; // No hay datos nuevos para este dataset } - // Obtener el último punto de datos válido const latestPoint = backendDataset.data[backendDataset.data.length - 1]; if (!latestPoint || latestPoint.y === null || latestPoint.y === undefined) { - return; + return; // Punto inválido } - // Verificar que el dataset existe en el chart - if (!chart.data.datasets[datasetIndex]) { - return; - } + // Garantizar monotonicidad en X sin forzar Date.now(): usar timestamp real del backend + const candidateX = typeof latestPoint.x === 'number' ? latestPoint.x : 0; + const finalX = Math.max(candidateX, lastPushedX + 1); - // Agregar el punto con timestamp correcto - const lastPushedX = sessionData.lastPushedXByDataset.get(datasetIndex) || 0; - const candidateX = latestPoint.x || timestamp; - const finalX = candidateX > lastPushedX ? candidateX : lastPushedX + 1; // garantizar x strictly increasing - - chart.data.datasets[datasetIndex].data.push({ x: finalX, y: latestPoint.y }); + chartDataset.data.push({ x: finalX, y: latestPoint.y }); sessionData.lastPushedXByDataset.set(datasetIndex, finalX); pointsAdded++; }); - // En modo fallback, manejar limpieza manual de datos antiguos + // En modo fallback (sin realtime), limpieza y update manual if (!isRealTimeMode) { this.cleanupOldDataFallback(sessionId); - // Actualizar chart manualmente + chart.update('quiet'); + } else if (pointsAdded > 0) { + // Modelo pull asíncrono: tras añadir datos en onRefresh, actualizar de forma 'quiet' + // Referencia: guía oficial (Pull Model - Asynchronous) chart.update('quiet'); } } @@ -1521,100 +1505,36 @@ class PlotManager { } /** - * Actualiza el refresh rate de una sesión de plot + * Ajusta el intervalo de adquisición de datos (no el frame rate) */ updateRefreshRate(sessionId, refreshRate) { try { const refreshRateMs = parseInt(refreshRate); - - // Validación de rango - if (isNaN(refreshRateMs)) { - console.error('❌ Invalid refresh rate - not a number:', refreshRate); - return; - } - - if (refreshRateMs < 100) { - console.warn('⚠️ Refresh rate too low, setting to minimum (100ms)'); - refreshRate = '100'; - } else if (refreshRateMs > 60000) { - console.warn('⚠️ Refresh rate too high, setting to maximum (60000ms)'); - refreshRate = '60000'; - } - - const finalRefreshRateMs = parseInt(refreshRate); - - // Actualizar el map de refresh rates + if (isNaN(refreshRateMs)) return; + const finalRefreshRateMs = Math.min(Math.max(refreshRateMs, 100), 60000); this.refreshRates.set(sessionId, finalRefreshRateMs); const sessionData = this.sessions.get(sessionId); - if (!sessionData || !sessionData.chart) { - console.error('❌ Session not found:', sessionId); - return; - } + if (!sessionData || !sessionData.chart) return; - // Actualizar la configuración del chart if (sessionData.isRealTimeMode) { - // Modo streaming con chartjs-plugin-streaming const chart = sessionData.chart; if (chart.scales && chart.scales.x && chart.scales.x.realtime) { - // Actualizar la configuración de refresh rate chart.scales.x.realtime.refresh = finalRefreshRateMs; - - // Forzar reinicio del intervalo de obtención de datos (onRefresh) const streaming = chart.$streaming; if (streaming) { - if (streaming.intervalId) { - clearInterval(streaming.intervalId); - } - // Recrear el intervalo SOLO para onRefresh; el render lo gestiona el plugin oficial + if (streaming.intervalId) clearInterval(streaming.intervalId); streaming.intervalId = setInterval(() => { if (!chart.scales.x.realtime.pause && typeof chart.scales.x.realtime.onRefresh === 'function') { chart.scales.x.realtime.onRefresh(chart); } }, finalRefreshRateMs); - - console.log(`🔄 Data refresh interval restarted with ${finalRefreshRateMs}ms`); } - - // Ajustar dinámicamente el delay a 2x refresh para evitar saltos visuales - if (chart.scales && chart.scales.x && chart.scales.x.realtime) { - const newDelay = Math.max(finalRefreshRateMs * 2, 0); - chart.scales.x.realtime.delay = newDelay; - if (chart.options && chart.options.scales && chart.options.scales.x && chart.options.scales.x.realtime) { - chart.options.scales.x.realtime.delay = newDelay; - } - console.log(`⏳ Realtime delay adjusted to ${newDelay}ms`); - } - - // También actualizar la configuración de opciones para futuros reinicios chart.options.scales.x.realtime.refresh = finalRefreshRateMs; } } else { - // Para modo manual, reiniciar el intervalo - console.log(`🔄 Manual refresh restarted with ${finalRefreshRateMs}ms`); this.startManualRefresh(sessionId); } - - // Sincronizar ambos inputs (main y tab) - const mainInput = document.getElementById(`refresh-rate-${sessionId}`); - const tabInput = document.getElementById(`refresh-rate-tab-${sessionId}`); - - if (mainInput && mainInput.value !== refreshRate) { - mainInput.value = refreshRate; - } - if (tabInput && tabInput.value !== refreshRate) { - tabInput.value = refreshRate; - } - - // Debug information - console.log(`⏱️ Plot ${sessionId}: Refresh rate updated to ${finalRefreshRateMs}ms`); - console.log(`📊 Plot ${sessionId}: Mode: ${sessionData.isRealTimeMode ? 'Real-time Streaming' : 'Manual Fallback'}`); - console.log(`🔧 Plot ${sessionId}: Chart scales available:`, !!sessionData.chart.scales); - if (sessionData.chart.scales && sessionData.chart.scales.x) { - console.log(`⚙️ Plot ${sessionId}: X-scale type:`, sessionData.chart.scales.x.type); - console.log(`⚡ Plot ${sessionId}: Realtime config:`, sessionData.chart.scales.x.realtime); - } - } catch (error) { console.error('❌ Error updating refresh rate:', error); } diff --git a/system_state.json b/system_state.json index 251eaf1..3170e15 100644 --- a/system_state.json +++ b/system_state.json @@ -1,11 +1,11 @@ { "last_state": { "should_connect": true, - "should_stream": true, + "should_stream": false, "active_datasets": [ "DAR" ] }, "auto_recovery_enabled": true, - "last_update": "2025-08-08T17:13:44.673911" + "last_update": "2025-08-08T19:35:31.456173" } \ No newline at end of file