Browse Source

Add print command response verification for #737

  After sending a print command via MQTT, monitor whether the printer's
  gcode_state changes within 15 seconds. If not, log a warning visible in
  support packages. Addresses silent command drops observed on P1S firmware
  01.09.01.00 where the printer ignores project_file commands while
  continuing to send status updates.
maziggy 2 tháng trước cách đây
mục cha
commit
00bc1e214a
2 tập tin đã thay đổi với 39 bổ sung0 xóa
  1. 1 0
      CHANGELOG.md
  2. 38 0
      backend/app/services/background_dispatch.py

+ 1 - 0
CHANGELOG.md

@@ -26,6 +26,7 @@ All notable changes to Bambuddy will be documented in this file.
 ### Changed
 
 ### Improved
+- **Print Command Response Verification** ([#737](https://github.com/maziggy/bambuddy/issues/737)) — After sending a print command, BambuBuddy now monitors whether the printer's state changes within 15 seconds. If the printer silently ignores the command (observed on some P1S firmware versions where the MQTT command handler becomes unresponsive), a warning is logged for diagnostics. This aids debugging when users report prints not starting despite BambuBuddy showing success.
 - **Compact Assign Spool Modal** ([#725](https://github.com/maziggy/bambuddy/issues/725)) — The "Assign Spool" modal now uses a compact 3-column grid layout instead of a vertical list, showing more spools at once without scrolling. Each card displays the spool name, color, and remaining/total weight. The modal is wider with a taller scroll area. Requested by @RosdasHH.
 - **Reformatted AMS Drying Presets Table** ([#732](https://github.com/maziggy/bambuddy/issues/732)) — The drying presets table in Settings now groups columns by AMS type (AMS 2 Pro, AMS-HT) with inline °C and h unit labels next to each input, replacing the previous flat column layout. Requested by @cadtoolbox.
 

+ 38 - 0
backend/app/services/background_dispatch.py

@@ -672,6 +672,10 @@ class BackgroundDispatchService:
                     )
                     raise RuntimeError("Failed to start print")
 
+                pre_state = getattr(printer_manager.get_status(job.printer_id), "state", None)
+                if pre_state:
+                    asyncio.create_task(self._verify_print_response(job.printer_id, printer_name, pre_state))
+
                 if job.requested_by_user_id and job.requested_by_username:
                     printer_manager.set_current_print_user(
                         job.printer_id,
@@ -837,12 +841,46 @@ class BackgroundDispatchService:
                     await db.rollback()
                     raise RuntimeError("Failed to start print")
 
+                pre_state = getattr(printer_manager.get_status(job.printer_id), "state", None)
+                if pre_state:
+                    asyncio.create_task(self._verify_print_response(job.printer_id, printer_name, pre_state))
+
                 await db.commit()
             except DispatchJobCancelled:
                 await db.rollback()
                 await self._set_active_message(job, f"Cancelled upload on {printer_name}.")
                 raise
 
+    @staticmethod
+    async def _verify_print_response(
+        printer_id: int,
+        printer_name: str,
+        pre_state: str,
+        timeout: float = 15.0,
+        poll_interval: float = 3.0,
+    ):
+        """Check if the printer responded to a print command.
+
+        Runs as a fire-and-forget background task after start_print() succeeds.
+        If the printer's gcode_state hasn't changed within the timeout, logs a
+        warning for diagnostics (visible in support packages).
+        """
+        deadline = time.monotonic() + timeout
+        while time.monotonic() < deadline:
+            await asyncio.sleep(poll_interval)
+            state = printer_manager.get_status(printer_id)
+            if not state:
+                return  # Printer disconnected
+            if state.state != pre_state:
+                return  # Printer responded
+        logger.warning(
+            "Printer %s (%d) did not respond to print command within %.0fs (state still %s) — printer may need restart",
+            printer_name,
+            printer_id,
+            timeout,
+            pre_state,
+        )
+
     @staticmethod
     async def _cleanup_sd_card_file(
         printer_ip: str,