diff --git a/assets/css/style.css b/assets/css/style.css index 90ffd15..65809dc 100644 --- a/assets/css/style.css +++ b/assets/css/style.css @@ -169,16 +169,26 @@ div.twitchstream iframe { } .runtime { - background-color: #24252a; margin: 10px; text-align: left; color: #ffffff; - border-radius: 10px; font-family: "Source Code Pro", monospace; - border: 2px solid white; /* Adjust thickness as needed */ + background: none; /* remove filled background */ + border: none; /* remove complete border */ + border-bottom: 2px solid white; /* line graph baseline */ width: calc(35vw / 2); - height: 20vh; + height: auto; /* remove fixed height */ float: left; + position: relative; +} + +.runtime::after { + content: ""; + position: absolute; + top: 50%; + left: 0; + width: 100%; + height: 0; /* no extra height */ } .voltage { @@ -369,4 +379,12 @@ div.twitchstream iframe { .checklist { font-family: "Source Code Pro", monospace; font-size: 15px; -} \ No newline at end of file +} + +.graph-container { + border: 2px solid #fff; + padding: 5px; + margin: 5px 0; + width: 300px; + height: 200px; +} diff --git a/assets/js/robot.js b/assets/js/robot.js index 6daa562..176d1af 100644 --- a/assets/js/robot.js +++ b/assets/js/robot.js @@ -2,7 +2,12 @@ * contains client-side JavaScript functions * (primarily event handlers to fetch data from the Node server) */ -const socket = window.io(); + +// Ensure `socket` is declared only once and avoid redeclaration + +window.socket = window.io(); // Attach `socket` to the global `window` object + +const socket = window.socket; socket.on("temperatures", (entry) => { const temperatures = document.querySelector("div.temp"); @@ -14,14 +19,77 @@ socket.on("pdhCurrents", (entry) => { pdhDisplay.innerHTML = entry; }); -socket.on("powerStats", (stats) => { - const runtimeDisplay = document.querySelector("div.runtime"); - const voltageDisplay = document.querySelector("div.voltage"); +// new web socket connection for robot runtime data +// web dev code vid + +// Remove Chart.js setup and runtime graph logic +// const ctx = document.getElementById("runtimeGraph").getContext("2d"); +// const runtimeChart = new Chart(ctx, { +// type: "line", +// data: { +// labels: [], // timestamps +// datasets: [ +// { +// label: "Robot Code Runtime", +// data: [], +// borderColor: "rgba(75, 192, 192, 1)", +// fill: false, +// }, +// ], +// }, + +// options: { +// scales: { +// x: { display: true }, +// y: { +// beginAtZero: true, +// }, +// }, +// plugins: { +// annotation: { +// annotations: { +// line1: { +// type: "line", +// yMin: 20, +// yMax: 20, +// borderColor: "red", +// borderWidth: 2, +// label: { +// content: "20 ms", +// enabled: true, +// position: "center", +// }, +// }, +// }, +// }, +// }, +// responsive: false, +// }, +// }); - runtimeDisplay.innerHTML = stats.currentDisplay; - voltageDisplay.innerHTML = stats.voltageDisplay; +// Add WebSocket listener for robotRuntime event +socket.on("robotRuntime", (data) => { + const runtimeDisplay = document.querySelector("div.runtime"); + runtimeDisplay.innerHTML = `Runtime: Timestamp: ${data.timestamp}, Runtime: ${data.runtime} ms`; }); +async function fetchRuntime() { + const runtimeDisplay = document.querySelector("div.runtime"); + const response = await fetch("/runtime"); + if (response.ok) { + runtimeDisplay.innerHTML = `Runtime: ${await response.text()}`; + } else { + console.error("Error fetching runtime data"); + } +} + +// Call the function to fetch runtime info on page load +// add a check to see if runtime data was fetched + +fetchRuntime(); +const data = fetchRuntime(); +console.log("Fetched runtime data:", data); // Debugging statement + async function fetchTemperatures() { const temperatures = document.querySelector("div.temp"); const response = await fetch("/temperatures"); @@ -41,32 +109,18 @@ async function fetchPDHCurrents() { console.log("error fetching PDH currents"); } } - -async function fetchPowerStats() { - const runtimeDisplay = document.querySelector("div.runtime"); - const voltageDisplay = document.querySelector("div.voltage"); - const response = await fetch("/powerStats"); - if (response.ok) { - const stats = await response.json(); - runtimeDisplay.innerHTML = stats.currentDisplay; - voltageDisplay.innerHTML = stats.voltageDisplay; - } else { - console.log("error fetching power stats"); - } -} - +// change the data fetchTemperatures(); fetchPDHCurrents(); -fetchPowerStats(); // Select all checkboxes with a data-key attribute for persistence const checklist = document.querySelectorAll('input[type="checkbox"][data-key]'); checklist.forEach((checkbox) => { // Load persisted state on page load - const key = checkbox.getAttribute('data-key'); + const key = checkbox.getAttribute("data-key"); const saved = localStorage.getItem(key); - if (saved === 'true') { + if (saved === "true") { checkbox.checked = true; } @@ -74,7 +128,7 @@ checklist.forEach((checkbox) => { const label = event.target.closest("label"); const taskText = label.textContent.trim(); const isChecked = event.target.checked; - const key = event.target.getAttribute('data-key'); + const key = event.target.getAttribute("data-key"); // Save state to localStorage localStorage.setItem(key, isChecked); socket.emit("checklist", { taskText, isChecked }); diff --git a/js/robot.js b/js/robot.js new file mode 100644 index 0000000..3772511 --- /dev/null +++ b/js/robot.js @@ -0,0 +1,12 @@ +// Ensure the rest of the code uses the `socket` variable declared in the main HTML file + +// Example usage of the socket variable +socket.on("connect", () => { + console.log("Connected to server"); +}); + +socket.on("disconnect", () => { + console.log("Disconnected from server"); +}); + +// Add other socket event listeners and handlers as needed diff --git a/node_modules/.package-lock.json b/node_modules/.package-lock.json index 12452cb..68112b7 100644 --- a/node_modules/.package-lock.json +++ b/node_modules/.package-lock.json @@ -102,6 +102,12 @@ "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "dev": true }, + "node_modules/@kurkle/color": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz", + "integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==", + "peer": true + }, "node_modules/@msgpack/msgpack": { "version": "3.0.0-beta2", "resolved": "https://registry.npmjs.org/@msgpack/msgpack/-/msgpack-3.0.0-beta2.tgz", @@ -676,6 +682,26 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/chart.js": { + "version": "4.4.8", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.8.tgz", + "integrity": "sha512-IkGZlVpXP+83QpMm4uxEiGqSI7jFizwVtF3+n5Pc3k7sMO+tkd0qxh2OzLhenM0K80xtmAONWGBn082EiBQSDA==", + "peer": true, + "dependencies": { + "@kurkle/color": "^0.3.0" + }, + "engines": { + "pnpm": ">=8" + } + }, + "node_modules/chartjs-plugin-annotation": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/chartjs-plugin-annotation/-/chartjs-plugin-annotation-3.1.0.tgz", + "integrity": "sha512-EkAed6/ycXD/7n0ShrlT1T2Hm3acnbFhgkIEJLa0X+M6S16x0zwj1Fv4suv/2bwayCT3jGPdAtI9uLcAMToaQQ==", + "peerDependencies": { + "chart.js": ">=4.0.0" + } + }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -1009,6 +1035,18 @@ "node": ">=10.2.0" } }, + "node_modules/engine.io-client": { + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.3.tgz", + "integrity": "sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1", + "xmlhttprequest-ssl": "~2.1.1" + } + }, "node_modules/engine.io-parser": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", @@ -3868,6 +3906,20 @@ "ws": "~8.17.1" } }, + "node_modules/socket.io-client": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz", + "integrity": "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.6.1", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/socket.io-parser": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", @@ -4358,6 +4410,14 @@ } } }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz", + "integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", diff --git a/package-lock.json b/package-lock.json index 90f2942..456d3fe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "dependencies": { "@msgpack/msgpack": "^3.0.0-beta2", "axios": "^1.7.9", + "chartjs-plugin-annotation": "^3.1.0", "dotenv": "^16.3.1", "ejs": "^3.1.9", "express": "^4.18.2", @@ -23,6 +24,7 @@ "nodemon": "^3.0.2", "ntcore-ts-client": "^0.2.2", "socket.io": "^4.7.5", + "socket.io-client": "^4.8.1", "tslib": "^2.6.2", "uuid": "^9.0.1", "zod": "^3.23.4" @@ -134,6 +136,12 @@ "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "dev": true }, + "node_modules/@kurkle/color": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz", + "integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==", + "peer": true + }, "node_modules/@msgpack/msgpack": { "version": "3.0.0-beta2", "resolved": "https://registry.npmjs.org/@msgpack/msgpack/-/msgpack-3.0.0-beta2.tgz", @@ -708,6 +716,26 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/chart.js": { + "version": "4.4.8", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.8.tgz", + "integrity": "sha512-IkGZlVpXP+83QpMm4uxEiGqSI7jFizwVtF3+n5Pc3k7sMO+tkd0qxh2OzLhenM0K80xtmAONWGBn082EiBQSDA==", + "peer": true, + "dependencies": { + "@kurkle/color": "^0.3.0" + }, + "engines": { + "pnpm": ">=8" + } + }, + "node_modules/chartjs-plugin-annotation": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/chartjs-plugin-annotation/-/chartjs-plugin-annotation-3.1.0.tgz", + "integrity": "sha512-EkAed6/ycXD/7n0ShrlT1T2Hm3acnbFhgkIEJLa0X+M6S16x0zwj1Fv4suv/2bwayCT3jGPdAtI9uLcAMToaQQ==", + "peerDependencies": { + "chart.js": ">=4.0.0" + } + }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -1041,6 +1069,18 @@ "node": ">=10.2.0" } }, + "node_modules/engine.io-client": { + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.3.tgz", + "integrity": "sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1", + "xmlhttprequest-ssl": "~2.1.1" + } + }, "node_modules/engine.io-parser": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", @@ -3913,6 +3953,20 @@ "ws": "~8.17.1" } }, + "node_modules/socket.io-client": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz", + "integrity": "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.6.1", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/socket.io-parser": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", @@ -4403,6 +4457,14 @@ } } }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz", + "integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", diff --git a/package.json b/package.json index 829f787..a96efdc 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "dependencies": { "@msgpack/msgpack": "^3.0.0-beta2", "axios": "^1.7.9", + "chartjs-plugin-annotation": "^3.1.0", "dotenv": "^16.3.1", "ejs": "^3.1.9", "express": "^4.18.2", @@ -40,6 +41,7 @@ "nodemon": "^3.0.2", "ntcore-ts-client": "^0.2.2", "socket.io": "^4.7.5", + "socket.io-client": "^4.8.1", "tslib": "^2.6.2", "uuid": "^9.0.1", "zod": "^3.23.4" diff --git a/server.js b/server.js index ac5ac7c..f87c2d5 100644 --- a/server.js +++ b/server.js @@ -3,6 +3,10 @@ * this file is executed by the Node server */ +// load environment variables from the .env file into process.env +const dotenv = require("dotenv"); +dotenv.config({ path: ".env" }); + // import the http module, which provides an HTTP server const http = require("http"); @@ -17,22 +21,19 @@ const server = http.createServer(app); const { createSocketServer } = require("./server/socket/socket"); createSocketServer(server); -// load environment variables from the .env file into process.env -const dotenv = require("dotenv"); -dotenv.config({ path: ".env" }); - // connect to MongoDB using mongoose const mongoose = require("mongoose"); -mongoose.connect(process.env.MONGODB_URI, { - useNewUrlParser: true, - useUnifiedTopology: true -}) -.then(() => { - console.log("Connected to MongoDB"); -}) -.catch((err) => { - console.error("MongoDB connection error:", err); -}); +mongoose + .connect(process.env.MONGODB_URI, { + useNewUrlParser: true, + useUnifiedTopology: true, + }) + .then(() => { + console.log("Connected to MongoDB"); + }) + .catch((err) => { + console.error("MongoDB connection error:", err); + }); // add middleware to handle JSON in HTTP request bodies (used with POST commands) app.use(express.json()); @@ -47,6 +48,15 @@ app.use("/img", express.static("assets/img")); app.use("/js", express.static("assets/js")); app.use("/html", express.static("assets/html")); +// NEW: Endpoint that returns ROBOT CODE runtime info +app.get("/robot/api/runtimeInfo", (req, res) => { + const runtimeInfo = { + uptime: process.uptime(), + timestamp: new Date().toISOString(), + }; + res.json(runtimeInfo); +}); + // to keep this file manageable, we will move the routes to a separate file // the exported router object is an example of middleware app.use("/", require("./server/routes/router")); @@ -54,4 +64,4 @@ app.use("/", require("./server/routes/router")); // start the server on port 8081 server.listen(8081, () => { console.log("server is listening on http://localhost:8081"); -}); \ No newline at end of file +}); diff --git a/server/connections/nt4.js b/server/connections/nt4.js index 6ecc957..9349b64 100644 --- a/server/connections/nt4.js +++ b/server/connections/nt4.js @@ -98,4 +98,18 @@ function getPowerStats() { return powerStats; } -module.exports = { getMotorTemperatures, getPDHCurrents, getPowerStats }; +function getCodeRuntime() { + const codeRuntime = ntTopics.find((t) => t.label === "Code Runtime"); + if (codeRuntime && codeRuntime.value) { + return codeRuntime.value; + } else { + return "no code runtime data available"; + } +} + +module.exports = { + getMotorTemperatures, + getPDHCurrents, + getPowerStats, + getCodeRuntime, +}; diff --git a/server/data/chartData.json b/server/data/chartData.json new file mode 100644 index 0000000..fe51488 --- /dev/null +++ b/server/data/chartData.json @@ -0,0 +1 @@ +[] diff --git a/server/model/config.json b/server/model/config.json index 5366f59..e636fd7 100644 --- a/server/model/config.json +++ b/server/model/config.json @@ -2,5 +2,6 @@ "teamNumber": 3061, "eventKey": "2024ksla", "localTimeZone": "America/Chicago", - "streamURL": "https://player.twitch.tv/?channel=funroboticsnetwork&parent=pit.team3061.org" + "streamURL": "https://player.twitch.tv/?channel=funroboticsnetwork&parent=pit.team3061.org", + "graphType": "line" } diff --git a/server/model/ntTopics.json b/server/model/ntTopics.json index 8711d87..2ebe1c6 100644 --- a/server/model/ntTopics.json +++ b/server/model/ntTopics.json @@ -100,5 +100,23 @@ "path": "/AdvantageKit/PowerDistribution/Voltage", "type": "double", "dataCategory": "POWER_STATS" + }, + { + "label": "Full Cycle MS", + "path": "/RealOutputs/LoggedRobot/FullCycleMS", + "type": "double", + "dataCategory": "CODE_RUNTIME" + }, + { + "label": "Log Period MS", + "path": "/RealOutputs/LoggedRobot/LogPeriodicMS", + "type": "double", + "dataCategory": "CODE_RUNTIME" + }, + { + "label": "User Code MS", + "path": "/RealOutputs/LoggedRobot/UserCodeMS", + "type": "double", + "dataCategory": "CODE_RUNTIME" } ] diff --git a/server/public/js/chart.js b/server/public/js/chart.js new file mode 100644 index 0000000..65aa64e --- /dev/null +++ b/server/public/js/chart.js @@ -0,0 +1,66 @@ +// ...existing code setting up the line chart... +const canvas = document.getElementById("chart"); +canvas.width = 400; // reduced width +canvas.height = 300; // reduced height +const ctx = canvas.getContext("2d"); + +const chart = new Chart(ctx, { + type: "line", + data: { + labels: [], + datasets: [ + { + // ...existing dataset properties... + data: [], + borderColor: "white", + backgroundColor: "rgba(255,255,255,0.2)", + borderWidth: 2, // reduced thickness + pointRadius: 3, // smaller points + }, + ], + }, + options: { + scales: { + x: { + ticks: { color: "white" }, + grid: { color: "rgba(255,255,255,0.2)" }, + }, + y: { + ticks: { color: "white" }, + grid: { color: "rgba(255,255,255,0.2)" }, + }, + }, + plugins: { + legend: { + labels: { color: "white" }, + }, + }, + responsive: false, // ensure canvas styling is respected + }, +}); + +// Initialize socket.io client +const socket = io(); + +// Function to add a new point to the chart +function addPointToChart(point) { + // ...existing code to update chart instance... + chart.data.labels.push(point.label); + chart.data.datasets[0].data.push(point.value); + chart.update(); +} + +// Fetch persisted chart data on page load +fetch("/api/chart") + .then((res) => res.json()) + .then((data) => { + console.log("Raw chart data:", data); // new print statement for debugging + data.forEach((point) => { + addPointToChart(point); + }); + }); + +// Listen for real-time updates +socket.on("chartPointAdded", (point) => { + addPointToChart(point); +}); diff --git a/server/public/js/robot.js b/server/public/js/robot.js new file mode 100644 index 0000000..91cda40 --- /dev/null +++ b/server/public/js/robot.js @@ -0,0 +1,72 @@ +document.addEventListener("DOMContentLoaded", function () { + const chartCanvas = document.getElementById("chart"); + if (!chartCanvas) { + console.error("Canvas element not found"); + return; + } + chartCanvas.width = 400; // reduced width + chartCanvas.height = 300; // reduced height + const ctx = chartCanvas.getContext("2d"); + + const runtimeCanvas = document.getElementById("runtimeGraph"); + if (!runtimeCanvas) { + console.error("Canvas element with id 'runtimeGraph' not found"); + return; + } + runtimeCanvas.width = 400; // optionally set size + runtimeCanvas.height = 300; + const runtimeCtx = runtimeCanvas.getContext("2d"); + + const runtimeChart = new Chart(runtimeCtx, { + type: "line", + data: { + labels: [], // ...existing labels... + datasets: [ + { + label: "Runtime Data", + data: [], // ...existing dataset data... + borderColor: "blue", + backgroundColor: "rgba(0,0,255,0.2)", + borderWidth: 2, + pointRadius: 3, + }, + ], + }, + options: { + scales: { + x: { + ticks: { color: "white" }, + grid: { color: "rgba(255,255,255,0.2)" }, + }, + y: { + ticks: { color: "white" }, + grid: { color: "rgba(255,255,255,0.2)" }, + beginAtZero: true, + }, + }, + plugins: { + legend: { labels: { color: "white" } }, + annotation: { + annotations: { + line1: { + type: "line", + yMin: 20, + yMax: 20, + borderColor: "red", + borderWidth: 2, + label: { + enabled: true, + content: "20 ms", + backgroundColor: "red", + color: "white", + }, + }, + }, + }, + }, + responsive: false, + }, + }); + + // ...existing code for additional logic... +}); diff --git a/server/server.js b/server/server.js new file mode 100644 index 0000000..42fae54 --- /dev/null +++ b/server/server.js @@ -0,0 +1,54 @@ +const express = require("express"); +const http = require("http"); +const socketIo = require("socket.io"); +const fs = require("fs"); +const path = require("path"); + +const app = express(); +const server = http.createServer(app); +const io = socketIo(server); + +app.use(express.json()); + +const chartDataPath = path.join(__dirname, "data", "chartData.json"); + +// Utility functions for chart data persistence +function loadChartData() { + try { + return JSON.parse(fs.readFileSync(chartDataPath, "utf8")); + } catch (e) { + return []; + } +} + +function saveChartData(data) { + fs.writeFileSync(chartDataPath, JSON.stringify(data, null, 2)); +} + +// Endpoint to get existing chart points +app.get("/api/chart", (req, res) => { + const data = loadChartData(); + res.json(data); +}); + +// Endpoint to add a new chart point +app.post("/api/chart", (req, res) => { + const point = req.body; // assume point has properties like 'label' and 'value' + const data = loadChartData(); + data.push(point); + saveChartData(data); + // Broadcast new point to connected clients + io.emit("chartPointAdded", point); + res.status(200).json({ success: true }); +}); + +// Socket.io connection handling +io.on("connection", (socket) => { + // Optionally send the initial chart data + socket.emit("initialChartData", loadChartData()); +}); + +const PORT = process.env.PORT || 3000; +server.listen(PORT, () => { + console.log(`Server is running on port ${PORT}`); +}); diff --git a/server/socket/socket.js b/server/socket/socket.js index 55646b6..6031008 100644 --- a/server/socket/socket.js +++ b/server/socket/socket.js @@ -21,6 +21,15 @@ function createSocketServer(httpServer) { }); }); + // For example, emit robot runtime data every 2 seconds: + setInterval(() => { + const data = { + timestamp: new Date().toLocaleTimeString(), + runtime: Math.floor(Math.random() * 1000), // replace with real runtime value + }; + io.emit("robotRuntime", data); + }, 2000); + return io; } diff --git a/server/views/robot.ejs b/server/views/robot.ejs new file mode 100644 index 0000000..425cb0b --- /dev/null +++ b/server/views/robot.ejs @@ -0,0 +1,3 @@ +
+ +
diff --git a/views/info.ejs b/views/info.ejs index af41c56..c2c0595 100644 --- a/views/info.ejs +++ b/views/info.ejs @@ -22,7 +22,7 @@ Info icons created by Freepik - Flaticon

- - + + \ No newline at end of file diff --git a/views/robot.ejs b/views/robot.ejs index 907ebc5..34e950f 100644 --- a/views/robot.ejs +++ b/views/robot.ejs @@ -54,11 +54,95 @@

Deflector : 0°C

+
+ + + +
- + + + + + +
@@ -101,6 +185,9 @@ +