test_external_mapping_on_ams_printer_3239.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. """A mapping made for an external spool, sent to a printer whose AMS holds it (#3239).
  2. Two A1 in one location, both with black PETG: A1-018 has no AMS and feeds it
  3. from the external spool (``[254]``), A1-002 holds it in AMS tray 0 and has an
  4. empty external spool. A failed job of A1-018 was queued again and moved to the
  5. location, kept ``[254]``, and was picked by A1-002, which then stopped at once
  6. with "External filament is missing". The #2799 check let it through because an
  7. external tray the printer doesn't report as loaded counted as no evidence.
  8. The printers' trays below are the reporter's, from the support bundle.
  9. """
  10. import json
  11. from types import SimpleNamespace
  12. from unittest.mock import AsyncMock, MagicMock, patch
  13. import pytest
  14. from backend.app.services.print_scheduler import PrintScheduler
  15. BLACK_PETG = [{"slot_id": 1, "type": "PETG", "color": "#000000", "tray_info_idx": "GFG99"}]
  16. EMPTY_EXTERNAL = {"id": "254", "tray_type": "", "tray_color": "00000000", "remain": 0, "tray_info_idx": ""}
  17. LOADED_EXTERNAL = {"id": "254", "tray_type": "PETG", "tray_color": "000000FF", "remain": 80, "tray_info_idx": "GFG99"}
  18. A1_002_AMS = [
  19. {
  20. "id": "0",
  21. "tray": [
  22. {"id": "0", "tray_type": "PETG", "tray_color": "000000FF", "tray_info_idx": "GFG99"},
  23. {"id": "1", "tray_type": "PETG", "tray_color": "C52C18FF", "tray_info_idx": "GFG99"},
  24. {"id": "2", "tray_type": "PETG", "tray_color": "AC95D5FF", "tray_info_idx": "GFG99"},
  25. {"id": "3", "tray_type": "PETG", "tray_color": "0085D5FF", "tray_info_idx": "GFG99"},
  26. ],
  27. }
  28. ]
  29. def _status(ams, vt_tray):
  30. return SimpleNamespace(
  31. raw_data={"ams": ams, "vt_tray": [vt_tray], "ams_extruder_map": {}},
  32. nozzles=[],
  33. fila_switch=None,
  34. )
  35. def _item():
  36. item = MagicMock()
  37. item.id = 2453
  38. item.printer_id = None
  39. item.ams_mapping = json.dumps([254])
  40. item.skip_filament_check = False
  41. item.filament_overrides = None
  42. item.required_filament_types = None
  43. item.manual_start = False
  44. item.filament_short = False
  45. return item
  46. def _scheduler():
  47. scheduler = PrintScheduler()
  48. scheduler._get_filament_requirements = AsyncMock(return_value=[dict(r) for r in BLACK_PETG])
  49. scheduler._get_bool_setting = AsyncMock(return_value=False)
  50. return scheduler
  51. async def _ensure(status):
  52. scheduler = _scheduler()
  53. item = _item()
  54. with patch("backend.app.services.print_scheduler.printer_manager") as pm:
  55. pm.get_status.return_value = status
  56. await scheduler._ensure_ams_mapping(AsyncMock(), 12, item)
  57. return json.loads(item.ams_mapping) if item.ams_mapping else None
  58. @pytest.mark.asyncio
  59. async def test_the_reporters_job_is_remapped_to_the_ams_tray():
  60. assert await _ensure(_status(A1_002_AMS, EMPTY_EXTERNAL)) == [0]
  61. @pytest.mark.asyncio
  62. async def test_the_printer_it_was_made_for_keeps_the_external_spool():
  63. """A1-018: no AMS, black PETG on the external spool."""
  64. assert await _ensure(_status([], LOADED_EXTERNAL)) == [254]
  65. @pytest.mark.asyncio
  66. async def test_an_external_spool_without_its_filament_set_keeps_printing():
  67. """No AMS, external spool loaded but never given a filament type: there is
  68. nothing else to feed from, so the job goes out as it always did."""
  69. assert await _ensure(_status([], EMPTY_EXTERNAL)) == [254]
  70. @pytest.mark.asyncio
  71. async def test_an_ams_without_the_filament_does_not_take_the_slot():
  72. """An empty external spool alone proves nothing when the AMS can't print the
  73. slot either; the mapping is left for the existing checks."""
  74. other_material = [
  75. {"id": "0", "tray": [{"id": "0", "tray_type": "PLA", "tray_color": "000000FF", "tray_info_idx": "GFL99"}]}
  76. ]
  77. scheduler = _scheduler()
  78. with patch("backend.app.services.print_scheduler.printer_manager") as pm:
  79. pm.get_status.return_value = _status(other_material, EMPTY_EXTERNAL)
  80. conflict = await scheduler._stored_mapping_conflict(AsyncMock(), 12, _item(), [254])
  81. assert conflict is None
  82. @pytest.mark.asyncio
  83. async def test_another_colour_in_the_ams_does_not_take_the_slot():
  84. """A job for this very printer whose external spool is just empty for now:
  85. the printer asking for the spool beats printing it in white."""
  86. white_only = [
  87. {"id": "0", "tray": [{"id": "0", "tray_type": "PETG", "tray_color": "FFFFFFFF", "tray_info_idx": "GFG99"}]}
  88. ]
  89. scheduler = _scheduler()
  90. with patch("backend.app.services.print_scheduler.printer_manager") as pm:
  91. pm.get_status.return_value = _status(white_only, EMPTY_EXTERNAL)
  92. conflict = await scheduler._stored_mapping_conflict(AsyncMock(), 12, _item(), [254])
  93. assert conflict is None
  94. @pytest.mark.asyncio
  95. async def test_the_conflict_names_the_empty_external_spool():
  96. scheduler = _scheduler()
  97. with patch("backend.app.services.print_scheduler.printer_manager") as pm:
  98. pm.get_status.return_value = _status(A1_002_AMS, EMPTY_EXTERNAL)
  99. conflict = await scheduler._stored_mapping_conflict(AsyncMock(), 12, _item(), [254])
  100. assert conflict is not None
  101. assert "external spool" in conflict
  102. assert "tray 0" in conflict
  103. @pytest.mark.asyncio
  104. async def test_print_anyway_still_keeps_the_users_mapping():
  105. scheduler = _scheduler()
  106. item = _item()
  107. item.skip_filament_check = True
  108. with patch("backend.app.services.print_scheduler.printer_manager") as pm:
  109. pm.get_status.return_value = _status(A1_002_AMS, EMPTY_EXTERNAL)
  110. await scheduler._ensure_ams_mapping(AsyncMock(), 12, item)
  111. assert json.loads(item.ams_mapping) == [254]