diff --git a/src/js/backend/src/backend.js b/src/js/backend/src/backend.js index f7fc5e5..de5892a 100644 --- a/src/js/backend/src/backend.js +++ b/src/js/backend/src/backend.js @@ -2,6 +2,7 @@ import { SerialPort, DelimiterParser } from 'serialport'; import {Daemon, Listener} from 'node-gpsd'; import { WebSocketServer } from 'ws'; +import express from 'express'; import fs from "fs"; class AEVBackend { @@ -9,7 +10,7 @@ class AEVBackend { this.logger = logger; this.config = config; this.ports = { - MCU: { + BMS: { enabled: false, port: null, parser: null, @@ -37,61 +38,62 @@ class AEVBackend { }; this.continue = { - MCU: true, + BMS: true, GPS: true, } + this.api = null; this.wss = null; } start() { this.logger.warn("Logger initialized in backend!"); - this.initMCU(); + this.initBMS(); this.initGPS(); this.initSocket(); } - initMCU() { - // Initialize serial port for MCU - if (fs.existsSync(this.config.MCU.path)) { - this.logger.success("MCU serial port is being opened...") - this.ports.MCU.enabled = true; - this.ports.MCU.port = new SerialPort({ - path: this.config.MCU.path, - baudRate: this.config.MCU.baudRate, + initBMS() { + // Initialize serial port for BMS + if (fs.existsSync(this.config.BMS.path)) { + this.logger.success("BMS serial port is being opened...") + this.ports.BMS.enabled = true; + this.ports.BMS.port = new SerialPort({ + path: this.config.BMS.path, + baudRate: this.config.BMS.baudRate, autoOpen: false, }); - this.ports.MCU.parser = this.ports.MCU.port.pipe(new DelimiterParser({ + this.ports.BMS.parser = this.ports.BMS.port.pipe(new DelimiterParser({ delimiter: 'sh', })); // console.log(this.logger) - this.ports.MCU.port.on('open', () => { + this.ports.BMS.port.on('open', () => { // const logger = this.logger; - this.logger.success("MCU serial port opened"); - this.continue.MCU = true; + this.logger.success("BMS serial port opened"); + this.continue.BMS = true; }); - this.ports.MCU.parser.on('data', (data) => { + this.ports.BMS.parser.on('data', (data) => { this.parseBMSData(data.toString().split("\n")); }); - this.ports.MCU.port.on('error', (error) => { - // this.logger.warn(`MCU serial port error: ${error}`); + this.ports.BMS.port.on('error', (error) => { + // this.logger.warn(`BMS serial port error: ${error}`); }); - this.ports.MCU.port.on('close', () => { - this.logger.warn("MCU serial port closed"); - this.continue.MCU = false; + this.ports.BMS.port.on('close', () => { + this.logger.warn("BMS serial port closed"); + this.continue.BMS = false; }); - this.ports.MCU.port.on('drain', () => { - this.logger.success("MCU serial port drained (write failed)"); + this.ports.BMS.port.on('drain', () => { + this.logger.success("BMS serial port drained (write failed)"); }); - this.ports.MCU.port.open( (err) => { + this.ports.BMS.port.open( (err) => { if (err) console.error(err) }) } else { - this.logger.fail("MCU serial port not found at " + this.config.MCU.path); + this.logger.fail("BMS serial port not found at " + this.config.BMS.path); } } @@ -139,14 +141,21 @@ class AEVBackend { } } - stopMCU() { - this.continue.MCU = false; - this.ports.MCU.port.close(); - this.logger.warn("MCU serial port closed"); + stopGPS() { + this.continue.GPS = false; + this.ports.GPS.listener.disconnect(); + this.ports.GPS.daemon.stop(); + this.logger.warn("GPS daemon stopped"); + } + + stopBMS() { + this.continue.BMS = false; + this.ports.BMS.port.close(); + this.logger.warn("BMS serial port closed"); } initSocket() { - if (this.ports.MCU.enabled || this.ports.GPS.enabled) { + if (this.ports.BMS.enabled || this.ports.GPS.enabled) { // Initialize WebSocket server this.wss = new WebSocketServer({ port: this.config.mainPort @@ -155,15 +164,21 @@ class AEVBackend { this.wss.on('connection', (ws) => { this.logger.success("Client connected to WebSocket server"); - if (this.ports.MCU.enabled) { - if (this.ports.MCU.port.isOpen) { + if (this.ports.BMS.enabled) { + if (this.ports.BMS.port.isOpen) { // Send BMS data to client half a second under a try-catch block setInterval(() => { try { - if (this.continue.MCU) { - this.ports.MCU.port.write("sh\n"); - // ws.send(JSON.stringify(this.ports.MCU.data)); - this.logger.debug("Updated BMS Data") + if (this.continue.BMS) { + try { + this.ports.BMS.port.write("\nsh\n"); + this.ports.BMS.port.drain(); + // ws.send(JSON.stringify(this.ports.BMS.data)); + // this.logger.debug("Updated BMS Data") + this.logger.debug("Wrote all commands, waiting for data response"); + } catch (error) { + console.log(error) + } } else { this.logger.warn("Told not to continue sending BMS data through socket"); } @@ -181,7 +196,7 @@ class AEVBackend { this.logger.debug("Received message from client: " + message); if (message === "bms-data") { this.logger.success("Client requested BMS data, sending it over") - reply = JSON.stringify(this.ports.MCU.data); + reply = JSON.stringify(this.ports.BMS.data); } else if (message === "gps-data") { this.logger.success("Client requested GPS data, sending it over") reply = JSON.stringify(this.ports.GPS.data); @@ -195,8 +210,8 @@ class AEVBackend { } } else if (message === "bms-restart") { try { - this.stopMCU(); - this.initMCU(); + this.stopBMS(); + this.initBMS(); reply = "BMS restarted"; this.logger.success("BMS restarted"); } catch (error) { @@ -215,10 +230,76 @@ class AEVBackend { } } + initAPI() { + this.api = express(); + this.api.use(express.json()); + + this.api.get('/', (req, res) => { + this.logger.success("Recieved GET request on /, replied with API status"); + res.send({ + message: "API server is running", + ports: { + BMS: this.ports.BMS.enabled, + GPS: this.ports.GPS.enabled, + } + }); + }); + + this.api.get('/bms', (req, res) => { + this.logger.success("Recieved GET request on /bms, replied with BMS status: " + this.ports.BMS.enabled); + if (this.ports.GPS.enabled) { + res.send({ enabled: true }); + } else { + res.send({ enabled: false }); + } + }); + this.api.get('/bms/data', (req, res) => { + this.logger.success("Recieved GET request on /bms/data, replied with BMS data"); + res.send(JSON.stringify(this.ports.BMS.data)); + }); + this.api.get('/bms/restart', (req, res) => { + try { + this.logger.success("Recieved GET request on /bms/restart, restarting BMS"); + this.stopBMS(); + this.initBMS(); + res.send({ status: "BMS restarted" }); + } catch (error) { + res.send({ status: "Error restarting BMS: " + error }); + } + }); + + this.api.get('/gps', (req, res) => { + this.logger.success("Recieved GET request on /gps, replied with GPS status: " + this.ports.GPS.enabled); + if (this.ports.GPS.enabled) { + res.send({ enabled: true }); + } else { + res.send({ enabled: false }); + } + }); + this.api.get('/gps/data', (req, res) => { + this.logger.success("Recieved GET request on /gps/data, replied with GPS data"); + res.send(JSON.stringify(this.ports.GPS.data)); + }); + this.api.get('/gps/restart', (req, res) => { + try { + this.logger.success("Recieved GET request on /gps/restart, restarting GPS"); + this.stopGPS(); + this.initGPS(); + res.send({ status: "GPS restarted" }); + } catch (error) { + res.send({ status: "Error restarting GPS: " + error }); + } + }); + + this.api.listen(3002, () => { + this.logger.success("API server started on port", 3002); + }); + } + parseBMSData(data) { // Parse BMS data try { - // Trim the first two elements of the array and the last element of the array (useless bc first is "\r" and last is "mcu> ") + // Trim the first two elements of the array and the last element of the array (useless bc first is "\r" and last is "BMS> ") data.shift(); data.shift(); data.pop(); @@ -311,11 +392,11 @@ class AEVBackend { // this.logger.log(dataObj) - this.ports.MCU.data = dataObj; - return this.ports.MCU.data; + this.ports.BMS.data = dataObj; + return this.ports.BMS.data; } catch (error) { this.logger.warn("Error parsing BMS data: " + error); - return this.ports.MCU.data; + return this.ports.BMS.data; } } diff --git a/src/js/backend/src/config.js b/src/js/backend/src/config.js index 2315c3e..a77eef4 100644 --- a/src/js/backend/src/config.js +++ b/src/js/backend/src/config.js @@ -1,5 +1,5 @@ let config = { - "MCU": { + "BMS": { "path": "/dev/ttyUSB0", "baudRate": 115200 }, diff --git a/src/js/backend/src/tests/api-client.js b/src/js/backend/src/tests/api-client.js new file mode 100644 index 0000000..9a784cc --- /dev/null +++ b/src/js/backend/src/tests/api-client.js @@ -0,0 +1,27 @@ +// GET request to the following routes for localhost:3002: /hello, /goodbye +// Print all responses to the console +import axios from 'axios'; + +const responses = {}; + +const api = axios.create({ + baseURL: 'http://localhost:3002', +}); + +api.get('/hello') + .then((response) => { + responses['/hello'] = response; + console.log(response); + }) +api.get('/goodbye') + .then((response) => { + responses['/goodbye'] = response; + console.log(response); + }) + + +// When everything is done, print all responses +// api.all([api.get('/hello'), api.get('/goodbye')]) +// .then(axios.spread((...responses) => { +// console.log(responses); +// })); diff --git a/src/js/backend/src/tests/api-test.js b/src/js/backend/src/tests/api-test.js new file mode 100644 index 0000000..791eb51 --- /dev/null +++ b/src/js/backend/src/tests/api-test.js @@ -0,0 +1,13 @@ +import express from 'express'; +const app = express(); + +app.get('/hello', (req, res) => { + res.send('Hello World'); +}); +app.get('/goodbye', (req, res) => { + res.send('Goodbye World'); +}); + +app.listen(3003, () => { + console.log('Server running on port 3002'); +}); \ No newline at end of file diff --git a/src/js/backend/src/tests/api.js b/src/js/backend/src/tests/api.js new file mode 100644 index 0000000..e1a734e --- /dev/null +++ b/src/js/backend/src/tests/api.js @@ -0,0 +1,54 @@ +// GET request to the following routes for localhost:3002: /, /bms, /bms/data, /bms/restart, /gps, /gps/data, /gps/restart +// Print all responses to the console +import axios from 'axios'; + +const responses = {}; + +const api = axios.create({ + baseURL: 'http://localhost:3002', +}); + +api.get('/') + .then((response) => { + responses['/'] = response; + console.log(response); + }) + +api.get('/bms') + .then((response) => { + responses['/bms'] = response; + console.log(response); + }) +api.get('/bms/data') + .then((response) => { + responses['/bms/data'] = response; + console.log(response); + }) +api.get('/bms/restart') + .then((response) => { + responses['/bms/restart'] = response; + console.log(response); + }) + +api.get('/gps') + .then((response) => { + responses['/gps'] = response; + console.log(response); + }) +api.get('/gps/data') + .then((response) => { + responses['/gps/data'] = response; + console.log(response); + }) +api.get('/gps/restart') + .then((response) => { + responses['/gps/restart'] = response; + console.log(response); + }) + + +// When everything is done, print all responses +api.all([api.get('/'), api.get('/bms'), api.get('/bms/data'), api.get('/bms/restart'), api.get('/gps'), api.get('/gps/data'), api.get('/gps/restart')]) + .then(axios.spread((...responses) => { + console.log(responses); + })); \ No newline at end of file diff --git a/src/js/backend/test.js b/src/js/backend/src/tests/test.js similarity index 91% rename from src/js/backend/test.js rename to src/js/backend/src/tests/test.js index b97b3c0..6a83e77 100644 --- a/src/js/backend/test.js +++ b/src/js/backend/src/tests/test.js @@ -1,5 +1,5 @@ import WebSocket from "ws"; -import Logger from "./src/logger.cjs"; +import Logger from "../logger.cjs"; const logger = new Logger(); const ws = new WebSocket("ws://localhost:3001");