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()