diff options
author | jakob.stendahl <jakob.stendahl@infomedia.dk> | 2022-12-17 21:31:41 +0100 |
---|---|---|
committer | Jakob Stendahl <jakob.stendahl@outlook.com> | 2022-12-17 21:31:41 +0100 |
commit | 1e588718a855ae2871a8841f6c6e621f49795454 (patch) | |
tree | 6599b3959554b307a571a73373114cb2d34a98ef /src/NeoRuntimeManager/index.cjs | |
parent | 6c37c28d7044a813fcde9ef80bf8852529b8305f (diff) | |
download | Luxcena-Neo-1e588718a855ae2871a8841f6c6e621f49795454.tar.gz Luxcena-Neo-1e588718a855ae2871a8841f6c6e621f49795454.zip |
Start moving to esm, work on updater
Diffstat (limited to 'src/NeoRuntimeManager/index.cjs')
-rw-r--r-- | src/NeoRuntimeManager/index.cjs | 339 |
1 files changed, 339 insertions, 0 deletions
diff --git a/src/NeoRuntimeManager/index.cjs b/src/NeoRuntimeManager/index.cjs new file mode 100644 index 0000000..81bd7e8 --- /dev/null +++ b/src/NeoRuntimeManager/index.cjs @@ -0,0 +1,339 @@ +/** + * 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.cjs"); +const IPC = require("./IPC.cjs"); +const logger = require("../Logger/index.cjs"); +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 {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 */ +let modeDebuggerActive = false; +/** @type {string} Should be the modeId the debugger is attached to */ +let modeDebuggerId = null; +/** @type {object} Handler for proc:start when debugger is active */ +let modeDebuggerProcStartHandler = null; +/** @type {object} The last received matrix setup */ +let matrix = null; +/** @type {object} intervall for sending current state */ +let debugModeStateEmitIntervall = null; + +eventEmitter.on("proc:exit", (code) => modeExitCode = code); +eventEmitter.on("matrix", (_matrix) => matrix = _matrix); + +/** + * 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(__appdir + "/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}".`); + + stopMode(); + + modeId = _modeId; + neoModules.userData.config.activeMode = modeId; + eventEmitter.emit("change", "mode", modeId); + + runtimeProcess = new RuntimeProcess(getModePath(_modeId), eventEmitter); + 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?"}; + } + return runtimeProcess.start(); +}; + +/** + * 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 = __appdir + "/NeoRuntime/builtin/" + path.join("/"); } + return path; +} + +/** + * 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 ipc.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; } + + 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}; + } +} + +/** + * Get all variables declared in mode + * + * @return {object} + */ +function getVariables() { + if (!modeRunning()) { return {}; } + return ipc.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; } + return ipc.sendCommand(IPC.COMMAND.SET_VAR, name, value); +} + +/** + * A function intented to be used in an interval to emit + * the current debug-state. + * + */ +function debugModeEmitState() { + eventEmitter.emit("debugger:state", { + mode: modeDebuggerId, + running: runtimeProcess.isRunning, + debugMode: modeDebuggerActive, + matrix: matrix + }); +} + +/** + * 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}`); + + if (modeDebuggerProcStartHandler == null) { + modeDebuggerProcStartHandler = eventEmitter.on("proc:start", () => { + setTimeout(() => { + ipc.sendCommand(IPC.COMMAND.SET_SEND_STRIP_BUF, true); + }, 2000); + }); + } else { + console.log(modeDebuggerProcStartHandler); + } + + if (debugModeStateEmitIntervall == null) { + debugModeStateEmitIntervall = setInterval(debugModeEmitState, 1000); + } + + modeDebuggerActive = true; + modeDebuggerId = debuggerModeId; + setTimeout(() => { + setMode(debuggerModeId); + }, 300); + 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; + eventEmitter.removeAllListeners("proc:start", modeDebuggerProcStartHandler); + modeDebuggerProcStartHandler = null; + + clearInterval(debugModeStateEmitIntervall); + debugModeStateEmitIntervall = null; + + ipc.sendCommand(IPC.COMMAND.SET_SEND_STRIP_BUF, false); + return {success: true} +} + +module.exports = (_neoModules) => { + neoModules = _neoModules; + ipc = new IPC.IPC(neoModules.userData.config.neoRuntimeIPC.socketFile, eventEmitter); + 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, + matrix + } +}; |