const fetchInterval = 500; // Configuration constants const MEAN = 0; const WARNING_LIMIT = 2000; const ERROR_LIMIT = 3000; const TARGET_VALUE = 0; // State let chart; let isAcquiring = false; let currentMode = 'live'; let activeSessionData = []; let history = []; let currentPage = 1; let fullDataPage = 1; // DOM Elements const startBtn = document.getElementById('startBtn'); const progressBar = document.getElementById('progressBar'); const progressContainer = document.getElementById('progressContainer'); const timerDisplay = document.getElementById('timerDisplay'); const tableBody = document.getElementById('measurementsTable'); const detailPlaceholder = document.getElementById('detailPlaceholder'); const detailContent = document.getElementById('detailContent'); const fullDataTableBody = document.getElementById('fullDataTableBody'); const historyTableBody = document.getElementById('historyTableBody'); const pageInfo = document.getElementById('pageInfo'); const prevBtn = document.getElementById('prevPage'); const nextBtn = document.getElementById('nextPage'); const fullPrevBtn = document.getElementById('fullPrevPage'); const fullNextBtn = document.getElementById('fullNextPage'); const liveDataContainer = document.getElementById('liveDataContainer'); const historyDetailContainer = document.getElementById('historyDetailContainer'); const selectedData = document.getElementById('selectedData'); const plcIpDisplay = document.getElementById('plcIpDisplay'); const connStatusDot = document.getElementById('connStatusDot'); const latencyList = document.getElementById('latencyList'); const acquisitionStatusList = document.getElementById('acquisitionStatusList'); // Initialize Chart.js function initChart() { const ctx = document.getElementById('liveChart').getContext('2d'); chart = new Chart(ctx, { type: 'line', data: { datasets: [ { label: 'Value (mm)', data: [], borderColor: '#3b82f6', backgroundColor: 'rgba(59, 130, 246, 0.1)', fill: true, pointRadius: 0, borderWidth: 2, tension: 0 }, { label: 'Warning UCL', data: [], borderColor: '#facc15', borderWidth: 1, pointRadius: 0, fill: false, borderDash: [5, 5] }, { label: 'Warning LCL', data: [], borderColor: '#facc15', borderWidth: 1, pointRadius: 0, fill: false, borderDash: [5, 5] }, { label: 'Error UCL', data: [], borderColor: '#f87171', borderWidth: 1, pointRadius: 0, fill: false, borderDash: [5, 5] }, { label: 'Error LCL', data: [], borderColor: '#f87171', borderWidth: 1, pointRadius: 0, fill: false, borderDash: [5, 5] }, { label: 'Target Value', data: [], borderColor: '#22c55e', borderWidth: 1.5, pointRadius: 0, fill: false, tension: 0 } ] }, options: { responsive: true, maintainAspectRatio: false, scales: { x: { type: 'linear', min: 0, max: 10000, title: { display: true, text: 'Position (mm)' } }, y: { min: -3000, max: 3000 } }, animation: false } }); } // Fetch data from Flask and update UI async function updateData() { try { const response = await fetch('/data'); if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`); const data = await response.json(); if (data) { // Update Connectivity Status if (data.connected !== undefined) { connStatusDot.className = data.connected ? 'w-2 h-2 bg-green-400 rounded-full mr-2 animate-pulse' : 'w-2 h-2 bg-red-600 rounded-full mr-2'; plcIpDisplay.textContent = data.plc_ip || "Disconnected"; } if (data.db100 && data.db100.duration !== undefined) { latencyList.textContent = `Latency: ${data.db100.duration.toFixed(2)}ms`; } // Update acquisition status text if (data.acq_status !== undefined) { acquisitionStatusList.textContent = `• Mode: ${data.acq_status}`; if (data.acq_status === "Acquisition") { acquisitionStatusList.parentElement.classList.replace('text-gray-600', 'text-blue-400'); } else { acquisitionStatusList.parentElement.classList.replace('text-blue-400', 'text-gray-600'); } } // Handle Live Chart Update if (data.db901 && data.db901.params) { const acq = data.db901.params.acquisizione; const fine = data.db901.params.fine_acq; const pronti = data.db901.params.pronti; // Prendi i limiti dinamici (ucl, lcl, target) dal JSON della risposta API const limits = data.limits || { ucl: 212, lcl: 208, target: 0 }; if (acq && !fine) { acquisitionStatusList.textContent = `• Mode: Acquisition`; acquisitionStatusList.parentElement.classList.replace('text-gray-600', 'text-blue-400'); if (data.db901.params.x_val !== undefined && data.db901.params.y_val !== undefined) { const x = data.db901.params.x_val; const y = data.db901.params.y_val; chart.data.datasets[0].data.push({ x, y }); chart.data.datasets[1].data.push({ x, y: limits.ucl }); chart.data.datasets[2].data.push({ x, y: limits.lcl }); chart.data.datasets[3].data.push({ x, y: limits.ucl }); chart.data.datasets[4].data.push({ x, y: limits.lcl }); chart.data.datasets[5].data.push({ x, y: limits.target }); if (chart.data.datasets[0].data.length > 100) { chart.data.datasets.forEach(ds => ds.data.shift()); } chart.update('none'); } } else if (fine && pronti) { acquisitionStatusList.textContent = `• Mode: Finished`; acquisitionStatusList.parentElement.classList.replace('text-blue-400', 'text-green-600'); if (data.db900 && data.db900.misure && data.db900.misure.length > 0) { activeSessionData = data.db900.misure.map(m => ({ x: m.x, y: m.y, ts: m.ts || new Date() })); chart.data.datasets.forEach(ds => ds.data = []); activeSessionData.forEach(p => { chart.data.datasets[0].data.push({ x: p.x, y: p.y }); chart.data.datasets[1].data.push({ x: p.x, y: limits.ucl }); chart.data.datasets[2].data.push({ x: p.x, y: limits.lcl }); chart.data.datasets[3].data.push({ x: p.x, y: limits.ucl }); chart.data.datasets[4].data.push({ x: p.x, y: limits.lcl }); chart.data.datasets[5].data.push({ x: p.x, y: limits.target }); }); chart.update(); // Mostra dashboard dettagli... } } else { acquisitionStatusList.textContent = `• Mode: Polling (5Hz)`; acquisitionStatusList.parentElement.classList.replace('text-blue-400', 'text-gray-600'); } } // if (data.db901 && data.db901.params) { // const x = data.db901.params.x; // const y = data.db901.params.y; // if (data.acq_status === "Acquisition") { // if (x !== undefined && y !== undefined) { // activeSessionData.push({x, y, ts: new Date()}); // chart.data.datasets[0].data.push({x, y}); // chart.data.datasets[1].data.push({x, y: MEAN + WARNING_LIMIT}); // chart.data.datasets[2].data.push({x, y: MEAN - WARNING_LIMIT}); // chart.data.datasets[3].data.push({x, y: MEAN + ERROR_LIMIT}); // chart.data.datasets[4].data.push({x, y: MEAN - ERROR_LIMIT}); // chart.data.datasets[5].data.push({x, y: TARGET_VALUE}); // if (chart.data.datasets[0].data.length > 100) { // chart.data.datasets.forEach(ds => ds.data.shift()); // } // chart.update(); // } // } else if (data.acq_status === "Finished") { // if (data.measurements && data.measurements.length > 0) { // activeSessionData = data.measurements.map(m => ({ // x: m.x, // y: m.y, // ts: m.ts || new Date() // })); // // Clear chart and load all measurements // chart.data.datasets.forEach(ds => ds.data = []); // activeSessionData.forEach(p => { // chart.data.datasets[0].data.push({x: p.x, y: p.y}); // chart.data.datasets[1].data.push({x: p.x, y: MEAN + WARNING_LIMIT}); // chart.data.datasets[2].data.push({x: p.x, y: MEAN - WARNING_LIMIT}); // chart.data.datasets[3].data.push({x: p.x, y: MEAN + ERROR_LIMIT}); // chart.data.datasets[4].data.push({x: p.x, y: MEAN - ERROR_LIMIT}); // chart.data.datasets[5].data.push({x: p.x, y: TARGET_VALUE}); // }); // chart.update(); // liveDataContainer.classList.remove('hidden'); // historyDetailContainer.classList.add('hidden'); // detailPlaceholder.classList.add('hidden'); // detailContent.classList.remove('hidden'); // renderFullSessionTable(); // } // } // } } } catch (err) { console.error("Error updating data:", err); } } // Fetch history async function fetchHistory() { try { const response = await fetch('/history'); if (response.ok) { history = await response.json(); renderHistoryTable(); } } catch (err) { console.error("Error fetching history:", err); } } function renderHistoryTable() { historyTableBody.innerHTML = ''; if (history.length === 0) { historyTableBody.innerHTML = '