display preview error handling

This commit is contained in:
Chuck
2025-08-10 16:27:49 -05:00
parent bd7d136504
commit 6ff8d8b5af
2 changed files with 42 additions and 18 deletions

View File

@@ -1412,6 +1412,9 @@
<div id="api-metrics" class="stat-card" style="text-align:left;"> <div id="api-metrics" class="stat-card" style="text-align:left;">
<div>Loading API metrics...</div> <div>Loading API metrics...</div>
<div style="margin-top:10px; font-size:12px; color:#666;">If empty, ensure the server is running and /api/metrics is reachable.</div> <div style="margin-top:10px; font-size:12px; color:#666;">If empty, ensure the server is running and /api/metrics is reachable.</div>
<div style="margin-top:10px;">
<button class="btn btn-primary" onclick="updateApiMetrics()"><i class="fas fa-sync"></i> Refresh API Metrics</button>
</div>
</div> </div>
</div> </div>
@@ -1992,6 +1995,10 @@
// Update image and meta label // Update image and meta label
img.style.imageRendering = 'pixelated'; img.style.imageRendering = 'pixelated';
img.onload = () => {
// Ensure LED dot overlay samples after image is ready
renderLedDots();
};
img.src = `data:image/png;base64,${data.image}`; img.src = `data:image/png;base64,${data.image}`;
const meta = document.getElementById('previewMeta'); const meta = document.getElementById('previewMeta');
if (meta) { if (meta) {
@@ -2034,9 +2041,8 @@
const ctx = ledCanvas.getContext('2d'); const ctx = ledCanvas.getContext('2d');
ctx.clearRect(0, 0, ledCanvas.width, ledCanvas.height); ctx.clearRect(0, 0, ledCanvas.width, ledCanvas.height);
// Draw black background (gaps between LEDs) // Clear previous overlay; do not forcibly black-out if sampling fails
ctx.fillStyle = '#000'; ctx.clearRect(0, 0, ledCanvas.width, ledCanvas.height);
ctx.fillRect(0, 0, ledCanvas.width, ledCanvas.height);
// Create an offscreen canvas to sample pixel colors // Create an offscreen canvas to sample pixel colors
const off = document.createElement('canvas'); const off = document.createElement('canvas');
@@ -2051,6 +2057,7 @@
} catch (_) { /* draw failures ignored */ } } catch (_) { /* draw failures ignored */ }
// Draw circular dots for each LED pixel // Draw circular dots for each LED pixel
let drawn = 0;
for (let y = 0; y < logicalHeight; y++) { for (let y = 0; y < logicalHeight; y++) {
for (let x = 0; x < logicalWidth; x++) { for (let x = 0; x < logicalWidth; x++) {
const pixel = offCtx.getImageData(x, y, 1, 1).data; const pixel = offCtx.getImageData(x, y, 1, 1).data;
@@ -2063,8 +2070,14 @@
ctx.beginPath(); ctx.beginPath();
ctx.arc(cx, cy, dotRadius, 0, Math.PI * 2); ctx.arc(cx, cy, dotRadius, 0, Math.PI * 2);
ctx.fill(); 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 // Tab functionality

View File

@@ -62,18 +62,28 @@ class DisplayMonitor:
try: try:
# Prefer service-provided snapshot if available (works when ledmatrix service is running) # Prefer service-provided snapshot if available (works when ledmatrix service is running)
if os.path.exists(snapshot_path): 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 img_bytes = None
for _ in range(2): try:
try: with open(snapshot_path, 'rb') as f:
with open(snapshot_path, 'rb') as f: img_bytes = f.read()
img_bytes = f.read() except Exception:
break img_bytes = None
except Exception: if img_bytes:
socketio.sleep(0.02) img_str = base64.b64encode(img_bytes).decode()
if not img_bytes: # If we can infer dimensions from display_manager, include them; else leave 0
raise RuntimeError('Snapshot read failed') width = display_manager.width if display_manager else 0
img_str = base64.b64encode(img_bytes).decode() 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 # If we can infer dimensions from display_manager, include them; else leave 0
width = display_manager.width if display_manager else 0 width = display_manager.width if display_manager else 0
height = display_manager.height 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) socketio.emit('display_update', current_display_data)
except Exception as e: except Exception:
logger.error(f"Display monitor error: {e}", exc_info=True) # Swallow errors in the monitor loop to avoid log spam
pass
# Yield to the async loop; target ~5-10 FPS # Yield to the async loop; target ~5-10 FPS
try: try:
socketio.sleep(0.2) socketio.sleep(0.1)
except Exception: except Exception:
time.sleep(0.2) time.sleep(0.1)
display_monitor = DisplayMonitor() display_monitor = DisplayMonitor()