print_jobs.py 3.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. """Telling the printer's own internal jobs apart from a user's print.
  2. Bambu firmware runs jobs on its own behalf -- bed levelling, vibration
  3. compensation, the pressure-advance line it lays down before a print when flow
  4. dynamics calibration is on -- and reports them over MQTT through exactly the
  5. same print-start and print-complete events a real print uses. Nothing about the
  6. event says "this one is mine": Bambuddy has to recognise the job by name.
  7. Getting that wrong is not free. An unrecognised calibration run has no 3MF
  8. anywhere on the printer, so the archive path sweeps FTP for a file that cannot
  9. exist -- six candidate names across five directories, with retries -- and then
  10. writes a no-3MF archive named after the calibration, on a printer that is in
  11. the middle of calibrating.
  12. Kept as a leaf module with no imports of its own so both the print-start and
  13. print-complete callbacks can share one answer.
  14. """
  15. # Job names the printer runs for itself. Matched exactly (after normalising),
  16. # not by prefix or substring: "auto" and "calib" are ordinary words in a user's
  17. # own filenames, and a rule loose enough to catch an unnamed future calibration
  18. # would silently swallow somebody's print.
  19. #
  20. # ``auto_cali_for_user`` is the bed-levelling / vibration run, normally reported
  21. # with a ``/usr/etc/print/`` path that the rule below catches on its own; it is
  22. # listed anyway because the path is not guaranteed and a name-only report of it
  23. # would otherwise slip through.
  24. #
  25. # ``auto_pa_line_calib_mode`` is the pressure-advance (K profile) line. This one
  26. # is reported as a *subtask name* with no ``/usr/`` path at all, which is why
  27. # the path rule alone was never enough.
  28. #
  29. # ``pa_line_calib_mode`` and ``pa_pattern_calib_mode`` are the same calibration
  30. # started by hand rather than automatically before a print. Manual flow dynamics
  31. # offers both shapes -- a line and a pattern -- and each reports under its own
  32. # name with no ``auto_`` prefix, so neither is covered by the automatic entry
  33. # above and both produced the same no-3MF archive. They are listed as two
  34. # literals rather than matched by a shared ``pa_`` stem for the reason the whole
  35. # set is exact: a stem rule would also swallow a user's own ``pa_bracket.3mf``.
  36. INTERNAL_JOB_NAMES = frozenset(
  37. {
  38. "auto_cali_for_user",
  39. "auto_pa_line_calib_mode",
  40. "pa_line_calib_mode",
  41. "pa_pattern_calib_mode",
  42. }
  43. )
  44. # Longest first: ``.gcode.3mf`` has to be stripped whole, or ``.3mf`` would
  45. # match first and leave a trailing ``.gcode`` behind.
  46. _PRINT_SUFFIXES = (".gcode.3mf", ".gcode", ".3mf")
  47. def _normalize_job_name(value: str) -> str:
  48. """Reduce a reported name to something comparable against the set above.
  49. Drops any directory part, one print-file suffix, and case. The printer is
  50. not consistent about which of these it includes -- the same calibration
  51. reports as a bare name in ``subtask_name`` and, when it appears at all, as
  52. a full path in ``gcode_file``.
  53. """
  54. name = value.rsplit("/", 1)[-1].rsplit("\\", 1)[-1].strip().casefold()
  55. for suffix in _PRINT_SUFFIXES:
  56. if name.endswith(suffix):
  57. return name[: -len(suffix)]
  58. return name
  59. def is_internal_printer_job(filename: str | None, subtask_name: str | None = None) -> bool:
  60. """True when this print event belongs to the printer, not to a user.
  61. Both fields are tested because neither is reliably populated: the
  62. pressure-advance line arrives as a subtask name with no filename, while the
  63. levelling run arrives as a ``/usr/etc/print/...`` path. A job is internal if
  64. *either* field says so.
  65. """
  66. if filename and filename.startswith("/usr/"):
  67. # Bambu keeps its own calibration gcode on the read-only system
  68. # partition. Nothing a user can print ever lives there, so the whole
  69. # prefix is safe to treat as internal without naming each file.
  70. return True
  71. return any(_normalize_job_name(value) in INTERNAL_JOB_NAMES for value in (filename, subtask_name) if value)