Actualización de application_events.json con nuevos eventos para la creación, eliminación y actualización de sesiones de plot. Se ajustaron las fechas de última actualización en varios archivos de configuración, incluyendo plc_datasets.json, plot_sessions.json y system_state.json. Se implementaron controles de tasa de refresco y ventana de tiempo en la interfaz de usuario, mejorando la experiencia de usuario en la visualización de datos en tiempo real. Además, se integró el plugin de zoom en los gráficos para una mejor interacción.
This commit is contained in:
parent
f78ccbdc1d
commit
8d693c48c7
|
@ -7836,8 +7836,317 @@
|
|||
"activated_datasets": 1,
|
||||
"total_datasets": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-08T19:38:56.880475",
|
||||
"level": "info",
|
||||
"event_type": "plot_session_removed",
|
||||
"message": "Plot session 'UR29' removed",
|
||||
"details": {
|
||||
"session_id": "plot_20"
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-08T20:00:43.326432",
|
||||
"level": "info",
|
||||
"event_type": "application_started",
|
||||
"message": "Application initialization completed successfully",
|
||||
"details": {}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-08T20:00:43.380505",
|
||||
"level": "info",
|
||||
"event_type": "dataset_activated",
|
||||
"message": "Dataset activated: DAR",
|
||||
"details": {
|
||||
"dataset_id": "DAR",
|
||||
"variables_count": 2,
|
||||
"streaming_count": 2,
|
||||
"prefix": "gateway_phoenix"
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-08T20:00:43.401098",
|
||||
"level": "info",
|
||||
"event_type": "csv_recording_started",
|
||||
"message": "CSV recording started: 1 datasets activated",
|
||||
"details": {
|
||||
"activated_datasets": 1,
|
||||
"total_datasets": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-08T20:01:26.642290",
|
||||
"level": "info",
|
||||
"event_type": "plot_session_created",
|
||||
"message": "Plot session 'UR29' created and started",
|
||||
"details": {
|
||||
"session_id": "plot_0",
|
||||
"variables": [
|
||||
"UR29_Brix",
|
||||
"UR29_ma"
|
||||
],
|
||||
"time_window": 60,
|
||||
"trigger_variable": null,
|
||||
"auto_started": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-08T20:07:27.277526",
|
||||
"level": "info",
|
||||
"event_type": "application_started",
|
||||
"message": "Application initialization completed successfully",
|
||||
"details": {}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-08T20:07:27.342707",
|
||||
"level": "info",
|
||||
"event_type": "dataset_activated",
|
||||
"message": "Dataset activated: DAR",
|
||||
"details": {
|
||||
"dataset_id": "DAR",
|
||||
"variables_count": 2,
|
||||
"streaming_count": 2,
|
||||
"prefix": "gateway_phoenix"
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-08T20:07:27.352708",
|
||||
"level": "info",
|
||||
"event_type": "csv_recording_started",
|
||||
"message": "CSV recording started: 1 datasets activated",
|
||||
"details": {
|
||||
"activated_datasets": 1,
|
||||
"total_datasets": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-08T20:08:04.536885",
|
||||
"level": "info",
|
||||
"event_type": "plot_session_updated",
|
||||
"message": "Plot session 'UR29' configuration updated",
|
||||
"details": {
|
||||
"session_id": "plot_0",
|
||||
"new_config": {
|
||||
"name": "UR29",
|
||||
"variables": [
|
||||
"UR29_Brix",
|
||||
"UR29_ma"
|
||||
],
|
||||
"time_window": 10,
|
||||
"y_min": null,
|
||||
"y_max": null,
|
||||
"trigger_variable": null,
|
||||
"trigger_enabled": false,
|
||||
"trigger_on_true": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-08T20:08:07.955693",
|
||||
"level": "info",
|
||||
"event_type": "plot_session_updated",
|
||||
"message": "Plot session 'UR29' configuration updated",
|
||||
"details": {
|
||||
"session_id": "plot_0",
|
||||
"new_config": {
|
||||
"name": "UR29",
|
||||
"variables": [
|
||||
"UR29_Brix",
|
||||
"UR29_ma"
|
||||
],
|
||||
"time_window": 30,
|
||||
"y_min": null,
|
||||
"y_max": null,
|
||||
"trigger_variable": null,
|
||||
"trigger_enabled": false,
|
||||
"trigger_on_true": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-08T20:10:10.377616",
|
||||
"level": "info",
|
||||
"event_type": "application_started",
|
||||
"message": "Application initialization completed successfully",
|
||||
"details": {}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-08T20:10:10.444457",
|
||||
"level": "info",
|
||||
"event_type": "dataset_activated",
|
||||
"message": "Dataset activated: DAR",
|
||||
"details": {
|
||||
"dataset_id": "DAR",
|
||||
"variables_count": 2,
|
||||
"streaming_count": 2,
|
||||
"prefix": "gateway_phoenix"
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-08T20:10:10.457492",
|
||||
"level": "info",
|
||||
"event_type": "csv_recording_started",
|
||||
"message": "CSV recording started: 1 datasets activated",
|
||||
"details": {
|
||||
"activated_datasets": 1,
|
||||
"total_datasets": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-08T20:12:14.116200",
|
||||
"level": "info",
|
||||
"event_type": "application_started",
|
||||
"message": "Application initialization completed successfully",
|
||||
"details": {}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-08T20:12:14.185262",
|
||||
"level": "info",
|
||||
"event_type": "dataset_activated",
|
||||
"message": "Dataset activated: DAR",
|
||||
"details": {
|
||||
"dataset_id": "DAR",
|
||||
"variables_count": 2,
|
||||
"streaming_count": 2,
|
||||
"prefix": "gateway_phoenix"
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-08T20:12:14.194260",
|
||||
"level": "info",
|
||||
"event_type": "csv_recording_started",
|
||||
"message": "CSV recording started: 1 datasets activated",
|
||||
"details": {
|
||||
"activated_datasets": 1,
|
||||
"total_datasets": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-09T00:58:14.018492",
|
||||
"level": "info",
|
||||
"event_type": "plot_session_updated",
|
||||
"message": "Plot session 'UR29' configuration updated",
|
||||
"details": {
|
||||
"session_id": "plot_0",
|
||||
"new_config": {
|
||||
"name": "UR29",
|
||||
"variables": [
|
||||
"UR29_Brix",
|
||||
"UR29_ma"
|
||||
],
|
||||
"time_window": 10,
|
||||
"y_min": null,
|
||||
"y_max": null,
|
||||
"trigger_variable": null,
|
||||
"trigger_enabled": false,
|
||||
"trigger_on_true": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-09T00:58:18.412418",
|
||||
"level": "info",
|
||||
"event_type": "plot_session_updated",
|
||||
"message": "Plot session 'UR29' configuration updated",
|
||||
"details": {
|
||||
"session_id": "plot_0",
|
||||
"new_config": {
|
||||
"name": "UR29",
|
||||
"variables": [
|
||||
"UR29_Brix",
|
||||
"UR29_ma"
|
||||
],
|
||||
"time_window": 120,
|
||||
"y_min": null,
|
||||
"y_max": null,
|
||||
"trigger_variable": null,
|
||||
"trigger_enabled": false,
|
||||
"trigger_on_true": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-09T00:59:29.400804",
|
||||
"level": "info",
|
||||
"event_type": "application_started",
|
||||
"message": "Application initialization completed successfully",
|
||||
"details": {}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-09T00:59:29.499648",
|
||||
"level": "info",
|
||||
"event_type": "dataset_activated",
|
||||
"message": "Dataset activated: DAR",
|
||||
"details": {
|
||||
"dataset_id": "DAR",
|
||||
"variables_count": 2,
|
||||
"streaming_count": 2,
|
||||
"prefix": "gateway_phoenix"
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-09T00:59:29.508648",
|
||||
"level": "info",
|
||||
"event_type": "csv_recording_started",
|
||||
"message": "CSV recording started: 1 datasets activated",
|
||||
"details": {
|
||||
"activated_datasets": 1,
|
||||
"total_datasets": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-09T00:59:52.220857",
|
||||
"level": "info",
|
||||
"event_type": "plot_session_updated",
|
||||
"message": "Plot session 'UR29' configuration updated",
|
||||
"details": {
|
||||
"session_id": "plot_0",
|
||||
"new_config": {
|
||||
"name": "UR29",
|
||||
"variables": [
|
||||
"UR29_Brix",
|
||||
"UR29_ma"
|
||||
],
|
||||
"time_window": 50,
|
||||
"y_min": null,
|
||||
"y_max": null,
|
||||
"trigger_variable": null,
|
||||
"trigger_enabled": false,
|
||||
"trigger_on_true": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-09T01:04:33.254115",
|
||||
"level": "info",
|
||||
"event_type": "application_started",
|
||||
"message": "Application initialization completed successfully",
|
||||
"details": {}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-09T01:04:33.318060",
|
||||
"level": "info",
|
||||
"event_type": "dataset_activated",
|
||||
"message": "Dataset activated: DAR",
|
||||
"details": {
|
||||
"dataset_id": "DAR",
|
||||
"variables_count": 2,
|
||||
"streaming_count": 2,
|
||||
"prefix": "gateway_phoenix"
|
||||
}
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-08-09T01:04:33.328051",
|
||||
"level": "info",
|
||||
"event_type": "csv_recording_started",
|
||||
"message": "CSV recording started: 1 datasets activated",
|
||||
"details": {
|
||||
"activated_datasets": 1,
|
||||
"total_datasets": 1
|
||||
}
|
||||
}
|
||||
],
|
||||
"last_updated": "2025-08-08T19:35:31.446174",
|
||||
"total_entries": 742
|
||||
"last_updated": "2025-08-09T01:04:33.328051",
|
||||
"total_entries": 767
|
||||
}
|
|
@ -33,5 +33,5 @@
|
|||
],
|
||||
"current_dataset_id": "DAR",
|
||||
"version": "1.0",
|
||||
"last_update": "2025-08-08T19:35:31.435189"
|
||||
"last_update": "2025-08-09T01:04:33.316045"
|
||||
}
|
|
@ -1,21 +1,21 @@
|
|||
{
|
||||
"plots": {
|
||||
"plot_20": {
|
||||
"plot_0": {
|
||||
"name": "UR29",
|
||||
"variables": [
|
||||
"UR29_Brix",
|
||||
"UR29_ma"
|
||||
],
|
||||
"time_window": 10,
|
||||
"time_window": 50,
|
||||
"y_min": null,
|
||||
"y_max": null,
|
||||
"trigger_variable": null,
|
||||
"trigger_enabled": false,
|
||||
"trigger_on_true": true,
|
||||
"session_id": "plot_20"
|
||||
"session_id": "plot_0"
|
||||
}
|
||||
},
|
||||
"session_counter": 21,
|
||||
"last_saved": "2025-08-08T16:24:02.832199",
|
||||
"session_counter": 1,
|
||||
"last_saved": "2025-08-09T00:59:52.219460",
|
||||
"version": "1.0"
|
||||
}
|
|
@ -37,6 +37,19 @@ class PlotManager {
|
|||
this.init();
|
||||
}
|
||||
|
||||
// Asegura el toggle correcto de la sección de Trigger dentro del formulario (usado en edición)
|
||||
togglePlotFormTriggerConfig() {
|
||||
try {
|
||||
const triggerEnabled = document.getElementById('plot-form-trigger-enabled');
|
||||
const triggerConfig = document.getElementById('plot-form-trigger-config');
|
||||
if (triggerConfig && triggerEnabled) {
|
||||
triggerConfig.style.display = triggerEnabled.checked ? 'block' : 'none';
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('togglePlotFormTriggerConfig failed', e);
|
||||
}
|
||||
}
|
||||
|
||||
init() {
|
||||
// Cargar sesiones existentes al inicializar
|
||||
this.loadExistingSessions();
|
||||
|
@ -284,6 +297,27 @@ class PlotManager {
|
|||
tooltip: {
|
||||
mode: 'index',
|
||||
intersect: false
|
||||
},
|
||||
// Integración con chartjs-plugin-zoom (pan/zoom en eje X)
|
||||
zoom: {
|
||||
pan: {
|
||||
enabled: true,
|
||||
mode: 'x'
|
||||
},
|
||||
zoom: {
|
||||
pinch: { enabled: true },
|
||||
wheel: { enabled: true },
|
||||
mode: 'x'
|
||||
},
|
||||
limits: {
|
||||
x: {
|
||||
// Permitir variar delay/duration en un rango razonable
|
||||
minDelay: 0,
|
||||
maxDelay: 4000,
|
||||
minDuration: 1000,
|
||||
maxDuration: (config.time_window || 60) * 1000
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -783,6 +817,23 @@ class PlotManager {
|
|||
|
||||
// Actualizar estadísticas del plot
|
||||
this.updatePlotStats(sessionId, sessionInfo);
|
||||
|
||||
// Sincronizar controles del sub-tab (refresh rate y time window)
|
||||
try {
|
||||
const refreshInput = document.getElementById(`refresh-rate-tab-${sessionId}`);
|
||||
if (refreshInput) {
|
||||
refreshInput.value = this.refreshRates.get(sessionId) || 1000;
|
||||
}
|
||||
|
||||
const timeWindowInput = document.getElementById(`time-window-tab-${sessionId}`);
|
||||
if (timeWindowInput) {
|
||||
const sessionData = this.sessions.get(sessionId);
|
||||
const currentWindow = (sessionData && sessionData.config && sessionData.config.time_window) ? sessionData.config.time_window : (plotConfig.time_window || 60);
|
||||
timeWindowInput.value = currentWindow;
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('Could not sync plot controls for session', sessionId, e);
|
||||
}
|
||||
}
|
||||
|
||||
updatePlotStats(sessionId, sessionInfo) {
|
||||
|
@ -1539,6 +1590,152 @@ class PlotManager {
|
|||
console.error('❌ Error updating refresh rate:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Actualiza dinámicamente la ventana de tiempo (span) del plot sin recrearlo
|
||||
*/
|
||||
updateTimeWindow(sessionId, timeWindowSeconds) {
|
||||
try {
|
||||
const seconds = parseInt(timeWindowSeconds);
|
||||
if (isNaN(seconds)) return;
|
||||
const clampedSeconds = Math.min(Math.max(seconds, 5), 3600);
|
||||
|
||||
const sessionData = this.sessions.get(sessionId);
|
||||
if (!sessionData || !sessionData.chart) return;
|
||||
|
||||
// Guardar en la config local
|
||||
sessionData.config = sessionData.config || {};
|
||||
sessionData.config.time_window = clampedSeconds;
|
||||
|
||||
const chart = sessionData.chart;
|
||||
|
||||
if (sessionData.isRealTimeMode) {
|
||||
// Modo realtime: ajustar duración de la escala en ms
|
||||
const ms = clampedSeconds * 1000;
|
||||
if (chart.scales && chart.scales.x && chart.scales.x.realtime) {
|
||||
chart.scales.x.realtime.duration = ms;
|
||||
}
|
||||
if (chart.options && chart.options.scales && chart.options.scales.x && chart.options.scales.x.realtime) {
|
||||
chart.options.scales.x.realtime.duration = ms;
|
||||
}
|
||||
chart.update('quiet');
|
||||
} else {
|
||||
// Modo fallback: limpiar datos fuera de la nueva ventana y refrescar
|
||||
this.cleanupOldDataFallback(sessionId);
|
||||
chart.update('quiet');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ Error updating time window:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler para cambios del control de ventana de tiempo en UI, y persistencia en backend
|
||||
*/
|
||||
async onTimeWindowChange(sessionId, timeWindowSeconds) {
|
||||
this.updateTimeWindow(sessionId, timeWindowSeconds);
|
||||
try {
|
||||
const sessionData = this.sessions.get(sessionId);
|
||||
if (!sessionData) return;
|
||||
|
||||
const baseConfig = sessionData.config || {};
|
||||
const payload = {
|
||||
name: baseConfig.name || `Plot ${sessionId}`,
|
||||
variables: Array.isArray(baseConfig.variables) ? baseConfig.variables : [],
|
||||
time_window: parseInt(timeWindowSeconds) || baseConfig.time_window || 60,
|
||||
y_min: baseConfig.y_min ?? null,
|
||||
y_max: baseConfig.y_max ?? null,
|
||||
trigger_enabled: baseConfig.trigger_enabled || false,
|
||||
trigger_variable: baseConfig.trigger_variable || null,
|
||||
trigger_on_true: baseConfig.trigger_on_true !== false,
|
||||
};
|
||||
|
||||
const response = await fetch(`/api/plots/${sessionId}/config`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
if (result && result.success) {
|
||||
sessionData.config = { ...baseConfig, ...payload };
|
||||
this.updatePlotStats(sessionId, sessionData.config);
|
||||
showNotification(result.message || 'Plot updated', 'success');
|
||||
} else if (result && result.error) {
|
||||
showNotification(result.error, 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ Error persisting time window:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Aplica una configuración de plot al chart existente sin recrearlo
|
||||
*/
|
||||
applyConfigToChart(sessionId, config) {
|
||||
const sessionData = this.sessions.get(sessionId);
|
||||
if (!sessionData || !sessionData.chart) return;
|
||||
|
||||
// Guardar config local
|
||||
sessionData.config = { ...(sessionData.config || {}), ...config };
|
||||
|
||||
// 1) Actualizar ventana de tiempo
|
||||
if (typeof config.time_window !== 'undefined') {
|
||||
this.updateTimeWindow(sessionId, config.time_window);
|
||||
}
|
||||
|
||||
// 2) Actualizar rango Y
|
||||
const chart = sessionData.chart;
|
||||
if (chart.options && chart.options.scales && chart.options.scales.y) {
|
||||
chart.options.scales.y.min = config.y_min ?? undefined;
|
||||
chart.options.scales.y.max = config.y_max ?? undefined;
|
||||
}
|
||||
|
||||
// 3) Actualizar datasets según variables
|
||||
if (Array.isArray(config.variables)) {
|
||||
const oldDatasets = chart.data.datasets || [];
|
||||
const oldByLabel = new Map();
|
||||
oldDatasets.forEach(ds => {
|
||||
if (ds && typeof ds.label === 'string') oldByLabel.set(ds.label, ds);
|
||||
});
|
||||
|
||||
const newDatasets = config.variables.map((variable, index) => {
|
||||
const existing = oldByLabel.get(variable);
|
||||
const color = this.getColor(variable, index);
|
||||
if (existing) {
|
||||
existing.label = variable;
|
||||
existing.borderColor = color;
|
||||
existing.backgroundColor = color + '20';
|
||||
existing.borderWidth = 2;
|
||||
existing.fill = false;
|
||||
existing.pointRadius = 0;
|
||||
existing.pointHoverRadius = 3;
|
||||
existing.tension = 0.1;
|
||||
return existing;
|
||||
}
|
||||
return {
|
||||
label: variable,
|
||||
data: [],
|
||||
borderColor: color,
|
||||
backgroundColor: color + '20',
|
||||
borderWidth: 2,
|
||||
fill: false,
|
||||
pointRadius: 0,
|
||||
pointHoverRadius: 3,
|
||||
tension: 0.1
|
||||
};
|
||||
});
|
||||
|
||||
chart.data.datasets = newDatasets;
|
||||
|
||||
// Recalcular índices y timestamps de empuje
|
||||
sessionData.datasetIndex = new Map();
|
||||
sessionData.lastPushedXByDataset = new Map();
|
||||
config.variables.forEach((variable, idx) => sessionData.datasetIndex.set(variable, idx));
|
||||
}
|
||||
|
||||
chart.update('quiet');
|
||||
}
|
||||
}
|
||||
|
||||
// Funciones de debug removidas para limpiar console.log
|
||||
|
@ -1661,6 +1858,17 @@ function initializePlotManager() {
|
|||
return;
|
||||
}
|
||||
|
||||
// Intentar registrar plugin de zoom (UMD) con soporte a default export
|
||||
try {
|
||||
// chartjs-plugin-zoom UMD expone global ChartZoom
|
||||
if (window.ChartZoom) {
|
||||
try { Chart.register(window.ChartZoom); } catch (_) { }
|
||||
}
|
||||
console.log('🔎 Zoom plugin registration attempted. has ChartZoom =', !!window.ChartZoom);
|
||||
} catch (e) {
|
||||
console.warn('⚠️ Zoom plugin registration attempt failed:', e);
|
||||
}
|
||||
|
||||
// Verificar si RealTimeScale está disponible
|
||||
const hasRealTimeScale = !!Chart.registry.scales.realtime;
|
||||
|
||||
|
|
|
@ -116,6 +116,29 @@ class TabManager {
|
|||
</span>
|
||||
</div>
|
||||
<div class="plot-canvas">
|
||||
<div style="display:flex; justify-content: space-between; align-items:center; gap: 0.5rem; margin-bottom: 0.5rem;">
|
||||
<div class="refresh-rate-control">
|
||||
<label for="refresh-rate-tab-${sessionId}" title="Chart Refresh Rate (ms)">⏱️</label>
|
||||
<input type="number" id="refresh-rate-tab-${sessionId}" class="refresh-rate-input"
|
||||
value="1000" min="100" max="60000" step="100"
|
||||
onchange="plotManager.updateRefreshRate('${sessionId}', this.value)"
|
||||
onkeypress="if(event.key==='Enter') plotManager.updateRefreshRate('${sessionId}', this.value)"
|
||||
title="Refresh rate in milliseconds (100-60000)">
|
||||
<span class="refresh-rate-unit">ms</span>
|
||||
</div>
|
||||
<div class="time-window-control">
|
||||
<label for="time-window-tab-${sessionId}" title="Time Window (seconds)">🕒</label>
|
||||
<input type="number" id="time-window-tab-${sessionId}" class="time-window-input"
|
||||
value="60" min="5" max="3600" step="5"
|
||||
onchange="plotManager.onTimeWindowChange('${sessionId}', this.value)"
|
||||
onkeypress="if(event.key==='Enter') plotManager.onTimeWindowChange('${sessionId}', this.value)"
|
||||
title="Visible time window in seconds (5-3600)">
|
||||
<span class="time-window-unit">s</span>
|
||||
</div>
|
||||
<div class="zoom-controls">
|
||||
<button type="button" class="outline" title="Reset Zoom" onclick="(function(){ try { const s=plotManager.sessions.get('${sessionId}'); if(s&&s.chart&&typeof s.chart.resetZoom==='function'){ s.chart.resetZoom('none'); } } catch(e){ console.warn(e);} })()">🔍 Reset</button>
|
||||
</div>
|
||||
</div>
|
||||
<canvas id="chart-${sessionId}"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -7,5 +7,5 @@
|
|||
]
|
||||
},
|
||||
"auto_recovery_enabled": true,
|
||||
"last_update": "2025-08-08T19:35:31.456173"
|
||||
"last_update": "2025-08-09T01:04:33.338067"
|
||||
}
|
|
@ -758,6 +758,7 @@
|
|||
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.9.1"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/luxon@2"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-luxon@1.3.1"></script>
|
||||
<script src="https://unpkg.com/chartjs-plugin-zoom@1.2.1/dist/chartjs-plugin-zoom.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-streaming@2.0.0"></script>
|
||||
|
||||
<!-- JavaScript Modules -->
|
||||
|
|
Loading…
Reference in New Issue