- Frontend v4 accessible sur réseau local (192.168.1.40) - Ports ouverts: 3002 (frontend), 5001 (backend), 5004 (dashboard) - Ollama GPU fonctionnel - Self-healing interactif - Dashboard confiance Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
271 lines
9.2 KiB
Python
271 lines
9.2 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
Test de Capture d'Écran Corrigé - RPA Vision V3
|
|
Auteur : Dom, Alice, Kiro - 8 janvier 2026
|
|
|
|
Version corrigée pour les problèmes de threading avec MSS.
|
|
"""
|
|
|
|
import cv2
|
|
import numpy as np
|
|
import mss
|
|
import base64
|
|
import io
|
|
from PIL import Image
|
|
import threading
|
|
import time
|
|
import logging
|
|
|
|
class FixedRealScreenCaptureService:
|
|
"""
|
|
Service de capture d'écran avec correction des problèmes de threading
|
|
"""
|
|
|
|
def __init__(self):
|
|
self.is_capturing = False
|
|
self.capture_thread = None
|
|
self.current_screenshot = None
|
|
self.detected_elements = []
|
|
|
|
self.capture_interval = 1.0
|
|
self.monitors = []
|
|
self.selected_monitor = 0
|
|
|
|
# Initialiser MSS dans le thread principal
|
|
self._init_mss()
|
|
|
|
def _init_mss(self):
|
|
"""Initialise MSS dans le thread principal"""
|
|
try:
|
|
with mss.mss() as sct:
|
|
self.monitors = sct.monitors
|
|
print(f"Détecté {len(self.monitors)} moniteurs")
|
|
for i, monitor in enumerate(self.monitors):
|
|
print(f"Moniteur {i}: {monitor}")
|
|
except Exception as e:
|
|
print(f"Erreur lors de l'initialisation MSS: {e}")
|
|
self.monitors = [{"top": 0, "left": 0, "width": 1920, "height": 1080}]
|
|
|
|
def get_monitors(self):
|
|
"""Retourne la liste des moniteurs disponibles"""
|
|
return [
|
|
{
|
|
"id": i,
|
|
"width": monitor.get("width", 0),
|
|
"height": monitor.get("height", 0),
|
|
"top": monitor.get("top", 0),
|
|
"left": monitor.get("left", 0)
|
|
}
|
|
for i, monitor in enumerate(self.monitors)
|
|
]
|
|
|
|
def select_monitor(self, monitor_id: int) -> bool:
|
|
"""Sélectionne le moniteur à capturer"""
|
|
if 0 <= monitor_id < len(self.monitors):
|
|
self.selected_monitor = monitor_id
|
|
print(f"Moniteur sélectionné: {monitor_id}")
|
|
return True
|
|
return False
|
|
|
|
def start_capture(self, interval: float = 1.0) -> bool:
|
|
"""Démarre la capture d'écran en temps réel"""
|
|
if self.is_capturing:
|
|
print("Capture déjà en cours")
|
|
return False
|
|
|
|
self.capture_interval = interval
|
|
self.is_capturing = True
|
|
|
|
# Démarrer le thread de capture
|
|
self.capture_thread = threading.Thread(target=self._capture_loop, daemon=True)
|
|
self.capture_thread.start()
|
|
|
|
print(f"Capture démarrée (intervalle: {interval}s)")
|
|
return True
|
|
|
|
def stop_capture(self) -> bool:
|
|
"""Arrête la capture d'écran"""
|
|
if not self.is_capturing:
|
|
return False
|
|
|
|
self.is_capturing = False
|
|
|
|
if self.capture_thread and self.capture_thread.is_alive():
|
|
self.capture_thread.join(timeout=2.0)
|
|
|
|
print("Capture arrêtée")
|
|
return True
|
|
|
|
def _capture_loop(self):
|
|
"""Boucle principale de capture avec MSS local au thread"""
|
|
# Créer une instance MSS locale au thread
|
|
try:
|
|
with mss.mss() as sct:
|
|
while self.is_capturing:
|
|
try:
|
|
# Capturer l'écran
|
|
screenshot = self._capture_screen_with_sct(sct)
|
|
if screenshot is not None:
|
|
self.current_screenshot = screenshot
|
|
print(f"Screenshot capturé: {screenshot.shape}")
|
|
|
|
# Attendre avant la prochaine capture
|
|
time.sleep(self.capture_interval)
|
|
|
|
except Exception as e:
|
|
print(f"Erreur dans la boucle de capture: {e}")
|
|
time.sleep(1.0)
|
|
except Exception as e:
|
|
print(f"Erreur lors de l'initialisation MSS dans le thread: {e}")
|
|
|
|
def _capture_screen_with_sct(self, sct):
|
|
"""Capture l'écran avec une instance MSS donnée"""
|
|
try:
|
|
if self.selected_monitor >= len(self.monitors):
|
|
self.selected_monitor = 0
|
|
|
|
monitor = self.monitors[self.selected_monitor]
|
|
|
|
# Capturer avec MSS
|
|
screenshot = sct.grab(monitor)
|
|
|
|
# Convertir en array numpy
|
|
img_array = np.array(screenshot)
|
|
|
|
# Convertir BGRA vers BGR (OpenCV)
|
|
if img_array.shape[2] == 4:
|
|
img_array = cv2.cvtColor(img_array, cv2.COLOR_BGRA2BGR)
|
|
|
|
return img_array
|
|
|
|
except Exception as e:
|
|
print(f"Erreur lors de la capture d'écran: {e}")
|
|
return None
|
|
|
|
def capture_single_screenshot(self):
|
|
"""Capture une seule image (pour test synchrone)"""
|
|
try:
|
|
with mss.mss() as sct:
|
|
return self._capture_screen_with_sct(sct)
|
|
except Exception as e:
|
|
print(f"Erreur lors de la capture unique: {e}")
|
|
return None
|
|
|
|
def get_current_screenshot_base64(self):
|
|
"""Retourne la capture d'écran actuelle en base64"""
|
|
if self.current_screenshot is None:
|
|
return None
|
|
|
|
try:
|
|
# Convertir en PIL Image
|
|
if len(self.current_screenshot.shape) == 3:
|
|
# BGR vers RGB
|
|
rgb_image = cv2.cvtColor(self.current_screenshot, cv2.COLOR_BGR2RGB)
|
|
pil_image = Image.fromarray(rgb_image)
|
|
else:
|
|
pil_image = Image.fromarray(self.current_screenshot)
|
|
|
|
# Redimensionner pour l'affichage web
|
|
max_width = 1200
|
|
if pil_image.width > max_width:
|
|
ratio = max_width / pil_image.width
|
|
new_height = int(pil_image.height * ratio)
|
|
pil_image = pil_image.resize((max_width, new_height), Image.Resampling.LANCZOS)
|
|
|
|
# Convertir en base64
|
|
buffer = io.BytesIO()
|
|
pil_image.save(buffer, format='JPEG', quality=85)
|
|
img_base64 = base64.b64encode(buffer.getvalue()).decode('utf-8')
|
|
|
|
return f"data:image/jpeg;base64,{img_base64}"
|
|
|
|
except Exception as e:
|
|
print(f"Erreur lors de la conversion base64: {e}")
|
|
return None
|
|
|
|
def get_status(self):
|
|
"""Retourne le statut du service"""
|
|
return {
|
|
"is_capturing": self.is_capturing,
|
|
"selected_monitor": self.selected_monitor,
|
|
"monitors_count": len(self.monitors),
|
|
"capture_interval": self.capture_interval,
|
|
"elements_detected": len(self.detected_elements),
|
|
"has_screenshot": self.current_screenshot is not None
|
|
}
|
|
|
|
def cleanup(self):
|
|
"""Nettoie les ressources"""
|
|
self.stop_capture()
|
|
|
|
def test_service_fixe():
|
|
"""Test du service corrigé"""
|
|
print("=== Test du Service de Capture Corrigé ===")
|
|
|
|
service = FixedRealScreenCaptureService()
|
|
|
|
try:
|
|
# 1. Test des moniteurs
|
|
monitors = service.get_monitors()
|
|
print(f"✅ Moniteurs détectés: {len(monitors)}")
|
|
|
|
# 2. Test de capture unique (synchrone)
|
|
print("Test de capture unique...")
|
|
screenshot = service.capture_single_screenshot()
|
|
if screenshot is not None:
|
|
print(f"✅ Capture unique réussie: {screenshot.shape}")
|
|
|
|
# Convertir en base64 pour test
|
|
service.current_screenshot = screenshot
|
|
base64_img = service.get_current_screenshot_base64()
|
|
if base64_img:
|
|
print(f"✅ Conversion base64 réussie: {len(base64_img)} caractères")
|
|
else:
|
|
print("❌ Échec conversion base64")
|
|
else:
|
|
print("❌ Échec capture unique")
|
|
|
|
# 3. Test de capture en continu
|
|
print("\nTest de capture en continu...")
|
|
success = service.start_capture(interval=1.0)
|
|
print(f"✅ Démarrage capture: {success}")
|
|
|
|
# Attendre quelques captures
|
|
print("Attente de 5 secondes...")
|
|
time.sleep(5.0)
|
|
|
|
# Vérifier le statut
|
|
status = service.get_status()
|
|
print(f"✅ Statut: {status}")
|
|
|
|
# Obtenir une capture
|
|
screenshot_b64 = service.get_current_screenshot_base64()
|
|
if screenshot_b64:
|
|
print(f"✅ Screenshot en continu: {len(screenshot_b64)} caractères")
|
|
else:
|
|
print("❌ Aucun screenshot en continu")
|
|
|
|
# Arrêter la capture
|
|
success = service.stop_capture()
|
|
print(f"✅ Arrêt capture: {success}")
|
|
|
|
return screenshot is not None
|
|
|
|
except Exception as e:
|
|
print(f"❌ Erreur lors du test: {e}")
|
|
import traceback
|
|
traceback.print_exc()
|
|
return False
|
|
|
|
finally:
|
|
service.cleanup()
|
|
|
|
if __name__ == "__main__":
|
|
print("🔍 Test du Service de Capture d'Écran Corrigé")
|
|
print("=" * 50)
|
|
|
|
if test_service_fixe():
|
|
print("\n🎉 Service de capture entièrement fonctionnel !")
|
|
else:
|
|
print("\n❌ Problème persistant avec le service de capture") |