From 9e7ee17fdef324029ce9815e25def9dc6f87e271 Mon Sep 17 00:00:00 2001 From: M Starch Date: Thu, 29 Jul 2021 23:05:39 -0700 Subject: [PATCH 1/9] lestarch: adding in sequence builder tab prototype --- src/fprime_gds/flask/app.py | 8 ++ src/fprime_gds/flask/default_settings.py | 1 + src/fprime_gds/flask/sequence.py | 92 +++++++++++++++++++ src/fprime_gds/flask/static/addons/enabled.js | 1 + .../addons/sequencer/addon-templates.js | 44 +++++++++ .../flask/static/addons/sequencer/addon.js | 76 +++++++++++++++ src/fprime_gds/flask/static/index.html | 13 +-- .../flask/static/js/vue-support/command.js | 13 ++- .../flask/static/js/vue-support/fptable.js | 4 +- .../flask/static/js/vue-support/tabetc.js | 2 +- 10 files changed, 243 insertions(+), 11 deletions(-) create mode 100644 src/fprime_gds/flask/sequence.py create mode 100644 src/fprime_gds/flask/static/addons/sequencer/addon-templates.js create mode 100644 src/fprime_gds/flask/static/addons/sequencer/addon.js diff --git a/src/fprime_gds/flask/app.py b/src/fprime_gds/flask/app.py index afa460f9..d361c624 100644 --- a/src/fprime_gds/flask/app.py +++ b/src/fprime_gds/flask/app.py @@ -21,6 +21,7 @@ import fprime_gds.flask.json import fprime_gds.flask.logs import fprime_gds.flask.updown +import fprime_gds.flask.sequence from . import components @@ -122,6 +123,13 @@ def construct_app(): "/download/files/", resource_class_args=[pipeline.files.downlinker], ) + api.add_resource( + fprime_gds.flask.sequence.SequenceCompiler, + "/sequence", + resource_class_args=[app.config["DICTIONARY"], app.config["UPLOADED_UPLINK_DEST"], pipeline.files.uplinker, + app.config["REMOTE_SEQ_DIRECTORY"]], + ) + # Optionally serve log files if app.config["SERVE_LOGS"]: api.add_resource( diff --git a/src/fprime_gds/flask/default_settings.py b/src/fprime_gds/flask/default_settings.py index 0b39488a..14e345f5 100644 --- a/src/fprime_gds/flask/default_settings.py +++ b/src/fprime_gds/flask/default_settings.py @@ -24,6 +24,7 @@ SERVE_LOGS = os.environ.get("SERVE_LOGS", "YES") == "YES" UPLOADED_UPLINK_DEST = uplink_dir UPLOADS_DEFAULT_DEST = uplink_dir +REMOTE_SEQ_DIRECTORY = "/seq" MAX_CONTENT_LENGTH = 32 * 1024 * 1024 # Max length of request is 32MiB # Gds config setup diff --git a/src/fprime_gds/flask/sequence.py b/src/fprime_gds/flask/sequence.py new file mode 100644 index 00000000..a5b42624 --- /dev/null +++ b/src/fprime_gds/flask/sequence.py @@ -0,0 +1,92 @@ +#### +# +#### +from pathlib import Path +import re +import sys +from io import StringIO +import flask_restful +import flask_restful.reqparse + +from fprime_gds.common.tools.seqgen import generateSequence, SeqGenException + + +class StdioTheif(object): + """ + This class consumes all standard out and error production produced with-in a context block (with :) capturing it. + """ + def __init__(self): + """ Setup our capture devices """ + self.io = StringIO() + self.output = "" + + def write(self, *args, **kwargs): + """ Write tee for all possible inputs """ + stripped_msg = " ".join(args) + self.io.write(stripped_msg, **kwargs) + + def __enter__(self): + """ Entry overwrite """ + sys.stdout = self + sys.stderr = self + return None + + def __exit__(self, *_): + """ No worries here """ + sys.stdout = sys.__stdout__ + sys.stderr = sys.__stderr__ + self.io.seek(0) + self.output = self.io.read() + + +class SequenceCompiler(flask_restful.Resource): + def __init__(self, dictionary, tempdir, uplinker, destination): + self.dictionary = dictionary + self.tempdir = Path(tempdir) + self.uplinker = uplinker + self.destination = destination + + self.parser = flask_restful.reqparse.RequestParser() + self.parser.add_argument( + "key", required=True, help="Protection key. Must be: 0xfeedcafe." + ) + self.parser.add_argument( + "name", required=True, help="Name of sequence file to create" + ) + self.parser.add_argument( + "text", required=True, help="Text of sequence file to create" + ) + self.parser.add_argument( + "uplink", required=True, help="Text of sequence file to create" + ) + + def put(self): + args = self.parser.parse_args() + key = args.get("key", None) + name = args.get("name", "") + text = args.get("text", "") + uplink = args.get("uplink", "false") == "true" + if key is None or int(key, 0) != 0xFEEDCAFE: + flask_restful.abort(403, message="{} is invalid command key. Supply 0xfeedcafe to run command.".format(key)) + elif not re.match(".*\.seq", name) or Path(name).name != name: + flask_restful.abort(403, message={"error": "Supply filename with .seq suffix", "type": "error"}) + temp_seq_path = self.tempdir / Path(name).name + temp_bin_path = temp_seq_path.with_suffix(".bin") + messages = "" + try: + with open(temp_seq_path, "w") as file_handle: + file_handle.write(text) + thief = StdioTheif() + with thief: + generateSequence(temp_seq_path, temp_bin_path, self.dictionary, 0xFFFF) + messages += thief.output + if uplink: + destination = Path(self.destination) / temp_bin_path.name + self.uplinker.enqueue(str(temp_bin_path), str(destination)) + messages += f"Uplinking to {destination}. Please confirm uplink EVRs before running.\n" + except OSError as ose: + flask_restful.abort(403, message={"error": str(ose), "type": "error"}) + except SeqGenException as exc: + flask_restful.abort(403, message={"error": str(exc), "type": "validation"}) + temp_seq_path.unlink(missing_ok=True) + return {"message": messages} \ No newline at end of file diff --git a/src/fprime_gds/flask/static/addons/enabled.js b/src/fprime_gds/flask/static/addons/enabled.js index cc6059b6..5ccdb37f 100644 --- a/src/fprime_gds/flask/static/addons/enabled.js +++ b/src/fprime_gds/flask/static/addons/enabled.js @@ -1,2 +1,3 @@ // Add addon imports here, try used to prevent errors from crashing GDS import "./image-display/addon.js" +import "./sequencer/addon.js" diff --git a/src/fprime_gds/flask/static/addons/sequencer/addon-templates.js b/src/fprime_gds/flask/static/addons/sequencer/addon-templates.js new file mode 100644 index 00000000..5b1fec48 --- /dev/null +++ b/src/fprime_gds/flask/static/addons/sequencer/addon-templates.js @@ -0,0 +1,44 @@ +export let sequencer_template = ` +
+
+

