Visione: Registra con quote non misurate salva NaN (esito fail) invece di 422 #118

Closed
opened 2026-10-28 16:12:39 +00:00 by Adriano · 5 comments
Owner

PC fiera 28/10 17:05, ricetta TEST1 task 14: su un pezzo con bava nel raccordo alto-destra DetectFillet arco_tr non produce uscita; /api/vision/execute risponde 422 'the graph did not produce R_tr.value, d_tl_tr.value, d_tr_br.value, d_tr_bl.value' (src/backend/services/vision_service.py execute_task, regola tutte-o-nessuna) e l'operatore non registra il pezzo. Decisione Adriano 28/10: la quota non misurata si registra con valore NaN, esito fail.

PC fiera 28/10 17:05, ricetta TEST1 task 14: su un pezzo con bava nel raccordo alto-destra DetectFillet arco_tr non produce uscita; /api/vision/execute risponde 422 'the graph did not produce R_tr.value, d_tl_tr.value, d_tr_br.value, d_tr_bl.value' (src/backend/services/vision_service.py execute_task, regola tutte-o-nessuna) e l'operatore non registra il pezzo. Decisione Adriano 28/10: la quota non misurata si registra con valore NaN, esito fail.
Adriano added the bug label 2026-10-28 16:12:39 +00:00
Author
Owner

ANALISI (Opus, passo 1) per #118 #119 #120 #121, da eseguire insieme: quattro agenti Sonnet in parallelo su file disgiunti. NON ANCORA AVVIATA (Adriano ha chiuso la sessione il 28/10 sera).

CONTRATTO (identico per i quattro agenti; gitea:Adriano/TieMeasureFlow#118 #119 #120 #121)
Repo TieMeasureFlow, base 4485803 (ramo lavoro/daitarn-tmf-dxfcal, installato sul PC fiera). Frontend in src/frontend/flask_app/, backend in src/backend/.
Agenti, worktree C:/Users/Adriano/lavoro/TieMeasureFlow-<nome>, ramo lavoro/daitarn-tmf-<nome>, file disgiunti:

  • BE nan: solo src/backend/** (codice, migrazione, test). #118 lato server.
  • F1 svg: solo file suoi: static/js/vision_overlay.js, nuovo static/js/save_image.js, i loro test (tests/js/test_vision_overlay.js, nuovo tests/js/test_save_image.js, wrapper pytest tests/test_vision_overlay_js.py, nuovo tests/test_save_image_js.py). #121 modulo, #119 helper, #118 etichette.
  • F2 operatore: templates/measure/task_execute.html, static/js/vision_execute.js e i loro test (tests/js/test_vision_rows.js, tests/js/test_vision_zoom.js, tests/test_vision_panel_fit.py, nuovi test JS/pytest a nome test_vision_*). #119 #120 #121 #118 nel pannello operatore.
  • F3 wizard: templates/maker/task_editor.html, static/js/dxf_recipe_wizard.js, templates/measure/task_complete.html, static/js/csv-export.js e i loro test (tests/js/test_dxf_zoom.js, tests/js/test_csv_export.js, nuovi test a nome test_wizard_*/test_csv_*/test_task_complete_*). #119 #121 nel wizard, #118 nel riepilogo e nel CSV.
    Nessuno tocca i file degli altri. Non si pusha: commit locali sul proprio ramo, chi coordina unisce.

API visione (BE la implementa, F2 la usa) — #118, decisione di Adriano 28/10: «la quota non misurata deve mettere valore NaN»:

  • POST /api/vision/check e POST /api/vision/execute: measurements ha UNA voce per ogni quota del task con vision_output, in ordine di marker_number. Prodotta: {subtask_id, value: <numero>, pass_fail}. Non prodotta (assente, null, NaN, inf, bool): {subtask_id, value: null, pass_fail: "fail"}.
  • execute non dà più 422 per uscite mancanti: salva la misura con value NULL, pass_fail fail, deviazione NULL, e il risultato visione come gli altri. Restano gli altri 422/409 (task senza grafo, nessuna quota con vision_output, versione non corrente, ...).
  • Le liste di misure del backend possono avere value: null (e deviation: null). In JSON è null, mai NaN.
  • Il frontend mostra un valore null come testo NaN, esito fail.

