active_print_session.py 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657
  1. """Durable copy of the filament-attribution context for an in-flight print."""
  2. from datetime import datetime
  3. from sqlalchemy import JSON, DateTime, ForeignKey
  4. from sqlalchemy.orm import Mapped, mapped_column
  5. from backend.app.core.database import Base
  6. class ActivePrintSession(Base):
  7. """Print-start context the completion path needs, persisted per printer.
  8. ``usage_tracker._active_sessions`` holds the same data in memory, and the
  9. tray-change log lives on ``PrinterState``. Both are lost when Bambuddy
  10. restarts mid-print, which on a long print silently destroys filament
  11. attribution: without the plate the 3MF parser sums every plate, without the
  12. assignment snapshot a spool unlinked at runout can't be resolved, and
  13. without the tray-change log an AMS-backup switch charges the whole print to
  14. whichever tray happened to finish it.
  15. One row per printer — a printer runs one print at a time. Written at print
  16. start, appended to on every tray change, deleted at completion. A leaked
  17. row (completion missed entirely) is harmless: print start overwrites it,
  18. and the completion path ignores a row whose ``started_at`` doesn't line up
  19. with the print it is closing.
  20. The Spoolman writer has had an equivalent durable row since #1820
  21. (``active_print_spoolman``); this is the internal-inventory counterpart.
  22. """
  23. __tablename__ = "active_print_sessions"
  24. printer_id: Mapped[int] = mapped_column(ForeignKey("printers.id", ondelete="CASCADE"), primary_key=True)
  25. print_name: Mapped[str] = mapped_column(default="")
  26. started_at: Mapped[datetime] = mapped_column(DateTime)
  27. # tray_now at print start — reliable, unlike at completion where the
  28. # printer has usually retracted and reports 255.
  29. tray_now_at_start: Mapped[int] = mapped_column(default=-1)
  30. # Queue item's plate for multi-plate 3MFs dispatched one plate at a time.
  31. plate_id: Mapped[int | None] = mapped_column(nullable=True)
  32. # Slicer slot -> global tray, as dispatched: [2]
  33. ams_mapping: Mapped[list | None] = mapped_column(JSON, nullable=True)
  34. # {"<ams_id>-<tray_id>": spool_id} — the assignment map as it stood before
  35. # the print could disturb it.
  36. spool_assignments: Mapped[dict | None] = mapped_column(JSON, nullable=True)
  37. # {"<ams_id>-<tray_id>": remain%} for the remain-delta fallback path.
  38. tray_remain_start: Mapped[dict | None] = mapped_column(JSON, nullable=True)
  39. # [[global_tray_id, layer_num], ...] mirroring PrinterState.tray_change_log.
  40. tray_change_log: Mapped[list | None] = mapped_column(JSON, nullable=True)