diff --git a/moonraker/components/file_manager/file_manager.py b/moonraker/components/file_manager/file_manager.py index 5fc1c13..71b375b 100644 --- a/moonraker/components/file_manager/file_manager.py +++ b/moonraker/components/file_manager/file_manager.py @@ -38,10 +38,12 @@ if TYPE_CHECKING: from inotify_simple import Event as InotifyEvent from confighelper import ConfigHelper from websockets import WebRequest + from klippy_connection import KlippyConnection from components import database from components import klippy_apis from components import shell_command from components.job_queue import JobQueue + from components.job_state import JobState StrOrPath = Union[str, pathlib.Path] DBComp = database.MoonrakerDatabase APIComp = klippy_apis.KlippyAPI @@ -403,7 +405,7 @@ class FileManager: if force: # Make sure that the directory does not contain a file # loaded by the virtual_sdcard - await self._handle_operation_check(dir_path) + self._handle_operation_check(dir_path) self.notify_sync_lock = NotifySyncLock(dir_path) try: await self.event_loop.run_in_thread( @@ -423,20 +425,19 @@ class FileManager: raise self.server.error("Operation Not Supported", 405) return result - async def _handle_operation_check(self, requested_path: str) -> bool: + def _handle_operation_check(self, requested_path: str) -> bool: if not self.get_relative_path("gcodes", requested_path): # Path not in the gcodes path return True - # Get virtual_sdcard status - kapis: APIComp = self.server.lookup_component('klippy_apis') - result: Dict[str, Any] - result = await kapis.query_objects({'print_stats': None}, {}) - pstats = result.get('print_stats', {}) - loaded_file: str = pstats.get('filename', "") - state: str = pstats.get('state', "") + kconn: KlippyConnection + kconn = self.server.lookup_component("klippy_connection") + job_state: JobState = self.server.lookup_component("job_state") + last_stats = job_state.get_last_stats() + loaded_file: str = last_stats.get('filename', "") + state: str = last_stats.get('state', "") gc_path = self.file_paths.get('gcodes', "") full_path = os.path.join(gc_path, loaded_file) - is_printing = state in ["printing", "paused"] + is_printing = kconn.is_ready() and state in ["printing", "paused"] if loaded_file and is_printing: if os.path.isdir(requested_path): # Check to see of the loaded file is in the request @@ -482,13 +483,13 @@ class FileManager: raise self.server.error(f"File {source_path} does not exist") # make sure the destination is not in use if os.path.exists(dest_path): - await self._handle_operation_check(dest_path) + self._handle_operation_check(dest_path) if ep == "/server/files/move": if source_root not in self.full_access_roots: raise self.server.error( f"Source path is read-only, cannot move: {source_root}") # if moving the file, make sure the source is not in use - await self._handle_operation_check(source_path) + self._handle_operation_check(source_path) op_func: Callable[..., str] = shutil.move result['source_item'] = { 'path': source, @@ -665,7 +666,7 @@ class FileManager: can_start: bool = False try: check_path: str = upload_info['dest_path'] - can_start = await self._handle_operation_check(check_path) + can_start = self._handle_operation_check(check_path) except self.server.error as e: if e.status_code == 403: raise self.server.error( @@ -857,7 +858,7 @@ class FileManager: if not os.path.isfile(full_path): raise self.server.error(f"Invalid file path: {path}") try: - await self._handle_operation_check(full_path) + self._handle_operation_check(full_path) except self.server.error as e: if e.status_code == 403: raise @@ -1683,7 +1684,7 @@ class MetadataStorage: def get(self, key: str, - default: _T = None + default: Optional[_T] = None ) -> Union[_T, Dict[str, Any]]: return deepcopy(self.metadata.get(key, default)) diff --git a/moonraker/components/power.py b/moonraker/components/power.py index 72a127d..695fbc6 100644 --- a/moonraker/components/power.py +++ b/moonraker/components/power.py @@ -33,6 +33,7 @@ if TYPE_CHECKING: from .mqtt import MQTTClient from .template import JinjaTemplate from .http_client import HttpClient + from klippy_connection import KlippyConnection APIComp = klippy_apis.KlippyAPI class PrinterPower: @@ -94,13 +95,6 @@ class PrinterPower: "job_queue:job_queue_changed", self._handle_job_queued) self.server.register_notification("power:power_changed") - async def _check_klippy_printing(self) -> bool: - kapis: APIComp = self.server.lookup_component('klippy_apis') - result: Dict[str, Any] = await kapis.query_objects( - {'print_stats': None}, default={}) - pstate = result.get('print_stats', {}).get('state', "").lower() - return pstate == "printing" - async def component_init(self) -> None: for dev in self.devices.values(): if not dev.initialize(): @@ -261,13 +255,6 @@ class PowerDevice: 'initial_state', None ) - async def _check_klippy_printing(self) -> bool: - kapis: APIComp = self.server.lookup_component('klippy_apis') - result: Dict[str, Any] = await kapis.query_objects( - {'print_stats': None}, default={}) - pstate = result.get('print_stats', {}).get('state', "").lower() - return pstate == "printing" - def _schedule_firmware_restart(self, state: str = "") -> None: if not self.need_scheduled_restart: return @@ -399,8 +386,9 @@ class PowerDevice: self.notify_power_changed() return cur_state if not force: - printing = await self._check_klippy_printing() - if self.locked_while_printing and printing: + kconn: KlippyConnection + kconn = self.server.lookup_component("klippy_connection") + if self.locked_while_printing and kconn.is_printing(): raise self.server.error( f"Unable to change power for {self.name} " "while printing") diff --git a/moonraker/components/update_manager/update_manager.py b/moonraker/components/update_manager/update_manager.py index 0dca628..70a4f75 100644 --- a/moonraker/components/update_manager/update_manager.py +++ b/moonraker/components/update_manager/update_manager.py @@ -40,7 +40,7 @@ if TYPE_CHECKING: from moonraker import Server from confighelper import ConfigHelper from websockets import WebRequest - from components.klippy_apis import KlippyAPI as APIComp + from klippy_connection import KlippyConnection from components.shell_command import ShellCommandFactory as SCMDComp from components.database import MoonrakerDatabase as DBComp from components.database import NamespaceWrapper @@ -68,6 +68,8 @@ class UpdateManager: def __init__(self, config: ConfigHelper) -> None: self.server = config.get_server() self.event_loop = self.server.get_event_loop() + self.kconn: KlippyConnection + self.kconn = self.server.lookup_component("klippy_connection") self.channel = config.get('channel', "dev") if self.channel not in ["dev", "beta"]: raise config.error( @@ -224,20 +226,13 @@ class UpdateManager: if notify: self.cmd_helper.notify_update_refreshed() - async def _check_klippy_printing(self) -> bool: - kapi: APIComp = self.server.lookup_component('klippy_apis') - result: Dict[str, Any] = await kapi.query_objects( - {'print_stats': None}, default={}) - pstate: str = result.get('print_stats', {}).get('state', "") - return pstate.lower() == "printing" - async def _handle_auto_refresh(self, eventtime: float) -> float: cur_hour = time.localtime(time.time()).tm_hour if self.initial_refresh_complete: # Update when the local time is between 12AM and 5AM if cur_hour >= MAX_UPDATE_HOUR: return eventtime + UPDATE_REFRESH_INTERVAL - if await self._check_klippy_printing(): + if self.kconn.is_printing(): # Don't Refresh during a print logging.info("Klippy is printing, auto refresh aborted") return eventtime + UPDATE_REFRESH_INTERVAL @@ -268,7 +263,7 @@ class UpdateManager: async def _handle_update_request(self, web_request: WebRequest ) -> str: - if await self._check_klippy_printing(): + if self.kconn.is_printing(): raise self.server.error("Update Refused: Klippy is printing") app: str = web_request.get_endpoint().split("/")[-1] if app == "client": @@ -391,7 +386,7 @@ class UpdateManager: if ( machine.validation_enabled() or self.cmd_helper.is_update_busy() or - await self._check_klippy_printing() or + self.kconn.is_printing() or not self.initial_refresh_complete ): if check_refresh: @@ -433,7 +428,7 @@ class UpdateManager: async def _handle_repo_recovery(self, web_request: WebRequest ) -> str: - if await self._check_klippy_printing(): + if self.kconn.is_printing(): raise self.server.error( "Recovery Attempt Refused: Klippy is printing") app: str = web_request.get_str('name') diff --git a/moonraker/klippy_connection.py b/moonraker/klippy_connection.py index b05cddb..458751d 100644 --- a/moonraker/klippy_connection.py +++ b/moonraker/klippy_connection.py @@ -35,6 +35,7 @@ if TYPE_CHECKING: from components.klippy_apis import KlippyAPI from components.file_manager.file_manager import FileManager from components.machine import Machine + from components.job_state import JobState FlexCallback = Callable[..., Optional[Coroutine]] # These endpoints are reserved for klippy/moonraker communication only and are @@ -550,6 +551,16 @@ class KlippyConnection: def is_connected(self) -> bool: return self.writer is not None and not self.closing + def is_ready(self) -> bool: + return self.state == "ready" + + def is_printing(self) -> bool: + if not self.is_ready(): + return False + job_state: JobState = self.server.lookup_component("job_state") + stats = job_state.get_last_stats() + return stats.get("state", "") == "printing" + async def _on_connection_closed(self) -> None: self.init_list = [] self._state = "disconnected"