From 5fe201182ad44e60b8f859443f9e447b5ab3b79d Mon Sep 17 00:00:00 2001 From: Kwstas Drakontidis Date: Thu, 12 Jun 2025 11:15:28 +0200 Subject: [PATCH] Map socket comm --- app/routes/private_routes.js | 11 ++-- app/utils/map.util.js | 15 ++++- app/utils/socket.util.js | 65 ++++++++++++++++++++++ can.js | 104 +++++++++++++++++++++-------------- server.js | 4 +- 5 files changed, 151 insertions(+), 48 deletions(-) create mode 100644 app/utils/socket.util.js diff --git a/app/routes/private_routes.js b/app/routes/private_routes.js index 6eaa489..1822650 100644 --- a/app/routes/private_routes.js +++ b/app/routes/private_routes.js @@ -6,6 +6,7 @@ const express = require('express'); const app_conf = require("../config/app.conf.js"); const map_conf = require("../config/map.conf.js"); const messages = require("../config/messages.conf.js"); +const socket = require("../utils/socket.util.js"); const map = require("../utils/map.util.js"); @@ -29,6 +30,7 @@ router.get("/disable", async (req, res) => { logger.info(`${req.method}: "${req.url}" => ${req.get("User-Agent")}`); fs.writeFileSync(path.join(path.dirname(require.main.filename), map_conf.map_storage_path, "selected"), ""); + socket.broadcastMap(null); res.status(200).send(); }); @@ -41,6 +43,7 @@ router.get("/enable", async (req, res) => { if (requested_map && map.mapExists(requested_map)) { fs.writeFileSync(path.join(path.dirname(require.main.filename), map_conf.map_storage_path, "selected"), requested_map); + socket.broadcastMap(requested_map); res.status(200).send(); } else { @@ -62,6 +65,7 @@ router.delete("/remove", async (req, res) => { selected_map_name = fs.readFileSync(path.join(path.dirname(require.main.filename), map_conf.map_storage_path, "selected"), "utf-8"); if (selected_map_name == element) { fs.writeFileSync(path.join(path.dirname(require.main.filename), map_conf.map_storage_path, "selected"), ""); + socket.broadcastMap(null); } } res.status(200).send(); @@ -195,12 +199,7 @@ router.get("/", async (req, res) => { try { const maps = await map.getAvailableMaps(); - var selected_map_name = ""; - - // Check if a map has been selected to show it - if (fs.existsSync(path.join(path.dirname(require.main.filename), map_conf.map_storage_path, "selected"))) { - selected_map_name = fs.readFileSync(path.join(path.dirname(require.main.filename), map_conf.map_storage_path, "selected"), "utf-8"); - } + var selected_map_name = map.getSelectedMap(); // Construct the maps table var table_data = ""; diff --git a/app/utils/map.util.js b/app/utils/map.util.js index 772d2e3..bd6f723 100644 --- a/app/utils/map.util.js +++ b/app/utils/map.util.js @@ -5,6 +5,18 @@ const uuid = require("uuid"); const map_conf = require("../config/map.conf"); const logger = require("./logger.util"); +// Returns map filename or "" +function getSelectedMap() { + var selected_map_name = ""; + + // Check if a map has been selected to show it + if (fs.existsSync(path.join(path.dirname(require.main.filename), map_conf.map_storage_path, "selected"))) { + selected_map_name = fs.readFileSync(path.join(path.dirname(require.main.filename), map_conf.map_storage_path, "selected"), "utf-8"); + } + + return(selected_map_name); +} + // Creates new map async function addMap(filename, meta, throttle, motor) { var total = ""; @@ -201,5 +213,6 @@ module.exports = { parseMapJson, mapExists, updateMap, - addMap + addMap, + getSelectedMap }; \ No newline at end of file diff --git a/app/utils/socket.util.js b/app/utils/socket.util.js new file mode 100644 index 0000000..626fbe6 --- /dev/null +++ b/app/utils/socket.util.js @@ -0,0 +1,65 @@ +const net = require("net"); +const fs = require("fs"); +const path = require("path"); +const map_conf = require("../config/map.conf"); +const map = require("../utils/map.util"); +const logger = require("../utils/logger.util"); +const can_conf = require("../config/can.conf") + +const SOCKET_PATH = path.join(path.dirname(require.main.filename), map_conf.map_storage_path, can_conf.socket_filename); +var clients = []; + +if (fs.existsSync(SOCKET_PATH)) { + logger.info("Removing existing map socket..."); + fs.unlinkSync(SOCKET_PATH); +} + +function broadcastMap(filename) { + logger.info("Map updated. Broadcasting new filename to map socket..."); + + let message = JSON.stringify({ "map": filename }); + + for (c of clients) { + c.write(message); + } +} + +function closeSocket() { + for (c of clients) { + c.end(); + } + + logger.info("Closing map socket."); + server.close(); +} + +const server = net.createServer((client) => { + // send selected map + logger.info("Client connected to map socket."); + + var map_filename = map.getSelectedMap(); + if (map_filename === "") { + map_filename = null; + } + + client.write(JSON.stringify({"map": map_filename})); + + // client array + clients.push(client); + client.on("end", () => { + logger.info("Client disconnected from map socket."); + const index = clients.indexOf(client); + if (index !== -1) { + clients.splice(index, 1); + } + }); +}); + +server.listen(SOCKET_PATH, () => { + logger.info("Started map socket"); +}); + +module.exports = { + broadcastMap, + closeSocket +} \ No newline at end of file diff --git a/can.js b/can.js index 0a8a0ef..274525b 100644 --- a/can.js +++ b/can.js @@ -1,15 +1,18 @@ const can = require("socketcan"); const csv = require("fast-csv"); const fs = require("fs"); +const net = require('net'); const can_conf = require("./app/config/can.conf"); const map_conf = require("./app/config/map.conf"); const path = require("path"); +const SOCKET_PATH = path.join(__dirname, map_conf.map_storage_path, can_conf.socket_filename); + var map_rpm = []; var map_throttle = []; -var loaded_map_name = ""; +var loaded_map_name = null; var loaded_map_data = []; var throttle_output = 0; @@ -31,7 +34,7 @@ function parseMap(filename) { let map = []; return new Promise((resolve, reject) => { - csv.parseFile(path.join(__dirname, map_conf.map_storage_path, filename), { comment: "#" }) + csv.parseFile((filename), { comment: "#" }) .on('error', error => reject(error)) .on('data', row => map.push(row)) .on('end', () => { name = filename; resolve([name, map]) }); @@ -67,24 +70,58 @@ function getMapIndex(array, input) { return Math.abs(array[left] - input) < Math.abs(array[right] - input) ? left : right; } +function connect() { + console.log("Connecting..."); + if (fs.existsSync(SOCKET_PATH)) { + const client = net.createConnection(SOCKET_PATH, () => { + console.log("Connected"); + }); + + client.on("data", async (data) => { + let enabled_map_name = JSON.parse(data)["map"]; + console.log(`Received: ${enabled_map_name}`); + if (enabled_map_name === null) { + console.log("Purging memory"); + loaded_map_name = null; + loaded_map_data = []; + } else if (enabled_map_name != loaded_map_name) { + console.log("Parsing map"); + [loaded_map_name, loaded_map_data] = await parseMap(path.join(__dirname, map_conf.map_storage_path, enabled_map_name)); + } + }); + + client.on("end", () => { + console.log("Disconnected"); + setTimeout(connect, can_conf.socket_connect_wait); + }); + + client.on("error", (err) => { + console.log(`Error: ${err.message}`); + setTimeout(connect, can_conf.socket_connect_wait); + }); + + } else { + setTimeout(connect, can_conf.socket_connect_wait); + } +} + (async () => { for (let i = 0; i <= map_conf.steps; i++) { map_rpm[i] = i * (map_conf.max_rpm / map_conf.steps); map_throttle[i] = i * (map_conf.max_throttle / map_conf.steps); } - if (fs.existsSync(path.join(__dirname, map_conf.map_storage_path, "selected"))) { - const enabled_map_name = fs.readFileSync(path.join(__dirname, map_conf.map_storage_path, "selected")); - [loaded_map_name, loaded_map_data] = await parseMap(enabled_map_name); - } + connect(); })(); channel.addListener("onMessage", async function (message) { switch (message.id) { case can_conf.selector.id: if (Buffer.compare(message.data, can_conf.selector.command) == 0) { + console.log("Enable") enabled = true; } else { + console.log("Disable"); enabled = false; } @@ -92,44 +129,31 @@ channel.addListener("onMessage", async function (message) { case can_conf.apps_id: const apps_percentage = ((message.data[1] << 8) | message.data[0]) / 1000; - throttle_output = loaded_map_data[getMapIndex(map_throttle, apps_percentage)][2]; + if (loaded_map_name !== null) { + throttle_output = loaded_map_data[getMapIndex(map_throttle, apps_percentage)][2]; + } else { + throttle_output = 0; + } break; case erpm_id: - if (enabled) { - if (fs.existsSync(path.join(__dirname, map_conf.map_storage_path, "selected"))) { - const enabled_map_name = fs.readFileSync(path.join(__dirname, map_conf.map_storage_path, "selected")); - - if (enabled_map_name != "") { - if (loaded_map_name != enabled_map_name) { - [loaded_map_name, loaded_map_data] = await parseMap(enabled_map_name); - } - - const ERPM = message.data[0] << 3 * 8 | message.data[1] << 2 * 8 | message.data[2] << 8 | message.data[3]; - - let max_current = loaded_map_data[getMapIndex(map_rpm, ERPM / can_conf.motor_poles)][(throttle_output > 0) ? 0 : 1]; - let current = throttle_output * max_current * 10; - channel.send({ - id: (throttle_output > 0) ? current_id : brake_current_id, - ext: false, - data: Buffer.from([(current >> 8) & 0xFF, current & 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]) - }); - } else { - channel.send({ - id: brake_current_id, - ext: false, - data: Buffer.from([0, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]) - }); - } - } else { - channel.send({ - id: brake_current_id, - ext: false, - data: Buffer.from([0, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]) - }); - } + if (enabled && (loaded_map_name !== null)) { + const ERPM = message.data[0] << 3 * 8 | message.data[1] << 2 * 8 | message.data[2] << 8 | message.data[3]; + + let max_current = loaded_map_data[getMapIndex(map_rpm, ERPM / can_conf.motor_poles)][(throttle_output > 0) ? 0 : 1]; + let current = throttle_output * max_current * 10; + channel.send({ + id: (throttle_output > 0) ? current_id : brake_current_id, + ext: false, + data: Buffer.from([(current >> 8) & 0xFF, current & 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]) + }); + } else if (enabled && (loaded_map_data === null)) { + channel.send({ + id: brake_current_id, + ext: false, + data: Buffer.from([0, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]) + }); } - break; default: { break; diff --git a/server.js b/server.js index 5c6a958..563ab0e 100644 --- a/server.js +++ b/server.js @@ -9,6 +9,7 @@ const public_routes = require('./app/routes/public_routes'); const private_routes = require('./app/routes/private_routes'); const logger = require("./app/utils/logger.util.js"); +const map_socket = require("./app/utils/socket.util.js"); const app = express(); var shutting_down = false; @@ -77,7 +78,8 @@ process.on("SIGINT", () => { */ function shutdown() { shutting_down = true; - + map_socket.closeSocket(); + server.close(() => { logger.info("Shutting down server."); });