acquisitore: il frame bloccato e' quello mostrato (Refs #94)

Con «Blocca immagine» la pagina aspetta l'anteprima in volo (max 3 s) e sospende
ciclo e «Scatta» prima di POST /camera/blocca, cosi' la copia del servizio e' l'ultimo
frame visibile nella tab Calibrazione.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-24 17:31:38 +02:00
parent 9beaf2ed67
commit 71daab184f
2 changed files with 25 additions and 2 deletions
+13 -2
View File
@@ -459,7 +459,7 @@ function cambioSorgente(salva) {
// ---- Anteprima (#87) // ---- Anteprima (#87)
// #91: ris = {w, h} dell'ultimo frame; #92: calAttiva = la sorgente ha una calibrazione attiva (null = non nota), bloccoCal = un 409 ha fermato le richieste calibrata; urlCal/seqCal = riquadro calibrata, bloccata/sorgenteBloccata/urlBlocco/seqBlocco = #94 immagine bloccata per il calcolo (il riquadro della tab Calibrazione non segue piu' il ciclo), lato = ultimo lato_max // #91: ris = {w, h} dell'ultimo frame; #92: calAttiva = la sorgente ha una calibrazione attiva (null = non nota), bloccoCal = un 409 ha fermato le richieste calibrata; urlCal/seqCal = riquadro calibrata, bloccata/sorgenteBloccata/urlBlocco/seqBlocco = #94 immagine bloccata per il calcolo (il riquadro della tab Calibrazione non segue piu' il ciclo), lato = ultimo lato_max
var A = { attiva: false, gen: 0, inVolo: false, url: null, urlCal: null, seqCal: 0, bloccata: false, sorgenteBloccata: null, urlBlocco: null, seqBlocco: 0, lato: 1280, calAttiva: null, bloccoCal: false, nitMax: 0, ris: null }; var A = { attiva: false, gen: 0, inVolo: false, url: null, urlCal: null, seqCal: 0, bloccata: false, bloccando: 0, sorgenteBloccata: null, urlBlocco: null, seqBlocco: 0, lato: 1280, calAttiva: null, bloccoCal: false, nitMax: 0, ris: null };
function azzeraIndicatori() { function azzeraIndicatori() {
A.nitMax = 0; A.nitMax = 0;
@@ -612,6 +612,8 @@ async function unaAnteprima(lato) {
async function ciclo(g) { async function ciclo(g) {
while (A.inVolo) await pausa(50); while (A.inVolo) await pausa(50);
while (A.attiva && g === A.gen) { while (A.attiva && g === A.gen) {
while (A.bloccando) await pausa(20); // #94: durante «Blocca immagine» nessuna nuova anteprima
if (!A.attiva || g !== A.gen) return;
var t0 = Date.now(); var t0 = Date.now();
var esito = await unaAnteprima(1280); var esito = await unaAnteprima(1280);
if (g !== A.gen) return; if (g !== A.gen) return;
@@ -640,6 +642,7 @@ async function scatta() {
$("scatta").disabled = $("scattaCal").disabled = true; // #94: anche il «Scatta» della tab Calibrazione $("scatta").disabled = $("scattaCal").disabled = true; // #94: anche il «Scatta» della tab Calibrazione
dire("msgAnteprima", "Scatto in corso...", ""); dire("msgAnteprima", "Scatto in corso...", "");
while (A.inVolo) await pausa(50); while (A.inVolo) await pausa(50);
while (A.bloccando) await pausa(20); // #94: niente scatto mentre si blocca l'immagine
await unaAnteprima(4096); await unaAnteprima(4096);
$("scatta").disabled = false; $("scatta").disabled = false;
$("scattaCal").disabled = A.bloccata; // #94: con l'immagine bloccata resta spento $("scattaCal").disabled = A.bloccata; // #94: con l'immagine bloccata resta spento
@@ -673,11 +676,17 @@ $("bloccaImg").onchange = async function () {
} }
A.bloccata = true; // subito: il riquadro smette di seguire il ciclo A.bloccata = true; // subito: il riquadro smette di seguire il ciclo
A.sorgenteBloccata = sorgente; A.sorgenteBloccata = sorgente;
A.urlBlocco = A.url; A.urlBlocco = null; // #94: lo si fissa dopo l'attesa, sul frame davvero mostrato
var n = ++A.seqBlocco; var n = ++A.seqBlocco;
$("scattaCal").disabled = true; $("scattaCal").disabled = true;
dire("msgBlocco", "Blocco in corso...", ""); dire("msgBlocco", "Blocco in corso...", "");
A.bloccando++; // #94: il ciclo e «Scatta» non chiedono nuove anteprime finche' la copia non e' fatta
try { try {
for (var k = 0; A.inVolo && k < 150; k++) await pausa(20); // #94: attende l'anteprima in volo (max 3 s): il frame copiato e' l'ultimo mostrato
if (n !== A.seqBlocco) return; // spenta o cambiata sorgente durante l'attesa: niente copia
if (A.inVolo) { sbloccaLocale(); dire("msgBlocco", "Anteprima ancora in corso: riprovare.", "errore"); return; }
A.urlBlocco = A.url; // #94: il frame mostrato in questo momento e' quello che il servizio copia
mostraInTabCal(A.url);
var r = await fetch("/camera/blocca", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ source: sorgente }) }); var r = await fetch("/camera/blocca", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ source: sorgente }) });
if (n !== A.seqBlocco) { // spenta o cambiata sorgente nel frattempo: la copia appena fatta si toglie if (n !== A.seqBlocco) { // spenta o cambiata sorgente nel frattempo: la copia appena fatta si toglie
if (r.ok) fetch("/camera/blocca?source=" + encodeURIComponent(sorgente), { method: "DELETE" }).catch(function () { /* niente da fare */ }); if (r.ok) fetch("/camera/blocca?source=" + encodeURIComponent(sorgente), { method: "DELETE" }).catch(function () { /* niente da fare */ });
@@ -688,6 +697,8 @@ $("bloccaImg").onchange = async function () {
dire("msgBlocco", "Immagine bloccata " + b.larghezza + "×" + b.altezza + ": «Calcola» usa questa.", "ok"); dire("msgBlocco", "Immagine bloccata " + b.larghezza + "×" + b.altezza + ": «Calcola» usa questa.", "ok");
} catch (e) { } catch (e) {
if (n === A.seqBlocco) { sbloccaLocale(); dire("msgBlocco", "Servizio non raggiungibile.", "errore"); } if (n === A.seqBlocco) { sbloccaLocale(); dire("msgBlocco", "Servizio non raggiungibile.", "errore"); }
} finally {
A.bloccando--; // #94: anche su errore o uscita anticipata
} }
}; };
+12
View File
@@ -142,3 +142,15 @@ def test_pagina_blocco_rotte_e_cambio_sorgente():
cambio = PAGINA.split("function cambioSorgente")[1].split("// ---- Anteprima")[0] cambio = PAGINA.split("function cambioSorgente")[1].split("// ---- Anteprima")[0]
assert "rilasciaBlocco()" in cambio assert "rilasciaBlocco()" in cambio
assert cambio.index("rilasciaBlocco()") < cambio.index("svuotaRiquadri()") assert cambio.index("rilasciaBlocco()") < cambio.index("svuotaRiquadri()")
def test_pagina_blocco_attende_l_anteprima_in_volo():
# #94: il frame bloccato e' quello mostrato: flag A.bloccando, attesa di A.inVolo (20 ms, max 3 s), poi POST, flag tolto in finally
blocco = PAGINA.split('$("bloccaImg").onchange')[1].split('$("avvia").onclick')[0]
assert "A.bloccando++" in blocco and "A.bloccando--" in blocco and "finally" in blocco
assert "k < 150" in blocco and "pausa(20)" in blocco
assert blocco.index("A.inVolo") < blocco.index('fetch("/camera/blocca"')
ciclo = PAGINA.split("async function ciclo")[1].split("function avviaAnteprima")[0]
assert "while (A.bloccando)" in ciclo and ciclo.index("A.bloccando") < ciclo.index("unaAnteprima(1280)")
scatta = PAGINA.split("async function scatta")[1].split("// #94: «Blocca immagine»")[0]
assert "while (A.bloccando)" in scatta and scatta.index("A.bloccando") < scatta.index("unaAnteprima(4096)")