diff --git a/ChangeLog b/ChangeLog index 97298d2b..40453ff4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -15,6 +15,7 @@ * Periodically send a keepalive gcode to keep the printer in PrusaLink mode * Support the set ready and cancel ready LCD menu toggle * Add gcodes that flag the state of a usb print for the printer statistics to get saved + * Handle the new re-print LCD menu item 0.7.1rc1 (2023-08-10) * Fixed HTTP response status on HEAD request for non-existing files diff --git a/prusa/link/printer_adapter/command_handlers.py b/prusa/link/printer_adapter/command_handlers.py index 8b955d16..e23a3881 100644 --- a/prusa/link/printer_adapter/command_handlers.py +++ b/prusa/link/printer_adapter/command_handlers.py @@ -27,6 +27,7 @@ from ..serial.helpers import enqueue_instruction, enqueue_list_from_str from ..util import file_is_on_sd, round_to_five from .command import Command, CommandFailed, FileNotFound, NotStateToPrint +from .model import Model from .state_manager import StateChange from .structures.model_classes import JobState from .structures.regular_expressions import ( @@ -555,3 +556,27 @@ def _run_command(self): default_source=self.source)) self.state_manager.idle() self.state_manager.stop_expecting_change() + + +class RePrint(StartPrint): + """Class for starting the last job again""" + command_name = "re-print" + + def __init__(self, **kwargs): + # Need to get the model sooner than it's available in self + model = Model.get_instance() + path = model.job.last_job_path + if path is None: + path = "" + super().__init__(path=path, **kwargs) + + def _run_command(self): + """Re-prints the last job, makes a noise and sends an LCD message + if that fails""" + try: + super()._run_command() + except CommandFailed as exception: + # Not an ideal way to do this, but less time-consuming + enqueue_instruction(self.serial_queue, "M300 P200 S600") + enqueue_instruction(self.serial_queue, "M117 \x7ECannot re-print") + raise exception diff --git a/prusa/link/printer_adapter/job.py b/prusa/link/printer_adapter/job.py index a412faad..73a3e52a 100644 --- a/prusa/link/printer_adapter/job.py +++ b/prusa/link/printer_adapter/job.py @@ -50,7 +50,8 @@ def __init__(self, printing_file_byte=None, job_state=JobState.IDLE, job_id=None, - job_id_offset=0) + job_id_offset=0, + last_job_path=None) self.data = self.model.job self.job_id_updated_signal.send(self, @@ -100,6 +101,7 @@ def job_started(self, command_id=None): self.model.print_stats.has_inbuilt_stats self.change_state(JobState.IN_PROGRESS) self.write() + self.update_last_job_path() log.debug("New job started, id = %s", self.data.job_id) self.job_id_updated_signal.send(self, job_id=self.data.get_job_id_for_api()) @@ -192,8 +194,15 @@ def set_file_path(self, path, path_incomplete, prepend_sd_storage): self.data.selected_file_size = file_obj.attrs["size"] self.model.job.from_sd = path.startswith( os.path.join("/", SD_STORAGE_NAME)) + self.update_last_job_path() self.job_info_updated() + def update_last_job_path(self): + """Updates the last job path to be used for the re-print menu item""" + if self.data.job_state != JobState.IN_PROGRESS: + return + self.data.last_job_path = self.data.selected_file_path + def get_job_info_data(self, for_connect=False): """Compiles the job info data into a dict""" if for_connect: diff --git a/prusa/link/printer_adapter/prusa_link.py b/prusa/link/printer_adapter/prusa_link.py index 9561183b..309739e8 100644 --- a/prusa/link/printer_adapter/prusa_link.py +++ b/prusa/link/printer_adapter/prusa_link.py @@ -53,6 +53,7 @@ JobInfo, LoadFilament, PausePrint, + RePrint, ResetPrinter, ResumePrint, SetReady, @@ -86,6 +87,7 @@ NOT_READY_REGEX, PAUSE_PRINT_REGEX, PRINTER_BOOT_REGEX, + REPRINT_REGEX, READY_REGEX, RESUME_PRINT_REGEX, TM_CAL_END_REGEX, @@ -196,6 +198,8 @@ def __init__(self, cfg: Config, settings: Settings) -> None: READY_REGEX, lambda sender, match: self.fw_set_ready()) self.serial_parser.add_decoupled_handler( NOT_READY_REGEX, lambda sender, match: self.fw_cancel_ready()) + self.serial_parser.add_decoupled_handler( + REPRINT_REGEX, lambda sender, match: self.fw_reprint()) # Init components first, so they all exist for signal binding stuff # TODO: does not need printer, the transfer object should be @@ -362,6 +366,8 @@ def debug_shell(self) -> None: result: Any = "" if command == "pause": result = self.command_queue.do_command(PausePrint()) + elif command == "reprint": + result = self.command_queue.do_command(RePrint()) elif command == "resume": result = self.command_queue.do_command(ResumePrint()) elif command == "stop": @@ -600,6 +606,12 @@ def fw_cancel_ready(self) -> None: command = CancelReady(source=Source.USER) self.command_queue.enqueue_command(command) + def fw_reprint(self) -> None: + """Prints the last job again, activated from the printer LCD screen""" + prctl_name() + command = RePrint(source=Source.USER) + self.command_queue.enqueue_command(command) + # --- Signal handlers --- def layer_trigger(self, _): """Passes the call to trigger to the camera controller""" diff --git a/prusa/link/printer_adapter/structures/module_data_classes.py b/prusa/link/printer_adapter/structures/module_data_classes.py index 61a4c1a3..70af82ea 100644 --- a/prusa/link/printer_adapter/structures/module_data_classes.py +++ b/prusa/link/printer_adapter/structures/module_data_classes.py @@ -82,6 +82,8 @@ class JobData(BaseModel): from_sd: Optional[bool] inbuilt_reporting: Optional[bool] + last_job_path: Optional[str] + job_state: JobState def get_job_id_for_api(self): diff --git a/prusa/link/printer_adapter/structures/regular_expressions.py b/prusa/link/printer_adapter/structures/regular_expressions.py index 235da9d8..c0558f69 100644 --- a/prusa/link/printer_adapter/structures/regular_expressions.py +++ b/prusa/link/printer_adapter/structures/regular_expressions.py @@ -69,6 +69,7 @@ CANCEL_REGEX = re.compile("^// action:cancel$") READY_REGEX = re.compile("^// action:ready$") NOT_READY_REGEX = re.compile("^// action:not_ready$") +REPRINT_REGEX = re.compile("^// action:reprint$") # This girthy regexp tries to capture all error messages requiring printer # reset using M999 or manual button, with connect, only manual reset shall # be accepted diff --git a/prusa/link/util.py b/prusa/link/util.py index bee1f7f8..0c4168bc 100644 --- a/prusa/link/util.py +++ b/prusa/link/util.py @@ -137,6 +137,8 @@ def get_gcode(line): def file_is_on_sd(path_parts): """Checks if the file path starts wit the sd cards' storage name""" + if len(path_parts) < 2: + return False return path_parts[1] == SD_STORAGE_NAME