From 7bdce37fd3f18e2712e18c4e2c64cac69af0aca1 Mon Sep 17 00:00:00 2001 From: Jakob Stendahl Date: Sun, 19 Sep 2021 19:43:11 +0200 Subject: :boom: Introduce new UI based on svelte, and rewrite a lot of the node app and the NeoRuntime --- src/NeoRuntimeManager/index.js | 309 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 309 insertions(+) create mode 100644 src/NeoRuntimeManager/index.js (limited to 'src/NeoRuntimeManager/index.js') diff --git a/src/NeoRuntimeManager/index.js b/src/NeoRuntimeManager/index.js new file mode 100644 index 0000000..62acb8a --- /dev/null +++ b/src/NeoRuntimeManager/index.js @@ -0,0 +1,309 @@ +/** + * This module is used to execute and communicate with a python NeoRuntime instance. + * + * @author jakobst1n. + * @since 19.12.2019 + */ + +const fs = require("fs"); +const fsPromises = fs.promises; +const RuntimeProcess = require("./RuntimeProcess"); +let logger = require(__basedir + "/src/logger"); +const EventEmitter = require('events'); + +/** @type {object} this should be a pointer to a object referencing all neoModules (see app.js) */ +let neoModules; + +/** @type {string} Currently active mode */ +let modeId = null; +/** @type {int} Last exit code of a mode */ +let modeExitCode = 0; +/** @type {RuntimeProcess} This is the current RuntimeProcess instance */ +let runtimeProcess = null; +/** @type {EventEmitter} This is used to emit events when things change */ +const eventEmitter = new EventEmitter(); +/** @type {boolean} If this is true, we will not do things the usual way */ +let modeDebuggerActive = false; +/** @type {string} Should be the modeId the debugger is attached to */ +let modeDebuggerId = null; + +eventEmitter.on("proc:exit", (code) => modeExitCode = code); + +/** + * Check if a path id actually a mode (if it is a folder with a script.py file) + * + * @param {string} path - Path to check. + * + * @return {boolean} wether the path points to a valid mode. + */ +function isMode(path) { + if (!fs.existsSync(path)) { return false; } + let folderStat = fs.statSync(path); + if (!folderStat.isDirectory()) { return false; } + if (!fs.existsSync(path + "/script.py")) { return false; } + return true; +} + +/** + * Get all ids of modes that can be set. + * + * @returns {array} All modeids + */ +function listModes() { + let modeDirs = [ + ["builtin/", fs.readdirSync(__basedir + "/NeoRuntime/builtin")], + ["remote/", fs.readdirSync(__datadir + "/remoteCode")], + ["user/", fs.readdirSync(__datadir + "/userCode")] + ] + let validModes = []; + for (modeDir of modeDirs) { + for (modeName of modeDir[1]) { + let modeId = `${modeDir[0]}${modeName}`; + if (isMode(getModePath(modeId))) { + validModes.push(modeId); + } + } + } + return validModes; +} + +/** + * Change mode, stop the old one and start the new one. + * + * @param {string} _modeId - Id of the mode to change to. + * + * @return {object} A standardform return object. + */ +function setMode(_modeId) { + if (modeDebuggerActive && (_modeId != modeDebuggerId)) { + return {success: false, reason: "debugger active", detail: "Cannot change mode when debugger is active."} + } + if (!isMode(getModePath(_modeId))) { + console.log(`Invalid mode "${_modeId}".`); + return {success: false, reason: "unknown modeId"}; + } + logger.info(`Changing mode to "${_modeId}".`); + + let globvarsTmp = {}; + let variablesTmp = {}; + if (runtimeProcess != null) { + globvarsTmp = runtimeProcess.globvars; + variablesTmp = runtimeProcess.variables; + } + + stopMode(); + + modeId = _modeId; + neoModules.userData.config.activeMode = modeId; + eventEmitter.emit("change", "mode", modeId); + + runtimeProcess = new RuntimeProcess(getModePath(_modeId), onVariableChange, eventEmitter); + runtimeProcess.globvars = globvarsTmp; + runtimeProcess.variables = variablesTmp; + startMode(); + return {success: true} +}; + +/** + * Get current mode + * + * @return {string} current modeId + */ +function currentMode() { + return modeId; +} + +/** + * Will attempt to stop current mode + * + * @return {object} A standardform return object. + */ +function stopMode(restart=false) { + if (modeRunning()) { + runtimeProcess.stop(restart); + } + return {success: true} +}; + +/** + * Will attempt to start current mode + * + * @return {object} A standardform return object. + */ +function startMode() { + if (runtimeProcess === null) { return {success: false, reason: "no runtimeprocess", detail: "Runtimeprocess not set, did you mean to call setMode?"}; } + runtimeProcess.start(); + return {success: true} +}; + +/** + * Will attempt to restart current mode + * + * @return {object} A standardform return object. + */ +function restartMode() { + return stopMode(true); +}; + +/** + * Checks if mode is running currently + * + * @return {boolean} if mode is running + */ +function modeRunning() { + if (runtimeProcess === null) { return false; } + return runtimeProcess.isRunning; +}; + +/** + * Get the full system path to a mode + * + * @param {string} modeId + * + * @return {string} Full path of mode + */ +function getModePath(modeId) { + let path = modeId.split("/"); + let location = path.splice(0, 1).toString(); + if (location === "user") { path = __datadir + "/userCode/" + path.join("/"); } + if (location === "remote") { path = __datadir + "/remoteCode/" + path.join("/"); } + if (location === "builtin") { path = __basedir + "/NeoRuntime/builtin/" + path.join("/"); } + return path; +} + +/** + * This should be called by RuntimeProcess when a variable changes in the mode + * + * @param {string} location - This is globvars/variables + * @param {string} name - Name of the variable + * @param {any} newValue - The new value of the variable + */ +function onVariableChange(location, name, newValue) { + if (location == "variables") { + eventEmitter.emit("change", `variable/${name}`, newValue) + } else if (location == "globvars") { + eventEmitter.emit("change", `${name}`, newValue) + } +} + +/** + * Function that returns all globvars (brightness, power_on) as the values they + * had last time we heard from the python script. + * + * @return {object} + */ +function getGlobvars() { + if (!modeRunning()) { return {}; } + return runtimeProcess.globvars; +} + +/** + * Sets value of a globvar power_on/brightness. + * + * @param {string} name - Name of the variable power_on/brightness + * @param {any} value - The value the variable should be set to + * + * @return {object} Standardform return object + */ +function setGlobvar(name, value) { + if (!modeRunning()) { return; } + runtimeProcess.proc.stdin.write(`:::setglob: ${name}:${value}\n`); + return {success: true} +} + +/** + * Get all variables declared in mode + * + * @return {object} + */ +function getVariables() { + if (!modeRunning()) { return {}; } + return runtimeProcess.variables; +} + +/** + * Sets value of a variable + * + * @param {string} name - Name of the variable + * @param {any} value - The value the variable should be set to + * + * @return {object} Standardform return object + */ +function setVariable(name, value) { + if (!modeRunning()) { return; } + runtimeProcess.proc.stdin.write(`:::setvar: ${name}:${value}\n`); + return {success: true} +} + +/** + * Start debugger for a mode + * + * @param {string} modeId - The mode to debug + * + * @return {object} Standardform return object + */ +function startDebugger(debuggerModeId) { + if (debuggerModeId.substr(0, 5) !== "user/") { return {success: false, reason: "not user mode"}; } + if (!isMode(getModePath(debuggerModeId))) { return {success: false, reason: "unknown modeId"}; } + if (modeDebuggerActive) { return {success: false, reason: "debugger already active"}; } + logger.info(`Starting debugger for ${debuggerModeId}`); + modeDebuggerActive = true; + modeDebuggerId = debuggerModeId; + if (debuggerModeId != modeId) { + setMode(debuggerModeId); + } else { + restartMode(); + } + return {success: true, code: fs.readFileSync(getModePath(debuggerModeId) + "/script.py").toString()} +} + +/** + * Save mode + */ +function saveModeCode(_modeId, code) { + if (!modeDebuggerActive) { return {success: false, reason: "debugger not active"}; }; + if (_modeId != modeDebuggerId) { return {success: false, reason: "modeid not the same as debuggermodeid"}; }; + fs.writeFileSync(getModePath(`${modeDebuggerId}/script.py`), code); + return {success: true}; +} + +/** + * Stop the active debugger + * + * @return {object} Standardform return object + */ +function stopDebugger() { + if (!modeDebuggerActive) { return {success: true, detail: "No debugger active"} } + logger.info(`Stopping debugger`); + modeDebuggerActive = false; + return {success: true} +} + +module.exports = (_neoModules) => { + neoModules = _neoModules; + return { + event: eventEmitter, + modes: listModes, + mode: { + current: currentMode, + set: (modeId) => setMode(modeId), + status: { + modeRunning: modeRunning(), + modeExitCode: modeExitCode + }, + globvars: { + get: getGlobvars, + set: setGlobvar + }, + variables: { + get: getVariables, + set: setVariable + } + }, + getModePath, + isMode, + modeRunning, + startDebugger, stopDebugger, saveModeCode, + startMode, stopMode, restartMode + } +}; \ No newline at end of file -- cgit v1.2.3 From 5cc8e0a8ed605a15b95b707b9d1b805f32271e3f Mon Sep 17 00:00:00 2001 From: Jakob Stendahl Date: Sun, 3 Oct 2021 16:44:59 +0200 Subject: :building_construction: Use UNIX socket for IPC instead of stdin/out --- NeoRuntime/Runtime/luxcena_neo/strip.py | 2 +- NeoRuntime/Runtime/neo_runtime.py | 141 +++++++++++----- src/NeoRuntimeManager/IPC.js | 178 +++++++++++++++++++++ src/NeoRuntimeManager/RuntimeProcess.js | 78 ++------- src/NeoRuntimeManager/index.js | 34 ++-- src/UserData/index.js | 3 + .../MainControls/ControlComponents.svelte | 2 +- 7 files changed, 321 insertions(+), 117 deletions(-) create mode 100644 src/NeoRuntimeManager/IPC.js (limited to 'src/NeoRuntimeManager/index.js') diff --git a/NeoRuntime/Runtime/luxcena_neo/strip.py b/NeoRuntime/Runtime/luxcena_neo/strip.py index a65b3f0..bfe2bbc 100644 --- a/NeoRuntime/Runtime/luxcena_neo/strip.py +++ b/NeoRuntime/Runtime/luxcena_neo/strip.py @@ -59,7 +59,7 @@ class Strip: self.__brightness = 255 self.__actual_brightness = self.__brightness - self.__globvars_path = path.join(path.split(path.dirname(path.abspath(__file__)))[0], "globvars.json") + self.__globvars_path = path.join(path.split(path.dirname(path.abspath(__file__)))[0], "state.json") if path.exists(self.__globvars_path): try: with open(self.__globvars_path, "r") as f: diff --git a/NeoRuntime/Runtime/neo_runtime.py b/NeoRuntime/Runtime/neo_runtime.py index e5941e2..4ecbc97 100644 --- a/NeoRuntime/Runtime/neo_runtime.py +++ b/NeoRuntime/Runtime/neo_runtime.py @@ -11,7 +11,8 @@ import time import threading import select import traceback -from os import path +import socket +from os import path, remove from luxcena_neo.strip import Strip @@ -41,7 +42,6 @@ def init_package(package_path, entry_module, strip): # Make the strip instance available in our modules setattr(module, "strip", strip) - module_entry_instance.declare_variables() return module_entry_instance def exec_module(module_executor_loop_func): @@ -53,10 +53,12 @@ def exec_module(module_executor_loop_func): class NeoRuntime: - def __init__(self, package_path, entry_module, strip_config_file): + def __init__(self, package_path, entry_module, strip_config_file, socket_file): self.__strip = init_strip(strip_config_file) self.__module_entry_instance = init_package(package_path, entry_module, self.__strip) self.__module_th = None + self.__socket_file = socket_file + self.__send_strip_buffer = False def start(self): @@ -66,49 +68,113 @@ class NeoRuntime: # This will run in this thread. print("> Starting to listen on stdin") + self.__s = None try: - self.__command_listener_loop() + self.__bind_socket() + self.__socket_listener() except KeyboardInterrupt: print("Exiting...") except Exception as e: traceback.print_exc() - + finally: + self.__close_socket() + + def __bind_socket(self): + if path.exists(self.__socket_file): + remove(self.__socket_file) + + self.__s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + self.__s.bind(self.__socket_file) + self.__s.listen(1) - def __command_listener_loop(self): + def __socket_listener(self): + self.__s_clients = [] last_send = time.perf_counter() + while True: if not self.__module_th.is_alive(): break - while sys.stdin in select.select([sys.stdin], [], [], 0)[0]: - line = sys.stdin.readline() - if line: - line = line.replace("\n", "") - if (line[0:10] == ":::setvar:"): - name, value = (line.split(" ", 1)[1]).replace("\"", "").split(":", 1) - if name in self.__module_entry_instance.vars: - self.__module_entry_instance.vars[name] = value - elif (line[0:11] == ":::setglob:"): - name, value = (line.split(" ", 1)[1]).replace("\"", "").split(":", 1) - if name == "brightness": - self.__strip.brightness = int(value) - elif name == "power_on": - self.__strip.power_on = value == "true" - else: - print(f"Unknown globvar \"{name}\"") - else: - if (time.perf_counter() - last_send) > 0.5: - _vars = "{" - for name, var in self.__module_entry_instance.vars: - _vars += f" \"{name}\" : {{ \"value\": \"{var.value}\", \"var_type\": \"{var.var_type}\" }}, " - if len(_vars) > 2: - _vars = _vars[0:-2] - _vars += "}" - - globvars = "{ \"power_on\": " + str(self.__strip.power_on).lower() + ", " - globvars += " \"brightness\":" + str(self.__strip.brightness) + " }" - print(f"{{ \":::data:\": {{ \"variables\": {_vars}, \"globvars\": {globvars} }} }}") - last_send = time.perf_counter() + r, w, e = select.select([self.__s, *self.__s_clients], self.__s_clients, [], 0) + + if (time.perf_counter() - last_send) > 0.5: + states = { + "variables": self.__module_entry_instance.var.to_dict(), + "globvars": { + "power_on": self.__strip.power_on, + "brightness": self.__strip.brightness + } + } + buf = bytes([1]) + bytes(json.dumps(states), "ascii") + + for ws in w: + try: + ws.send(buf) + except BrokenPipeError: + self.__s_clients.remove(ws) + ws.close() + + last_send = time.perf_counter() + + for rs in r: + if rs is self.__s: + c, a = self.__s.accept() + self.__s_clients.append(c) + else: + data = rs.recv(128) + if not data: + self.__s_clients.remove(rs) + rs.close() + else: + self.__execute_command(data) + def __close_socket(self): + if (self.__s is None): return + r, w, e = select.select([self.__s, *self.__s_clients], self.__s_clients, [], 0) + for ws in w: + try: + ws.shutdown(socket.SHUT_RDWR) + except BrokenPipeError: + ws.close() + self.__s_clients.remove(ws) + ws.close() + self.__s.close() + + + + def __execute_command(self, command): + """ + command should be of type bytes + first byte indicates command type (currently setglob or setvar) + + for command type 1 + byte 1 indicates which globvar + byte 2 indicates value + for command type 2 + first 32 bytes are the var name + + """ + # print(command.hex(" ")) + if command[0] == 0: + if command[1] == 0: + self.__strip.power_on = (command[2] == 1) + print(f"Strip power: {self.__strip.power_on}") + elif command[1] == 1: + self.__strip.brightness = command[2] + print(f"Strip brightness: {self.__strip.brightness}") + else: + print(f"Unknown globvar {command[1]}.") + elif command[0] == 1: + name = command[3:3+command[1]].decode("ascii") + value = command[3+command[1]:3+command[1]+command[2]].decode("ascii") + if name in self.__module_entry_instance.var: + self.__module_entry_instance.var[name] = value + else: + print(f"Unknown variable {name}") + elif command[0] == 2: + self.__send_strip_buffer = (command[1] == 1) + else: + print("UNKNOWN COMMAND") + def __module_loop(self): self.__module_entry_instance.on_start() @@ -153,11 +219,14 @@ if __name__ == "__main__": parser.add_argument('--strip-config', help='Path to the strip config file.') parser.add_argument('--mode-path', help='Path of the folder the mode is in.') parser.add_argument('--mode-entry', help='Path of the module that is the entry-point of the module.') + parser.add_argument('--socket-file', help='The socket file the runtime will use to allow communication [default: /tmp/neo_runtime.sock].', default='/tmp/neo_runtime.sock') + parser.add_argument('--socket-enable', help='Wether to enable socket communication [default: true].', default=True) args = parser.parse_args() args.strip_config = args.strip_config.replace("\"", "") args.mode_path = args.mode_path.replace("\"", "") args.mode_entry = args.mode_entry.replace("\"", "") + args.socket_file = args.socket_file.replace("\"", "") if not path.exists(args.strip_config): print(f"Strip config not found ({args.strip_config}).") sys.exit(1) @@ -172,6 +241,6 @@ if __name__ == "__main__": print(f"Module : {args.mode_path}/{args.mode_entry}") print(f"> Starting \"{args.mode_path}\" in NeoRuntime.") - runtime = NeoRuntime(args.mode_path, args.mode_entry, args.strip_config) + runtime = NeoRuntime(args.mode_path, args.mode_entry, args.strip_config, args.socket_file) runtime.start() print ("> NeoRuntime exited...") diff --git a/src/NeoRuntimeManager/IPC.js b/src/NeoRuntimeManager/IPC.js new file mode 100644 index 0000000..817c049 --- /dev/null +++ b/src/NeoRuntimeManager/IPC.js @@ -0,0 +1,178 @@ +/** + * This module is used to communicate with a python NeoRuntime instance. + * + * @author jakobst1n. + * @since 3.10.2021 + */ + +const net = require("net"); +let logger = require(__basedir + "/src/logger"); + +/** @type {int} How long wait between each reconnection attempt */ +const RECONNECT_INTERVAL = 1000; +/** @type {Object} ENUM-ish for command that can be sent to neoruntime */ +const COMMAND = Object.freeze({SET_GLOB : 0, + SET_VAR : 1, + SET_SEND_STRIP_BUF: 2}); +/** @type {Object} ENUM-ish for globvars */ +const GLOBVAR = Object.freeze({POWER_ON : 0, + BRIGHTNESS: 1}); +/** @type {Object} ENUM-ish for what type of data neoruntime sends */ +const DATATYPE = Object.freeze({STATES : 1, + STRIP_BUF: 2}); + +/** + * class that will keep a active connection to a socket if possible, and + * automatically reconnect. It will emit events when data is received, + * and it will send commands to the process. */ +class IPC { + + constructor(_socketFile, _eventEmitter) { + this.socketFile = _socketFile; + this.eventEmitter = _eventEmitter; + + this.client; + this.connected = false; + this.reconnectInterval = false; + + this.globvars = {}; + this.variables = {}; + + this.reconnect(); + } + + /** + * If we are not already attempting to reconnect, this will start a + * interval that tries to reconnect. */ + reconnect() { + if (this.reconnectInterval === false) { + this.reconnectInterval = setInterval(this.tryOpenSocketConnection.bind(this), RECONNECT_INTERVAL); + } + } + + /** + * This will attempt to connect to the socket, and then setup all listeners + * if it succedes. */ + tryOpenSocketConnection() { + // logger.info("Attempting to start IPC"); + + this.client = net.createConnection(this.socketFile) + .on('connect', () => { + clearInterval(this.reconnectInterval); + this.reconnectInterval = false; + // logger.info("IPC Connected."); + }) + .on("ready", () => { + this.connected = true; + }) + .on('data', (data) => { + switch (data[0]) { + case DATATYPE.STATES: + let json_data; + try { + json_data = JSON.parse(data.toString("ascii", 1)); + } catch (e) { + logger.warning("Could not parse json data from neoruntime"); + return; + } + + if (json_data.hasOwnProperty("globvars")) { + forEachDiff(json_data["globvars"], this.globvars, (key, newVal) => { + this.eventEmitter.emit("change", key, newVal); + }); + this.globvars = json_data["globvars"]; + } + if (json_data.hasOwnProperty("variables")) { + forEachDiff(json_data["variables"], this.variables, (key, newVal) => { + this.eventEmitter.emit("change", `variable/${key}`, newVal); + }); + this.variables = json_data["variables"]; + } + break; + + default: + logger.info(data); + } + + }) + .on("timeout", () => { + logger.info("IPC Timeout"); + }) + .on("close", (hadError) => { + // logger.info("IPC Close, hadError: ", hadError); + this.connected = false; + this.reconnect(); + }) + .on("end", () => { + // logger.info("IPC End"); + this.connected = false; + }) + .on('error', (data) => { + // logger.info('IPC Server not active.'); + this.connected = false; + this.reconnect(); + }) + ; + } + + /** + * Will send a command to the socket if we have a active connection, + * if not it will just drop the command. there is no queue implemented + * for such events. */ + sendCommand(commandType, name, value) { + if (this.connected) { + let buf = Buffer.allocUnsafe(128); // It's fine, we know what we are doing + // let buf = Buffer.alloc(128); + + switch (commandType) { + case (COMMAND.SET_GLOB): + buf[1] = name; + buf[2] = value; + break; + case (COMMAND.SET_VAR): + if (name.length > 32) { return {success: false, reason: "name too long", detail: "max size of name is 32 bytes"}; } + if (name.length > 93) { return {success: false, reason: "value too long", detail: "max size of value is 93 bytes"}; } + buf[1] = name.length; + buf[2] = value.length; + buf.write(name, 3, name.length, "ascii"); + buf.write(value, 3+name.length, value.length, "ascii"); + break; + case (COMMAND.SET_SEND_STRIP_BUF): + buf[1] = (name) ? 1 : 0; + default: + logger.info(`IPC UNKNOWN COMMANDTYPE ${commandType}`) + return; + } + + buf[0] = commandType; + this.client.write(buf); + return {success: true} + } + return {success: false, reason: "socket not connected", detail: "This usually means the python script is not running"}; + } + +} + +const isObject = v => v && typeof v === 'object'; + +/** + * Will call callback on all the differences between the dicts + */ +function forEachDiff(dict1, dict2, callback) { + for (const key of new Set([...Object.keys(dict1), ...Object.keys(dict2)])) { + if (isObject(dict1[key]) && isObject(dict2[key])) { + if (dict1[key].value !== dict2[key].value) { + callback(key, dict1[key]); + } + } else if (dict1[key] !== dict2[key]) { + if (isObject(dict2[key]) && (dict1[key] == null)) { + dict2[key].value = null; + callback(key, dict2[key]) + } else { + callback(key, dict1[key]); + } + } + } +} + +module.exports = {IPC, COMMAND, GLOBVAR}; \ No newline at end of file diff --git a/src/NeoRuntimeManager/RuntimeProcess.js b/src/NeoRuntimeManager/RuntimeProcess.js index 60f6a28..60c1de9 100644 --- a/src/NeoRuntimeManager/RuntimeProcess.js +++ b/src/NeoRuntimeManager/RuntimeProcess.js @@ -3,10 +3,11 @@ let spawn = require("child_process"); class RuntimeProcess { - constructor(_modePath, _onVarChange, _eventEmitter) { + constructor(_modePath, _eventEmitter) { this.modePath = _modePath; this.logfile = `${this.modePath}/mode.log`; - + this.errfile = `${this.modePath}/mode.error`; + this.stdout = ""; this.stderr = ""; @@ -16,9 +17,6 @@ class RuntimeProcess { this.isRunning = false; this.exitCode = null; - this.variables = {}; - this.globvars = {}; - this.onVarChange = _onVarChange; this.eventEmitter = _eventEmitter; } @@ -29,7 +27,7 @@ class RuntimeProcess { } this.isRunning = true; this.proc = spawn.spawn( - "python3", + "python3", [ "-u", // This makes us able to get real-time output `${__basedir}/NeoRuntime/Runtime/neo_runtime.py`, @@ -44,28 +42,13 @@ class RuntimeProcess { }); fs.ensureFileSync(this.logfile); + fs.ensureFileSync(this.errfile); this.eventEmitter.emit("proc:start"); this.proc.stdout.on('data', (_stdout) => { let stdout_str = _stdout.toString(); - - let regex = /{ ":::data:": { (.*) } }/gi; - let data = stdout_str.match(regex); - stdout_str = stdout_str.replace(regex, () => ""); - - if ((data != null) && (data.length > 0)) { - try { - this.processVarData(data) - } catch {} - } - - if (stdout_str.replace("\n", "").replace(" ", "") == "") { - // In this case, we want to ignore the data. - } else { - // stdout_str = stdout_str.replace(/\n$/, "") - fs.appendFile(this.logfile, "\n====stdout====\n" + stdout_str); - this.eventEmitter.emit("proc:stdout", stdout_str); - } + fs.appendFile(this.logfile, `[${timestamp()}]: ` + stdout_str); + this.eventEmitter.emit("proc:stdout", stdout_str); }); this.proc.stdout.on('end', () => { @@ -73,9 +56,8 @@ class RuntimeProcess { }); this.proc.stderr.on('data', (_stderr) => { - // let stderr_str = _stderr.toString().replace(/\n$/, "") - let stderr_str = _stderr.toString() - fs.appendFile(this.logfile, "\n====stderr====\n" + stderr_str); + let stderr_str = _stderr.toString(); + fs.appendFile(this.errfile, `[${timestamp()}]: ` + stderr_str); this.eventEmitter.emit("proc:stderr", stderr_str); }); @@ -85,7 +67,7 @@ class RuntimeProcess { this.proc.on('close', (code) => { if (code) { - fs.appendFile(this.logfile, "\n====close====\nScript exited with code " + code.toString()); + fs.appendFile(this.logfile, `[${timestamp()}]: ` + "Script exited with code " + code.toString()); } this.eventEmitter.emit("proc:exit", 0); this.isRunning = false; @@ -106,45 +88,15 @@ class RuntimeProcess { console.log(err); } } - - processVarData(data) { - data = JSON.parse(data)[":::data:"]; - if (data.hasOwnProperty("globvars")) { - forEachDiff(data["globvars"], this.globvars, (key, newVal) => { - this.onVarChange("globvars", key, newVal); - }); - this.globvars = data["globvars"]; - } - if (data.hasOwnProperty("variables")) { - forEachDiff(data["variables"], this.variables, (key, newVal) => { - this.onVarChange("variables", key, newVal); - }); - this.variables = data["variables"]; - } - } - } -const isObject = v => v && typeof v === 'object'; - /** - * Will call callback on all the differences between the dicts + * Creates and returns a timestamp that can be used in logfiles. + * + * @return {string} timestamp */ -function forEachDiff(dict1, dict2, callback) { - for (const key of new Set([...Object.keys(dict1), ...Object.keys(dict2)])) { - if (isObject(dict1[key]) && isObject(dict2[key])) { - if (dict1[key].value !== dict2[key].value) { - callback(key, dict1[key]); - } - } else if (dict1[key] !== dict2[key]) { - if (isObject(dict2[key]) && (dict1[key] == null)) { - dict2[key].value = null; - callback(key, dict2[key]) - } else { - callback(key, dict1[key]); - } - } - } +function timestamp() { + return (new Date()).toISOString(); } module.exports = RuntimeProcess; diff --git a/src/NeoRuntimeManager/index.js b/src/NeoRuntimeManager/index.js index 62acb8a..6238323 100644 --- a/src/NeoRuntimeManager/index.js +++ b/src/NeoRuntimeManager/index.js @@ -8,6 +8,7 @@ const fs = require("fs"); const fsPromises = fs.promises; const RuntimeProcess = require("./RuntimeProcess"); +const IPC = require("./IPC"); let logger = require(__basedir + "/src/logger"); const EventEmitter = require('events'); @@ -20,6 +21,8 @@ let modeId = null; let modeExitCode = 0; /** @type {RuntimeProcess} This is the current RuntimeProcess instance */ let runtimeProcess = null; +/** @type {IPC} The IPC instance, used to communicate with the script */ +let ipc = null; /** @type {EventEmitter} This is used to emit events when things change */ const eventEmitter = new EventEmitter(); /** @type {boolean} If this is true, we will not do things the usual way */ @@ -83,13 +86,6 @@ function setMode(_modeId) { return {success: false, reason: "unknown modeId"}; } logger.info(`Changing mode to "${_modeId}".`); - - let globvarsTmp = {}; - let variablesTmp = {}; - if (runtimeProcess != null) { - globvarsTmp = runtimeProcess.globvars; - variablesTmp = runtimeProcess.variables; - } stopMode(); @@ -97,10 +93,9 @@ function setMode(_modeId) { neoModules.userData.config.activeMode = modeId; eventEmitter.emit("change", "mode", modeId); - runtimeProcess = new RuntimeProcess(getModePath(_modeId), onVariableChange, eventEmitter); - runtimeProcess.globvars = globvarsTmp; - runtimeProcess.variables = variablesTmp; + runtimeProcess = new RuntimeProcess(getModePath(_modeId), eventEmitter); startMode(); + return {success: true} }; @@ -194,7 +189,7 @@ function onVariableChange(location, name, newValue) { */ function getGlobvars() { if (!modeRunning()) { return {}; } - return runtimeProcess.globvars; + return ipc.globvars; } /** @@ -207,8 +202,15 @@ function getGlobvars() { */ function setGlobvar(name, value) { if (!modeRunning()) { return; } - runtimeProcess.proc.stdin.write(`:::setglob: ${name}:${value}\n`); - return {success: true} + + switch(name) { + case "power_on": + return ipc.sendCommand(IPC.COMMAND.SET_GLOB, IPC.GLOBVAR.POWER_ON, (value) ? 1 : 0); + case "brightness": + return ipc.sendCommand(IPC.COMMAND.SET_GLOB, IPC.GLOBVAR.BRIGHTNESS, value); + default: + return {success: false, reason: "unknown globvar", detail: name}; + } } /** @@ -218,7 +220,7 @@ function setGlobvar(name, value) { */ function getVariables() { if (!modeRunning()) { return {}; } - return runtimeProcess.variables; + return ipc.variables; } /** @@ -231,8 +233,7 @@ function getVariables() { */ function setVariable(name, value) { if (!modeRunning()) { return; } - runtimeProcess.proc.stdin.write(`:::setvar: ${name}:${value}\n`); - return {success: true} + return ipc.sendCommand(IPC.COMMAND.SET_VAR, name, value); } /** @@ -281,6 +282,7 @@ function stopDebugger() { module.exports = (_neoModules) => { neoModules = _neoModules; + ipc = new IPC.IPC(neoModules.userData.config.neoRuntimeIPC.socketFile, eventEmitter); return { event: eventEmitter, modes: listModes, diff --git a/src/UserData/index.js b/src/UserData/index.js index e5318c9..704c5d5 100644 --- a/src/UserData/index.js +++ b/src/UserData/index.js @@ -40,6 +40,9 @@ function ensureMainConfig() { if (config.DiscoveryServer.address == null) { config.DiscoveryServer.address = "https://erj46s.deta.dev"; } if (config.DiscoveryServer.broadcastSelf == null) { config.DiscoveryServer.broadcastSelf = false; } + if (config.neoRuntimeIPC == null) { config.neoRuntimeIPC = {}; } + if (config.neoRuntimeIPC.socketFile == null) { config.neoRuntimeIPC.socketFile = "/tmp/neo_runtime.sock"; } + fse.writeFileSync(__datadir + "/config/config.ini", ini.encode(config)) } diff --git a/src_frontend/Components/MainControls/ControlComponents.svelte b/src_frontend/Components/MainControls/ControlComponents.svelte index 5f6d165..65bd1c4 100644 --- a/src_frontend/Components/MainControls/ControlComponents.svelte +++ b/src_frontend/Components/MainControls/ControlComponents.svelte @@ -45,7 +45,7 @@ } name = name.replace("variable/", ""); - switch (value.var_type) { + switch (value.type) { case "COLOR": if (value.value == null) { delete colorVariables[name]; -- cgit v1.2.3 From 8fe3b246a12d409b0819b574a050b64ca96ce251 Mon Sep 17 00:00:00 2001 From: jakobst1n Date: Sun, 3 Oct 2021 18:36:12 +0200 Subject: :boom: Change paths to be in normal linux locations --- app.js | 27 +++--- bin/install.sh | 142 +++++++------------------------- bin/luxcena-neo-cli.sh | 41 ++++++++- bin/luxcena-neo.service | 6 +- bin/luxcena-neo.sh | 4 +- src/NeoRuntimeManager/RuntimeProcess.js | 4 +- src/NeoRuntimeManager/index.js | 8 +- src/SSLCert/index.js | 6 +- src/SelfUpdater/index.js | 4 +- src/SocketIO/index.js | 4 +- src/UserData/index.js | 48 +++++------ 11 files changed, 124 insertions(+), 170 deletions(-) (limited to 'src/NeoRuntimeManager/index.js') diff --git a/app.js b/app.js index 0196645..30146ea 100644 --- a/app.js +++ b/app.js @@ -2,22 +2,27 @@ let fse = require("fs-extra"); let events = require('events'); // Firstly we set up all globals, check that the usrData dir exists, if not, we run the setup -let userDir = "/home/lux-neo"; -if (process.argv.length >= 3) { userDir = process.argv[2]; } -if (!fse.existsSync(userDir + "/userdata/")) { +global.__appdir = "/opt/luxcena-neo"; +global.__configdir = "/etc/luxcena-neo"; +global.__datadir = "/var/luxcena-neo"; +global.__logdir = "/var/log/luxcena-neo"; + +if ((process.argv.length >= 3) && (process.argv[2] == "dev")) { + global.__appdir = __dirname; + global.__configdir = __dirname + "/tmp/config"; + global.__datadir = __dirname + "/tmp/userdata"; + global.__logdir = __dirname + "/tmp/logs"; +} +if (!fse.existsSync(global.__datadir)) { console.log(`CRITICAL UserDir not found '${userDir}'! Exiting...`); process.exit(1); } -// Global path variables -global.__basedir = __dirname + ""; -global.__datadir = userDir + "/userdata"; -global.__logdir = userDir + "/logs"; // global eventEmitter global.__event = new events.EventEmitter(); // Secondly we setup the logger, -let logger = require("./src/logger"); +let logger = require("./src/Logger"); logger.info("Starting Luxcena-Neo..."); let neoModules = {}; @@ -33,14 +38,14 @@ let express = require("express"); let https = require("https"); let app = express(); let server = https.createServer({ - key: fse.readFileSync(__datadir + "/config/certs/privkey.pem"), - cert: fse.readFileSync(__datadir + "/config/certs/cert.pem") + key: fse.readFileSync(__configdir + "/certs/privkey.pem"), + cert: fse.readFileSync(__configdir + "/certs/cert.pem") }, app ); let io = require("socket.io")(server); require("./src/SocketIO")(neoModules, io); -app.use("/", express.static(__basedir + "/public")); +app.use("/", express.static(__appdir + "/public")); server.listen(neoModules.userData.config.HTTP.port, () => { let host = server.address().address; diff --git a/bin/install.sh b/bin/install.sh index cd4f07b..a7b811b 100755 --- a/bin/install.sh +++ b/bin/install.sh @@ -5,9 +5,6 @@ printf '%s\n' "Luxcena-neo Installer" tput sgr0 printf '\e[93m%s\e[0m\n\n' "---------------------" -LOG="/tmp/luxcena-neo.install.log" -echo "Starting Luxcena-neo installer..." > $LOG - if [ "$EUID" -ne 0 ]; then echo "You need to run this script as root." echo "Try running with 'sudo ./bin/install.sh'" @@ -16,139 +13,56 @@ fi function die() { tput setaf 1 - printf "\n\nInstall failed.\n" - printf "Check the logfile at '/tmp/lucxena-neo.install.log'.\n" - printf "Use this command to see the last 30 lines of the file;\n" - printf " tail -n 30 /tmp/luxcena-neo.install.log" + printf "\n\nInstall failed, successfull steps not reversed.\n" tput sgr0 exit 1 } -function dlgYN() { - tput sc - tput setaf 4 - printf "$1 (y/n)? " - while : - do - read -n 1 -p "" YNQuestionAnswer - if [[ $YNQuestionAnswer == "y" ]]; then - tput rc; tput el - printf ". $1?: \e[0;32mYes\e[0m\n" - tput sc - eval $2=1 # Set parameter 2 of input to the return value - break - elif [[ $YNQuestionAnswer == "n" ]]; then - tput rc; tput el - printf ". $1?: \e[0;31mNo\e[0m\n" - eval $2=0 # Set parameter 2 of input to the return value - break - fi - done -} - -# Update system -dlgYN ". Update your system" res -if [ $res -eq 1 ]; then - tput sc - apt-get -y update &>> $LOG || die - apt-get -y upgrade &>> $LOG || die - tput rc; tput ed -fi - -# Install packages -dlgYN ". Install required packages" res -if [ $res -eq 1 ]; then - tput sc - apt-get -y install nodejs scons python-dev swig &>> $LOG || die - if [ $? -eq 0 ]; then - tput rc; tput ed - printf "✓" - else - printf "\nInstall failed.\n" - exit 1 - fi -else - tput setaf 2 - printf " We are now assuming that all the following packages exists on your system:\n" - printf " nodejs scons python-dev swig\n" - tput sgr0 -fi - -# Install led-library -dlgYN ". Install jgarff's rpi_ws281x library" res -if [ $res -eq 1 ]; then - tput sc - git clone https://github.com/jgarff/rpi_ws281x /tmp/rpi_ws281x # TODO CHANGE PATH - python /tmp/rpi_ws281x/python/setup.py install # TODO CHANGE PAHT - if [ $? -eq 0 ]; then - tput rc; tput ed - printf "✓" - else - printf "\nInstall failed.\n" - exit 1 - fi -fi - -tput setaf 4 -printf ". Installing the app itself...\n" -tput sgr0 - # Create user 'luxcena-neo' tput setaf 8 -printf '%s\n' " - Creating user 'lux-neo'..." +printf '%s\n' "- Creating user 'lux-neo'..." tput sgr0 username="lux-neo" egrep "^$username" /etc/passwd >/dev/null if [ $? -eq 0 ]; then - echo "User already exists, continuing..." + echo "User already exists, continuing..." else - #pass=$(perl -e 'print crypt($ARGV[0], "password")' $password) - useradd -m $username &>> $LOG || die + useradd -m $username || die fi +usermod -a -G gpio $username +usermod -a -G spi $username # First we make our directories tput setaf 8 -printf '%s\n' " - Making app-dir (/bin/luxcena-neo)..." -tput sgr0 -userDir=$(eval echo "~$username") -#mkdir -p "$userDir/install" &>> $LOG || die -#chown $username:$username "$userDir/install" &>> $LOG || die -mkdir -p "$userDir/src" &>> $LOG || die -chown $username:$username "$userDir/src" &>> $LOG || die -mkdir -p "$userDir/userdata" &>> $LOG || die -chown $username:$username "$userDir/userdata" &>> $LOG || die - -# Third we copy the source into the correct swap-folder -tput setaf 8 -printf '%s\n' " - Copying sourceCode to app-dir..." +printf '%s\n' "- Making directories..." tput sgr0 -cp -r . "$userDir/src" &>> $LOG || die -chown -R $username:$username "$userDir/src" &>> $LOG || die - -# fourth we run npm i -tput setaf 8 -printf '%s\n' " - Running npm i..." -tput sgr0 -tput sc -export NODE_ENV=production &>> $LOG || die -runuser -l $username -c 'npm --prefix ~/src install ~/src --only=production' &>> $LOG || die # This is probably a bit overkill to have --only=... but better safe than sorry? -tput rc; tput ed +[ -d "/opt/luxcena-neo/" ] && echo "Seems like luxcena-neo is already installed, please do update instead" && die +mkdir -p "/opt/luxcena-neo" || die +chown $username:$username "/opt/luxcena-neo" || die +mkdir -p "/var/luxcena-neo" || die +chown $username:$username "/var/luxcena-neo" || die +mkdir -p "/etc/luxcena-neo" || die +chown $username:$username "/etc/luxcena-neo" || die +mkdir -p "/var/log/luxcena-neo" || die +chown $username:$username "/var/log/luxcena-neo" || die + +printf '%s' "Which branch do you want to install (default: master)? " +read BRANCH +if [ -z "$BRANCH" ]; then + BRANCH="master" +fi -# fourth we copy the cli to our bin folder +# Get source code tput setaf 8 -printf '%s\n' " - Adding cli-script..." +printf '%s\n' "- Fetch source code..." tput sgr0 -cp bin/luxcena-neo-cli.sh /usr/bin/luxcena-neo-cli.sh &>> $LOG || die -ln -sf /usr/bin/luxcena-neo-cli.sh /usr/bin/lux-neo &>> $LOG || die -tput rc; tput ed +runuser -l $username -c "git clone -b $BRANCH https://github.com/jakobst1n/luxcena-neo /opt/luxcena-neo/" || die -# Fifth we add the service files +# Install all packages, build the app, and prepare everything tput setaf 8 -printf '%s\n' " - Adding service-file to systemd..." +printf '%s\n' "- Running installer (updater) from newly fetched source code..." tput sgr0 -cp bin/luxcena-neo.service /etc/systemd/system/luxcena-neo.service &>> $LOG || die -systemctl daemon-reload &>> $LOG || die +/opt/luxcena-neo/bin/luxcena-neo-cli.sh update || die # Installation is done! printf '\n\e[5m%s\e[0m\n' "🎉Luxcena-Neo is now installed🎉" -printf 'You can now delete this folder' diff --git a/bin/luxcena-neo-cli.sh b/bin/luxcena-neo-cli.sh index defb766..b3c6553 100755 --- a/bin/luxcena-neo-cli.sh +++ b/bin/luxcena-neo-cli.sh @@ -65,14 +65,49 @@ if [ "$action" == "update" ]; then exit 1 fi + # Stop the service if it is running already systemctl stop luxcena-neo - runuser -l 'lux-neo' -c 'git -C ~/src pull' + # Go to source code directory + WDIR="/opt/luxcena-neo" + #cd "$WDIR" + + # Fetch newest changes on branch + runuser -l 'lux-neo' -c "git -C $WDIR pull" || die + + # Add node repo + curl -fsSL https://deb.nodesource.com/setup_14.x | bash - || die + + # Make sure nodejs and prerequisites is installed + apt install nodejs python-pip || die + + # Make sure we have python virtualenv installed + pip3 install virtualenv || die + + # Create and configure python virtualenv + runuser -l 'lux-neo' -c "rm -rf $WDIR/NeoRuntime/Runtime/venv" || die + runuser -l 'lux-neo' -c "virtualenv -p /usr/bin/python3 $WDIR/NeoRuntime/Runtime/venv" || die + runuser -l 'lux-neo' -c "source $WDIR/NeoRuntime/Runtime/venv/bin/activate && pip install rpi_ws281x" || die + + # Build and run all npm scripts if [ "$2" != "skipNode" ]; then - runuser -l 'lux-neo' -c 'export NODE_ENV=production; npm --prefix ~/src install ~/src --only=production' + runuser -l 'lux-neo' -c "export NODE_ENV=development; npm --prefix $WDIR install $WDIR" || die fi + ##runuser -l 'lux-neo' -c "cd $WDIR && npm run build:frontend" || die + ##runuser -l 'lux-neo' -c "cd $WDIR && npm run build:fontawesome" || die + ##runuser -l 'lux-neo' -c "cd $WDIR && npm run build:dialog-polyfill" || die + runuser -l 'lux-neo' -c "npm --prefix \"$WDIR\" run build:frontend" || die + runuser -l 'lux-neo' -c "npm --prefix \"$WDIR\" run build:fontawesome" || die + runuser -l 'lux-neo' -c "npm --prefix \"$WDIR\" run build:dialog-polyfill" || die + + + # Install new cli script + cp /opt/luxcena-neo/bin/luxcena-neo-cli.sh /usr/bin/luxcena-neo-cli.sh || die + + # Install updated systemd script + cp /opt/luxcena-neo/bin/luxcena-neo.service /etc/systemd/system/luxcena-neo.service || die + systemctl daemon-reload || die - cp /home/lux-neo/src/bin/luxcena-neo-cli.sh /usr/bin/luxcena-neo-cli.sh printf "Update complete.\n" systemctl start luxcena-neo exit 0 diff --git a/bin/luxcena-neo.service b/bin/luxcena-neo.service index efea1ad..b4115be 100644 --- a/bin/luxcena-neo.service +++ b/bin/luxcena-neo.service @@ -2,13 +2,13 @@ Description=Luxcena Neo [Service] -ExecStart=/home/lux-neo/src/bin/luxcena-neo.sh +ExecStart=/opt/luxcena-neo/bin/luxcena-neo.sh Restart=always RestartSec=10 Environment=PATH=/usr/bin:/usr/local/bin -Environment=NODE_ENV=production -WorkingDirectory=/home/lux-neo/src/ +Environment=NODE_ENV=development +WorkingDirectory=/opt/luxcena-neo/ [Install] WantedBy=multi-user.target diff --git a/bin/luxcena-neo.sh b/bin/luxcena-neo.sh index fc41f75..a861d87 100755 --- a/bin/luxcena-neo.sh +++ b/bin/luxcena-neo.sh @@ -5,5 +5,5 @@ # the server needs root as well. #runuser -l pi -c "export NODE_ENV=production; node ~/luxcena-neo-install/src/app.js" -export NODE_ENV=production -node /home/lux-neo/src/app.js >> /home/lux-neo/logs/service.log +export NODE_ENV=development +node /opt/luxcena-neo/app.js >> /var/log/luxcena-neo/service.log diff --git a/src/NeoRuntimeManager/RuntimeProcess.js b/src/NeoRuntimeManager/RuntimeProcess.js index 60f6a28..0058382 100644 --- a/src/NeoRuntimeManager/RuntimeProcess.js +++ b/src/NeoRuntimeManager/RuntimeProcess.js @@ -32,8 +32,8 @@ class RuntimeProcess { "python3", [ "-u", // This makes us able to get real-time output - `${__basedir}/NeoRuntime/Runtime/neo_runtime.py`, - `--strip-config="${__datadir}/config/strip.ini"`, + `${__appdir}/NeoRuntime/Runtime/neo_runtime.py`, + `--strip-config="${__configdir}/strip.ini"`, `--mode-path="${this.modePath}"`, `--mode-entry=script` ] diff --git a/src/NeoRuntimeManager/index.js b/src/NeoRuntimeManager/index.js index 62acb8a..e4718e4 100644 --- a/src/NeoRuntimeManager/index.js +++ b/src/NeoRuntimeManager/index.js @@ -8,7 +8,7 @@ const fs = require("fs"); const fsPromises = fs.promises; const RuntimeProcess = require("./RuntimeProcess"); -let logger = require(__basedir + "/src/logger"); +let logger = require(__appdir + "/src/Logger"); const EventEmitter = require('events'); /** @type {object} this should be a pointer to a object referencing all neoModules (see app.js) */ @@ -51,7 +51,7 @@ function isMode(path) { */ function listModes() { let modeDirs = [ - ["builtin/", fs.readdirSync(__basedir + "/NeoRuntime/builtin")], + ["builtin/", fs.readdirSync(__appdir + "/NeoRuntime/builtin")], ["remote/", fs.readdirSync(__datadir + "/remoteCode")], ["user/", fs.readdirSync(__datadir + "/userCode")] ] @@ -167,7 +167,7 @@ function getModePath(modeId) { let location = path.splice(0, 1).toString(); if (location === "user") { path = __datadir + "/userCode/" + path.join("/"); } if (location === "remote") { path = __datadir + "/remoteCode/" + path.join("/"); } - if (location === "builtin") { path = __basedir + "/NeoRuntime/builtin/" + path.join("/"); } + if (location === "builtin") { path = __appdir + "/NeoRuntime/builtin/" + path.join("/"); } return path; } @@ -306,4 +306,4 @@ module.exports = (_neoModules) => { startDebugger, stopDebugger, saveModeCode, startMode, stopMode, restartMode } -}; \ No newline at end of file +}; diff --git a/src/SSLCert/index.js b/src/SSLCert/index.js index 6dad579..d235c9b 100644 --- a/src/SSLCert/index.js +++ b/src/SSLCert/index.js @@ -7,7 +7,7 @@ * @author jakobst1n. * @since 14.16.2019 */ - let logger = require(__basedir + "/src/logger"); + let logger = require(__appdir + "/src/Logger"); const fs = require("fs"); const { execSync } = require("child_process"); @@ -20,7 +20,7 @@ var neoModules; class CertMon { constructor(configPath, certPath, httpsConfig) { - this.certPath = __datadir + "/config/certs/"; + this.certPath = __configdir + "/certs/"; let valid = this.checkValidity(); if (!valid) { @@ -141,4 +141,4 @@ module.exports = (_neoModules) => { neoModules = _neoModules; return new CertMon(); }; - \ No newline at end of file + diff --git a/src/SelfUpdater/index.js b/src/SelfUpdater/index.js index 3c5546b..5a9baa3 100644 --- a/src/SelfUpdater/index.js +++ b/src/SelfUpdater/index.js @@ -2,14 +2,14 @@ let fs = require("fs-extra"); let url = require("url"); let request = require('request'); let exec = require("child_process").exec; -let logger = require(__basedir + "/src/logger"); +let logger = require(__appdir + "/src/Logger"); let neoModules; class VersionChecker { constructor() { - this.CPackageJson = JSON.parse(fs.readFileSync(__basedir + "/package.json")); + this.CPackageJson = JSON.parse(fs.readFileSync(__appdir + "/package.json")); this.version = this.CPackageJson["version"]; this.repoLink = this.CPackageJson["repository"]["url"]; diff --git a/src/SocketIO/index.js b/src/SocketIO/index.js index fdaad56..e20460d 100644 --- a/src/SocketIO/index.js +++ b/src/SocketIO/index.js @@ -8,7 +8,7 @@ * @since 19.12.2019 */ -let logger = require(__basedir + "/src/logger"); +let logger = require(__appdir + "/src/Logger"); var exec = require('child_process').exec; var CryptoJS = require("crypto-js"); let fs = require("fs"); @@ -350,4 +350,4 @@ module.exports = (_neoModules, io) => { authorizedNamespace: createAuthorizedNamespace(io) } }; - \ No newline at end of file + diff --git a/src/UserData/index.js b/src/UserData/index.js index e5318c9..4684a75 100644 --- a/src/UserData/index.js +++ b/src/UserData/index.js @@ -6,7 +6,7 @@ * @since 19.12.2019 */ -let logger = require(__basedir + "/src/logger"); +let logger = require(__appdir + "/src/Logger"); let fse = require("fs-extra"); let ini = require('ini'); @@ -16,7 +16,7 @@ let neoModules; * This method will ensure that all required fields are in config.ini */ function ensureMainConfig() { - var config = ini.decode(fse.readFileSync(__datadir + "/config/config.ini", 'utf-8')) + var config = ini.decode(fse.readFileSync(__configdir + "/config.ini", 'utf-8')) if (config.instanceName == null) { config.instanceName = "neoStrip"; } if (config.activeMode == null) { config.activeMode = "builtin/static"; } @@ -40,14 +40,14 @@ function ensureMainConfig() { if (config.DiscoveryServer.address == null) { config.DiscoveryServer.address = "https://erj46s.deta.dev"; } if (config.DiscoveryServer.broadcastSelf == null) { config.DiscoveryServer.broadcastSelf = false; } - fse.writeFileSync(__datadir + "/config/config.ini", ini.encode(config)) + fse.writeFileSync(__configdir + "/config.ini", ini.encode(config)) } /** * This method will ensure that all required fields are in config.ini */ function ensureStripConfig() { - var config = ini.decode(fse.readFileSync(__datadir + "/config/strip.ini", 'utf-8')) + var config = ini.decode(fse.readFileSync(__configdir + "/strip.ini", 'utf-8')) if (config.DEFAULT == null) { config.DEFAULT = {}; } if (config.DEFAULT.led_pin == null) { config.DEFAULT.led_pin = 18; } @@ -58,7 +58,7 @@ function ensureStripConfig() { if (config.DEFAULT.segments == null) { config.DEFAULT.segments = ""; } if (config.DEFAULT.matrix == null) { config.DEFAULT.matrix = ""; } - fse.writeFileSync(__datadir + "/config/strip.ini", ini.encode(config)) + fse.writeFileSync(__configdir + "/strip.ini", ini.encode(config)) } /** @@ -70,24 +70,24 @@ function init() { logger.info("Ensuring all folder in UserDir exists..."); fse.ensureDirSync(__datadir + "/"); - fse.ensureDirSync(__datadir + "/config/"); - fse.ensureDirSync(__datadir + "/config/certs"); + fse.ensureDirSync(__configdir); + fse.ensureDirSync(__configdir + "/certs"); fse.ensureDirSync(__datadir + "/userCode/"); fse.ensureDirSync(__datadir + "/remoteCode/"); // Generate config-files - if (!fse.existsSync(__datadir + "/config/config.ini")) { - fse.closeSync(fse.openSync(__datadir + "/config/config.ini", 'w')); + if (!fse.existsSync(__configdir + "/config.ini")) { + fse.closeSync(fse.openSync(__configdir + "/config.ini", 'w')); } ensureMainConfig(); - if (!fse.existsSync(__datadir + "/config/strip.ini")) { - fse.closeSync(fse.openSync(__datadir + "/config/strip.ini", 'w')); + if (!fse.existsSync(__configdir + "/strip.ini")) { + fse.closeSync(fse.openSync(__configdir + "/strip.ini", 'w')); } ensureStripConfig(); - if (!fse.existsSync(__datadir + "/config/users.ini")) { - fse.writeFileSync(__datadir + "/config/users.ini", ini.encode({ + if (!fse.existsSync(__configdir + "/users.ini")) { + fse.writeFileSync(__configdir + "/users.ini", ini.encode({ "neo": { "password": "5adbc90fb4716fff62d9cf634837e22f29b011803ba29cee51f921b920fa941651737bd15d00dc72e4cbeee5e64e06ec99cc50ea917285a029797a98740cce0f", "salt": "59b6de1040f3ae3c63de984ca5d61ef46f41dc6ecead3a9d5dab69f0bb3636aa49017e179b74dbcdb407f62bc139a7d55aa78fe2bbdd5327609ea124b2fa03b1" @@ -193,11 +193,11 @@ function getFullConfig(file, addSetters=true) { * @return {object} Standardform return object */ function saveUser(username, salt, password) { - let config = ini.decode(fse.readFileSync(__datadir + "/config/users.ini", 'utf-8')) + let config = ini.decode(fse.readFileSync(__configdir + "/users.ini", 'utf-8')) config[username] = {} config[username].salt = salt config[username].password = password - fse.writeFileSync(__datadir + "/config/users.ini", ini.encode(config)) + fse.writeFileSync(__configdir + "/users.ini", ini.encode(config)) return {success: true} } @@ -207,7 +207,7 @@ function getFullConfig(file, addSetters=true) { * @return {object} with username, salt and hash properties. */ function getUser(username) { - let config = ini.decode(fse.readFileSync(__datadir + "/config/users.ini", 'utf-8')) + let config = ini.decode(fse.readFileSync(__configdir + "/users.ini", 'utf-8')) if (Object.prototype.hasOwnProperty.call(config, username)) { return {...config[username], username: username} } @@ -220,7 +220,7 @@ function getUser(username) { * @return {array} usernames */ function getUsers() { - let config = ini.decode(fse.readFileSync(__datadir + "/config/users.ini", "utf-8")); + let config = ini.decode(fse.readFileSync(__configdir + "/users.ini", "utf-8")); let users = []; for (const username of Object.keys(config)) { users.push(username); @@ -234,11 +234,11 @@ function getUsers() { * @return {object} Standardform success object. */ function deleteUser(username) { - let config = ini.decode(fse.readFileSync(__datadir + "/config/users.ini", 'utf-8')) + let config = ini.decode(fse.readFileSync(__configdir + "/users.ini", 'utf-8')) if (config.length <= 1) { return {success: false, reason: "cannot delete only user"}; } if (!Object.prototype.hasOwnProperty.call(config, username)) { return {success: false, reason: "user not found", detail: username}; } delete config[username]; - fse.writeFileSync(__datadir + "/config/users.ini", ini.encode(config)); + fse.writeFileSync(__configdir + "/users.ini", ini.encode(config)); return {success: true} } @@ -254,7 +254,7 @@ function deleteUser(username) { function createNewUserMode(name, template) { source_script = null; if ((template === "template/base") || (template === "") || (template == null)) { - source_script = __basedir + "/NeoRuntime/special/template_base/"; + source_script = __appdir + "/NeoRuntime/special/template_base/"; } else { source_script = neoModules.neoRuntimeManager.getModePath(template); } @@ -310,7 +310,7 @@ module.exports = (_neoModules) => { }, strip: { get: () => { - let c = getFullConfig(`${__datadir}/config/strip.ini`, addSetters=false); + let c = getFullConfig(`${__configdir}/strip.ini`, addSetters=false); c.DEFAULT.matrix = JSON.parse(c.DEFAULT.matrix); c.DEFAULT.segments = c.DEFAULT.segments.split(" "); return c.DEFAULT; @@ -318,13 +318,13 @@ module.exports = (_neoModules) => { set: (c) => { c.segments = c.segments.join(" "); c.matrix = JSON.stringify(c.matrix); - return saveConfig(`${__datadir}/config/strip.ini`, {DEFAULT: c}, removeSetters=false); + return saveConfig(`${__configdir}/strip.ini`, {DEFAULT: c}, removeSetters=false); }, }, - config: getFullConfig(`${__datadir}/config/config.ini`), + config: getFullConfig(`${__configdir}/config.ini`), mode: { create: createNewUserMode, delete: deleteUserMode } } -}; \ No newline at end of file +}; -- cgit v1.2.3