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
|
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:
|
def _apri_browser_quando_pronto(url: str, porte: tuple[int, ...]) -> None:
|
||||||
# Aspetta che i server ascoltino (max ~60 s), poi apre il browser predefinito
|
# Aspetta che i server ascoltino (max ~60 s), poi apre il browser predefinito
|
||||||
for _ in range(300):
|
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)
|
registra_strumenti() # prima del server: il worker di visione legge registry() gia' popolato (#82)
|
||||||
thread = avvia_server(args.host, dati)
|
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:
|
if not args.senza_browser:
|
||||||
# database senza utenti: si apre la pagina di setup del backend (SETUP_PASSWORD in config.env)
|
# 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")
|
url = (f"http://127.0.0.1:{PORTA_BACKEND}/api/setup" if _nessun_utente(dati / "tmf.db")
|
||||||
|
|||||||
Reference in New Issue
Block a user