From 76b9b075a953ef57dd0bef645147c799532a2928 Mon Sep 17 00:00:00 2001 From: thomas-bc Date: Mon, 12 Jun 2023 18:18:03 -0700 Subject: [PATCH 01/15] Visualize with hardcoded dependencies --- src/fprime/fpp/cli.py | 32 +++++++++- src/fprime/fpp/common.py | 14 +++++ src/fprime/util/cli.py | 4 ++ src/fprime/visualize/__init__.py | 0 src/fprime/visualize/cli.py | 100 +++++++++++++++++++++++++++++++ 5 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 src/fprime/visualize/__init__.py create mode 100644 src/fprime/visualize/cli.py diff --git a/src/fprime/fpp/cli.py b/src/fprime/fpp/cli.py index 14a04a6c..f4ad4ca4 100644 --- a/src/fprime/fpp/cli.py +++ b/src/fprime/fpp/cli.py @@ -34,6 +34,30 @@ def run_fpp_check( args=({}, ["-u", parsed.unconnected] if parsed.unconnected else []), ) +def run_fpp_to_xml( + build: "Build", + parsed: argparse.Namespace, + _: Dict[str, str], + __: Dict[str, str], + ___: List[str], +): + """Run fpp check application + + Handles the fpp-check endpoint by running the utility fpp-check. + + Args: + build: build directory output + parsed: parsed input arguments + _: unused cmake_args + __: unused make_args + ___: unused pass-through arguments + """ + FppUtility("fpp-to-xml").execute( + build, + parsed.path, + args=({}, ["--directory", parsed.directory] if parsed.directory else []), + ) + def add_fpp_parsers( subparsers, common: argparse.ArgumentParser @@ -56,4 +80,10 @@ def add_fpp_parsers( check_parser.add_argument( "-u", "--unconnected", default=None, help="write unconnected ports to file" ) - return {"fpp-check": run_fpp_check}, {"fpp-check": check_parser} + fpp_to_xml_parser = subparsers.add_parser( + "fpp-to-xml", help="Runs fpp-to-xml utility", parents=[common], add_help=False + ) + fpp_to_xml_parser.add_argument( + "--directory", default=None, help="Output directory" + ) + return {"fpp-check": run_fpp_check, "fpp-to-xml": run_fpp_to_xml}, {"fpp-check": check_parser, "fpp-to-xml": fpp_to_xml_parser} diff --git a/src/fprime/fpp/common.py b/src/fprime/fpp/common.py index b25463b2..a1df2399 100644 --- a/src/fprime/fpp/common.py +++ b/src/fprime/fpp/common.py @@ -7,6 +7,8 @@ import itertools import subprocess from pathlib import Path +import shutil +import sys from typing import Dict, List, Tuple from fprime.common.error import FprimeException @@ -42,6 +44,10 @@ def __init__(self, name): """ super().__init__(TargetScope.LOCAL) self.utility = name + + def is_supported(self): + """Returns whether this utility is supported""" + return bool(shutil.which(self.utility)) @staticmethod def get_locations_file(builder: Build) -> Path: @@ -98,6 +104,14 @@ def execute( args: extra arguments to supply to the utility """ # First refresh the cache but only if it detects it needs too + + if not self.is_supported(): + print( + f"[ERROR] Cannot find executable: {self.utility}.", + file=sys.stderr, + ) + return 1 + builder.cmake.cmake_refresh_cache(builder.build_dir, False) # Read files and arguments diff --git a/src/fprime/util/cli.py b/src/fprime/util/cli.py index eceb5f8d..204beb38 100644 --- a/src/fprime/util/cli.py +++ b/src/fprime/util/cli.py @@ -18,6 +18,7 @@ from fprime.util.build_helper import load_build from fprime.util.commands import run_code_format, run_hash_to_file, run_info, run_new from fprime.util.help_text import HelpText +from fprime.visualize.cli import add_fprime_viz_parsers def utility_entry(args): @@ -301,10 +302,13 @@ def parse_args(args): subparsers, common_parser, HelpText ) fpp_runners, fpp_parsers = add_fpp_parsers(subparsers, common_parser) + viz_runners, viz_parsers = add_fprime_viz_parsers(subparsers, common_parser) parsers.update(fbuild_parsers) parsers.update(fpp_parsers) + parsers.update(viz_parsers) runners.update(fbuild_runners) runners.update(fpp_runners) + runners.update(viz_runners) runners.update(add_special_parsers(subparsers, common_parser, HelpText)) # Parse and prepare to run diff --git a/src/fprime/visualize/__init__.py b/src/fprime/visualize/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/fprime/visualize/cli.py b/src/fprime/visualize/cli.py new file mode 100644 index 00000000..70524176 --- /dev/null +++ b/src/fprime/visualize/cli.py @@ -0,0 +1,100 @@ +""" fprime.visualize.cli: Command line targets for fprime-viz + +@thomas-bc +""" +import argparse +from pathlib import Path +# import os +# import shutil +import subprocess +from typing import Callable, Dict, List, Tuple + +from fprime.fpp.common import FppUtility + +FPL_INSTALL_DIR = "/Users/chammard/Work/fp/fprime-layout/bin" +FPV_INSTALL_DIR = "/Users/chammard/Work/fp/fprime-visual" + +def run_fprime_viz( + build: "Build", + parsed: argparse.Namespace, + _: Dict[str, str], + __: Dict[str, str], + ___: List[str], +): + """Run pipeline of utilities to generate visualization. This includes: + - fpp-to-xml + - fpl-convert-xml + - fpl-layout + - start nodemon process to serve visualization + + Args: + build: build directory output + parsed: parsed input arguments + _: unused cmake_args + __: unused make_args + ___: unused pass-through arguments + """ + viz_cache = Path(".fpp-viz-cache") + xml_cache = Path(".fpp-viz-cache/xml") + xml_cache.mkdir(parents=True, exist_ok=True) + + # Run fpp-to-xml + FppUtility("fpp-to-xml").execute( + build, + parsed.path, + args=({}, ["--directory", ".fpp-viz-cache/xml"]) #["--directory", parsed.directory] if parsed.directory else ["--directory", ".fpp-viz-cache"]), + ) + topology_match = list(xml_cache.glob("*TopologyAppAi.xml")) + if len(topology_match) == 1: + topology_xml = topology_match[0] + else: + raise Exception(f"Found {len(topology_match)} '*TopologyAppAi.xml' topology files - expected 1") + + print(f"Found topology XML file: {topology_xml.resolve()}") + + topology_txt = viz_cache / "Topology.txt" + topology_json = viz_cache / "Topology.json" + + # Execute: fpl-convert-xml Topology.xml > Topology.txt + with open(topology_txt.resolve(), "w") as txt_file: + subprocess.run([f"{FPL_INSTALL_DIR}/fpl-convert-xml", topology_xml.resolve()], stdout=txt_file, check=True) + + # Execute: fpl-layout < Topology.txt > Topology.json + with open(topology_json.resolve(), "w") as json_file: + with open(topology_txt.resolve(), "r") as txt_file: + subprocess.run([f"{FPL_INSTALL_DIR}/fpl-layout"], stdin=txt_file, stdout=json_file, check=True) + + # Run nodemon - this will eventually be replaced with Flask app so whatevs' is fine for now + fpv_env = viz_cache / ".fpv-env" + with open(fpv_env.resolve(), "w") as env_file: + env_file.write(f"DATA_FOLDER={viz_cache.resolve()}\n") + print("[INFO] Starting nodemon server...") + subprocess.run(["nodemon", f"{FPV_INSTALL_DIR}/server/index.js", fpv_env.resolve()], check=True) + return 0 + + + + + +def add_fprime_viz_parsers( + subparsers, common: argparse.ArgumentParser +) -> Tuple[Dict[str, Callable], Dict[str, argparse.ArgumentParser]]: + """Sets up the fprime-viz command line parsers + + Creates command line parsers for fprime-viz commands and associates these commands to processing functions for those fpp + commands. + + Args: + subparsers: subparsers to add to + common: common parser for all fprime-util commands + + Returns: + Tuple of dictionary mapping command name to processor, and command to parser + """ + viz_parser = subparsers.add_parser( + "fpp-viz", help="Runs visualization pipeline", parents=[common], add_help=False + ) + viz_parser.add_argument( + "-z", "--test", default=None, help="Test" + ) + return {"fpp-viz": run_fprime_viz}, {"fpp-viz": viz_parser} \ No newline at end of file From 839920e62a28eac55992f7d5208f3933ca10ebad Mon Sep 17 00:00:00 2001 From: thomas-bc Date: Mon, 12 Jun 2023 18:21:49 -0700 Subject: [PATCH 02/15] move visualize under fpp/ --- src/fprime/{visualize/cli.py => fpp/visualize.py} | 6 +++--- src/fprime/util/cli.py | 4 ++-- src/fprime/visualize/__init__.py | 0 3 files changed, 5 insertions(+), 5 deletions(-) rename src/fprime/{visualize/cli.py => fpp/visualize.py} (96%) delete mode 100644 src/fprime/visualize/__init__.py diff --git a/src/fprime/visualize/cli.py b/src/fprime/fpp/visualize.py similarity index 96% rename from src/fprime/visualize/cli.py rename to src/fprime/fpp/visualize.py index 70524176..5606c12b 100644 --- a/src/fprime/visualize/cli.py +++ b/src/fprime/fpp/visualize.py @@ -14,7 +14,7 @@ FPL_INSTALL_DIR = "/Users/chammard/Work/fp/fprime-layout/bin" FPV_INSTALL_DIR = "/Users/chammard/Work/fp/fprime-visual" -def run_fprime_viz( +def run_fpp_viz( build: "Build", parsed: argparse.Namespace, _: Dict[str, str], @@ -76,7 +76,7 @@ def run_fprime_viz( -def add_fprime_viz_parsers( +def add_fpp_viz_parsers( subparsers, common: argparse.ArgumentParser ) -> Tuple[Dict[str, Callable], Dict[str, argparse.ArgumentParser]]: """Sets up the fprime-viz command line parsers @@ -97,4 +97,4 @@ def add_fprime_viz_parsers( viz_parser.add_argument( "-z", "--test", default=None, help="Test" ) - return {"fpp-viz": run_fprime_viz}, {"fpp-viz": viz_parser} \ No newline at end of file + return {"fpp-viz": run_fpp_viz}, {"fpp-viz": viz_parser} \ No newline at end of file diff --git a/src/fprime/util/cli.py b/src/fprime/util/cli.py index 204beb38..5cb3240f 100644 --- a/src/fprime/util/cli.py +++ b/src/fprime/util/cli.py @@ -18,7 +18,7 @@ from fprime.util.build_helper import load_build from fprime.util.commands import run_code_format, run_hash_to_file, run_info, run_new from fprime.util.help_text import HelpText -from fprime.visualize.cli import add_fprime_viz_parsers +from fprime.fpp.visualize import add_fpp_viz_parsers def utility_entry(args): @@ -302,7 +302,7 @@ def parse_args(args): subparsers, common_parser, HelpText ) fpp_runners, fpp_parsers = add_fpp_parsers(subparsers, common_parser) - viz_runners, viz_parsers = add_fprime_viz_parsers(subparsers, common_parser) + viz_runners, viz_parsers = add_fpp_viz_parsers(subparsers, common_parser) parsers.update(fbuild_parsers) parsers.update(fpp_parsers) parsers.update(viz_parsers) diff --git a/src/fprime/visualize/__init__.py b/src/fprime/visualize/__init__.py deleted file mode 100644 index e69de29b..00000000 From 64732252602455e7b3c86113a766ad510d3b44a7 Mon Sep 17 00:00:00 2001 From: thomas-bc Date: Thu, 15 Jun 2023 19:21:33 -0700 Subject: [PATCH 03/15] Use fprime_visual Flask app --- src/fprime/fpp/visualize.py | 62 ++++++++++++++++++++++++------------- 1 file changed, 41 insertions(+), 21 deletions(-) diff --git a/src/fprime/fpp/visualize.py b/src/fprime/fpp/visualize.py index 5606c12b..b07d797c 100644 --- a/src/fprime/fpp/visualize.py +++ b/src/fprime/fpp/visualize.py @@ -1,18 +1,20 @@ -""" fprime.visualize.cli: Command line targets for fprime-viz +""" fprime.fpp.visualize: Command line targets for fprime-viz -@thomas-bc +@author thomas-bc """ import argparse -from pathlib import Path -# import os -# import shutil +import os import subprocess +from pathlib import Path from typing import Callable, Dict, List, Tuple from fprime.fpp.common import FppUtility -FPL_INSTALL_DIR = "/Users/chammard/Work/fp/fprime-layout/bin" -FPV_INSTALL_DIR = "/Users/chammard/Work/fp/fprime-visual" +from fprime_visual.flask.app import construct_app + + +FPL_INSTALL_DIR = os.getenv('FPL_INSTALL_DIR', '.') +FPV_INSTALL_DIR = os.getenv('FPV_INSTALL_DIR', '.') def run_fpp_viz( build: "Build", @@ -34,10 +36,12 @@ def run_fpp_viz( __: unused make_args ___: unused pass-through arguments """ + + # We could use the build directory, but it's quirky because not managed by CMake ?? viz_cache = Path(".fpp-viz-cache") xml_cache = Path(".fpp-viz-cache/xml") xml_cache.mkdir(parents=True, exist_ok=True) - + # Run fpp-to-xml FppUtility("fpp-to-xml").execute( build, @@ -50,7 +54,7 @@ def run_fpp_viz( else: raise Exception(f"Found {len(topology_match)} '*TopologyAppAi.xml' topology files - expected 1") - print(f"Found topology XML file: {topology_xml.resolve()}") + print(f"Generated topology XML file: {topology_xml.resolve()}") topology_txt = viz_cache / "Topology.txt" topology_json = viz_cache / "Topology.json" @@ -64,18 +68,34 @@ def run_fpp_viz( with open(topology_txt.resolve(), "r") as txt_file: subprocess.run([f"{FPL_INSTALL_DIR}/fpl-layout"], stdin=txt_file, stdout=json_file, check=True) - # Run nodemon - this will eventually be replaced with Flask app so whatevs' is fine for now - fpv_env = viz_cache / ".fpv-env" - with open(fpv_env.resolve(), "w") as env_file: - env_file.write(f"DATA_FOLDER={viz_cache.resolve()}\n") - print("[INFO] Starting nodemon server...") - subprocess.run(["nodemon", f"{FPV_INSTALL_DIR}/server/index.js", fpv_env.resolve()], check=True) + print("Extracting subtopologies...") + # Extract subtopologies from Topology.xml + extract_cache = viz_cache / "extracted" + extract_cache.mkdir(parents=True, exist_ok=True) + # Execute: fpl-extract-xml -d extracted/ Topology.xml + subprocess.run([f"{FPL_INSTALL_DIR}/fpl-extract-xml", "-d", extract_cache.resolve(), topology_xml.resolve()], check=True) + subtopologies = list(extract_cache.glob("*.xml")) + for subtopology in subtopologies: + # Execute: fpl-convert-xml subtopology.xml > subtopology.txt + subtopology_txt = extract_cache / f"{subtopology.stem}.txt" + with open(subtopology_txt.resolve(), "w") as txt_file: + subprocess.run([f"{FPL_INSTALL_DIR}/fpl-convert-xml", subtopology.resolve()], stdout=txt_file, check=True) + # Execute: fpl-layout < subtopology.txt > subtopology.json + subtopology_json = viz_cache / f"{subtopology.stem}.json" + with open(subtopology_json.resolve(), "w") as json_file: + with open(subtopology_txt.resolve(), "r") as txt_file: + subprocess.run([f"{FPL_INSTALL_DIR}/fpl-layout"], stdin=txt_file, stdout=json_file, check=True) + + print("[INFO] Starting fprime-visual server...") + config = {"SOURCE_DIRS": [str(viz_cache.resolve())]} + app = construct_app(config) + try: + app.run(port=7000) + except KeyboardInterrupt: + print("[INFO] CTRL-C received. Exiting.") return 0 - - - def add_fpp_viz_parsers( subparsers, common: argparse.ArgumentParser ) -> Tuple[Dict[str, Callable], Dict[str, argparse.ArgumentParser]]: @@ -92,9 +112,9 @@ def add_fpp_viz_parsers( Tuple of dictionary mapping command name to processor, and command to parser """ viz_parser = subparsers.add_parser( - "fpp-viz", help="Runs visualization pipeline", parents=[common], add_help=False + "visualize", help="Runs visualization pipeline", parents=[common], add_help=False ) viz_parser.add_argument( - "-z", "--test", default=None, help="Test" + "--gui-port", help="Set the GUI port for fprime-visual [default: 7000]", required=False, default=7000 ) - return {"fpp-viz": run_fpp_viz}, {"fpp-viz": viz_parser} \ No newline at end of file + return {"visualize": run_fpp_viz}, {"visualize": viz_parser} \ No newline at end of file From ca48d9fc547ec8783cd5d030206c8266d76884d1 Mon Sep 17 00:00:00 2001 From: thomas-bc Date: Thu, 15 Jun 2023 19:52:46 -0700 Subject: [PATCH 04/15] assume layout tools in PATH --- src/fprime/fpp/visualize.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/fprime/fpp/visualize.py b/src/fprime/fpp/visualize.py index b07d797c..0edc1796 100644 --- a/src/fprime/fpp/visualize.py +++ b/src/fprime/fpp/visualize.py @@ -13,9 +13,6 @@ from fprime_visual.flask.app import construct_app -FPL_INSTALL_DIR = os.getenv('FPL_INSTALL_DIR', '.') -FPV_INSTALL_DIR = os.getenv('FPV_INSTALL_DIR', '.') - def run_fpp_viz( build: "Build", parsed: argparse.Namespace, @@ -27,7 +24,7 @@ def run_fpp_viz( - fpp-to-xml - fpl-convert-xml - fpl-layout - - start nodemon process to serve visualization + - start fprime-visual Flask app to serve visualization Args: build: build directory output @@ -61,36 +58,36 @@ def run_fpp_viz( # Execute: fpl-convert-xml Topology.xml > Topology.txt with open(topology_txt.resolve(), "w") as txt_file: - subprocess.run([f"{FPL_INSTALL_DIR}/fpl-convert-xml", topology_xml.resolve()], stdout=txt_file, check=True) + subprocess.run(["fpl-convert-xml", topology_xml.resolve()], stdout=txt_file, check=True) # Execute: fpl-layout < Topology.txt > Topology.json with open(topology_json.resolve(), "w") as json_file: with open(topology_txt.resolve(), "r") as txt_file: - subprocess.run([f"{FPL_INSTALL_DIR}/fpl-layout"], stdin=txt_file, stdout=json_file, check=True) + subprocess.run(["fpl-layout"], stdin=txt_file, stdout=json_file, check=True) print("Extracting subtopologies...") # Extract subtopologies from Topology.xml extract_cache = viz_cache / "extracted" extract_cache.mkdir(parents=True, exist_ok=True) # Execute: fpl-extract-xml -d extracted/ Topology.xml - subprocess.run([f"{FPL_INSTALL_DIR}/fpl-extract-xml", "-d", extract_cache.resolve(), topology_xml.resolve()], check=True) + subprocess.run(["fpl-extract-xml", "-d", extract_cache.resolve(), topology_xml.resolve()], check=True) subtopologies = list(extract_cache.glob("*.xml")) for subtopology in subtopologies: # Execute: fpl-convert-xml subtopology.xml > subtopology.txt subtopology_txt = extract_cache / f"{subtopology.stem}.txt" with open(subtopology_txt.resolve(), "w") as txt_file: - subprocess.run([f"{FPL_INSTALL_DIR}/fpl-convert-xml", subtopology.resolve()], stdout=txt_file, check=True) + subprocess.run(["fpl-convert-xml", subtopology.resolve()], stdout=txt_file, check=True) # Execute: fpl-layout < subtopology.txt > subtopology.json subtopology_json = viz_cache / f"{subtopology.stem}.json" with open(subtopology_json.resolve(), "w") as json_file: with open(subtopology_txt.resolve(), "r") as txt_file: - subprocess.run([f"{FPL_INSTALL_DIR}/fpl-layout"], stdin=txt_file, stdout=json_file, check=True) + subprocess.run(["fpl-layout"], stdin=txt_file, stdout=json_file, check=True) print("[INFO] Starting fprime-visual server...") config = {"SOURCE_DIRS": [str(viz_cache.resolve())]} app = construct_app(config) try: - app.run(port=7000) + app.run(port=parsed.gui_port) except KeyboardInterrupt: print("[INFO] CTRL-C received. Exiting.") return 0 From 94d09b68de78d0b0cd76d90e385bbdd0f2f228a2 Mon Sep 17 00:00:00 2001 From: thomas-bc Date: Thu, 15 Jun 2023 20:40:20 -0700 Subject: [PATCH 05/15] spelling --- .github/actions/spelling/expect.txt | 2 ++ src/fprime/fpp/visualize.py | 5 ++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt index 2d6b6ad9..49d49ee2 100644 --- a/.github/actions/spelling/expect.txt +++ b/.github/actions/spelling/expect.txt @@ -75,6 +75,7 @@ filepath firest floordiv FPGA +fpl fpp fprime fromkeys @@ -230,6 +231,7 @@ stylesheet subdir subparser Subproc +subtopology sys tcanham Tcp diff --git a/src/fprime/fpp/visualize.py b/src/fprime/fpp/visualize.py index 0edc1796..eb218b28 100644 --- a/src/fprime/fpp/visualize.py +++ b/src/fprime/fpp/visualize.py @@ -3,7 +3,6 @@ @author thomas-bc """ import argparse -import os import subprocess from pathlib import Path from typing import Callable, Dict, List, Tuple @@ -13,7 +12,7 @@ from fprime_visual.flask.app import construct_app -def run_fpp_viz( +def run_fprime_visualize( build: "Build", parsed: argparse.Namespace, _: Dict[str, str], @@ -114,4 +113,4 @@ def add_fpp_viz_parsers( viz_parser.add_argument( "--gui-port", help="Set the GUI port for fprime-visual [default: 7000]", required=False, default=7000 ) - return {"visualize": run_fpp_viz}, {"visualize": viz_parser} \ No newline at end of file + return {"visualize": run_fprime_visualize}, {"visualize": viz_parser} \ No newline at end of file From 31c4ec38be67cbeaa4bebb50b2754d2ad93f88b2 Mon Sep 17 00:00:00 2001 From: thomas-bc Date: Tue, 20 Jun 2023 11:11:14 -0700 Subject: [PATCH 06/15] formatting --- src/fprime/fpp/cli.py | 10 ++++---- src/fprime/fpp/visualize.py | 48 ++++++++++++++++++++++++++----------- 2 files changed, 40 insertions(+), 18 deletions(-) diff --git a/src/fprime/fpp/cli.py b/src/fprime/fpp/cli.py index f4ad4ca4..a0986f46 100644 --- a/src/fprime/fpp/cli.py +++ b/src/fprime/fpp/cli.py @@ -34,6 +34,7 @@ def run_fpp_check( args=({}, ["-u", parsed.unconnected] if parsed.unconnected else []), ) + def run_fpp_to_xml( build: "Build", parsed: argparse.Namespace, @@ -83,7 +84,8 @@ def add_fpp_parsers( fpp_to_xml_parser = subparsers.add_parser( "fpp-to-xml", help="Runs fpp-to-xml utility", parents=[common], add_help=False ) - fpp_to_xml_parser.add_argument( - "--directory", default=None, help="Output directory" - ) - return {"fpp-check": run_fpp_check, "fpp-to-xml": run_fpp_to_xml}, {"fpp-check": check_parser, "fpp-to-xml": fpp_to_xml_parser} + fpp_to_xml_parser.add_argument("--directory", default=None, help="Output directory") + return {"fpp-check": run_fpp_check, "fpp-to-xml": run_fpp_to_xml}, { + "fpp-check": check_parser, + "fpp-to-xml": fpp_to_xml_parser, + } diff --git a/src/fprime/fpp/visualize.py b/src/fprime/fpp/visualize.py index eb218b28..eb0a8161 100644 --- a/src/fprime/fpp/visualize.py +++ b/src/fprime/fpp/visualize.py @@ -32,7 +32,7 @@ def run_fprime_visualize( __: unused make_args ___: unused pass-through arguments """ - + # We could use the build directory, but it's quirky because not managed by CMake ?? viz_cache = Path(".fpp-viz-cache") xml_cache = Path(".fpp-viz-cache/xml") @@ -42,14 +42,19 @@ def run_fprime_visualize( FppUtility("fpp-to-xml").execute( build, parsed.path, - args=({}, ["--directory", ".fpp-viz-cache/xml"]) #["--directory", parsed.directory] if parsed.directory else ["--directory", ".fpp-viz-cache"]), + args=( + {}, + ["--directory", ".fpp-viz-cache/xml"], + ), # ["--directory", parsed.directory] if parsed.directory else ["--directory", ".fpp-viz-cache"]), ) topology_match = list(xml_cache.glob("*TopologyAppAi.xml")) if len(topology_match) == 1: topology_xml = topology_match[0] else: - raise Exception(f"Found {len(topology_match)} '*TopologyAppAi.xml' topology files - expected 1") - + raise Exception( + f"Found {len(topology_match)} '*TopologyAppAi.xml' topology files - expected 1" + ) + print(f"Generated topology XML file: {topology_xml.resolve()}") topology_txt = viz_cache / "Topology.txt" @@ -57,11 +62,13 @@ def run_fprime_visualize( # Execute: fpl-convert-xml Topology.xml > Topology.txt with open(topology_txt.resolve(), "w") as txt_file: - subprocess.run(["fpl-convert-xml", topology_xml.resolve()], stdout=txt_file, check=True) - + subprocess.run( + ["fpl-convert-xml", topology_xml.resolve()], stdout=txt_file, check=True + ) + # Execute: fpl-layout < Topology.txt > Topology.json with open(topology_json.resolve(), "w") as json_file: - with open(topology_txt.resolve(), "r") as txt_file: + with open(topology_txt.resolve(), "r") as txt_file: subprocess.run(["fpl-layout"], stdin=txt_file, stdout=json_file, check=True) print("Extracting subtopologies...") @@ -69,18 +76,25 @@ def run_fprime_visualize( extract_cache = viz_cache / "extracted" extract_cache.mkdir(parents=True, exist_ok=True) # Execute: fpl-extract-xml -d extracted/ Topology.xml - subprocess.run(["fpl-extract-xml", "-d", extract_cache.resolve(), topology_xml.resolve()], check=True) + subprocess.run( + ["fpl-extract-xml", "-d", extract_cache.resolve(), topology_xml.resolve()], + check=True, + ) subtopologies = list(extract_cache.glob("*.xml")) for subtopology in subtopologies: # Execute: fpl-convert-xml subtopology.xml > subtopology.txt subtopology_txt = extract_cache / f"{subtopology.stem}.txt" with open(subtopology_txt.resolve(), "w") as txt_file: - subprocess.run(["fpl-convert-xml", subtopology.resolve()], stdout=txt_file, check=True) + subprocess.run( + ["fpl-convert-xml", subtopology.resolve()], stdout=txt_file, check=True + ) # Execute: fpl-layout < subtopology.txt > subtopology.json subtopology_json = viz_cache / f"{subtopology.stem}.json" with open(subtopology_json.resolve(), "w") as json_file: - with open(subtopology_txt.resolve(), "r") as txt_file: - subprocess.run(["fpl-layout"], stdin=txt_file, stdout=json_file, check=True) + with open(subtopology_txt.resolve(), "r") as txt_file: + subprocess.run( + ["fpl-layout"], stdin=txt_file, stdout=json_file, check=True + ) print("[INFO] Starting fprime-visual server...") config = {"SOURCE_DIRS": [str(viz_cache.resolve())]} @@ -108,9 +122,15 @@ def add_fpp_viz_parsers( Tuple of dictionary mapping command name to processor, and command to parser """ viz_parser = subparsers.add_parser( - "visualize", help="Runs visualization pipeline", parents=[common], add_help=False + "visualize", + help="Runs visualization pipeline", + parents=[common], + add_help=False, ) viz_parser.add_argument( - "--gui-port", help="Set the GUI port for fprime-visual [default: 7000]", required=False, default=7000 + "--gui-port", + help="Set the GUI port for fprime-visual [default: 7000]", + required=False, + default=7000, ) - return {"visualize": run_fprime_visualize}, {"visualize": viz_parser} \ No newline at end of file + return {"visualize": run_fprime_visualize}, {"visualize": viz_parser} From 25ecd954c6ec42f8f6c892e7a5c168df2236dbc0 Mon Sep 17 00:00:00 2001 From: thomas-bc Date: Mon, 26 Jun 2023 13:19:49 -0700 Subject: [PATCH 07/15] Soft dependency on fprime_visual --- src/fprime/fpp/visualize.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/fprime/fpp/visualize.py b/src/fprime/fpp/visualize.py index eb0a8161..e76c8209 100644 --- a/src/fprime/fpp/visualize.py +++ b/src/fprime/fpp/visualize.py @@ -1,15 +1,19 @@ -""" fprime.fpp.visualize: Command line targets for fprime-viz +""" fprime.fpp.visualize: Command line targets for fprime-util visualize @author thomas-bc """ import argparse +import shutil import subprocess from pathlib import Path from typing import Callable, Dict, List, Tuple from fprime.fpp.common import FppUtility -from fprime_visual.flask.app import construct_app +try: + from fprime_visual.flask.app import construct_app +except ImportError: + construct_app = None def run_fprime_visualize( @@ -32,6 +36,15 @@ def run_fprime_visualize( __: unused make_args ___: unused pass-through arguments """ + if construct_app is None: + raise ModuleNotFoundError( + "fprime-visual is not installed. Please install with `pip install fprime-visual`" + ) + + if not (shutil.which("fpl-convert-xml") and shutil.which("fpl-layout")): + raise FileNotFoundError( + "fpl-layout is not installed. Please install with `pip install fprime-fpp>1.2.0`" + ) # We could use the build directory, but it's quirky because not managed by CMake ?? viz_cache = Path(".fpp-viz-cache") @@ -72,7 +85,6 @@ def run_fprime_visualize( subprocess.run(["fpl-layout"], stdin=txt_file, stdout=json_file, check=True) print("Extracting subtopologies...") - # Extract subtopologies from Topology.xml extract_cache = viz_cache / "extracted" extract_cache.mkdir(parents=True, exist_ok=True) # Execute: fpl-extract-xml -d extracted/ Topology.xml From eef3cf82117e5ea445a25833fc7a68cd7acb1bb9 Mon Sep 17 00:00:00 2001 From: thomas-bc Date: Tue, 27 Jun 2023 16:20:13 -0700 Subject: [PATCH 08/15] Add --cache-dir option --- src/fprime/fpp/common.py | 2 +- src/fprime/fpp/visualize.py | 20 +++++++++++++------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/fprime/fpp/common.py b/src/fprime/fpp/common.py index a1df2399..3cebfedf 100644 --- a/src/fprime/fpp/common.py +++ b/src/fprime/fpp/common.py @@ -44,7 +44,7 @@ def __init__(self, name): """ super().__init__(TargetScope.LOCAL) self.utility = name - + def is_supported(self): """Returns whether this utility is supported""" return bool(shutil.which(self.utility)) diff --git a/src/fprime/fpp/visualize.py b/src/fprime/fpp/visualize.py index e76c8209..9cfb42ee 100644 --- a/src/fprime/fpp/visualize.py +++ b/src/fprime/fpp/visualize.py @@ -46,9 +46,8 @@ def run_fprime_visualize( "fpl-layout is not installed. Please install with `pip install fprime-fpp>1.2.0`" ) - # We could use the build directory, but it's quirky because not managed by CMake ?? - viz_cache = Path(".fpp-viz-cache") - xml_cache = Path(".fpp-viz-cache/xml") + viz_cache = Path(parsed.cache_dir).resolve() + xml_cache = (viz_cache / "xml").resolve() xml_cache.mkdir(parents=True, exist_ok=True) # Run fpp-to-xml @@ -57,8 +56,8 @@ def run_fprime_visualize( parsed.path, args=( {}, - ["--directory", ".fpp-viz-cache/xml"], - ), # ["--directory", parsed.directory] if parsed.directory else ["--directory", ".fpp-viz-cache"]), + ["--directory", str(xml_cache)], + ), ) topology_match = list(xml_cache.glob("*TopologyAppAi.xml")) if len(topology_match) == 1: @@ -108,7 +107,8 @@ def run_fprime_visualize( ["fpl-layout"], stdin=txt_file, stdout=json_file, check=True ) - print("[INFO] Starting fprime-visual server...") + print( "[INFO] Starting fprime-visual server...") + print(f"[INFO] Serving files in {str(viz_cache.resolve())}") config = {"SOURCE_DIRS": [str(viz_cache.resolve())]} app = construct_app(config) try: @@ -141,8 +141,14 @@ def add_fpp_viz_parsers( ) viz_parser.add_argument( "--gui-port", - help="Set the GUI port for fprime-visual [default: 7000]", + help="Set the GUI port for fprime-visual [default: %(default)s]", required=False, default=7000, ) + viz_parser.add_argument( + "--cache-dir", + help="Set the directory to store layout files in [default: %(default)s]", + required=False, + default=".visualize-cache", + ) return {"visualize": run_fprime_visualize}, {"visualize": viz_parser} From 52eeb71774d9abd393fe8ec33fc673876731247a Mon Sep 17 00:00:00 2001 From: thomas-bc Date: Wed, 5 Jul 2023 17:05:04 -0700 Subject: [PATCH 09/15] formatting --- src/fprime/fpp/visualize.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fprime/fpp/visualize.py b/src/fprime/fpp/visualize.py index 9cfb42ee..156ff234 100644 --- a/src/fprime/fpp/visualize.py +++ b/src/fprime/fpp/visualize.py @@ -107,7 +107,7 @@ def run_fprime_visualize( ["fpl-layout"], stdin=txt_file, stdout=json_file, check=True ) - print( "[INFO] Starting fprime-visual server...") + print("[INFO] Starting fprime-visual server...") print(f"[INFO] Serving files in {str(viz_cache.resolve())}") config = {"SOURCE_DIRS": [str(viz_cache.resolve())]} app = construct_app(config) From 7374dc24e5b0f1fa518b36ad6b65125d76d37902 Mon Sep 17 00:00:00 2001 From: thomas-bc Date: Mon, 10 Jul 2023 20:14:01 -0700 Subject: [PATCH 10/15] Review changes --- src/fprime/fpp/cli.py | 4 +--- src/fprime/fpp/visualize.py | 6 +++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/fprime/fpp/cli.py b/src/fprime/fpp/cli.py index a0986f46..bcaebe31 100644 --- a/src/fprime/fpp/cli.py +++ b/src/fprime/fpp/cli.py @@ -42,9 +42,7 @@ def run_fpp_to_xml( __: Dict[str, str], ___: List[str], ): - """Run fpp check application - - Handles the fpp-check endpoint by running the utility fpp-check. + """Run the fpp-to-xml utility Args: build: build directory output diff --git a/src/fprime/fpp/visualize.py b/src/fprime/fpp/visualize.py index 156ff234..b373b075 100644 --- a/src/fprime/fpp/visualize.py +++ b/src/fprime/fpp/visualize.py @@ -46,7 +46,7 @@ def run_fprime_visualize( "fpl-layout is not installed. Please install with `pip install fprime-fpp>1.2.0`" ) - viz_cache = Path(parsed.cache_dir).resolve() + viz_cache = Path(parsed.working_dir).resolve() xml_cache = (viz_cache / "xml").resolve() xml_cache.mkdir(parents=True, exist_ok=True) @@ -146,9 +146,9 @@ def add_fpp_viz_parsers( default=7000, ) viz_parser.add_argument( - "--cache-dir", + "--working-dir", help="Set the directory to store layout files in [default: %(default)s]", required=False, - default=".visualize-cache", + default="/tmp/fprime-visualize", ) return {"visualize": run_fprime_visualize}, {"visualize": viz_parser} From 047c2acb7d449d9b8eccacdc52f6a236dc935c88 Mon Sep 17 00:00:00 2001 From: thomas-bc Date: Tue, 11 Jul 2023 10:24:46 -0700 Subject: [PATCH 11/15] Verbose permission error --- src/fprime/fpp/visualize.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/fprime/fpp/visualize.py b/src/fprime/fpp/visualize.py index b373b075..d01e9de3 100644 --- a/src/fprime/fpp/visualize.py +++ b/src/fprime/fpp/visualize.py @@ -48,7 +48,12 @@ def run_fprime_visualize( viz_cache = Path(parsed.working_dir).resolve() xml_cache = (viz_cache / "xml").resolve() - xml_cache.mkdir(parents=True, exist_ok=True) + try: + xml_cache.mkdir(parents=True, exist_ok=True) + except PermissionError: + raise PermissionError( + f"Unable to write to {viz_cache.resolve()}. Use --working-dir to set a different location." + ) # Run fpp-to-xml FppUtility("fpp-to-xml").execute( From acaf667dc380e17850624440661955ff8e10e05f Mon Sep 17 00:00:00 2001 From: thomas-bc Date: Tue, 11 Jul 2023 11:38:18 -0700 Subject: [PATCH 12/15] Use tempfile if working-dir not specified --- src/fprime/fpp/visualize.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/fprime/fpp/visualize.py b/src/fprime/fpp/visualize.py index d01e9de3..1996d63c 100644 --- a/src/fprime/fpp/visualize.py +++ b/src/fprime/fpp/visualize.py @@ -5,6 +5,7 @@ import argparse import shutil import subprocess +import tempfile from pathlib import Path from typing import Callable, Dict, List, Tuple @@ -46,10 +47,20 @@ def run_fprime_visualize( "fpl-layout is not installed. Please install with `pip install fprime-fpp>1.2.0`" ) - viz_cache = Path(parsed.working_dir).resolve() + # Set up working directory using specified directory, or create a temporary one + if parsed.working_dir: + viz_cache = Path(parsed.working_dir).resolve() + else: + viz_cache = Path( + tempfile.TemporaryDirectory(prefix="fprime-visual-").name + ).resolve() + + # Set subpaths for different types of generated files xml_cache = (viz_cache / "xml").resolve() + extract_cache = (viz_cache / "extracted").resolve() try: xml_cache.mkdir(parents=True, exist_ok=True) + extract_cache.mkdir(parents=True, exist_ok=True) except PermissionError: raise PermissionError( f"Unable to write to {viz_cache.resolve()}. Use --working-dir to set a different location." @@ -89,8 +100,6 @@ def run_fprime_visualize( subprocess.run(["fpl-layout"], stdin=txt_file, stdout=json_file, check=True) print("Extracting subtopologies...") - extract_cache = viz_cache / "extracted" - extract_cache.mkdir(parents=True, exist_ok=True) # Execute: fpl-extract-xml -d extracted/ Topology.xml subprocess.run( ["fpl-extract-xml", "-d", extract_cache.resolve(), topology_xml.resolve()], @@ -152,8 +161,7 @@ def add_fpp_viz_parsers( ) viz_parser.add_argument( "--working-dir", - help="Set the directory to store layout files in [default: %(default)s]", + help="Set the directory to store layout files in (default to ephemeral location)", required=False, - default="/tmp/fprime-visualize", ) return {"visualize": run_fprime_visualize}, {"visualize": viz_parser} From 9614ece81f076e1f2076c07743cd088276826e84 Mon Sep 17 00:00:00 2001 From: thomas-bc Date: Tue, 11 Jul 2023 11:49:35 -0700 Subject: [PATCH 13/15] spelling --- src/fprime/fpp/visualize.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fprime/fpp/visualize.py b/src/fprime/fpp/visualize.py index 1996d63c..fac2ff22 100644 --- a/src/fprime/fpp/visualize.py +++ b/src/fprime/fpp/visualize.py @@ -55,7 +55,7 @@ def run_fprime_visualize( tempfile.TemporaryDirectory(prefix="fprime-visual-").name ).resolve() - # Set subpaths for different types of generated files + # Set sub-paths for different types of generated files xml_cache = (viz_cache / "xml").resolve() extract_cache = (viz_cache / "extracted").resolve() try: From c459f44146c2d6751a24844e591aff71c0ff3ffa Mon Sep 17 00:00:00 2001 From: thomas-bc Date: Tue, 11 Jul 2023 11:57:00 -0700 Subject: [PATCH 14/15] Fix is_supported parameters --- src/fprime/fpp/common.py | 2 +- src/fprime/util/code_formatter.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fprime/fpp/common.py b/src/fprime/fpp/common.py index 3cebfedf..7dde08f7 100644 --- a/src/fprime/fpp/common.py +++ b/src/fprime/fpp/common.py @@ -45,7 +45,7 @@ def __init__(self, name): super().__init__(TargetScope.LOCAL) self.utility = name - def is_supported(self): + def is_supported(self, _ = None, __ = None): """Returns whether this utility is supported""" return bool(shutil.which(self.utility)) diff --git a/src/fprime/util/code_formatter.py b/src/fprime/util/code_formatter.py index 2607b483..eaa0eb8a 100644 --- a/src/fprime/util/code_formatter.py +++ b/src/fprime/util/code_formatter.py @@ -55,7 +55,7 @@ def __init__(self, executable: str, style_file: "Path", options: Dict): self.allowed_extensions = ALLOWED_EXTENSIONS.copy() self._files_to_format: List[Path] = [] - def is_supported(self) -> bool: + def is_supported(self, _ = None, __ = None) -> bool: return bool(shutil.which(self.executable)) def allow_extension(self, file_ext: str) -> None: From de1f27600e68d62ac7741ef5e4d804898bd1816a Mon Sep 17 00:00:00 2001 From: thomas-bc Date: Tue, 11 Jul 2023 13:22:54 -0700 Subject: [PATCH 15/15] Formatting --- src/fprime/fpp/common.py | 2 +- src/fprime/util/code_formatter.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fprime/fpp/common.py b/src/fprime/fpp/common.py index 7dde08f7..3ddc1e4d 100644 --- a/src/fprime/fpp/common.py +++ b/src/fprime/fpp/common.py @@ -45,7 +45,7 @@ def __init__(self, name): super().__init__(TargetScope.LOCAL) self.utility = name - def is_supported(self, _ = None, __ = None): + def is_supported(self, _=None, __=None): """Returns whether this utility is supported""" return bool(shutil.which(self.utility)) diff --git a/src/fprime/util/code_formatter.py b/src/fprime/util/code_formatter.py index eaa0eb8a..ace42be8 100644 --- a/src/fprime/util/code_formatter.py +++ b/src/fprime/util/code_formatter.py @@ -55,7 +55,7 @@ def __init__(self, executable: str, style_file: "Path", options: Dict): self.allowed_extensions = ALLOWED_EXTENSIONS.copy() self._files_to_format: List[Path] = [] - def is_supported(self, _ = None, __ = None) -> bool: + def is_supported(self, _=None, __=None) -> bool: return bool(shutil.which(self.executable)) def allow_extension(self, file_ext: str) -> None: