Avvio: scalda le camere configurate con un grab a vuoto. Refs #108
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -348,6 +348,69 @@ def _nessun_utente(db: Path) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
def camere_da_scaldare(db: Path) -> list[tuple[str, str]]:
|
||||
"""Coppie (camera_url, camera_source) configurate nel db (stazioni e task di ricetta), senza doppioni.
|
||||
Solo sorgenti "camera:" che aprono hardware (non "camera:folder"). Errore sqlite -> lista vuota (#108)."""
|
||||
coppie: list[tuple[str, str]] = []
|
||||
|
||||
def aggiungi(url, src) -> None:
|
||||
# #108: valida la coppia e scarta duplicati e sorgenti che non aprono hardware
|
||||
if not (isinstance(url, str) and isinstance(src, str) and url.strip() and src.strip()):
|
||||
return
|
||||
if not src.startswith("camera:") or src == "camera:folder":
|
||||
return
|
||||
if (url, src) not in coppie:
|
||||
coppie.append((url, src))
|
||||
|
||||
try:
|
||||
con = sqlite3.connect(f"file:{db.as_posix()}?mode=ro", uri=True, timeout=2)
|
||||
try:
|
||||
for url, src in con.execute("SELECT camera_url, camera_source FROM stations"):
|
||||
aggiungi(url, src)
|
||||
for (blob,) in con.execute("SELECT acquisition_json FROM recipe_tasks WHERE acquisition_json IS NOT NULL"):
|
||||
try:
|
||||
cfg = json.loads(blob)
|
||||
except (TypeError, ValueError):
|
||||
continue # #108: JSON non valido, si salta
|
||||
if isinstance(cfg, dict):
|
||||
aggiungi(cfg.get("camera_url"), cfg.get("camera_source"))
|
||||
finally:
|
||||
con.close()
|
||||
except Exception:
|
||||
return []
|
||||
return coppie
|
||||
|
||||
|
||||
def scalda_camere(db: Path) -> None:
|
||||
"""Apre ogni camera configurata con un grab a vuoto, cosi' il primo "Acquisisci" non aspetta ~25 s (#108).
|
||||
Gira in un thread daemon: nessuna eccezione esce."""
|
||||
try:
|
||||
camere = camere_da_scaldare(db)
|
||||
if not camere:
|
||||
return
|
||||
for _ in range(300): # aspetta il servizio camera (max ~60 s), come _apri_browser_quando_pronto
|
||||
if _porta_aperta(PORTA_CAMERA):
|
||||
break
|
||||
time.sleep(0.2)
|
||||
else:
|
||||
log.warning("scalda camere: servizio camera non raggiungibile, salto")
|
||||
return
|
||||
for url, src in camere:
|
||||
t0 = time.monotonic()
|
||||
try:
|
||||
richiesta = urllib.request.Request(
|
||||
f"{url.rstrip('/')}/camera/grab",
|
||||
data=json.dumps({"source": src, "trigger": "continuo", "timeout_ms": 5000}).encode("utf-8"),
|
||||
headers={"Content-Type": "application/json"}, method="POST")
|
||||
with urllib.request.urlopen(richiesta, timeout=90) as r:
|
||||
esito = f"HTTP {r.status}"
|
||||
log.info("scalda camera %s: %s in %.1f s", src, esito, time.monotonic() - t0)
|
||||
except Exception as e: # #108: una camera che non risponde non ferma le altre
|
||||
log.warning("scalda camera %s: %s dopo %.1f s", src, e, time.monotonic() - t0)
|
||||
except Exception:
|
||||
log.exception("scalda camere non riuscito")
|
||||
|
||||
|
||||
def _apri_browser_quando_pronto(url: str, porte: tuple[int, ...]) -> None:
|
||||
# Aspetta che i server ascoltino (max ~60 s), poi apre il browser predefinito
|
||||
for _ in range(300):
|
||||
@@ -509,6 +572,8 @@ def main(argv=None) -> int:
|
||||
|
||||
registra_strumenti() # prima del server: il worker di visione legge registry() gia' popolato (#82)
|
||||
thread = avvia_server(args.host, dati)
|
||||
# #108: apre le camere configurate con un grab a vuoto, con o senza browser
|
||||
threading.Thread(target=scalda_camere, args=(dati / "tmf.db",), daemon=True).start()
|
||||
if not args.senza_browser:
|
||||
# database senza utenti: si apre la pagina di setup del backend (SETUP_PASSWORD in config.env)
|
||||
url = (f"http://127.0.0.1:{PORTA_BACKEND}/api/setup" if _nessun_utente(dati / "tmf.db")
|
||||
|
||||
Reference in New Issue
Block a user