mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-04-12 05:42:59 +00:00
change web ui v2 server side loading
This commit is contained in:
@@ -1779,7 +1779,7 @@
|
|||||||
|
|
||||||
// Socket.IO connection
|
// Socket.IO connection
|
||||||
function initializeSocket() {
|
function initializeSocket() {
|
||||||
socket = io();
|
socket = io({ transports: ['websocket', 'polling'] });
|
||||||
|
|
||||||
socket.on('connect', function() {
|
socket.on('connect', function() {
|
||||||
updateConnectionStatus(true);
|
updateConnectionStatus(true);
|
||||||
@@ -1789,6 +1789,17 @@
|
|||||||
socket.on('disconnect', function() {
|
socket.on('disconnect', function() {
|
||||||
updateConnectionStatus(false);
|
updateConnectionStatus(false);
|
||||||
showNotification('Disconnected from LED Matrix', 'error');
|
showNotification('Disconnected from LED Matrix', 'error');
|
||||||
|
// Try to reconnect with exponential backoff
|
||||||
|
let attempt = 0;
|
||||||
|
const retry = () => {
|
||||||
|
attempt++;
|
||||||
|
const delay = Math.min(30000, 1000 * Math.pow(2, attempt));
|
||||||
|
setTimeout(() => {
|
||||||
|
if (socket.connected) return;
|
||||||
|
socket.connect();
|
||||||
|
}, delay);
|
||||||
|
};
|
||||||
|
retry();
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on('display_update', function(data) {
|
socket.on('display_update', function(data) {
|
||||||
|
|||||||
@@ -19,7 +19,15 @@ import logging
|
|||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
app.secret_key = os.urandom(24)
|
app.secret_key = os.urandom(24)
|
||||||
socketio = SocketIO(app, cors_allowed_origins="*")
|
# Prefer eventlet if available for stable websockets on Pi; fall back gracefully
|
||||||
|
async_mode = None
|
||||||
|
try:
|
||||||
|
import eventlet # noqa: F401
|
||||||
|
async_mode = 'eventlet'
|
||||||
|
except Exception:
|
||||||
|
async_mode = 'threading'
|
||||||
|
|
||||||
|
socketio = SocketIO(app, cors_allowed_origins="*", async_mode=async_mode)
|
||||||
|
|
||||||
# Global variables
|
# Global variables
|
||||||
config_manager = ConfigManager()
|
config_manager = ConfigManager()
|
||||||
@@ -36,48 +44,45 @@ class DisplayMonitor:
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.running = False
|
self.running = False
|
||||||
self.thread = None
|
self.thread = None
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
if not self.running:
|
if not self.running:
|
||||||
self.running = True
|
self.running = True
|
||||||
self.thread = threading.Thread(target=self._monitor_loop)
|
# Use SocketIO background task for better async compatibility
|
||||||
self.thread.daemon = True
|
self.thread = socketio.start_background_task(self._monitor_loop)
|
||||||
self.thread.start()
|
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
self.running = False
|
self.running = False
|
||||||
if self.thread:
|
# Background task will exit on next loop; no join needed
|
||||||
self.thread.join()
|
|
||||||
|
|
||||||
def _monitor_loop(self):
|
def _monitor_loop(self):
|
||||||
global display_manager, current_display_data
|
global display_manager, current_display_data
|
||||||
while self.running:
|
while self.running:
|
||||||
try:
|
try:
|
||||||
if display_manager and hasattr(display_manager, 'image'):
|
if display_manager and hasattr(display_manager, 'image'):
|
||||||
# Convert PIL image to base64 for web display
|
# Convert raw image to base64 (no server-side scaling; client scales visually)
|
||||||
img_buffer = io.BytesIO()
|
img_buffer = io.BytesIO()
|
||||||
# Scale up the image for better visibility (8x instead of 4x for better clarity)
|
display_manager.image.save(img_buffer, format='PNG')
|
||||||
scaled_img = display_manager.image.resize((
|
|
||||||
display_manager.image.width * 8,
|
|
||||||
display_manager.image.height * 8
|
|
||||||
), Image.NEAREST)
|
|
||||||
scaled_img.save(img_buffer, format='PNG')
|
|
||||||
img_str = base64.b64encode(img_buffer.getvalue()).decode()
|
img_str = base64.b64encode(img_buffer.getvalue()).decode()
|
||||||
|
|
||||||
current_display_data = {
|
current_display_data = {
|
||||||
'image': img_str,
|
'image': img_str,
|
||||||
'width': display_manager.width,
|
'width': display_manager.width,
|
||||||
'height': display_manager.height,
|
'height': display_manager.height,
|
||||||
'timestamp': time.time()
|
'timestamp': time.time()
|
||||||
}
|
}
|
||||||
|
|
||||||
# Emit to all connected clients
|
# Emit to all connected clients
|
||||||
socketio.emit('display_update', current_display_data)
|
socketio.emit('display_update', current_display_data)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Display monitor error: {e}", exc_info=True)
|
logger.error(f"Display monitor error: {e}", exc_info=True)
|
||||||
|
|
||||||
time.sleep(0.05) # Update 20 times per second for smoother display
|
# Yield to the async loop; target ~10 FPS to reduce load
|
||||||
|
try:
|
||||||
|
socketio.sleep(0.1)
|
||||||
|
except Exception:
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
display_monitor = DisplayMonitor()
|
display_monitor = DisplayMonitor()
|
||||||
|
|
||||||
@@ -126,7 +131,7 @@ def get_system_status():
|
|||||||
"""Get current system status including display state, performance metrics, and CPU utilization."""
|
"""Get current system status including display state, performance metrics, and CPU utilization."""
|
||||||
try:
|
try:
|
||||||
# Check if display service is running
|
# Check if display service is running
|
||||||
result = subprocess.run(['sudo', 'systemctl', 'is-active', 'ledmatrix'],
|
result = subprocess.run(['systemctl', 'is-active', 'ledmatrix'],
|
||||||
capture_output=True, text=True)
|
capture_output=True, text=True)
|
||||||
service_active = result.stdout.strip() == 'active'
|
service_active = result.stdout.strip() == 'active'
|
||||||
|
|
||||||
@@ -134,8 +139,8 @@ def get_system_status():
|
|||||||
memory = psutil.virtual_memory()
|
memory = psutil.virtual_memory()
|
||||||
mem_used_percent = round(memory.percent, 1)
|
mem_used_percent = round(memory.percent, 1)
|
||||||
|
|
||||||
# Get CPU utilization
|
# Get CPU utilization (non-blocking to avoid stalling the event loop)
|
||||||
cpu_percent = round(psutil.cpu_percent(interval=0.1), 1)
|
cpu_percent = round(psutil.cpu_percent(interval=None), 1)
|
||||||
|
|
||||||
# Get CPU temperature
|
# Get CPU temperature
|
||||||
try:
|
try:
|
||||||
@@ -191,8 +196,8 @@ def start_display():
|
|||||||
logger.info("DisplayManager initialized successfully")
|
logger.info("DisplayManager initialized successfully")
|
||||||
except Exception as dm_error:
|
except Exception as dm_error:
|
||||||
logger.error(f"Failed to initialize DisplayManager: {dm_error}")
|
logger.error(f"Failed to initialize DisplayManager: {dm_error}")
|
||||||
# Create a fallback display manager for web simulation
|
# Re-attempt with minimal config to enable fallback simulation
|
||||||
display_manager = DisplayManager(config)
|
display_manager = DisplayManager({'display': {'hardware': {}}})
|
||||||
logger.info("Using fallback DisplayManager for web simulation")
|
logger.info("Using fallback DisplayManager for web simulation")
|
||||||
|
|
||||||
display_monitor.start()
|
display_monitor.start()
|
||||||
@@ -871,4 +876,5 @@ if __name__ == '__main__':
|
|||||||
display_monitor.start()
|
display_monitor.start()
|
||||||
|
|
||||||
# Run the app
|
# Run the app
|
||||||
socketio.run(app, host='0.0.0.0', port=5001, debug=False, allow_unsafe_werkzeug=True)
|
# eventlet/gevent provide a proper WSGI server; Werkzeug is fine for dev
|
||||||
|
socketio.run(app, host='0.0.0.0', port=5001, debug=False)
|
||||||
Reference in New Issue
Block a user