active_print_spoolman.py 2.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253
  1. """Track Spoolman data for active prints."""
  2. from sqlalchemy import JSON, ForeignKey, UniqueConstraint
  3. from sqlalchemy.orm import Mapped, mapped_column
  4. from backend.app.core.database import Base
  5. class ActivePrintSpoolman(Base):
  6. """Stores Spoolman tracking data for active prints.
  7. This data is captured at print start and used at print completion
  8. to report per-filament usage to the correct Spoolman spools.
  9. Rows are deleted after print completes.
  10. Key: (printer_id, archive_id) - allows same archive on different printers
  11. """
  12. __tablename__ = "active_print_spoolman"
  13. __table_args__ = (UniqueConstraint("printer_id", "archive_id", name="uq_printer_archive"),)
  14. id: Mapped[int] = mapped_column(primary_key=True)
  15. printer_id: Mapped[int] = mapped_column(ForeignKey("printers.id", ondelete="CASCADE"))
  16. archive_id: Mapped[int] = mapped_column(ForeignKey("print_archives.id", ondelete="CASCADE"))
  17. # Per-filament usage from 3MF: [{"slot_id": 1, "used_g": 50.5, "type": "PLA"}, ...]
  18. # Nullable for the no-3MF case ("Untitled" prints where Bambu didn't keep a
  19. # .gcode.3mf on the printer): the row still gets created so the completion
  20. # path can use ``tray_remain_start`` for an AMS remain%-delta write,
  21. # mirroring the internal-inventory Path 2 fallback in usage_tracker (#1820).
  22. filament_usage: Mapped[list | None] = mapped_column(JSON, nullable=True)
  23. # AMS tray state at print start: {0: {"tray_uuid": "...", "tag_uid": "..."}, ...}
  24. ams_trays: Mapped[dict] = mapped_column(JSON)
  25. # Custom slot-to-tray mapping from queue (optional): [5, -1, 2, -1]
  26. slot_to_tray: Mapped[list | None] = mapped_column(JSON, nullable=True)
  27. # Per-layer cumulative usage from G-code parsing (for accurate partial usage)
  28. # Format: {"0": {0: 125.5}, "1": {0: 250.0, 1: 50.0}, ...}
  29. # Keys are layer numbers (as strings for JSON), values are filament_id -> mm
  30. layer_usage: Mapped[dict | None] = mapped_column(JSON, nullable=True)
  31. # Filament properties (density, diameter per filament slot)
  32. # Format: {1: {"density": 1.24, "diameter": 1.75, "type": "PLA"}, ...}
  33. filament_properties: Mapped[dict | None] = mapped_column(JSON, nullable=True)
  34. # AMS tray remain% per slot at print start, captured so the completion
  35. # path can compute a remain-delta when the 3MF didn't cover a slot (or
  36. # there was no 3MF at all — #1820). Matches the internal-inventory
  37. # ``tray_remain_start`` snapshot at usage_tracker.py:301.
  38. # Format: {"<ams_id>-<tray_id>": {"remain": int, "tray_uuid": str}, ...}
  39. tray_remain_start: Mapped[dict | None] = mapped_column(JSON, nullable=True)