Actualización de archivos de configuración y estado del sistema, incluyendo la adición de nuevos eventos en application_events.json para la gestión de sesiones de plot y mejoras en el sistema de streaming. Se implementaron funciones de depuración en plotting.js y se ajustaron las fechas de última actualización en varios archivos. Además, se mejoró la interfaz de usuario en index.html y se realizaron ajustes en la gestión de sesiones en PlotManager para optimizar la experiencia del usuario.
This commit is contained in:
parent
8195b5d430
commit
55c4e67cf7
|
@ -0,0 +1,198 @@
|
|||
# 📈 Chart.js Plugin Streaming - Integración
|
||||
|
||||
## 🚀 Implementación Exitosa
|
||||
|
||||
Se ha integrado exitosamente **chartjs-plugin-streaming** en la aplicación PLC S7-315 Streamer & Logger para mejorar significativamente el sistema de plotting en tiempo real.
|
||||
|
||||
## 📋 ¿Qué se ha cambiado?
|
||||
|
||||
### 1. **Archivos agregados**
|
||||
```
|
||||
static/js/chartjs-streaming/
|
||||
├── chartjs-plugin-streaming.js # 📦 Archivo integrador principal
|
||||
├── plugin.streaming.js # 🔧 Plugin de streaming
|
||||
├── plugin.zoom.js # 🔍 Plugin de zoom
|
||||
├── scale.realtime.js # ⏰ Escala de tiempo real
|
||||
└── helpers.streaming.js # 🛠️ Utilidades
|
||||
```
|
||||
|
||||
### 2. **Archivos modificados**
|
||||
- `templates/index.html` - Carga de nueva librería
|
||||
- `static/js/plotting.js` - Sistema de plotting reescrito para streaming
|
||||
|
||||
## 🔧 Características Implementadas
|
||||
|
||||
### ✅ **Ventajas del nuevo sistema:**
|
||||
|
||||
1. **📊 Streaming automático**: Los gráficos se actualizan automáticamente
|
||||
2. **🔄 Gestión inteligente de memoria**: Elimina datos antiguos automáticamente
|
||||
3. **⚡ Mejor rendimiento**: Optimizado para datos en tiempo real
|
||||
4. **🎛️ Controles avanzados**: Pause/Resume, Clear, mejor interactividad
|
||||
5. **📏 Escalas dinámicas**: Ventana de tiempo deslizante automática
|
||||
6. **🎨 Configuración flexible**: Duración, frecuencia, límites Y personalizables
|
||||
|
||||
### ✅ **Funcionalidades disponibles:**
|
||||
|
||||
#### **Configuración automática**
|
||||
```javascript
|
||||
// Se crea automáticamente con configuración optimizada
|
||||
const config = ChartStreaming.createStreamingChartConfig({
|
||||
duration: 60000, // 60 segundos de ventana
|
||||
refresh: 500, // Actualizar cada 500ms
|
||||
frameRate: 30, // 30 FPS
|
||||
yMin: -100, // Límite inferior Y
|
||||
yMax: 100 // Límite superior Y
|
||||
});
|
||||
```
|
||||
|
||||
#### **Controles de streaming**
|
||||
- **▶️ Start**: Inicia el streaming en tiempo real
|
||||
- **⏸️ Pause**: Pausa temporalmente el streaming
|
||||
- **🗑️ Clear**: Limpia todos los datos del gráfico
|
||||
- **⏹️ Stop**: Detiene completamente el streaming
|
||||
|
||||
#### **Gestión automática de datos**
|
||||
- Los datos se agregan automáticamente conforme llegan
|
||||
- Los datos antiguos se eliminan según la configuración TTL
|
||||
- La ventana de tiempo se desliza automáticamente
|
||||
|
||||
## 🎯 Beneficios para el Usuario
|
||||
|
||||
### **Antes (Sistema manual)**
|
||||
```javascript
|
||||
// Gestión manual de escalas y datos
|
||||
chart.data.datasets = plotData.datasets;
|
||||
chart.options.scales.x.min = startTime;
|
||||
chart.options.scales.x.max = endTime;
|
||||
chart.update('none');
|
||||
```
|
||||
|
||||
### **Ahora (Sistema streaming)**
|
||||
```javascript
|
||||
// Automático - solo agregar datos
|
||||
ChartStreaming.addStreamingData(chart, datasetIndex, {
|
||||
x: timestamp,
|
||||
y: value
|
||||
});
|
||||
// El plugin maneja todo lo demás automáticamente
|
||||
```
|
||||
|
||||
## 🔧 Configuración por Defecto
|
||||
|
||||
### **Escalas de tiempo real**
|
||||
- **Duración**: 60 segundos por defecto (configurable por plot)
|
||||
- **Refresco**: 500ms (datos se obtienen automáticamente del backend)
|
||||
- **Frame rate**: 30 FPS para animaciones suaves
|
||||
- **TTL**: Configurable para limpieza automática de datos
|
||||
|
||||
### **Optimizaciones de rendimiento**
|
||||
- Sin animaciones innecesarias
|
||||
- Puntos de datos ocultos (solo visible en hover)
|
||||
- Líneas suaves con tensión optimizada
|
||||
- Actualización silenciosa ("quiet mode")
|
||||
|
||||
## 📚 API Disponible
|
||||
|
||||
### **Funciones principales:**
|
||||
```javascript
|
||||
// Control global
|
||||
window.ChartStreaming.createStreamingChartConfig(options)
|
||||
window.ChartStreaming.addStreamingData(chart, datasetIndex, data)
|
||||
window.ChartStreaming.setStreamingPause(chart, paused)
|
||||
window.ChartStreaming.clearStreamingData(chart)
|
||||
|
||||
// Control por sesión (PlotManager)
|
||||
plotManager.setStreamingPause(sessionId, paused)
|
||||
plotManager.clearStreamingData(sessionId)
|
||||
plotManager.refreshStreamingData(sessionId, chart) // Automática
|
||||
```
|
||||
|
||||
### **Configuración personalizada:**
|
||||
```javascript
|
||||
{
|
||||
duration: 60000, // Ventana de tiempo en ms
|
||||
delay: 0, // Retraso en ms
|
||||
refresh: 500, // Intervalo de actualización en ms
|
||||
frameRate: 30, // FPS para animaciones
|
||||
pause: false, // Estado inicial
|
||||
ttl: undefined, // Tiempo de vida de datos
|
||||
yMin: undefined, // Límite inferior Y
|
||||
yMax: undefined, // Límite superior Y
|
||||
onRefresh: function // Callback para obtener datos
|
||||
}
|
||||
```
|
||||
|
||||
## 🔗 Integración con Backend
|
||||
|
||||
### **Flujo de datos actualizado:**
|
||||
1. **Backend**: Genera datos en `/api/plots/{sessionId}/data`
|
||||
2. **Plugin**: Llama automáticamente `onRefresh` cada 500ms
|
||||
3. **PlotManager**: Obtiene datos del backend en `refreshStreamingData`
|
||||
4. **Chart**: Se actualiza automáticamente con nuevos datos
|
||||
|
||||
### **Compatibilidad:**
|
||||
- ✅ Funciona con todos los endpoints existentes
|
||||
- ✅ Compatible con triggers booleanos
|
||||
- ✅ Mantiene configuración Y min/max
|
||||
- ✅ Preserva colores y estilos de variables
|
||||
|
||||
## 🎮 Controles de Usuario
|
||||
|
||||
### **Interfaz actualizada:**
|
||||
- Los botones **Start/Pause/Clear/Stop** ahora controlan streaming
|
||||
- **Pause**: Congela la visualización manteniendo datos
|
||||
- **Clear**: Limpia gráfico pero mantiene configuración
|
||||
- **Stop**: Pausa streaming y notifica al backend
|
||||
|
||||
### **Retrocompatibilidad:**
|
||||
- Todas las funciones existentes siguen funcionando
|
||||
- Los plots existentes se migran automáticamente
|
||||
- La API del backend no ha cambiado
|
||||
|
||||
## 🔬 Debug y Troubleshooting
|
||||
|
||||
### **Habilitar debug:**
|
||||
```javascript
|
||||
// En consola del navegador
|
||||
enablePlotDebug()
|
||||
```
|
||||
|
||||
### **Logs disponibles:**
|
||||
- Inicialización de datasets de streaming
|
||||
- Agregado de nuevos puntos de datos
|
||||
- Control de pause/resume
|
||||
- Limpieza de datos
|
||||
- Errores de conexión con backend
|
||||
|
||||
### **Verificar integración:**
|
||||
```javascript
|
||||
// En consola del navegador
|
||||
testPlotSystem()
|
||||
```
|
||||
|
||||
## 🚀 Próximos Pasos Recomendados
|
||||
|
||||
1. **✅ Probar con datos reales** del PLC
|
||||
2. **🎛️ Ajustar configuraciones** según necesidades específicas
|
||||
3. **📊 Optimizar intervalos** de refresco según carga del sistema
|
||||
4. **🔧 Personalizar colores** y estilos según preferencias
|
||||
|
||||
## 💡 Tips de Uso
|
||||
|
||||
### **Para mejor rendimiento:**
|
||||
- Usa intervalos de refresco ≥ 500ms para reducir carga
|
||||
- Configura TTL para limpiar datos antiguos automáticamente
|
||||
- Mantén ≤ 10 variables por plot para fluidez óptima
|
||||
|
||||
### **Para debugging:**
|
||||
- Activa logs de debug cuando desarrolles
|
||||
- Usa la consola del navegador para inspeccionar
|
||||
- Verifica conectividad PLC antes de crear plots
|
||||
|
||||
---
|
||||
|
||||
## 🎉 ¡Integración Completada!
|
||||
|
||||
La aplicación ahora cuenta con un sistema de plotting en tiempo real robusto, eficiente y fácil de usar, potenciado por **chartjs-plugin-streaming**.
|
||||
|
||||
**¡Disfruta de tus gráficos en tiempo real mejorados!** 📈✨
|
|
@ -0,0 +1,247 @@
|
|||
# 🔧 Troubleshooting Chart.js Streaming - Guía de Resolución
|
||||
|
||||
## 🚨 Problema Reportado
|
||||
|
||||
**Síntomas:**
|
||||
- ✅ Status muestra "Active"
|
||||
- ✅ Variables cambia de 0 a 1
|
||||
- ❌ Data Points se mantiene en 0
|
||||
- ❌ No se ve ningún plot dentro de la grilla
|
||||
- ❌ La línea de tiempo no se mueve
|
||||
- ⚠️ La escala Y cambia pero es lo único que funciona
|
||||
|
||||
## 🔍 Diagnóstico Paso a Paso
|
||||
|
||||
### **Paso 1: Verificar que se cargó chartjs-plugin-streaming**
|
||||
|
||||
Abrir **Consola del Navegador** (F12) y ejecutar:
|
||||
|
||||
```javascript
|
||||
verifyStreamingIntegration()
|
||||
```
|
||||
|
||||
**Resultado esperado:**
|
||||
```
|
||||
🧪 Verificando integración de Chart.js Streaming...
|
||||
✅ Chart.js cargado: true
|
||||
✅ ChartStreaming cargado: true
|
||||
✅ PlotManager cargado: true
|
||||
✅ Sesiones de streaming activas: 1
|
||||
```
|
||||
|
||||
**Si ChartStreaming cargado: false:**
|
||||
1. Verificar que `chartjs-plugin-streaming.js` se carga correctamente
|
||||
2. Revisar errores en la consola
|
||||
3. Recargar la página
|
||||
|
||||
### **Paso 2: Habilitar Debug Detallado**
|
||||
|
||||
```javascript
|
||||
enablePlotDebug()
|
||||
```
|
||||
|
||||
### **Paso 3: Forzar Actualización de Datos**
|
||||
|
||||
```javascript
|
||||
forceStreamingUpdate()
|
||||
```
|
||||
|
||||
**Buscar en consola:**
|
||||
```
|
||||
📈 Plot plot_13: Fetching data from backend...
|
||||
📈 Plot plot_13: Received data: {...}
|
||||
📈 Plot plot_13: Processing X datasets for streaming
|
||||
```
|
||||
|
||||
### **Paso 4: Verificar Datos del Backend**
|
||||
|
||||
Ejecutar en consola:
|
||||
|
||||
```javascript
|
||||
fetch('/api/plots/plot_13/data')
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
console.log('📊 Backend data:', data);
|
||||
console.log('📊 Datasets:', data.datasets?.length || 0);
|
||||
console.log('📊 Data points per dataset:',
|
||||
data.datasets?.map(d => d.data?.length || 0) || []);
|
||||
});
|
||||
```
|
||||
|
||||
**Resultado esperado:**
|
||||
```
|
||||
📊 Backend data: {session_id: "plot_13", datasets: [...], data_points_count: X}
|
||||
📊 Datasets: 1
|
||||
📊 Data points per dataset: [5, 8, 12]
|
||||
```
|
||||
|
||||
### **Paso 5: Verificar Configuración del Chart**
|
||||
|
||||
```javascript
|
||||
// Para la sesión activa (ej: plot_13)
|
||||
const sessionData = plotManager.sessions.get('plot_13');
|
||||
console.log('📈 Chart config:', {
|
||||
hasChart: !!sessionData?.chart,
|
||||
scaleType: sessionData?.chart?.scales?.x?.type,
|
||||
hasRealTimeScale: sessionData?.chart?.scales?.x?.constructor?.name,
|
||||
streamingEnabled: !!sessionData?.chart?.$streaming?.enabled,
|
||||
datasets: sessionData?.chart?.data?.datasets?.length || 0
|
||||
});
|
||||
```
|
||||
|
||||
**Resultado esperado:**
|
||||
```
|
||||
📈 Chart config: {
|
||||
hasChart: true,
|
||||
scaleType: "realtime",
|
||||
hasRealTimeScale: "RealTimeScale",
|
||||
streamingEnabled: true,
|
||||
datasets: 1
|
||||
}
|
||||
```
|
||||
|
||||
## 🛠️ Soluciones Comunes
|
||||
|
||||
### **Problema: ChartStreaming no está cargado**
|
||||
|
||||
**Causa:** El archivo `chartjs-plugin-streaming.js` no se carga correctamente.
|
||||
|
||||
**Solución:**
|
||||
1. Verificar que el archivo existe en `static/js/chartjs-streaming/chartjs-plugin-streaming.js`
|
||||
2. Revisar que el HTML incluye: `<script src="/static/js/chartjs-streaming/chartjs-plugin-streaming.js"></script>`
|
||||
3. Verificar orden de carga (debe ser después de Chart.js y antes de plotting.js)
|
||||
|
||||
### **Problema: Backend devuelve datos pero no aparecen en el chart**
|
||||
|
||||
**Causa:** Error en el procesamiento de datos o timestamps incorrectos.
|
||||
|
||||
**Solución:**
|
||||
```javascript
|
||||
// Verificar timestamps de los datos
|
||||
fetch('/api/plots/plot_13/data')
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
const firstDataset = data.datasets[0];
|
||||
const firstPoint = firstDataset.data[0];
|
||||
console.log('📊 First point timestamp:', firstPoint.x);
|
||||
console.log('📊 Current time:', Date.now());
|
||||
console.log('📊 Time difference (sec):', (Date.now() - firstPoint.x) / 1000);
|
||||
});
|
||||
```
|
||||
|
||||
Si la diferencia de tiempo es muy grande (>60 segundos), el punto puede estar fuera de la ventana de tiempo.
|
||||
|
||||
### **Problema: Escala realtime no funciona**
|
||||
|
||||
**Causa:** La escala no se inicializó correctamente.
|
||||
|
||||
**Solución:**
|
||||
```javascript
|
||||
// Re-inicializar plot
|
||||
const sessionId = 'plot_13'; // Cambiar por tu session ID
|
||||
plotManager.controlPlot(sessionId, 'stop');
|
||||
setTimeout(() => {
|
||||
plotManager.controlPlot(sessionId, 'start');
|
||||
}, 1000);
|
||||
```
|
||||
|
||||
### **Problema: Data Points siempre en 0**
|
||||
|
||||
**Causa:** Los datos no se están agregando al chart o se eliminan inmediatamente.
|
||||
|
||||
**Solución verificar:**
|
||||
1. **TTL Configuration**: Los datos pueden estar expirando muy rápido
|
||||
2. **Timestamp Format**: Los timestamps pueden estar en formato incorrecto
|
||||
3. **Dataset Index**: Los datos se pueden estar agregando al dataset incorrecto
|
||||
|
||||
```javascript
|
||||
// Agregar punto de prueba manualmente
|
||||
const sessionData = plotManager.sessions.get('plot_13');
|
||||
if (sessionData?.chart) {
|
||||
window.ChartStreaming.addStreamingData(sessionData.chart, 0, {
|
||||
x: Date.now(),
|
||||
y: Math.random() * 100
|
||||
});
|
||||
console.log('📈 Test point added');
|
||||
}
|
||||
```
|
||||
|
||||
## 🎯 Test de Resolución Rápida
|
||||
|
||||
**Ejecutar este script completo en consola:**
|
||||
|
||||
```javascript
|
||||
// Test completo de diagnóstico
|
||||
console.log('🔧 DIAGNÓSTICO COMPLETO');
|
||||
console.log('='.repeat(50));
|
||||
|
||||
// 1. Verificar componentes básicos
|
||||
console.log('1️⃣ COMPONENTES:');
|
||||
console.log('Chart.js:', typeof Chart !== 'undefined' ? '✅' : '❌');
|
||||
console.log('ChartStreaming:', typeof window.ChartStreaming !== 'undefined' ? '✅' : '❌');
|
||||
console.log('PlotManager:', typeof plotManager !== 'undefined' ? '✅' : '❌');
|
||||
|
||||
// 2. Verificar sesiones activas
|
||||
if (plotManager && plotManager.sessions.size > 0) {
|
||||
console.log('\n2️⃣ SESIONES ACTIVAS:');
|
||||
for (const [sessionId, sessionData] of plotManager.sessions) {
|
||||
console.log(`📈 ${sessionId}:`, {
|
||||
hasChart: !!sessionData.chart,
|
||||
scaleType: sessionData.chart?.scales?.x?.type,
|
||||
datasets: sessionData.chart?.data?.datasets?.length || 0,
|
||||
dataPoints: sessionData.chart?.data?.datasets?.reduce((total, d) => total + (d.data?.length || 0), 0) || 0
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Test de backend data
|
||||
console.log('\n3️⃣ BACKEND DATA TEST:');
|
||||
if (plotManager && plotManager.sessions.size > 0) {
|
||||
const firstSessionId = Array.from(plotManager.sessions.keys())[0];
|
||||
fetch(`/api/plots/${firstSessionId}/data`)
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
console.log('📊 Backend response:', {
|
||||
success: !!data.datasets,
|
||||
datasets: data.datasets?.length || 0,
|
||||
totalPoints: data.data_points_count || 0,
|
||||
firstDatasetPoints: data.datasets?.[0]?.data?.length || 0
|
||||
});
|
||||
})
|
||||
.catch(err => console.log('❌ Backend error:', err.message));
|
||||
}
|
||||
|
||||
console.log('\n4️⃣ NEXT STEPS:');
|
||||
console.log('- enablePlotDebug() para logs detallados');
|
||||
console.log('- forceStreamingUpdate() para forzar actualización');
|
||||
console.log('- Si persiste el problema, revisar configuración del backend');
|
||||
```
|
||||
|
||||
## 📞 Contacto de Soporte
|
||||
|
||||
Si después de estos pasos el problema persiste:
|
||||
|
||||
1. **Compartir resultado completo** del diagnóstico en consola
|
||||
2. **Verificar logs del backend** en la terminal donde corre `python main.py`
|
||||
3. **Revisar Network tab** en DevTools para errores de red
|
||||
|
||||
---
|
||||
|
||||
## 🎉 Resultado Esperado
|
||||
|
||||
Cuando funcione correctamente verás:
|
||||
|
||||
```
|
||||
📈 Chart.js Streaming Plugin loaded successfully
|
||||
📈 RealTimeScale initialized: {duration: 60000, refresh: 500, pause: false}
|
||||
📈 Plot plot_13: Successfully initialized 1 streaming datasets
|
||||
📈 Plot plot_13: Fetching data from backend...
|
||||
📈 Plot plot_13: Adding 3 new points for UR29_Brix
|
||||
📈 Added point to dataset 0 (UR29_Brix): x=1642598234567, y=54.258
|
||||
```
|
||||
|
||||
Y el gráfico mostrará:
|
||||
- ✅ Línea de tiempo deslizándose automáticamente
|
||||
- ✅ Data Points incrementándose
|
||||
- ✅ Líneas de variables dibujándose en tiempo real
|
||||
- ✅ Escala Y ajustándose a los datos
|
|
@ -4119,8 +4119,473 @@
|
|||
"trigger_variable": null,
|
||||
"auto_started": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-07-21T17:01:18.871713",
|
||||
"level": "info",
|
||||
"event_type": "application_started",
|
||||
"message": "Application initialization completed successfully",
|
||||
"details": {}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-07-21T17:01:18.903374",
|
||||
"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-21T17:01:18.912924",
|
||||
"level": "info",
|
||||
"event_type": "csv_recording_started",
|
||||
"message": "CSV recording started: 1 datasets activated",
|
||||
"details": {
|
||||
"activated_datasets": 1,
|
||||
"total_datasets": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-07-21T17:13:36.924970",
|
||||
"level": "info",
|
||||
"event_type": "plot_session_removed",
|
||||
"message": "Plot session 'Conducibilita Prodotto' removed",
|
||||
"details": {
|
||||
"session_id": "plot_9"
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-07-21T17:13:40.385435",
|
||||
"level": "info",
|
||||
"event_type": "plot_session_removed",
|
||||
"message": "Plot session 'Brix' removed",
|
||||
"details": {
|
||||
"session_id": "plot_12"
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-07-21T17:13:59.375435",
|
||||
"level": "info",
|
||||
"event_type": "plot_session_created",
|
||||
"message": "Plot session 'Brix' created and started",
|
||||
"details": {
|
||||
"session_id": "plot_13",
|
||||
"variables": [
|
||||
"UR29_Brix"
|
||||
],
|
||||
"time_window": 10,
|
||||
"trigger_variable": null,
|
||||
"auto_started": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-07-21T17:33:27.303533",
|
||||
"level": "info",
|
||||
"event_type": "application_started",
|
||||
"message": "Application initialization completed successfully",
|
||||
"details": {}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-07-21T17:33:27.334173",
|
||||
"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-21T17:33:27.345774",
|
||||
"level": "info",
|
||||
"event_type": "csv_recording_started",
|
||||
"message": "CSV recording started: 1 datasets activated",
|
||||
"details": {
|
||||
"activated_datasets": 1,
|
||||
"total_datasets": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-07-21T17:34:51.459774",
|
||||
"level": "info",
|
||||
"event_type": "application_started",
|
||||
"message": "Application initialization completed successfully",
|
||||
"details": {}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-07-21T17:34:51.489625",
|
||||
"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-21T17:34:51.498003",
|
||||
"level": "info",
|
||||
"event_type": "csv_recording_started",
|
||||
"message": "CSV recording started: 1 datasets activated",
|
||||
"details": {
|
||||
"activated_datasets": 1,
|
||||
"total_datasets": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-07-21T17:38:38.361177",
|
||||
"level": "info",
|
||||
"event_type": "application_started",
|
||||
"message": "Application initialization completed successfully",
|
||||
"details": {}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-07-21T17:38:38.394439",
|
||||
"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-21T17:38:38.402804",
|
||||
"level": "info",
|
||||
"event_type": "csv_recording_started",
|
||||
"message": "CSV recording started: 1 datasets activated",
|
||||
"details": {
|
||||
"activated_datasets": 1,
|
||||
"total_datasets": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-07-21T18:22:11.672676",
|
||||
"level": "info",
|
||||
"event_type": "csv_recording_stopped",
|
||||
"message": "CSV recording stopped (dataset threads continue for UDP streaming)",
|
||||
"details": {}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-07-21T18:22:11.682630",
|
||||
"level": "info",
|
||||
"event_type": "udp_streaming_stopped",
|
||||
"message": "UDP streaming to PlotJuggler stopped (CSV recording continues)",
|
||||
"details": {}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-07-21T18:22:11.818203",
|
||||
"level": "info",
|
||||
"event_type": "dataset_deactivated",
|
||||
"message": "Dataset deactivated: DAR",
|
||||
"details": {
|
||||
"dataset_id": "dar"
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-07-21T18:22:11.827661",
|
||||
"level": "info",
|
||||
"event_type": "plc_disconnection",
|
||||
"message": "Disconnected from PLC 10.1.33.11 (stopped recording and streaming)",
|
||||
"details": {}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-07-21T18:24:05.417266",
|
||||
"level": "info",
|
||||
"event_type": "plot_session_removed",
|
||||
"message": "Plot session 'Brix' removed",
|
||||
"details": {
|
||||
"session_id": "plot_13"
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-07-21T18:25:07.058543",
|
||||
"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-21T18:25:07.066931",
|
||||
"level": "info",
|
||||
"event_type": "csv_recording_started",
|
||||
"message": "CSV recording started: 1 datasets activated",
|
||||
"details": {
|
||||
"activated_datasets": 1,
|
||||
"total_datasets": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-07-21T18:25:07.076741",
|
||||
"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-07-21T18:25:18.499928",
|
||||
"level": "info",
|
||||
"event_type": "plot_session_created",
|
||||
"message": "Plot session 'Brix' created and started",
|
||||
"details": {
|
||||
"session_id": "plot_14",
|
||||
"variables": [
|
||||
"UR29_Brix"
|
||||
],
|
||||
"time_window": 60,
|
||||
"trigger_variable": null,
|
||||
"auto_started": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-07-21T18:26:03.568841",
|
||||
"level": "info",
|
||||
"event_type": "application_started",
|
||||
"message": "Application initialization completed successfully",
|
||||
"details": {}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-07-21T18:26:03.600360",
|
||||
"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-21T18:26:03.609460",
|
||||
"level": "info",
|
||||
"event_type": "csv_recording_started",
|
||||
"message": "CSV recording started: 1 datasets activated",
|
||||
"details": {
|
||||
"activated_datasets": 1,
|
||||
"total_datasets": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-07-21T18:27:08.559092",
|
||||
"level": "info",
|
||||
"event_type": "application_started",
|
||||
"message": "Application initialization completed successfully",
|
||||
"details": {}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-07-21T18:27:08.586976",
|
||||
"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-21T18:27:08.593701",
|
||||
"level": "info",
|
||||
"event_type": "csv_recording_started",
|
||||
"message": "CSV recording started: 1 datasets activated",
|
||||
"details": {
|
||||
"activated_datasets": 1,
|
||||
"total_datasets": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-07-21T18:27:09.538108",
|
||||
"level": "info",
|
||||
"event_type": "csv_recording_stopped",
|
||||
"message": "CSV recording stopped (dataset threads continue for UDP streaming)",
|
||||
"details": {}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-07-21T18:27:09.545334",
|
||||
"level": "info",
|
||||
"event_type": "udp_streaming_stopped",
|
||||
"message": "UDP streaming to PlotJuggler stopped (CSV recording continues)",
|
||||
"details": {}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-07-21T18:27:09.596088",
|
||||
"level": "info",
|
||||
"event_type": "dataset_deactivated",
|
||||
"message": "Dataset deactivated: DAR",
|
||||
"details": {
|
||||
"dataset_id": "dar"
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-07-21T18:27:09.606944",
|
||||
"level": "info",
|
||||
"event_type": "plc_disconnection",
|
||||
"message": "Disconnected from PLC 10.1.33.11 (stopped recording and streaming)",
|
||||
"details": {}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-07-21T18:27:12.713350",
|
||||
"level": "info",
|
||||
"event_type": "csv_recording_stopped",
|
||||
"message": "CSV recording stopped (dataset threads continue for UDP streaming)",
|
||||
"details": {}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-07-21T18:27:12.727587",
|
||||
"level": "info",
|
||||
"event_type": "udp_streaming_stopped",
|
||||
"message": "UDP streaming to PlotJuggler stopped (CSV recording continues)",
|
||||
"details": {}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-07-21T18:27:12.762044",
|
||||
"level": "info",
|
||||
"event_type": "plc_disconnection",
|
||||
"message": "Disconnected from PLC 10.1.33.11 (stopped recording and streaming)",
|
||||
"details": {}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-07-21T18:27:26.860032",
|
||||
"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-21T18:27:26.872811",
|
||||
"level": "info",
|
||||
"event_type": "csv_recording_started",
|
||||
"message": "CSV recording started: 1 datasets activated",
|
||||
"details": {
|
||||
"activated_datasets": 1,
|
||||
"total_datasets": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-07-21T18:27:26.884920",
|
||||
"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-07-21T18:34:08.696308",
|
||||
"level": "info",
|
||||
"event_type": "application_started",
|
||||
"message": "Application initialization completed successfully",
|
||||
"details": {}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-07-21T18:34:08.730392",
|
||||
"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-21T18:34:08.738627",
|
||||
"level": "info",
|
||||
"event_type": "csv_recording_started",
|
||||
"message": "CSV recording started: 1 datasets activated",
|
||||
"details": {
|
||||
"activated_datasets": 1,
|
||||
"total_datasets": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-07-21T18:34:46.471786",
|
||||
"level": "info",
|
||||
"event_type": "plot_session_removed",
|
||||
"message": "Plot session 'Brix' removed",
|
||||
"details": {
|
||||
"session_id": "plot_14"
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-07-21T18:35:13.057820",
|
||||
"level": "info",
|
||||
"event_type": "plot_session_created",
|
||||
"message": "Plot session 'Brix' created and started",
|
||||
"details": {
|
||||
"session_id": "plot_15",
|
||||
"variables": [
|
||||
"UR29_Brix"
|
||||
],
|
||||
"time_window": 60,
|
||||
"trigger_variable": null,
|
||||
"auto_started": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-07-21T18:36:17.216085",
|
||||
"level": "info",
|
||||
"event_type": "plot_session_removed",
|
||||
"message": "Plot session 'Brix' removed",
|
||||
"details": {
|
||||
"session_id": "plot_15"
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-07-21T18:38:00.731968",
|
||||
"level": "info",
|
||||
"event_type": "plot_session_created",
|
||||
"message": "Plot session 'TEst' created and started",
|
||||
"details": {
|
||||
"session_id": "plot_16",
|
||||
"variables": [
|
||||
"UR29_Brix"
|
||||
],
|
||||
"time_window": 60,
|
||||
"trigger_variable": null,
|
||||
"auto_started": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"last_updated": "2025-07-21T14:44:25.311128",
|
||||
"total_entries": 389
|
||||
"last_updated": "2025-07-21T18:38:00.731968",
|
||||
"total_entries": 436
|
||||
}
|
|
@ -48,8 +48,8 @@
|
|||
"streaming_variables": [
|
||||
"UR29_Brix_Digital",
|
||||
"UR62_Brix",
|
||||
"UR29_Brix",
|
||||
"CTS306_PV"
|
||||
"CTS306_PV",
|
||||
"UR29_Brix"
|
||||
],
|
||||
"sampling_interval": 0.2,
|
||||
"enabled": true,
|
||||
|
@ -70,5 +70,5 @@
|
|||
],
|
||||
"current_dataset_id": "dar",
|
||||
"version": "1.0",
|
||||
"last_update": "2025-07-21T14:43:41.517456"
|
||||
"last_update": "2025-07-21T18:34:08.728102"
|
||||
}
|
|
@ -1,34 +1,20 @@
|
|||
{
|
||||
"plots": {
|
||||
"plot_9": {
|
||||
"name": "Conducibilita Prodotto",
|
||||
"plot_16": {
|
||||
"name": "TEst",
|
||||
"variables": [
|
||||
"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_12": {
|
||||
"name": "Brix",
|
||||
"variables": [
|
||||
"UR29_Brix",
|
||||
"UR62_Brix"
|
||||
],
|
||||
"time_window": 60,
|
||||
"y_min": null,
|
||||
"y_max": null,
|
||||
"trigger_variable": null,
|
||||
"trigger_enabled": false,
|
||||
"trigger_on_true": true,
|
||||
"session_id": "plot_12"
|
||||
"session_id": "plot_16"
|
||||
}
|
||||
},
|
||||
"session_counter": 13,
|
||||
"last_saved": "2025-07-21T14:44:25.311128",
|
||||
"session_counter": 17,
|
||||
"last_saved": "2025-07-21T18:38:00.730994",
|
||||
"version": "1.0"
|
||||
}
|
|
@ -0,0 +1,405 @@
|
|||
/**
|
||||
* 📈 Chart.js Plugin Streaming Integration
|
||||
* Integra chartjs-plugin-streaming para plotting en tiempo real
|
||||
*
|
||||
* Combinación de módulos:
|
||||
* - helpers.streaming.js
|
||||
* - scale.realtime.js
|
||||
* - plugin.streaming.js
|
||||
* - plugin.zoom.js (integración con zoom)
|
||||
*/
|
||||
|
||||
(function (global, factory) {
|
||||
if (typeof exports === 'object' && typeof module !== 'undefined') {
|
||||
factory(exports, require('chart.js'));
|
||||
} else if (typeof define === 'function' && define.amd) {
|
||||
define(['exports', 'chart.js'], factory);
|
||||
} else {
|
||||
global = global || self;
|
||||
factory(global.ChartStreaming = {}, global.Chart);
|
||||
}
|
||||
})(this, function (exports, Chart) {
|
||||
'use strict';
|
||||
|
||||
// ============= HELPERS.STREAMING.JS =============
|
||||
function clamp(value, min, max) {
|
||||
return Math.min(Math.max(value, min), max);
|
||||
}
|
||||
|
||||
function resolveOption(scale, key) {
|
||||
const realtimeOptions = scale.options.realtime || {};
|
||||
const scaleOptions = scale.options;
|
||||
|
||||
if (realtimeOptions[key] !== undefined) {
|
||||
return realtimeOptions[key];
|
||||
}
|
||||
if (scaleOptions[key] !== undefined) {
|
||||
return scaleOptions[key];
|
||||
}
|
||||
|
||||
// Valores por defecto
|
||||
const defaults = {
|
||||
duration: 10000,
|
||||
delay: 0,
|
||||
refresh: 1000,
|
||||
frameRate: 30,
|
||||
pause: false,
|
||||
ttl: undefined,
|
||||
onRefresh: null
|
||||
};
|
||||
|
||||
return defaults[key];
|
||||
}
|
||||
|
||||
function getAxisMap(element, keys, meta) {
|
||||
const axis = meta.vAxisID || 'y';
|
||||
return keys[axis] || [];
|
||||
}
|
||||
|
||||
// ============= SCALE.REALTIME.JS (Corregido) =============
|
||||
class RealTimeScale extends Chart.Scale {
|
||||
constructor(cfg) {
|
||||
super(cfg);
|
||||
this.type = 'realtime';
|
||||
}
|
||||
|
||||
init(scaleOptions, scaleContext) {
|
||||
super.init(scaleOptions, scaleContext);
|
||||
|
||||
const me = this;
|
||||
const chart = me.chart;
|
||||
const streaming = chart.$streaming = chart.$streaming || {};
|
||||
streaming.enabled = true; // Marcar como streaming activo
|
||||
|
||||
// 🔧 DEBUG: Ver qué opciones estamos recibiendo
|
||||
console.log('📈 RealTimeScale DEBUG - scaleOptions:', scaleOptions);
|
||||
console.log('📈 RealTimeScale DEBUG - me.options:', me.options);
|
||||
console.log('📈 RealTimeScale DEBUG - me.options.realtime:', me.options.realtime);
|
||||
|
||||
// Inicializar opciones de tiempo real
|
||||
const onRefreshResolved = resolveOption(me, 'onRefresh');
|
||||
console.log('📈 RealTimeScale DEBUG - onRefresh resolved:', onRefreshResolved, typeof onRefreshResolved);
|
||||
|
||||
me.realtime = {
|
||||
duration: resolveOption(me, 'duration'),
|
||||
delay: resolveOption(me, 'delay'),
|
||||
refresh: resolveOption(me, 'refresh'),
|
||||
frameRate: resolveOption(me, 'frameRate'),
|
||||
pause: resolveOption(me, 'pause'),
|
||||
ttl: resolveOption(me, 'ttl'),
|
||||
onRefresh: onRefreshResolved
|
||||
};
|
||||
|
||||
console.log('📈 RealTimeScale initialized:', {
|
||||
duration: me.realtime.duration,
|
||||
refresh: me.realtime.refresh,
|
||||
pause: me.realtime.pause,
|
||||
hasOnRefresh: typeof me.realtime.onRefresh === 'function'
|
||||
});
|
||||
|
||||
// Configurar intervalo de actualización
|
||||
if (!streaming.intervalId && me.realtime.refresh > 0) {
|
||||
streaming.intervalId = setInterval(() => {
|
||||
if (!me.realtime.pause && typeof me.realtime.onRefresh === 'function') {
|
||||
me.realtime.onRefresh(chart);
|
||||
}
|
||||
me.updateRealTimeData();
|
||||
chart.update('quiet');
|
||||
}, me.realtime.refresh);
|
||||
|
||||
console.log('📈 RealTimeScale interval started:', me.realtime.refresh + 'ms');
|
||||
}
|
||||
}
|
||||
|
||||
updateRealTimeData() {
|
||||
const me = this;
|
||||
const chart = me.chart;
|
||||
|
||||
if (!chart.data || !chart.data.datasets) {
|
||||
return;
|
||||
}
|
||||
|
||||
const now = Date.now();
|
||||
const duration = me.realtime.duration;
|
||||
const delay = me.realtime.delay;
|
||||
const ttl = me.realtime.ttl || duration * 2; // TTL por defecto
|
||||
|
||||
// Calcular ventana de tiempo
|
||||
me.max = now - delay;
|
||||
me.min = me.max - duration;
|
||||
|
||||
// Limpiar datos antiguos automáticamente
|
||||
const cutoff = now - ttl;
|
||||
chart.data.datasets.forEach(dataset => {
|
||||
if (dataset.data) {
|
||||
const oldLength = dataset.data.length;
|
||||
dataset.data = dataset.data.filter(point => point.x > cutoff);
|
||||
if (oldLength !== dataset.data.length) {
|
||||
console.log(`📈 Cleaned ${oldLength - dataset.data.length} old points from ${dataset.label}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
update(args) {
|
||||
this.updateRealTimeData();
|
||||
super.update(args);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
const me = this;
|
||||
const chart = me.chart;
|
||||
const streaming = chart.$streaming;
|
||||
|
||||
if (streaming && streaming.intervalId) {
|
||||
clearInterval(streaming.intervalId);
|
||||
delete streaming.intervalId;
|
||||
console.log('📈 RealTimeScale interval cleared');
|
||||
}
|
||||
|
||||
super.destroy();
|
||||
}
|
||||
|
||||
static id = 'realtime';
|
||||
static defaults = {
|
||||
realtime: {
|
||||
duration: 10000,
|
||||
delay: 0,
|
||||
refresh: 1000,
|
||||
frameRate: 30,
|
||||
pause: false,
|
||||
ttl: undefined,
|
||||
onRefresh: null
|
||||
},
|
||||
time: {
|
||||
unit: 'second',
|
||||
displayFormats: {
|
||||
second: 'HH:mm:ss'
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// ============= PLUGIN.STREAMING.JS (Simplificado) =============
|
||||
const streamingPlugin = {
|
||||
id: 'streaming',
|
||||
|
||||
beforeInit(chart) {
|
||||
const streaming = chart.$streaming = chart.$streaming || {};
|
||||
streaming.enabled = false;
|
||||
|
||||
// Detectar si hay escalas realtime
|
||||
const scales = chart.options.scales || {};
|
||||
Object.keys(scales).forEach(scaleId => {
|
||||
if (scales[scaleId].type === 'realtime') {
|
||||
streaming.enabled = true;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
afterInit(chart) {
|
||||
const streaming = chart.$streaming;
|
||||
if (streaming && streaming.enabled) {
|
||||
// Configurar actualización automática
|
||||
const update = chart.update;
|
||||
chart.update = function (mode) {
|
||||
if (mode === 'quiet') {
|
||||
// Actualización silenciosa para streaming
|
||||
Chart.prototype.update.call(this, mode);
|
||||
} else {
|
||||
update.call(this, mode);
|
||||
}
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
beforeUpdate(chart) {
|
||||
const streaming = chart.$streaming;
|
||||
if (!streaming || !streaming.enabled) return;
|
||||
|
||||
// Permitir que las líneas Bézier se extiendan fuera del área del gráfico
|
||||
const elements = chart.options.elements || {};
|
||||
if (elements.line) {
|
||||
elements.line.capBezierPoints = false;
|
||||
}
|
||||
},
|
||||
|
||||
destroy(chart) {
|
||||
const streaming = chart.$streaming;
|
||||
if (streaming && streaming.intervalId) {
|
||||
clearInterval(streaming.intervalId);
|
||||
delete streaming.intervalId;
|
||||
}
|
||||
delete chart.$streaming;
|
||||
}
|
||||
};
|
||||
|
||||
// ============= REGISTRO DE COMPONENTES =============
|
||||
|
||||
// Registrar escala realtime
|
||||
Chart.register(RealTimeScale);
|
||||
|
||||
// Registrar plugin de streaming
|
||||
Chart.register(streamingPlugin);
|
||||
|
||||
// ============= UTILIDADES PARA LA APLICACIÓN =============
|
||||
|
||||
/**
|
||||
* Crea una configuración de Chart.js optimizada para streaming
|
||||
*/
|
||||
function createStreamingChartConfig(options = {}) {
|
||||
const config = {
|
||||
type: 'line',
|
||||
data: {
|
||||
datasets: []
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
animation: false, // Desactivar animaciones para mejor performance
|
||||
|
||||
scales: {
|
||||
x: {
|
||||
type: 'realtime',
|
||||
realtime: {
|
||||
duration: options.duration || 60000, // 60 segundos por defecto
|
||||
delay: options.delay || 0,
|
||||
refresh: options.refresh || 1000, // 1 segundo
|
||||
frameRate: options.frameRate || 30,
|
||||
pause: options.pause || false,
|
||||
ttl: options.ttl || undefined,
|
||||
onRefresh: options.onRefresh || null
|
||||
},
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Tiempo'
|
||||
}
|
||||
},
|
||||
y: {
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Valor'
|
||||
},
|
||||
min: options.yMin,
|
||||
max: options.yMax
|
||||
}
|
||||
},
|
||||
|
||||
plugins: {
|
||||
legend: {
|
||||
display: true,
|
||||
position: 'top'
|
||||
},
|
||||
tooltip: {
|
||||
mode: 'index',
|
||||
intersect: false
|
||||
}
|
||||
},
|
||||
|
||||
interaction: {
|
||||
mode: 'nearest',
|
||||
axis: 'x',
|
||||
intersect: false
|
||||
},
|
||||
|
||||
elements: {
|
||||
point: {
|
||||
radius: 0, // Sin puntos para mejor performance
|
||||
hoverRadius: 3
|
||||
},
|
||||
line: {
|
||||
tension: 0.1,
|
||||
borderWidth: 2
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: ['streaming']
|
||||
};
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Agrega datos a un dataset de streaming
|
||||
*/
|
||||
function addStreamingData(chart, datasetIndex, data) {
|
||||
if (!chart || !chart.data || !chart.data.datasets[datasetIndex]) {
|
||||
console.warn(`📈 Cannot add streaming data - chart or dataset ${datasetIndex} not found`);
|
||||
return false;
|
||||
}
|
||||
|
||||
const dataset = chart.data.datasets[datasetIndex];
|
||||
if (!dataset.data) {
|
||||
dataset.data = [];
|
||||
}
|
||||
|
||||
// Agregar nuevo punto con timestamp
|
||||
const timestamp = data.x || Date.now();
|
||||
const newPoint = {
|
||||
x: timestamp,
|
||||
y: data.y
|
||||
};
|
||||
|
||||
dataset.data.push(newPoint);
|
||||
|
||||
console.log(`📈 Added point to dataset ${datasetIndex} (${dataset.label}): x=${timestamp}, y=${data.y}`);
|
||||
|
||||
// Chart.js se encarga automáticamente de eliminar datos antiguos
|
||||
// basado en la configuración de TTL y duration de la escala realtime
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Controla la pausa/reanudación del streaming
|
||||
*/
|
||||
function setStreamingPause(chart, paused) {
|
||||
if (!chart || !chart.$streaming) return;
|
||||
|
||||
const scales = chart.scales;
|
||||
Object.keys(scales).forEach(scaleId => {
|
||||
const scale = scales[scaleId];
|
||||
if (scale instanceof RealTimeScale) {
|
||||
scale.realtime.pause = paused;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Limpia todos los datos de streaming
|
||||
*/
|
||||
function clearStreamingData(chart) {
|
||||
if (!chart || !chart.data) return;
|
||||
|
||||
chart.data.datasets.forEach(dataset => {
|
||||
if (dataset.data) {
|
||||
dataset.data.length = 0;
|
||||
}
|
||||
});
|
||||
|
||||
chart.update('quiet');
|
||||
}
|
||||
|
||||
// ============= EXPORTS =============
|
||||
|
||||
// Exportar para uso en la aplicación
|
||||
exports.RealTimeScale = RealTimeScale;
|
||||
exports.streamingPlugin = streamingPlugin;
|
||||
exports.createStreamingChartConfig = createStreamingChartConfig;
|
||||
exports.addStreamingData = addStreamingData;
|
||||
exports.setStreamingPause = setStreamingPause;
|
||||
exports.clearStreamingData = clearStreamingData;
|
||||
|
||||
// Hacer disponible globalmente
|
||||
if (typeof window !== 'undefined') {
|
||||
window.ChartStreaming = {
|
||||
createStreamingChartConfig,
|
||||
addStreamingData,
|
||||
setStreamingPause,
|
||||
clearStreamingData,
|
||||
RealTimeScale,
|
||||
streamingPlugin
|
||||
};
|
||||
}
|
||||
|
||||
console.log('📈 Chart.js Streaming Plugin loaded successfully');
|
||||
});
|
|
@ -0,0 +1,85 @@
|
|||
import {callback as call, each, noop, requestAnimFrame, valueOrDefault} from 'chart.js/helpers';
|
||||
|
||||
export function clamp(value, lower, upper) {
|
||||
return Math.min(Math.max(value, lower), upper);
|
||||
}
|
||||
|
||||
export function resolveOption(scale, key) {
|
||||
const realtimeOpts = scale.options.realtime;
|
||||
const streamingOpts = scale.chart.options.plugins.streaming;
|
||||
return valueOrDefault(realtimeOpts[key], streamingOpts[key]);
|
||||
}
|
||||
|
||||
export function getAxisMap(element, {x, y}, {xAxisID, yAxisID}) {
|
||||
const axisMap = {};
|
||||
|
||||
each(x, key => {
|
||||
axisMap[key] = {axisId: xAxisID};
|
||||
});
|
||||
each(y, key => {
|
||||
axisMap[key] = {axisId: yAxisID};
|
||||
});
|
||||
return axisMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel animation polyfill
|
||||
*/
|
||||
const cancelAnimFrame = (function() {
|
||||
if (typeof window === 'undefined') {
|
||||
return noop;
|
||||
}
|
||||
return window.cancelAnimationFrame;
|
||||
}());
|
||||
|
||||
export function startFrameRefreshTimer(context, func) {
|
||||
if (!context.frameRequestID) {
|
||||
const refresh = () => {
|
||||
const nextRefresh = context.nextRefresh || 0;
|
||||
const now = Date.now();
|
||||
|
||||
if (nextRefresh <= now) {
|
||||
const newFrameRate = call(func);
|
||||
const frameDuration = 1000 / (Math.max(newFrameRate, 0) || 30);
|
||||
const newNextRefresh = context.nextRefresh + frameDuration || 0;
|
||||
|
||||
context.nextRefresh = newNextRefresh > now ? newNextRefresh : now + frameDuration;
|
||||
}
|
||||
context.frameRequestID = requestAnimFrame.call(window, refresh);
|
||||
};
|
||||
context.frameRequestID = requestAnimFrame.call(window, refresh);
|
||||
}
|
||||
}
|
||||
|
||||
export function stopFrameRefreshTimer(context) {
|
||||
const frameRequestID = context.frameRequestID;
|
||||
|
||||
if (frameRequestID) {
|
||||
cancelAnimFrame.call(window, frameRequestID);
|
||||
delete context.frameRequestID;
|
||||
}
|
||||
}
|
||||
|
||||
export function stopDataRefreshTimer(context) {
|
||||
const refreshTimerID = context.refreshTimerID;
|
||||
|
||||
if (refreshTimerID) {
|
||||
clearInterval(refreshTimerID);
|
||||
delete context.refreshTimerID;
|
||||
delete context.refreshInterval;
|
||||
}
|
||||
}
|
||||
|
||||
export function startDataRefreshTimer(context, func, interval) {
|
||||
if (!context.refreshTimerID) {
|
||||
context.refreshTimerID = setInterval(() => {
|
||||
const newInterval = call(func);
|
||||
|
||||
if (context.refreshInterval !== newInterval && !isNaN(newInterval)) {
|
||||
stopDataRefreshTimer(context);
|
||||
startDataRefreshTimer(context, func, newInterval);
|
||||
}
|
||||
}, interval || 0);
|
||||
context.refreshInterval = interval || 0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,216 @@
|
|||
import {Chart, DatasetController, defaults, registry} from 'chart.js';
|
||||
import {each, noop, getRelativePosition, clipArea, unclipArea} from 'chart.js/helpers';
|
||||
import {getAxisMap} from '../helpers/helpers.streaming';
|
||||
import {attachChart as annotationAttachChart, detachChart as annotationDetachChart} from '../plugins/plugin.annotation';
|
||||
import {update as tooltipUpdate} from '../plugins/plugin.tooltip';
|
||||
import {attachChart as zoomAttachChart, detachChart as zoomDetachChart} from '../plugins/plugin.zoom';
|
||||
import RealTimeScale from '../scales/scale.realtime';
|
||||
import {version} from '../../package.json';
|
||||
|
||||
defaults.set('transitions', {
|
||||
quiet: {
|
||||
animation: {
|
||||
duration: 0
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const transitionKeys = {x: ['x', 'cp1x', 'cp2x'], y: ['y', 'cp1y', 'cp2y']};
|
||||
|
||||
function update(mode) {
|
||||
const me = this;
|
||||
|
||||
if (mode === 'quiet') {
|
||||
each(me.data.datasets, (dataset, datasetIndex) => {
|
||||
const controller = me.getDatasetMeta(datasetIndex).controller;
|
||||
|
||||
// Set transition mode to 'quiet'
|
||||
controller._setStyle = function(element, index, _mode, active) {
|
||||
DatasetController.prototype._setStyle.call(this, element, index, 'quiet', active);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
Chart.prototype.update.call(me, mode);
|
||||
|
||||
if (mode === 'quiet') {
|
||||
each(me.data.datasets, (dataset, datasetIndex) => {
|
||||
delete me.getDatasetMeta(datasetIndex).controller._setStyle;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function render(chart) {
|
||||
const streaming = chart.$streaming;
|
||||
|
||||
chart.render();
|
||||
|
||||
if (streaming.lastMouseEvent) {
|
||||
setTimeout(() => {
|
||||
const lastMouseEvent = streaming.lastMouseEvent;
|
||||
if (lastMouseEvent) {
|
||||
chart._eventHandler(lastMouseEvent);
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
id: 'streaming',
|
||||
|
||||
version,
|
||||
|
||||
beforeInit(chart) {
|
||||
const streaming = chart.$streaming = chart.$streaming || {render};
|
||||
const canvas = streaming.canvas = chart.canvas;
|
||||
const mouseEventListener = streaming.mouseEventListener = event => {
|
||||
const pos = getRelativePosition(event, chart);
|
||||
streaming.lastMouseEvent = {
|
||||
type: 'mousemove',
|
||||
chart: chart,
|
||||
native: event,
|
||||
x: pos.x,
|
||||
y: pos.y
|
||||
};
|
||||
};
|
||||
|
||||
canvas.addEventListener('mousedown', mouseEventListener);
|
||||
canvas.addEventListener('mouseup', mouseEventListener);
|
||||
},
|
||||
|
||||
afterInit(chart) {
|
||||
chart.update = update;
|
||||
},
|
||||
|
||||
beforeUpdate(chart) {
|
||||
const {scales, elements} = chart.options;
|
||||
const tooltip = chart.tooltip;
|
||||
|
||||
each(scales, ({type}) => {
|
||||
if (type === 'realtime') {
|
||||
// Allow Bézier control to be outside the chart
|
||||
elements.line.capBezierPoints = false;
|
||||
}
|
||||
});
|
||||
|
||||
if (tooltip) {
|
||||
tooltip.update = tooltipUpdate;
|
||||
}
|
||||
|
||||
try {
|
||||
const plugin = registry.getPlugin('annotation');
|
||||
annotationAttachChart(plugin, chart);
|
||||
} catch (e) {
|
||||
annotationDetachChart(chart);
|
||||
}
|
||||
|
||||
try {
|
||||
const plugin = registry.getPlugin('zoom');
|
||||
zoomAttachChart(plugin, chart);
|
||||
} catch (e) {
|
||||
zoomDetachChart(chart);
|
||||
}
|
||||
},
|
||||
|
||||
beforeDatasetUpdate(chart, args) {
|
||||
const {meta, mode} = args;
|
||||
|
||||
if (mode === 'quiet') {
|
||||
const {controller, $animations} = meta;
|
||||
|
||||
// Skip updating element options if show/hide transition is active
|
||||
if ($animations && $animations.visible && $animations.visible._active) {
|
||||
controller.updateElement = noop;
|
||||
controller.updateSharedOptions = noop;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
afterDatasetUpdate(chart, args) {
|
||||
const {meta, mode} = args;
|
||||
const {data: elements = [], dataset: element, controller} = meta;
|
||||
|
||||
for (let i = 0, ilen = elements.length; i < ilen; ++i) {
|
||||
elements[i].$streaming = getAxisMap(elements[i], transitionKeys, meta);
|
||||
}
|
||||
if (element) {
|
||||
element.$streaming = getAxisMap(element, transitionKeys, meta);
|
||||
}
|
||||
|
||||
if (mode === 'quiet') {
|
||||
delete controller.updateElement;
|
||||
delete controller.updateSharedOptions;
|
||||
}
|
||||
},
|
||||
|
||||
beforeDatasetDraw(chart, args) {
|
||||
const {ctx, chartArea, width, height} = chart;
|
||||
const {xAxisID, yAxisID, controller} = args.meta;
|
||||
const area = {
|
||||
left: 0,
|
||||
top: 0,
|
||||
right: width,
|
||||
bottom: height
|
||||
};
|
||||
|
||||
if (xAxisID && controller.getScaleForId(xAxisID) instanceof RealTimeScale) {
|
||||
area.left = chartArea.left;
|
||||
area.right = chartArea.right;
|
||||
}
|
||||
if (yAxisID && controller.getScaleForId(yAxisID) instanceof RealTimeScale) {
|
||||
area.top = chartArea.top;
|
||||
area.bottom = chartArea.bottom;
|
||||
}
|
||||
clipArea(ctx, area);
|
||||
},
|
||||
|
||||
afterDatasetDraw(chart) {
|
||||
unclipArea(chart.ctx);
|
||||
},
|
||||
|
||||
beforeEvent(chart, args) {
|
||||
const streaming = chart.$streaming;
|
||||
const event = args.event;
|
||||
|
||||
if (event.type === 'mousemove') {
|
||||
// Save mousemove event for reuse
|
||||
streaming.lastMouseEvent = event;
|
||||
} else if (event.type === 'mouseout') {
|
||||
// Remove mousemove event
|
||||
delete streaming.lastMouseEvent;
|
||||
}
|
||||
},
|
||||
|
||||
destroy(chart) {
|
||||
const {scales, $streaming: streaming, tooltip} = chart;
|
||||
const {canvas, mouseEventListener} = streaming;
|
||||
|
||||
delete chart.update;
|
||||
if (tooltip) {
|
||||
delete tooltip.update;
|
||||
}
|
||||
|
||||
canvas.removeEventListener('mousedown', mouseEventListener);
|
||||
canvas.removeEventListener('mouseup', mouseEventListener);
|
||||
|
||||
each(scales, scale => {
|
||||
if (scale instanceof RealTimeScale) {
|
||||
scale.destroy();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
defaults: {
|
||||
duration: 10000,
|
||||
delay: 0,
|
||||
frameRate: 30,
|
||||
refresh: 1000,
|
||||
onRefresh: null,
|
||||
pause: false,
|
||||
ttl: undefined
|
||||
},
|
||||
|
||||
descriptors: {
|
||||
_scriptable: name => name !== 'onRefresh'
|
||||
}
|
||||
};
|
|
@ -0,0 +1,125 @@
|
|||
import {each} from 'chart.js/helpers';
|
||||
import {clamp, resolveOption} from '../helpers/helpers.streaming';
|
||||
|
||||
const chartStates = new WeakMap();
|
||||
|
||||
function getState(chart) {
|
||||
let state = chartStates.get(chart);
|
||||
|
||||
if (!state) {
|
||||
state = {originalScaleOptions: {}};
|
||||
chartStates.set(chart, state);
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
function removeState(chart) {
|
||||
chartStates.delete(chart);
|
||||
}
|
||||
|
||||
function storeOriginalScaleOptions(chart) {
|
||||
const {originalScaleOptions} = getState(chart);
|
||||
const scales = chart.scales;
|
||||
|
||||
each(scales, scale => {
|
||||
const id = scale.id;
|
||||
|
||||
if (!originalScaleOptions[id]) {
|
||||
originalScaleOptions[id] = {
|
||||
duration: resolveOption(scale, 'duration'),
|
||||
delay: resolveOption(scale, 'delay')
|
||||
};
|
||||
}
|
||||
});
|
||||
each(originalScaleOptions, (opt, key) => {
|
||||
if (!scales[key]) {
|
||||
delete originalScaleOptions[key];
|
||||
}
|
||||
});
|
||||
return originalScaleOptions;
|
||||
}
|
||||
|
||||
function zoomRealTimeScale(scale, zoom, center, limits) {
|
||||
const {chart, axis} = scale;
|
||||
const {minDuration = 0, maxDuration = Infinity, minDelay = -Infinity, maxDelay = Infinity} = limits && limits[axis] || {};
|
||||
const realtimeOpts = scale.options.realtime;
|
||||
const duration = resolveOption(scale, 'duration');
|
||||
const delay = resolveOption(scale, 'delay');
|
||||
const newDuration = clamp(duration * (2 - zoom), minDuration, maxDuration);
|
||||
let maxPercent, newDelay;
|
||||
|
||||
storeOriginalScaleOptions(chart);
|
||||
|
||||
if (scale.isHorizontal()) {
|
||||
maxPercent = (scale.right - center.x) / (scale.right - scale.left);
|
||||
} else {
|
||||
maxPercent = (scale.bottom - center.y) / (scale.bottom - scale.top);
|
||||
}
|
||||
newDelay = delay + maxPercent * (duration - newDuration);
|
||||
realtimeOpts.duration = newDuration;
|
||||
realtimeOpts.delay = clamp(newDelay, minDelay, maxDelay);
|
||||
return newDuration !== scale.max - scale.min;
|
||||
}
|
||||
|
||||
function panRealTimeScale(scale, delta, limits) {
|
||||
const {chart, axis} = scale;
|
||||
const {minDelay = -Infinity, maxDelay = Infinity} = limits && limits[axis] || {};
|
||||
const delay = resolveOption(scale, 'delay');
|
||||
const newDelay = delay + (scale.getValueForPixel(delta) - scale.getValueForPixel(0));
|
||||
|
||||
storeOriginalScaleOptions(chart);
|
||||
|
||||
scale.options.realtime.delay = clamp(newDelay, minDelay, maxDelay);
|
||||
return true;
|
||||
}
|
||||
|
||||
function resetRealTimeScaleOptions(chart) {
|
||||
const originalScaleOptions = storeOriginalScaleOptions(chart);
|
||||
|
||||
each(chart.scales, scale => {
|
||||
const realtimeOptions = scale.options.realtime;
|
||||
|
||||
if (realtimeOptions) {
|
||||
const original = originalScaleOptions[scale.id];
|
||||
|
||||
if (original) {
|
||||
realtimeOptions.duration = original.duration;
|
||||
realtimeOptions.delay = original.delay;
|
||||
} else {
|
||||
delete realtimeOptions.duration;
|
||||
delete realtimeOptions.delay;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function initZoomPlugin(plugin) {
|
||||
plugin.zoomFunctions.realtime = zoomRealTimeScale;
|
||||
plugin.panFunctions.realtime = panRealTimeScale;
|
||||
}
|
||||
|
||||
export function attachChart(plugin, chart) {
|
||||
const streaming = chart.$streaming;
|
||||
|
||||
if (streaming.zoomPlugin !== plugin) {
|
||||
const resetZoom = streaming.resetZoom = chart.resetZoom;
|
||||
|
||||
initZoomPlugin(plugin);
|
||||
chart.resetZoom = transition => {
|
||||
resetRealTimeScaleOptions(chart);
|
||||
resetZoom(transition);
|
||||
};
|
||||
streaming.zoomPlugin = plugin;
|
||||
}
|
||||
}
|
||||
|
||||
export function detachChart(chart) {
|
||||
const streaming = chart.$streaming;
|
||||
|
||||
if (streaming.zoomPlugin) {
|
||||
chart.resetZoom = streaming.resetZoom;
|
||||
removeState(chart);
|
||||
delete streaming.resetZoom;
|
||||
delete streaming.zoomPlugin;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,507 @@
|
|||
import {defaults, TimeScale} from 'chart.js';
|
||||
import {_lookup, callback as call, each, isArray, isFinite, isNumber, noop, clipArea, unclipArea} from 'chart.js/helpers';
|
||||
import {resolveOption, startFrameRefreshTimer, stopFrameRefreshTimer, startDataRefreshTimer, stopDataRefreshTimer} from '../helpers/helpers.streaming';
|
||||
import {getElements} from '../plugins/plugin.annotation';
|
||||
|
||||
// Ported from Chart.js 2.8.0 35273ee.
|
||||
const INTERVALS = {
|
||||
millisecond: {
|
||||
common: true,
|
||||
size: 1,
|
||||
steps: [1, 2, 5, 10, 20, 50, 100, 250, 500]
|
||||
},
|
||||
second: {
|
||||
common: true,
|
||||
size: 1000,
|
||||
steps: [1, 2, 5, 10, 15, 30]
|
||||
},
|
||||
minute: {
|
||||
common: true,
|
||||
size: 60000,
|
||||
steps: [1, 2, 5, 10, 15, 30]
|
||||
},
|
||||
hour: {
|
||||
common: true,
|
||||
size: 3600000,
|
||||
steps: [1, 2, 3, 6, 12]
|
||||
},
|
||||
day: {
|
||||
common: true,
|
||||
size: 86400000,
|
||||
steps: [1, 2, 5]
|
||||
},
|
||||
week: {
|
||||
common: false,
|
||||
size: 604800000,
|
||||
steps: [1, 2, 3, 4]
|
||||
},
|
||||
month: {
|
||||
common: true,
|
||||
size: 2.628e9,
|
||||
steps: [1, 2, 3]
|
||||
},
|
||||
quarter: {
|
||||
common: false,
|
||||
size: 7.884e9,
|
||||
steps: [1, 2, 3, 4]
|
||||
},
|
||||
year: {
|
||||
common: true,
|
||||
size: 3.154e10
|
||||
}
|
||||
};
|
||||
|
||||
// Ported from Chart.js 2.8.0 35273ee.
|
||||
const UNITS = Object.keys(INTERVALS);
|
||||
|
||||
// Ported from Chart.js 2.8.0 35273ee.
|
||||
function determineStepSize(min, max, unit, capacity) {
|
||||
const range = max - min;
|
||||
const {size: milliseconds, steps} = INTERVALS[unit];
|
||||
let factor;
|
||||
|
||||
if (!steps) {
|
||||
return Math.ceil(range / (capacity * milliseconds));
|
||||
}
|
||||
|
||||
for (let i = 0, ilen = steps.length; i < ilen; ++i) {
|
||||
factor = steps[i];
|
||||
if (Math.ceil(range / (milliseconds * factor)) <= capacity) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return factor;
|
||||
}
|
||||
|
||||
// Ported from Chart.js 2.8.0 35273ee.
|
||||
function determineUnitForAutoTicks(minUnit, min, max, capacity) {
|
||||
const range = max - min;
|
||||
const ilen = UNITS.length;
|
||||
|
||||
for (let i = UNITS.indexOf(minUnit); i < ilen - 1; ++i) {
|
||||
const {common, size, steps} = INTERVALS[UNITS[i]];
|
||||
const factor = steps ? steps[steps.length - 1] : Number.MAX_SAFE_INTEGER;
|
||||
|
||||
if (common && Math.ceil(range / (factor * size)) <= capacity) {
|
||||
return UNITS[i];
|
||||
}
|
||||
}
|
||||
|
||||
return UNITS[ilen - 1];
|
||||
}
|
||||
|
||||
// Ported from Chart.js 2.8.0 35273ee.
|
||||
function determineMajorUnit(unit) {
|
||||
for (let i = UNITS.indexOf(unit) + 1, ilen = UNITS.length; i < ilen; ++i) {
|
||||
if (INTERVALS[UNITS[i]].common) {
|
||||
return UNITS[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ported from Chart.js 3.2.0 e1404ac.
|
||||
function addTick(ticks, time, timestamps) {
|
||||
if (!timestamps) {
|
||||
ticks[time] = true;
|
||||
} else if (timestamps.length) {
|
||||
const {lo, hi} = _lookup(timestamps, time);
|
||||
const timestamp = timestamps[lo] >= time ? timestamps[lo] : timestamps[hi];
|
||||
ticks[timestamp] = true;
|
||||
}
|
||||
}
|
||||
|
||||
const datasetPropertyKeys = [
|
||||
'pointBackgroundColor',
|
||||
'pointBorderColor',
|
||||
'pointBorderWidth',
|
||||
'pointRadius',
|
||||
'pointRotation',
|
||||
'pointStyle',
|
||||
'pointHitRadius',
|
||||
'pointHoverBackgroundColor',
|
||||
'pointHoverBorderColor',
|
||||
'pointHoverBorderWidth',
|
||||
'pointHoverRadius',
|
||||
'backgroundColor',
|
||||
'borderColor',
|
||||
'borderSkipped',
|
||||
'borderWidth',
|
||||
'hoverBackgroundColor',
|
||||
'hoverBorderColor',
|
||||
'hoverBorderWidth',
|
||||
'hoverRadius',
|
||||
'hitRadius',
|
||||
'radius',
|
||||
'rotation'
|
||||
];
|
||||
|
||||
function clean(scale) {
|
||||
const {chart, id, max} = scale;
|
||||
const duration = resolveOption(scale, 'duration');
|
||||
const delay = resolveOption(scale, 'delay');
|
||||
const ttl = resolveOption(scale, 'ttl');
|
||||
const pause = resolveOption(scale, 'pause');
|
||||
const min = Date.now() - (isNaN(ttl) ? duration + delay : ttl);
|
||||
let i, start, count, removalRange;
|
||||
|
||||
// Remove old data
|
||||
each(chart.data.datasets, (dataset, datasetIndex) => {
|
||||
const meta = chart.getDatasetMeta(datasetIndex);
|
||||
const axis = id === meta.xAxisID && 'x' || id === meta.yAxisID && 'y';
|
||||
|
||||
if (axis) {
|
||||
const controller = meta.controller;
|
||||
const data = dataset.data;
|
||||
const length = data.length;
|
||||
|
||||
if (pause) {
|
||||
// If the scale is paused, preserve the visible data points
|
||||
for (i = 0; i < length; ++i) {
|
||||
const point = controller.getParsed(i);
|
||||
if (point && !(point[axis] < max)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
start = i + 2;
|
||||
} else {
|
||||
start = 0;
|
||||
}
|
||||
|
||||
for (i = start; i < length; ++i) {
|
||||
const point = controller.getParsed(i);
|
||||
if (!point || !(point[axis] <= min)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
count = i - start;
|
||||
if (isNaN(ttl)) {
|
||||
// Keep the last two data points outside the range not to affect the existing bezier curve
|
||||
count = Math.max(count - 2, 0);
|
||||
}
|
||||
|
||||
data.splice(start, count);
|
||||
each(datasetPropertyKeys, key => {
|
||||
if (isArray(dataset[key])) {
|
||||
dataset[key].splice(start, count);
|
||||
}
|
||||
});
|
||||
each(dataset.datalabels, value => {
|
||||
if (isArray(value)) {
|
||||
value.splice(start, count);
|
||||
}
|
||||
});
|
||||
if (typeof data[0] !== 'object') {
|
||||
removalRange = {
|
||||
start: start,
|
||||
count: count
|
||||
};
|
||||
}
|
||||
|
||||
each(chart._active, (item, index) => {
|
||||
if (item.datasetIndex === datasetIndex && item.index >= start) {
|
||||
if (item.index >= start + count) {
|
||||
item.index -= count;
|
||||
} else {
|
||||
chart._active.splice(index, 1);
|
||||
}
|
||||
}
|
||||
}, null, true);
|
||||
}
|
||||
});
|
||||
if (removalRange) {
|
||||
chart.data.labels.splice(removalRange.start, removalRange.count);
|
||||
}
|
||||
}
|
||||
|
||||
function transition(element, id, translate) {
|
||||
const animations = element.$animations || {};
|
||||
|
||||
each(element.$streaming, (item, key) => {
|
||||
if (item.axisId === id) {
|
||||
const delta = item.reverse ? -translate : translate;
|
||||
const animation = animations[key];
|
||||
|
||||
if (isFinite(element[key])) {
|
||||
element[key] -= delta;
|
||||
}
|
||||
if (animation) {
|
||||
animation._from -= delta;
|
||||
animation._to -= delta;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function scroll(scale) {
|
||||
const {chart, id, $realtime: realtime} = scale;
|
||||
const duration = resolveOption(scale, 'duration');
|
||||
const delay = resolveOption(scale, 'delay');
|
||||
const isHorizontal = scale.isHorizontal();
|
||||
const length = isHorizontal ? scale.width : scale.height;
|
||||
const now = Date.now();
|
||||
const tooltip = chart.tooltip;
|
||||
const annotations = getElements(chart);
|
||||
let offset = length * (now - realtime.head) / duration;
|
||||
|
||||
if (isHorizontal === !!scale.options.reverse) {
|
||||
offset = -offset;
|
||||
}
|
||||
|
||||
// Shift all the elements leftward or downward
|
||||
each(chart.data.datasets, (dataset, datasetIndex) => {
|
||||
const meta = chart.getDatasetMeta(datasetIndex);
|
||||
const {data: elements = [], dataset: element} = meta;
|
||||
|
||||
for (let i = 0, ilen = elements.length; i < ilen; ++i) {
|
||||
transition(elements[i], id, offset);
|
||||
}
|
||||
if (element) {
|
||||
transition(element, id, offset);
|
||||
delete element._path;
|
||||
}
|
||||
});
|
||||
|
||||
// Shift all the annotation elements leftward or downward
|
||||
for (let i = 0, ilen = annotations.length; i < ilen; ++i) {
|
||||
transition(annotations[i], id, offset);
|
||||
}
|
||||
|
||||
// Shift tooltip leftward or downward
|
||||
if (tooltip) {
|
||||
transition(tooltip, id, offset);
|
||||
}
|
||||
|
||||
scale.max = now - delay;
|
||||
scale.min = scale.max - duration;
|
||||
|
||||
realtime.head = now;
|
||||
}
|
||||
|
||||
export default class RealTimeScale extends TimeScale {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.$realtime = this.$realtime || {};
|
||||
}
|
||||
|
||||
init(scaleOpts, opts) {
|
||||
const me = this;
|
||||
|
||||
super.init(scaleOpts, opts);
|
||||
startDataRefreshTimer(me.$realtime, () => {
|
||||
const chart = me.chart;
|
||||
const onRefresh = resolveOption(me, 'onRefresh');
|
||||
|
||||
call(onRefresh, [chart], me);
|
||||
clean(me);
|
||||
chart.update('quiet');
|
||||
return resolveOption(me, 'refresh');
|
||||
});
|
||||
}
|
||||
|
||||
update(maxWidth, maxHeight, margins) {
|
||||
const me = this;
|
||||
const {$realtime: realtime, options} = me;
|
||||
const {bounds, offset, ticks: ticksOpts} = options;
|
||||
const {autoSkip, source, major: majorTicksOpts} = ticksOpts;
|
||||
const majorEnabled = majorTicksOpts.enabled;
|
||||
|
||||
if (resolveOption(me, 'pause')) {
|
||||
stopFrameRefreshTimer(realtime);
|
||||
} else {
|
||||
if (!realtime.frameRequestID) {
|
||||
realtime.head = Date.now();
|
||||
}
|
||||
startFrameRefreshTimer(realtime, () => {
|
||||
const chart = me.chart;
|
||||
const streaming = chart.$streaming;
|
||||
|
||||
scroll(me);
|
||||
if (streaming) {
|
||||
call(streaming.render, [chart]);
|
||||
}
|
||||
return resolveOption(me, 'frameRate');
|
||||
});
|
||||
}
|
||||
|
||||
options.bounds = undefined;
|
||||
options.offset = false;
|
||||
ticksOpts.autoSkip = false;
|
||||
ticksOpts.source = source === 'auto' ? '' : source;
|
||||
majorTicksOpts.enabled = true;
|
||||
|
||||
super.update(maxWidth, maxHeight, margins);
|
||||
|
||||
options.bounds = bounds;
|
||||
options.offset = offset;
|
||||
ticksOpts.autoSkip = autoSkip;
|
||||
ticksOpts.source = source;
|
||||
majorTicksOpts.enabled = majorEnabled;
|
||||
}
|
||||
|
||||
buildTicks() {
|
||||
const me = this;
|
||||
const duration = resolveOption(me, 'duration');
|
||||
const delay = resolveOption(me, 'delay');
|
||||
const max = me.$realtime.head - delay;
|
||||
const min = max - duration;
|
||||
const maxArray = [1e15, max];
|
||||
const minArray = [-1e15, min];
|
||||
|
||||
Object.defineProperty(me, 'min', {
|
||||
get: () => minArray.shift(),
|
||||
set: noop
|
||||
});
|
||||
Object.defineProperty(me, 'max', {
|
||||
get: () => maxArray.shift(),
|
||||
set: noop
|
||||
});
|
||||
|
||||
const ticks = super.buildTicks();
|
||||
|
||||
delete me.min;
|
||||
delete me.max;
|
||||
me.min = min;
|
||||
me.max = max;
|
||||
|
||||
return ticks;
|
||||
}
|
||||
|
||||
calculateLabelRotation() {
|
||||
const ticksOpts = this.options.ticks;
|
||||
const maxRotation = ticksOpts.maxRotation;
|
||||
|
||||
ticksOpts.maxRotation = ticksOpts.minRotation || 0;
|
||||
super.calculateLabelRotation();
|
||||
ticksOpts.maxRotation = maxRotation;
|
||||
}
|
||||
|
||||
fit() {
|
||||
const me = this;
|
||||
const options = me.options;
|
||||
|
||||
super.fit();
|
||||
|
||||
if (options.ticks.display && options.display && me.isHorizontal()) {
|
||||
me.paddingLeft = 3;
|
||||
me.paddingRight = 3;
|
||||
me._handleMargins();
|
||||
}
|
||||
}
|
||||
|
||||
draw(chartArea) {
|
||||
const me = this;
|
||||
const {chart, ctx} = me;
|
||||
const area = me.isHorizontal() ?
|
||||
{
|
||||
left: chartArea.left,
|
||||
top: 0,
|
||||
right: chartArea.right,
|
||||
bottom: chart.height
|
||||
} : {
|
||||
left: 0,
|
||||
top: chartArea.top,
|
||||
right: chart.width,
|
||||
bottom: chartArea.bottom
|
||||
};
|
||||
|
||||
me._gridLineItems = null;
|
||||
me._labelItems = null;
|
||||
|
||||
// Clip and draw the scale
|
||||
clipArea(ctx, area);
|
||||
super.draw(chartArea);
|
||||
unclipArea(ctx);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
const realtime = this.$realtime;
|
||||
|
||||
stopFrameRefreshTimer(realtime);
|
||||
stopDataRefreshTimer(realtime);
|
||||
}
|
||||
|
||||
_generate() {
|
||||
const me = this;
|
||||
const adapter = me._adapter;
|
||||
const duration = resolveOption(me, 'duration');
|
||||
const delay = resolveOption(me, 'delay');
|
||||
const refresh = resolveOption(me, 'refresh');
|
||||
const max = me.$realtime.head - delay;
|
||||
const min = max - duration;
|
||||
const capacity = me._getLabelCapacity(min);
|
||||
const {time: timeOpts, ticks: ticksOpts} = me.options;
|
||||
const minor = timeOpts.unit || determineUnitForAutoTicks(timeOpts.minUnit, min, max, capacity);
|
||||
const major = determineMajorUnit(minor);
|
||||
const stepSize = timeOpts.stepSize || determineStepSize(min, max, minor, capacity);
|
||||
const weekday = minor === 'week' ? timeOpts.isoWeekday : false;
|
||||
const majorTicksEnabled = ticksOpts.major.enabled;
|
||||
const hasWeekday = isNumber(weekday) || weekday === true;
|
||||
const interval = INTERVALS[minor];
|
||||
const ticks = {};
|
||||
let first = min;
|
||||
let time, count;
|
||||
|
||||
// For 'week' unit, handle the first day of week option
|
||||
if (hasWeekday) {
|
||||
first = +adapter.startOf(first, 'isoWeek', weekday);
|
||||
}
|
||||
|
||||
// Align first ticks on unit
|
||||
first = +adapter.startOf(first, hasWeekday ? 'day' : minor);
|
||||
|
||||
// Prevent browser from freezing in case user options request millions of milliseconds
|
||||
if (adapter.diff(max, min, minor) > 100000 * stepSize) {
|
||||
throw new Error(min + ' and ' + max + ' are too far apart with stepSize of ' + stepSize + ' ' + minor);
|
||||
}
|
||||
|
||||
time = first;
|
||||
|
||||
if (majorTicksEnabled && major && !hasWeekday && !timeOpts.round) {
|
||||
// Align the first tick on the previous `minor` unit aligned on the `major` unit:
|
||||
// we first aligned time on the previous `major` unit then add the number of full
|
||||
// stepSize there is between first and the previous major time.
|
||||
time = +adapter.startOf(time, major);
|
||||
time = +adapter.add(time, ~~((first - time) / (interval.size * stepSize)) * stepSize, minor);
|
||||
}
|
||||
|
||||
const timestamps = ticksOpts.source === 'data' && me.getDataTimestamps();
|
||||
for (count = 0; time < max + refresh; time = +adapter.add(time, stepSize, minor), count++) {
|
||||
addTick(ticks, time, timestamps);
|
||||
}
|
||||
|
||||
if (time === max + refresh || count === 1) {
|
||||
addTick(ticks, time, timestamps);
|
||||
}
|
||||
|
||||
return Object.keys(ticks).sort((a, b) => a - b).map(x => +x);
|
||||
}
|
||||
}
|
||||
|
||||
RealTimeScale.id = 'realtime';
|
||||
|
||||
RealTimeScale.defaults = {
|
||||
bounds: 'data',
|
||||
adapters: {},
|
||||
time: {
|
||||
parser: false, // false == a pattern string from or a custom callback that converts its argument to a timestamp
|
||||
unit: false, // false == automatic or override with week, month, year, etc.
|
||||
round: false, // none, or override with week, month, year, etc.
|
||||
isoWeekday: false, // override week start day - see http://momentjs.com/docs/#/get-set/iso-weekday/
|
||||
minUnit: 'millisecond',
|
||||
displayFormats: {}
|
||||
},
|
||||
realtime: {},
|
||||
ticks: {
|
||||
autoSkip: false,
|
||||
source: 'auto',
|
||||
major: {
|
||||
enabled: true
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
defaults.describe('scale.realtime', {
|
||||
_scriptable: name => name !== 'onRefresh'
|
||||
});
|
|
@ -0,0 +1,387 @@
|
|||
/**
|
||||
* 🔧 Script de Diagnóstico Detallado para Chart.js Streaming
|
||||
* Ejecutar en consola del navegador para identificar el problema exacto
|
||||
*/
|
||||
|
||||
// Función principal de diagnóstico
|
||||
window.diagnoseStreamingIssue = async function () {
|
||||
console.log('🔧 DIAGNÓSTICO DETALLADO DE STREAMING');
|
||||
console.log('='.repeat(60));
|
||||
|
||||
const results = {
|
||||
librariesLoaded: false,
|
||||
plotManagerReady: false,
|
||||
sessionExists: false,
|
||||
backendData: null,
|
||||
chartConfig: null,
|
||||
streamingWorking: false,
|
||||
errors: []
|
||||
};
|
||||
|
||||
try {
|
||||
// 1. Verificar librerías básicas
|
||||
console.log('\n1️⃣ VERIFICANDO LIBRERÍAS...');
|
||||
results.librariesLoaded = {
|
||||
chartjs: typeof Chart !== 'undefined',
|
||||
chartStreaming: typeof window.ChartStreaming !== 'undefined',
|
||||
plotManager: typeof plotManager !== 'undefined'
|
||||
};
|
||||
|
||||
console.log('Chart.js:', results.librariesLoaded.chartjs ? '✅' : '❌');
|
||||
console.log('ChartStreaming:', results.librariesLoaded.chartStreaming ? '✅' : '❌');
|
||||
console.log('PlotManager:', results.librariesLoaded.plotManager ? '✅' : '❌');
|
||||
|
||||
if (!results.librariesLoaded.chartStreaming) {
|
||||
results.errors.push('ChartStreaming plugin no está cargado');
|
||||
console.error('❌ ChartStreaming plugin no está disponible');
|
||||
return results;
|
||||
}
|
||||
|
||||
// 2. Verificar PlotManager
|
||||
console.log('\n2️⃣ VERIFICANDO PLOT MANAGER...');
|
||||
const pm = window.plotManager || plotManager;
|
||||
if (pm && pm.sessions) {
|
||||
results.plotManagerReady = true;
|
||||
console.log('✅ PlotManager inicializado');
|
||||
console.log('📊 Sesiones activas:', pm.sessions.size);
|
||||
|
||||
if (pm.sessions.size === 0) {
|
||||
console.log('⚠️ No hay sesiones de plot activas');
|
||||
console.log('💡 Crea un nuevo plot para continuar el diagnóstico');
|
||||
return results;
|
||||
}
|
||||
} else {
|
||||
results.errors.push('PlotManager no está inicializado');
|
||||
console.error('❌ PlotManager no está disponible');
|
||||
console.log('💡 Tip: Espera unos segundos e intenta de nuevo, plotManager podría estar inicializándose');
|
||||
return results;
|
||||
}
|
||||
|
||||
// 3. Verificar sesión específica
|
||||
console.log('\n3️⃣ ANALIZANDO SESIÓN DE PLOT...');
|
||||
const sessionId = Array.from(pm.sessions.keys())[0];
|
||||
const sessionData = pm.sessions.get(sessionId);
|
||||
|
||||
if (sessionData) {
|
||||
results.sessionExists = true;
|
||||
console.log(`📈 Sesión encontrada: ${sessionId}`);
|
||||
|
||||
// Verificar configuración del chart
|
||||
results.chartConfig = {
|
||||
hasChart: !!sessionData.chart,
|
||||
chartType: sessionData.chart?.config?.type,
|
||||
scaleType: sessionData.chart?.scales?.x?.type,
|
||||
scaleConstructor: sessionData.chart?.scales?.x?.constructor?.name,
|
||||
streamingEnabled: !!sessionData.chart?.$streaming?.enabled,
|
||||
datasets: sessionData.chart?.data?.datasets?.length || 0,
|
||||
dataPoints: sessionData.chart?.data?.datasets?.reduce((total, d) => total + (d.data?.length || 0), 0) || 0
|
||||
};
|
||||
|
||||
console.log('Chart Config:', results.chartConfig);
|
||||
|
||||
// Verificar si la escala es realtime
|
||||
if (results.chartConfig.scaleType !== 'realtime') {
|
||||
results.errors.push('La escala X no es de tipo realtime');
|
||||
console.error('❌ Escala X no es realtime:', results.chartConfig.scaleType);
|
||||
} else {
|
||||
console.log('✅ Escala realtime configurada correctamente');
|
||||
}
|
||||
|
||||
// Verificar streaming
|
||||
if (!results.chartConfig.streamingEnabled) {
|
||||
results.errors.push('Streaming no está habilitado en el chart');
|
||||
console.error('❌ Streaming no está habilitado');
|
||||
} else {
|
||||
console.log('✅ Streaming habilitado en el chart');
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Verificar datos del backend
|
||||
console.log('\n4️⃣ VERIFICANDO DATOS DEL BACKEND...');
|
||||
try {
|
||||
const response = await fetch(`/api/plots/${sessionId}/data`);
|
||||
results.backendData = await response.json();
|
||||
|
||||
console.log('📊 Respuesta del backend:', {
|
||||
success: !!results.backendData.datasets,
|
||||
datasets: results.backendData.datasets?.length || 0,
|
||||
totalPoints: results.backendData.data_points_count || 0,
|
||||
isActive: results.backendData.is_active,
|
||||
isPaused: results.backendData.is_paused
|
||||
});
|
||||
|
||||
if (results.backendData.datasets && results.backendData.datasets.length > 0) {
|
||||
console.log('✅ Backend devuelve datos');
|
||||
|
||||
// Verificar estructura de datos
|
||||
const firstDataset = results.backendData.datasets[0];
|
||||
console.log('📈 Primer dataset:', {
|
||||
label: firstDataset.label,
|
||||
dataPoints: firstDataset.data?.length || 0,
|
||||
samplePoint: firstDataset.data?.[0]
|
||||
});
|
||||
|
||||
if (firstDataset.data && firstDataset.data.length > 0) {
|
||||
console.log('✅ Dataset tiene puntos de datos');
|
||||
|
||||
// Verificar formato de timestamps
|
||||
const firstPoint = firstDataset.data[0];
|
||||
const currentTime = Date.now();
|
||||
const timeDiff = Math.abs(currentTime - firstPoint.x);
|
||||
|
||||
console.log('⏰ Análisis de timestamps:', {
|
||||
firstPointTime: firstPoint.x,
|
||||
currentTime: currentTime,
|
||||
differenceMs: timeDiff,
|
||||
differenceSec: Math.round(timeDiff / 1000),
|
||||
isRecent: timeDiff < 300000 // 5 minutos
|
||||
});
|
||||
|
||||
if (timeDiff > 300000) {
|
||||
results.errors.push('Los timestamps de los datos son muy antiguos');
|
||||
console.warn('⚠️ Los datos parecen ser muy antiguos');
|
||||
}
|
||||
} else {
|
||||
results.errors.push('El dataset no tiene puntos de datos');
|
||||
console.error('❌ El dataset está vacío');
|
||||
}
|
||||
} else {
|
||||
results.errors.push('Backend no devuelve datasets');
|
||||
console.error('❌ Backend no devuelve datos válidos');
|
||||
}
|
||||
} catch (error) {
|
||||
results.errors.push(`Error al obtener datos del backend: ${error.message}`);
|
||||
console.error('❌ Error del backend:', error);
|
||||
}
|
||||
|
||||
// 5. Test de funcionalidad streaming
|
||||
console.log('\n5️⃣ PROBANDO FUNCIONALIDAD STREAMING...');
|
||||
if (sessionData && sessionData.chart && results.backendData) {
|
||||
try {
|
||||
// Intentar agregar un punto de prueba
|
||||
const testResult = window.ChartStreaming.addStreamingData(sessionData.chart, 0, {
|
||||
x: Date.now(),
|
||||
y: Math.random() * 100
|
||||
});
|
||||
|
||||
console.log('🧪 Test de addStreamingData:', testResult ? '✅' : '❌');
|
||||
|
||||
if (testResult) {
|
||||
// Verificar si el punto se agregó
|
||||
const currentDataPoints = sessionData.chart.data.datasets[0]?.data?.length || 0;
|
||||
console.log('📊 Puntos después del test:', currentDataPoints);
|
||||
results.streamingWorking = currentDataPoints > 0;
|
||||
} else {
|
||||
results.errors.push('addStreamingData falló');
|
||||
}
|
||||
} catch (error) {
|
||||
results.errors.push(`Error en test de streaming: ${error.message}`);
|
||||
console.error('❌ Error en test de streaming:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// 6. Resumen final
|
||||
console.log('\n6️⃣ RESUMEN DEL DIAGNÓSTICO');
|
||||
console.log('='.repeat(40));
|
||||
|
||||
if (results.errors.length === 0) {
|
||||
console.log('🎉 No se encontraron errores graves');
|
||||
if (!results.streamingWorking) {
|
||||
console.log('⚠️ Sin embargo, el streaming no parece estar funcionando');
|
||||
console.log('💡 Intenta: forceStreamingUpdate()');
|
||||
}
|
||||
} else {
|
||||
console.log('❌ Errores encontrados:');
|
||||
results.errors.forEach((error, index) => {
|
||||
console.log(` ${index + 1}. ${error}`);
|
||||
});
|
||||
}
|
||||
|
||||
// 7. Recomendaciones
|
||||
console.log('\n7️⃣ PRÓXIMOS PASOS RECOMENDADOS:');
|
||||
|
||||
if (!results.librariesLoaded.chartStreaming) {
|
||||
console.log('🔧 1. Recargar la página para asegurar que el plugin se carga');
|
||||
}
|
||||
|
||||
if (results.errors.some(e => e.includes('timestamp'))) {
|
||||
console.log('🔧 2. Ejecutar clearStreamingData() y reiniciar el plot');
|
||||
}
|
||||
|
||||
if (results.backendData && !results.streamingWorking) {
|
||||
console.log('🔧 3. Ejecutar enablePlotDebug() y forceStreamingUpdate()');
|
||||
}
|
||||
|
||||
console.log('🔧 4. Si persiste: plotManager.controlPlot("' + sessionId + '", "stop") y luego "start"');
|
||||
|
||||
return results;
|
||||
|
||||
} catch (error) {
|
||||
console.error('💥 Error durante el diagnóstico:', error);
|
||||
results.errors.push(`Error fatal: ${error.message}`);
|
||||
return results;
|
||||
}
|
||||
};
|
||||
|
||||
// Función para limpiar y reiniciar streaming
|
||||
window.resetStreaming = function (sessionId = null) {
|
||||
console.log('🔄 REINICIANDO STREAMING...');
|
||||
|
||||
const pm = window.plotManager || plotManager;
|
||||
if (!pm || !pm.sessions) {
|
||||
console.error('❌ PlotManager no disponible');
|
||||
return false;
|
||||
}
|
||||
|
||||
const targetSessionId = sessionId || Array.from(pm.sessions.keys())[0];
|
||||
|
||||
if (!targetSessionId) {
|
||||
console.error('❌ No hay sesiones disponibles');
|
||||
return false;
|
||||
}
|
||||
|
||||
console.log(`🔄 Reiniciando sesión: ${targetSessionId}`);
|
||||
|
||||
try {
|
||||
// 1. Limpiar datos existentes
|
||||
if (pm.clearStreamingData) {
|
||||
pm.clearStreamingData(targetSessionId);
|
||||
console.log('✅ Datos de streaming limpiados');
|
||||
}
|
||||
|
||||
// 2. Pausar y reiniciar
|
||||
pm.controlPlot(targetSessionId, 'stop');
|
||||
console.log('⏹️ Plot detenido');
|
||||
|
||||
setTimeout(() => {
|
||||
pm.controlPlot(targetSessionId, 'start');
|
||||
console.log('▶️ Plot reiniciado');
|
||||
|
||||
// 3. Forzar actualización después de un momento
|
||||
setTimeout(() => {
|
||||
if (pm.refreshStreamingData) {
|
||||
const sessionData = pm.sessions.get(targetSessionId);
|
||||
if (sessionData?.chart) {
|
||||
pm.refreshStreamingData(targetSessionId, sessionData.chart);
|
||||
console.log('🔄 Actualización forzada');
|
||||
}
|
||||
}
|
||||
}, 1000);
|
||||
}, 500);
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('❌ Error al reiniciar streaming:', error);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Función de test rápido
|
||||
window.quickStreamingTest = function () {
|
||||
console.log('⚡ TEST RÁPIDO DE STREAMING');
|
||||
console.log('-'.repeat(30));
|
||||
|
||||
const pm = window.plotManager || plotManager;
|
||||
const checks = {
|
||||
plotManager: !!pm,
|
||||
sessions: pm?.sessions?.size || 0,
|
||||
chartStreaming: !!window.ChartStreaming
|
||||
};
|
||||
|
||||
Object.entries(checks).forEach(([key, value]) => {
|
||||
console.log(`${key}: ${value ? '✅' : '❌'} ${value}`);
|
||||
});
|
||||
|
||||
if (checks.sessions > 0) {
|
||||
const sessionId = Array.from(pm.sessions.keys())[0];
|
||||
console.log(`\n🧪 Probando sesión: ${sessionId}`);
|
||||
|
||||
// Ejecutar diagnóstico completo
|
||||
return diagnoseStreamingIssue();
|
||||
} else {
|
||||
console.log('\n💡 Crea un plot primero, luego ejecuta: quickStreamingTest()');
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Función específica para diagnosticar el onRefresh callback
|
||||
window.diagnoseOnRefreshIssue = function () {
|
||||
console.log('🔧 DIAGNÓSTICO ESPECÍFICO DEL ONREFRESH CALLBACK');
|
||||
console.log('='.repeat(50));
|
||||
|
||||
const pm = window.plotManager || plotManager;
|
||||
if (!pm || !pm.sessions) {
|
||||
console.error('❌ PlotManager no disponible');
|
||||
console.log('💡 Tip: Espera unos segundos e intenta de nuevo');
|
||||
return;
|
||||
}
|
||||
|
||||
if (pm.sessions.size === 0) {
|
||||
console.log('⚠️ No hay sesiones activas. Crea un plot primero.');
|
||||
return;
|
||||
}
|
||||
|
||||
const sessionId = Array.from(pm.sessions.keys())[0];
|
||||
const sessionData = pm.sessions.get(sessionId);
|
||||
|
||||
if (!sessionData || !sessionData.chart) {
|
||||
console.error('❌ No se encuentra el chart para la sesión');
|
||||
return;
|
||||
}
|
||||
|
||||
const chart = sessionData.chart;
|
||||
const realTimeScale = chart.scales.x;
|
||||
|
||||
console.log('📊 Información del Chart:');
|
||||
console.log('- Chart existe:', !!chart);
|
||||
console.log('- Chart type:', chart.config.type);
|
||||
console.log('- Chart scales:', Object.keys(chart.scales));
|
||||
|
||||
console.log('\n📈 Información de la Escala RealTime:');
|
||||
console.log('- Scale type:', realTimeScale.type);
|
||||
console.log('- Scale constructor:', realTimeScale.constructor.name);
|
||||
console.log('- Scale options:', realTimeScale.options);
|
||||
console.log('- Scale realtime options:', realTimeScale.options.realtime);
|
||||
|
||||
if (realTimeScale.realtime) {
|
||||
console.log('\n⚙️ Configuración RealTime:');
|
||||
console.log('- Duration:', realTimeScale.realtime.duration);
|
||||
console.log('- Refresh:', realTimeScale.realtime.refresh);
|
||||
console.log('- Pause:', realTimeScale.realtime.pause);
|
||||
console.log('- onRefresh type:', typeof realTimeScale.realtime.onRefresh);
|
||||
console.log('- onRefresh value:', realTimeScale.realtime.onRefresh);
|
||||
|
||||
if (typeof realTimeScale.realtime.onRefresh === 'function') {
|
||||
console.log('✅ onRefresh callback está configurado correctamente');
|
||||
|
||||
// Test manual del callback
|
||||
console.log('\n🧪 Probando callback onRefresh manualmente...');
|
||||
try {
|
||||
realTimeScale.realtime.onRefresh(chart);
|
||||
console.log('✅ Callback onRefresh ejecutado sin errores');
|
||||
} catch (error) {
|
||||
console.error('❌ Error al ejecutar callback onRefresh:', error);
|
||||
}
|
||||
} else {
|
||||
console.error('❌ onRefresh callback NO está configurado como función');
|
||||
}
|
||||
} else {
|
||||
console.error('❌ No se encontró configuración realtime en la escala');
|
||||
}
|
||||
|
||||
// Verificar streaming en chart
|
||||
if (chart.$streaming) {
|
||||
console.log('\n🔄 Estado del Streaming:');
|
||||
console.log('- Streaming enabled:', chart.$streaming.enabled);
|
||||
console.log('- Interval ID:', chart.$streaming.intervalId);
|
||||
console.log('- Interval activo:', !!chart.$streaming.intervalId);
|
||||
} else {
|
||||
console.error('❌ No se encontró objeto $streaming en el chart');
|
||||
}
|
||||
};
|
||||
|
||||
console.log('🔧 Scripts de diagnóstico cargados:');
|
||||
console.log('- diagnoseStreamingIssue() - Diagnóstico completo');
|
||||
console.log('- resetStreaming() - Reiniciar streaming');
|
||||
console.log('- quickStreamingTest() - Test rápido');
|
||||
console.log('- diagnoseOnRefreshIssue() - Diagnóstico específico del onRefresh');
|
|
@ -4,20 +4,29 @@
|
|||
|
||||
// Refrescar log de eventos
|
||||
function refreshEventLog() {
|
||||
const limit = document.getElementById('log-limit').value;
|
||||
const limitElement = document.getElementById('log-limit');
|
||||
const limit = limitElement ? limitElement.value : 100;
|
||||
|
||||
fetch(`/api/events?limit=${limit}`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
const logContainer = document.getElementById('events-log');
|
||||
const logStats = document.getElementById('log-stats');
|
||||
const logContainer = document.getElementById('events-container');
|
||||
const logStats = document.getElementById('events-count');
|
||||
|
||||
// Verificar que los elementos existan
|
||||
if (!logContainer) {
|
||||
console.warn('Events container not found');
|
||||
return;
|
||||
}
|
||||
|
||||
// Limpiar entradas existentes
|
||||
logContainer.innerHTML = '';
|
||||
|
||||
// Actualizar estadísticas
|
||||
logStats.textContent = `Showing ${data.showing} of ${data.total_events} events`;
|
||||
if (logStats) {
|
||||
logStats.textContent = `${data.showing} of ${data.total_events}`;
|
||||
}
|
||||
|
||||
// Añadir eventos (orden inverso para mostrar primero los más nuevos)
|
||||
const events = data.events.reverse();
|
||||
|
@ -103,7 +112,7 @@ function initEventListeners() {
|
|||
}
|
||||
|
||||
// Función para cargar eventos en el tab de events
|
||||
window.loadEvents = function() {
|
||||
window.loadEvents = function () {
|
||||
fetch('/api/events?limit=50')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
|
@ -111,11 +120,19 @@ window.loadEvents = function() {
|
|||
const eventsContainer = document.getElementById('events-container');
|
||||
const eventsCount = document.getElementById('events-count');
|
||||
|
||||
// Verificar que los elementos existan
|
||||
if (!eventsContainer) {
|
||||
console.warn('Events container not found in loadEvents');
|
||||
return;
|
||||
}
|
||||
|
||||
// Limpiar contenedor
|
||||
eventsContainer.innerHTML = '';
|
||||
|
||||
// Actualizar contador
|
||||
eventsCount.textContent = data.showing || 0;
|
||||
if (eventsCount) {
|
||||
eventsCount.textContent = data.showing || 0;
|
||||
}
|
||||
|
||||
// Añadir eventos (orden inverso para mostrar primero los más nuevos)
|
||||
const events = data.events.reverse();
|
||||
|
|
|
@ -175,13 +175,20 @@ class PlotManager {
|
|||
|
||||
async updateSessionData(sessionId) {
|
||||
try {
|
||||
// 🚀 NUEVO: Para streaming, el refreshStreamingData maneja la actualización automática
|
||||
// Esta función ahora es principalmente para compatibilidad y casos especiales
|
||||
const sessionData = this.sessions.get(sessionId);
|
||||
if (!sessionData || !sessionData.chart) {
|
||||
return;
|
||||
}
|
||||
|
||||
const response = await fetch(`/api/plots/${sessionId}/data`);
|
||||
const plotData = await response.json();
|
||||
|
||||
if (plotData.datasets) {
|
||||
// 🔧 DEBUG: Log para troubleshooting
|
||||
if (plotData.datasets.length > 1) {
|
||||
plotDebugLog(`📈 Plot ${sessionId}: Updating ${plotData.datasets.length} variables, ${plotData.data_points_count} total points`);
|
||||
plotDebugLog(`📈 Plot ${sessionId}: Manual update ${plotData.datasets.length} variables, ${plotData.data_points_count} total points`);
|
||||
}
|
||||
this.updateChart(sessionId, plotData);
|
||||
} else {
|
||||
|
@ -233,83 +240,57 @@ class PlotManager {
|
|||
|
||||
document.getElementById('plot-sessions-container').appendChild(container);
|
||||
|
||||
// Crear Chart.js
|
||||
// 🚀 NUEVO: Usar chartjs-plugin-streaming para crear chart
|
||||
const ctx = document.getElementById(`chart-${sessionId}`).getContext('2d');
|
||||
const chart = new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
datasets: []
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
animation: {
|
||||
duration: 0 // Sin animación para mejor performance
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
type: 'time',
|
||||
time: {
|
||||
unit: 'second',
|
||||
displayFormats: {
|
||||
second: 'HH:mm:ss'
|
||||
}
|
||||
},
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Time'
|
||||
}
|
||||
},
|
||||
y: {
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Value'
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: {
|
||||
legend: {
|
||||
display: true,
|
||||
position: 'top'
|
||||
},
|
||||
tooltip: {
|
||||
mode: 'index',
|
||||
intersect: false
|
||||
}
|
||||
},
|
||||
interaction: {
|
||||
mode: 'nearest',
|
||||
axis: 'x',
|
||||
intersect: false
|
||||
}
|
||||
|
||||
// Configurar streaming con la nueva librería
|
||||
const streamingConfig = window.ChartStreaming.createStreamingChartConfig({
|
||||
duration: (config.time_window || 60) * 1000, // Convertir a milisegundos
|
||||
refresh: 500, // Actualizar cada 500ms
|
||||
frameRate: 30,
|
||||
pause: false,
|
||||
yMin: config.y_min,
|
||||
yMax: config.y_max,
|
||||
onRefresh: (chart) => {
|
||||
// Esta función se llama automáticamente para obtener nuevos datos
|
||||
this.refreshStreamingData(sessionId, chart);
|
||||
}
|
||||
});
|
||||
|
||||
this.sessions.set(sessionId, chart);
|
||||
const chart = new Chart(ctx, streamingConfig);
|
||||
|
||||
// Actualizar datos iniciales
|
||||
this.updateSessionData(sessionId);
|
||||
this.sessions.set(sessionId, {
|
||||
chart: chart,
|
||||
config: config,
|
||||
lastDataFetch: 0,
|
||||
datasets: new Map() // Para tracking de datasets por variable
|
||||
});
|
||||
|
||||
console.log(`📈 Created plot session: ${sessionId}`);
|
||||
// Inicializar datasets para las variables del plot
|
||||
this.initializeStreamingDatasets(sessionId, config);
|
||||
|
||||
console.log(`📈 Created streaming plot session: ${sessionId}`);
|
||||
}
|
||||
|
||||
updateChart(sessionId, plotData) {
|
||||
const chart = this.sessions.get(sessionId);
|
||||
if (!chart) {
|
||||
const sessionData = this.sessions.get(sessionId);
|
||||
if (!sessionData || !sessionData.chart) {
|
||||
console.warn(`📈 Plot ${sessionId}: Chart not found in sessions`);
|
||||
return;
|
||||
}
|
||||
|
||||
const chart = sessionData.chart;
|
||||
|
||||
// 🔧 DEBUG: Verificar datos antes de actualizar
|
||||
if (plotData.datasets && plotData.datasets.length > 0) {
|
||||
plotDebugLog(`📈 Plot ${sessionId}: Updating chart with ${plotData.datasets.length} datasets`);
|
||||
plotDebugLog(`📈 Plot ${sessionId}: Updating streaming chart with ${plotData.datasets.length} datasets`);
|
||||
plotData.datasets.forEach((dataset, idx) => {
|
||||
plotDebugLog(` - Variable ${idx + 1}: ${dataset.label} (${dataset.data.length} points)`);
|
||||
});
|
||||
}
|
||||
|
||||
// Actualizar datasets
|
||||
chart.data.datasets = plotData.datasets || [];
|
||||
// 🚀 NUEVO: Para streaming, agregamos nuevos datos en lugar de reemplazar todo
|
||||
this.updateStreamingData(sessionId, plotData);
|
||||
|
||||
// Actualizar escalas Y - mejore el auto-scaling
|
||||
if (plotData.y_min !== undefined && plotData.y_max !== undefined) {
|
||||
|
@ -330,7 +311,7 @@ class PlotManager {
|
|||
// 🔧 DEBUG: Log si el contador no coincide
|
||||
const calculatedPoints = (plotData.datasets || []).reduce((sum, dataset) => sum + (dataset.data?.length || 0), 0);
|
||||
if (totalPoints !== calculatedPoints) {
|
||||
console.warn(`📈 Plot ${sessionId}: Points mismatch - reported: ${totalPoints}, calculated: ${calculatedPoints}`);
|
||||
plotDebugLog(`📈 Plot ${sessionId}: Points mismatch - reported: ${totalPoints}, calculated: ${calculatedPoints}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -346,12 +327,20 @@ class PlotManager {
|
|||
});
|
||||
}
|
||||
|
||||
// Actualizar chart
|
||||
chart.update('none');
|
||||
// El chart de streaming se actualiza automáticamente, no necesitamos llamar update manualmente
|
||||
}
|
||||
|
||||
async controlPlot(sessionId, action) {
|
||||
try {
|
||||
// 🚀 NUEVO: Controlar streaming localmente para algunas acciones
|
||||
if (action === 'pause') {
|
||||
this.setStreamingPause(sessionId, true);
|
||||
} else if (action === 'start') {
|
||||
this.setStreamingPause(sessionId, false);
|
||||
} else if (action === 'clear') {
|
||||
this.clearStreamingData(sessionId);
|
||||
}
|
||||
|
||||
const response = await fetch(`/api/plots/${sessionId}/control`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
|
@ -366,8 +355,15 @@ class PlotManager {
|
|||
// 🔑 NUEVO: Actualizar status inmediatamente después de la acción
|
||||
await this.updateSessionStatus(sessionId);
|
||||
|
||||
// Actualizar datos inmediatamente
|
||||
await this.updateSessionData(sessionId);
|
||||
// Para stop, también pausar el streaming
|
||||
if (action === 'stop') {
|
||||
this.setStreamingPause(sessionId, true);
|
||||
}
|
||||
|
||||
// Para start, asegurar que el streaming esté activo
|
||||
if (action === 'start') {
|
||||
this.setStreamingPause(sessionId, false);
|
||||
}
|
||||
|
||||
showNotification(result.message, 'success');
|
||||
} else {
|
||||
|
@ -387,6 +383,17 @@ class PlotManager {
|
|||
|
||||
if (data.success && data.config) {
|
||||
this.updatePlotStats(sessionId, data.config);
|
||||
|
||||
// 🚀 NUEVO: Actualizar estado de streaming basado en el status del backend
|
||||
const sessionData = this.sessions.get(sessionId);
|
||||
if (sessionData) {
|
||||
// Actualizar configuración local
|
||||
sessionData.config = data.config;
|
||||
|
||||
// Controlar pausa del streaming basado en el estado del plot
|
||||
const shouldPause = !data.config.is_active || data.config.is_paused;
|
||||
this.setStreamingPause(sessionId, shouldPause);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error updating session status ${sessionId}:`, error);
|
||||
|
@ -415,9 +422,9 @@ class PlotManager {
|
|||
|
||||
removeChart(sessionId) {
|
||||
// Destruir Chart.js
|
||||
const chart = this.sessions.get(sessionId);
|
||||
if (chart) {
|
||||
chart.destroy();
|
||||
const sessionData = this.sessions.get(sessionId);
|
||||
if (sessionData && sessionData.chart) {
|
||||
sessionData.chart.destroy();
|
||||
this.sessions.delete(sessionId);
|
||||
}
|
||||
|
||||
|
@ -428,70 +435,266 @@ class PlotManager {
|
|||
}
|
||||
}
|
||||
|
||||
// 🚀 NUEVAS FUNCIONES PARA STREAMING
|
||||
|
||||
/**
|
||||
* Inicializa los datasets de streaming para las variables del plot
|
||||
*/
|
||||
initializeStreamingDatasets(sessionId, config) {
|
||||
const sessionData = this.sessions.get(sessionId);
|
||||
if (!sessionData || !sessionData.chart || !config.variables) {
|
||||
plotDebugLog(`📈 Plot ${sessionId}: Cannot initialize datasets - missing data`);
|
||||
return;
|
||||
}
|
||||
|
||||
const chart = sessionData.chart;
|
||||
const datasets = [];
|
||||
|
||||
plotDebugLog(`📈 Plot ${sessionId}: Initializing ${config.variables.length} streaming datasets`);
|
||||
|
||||
config.variables.forEach((variable, index) => {
|
||||
const color = this.getColor(variable, index);
|
||||
const dataset = {
|
||||
label: variable,
|
||||
data: [],
|
||||
borderColor: color,
|
||||
backgroundColor: color + '20', // Color con transparencia
|
||||
borderWidth: 2,
|
||||
fill: false,
|
||||
pointRadius: 0,
|
||||
pointHoverRadius: 3,
|
||||
tension: 0.1
|
||||
};
|
||||
|
||||
datasets.push(dataset);
|
||||
sessionData.datasets.set(variable, index);
|
||||
|
||||
plotDebugLog(`📈 Plot ${sessionId}: Dataset ${index}: ${variable} (color: ${color})`);
|
||||
});
|
||||
|
||||
// Inicializar timestamps tracking
|
||||
sessionData.lastTimestamps = {};
|
||||
|
||||
chart.data.datasets = datasets;
|
||||
chart.update('quiet');
|
||||
|
||||
plotDebugLog(`📈 Plot ${sessionId}: Successfully initialized ${datasets.length} streaming datasets`);
|
||||
|
||||
// 🔧 DEBUG: Verificar configuración del chart
|
||||
plotDebugLog(`📈 Plot ${sessionId}: Chart config:`, {
|
||||
type: chart.config.type,
|
||||
scaleType: chart.scales.x?.type,
|
||||
hasRealTimeScale: chart.scales.x?.constructor?.name,
|
||||
streamingEnabled: !!chart.$streaming?.enabled
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Función llamada automáticamente por chartjs-plugin-streaming para obtener nuevos datos
|
||||
*/
|
||||
async refreshStreamingData(sessionId, chart) {
|
||||
try {
|
||||
// Evitar llamadas muy frecuentes
|
||||
const sessionData = this.sessions.get(sessionId);
|
||||
if (!sessionData) return;
|
||||
|
||||
const now = Date.now();
|
||||
if (now - sessionData.lastDataFetch < 800) { // Mínimo 800ms entre llamadas
|
||||
return;
|
||||
}
|
||||
sessionData.lastDataFetch = now;
|
||||
|
||||
// 🔧 DEBUG: Log de llamada
|
||||
plotDebugLog(`📈 Plot ${sessionId}: Fetching data from backend...`);
|
||||
|
||||
// Obtener datos del backend
|
||||
const response = await fetch(`/api/plots/${sessionId}/data`);
|
||||
const plotData = await response.json();
|
||||
|
||||
plotDebugLog(`📈 Plot ${sessionId}: Received data:`, plotData);
|
||||
|
||||
if (plotData && plotData.datasets && plotData.datasets.length > 0) {
|
||||
// Agregar los nuevos puntos de datos al chart de streaming
|
||||
this.processBackendDataForStreaming(sessionId, plotData);
|
||||
|
||||
// Actualizar contador de puntos
|
||||
const pointsElement = document.getElementById(`points-${sessionId}`);
|
||||
if (pointsElement) {
|
||||
pointsElement.textContent = plotData.data_points_count || 0;
|
||||
}
|
||||
} else {
|
||||
plotDebugLog(`📈 Plot ${sessionId}: No datasets received or empty data`);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error(`Error refreshing streaming data for ${sessionId}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Actualiza los datos de streaming del chart
|
||||
*/
|
||||
updateStreamingData(sessionId, plotData) {
|
||||
const sessionData = this.sessions.get(sessionId);
|
||||
if (!sessionData || !sessionData.chart || !plotData.datasets) {
|
||||
return;
|
||||
}
|
||||
|
||||
const chart = sessionData.chart;
|
||||
|
||||
// Para cada dataset del backend, agregar los nuevos puntos
|
||||
plotData.datasets.forEach((backendDataset, index) => {
|
||||
if (chart.data.datasets[index] && backendDataset.data) {
|
||||
const chartDataset = chart.data.datasets[index];
|
||||
|
||||
// Obtener los últimos puntos que no tengamos aún
|
||||
const existingPoints = chartDataset.data.length;
|
||||
const newPoints = backendDataset.data.slice(existingPoints);
|
||||
|
||||
newPoints.forEach(point => {
|
||||
window.ChartStreaming.addStreamingData(chart, index, {
|
||||
x: point.x,
|
||||
y: point.y
|
||||
});
|
||||
});
|
||||
|
||||
plotDebugLog(`📈 Plot ${sessionId}: Added ${newPoints.length} new points to dataset ${index}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Procesa datos del backend para el chart de streaming
|
||||
*/
|
||||
processBackendDataForStreaming(sessionId, plotData) {
|
||||
const sessionData = this.sessions.get(sessionId);
|
||||
if (!sessionData || !sessionData.chart || !plotData.datasets) {
|
||||
return;
|
||||
}
|
||||
|
||||
const chart = sessionData.chart;
|
||||
|
||||
// Asegurar que tenemos tracking de los últimos timestamps por dataset
|
||||
if (!sessionData.lastTimestamps) {
|
||||
sessionData.lastTimestamps = {};
|
||||
}
|
||||
|
||||
plotDebugLog(`📈 Plot ${sessionId}: Processing ${plotData.datasets.length} datasets for streaming`);
|
||||
|
||||
// Para cada dataset del backend
|
||||
plotData.datasets.forEach((backendDataset, index) => {
|
||||
const variableName = backendDataset.label;
|
||||
|
||||
if (!backendDataset.data || backendDataset.data.length === 0) {
|
||||
plotDebugLog(`📈 Plot ${sessionId}: Dataset ${variableName} has no data`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Obtener el último timestamp que agregamos para esta variable
|
||||
const lastTimestamp = sessionData.lastTimestamps[variableName] || 0;
|
||||
|
||||
// Filtrar solo los puntos nuevos (timestamp mayor al último que agregamos)
|
||||
const newPoints = backendDataset.data.filter(point => point.x > lastTimestamp);
|
||||
|
||||
if (newPoints.length > 0) {
|
||||
plotDebugLog(`📈 Plot ${sessionId}: Adding ${newPoints.length} new points for ${variableName}`);
|
||||
|
||||
// Agregar cada nuevo punto al chart de streaming
|
||||
newPoints.forEach(point => {
|
||||
if (point.y !== null && point.y !== undefined) {
|
||||
window.ChartStreaming.addStreamingData(chart, index, {
|
||||
x: point.x, // Usar timestamp del backend
|
||||
y: point.y
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Actualizar el último timestamp procesado para esta variable
|
||||
const latestPoint = newPoints[newPoints.length - 1];
|
||||
sessionData.lastTimestamps[variableName] = latestPoint.x;
|
||||
|
||||
plotDebugLog(`📈 Plot ${sessionId}: Updated last timestamp for ${variableName} to ${latestPoint.x}`);
|
||||
} else {
|
||||
plotDebugLog(`📈 Plot ${sessionId}: No new points for ${variableName} (last: ${lastTimestamp})`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Controla la pausa/reanudación del streaming
|
||||
*/
|
||||
setStreamingPause(sessionId, paused) {
|
||||
const sessionData = this.sessions.get(sessionId);
|
||||
if (sessionData && sessionData.chart) {
|
||||
window.ChartStreaming.setStreamingPause(sessionData.chart, paused);
|
||||
plotDebugLog(`📈 Plot ${sessionId}: Streaming ${paused ? 'paused' : 'resumed'}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Limpia todos los datos de streaming
|
||||
*/
|
||||
clearStreamingData(sessionId) {
|
||||
const sessionData = this.sessions.get(sessionId);
|
||||
if (sessionData && sessionData.chart) {
|
||||
window.ChartStreaming.clearStreamingData(sessionData.chart);
|
||||
plotDebugLog(`📈 Plot ${sessionId}: Streaming data cleared`);
|
||||
}
|
||||
}
|
||||
|
||||
async createPlotSessionTab(sessionId, sessionInfo) {
|
||||
// Crear tab dinámico para el plot
|
||||
if (typeof tabManager !== 'undefined') {
|
||||
tabManager.createPlotTab(sessionId, sessionInfo.name || `Plot ${sessionId}`);
|
||||
}
|
||||
|
||||
// Crear el Chart.js en el canvas del tab
|
||||
// 🚀 NUEVO: Obtener configuración completa del plot para el streaming
|
||||
let plotConfig = sessionInfo;
|
||||
try {
|
||||
const configResponse = await fetch(`/api/plots/${sessionId}/config`);
|
||||
const configData = await configResponse.json();
|
||||
if (configData.success && configData.config) {
|
||||
plotConfig = configData.config;
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`Could not load plot config for ${sessionId}, using basic info`);
|
||||
}
|
||||
|
||||
// Crear el Chart.js con streaming en el canvas del tab
|
||||
const ctx = document.getElementById(`chart-${sessionId}`).getContext('2d');
|
||||
const chart = new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
datasets: []
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
animation: {
|
||||
duration: 0 // Sin animación para mejor performance
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
type: 'time',
|
||||
time: {
|
||||
unit: 'second',
|
||||
displayFormats: {
|
||||
second: 'HH:mm:ss'
|
||||
}
|
||||
},
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Time'
|
||||
}
|
||||
},
|
||||
y: {
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Value'
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: {
|
||||
legend: {
|
||||
display: true,
|
||||
position: 'top'
|
||||
},
|
||||
tooltip: {
|
||||
mode: 'index',
|
||||
intersect: false
|
||||
}
|
||||
},
|
||||
interaction: {
|
||||
mode: 'nearest',
|
||||
axis: 'x',
|
||||
intersect: false
|
||||
}
|
||||
|
||||
// Configurar streaming con la nueva librería
|
||||
const streamingConfig = window.ChartStreaming.createStreamingChartConfig({
|
||||
duration: (plotConfig.time_window || 60) * 1000, // Convertir a milisegundos
|
||||
refresh: 500, // Actualizar cada 500ms
|
||||
frameRate: 30,
|
||||
pause: !plotConfig.is_active, // Pausar si no está activo
|
||||
yMin: plotConfig.y_min,
|
||||
yMax: plotConfig.y_max,
|
||||
onRefresh: (chart) => {
|
||||
// Esta función se llama automáticamente para obtener nuevos datos
|
||||
this.refreshStreamingData(sessionId, chart);
|
||||
}
|
||||
});
|
||||
|
||||
this.sessions.set(sessionId, chart);
|
||||
const chart = new Chart(ctx, streamingConfig);
|
||||
|
||||
this.sessions.set(sessionId, {
|
||||
chart: chart,
|
||||
config: plotConfig,
|
||||
lastDataFetch: 0,
|
||||
datasets: new Map() // Para tracking de datasets por variable
|
||||
});
|
||||
|
||||
// Inicializar datasets para las variables del plot
|
||||
if (plotConfig.variables) {
|
||||
this.initializeStreamingDatasets(sessionId, plotConfig);
|
||||
}
|
||||
|
||||
// Actualizar estadísticas del plot
|
||||
this.updatePlotStats(sessionId, sessionInfo);
|
||||
|
||||
console.log(`📈 Created plot tab and chart for session: ${sessionId}`);
|
||||
console.log(`📈 Created streaming plot tab and chart for session: ${sessionId}`);
|
||||
}
|
||||
|
||||
updatePlotStats(sessionId, sessionInfo) {
|
||||
|
@ -1205,8 +1408,10 @@ class PlotManager {
|
|||
this.stopAutoUpdate();
|
||||
|
||||
// Destruir todos los charts
|
||||
for (const [sessionId, chart] of this.sessions) {
|
||||
chart.destroy();
|
||||
for (const [sessionId, sessionData] of this.sessions) {
|
||||
if (sessionData && sessionData.chart) {
|
||||
sessionData.chart.destroy();
|
||||
}
|
||||
}
|
||||
this.sessions.clear();
|
||||
|
||||
|
@ -1214,6 +1419,81 @@ class PlotManager {
|
|||
}
|
||||
}
|
||||
|
||||
// 🔧 NUEVAS FUNCIONES DE DEBUG Y VERIFICACIÓN
|
||||
|
||||
/**
|
||||
* Verifica que chartjs-plugin-streaming esté cargado correctamente
|
||||
*/
|
||||
window.verifyStreamingIntegration = function () {
|
||||
console.log('🧪 Verificando integración de Chart.js Streaming...');
|
||||
|
||||
const checks = {
|
||||
chartjsLoaded: typeof Chart !== 'undefined',
|
||||
streamingLoaded: typeof window.ChartStreaming !== 'undefined',
|
||||
plotManagerLoaded: typeof plotManager !== 'undefined',
|
||||
activeStreamingSessions: 0
|
||||
};
|
||||
|
||||
console.log('✅ Chart.js cargado:', checks.chartjsLoaded);
|
||||
console.log('✅ ChartStreaming cargado:', checks.streamingLoaded);
|
||||
console.log('✅ PlotManager cargado:', checks.plotManagerLoaded);
|
||||
|
||||
if (checks.plotManagerLoaded && plotManager.sessions) {
|
||||
checks.activeStreamingSessions = plotManager.sessions.size;
|
||||
console.log('✅ Sesiones de streaming activas:', checks.activeStreamingSessions);
|
||||
|
||||
// Verificar cada sesión
|
||||
for (const [sessionId, sessionData] of plotManager.sessions) {
|
||||
console.log(`📈 Sesión ${sessionId}:`, {
|
||||
hasChart: !!sessionData.chart,
|
||||
chartType: sessionData.chart?.config?.type,
|
||||
scaleType: sessionData.chart?.scales?.x?.type,
|
||||
datasets: sessionData.chart?.data?.datasets?.length || 0,
|
||||
lastTimestamps: sessionData.lastTimestamps,
|
||||
streaming: !!sessionData.chart?.$streaming?.enabled
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (checks.streamingLoaded) {
|
||||
console.log('✅ ChartStreaming functions:', Object.keys(window.ChartStreaming));
|
||||
|
||||
// Test de configuración
|
||||
try {
|
||||
const testConfig = window.ChartStreaming.createStreamingChartConfig({
|
||||
duration: 10000,
|
||||
refresh: 1000
|
||||
});
|
||||
console.log('✅ Test configuración streaming:', !!testConfig);
|
||||
} catch (e) {
|
||||
console.log('❌ Error en test configuración:', e.message);
|
||||
}
|
||||
}
|
||||
|
||||
return checks;
|
||||
};
|
||||
|
||||
/**
|
||||
* Fuerza una actualización de datos para todas las sesiones
|
||||
*/
|
||||
window.forceStreamingUpdate = function () {
|
||||
if (!plotManager || !plotManager.sessions) {
|
||||
console.log('❌ PlotManager no disponible');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('🔄 Forzando actualización de streaming...');
|
||||
|
||||
for (const [sessionId, sessionData] of plotManager.sessions) {
|
||||
console.log(`🔄 Actualizando sesión ${sessionId}...`);
|
||||
|
||||
if (sessionData.chart) {
|
||||
// Forzar refresh de datos
|
||||
plotManager.refreshStreamingData(sessionId, sessionData.chart);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Función global para toggle de trigger config
|
||||
window.togglePlotFormTriggerConfig = function () {
|
||||
const triggerEnabled = document.getElementById('plot-form-trigger-enabled');
|
||||
|
@ -1298,8 +1578,38 @@ window.showNotification = function (message, type = 'info') {
|
|||
let plotManager = null;
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
console.log('📈 Initializing Plot Manager with streaming support...');
|
||||
|
||||
// Verificar que chartjs-plugin-streaming esté cargado
|
||||
if (typeof window.ChartStreaming === 'undefined') {
|
||||
console.error('❌ ChartStreaming plugin not loaded! The streaming functionality will not work.');
|
||||
console.log('💡 Make sure chartjs-plugin-streaming.js is loaded before plotting.js');
|
||||
} else {
|
||||
console.log('✅ ChartStreaming plugin loaded successfully');
|
||||
console.log('Available functions:', Object.keys(window.ChartStreaming));
|
||||
}
|
||||
|
||||
// Exportar clase PlotManager globalmente
|
||||
window.PlotManager = PlotManager;
|
||||
|
||||
// Inicializar Plot Manager
|
||||
plotManager = new PlotManager();
|
||||
window.plotManager = new PlotManager();
|
||||
|
||||
// Para compatibilidad
|
||||
plotManager = window.plotManager;
|
||||
|
||||
// Auto-verificar integración después de un breve delay
|
||||
setTimeout(() => {
|
||||
window.verifyStreamingIntegration();
|
||||
|
||||
// Si hay plots activos, mostrar información de debugging
|
||||
if (plotManager.sessions.size > 0) {
|
||||
console.log('📈 Active plot sessions detected. To debug:');
|
||||
console.log('- enablePlotDebug() - Enable detailed logging');
|
||||
console.log('- forceStreamingUpdate() - Force data update');
|
||||
console.log('- verifyStreamingIntegration() - Check system status');
|
||||
}
|
||||
}, 2000);
|
||||
|
||||
// Cerrar modales con Escape (pero prevenir cierre accidental)
|
||||
document.addEventListener('keydown', function (e) {
|
||||
|
|
|
@ -0,0 +1,133 @@
|
|||
/**
|
||||
* 🧪 Test Rápido de Verificación Post-Fix
|
||||
* Ejecutar después de cargar la página para verificar que todo funciona
|
||||
*/
|
||||
|
||||
window.quickFixTest = function () {
|
||||
console.log('🧪 TEST RÁPIDO POST-FIX');
|
||||
console.log('='.repeat(40));
|
||||
|
||||
const results = {
|
||||
duplicatedElements: false,
|
||||
plotManagerAvailable: false,
|
||||
eventsWorking: false,
|
||||
chartStreamingLoaded: false,
|
||||
debugFunctionsLoaded: false
|
||||
};
|
||||
|
||||
// 1. Verificar elementos duplicados
|
||||
console.log('\n1️⃣ VERIFICANDO ELEMENTOS DUPLICADOS...');
|
||||
const eventsContainers = document.querySelectorAll('#events-container');
|
||||
const refreshBtns = document.querySelectorAll('#refresh-events-btn');
|
||||
|
||||
console.log(`- events-container: ${eventsContainers.length} (esperado: 1)`);
|
||||
console.log(`- refresh-events-btn: ${refreshBtns.length} (esperado: 1)`);
|
||||
|
||||
results.duplicatedElements = eventsContainers.length === 1 && refreshBtns.length === 1;
|
||||
console.log(results.duplicatedElements ? '✅ Sin duplicados' : '❌ Hay elementos duplicados');
|
||||
|
||||
// 2. Verificar PlotManager
|
||||
console.log('\n2️⃣ VERIFICANDO PLOTMANAGER...');
|
||||
const pm = window.plotManager || plotManager;
|
||||
results.plotManagerAvailable = !!pm;
|
||||
|
||||
console.log(`- window.plotManager: ${!!window.plotManager}`);
|
||||
console.log(`- plotManager global: ${!!plotManager}`);
|
||||
console.log(`- PlotManager class: ${!!window.PlotManager}`);
|
||||
console.log(results.plotManagerAvailable ? '✅ PlotManager disponible' : '❌ PlotManager no disponible');
|
||||
|
||||
// 3. Verificar Events
|
||||
console.log('\n3️⃣ VERIFICANDO EVENTS...');
|
||||
const eventsContainer = document.getElementById('events-container');
|
||||
const eventsCount = document.getElementById('events-count');
|
||||
|
||||
console.log(`- events-container existe: ${!!eventsContainer}`);
|
||||
console.log(`- events-count existe: ${!!eventsCount}`);
|
||||
|
||||
results.eventsWorking = !!eventsContainer && !!eventsCount;
|
||||
console.log(results.eventsWorking ? '✅ Elements de events OK' : '❌ Faltan elementos de events');
|
||||
|
||||
// 4. Verificar Chart.js Streaming
|
||||
console.log('\n4️⃣ VERIFICANDO CHART.JS STREAMING...');
|
||||
results.chartStreamingLoaded = !!window.ChartStreaming;
|
||||
|
||||
if (results.chartStreamingLoaded) {
|
||||
const functions = Object.keys(window.ChartStreaming);
|
||||
console.log(`✅ ChartStreaming cargado con ${functions.length} funciones`);
|
||||
console.log(`- Funciones: ${functions.join(', ')}`);
|
||||
} else {
|
||||
console.log('❌ ChartStreaming no cargado');
|
||||
}
|
||||
|
||||
// 5. Verificar funciones de debug
|
||||
console.log('\n5️⃣ VERIFICANDO FUNCIONES DE DEBUG...');
|
||||
const debugFunctions = [
|
||||
'diagnoseStreamingIssue',
|
||||
'resetStreaming',
|
||||
'quickStreamingTest',
|
||||
'diagnoseOnRefreshIssue'
|
||||
];
|
||||
|
||||
const availableFunctions = debugFunctions.filter(fn => typeof window[fn] === 'function');
|
||||
results.debugFunctionsLoaded = availableFunctions.length === debugFunctions.length;
|
||||
|
||||
console.log(`✅ Funciones cargadas: ${availableFunctions.join(', ')}`);
|
||||
if (availableFunctions.length < debugFunctions.length) {
|
||||
const missing = debugFunctions.filter(fn => typeof window[fn] !== 'function');
|
||||
console.log(`❌ Funciones faltantes: ${missing.join(', ')}`);
|
||||
}
|
||||
|
||||
// 6. Resumen
|
||||
console.log('\n6️⃣ RESUMEN FINAL');
|
||||
console.log('='.repeat(30));
|
||||
|
||||
const allGood = Object.values(results).every(Boolean);
|
||||
|
||||
if (allGood) {
|
||||
console.log('🎉 ¡TODOS LOS PROBLEMAS ARREGLADOS!');
|
||||
console.log('✅ Ya puedes crear plots y usar el diagnóstico');
|
||||
console.log('');
|
||||
console.log('📋 Próximos pasos:');
|
||||
console.log('1. Ve a la pestaña "Real-Time Plotting"');
|
||||
console.log('2. Crea un nuevo plot');
|
||||
console.log('3. Ejecuta diagnoseOnRefreshIssue() para verificar streaming');
|
||||
} else {
|
||||
console.log('⚠️ Algunos problemas persisten:');
|
||||
Object.entries(results).forEach(([key, value]) => {
|
||||
if (!value) {
|
||||
console.log(` ❌ ${key}`);
|
||||
}
|
||||
});
|
||||
console.log('');
|
||||
console.log('💡 Intenta recargar la página (F5) y ejecutar quickFixTest() de nuevo');
|
||||
}
|
||||
|
||||
return results;
|
||||
};
|
||||
|
||||
// Test básico de events
|
||||
window.testEventsIntegration = function () {
|
||||
console.log('🧪 PROBANDO INTEGRACIÓN DE EVENTS...');
|
||||
|
||||
try {
|
||||
refreshEventLog();
|
||||
console.log('✅ refreshEventLog() ejecutado sin errores');
|
||||
|
||||
// Test loadEvents
|
||||
if (typeof window.loadEvents === 'function') {
|
||||
window.loadEvents();
|
||||
console.log('✅ loadEvents() ejecutado sin errores');
|
||||
} else {
|
||||
console.log('❌ loadEvents() no disponible');
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('❌ Error en events:', error);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
console.log('🧪 Funciones de test cargadas:');
|
||||
console.log('- quickFixTest() - Verificación completa post-fix');
|
||||
console.log('- testEventsIntegration() - Test específico de events');
|
|
@ -0,0 +1,216 @@
|
|||
/**
|
||||
* 🧪 Script de prueba para Chart.js Plugin Streaming
|
||||
* Ejecutar en consola del navegador para verificar integración
|
||||
*/
|
||||
|
||||
// Test básico de disponibilidad
|
||||
function testStreamingAvailability() {
|
||||
console.log('🧪 Testing Chart.js Streaming Plugin availability...');
|
||||
|
||||
let results = {
|
||||
chartjsLoaded: typeof Chart !== 'undefined',
|
||||
streamingLoaded: typeof window.ChartStreaming !== 'undefined',
|
||||
realTimeScaleRegistered: false,
|
||||
streamingPluginRegistered: false
|
||||
};
|
||||
|
||||
// Verificar que Chart.js esté cargado
|
||||
if (results.chartjsLoaded) {
|
||||
console.log('✅ Chart.js is loaded');
|
||||
|
||||
// Verificar escala realtime
|
||||
try {
|
||||
Chart.register(window.ChartStreaming.RealTimeScale);
|
||||
results.realTimeScaleRegistered = true;
|
||||
console.log('✅ RealTime scale is available');
|
||||
} catch (e) {
|
||||
console.log('❌ RealTime scale registration failed:', e.message);
|
||||
}
|
||||
|
||||
// Verificar plugin de streaming
|
||||
try {
|
||||
Chart.register(window.ChartStreaming.streamingPlugin);
|
||||
results.streamingPluginRegistered = true;
|
||||
console.log('✅ Streaming plugin is available');
|
||||
} catch (e) {
|
||||
console.log('❌ Streaming plugin registration failed:', e.message);
|
||||
}
|
||||
} else {
|
||||
console.log('❌ Chart.js is not loaded');
|
||||
}
|
||||
|
||||
// Verificar ChartStreaming global
|
||||
if (results.streamingLoaded) {
|
||||
console.log('✅ ChartStreaming global object is available');
|
||||
console.log('Available functions:', Object.keys(window.ChartStreaming));
|
||||
} else {
|
||||
console.log('❌ ChartStreaming global object is not available');
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
// Test de creación de configuración
|
||||
function testStreamingConfig() {
|
||||
console.log('🧪 Testing streaming configuration creation...');
|
||||
|
||||
if (typeof window.ChartStreaming === 'undefined') {
|
||||
console.log('❌ ChartStreaming not available');
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
const config = window.ChartStreaming.createStreamingChartConfig({
|
||||
duration: 30000,
|
||||
refresh: 1000,
|
||||
yMin: 0,
|
||||
yMax: 100
|
||||
});
|
||||
|
||||
console.log('✅ Streaming configuration created successfully');
|
||||
console.log('Config structure:', {
|
||||
type: config.type,
|
||||
hasRealTimeScale: config.options.scales.x.type === 'realtime',
|
||||
duration: config.options.scales.x.realtime.duration,
|
||||
refresh: config.options.scales.x.realtime.refresh
|
||||
});
|
||||
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.log('❌ Failed to create streaming configuration:', e.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Test de creación de chart (requiere canvas)
|
||||
function testStreamingChart() {
|
||||
console.log('🧪 Testing streaming chart creation...');
|
||||
|
||||
// Buscar un canvas existente o crear uno temporal
|
||||
let canvas = document.querySelector('canvas');
|
||||
let isTemporary = false;
|
||||
|
||||
if (!canvas) {
|
||||
console.log('Creating temporary canvas for testing...');
|
||||
canvas = document.createElement('canvas');
|
||||
canvas.id = 'test-streaming-canvas';
|
||||
canvas.style.display = 'none';
|
||||
document.body.appendChild(canvas);
|
||||
isTemporary = true;
|
||||
}
|
||||
|
||||
try {
|
||||
const ctx = canvas.getContext('2d');
|
||||
const config = window.ChartStreaming.createStreamingChartConfig({
|
||||
duration: 10000,
|
||||
refresh: 2000
|
||||
});
|
||||
|
||||
const chart = new Chart(ctx, config);
|
||||
|
||||
console.log('✅ Streaming chart created successfully');
|
||||
console.log('Chart info:', {
|
||||
id: chart.id,
|
||||
type: chart.config.type,
|
||||
hasStreamingPlugin: chart.config.plugins.includes('streaming'),
|
||||
scaleType: chart.scales.x.type
|
||||
});
|
||||
|
||||
// Cleanup
|
||||
chart.destroy();
|
||||
if (isTemporary) {
|
||||
canvas.remove();
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.log('❌ Failed to create streaming chart:', e.message);
|
||||
|
||||
// Cleanup en caso de error
|
||||
if (isTemporary && canvas.parentNode) {
|
||||
canvas.remove();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Test completo
|
||||
function runStreamingTests() {
|
||||
console.log('🚀 Starting Chart.js Streaming Plugin Integration Tests');
|
||||
console.log('='.repeat(60));
|
||||
|
||||
const results = {
|
||||
availability: testStreamingAvailability(),
|
||||
config: testStreamingConfig(),
|
||||
chart: testStreamingChart()
|
||||
};
|
||||
|
||||
console.log('='.repeat(60));
|
||||
console.log('📊 Test Results Summary:');
|
||||
console.log('- Availability:', results.availability.chartjsLoaded && results.availability.streamingLoaded ? '✅' : '❌');
|
||||
console.log('- Configuration:', results.config ? '✅' : '❌');
|
||||
console.log('- Chart Creation:', results.chart ? '✅' : '❌');
|
||||
|
||||
const allPassed = results.availability.chartjsLoaded &&
|
||||
results.availability.streamingLoaded &&
|
||||
results.config &&
|
||||
results.chart;
|
||||
|
||||
if (allPassed) {
|
||||
console.log('🎉 All tests passed! Chart.js Streaming Plugin is ready to use.');
|
||||
} else {
|
||||
console.log('⚠️ Some tests failed. Check the logs above for details.');
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
// Test específico para la aplicación PLC
|
||||
function testPlotManagerIntegration() {
|
||||
console.log('🧪 Testing PlotManager integration...');
|
||||
|
||||
if (typeof plotManager === 'undefined') {
|
||||
console.log('❌ PlotManager not available (might not be on plotting tab)');
|
||||
return false;
|
||||
}
|
||||
|
||||
console.log('✅ PlotManager is available');
|
||||
console.log('PlotManager info:', {
|
||||
isInitialized: plotManager.isInitialized,
|
||||
sessionsCount: plotManager.sessions.size,
|
||||
colorsAvailable: plotManager.colors.length
|
||||
});
|
||||
|
||||
// Verificar métodos de streaming
|
||||
const streamingMethods = [
|
||||
'setStreamingPause',
|
||||
'clearStreamingData',
|
||||
'refreshStreamingData',
|
||||
'initializeStreamingDatasets'
|
||||
];
|
||||
|
||||
const availableMethods = streamingMethods.filter(method =>
|
||||
typeof plotManager[method] === 'function'
|
||||
);
|
||||
|
||||
console.log('Available streaming methods:', availableMethods);
|
||||
|
||||
return availableMethods.length === streamingMethods.length;
|
||||
}
|
||||
|
||||
// Hacer funciones disponibles globalmente para testing manual
|
||||
if (typeof window !== 'undefined') {
|
||||
window.testStreamingAvailability = testStreamingAvailability;
|
||||
window.testStreamingConfig = testStreamingConfig;
|
||||
window.testStreamingChart = testStreamingChart;
|
||||
window.runStreamingTests = runStreamingTests;
|
||||
window.testPlotManagerIntegration = testPlotManagerIntegration;
|
||||
|
||||
console.log('🧪 Streaming test functions loaded. Available commands:');
|
||||
console.log('- testStreamingAvailability()');
|
||||
console.log('- testStreamingConfig()');
|
||||
console.log('- testStreamingChart()');
|
||||
console.log('- runStreamingTests()');
|
||||
console.log('- testPlotManagerIntegration()');
|
||||
}
|
|
@ -7,5 +7,5 @@
|
|||
]
|
||||
},
|
||||
"auto_recovery_enabled": true,
|
||||
"last_update": "2025-07-21T14:43:41.538809"
|
||||
"last_update": "2025-07-21T18:34:08.746917"
|
||||
}
|
|
@ -669,32 +669,6 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<!-- 📋 EVENTS TAB -->
|
||||
<div class="tab-content" id="events-tab">
|
||||
<article>
|
||||
<header>📋 Events & System Logs</header>
|
||||
<div class="info-section">
|
||||
<p><strong>📋 Event Logging:</strong> Monitor system events, errors, and operational status</p>
|
||||
<p><strong>🔍 Real-time Updates:</strong> Events are automatically updated as they occur</p>
|
||||
<p><strong>📊 Filtering:</strong> Filter events by type and time range</p>
|
||||
</div>
|
||||
|
||||
<div class="log-controls">
|
||||
<button id="refresh-events-btn">🔄 Refresh Events</button>
|
||||
<button id="clear-events-btn" class="secondary">🗑️ Clear Events</button>
|
||||
<div class="log-stats">
|
||||
<span id="events-count">Loading...</span> events
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="log-container" id="events-container">
|
||||
<p>Loading events...</p>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<!-- Variable Selection Modal (NEW) -->
|
||||
<div id="variable-selection-modal" class="modal" style="display: none;">
|
||||
<div class="modal-content variable-modal">
|
||||
|
@ -784,6 +758,9 @@
|
|||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns"></script>
|
||||
|
||||
<!-- Chart.js Streaming Plugin -->
|
||||
<script src="/static/js/chartjs-streaming/chartjs-plugin-streaming.js"></script>
|
||||
|
||||
<!-- JavaScript Modules -->
|
||||
<script src="/static/js/utils.js"></script>
|
||||
<script src="/static/js/theme.js"></script>
|
||||
|
@ -797,6 +774,12 @@
|
|||
<script src="/static/js/tabs.js"></script>
|
||||
<script src="/static/js/plotting.js"></script>
|
||||
<script src="/static/js/main.js"></script>
|
||||
|
||||
<!-- Debug Streaming Script - Debe cargarse al final -->
|
||||
<script src="/static/js/debug-streaming.js"></script>
|
||||
|
||||
<!-- Quick Fix Test Script -->
|
||||
<script src="/static/js/quick-fix-test.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,378 @@
|
|||
/**
|
||||
* 📈 Chart.js Plugin Streaming Integration
|
||||
* Integra chartjs-plugin-streaming para plotting en tiempo real
|
||||
*
|
||||
* Combinación de módulos:
|
||||
* - helpers.streaming.js
|
||||
* - scale.realtime.js
|
||||
* - plugin.streaming.js
|
||||
* - plugin.zoom.js (integración con zoom)
|
||||
*/
|
||||
|
||||
(function (global, factory) {
|
||||
if (typeof exports === 'object' && typeof module !== 'undefined') {
|
||||
factory(exports, require('chart.js'));
|
||||
} else if (typeof define === 'function' && define.amd) {
|
||||
define(['exports', 'chart.js'], factory);
|
||||
} else {
|
||||
global = global || self;
|
||||
factory(global.ChartStreaming = {}, global.Chart);
|
||||
}
|
||||
})(this, function (exports, Chart) {
|
||||
'use strict';
|
||||
|
||||
// ============= HELPERS.STREAMING.JS =============
|
||||
function clamp(value, min, max) {
|
||||
return Math.min(Math.max(value, min), max);
|
||||
}
|
||||
|
||||
function resolveOption(scale, key) {
|
||||
const realtimeOptions = scale.options.realtime || {};
|
||||
const scaleOptions = scale.options;
|
||||
|
||||
if (realtimeOptions[key] !== undefined) {
|
||||
return realtimeOptions[key];
|
||||
}
|
||||
if (scaleOptions[key] !== undefined) {
|
||||
return scaleOptions[key];
|
||||
}
|
||||
|
||||
// Valores por defecto
|
||||
const defaults = {
|
||||
duration: 10000,
|
||||
delay: 0,
|
||||
refresh: 1000,
|
||||
frameRate: 30,
|
||||
pause: false,
|
||||
ttl: undefined,
|
||||
onRefresh: null
|
||||
};
|
||||
|
||||
return defaults[key];
|
||||
}
|
||||
|
||||
function getAxisMap(element, keys, meta) {
|
||||
const axis = meta.vAxisID || 'y';
|
||||
return keys[axis] || [];
|
||||
}
|
||||
|
||||
// ============= SCALE.REALTIME.JS (Simplificado) =============
|
||||
class RealTimeScale extends Chart.Scale {
|
||||
constructor(cfg) {
|
||||
super(cfg);
|
||||
this.type = 'realtime';
|
||||
}
|
||||
|
||||
init(scaleOptions, scaleContext) {
|
||||
super.init(scaleOptions, scaleContext);
|
||||
|
||||
const me = this;
|
||||
const chart = me.chart;
|
||||
const streaming = chart.$streaming = chart.$streaming || {};
|
||||
|
||||
// Inicializar opciones de tiempo real
|
||||
const realtimeOptions = me.options.realtime || {};
|
||||
me.realtime = {
|
||||
duration: resolveOption(me, 'duration'),
|
||||
delay: resolveOption(me, 'delay'),
|
||||
refresh: resolveOption(me, 'refresh'),
|
||||
frameRate: resolveOption(me, 'frameRate'),
|
||||
pause: resolveOption(me, 'pause'),
|
||||
ttl: resolveOption(me, 'ttl'),
|
||||
onRefresh: resolveOption(me, 'onRefresh')
|
||||
};
|
||||
|
||||
// Configurar intervalo de actualización
|
||||
if (!streaming.intervalId && me.realtime.refresh > 0) {
|
||||
streaming.intervalId = setInterval(() => {
|
||||
if (!me.realtime.pause && typeof me.realtime.onRefresh === 'function') {
|
||||
me.realtime.onRefresh(chart);
|
||||
}
|
||||
me.update();
|
||||
chart.update('quiet');
|
||||
}, me.realtime.refresh);
|
||||
}
|
||||
}
|
||||
|
||||
update(args) {
|
||||
const me = this;
|
||||
const chart = me.chart;
|
||||
|
||||
if (!chart.data || !chart.data.datasets) {
|
||||
return;
|
||||
}
|
||||
|
||||
const now = Date.now();
|
||||
const duration = me.realtime.duration;
|
||||
const delay = me.realtime.delay;
|
||||
const ttl = me.realtime.ttl;
|
||||
|
||||
// Calcular ventana de tiempo
|
||||
me.max = now - delay;
|
||||
me.min = me.max - duration;
|
||||
|
||||
// Limpiar datos antiguos si TTL está configurado
|
||||
if (ttl) {
|
||||
const cutoff = now - ttl;
|
||||
chart.data.datasets.forEach(dataset => {
|
||||
if (dataset.data) {
|
||||
dataset.data = dataset.data.filter(point => {
|
||||
return point.x > cutoff;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
super.update(args);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
const me = this;
|
||||
const chart = me.chart;
|
||||
const streaming = chart.$streaming;
|
||||
|
||||
if (streaming && streaming.intervalId) {
|
||||
clearInterval(streaming.intervalId);
|
||||
delete streaming.intervalId;
|
||||
}
|
||||
|
||||
super.destroy();
|
||||
}
|
||||
|
||||
static id = 'realtime';
|
||||
static defaults = {
|
||||
realtime: {
|
||||
duration: 10000,
|
||||
delay: 0,
|
||||
refresh: 1000,
|
||||
frameRate: 30,
|
||||
pause: false,
|
||||
ttl: undefined,
|
||||
onRefresh: null
|
||||
},
|
||||
time: {
|
||||
unit: 'second',
|
||||
displayFormats: {
|
||||
second: 'HH:mm:ss'
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// ============= PLUGIN.STREAMING.JS (Simplificado) =============
|
||||
const streamingPlugin = {
|
||||
id: 'streaming',
|
||||
|
||||
beforeInit(chart) {
|
||||
const streaming = chart.$streaming = chart.$streaming || {};
|
||||
streaming.enabled = false;
|
||||
|
||||
// Detectar si hay escalas realtime
|
||||
const scales = chart.options.scales || {};
|
||||
Object.keys(scales).forEach(scaleId => {
|
||||
if (scales[scaleId].type === 'realtime') {
|
||||
streaming.enabled = true;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
afterInit(chart) {
|
||||
const streaming = chart.$streaming;
|
||||
if (streaming && streaming.enabled) {
|
||||
// Configurar actualización automática
|
||||
const update = chart.update;
|
||||
chart.update = function (mode) {
|
||||
if (mode === 'quiet') {
|
||||
// Actualización silenciosa para streaming
|
||||
Chart.prototype.update.call(this, mode);
|
||||
} else {
|
||||
update.call(this, mode);
|
||||
}
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
beforeUpdate(chart) {
|
||||
const streaming = chart.$streaming;
|
||||
if (!streaming || !streaming.enabled) return;
|
||||
|
||||
// Permitir que las líneas Bézier se extiendan fuera del área del gráfico
|
||||
const elements = chart.options.elements || {};
|
||||
if (elements.line) {
|
||||
elements.line.capBezierPoints = false;
|
||||
}
|
||||
},
|
||||
|
||||
destroy(chart) {
|
||||
const streaming = chart.$streaming;
|
||||
if (streaming && streaming.intervalId) {
|
||||
clearInterval(streaming.intervalId);
|
||||
delete streaming.intervalId;
|
||||
}
|
||||
delete chart.$streaming;
|
||||
}
|
||||
};
|
||||
|
||||
// ============= REGISTRO DE COMPONENTES =============
|
||||
|
||||
// Registrar escala realtime
|
||||
Chart.register(RealTimeScale);
|
||||
|
||||
// Registrar plugin de streaming
|
||||
Chart.register(streamingPlugin);
|
||||
|
||||
// ============= UTILIDADES PARA LA APLICACIÓN =============
|
||||
|
||||
/**
|
||||
* Crea una configuración de Chart.js optimizada para streaming
|
||||
*/
|
||||
function createStreamingChartConfig(options = {}) {
|
||||
const config = {
|
||||
type: 'line',
|
||||
data: {
|
||||
datasets: []
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
animation: false, // Desactivar animaciones para mejor performance
|
||||
|
||||
scales: {
|
||||
x: {
|
||||
type: 'realtime',
|
||||
realtime: {
|
||||
duration: options.duration || 60000, // 60 segundos por defecto
|
||||
delay: options.delay || 0,
|
||||
refresh: options.refresh || 1000, // 1 segundo
|
||||
frameRate: options.frameRate || 30,
|
||||
pause: options.pause || false,
|
||||
ttl: options.ttl || undefined,
|
||||
onRefresh: options.onRefresh || null
|
||||
},
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Tiempo'
|
||||
}
|
||||
},
|
||||
y: {
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Valor'
|
||||
},
|
||||
min: options.yMin,
|
||||
max: options.yMax
|
||||
}
|
||||
},
|
||||
|
||||
plugins: {
|
||||
legend: {
|
||||
display: true,
|
||||
position: 'top'
|
||||
},
|
||||
tooltip: {
|
||||
mode: 'index',
|
||||
intersect: false
|
||||
}
|
||||
},
|
||||
|
||||
interaction: {
|
||||
mode: 'nearest',
|
||||
axis: 'x',
|
||||
intersect: false
|
||||
},
|
||||
|
||||
elements: {
|
||||
point: {
|
||||
radius: 0, // Sin puntos para mejor performance
|
||||
hoverRadius: 3
|
||||
},
|
||||
line: {
|
||||
tension: 0.1,
|
||||
borderWidth: 2
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: ['streaming']
|
||||
};
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Agrega datos a un dataset de streaming
|
||||
*/
|
||||
function addStreamingData(chart, datasetIndex, data) {
|
||||
if (!chart || !chart.data || !chart.data.datasets[datasetIndex]) {
|
||||
return;
|
||||
}
|
||||
|
||||
const dataset = chart.data.datasets[datasetIndex];
|
||||
if (!dataset.data) {
|
||||
dataset.data = [];
|
||||
}
|
||||
|
||||
// Agregar nuevo punto con timestamp
|
||||
const timestamp = data.x || Date.now();
|
||||
dataset.data.push({
|
||||
x: timestamp,
|
||||
y: data.y
|
||||
});
|
||||
|
||||
// Chart.js se encarga automáticamente de eliminar datos antiguos
|
||||
// basado en la configuración de TTL y duration de la escala realtime
|
||||
}
|
||||
|
||||
/**
|
||||
* Controla la pausa/reanudación del streaming
|
||||
*/
|
||||
function setStreamingPause(chart, paused) {
|
||||
if (!chart || !chart.$streaming) return;
|
||||
|
||||
const scales = chart.scales;
|
||||
Object.keys(scales).forEach(scaleId => {
|
||||
const scale = scales[scaleId];
|
||||
if (scale instanceof RealTimeScale) {
|
||||
scale.realtime.pause = paused;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Limpia todos los datos de streaming
|
||||
*/
|
||||
function clearStreamingData(chart) {
|
||||
if (!chart || !chart.data) return;
|
||||
|
||||
chart.data.datasets.forEach(dataset => {
|
||||
if (dataset.data) {
|
||||
dataset.data.length = 0;
|
||||
}
|
||||
});
|
||||
|
||||
chart.update('quiet');
|
||||
}
|
||||
|
||||
// ============= EXPORTS =============
|
||||
|
||||
// Exportar para uso en la aplicación
|
||||
exports.RealTimeScale = RealTimeScale;
|
||||
exports.streamingPlugin = streamingPlugin;
|
||||
exports.createStreamingChartConfig = createStreamingChartConfig;
|
||||
exports.addStreamingData = addStreamingData;
|
||||
exports.setStreamingPause = setStreamingPause;
|
||||
exports.clearStreamingData = clearStreamingData;
|
||||
|
||||
// Hacer disponible globalmente
|
||||
if (typeof window !== 'undefined') {
|
||||
window.ChartStreaming = {
|
||||
createStreamingChartConfig,
|
||||
addStreamingData,
|
||||
setStreamingPause,
|
||||
clearStreamingData,
|
||||
RealTimeScale,
|
||||
streamingPlugin
|
||||
};
|
||||
}
|
||||
|
||||
console.log('📈 Chart.js Streaming Plugin loaded successfully');
|
||||
});
|
|
@ -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
|
||||
|
|
|
@ -3,10 +3,60 @@
|
|||
* Maneja sesiones de plotting con Chart.js y comunicación con el backend
|
||||
*/
|
||||
|
||||
// 🔧 Global debugging para plots
|
||||
window.enablePlotDebug = () => {
|
||||
window.plotDebugEnabled = true;
|
||||
console.log('📈 Plot debugging enabled. Check console for detailed logs.');
|
||||
};
|
||||
|
||||
window.disablePlotDebug = () => {
|
||||
window.plotDebugEnabled = false;
|
||||
console.log('📈 Plot debugging disabled.');
|
||||
};
|
||||
|
||||
// Helper function para logging condicional
|
||||
function plotDebugLog(...args) {
|
||||
if (window.plotDebugEnabled) {
|
||||
console.debug(...args);
|
||||
}
|
||||
}
|
||||
|
||||
// 🔧 Test function para verificar que todo funciona
|
||||
window.testPlotSystem = async () => {
|
||||
console.log('📈 Testing plot system...');
|
||||
|
||||
try {
|
||||
// Test 1: Verificar disponibilidad de variables
|
||||
const varsResponse = await fetch('/api/plots/variables');
|
||||
const varsData = await varsResponse.json();
|
||||
console.log('✅ Available variables:', varsData.available_variables.length);
|
||||
console.log('✅ Boolean variables for triggers:', varsData.boolean_variables.length);
|
||||
|
||||
// Test 2: Verificar plots existentes
|
||||
const plotsResponse = await fetch('/api/plots');
|
||||
const plotsData = await plotsResponse.json();
|
||||
console.log('✅ Existing plot sessions:', plotsData.sessions.length);
|
||||
|
||||
// Test 3: Si hay plots, verificar datos
|
||||
if (plotsData.sessions.length > 0) {
|
||||
const sessionId = plotsData.sessions[0].session_id;
|
||||
const dataResponse = await fetch(`/api/plots/${sessionId}/data`);
|
||||
const dataResult = await dataResponse.json();
|
||||
console.log(`✅ Plot ${sessionId} has ${dataResult.datasets?.length || 0} datasets with ${dataResult.data_points_count || 0} points`);
|
||||
}
|
||||
|
||||
console.log('📈 Plot system test completed. Enable debug with enablePlotDebug() for detailed logs.');
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Plot system test failed:', error);
|
||||
}
|
||||
};
|
||||
|
||||
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 +98,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 +136,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 +148,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,13 +164,35 @@ 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 {
|
||||
// 🚀 NUEVO: Para streaming, el refreshStreamingData maneja la actualización automática
|
||||
// Esta función ahora es principalmente para compatibilidad y casos especiales
|
||||
const sessionData = this.sessions.get(sessionId);
|
||||
if (!sessionData || !sessionData.chart) {
|
||||
return;
|
||||
}
|
||||
|
||||
const response = await fetch(`/api/plots/${sessionId}/data`);
|
||||
const plotData = await response.json();
|
||||
|
||||
if (plotData.datasets) {
|
||||
// 🔧 DEBUG: Log para troubleshooting
|
||||
if (plotData.datasets.length > 1) {
|
||||
plotDebugLog(`📈 Plot ${sessionId}: Manual update ${plotData.datasets.length} variables, ${plotData.data_points_count} total points`);
|
||||
}
|
||||
this.updateChart(sessionId, plotData);
|
||||
} else {
|
||||
console.warn(`📈 Plot ${sessionId}: No datasets received from API`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error updating session ${sessionId}:`, error);
|
||||
|
@ -133,91 +240,107 @@ class PlotManager {
|
|||
|
||||
document.getElementById('plot-sessions-container').appendChild(container);
|
||||
|
||||
// Crear Chart.js
|
||||
// 🚀 NUEVO: Usar chartjs-plugin-streaming para crear chart
|
||||
const ctx = document.getElementById(`chart-${sessionId}`).getContext('2d');
|
||||
const chart = new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
datasets: []
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
animation: {
|
||||
duration: 0 // Sin animación para mejor performance
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
type: 'time',
|
||||
time: {
|
||||
unit: 'second',
|
||||
displayFormats: {
|
||||
second: 'HH:mm:ss'
|
||||
}
|
||||
},
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Time'
|
||||
}
|
||||
},
|
||||
y: {
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Value'
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: {
|
||||
legend: {
|
||||
display: true,
|
||||
position: 'top'
|
||||
},
|
||||
tooltip: {
|
||||
mode: 'index',
|
||||
intersect: false
|
||||
}
|
||||
},
|
||||
interaction: {
|
||||
mode: 'nearest',
|
||||
axis: 'x',
|
||||
intersect: false
|
||||
}
|
||||
|
||||
// Configurar streaming con la nueva librería
|
||||
const streamingConfig = window.ChartStreaming.createStreamingChartConfig({
|
||||
duration: (config.time_window || 60) * 1000, // Convertir a milisegundos
|
||||
refresh: 500, // Actualizar cada 500ms
|
||||
frameRate: 30,
|
||||
pause: false,
|
||||
yMin: config.y_min,
|
||||
yMax: config.y_max,
|
||||
onRefresh: (chart) => {
|
||||
// Esta función se llama automáticamente para obtener nuevos datos
|
||||
this.refreshStreamingData(sessionId, chart);
|
||||
}
|
||||
});
|
||||
|
||||
this.sessions.set(sessionId, chart);
|
||||
const chart = new Chart(ctx, streamingConfig);
|
||||
|
||||
// Actualizar datos iniciales
|
||||
this.updateSessionData(sessionId);
|
||||
this.sessions.set(sessionId, {
|
||||
chart: chart,
|
||||
config: config,
|
||||
lastDataFetch: 0,
|
||||
datasets: new Map() // Para tracking de datasets por variable
|
||||
});
|
||||
|
||||
console.log(`📈 Created plot session: ${sessionId}`);
|
||||
// Inicializar datasets para las variables del plot
|
||||
this.initializeStreamingDatasets(sessionId, config);
|
||||
|
||||
console.log(`📈 Created streaming plot session: ${sessionId}`);
|
||||
}
|
||||
|
||||
updateChart(sessionId, plotData) {
|
||||
const chart = this.sessions.get(sessionId);
|
||||
if (!chart) return;
|
||||
const sessionData = this.sessions.get(sessionId);
|
||||
if (!sessionData || !sessionData.chart) {
|
||||
console.warn(`📈 Plot ${sessionId}: Chart not found in sessions`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Actualizar datasets
|
||||
chart.data.datasets = plotData.datasets;
|
||||
const chart = sessionData.chart;
|
||||
|
||||
// Actualizar escalas Y si están definidas
|
||||
if (plotData.y_min !== undefined || plotData.y_max !== undefined) {
|
||||
// 🔧 DEBUG: Verificar datos antes de actualizar
|
||||
if (plotData.datasets && plotData.datasets.length > 0) {
|
||||
plotDebugLog(`📈 Plot ${sessionId}: Updating streaming chart with ${plotData.datasets.length} datasets`);
|
||||
plotData.datasets.forEach((dataset, idx) => {
|
||||
plotDebugLog(` - Variable ${idx + 1}: ${dataset.label} (${dataset.data.length} points)`);
|
||||
});
|
||||
}
|
||||
|
||||
// 🚀 NUEVO: Para streaming, agregamos nuevos datos en lugar de reemplazar todo
|
||||
this.updateStreamingData(sessionId, plotData);
|
||||
|
||||
// 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
|
||||
const pointsElement = document.getElementById(`points-${sessionId}`);
|
||||
if (pointsElement) {
|
||||
pointsElement.textContent = plotData.data_points_count || 0;
|
||||
const totalPoints = plotData.data_points_count || 0;
|
||||
pointsElement.textContent = totalPoints;
|
||||
|
||||
// 🔧 DEBUG: Log si el contador no coincide
|
||||
const calculatedPoints = (plotData.datasets || []).reduce((sum, dataset) => sum + (dataset.data?.length || 0), 0);
|
||||
if (totalPoints !== calculatedPoints) {
|
||||
plotDebugLog(`📈 Plot ${sessionId}: Points mismatch - reported: ${totalPoints}, calculated: ${calculatedPoints}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Actualizar chart
|
||||
chart.update('none');
|
||||
// 🔑 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
|
||||
});
|
||||
}
|
||||
|
||||
// El chart de streaming se actualiza automáticamente, no necesitamos llamar update manualmente
|
||||
}
|
||||
|
||||
async controlPlot(sessionId, action) {
|
||||
try {
|
||||
// 🚀 NUEVO: Controlar streaming localmente para algunas acciones
|
||||
if (action === 'pause') {
|
||||
this.setStreamingPause(sessionId, true);
|
||||
} else if (action === 'start') {
|
||||
this.setStreamingPause(sessionId, false);
|
||||
} else if (action === 'clear') {
|
||||
this.clearStreamingData(sessionId);
|
||||
}
|
||||
|
||||
const response = await fetch(`/api/plots/${sessionId}/control`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
|
@ -229,12 +352,17 @@ class PlotManager {
|
|||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
// Actualizar UI según la acción
|
||||
// 🔑 NUEVO: Actualizar status inmediatamente después de la acción
|
||||
await this.updateSessionStatus(sessionId);
|
||||
|
||||
// Para stop, también pausar el streaming
|
||||
if (action === 'stop') {
|
||||
this.removeChart(sessionId);
|
||||
} else {
|
||||
// Actualizar datos inmediatamente
|
||||
await this.updateSessionData(sessionId);
|
||||
this.setStreamingPause(sessionId, true);
|
||||
}
|
||||
|
||||
// Para start, asegurar que el streaming esté activo
|
||||
if (action === 'start') {
|
||||
this.setStreamingPause(sessionId, false);
|
||||
}
|
||||
|
||||
showNotification(result.message, 'success');
|
||||
|
@ -247,6 +375,31 @@ 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);
|
||||
|
||||
// 🚀 NUEVO: Actualizar estado de streaming basado en el status del backend
|
||||
const sessionData = this.sessions.get(sessionId);
|
||||
if (sessionData) {
|
||||
// Actualizar configuración local
|
||||
sessionData.config = data.config;
|
||||
|
||||
// Controlar pausa del streaming basado en el estado del plot
|
||||
const shouldPause = !data.config.is_active || data.config.is_paused;
|
||||
this.setStreamingPause(sessionId, shouldPause);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error updating session status ${sessionId}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
async removePlot(sessionId) {
|
||||
try {
|
||||
const response = await fetch(`/api/plots/${sessionId}`, {
|
||||
|
@ -269,9 +422,9 @@ class PlotManager {
|
|||
|
||||
removeChart(sessionId) {
|
||||
// Destruir Chart.js
|
||||
const chart = this.sessions.get(sessionId);
|
||||
if (chart) {
|
||||
chart.destroy();
|
||||
const sessionData = this.sessions.get(sessionId);
|
||||
if (sessionData && sessionData.chart) {
|
||||
sessionData.chart.destroy();
|
||||
this.sessions.delete(sessionId);
|
||||
}
|
||||
|
||||
|
@ -282,70 +435,208 @@ class PlotManager {
|
|||
}
|
||||
}
|
||||
|
||||
// 🚀 NUEVAS FUNCIONES PARA STREAMING
|
||||
|
||||
/**
|
||||
* Inicializa los datasets de streaming para las variables del plot
|
||||
*/
|
||||
initializeStreamingDatasets(sessionId, config) {
|
||||
const sessionData = this.sessions.get(sessionId);
|
||||
if (!sessionData || !sessionData.chart || !config.variables) {
|
||||
return;
|
||||
}
|
||||
|
||||
const chart = sessionData.chart;
|
||||
const datasets = [];
|
||||
|
||||
config.variables.forEach((variable, index) => {
|
||||
const color = this.getColor(variable, index);
|
||||
const dataset = {
|
||||
label: variable,
|
||||
data: [],
|
||||
borderColor: color,
|
||||
backgroundColor: color + '20', // Color con transparencia
|
||||
borderWidth: 2,
|
||||
fill: false,
|
||||
pointRadius: 0,
|
||||
pointHoverRadius: 3,
|
||||
tension: 0.1
|
||||
};
|
||||
|
||||
datasets.push(dataset);
|
||||
sessionData.datasets.set(variable, index);
|
||||
});
|
||||
|
||||
chart.data.datasets = datasets;
|
||||
chart.update('quiet');
|
||||
|
||||
plotDebugLog(`📈 Plot ${sessionId}: Initialized ${datasets.length} streaming datasets`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Función llamada automáticamente por chartjs-plugin-streaming para obtener nuevos datos
|
||||
*/
|
||||
async refreshStreamingData(sessionId, chart) {
|
||||
try {
|
||||
// Evitar llamadas muy frecuentes
|
||||
const sessionData = this.sessions.get(sessionId);
|
||||
if (!sessionData) return;
|
||||
|
||||
const now = Date.now();
|
||||
if (now - sessionData.lastDataFetch < 400) { // Mínimo 400ms entre llamadas
|
||||
return;
|
||||
}
|
||||
sessionData.lastDataFetch = now;
|
||||
|
||||
// Obtener datos del backend
|
||||
const response = await fetch(`/api/plots/${sessionId}/data`);
|
||||
const plotData = await response.json();
|
||||
|
||||
if (plotData && plotData.datasets) {
|
||||
// Para streaming, solo agregamos los datos más recientes
|
||||
this.addLatestDataToStreamingChart(sessionId, plotData);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error(`Error refreshing streaming data for ${sessionId}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Actualiza los datos de streaming del chart
|
||||
*/
|
||||
updateStreamingData(sessionId, plotData) {
|
||||
const sessionData = this.sessions.get(sessionId);
|
||||
if (!sessionData || !sessionData.chart || !plotData.datasets) {
|
||||
return;
|
||||
}
|
||||
|
||||
const chart = sessionData.chart;
|
||||
|
||||
// Para cada dataset del backend, agregar los nuevos puntos
|
||||
plotData.datasets.forEach((backendDataset, index) => {
|
||||
if (chart.data.datasets[index] && backendDataset.data) {
|
||||
const chartDataset = chart.data.datasets[index];
|
||||
|
||||
// Obtener los últimos puntos que no tengamos aún
|
||||
const existingPoints = chartDataset.data.length;
|
||||
const newPoints = backendDataset.data.slice(existingPoints);
|
||||
|
||||
newPoints.forEach(point => {
|
||||
window.ChartStreaming.addStreamingData(chart, index, {
|
||||
x: point.x,
|
||||
y: point.y
|
||||
});
|
||||
});
|
||||
|
||||
plotDebugLog(`📈 Plot ${sessionId}: Added ${newPoints.length} new points to dataset ${index}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Agrega solo los datos más recientes al chart de streaming
|
||||
*/
|
||||
addLatestDataToStreamingChart(sessionId, plotData) {
|
||||
const sessionData = this.sessions.get(sessionId);
|
||||
if (!sessionData || !sessionData.chart || !plotData.datasets) {
|
||||
return;
|
||||
}
|
||||
|
||||
const chart = sessionData.chart;
|
||||
const now = Date.now();
|
||||
|
||||
// Para cada variable, agregar el punto más reciente
|
||||
plotData.datasets.forEach((dataset, index) => {
|
||||
if (dataset.data && dataset.data.length > 0) {
|
||||
// Obtener el punto más reciente
|
||||
const latestPoint = dataset.data[dataset.data.length - 1];
|
||||
|
||||
if (latestPoint && latestPoint.y !== null && latestPoint.y !== undefined) {
|
||||
window.ChartStreaming.addStreamingData(chart, index, {
|
||||
x: now, // Usar timestamp actual para streaming en tiempo real
|
||||
y: latestPoint.y
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Controla la pausa/reanudación del streaming
|
||||
*/
|
||||
setStreamingPause(sessionId, paused) {
|
||||
const sessionData = this.sessions.get(sessionId);
|
||||
if (sessionData && sessionData.chart) {
|
||||
window.ChartStreaming.setStreamingPause(sessionData.chart, paused);
|
||||
plotDebugLog(`📈 Plot ${sessionId}: Streaming ${paused ? 'paused' : 'resumed'}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Limpia todos los datos de streaming
|
||||
*/
|
||||
clearStreamingData(sessionId) {
|
||||
const sessionData = this.sessions.get(sessionId);
|
||||
if (sessionData && sessionData.chart) {
|
||||
window.ChartStreaming.clearStreamingData(sessionData.chart);
|
||||
plotDebugLog(`📈 Plot ${sessionId}: Streaming data cleared`);
|
||||
}
|
||||
}
|
||||
|
||||
async createPlotSessionTab(sessionId, sessionInfo) {
|
||||
// Crear tab dinámico para el plot
|
||||
if (typeof tabManager !== 'undefined') {
|
||||
tabManager.createPlotTab(sessionId, sessionInfo.name || `Plot ${sessionId}`);
|
||||
}
|
||||
|
||||
// Crear el Chart.js en el canvas del tab
|
||||
// 🚀 NUEVO: Obtener configuración completa del plot para el streaming
|
||||
let plotConfig = sessionInfo;
|
||||
try {
|
||||
const configResponse = await fetch(`/api/plots/${sessionId}/config`);
|
||||
const configData = await configResponse.json();
|
||||
if (configData.success && configData.config) {
|
||||
plotConfig = configData.config;
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`Could not load plot config for ${sessionId}, using basic info`);
|
||||
}
|
||||
|
||||
// Crear el Chart.js con streaming en el canvas del tab
|
||||
const ctx = document.getElementById(`chart-${sessionId}`).getContext('2d');
|
||||
const chart = new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
datasets: []
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
animation: {
|
||||
duration: 0 // Sin animación para mejor performance
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
type: 'time',
|
||||
time: {
|
||||
unit: 'second',
|
||||
displayFormats: {
|
||||
second: 'HH:mm:ss'
|
||||
}
|
||||
},
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Time'
|
||||
}
|
||||
},
|
||||
y: {
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Value'
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: {
|
||||
legend: {
|
||||
display: true,
|
||||
position: 'top'
|
||||
},
|
||||
tooltip: {
|
||||
mode: 'index',
|
||||
intersect: false
|
||||
}
|
||||
},
|
||||
interaction: {
|
||||
mode: 'nearest',
|
||||
axis: 'x',
|
||||
intersect: false
|
||||
}
|
||||
|
||||
// Configurar streaming con la nueva librería
|
||||
const streamingConfig = window.ChartStreaming.createStreamingChartConfig({
|
||||
duration: (plotConfig.time_window || 60) * 1000, // Convertir a milisegundos
|
||||
refresh: 500, // Actualizar cada 500ms
|
||||
frameRate: 30,
|
||||
pause: !plotConfig.is_active, // Pausar si no está activo
|
||||
yMin: plotConfig.y_min,
|
||||
yMax: plotConfig.y_max,
|
||||
onRefresh: (chart) => {
|
||||
// Esta función se llama automáticamente para obtener nuevos datos
|
||||
this.refreshStreamingData(sessionId, chart);
|
||||
}
|
||||
});
|
||||
|
||||
this.sessions.set(sessionId, chart);
|
||||
const chart = new Chart(ctx, streamingConfig);
|
||||
|
||||
this.sessions.set(sessionId, {
|
||||
chart: chart,
|
||||
config: plotConfig,
|
||||
lastDataFetch: 0,
|
||||
datasets: new Map() // Para tracking de datasets por variable
|
||||
});
|
||||
|
||||
// Inicializar datasets para las variables del plot
|
||||
if (plotConfig.variables) {
|
||||
this.initializeStreamingDatasets(sessionId, plotConfig);
|
||||
}
|
||||
|
||||
// Actualizar estadísticas del plot
|
||||
this.updatePlotStats(sessionId, sessionInfo);
|
||||
|
||||
console.log(`📈 Created plot tab and chart for session: ${sessionId}`);
|
||||
console.log(`📈 Created streaming plot tab and chart for session: ${sessionId}`);
|
||||
}
|
||||
|
||||
updatePlotStats(sessionId, sessionInfo) {
|
||||
|
@ -377,17 +668,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 +698,9 @@ class PlotManager {
|
|||
tabManager.switchSubTab(`plot-${result.session_id}`);
|
||||
}
|
||||
|
||||
// 🔑 NUEVO: Los plots se auto-inician en el backend, no necesitamos start manual aquí
|
||||
console.log(`Plot session created and auto-started: ${result.session_id}`);
|
||||
|
||||
showNotification(result.message, 'success');
|
||||
return result.session_id;
|
||||
} else {
|
||||
|
@ -947,6 +1253,9 @@ class PlotManager {
|
|||
if (config.y_min) config.y_min = parseFloat(config.y_min);
|
||||
if (config.y_max) config.y_max = parseFloat(config.y_max);
|
||||
|
||||
// 🔧 DEBUG: Log configuración del plot
|
||||
plotDebugLog('📈 Creating plot with config:', config);
|
||||
|
||||
try {
|
||||
let response;
|
||||
let sessionId = null;
|
||||
|
@ -1041,8 +1350,10 @@ class PlotManager {
|
|||
this.stopAutoUpdate();
|
||||
|
||||
// Destruir todos los charts
|
||||
for (const [sessionId, chart] of this.sessions) {
|
||||
chart.destroy();
|
||||
for (const [sessionId, sessionData] of this.sessions) {
|
||||
if (sessionData && sessionData.chart) {
|
||||
sessionData.chart.destroy();
|
||||
}
|
||||
}
|
||||
this.sessions.clear();
|
||||
|
||||
|
|
|
@ -0,0 +1,856 @@
|
|||
plotting.js:20 📈 Plot plot_16: Updating streaming chart with 1 datasets
|
||||
plotting.js:20 - Variable 1: UR29_Brix (31 points)
|
||||
plotting.js:20 📈 Plot plot_16: Added 0 new points to dataset 0
|
||||
debug-streaming.js:8 🔧 DIAGNÓSTICO DETALLADO DE STREAMING
|
||||
debug-streaming.js:9 ============================================================
|
||||
debug-streaming.js:23
|
||||
1️⃣ VERIFICANDO LIBRERÍAS...
|
||||
debug-streaming.js:30 Chart.js: ✅
|
||||
debug-streaming.js:31 ChartStreaming: ✅
|
||||
debug-streaming.js:32 PlotManager: ✅
|
||||
debug-streaming.js:41
|
||||
2️⃣ VERIFICANDO PLOT MANAGER...
|
||||
debug-streaming.js:44 ✅ PlotManager inicializado
|
||||
debug-streaming.js:45 📊 Sesiones activas: 1
|
||||
debug-streaming.js:59
|
||||
3️⃣ ANALIZANDO SESIÓN DE PLOT...
|
||||
debug-streaming.js:65 📈 Sesión encontrada: plot_16
|
||||
debug-streaming.js:78 Chart Config: {hasChart: true, chartType: 'line', scaleType: 'realtime', scaleConstructor: 'RealTimeScale', streamingEnabled: true, …}
|
||||
debug-streaming.js:85 ✅ Escala realtime configurada correctamente
|
||||
debug-streaming.js:93 ✅ Streaming habilitado en el chart
|
||||
debug-streaming.js:98
|
||||
4️⃣ VERIFICANDO DATOS DEL BACKEND...
|
||||
plotting.js:9 📈 Plot debugging enabled. Check console for detailed logs.
|
||||
debug-streaming.js:278 ⚡ TEST RÁPIDO DE STREAMING
|
||||
debug-streaming.js:279 ------------------------------
|
||||
debug-streaming.js:288 plotManager: ✅ true
|
||||
debug-streaming.js:288 sessions: ✅ 1
|
||||
debug-streaming.js:288 chartStreaming: ✅ true
|
||||
debug-streaming.js:293
|
||||
🧪 Probando sesión: plot_16
|
||||
debug-streaming.js:8 🔧 DIAGNÓSTICO DETALLADO DE STREAMING
|
||||
debug-streaming.js:9 ============================================================
|
||||
debug-streaming.js:23
|
||||
1️⃣ VERIFICANDO LIBRERÍAS...
|
||||
debug-streaming.js:30 Chart.js: ✅
|
||||
debug-streaming.js:31 ChartStreaming: ✅
|
||||
debug-streaming.js:32 PlotManager: ✅
|
||||
debug-streaming.js:41
|
||||
2️⃣ VERIFICANDO PLOT MANAGER...
|
||||
debug-streaming.js:44 ✅ PlotManager inicializado
|
||||
debug-streaming.js:45 📊 Sesiones activas: 1
|
||||
debug-streaming.js:59
|
||||
3️⃣ ANALIZANDO SESIÓN DE PLOT...
|
||||
debug-streaming.js:65 📈 Sesión encontrada: plot_16
|
||||
debug-streaming.js:78 Chart Config: {hasChart: true, chartType: 'line', scaleType: 'realtime', scaleConstructor: 'RealTimeScale', streamingEnabled: true, …}
|
||||
debug-streaming.js:85 ✅ Escala realtime configurada correctamente
|
||||
debug-streaming.js:93 ✅ Streaming habilitado en el chart
|
||||
debug-streaming.js:98
|
||||
4️⃣ VERIFICANDO DATOS DEL BACKEND...
|
||||
chartjs-plugin-streaming.js:75 📈 RealTimeScale DEBUG - scaleOptions: Proxy(Object) {_cacheable: false, _proxy: Proxy(Object), _context: {…}, _subProxy: undefined, _stack: Set(0), …}
|
||||
chartjs-plugin-streaming.js:76 📈 RealTimeScale DEBUG - me.options: Proxy(Object) {_cacheable: false, _proxy: Proxy(Object), _context: {…}, _subProxy: undefined, _stack: Set(0), …}
|
||||
chartjs-plugin-streaming.js:77 📈 RealTimeScale DEBUG - me.options.realtime: Proxy(Object) {_cacheable: false, _proxy: Proxy(Object), _context: {…}, _subProxy: undefined, _stack: Set(0), …}
|
||||
plotting.js:20 📈 Plot plot_16: Fetching data from backend...
|
||||
chartjs-plugin-streaming.js:81 📈 RealTimeScale DEBUG - onRefresh resolved: null object
|
||||
chartjs-plugin-streaming.js:93 📈 RealTimeScale initialized: {duration: 60000, refresh: 500, pause: false, hasOnRefresh: false}
|
||||
debug-streaming.js:8 🔧 DIAGNÓSTICO DETALLADO DE STREAMING
|
||||
debug-streaming.js:9 ============================================================
|
||||
debug-streaming.js:23
|
||||
1️⃣ VERIFICANDO LIBRERÍAS...
|
||||
debug-streaming.js:30 Chart.js: ✅
|
||||
debug-streaming.js:31 ChartStreaming: ✅
|
||||
debug-streaming.js:32 PlotManager: ✅
|
||||
debug-streaming.js:41
|
||||
2️⃣ VERIFICANDO PLOT MANAGER...
|
||||
debug-streaming.js:44 ✅ PlotManager inicializado
|
||||
debug-streaming.js:45 📊 Sesiones activas: 1
|
||||
|
||||
3️⃣ ANALIZANDO SESIÓN DE PLOT...
|
||||
debug-streaming.js:65 📈 Sesión encontrada: plot_16
|
||||
debug-streaming.js:78 Chart Config: {hasChart: true, chartType: 'line', scaleType: 'realtime', scaleConstructor: 'RealTimeScale', streamingEnabled: true, …}
|
||||
debug-streaming.js:85 ✅ Escala realtime configurada correctamente
|
||||
debug-streaming.js:93 ✅ Streaming habilitado en el chart
|
||||
debug-streaming.js:98
|
||||
4️⃣ VERIFICANDO DATOS DEL BACKEND...
|
||||
plotting.js:9 📈 Plot debugging enabled. Check console for detailed logs.
|
||||
debug-streaming.js:278 ⚡ TEST RÁPIDO DE STREAMING
|
||||
debug-streaming.js:279 ------------------------------
|
||||
debug-streaming.js:288 plotManager: ✅ true
|
||||
debug-streaming.js:288 sessions: ✅ 1
|
||||
debug-streaming.js:288 chartStreaming: ✅ true
|
||||
debug-streaming.js:293
|
||||
🧪 Probando sesión: plot_16
|
||||
debug-streaming.js:8 🔧 DIAGNÓSTICO DETALLADO DE STREAMING
|
||||
debug-streaming.js:9 ============================================================
|
||||
debug-streaming.js:23
|
||||
1️⃣ VERIFICANDO LIBRERÍAS...
|
||||
debug-streaming.js:30 Chart.js: ✅
|
||||
debug-streaming.js:31 ChartStreaming: ✅
|
||||
debug-streaming.js:32 PlotManager: ✅
|
||||
debug-streaming.js:41
|
||||
2️⃣ VERIFICANDO PLOT MANAGER...
|
||||
debug-streaming.js:44 ✅ PlotManager inicializado
|
||||
debug-streaming.js:45 📊 Sesiones activas: 1
|
||||
debug-streaming.js:59
|
||||
3️⃣ ANALIZANDO SESIÓN DE PLOT...
|
||||
debug-streaming.js:65 📈 Sesión encontrada: plot_16
|
||||
debug-streaming.js:78 Chart Config: {hasChart: true, chartType: 'line', scaleType: 'realtime', scaleConstructor: 'RealTimeScale', streamingEnabled: true, …}
|
||||
debug-streaming.js:85 ✅ Escala realtime configurada correctamente
|
||||
debug-streaming.js:93 ✅ Streaming habilitado en el chart
|
||||
debug-streaming.js:98
|
||||
4️⃣ VERIFICANDO DATOS DEL BACKEND...
|
||||
debug-streaming.js:103 📊 Respuesta del backend: {success: true, datasets: 1, totalPoints: 32, isActive: true, isPaused: false}
|
||||
debug-streaming.js:112 ✅ Backend devuelve datos
|
||||
debug-streaming.js:116 📈 Primer dataset: {label: 'UR29_Brix', dataPoints: 32, samplePoint: {…}}
|
||||
debug-streaming.js:123 ✅ Dataset tiene puntos de datos
|
||||
debug-streaming.js:130 ⏰ Análisis de timestamps: {firstPointTime: 1753115880879.299, currentTime: 1753115887307, differenceMs: 6427.700927734375, differenceSec: 6, isRecent: true}
|
||||
debug-streaming.js:156
|
||||
5️⃣ PROBANDO FUNCIONALIDAD STREAMING...
|
||||
chartjs-plugin-streaming.js:345 📈 Added point to dataset 0 (UR29_Brix): x=1753115887307, y=67.77492245225156
|
||||
debug-streaming.js:165 🧪 Test de addStreamingData: ✅
|
||||
debug-streaming.js:170 📊 Puntos después del test: 38
|
||||
debug-streaming.js:182
|
||||
6️⃣ RESUMEN DEL DIAGNÓSTICO
|
||||
debug-streaming.js:183 ========================================
|
||||
debug-streaming.js:186 🎉 No se encontraron errores graves
|
||||
debug-streaming.js:199
|
||||
7️⃣ PRÓXIMOS PASOS RECOMENDADOS:
|
||||
debug-streaming.js:213 🔧 4. Si persiste: plotManager.controlPlot("plot_16", "stop") y luego "start"
|
||||
plotting.js:20 📈 Plot plot_16: Streaming resumed
|
||||
🔧 DIAGNÓSTICO DETALLADO DE STREAMING
|
||||
============================================================
|
||||
|
||||
1️⃣ VERIFICANDO LIBRERÍAS...
|
||||
Chart.js: ✅
|
||||
ChartStreaming: ✅
|
||||
PlotManager: ✅
|
||||
|
||||
2️⃣ VERIFICANDO PLOT MANAGER...
|
||||
✅ PlotManager inicializado
|
||||
📊 Sesiones activas: 1
|
||||
|
||||
3️⃣ ANALIZANDO SESIÓN DE PLOT...
|
||||
📈 Sesión encontrada: plot_16
|
||||
Chart Config: {hasChart: true, chartType: 'line', scaleType: 'realtime', scaleConstructor: 'RealTimeScale', streamingEnabled: true, …}
|
||||
✅ Escala realtime configurada correctamente
|
||||
✅ Streaming habilitado en el chart
|
||||
|
||||
4️⃣ VERIFICANDO DATOS DEL BACKEND...
|
||||
plotting.js:9 📈 Plot debugging enabled. Check console for detailed logs.
|
||||
debug-streaming.js:278 ⚡ TEST RÁPIDO DE STREAMING
|
||||
debug-streaming.js:279 ------------------------------
|
||||
debug-streaming.js:288 plotManager: ✅ true
|
||||
debug-streaming.js:288 sessions: ✅ 1
|
||||
debug-streaming.js:288 chartStreaming: ✅ true
|
||||
debug-streaming.js:293
|
||||
🧪 Probando sesión: plot_16
|
||||
debug-streaming.js:8 🔧 DIAGNÓSTICO DETALLADO DE STREAMING
|
||||
debug-streaming.js:9 ============================================================
|
||||
debug-streaming.js:23
|
||||
1️⃣ VERIFICANDO LIBRERÍAS...
|
||||
debug-streaming.js:30 Chart.js: ✅
|
||||
debug-streaming.js:31 ChartStreaming: ✅
|
||||
debug-streaming.js:32 PlotManager: ✅
|
||||
debug-streaming.js:41
|
||||
2️⃣ VERIFICANDO PLOT MANAGER...
|
||||
debug-streaming.js:44 ✅ PlotManager inicializado
|
||||
debug-streaming.js:45 📊 Sesiones activas: 1
|
||||
debug-streaming.js:59
|
||||
3️⃣ ANALIZANDO SESIÓN DE PLOT...
|
||||
debug-streaming.js:65 📈 Sesión encontrada: plot_16
|
||||
debug-streaming.js:78 Chart Config: {hasChart: true, chartType: 'line', scaleType: 'realtime', scaleConstructor: 'RealTimeScale', streamingEnabled: true, …}
|
||||
debug-streaming.js:85 ✅ Escala realtime configurada correctamente
|
||||
debug-streaming.js:93 ✅ Streaming habilitado en el chart
|
||||
debug-streaming.js:98
|
||||
4️⃣ VERIFICANDO DATOS DEL BACKEND...
|
||||
chartjs-plugin-streaming.js:75 📈 RealTimeScale DEBUG - scaleOptions: Proxy(Object) {_cacheable: false, _proxy: Proxy(Object), _context: {…}, _subProxy: undefined, _stack: Set(0), …}
|
||||
chartjs-plugin-streaming.js:76 📈 RealTimeScale DEBUG - me.options: Proxy(Object) {_cacheable: false, _proxy: Proxy(Object), _context: {…}, _subProxy: undefined, _stack: Set(0), …}
|
||||
chartjs-plugin-streaming.js:77 📈 RealTimeScale DEBUG - me.options.realtime: Proxy(Object) {_cacheable: false, _proxy: Proxy(Object), _context: {…}, _subProxy: undefined, _stack: Set(0), …}
|
||||
chartjs-plugin-streaming.js:81 📈 RealTimeScale DEBUG - onRefresh resolved: null object
|
||||
chartjs-plugin-streaming.js:93 📈 RealTimeScale initialized: {duration: 60000, refresh: 500, pause: false, hasOnRefresh: false}
|
||||
debug-streaming.js:103 📊 Respuesta del backend: {success: true, datasets: 1, totalPoints: 34, isActive: true, isPaused: false}
|
||||
debug-streaming.js:112 ✅ Backend devuelve datos
|
||||
debug-streaming.js:116 📈 Primer dataset: {label: 'UR29_Brix', dataPoints: 34, samplePoint: {…}}
|
||||
debug-streaming.js:123 ✅ Dataset tiene puntos de datos
|
||||
debug-streaming.js:130 ⏰ Análisis de timestamps: {firstPointTime: 1753115880879.299, currentTime: 1753115887617, differenceMs: 6737.700927734375, differenceSec: 7, isRecent: true}
|
||||
debug-streaming.js:156
|
||||
5️⃣ PROBANDO FUNCIONALIDAD STREAMING...
|
||||
chartjs-plugin-streaming.js:345 📈 Added point to dataset 0 (UR29_Brix): x=1753115887617, y=11.743880180299548
|
||||
debug-streaming.js:165 🧪 Test de addStreamingData: ✅
|
||||
debug-streaming.js:170 📊 Puntos después del test: 39
|
||||
debug-streaming.js:182
|
||||
6️⃣ RESUMEN DEL DIAGNÓSTICO
|
||||
debug-streaming.js:183 ========================================
|
||||
debug-streaming.js:186 🎉 No se encontraron errores graves
|
||||
debug-streaming.js:199
|
||||
7️⃣ PRÓXIMOS PASOS RECOMENDADOS:
|
||||
debug-streaming.js:213 🔧 4. Si persiste: plotManager.controlPlot("plot_16", "stop") y luego "start"
|
||||
debug-streaming.js:8 🔧 DIAGNÓSTICO DETALLADO DE STREAMING
|
||||
debug-streaming.js:9 ============================================================
|
||||
debug-streaming.js:23
|
||||
1️⃣ VERIFICANDO LIBRERÍAS...
|
||||
debug-streaming.js:30 Chart.js: ✅
|
||||
debug-streaming.js:31 ChartStreaming: ✅
|
||||
debug-streaming.js:32 PlotManager: ✅
|
||||
debug-streaming.js:41
|
||||
2️⃣ VERIFICANDO PLOT MANAGER...
|
||||
debug-streaming.js:44 ✅ PlotManager inicializado
|
||||
debug-streaming.js:45 📊 Sesiones activas: 1
|
||||
debug-streaming.js:59
|
||||
3️⃣ ANALIZANDO SESIÓN DE PLOT...
|
||||
debug-streaming.js:65 📈 Sesión encontrada: plot_16
|
||||
debug-streaming.js:78 Chart Config: {hasChart: true, chartType: 'line', scaleType: 'realtime', scaleConstructor: 'RealTimeScale', streamingEnabled: true, …}
|
||||
debug-streaming.js:85 ✅ Escala realtime configurada correctamente
|
||||
debug-streaming.js:93 ✅ Streaming habilitado en el chart
|
||||
debug-streaming.js:98
|
||||
4️⃣ VERIFICANDO DATOS DEL BACKEND...
|
||||
📈 Plot debugging enabled. Check console for detailed logs.
|
||||
⚡ TEST RÁPIDO DE STREAMING
|
||||
debug-streaming.js:279 ------------------------------
|
||||
debug-streaming.js:288 plotManager: ✅ true
|
||||
debug-streaming.js:288 sessions: ✅ 1
|
||||
debug-streaming.js:288 chartStreaming: ✅ true
|
||||
debug-streaming.js:293
|
||||
🧪 Probando sesión: plot_16
|
||||
debug-streaming.js:8 🔧 DIAGNÓSTICO DETALLADO DE STREAMING
|
||||
debug-streaming.js:9 ============================================================
|
||||
debug-streaming.js:23
|
||||
1️⃣ VERIFICANDO LIBRERÍAS...
|
||||
debug-streaming.js:30 Chart.js: ✅
|
||||
debug-streaming.js:31 ChartStreaming: ✅
|
||||
debug-streaming.js:32 PlotManager: ✅
|
||||
debug-streaming.js:41
|
||||
2️⃣ VERIFICANDO PLOT MANAGER...
|
||||
debug-streaming.js:44 ✅ PlotManager inicializado
|
||||
debug-streaming.js:45 📊 Sesiones activas: 1
|
||||
debug-streaming.js:59
|
||||
3️⃣ ANALIZANDO SESIÓN DE PLOT...
|
||||
debug-streaming.js:65 📈 Sesión encontrada: plot_16
|
||||
debug-streaming.js:78 Chart Config: {hasChart: true, chartType: 'line', scaleType: 'realtime', scaleConstructor: 'RealTimeScale', streamingEnabled: true, …}
|
||||
debug-streaming.js:85 ✅ Escala realtime configurada correctamente
|
||||
debug-streaming.js:93 ✅ Streaming habilitado en el chart
|
||||
debug-streaming.js:98
|
||||
4️⃣ VERIFICANDO DATOS DEL BACKEND...
|
||||
plotting.js:20 📈 Plot plot_16: Received data: {data_points_count: 35, datasets: Array(1), is_active: true, is_paused: false, last_update: 1753115887.7399745, …}
|
||||
plotting.js:20 📈 Plot plot_16: Processing 1 datasets for streaming
|
||||
plotting.js:20 📈 Plot plot_16: Adding 17 new points for UR29_Brix
|
||||
chartjs-plugin-streaming.js:345 📈 Added point to dataset 0 (UR29_Brix): x=1753115884519.0737, y=44.65407943725586
|
||||
chartjs-plugin-streaming.js:345 📈 Added point to dataset 0 (UR29_Brix): x=1753115884719.9353, y=46.29325866699219
|
||||
chartjs-plugin-streaming.js:345 📈 Added point to dataset 0 (UR29_Brix): x=1753115884920.8208, y=47.510704040527344
|
||||
chartjs-plugin-streaming.js:345 📈 Added point to dataset 0 (UR29_Brix): x=1753115885121.4167, y=47.90060806274414
|
||||
chartjs-plugin-streaming.js:345 📈 Added point to dataset 0 (UR29_Brix): x=1753115885323.425, y=47.90060806274414
|
||||
chartjs-plugin-streaming.js:345 📈 Added point to dataset 0 (UR29_Brix): x=1753115885522.0952, y=49.372684478759766
|
||||
chartjs-plugin-streaming.js:345 📈 Added point to dataset 0 (UR29_Brix): x=1753115885724.864, y=50.64583206176758
|
||||
chartjs-plugin-streaming.js:345 📈 Added point to dataset 0 (UR29_Brix): x=1753115885925.8164, y=50.68561935424805
|
||||
chartjs-plugin-streaming.js:345 📈 Added point to dataset 0 (UR29_Brix): x=1753115886127.653, y=51.82349395751953
|
||||
chartjs-plugin-streaming.js:345 📈 Added point to dataset 0 (UR29_Brix): x=1753115886327.9204, y=51.831451416015625
|
||||
chartjs-plugin-streaming.js:345 📈 Added point to dataset 0 (UR29_Brix): x=1753115886529.75, y=52.46802520751953
|
||||
chartjs-plugin-streaming.js:345 📈 Added point to dataset 0 (UR29_Brix): x=1753115886731.3887, y=52.810184478759766
|
||||
chartjs-plugin-streaming.js:345 📈 Added point to dataset 0 (UR29_Brix): x=1753115886934.3843, y=53.60590362548828
|
||||
chartjs-plugin-streaming.js:345 📈 Added point to dataset 0 (UR29_Brix): x=1753115887135.6711, y=53.60590362548828
|
||||
chartjs-plugin-streaming.js:345 📈 Added point to dataset 0 (UR29_Brix): x=1753115887337.3464, y=54.2265625
|
||||
chartjs-plugin-streaming.js:345 📈 Added point to dataset 0 (UR29_Brix): x=1753115887538.3455, y=54.2265625
|
||||
chartjs-plugin-streaming.js:345 📈 Added point to dataset 0 (UR29_Brix): x=1753115887739.9746, y=54.65625
|
||||
plotting.js:20 📈 Plot plot_16: Updated last timestamp for UR29_Brix to 1753115887739.9746
|
||||
chartjs-plugin-streaming.js:75 📈 RealTimeScale DEBUG - scaleOptions: Proxy(Object) {_cacheable: false, _proxy: Proxy(Object), _context: {…}, _subProxy: undefined, _stack: Set(0), …}
|
||||
chartjs-plugin-streaming.js:76 📈 RealTimeScale DEBUG - me.options: Proxy(Object) {_cacheable: false, _proxy: Proxy(Object), _context: {…}, _subProxy: undefined, _stack: Set(0), …}
|
||||
chartjs-plugin-streaming.js:77 📈 RealTimeScale DEBUG - me.options.realtime: Proxy(Object) {_cacheable: false, _proxy: Proxy(Object), _context: {…}, _subProxy: undefined, _stack: Set(0), …}
|
||||
plotting.js:20 📈 Plot plot_16: Fetching data from backend...
|
||||
chartjs-plugin-streaming.js:81 📈 RealTimeScale DEBUG - onRefresh resolved: null object
|
||||
chartjs-plugin-streaming.js:93 📈 RealTimeScale initialized: {duration: 60000, refresh: 500, pause: false, hasOnRefresh: false}
|
||||
debug-streaming.js:8 🔧 DIAGNÓSTICO DETALLADO DE STREAMING
|
||||
debug-streaming.js:9 ============================================================
|
||||
debug-streaming.js:23
|
||||
1️⃣ VERIFICANDO LIBRERÍAS...
|
||||
debug-streaming.js:30 Chart.js: ✅
|
||||
debug-streaming.js:31 ChartStreaming: ✅
|
||||
debug-streaming.js:32 PlotManager: ✅
|
||||
debug-streaming.js:41
|
||||
2️⃣ VERIFICANDO PLOT MANAGER...
|
||||
debug-streaming.js:44 ✅ PlotManager inicializado
|
||||
debug-streaming.js:45 📊 Sesiones activas: 1
|
||||
debug-streaming.js:59
|
||||
3️⃣ ANALIZANDO SESIÓN DE PLOT...
|
||||
debug-streaming.js:65 📈 Sesión encontrada: plot_16
|
||||
debug-streaming.js:78 Chart Config: {hasChart: true, chartType: 'line', scaleType: 'realtime', scaleConstructor: 'RealTimeScale', streamingEnabled: true, …}
|
||||
debug-streaming.js:85 ✅ Escala realtime configurada correctamente
|
||||
debug-streaming.js:93 ✅ Streaming habilitado en el chart
|
||||
debug-streaming.js:98
|
||||
4️⃣ VERIFICANDO DATOS DEL BACKEND...
|
||||
plotting.js:9 📈 Plot debugging enabled. Check console for detailed logs.
|
||||
debug-streaming.js:278 ⚡ TEST RÁPIDO DE STREAMING
|
||||
debug-streaming.js:279 ------------------------------
|
||||
debug-streaming.js:288 plotManager: ✅ true
|
||||
debug-streaming.js:288 sessions: ✅ 1
|
||||
debug-streaming.js:288 chartStreaming: ✅ true
|
||||
debug-streaming.js:293
|
||||
🧪 Probando sesión: plot_16
|
||||
debug-streaming.js:8 🔧 DIAGNÓSTICO DETALLADO DE STREAMING
|
||||
debug-streaming.js:9 ============================================================
|
||||
debug-streaming.js:23
|
||||
1️⃣ VERIFICANDO LIBRERÍAS...
|
||||
debug-streaming.js:30 Chart.js: ✅
|
||||
debug-streaming.js:31 ChartStreaming: ✅
|
||||
debug-streaming.js:32 PlotManager: ✅
|
||||
debug-streaming.js:41
|
||||
2️⃣ VERIFICANDO PLOT MANAGER...
|
||||
debug-streaming.js:44 ✅ PlotManager inicializado
|
||||
debug-streaming.js:45 📊 Sesiones activas: 1
|
||||
debug-streaming.js:59
|
||||
3️⃣ ANALIZANDO SESIÓN DE PLOT...
|
||||
debug-streaming.js:65 📈 Sesión encontrada: plot_16
|
||||
debug-streaming.js:78 Chart Config: {hasChart: true, chartType: 'line', scaleType: 'realtime', scaleConstructor: 'RealTimeScale', streamingEnabled: true, …}
|
||||
debug-streaming.js:85 ✅ Escala realtime configurada correctamente
|
||||
debug-streaming.js:93 ✅ Streaming habilitado en el chart
|
||||
debug-streaming.js:98
|
||||
4️⃣ VERIFICANDO DATOS DEL BACKEND...
|
||||
debug-streaming.js:103 📊 Respuesta del backend: {success: true, datasets: 1, totalPoints: 37, isActive: true, isPaused: false}
|
||||
debug-streaming.js:112 ✅ Backend devuelve datos
|
||||
debug-streaming.js:116 📈 Primer dataset: {label: 'UR29_Brix', dataPoints: 37, samplePoint: {…}}
|
||||
debug-streaming.js:123 ✅ Dataset tiene puntos de datos
|
||||
debug-streaming.js:130 ⏰ Análisis de timestamps: {firstPointTime: 1753115880879.299, currentTime: 1753115888236, differenceMs: 7356.700927734375, differenceSec: 7, isRecent: true}
|
||||
debug-streaming.js:156
|
||||
5️⃣ PROBANDO FUNCIONALIDAD STREAMING...
|
||||
chartjs-plugin-streaming.js:345 📈 Added point to dataset 0 (UR29_Brix): x=1753115888237, y=23.50854350922291
|
||||
debug-streaming.js:165 🧪 Test de addStreamingData: ✅
|
||||
debug-streaming.js:170 📊 Puntos después del test: 57
|
||||
debug-streaming.js:182
|
||||
6️⃣ RESUMEN DEL DIAGNÓSTICO
|
||||
debug-streaming.js:183 ========================================
|
||||
debug-streaming.js:186 🎉 No se encontraron errores graves
|
||||
debug-streaming.js:199
|
||||
7️⃣ PRÓXIMOS PASOS RECOMENDADOS:
|
||||
debug-streaming.js:213 🔧 4. Si persiste: plotManager.controlPlot("plot_16", "stop") y luego "start"
|
||||
debug-streaming.js:8 🔧 DIAGNÓSTICO DETALLADO DE STREAMING
|
||||
debug-streaming.js:9 ============================================================
|
||||
debug-streaming.js:23
|
||||
1️⃣ VERIFICANDO LIBRERÍAS...
|
||||
debug-streaming.js:30 Chart.js: ✅
|
||||
debug-streaming.js:31 ChartStreaming: ✅
|
||||
debug-streaming.js:32 PlotManager: ✅
|
||||
debug-streaming.js:41
|
||||
2️⃣ VERIFICANDO PLOT MANAGER...
|
||||
debug-streaming.js:44 ✅ PlotManager inicializado
|
||||
debug-streaming.js:45 📊 Sesiones activas: 1
|
||||
debug-streaming.js:59
|
||||
3️⃣ ANALIZANDO SESIÓN DE PLOT...
|
||||
debug-streaming.js:65 📈 Sesión encontrada: plot_16
|
||||
debug-streaming.js:78 Chart Config: {hasChart: true, chartType: 'line', scaleType: 'realtime', scaleConstructor: 'RealTimeScale', streamingEnabled: true, …}
|
||||
debug-streaming.js:85 ✅ Escala realtime configurada correctamente
|
||||
debug-streaming.js:93 ✅ Streaming habilitado en el chart
|
||||
debug-streaming.js:98
|
||||
4️⃣ VERIFICANDO DATOS DEL BACKEND...
|
||||
plotting.js:9 📈 Plot debugging enabled. Check console for detailed logs.
|
||||
debug-streaming.js:278 ⚡ TEST RÁPIDO DE STREAMING
|
||||
debug-streaming.js:279 ------------------------------
|
||||
debug-streaming.js:288 plotManager: ✅ true
|
||||
debug-streaming.js:288 sessions: ✅ 1
|
||||
debug-streaming.js:288 chartStreaming: ✅ true
|
||||
debug-streaming.js:293
|
||||
🧪 Probando sesión: plot_16
|
||||
debug-streaming.js:8 🔧 DIAGNÓSTICO DETALLADO DE STREAMING
|
||||
debug-streaming.js:9 ============================================================
|
||||
debug-streaming.js:23
|
||||
1️⃣ VERIFICANDO LIBRERÍAS...
|
||||
debug-streaming.js:30 Chart.js: ✅
|
||||
debug-streaming.js:31 ChartStreaming: ✅
|
||||
debug-streaming.js:32 PlotManager: ✅
|
||||
debug-streaming.js:41
|
||||
2️⃣ VERIFICANDO PLOT MANAGER...
|
||||
debug-streaming.js:44 ✅ PlotManager inicializado
|
||||
debug-streaming.js:45 📊 Sesiones activas: 1
|
||||
debug-streaming.js:59
|
||||
3️⃣ ANALIZANDO SESIÓN DE PLOT...
|
||||
debug-streaming.js:65 📈 Sesión encontrada: plot_16
|
||||
debug-streaming.js:78 Chart Config: {hasChart: true, chartType: 'line', scaleType: 'realtime', scaleConstructor: 'RealTimeScale', streamingEnabled: true, …}
|
||||
debug-streaming.js:85 ✅ Escala realtime configurada correctamente
|
||||
debug-streaming.js:93 ✅ Streaming habilitado en el chart
|
||||
debug-streaming.js:98
|
||||
4️⃣ VERIFICANDO DATOS DEL BACKEND...
|
||||
debug-streaming.js:103 📊 Respuesta del backend: {success: true, datasets: 1, totalPoints: 38, isActive: true, isPaused: false}
|
||||
debug-streaming.js:112 ✅ Backend devuelve datos
|
||||
debug-streaming.js:116 📈 Primer dataset: {label: 'UR29_Brix', dataPoints: 38, samplePoint: {…}}
|
||||
debug-streaming.js:123 ✅ Dataset tiene puntos de datos
|
||||
debug-streaming.js:130 ⏰ Análisis de timestamps: {firstPointTime: 1753115880879.299, currentTime: 1753115888548, differenceMs: 7668.700927734375, differenceSec: 8, isRecent: true}
|
||||
debug-streaming.js:156
|
||||
5️⃣ PROBANDO FUNCIONALIDAD STREAMING...
|
||||
chartjs-plugin-streaming.js:345 📈 Added point to dataset 0 (UR29_Brix): x=1753115888548, y=4.850139391135455
|
||||
debug-streaming.js:165 🧪 Test de addStreamingData: ✅
|
||||
debug-streaming.js:170 📊 Puntos después del test: 58
|
||||
debug-streaming.js:182
|
||||
6️⃣ RESUMEN DEL DIAGNÓSTICO
|
||||
debug-streaming.js:183 ========================================
|
||||
debug-streaming.js:186 🎉 No se encontraron errores graves
|
||||
debug-streaming.js:199
|
||||
7️⃣ PRÓXIMOS PASOS RECOMENDADOS:
|
||||
debug-streaming.js:213 🔧 4. Si persiste: plotManager.controlPlot("plot_16", "stop") y luego "start"
|
||||
chartjs-plugin-streaming.js:75 📈 RealTimeScale DEBUG - scaleOptions: Proxy(Object) {_cacheable: false, _proxy: Proxy(Object), _context: {…}, _subProxy: undefined, _stack: Set(0), …}
|
||||
chartjs-plugin-streaming.js:76 📈 RealTimeScale DEBUG - me.options: Proxy(Object) {_cacheable: false, _proxy: Proxy(Object), _context: {…}, _subProxy: undefined, _stack: Set(0), …}
|
||||
chartjs-plugin-streaming.js:77 📈 RealTimeScale DEBUG - me.options.realtime: Proxy(Object) {_cacheable: false, _proxy: Proxy(Object), _context: {…}, _subProxy: undefined, _stack: Set(0), …}
|
||||
chartjs-plugin-streaming.js:81 📈 RealTimeScale DEBUG - onRefresh resolved: null object
|
||||
chartjs-plugin-streaming.js:93 📈 RealTimeScale initialized: {duration: 60000, refresh: 500, pause: false, hasOnRefresh: false}
|
||||
debug-streaming.js:8 🔧 DIAGNÓSTICO DETALLADO DE STREAMING
|
||||
debug-streaming.js:9 ============================================================
|
||||
debug-streaming.js:23
|
||||
1️⃣ VERIFICANDO LIBRERÍAS...
|
||||
debug-streaming.js:30 Chart.js: ✅
|
||||
debug-streaming.js:31 ChartStreaming: ✅
|
||||
debug-streaming.js:32 PlotManager: ✅
|
||||
debug-streaming.js:41
|
||||
2️⃣ VERIFICANDO PLOT MANAGER...
|
||||
debug-streaming.js:44 ✅ PlotManager inicializado
|
||||
debug-streaming.js:45 📊 Sesiones activas: 1
|
||||
debug-streaming.js:59
|
||||
3️⃣ ANALIZANDO SESIÓN DE PLOT...
|
||||
debug-streaming.js:65 📈 Sesión encontrada: plot_16
|
||||
debug-streaming.js:78 Chart Config: {hasChart: true, chartType: 'line', scaleType: 'realtime', scaleConstructor: 'RealTimeScale', streamingEnabled: true, …}
|
||||
debug-streaming.js:85 ✅ Escala realtime configurada correctamente
|
||||
debug-streaming.js:93 ✅ Streaming habilitado en el chart
|
||||
debug-streaming.js:98
|
||||
4️⃣ VERIFICANDO DATOS DEL BACKEND...
|
||||
plotting.js:9 📈 Plot debugging enabled. Check console for detailed logs.
|
||||
debug-streaming.js:278 ⚡ TEST RÁPIDO DE STREAMING
|
||||
debug-streaming.js:279 ------------------------------
|
||||
debug-streaming.js:288 plotManager: ✅ true
|
||||
debug-streaming.js:288 sessions: ✅ 1
|
||||
debug-streaming.js:288 chartStreaming: ✅ true
|
||||
debug-streaming.js:293
|
||||
🧪 Probando sesión: plot_16
|
||||
debug-streaming.js:8 🔧 DIAGNÓSTICO DETALLADO DE STREAMING
|
||||
debug-streaming.js:9 ============================================================
|
||||
debug-streaming.js:23
|
||||
1️⃣ VERIFICANDO LIBRERÍAS...
|
||||
debug-streaming.js:30 Chart.js: ✅
|
||||
debug-streaming.js:31 ChartStreaming: ✅
|
||||
debug-streaming.js:32 PlotManager: ✅
|
||||
debug-streaming.js:41
|
||||
2️⃣ VERIFICANDO PLOT MANAGER...
|
||||
debug-streaming.js:44 ✅ PlotManager inicializado
|
||||
debug-streaming.js:45 📊 Sesiones activas: 1
|
||||
debug-streaming.js:59
|
||||
3️⃣ ANALIZANDO SESIÓN DE PLOT...
|
||||
debug-streaming.js:65 📈 Sesión encontrada: plot_16
|
||||
debug-streaming.js:78 Chart Config: {hasChart: true, chartType: 'line', scaleType: 'realtime', scaleConstructor: 'RealTimeScale', streamingEnabled: true, …}
|
||||
debug-streaming.js:85 ✅ Escala realtime configurada correctamente
|
||||
debug-streaming.js:93 ✅ Streaming habilitado en el chart
|
||||
debug-streaming.js:98
|
||||
4️⃣ VERIFICANDO DATOS DEL BACKEND...
|
||||
plotting.js:20 📈 Plot plot_16: Updating streaming chart with 1 datasets
|
||||
plotting.js:20 - Variable 1: UR29_Brix (40 points)
|
||||
plotting.js:20 📈 Plot plot_16: Added 0 new points to dataset 0
|
||||
debug-streaming.js:8 🔧 DIAGNÓSTICO DETALLADO DE STREAMING
|
||||
debug-streaming.js:9 ============================================================
|
||||
debug-streaming.js:23
|
||||
1️⃣ VERIFICANDO LIBRERÍAS...
|
||||
debug-streaming.js:30 Chart.js: ✅
|
||||
debug-streaming.js:31 ChartStreaming: ✅
|
||||
debug-streaming.js:32 PlotManager: ✅
|
||||
debug-streaming.js:41
|
||||
2️⃣ VERIFICANDO PLOT MANAGER...
|
||||
debug-streaming.js:44 ✅ PlotManager inicializado
|
||||
debug-streaming.js:45 📊 Sesiones activas: 1
|
||||
debug-streaming.js:59
|
||||
3️⃣ ANALIZANDO SESIÓN DE PLOT...
|
||||
debug-streaming.js:65 📈 Sesión encontrada: plot_16
|
||||
debug-streaming.js:78 Chart Config: {hasChart: true, chartType: 'line', scaleType: 'realtime', scaleConstructor: 'RealTimeScale', streamingEnabled: true, …}
|
||||
debug-streaming.js:85 ✅ Escala realtime configurada correctamente
|
||||
debug-streaming.js:93 ✅ Streaming habilitado en el chart
|
||||
debug-streaming.js:98
|
||||
4️⃣ VERIFICANDO DATOS DEL BACKEND...
|
||||
plotting.js:9 📈 Plot debugging enabled. Check console for detailed logs.
|
||||
debug-streaming.js:278 ⚡ TEST RÁPIDO DE STREAMING
|
||||
debug-streaming.js:279 ------------------------------
|
||||
debug-streaming.js:288 plotManager: ✅ true
|
||||
debug-streaming.js:288 sessions: ✅ 1
|
||||
debug-streaming.js:288 chartStreaming: ✅ true
|
||||
debug-streaming.js:293
|
||||
🧪 Probando sesión: plot_16
|
||||
debug-streaming.js:8 🔧 DIAGNÓSTICO DETALLADO DE STREAMING
|
||||
debug-streaming.js:9 ============================================================
|
||||
debug-streaming.js:23
|
||||
1️⃣ VERIFICANDO LIBRERÍAS...
|
||||
debug-streaming.js:30 Chart.js: ✅
|
||||
debug-streaming.js:31 ChartStreaming: ✅
|
||||
debug-streaming.js:32 PlotManager: ✅
|
||||
debug-streaming.js:41
|
||||
2️⃣ VERIFICANDO PLOT MANAGER...
|
||||
debug-streaming.js:44 ✅ PlotManager inicializado
|
||||
debug-streaming.js:45 📊 Sesiones activas: 1
|
||||
debug-streaming.js:59
|
||||
3️⃣ ANALIZANDO SESIÓN DE PLOT...
|
||||
debug-streaming.js:65 📈 Sesión encontrada: plot_16
|
||||
debug-streaming.js:78 Chart Config: {hasChart: true, chartType: 'line', scaleType: 'realtime', scaleConstructor: 'RealTimeScale', streamingEnabled: true, …}
|
||||
debug-streaming.js:85 ✅ Escala realtime configurada correctamente
|
||||
debug-streaming.js:93 ✅ Streaming habilitado en el chart
|
||||
debug-streaming.js:98
|
||||
4️⃣ VERIFICANDO DATOS DEL BACKEND...
|
||||
chartjs-plugin-streaming.js:75 📈 RealTimeScale DEBUG - scaleOptions: Proxy(Object) {_cacheable: false, _proxy: Proxy(Object), _context: {…}, _subProxy: undefined, _stack: Set(0), …}
|
||||
chartjs-plugin-streaming.js:76 📈 RealTimeScale DEBUG - me.options: Proxy(Object) {_cacheable: false, _proxy: Proxy(Object), _context: {…}, _subProxy: undefined, _stack: Set(0), …}
|
||||
chartjs-plugin-streaming.js:77 📈 RealTimeScale DEBUG - me.options.realtime: Proxy(Object) {_cacheable: false, _proxy: Proxy(Object), _context: {…}, _subProxy: undefined, _stack: Set(0), …}
|
||||
plotting.js:20 📈 Plot plot_16: Fetching data from backend...
|
||||
chartjs-plugin-streaming.js:81 📈 RealTimeScale DEBUG - onRefresh resolved: null object
|
||||
chartjs-plugin-streaming.js:93 📈 RealTimeScale initialized: {duration: 60000, refresh: 500, pause: false, hasOnRefresh: false}
|
||||
📊 Respuesta del backend: {success: true, datasets: 1, totalPoints: 42, isActive: true, isPaused: false}
|
||||
✅ Backend devuelve datos
|
||||
📈 Primer dataset: {label: 'UR29_Brix', dataPoints: 42, samplePoint: {…}}
|
||||
✅ Dataset tiene puntos de datos
|
||||
⏰ Análisis de timestamps: {firstPointTime: 1753115880879.299, currentTime: 1753115889176, differenceMs: 8296.700927734375, differenceSec: 8, isRecent: true}
|
||||
|
||||
5️⃣ PROBANDO FUNCIONALIDAD STREAMING...
|
||||
📈 Added point to dataset 0 (UR29_Brix): x=1753115889176, y=42.83092459112214
|
||||
🧪 Test de addStreamingData: ✅
|
||||
📊 Puntos después del test: 59
|
||||
|
||||
6️⃣ RESUMEN DEL DIAGNÓSTICO
|
||||
debug-streaming.js:183 ========================================
|
||||
debug-streaming.js:186 🎉 No se encontraron errores graves
|
||||
debug-streaming.js:199
|
||||
7️⃣ PRÓXIMOS PASOS RECOMENDADOS:
|
||||
debug-streaming.js:213 🔧 4. Si persiste: plotManager.controlPlot("plot_16", "stop") y luego "start"
|
||||
debug-streaming.js:8 🔧 DIAGNÓSTICO DETALLADO DE STREAMING
|
||||
debug-streaming.js:9 ============================================================
|
||||
debug-streaming.js:23
|
||||
1️⃣ VERIFICANDO LIBRERÍAS...
|
||||
debug-streaming.js:30 Chart.js: ✅
|
||||
debug-streaming.js:31 ChartStreaming: ✅
|
||||
debug-streaming.js:32 PlotManager: ✅
|
||||
debug-streaming.js:41
|
||||
2️⃣ VERIFICANDO PLOT MANAGER...
|
||||
debug-streaming.js:44 ✅ PlotManager inicializado
|
||||
debug-streaming.js:45 📊 Sesiones activas: 1
|
||||
debug-streaming.js:59
|
||||
3️⃣ ANALIZANDO SESIÓN DE PLOT...
|
||||
debug-streaming.js:65 📈 Sesión encontrada: plot_16
|
||||
debug-streaming.js:78 Chart Config: {hasChart: true, chartType: 'line', scaleType: 'realtime', scaleConstructor: 'RealTimeScale', streamingEnabled: true, …}
|
||||
debug-streaming.js:85 ✅ Escala realtime configurada correctamente
|
||||
debug-streaming.js:93 ✅ Streaming habilitado en el chart
|
||||
debug-streaming.js:98
|
||||
4️⃣ VERIFICANDO DATOS DEL BACKEND...
|
||||
plotting.js:9 📈 Plot debugging enabled. Check console for detailed logs.
|
||||
debug-streaming.js:278 ⚡ TEST RÁPIDO DE STREAMING
|
||||
debug-streaming.js:279 ------------------------------
|
||||
debug-streaming.js:288 plotManager: ✅ true
|
||||
debug-streaming.js:288 sessions: ✅ 1
|
||||
debug-streaming.js:288 chartStreaming: ✅ true
|
||||
debug-streaming.js:293
|
||||
🧪 Probando sesión: plot_16
|
||||
debug-streaming.js:8 🔧 DIAGNÓSTICO DETALLADO DE STREAMING
|
||||
debug-streaming.js:9 ============================================================
|
||||
debug-streaming.js:23
|
||||
1️⃣ VERIFICANDO LIBRERÍAS...
|
||||
debug-streaming.js:30 Chart.js: ✅
|
||||
debug-streaming.js:31 ChartStreaming: ✅
|
||||
debug-streaming.js:32 PlotManager: ✅
|
||||
debug-streaming.js:41
|
||||
2️⃣ VERIFICANDO PLOT MANAGER...
|
||||
debug-streaming.js:44 ✅ PlotManager inicializado
|
||||
debug-streaming.js:45 📊 Sesiones activas: 1
|
||||
debug-streaming.js:59
|
||||
3️⃣ ANALIZANDO SESIÓN DE PLOT...
|
||||
debug-streaming.js:65 📈 Sesión encontrada: plot_16
|
||||
debug-streaming.js:78 Chart Config: {hasChart: true, chartType: 'line', scaleType: 'realtime', scaleConstructor: 'RealTimeScale', streamingEnabled: true, …}
|
||||
debug-streaming.js:85 ✅ Escala realtime configurada correctamente
|
||||
debug-streaming.js:93 ✅ Streaming habilitado en el chart
|
||||
debug-streaming.js:98
|
||||
4️⃣ VERIFICANDO DATOS DEL BACKEND...
|
||||
debug-streaming.js:8 🔧 DIAGNÓSTICO DETALLADO DE STREAMING
|
||||
debug-streaming.js:9 ============================================================
|
||||
debug-streaming.js:23
|
||||
1️⃣ VERIFICANDO LIBRERÍAS...
|
||||
debug-streaming.js:30 Chart.js: ✅
|
||||
debug-streaming.js:31 ChartStreaming: ✅
|
||||
debug-streaming.js:32 PlotManager: ✅
|
||||
debug-streaming.js:41
|
||||
2️⃣ VERIFICANDO PLOT MANAGER...
|
||||
debug-streaming.js:44 ✅ PlotManager inicializado
|
||||
debug-streaming.js:45 📊 Sesiones activas: 1
|
||||
debug-streaming.js:59
|
||||
3️⃣ ANALIZANDO SESIÓN DE PLOT...
|
||||
debug-streaming.js:65 📈 Sesión encontrada: plot_16
|
||||
debug-streaming.js:78 Chart Config: {hasChart: true, chartType: 'line', scaleType: 'realtime', scaleConstructor: 'RealTimeScale', streamingEnabled: true, …}
|
||||
debug-streaming.js:85 ✅ Escala realtime configurada correctamente
|
||||
debug-streaming.js:93 ✅ Streaming habilitado en el chart
|
||||
debug-streaming.js:98
|
||||
4️⃣ VERIFICANDO DATOS DEL BACKEND...
|
||||
plotting.js:9 📈 Plot debugging enabled. Check console for detailed logs.
|
||||
debug-streaming.js:278 ⚡ TEST RÁPIDO DE STREAMING
|
||||
debug-streaming.js:279 ------------------------------
|
||||
debug-streaming.js:288 plotManager: ✅ true
|
||||
debug-streaming.js:288 sessions: ✅ 1
|
||||
debug-streaming.js:288 chartStreaming: ✅ true
|
||||
debug-streaming.js:293
|
||||
🧪 Probando sesión: plot_16
|
||||
debug-streaming.js:8 🔧 DIAGNÓSTICO DETALLADO DE STREAMING
|
||||
debug-streaming.js:9 ============================================================
|
||||
debug-streaming.js:23
|
||||
1️⃣ VERIFICANDO LIBRERÍAS...
|
||||
debug-streaming.js:30 Chart.js: ✅
|
||||
debug-streaming.js:31 ChartStreaming: ✅
|
||||
debug-streaming.js:32 PlotManager: ✅
|
||||
debug-streaming.js:41
|
||||
2️⃣ VERIFICANDO PLOT MANAGER...
|
||||
✅ PlotManager inicializado
|
||||
📊 Sesiones activas: 1
|
||||
|
||||
3️⃣ ANALIZANDO SESIÓN DE PLOT...
|
||||
📈 Sesión encontrada: plot_16
|
||||
Chart Config: {hasChart: true, chartType: 'line', scaleType: 'realtime', scaleConstructor: 'RealTimeScale', streamingEnabled: true, …}
|
||||
✅ Escala realtime configurada correctamente
|
||||
✅ Streaming habilitado en el chart
|
||||
|
||||
4️⃣ VERIFICANDO DATOS DEL BACKEND...
|
||||
📊 Respuesta del backend: {success: true, datasets: 1, totalPoints: 43, isActive: true, isPaused: false}
|
||||
✅ Backend devuelve datos
|
||||
📈 Primer dataset: {label: 'UR29_Brix', dataPoints: 43, samplePoint: {…}}
|
||||
✅ Dataset tiene puntos de datos
|
||||
⏰ Análisis de timestamps: {firstPointTime: 1753115880879.299, currentTime: 1753115889485, differenceMs: 8605.700927734375, differenceSec: 9, isRecent: true}
|
||||
|
||||
5️⃣ PROBANDO FUNCIONALIDAD STREAMING...
|
||||
📈 Added point to dataset 0 (UR29_Brix): x=1753115889485, y=0.23291564278824506
|
||||
🧪 Test de addStreamingData: ✅
|
||||
📊 Puntos después del test: 60
|
||||
|
||||
6️⃣ RESUMEN DEL DIAGNÓSTICO
|
||||
========================================
|
||||
🎉 No se encontraron errores graves
|
||||
|
||||
7️⃣ PRÓXIMOS PASOS RECOMENDADOS:
|
||||
🔧 4. Si persiste: plotManager.controlPlot("plot_16", "stop") y luego "start"
|
||||
📈 Plot plot_16: Streaming resumed
|
||||
📈 RealTimeScale DEBUG - scaleOptions: Proxy(Object) {_cacheable: false, _proxy: Proxy(Object), _context: {…}, _subProxy: undefined, _stack: Set(0), …}
|
||||
📈 RealTimeScale DEBUG - me.options: Proxy(Object) {_cacheable: false, _proxy: Proxy(Object), _context: {…}, _subProxy: undefined, _stack: Set(0), …}
|
||||
📈 RealTimeScale DEBUG - me.options.realtime: Proxy(Object) {_cacheable: false, _proxy: Proxy(Object), _context: {…}, _subProxy: undefined, _stack: Set(0), …}
|
||||
📈 RealTimeScale DEBUG - onRefresh resolved: null object
|
||||
📈 RealTimeScale initialized: {duration: 60000, refresh: 500, pause: false, hasOnRefresh: false}
|
||||
🔧 DIAGNÓSTICO DETALLADO DE STREAMING
|
||||
============================================================
|
||||
|
||||
1️⃣ VERIFICANDO LIBRERÍAS...
|
||||
Chart.js: ✅
|
||||
ChartStreaming: ✅
|
||||
PlotManager: ✅
|
||||
|
||||
2️⃣ VERIFICANDO PLOT MANAGER...
|
||||
✅ PlotManager inicializado
|
||||
📊 Sesiones activas: 1
|
||||
|
||||
3️⃣ ANALIZANDO SESIÓN DE PLOT...
|
||||
📈 Sesión encontrada: plot_16
|
||||
Chart Config: {hasChart: true, chartType: 'line', scaleType: 'realtime', scaleConstructor: 'RealTimeScale', streamingEnabled: true, …}
|
||||
✅ Escala realtime configurada correctamente
|
||||
✅ Streaming habilitado en el chart
|
||||
|
||||
4️⃣ VERIFICANDO DATOS DEL BACKEND...
|
||||
📈 Plot debugging enabled. Check console for detailed logs.
|
||||
⚡ TEST RÁPIDO DE STREAMING
|
||||
------------------------------
|
||||
plotManager: ✅ true
|
||||
sessions: ✅ 1
|
||||
chartStreaming: ✅ true
|
||||
|
||||
🧪 Probando sesión: plot_16
|
||||
🔧 DIAGNÓSTICO DETALLADO DE STREAMING
|
||||
============================================================
|
||||
|
||||
1️⃣ VERIFICANDO LIBRERÍAS...
|
||||
Chart.js: ✅
|
||||
ChartStreaming: ✅
|
||||
PlotManager: ✅
|
||||
|
||||
2️⃣ VERIFICANDO PLOT MANAGER...
|
||||
✅ PlotManager inicializado
|
||||
📊 Sesiones activas: 1
|
||||
|
||||
3️⃣ ANALIZANDO SESIÓN DE PLOT...
|
||||
📈 Sesión encontrada: plot_16
|
||||
Chart Config: {hasChart: true, chartType: 'line', scaleType: 'realtime', scaleConstructor: 'RealTimeScale', streamingEnabled: true, …}
|
||||
✅ Escala realtime configurada correctamente
|
||||
✅ Streaming habilitado en el chart
|
||||
|
||||
4️⃣ VERIFICANDO DATOS DEL BACKEND...
|
||||
📈 Plot plot_16: Updating streaming chart with 1 datasets
|
||||
- Variable 1: UR29_Brix (45 points)
|
||||
📈 Plot plot_16: Added 0 new points to dataset 0
|
||||
🔧 DIAGNÓSTICO DETALLADO DE STREAMING
|
||||
============================================================
|
||||
|
||||
1️⃣ VERIFICANDO LIBRERÍAS...
|
||||
Chart.js: ✅
|
||||
ChartStreaming: ✅
|
||||
PlotManager: ✅
|
||||
|
||||
2️⃣ VERIFICANDO PLOT MANAGER...
|
||||
✅ PlotManager inicializado
|
||||
📊 Sesiones activas: 1
|
||||
|
||||
3️⃣ ANALIZANDO SESIÓN DE PLOT...
|
||||
📈 Sesión encontrada: plot_16
|
||||
Chart Config: {hasChart: true, chartType: 'line', scaleType: 'realtime', scaleConstructor: 'RealTimeScale', streamingEnabled: true, …}
|
||||
✅ Escala realtime configurada correctamente
|
||||
✅ Streaming habilitado en el chart
|
||||
|
||||
4️⃣ VERIFICANDO DATOS DEL BACKEND...
|
||||
📈 Plot debugging enabled. Check console for detailed logs.
|
||||
⚡ TEST RÁPIDO DE STREAMING
|
||||
------------------------------
|
||||
plotManager: ✅ true
|
||||
sessions: ✅ 1
|
||||
chartStreaming: ✅ true
|
||||
|
||||
🧪 Probando sesión: plot_16
|
||||
🔧 DIAGNÓSTICO DETALLADO DE STREAMING
|
||||
============================================================
|
||||
|
||||
1️⃣ VERIFICANDO LIBRERÍAS...
|
||||
Chart.js: ✅
|
||||
ChartStreaming: ✅
|
||||
PlotManager: ✅
|
||||
|
||||
2️⃣ VERIFICANDO PLOT MANAGER...
|
||||
✅ PlotManager inicializado
|
||||
📊 Sesiones activas: 1
|
||||
|
||||
3️⃣ ANALIZANDO SESIÓN DE PLOT...
|
||||
📈 Sesión encontrada: plot_16
|
||||
Chart Config: {hasChart: true, chartType: 'line', scaleType: 'realtime', scaleConstructor: 'RealTimeScale', streamingEnabled: true, …}
|
||||
✅ Escala realtime configurada correctamente
|
||||
✅ Streaming habilitado en el chart
|
||||
|
||||
4️⃣ VERIFICANDO DATOS DEL BACKEND...
|
||||
📈 RealTimeScale DEBUG - scaleOptions: Proxy(Object) {_cacheable: false, _proxy: Proxy(Object), _context: {…}, _subProxy: undefined, _stack: Set(0), …}
|
||||
📈 RealTimeScale DEBUG - me.options: Proxy(Object) {_cacheable: false, _proxy: Proxy(Object), _context: {…}, _subProxy: undefined, _stack: Set(0), …}
|
||||
📈 RealTimeScale DEBUG - me.options.realtime: Proxy(Object) {_cacheable: false, _proxy: Proxy(Object), _context: {…}, _subProxy: undefined, _stack: Set(0), …}
|
||||
📈 Plot plot_16: Fetching data from backend...
|
||||
📈 RealTimeScale DEBUG - onRefresh resolved: null object
|
||||
📈 RealTimeScale initialized: {duration: 60000, refresh: 500, pause: false, hasOnRefresh: false}
|
||||
📊 Respuesta del backend: {success: true, datasets: 1, totalPoints: 46, isActive: true, isPaused: false}
|
||||
✅ Backend devuelve datos
|
||||
📈 Primer dataset: {label: 'UR29_Brix', dataPoints: 46, samplePoint: {…}}
|
||||
✅ Dataset tiene puntos de datos
|
||||
⏰ Análisis de timestamps: {firstPointTime: 1753115880879.299, currentTime: 1753115890109, differenceMs: 9229.700927734375, differenceSec: 9, isRecent: true}
|
||||
|
||||
5️⃣ PROBANDO FUNCIONALIDAD STREAMING...
|
||||
📈 Added point to dataset 0 (UR29_Brix): x=1753115890109, y=15.728858368162268
|
||||
🧪 Test de addStreamingData: ✅
|
||||
📊 Puntos después del test: 61
|
||||
|
||||
6️⃣ RESUMEN DEL DIAGNÓSTICO
|
||||
========================================
|
||||
🎉 No se encontraron errores graves
|
||||
|
||||
7️⃣ PRÓXIMOS PASOS RECOMENDADOS:
|
||||
🔧 4. Si persiste: plotManager.controlPlot("plot_16", "stop") y luego "start"
|
||||
🔧 DIAGNÓSTICO DETALLADO DE STREAMING
|
||||
============================================================
|
||||
|
||||
1️⃣ VERIFICANDO LIBRERÍAS...
|
||||
Chart.js: ✅
|
||||
ChartStreaming: ✅
|
||||
PlotManager: ✅
|
||||
|
||||
2️⃣ VERIFICANDO PLOT MANAGER...
|
||||
✅ PlotManager inicializado
|
||||
📊 Sesiones activas: 1
|
||||
|
||||
3️⃣ ANALIZANDO SESIÓN DE PLOT...
|
||||
📈 Sesión encontrada: plot_16
|
||||
Chart Config: {hasChart: true, chartType: 'line', scaleType: 'realtime', scaleConstructor: 'RealTimeScale', streamingEnabled: true, …}
|
||||
✅ Escala realtime configurada correctamente
|
||||
✅ Streaming habilitado en el chart
|
||||
|
||||
4️⃣ VERIFICANDO DATOS DEL BACKEND...
|
||||
📈 Plot debugging enabled. Check console for detailed logs.
|
||||
⚡ TEST RÁPIDO DE STREAMING
|
||||
------------------------------
|
||||
plotManager: ✅ true
|
||||
sessions: ✅ 1
|
||||
chartStreaming: ✅ true
|
||||
|
||||
🧪 Probando sesión: plot_16
|
||||
🔧 DIAGNÓSTICO DETALLADO DE STREAMING
|
||||
============================================================
|
||||
|
||||
1️⃣ VERIFICANDO LIBRERÍAS...
|
||||
Chart.js: ✅
|
||||
ChartStreaming: ✅
|
||||
PlotManager: ✅
|
||||
|
||||
2️⃣ VERIFICANDO PLOT MANAGER...
|
||||
✅ PlotManager inicializado
|
||||
📊 Sesiones activas: 1
|
||||
|
||||
3️⃣ ANALIZANDO SESIÓN DE PLOT...
|
||||
📈 Sesión encontrada: plot_16
|
||||
Chart Config: {hasChart: true, chartType: 'line', scaleType: 'realtime', scaleConstructor: 'RealTimeScale', streamingEnabled: true, …}
|
||||
✅ Escala realtime configurada correctamente
|
||||
✅ Streaming habilitado en el chart
|
||||
|
||||
4️⃣ VERIFICANDO DATOS DEL BACKEND...
|
||||
📊 Respuesta del backend: {success: true, datasets: 1, totalPoints: 48, isActive: true, isPaused: false}
|
||||
✅ Backend devuelve datos
|
||||
📈 Primer dataset: {label: 'UR29_Brix', dataPoints: 48, samplePoint: {…}}
|
||||
✅ Dataset tiene puntos de datos
|
||||
⏰ Análisis de timestamps: {firstPointTime: 1753115880879.299, currentTime: 1753115890413, differenceMs: 9533.700927734375, differenceSec: 10, isRecent: true}
|
||||
|
||||
5️⃣ PROBANDO FUNCIONALIDAD STREAMING...
|
||||
📈 Added point to dataset 0 (UR29_Brix): x=1753115890413, y=93.73804066063487
|
||||
🧪 Test de addStreamingData: ✅
|
||||
📊 Puntos después del test: 62
|
||||
|
||||
6️⃣ RESUMEN DEL DIAGNÓSTICO
|
||||
========================================
|
||||
🎉 No se encontraron errores graves
|
||||
|
||||
7️⃣ PRÓXIMOS PASOS RECOMENDADOS:
|
||||
🔧 4. Si persiste: plotManager.controlPlot("plot_16", "stop") y luego "start"
|
||||
🔧 DIAGNÓSTICO DETALLADO DE STREAMING
|
||||
============================================================
|
||||
|
||||
1️⃣ VERIFICANDO LIBRERÍAS...
|
||||
Chart.js: ✅
|
||||
ChartStreaming: ✅
|
||||
PlotManager: ✅
|
||||
|
||||
2️⃣ VERIFICANDO PLOT MANAGER...
|
||||
✅ PlotManager inicializado
|
||||
📊 Sesiones activas: 1
|
||||
|
||||
3️⃣ ANALIZANDO SESIÓN DE PLOT...
|
||||
📈 Sesión encontrada: plot_16
|
||||
Chart Config: {hasChart: true, chartType: 'line', scaleType: 'realtime', scaleConstructor: 'RealTimeScale', streamingEnabled: true, …}
|
||||
✅ Escala realtime configurada correctamente
|
||||
✅ Streaming habilitado en el chart
|
||||
|
||||
4️⃣ VERIFICANDO DATOS DEL BACKEND...
|
||||
📈 Plot debugging enabled. Check console for detailed logs.
|
||||
⚡ TEST RÁPIDO DE STREAMING
|
||||
------------------------------
|
||||
plotManager: ✅ true
|
||||
sessions: ✅ 1
|
||||
chartStreaming: ✅ true
|
||||
|
||||
🧪 Probando sesión: plot_16
|
||||
🔧 DIAGNÓSTICO DETALLADO DE STREAMING
|
||||
============================================================
|
||||
|
||||
1️⃣ VERIFICANDO LIBRERÍAS...
|
||||
Chart.js: ✅
|
||||
ChartStreaming: ✅
|
||||
PlotManager: ✅
|
||||
|
||||
2️⃣ VERIFICANDO PLOT MANAGER...
|
||||
✅ PlotManager inicializado
|
||||
📊 Sesiones activas: 1
|
||||
|
||||
3️⃣ ANALIZANDO SESIÓN DE PLOT...
|
||||
📈 Sesión encontrada: plot_16
|
||||
Chart Config: {hasChart: true, chartType: 'line', scaleType: 'realtime', scaleConstructor: 'RealTimeScale', streamingEnabled: true, …}
|
||||
✅ Escala realtime configurada correctamente
|
||||
✅ Streaming habilitado en el chart
|
||||
|
||||
4️⃣ VERIFICANDO DATOS DEL BACKEND...
|
||||
📈 RealTimeScale DEBUG - scaleOptions: Proxy(Object) {_cacheable: false, _proxy: Proxy(Object), _context: {…}, _subProxy: undefined, _stack: Set(0), …}
|
||||
📈 RealTimeScale DEBUG - me.options: Proxy(Object) {_cacheable: false, _proxy: Proxy(Object), _context: {…}, _subProxy: undefined, _stack: Set(0), …}
|
||||
📈 RealTimeScale DEBUG - me.options.realtime: Proxy(Object) {_cacheable: false, _proxy: Proxy(Object), _context: {…}, _subProxy: undefined, _stack: Set(0), …}
|
||||
📈 RealTimeScale DEBUG - onRefresh resolved: null object
|
||||
📈 RealTimeScale initialized: {duration: 60000, refresh: 500, pause: false, hasOnRefresh: false}
|
Loading…
Reference in New Issue