Modulo static/js/vision_overlay.js (F1 lo estende, F2 e F3 lo usano), oltre a draw, anchor, labels che restano:

  • NUOVO VisionOverlay.svg(overlay, opts) → stringa con i soli figli SVG (senza <svg> esterno), coordinate in px immagine. opts = {zoom = 1, lw = 2, cross = 6}. Ogni forma: fill="none", vector-effect="non-scaling-stroke", stroke col colore del ruolo, stroke-width = lw (px schermo, costante a ogni zoom); ruolo roi: stroke-dasharray="6 4" e stroke-width = lw*0.75. points = croci con semi-braccio cross / zoom px immagine (costante a video). Arc come path SVG (porting di percorsoArco dello Studio). Numeri con al più 2 decimali; nel markup solo numeri e colori fissi, mai stringhe dei dati. Elementi ignoti o malformati saltati.
  • labels(rows, overlay): riga con value null, undefined o non finito → nessuna etichetta (attenzione: Number(null) è 0).
  • Uso nella pagina: <svg x-ref="..." class="absolute inset-0 w-full h-full pointer-events-none" preserveAspectRatio="none"></svg> al posto del canvas, dentro il wrapper dello zoom; il JS della pagina imposta setAttribute('viewBox', '0 0 ' + w + ' ' + h) (dimensioni naturali) e innerHTML = VisionOverlay.svg(overlay, {zoom}) a ogni risultato, load dell'immagine e cambio di zoom.

Helper static/js/save_image.js (F1 lo scrive, F2 e F3 lo usano) — #119, decisione di Adriano: frame grezzo PNG, senza disegni:

  • Script classico, globale tmfSaveImage(src, baseName) (window.tmfSaveImage e module.exports). src = URL dell'immagine mostrata (blob: o http). Scarica il blob con fetch(src); se non è image/png lo ridisegna su canvas e lo converte in PNG; nome proposto <baseName>_<AAAAMMGG-hhmmss>.png (baseName ripulito da caratteri non validi per un nome file). Con window.showSaveFilePicker apre «Salva con nome» (tipo PNG); senza, ripiega su <a download>. Annullamento dell'utente (AbortError) → nessun errore. Ritorna una Promise che si risolve a true/false.
  • Funzione pura esportata per i test: tmfSaveImageName(baseName, date).
  • Nella pagina: <script src="{{ url_for('static', filename='js/save_image.js') }}"></script>. Pulsante nella barra zoom, dopo «Adatta» e prima della percentuale: <button type="button" class="px-1.5 text-xs rounded hover:bg-white/20" @click="..." title="{{ _('Salva immagine') }}">{{ _('Salva') }}</button>.

Regole comuni:

  • Ogni modifica porta un commento breve in linea con il riferimento all'issue, stile del file (italiano, es. // #118: ..., {# #119: ... #}, <!-- #121: ... --> come nel file).
  • Alpine 3.15.12: un :style stringa fa setAttribute('style') e cancella lo style statico → niente style= statico su elementi con :style; mai x-show e :style stringa sullo stesso elemento.
  • Test: export PATH="/c/Program Files/nodejs:$PATH"; JS con node <file> (veloce); pytest dal worktree con uv run --all-extras pytest -q <file> (il primo avvio crea .venv in pochi secondi; ~90 s per 10 test: lanciare solo i file toccati, non la suite intera).
  • Commit con messaggio in italiano, una riga + Refs #N, e in fondo Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>. Mai Fixes.
  • Battito: prima di ogni passo e almeno una volta al minuto echo "$(date +%H:%M:%S) sto facendo X" >> <file battito>.
  • Budget ~60 chiamate. Un problema che resiste a tre tentativi: fermati e riporta. Un parziale dichiarato è un esito accettabile. Commit presto, non alla fine.
  • Rapporto finale nel formato: ESITO / RIF / COSA / FILE / PROVA / MANCA, più l'ID del modello del tuo prompt di sistema.

COMPITI PER AGENTE (analisi Opus, passo 1; il contratto sopra vale per tutti)

