From 6ff8d8b5af978b1b646767c928d9acd3c9a2dbc8 Mon Sep 17 00:00:00 2001 From: Chuck <33324927+ChuckBuilds@users.noreply.github.com> Date: Sun, 10 Aug 2025 16:27:49 -0500 Subject: [PATCH] display preview error handling --- templates/index_v2.html | 19 ++++++++++++++++--- web_interface_v2.py | 41 ++++++++++++++++++++++++++--------------- 2 files changed, 42 insertions(+), 18 deletions(-) diff --git a/templates/index_v2.html b/templates/index_v2.html index a065835f..0b5fdc35 100644 --- a/templates/index_v2.html +++ b/templates/index_v2.html @@ -1412,6 +1412,9 @@
Loading API metrics...
If empty, ensure the server is running and /api/metrics is reachable.
+
+ +
@@ -1992,6 +1995,10 @@ // Update image and meta label img.style.imageRendering = 'pixelated'; + img.onload = () => { + // Ensure LED dot overlay samples after image is ready + renderLedDots(); + }; img.src = `data:image/png;base64,${data.image}`; const meta = document.getElementById('previewMeta'); if (meta) { @@ -2034,9 +2041,8 @@ const ctx = ledCanvas.getContext('2d'); ctx.clearRect(0, 0, ledCanvas.width, ledCanvas.height); - // Draw black background (gaps between LEDs) - ctx.fillStyle = '#000'; - ctx.fillRect(0, 0, ledCanvas.width, ledCanvas.height); + // Clear previous overlay; do not forcibly black-out if sampling fails + ctx.clearRect(0, 0, ledCanvas.width, ledCanvas.height); // Create an offscreen canvas to sample pixel colors const off = document.createElement('canvas'); @@ -2051,6 +2057,7 @@ } catch (_) { /* draw failures ignored */ } // Draw circular dots for each LED pixel + let drawn = 0; for (let y = 0; y < logicalHeight; y++) { for (let x = 0; x < logicalWidth; x++) { const pixel = offCtx.getImageData(x, y, 1, 1).data; @@ -2063,8 +2070,14 @@ ctx.beginPath(); ctx.arc(cx, cy, dotRadius, 0, Math.PI * 2); ctx.fill(); + drawn++; } } + + // If nothing was drawn (e.g., image not ready), hide overlay to show base image + if (drawn === 0) { + ledCanvas.style.display = 'none'; + } } // Tab functionality diff --git a/web_interface_v2.py b/web_interface_v2.py index 72b2bfe6..7b47d9ef 100644 --- a/web_interface_v2.py +++ b/web_interface_v2.py @@ -62,18 +62,28 @@ class DisplayMonitor: try: # Prefer service-provided snapshot if available (works when ledmatrix service is running) if os.path.exists(snapshot_path): - # Read atomically by reopening; ignore partials by retrying once + # Read atomically by reopening; ignore partials by skipping this frame img_bytes = None - for _ in range(2): - try: - with open(snapshot_path, 'rb') as f: - img_bytes = f.read() - break - except Exception: - socketio.sleep(0.02) - if not img_bytes: - raise RuntimeError('Snapshot read failed') - img_str = base64.b64encode(img_bytes).decode() + try: + with open(snapshot_path, 'rb') as f: + img_bytes = f.read() + except Exception: + img_bytes = None + if img_bytes: + img_str = base64.b64encode(img_bytes).decode() + # If we can infer dimensions from display_manager, include them; else leave 0 + width = display_manager.width if display_manager else 0 + height = display_manager.height if display_manager else 0 + current_display_data = { + 'image': img_str, + 'width': width, + 'height': height, + 'timestamp': time.time() + } + socketio.emit('display_update', current_display_data) + # Yield and continue to next frame + socketio.sleep(0.1) + continue # If we can infer dimensions from display_manager, include them; else leave 0 width = display_manager.width if display_manager else 0 height = display_manager.height if display_manager else 0 @@ -97,14 +107,15 @@ class DisplayMonitor: } socketio.emit('display_update', current_display_data) - except Exception as e: - logger.error(f"Display monitor error: {e}", exc_info=True) + except Exception: + # Swallow errors in the monitor loop to avoid log spam + pass # Yield to the async loop; target ~5-10 FPS try: - socketio.sleep(0.2) + socketio.sleep(0.1) except Exception: - time.sleep(0.2) + time.sleep(0.1) display_monitor = DisplayMonitor()