Browse Source

Fix SD card error after FTP upload by waiting for 226 response (#645)

  The FTP upload skipped reading the server's "Transfer complete"
  confirmation, sending the print command before the file was flushed
  to the SD card. Now waits for the 226 response with a 60s timeout.
maziggy 2 months ago
parent
commit
0efb4690b9
2 changed files with 35 additions and 5 deletions
  1. 1 0
      CHANGELOG.md
  2. 34 5
      backend/app/services/bambu_ftp.py

+ 1 - 0
CHANGELOG.md

@@ -13,6 +13,7 @@ All notable changes to Bambuddy will be documented in this file.
 - **Separate Permission for AMS Spool Assignments** ([#635](https://github.com/maziggy/bambuddy/issues/635)) — Added a new `inventory:view_assignments` permission that controls whether spool-to-AMS-slot assignment data is visible on the Printers page. Previously, viewing spool assignments on printer cards required `inventory:read`, which also exposed the full Inventory page in the sidebar. Admins can now grant `inventory:view_assignments` without `inventory:read` so users can see what's loaded in the AMS without accessing the full spool inventory. All default groups (Administrators, Operators, Viewers) include the new permission automatically. Also fixed multi-word permission labels in the group editor (e.g. "Update_Own" → "Update Own"). Reported by @Minebuddy.
 
 ### Fixed
+- **SD Card Error After FTP Upload** ([#645](https://github.com/maziggy/bambuddy/issues/645)) — After printing one file, subsequent prints could fail with `0500-C010 "MicroSD Card read/write exception"` until Bambuddy was restarted. The FTP upload used `transfercmd()` for A1 compatibility but skipped reading the server's 226 "Transfer complete" response, leaving the SD card file write unconfirmed. The print command was sent via MQTT before the printer's FTP server had finished flushing the file to disk. Now waits for the 226 confirmation after each upload (with a 60-second timeout for slower models like H2D). Reported by @lanfi89, confirmed by @Bademeister89.
 - **P2S Shows Carbon Rod Maintenance Tasks** ([#640](https://github.com/maziggy/bambuddy/issues/640)) — The P2S was incorrectly classified as a carbon rod printer, showing "Lubricate Carbon Rods" and "Clean Carbon Rods" maintenance tasks. The P2S uses hardened steel linear shafts, not carbon fiber rods. Added a new `steel_rod` motion system category and "Lubricate Steel Rods" / "Clean Steel Rods" maintenance tasks specific to the P2S. X1/P1 series continue to show carbon rod tasks; A1/H2 series continue to show linear rail tasks. Reported by @maziggy.
 - **Dispatch Toast Stuck After Second Print** — The print dispatch progress toast ("Starting prints…") stayed visible forever after the second print dispatch in a session. The dedup guard (`lastDispatchSummaryRef`) that prevents duplicate completion toasts was never reset between batches, so every single-printer dispatch produced the same summary key (`"first-complete:1:0"`). The first print completed normally, but subsequent completions matched the stale ref and skipped creating the done toast — leaving the progress toast stuck in "Processing" state with no way to dismiss except a page reload. Now resets the dedup guard whenever the dispatch toast is dismissed (auto-dismiss timeout, cleanup events) and when a new batch starts.
 

+ 34 - 5
backend/app/services/bambu_ftp.py

@@ -413,11 +413,30 @@ class BambuFTPClient:
                     except OSError:
                         pass
 
-            # Skip voidresp() for all models — the data transfer is already complete.
-            # A1 models hang indefinitely on voidresp(). H2D printers (vsFTPd) delay
-            # the 226 response by 30+ seconds after data is fully sent. Even X1C/P1S
-            # gain nothing from waiting — the file is on the SD card once sendall() returns.
-            # Verified via direct curl upload: 226 arrives ~32s after data channel closes.
+            # Wait for the server's 226 "Transfer complete" response to confirm
+            # the file has been flushed to the SD card. Without this, the printer
+            # may try to read an incomplete file when the print command is sent,
+            # causing 0500-C010 "MicroSD Card read/write exception" errors.
+            # See: https://bugs.python.org/issue25458 (ftplib response desync)
+            try:
+                old_timeout = self._ftp.sock.gettimeout()
+                # Use a generous timeout — H2D printers can take 30+ seconds
+                # to send the 226 after the data channel closes.
+                self._ftp.sock.settimeout(max(self.timeout, 60))
+                try:
+                    resp = self._ftp.voidresp()
+                    logger.info("FTP STOR confirmed for %s: %s", remote_path, resp.strip())
+                finally:
+                    self._ftp.sock.settimeout(old_timeout)
+            except Exception as e:
+                # Timeout or error reading 226 — log but proceed, the data
+                # was fully sent so the file is likely on the SD card.
+                logger.warning(
+                    "FTP STOR confirmation not received for %s (proceeding): %s (%s)",
+                    remote_path,
+                    e,
+                    type(e).__name__,
+                )
 
             if callback_exception is not None:
                 cleanup_ok = False
@@ -489,6 +508,16 @@ class BambuFTPClient:
                     conn.close()
                 except OSError:
                     pass
+            # Wait for 226 confirmation (see upload_file for rationale)
+            try:
+                old_timeout = self._ftp.sock.gettimeout()
+                self._ftp.sock.settimeout(max(self.timeout, 60))
+                try:
+                    self._ftp.voidresp()
+                finally:
+                    self._ftp.sock.settimeout(old_timeout)
+            except Exception:
+                pass  # Best-effort — data was sent, proceed
             return True
         except (OSError, ftplib.Error):
             return False