BE nan — #118 lato server

  • src/backend/migrations/versions/022_measurement_value_nullable.py (stile di 021_task_acquisition.py): measurements.value nullable con op.batch_alter_table (SQLite). Se anche la colonna della deviazione è NOT NULL, stessa cosa. Downgrade: torna NOT NULL (fallisce se ci sono righe NULL: scriverlo nel docstring). Le migrazioni girano all'avvio (packaging/windows/avvio.py → alembic upgrade head).
  • src/backend/models/orm/measurement.py:29: Mapped[Optional[float]], nullable=True.
  • src/backend/models/api/measurement.py: i modelli di risposta (:46, :111 e simili) value: float | None (e deviazione); quello di creazione manuale (:19) resta float obbligatorio.
  • src/backend/models/api/vision.py:10: value: float | None.
  • src/backend/services/measurement_service.py save_measurement (:353): accetta value=None (solo il percorso visione la usa): pass_fail fail, deviazione None, niente _validate_measured_value; :305 float(m.value) sicuro con None.
  • src/backend/services/vision_service.py execute_task (~:690-748): niente 422 per le uscite mancanti; per ogni quota value = float(outputs[q.vision_output]) if _is_measurable(...) else None. check_task (:752): le quote non prodotte entrano in measurements con value: None, pass_fail: "fail" (aggiorna il docstring, che dice il contrario).
  • Calcoli numerici, i None si saltano (contano solo nei conteggi di esito): api/routers/statistics.py:149,174,204, services/report_service.py:321, services/monitor_service.py:99.
  • Testi ed esportazioni, None → NaN: services/report_service.py:429, services/production_export_service.py:162, api/routers/measurements.py:360 (CSV).
  • Cerca in src/backend (esclusi i test) altri usi di .value di una misura (float(m.value), Measurement.value, m.value -) e rendili sicuri. Gli aggregati SQL (avg/min/max) ignorano già NULL.
  • Invariato: il blocco di tolleranza (pending_authorisation): una quota NaN è un fail come gli altri.
  • TEST in src/backend/tests/ (stile dei test di visione esistenti, worker finto): execute con un'uscita mancante → 200, misura salvata con value None e fail, risposta value: null; check con uscita mancante → voce null/fail; endpoint statistiche con una misura None non va in errore e la esclude dalle medie; CSV con NaN; la migrazione 022 su SQLite (se c'è un test delle migrazioni, estendilo). Lancia anche i test esistenti di visione, misure e statistiche.

