print_control.py 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263
  1. """Stopping a print on a user's behalf.
  2. Every place a user can stop a print goes through here, so none can send the
  3. stop without the "stopped by the user" mark: the printer card, the queue and
  4. the webhook routes call stop_print_by_user; the HMS error dialog's "Stop
  5. Printing" sends its own stop payload and calls mark_stopped_by_user. Without
  6. the mark the printer's resulting "failed"/"aborted" status is taken at face
  7. value: a "print failed" notification goes out, and on some printers the
  8. cancel sequence's own error is reported as a fault such as a layer shift.
  9. """
  10. import logging
  11. from backend.app.services.printer_manager import printer_manager
  12. logger = logging.getLogger(__name__)
  13. def mark_stopped_by_user(printer_id: int) -> None:
  14. """Record that the user stopped the print on this printer (see module docstring)."""
  15. try:
  16. from backend.app.main import mark_printer_stopped_by_user
  17. mark_printer_stopped_by_user(printer_id)
  18. except Exception as e:
  19. logger.warning("Failed to mark printer %s as user-stopped: %s", printer_id, e)
  20. def stop_print_by_user(printer_id: int, *, mark_when_unsent: bool = False) -> bool:
  21. """Send the stop command; returns whether it was sent.
  22. The printer is marked as stopped by the user when the command was sent
  23. and the printer is not known to be idle (nothing to stop), or always with
  24. ``mark_when_unsent`` (the queue, which closes the job even
  25. when the printer is offline, so a "failed" it reports later still reads
  26. as the user's cancel). Errors sending the command propagate to the caller.
  27. """
  28. try:
  29. sent = printer_manager.stop_print(printer_id)
  30. except Exception:
  31. if mark_when_unsent:
  32. mark_stopped_by_user(printer_id)
  33. raise
  34. if mark_when_unsent or (sent and not _known_idle(printer_id)):
  35. mark_stopped_by_user(printer_id)
  36. return sent
  37. def _known_idle(printer_id: int) -> bool:
  38. """The printer reports a state with no print loaded.
  39. A stop sent then stops nothing, and its mark would linger until the next
  40. print's start clears it -- which a new MQTT client that first sees that
  41. print mid-way never runs, so the print's later failure read as the user's
  42. cancel (USP/I4). An unknown state is not idle: the mark is the safe side.
  43. That includes "unknown", the state before the first report after a
  44. (re)connect.
  45. """
  46. status = printer_manager.get_status(printer_id)
  47. state = getattr(status, "state", None)
  48. if not state or state == "unknown":
  49. return False
  50. return state not in printer_manager.ACTIVE_PRINT_STATES