Skip to content

Commit

Permalink
Send keepalive to keep printer in PrusaLink mode
Browse files Browse the repository at this point in the history
  • Loading branch information
TojikCZ committed Nov 22, 2023
1 parent a930963 commit 6a88e0d
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 2 deletions.
1 change: 1 addition & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
* Add a way to download logs even on systemd journal based systems
* Bump the API version so it is the same as the xBuddy reported one
* Support the new multi-level telemetry data structure
* Periodically send a keepalive gcode to keep the printer in PrusaLink mode

0.7.1rc1 (2023-08-10)
* Fixed HTTP response status on HEAD request for non-existing files
Expand Down
1 change: 1 addition & 0 deletions prusa/link/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@
CAMERA_REGISTER_TIMEOUT = 5
TIME_FOR_SNAPSHOT = 1
PRINT_END_TIMEOUT = 11
KEEPALIVE_INTERVAL = 12

# --- Lcd queue ---
LCD_QUEUE_SIZE = 30
Expand Down
64 changes: 64 additions & 0 deletions prusa/link/printer_adapter/keepalive.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
"""Contains the keepalive implementation"""
from enum import Enum
from threading import Event, Thread
from time import monotonic

from ..const import KEEPALIVE_INTERVAL
from ..serial.helpers import enqueue_instruction, wait_for_instruction
from ..serial.serial_queue import SerialQueue
from .structures.mc_singleton import MCSingleton


class KeepaliveMode(Enum):
"""The modes the keepalive can be in"""
PL = "PL" # PrusaLink
PC = "PC" # PrusaConnect


class Keepalive(metaclass=MCSingleton):
"""Its job is to keep the PrusaLink printer mode on"""

def __init__(self, serial_queue: SerialQueue):
self.serial_queue: SerialQueue = serial_queue

self.mode = KeepaliveMode.PL

self.quit_evt = Event()
self.wait_evt = Event()
self.keepalive_thread: Thread = Thread(target=self._keepalive,
name="Keepalive")

self.last_keepalive = monotonic() - KEEPALIVE_INTERVAL

def start(self):
"""Starts the module"""
self.keepalive_thread.start()

def set_use_connect(self, use_connect: bool):
"""Changes the mode of the keepalive"""
self.mode = KeepaliveMode.PC if use_connect else KeepaliveMode.PL
self.last_keepalive = monotonic() - KEEPALIVE_INTERVAL
self.wait_evt.set()

def _keepalive(self):
"""Keep sending out a signal, that PrusaLink is connected"""
while not self.quit_evt.is_set():
instruction = enqueue_instruction(
self.serial_queue, f"M79 S\"{self.mode.value}\"",
to_front=True)
self.last_keepalive = monotonic()
wait_for_instruction(instruction, should_wait_evt=self.quit_evt)
to_wait = self.last_keepalive + KEEPALIVE_INTERVAL - monotonic()
if to_wait >= 0:
self.wait_evt.wait(to_wait)
if self.wait_evt.is_set():
self.wait_evt.clear()

def stop(self):
"""Stops the keepalive sender"""
self.quit_evt.set()
self.wait_evt.set()

def wait_stopped(self):
"""Waits for Keepalive thread to stop"""
self.keepalive_thread.join()
11 changes: 10 additions & 1 deletion prusa/link/printer_adapter/prusa_link.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
from .filesystem.storage_controller import StorageController
from .ip_updater import IPUpdater
from .job import Job, JobState
from .keepalive import Keepalive
from .lcd_printer import LCDPrinter
from .model import Model
from .print_stat_doubler import PrintStatDoubler
Expand Down Expand Up @@ -139,6 +140,9 @@ def __init__(self, cfg: Config, settings: Settings) -> None:
threshold_path=self.cfg.daemon.threshold_file)
# -----

self.keepalive = Keepalive(self.serial_queue)
self.keepalive.set_use_connect(self.settings.use_connect())

self.printer = MyPrinter()

drivers: List[Type[CameraDriver]] = [V4L2Driver]
Expand Down Expand Up @@ -315,6 +319,7 @@ def __init__(self, cfg: Config, settings: Settings) -> None:
self.telemetry_passer)
self.auto_telemetry.start()

self.keepalive.start()
self.printer_polling.start()
self.storage_controller.start()
self.ip_updater.start()
Expand Down Expand Up @@ -389,6 +394,7 @@ def stop(self, fast: bool = False) -> None:
self.printer.indicate_stop()
self.printer_polling.stop()
self.storage_controller.stop()
self.keepalive.stop()
self.lcd_printer.stop(fast)
# This is for pylint to stop complaining, I'd like stop(fast) more
if fast:
Expand Down Expand Up @@ -417,6 +423,7 @@ def stop(self, fast: bool = False) -> None:
self.printer.wait_stopped()
self.printer_polling.wait_stopped()
self.storage_controller.wait_stopped()
self.keepalive.wait_stopped()
self.lcd_printer.wait_stopped()
self.ip_updater.wait_stopped()
self.camera_governor.wait_stopped()
Expand Down Expand Up @@ -766,7 +773,9 @@ def printer_registered(self, token: str) -> None:
self.settings.printer.type = printer_type_string
self.settings.service_connect.token = token
self.settings.update_sections()
use_connect_errors(self.settings.use_connect())
use_connect = self.settings.use_connect()
use_connect_errors(use_connect)
self.keepalive.set_use_connect(use_connect)
with open(self.cfg.printer.settings, 'w', encoding='utf-8') as ini:
self.settings.write(ini)

Expand Down
6 changes: 5 additions & 1 deletion prusa/link/sdk_augmentation/printer.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from .. import __version__
from ..conditions import use_connect_errors
from ..const import PRINTER_CONF_TYPES
from ..printer_adapter.keepalive import Keepalive
from ..printer_adapter.lcd_printer import LCDPrinter
from ..printer_adapter.model import Model
from ..printer_adapter.structures.mc_singleton import MCSingleton
Expand All @@ -35,6 +36,7 @@ class MyPrinter(SDKPrinter, metaclass=MCSingleton):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.lcd_printer = LCDPrinter.get_instance()
self.keepalive = Keepalive.get_instance()
self.download_thread = Thread(target=self.download_loop,
name="download")
self.model = Model.get_instance()
Expand Down Expand Up @@ -81,7 +83,9 @@ def connection_from_settings(self, settings):
token = settings.service_connect.token

self.set_connection(server, token)
use_connect_errors(settings.use_connect())
use_connect = settings.use_connect()
self.keepalive.set_use_connect(use_connect)
use_connect_errors(use_connect)

def get_file_info(self, caller: Command) -> Dict[str, Any]:
"""Return file info for a given file
Expand Down

0 comments on commit 6a88e0d

Please sign in to comment.