F1 svg — #121 modulo, #119 helper, #118 etichette

  • static/js/vision_overlay.js: aggiungi svg(overlay, opts) come da contratto (porting di percorsoArco/formeSvg dello Studio in C:/Users/Adriano/lavoro/TieVision-Assistant/vendor/TieVision-Studio/frontend/src/comune/overlay.js); labels salta i valori null/non finiti. draw/anchor restano.
  • Nuovo static/js/save_image.js come da contratto (riusa l'idea di static/js/image_to_png.js, senza toccarlo).
  • TEST: estendi tests/js/test_vision_overlay.js (fixture tests/js/fixtures/vision_check_task14.json: svg dà 25 forme, ognuna con vector-effect="non-scaling-stroke"; roi con stroke-dasharray; croce dei points con semi-braccio 6 a zoom 1 e 3 a zoom 2; path dell'arco con estremi calcolati; malformati saltati; nessuna stringa dei dati nel markup; labels con value: null → nessuna etichetta). Nuovo tests/js/test_save_image.js (nome proposto, pulizia del baseName, ramo showSaveFilePicker e ramo <a download> con finti, AbortError → false senza eccezioni) + wrapper tests/test_save_image_js.py.

F2 operatore — pannello visione operatore

  • #119 barra in alto (templates/measure/task_execute.html ~540-575): con camera solo «Acquisisci da camera» (grab(), testo {{ _('Acquisisci da camera') }}; in modo in_linea resta Pausa/Riprendi al suo posto) e «Registra»; l'input file «Acquisisci immagine/Riprova» solo senza camera ({% if not camera_enabled %}). I badge di stato restano. Barra zoom: pulsante Salva → tmfSaveImage(shownImage, <nome del task, da __visionCfg o dal titolo>). Include save_image.js.
  • #120 conteggio e Completato:
    • static/js/vision_execute.js record(): a salvataggio riuscito window.dispatchEvent(new CustomEvent('vision-recorded', { detail: { measurements: this.result.measurements } })); su task-result-reset (evento già emesso da resetForNewCycle) torna saved = false.
    • componente principale di task_execute.html: stato visionRecorded: false; ascolta vision-recorded (@vision-recorded.window sulla radice o addEventListener in init) → measurements = le misure ricevute ({subtask_id, value, pass_fail, deviation: value == null ? null : value - nominal}) e visionRecorded = true; in resetForNewCycle visionRecorded = false.
    • canLeaveTask (:1376): per isVisionTask serve visionRecorded (vale anche per dxf_compare, dove oggi «Completato» è attivo subito).
    • missingQuotesLabel/blockedReason: per isVisionTask non registrato → {{ _('Registra il pezzo')|tojson }}, non «Mancano N quote».
    • ripristino al caricamento (:1454-1471): deviation null se value null; per isVisionTask visionRecorded = true se ha ripristinato misure.
  • #118: righe con value null → cella NaN (non Number(null).toFixed), esito fail; tolleranze come ora.
  • #121: <canvas x-ref="overlay"> → <svg x-ref="overlay" ...> del contratto, nel wrapper dello zoom; drawOverlay imposta viewBox e innerHTML = VisionOverlay.svg(overlay, { zoom: this.z.zoom }) (vuoto se VisionOverlay manca); ridisegna a ogni cambio di z.zoom ($watch in init o chiamate in zoomBy/zFit). Le etichette HTML restano.
  • TEST: aggiorna tests/js/test_vision_rows.js, tests/js/test_vision_zoom.js, tests/test_vision_panel_fit.py; nuovi controlli (JS con finti di VisionOverlay/tmfSaveImage/window.dispatchEvent, oppure pytest sul testo del template): evento vision-recorded emesso dopo un record riuscito e non dopo uno fallito; barra con camera senza input file; canLeaveTask dipende da visionRecorded; svg al posto del canvas; NaN nella cella. tests/test_template_js_syntax.py verde.

F3 wizard — wizard DXF, riepilogo, CSV

  • #119 templates/maker/task_editor.html: Salva nella barra dell'immagine (:735-741, tmfSaveImage(imageSrc, 'immagine')) e in quella della Prova (:816-823, tmfSaveImage(<src dell'immagine della Prova>, 'prova')); non nella barra del DXF (è un disegno). Include save_image.js e vision_overlay.js prima di dxf_recipe_wizard.js (:1519).
  • #121 Prova: <canvas x-ref="overlay"> (:833) → <svg x-ref="overlay" ...> del contratto; static/js/dxf_recipe_wizard.js drawOverlay (:585) usa VisionOverlay.svg(shapes, { zoom: this.zoom }) (shapes = this.result.result.overlay), viewBox dalle dimensioni naturali, ridisegno a ogni cambio di this.zoom (zoomBy, fit, ruota); senza VisionOverlay vuoto.
  • #118 templates/measure/task_complete.html:198: m.value None → NaN (oggi "%.3f"|format va in errore); static/js/csv-export.js:91: formatNumber(null) → NaN per il valore misurato (le tolleranze nulle restano come ora).
  • TEST: tests/js/test_csv_export.js (valore null → NaN); test pytest di task_complete.html con una misura value=None (cerca un test esistente che renda la pagina); tests/js/test_dxf_zoom.js e un controllo del template (svg al posto del canvas, pulsanti Salva in due barre e non in quella del DXF). tests/test_template_js_syntax.py verde.

DIAGNOSI 422 (VERIFICATO 28/10 17:05 sul PC fiera, task 14 TEST1, frame live): check → 8 misure su 12, mancano R_tr.value, d_tl_tr.value, d_tr_br.value, d_tr_bl.value; DetectFillet arco_tr non trova il raccordo (angolo con bava/sporco, pezzo ruotato di ~90°), failures []. Frame in DESKTOP-RABEHJJ C:\Tielogic\backup\grab-2810\live-registra.png.

DEPLOY dopo: tocca il backend con una migrazione (022) → serve il riavvio di TMF sul PC fiera (task tielogic-avvia-tmf), da chiedere ad Adriano prima.

ANALISI (Opus, passo 1) per #118 #119 #120 #121, da eseguire insieme: quattro agenti Sonnet in parallelo su file disgiunti. NON ANCORA AVVIATA (Adriano ha chiuso la sessione il 28/10 sera). CONTRATTO (identico per i quattro agenti; gitea:Adriano/TieMeasureFlow#118 #119 #120 #121) Repo TieMeasureFlow, base `4485803` (ramo `lavoro/daitarn-tmf-dxfcal`, installato sul PC fiera). Frontend in `src/frontend/flask_app/`, backend in `src/backend/`. Agenti, worktree `C:/Users/Adriano/lavoro/TieMeasureFlow-<nome>`, ramo `lavoro/daitarn-tmf-<nome>`, file disgiunti: - BE `nan`: solo `src/backend/**` (codice, migrazione, test). #118 lato server. - F1 `svg`: solo file suoi: `static/js/vision_overlay.js`, nuovo `static/js/save_image.js`, i loro test (`tests/js/test_vision_overlay.js`, nuovo `tests/js/test_save_image.js`, wrapper pytest `tests/test_vision_overlay_js.py`, nuovo `tests/test_save_image_js.py`). #121 modulo, #119 helper, #118 etichette. - F2 `operatore`: `templates/measure/task_execute.html`, `static/js/vision_execute.js` e i loro test (`tests/js/test_vision_rows.js`, `tests/js/test_vision_zoom.js`, `tests/test_vision_panel_fit.py`, nuovi test JS/pytest a nome `test_vision_*`). #119 #120 #121 #118 nel pannello operatore. - F3 `wizard`: `templates/maker/task_editor.html`, `static/js/dxf_recipe_wizard.js`, `templates/measure/task_complete.html`, `static/js/csv-export.js` e i loro test (`tests/js/test_dxf_zoom.js`, `tests/js/test_csv_export.js`, nuovi test a nome `test_wizard_*`/`test_csv_*`/`test_task_complete_*`). #119 #121 nel wizard, #118 nel riepilogo e nel CSV. Nessuno tocca i file degli altri. Non si pusha: commit locali sul proprio ramo, chi coordina unisce. API visione (BE la implementa, F2 la usa) — #118, decisione di Adriano 28/10: «la quota non misurata deve mettere valore NaN»: - `POST /api/vision/check` e `POST /api/vision/execute`: `measurements` ha UNA voce per ogni quota del task con `vision_output`, in ordine di `marker_number`. Prodotta: `{subtask_id, value: <numero>, pass_fail}`. Non prodotta (assente, null, NaN, inf, bool): `{subtask_id, value: null, pass_fail: "fail"}`. - `execute` non dà più 422 per uscite mancanti: salva la misura con `value` NULL, `pass_fail` `fail`, deviazione NULL, e il risultato visione come gli altri. Restano gli altri 422/409 (task senza grafo, nessuna quota con vision_output, versione non corrente, ...). - Le liste di misure del backend possono avere `value: null` (e `deviation: null`). In JSON è `null`, mai `NaN`. - Il frontend mostra un valore null come testo `NaN`, esito `fail`. Modulo `static/js/vision_overlay.js` (F1 lo estende, F2 e F3 lo usano), oltre a `draw`, `anchor`, `labels` che restano: - NUOVO `VisionOverlay.svg(overlay, opts)` → stringa con i soli figli SVG (senza `<svg>` esterno), coordinate in px immagine. `opts = {zoom = 1, lw = 2, cross = 6}`. Ogni forma: `fill="none"`, `vector-effect="non-scaling-stroke"`, `stroke` col colore del ruolo, `stroke-width` = `lw` (px schermo, costante a ogni zoom); ruolo `roi`: `stroke-dasharray="6 4"` e `stroke-width` = `lw*0.75`. `points` = croci con semi-braccio `cross / zoom` px immagine (costante a video). Arc come path SVG (porting di `percorsoArco` dello Studio). Numeri con al più 2 decimali; nel markup solo numeri e colori fissi, mai stringhe dei dati. Elementi ignoti o malformati saltati. - `labels(rows, overlay)`: riga con `value` null, undefined o non finito → nessuna etichetta (attenzione: `Number(null)` è 0). - Uso nella pagina: `<svg x-ref="..." class="absolute inset-0 w-full h-full pointer-events-none" preserveAspectRatio="none"></svg>` al posto del canvas, dentro il wrapper dello zoom; il JS della pagina imposta `setAttribute('viewBox', '0 0 ' + w + ' ' + h)` (dimensioni naturali) e `innerHTML = VisionOverlay.svg(overlay, {zoom})` a ogni risultato, load dell'immagine e cambio di zoom. Helper `static/js/save_image.js` (F1 lo scrive, F2 e F3 lo usano) — #119, decisione di Adriano: frame grezzo PNG, senza disegni: - Script classico, globale `tmfSaveImage(src, baseName)` (`window.tmfSaveImage` e `module.exports`). `src` = URL dell'immagine mostrata (blob: o http). Scarica il blob con `fetch(src)`; se non è `image/png` lo ridisegna su canvas e lo converte in PNG; nome proposto `<baseName>_<AAAAMMGG-hhmmss>.png` (baseName ripulito da caratteri non validi per un nome file). Con `window.showSaveFilePicker` apre «Salva con nome» (tipo PNG); senza, ripiega su `<a download>`. Annullamento dell'utente (`AbortError`) → nessun errore. Ritorna una Promise che si risolve a `true`/`false`. - Funzione pura esportata per i test: `tmfSaveImageName(baseName, date)`. - Nella pagina: `<script src="{{ url_for('static', filename='js/save_image.js') }}"></script>`. Pulsante nella barra zoom, dopo «Adatta» e prima della percentuale: `<button type="button" class="px-1.5 text-xs rounded hover:bg-white/20" @click="..." title="{{ _('Salva immagine') }}">{{ _('Salva') }}</button>`. Regole comuni: - Ogni modifica porta un commento breve in linea con il riferimento all'issue, stile del file (italiano, es. `// #118: ...`, `{# #119: ... #}`, `<!-- #121: ... -->` come nel file). - Alpine 3.15.12: un `:style` stringa fa `setAttribute('style')` e cancella lo `style` statico → niente `style=` statico su elementi con `:style`; mai `x-show` e `:style` stringa sullo stesso elemento. - Test: `export PATH="/c/Program Files/nodejs:$PATH"`; JS con `node <file>` (veloce); pytest dal worktree con `uv run --all-extras pytest -q <file>` (il primo avvio crea `.venv` in pochi secondi; ~90 s per 10 test: lanciare solo i file toccati, non la suite intera). - Commit con messaggio in italiano, una riga + `Refs #N`, e in fondo `Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>`. Mai `Fixes`. - Battito: prima di ogni passo e almeno una volta al minuto `echo "$(date +%H:%M:%S) sto facendo X" >> <file battito>`. - Budget ~60 chiamate. Un problema che resiste a tre tentativi: fermati e riporta. Un parziale dichiarato è un esito accettabile. Commit presto, non alla fine. - Rapporto finale nel formato: ESITO / RIF / COSA / FILE / PROVA / MANCA, più l'ID del modello del tuo prompt di sistema. COMPITI PER AGENTE (analisi Opus, passo 1; il contratto sopra vale per tutti) ## BE `nan` — #118 lato server - `src/backend/migrations/versions/022_measurement_value_nullable.py` (stile di `021_task_acquisition.py`): `measurements.value` nullable con `op.batch_alter_table` (SQLite). Se anche la colonna della deviazione è NOT NULL, stessa cosa. Downgrade: torna NOT NULL (fallisce se ci sono righe NULL: scriverlo nel docstring). Le migrazioni girano all'avvio (`packaging/windows/avvio.py` → `alembic upgrade head`). - `src/backend/models/orm/measurement.py:29`: `Mapped[Optional[float]]`, `nullable=True`. - `src/backend/models/api/measurement.py`: i modelli di risposta (`:46`, `:111` e simili) `value: float | None` (e deviazione); quello di creazione manuale (`:19`) resta `float` obbligatorio. - `src/backend/models/api/vision.py:10`: `value: float | None`. - `src/backend/services/measurement_service.py` `save_measurement` (`:353`): accetta `value=None` (solo il percorso visione la usa): `pass_fail` `fail`, deviazione None, niente `_validate_measured_value`; `:305` `float(m.value)` sicuro con None. - `src/backend/services/vision_service.py` `execute_task` (~`:690-748`): niente 422 per le uscite mancanti; per ogni quota `value = float(outputs[q.vision_output]) if _is_measurable(...) else None`. `check_task` (`:752`): le quote non prodotte entrano in `measurements` con `value: None`, `pass_fail: "fail"` (aggiorna il docstring, che dice il contrario). - Calcoli numerici, i None si saltano (contano solo nei conteggi di esito): `api/routers/statistics.py:149,174,204`, `services/report_service.py:321`, `services/monitor_service.py:99`. - Testi ed esportazioni, None → `NaN`: `services/report_service.py:429`, `services/production_export_service.py:162`, `api/routers/measurements.py:360` (CSV). - Cerca in `src/backend` (esclusi i test) altri usi di `.value` di una misura (`float(m.value)`, `Measurement.value`, `m.value -`) e rendili sicuri. Gli aggregati SQL (avg/min/max) ignorano già NULL. - Invariato: il blocco di tolleranza (`pending_authorisation`): una quota NaN è un fail come gli altri. - TEST in `src/backend/tests/` (stile dei test di visione esistenti, worker finto): execute con un'uscita mancante → 200, misura salvata con `value` None e `fail`, risposta `value: null`; check con uscita mancante → voce `null`/`fail`; endpoint statistiche con una misura None non va in errore e la esclude dalle medie; CSV con `NaN`; la migrazione 022 su SQLite (se c'è un test delle migrazioni, estendilo). Lancia anche i test esistenti di visione, misure e statistiche. ## F1 `svg` — #121 modulo, #119 helper, #118 etichette - `static/js/vision_overlay.js`: aggiungi `svg(overlay, opts)` come da contratto (porting di `percorsoArco`/`formeSvg` dello Studio in `C:/Users/Adriano/lavoro/TieVision-Assistant/vendor/TieVision-Studio/frontend/src/comune/overlay.js`); `labels` salta i valori null/non finiti. `draw`/`anchor` restano. - Nuovo `static/js/save_image.js` come da contratto (riusa l'idea di `static/js/image_to_png.js`, senza toccarlo). - TEST: estendi `tests/js/test_vision_overlay.js` (fixture `tests/js/fixtures/vision_check_task14.json`: `svg` dà 25 forme, ognuna con `vector-effect="non-scaling-stroke"`; roi con `stroke-dasharray`; croce dei points con semi-braccio 6 a zoom 1 e 3 a zoom 2; path dell'arco con estremi calcolati; malformati saltati; nessuna stringa dei dati nel markup; `labels` con `value: null` → nessuna etichetta). Nuovo `tests/js/test_save_image.js` (nome proposto, pulizia del baseName, ramo `showSaveFilePicker` e ramo `<a download>` con finti, `AbortError` → `false` senza eccezioni) + wrapper `tests/test_save_image_js.py`. ## F2 `operatore` — pannello visione operatore - #119 barra in alto (`templates/measure/task_execute.html` ~540-575): con camera solo «Acquisisci da camera» (`grab()`, testo `{{ _('Acquisisci da camera') }}`; in modo `in_linea` resta Pausa/Riprendi al suo posto) e «Registra»; l'input file «Acquisisci immagine/Riprova» solo senza camera (`{% if not camera_enabled %}`). I badge di stato restano. Barra zoom: pulsante Salva → `tmfSaveImage(shownImage, <nome del task, da __visionCfg o dal titolo>)`. Include `save_image.js`. - #120 conteggio e Completato: - `static/js/vision_execute.js` `record()`: a salvataggio riuscito `window.dispatchEvent(new CustomEvent('vision-recorded', { detail: { measurements: this.result.measurements } }))`; su `task-result-reset` (evento già emesso da `resetForNewCycle`) torna `saved = false`. - componente principale di `task_execute.html`: stato `visionRecorded: false`; ascolta `vision-recorded` (`@vision-recorded.window` sulla radice o `addEventListener` in init) → `measurements` = le misure ricevute (`{subtask_id, value, pass_fail, deviation: value == null ? null : value - nominal}`) e `visionRecorded = true`; in `resetForNewCycle` `visionRecorded = false`. - `canLeaveTask` (`:1376`): per `isVisionTask` serve `visionRecorded` (vale anche per `dxf_compare`, dove oggi «Completato» è attivo subito). - `missingQuotesLabel`/`blockedReason`: per `isVisionTask` non registrato → `{{ _('Registra il pezzo')|tojson }}`, non «Mancano N quote». - ripristino al caricamento (`:1454-1471`): `deviation` null se `value` null; per `isVisionTask` `visionRecorded = true` se ha ripristinato misure. - #118: righe con `value` null → cella `NaN` (non `Number(null).toFixed`), esito fail; tolleranze come ora. - #121: `<canvas x-ref="overlay">` → `<svg x-ref="overlay" ...>` del contratto, nel wrapper dello zoom; `drawOverlay` imposta `viewBox` e `innerHTML = VisionOverlay.svg(overlay, { zoom: this.z.zoom })` (vuoto se `VisionOverlay` manca); ridisegna a ogni cambio di `z.zoom` (`$watch` in init o chiamate in `zoomBy`/`zFit`). Le etichette HTML restano. - TEST: aggiorna `tests/js/test_vision_rows.js`, `tests/js/test_vision_zoom.js`, `tests/test_vision_panel_fit.py`; nuovi controlli (JS con finti di `VisionOverlay`/`tmfSaveImage`/`window.dispatchEvent`, oppure pytest sul testo del template): evento `vision-recorded` emesso dopo un record riuscito e non dopo uno fallito; barra con camera senza input file; `canLeaveTask` dipende da `visionRecorded`; svg al posto del canvas; NaN nella cella. `tests/test_template_js_syntax.py` verde. ## F3 `wizard` — wizard DXF, riepilogo, CSV - #119 `templates/maker/task_editor.html`: Salva nella barra dell'immagine (`:735-741`, `tmfSaveImage(imageSrc, 'immagine')`) e in quella della Prova (`:816-823`, `tmfSaveImage(<src dell'immagine della Prova>, 'prova')`); non nella barra del DXF (è un disegno). Include `save_image.js` e `vision_overlay.js` prima di `dxf_recipe_wizard.js` (`:1519`). - #121 Prova: `<canvas x-ref="overlay">` (`:833`) → `<svg x-ref="overlay" ...>` del contratto; `static/js/dxf_recipe_wizard.js` `drawOverlay` (`:585`) usa `VisionOverlay.svg(shapes, { zoom: this.zoom })` (shapes = `this.result.result.overlay`), `viewBox` dalle dimensioni naturali, ridisegno a ogni cambio di `this.zoom` (`zoomBy`, `fit`, ruota); senza `VisionOverlay` vuoto. - #118 `templates/measure/task_complete.html:198`: `m.value` None → `NaN` (oggi `"%.3f"|format` va in errore); `static/js/csv-export.js:91`: `formatNumber(null)` → `NaN` per il valore misurato (le tolleranze nulle restano come ora). - TEST: `tests/js/test_csv_export.js` (valore null → `NaN`); test pytest di `task_complete.html` con una misura `value=None` (cerca un test esistente che renda la pagina); `tests/js/test_dxf_zoom.js` e un controllo del template (svg al posto del canvas, pulsanti Salva in due barre e non in quella del DXF). `tests/test_template_js_syntax.py` verde. DIAGNOSI 422 (VERIFICATO 28/10 17:05 sul PC fiera, task 14 TEST1, frame live): check → 8 misure su 12, mancano R_tr.value, d_tl_tr.value, d_tr_br.value, d_tr_bl.value; DetectFillet arco_tr non trova il raccordo (angolo con bava/sporco, pezzo ruotato di ~90°), failures []. Frame in DESKTOP-RABEHJJ C:\Tielogic\backup\grab-2810\live-registra.png. DEPLOY dopo: tocca il backend con una migrazione (022) → serve il riavvio di TMF sul PC fiera (task tielogic-avvia-tmf), da chiedere ad Adriano prima.
Adriano added the assegnata:daitarn label 2026-10-28 20:13:54 +00:00
Author
Owner

🔵 daitarn — preso in carico 2026-10-28T20:13:52Z
ramo: lavoro/daitarn · capacità: build-windows, gpu

🔵 daitarn — **preso in carico** 2026-10-28T20:13:52Z ramo: `lavoro/daitarn` · capacità: build-windows, gpu
Author
Owner

ESITO: decisione confermata
RIF: gitea:Adriano/TieMeasureFlow#118 (revisione Fable, segnalazione 1)
COSA: Adriano, 28/10/2026 sera: una quota NaN (value null) chiede il capoturno come una quota fuori tolleranza. Comportamento già implementato su lavoro/daitarn-tmf-dxfcal @ 5897a9e: la quota NaN è fail, pending_authorisation come gli altri fail; la prossima acquisizione non è bloccata.
PROVA: src/backend/tests/test_vision_execute.py::test_a_nan_quote_needs_supervisor_authorisation (#125) → passa.

ESITO: decisione confermata RIF: gitea:Adriano/TieMeasureFlow#118 (revisione Fable, segnalazione 1) COSA: Adriano, 28/10/2026 sera: una quota NaN (value null) chiede il capoturno come una quota fuori tolleranza. Comportamento già implementato su `lavoro/daitarn-tmf-dxfcal` @ `5897a9e`: la quota NaN è `fail`, `pending_authorisation` come gli altri fail; la prossima acquisizione non è bloccata. PROVA: `src/backend/tests/test_vision_execute.py::test_a_nan_quote_needs_supervisor_authorisation` (#125) → passa.
Adriano removed the assegnata:daitarn label 2026-10-29 02:25:10 +00:00
Author
Owner

🤖 goldrake — liberata nessun segno di daitarn da 6.2 h — 2026-10-29T02:25:10Z

🤖 goldrake — **liberata** nessun segno di daitarn da 6.2 h — 2026-10-29T02:25:10Z
Author
Owner

Soluzione

Quota non prodotta registrata come NaN (value NULL, esito fail: chiede il capoturno come un fuori tolleranza, decisione di Adriano 28/10) invece del 422; monitor e riepilogo mostrano NaN. Migrazione 022 measurements.value nullable. Commit 06f308b fd3384f 17344aa b01bba5 c904800 17ea010. Ramo lavoro/daitarn-tmf-dxfcal @ 5897a9e. Deploy su TMF-FIERA (DESKTOP-RABEHJJ) 29/10/2026 08:53 con dep118.ps1: 26 file, hash SHA256 uguali al manifesto, 4 porte su in 24 s, worker ok, frontend 200; backup in C:\Tielogic\backup\pre-118. VERIFICATO sul DB del PC fiera: alembic 022_measurement_value_nullable, measurements.value nullable.

**Soluzione** Quota non prodotta registrata come NaN (value NULL, esito fail: chiede il capoturno come un fuori tolleranza, decisione di Adriano 28/10) invece del 422; monitor e riepilogo mostrano NaN. Migrazione 022 measurements.value nullable. Commit 06f308b fd3384f 17344aa b01bba5 c904800 17ea010. Ramo lavoro/daitarn-tmf-dxfcal @ 5897a9e. Deploy su TMF-FIERA (DESKTOP-RABEHJJ) 29/10/2026 08:53 con dep118.ps1: 26 file, hash SHA256 uguali al manifesto, 4 porte su in 24 s, worker ok, frontend 200; backup in C:\Tielogic\backup\pre-118. VERIFICATO sul DB del PC fiera: alembic 022_measurement_value_nullable, measurements.value nullable.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Adriano/TieMeasureFlow#118