Command Sequencer

+
+
+ +
+ +
{{ (messages.error) ? messages.error : "Supply filename ending with .seq" }}
+
+
+ + +
+
+ + + +
+
+
+
+ + + + +
+ +
+
+ +
+ + Sequence compilation output + +
+`; \ No newline at end of file diff --git a/src/fprime_gds/flask/static/addons/sequencer/addon.js b/src/fprime_gds/flask/static/addons/sequencer/addon.js new file mode 100644 index 00000000..350df3d8 --- /dev/null +++ b/src/fprime_gds/flask/static/addons/sequencer/addon.js @@ -0,0 +1,76 @@ +import {sequencer_template} from "./addon-templates.js"; +import {_loader} from "../../js/loader.js"; + +function modify_paste(paste, skip=false) { + let lines = paste.split(/\r?\n/); + for (let i = (skip) ? 0 : 1; i < lines.length; i++) { + let line = lines[i]; + if (line != "" && line[0] != ';' && !line.match(/^R\d{2}:\d{2}:\d{2}(.\d{3})?[, ]?.*/)) { + lines[i] = "R00:00:00.000 " + line; + } + } + return lines.join("\n"); +} + +Vue.component("sequencer", { + data: function () { + return { + sequence: { + name: "", + text: "; Input sequence here\nR00:00:00 cmdDisp.CMD_NO_OP", + }, + messages: { + validation: "", + error: "" + }, + active: false, + builder: false, + // Paste temporary variables + previous: "", + paste: "" + }; + }, + template: sequencer_template, + methods: { + onPaste(event) { + let paste = (event.clipboardData || window.clipboardData).getData('text'); + this.previous = this.sequence.text; + this.paste = paste; + }, + setSequence(file) { + const fileReader = new FileReader(); + const _self = this; + fileReader.onload = function(event) { + const file_text = event.target.result; + _self.sequence.text = file_text; + _self.sequence.name = file.name; + }; + fileReader.readAsText(file); + }, + sendSequence(uplink) { + let _self = this; + this.active = true; + this.messages.validation = ""; + this.messages.error = ""; + _loader.load("/sequence", "PUT", + { + "key": 0xfeedcafe, + "name": this.sequence.name, + "text": this.sequence.text, + "uplink": uplink ? "true" : "false" + } + ).then(function(response) { + _self.active = false; + let message = response.message || "No server response"; + _self.messages.validation = message; + }) + .catch(function(response) { + _self.active = false; + let parsed = JSON.parse(response); + let message = parsed.message.error || parsed; + let type = parsed.message.type || "error"; + _self.messages[type] = message; + }); + } + }, +}); \ No newline at end of file diff --git a/src/fprime_gds/flask/static/index.html b/src/fprime_gds/flask/static/index.html index a03b2537..4f97708c 100644 --- a/src/fprime_gds/flask/static/index.html +++ b/src/fprime_gds/flask/static/index.html @@ -86,13 +86,13 @@
- +
@@ -176,12 +176,12 @@