const canvasA = document.querySelector("#chartA"); const canvasB = document.querySelector("#chartB"); const chartUpdateInt = document.querySelector("#chartUpd").value; document.querySelector("#stopRec").disabled = true; let data; let record = 0; let filename; let csvData; let labelA, labelB; const chartAdata = { data: { labels: [], datasets: [ { label: "Channel A temperature", backgroundColor: "red", borderColor: "red", data: [], }, ], }, }; const chartBdata = { data: { labels: [], datasets: [ { label: "Channel B temperature", backgroundColor: "blue", borderColor: "blue", data: [], }, ], }, }; const options = { animation: false, elements: { point: { pointStyle: false, }, }, responsive: true, plugins: { legend: { labels: { color: "rgba(250, 250, 250, 1)", }, }, }, scales: { y: { title: { display: true, text: "Temperature (°C)", color: "rgb(250, 250, 250)", }, grid: { color: "rgba(250, 250, 250, 0.5)", }, ticks: { color: "rgba(250, 250, 250, 0.5)", }, suggestedMin: 0, suggestedMax: 50, }, x: { title: { display: true, text: "Time (s)", color: "rgb(250, 250, 250)", }, grid: { color: "rgba(250, 250, 250, 0.5)", }, ticks: { color: "rgba(250, 250, 250, 0.5)", }, }, }, }; fetch("/int") .then((res) => res.text()) .then((textResponse) => { document.querySelector("#updInt").innerText = textResponse; }); document.querySelector("#cupdInt").innerText = chartUpdateInt; document.querySelector("#recInfo").style.display = "none"; const chartA = new Chart(canvasA, { type: "line", data: { labels: [], datasets: [ { label: "Channel A temperature", backgroundColor: "red", borderColor: "red", data: [], }, ], }, options: options, }); const chartB = new Chart(canvasB, { type: "line", data: { labels: [], datasets: [ { label: "Channel B temperature", backgroundColor: "blue", borderColor: "blue", data: [], }, ], }, options: options, }); var gateway = `ws://${window.location.hostname}/ws`; var websocket; // Init web socket when the page loads window.addEventListener("load", onload); function onload(event) { initWebSocket(); } function getReadings() { websocket.send("getReadings"); } function setUpdateInterval() { let updateInt = document.querySelector("#upd").value; websocket.send("u" + updateInt); location.reload(); } function getUpdateInterval() { var xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function () { if (this.readyState == 4 && this.status == 200) { document.querySelector("#upd").value = this.responseText; } }; xhttp.open("GET", "/int", true); xhttp.send(); } function initWebSocket() { console.log("Trying to open a WebSocket connection..."); websocket = new WebSocket(gateway); websocket.onopen = onOpen; websocket.onclose = onClose; websocket.onmessage = onMessage; } // When websocket is established, call the getReadings() function function onOpen(event) { console.log("Connection opened"); getReadings(); } function onClose(event) { console.log("Connection closed"); setTimeout(initWebSocket, 2000); csvData.length = 0; } function addData(chart, label, data) { chart.data.labels.push(label); chart.data.datasets.forEach((dataset) => { dataset.data.push(data); }); chart.update(); } function removeFirstData(chart) { chart.data.labels.splice(0, 1); chart.data.datasets.forEach((dataset) => { dataset.data.shift(); }); } // Function that receives the message from the ESP32 with the readings function onMessage(event) { //console.log(event.data); data = JSON.parse(event.data); let timestamp = new Date(); document.querySelector("#time").innerHTML = timestamp.getTime(); data["time"] = timestamp.getTime(); if (record == 1) { csvData.push(createCSV(data)); } } setInterval(function () { if (isNaN(data["tempA"])) { document.querySelector("#error-tempA").innerHTML = "

" + data["tempA"] + "

"; } else { document.querySelector("#error-tempA").innerText = ""; } if (isNaN(data["tempB"])) { document.querySelector("#error-tempB").innerHTML = "

" + data["tempB"] + "

"; } else { document.querySelector("#error-tempB").innerText = ""; } addData(chartA, data["time"], data["tempA"]); addData(chartB, data["time"], data["tempB"]); }, chartUpdateInt); function escapeValue(value) { var result = ""; var escape = false; for (var i = 0; i < value.length; ++i) { var entry = value[i]; result += entry; switch (entry) { case '"': result += '"'; case ",": escape = true; } } if (escape) return '"' + result + '"'; return result; } function createCSV(object) { var body = ""; for (var column in object) { if (object.hasOwnProperty(column)) { if (body) body += ","; var value = object[column]; if (value == null) continue; body += escapeValue(value.toString()); } } return body; } function startRecording() { let timedRec = document.querySelector("#timedRec").checked; date = new Date(); let filedate = date.toISOString().slice(0, -5).replaceAll(":", ""); filename = "therminator-" + filedate + ".csv"; document.querySelector("#filename").innerText = filename; labelA = document.querySelector("#labelA").value; labelB = document.querySelector("#labelB").value; csvData = ['time,"' + labelA + '","' + labelB + '"']; record = 1; document.querySelector("#startRec").disabled = true; document.querySelector("#stopRec").disabled = false; document.querySelector("#recInfo").style.display = "inline-grid"; document.querySelector("#timedSettings").style.display = "none"; document.querySelector("#timedCheck").style.display = "none"; if (timedRec) { let timer = document.querySelector("#timer").value; if (timer == 0) { alert("Recording time must be non-zero"); return; } timer = timer * 60000; let timeleft = timer / 1000; let disp = document.querySelector("#timeLeft"); startTimer(timeleft, disp); setTimeout(stopRecording, timer); } } function startTimer(duration, display) { var timer = duration, minutes, seconds; setInterval(function () { minutes = parseInt(timer / 60, 10); seconds = parseInt(timer % 60, 10); minutes = minutes < 10 ? "0" + minutes : minutes; seconds = seconds < 10 ? "0" + seconds : seconds; display.textContent = minutes + ":" + seconds; if (--timer < 0) { timer = duration; } }, 1000); } function fancyTimeFormat(e) { const h = Math.floor(e / 3600) .toString() .padStart(2, "0"), m = Math.floor((e % 3600) / 60) .toString() .padStart(2, "0"), s = Math.floor(e % 60) .toString() .padStart(2, "0"); return h + ":" + m + ":" + s; } function stopRecording() { record = 0; let csvFile = csvData.join("\n"); const blob = new Blob([csvFile], { type: "text/csv;charset=utf-8," }); const objUrl = URL.createObjectURL(blob); const link = document.createElement("a"); link.setAttribute("href", objUrl); link.setAttribute("download", filename); document.querySelector("body").append(link); link.click(); csvData.length = 0; document.querySelector("#startRec").disabled = false; document.querySelector("#stopRec").disabled = true; document.querySelector("#recInfo").style.display = "none"; document.querySelector("#timedSettings").style.display = "none"; document.querySelector("#timedCheck").style.display = "inline-block"; document.querySelector("#timedRec").checked = false; document.querySelector("#timeLeft").style.display = "none"; } const beforeUnloadHandler = (event) => { // Recommended event.preventDefault(); // Included for legacy support, e.g. Chrome/Edge < 119 event.returnValue = true; }; onbeforeunload = function () { if (record) { return "You are still recording data! Are you sure you want to leave?"; } }; document.querySelector("#timedRec").onchange = function () { if (this.checked) { document.querySelector("#timedSettings").style.display = "inherit"; } else { document.querySelector("#timedSettings").style.display = "none"; } };