Compare commits

..
10 Commits
Author SHA1 Message Date
admin 7006e8ccc6 Send msg on apps & bug fix 2025-06-18 18:44:39 +02:00
admin 85845da0b8 Test file 2025-06-12 11:15:42 +02:00
admin 5fe201182a Map socket comm 2025-06-12 11:15:28 +02:00
admin de15de87a9 Update package-lock 2025-06-09 10:07:33 +02:00
admin b1257da48d Removed unnecessary modules 2025-06-09 10:03:26 +02:00
admin 9f20458b19 Added comments 2025-06-09 09:48:31 +02:00
admin f984a5c241 Bug fix 2025-06-09 08:59:44 +02:00
admin 9081853a6f General refactor 2025-06-09 08:53:29 +02:00
admin e9296b44be Code refactor 2025-06-07 10:12:47 +02:00
admin 146816184c Bug fixes 2025-06-07 09:44:26 +02:00
21 changed files with 333 additions and 492 deletions
+37 -7
View File
@@ -6,15 +6,18 @@ 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");
const router = express.Router();
// Gets an Express response object an emoji code and a string message and renders as error message
function errorPageRenderer(res, emoji, message) {
res.render("error", {
page_title: app_conf.name,
program: app_conf.program,
app_url: app_conf.app_url,
css_path: path.join(app_conf.paths.css.web, "stylesheet.css"),
js_path: path.join(app_conf.paths.js.web, "error.js"),
error_emoji: emoji,
@@ -22,14 +25,17 @@ function errorPageRenderer(res, emoji, message) {
})
}
// Disable map endpoint
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();
});
// Enable map endpoint
router.get("/enable", async (req, res) => {
logger.info(`${req.method}: "${req.url}" => ${req.get("User-Agent")}`);
@@ -37,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 {
@@ -44,12 +51,23 @@ router.get("/enable", async (req, res) => {
}
});
// Remove map endpoint
router.delete("/remove", async (req, res) => {
logger.info(`${req.method}: "${req.url}" => ${req.get("User-Agent")}`);
if (req.query["element"]) {
// Get map name
var element = req.query["element"];
if (element) {
try {
fs.unlinkSync(path.join(path.dirname(require.main.filename), map_conf.map_storage_path, req.query["element"]));
// Delete map file and check if the map was selected - to disable it
fs.unlinkSync(path.join(path.dirname(require.main.filename), map_conf.map_storage_path, element));
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");
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();
} catch (error) {
res.status(500).send();
@@ -60,6 +78,7 @@ router.delete("/remove", async (req, res) => {
}
});
// Upload map file
router.post("/upload", async (req, res) => {
logger.info(`${req.method}: "${req.url}" => ${req.get("User-Agent")}`);
@@ -79,6 +98,7 @@ router.post("/upload", async (req, res) => {
}
});
// Download map
router.get("/download", async (req, res) => {
logger.info(`${req.method}: "${req.url}" => ${req.get("User-Agent")}`);
@@ -89,9 +109,11 @@ router.get("/download", async (req, res) => {
}
});
// Add new map
router.post("/add", async (req, res) => {
logger.info(`${req.method}: "${req.url}" => ${req.get("User-Agent")}`);
// Get new map data and write it to csv
if (req.body) {
await map.addMap(`${map_conf.prefix}_${uuid.v4()}.${map_conf.extension}`, req.body["meta"], req.body["throttle"], req.body["motor"]);
@@ -101,11 +123,14 @@ router.post("/add", async (req, res) => {
}
});
// Edit map data
router.post("/edit", async (req, res) => {
logger.info(`${req.method}: "${req.url}" => ${req.get("User-Agent")}`);
// Check if a map name and data have been received
if (req.query["element"] && req.body) {
try {
// Update the map file
await map.updateMap(req.query["element"], req.body["meta"], req.body["throttle"], req.body["motor"]);
res.status(200).send();
@@ -118,12 +143,14 @@ router.post("/edit", async (req, res) => {
}
});
// Add map page
router.get("/add", async (req, res) => {
logger.info(`${req.method}: "${req.url}" => ${req.get("User-Agent")}`);
res.render("add", {
page_title: app_conf.name,
program: app_conf.program,
app_url: app_conf.app_url,
css_path: path.join(app_conf.paths.css.web, "stylesheet.css"),
js_path: path.join(app_conf.paths.js.web, "add.js"),
chartjs: app_conf.paths.chartjs.web + "/chart.umd.js",
@@ -133,6 +160,7 @@ router.get("/add", async (req, res) => {
});
});
// Edit map page
router.get("/edit", async (req, res) => {
logger.info(`${req.method}: "${req.url}" => ${req.get("User-Agent")}`);
@@ -143,6 +171,7 @@ router.get("/edit", async (req, res) => {
res.render("edit", {
page_title: app_conf.name,
program: app_conf.program,
app_url: app_conf.app_url,
css_path: path.join(app_conf.paths.css.web, "stylesheet.css"),
js_path: path.join(app_conf.paths.js.web, "edit.js"),
chartjs: app_conf.paths.chartjs.web + "/chart.umd.js",
@@ -164,17 +193,15 @@ router.get("/edit", async (req, res) => {
}
});
// Main page
router.get("/", async (req, res) => {
logger.info(`${req.method}: "${req.url}" => ${req.get("User-Agent")}`);
try {
const maps = await map.getAvailableMaps();
var selected_map_name = "";
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 = "";
if (Object.keys(maps).length) {
for (m of Object.keys(maps)) {
@@ -203,6 +230,7 @@ router.get("/", async (req, res) => {
res.render("index", {
page_title: app_conf.name,
program: app_conf.program,
app_url: app_conf.app_url,
css_path: path.join(app_conf.paths.css.web, "stylesheet.css"),
js_path: path.join(app_conf.paths.js.web, "index.js"),
map_name_msg: (selected_map_name != "" ? "Using: " + selected_map_name : ""),
@@ -218,10 +246,12 @@ router.get("/", async (req, res) => {
}
});
// About page
router.get("/about", (req, res) => {
res.render("about", {
page_title: app_conf.name,
program: app_conf.program,
app_url: app_conf.app_url,
css_path: path.join(app_conf.paths.css.web, "stylesheet.css"),
version: app_conf.version
});
+3 -10
View File
@@ -9,18 +9,11 @@ router.use(app_conf.paths.js.web, express.static(path.join(path.dirname(require.
router.use(app_conf.paths.chartjs.web, express.static(path.join(path.dirname(require.main.filename), app_conf.paths.chartjs.src)));
/*
* Main endpoint - might implement authentication step here...
* Main endpoint - authentication happens here
* Rewritting the whole url to fix logout bug
*/
router.get("/", (req, res) => {
res.render("auth", {
page_title: app_conf.name,
program: app_conf.program,
css_path: path.join(app_conf.paths.css.web, "stylesheet.css"),
protocol: app_conf.protocol,
host: app_conf.host,
port: app_conf.port
});
res.redirect(`${app_conf.protocol}://${app_conf.host}:${app_conf.port}${app_conf.app_url}`);
});
module.exports = router;
+27 -3
View File
@@ -5,10 +5,24 @@ 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 = "";
var map = [[[]]];
// Get the new map metadata
if (meta) {
if ("name" in meta) {
total += `#name:${meta["name"]}\n`;
@@ -21,7 +35,9 @@ async function addMap(filename, meta, throttle, motor) {
}
}
// Check if the required data has been received
if (throttle && motor && ("0" in motor) && ("1" in motor)) {
// Creating csv and storing it
const throttle_values = Object.values(throttle);
const current_values = Object.values(motor["0"]);
const brake_values = Object.values(motor["1"]);
@@ -39,10 +55,13 @@ async function addMap(filename, meta, throttle, motor) {
}
}
// Update current map file
async function updateMap(filename, meta, throttle, motor) {
// Get merged map comments and data
var total = await generateComments(filename, meta);
const map = await generateData(filename, throttle, motor);
// Create and store file
try {
await csv.writeToString(map).then(data => total += data);
fs.writeFileSync(path.join(path.dirname(require.main.filename), map_conf.map_storage_path, filename), total);
@@ -55,6 +74,7 @@ async function updateMap(filename, meta, throttle, motor) {
}
}
// Merges data from a stored map and a new map
async function generateData(filename, throttle, motor) {
const map = await parseMap(filename, 0);
@@ -80,6 +100,7 @@ async function generateData(filename, throttle, motor) {
return map;
}
// Merges comments from stored map and new map
async function generateComments(filename, meta) {
const comments = await parseMapJson(filename, 1);
var comment_string = "";
@@ -105,6 +126,7 @@ async function generateComments(filename, meta) {
return comment_string;
}
// Check if a map exists
function mapExists(filename) {
if (path.extname(filename) != ".csv") {
return false;
@@ -112,7 +134,7 @@ function mapExists(filename) {
return fs.existsSync(path.join(path.dirname(require.main.filename), map_conf.map_storage_path, filename));
}
// mode: comments 1 or data 0
// Parse a map and returns it as array. On 1 returns comments on 0 returns data
function parseMap(filename, mode) {
var result = [];
@@ -131,6 +153,7 @@ function parseMap(filename, mode) {
});
}
// Parse map data as JSON
async function parseMapJson(filename, mode) {
const map = await parseMap(filename, mode);
var json_map = {};
@@ -150,8 +173,8 @@ async function parseMapJson(filename, mode) {
return json_map
}
// Get available map names in storage directory
async function getAvailableMaps() {
// path in app_conf
var available_maps = {};
try {
@@ -190,5 +213,6 @@ module.exports = {
parseMapJson,
mapExists,
updateMap,
addMap
addMap,
getSelectedMap
};
+65
View File
@@ -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
}
+1 -1
View File
@@ -30,7 +30,7 @@
style="font-size: 150%;">GitHub</a>
</center>
<br>
<a href="/app"><span>&#129152; </span>Return</a>
<a href="<%= app_url %>"><span>&#129152; </span>Return</a>
</div>
</body>
+3 -3
View File
@@ -16,7 +16,7 @@
<script src="<%= js_path %>" defer></script>
<script src="<%= chartjs %>"></script>
<script defer>
var steps = '<%= steps %>', max_rpm = '<%= max_rpm %>', max_throttle = '<%= max_throttle %>';
var steps = '<%= steps %>', max_rpm = '<%= max_rpm %>', max_throttle = '<%= max_throttle %>', app_url = '<%= app_url %>';
</script>
</head>
@@ -55,12 +55,12 @@
<table id="motor_table" style="text-align: center;"></table>
</div>
<div style="display: flex; flex-direction: row; justify-content: space-between;">
<a href="/app"><span>&#129152; </span>Return</a>
<a href="<%= app_url %>"><span>&#129152; </span>Return</a>
<label id="commit" style="cursor: pointer;"><span>&#9989; </span>Commit</label>
</div>
</div>
<div class="about_div">
<center><a href="/app/about">About</label></center>
<center><a href="<%= app_url %>/about">About</label></center>
</div>
</body>
-33
View File
@@ -1,33 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>
<%= page_title %>
</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link
href="https://fonts.googleapis.com/css2?family=Ubuntu:ital,wght@0,300;0,400;0,500;0,700;1,300;1,400;1,500;1,700&display=swap"
rel="stylesheet">
<link rel="stylesheet" href="<%= css_path %>">
</head>
<body>
<div class="control_div">
<label
style='font-family: "Ubuntu", sans-serif; font-weight: 500; font-style: normal; font-size: 200%;'>Centaurus
Racing Team</label>
</div>
<div class="main_div"
style="display: flex; flex-direction: column; justify-content: space-between; font-size: 150%; margin-top: 15px; margin-bottom: 15px;">
<div style="display: flex; flex-direction: row; justify-content: space-evenly;">
<label><%= page_title %> <%= program %></label>
<a href="<%= protocol %>://<%= host %>:<%= port %>/app">Enter</a>
</div>
</div>
</body>
</html>
+3 -3
View File
@@ -16,7 +16,7 @@
<script type="module" src="<%= js_path %>" defer></script>
<script src="<%= chartjs %>"></script>
<script defer>
var map = '<%- map %>', map_meta = '<%- map_meta %>', steps = '<%= steps %>', max_rpm = '<%= max_rpm %>', max_throttle = '<%= max_throttle %>';
var map = '<%- map %>', map_meta = '<%- map_meta %>', steps = '<%= steps %>', max_rpm = '<%= max_rpm %>', max_throttle = '<%= max_throttle %>', app_url = '<%= app_url %>';
</script>
</head>
@@ -60,12 +60,12 @@
<table id="motor_table" style="text-align: center;"></table>
</div>
<div style="display: flex; flex-direction: row; justify-content: space-between;">
<a href="/app"><span>&#129152; </span>Return</a>
<a href="<%= app_url %>"><span>&#129152; </span>Return</a>
<label id="commit" style="cursor: pointer;"><span>&#9989; </span>Commit</label>
</div>
</div>
<div class="about_div">
<center><a href="/app/about">About</label></center>
<center><a href="<%= app_url %>/about">About</label></center>
</div>
</body>
+4 -1
View File
@@ -14,6 +14,9 @@
rel="stylesheet">
<link rel="stylesheet" href="<%= css_path %>">
<script src="<%= js_path %>" defer></script>
<script defer>
var app_url = '<%= app_url %>';
</script>
</head>
<body>
@@ -38,7 +41,7 @@
<%= error_message %>
</label>
<br>
<a href="/app"><span>&#129152; </span>Return</a>
<a href="<%= app_url %>"><span>&#129152; </span>Return</a>
</div>
</body>
+4 -1
View File
@@ -14,6 +14,9 @@
rel="stylesheet">
<link rel="stylesheet" href="<%= css_path %>">
<script src="<%= js_path %>" defer></script>
<script defer>
var app_url = '<%= app_url %>';
</script>
</head>
<body>
@@ -47,7 +50,7 @@
</table>
</div>
<div class="about_div">
<center><a href="/app/about">About</label></center>
<center><a href="<%= app_url %>/about">About</label></center>
</div>
</body>
+73 -53
View File
@@ -1,22 +1,28 @@
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;
var ERPM = 0;
var enabled = false;
var channel = can.createRawChannel(can_conf.network_id, true);
// Some test messages...
// 404#0000245E00710186
// 019#02
// 050#9702
@@ -26,13 +32,14 @@ const brake_current_id = (0x02 << 5) | can_conf.controller.id;
const erpm_id = (0x20 << 5) | can_conf.controller.id;
function parseMap(filename) {
loaded_map_data = [];
let name = "";
let map = [];
return new Promise((resolve, reject) => {
csv.parseFile(`app/storage/${filename}`, { comment: "#" })
csv.parseFile((filename), { comment: "#" })
.on('error', error => reject(error))
.on('data', row => loaded_map_data.push(row))
.on('end', () => { loaded_map_name = filename; resolve() });
.on('data', row => map.push(row))
.on('end', () => { name = filename; resolve([name, map]) });
});
}
@@ -65,85 +72,98 @@ 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(map_conf.selected_map_path)) {
const enabled_map_name = fs.readFileSync(map_conf.selected_map_path);
if (enabled_map_name != "") {
if (loaded_map_name != enabled_map_name) {
await parseMap(enabled_map_name);
}
}
}
connect();
})();
channel.addListener("onMessage", async function (message) {
switch (message.id) {
case can_conf.selector.id:
console.log("==INVOKE SELECTOR==");
if (Buffer.compare(message.data, can_conf.selector.command) == 0) {
console.log("enable");
console.log("Enable")
enabled = true;
} else {
console.log("disable");
console.log("Disable");
enabled = false;
}
break;
case can_conf.apps_id:
console.log("==INVOKE APPS==");
const apps_percentage = ((message.data[1] << 8) | message.data[0]) / 1000;
var max_current = 0;
throttle_output = 0;
if (loaded_map_name !== null) {
throttle_output = loaded_map_data[getMapIndex(map_throttle, apps_percentage)][2];
console.log(throttle_output);
break;
case erpm_id:
console.log("==INVOKE ERPM==");
if (enabled) {
console.log("enabled");
if (fs.existsSync(map_conf.selected_map_path)) {
const enabled_map_name = fs.readFileSync(map_conf.selected_map_path);
if (enabled_map_name != "") {
console.log("map selected");
if (loaded_map_name != enabled_map_name) {
await parseMap(enabled_map_name);
max_current = loaded_map_data[getMapIndex(map_rpm, ERPM / can_conf.motor_poles)][(throttle_output > 0) ? 0 : 1];
}
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;
console.log(max_current);
console.log(current);
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 {
console.log("map not selected");
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])
});
}
}
break;
case erpm_id:
if ((enabled == true) && (loaded_map_name !== null)) {
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 == true) && (loaded_map_name === null)) {
channel.send({
id: brake_current_id,
ext: false,
data: Buffer.from([0, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF])
});
}
break;
default: {
break;
+1 -345
View File
@@ -4,14 +4,13 @@
"requires": true,
"packages": {
"": {
"name": "throttle_motor_mapping",
"dependencies": {
"better-sqlite3": "^11.10.0",
"chart.js": "^4.4.9",
"ejs": "^3.1.10",
"express": "^5.1.0",
"express-basic-auth": "^1.2.1",
"express-fileupload": "^1.5.1",
"expressjs": "^1.0.1",
"fast-csv": "^5.0.2",
"socketcan": "^4.0.6",
"uuid": "^11.1.0",
@@ -195,25 +194,6 @@
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
"node_modules/base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
},
"node_modules/basic-auth": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
@@ -230,35 +210,6 @@
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"node_modules/better-sqlite3": {
"version": "11.10.0",
"resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-11.10.0.tgz",
"integrity": "sha512-EwhOpyXiOEL/lKzHz9AW1msWFNzGc/z+LzeB3/jnFJpxu+th2yqvzsSWas1v9jgs9+xiXJcD5A8CJxAG2TaghQ==",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"bindings": "^1.5.0",
"prebuild-install": "^7.1.1"
}
},
"node_modules/bindings": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
"dependencies": {
"file-uri-to-path": "1.0.0"
}
},
"node_modules/bl": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
"integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
"dependencies": {
"buffer": "^5.5.0",
"inherits": "^2.0.4",
"readable-stream": "^3.4.0"
}
},
"node_modules/body-parser": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz",
@@ -286,29 +237,6 @@
"balanced-match": "^1.0.0"
}
},
"node_modules/buffer": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"dependencies": {
"base64-js": "^1.3.1",
"ieee754": "^1.1.13"
}
},
"node_modules/busboy": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
@@ -572,28 +500,6 @@
}
}
},
"node_modules/decompress-response": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
"integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
"dependencies": {
"mimic-response": "^3.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/deep-extend": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
"engines": {
"node": ">=4.0.0"
}
},
"node_modules/depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
@@ -602,14 +508,6 @@
"node": ">= 0.8"
}
},
"node_modules/detect-libc": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz",
"integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==",
"engines": {
"node": ">=8"
}
},
"node_modules/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
@@ -675,14 +573,6 @@
"iconv-lite": "^0.6.2"
}
},
"node_modules/end-of-stream": {
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
"integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
"dependencies": {
"once": "^1.4.0"
}
},
"node_modules/env-paths": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
@@ -736,14 +626,6 @@
"node": ">= 0.6"
}
},
"node_modules/expand-template": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
"integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==",
"engines": {
"node": ">=6"
}
},
"node_modules/exponential-backoff": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.2.tgz",
@@ -809,12 +691,6 @@
"node": ">=12.0.0"
}
},
"node_modules/expressjs": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/expressjs/-/expressjs-1.0.1.tgz",
"integrity": "sha512-eFnQ5bMJxTZ29XwRJPV8ee/OURBBMS6Fm+b5rvMMEyz6u2IxPEh2SRzMZt9WvgnV+SMLmnzkALE1DnGG1HxJCw==",
"deprecated": "This is a typosquat on the popular Express package. This is not maintained nor is the original Express package."
},
"node_modules/fast-csv": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/fast-csv/-/fast-csv-5.0.2.tgz",
@@ -846,11 +722,6 @@
"resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz",
"integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw=="
},
"node_modules/file-uri-to-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="
},
"node_modules/filelist": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz",
@@ -924,11 +795,6 @@
"node": ">= 0.8"
}
},
"node_modules/fs-constants": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
},
"node_modules/fs-minipass": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz",
@@ -983,11 +849,6 @@
"node": ">= 0.4"
}
},
"node_modules/github-from-package": {
"version": "0.0.0",
"resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
"integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw=="
},
"node_modules/glob": {
"version": "10.4.5",
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
@@ -1109,25 +970,6 @@
"node": ">=0.10.0"
}
},
"node_modules/ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
},
"node_modules/imurmurhash": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
@@ -1141,11 +983,6 @@
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"node_modules/ini": {
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="
},
"node_modules/ip-address": {
"version": "9.0.5",
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz",
@@ -1404,17 +1241,6 @@
"node": ">= 0.6"
}
},
"node_modules/mimic-response": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
"integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/minimatch": {
"version": "9.0.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
@@ -1429,14 +1255,6 @@
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/minimist": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/minipass": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
@@ -1578,11 +1396,6 @@
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/mkdirp-classic": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
"integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="
},
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@@ -1593,11 +1406,6 @@
"resolved": "https://registry.npmjs.org/nan/-/nan-2.22.2.tgz",
"integrity": "sha512-DANghxFkS1plDdRsX0X9pm0Z6SJNN6gBdtXfanwoZ8hooC5gosGFSBGRYHUVPz1asKA/kMRqDRdHrluZ61SpBQ=="
},
"node_modules/napi-build-utils": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz",
"integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA=="
},
"node_modules/negotiator": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
@@ -1606,17 +1414,6 @@
"node": ">= 0.6"
}
},
"node_modules/node-abi": {
"version": "3.75.0",
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.75.0.tgz",
"integrity": "sha512-OhYaY5sDsIka7H7AtijtI9jwGYLyl29eQn/W623DiN/MIv5sUqc4g7BIDThX+gb7di9f6xK02nkp8sdfFWZLTg==",
"dependencies": {
"semver": "^7.3.5"
},
"engines": {
"node": ">=10"
}
},
"node_modules/node-gyp": {
"version": "11.2.0",
"resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-11.2.0.tgz",
@@ -1758,31 +1555,6 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/prebuild-install": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz",
"integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==",
"dependencies": {
"detect-libc": "^2.0.0",
"expand-template": "^2.0.3",
"github-from-package": "0.0.0",
"minimist": "^1.2.3",
"mkdirp-classic": "^0.5.3",
"napi-build-utils": "^2.0.0",
"node-abi": "^3.3.0",
"pump": "^3.0.0",
"rc": "^1.2.7",
"simple-get": "^4.0.0",
"tar-fs": "^2.0.0",
"tunnel-agent": "^0.6.0"
},
"bin": {
"prebuild-install": "bin.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/proc-log": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/proc-log/-/proc-log-5.0.0.tgz",
@@ -1815,15 +1587,6 @@
"node": ">= 0.10"
}
},
"node_modules/pump": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz",
"integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==",
"dependencies": {
"end-of-stream": "^1.1.0",
"once": "^1.3.1"
}
},
"node_modules/qs": {
"version": "6.14.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
@@ -1860,20 +1623,6 @@
"node": ">= 0.8"
}
},
"node_modules/rc": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
"integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
"dependencies": {
"deep-extend": "^0.6.0",
"ini": "~1.3.0",
"minimist": "^1.2.0",
"strip-json-comments": "~2.0.1"
},
"bin": {
"rc": "cli.js"
}
},
"node_modules/readable-stream": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
@@ -2096,49 +1845,6 @@
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/simple-concat": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
},
"node_modules/simple-get": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz",
"integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"dependencies": {
"decompress-response": "^6.0.0",
"once": "^1.3.1",
"simple-concat": "^1.0.0"
}
},
"node_modules/simple-swizzle": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
@@ -2332,14 +2038,6 @@
"node": ">=8"
}
},
"node_modules/strip-json-comments": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
"integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
@@ -2368,37 +2066,6 @@
"node": ">=18"
}
},
"node_modules/tar-fs": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.2.tgz",
"integrity": "sha512-EsaAXwxmx8UB7FRKqeozqEPop69DXcmYwTQwXvyAPF352HJsPdkVhvTaDPYqfNgruveJIJy3TA2l+2zj8LJIJA==",
"dependencies": {
"chownr": "^1.1.1",
"mkdirp-classic": "^0.5.2",
"pump": "^3.0.0",
"tar-stream": "^2.1.4"
}
},
"node_modules/tar-fs/node_modules/chownr": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
},
"node_modules/tar-stream": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
"integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
"dependencies": {
"bl": "^4.0.3",
"end-of-stream": "^1.4.1",
"fs-constants": "^1.0.0",
"inherits": "^2.0.3",
"readable-stream": "^3.1.1"
},
"engines": {
"node": ">=6"
}
},
"node_modules/text-hex": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz",
@@ -2435,17 +2102,6 @@
"node": ">= 14.0.0"
}
},
"node_modules/tunnel-agent": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
"integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
"dependencies": {
"safe-buffer": "^5.0.1"
},
"engines": {
"node": "*"
}
},
"node_modules/type-is": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz",
-2
View File
@@ -1,12 +1,10 @@
{
"dependencies": {
"better-sqlite3": "^11.10.0",
"chart.js": "^4.4.9",
"ejs": "^3.1.10",
"express": "^5.1.0",
"express-basic-auth": "^1.2.1",
"express-fileupload": "^1.5.1",
"expressjs": "^1.0.1",
"fast-csv": "^5.0.2",
"socketcan": "^4.0.6",
"uuid": "^11.1.0",
+2 -1
View File
@@ -4,7 +4,8 @@ module.exports = {
program: "Throttle & Motor Mapping",
host: "localhost",
protocol: "http",
port: 8080,
port: 8081,
app_url: "/app",
username: "admin",
password: "password",
logger: {
+2 -1
View File
@@ -1,7 +1,8 @@
module.exports = {
prefix: "map",
extension: "csv",
steps: 10,
max_rpm: 6500,
max_throttle: 1,
selected_map_path: "app/storage/selected"
map_storage_path: "/app/storage"
}
+5 -3
View File
@@ -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;
@@ -17,6 +18,7 @@ var shutting_down = false;
* Static middleware - serve css and js files to public
*/
app.use(express.json());
// Set temp directory and upload size limit
app.use(fileUpload({
limits: { fileSize: 102400 },
useTempFiles: true,
@@ -25,7 +27,7 @@ app.use(fileUpload({
app.use("/", public_routes);
app.use("/app", basicAuth({
app.use(`${app_conf.app_url}`, basicAuth({
users: {
[app_conf.username]: app_conf.password
},
@@ -39,8 +41,7 @@ app.use("/app", basicAuth({
app.set("view engine", "ejs");
app.set("views", path.join(__dirname, app_conf.views_directory));
const server = app.listen(app_conf.port, (error) => {
logger.info("Starting Database Editor.");
logger.info(`Starting ${app_conf.name} ${app_conf.program}.`);
fs.rm(path.join(__dirname, app_conf.temp_directory, "/."), { recursive: true }, (error) => {
if (error == null) {
@@ -77,6 +78,7 @@ process.on("SIGINT", () => {
*/
function shutdown() {
shutting_down = true;
map_socket.closeSocket();
server.close(() => {
logger.info("Shutting down server.");
+16 -3
View File
@@ -25,6 +25,7 @@ function setEventListeners(element, cb) {
};
}
// Creates a request body with all data to add new map
setEventListeners(commit, async () => {
if (changes) {
let req_body = {};
@@ -33,33 +34,40 @@ setEventListeners(commit, async () => {
req_body["throttle"] = {};
req_body["motor"] = { 0: {}, 1: {} };
// blue color means the value has been changed
if (map_name.style.backgroundColor == "blue") {
// Write the map name metadata
req_body["meta"]["name"] = map_name.value;
}
if (map_id.style.backgroundColor == "blue") {
// Write the map id metadata
req_body["meta"]["id"] = map_id.value;
}
if (map_description.style.backgroundColor == "blue") {
// Write the map description metadata
req_body["meta"]["description"] = map_description.value;
}
// Write the throttle output data for each step
for (let i = 1; i < throttle_table.rows[1].cells.length; i++) {
let e = throttle_table.rows[1].cells[i].firstChild;
req_body["throttle"][i - 1] = (e.value ? e.value : e.placeholder);
}
// Write the motor current data for each step
for (let i = 1; i < motor_table.rows[1].cells.length; i++) {
let e = motor_table.rows[1].cells[i].firstChild;
req_body["motor"][0][i - 1] = (e.value ? e.value : e.placeholder);
}
// Write the motor brake current data for each step
for (let i = 1; i < motor_table.rows[2].cells.length; i++) {
let e = motor_table.rows[2].cells[i].firstChild;
req_body["motor"][1][i - 1] = (e.value ? e.value : e.placeholder);
}
try {
const response = await fetch("/app/add", {
const response = await fetch(`${app_url}/add`, {
method: "POST",
body: JSON.stringify(req_body),
headers: {
@@ -69,17 +77,17 @@ setEventListeners(commit, async () => {
if (!response.ok) {
throw new Error(`Response status: ${response.status}`);
} else {
window.location.href = "/app";
window.location.href = app_url;
}
} catch (error) {
alert(`Add unsuccessful. ${error.message}`);
console.error(error.message);
}
} else {
alert("Nothing to commit.");
}
});
// Handles the throttle chart and table
(function () {
let data = [];
@@ -90,6 +98,7 @@ setEventListeners(commit, async () => {
throttle_output_row.appendChild(Object.assign(document.createElement("th"), { textContent: "Throttle output" }));
for (let i = 0; i <= steps; i++) {
// Initialize throttle table
let c_percentage = i * ((max_throttle * 100) / steps);
let c_output = 0;
@@ -107,6 +116,7 @@ setEventListeners(commit, async () => {
placeholder: c_output
})
// onchange event, tracks number of changes, checks the value and changes color to blue
input.onchange = async function () {
if (this.value) {
let value = parseFloat(this.value);
@@ -136,6 +146,7 @@ setEventListeners(commit, async () => {
this.style.backgroundColor = "";
changes--;
// Update chart
throttle_chart.data.datasets[0].data[this.parentElement.cellIndex - 1] = this.placeholder;
throttle_chart.update();
}
@@ -149,6 +160,7 @@ setEventListeners(commit, async () => {
throttle_table.appendChild(throttle_percentage_row);
throttle_table.appendChild(throttle_output_row);
// Chart class object
const throttle_chart = new Chart(
throttle_plot,
{
@@ -170,6 +182,7 @@ setEventListeners(commit, async () => {
);
})();
// Same as throttle but for motor current and motor brake current
(function () {
let data = [];
+8 -9
View File
@@ -1,3 +1,4 @@
// Same as map add.js with minor changes
const commit = document.getElementById("commit");
const enable = document.getElementById("enable");
const download = document.getElementById("download");
@@ -32,13 +33,13 @@ setEventListeners(enable, async () => {
const map_filename = (new URLSearchParams(window.location.search)).get("element");
try {
const response = await fetch(`/app/enable?element=${map_filename}`, {
const response = await fetch(`${app_url}/enable?element=${map_filename}`, {
method: "GET"
});
if (!response.ok) {
throw new Error(`Response status: ${response.status}`);
} else {
window.location.href = "/app";
window.location.href = app_url;
}
} catch (error) {
alert(`Could not enable map. ${error.message}`);
@@ -49,17 +50,16 @@ setEventListeners(remove, async () => {
const map_filename = (new URLSearchParams(window.location.search)).get("element");
try {
const response = await fetch(`/app/remove?element=${map_filename}`, {
const response = await fetch(`${app_url}/remove?element=${map_filename}`, {
method: "DELETE"
});
if (!response.ok) {
throw new Error(`Response status: ${response.status}`);
} else {
window.location.href = "/app";
window.location.href = app_url;
}
} catch (error) {
alert(`Remove unsuccessful. ${error.message}`);
console.error(error.message);
}
});
@@ -101,7 +101,7 @@ setEventListeners(commit, async () => {
}
try {
const response = await fetch(`/app/edit?element=${map_filename}`, {
const response = await fetch(`${app_url}/edit?element=${map_filename}`, {
method: "POST",
body: JSON.stringify(req_body),
headers: {
@@ -111,11 +111,10 @@ setEventListeners(commit, async () => {
if (!response.ok) {
throw new Error(`Response status: ${response.status}`);
} else {
window.location.href = "/app";
window.location.href = app_url;
}
} catch (error) {
alert(`Edit unsuccessful. ${error.message}`);
console.error(error.message);
}
} else {
alert("Nothing to commit.");
@@ -125,7 +124,7 @@ setEventListeners(commit, async () => {
setEventListeners(download, () => {
const map_filename = (new URLSearchParams(window.location.search)).get("element");
window.location.href = `/app/download?element=${map_filename}`;
window.location.href = `${app_url}/download?element=${map_filename}`;
});
var parsed_map = JSON.parse(map);
+2 -2
View File
@@ -18,7 +18,7 @@ function setEventListeners(element, cb) {
}
setEventListeners(add, () => {
window.location.href = "/app/add";
window.location.href = `${app_url}/add`;
});
setEventListeners(upload, async () => {
@@ -27,7 +27,7 @@ setEventListeners(upload, async () => {
const formData = new FormData();
formData.append('file', file);
fetch('/app/upload', {
fetch(`${app_url}/upload`, {
method: 'POST',
body: formData
}).then((response) => {
+5 -5
View File
@@ -22,13 +22,13 @@ function setEventListeners(element, cb) {
setEventListeners(disable, async () => {
try {
const response = await fetch(`/app/disable`, {
const response = await fetch(`${app_url}/disable`, {
method: "GET"
});
if (!response.ok) {
throw new Error(`Response status: ${response.status}`);
} else {
window.location.href = "/app";
window.location.href = app_url;
}
} catch (error) {
alert(`Could not disable map. ${error.message}`);
@@ -41,7 +41,7 @@ setEventListeners(upload, async () => {
const formData = new FormData();
formData.append('file', file);
fetch('/app/upload', {
fetch(`${app_url}/upload`, {
method: 'POST',
body: formData
}).then((response) => {
@@ -58,7 +58,7 @@ setEventListeners(upload, async () => {
});
setEventListeners(add, () => {
window.location.href = "/app/add";
window.location.href = `${app_url}/add`;
});
setEventListeners(logout, () => {
@@ -69,6 +69,6 @@ const map_rows = map_table.rows;
for (let i = 1; i < map_rows.length; i++) {
setEventListeners(map_rows[i], () => {
window.location.href = `/app/edit?element=${map_rows[i].cells[0].innerText}`;
window.location.href = `${app_url}/edit?element=${map_rows[i].cells[0].innerText}`;
});
}
+66
View File
@@ -0,0 +1,66 @@
const can = require("socketcan");
var channel = can.createRawChannel("vcan0", true);
const INVR_ID = 0x04;
const ERPM_ID = (0x20 << 5) | INVR_ID;
const APPS_ID = 0x50;
const SELC_ID = 0x19;
const MXCU_ID = (0x01 << 5) | INVR_ID;
const MXBC_ID = (0x02 << 5) | INVR_ID;
var APPS = 0;
var ERPM = 0;
// SELECT
(function sendSELC() {
channel.send({
id: SELC_ID,
ext: false,
data: Buffer.from([0x02]) // ENABLE CONTROL BY TELEMETRY
});
})();
// APPS
function sendAPPS() {
APPS = Math.floor(Math.random() * 1000);
console.log(`APPS: ${APPS / 10}%`);
channel.send({
id: APPS_ID,
ext: false,
data: Buffer.from([APPS & 0xFF, APPS >> 8]) // SEND APPS PERCENTAGE
});
setTimeout(sendAPPS, Math.floor(Math.random() * 5000));
}
// ERPM
(function sendERPM() {
ERPM = Math.floor(Math.random() * 65000);
console.log(`ERPM: ${ERPM}`);
channel.send({
id: ERPM_ID,
ext: false,
data: Buffer.from([(ERPM >> 8 * 3) & 0xFF, (ERPM >> 8 * 2) & 0xFF, (ERPM >> 8) & 0xFF, ERPM & 0xFF, 0x00, 0x71, 0x01, 0x86]) // SEND ERPM
});
setTimeout(sendERPM, Math.floor(Math.random() * 10000));
})();
sendAPPS();
channel.addListener("onMessage", async function (message) {
switch (message.id) {
case MXCU_ID:
console.log(`CURRENT: ${message.data[0] << 8 | message.data[1]}`);
break;
case MXBC_ID:
console.log(`BRAKE: ${message.data[0] << 8 | message.data[1]}`);
break;
default:
break;
}
});
channel.start();