WIP Refactor printer logic into states and subsystem objects.

This commit is contained in:
Anton Skrypnyk
2024-07-24 17:15:46 +03:00
parent 75b0a11fef
commit 155f3d2bd3
16 changed files with 1059 additions and 903 deletions

View File

@ -0,0 +1,39 @@
import logging
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from octoprint_bambu_printer.printer.bambu_virtual_printer import (
BambuVirtualPrinter,
)
class APrinterState:
def __init__(self, printer: BambuVirtualPrinter) -> None:
self._log = logging.getLogger(
"octoprint.plugins.bambu_printer.BambuPrinter.states"
)
self._printer = printer
def init(self):
pass
def finalize(self):
pass
def handle_gcode(self, gcode):
self._log.debug(f"{self.__class__.__name__} gcode execution disabled")
def connect(self):
self._log_skip_state_transition("connect")
def pause(self):
self._log_skip_state_transition("pause")
def cancel(self):
self._log_skip_state_transition("cancel")
def resume(self):
self._log_skip_state_transition("resume")
def _log_skip_state_transition(self, method):
self._log.debug(f"skipping {self.__class__.__name__} state transition {method}")

View File

@ -0,0 +1,7 @@
from __future__ import annotations
from octoprint_bambu_printer.printer.states.a_printer_state import APrinterState
class IdleState(APrinterState):
pass

View File

@ -0,0 +1,37 @@
import threading
from octoprint.util import RepeatedTimer
from octoprint_bambu_printer.printer.bambu_virtual_printer import BambuVirtualPrinter
from octoprint_bambu_printer.printer.states.a_printer_state import APrinterState
class PausedState(APrinterState):
def __init__(self, printer: BambuVirtualPrinter) -> None:
super().__init__(printer)
self._pausedLock = threading.Event()
def init(self):
if not self._pausedLock.is_set():
self._pausedLock.set()
self._printer.sendIO("// action:paused")
self._sendPaused()
def finalize(self):
if self._pausedLock.is_set():
self._pausedLock.clear()
def _sendPaused(self):
if self._printer.current_print_job is None:
self._log.warn("job paused, but no print job available?")
return
paused_timer = RepeatedTimer(
interval=3.0,
function=self._printer.report_print_job_status,
daemon=True,
run_first=True,
condition=self._pausedLock.is_set,
)
paused_timer.start()

View File

@ -0,0 +1,19 @@
from __future__ import annotations
from octoprint_bambu_printer.printer.states.a_printer_state import APrinterState
class PrintFinishedState(APrinterState):
def init(self):
if self._printer.current_print_job is not None:
self._printer.current_print_job.progress = 100
self._finishSdPrint()
def _finishSdPrint(self):
if self._printer.is_running:
self._printer.sendIO("Done printing file")
self._selectedSdFilePos = 0
self._selectedSdFileSize = 0
self._sdPrinting = False
self._sdPrintStarting = False
self._sdPrinter = None

View File

@ -0,0 +1,131 @@
from __future__ import annotations
import threading
import pybambu
import pybambu.models
import pybambu.commands
from octoprint_bambu_printer.printer.bambu_virtual_printer import BambuVirtualPrinter
from octoprint_bambu_printer.printer.print_job import PrintJob
from octoprint_bambu_printer.printer.states.a_printer_state import APrinterState
class PrintingState(APrinterState):
def __init__(self, printer: BambuVirtualPrinter) -> None:
super().__init__(printer)
self._printingLock = threading.Event()
self._print_job: PrintJob | None = None
self._sd_printing_thread = None
@property
def print_job(self):
return self._print_job
def init(self):
if not self._printingLock.is_set():
self._printingLock.set()
def finalize(self):
if self._printingLock.is_set():
self._printingLock.clear()
def _start_worker_thread(self, from_printer: bool = False):
if self._sd_printing_thread is None:
self._sdPrinting = True
self._sdPrintStarting = True
self._sd_printing_thread = threading.Thread(
target=self._printing_worker, kwargs={"from_printer": from_printer}
)
self._sd_printing_thread.start()
def set_print_job_info(self, print_job_info):
filename: str = print_job_info.get("subtask_name")
project_file_info = self._printer.file_system.search_by_stem(
filename, [".3mf", ".gcode.3mf"]
)
if project_file_info is None:
self._log.debug(f"No 3mf file found for {print_job_info}")
return
if self._printer.file_system.select_file(filename):
self._printer.sendOk()
self.start_new_print(from_printer=True)
# fuzzy math here to get print percentage to match BambuStudio
progress = print_job_info.get("print_percentage")
self._print_job = PrintJob(project_file_info, 0)
self._print_job.progress =
def start_new_print(self, from_printer: bool = False):
if self._printer.file_system.selected_file is not None:
self._start_worker_thread(from_printer)
if self._sd_printing_thread is not None:
if self._printer.bambu_client.connected:
if self._printer.bambu_client.publish(pybambu.commands.RESUME):
self._log.info("print resumed")
else:
self._log.info("print resume failed")
return True
def _printing_worker(self, from_printer: bool = False):
try:
if not from_printer and self._printer.bambu_client.connected:
selected_file = self._printer.file_system.selected_file
print_command = {
"print": {
"sequence_id": 0,
"command": "project_file",
"param": "Metadata/plate_1.gcode",
"md5": "",
"profile_id": "0",
"project_id": "0",
"subtask_id": "0",
"task_id": "0",
"subtask_name": f"{selected_file}",
"file": f"{selected_file}",
"url": (
f"file:///mnt/sdcard/{selected_file}"
if self._printer._settings.get_boolean(["device_type"])
in ["X1", "X1C"]
else f"file:///sdcard/{selected_file}"
),
"timelapse": self._printer._settings.get_boolean(["timelapse"]),
"bed_leveling": self._printer._settings.get_boolean(["bed_leveling"]),
"flow_cali": self._printer._settings.get_boolean(["flow_cali"]),
"vibration_cali": self._printer._settings.get_boolean(
["vibration_cali"]
),
"layer_inspect": self._printer._settings.get_boolean(["layer_inspect"]),
"use_ams": self._printer._settings.get_boolean(["use_ams"]),
}
}
self._printer.bambu_client.publish(print_command)
while self._selectedSdFilePos < self._selectedSdFileSize:
if self._killed or not self._sdPrinting:
break
# if we are paused, wait for resuming
self._sdPrintingSemaphore.wait()
self._reportSdStatus()
time.sleep(3)
self._log.debug(f"SD File Print: {self._selectedSdFile}")
except AttributeError:
if self.outgoing is not None:
raise
self._printer.change_state(self._printer._state_finished)
def cancel(self):
if self._printer.bambu_client.connected:
if self._printer.bambu_client.publish(pybambu.commands.STOP):
self._log.info("print cancelled")
self._printer.change_state(self._printer._state_finished)
return True
else:
self._log.info("print cancel failed")
return False
return False