Projektarbeit-MYP/backend/start_kiosk_optimized.py

327 lines
11 KiB
Python
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
OPTIMIERTES KIOSK-STARTUP SCRIPT
==============================
Startet die Flask-App mit optimierter Konfiguration für Kiosk-Betrieb.
Behebt die Probleme mit "unreachable" und langen Ladezeiten.
Verwendung:
python start_kiosk_optimized.py [--port PORT] [--debug]
Autor: AutoFix-System
Datum: $(date)
"""
import os
import sys
import time
import signal
import socket
import subprocess
import multiprocessing
from pathlib import Path
# ===== KONFIGURATION =====
DEFAULT_PORT = 5000
DEFAULT_HOST = "127.0.0.1" # IPv4 only für bessere Kompatibilität
WORKERS = min(4, multiprocessing.cpu_count()) # Optimal für Kiosk
TIMEOUT = 120 # 2 Minuten Timeout
KEEPALIVE = 2 # 2 Sekunden Keep-Alive
MAX_REQUESTS = 1000 # Worker-Recycling
class KioskOptimizer:
"""Optimiert die App für Kiosk-Betrieb"""
def __init__(self):
self.base_dir = Path(__file__).parent
self.app_file = self.base_dir / "app.py"
self.log_file = self.base_dir / "logs" / "kiosk.log"
self.pid_file = self.base_dir / "kiosk.pid"
# Stelle sicher, dass Log-Verzeichnis existiert
self.log_file.parent.mkdir(exist_ok=True)
def check_dependencies(self):
"""Prüft ob alle Abhängigkeiten verfügbar sind"""
try:
import gunicorn
print("✅ Gunicorn verfügbar")
except ImportError:
print("❌ Gunicorn nicht installiert!")
print("Installiere mit: pip install gunicorn")
sys.exit(1)
if not self.app_file.exists():
print(f"❌ App-Datei nicht gefunden: {self.app_file}")
sys.exit(1)
print("✅ Alle Abhängigkeiten verfügbar")
def fix_ipv6_issues(self):
"""Behebt IPv6-Auflösungsprobleme"""
print("🔧 Behebe IPv6-Auflösungsprobleme...")
# Windows: Prüfe hosts-Datei
hosts_file = Path("C:/Windows/System32/drivers/etc/hosts")
try:
if hosts_file.exists():
content = hosts_file.read_text(encoding='utf-8')
# Prüfe auf IPv6 localhost-Eintrag
if "::1" in content and "localhost" in content:
print("⚠️ IPv6 localhost-Eintrag in hosts-Datei gefunden")
print("💡 Empfehlung: Verwende 127.0.0.1 statt localhost im Kiosk")
except Exception as e:
print(f"⚠️ Hosts-Datei-Check fehlgeschlagen: {e}")
print("✅ IPv6-Issues-Check abgeschlossen")
def kill_hanging_processes(self):
"""Beendet hängende Flask/Python-Prozesse"""
print("🔄 Beende hängende Prozesse...")
# Finde Python-Prozesse die auf Port 5000 hören
try:
result = subprocess.run(
["netstat", "-ano"],
capture_output=True,
text=True,
shell=True
)
lines = result.stdout.split('\n')
hanging_pids = set()
for line in lines:
if ":5000" in line and ("WARTEND" in line or "ESTABLISHED" in line):
parts = line.split()
if len(parts) >= 5:
pid = parts[-1]
if pid.isdigit():
hanging_pids.add(int(pid))
# Töte hängende Prozesse
for pid in hanging_pids:
try:
if pid != 0: # 0 ist System-PID
subprocess.run(["taskkill", "/F", "/PID", str(pid)],
capture_output=True, shell=True)
print(f"💀 Prozess {pid} beendet")
except:
pass
if hanging_pids:
print(f"{len(hanging_pids)} hängende Prozesse beendet")
time.sleep(2) # Kurz warten
else:
print("✅ Keine hängenden Prozesse gefunden")
except Exception as e:
print(f"⚠️ Prozess-Cleanup fehlgeschlagen: {e}")
def check_port_availability(self, host, port):
"""Prüft ob Port verfügbar ist"""
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
sock.settimeout(1)
result = sock.connect_ex((host, port))
return result != 0 # Port ist frei wenn connect fehlschlägt
finally:
sock.close()
def start_with_gunicorn(self, host=DEFAULT_HOST, port=DEFAULT_PORT, debug=False):
"""Startet die App mit Gunicorn"""
print(f"🚀 Starte Kiosk-App mit Gunicorn auf {host}:{port}")
# Gunicorn-Konfiguration für optimale Kiosk-Performance
gunicorn_config = [
"gunicorn",
"--bind", f"{host}:{port}",
"--workers", str(WORKERS),
"--worker-class", "sync", # Sync für einfache Requests, funktioniert am stabilsten
"--timeout", str(TIMEOUT),
"--keepalive", str(KEEPALIVE),
"--max-requests", str(MAX_REQUESTS),
"--max-requests-jitter", "50",
"--preload", # App einmal laden, dann forken
"--worker-tmp-dir", "/dev/shm" if os.name != 'nt' else ".",
"--log-level", "info" if not debug else "debug",
"--access-logfile", str(self.log_file),
"--error-logfile", str(self.log_file),
"--capture-output",
"--enable-stdio-inheritance"
]
# Windows-spezifische Optimierungen
if os.name == 'nt':
gunicorn_config.extend([
"--worker-connections", "100", # Weniger Connections auf Windows
"--threads", "2" # Threading für Windows
])
# Debug-Modus Anpassungen
if debug:
gunicorn_config.extend([
"--reload",
"--log-level", "debug"
])
# App-Modul
gunicorn_config.append("app:app")
print(f"🔧 Gunicorn-Kommando: {' '.join(gunicorn_config)}")
# Umgebungsvariablen für optimierte App setzen
env = os.environ.copy()
env.update({
"FLASK_ENV": "production" if not debug else "development",
"USE_OPTIMIZED_CONFIG": "true",
"FORCE_OPTIMIZED_MODE": "true",
"PYTHONUNBUFFERED": "1",
"PYTHONIOENCODING": "utf-8"
})
# Starte Gunicorn
try:
# PID speichern
process = subprocess.Popen(
gunicorn_config,
cwd=str(self.base_dir),
env=env,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
universal_newlines=True
)
# PID in Datei schreiben
with open(self.pid_file, 'w') as f:
f.write(str(process.pid))
print(f"✅ Gunicorn gestartet (PID: {process.pid})")
print(f"📋 Logs: {self.log_file}")
print(f"🌐 URL: http://{host}:{port}")
print(f"🔄 Für Kiosk verwende: http://127.0.0.1:{port}")
# Warte kurz und teste Verbindung
time.sleep(3)
if self.test_connection(host, port):
print("🎉 APP ERFOLGREICH GESTARTET!")
print(f"💡 Kiosk-Browser auf http://127.0.0.1:{port} zeigen lassen")
return process
else:
print("❌ App startet nicht korrekt!")
process.terminate()
return None
except Exception as e:
print(f"❌ Fehler beim Starten: {e}")
return None
def test_connection(self, host, port, retries=5):
"""Testet die Verbindung zur App"""
print(f"🔍 Teste Verbindung zu {host}:{port}...")
for i in range(retries):
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(2)
result = sock.connect_ex((host, port))
sock.close()
if result == 0:
print(f"✅ Verbindung erfolgreich (Versuch {i+1})")
return True
else:
print(f"⏳ Versuch {i+1} fehlgeschlagen, warte...")
time.sleep(1)
except Exception as e:
print(f"⚠️ Verbindungstest Versuch {i+1} fehlgeschlagen: {e}")
time.sleep(1)
print("❌ Verbindung fehlgeschlagen!")
return False
def stop(self):
"""Stoppt die App"""
if self.pid_file.exists():
try:
with open(self.pid_file, 'r') as f:
pid = int(f.read().strip())
print(f"🛑 Stoppe App (PID: {pid})")
# Windows: taskkill verwenden
if os.name == 'nt':
subprocess.run(["taskkill", "/F", "/PID", str(pid)],
capture_output=True, shell=True)
else:
os.kill(pid, signal.SIGTERM)
self.pid_file.unlink()
print("✅ App gestoppt")
except Exception as e:
print(f"⚠️ Fehler beim Stoppen: {e}")
else:
print(" Keine PID-Datei gefunden")
def main():
"""Hauptfunktion"""
import argparse
parser = argparse.ArgumentParser(description="Optimiertes Kiosk-Startup für Flask-App")
parser.add_argument("--port", type=int, default=DEFAULT_PORT, help="Port (Standard: 5000)")
parser.add_argument("--host", default=DEFAULT_HOST, help="Host (Standard: 127.0.0.1)")
parser.add_argument("--debug", action="store_true", help="Debug-Modus")
parser.add_argument("--stop", action="store_true", help="App stoppen")
parser.add_argument("--force-kill", action="store_true", help="Alle hängenden Prozesse beenden")
args = parser.parse_args()
optimizer = KioskOptimizer()
print("🚀 KIOSK-OPTIMIZER GESTARTET")
print("=" * 50)
if args.stop:
optimizer.stop()
return
if args.force_kill:
optimizer.kill_hanging_processes()
return
# Systemchecks
optimizer.check_dependencies()
optimizer.fix_ipv6_issues()
# Port-Check
if not optimizer.check_port_availability(args.host, args.port):
print(f"❌ Port {args.port} ist bereits belegt!")
print("💡 Versuche --force-kill um hängende Prozesse zu beenden")
sys.exit(1)
# Cleanup alter Prozesse
optimizer.kill_hanging_processes()
# App starten
process = optimizer.start_with_gunicorn(args.host, args.port, args.debug)
if process:
try:
# Warte auf CTRL+C
print("\n⌨️ Drücke CTRL+C zum Beenden...")
process.wait()
except KeyboardInterrupt:
print("\n🛑 CTRL+C erkannt, stoppe App...")
optimizer.stop()
else:
print("❌ App-Start fehlgeschlagen!")
sys.exit(1)
if __name__ == "__main__":
main()