From ed7075f4c58e01361979ad7aa42877bac56fcea4 Mon Sep 17 00:00:00 2001 From: Levy Ehrstein Date: Thu, 13 Aug 2020 22:02:20 +0200 Subject: [PATCH 01/17] Add warning for includes in input files --- webduino_generator/generator.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/webduino_generator/generator.py b/webduino_generator/generator.py index 4f98aa8..c4eab49 100644 --- a/webduino_generator/generator.py +++ b/webduino_generator/generator.py @@ -107,6 +107,17 @@ def inner(mime, mime_hash): # Try to handle file non-binary UTF-8 file. with open(os.path.join(input_path, file_name), 'r', encoding="UTF-8") as file: if (file_name.endswith(".cpp")): + # Warn user when using includes + line_num = 0 + for line in file.readlines(): + line_num += 1 + if line.startswith("#include "): + userio.print("") + userio.warn(file_name + " line " + str(line_num) + ":") + userio.print("| Putting includes in input files is not recommended!") + userio.print("| This might cause conflicts or large sketch sizes!") + userio.print("| Please put them int the template files instead.") + # Handle dynamic content (cpp files) file_content = file.read().replace("\n", "\n\t") file_type = 2 # Dynamic content From 4bec078021054c8c355c5c3ee33f1fa70475dfc7 Mon Sep 17 00:00:00 2001 From: Levy Ehrstein Date: Thu, 13 Aug 2020 22:35:19 +0200 Subject: [PATCH 02/17] Implement open command --- webduino_generator/entrypoint.py | 44 +++++++++++++++++++++++++++----- webduino_generator/helper.py | 11 +++++++- webduino_generator/project.py | 19 ++++++++++++++ 3 files changed, 67 insertions(+), 7 deletions(-) diff --git a/webduino_generator/entrypoint.py b/webduino_generator/entrypoint.py index d67064f..b6178a4 100644 --- a/webduino_generator/entrypoint.py +++ b/webduino_generator/entrypoint.py @@ -1,9 +1,10 @@ import argparse +import subprocess from .__init__ import __version__ from .userio import UserIO, get_ssid_pass -from .helper import cpp_str_esc, cpp_img_esc, get_files_rec, shorten -from .project import project_make_new, project_generate +from .helper import cpp_str_esc, cpp_img_esc, get_files_rec, shorten, get_tool +from .project import project_make_new, project_generate, project_get_sketch from .generator import * @@ -51,6 +52,30 @@ def command_build(userio, args): project_generate(userio, args.target, args.quiet) +def command_open(userio, args): + userio.section("Opening project output") + + # Get project output location + sketch_path = project_get_sketch(userio, args.target) + userio.print("Sketch located: " + sketch_path, verbose=True) + + # Get arduino IDE location + ide_path = get_tool("arduino") + if ide_path is None: + userio.error("Could not locate 'arduino' command. Is an arduino IDE istalled?") + userio.print("IDE located: " + ide_path, verbose=True) + + # Launch IDE + if args.detach: + userio.print("Opening IDE detached...") + subprocess.Popen([ide_path, sketch_path], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL) + else: + userio.print("Opening IDE...") + subprocess.call([ide_path, sketch_path]) + + def main(): install_traceback() userio = UserIO() @@ -100,19 +125,26 @@ def main(): help="Connection mode/library to be used") parser_init.add_argument("-f", "--force", action="store_true", dest='force', - help="Delete files that block project creation.") + help="Delete files that block project creation") parser_build = subparsers.add_parser("build", help="Generate Arduino code from current project") parser_build.add_argument("target", metavar="target", type=str, default=".", nargs="?", - help="Target folder where project will be created") + help="Root folder of target project") parser_build.add_argument("-q", "--quiet", action="store_true", dest='quiet', help="Hides password warning") + parser_open = subparsers.add_parser("open", help="Open generated code in arduino ide") + parser_open.add_argument("target", metavar="target", type=str, + default=".", nargs="?", + help="Root folder of target project") + parser_open.add_argument("-d", "--detach", + action="store_true", dest='detach', + help="Spawns IDE in a new thread") + parser_compile = subparsers.add_parser("compile", help="Compile Arduino code from current project") parser_upload = subparsers.add_parser("upload", help="Upload Arduino code from current project") - parser_open = subparsers.add_parser("open", help="Open generated code in arduino ide") parser_version = subparsers.add_parser("version", help="Display current version") # Global arguments @@ -151,7 +183,7 @@ def handle(): elif args.command == "upload": raise NotImplementedError elif args.command == "open": - raise NotImplementedError + command_open(userio, args) elif args.command == "generate": command_generate(userio, args) else: diff --git a/webduino_generator/helper.py b/webduino_generator/helper.py index 74ee666..2a20b50 100644 --- a/webduino_generator/helper.py +++ b/webduino_generator/helper.py @@ -1,5 +1,7 @@ import os +from shutil import which + def cpp_str_esc(s, encoding='ascii'): if isinstance(s, bytes): @@ -33,4 +35,11 @@ def get_files_rec(parent): def shorten(text, maxLength): - return str(text)[:maxLength] + ("..." if maxLength < len(str(text)) else "") \ No newline at end of file + return str(text)[:maxLength] + ("..." if maxLength < len(str(text)) else "") + + +def get_tool(name): + """Returns absolute path of command. + Returns None if command it not found.""" + + return which(name) diff --git a/webduino_generator/project.py b/webduino_generator/project.py index 168ea17..0a8be68 100644 --- a/webduino_generator/project.py +++ b/webduino_generator/project.py @@ -64,6 +64,25 @@ def project_config_readmeta(userio, config_path): return config["METADATA"] +def project_get_sketch(userio, project_path): + '''Returns location of arduino output sketch. (main.ino) + Returns None if project was not build yet.''' + + # Check project + if not project_check(project_path): + userio.error("Target project not found!") + + # Read project data + input_path, output_path, template_path = project_config_readproject(userio, project_path) + + # Check if project has been build yet + sketch_path = os.path.join(output_path, "main", "main.ino") + if not os.path.exists(sketch_path) or not os.path.isfile(sketch_path): + return None + + return sketch_path + + def project_make_new(userio, project_path, delete_block, mode, ssid, port): # Check port if port < 0 or port > 65535: From e38b1581a44bbe37d4e49266e0d72bfe89f26fef Mon Sep 17 00:00:00 2001 From: Levy Ehrstein Date: Thu, 13 Aug 2020 22:36:43 +0200 Subject: [PATCH 03/17] Add open command to README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9c208fd..e38483f 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ The following will build the demo website included in this repository. $ git clone https://github.com/StoneLabs/webduino-generator $ cd webduino-generator/ $ wgen generate input -$ arduino main/main.ino +$ wgen open Or use arduino-cli to compile and upload directly from the shell (linux only) $ ./uploader.sh @@ -29,7 +29,7 @@ Aside from build a random folder you can create a project. By default a simple h ``` $ wgen init $ wgen build -$ arduino output/main/main.ino +$ wgen open ``` ### Note From 583a1d6cd347e193570ed39c9eb9eaad0da8cc56 Mon Sep 17 00:00:00 2001 From: Levy Ehrstein Date: Fri, 14 Aug 2020 00:03:10 +0200 Subject: [PATCH 04/17] Implement compile command --- setup.py | 2 +- webduino_generator/arduinocli.py | 73 ++++++++++++++++++++++++++++++++ webduino_generator/entrypoint.py | 19 ++++++++- 3 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 webduino_generator/arduinocli.py diff --git a/setup.py b/setup.py index d686c86..0c19da5 100644 --- a/setup.py +++ b/setup.py @@ -17,5 +17,5 @@ 'console_scripts': ['webduino-generator=webduino_generator.entrypoint:main', 'wgen=webduino_generator.entrypoint:main'], }, - install_requires=["jinja2", "rich"], + install_requires=["jinja2", "rich", "simple-term-menu"], ) \ No newline at end of file diff --git a/webduino_generator/arduinocli.py b/webduino_generator/arduinocli.py new file mode 100644 index 0000000..450e9b5 --- /dev/null +++ b/webduino_generator/arduinocli.py @@ -0,0 +1,73 @@ +import subprocess +import json + +from simple_term_menu import TerminalMenu +from .helper import get_tool + + +def get_cli_path(userio): + # Get arduino IDE location + cli_path = get_tool("arduino-cli") + if cli_path is None: + userio.error("Could not locate 'arduino-cli' command. Is arduino-cli istalled?") + userio.print("CLI located: " + cli_path, verbose=True) + + return cli_path + + +def get_boards(userio, list_all=False, require_name=True, require_fqbn=False, require_port=False): + cli_path = get_cli_path(userio) + + if list_all: + result = subprocess.run([cli_path, "board", "listall", "--format=json"], stdout=subprocess.PIPE) + else: + result = subprocess.run([cli_path, "board", "list", "--format=json"], stdout=subprocess.PIPE) + + userio.print("Called arduino-cli with:", verbose=True) + userio.print(result.args, verbose=True) + userio.print("Dumping arduino-cli response:", verbose=True) + userio.print(result.stdout.decode('utf-8'), verbose=True) + + if not result.returncode == 0: + userio.error("arduino-cli exited with code " + str(result.returncode)) + + try: + boards = json.loads(result.stdout.decode('utf-8')) + except Exception: + userio.error("arduino-cli returned invalid JSON") + + # arduino-cli board listall packes the result in a dict + if list_all: + if "boards" not in boards: + userio.error("Could not parse arduino-cli output") + boards = boards["boards"] + + # Filter out invalid entries (or unwanted) + if require_name: + boards = [board for board in boards if "name" in board] + if require_fqbn: + boards = [board for board in boards if "FQBN" in board] + if require_port: + boards = [board for board in boards if "port" in board] + + userio.print("Dumping processed arduino-cli response:", verbose=True) + userio.print(boards, verbose=True) + + return boards + + +def sketch_compile(userio, sketch_path): + boards = get_boards(userio, True, require_fqbn=True) + + # Query user to select a board + userio.print("Please select target board:") + terminal_menu = TerminalMenu([board["name"] for board in boards], menu_highlight_style=None) + + board = boards[terminal_menu.show()] + + userio.print("Selected board: ", verbose=True) + userio.print(board, verbose=True) + + # Compile sketch + cli_path = get_cli_path(userio) + subprocess.run([cli_path, "compile", "--fqbn", board["FQBN"], sketch_path]) diff --git a/webduino_generator/entrypoint.py b/webduino_generator/entrypoint.py index b6178a4..1380915 100644 --- a/webduino_generator/entrypoint.py +++ b/webduino_generator/entrypoint.py @@ -1,10 +1,12 @@ import argparse import subprocess +import json from .__init__ import __version__ from .userio import UserIO, get_ssid_pass from .helper import cpp_str_esc, cpp_img_esc, get_files_rec, shorten, get_tool from .project import project_make_new, project_generate, project_get_sketch +from .arduinocli import sketch_compile from .generator import * @@ -76,6 +78,17 @@ def command_open(userio, args): subprocess.call([ide_path, sketch_path]) +def command_compile(userio, args): + userio.section("Compiling project output") + + # Get project output location + sketch_path = project_get_sketch(userio, args.target) + userio.print("Sketch located: " + sketch_path, verbose=True) + + # Compile sketch using arduino-cli + sketch_compile(userio, sketch_path) + + def main(): install_traceback() userio = UserIO() @@ -144,6 +157,10 @@ def main(): help="Spawns IDE in a new thread") parser_compile = subparsers.add_parser("compile", help="Compile Arduino code from current project") + parser_compile.add_argument("target", metavar="target", type=str, + default=".", nargs="?", + help="Root folder of target project") + parser_upload = subparsers.add_parser("upload", help="Upload Arduino code from current project") parser_version = subparsers.add_parser("version", help="Display current version") @@ -179,7 +196,7 @@ def handle(): elif args.command == "build": command_build(userio, args) elif args.command == "compile": - raise NotImplementedError + command_compile(userio, args) elif args.command == "upload": raise NotImplementedError elif args.command == "open": From 2bdf111168443f2ce61db7fa31418aa5f81bfdf0 Mon Sep 17 00:00:00 2001 From: Levy Ehrstein Date: Fri, 14 Aug 2020 00:25:42 +0200 Subject: [PATCH 05/17] Implement upload command --- webduino_generator/arduinocli.py | 89 +++++++++++++++++++++++++++----- webduino_generator/entrypoint.py | 21 ++++++-- 2 files changed, 93 insertions(+), 17 deletions(-) diff --git a/webduino_generator/arduinocli.py b/webduino_generator/arduinocli.py index 450e9b5..ec6d987 100644 --- a/webduino_generator/arduinocli.py +++ b/webduino_generator/arduinocli.py @@ -14,8 +14,7 @@ def get_cli_path(userio): return cli_path - -def get_boards(userio, list_all=False, require_name=True, require_fqbn=False, require_port=False): +def get_boards_json(userio, list_all=False): cli_path = get_cli_path(userio) if list_all: @@ -36,19 +35,20 @@ def get_boards(userio, list_all=False, require_name=True, require_fqbn=False, re except Exception: userio.error("arduino-cli returned invalid JSON") + return boards + + +def get_boards(userio): + boards = get_boards_json(userio, True) + # arduino-cli board listall packes the result in a dict - if list_all: - if "boards" not in boards: - userio.error("Could not parse arduino-cli output") - boards = boards["boards"] + if "boards" not in boards: + userio.error("Could not parse arduino-cli output") + boards = boards["boards"] # Filter out invalid entries (or unwanted) - if require_name: - boards = [board for board in boards if "name" in board] - if require_fqbn: - boards = [board for board in boards if "FQBN" in board] - if require_port: - boards = [board for board in boards if "port" in board] + boards = [board for board in boards if "name" in board] + boards = [board for board in boards if "FQBN" in board] userio.print("Dumping processed arduino-cli response:", verbose=True) userio.print(boards, verbose=True) @@ -56,14 +56,49 @@ def get_boards(userio, list_all=False, require_name=True, require_fqbn=False, re return boards +def get_boards_connected(userio): + boards = get_boards_json(userio, False) + + processed = [] + for board in boards: + if "boards" not in board: + continue + if "address" not in board: + continue + if len(board["boards"]) != 1: + continue + if "FQBN" not in board["boards"][0]: + continue + if "name" not in board["boards"][0]: + continue + processed += [{ + "name": board["boards"][0]["name"], + "FQBN": board["boards"][0]["FQBN"], + "address": board["address"] + }] + + userio.print("Dumping processed arduino-cli response:", verbose=True) + userio.print(processed, verbose=True) + + return processed + + def sketch_compile(userio, sketch_path): - boards = get_boards(userio, True, require_fqbn=True) + boards = get_boards(userio) + + if len(boards) == 0: + userio.error("No boards found!") # Query user to select a board userio.print("Please select target board:") terminal_menu = TerminalMenu([board["name"] for board in boards], menu_highlight_style=None) - board = boards[terminal_menu.show()] + selection = terminal_menu.show() + + # Menu cancled by user + if selection is None: + exit(0) + board = boards[selection] userio.print("Selected board: ", verbose=True) userio.print(board, verbose=True) @@ -71,3 +106,29 @@ def sketch_compile(userio, sketch_path): # Compile sketch cli_path = get_cli_path(userio) subprocess.run([cli_path, "compile", "--fqbn", board["FQBN"], sketch_path]) + + +def sketch_upload(userio, sketch_path): + boards = get_boards_connected(userio) + + if len(boards) == 0: + userio.error("No boards found!") + + # Query user to select a board + userio.print("Please select target board:") + terminal_menu = TerminalMenu([board["address"] + ": " + board["name"] + for board in boards], menu_highlight_style=None) + + selection = terminal_menu.show() + + # Menu cancled by user + if selection is None: + exit(0) + board = boards[selection] + + userio.print("Selected board: ", verbose=True) + userio.print(board, verbose=True) + + # Compile sketch + cli_path = get_cli_path(userio) + subprocess.run([cli_path, "upload", "-p", board["address"], "--fqbn", board["FQBN"], sketch_path]) \ No newline at end of file diff --git a/webduino_generator/entrypoint.py b/webduino_generator/entrypoint.py index 1380915..0166ac5 100644 --- a/webduino_generator/entrypoint.py +++ b/webduino_generator/entrypoint.py @@ -6,7 +6,7 @@ from .userio import UserIO, get_ssid_pass from .helper import cpp_str_esc, cpp_img_esc, get_files_rec, shorten, get_tool from .project import project_make_new, project_generate, project_get_sketch -from .arduinocli import sketch_compile +from .arduinocli import sketch_compile, sketch_upload from .generator import * @@ -89,6 +89,17 @@ def command_compile(userio, args): sketch_compile(userio, sketch_path) +def command_upload(userio, args): + userio.section("Uploading project output") + + # Get project output location + sketch_path = project_get_sketch(userio, args.target) + userio.print("Sketch located: " + sketch_path, verbose=True) + + # Compile sketch using arduino-cli + sketch_upload(userio, sketch_path) + + def main(): install_traceback() userio = UserIO() @@ -160,8 +171,12 @@ def main(): parser_compile.add_argument("target", metavar="target", type=str, default=".", nargs="?", help="Root folder of target project") - + parser_upload = subparsers.add_parser("upload", help="Upload Arduino code from current project") + parser_upload.add_argument("target", metavar="target", type=str, + default=".", nargs="?", + help="Root folder of target project") + parser_version = subparsers.add_parser("version", help="Display current version") # Global arguments @@ -198,7 +213,7 @@ def handle(): elif args.command == "compile": command_compile(userio, args) elif args.command == "upload": - raise NotImplementedError + command_upload(userio, args) elif args.command == "open": command_open(userio, args) elif args.command == "generate": From f6563c9f83280d80c82821249252c0ac52865bc3 Mon Sep 17 00:00:00 2001 From: Levy Ehrstein Date: Fri, 14 Aug 2020 00:53:18 +0200 Subject: [PATCH 06/17] Refactor code; Read FQBN from project config --- webduino_generator/arduinocli.py | 21 ++++++++---- webduino_generator/entrypoint.py | 21 ++---------- webduino_generator/project.py | 56 +++++++++++++++++++++++++++++--- 3 files changed, 70 insertions(+), 28 deletions(-) diff --git a/webduino_generator/arduinocli.py b/webduino_generator/arduinocli.py index ec6d987..f743587 100644 --- a/webduino_generator/arduinocli.py +++ b/webduino_generator/arduinocli.py @@ -14,6 +14,7 @@ def get_cli_path(userio): return cli_path + def get_boards_json(userio, list_all=False): cli_path = get_cli_path(userio) @@ -83,7 +84,7 @@ def get_boards_connected(userio): return processed -def sketch_compile(userio, sketch_path): +def get_board(userio): boards = get_boards(userio) if len(boards) == 0: @@ -103,12 +104,10 @@ def sketch_compile(userio, sketch_path): userio.print("Selected board: ", verbose=True) userio.print(board, verbose=True) - # Compile sketch - cli_path = get_cli_path(userio) - subprocess.run([cli_path, "compile", "--fqbn", board["FQBN"], sketch_path]) + return board["name"], board["FQBN"] -def sketch_upload(userio, sketch_path): +def get_board_connected(userio): boards = get_boards_connected(userio) if len(boards) == 0: @@ -129,6 +128,16 @@ def sketch_upload(userio, sketch_path): userio.print("Selected board: ", verbose=True) userio.print(board, verbose=True) + return board["name"], board["FQBN"], board["address"] + + +def sketch_compile(userio, sketch_path, fqbn): # Compile sketch cli_path = get_cli_path(userio) - subprocess.run([cli_path, "upload", "-p", board["address"], "--fqbn", board["FQBN"], sketch_path]) \ No newline at end of file + subprocess.run([cli_path, "compile", "--fqbn", fqbn, sketch_path]) + + +def sketch_upload(userio, sketch_path, fqbn, address): + # Upload sketch + cli_path = get_cli_path(userio) + subprocess.run([cli_path, "upload", "-p", address, "--fqbn", fqbn, sketch_path]) diff --git a/webduino_generator/entrypoint.py b/webduino_generator/entrypoint.py index 0166ac5..bd49ec9 100644 --- a/webduino_generator/entrypoint.py +++ b/webduino_generator/entrypoint.py @@ -5,8 +5,7 @@ from .__init__ import __version__ from .userio import UserIO, get_ssid_pass from .helper import cpp_str_esc, cpp_img_esc, get_files_rec, shorten, get_tool -from .project import project_make_new, project_generate, project_get_sketch -from .arduinocli import sketch_compile, sketch_upload +from .project import project_make_new, project_generate, project_get_sketch, project_compile, project_upload from .generator import * @@ -79,25 +78,11 @@ def command_open(userio, args): def command_compile(userio, args): - userio.section("Compiling project output") - - # Get project output location - sketch_path = project_get_sketch(userio, args.target) - userio.print("Sketch located: " + sketch_path, verbose=True) - - # Compile sketch using arduino-cli - sketch_compile(userio, sketch_path) + project_compile(userio, args.target) def command_upload(userio, args): - userio.section("Uploading project output") - - # Get project output location - sketch_path = project_get_sketch(userio, args.target) - userio.print("Sketch located: " + sketch_path, verbose=True) - - # Compile sketch using arduino-cli - sketch_upload(userio, sketch_path) + project_upload(userio, args.target) def main(): diff --git a/webduino_generator/project.py b/webduino_generator/project.py index 0a8be68..31f9331 100644 --- a/webduino_generator/project.py +++ b/webduino_generator/project.py @@ -5,11 +5,16 @@ from .helper import get_files_rec from .generator import get_template_path, get_demo_path, generate +from .arduinocli import sketch_compile, sketch_upload, get_board, get_board_connected from .userio import get_ssid_pass +def project_get_configfile(project_path): + return os.path.join(project_path, "project.wgen") + + def project_check(target): - project_file = os.path.join(target, "project.wgen") + project_file = project_get_configfile(target) return os.path.exists(project_file) and os.path.isfile(project_file) @@ -39,7 +44,7 @@ def project_config_make(input_path, template_path, output_path, def project_config_readproject(userio, config_path): config = configparser.ConfigParser() - config.read(os.path.join(config_path, "project.wgen")) + config.read(project_get_configfile(config_path)) if "PROJECT" not in config.sections() or \ "input_path" not in config["PROJECT"] or \ @@ -56,7 +61,7 @@ def project_config_readproject(userio, config_path): def project_config_readmeta(userio, config_path): config = configparser.ConfigParser() - config.read(os.path.join(config_path, "project.wgen")) + config.read(project_get_configfile(config_path)) if "METADATA" not in config.sections(): userio.error("Invalid project file!") @@ -64,6 +69,19 @@ def project_config_readmeta(userio, config_path): return config["METADATA"] +def project_config_readfqbn(userio, config_path): + config = configparser.ConfigParser() + config.read(project_get_configfile(config_path)) + + if "TARGET" not in config.sections(): + return None + + if "FQBN" not in config["TARGET"]: + return None + + return config["TARGET"]["FQBN"] + + def project_get_sketch(userio, project_path): '''Returns location of arduino output sketch. (main.ino) Returns None if project was not build yet.''' @@ -125,7 +143,7 @@ def project_make_new(userio, project_path, delete_block, mode, ssid, port): path_template_src = get_template_path() path_config = os.path.join(project_path, ".wgen") - path_config_file = os.path.join(project_path, "project.wgen") + path_config_file = project_get_configfile(project_path) # Helper function to delete file or folder def delete_file_or_folder(target): @@ -193,3 +211,33 @@ def project_generate(userio, target, quiet): # Eventually create output generate(userio, input_path, output_path, template_path, meta_data) + + +def project_compile(userio, project_path): + userio.section("Compiling project output") + + # Get project output location + sketch_path = project_get_sketch(userio, project_path) + userio.print("Sketch located: " + sketch_path, verbose=True) + + # Get target FQBN + fqbn = project_config_readfqbn(userio, project_path) + if fqbn is None: + name, fqbn = get_board(userio) + + # Compile sketch using arduino-cli + sketch_compile(userio, sketch_path, fqbn) + + +def project_upload(userio, project_path): + userio.section("Compiling project output") + + # Get project output location + sketch_path = project_get_sketch(userio, project_path) + userio.print("Sketch located: " + sketch_path, verbose=True) + + # Get target FQBN + name, fqbn, address = get_board_connected(userio) + + # Compile sketch using arduino-cli + sketch_upload(userio, sketch_path, fqbn, address) From c0f17790fdbef557cf5c510b27fa516835faf4e2 Mon Sep 17 00:00:00 2001 From: Levy Ehrstein Date: Fri, 14 Aug 2020 00:57:53 +0200 Subject: [PATCH 07/17] Add --select-device option to compile command --- webduino_generator/entrypoint.py | 6 +++++- webduino_generator/project.py | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/webduino_generator/entrypoint.py b/webduino_generator/entrypoint.py index bd49ec9..5ca4839 100644 --- a/webduino_generator/entrypoint.py +++ b/webduino_generator/entrypoint.py @@ -78,7 +78,8 @@ def command_open(userio, args): def command_compile(userio, args): - project_compile(userio, args.target) + project_compile(userio, args.target, + force_select=args.select_device) def command_upload(userio, args): @@ -156,6 +157,9 @@ def main(): parser_compile.add_argument("target", metavar="target", type=str, default=".", nargs="?", help="Root folder of target project") + parser_compile.add_argument("--select-device", + action="store_true", dest='select_device', + help="Ignore saved target device and select another one") parser_upload = subparsers.add_parser("upload", help="Upload Arduino code from current project") parser_upload.add_argument("target", metavar="target", type=str, diff --git a/webduino_generator/project.py b/webduino_generator/project.py index 31f9331..3f7baec 100644 --- a/webduino_generator/project.py +++ b/webduino_generator/project.py @@ -213,7 +213,7 @@ def project_generate(userio, target, quiet): generate(userio, input_path, output_path, template_path, meta_data) -def project_compile(userio, project_path): +def project_compile(userio, project_path, force_select=False): userio.section("Compiling project output") # Get project output location @@ -222,7 +222,7 @@ def project_compile(userio, project_path): # Get target FQBN fqbn = project_config_readfqbn(userio, project_path) - if fqbn is None: + if force_select or fqbn is None: name, fqbn = get_board(userio) # Compile sketch using arduino-cli From eafdfd02f4148d42b866e1f1e51fc3e36c3ecf81 Mon Sep 17 00:00:00 2001 From: Levy Ehrstein Date: Fri, 14 Aug 2020 01:07:00 +0200 Subject: [PATCH 08/17] Add options to compile to save and select targets --- webduino_generator/entrypoint.py | 5 ++++- webduino_generator/project.py | 16 +++++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/webduino_generator/entrypoint.py b/webduino_generator/entrypoint.py index 5ca4839..f0660cc 100644 --- a/webduino_generator/entrypoint.py +++ b/webduino_generator/entrypoint.py @@ -78,7 +78,7 @@ def command_open(userio, args): def command_compile(userio, args): - project_compile(userio, args.target, + project_compile(userio, args.target, save=args.save, force_select=args.select_device) @@ -160,6 +160,9 @@ def main(): parser_compile.add_argument("--select-device", action="store_true", dest='select_device', help="Ignore saved target device and select another one") + parser_compile.add_argument("--save", + action="store_true", dest='save', + help="Safe selected target to project files") parser_upload = subparsers.add_parser("upload", help="Upload Arduino code from current project") parser_upload.add_argument("target", metavar="target", type=str, diff --git a/webduino_generator/project.py b/webduino_generator/project.py index 3f7baec..45f47f8 100644 --- a/webduino_generator/project.py +++ b/webduino_generator/project.py @@ -82,6 +82,18 @@ def project_config_readfqbn(userio, config_path): return config["TARGET"]["FQBN"] +def project_config_writefqbn(userio, config_path, fqbn): + config = configparser.ConfigParser() + config.read(project_get_configfile(config_path)) + + if "TARGET" not in config.sections(): + config["TARGET"] = {} + config["TARGET"]["FQBN"] = fqbn + + with open(project_get_configfile(config_path), "w") as file: + config.write(file) + + def project_get_sketch(userio, project_path): '''Returns location of arduino output sketch. (main.ino) Returns None if project was not build yet.''' @@ -213,7 +225,7 @@ def project_generate(userio, target, quiet): generate(userio, input_path, output_path, template_path, meta_data) -def project_compile(userio, project_path, force_select=False): +def project_compile(userio, project_path, force_select=False, save=False): userio.section("Compiling project output") # Get project output location @@ -224,6 +236,8 @@ def project_compile(userio, project_path, force_select=False): fqbn = project_config_readfqbn(userio, project_path) if force_select or fqbn is None: name, fqbn = get_board(userio) + if save: + project_config_writefqbn(userio, project_path, fqbn) # Compile sketch using arduino-cli sketch_compile(userio, sketch_path, fqbn) From cada41985c8b839690ea8251a84beb58c5aa4a1f Mon Sep 17 00:00:00 2001 From: Levy Ehrstein Date: Fri, 14 Aug 2020 13:40:45 +0200 Subject: [PATCH 09/17] Refactor project.py (OOP, WIP) --- webduino_generator/entrypoint.py | 16 +- webduino_generator/project.py | 440 ++++++++++++++++--------------- 2 files changed, 234 insertions(+), 222 deletions(-) diff --git a/webduino_generator/entrypoint.py b/webduino_generator/entrypoint.py index f0660cc..3a7099b 100644 --- a/webduino_generator/entrypoint.py +++ b/webduino_generator/entrypoint.py @@ -5,7 +5,7 @@ from .__init__ import __version__ from .userio import UserIO, get_ssid_pass from .helper import cpp_str_esc, cpp_img_esc, get_files_rec, shorten, get_tool -from .project import project_make_new, project_generate, project_get_sketch, project_compile, project_upload +from .project import Project from .generator import * @@ -45,12 +45,13 @@ def command_generate(userio, args): def command_init(userio, args): - project_make_new(userio, args.target, args.force, - args.mode, args.ssid, args.port) + Project.project_make_new(userio, args.target, args.force, + args.mode, args.ssid, args.port) def command_build(userio, args): - project_generate(userio, args.target, args.quiet) + project = Project(userio, args.target) + project.project_generate(args.quiet) def command_open(userio, args): @@ -78,12 +79,13 @@ def command_open(userio, args): def command_compile(userio, args): - project_compile(userio, args.target, save=args.save, - force_select=args.select_device) + project = Project(userio, args.target) + project.project_compile(save=args.save, force_select=args.select_device) def command_upload(userio, args): - project_upload(userio, args.target) + project = Project(userio, args.target) + project.project_upload() def main(): diff --git a/webduino_generator/project.py b/webduino_generator/project.py index 45f47f8..4b6e16a 100644 --- a/webduino_generator/project.py +++ b/webduino_generator/project.py @@ -8,250 +8,260 @@ from .arduinocli import sketch_compile, sketch_upload, get_board, get_board_connected from .userio import get_ssid_pass +class Project(): + userio = None + root_path = "" + + def __init__(self, userio, root_path): + self.userio = userio + self.root_path = root_path + + def get_config_file_path(self): + '''Returns path to project.wgen of current project. + Program is exited if no config is found''' + + config_path = os.path.join(self.root_path, "project.wgen") + return config_path + + def project_check(self): + config_path = self.get_config_file_path() + if not os.path.exists(config_path) or \ + not os.path.isfile(config_path): + return False + return True + + @staticmethod + def make_config(input_path, template_path, output_path, + mode, ssid, port) -> str: + '''Returns content of default config as string''' + + # Make new config file with default content + config = configparser.ConfigParser() + config["PROJECT"] = \ + { + "input_path": input_path, + "template_path": template_path, + "output_path": output_path, + } + config["METADATA"] = \ + { + "mode": mode, + "ssid": ssid, + "port": port, + } + + # Write to buffer and return content + with io.StringIO() as buffer: + config.write(buffer) + buffer.seek(0) + return buffer.read() + + def read_project(self): + config = configparser.ConfigParser() + config.read(self.get_config_file_path()) + + if "PROJECT" not in config.sections() or \ + "input_path" not in config["PROJECT"] or \ + "output_path" not in config["PROJECT"] or \ + "template_path" not in config["PROJECT"]: + self.userio.error("Invalid project file!") + + input_path = os.path.join(self.root_path, config["PROJECT"]["input_path"]) + output_path = os.path.join(self.root_path, config["PROJECT"]["output_path"]) + template_path = os.path.join(self.root_path, config["PROJECT"]["template_path"]) + + return input_path, output_path, template_path + + def project_config_readmeta(self): + config = configparser.ConfigParser() + config.read(self.get_config_file_path()) + + if "METADATA" not in config.sections(): + self.userio.error("Invalid project file!") + + return config["METADATA"] + + def project_config_readfqbn(self): + config = configparser.ConfigParser() + config.read(self.get_config_file_path()) + + if "TARGET" not in config.sections(): + return None + + if "FQBN" not in config["TARGET"]: + return None + + return config["TARGET"]["FQBN"] + + def project_config_writefqbn(self, fqbn): + config = configparser.ConfigParser() + config.read(self.get_config_file_path()) + + if "TARGET" not in config.sections(): + config["TARGET"] = {} + config["TARGET"]["FQBN"] = fqbn + + with open(self.get_config_file_path(), "w") as file: + config.write(file) + + def project_get_sketch(self): + '''Returns location of arduino output sketch. (main.ino) + Returns None if project was not build yet.''' + + # Check project + if not self.project_check(): + self.userio.error("Target project not found!") + + # Read project data + input_path, output_path, template_path = self.read_project() + + # Check if project has been build yet + sketch_path = os.path.join(output_path, "main", "main.ino") + if not os.path.exists(sketch_path) or not os.path.isfile(sketch_path): + return None + + return sketch_path + + @staticmethod + def project_make_new(userio, project_path, delete_block, mode, ssid, port): + # Check port + if port < 0 or port > 65535: + userio.error("Invalid port!") + + # Check mode + if mode.lower() != "wifinina": + userio.error("Target mode not supported!\nSupported modes: wifinina") + + # Check ssid + if ssid == "": + ssid = userio.get_user("Please enter network credentials:", "SSID: ") + + # Check if target exists + if not os.path.exists(project_path): + userio.error("Path " + project_path + " is invalid or does not exist!") -def project_get_configfile(project_path): - return os.path.join(project_path, "project.wgen") + userio.section("Generating 'hello world' project") + # Check if target is empty + if len(get_files_rec(project_path)) > 0: + userio.warn("Target folder (%s) is not empty!" + % os.path.abspath(project_path)) -def project_check(target): - project_file = project_get_configfile(target) - return os.path.exists(project_file) and os.path.isfile(project_file) + userio.warn("Data will %sbe deleted by this action!" + % ("" if delete_block else "not ")) + userio.print("Press Enter to continue anyway. Ctrl+C to cancel!") + try: + input() + except KeyboardInterrupt: + return -def project_config_make(input_path, template_path, output_path, - mode, ssid, port) -> str: - # Make new config file with default content - config = configparser.ConfigParser() - config["PROJECT"] = \ - { - "input_path": input_path, - "template_path": template_path, - "output_path": output_path, - } - config["METADATA"] = \ - { - "mode": mode, - "ssid": ssid, - "port": port, - } + # Open project + project = Project(userio, project_path) - # Write to buffer and return content - with io.StringIO() as buffer: - config.write(buffer) - buffer.seek(0) - return buffer.read() + # Get paths to target files/folders beforehand + path_input = os.path.join(project_path, "input") + path_output = os.path.join(project_path, "output") + path_template = os.path.join(project_path, "template") + path_input_src = get_demo_path() + path_template_src = get_template_path() -def project_config_readproject(userio, config_path): - config = configparser.ConfigParser() - config.read(project_get_configfile(config_path)) + path_config = os.path.join(project_path, ".wgen") + path_config_file = project.get_config_file_path() - if "PROJECT" not in config.sections() or \ - "input_path" not in config["PROJECT"] or \ - "output_path" not in config["PROJECT"] or \ - "template_path" not in config["PROJECT"]: - userio.error("Invalid project file!") - - input_path = os.path.join(config_path, config["PROJECT"]["input_path"]) - output_path = os.path.join(config_path, config["PROJECT"]["output_path"]) - template_path = os.path.join(config_path, config["PROJECT"]["template_path"]) - - return input_path, output_path, template_path - - -def project_config_readmeta(userio, config_path): - config = configparser.ConfigParser() - config.read(project_get_configfile(config_path)) - - if "METADATA" not in config.sections(): - userio.error("Invalid project file!") - - return config["METADATA"] - - -def project_config_readfqbn(userio, config_path): - config = configparser.ConfigParser() - config.read(project_get_configfile(config_path)) - - if "TARGET" not in config.sections(): - return None - - if "FQBN" not in config["TARGET"]: - return None - - return config["TARGET"]["FQBN"] - - -def project_config_writefqbn(userio, config_path, fqbn): - config = configparser.ConfigParser() - config.read(project_get_configfile(config_path)) - - if "TARGET" not in config.sections(): - config["TARGET"] = {} - config["TARGET"]["FQBN"] = fqbn - - with open(project_get_configfile(config_path), "w") as file: - config.write(file) - - -def project_get_sketch(userio, project_path): - '''Returns location of arduino output sketch. (main.ino) - Returns None if project was not build yet.''' - - # Check project - if not project_check(project_path): - userio.error("Target project not found!") - - # Read project data - input_path, output_path, template_path = project_config_readproject(userio, project_path) - - # Check if project has been build yet - sketch_path = os.path.join(output_path, "main", "main.ino") - if not os.path.exists(sketch_path) or not os.path.isfile(sketch_path): - return None - - return sketch_path - - -def project_make_new(userio, project_path, delete_block, mode, ssid, port): - # Check port - if port < 0 or port > 65535: - userio.error("Invalid port!") - - # Check mode - if mode.lower() != "wifinina": - userio.error("Target mode not supported!\nSupported modes: wifinina") - - # Check ssid - if ssid == "": - ssid = userio.get_user("Please enter network credentials:", "SSID: ") - - # Check if target exists - if not os.path.exists(project_path): - userio.error("Path " + project_path + " is invalid or does not exist!") - - userio.section("Generating 'hello world' project") - - # Check if target is empty - if len(get_files_rec(project_path)) > 0: - userio.warn("Target folder (%s) is not empty!" - % os.path.abspath(project_path)) - - userio.warn("Data will %sbe deleted by this action!" - % ("" if delete_block else "not ")) - - userio.print("Press Enter to continue anyway. Ctrl+C to cancel!") - try: - input() - except KeyboardInterrupt: - return - - # Get paths to target files/folders beforehand - path_input = os.path.join(project_path, "input") - path_output = os.path.join(project_path, "output") - path_template = os.path.join(project_path, "template") - - path_input_src = get_demo_path() - path_template_src = get_template_path() - - path_config = os.path.join(project_path, ".wgen") - path_config_file = project_get_configfile(project_path) - - # Helper function to delete file or folder - def delete_file_or_folder(target): - if os.path.isfile(target): - os.remove(target) - else: - shutil.rmtree(target) - - def error_or_delete(target, name): - if os.path.exists(target): - if delete_block: - delete_file_or_folder(target) + # Helper function to delete file or folder + def delete_file_or_folder(target): + if os.path.isfile(target): + os.remove(target) else: - userio.error(name + " exists! (" + target + ")") - - # Check target files before we start - # as not to leave a half initialized project - error_or_delete(path_config, "Config folder") - error_or_delete(path_config_file, "Config file") - error_or_delete(path_input, "Input folder") - error_or_delete(path_template, "Template folder") - error_or_delete(path_output, "Output folder") - - # Eventually, create project files - userio.print("Creating project files") + shutil.rmtree(target) - # Project config folder - os.mkdir(path_config) + def error_or_delete(target, name): + if os.path.exists(target): + if delete_block: + delete_file_or_folder(target) + else: + userio.error(name + " exists! (" + target + ")") - # Project config file - with open(path_config_file, "w") as config_file: - config_file.write(project_config_make(path_input, path_template, - path_output, mode, ssid, port)) + # Check target files before we start + # as not to leave a half initialized project + error_or_delete(path_config, "Config folder") + error_or_delete(path_config_file, "Config file") + error_or_delete(path_input, "Input folder") + error_or_delete(path_template, "Template folder") + error_or_delete(path_output, "Output folder") - # Project output folder - userio.print("Creating output folder", verbose=True) - os.mkdir(path_output) + # Eventually, create project files + userio.print("Creating project files") - # Project output folder - userio.print("Creating input files", verbose=True) - shutil.copytree(path_input_src, path_input) + # Project config folder + os.mkdir(path_config) - userio.print("Creating template files", verbose=True) - shutil.copytree(path_template_src, path_template) + # Project config file + with open(path_config_file, "w") as config_file: + config_file.write(Project.make_config(path_input, path_template, + path_output, mode, ssid, port)) - userio.section("Project created successfully.") - userio.print("Use 'webduino-generator build' to build your project.") + # Project output folder + userio.print("Creating output folder", verbose=True) + os.mkdir(path_output) + # Project output folder + userio.print("Creating input files", verbose=True) + shutil.copytree(path_input_src, path_input) -def project_generate(userio, target, quiet): - # Check project - if not project_check(target): - userio.error("Target project not found!") + userio.print("Creating template files", verbose=True) + shutil.copytree(path_template_src, path_template) - # Read project data - input_path, output_path, template_path = project_config_readproject(userio, target) - meta_data = project_config_readmeta(userio, target) + userio.section("Project created successfully.") + userio.print("Use 'webduino-generator build' to build your project.") - # Enter ssid is none is in config - if "ssid" not in meta_data: - meta_data["ssid"] = "" + def project_generate(self, quiet): + # Check project + if not self.project_check(): + self.userio.error("Target project not found!") - # Get password (and ssid if necessary) - meta_data["ssid"], meta_data["pass"] = get_ssid_pass(userio, meta_data["ssid"], quiet) + # Read project data + input_path, output_path, template_path = self.read_project() + meta_data = self.project_config_readmeta() - # Eventually create output - generate(userio, input_path, output_path, template_path, meta_data) + # Enter ssid is none is in config + if "ssid" not in meta_data: + meta_data["ssid"] = "" + # Get password (and ssid if necessary) + meta_data["ssid"], meta_data["pass"] = get_ssid_pass(self.userio, meta_data["ssid"], quiet) -def project_compile(userio, project_path, force_select=False, save=False): - userio.section("Compiling project output") + # Eventually create output + generate(self.userio, input_path, output_path, template_path, meta_data) - # Get project output location - sketch_path = project_get_sketch(userio, project_path) - userio.print("Sketch located: " + sketch_path, verbose=True) + def project_compile(self, force_select=False, save=False): + self.userio.section("Compiling project output") - # Get target FQBN - fqbn = project_config_readfqbn(userio, project_path) - if force_select or fqbn is None: - name, fqbn = get_board(userio) - if save: - project_config_writefqbn(userio, project_path, fqbn) + # Get project output location + sketch_path = self.project_get_sketch() + self.userio.print("Sketch located: " + sketch_path, verbose=True) - # Compile sketch using arduino-cli - sketch_compile(userio, sketch_path, fqbn) + # Get target FQBN + fqbn = self.project_config_readfqbn() + if force_select or fqbn is None: + name, fqbn = get_board(self.userio) + if save: + self.project_config_writefqbn(fqbn) + # Compile sketch using arduino-cli + sketch_compile(self.userio, sketch_path, fqbn) -def project_upload(userio, project_path): - userio.section("Compiling project output") + def project_upload(self): + self.userio.section("Compiling project output") - # Get project output location - sketch_path = project_get_sketch(userio, project_path) - userio.print("Sketch located: " + sketch_path, verbose=True) + # Get project output location + sketch_path = self.project_get_sketch() + self.userio.print("Sketch located: " + sketch_path, verbose=True) - # Get target FQBN - name, fqbn, address = get_board_connected(userio) + # Get target FQBN + name, fqbn, address = get_board_connected(self.userio) - # Compile sketch using arduino-cli - sketch_upload(userio, sketch_path, fqbn, address) + # Compile sketch using arduino-cli + sketch_upload(self.userio, sketch_path, fqbn, address) From 75e9c450c15990216644c0cb8dc4859f4278084c Mon Sep 17 00:00:00 2001 From: Levy Ehrstein Date: Fri, 14 Aug 2020 14:00:11 +0200 Subject: [PATCH 10/17] Refactor code --- webduino_generator/entrypoint.py | 8 +- webduino_generator/project.py | 211 ++++++++++++++++--------------- 2 files changed, 115 insertions(+), 104 deletions(-) diff --git a/webduino_generator/entrypoint.py b/webduino_generator/entrypoint.py index 3a7099b..07d6780 100644 --- a/webduino_generator/entrypoint.py +++ b/webduino_generator/entrypoint.py @@ -45,13 +45,13 @@ def command_generate(userio, args): def command_init(userio, args): - Project.project_make_new(userio, args.target, args.force, + Project.create_project(userio, args.target, args.force, args.mode, args.ssid, args.port) def command_build(userio, args): project = Project(userio, args.target) - project.project_generate(args.quiet) + project.generate(args.quiet) def command_open(userio, args): @@ -80,12 +80,12 @@ def command_open(userio, args): def command_compile(userio, args): project = Project(userio, args.target) - project.project_compile(save=args.save, force_select=args.select_device) + project.compile(save=args.save, force_select=args.select_device) def command_upload(userio, args): project = Project(userio, args.target) - project.project_upload() + project.upload() def main(): diff --git a/webduino_generator/project.py b/webduino_generator/project.py index 4b6e16a..43c4c15 100644 --- a/webduino_generator/project.py +++ b/webduino_generator/project.py @@ -8,28 +8,11 @@ from .arduinocli import sketch_compile, sketch_upload, get_board, get_board_connected from .userio import get_ssid_pass + class Project(): userio = None root_path = "" - def __init__(self, userio, root_path): - self.userio = userio - self.root_path = root_path - - def get_config_file_path(self): - '''Returns path to project.wgen of current project. - Program is exited if no config is found''' - - config_path = os.path.join(self.root_path, "project.wgen") - return config_path - - def project_check(self): - config_path = self.get_config_file_path() - if not os.path.exists(config_path) or \ - not os.path.isfile(config_path): - return False - return True - @staticmethod def make_config(input_path, template_path, output_path, mode, ssid, port) -> str: @@ -56,74 +39,10 @@ def make_config(input_path, template_path, output_path, buffer.seek(0) return buffer.read() - def read_project(self): - config = configparser.ConfigParser() - config.read(self.get_config_file_path()) - - if "PROJECT" not in config.sections() or \ - "input_path" not in config["PROJECT"] or \ - "output_path" not in config["PROJECT"] or \ - "template_path" not in config["PROJECT"]: - self.userio.error("Invalid project file!") - - input_path = os.path.join(self.root_path, config["PROJECT"]["input_path"]) - output_path = os.path.join(self.root_path, config["PROJECT"]["output_path"]) - template_path = os.path.join(self.root_path, config["PROJECT"]["template_path"]) - - return input_path, output_path, template_path - - def project_config_readmeta(self): - config = configparser.ConfigParser() - config.read(self.get_config_file_path()) - - if "METADATA" not in config.sections(): - self.userio.error("Invalid project file!") - - return config["METADATA"] - - def project_config_readfqbn(self): - config = configparser.ConfigParser() - config.read(self.get_config_file_path()) - - if "TARGET" not in config.sections(): - return None - - if "FQBN" not in config["TARGET"]: - return None - - return config["TARGET"]["FQBN"] - - def project_config_writefqbn(self, fqbn): - config = configparser.ConfigParser() - config.read(self.get_config_file_path()) - - if "TARGET" not in config.sections(): - config["TARGET"] = {} - config["TARGET"]["FQBN"] = fqbn - - with open(self.get_config_file_path(), "w") as file: - config.write(file) - - def project_get_sketch(self): - '''Returns location of arduino output sketch. (main.ino) - Returns None if project was not build yet.''' - - # Check project - if not self.project_check(): - self.userio.error("Target project not found!") - - # Read project data - input_path, output_path, template_path = self.read_project() - - # Check if project has been build yet - sketch_path = os.path.join(output_path, "main", "main.ino") - if not os.path.exists(sketch_path) or not os.path.isfile(sketch_path): - return None - - return sketch_path - @staticmethod - def project_make_new(userio, project_path, delete_block, mode, ssid, port): + def create_project(userio, project_path, delete_block, mode, ssid, port): + '''Creates new project in specified location''' + # Check port if port < 0 or port > 65535: userio.error("Invalid port!") @@ -156,8 +75,8 @@ def project_make_new(userio, project_path, delete_block, mode, ssid, port): except KeyboardInterrupt: return - # Open project - project = Project(userio, project_path) + # Open project and do not check if its a valid one + project = Project(userio, project_path, check_valid=False) # Get paths to target files/folders beforehand path_input = os.path.join(project_path, "input") @@ -167,7 +86,7 @@ def project_make_new(userio, project_path, delete_block, mode, ssid, port): path_input_src = get_demo_path() path_template_src = get_template_path() - path_config = os.path.join(project_path, ".wgen") + path_config = project.get_config_folder_path() path_config_file = project.get_config_file_path() # Helper function to delete file or folder @@ -217,14 +136,106 @@ def error_or_delete(target, name): userio.section("Project created successfully.") userio.print("Use 'webduino-generator build' to build your project.") - def project_generate(self, quiet): - # Check project - if not self.project_check(): - self.userio.error("Target project not found!") + def __init__(self, userio, root_path, check_valid=True): + '''Open existing project. Will error if invalid project + is passed and check_valid is True''' + + self.userio = userio + self.root_path = root_path + + if check_valid and not self.check(): + self.userio.error("Invalid project passed.") + + def check(self): + '''Checks whether current project is a valid project''' + + config_path = self.get_config_file_path() + if not os.path.exists(config_path) or \ + not os.path.isfile(config_path): + return False + return True + + def get_config_file_path(self): + '''Returns path to project.wgen of current project.''' + + config_path = os.path.join(self.root_path, "project.wgen") + return config_path + + def get_config_folder_path(self): + '''Returns path to .wgen config folder of current project.''' + + config_path = os.path.join(self.root_path, ".wgen") + return config_path + + def get_sketch_path(self): + '''Returns location of arduino output sketch. (main.ino) + Returns None if project was not build yet.''' + + # Read project data + input_path, output_path, template_path = self.read_config_project() + + # Check if project has been build yet + sketch_path = os.path.join(output_path, "main", "main.ino") + if not os.path.exists(sketch_path) or not os.path.isfile(sketch_path): + return None + + return sketch_path + + def read_config_project(self): + '''Returns input_path, output_path, template_path of current + project.''' + config = configparser.ConfigParser() + config.read(self.get_config_file_path()) + + if "PROJECT" not in config.sections() or \ + "input_path" not in config["PROJECT"] or \ + "output_path" not in config["PROJECT"] or \ + "template_path" not in config["PROJECT"]: + self.userio.error("Invalid project file!") + + input_path = os.path.join(self.root_path, config["PROJECT"]["input_path"]) + output_path = os.path.join(self.root_path, config["PROJECT"]["output_path"]) + template_path = os.path.join(self.root_path, config["PROJECT"]["template_path"]) + + return input_path, output_path, template_path + + def read_config_meta(self): + config = configparser.ConfigParser() + config.read(self.get_config_file_path()) + + if "METADATA" not in config.sections(): + self.userio.error("Invalid project file!") + + return config["METADATA"] + + def read_config_fqbn(self): + config = configparser.ConfigParser() + config.read(self.get_config_file_path()) + + if "TARGET" not in config.sections(): + return None + + if "FQBN" not in config["TARGET"]: + return None + + return config["TARGET"]["FQBN"] + + def write_config_fqbn(self, fqbn): + config = configparser.ConfigParser() + config.read(self.get_config_file_path()) + + if "TARGET" not in config.sections(): + config["TARGET"] = {} + config["TARGET"]["FQBN"] = fqbn + + with open(self.get_config_file_path(), "w") as file: + config.write(file) + + def generate(self, quiet): # Read project data - input_path, output_path, template_path = self.read_project() - meta_data = self.project_config_readmeta() + input_path, output_path, template_path = self.read_config_project() + meta_data = self.read_config_meta() # Enter ssid is none is in config if "ssid" not in meta_data: @@ -236,28 +247,28 @@ def project_generate(self, quiet): # Eventually create output generate(self.userio, input_path, output_path, template_path, meta_data) - def project_compile(self, force_select=False, save=False): + def compile(self, force_select=False, save=False): self.userio.section("Compiling project output") # Get project output location - sketch_path = self.project_get_sketch() + sketch_path = self.get_sketch_path() self.userio.print("Sketch located: " + sketch_path, verbose=True) # Get target FQBN - fqbn = self.project_config_readfqbn() + fqbn = self.read_config_fqbn() if force_select or fqbn is None: name, fqbn = get_board(self.userio) if save: - self.project_config_writefqbn(fqbn) + self.write_config_fqbn(fqbn) # Compile sketch using arduino-cli sketch_compile(self.userio, sketch_path, fqbn) - def project_upload(self): + def upload(self): self.userio.section("Compiling project output") # Get project output location - sketch_path = self.project_get_sketch() + sketch_path = self.get_sketch_path() self.userio.print("Sketch located: " + sketch_path, verbose=True) # Get target FQBN From 762e32cb1dde060038ff7f03eab716551b62b834 Mon Sep 17 00:00:00 2001 From: Levy Ehrstein Date: Fri, 14 Aug 2020 14:03:02 +0200 Subject: [PATCH 11/17] Fix open command --- webduino_generator/{arduinocli.py => arduino.py} | 0 webduino_generator/entrypoint.py | 5 +++-- webduino_generator/project.py | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) rename webduino_generator/{arduinocli.py => arduino.py} (100%) diff --git a/webduino_generator/arduinocli.py b/webduino_generator/arduino.py similarity index 100% rename from webduino_generator/arduinocli.py rename to webduino_generator/arduino.py diff --git a/webduino_generator/entrypoint.py b/webduino_generator/entrypoint.py index 07d6780..c5eb5bb 100644 --- a/webduino_generator/entrypoint.py +++ b/webduino_generator/entrypoint.py @@ -46,7 +46,7 @@ def command_generate(userio, args): def command_init(userio, args): Project.create_project(userio, args.target, args.force, - args.mode, args.ssid, args.port) + args.mode, args.ssid, args.port) def command_build(userio, args): @@ -58,7 +58,8 @@ def command_open(userio, args): userio.section("Opening project output") # Get project output location - sketch_path = project_get_sketch(userio, args.target) + project = Project(userio, args.target) + sketch_path = project.get_sketch_path() userio.print("Sketch located: " + sketch_path, verbose=True) # Get arduino IDE location diff --git a/webduino_generator/project.py b/webduino_generator/project.py index 43c4c15..ce5a821 100644 --- a/webduino_generator/project.py +++ b/webduino_generator/project.py @@ -5,7 +5,7 @@ from .helper import get_files_rec from .generator import get_template_path, get_demo_path, generate -from .arduinocli import sketch_compile, sketch_upload, get_board, get_board_connected +from .arduino import sketch_compile, sketch_upload, get_board, get_board_connected from .userio import get_ssid_pass From f8cc5e28788cdb0719cb33c37b30538470008be5 Mon Sep 17 00:00:00 2001 From: Levy Ehrstein Date: Fri, 14 Aug 2020 14:04:17 +0200 Subject: [PATCH 12/17] Refactor code --- webduino_generator/arduino.py | 10 ++++++++++ webduino_generator/entrypoint.py | 6 ++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/webduino_generator/arduino.py b/webduino_generator/arduino.py index f743587..4036f30 100644 --- a/webduino_generator/arduino.py +++ b/webduino_generator/arduino.py @@ -5,6 +5,16 @@ from .helper import get_tool +def get_ide_path(userio): + # Get arduino IDE location + ide_path = get_tool("arduino") + if ide_path is None: + userio.error("Could not locate 'arduino' command. Is the arduino IDE istalled?") + userio.print("IDE located: " + ide_path, verbose=True) + + return ide_path + + def get_cli_path(userio): # Get arduino IDE location cli_path = get_tool("arduino-cli") diff --git a/webduino_generator/entrypoint.py b/webduino_generator/entrypoint.py index c5eb5bb..0b192ea 100644 --- a/webduino_generator/entrypoint.py +++ b/webduino_generator/entrypoint.py @@ -6,6 +6,7 @@ from .userio import UserIO, get_ssid_pass from .helper import cpp_str_esc, cpp_img_esc, get_files_rec, shorten, get_tool from .project import Project +from .arduino import get_ide_path from .generator import * @@ -63,10 +64,7 @@ def command_open(userio, args): userio.print("Sketch located: " + sketch_path, verbose=True) # Get arduino IDE location - ide_path = get_tool("arduino") - if ide_path is None: - userio.error("Could not locate 'arduino' command. Is an arduino IDE istalled?") - userio.print("IDE located: " + ide_path, verbose=True) + ide_path = get_ide_path(userio) # Launch IDE if args.detach: From 698d1853aebcdbb808480faa4cd9092f7e2f296c Mon Sep 17 00:00:00 2001 From: Levy Ehrstein Date: Fri, 14 Aug 2020 14:08:56 +0200 Subject: [PATCH 13/17] Add sorting to board selection menu --- webduino_generator/arduino.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/webduino_generator/arduino.py b/webduino_generator/arduino.py index 4036f30..22c3afd 100644 --- a/webduino_generator/arduino.py +++ b/webduino_generator/arduino.py @@ -61,6 +61,9 @@ def get_boards(userio): boards = [board for board in boards if "name" in board] boards = [board for board in boards if "FQBN" in board] + # Sort boards for the user + boards = sorted(boards, key=lambda board: board["name"]) + userio.print("Dumping processed arduino-cli response:", verbose=True) userio.print(boards, verbose=True) @@ -88,6 +91,9 @@ def get_boards_connected(userio): "address": board["address"] }] + # Sort boards for the user + processed = sorted(processed, key=lambda board: board["name"]) + userio.print("Dumping processed arduino-cli response:", verbose=True) userio.print(processed, verbose=True) From 5ac09b7b7d1798cde8fafe7329037a796d886c48 Mon Sep 17 00:00:00 2001 From: Levy Ehrstein Date: Fri, 14 Aug 2020 14:11:36 +0200 Subject: [PATCH 14/17] Make generator output deterministic --- webduino_generator/generator.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/webduino_generator/generator.py b/webduino_generator/generator.py index c4eab49..c235ed2 100644 --- a/webduino_generator/generator.py +++ b/webduino_generator/generator.py @@ -40,6 +40,10 @@ def get_input_data(userio, input_path): # Get list of all files files = get_files_rec(input_path) userio.print("Processing " + str(len(files)) + " files...", verbose=True) + + # Sort files for deterministic output + files = sorted(files) + userio.quick_table("", ["Input Files"], [[_file] for _file in files], verbose=True) From 171daf87f0721be6d8e7c518262e1e7b2645af79 Mon Sep 17 00:00:00 2001 From: Levy Ehrstein Date: Fri, 14 Aug 2020 14:25:23 +0200 Subject: [PATCH 15/17] Fix minor stuff; Add git to version command --- webduino_generator/__init__.py | 1 + webduino_generator/entrypoint.py | 3 ++- webduino_generator/helper.py | 2 +- webduino_generator/project.py | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/webduino_generator/__init__.py b/webduino_generator/__init__.py index fa89108..cd0ce75 100644 --- a/webduino_generator/__init__.py +++ b/webduino_generator/__init__.py @@ -2,4 +2,5 @@ __author__ = 'Levy Ehrstein' __email__ = 'levyehrstein@googlemail.com' +__website__ = 'https://github.com/StoneLabs/webduino-generator' __version__ = '0.4' diff --git a/webduino_generator/entrypoint.py b/webduino_generator/entrypoint.py index 0b192ea..03aa0dd 100644 --- a/webduino_generator/entrypoint.py +++ b/webduino_generator/entrypoint.py @@ -2,7 +2,7 @@ import subprocess import json -from .__init__ import __version__ +from .__init__ import __version__, __website__ from .userio import UserIO, get_ssid_pass from .helper import cpp_str_esc, cpp_img_esc, get_files_rec, shorten, get_tool from .project import Project @@ -12,6 +12,7 @@ def command_version(userio, args): userio.print("Current version: " + __version__) + userio.print(__website__) def command_generate(userio, args): diff --git a/webduino_generator/helper.py b/webduino_generator/helper.py index 2a20b50..f02bff1 100644 --- a/webduino_generator/helper.py +++ b/webduino_generator/helper.py @@ -26,7 +26,7 @@ def get_files_rec(parent): rel_dir = os.path.relpath(dir_, parent) rel_file = os.path.join(rel_dir, file_name) - rel_file = rel_file.replace("\\","/") + rel_file = rel_file.replace("\\", "/") if rel_file.startswith("./"): rel_file = rel_file[2:] diff --git a/webduino_generator/project.py b/webduino_generator/project.py index ce5a821..db4c535 100644 --- a/webduino_generator/project.py +++ b/webduino_generator/project.py @@ -265,7 +265,7 @@ def compile(self, force_select=False, save=False): sketch_compile(self.userio, sketch_path, fqbn) def upload(self): - self.userio.section("Compiling project output") + self.userio.section("Uploading project output") # Get project output location sketch_path = self.get_sketch_path() From 6fca9e254bb558ac86492525b964bad5584c544e Mon Sep 17 00:00:00 2001 From: Levy Ehrstein Date: Fri, 14 Aug 2020 14:30:46 +0200 Subject: [PATCH 16/17] Add more exception handling --- webduino_generator/project.py | 4 ++++ webduino_generator/userio.py | 10 ++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/webduino_generator/project.py b/webduino_generator/project.py index db4c535..a7eac6b 100644 --- a/webduino_generator/project.py +++ b/webduino_generator/project.py @@ -252,6 +252,8 @@ def compile(self, force_select=False, save=False): # Get project output location sketch_path = self.get_sketch_path() + if sketch_path is None: + self.userio.error("Could not locate output files!") self.userio.print("Sketch located: " + sketch_path, verbose=True) # Get target FQBN @@ -269,6 +271,8 @@ def upload(self): # Get project output location sketch_path = self.get_sketch_path() + if sketch_path is None: + self.userio.error("Could not locate output files!") self.userio.print("Sketch located: " + sketch_path, verbose=True) # Get target FQBN diff --git a/webduino_generator/userio.py b/webduino_generator/userio.py index e84bba3..2cbf304 100644 --- a/webduino_generator/userio.py +++ b/webduino_generator/userio.py @@ -62,11 +62,17 @@ def get_user(self, prompt: str, userText: str) -> Tuple[str, str]: if prompt is not None: self.console.print(prompt) self.console.print(userText, end="") - userValue = input() + try: + userValue = input() + except KeyboardInterrupt: + exit(1) return userValue def get_pass(self, prompt: str, passText: str) -> Tuple[str, str]: if prompt is not None: self.console.print(prompt) - passValue = getpass.getpass(passText) + try: + passValue = getpass.getpass(passText) + except KeyboardInterrupt: + exit(1) return passValue From 16ed9832a86dcacd118a58cb2ddabe8bffc460d7 Mon Sep 17 00:00:00 2001 From: Levy Ehrstein Date: Fri, 14 Aug 2020 14:31:32 +0200 Subject: [PATCH 17/17] Uprev 0.5 for merge in master --- setup.py | 2 +- webduino_generator/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 0c19da5..efe978c 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ setup( name='webduino-generator', - version='0.4', + version='0.5', license='UNLICENSE', url='https://github.com/StoneLabs/webduino-generator', author='Levy Ehrstein', diff --git a/webduino_generator/__init__.py b/webduino_generator/__init__.py index cd0ce75..e13f6b2 100644 --- a/webduino_generator/__init__.py +++ b/webduino_generator/__init__.py @@ -3,4 +3,4 @@ __author__ = 'Levy Ehrstein' __email__ = 'levyehrstein@googlemail.com' __website__ = 'https://github.com/StoneLabs/webduino-generator' -__version__ = '0.4' +__version__ = '0.5'