let ws; let lastMessageTimestamp = 0; const IDLE_THRESHOLD_MS = 1000; const loadingIndicator = document.getElementById("loadingIndicator"); let chartA, chartB; let dataA = { labels: [], datasets: [ { label: "RPM", data: [] }, { label: "Coils12 Delay", data: [] }, { label: "Coils34 Delay", data: [] } ] }; let dataB = { labels: [], datasets: [ { label: "RPM", data: [] }, { label: "Coils12 Delay", data: [] }, { label: "Coils34 Delay", data: [] } ] }; function setLoadingIndicator(visible) { if (!loadingIndicator) { return; } loadingIndicator.classList.toggle("hidden", !visible); } function updateLoadingState() { const isConnected = ws && ws.readyState === WebSocket.OPEN; const idle = Date.now() - lastMessageTimestamp >= IDLE_THRESHOLD_MS; setLoadingIndicator(isConnected && idle); } function connectWS() { ws = new WebSocket("ws://" + location.host + "/ws"); ws.onopen = () => { console.log("WebSocket connesso"); lastMessageTimestamp = Date.now(); setLoadingIndicator(false); ws.send(JSON.stringify({ cmd: "setTime", time: Math.floor(Date.now() / 1000) })); }; ws.onclose = () => { console.log("WebSocket disconnesso, retry..."); setLoadingIndicator(false); setTimeout(connectWS, 5000); }; ws.onmessage = (event) => { let data; try { data = JSON.parse(event.data); } catch (e) { console.error("Invalid JSON received", e); return; } lastMessageTimestamp = Date.now(); setLoadingIndicator(false); updateCharts(data) // Update Box_A if (data.box_a) { const boxA = data.box_a; document.getElementById("a_datavalid").textContent = boxA.datavalid ?? "-"; document.getElementById("a_timestamp").textContent = boxA.timestamp ?? "-"; document.getElementById("a_volts_gen").textContent = boxA.volts_gen ?? "-"; document.getElementById("a_eng_rpm").textContent = boxA.eng_rpm ?? "-"; document.getElementById("a_adc_read_time").textContent = boxA.adc_read_time ?? "-"; document.getElementById("a_n_queue_errors").textContent = boxA.n_queue_errors ?? "-"; const coils12A = boxA.coils12 || {}; const coils34A = boxA.coils34 || {}; document.getElementById("a_coils12_spark_delay").textContent = coils12A.spark_delay ?? "-"; document.getElementById("a_coils34_spark_delay").textContent = coils34A.spark_delay ?? "-"; document.getElementById("a_coils12_spark_status").textContent = coils12A.spark_status ?? "-"; document.getElementById("a_coils34_spark_status").textContent = coils34A.spark_status ?? "-"; document.getElementById("a_coils12_sstart_status").textContent = coils12A.sstart_status ?? "-"; document.getElementById("a_coils34_sstart_status").textContent = coils34A.sstart_status ?? "-"; document.getElementById("a_coils12_peak_p_in").textContent = coils12A.peak_p_in ?? "-"; document.getElementById("a_coils34_peak_p_in").textContent = coils34A.peak_p_in ?? "-"; document.getElementById("a_coils12_peak_n_in").textContent = coils12A.peak_n_in ?? "-"; document.getElementById("a_coils34_peak_n_in").textContent = coils34A.peak_n_in ?? "-"; document.getElementById("a_coils12_peak_p_out").textContent = coils12A.peak_p_out ?? "-"; document.getElementById("a_coils34_peak_p_out").textContent = coils34A.peak_p_out ?? "-"; document.getElementById("a_coils12_peak_n_out").textContent = coils12A.peak_n_out ?? "-"; document.getElementById("a_coils34_peak_n_out").textContent = coils34A.peak_n_out ?? "-"; document.getElementById("a_coils12_level_spark").textContent = coils12A.level_spark ?? "-"; document.getElementById("a_coils34_level_spark").textContent = coils34A.level_spark ?? "-"; document.getElementById("a_coils12_n_events").textContent = coils12A.n_events ?? "-"; document.getElementById("a_coils34_n_events").textContent = coils34A.n_events ?? "-"; document.getElementById("a_coils12_n_missed_firing").textContent = coils12A.n_missed_firing ?? "-"; document.getElementById("a_coils34_n_missed_firing").textContent = coils34A.n_missed_firing ?? "-"; } // Update Box_B if (data.box_b) { const boxB = data.box_b; document.getElementById("b_datavalid").textContent = boxB.datavalid ?? "-"; document.getElementById("b_timestamp").textContent = boxB.timestamp ?? "-"; document.getElementById("b_volts_gen").textContent = boxB.volts_gen ?? "-"; document.getElementById("b_eng_rpm").textContent = boxB.eng_rpm ?? "-"; document.getElementById("b_adc_read_time").textContent = boxB.adc_read_time ?? "-"; document.getElementById("b_n_queue_errors").textContent = boxB.n_queue_errors ?? "-"; const coils12B = boxB.coils12 || {}; const coils34B = boxB.coils34 || {}; document.getElementById("b_coils12_spark_delay").textContent = coils12B.spark_delay ?? "-"; document.getElementById("b_coils34_spark_delay").textContent = coils34B.spark_delay ?? "-"; document.getElementById("b_coils12_spark_status").textContent = coils12B.spark_status ?? "-"; document.getElementById("b_coils34_spark_status").textContent = coils34B.spark_status ?? "-"; document.getElementById("b_coils12_sstart_status").textContent = coils12B.sstart_status ?? "-"; document.getElementById("b_coils34_sstart_status").textContent = coils34B.sstart_status ?? "-"; document.getElementById("b_coils12_peak_p_in").textContent = coils12B.peak_p_in ?? "-"; document.getElementById("b_coils34_peak_p_in").textContent = coils34B.peak_p_in ?? "-"; document.getElementById("b_coils12_peak_n_in").textContent = coils12B.peak_n_in ?? "-"; document.getElementById("b_coils34_peak_n_in").textContent = coils34B.peak_n_in ?? "-"; document.getElementById("b_coils12_peak_p_out").textContent = coils12B.peak_p_out ?? "-"; document.getElementById("b_coils34_peak_p_out").textContent = coils34B.peak_p_out ?? "-"; document.getElementById("b_coils12_peak_n_out").textContent = coils12B.peak_n_out ?? "-"; document.getElementById("b_coils34_peak_n_out").textContent = coils34B.peak_n_out ?? "-"; document.getElementById("b_coils12_level_spark").textContent = coils12B.level_spark ?? "-"; document.getElementById("b_coils34_level_spark").textContent = coils34B.level_spark ?? "-"; document.getElementById("b_coils12_n_events").textContent = coils12B.n_events ?? "-"; document.getElementById("b_coils34_n_events").textContent = coils34B.n_events ?? "-"; document.getElementById("b_coils12_n_missed_firing").textContent = coils12B.n_missed_firing ?? "-"; document.getElementById("b_coils34_n_missed_firing").textContent = coils34B.n_missed_firing ?? "-"; } }; } function updateCharts(data) { const t = new Date().toLocaleTimeString(); // ===== BOX A ===== dataA.labels.push(t); if (data.box_a) { dataA.datasets[0].data.push(data.box_a.eng_rpm / 10); dataA.datasets[1].data.push(data.box_a.coils12.spark_delay); dataA.datasets[2].data.push(data.box_a.coils34.spark_delay); } else { dataA.datasets[0].data.push(undefined); dataA.datasets[1].data.push(undefined); dataA.datasets[2].data.push(undefined); } // ===== BOX B ===== dataB.labels.push(t); if (data.box_b) { dataB.datasets[0].data.push(data.box_b.eng_rpm / 10); dataB.datasets[1].data.push(data.box_b.coils12.spark_delay); dataB.datasets[2].data.push(data.box_b.coils34.spark_delay); } else { dataB.datasets[0].data.push(undefined); dataB.datasets[1].data.push(undefined); dataB.datasets[2].data.push(undefined); } // limite buffer const maxPoints = 100; if (dataA.labels.length > maxPoints) { dataA.labels.shift(); dataA.datasets.forEach(d => d.data.shift()); } if (dataB.labels.length > maxPoints) { dataB.labels.shift(); dataB.datasets.forEach(d => d.data.shift()); } chartA.update(); chartB.update(); } function start() { fetch("/start"); } function stop() { fetch("/stop"); } function uploadLittleFS() { const fileInput = document.getElementById("littlefsFile"); const status = document.getElementById("uploadStatus"); if (!fileInput || fileInput.files.length === 0) { if (status) status.textContent = "Select a file first."; return; } const file = fileInput.files[0]; const formData = new FormData(); formData.append("file", file, file.name); if (status) status.textContent = "Uploading..."; fetch("/upload", { method: "POST", body: formData, }) .then((resp) => { if (!resp.ok) { throw new Error("Upload failed: " + resp.status + " " + resp.statusText); } return resp.text(); }) .then(() => { if (status) status.textContent = "Uploaded: " + file.name; fileInput.value = ""; }) .catch((err) => { if (status) status.textContent = err.message; }); } function openTab(tabId) { document.querySelectorAll('.tab-content').forEach(tab => { tab.classList.remove('active'); }); document.querySelectorAll('.tab-button').forEach(btn => { btn.classList.remove('active'); }); document.getElementById(tabId).classList.add('active'); event.target.classList.add('active'); } function initCharts() { const ctxA = document.getElementById('chartA').getContext('2d'); const ctxB = document.getElementById('chartB').getContext('2d'); chartA = new Chart(ctxA, { type: 'line', data: dataA, options: { animation: false, responsive: true, scales: { x: { display: true }, y: { beginAtZero: true } } } }); chartB = new Chart(ctxB, { type: 'line', data: dataB, options: { animation: false, responsive: true, scales: { x: { display: true }, y: { beginAtZero: true } } } }); } window.onload = () => { initCharts(); }; setInterval(updateLoadingState, 200); connectWS();