Actualización de application_events.json con nuevos eventos relacionados con la reanudación automática de datasets tras reconexiones al PLC. Se ajustaron las fechas de última actualización en varios archivos de configuración, incluyendo plc_datasets.json y system_state.json. Se modificaron los offsets y configuraciones de variables en plc_datasets.json para mejorar la gestión de datos. Además, se realizaron optimizaciones en plotting.js para mejorar la experiencia de usuario en la visualización de datos en tiempo real.

This commit is contained in:
Miguel 2025-08-08 19:36:59 +02:00
parent cf5a169cce
commit f78ccbdc1d
4 changed files with 828 additions and 138 deletions

View File

@ -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
}

View File

@ -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"
}

View File

@ -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 {
<button class="btn btn-sm" onclick="plotManager.removePlot('${sessionId}')" title="Remove">
Remove
</button>
<div class="refresh-rate-control">
<label for="refresh-rate-${sessionId}" title="Chart Refresh Rate (ms)"></label>
<input type="number" id="refresh-rate-${sessionId}" class="refresh-rate-input"
value="1000" min="100" max="60000" step="100"
onchange="plotManager.updateRefreshRate('${sessionId}', this.value)"
onkeypress="if(event.key==='Enter') plotManager.updateRefreshRate('${sessionId}', this.value)"
title="Refresh rate in milliseconds (100-60000)">
<span class="refresh-rate-unit">ms</span>
</div>
</div>
</div>
<div class="plot-info">
@ -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);
}

View File

@ -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"
}