const csv = require("fast-csv"); const fs = require("fs"); const path = require("path"); const uuid = require("uuid"); const map_conf = require("../config/map.conf"); const logger = require("./logger.util"); // 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`; } if ("id" in meta) { total += `#id:${meta["id"]}\n`; } if ("description" in meta) { total += `#description:${meta["description"]}\n`; } } // 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"]); for (let i = 0; i <= map_conf.steps; i++) { map[i] = [current_values[i], brake_values[i], throttle_values[i]]; } } 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); } catch (error) { throw new Error(error); } } // 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); fs.renameSync(path.join(path.dirname(require.main.filename), map_conf.map_storage_path, filename), path.join(path.dirname(require.main.filename), map_conf.map_storage_path, `${map_conf.prefix}_${uuid.v4()}.${map_conf.extension}`)); if (fs.existsSync(path.join(path.dirname(require.main.filename), map_conf.map_storage_path, "selected"))) { fs.unlinkSync(path.join(path.dirname(require.main.filename), map_conf.map_storage_path, "selected")); } } catch (error) { throw new Error(error); } } // Merges data from a stored map and a new map 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; } // Merges comments from stored map and new 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; } // Check if a map exists function mapExists(filename) { if (path.extname(filename) != ".csv") { return false; } return fs.existsSync(path.join(path.dirname(require.main.filename), map_conf.map_storage_path, filename)); } // Parse a map and returns it as array. On 1 returns comments on 0 returns data function parseMap(filename, mode) { var result = []; return new Promise((resolve, reject) => { if (mode) { csv.parseFile(path.join(path.dirname(require.main.filename), map_conf.map_storage_path, 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(path.join(path.dirname(require.main.filename), map_conf.map_storage_path, filename), { comment: "#" }) .on('error', error => reject(error)) .on('data', row => result.push(row)) .on('end', () => resolve(result)); } }); } // Parse map data as JSON 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 } // Get available map names in storage directory async function getAvailableMaps() { var available_maps = {}; try { var files = fs.readdirSync(path.join(path.dirname(require.main.filename), map_conf.map_storage_path)); for (const file of files) { if (path.extname(file) == `.${map_conf.extension}`) { 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); } } } } catch (error) { if (error.code === "ENOENT") { logger.info("No storage directory found. Creating directory."); fs.mkdirSync(path.join(path.dirname(require.main.filename), map_conf.map_storage_path)); } } return available_maps; } module.exports = { getAvailableMaps, parseMap, parseMapJson, mapExists, updateMap, addMap };