From 1aabcf88fc8a8d9adc702b47b990765e7eb6cef9 Mon Sep 17 00:00:00 2001 From: Michal Zoubek Date: Mon, 24 Jul 2023 09:27:15 +0200 Subject: [PATCH] Added new command UPGRADE for PrusaLink upgrade --- ChangeLog | 1 + .../link/printer_adapter/command_handlers.py | 40 +++++++++++++++++++ prusa/link/printer_adapter/prusa_link.py | 9 +++++ prusa/link/web/main.py | 18 ++++----- 4 files changed, 57 insertions(+), 11 deletions(-) diff --git a/ChangeLog b/ChangeLog index 34252504..afbefce8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -24,6 +24,7 @@ - Add support to be running behind the proxy * Hold the STOPPED and FINISHED state for at least 11s * Fix MK2(.5)S SN being broken on multi-instance images + * Implemented UPGRADE high lvl Connect command 0.7.0 (2023-05-19) * Fixed printer sends info about api key change to Connect after change diff --git a/prusa/link/printer_adapter/command_handlers.py b/prusa/link/printer_adapter/command_handlers.py index 77fb4a8c..2700064e 100644 --- a/prusa/link/printer_adapter/command_handlers.py +++ b/prusa/link/printer_adapter/command_handlers.py @@ -8,6 +8,8 @@ import logging from pathlib import Path from re import Match +from subprocess import STDOUT, CalledProcessError, check_call, check_output +from sys import executable from threading import Event from time import time from typing import Dict, Optional, Set @@ -36,6 +38,21 @@ log = logging.getLogger(__name__) +def check_update_prusalink(): + """Run the bash script to check for PrusaLink updates and return output""" + return check_output( + [executable, '-m', 'pip', 'install', '--no-deps', '--dry-run', + '-U', 'prusalink'], stderr=STDOUT).decode() + + +def update_prusalink(): + """Run the bash script to update PrusaLink and return output""" + return check_output( + [executable, '-m', 'pip', 'install', '-U', + '--upgrade-strategy', 'only-if-needed', 'prusalink'], + stderr=STDOUT).decode() + + class TryUntilState(Command): """A base for commands stop, pause and resume print""" command_name = "pause/stop/resume print" @@ -447,6 +464,29 @@ def waiter(sender, match): "or our serial reading component broke..") +class UpgradeLink(Command): + """Class for upgrading PrusaLink""" + command_name = "upgrade_link" + + def _run_command(self): + pass + try: + output = update_prusalink() + + # No update available + if "Installing collected packages" not in output: + raise CommandFailed("No update available") + + # New version was installed correctly - restart PrusaLink + check_call([executable, '-m', 'prusalink', 'restart']) + log.info("PrusaLink upgraded successfully") + + # There's a problem with package installation, or it does not exist + except CalledProcessError as exception: + raise CommandFailed("There's a problem with package installation, " + "or it does not exist") from exception + + class JobInfo(Command): """Class for sending/getting the job info""" command_name = "job_info" diff --git a/prusa/link/printer_adapter/prusa_link.py b/prusa/link/printer_adapter/prusa_link.py index 546fe549..1a35557b 100644 --- a/prusa/link/printer_adapter/prusa_link.py +++ b/prusa/link/printer_adapter/prusa_link.py @@ -59,6 +59,7 @@ StartPrint, StopPrint, UnloadFilament, + UpgradeLink, ) from .command_queue import CommandQueue, CommandResult from .file_printer import FilePrinter @@ -160,6 +161,7 @@ def __init__(self, cfg: Config, settings: Settings) -> None: self.printer.set_handler(CommandType.GCODE, self.execute_gcode) self.printer.set_handler(CommandType.PAUSE_PRINT, self.pause_print) self.printer.set_handler(CommandType.RESET_PRINTER, self.reset_printer) + self.printer.set_handler(CommandType.UPGRADE, self.upgrade_link) self.printer.set_handler(CommandType.RESUME_PRINT, self.resume_print) self.printer.set_handler(CommandType.START_PRINT, self.start_print) self.printer.set_handler(CommandType.STOP_PRINT, self.stop_print) @@ -504,6 +506,13 @@ def reset_printer(self, caller: SDKCommand) -> CommandResult: command = ResetPrinter(command_id=caller.command_id) return self.command_queue.force_command(command) + def upgrade_link(self, caller: SDKCommand) -> CommandResult: + """ + Connects the command to upgrade link from CONNECT with its handler + """ + command = UpgradeLink(command_id=caller.command_id) + return self.command_queue.do_command(command) + def job_info(self, caller: SDKCommand) -> CommandResult: """ Connects the command to send job info from CONNECT with its handler diff --git a/prusa/link/web/main.py b/prusa/link/web/main.py index 85800ef5..417c530e 100644 --- a/prusa/link/web/main.py +++ b/prusa/link/web/main.py @@ -1,11 +1,10 @@ """Main pages and core API""" import logging -import subprocess from os import listdir from os.path import basename, getmtime, getsize, join from socket import gethostname -from subprocess import CalledProcessError, check_output -from sys import executable, version +from subprocess import CalledProcessError +from sys import version from gcode_metadata import get_metadata from pkg_resources import working_set # type: ignore @@ -31,6 +30,8 @@ SetReady, StartPrint, StopPrint, + check_update_prusalink, + update_prusalink, ) from ..printer_adapter.job import Job, JobState from .lib.auth import REALM, check_api_digest, check_config @@ -633,9 +634,8 @@ def api_update(req, env): if env == "prusalink": try: - output = check_output( - [executable, '-m', 'pip', 'install', '--no-deps', '--dry-run', - '-U', 'prusalink'], stderr=subprocess.STDOUT).decode() + output = check_update_prusalink() + # There's a problem with package installation, or it does not exist except CalledProcessError as exception: raise conditions.UnavailableUpdate(exception.output.decode()) \ @@ -666,11 +666,7 @@ def api_update_post(req, env): # pylint: disable=unused-argument if env == "prusalink": try: - output = \ - check_output( - [executable, '-m', 'pip', 'install', '-U', - '--upgrade-strategy', 'only-if-needed', 'prusalink'], - stderr=subprocess.STDOUT).decode() + output = update_prusalink() # No update available if "Installing collected packages" not in output: