diff options
Diffstat (limited to 'src/NeoRuntimeManager/RuntimeProcess.js')
-rw-r--r-- | src/NeoRuntimeManager/RuntimeProcess.js | 150 |
1 files changed, 150 insertions, 0 deletions
diff --git a/src/NeoRuntimeManager/RuntimeProcess.js b/src/NeoRuntimeManager/RuntimeProcess.js new file mode 100644 index 0000000..60f6a28 --- /dev/null +++ b/src/NeoRuntimeManager/RuntimeProcess.js @@ -0,0 +1,150 @@ +let fs = require("fs-extra"); +let spawn = require("child_process"); + +class RuntimeProcess { + + constructor(_modePath, _onVarChange, _eventEmitter) { + this.modePath = _modePath; + this.logfile = `${this.modePath}/mode.log`; + + this.stdout = ""; + this.stderr = ""; + + this.fl = false; + this.proc = null; + + this.isRunning = false; + this.exitCode = null; + + this.variables = {}; + this.globvars = {}; + this.onVarChange = _onVarChange; + this.eventEmitter = _eventEmitter; + } + + start() { + if (this.isRunning) { + console.log("PROCESS ALREADY RUNNING"); + return; + } + this.isRunning = true; + this.proc = spawn.spawn( + "python3", + [ + "-u", // This makes us able to get real-time output + `${__basedir}/NeoRuntime/Runtime/neo_runtime.py`, + `--strip-config="${__datadir}/config/strip.ini"`, + `--mode-path="${this.modePath}"`, + `--mode-entry=script` + ] + ); + + this.proc.on('error', (err) => { + console.log(err); + }); + + fs.ensureFileSync(this.logfile); + 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); + } + }); + + this.proc.stdout.on('end', () => { + fs.appendFile(this.logfile, "\n"); + }); + + 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); + this.eventEmitter.emit("proc:stderr", stderr_str); + }); + + this.proc.stderr.on('end', () => { + fs.appendFile(this.logfile, "\n"); + }); + + this.proc.on('close', (code) => { + if (code) { + fs.appendFile(this.logfile, "\n====close====\nScript exited with code " + code.toString()); + } + this.eventEmitter.emit("proc:exit", 0); + this.isRunning = false; + this.exitCode = code; + }); + + } + + stop(restart=false) { + try { + if (restart) { + this.proc.once("close", () => { + setTimeout(() => this.start(), 500); + }); + } + this.proc.kill("SIGINT"); + } catch (err) { + 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 + */ +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 = RuntimeProcess; |