Fix reset print job after new file selected.
This commit is contained in:
parent
f35f456eb2
commit
55b78cea05
@ -0,0 +1,2 @@
|
|||||||
|
__author__ = "Gina Häußge <osd@foosel.net>"
|
||||||
|
__license__ = "GNU Affero General Public License http://www.gnu.org/licenses/agpl.html"
|
@ -1,6 +1,4 @@
|
|||||||
__author__ = "Gina Häußge <osd@foosel.net>"
|
from __future__ import annotations
|
||||||
__license__ = "GNU Affero General Public License http://www.gnu.org/licenses/agpl.html"
|
|
||||||
|
|
||||||
|
|
||||||
import collections
|
import collections
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
@ -113,10 +111,12 @@ class BambuVirtualPrinter:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def current_print_job(self):
|
def current_print_job(self):
|
||||||
if self._current_print_job is None:
|
|
||||||
self.update_print_job_info()
|
|
||||||
return self._current_print_job
|
return self._current_print_job
|
||||||
|
|
||||||
|
@current_print_job.setter
|
||||||
|
def current_print_job(self, value):
|
||||||
|
self._current_print_job = value
|
||||||
|
|
||||||
def change_state(self, new_state: APrinterState):
|
def change_state(self, new_state: APrinterState):
|
||||||
self._state_change_queue.put(new_state)
|
self._state_change_queue.put(new_state)
|
||||||
|
|
||||||
@ -126,28 +126,9 @@ class BambuVirtualPrinter:
|
|||||||
elif event_type == "event_printer_data_update":
|
elif event_type == "event_printer_data_update":
|
||||||
self._update_printer_info()
|
self._update_printer_info()
|
||||||
|
|
||||||
def update_print_job_info(self):
|
|
||||||
print_job_info = self.bambu_client.get_device().print_job
|
|
||||||
task_name: str = print_job_info.subtask_name
|
|
||||||
project_file_info = self.file_system.get_data_by_suffix(
|
|
||||||
task_name, [".3mf", ".gcode.3mf"]
|
|
||||||
)
|
|
||||||
if project_file_info is None:
|
|
||||||
self._log.debug(f"No 3mf file found for {print_job_info}")
|
|
||||||
self._current_print_job = None
|
|
||||||
return
|
|
||||||
|
|
||||||
if self.file_system.select_file(project_file_info.file_name):
|
|
||||||
self.sendOk()
|
|
||||||
|
|
||||||
# fuzzy math here to get print percentage to match BambuStudio
|
|
||||||
progress = print_job_info.print_percentage
|
|
||||||
self._current_print_job = PrintJob(project_file_info, 0)
|
|
||||||
self._current_print_job.progress = progress
|
|
||||||
|
|
||||||
def _update_printer_info(self):
|
def _update_printer_info(self):
|
||||||
device_data = self.bambu_client.get_device()
|
device_data = self.bambu_client.get_device()
|
||||||
print_job = device_data.print_job
|
print_job_state = device_data.print_job.gcode_state
|
||||||
temperatures = device_data.temperature
|
temperatures = device_data.temperature
|
||||||
|
|
||||||
self.lastTempAt = time.monotonic()
|
self.lastTempAt = time.monotonic()
|
||||||
@ -158,17 +139,17 @@ class BambuVirtualPrinter:
|
|||||||
self._telemetry.chamberTemp = temperatures.chamber_temp
|
self._telemetry.chamberTemp = temperatures.chamber_temp
|
||||||
|
|
||||||
if (
|
if (
|
||||||
print_job.gcode_state == "IDLE"
|
print_job_state == "IDLE"
|
||||||
or print_job.gcode_state == "FINISH"
|
or print_job_state == "FINISH"
|
||||||
or print_job.gcode_state == "FAILED"
|
or print_job_state == "FAILED"
|
||||||
):
|
):
|
||||||
self.change_state(self._state_idle)
|
self.change_state(self._state_idle)
|
||||||
elif print_job.gcode_state == "RUNNING":
|
elif print_job_state == "RUNNING":
|
||||||
self.change_state(self._state_printing)
|
self.change_state(self._state_printing)
|
||||||
elif print_job.gcode_state == "PAUSE":
|
elif print_job_state == "PAUSE":
|
||||||
self.change_state(self._state_paused)
|
self.change_state(self._state_paused)
|
||||||
else:
|
else:
|
||||||
self._log.warn(f"Unknown print job state: {print_job.gcode_state}")
|
self._log.warn(f"Unknown print job state: {print_job_state}")
|
||||||
|
|
||||||
def _update_hms_errors(self):
|
def _update_hms_errors(self):
|
||||||
bambu_printer = self.bambu_client.get_device()
|
bambu_printer = self.bambu_client.get_device()
|
||||||
@ -312,13 +293,18 @@ class BambuVirtualPrinter:
|
|||||||
def _select_sd_file(self, data: str) -> bool:
|
def _select_sd_file(self, data: str) -> bool:
|
||||||
filename = data.split(maxsplit=1)[1].strip()
|
filename = data.split(maxsplit=1)[1].strip()
|
||||||
self._list_sd()
|
self._list_sd()
|
||||||
if self.file_system.select_file(filename):
|
if not self.file_system.select_file(filename):
|
||||||
assert self.file_system.selected_file is not None
|
return False
|
||||||
self.sendIO(
|
|
||||||
f"File opened: {self.file_system.selected_file.file_name} "
|
assert self.file_system.selected_file is not None
|
||||||
f"Size: {self.file_system.selected_file.size}"
|
self._current_state.update_print_job_info()
|
||||||
)
|
|
||||||
self.sendIO("File selected")
|
self.sendIO(
|
||||||
|
f"File opened: {self.file_system.selected_file.file_name} "
|
||||||
|
f"Size: {self.file_system.selected_file.size}"
|
||||||
|
)
|
||||||
|
self.sendIO("File selected")
|
||||||
|
return True
|
||||||
|
|
||||||
@gcode_executor.register("M26")
|
@gcode_executor.register("M26")
|
||||||
def _set_sd_position(self, data: str) -> bool:
|
def _set_sd_position(self, data: str) -> bool:
|
||||||
@ -345,7 +331,6 @@ class BambuVirtualPrinter:
|
|||||||
else:
|
else:
|
||||||
self._sdstatus_reporter = None
|
self._sdstatus_reporter = None
|
||||||
|
|
||||||
self.update_print_job_info()
|
|
||||||
self.report_print_job_status()
|
self.report_print_job_status()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@ -528,6 +513,10 @@ class BambuVirtualPrinter:
|
|||||||
self._state_change_queue.task_done()
|
self._state_change_queue.task_done()
|
||||||
except queue.Empty:
|
except queue.Empty:
|
||||||
continue
|
continue
|
||||||
|
except Exception as e:
|
||||||
|
self._state_change_queue.task_done()
|
||||||
|
raise e
|
||||||
|
self._current_state.finalize()
|
||||||
|
|
||||||
def _trigger_change_state(self, new_state: APrinterState):
|
def _trigger_change_state(self, new_state: APrinterState):
|
||||||
if self._current_state == new_state:
|
if self._current_state == new_state:
|
||||||
|
@ -7,14 +7,10 @@ from octoprint_bambu_printer.printer.remote_sd_card_file_list import FileInfo
|
|||||||
@dataclass
|
@dataclass
|
||||||
class PrintJob:
|
class PrintJob:
|
||||||
file_info: FileInfo
|
file_info: FileInfo
|
||||||
file_position: int
|
progress: int
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def progress(self):
|
def file_position(self):
|
||||||
if self.file_info.size is None:
|
if self.file_info.size is None:
|
||||||
return 100
|
return 0
|
||||||
return 100 * self.file_position / self.file_info.size
|
return int(self.file_info.size * self.progress / 100)
|
||||||
|
|
||||||
@progress.setter
|
|
||||||
def progress(self, value):
|
|
||||||
self.file_position = int(self.file_info.size * value / 100)
|
|
||||||
|
@ -48,6 +48,52 @@ class RemoteSDCardFileList:
|
|||||||
def has_selected_file(self):
|
def has_selected_file(self):
|
||||||
return self._selected_file_info is not None
|
return self._selected_file_info is not None
|
||||||
|
|
||||||
|
def remove_file_selection(self):
|
||||||
|
self._selected_file_info = None
|
||||||
|
|
||||||
|
def get_all_files(self):
|
||||||
|
self._update_existing_files_info()
|
||||||
|
self._logger.debug(f"get_all_files return: {self._file_data_cache}")
|
||||||
|
return list(self._file_data_cache.values())
|
||||||
|
|
||||||
|
def get_data_by_suffix(self, file_stem: str, allowed_suffixes: list[str]):
|
||||||
|
if file_stem == "":
|
||||||
|
return None
|
||||||
|
|
||||||
|
file_data = self._get_cached_data_by_suffix(file_stem, allowed_suffixes)
|
||||||
|
if file_data is None:
|
||||||
|
self._update_existing_files_info()
|
||||||
|
file_data = self._get_cached_data_by_suffix(file_stem, allowed_suffixes)
|
||||||
|
return file_data
|
||||||
|
|
||||||
|
def select_file(self, file_path: str) -> bool:
|
||||||
|
self._logger.debug(f"_selectSdFile: {file_path}")
|
||||||
|
file_name = Path(file_path).name
|
||||||
|
file_info = self._get_cached_file_data(file_name)
|
||||||
|
if file_info is None:
|
||||||
|
self._logger.error(f"{file_name} open failed")
|
||||||
|
return False
|
||||||
|
|
||||||
|
self._selected_file_info = file_info
|
||||||
|
return True
|
||||||
|
|
||||||
|
def delete_file(self, file_path: str) -> None:
|
||||||
|
host = self._settings.get(["host"])
|
||||||
|
access_code = self._settings.get(["access_code"])
|
||||||
|
|
||||||
|
file_info = self._get_cached_file_data(file_path)
|
||||||
|
if file_info is not None:
|
||||||
|
ftp = IoTFTPSClient(
|
||||||
|
f"{host}", 990, "bblp", f"{access_code}", ssl_implicit=True
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
if ftp.delete_file(str(file_info.path)):
|
||||||
|
self._logger.debug(f"{file_path} deleted")
|
||||||
|
else:
|
||||||
|
raise Exception("delete failed")
|
||||||
|
except Exception as e:
|
||||||
|
self._logger.debug(f"Error deleting file {file_path}")
|
||||||
|
|
||||||
def _get_ftp_file_info(
|
def _get_ftp_file_info(
|
||||||
self, ftp: IoTFTPSClient, file_path: Path, existing_files: list[str]
|
self, ftp: IoTFTPSClient, file_path: Path, existing_files: list[str]
|
||||||
):
|
):
|
||||||
@ -106,11 +152,6 @@ class RemoteSDCardFileList:
|
|||||||
self._logger.debug(f"get file data: {data}")
|
self._logger.debug(f"get file data: {data}")
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def get_all_files(self):
|
|
||||||
self._update_existing_files_info()
|
|
||||||
self._logger.debug(f"get_all_files return: {self._file_data_cache}")
|
|
||||||
return list(self._file_data_cache.values())
|
|
||||||
|
|
||||||
def _update_existing_files_info(self):
|
def _update_existing_files_info(self):
|
||||||
file_info_list = self._get_existing_files_info()
|
file_info_list = self._get_existing_files_info()
|
||||||
self._file_alias_cache = {
|
self._file_alias_cache = {
|
||||||
@ -126,47 +167,3 @@ class RemoteSDCardFileList:
|
|||||||
if file_data is not None:
|
if file_data is not None:
|
||||||
return file_data
|
return file_data
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_data_by_suffix(self, file_stem: str, allowed_suffixes: list[str]):
|
|
||||||
file_data = self._get_cached_data_by_suffix(file_stem, allowed_suffixes)
|
|
||||||
if file_data is None:
|
|
||||||
self._update_existing_files_info()
|
|
||||||
file_data = self._get_cached_data_by_suffix(file_stem, allowed_suffixes)
|
|
||||||
return file_data
|
|
||||||
|
|
||||||
def select_file(self, file_path: str, check_already_open: bool = False) -> bool:
|
|
||||||
self._logger.debug(
|
|
||||||
f"_selectSdFile: {file_path}, check_already_open={check_already_open}"
|
|
||||||
)
|
|
||||||
file_name = Path(file_path).name
|
|
||||||
file_info = self._get_cached_file_data(file_name)
|
|
||||||
if file_info is None:
|
|
||||||
self._logger.error(f"{file_name} open failed")
|
|
||||||
return False
|
|
||||||
|
|
||||||
if (
|
|
||||||
self._selected_file_info is not None
|
|
||||||
and self._selected_file_info.path == file_info.path
|
|
||||||
and check_already_open
|
|
||||||
):
|
|
||||||
return True
|
|
||||||
|
|
||||||
self._selected_file_info = file_info
|
|
||||||
return True
|
|
||||||
|
|
||||||
def delete_file(self, file_path: str) -> None:
|
|
||||||
host = self._settings.get(["host"])
|
|
||||||
access_code = self._settings.get(["access_code"])
|
|
||||||
|
|
||||||
file_info = self._get_cached_file_data(file_path)
|
|
||||||
if file_info is not None:
|
|
||||||
ftp = IoTFTPSClient(
|
|
||||||
f"{host}", 990, "bblp", f"{access_code}", ssl_implicit=True
|
|
||||||
)
|
|
||||||
try:
|
|
||||||
if ftp.delete_file(str(file_info.path)):
|
|
||||||
self._logger.debug(f"{file_path} deleted")
|
|
||||||
else:
|
|
||||||
raise Exception("delete failed")
|
|
||||||
except Exception as e:
|
|
||||||
self._logger.debug(f"Error deleting file {file_path}")
|
|
||||||
|
@ -25,6 +25,9 @@ class APrinterState:
|
|||||||
def handle_gcode(self, gcode):
|
def handle_gcode(self, gcode):
|
||||||
self._log.debug(f"{self.__class__.__name__} gcode execution disabled")
|
self._log.debug(f"{self.__class__.__name__} gcode execution disabled")
|
||||||
|
|
||||||
|
def update_print_job_info(self):
|
||||||
|
self._log_skip_state_transition("start_new_print")
|
||||||
|
|
||||||
def start_new_print(self):
|
def start_new_print(self):
|
||||||
self._log_skip_state_transition("start_new_print")
|
self._log_skip_state_transition("start_new_print")
|
||||||
|
|
||||||
|
@ -1,15 +1,19 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from octoprint_bambu_printer.printer.print_job import PrintJob
|
||||||
from octoprint_bambu_printer.printer.states.a_printer_state import APrinterState
|
from octoprint_bambu_printer.printer.states.a_printer_state import APrinterState
|
||||||
|
|
||||||
|
|
||||||
class IdleState(APrinterState):
|
class IdleState(APrinterState):
|
||||||
|
|
||||||
|
def init(self):
|
||||||
|
if self._printer.file_system.has_selected_file:
|
||||||
|
self.update_print_job_info()
|
||||||
|
|
||||||
def start_new_print(self):
|
def start_new_print(self):
|
||||||
selected_file = self._printer.file_system.selected_file
|
selected_file = self._printer.file_system.selected_file
|
||||||
if selected_file is None:
|
if selected_file is None:
|
||||||
self._log.warn("Cannot start print job if file was not selected")
|
self._log.warn("Cannot start print job if file was not selected")
|
||||||
self._printer.change_state(self._printer._state_idle)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
print_command = self._get_print_command_for_file(selected_file)
|
print_command = self._get_print_command_for_file(selected_file)
|
||||||
@ -51,3 +55,9 @@ class IdleState(APrinterState):
|
|||||||
}
|
}
|
||||||
|
|
||||||
return print_command
|
return print_command
|
||||||
|
|
||||||
|
def update_print_job_info(self):
|
||||||
|
if self._printer.file_system.selected_file is not None:
|
||||||
|
self._printer.current_print_job = PrintJob(
|
||||||
|
self._printer.file_system.selected_file, 0
|
||||||
|
)
|
||||||
|
@ -23,16 +23,12 @@ class PrintingState(APrinterState):
|
|||||||
def __init__(self, printer: BambuVirtualPrinter) -> None:
|
def __init__(self, printer: BambuVirtualPrinter) -> None:
|
||||||
super().__init__(printer)
|
super().__init__(printer)
|
||||||
self._is_printing = False
|
self._is_printing = False
|
||||||
self._print_job: PrintJob | None = None
|
|
||||||
self._sd_printing_thread = None
|
self._sd_printing_thread = None
|
||||||
|
|
||||||
@property
|
|
||||||
def print_job(self):
|
|
||||||
return self._print_job
|
|
||||||
|
|
||||||
def init(self):
|
def init(self):
|
||||||
self._is_printing = True
|
self._is_printing = True
|
||||||
self._printer.update_print_job_info()
|
self._printer.file_system.remove_file_selection()
|
||||||
|
self.update_print_job_info()
|
||||||
self._start_worker_thread()
|
self._start_worker_thread()
|
||||||
|
|
||||||
def finalize(self):
|
def finalize(self):
|
||||||
@ -51,10 +47,9 @@ class PrintingState(APrinterState):
|
|||||||
while (
|
while (
|
||||||
self._is_printing
|
self._is_printing
|
||||||
and self._printer.current_print_job is not None
|
and self._printer.current_print_job is not None
|
||||||
and self._printer.current_print_job.file_position
|
and self._printer.current_print_job.progress < 100
|
||||||
< self._printer.current_print_job.file_info.size
|
|
||||||
):
|
):
|
||||||
self._printer.update_print_job_info()
|
self.update_print_job_info()
|
||||||
self._printer.report_print_job_status()
|
self._printer.report_print_job_status()
|
||||||
time.sleep(3)
|
time.sleep(3)
|
||||||
|
|
||||||
@ -63,12 +58,23 @@ class PrintingState(APrinterState):
|
|||||||
self._log.warn("Printing state was triggered with empty print job")
|
self._log.warn("Printing state was triggered with empty print job")
|
||||||
return
|
return
|
||||||
|
|
||||||
if (
|
if self._printer.current_print_job.progress >= 100:
|
||||||
self._printer.current_print_job.file_position
|
|
||||||
>= self._printer.current_print_job.file_info.size
|
|
||||||
):
|
|
||||||
self._finish_print()
|
self._finish_print()
|
||||||
|
|
||||||
|
def update_print_job_info(self):
|
||||||
|
print_job_info = self._printer.bambu_client.get_device().print_job
|
||||||
|
task_name: str = print_job_info.subtask_name
|
||||||
|
project_file_info = self._printer.file_system.get_data_by_suffix(
|
||||||
|
task_name, [".3mf", ".gcode.3mf"]
|
||||||
|
)
|
||||||
|
if project_file_info is None:
|
||||||
|
self._log.debug(f"No 3mf file found for {print_job_info}")
|
||||||
|
self._current_print_job = None
|
||||||
|
return
|
||||||
|
|
||||||
|
progress = print_job_info.print_percentage
|
||||||
|
self._printer.current_print_job = PrintJob(project_file_info, progress)
|
||||||
|
|
||||||
def pause_print(self):
|
def pause_print(self):
|
||||||
if self._printer.bambu_client.connected:
|
if self._printer.bambu_client.connected:
|
||||||
if self._printer.bambu_client.publish(pybambu.commands.PAUSE):
|
if self._printer.bambu_client.publish(pybambu.commands.PAUSE):
|
||||||
|
@ -318,3 +318,56 @@ def test_regular_move(printer: BambuVirtualPrinter, bambu_client_mock):
|
|||||||
|
|
||||||
gcode_command["print"]["param"] = "G1 X10 Y10\n"
|
gcode_command["print"]["param"] = "G1 X10 Y10\n"
|
||||||
bambu_client_mock.publish.assert_called_with(gcode_command)
|
bambu_client_mock.publish.assert_called_with(gcode_command)
|
||||||
|
|
||||||
|
|
||||||
|
def test_file_selection_does_not_affect_current_print(
|
||||||
|
printer: BambuVirtualPrinter, print_job_mock
|
||||||
|
):
|
||||||
|
print_job_mock.subtask_name = "print.3mf"
|
||||||
|
|
||||||
|
printer.write(b"M23 print.3mf\nM24\n")
|
||||||
|
printer.flush()
|
||||||
|
printer.readlines()
|
||||||
|
assert isinstance(printer.current_state, PrintingState)
|
||||||
|
assert printer.current_print_job is not None
|
||||||
|
assert printer.current_print_job.file_info.file_name == "print.3mf"
|
||||||
|
assert printer.current_print_job.progress == 0
|
||||||
|
|
||||||
|
printer.write(b"M23 print2.3mf\n")
|
||||||
|
printer.flush()
|
||||||
|
assert printer.current_print_job is not None
|
||||||
|
assert printer.current_print_job.file_info.file_name == "print.3mf"
|
||||||
|
assert printer.current_print_job.progress == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_finished_print_job_reset_after_new_file_selected(
|
||||||
|
printer: BambuVirtualPrinter, print_job_mock
|
||||||
|
):
|
||||||
|
print_job_mock.subtask_name = "print.3mf"
|
||||||
|
|
||||||
|
printer.write(b"M23 print.3mf\nM24\n")
|
||||||
|
printer.flush()
|
||||||
|
printer.readlines()
|
||||||
|
assert isinstance(printer.current_state, PrintingState)
|
||||||
|
assert printer.current_print_job is not None
|
||||||
|
assert printer.current_print_job.file_info.file_name == "print.3mf"
|
||||||
|
assert printer.current_print_job.progress == 0
|
||||||
|
|
||||||
|
print_job_mock.print_percentage = 100
|
||||||
|
printer.current_state.update_print_job_info()
|
||||||
|
assert isinstance(printer.current_state, PrintingState)
|
||||||
|
assert printer.current_print_job.progress == 100
|
||||||
|
|
||||||
|
print_job_mock.gcode_state = "FINISH"
|
||||||
|
printer.new_update("event_printer_data_update")
|
||||||
|
printer.flush()
|
||||||
|
assert isinstance(printer.current_state, IdleState)
|
||||||
|
assert printer.current_print_job is not None
|
||||||
|
assert printer.current_print_job.file_info.file_name == "print.3mf"
|
||||||
|
assert printer.current_print_job.progress == 100
|
||||||
|
|
||||||
|
printer.write(b"M23 print2.3mf\n")
|
||||||
|
printer.flush()
|
||||||
|
assert printer.current_print_job is not None
|
||||||
|
assert printer.current_print_job.file_info.file_name == "print2.3mf"
|
||||||
|
assert printer.current_print_job.progress == 0
|
||||||
|
Loading…
x
Reference in New Issue
Block a user