klippy_connection: add is_printing() and is_ready() methods
Several components throughout Moonraker determine whether or not Klipper is printing or is ready before taking action. This centralizes queries in one area. The checks do not query Klipper directly but rather rely on subscriptions to push state to Moonraker. Signed-off-by: Eric Callahan <arksine.code@gmail.com>
This commit is contained in:
parent
019c5fc416
commit
ecf7fb9267
|
@ -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))
|
||||
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue