Initial commit
This commit is contained in:
@@ -0,0 +1,204 @@
|
||||
const logger = require("../utils/logger.util.js");
|
||||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
const uuid = require("uuid");
|
||||
const app_conf = require("../config/app.conf.js");
|
||||
const map_conf = require("../config/map.conf.js");
|
||||
const messages = require("../config/messages.conf.js");
|
||||
|
||||
const map = require("../utils/map.util.js");
|
||||
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
|
||||
function errorPageRenderer(res, emoji, message) {
|
||||
res.render("error", {
|
||||
page_title: app_conf.name,
|
||||
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,
|
||||
error_message: message
|
||||
})
|
||||
}
|
||||
|
||||
router.delete("/remove", async (req, res) => {
|
||||
logger.info(`${req.method}: "${req.url}" => ${req.get("User-Agent")}`);
|
||||
|
||||
if (req.query["element"]) {
|
||||
try {
|
||||
fs.unlinkSync(`app/storage/${req.query["element"]}`);
|
||||
res.status(200).send();
|
||||
} catch (error) {
|
||||
res.status(500).send();
|
||||
logger.error(error.message);
|
||||
}
|
||||
} else {
|
||||
res.status(400).send();
|
||||
}
|
||||
});
|
||||
|
||||
router.post("/upload", async (req, res) => {
|
||||
logger.info(`${req.method}: "${req.url}" => ${req.get("User-Agent")}`);
|
||||
|
||||
if (!req.files || !req.files.file) {
|
||||
res.status(422).send();
|
||||
} else {
|
||||
const uploaded_file = req.files.file;
|
||||
|
||||
let map_name = path.parse(uploaded_file["name"])["name"];
|
||||
if (map.mapExists(uploaded_file["name"])) {
|
||||
map_name += `_${uuid.v4()}`;
|
||||
}
|
||||
|
||||
fs.copyFile(uploaded_file.tempFilePath, `app/storage/${map_name}.csv`, (error) => {
|
||||
if (error) {
|
||||
logger.error(error);
|
||||
res.status(500).send();
|
||||
} else {
|
||||
res.status(201).send();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
router.get("/download", async (req, res) => {
|
||||
logger.info(`${req.method}: "${req.url}" => ${req.get("User-Agent")}`);
|
||||
|
||||
if (req.query["element"]) {
|
||||
res.download(`app/storage/${req.query["element"]}`);
|
||||
} else {
|
||||
res.status(400).send();
|
||||
}
|
||||
});
|
||||
|
||||
router.post("/add", async (req, res) => {
|
||||
logger.info(`${req.method}: "${req.url}" => ${req.get("User-Agent")}`);
|
||||
|
||||
if (req.body) {
|
||||
await map.addMap(`map_${uuid.v4()}.csv`, req.body["meta"], req.body["throttle"], req.body["motor"]);
|
||||
|
||||
res.status(200).send();
|
||||
} else {
|
||||
res.status(400).send();
|
||||
}
|
||||
});
|
||||
|
||||
router.post("/edit", async (req, res) => {
|
||||
logger.info(`${req.method}: "${req.url}" => ${req.get("User-Agent")}`);
|
||||
|
||||
if (req.query["element"] && req.body) {
|
||||
try {
|
||||
await map.updateMap(req.query["element"], req.body["meta"], req.body["throttle"], req.body["motor"]);
|
||||
|
||||
res.status(200).send();
|
||||
} catch (error) {
|
||||
res.status(500).send();
|
||||
logger.error(error.message);
|
||||
}
|
||||
} else {
|
||||
res.status(400).send();
|
||||
}
|
||||
});
|
||||
|
||||
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,
|
||||
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",
|
||||
steps: map_conf.steps,
|
||||
max_rpm: map_conf.max_rpm,
|
||||
max_throttle: map_conf.max_throttle
|
||||
});
|
||||
});
|
||||
|
||||
router.get("/edit", async (req, res) => {
|
||||
logger.info(`${req.method}: "${req.url}" => ${req.get("User-Agent")}`);
|
||||
|
||||
const requested_map = req.query["element"];
|
||||
if (requested_map) {
|
||||
try {
|
||||
if (map.mapExists(requested_map)) {
|
||||
res.render("edit", {
|
||||
page_title: app_conf.name,
|
||||
program: app_conf.program,
|
||||
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",
|
||||
map: JSON.stringify(await map.parseMapJson(requested_map, 0)),
|
||||
map_meta: JSON.stringify(await map.parseMapJson(requested_map, 1)),
|
||||
steps: map_conf.steps,
|
||||
max_rpm: map_conf.max_rpm,
|
||||
max_throttle: map_conf.max_throttle
|
||||
});
|
||||
} else {
|
||||
errorPageRenderer(res, "🔍❌", messages.not_found);
|
||||
}
|
||||
} catch (error) {
|
||||
errorPageRenderer(res, "🌋💥", messages.internal_server_error);
|
||||
logger.error(error.message);
|
||||
}
|
||||
} else {
|
||||
errorPageRenderer(res, "✋⛔", messages.bad_request);
|
||||
}
|
||||
});
|
||||
|
||||
router.get("/", async (req, res) => {
|
||||
logger.info(`${req.method}: "${req.url}" => ${req.get("User-Agent")}`);
|
||||
|
||||
try {
|
||||
const maps = await map.getAvailableMaps();
|
||||
|
||||
if (Object.keys(maps).length) {
|
||||
var table_data = "";
|
||||
for (m of Object.keys(maps)) {
|
||||
table_data += `<tr><td>${m}</td>`;
|
||||
if ("name" in maps[m]) {
|
||||
table_data += `<td>${maps[m]["name"]}</td>`;
|
||||
} else {
|
||||
table_data += "<td>-</td>";
|
||||
}
|
||||
|
||||
if ("id" in maps[m]) {
|
||||
table_data += `<td>${maps[m]["id"]}</td>`;
|
||||
} else {
|
||||
table_data += "<td>-</td>";
|
||||
}
|
||||
|
||||
if ("description" in maps[m]) {
|
||||
table_data += `<td>${maps[m]["description"]}</td>`;
|
||||
} else {
|
||||
table_data += "<td>-</td>";
|
||||
}
|
||||
|
||||
table_data += "</tr>";
|
||||
}
|
||||
|
||||
res.render("index", {
|
||||
page_title: app_conf.name,
|
||||
program: app_conf.program,
|
||||
css_path: path.join(app_conf.paths.css.web, "stylesheet.css"),
|
||||
js_path: path.join(app_conf.paths.js.web, "index.js"),
|
||||
table_data: table_data
|
||||
});
|
||||
} else {
|
||||
errorPageRenderer(res, "❓", messages.empty_db);
|
||||
}
|
||||
} catch (error) {
|
||||
errorPageRenderer(res, "🌋💥", messages.internal_server_error);
|
||||
logger.error(error.message);
|
||||
}
|
||||
});
|
||||
|
||||
router.get("/about", (req, res) => {
|
||||
res.render("about", {
|
||||
page_title: app_conf.name,
|
||||
css_path: path.join(app_conf.paths.css.web, "stylesheet.css"),
|
||||
version: app_conf.version
|
||||
});
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
@@ -0,0 +1,26 @@
|
||||
const path = require("path");
|
||||
const app_conf = require("../config/app.conf.js");
|
||||
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
|
||||
router.use(app_conf.paths.css.web, express.static(path.join(path.dirname(require.main.filename), app_conf.paths.css.src)));
|
||||
router.use(app_conf.paths.js.web, express.static(path.join(path.dirname(require.main.filename), app_conf.paths.js.src)));
|
||||
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...
|
||||
*/
|
||||
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
|
||||
});
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
@@ -0,0 +1,24 @@
|
||||
const winston = require("winston");
|
||||
const app_conf = require("../config/app.conf");
|
||||
const path = require("path");
|
||||
|
||||
/*
|
||||
* Initialize console and file logger
|
||||
*/
|
||||
const logger = winston.createLogger({
|
||||
levels: winston.config.syslog.levels,
|
||||
level: process.env.LOG_LEVEL || app_conf.logger.level,
|
||||
format: winston.format.combine(
|
||||
winston.format.timestamp({
|
||||
format: app_conf.timestamp_format,
|
||||
}),
|
||||
winston.format.printf((info) => `[${info.timestamp}][${info.level}]: ${info.message}`)
|
||||
),
|
||||
transports: [
|
||||
new winston.transports.File({ filename: path.join(path.dirname(require.main.filename), app_conf.logger.error_path), level: "error", }),
|
||||
new winston.transports.File({ filename: path.join(path.dirname(require.main.filename), app_conf.logger.full_path) }),
|
||||
new winston.transports.Console()
|
||||
],
|
||||
});
|
||||
|
||||
module.exports = logger;
|
||||
@@ -0,0 +1,180 @@
|
||||
const csv = require("fast-csv");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const { steps } = require("../config/map.conf");
|
||||
|
||||
async function addMap(filename, meta, throttle, motor) {
|
||||
var total = "";
|
||||
var map = [[[]]];
|
||||
|
||||
if (meta) {
|
||||
if ("name" in meta) {
|
||||
total += `#name:${meta["name"]}\n`;
|
||||
}
|
||||
if ("id" in meta) {
|
||||
total += `#id:${meta["id"]}\n`;
|
||||
}
|
||||
if ("description" in meta) {
|
||||
total += `#description:${meta["description"]}\n`;
|
||||
}
|
||||
}
|
||||
|
||||
if (throttle && motor && ("0" in motor) && ("1" in motor)) {
|
||||
const throttle_values = Object.values(throttle);
|
||||
const current_values = Object.values(motor["0"]);
|
||||
const brake_values = Object.values(motor["1"]);
|
||||
|
||||
for (let i = 0; i <= steps; i++) {
|
||||
map[i] = [current_values[i], brake_values[i], throttle_values[i]];
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await csv.writeToString(map).then(data => total += data);
|
||||
fs.writeFileSync(`app/storage/${filename}`, total);
|
||||
} catch (error) {
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
|
||||
async function updateMap(filename, meta, throttle, motor) {
|
||||
var total = await generateComments(filename, meta);
|
||||
const map = await generateData(filename, throttle, motor);
|
||||
|
||||
try {
|
||||
await csv.writeToString(map).then(data => total += data);
|
||||
fs.writeFileSync(`app/storage/${filename}`, total);
|
||||
} catch (error) {
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
|
||||
async function generateData(filename, throttle, motor) {
|
||||
const map = await parseMap(filename, 0);
|
||||
|
||||
if (throttle) {
|
||||
const throttle_keys = Object.keys(throttle);
|
||||
for (k of throttle_keys) {
|
||||
map[k][2] = throttle[k];
|
||||
}
|
||||
}
|
||||
|
||||
if (motor && ("0" in motor) && ("1" in motor)) {
|
||||
const current_keys = Object.keys(motor["0"]);
|
||||
for (k of current_keys) {
|
||||
map[k][0] = motor["0"][k];
|
||||
}
|
||||
|
||||
const brake_keys = Object.keys(motor["1"]);
|
||||
for (k of brake_keys) {
|
||||
map[k][1] = motor["1"][k];
|
||||
}
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
async function generateComments(filename, meta) {
|
||||
const comments = await parseMapJson(filename, 1);
|
||||
var comment_string = "";
|
||||
|
||||
if (meta) {
|
||||
if ("name" in meta) {
|
||||
comments["name"] = meta["name"];
|
||||
}
|
||||
|
||||
if ("id" in meta) {
|
||||
comments["id"] = meta["id"];
|
||||
}
|
||||
|
||||
if ("description" in meta) {
|
||||
comments["description"] = meta["description"];
|
||||
}
|
||||
|
||||
for (k of Object.keys(comments)) {
|
||||
comment_string += `#${k}:${comments[k]}\n`;
|
||||
}
|
||||
}
|
||||
|
||||
return comment_string;
|
||||
}
|
||||
|
||||
function mapExists(filename) {
|
||||
if (path.extname(filename) != ".csv") {
|
||||
return false;
|
||||
}
|
||||
return fs.existsSync(`app/storage/${filename}`);
|
||||
}
|
||||
|
||||
// mode: comments 1 or data 0
|
||||
function parseMap(filename, mode) {
|
||||
var result = [];
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
if (mode) {
|
||||
csv.parseFile(`app/storage/${filename}`)
|
||||
.on('error', error => reject(error))
|
||||
.on('data', row => row[0][0] === "#" ? result.push(row[0].slice(1)) : resolve(result))
|
||||
.on('end', () => resolve(result));
|
||||
} else {
|
||||
csv.parseFile(`app/storage/${filename}`, { comment: "#" })
|
||||
.on('error', error => reject(error))
|
||||
.on('data', row => result.push(row))
|
||||
.on('end', () => resolve(result));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function parseMapJson(filename, mode) {
|
||||
const map = await parseMap(filename, mode);
|
||||
var json_map = {};
|
||||
|
||||
if (mode) {
|
||||
if (map && map.length) {
|
||||
for (const m of map) {
|
||||
json_map[m.slice(0, m.indexOf(":")).toLowerCase()] = m.slice(m.indexOf(":") + 1)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < map.length; i++) {
|
||||
json_map[i] = map[i]
|
||||
}
|
||||
}
|
||||
|
||||
return json_map
|
||||
}
|
||||
|
||||
async function getAvailableMaps() {
|
||||
// path in app_conf
|
||||
var files = fs.readdirSync("app/storage");
|
||||
var available_maps = {};
|
||||
|
||||
for (const file of files) {
|
||||
if (path.extname(file) == ".csv") {
|
||||
available_maps[file] = {};
|
||||
|
||||
try {
|
||||
let meta = await parseMap(file, 1);
|
||||
|
||||
if (meta && meta.length) {
|
||||
for (const m of meta) {
|
||||
available_maps[file][m.slice(0, m.indexOf(":")).toLowerCase()] = m.slice(m.indexOf(":") + 1)
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return available_maps;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getAvailableMaps,
|
||||
parseMap,
|
||||
parseMapJson,
|
||||
mapExists,
|
||||
updateMap,
|
||||
addMap
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
<!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" style="display: flex; flex-direction: row; justify-content: space-between;">
|
||||
<label style="font-size: 200%;">CRTelemetry SQLite3</label>
|
||||
</div>
|
||||
<div class="main_div">
|
||||
<center>
|
||||
<label>Version <%= version %></label>
|
||||
<br>
|
||||
<a href="https://github.com/Centaurus-Racing-Team/5G-telemetry" target="_blank"
|
||||
style="font-size: 150%;">GitHub</a>
|
||||
</center>
|
||||
<br>
|
||||
<a href="/app"><span>🢀 </span>Return</a>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,73 @@
|
||||
<!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 %>">
|
||||
<script src="<%= js_path %>" defer></script>
|
||||
<script src="<%= chartjs %>"></script>
|
||||
<script defer>
|
||||
var steps = '<%= steps %>', max_rpm = '<%= max_rpm %>', max_throttle = '<%= max_throttle %>';
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="control_div" style="display: flex; flex-direction: row; justify-content: space-between;">
|
||||
<label style="font-size: 200%;">
|
||||
<%= page_title%>
|
||||
<%= program %>
|
||||
</label>
|
||||
<div style="display: flex;align-items: center;">
|
||||
<label id="download" style="cursor: pointer; margin-right: 10px;"><span>🡇
|
||||
</span>Download</label>
|
||||
<label id="remove" style="cursor: pointer; margin-left: 10px; margin-right: 10px;"><span>-
|
||||
</span>Remove</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="main_div">
|
||||
<center><label>Add record</label></center>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>id</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input id="map_name" type="text" placeholder="map_name"
|
||||
style="width: 100%; height: 100%; box-sizing: border-box; font-size: 16px;"></td>
|
||||
<td><input id="map_id" type="text" placeholder="map_id"
|
||||
style="width: 100%; height: 100%; box-sizing: border-box; font-size: 16px;"></td>
|
||||
<td><input id="map_description" type="text" placeholder="map_description"
|
||||
style="width: 100%; height: 100%; box-sizing: border-box; font-size: 16px;"></td>
|
||||
</tr>
|
||||
</table>
|
||||
<hr class="solid">
|
||||
<div>
|
||||
<div style="height: 50vh; width: 100%;"><canvas id="throttle_plot"></canvas></div>
|
||||
<table id="throttle_table" style="text-align: center;"></table>
|
||||
</div>
|
||||
<hr class="solid">
|
||||
<div>
|
||||
<div style="height: 50vh; width: 100%;"><canvas id="motor_plot"></canvas></div>
|
||||
<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>🢀 </span>Return</a>
|
||||
<label id="commit" style="cursor: pointer;"><span>✅ </span>Commit</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="about_div">
|
||||
<center><a href="/app/about">About</label></center>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,33 @@
|
||||
<!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>
|
||||
@@ -0,0 +1,70 @@
|
||||
<!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 %>">
|
||||
<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 %>';
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="control_div" style="display: flex; flex-direction: row; justify-content: space-between;">
|
||||
<label style="font-size: 200%;">
|
||||
<%= page_title%>
|
||||
<%= program %>
|
||||
</label>
|
||||
<div style="display: flex;align-items: center;">
|
||||
<label id="download" style="cursor: pointer; margin-right: 10px;"><span>🡇
|
||||
</span>Download</label>
|
||||
<label id="remove" style="cursor: pointer; margin-left: 10px; margin-right: 10px;"><span>-
|
||||
</span>Remove</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="main_div">
|
||||
<center><label>Edit record</label></center>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>id</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input id="map_name" type="text" placeholder="map_name" style="width: 100%; height: 100%; box-sizing: border-box; font-size: 16px;"></td>
|
||||
<td><input id="map_id" type="text" placeholder="map_id" style="width: 100%; height: 100%; box-sizing: border-box; font-size: 16px;"></td>
|
||||
<td><input id="map_description" type="text" placeholder="map_description" style="width: 100%; height: 100%; box-sizing: border-box; font-size: 16px;"></td>
|
||||
</tr>
|
||||
</table>
|
||||
<hr class="solid">
|
||||
<div>
|
||||
<div style="height: 50vh; width: 100%;"><canvas id="throttle_plot"></canvas></div>
|
||||
<table id="throttle_table" style="text-align: center;"></table>
|
||||
</div>
|
||||
<hr class="solid">
|
||||
<div>
|
||||
<div style="height: 50vh; width: 100%;"><canvas id="motor_plot"></canvas></div>
|
||||
<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>🢀 </span>Return</a>
|
||||
<label id="commit" style="cursor: pointer;"><span>✅ </span>Commit</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="about_div">
|
||||
<center><a href="/app/about">About</label></center>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,39 @@
|
||||
<!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 %>">
|
||||
<script src="<%= js_path %>" defer></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<input type="file" id="file_input" accept=".db" style="display: none;" />
|
||||
<div class="control_div" style="display: flex; flex-direction: row; justify-content: space-between;">
|
||||
<label style="font-size: 200%;">CRTelemetry SQLite3</label>
|
||||
<div style="display: flex;align-items: center;">
|
||||
<label id="logout" style="cursor: pointer;"><span>🡄 </span>Logout</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="main_div">
|
||||
<label style="font-size: 150%;">
|
||||
<span>
|
||||
<%- error_emoji %>
|
||||
</span>
|
||||
<%= error_message %>
|
||||
</label>
|
||||
<br>
|
||||
<a href="/app"><span>🢀 </span>Return</a>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,52 @@
|
||||
<!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 %>">
|
||||
<script src="<%= js_path %>" defer></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<input type="file" id="file_input" accept=".csv" style="display: none;" />
|
||||
<div class="control_div" style="display: flex; flex-direction: row; justify-content: space-between;">
|
||||
<label style="font-size: 200%;">
|
||||
<%= page_title %>
|
||||
<%= program %>
|
||||
</label>
|
||||
<div style="display: flex;align-items: center;">
|
||||
<label id="add" style="cursor: pointer; margin-right: 10px;"><span>+ </span>Add</label>
|
||||
<label id="upload" style="cursor: pointer; margin-left: 10px; margin-right: 10px;"><span>🡅
|
||||
</span>Upload</label>
|
||||
<label id="logout" style="cursor: pointer; margin-left: 10px;"><span>🡄 </span>Logout</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="main_div">
|
||||
<center><label>Main</label></center>
|
||||
<table id="sql_table">
|
||||
<tr>
|
||||
<th>Filename</th>
|
||||
<th>Name</th>
|
||||
<th>id</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<%- table_data %>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="about_div">
|
||||
<center><a href="/app/about">About</label></center>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Reference in New Issue
Block a user