Visione: Registra con quote non misurate salva NaN (esito fail) invece di 422 #118
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
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.
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(ramolavoro/daitarn-tmf-dxfcal, installato sul PC fiera). Frontend insrc/frontend/flask_app/, backend insrc/backend/.Agenti, worktree
C:/Users/Adriano/lavoro/TieMeasureFlow-<nome>, ramolavoro/daitarn-tmf-<nome>, file disgiunti:nan: solosrc/backend/**(codice, migrazione, test). #118 lato server.svg: solo file suoi:static/js/vision_overlay.js, nuovostatic/js/save_image.js, i loro test (tests/js/test_vision_overlay.js, nuovotests/js/test_save_image.js, wrapper pytesttests/test_vision_overlay_js.py, nuovotests/test_save_image_js.py). #121 modulo, #119 helper, #118 etichette.operatore:templates/measure/task_execute.html,static/js/vision_execute.jse 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 nometest_vision_*). #119 #120 #121 #118 nel pannello operatore.wizard:templates/maker/task_editor.html,static/js/dxf_recipe_wizard.js,templates/measure/task_complete.html,static/js/csv-export.jse i loro test (tests/js/test_dxf_zoom.js,tests/js/test_csv_export.js, nuovi test a nometest_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/checkePOST /api/vision/execute:measurementsha UNA voce per ogni quota del task convision_output, in ordine dimarker_number. Prodotta:{subtask_id, value: <numero>, pass_fail}. Non prodotta (assente, null, NaN, inf, bool):{subtask_id, value: null, pass_fail: "fail"}.executenon dà più 422 per uscite mancanti: salva la misura convalueNULL,pass_failfail, 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, ...).value: null(edeviation: null). In JSON ènull, maiNaN.NaN, esitofail.Modulo
static/js/vision_overlay.js(F1 lo estende, F2 e F3 lo usano), oltre adraw,anchor,labelsche restano: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",strokecol colore del ruolo,stroke-width=lw(px schermo, costante a ogni zoom); ruoloroi:stroke-dasharray="6 4"estroke-width=lw*0.75.points= croci con semi-bracciocross / zoompx immagine (costante a video). Arc come path SVG (porting dipercorsoArcodello 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 convaluenull, undefined o non finito → nessuna etichetta (attenzione:Number(null)è 0).<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 impostasetAttribute('viewBox', '0 0 ' + w + ' ' + h)(dimensioni naturali) einnerHTML = 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:tmfSaveImage(src, baseName)(window.tmfSaveImageemodule.exports).src= URL dell'immagine mostrata (blob: o http). Scarica il blob confetch(src); se non èimage/pnglo ridisegna su canvas e lo converte in PNG; nome proposto<baseName>_<AAAAMMGG-hhmmss>.png(baseName ripulito da caratteri non validi per un nome file). Conwindow.showSaveFilePickerapre «Salva con nome» (tipo PNG); senza, ripiega su<a download>. Annullamento dell'utente (AbortError) → nessun errore. Ritorna una Promise che si risolve atrue/false.tmfSaveImageName(baseName, date).<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:
// #118: ...,{# #119: ... #},<!-- #121: ... -->come nel file).:stylestringa fasetAttribute('style')e cancella lostylestatico → nientestyle=statico su elementi con:style; maix-showe:stylestringa sullo stesso elemento.export PATH="/c/Program Files/nodejs:$PATH"; JS connode <file>(veloce); pytest dal worktree conuv run --all-extras pytest -q <file>(il primo avvio crea.venvin pochi secondi; ~90 s per 10 test: lanciare solo i file toccati, non la suite intera).Refs #N, e in fondoCo-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>. MaiFixes.echo "$(date +%H:%M:%S) sto facendo X" >> <file battito>.COMPITI PER AGENTE (analisi Opus, passo 1; il contratto sopra vale per tutti)
BE
nan— #118 lato serversrc/backend/migrations/versions/022_measurement_value_nullable.py(stile di021_task_acquisition.py):measurements.valuenullable conop.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,:111e simili)value: float | None(e deviazione); quello di creazione manuale (:19) restafloatobbligatorio.src/backend/models/api/vision.py:10:value: float | None.src/backend/services/measurement_service.pysave_measurement(:353): accettavalue=None(solo il percorso visione la usa):pass_failfail, deviazione None, niente_validate_measured_value;:305float(m.value)sicuro con None.src/backend/services/vision_service.pyexecute_task(~:690-748): niente 422 per le uscite mancanti; per ogni quotavalue = float(outputs[q.vision_output]) if _is_measurable(...) else None.check_task(:752): le quote non prodotte entrano inmeasurementsconvalue: None,pass_fail: "fail"(aggiorna il docstring, che dice il contrario).api/routers/statistics.py:149,174,204,services/report_service.py:321,services/monitor_service.py:99.NaN:services/report_service.py:429,services/production_export_service.py:162,api/routers/measurements.py:360(CSV).src/backend(esclusi i test) altri usi di.valuedi una misura (float(m.value),Measurement.value,m.value -) e rendili sicuri. Gli aggregati SQL (avg/min/max) ignorano già NULL.pending_authorisation): una quota NaN è un fail come gli altri.src/backend/tests/(stile dei test di visione esistenti, worker finto): execute con un'uscita mancante → 200, misura salvata convalueNone efail, rispostavalue: null; check con uscita mancante → vocenull/fail; endpoint statistiche con una misura None non va in errore e la esclude dalle medie; CSV conNaN; 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 etichettestatic/js/vision_overlay.js: aggiungisvg(overlay, opts)come da contratto (porting dipercorsoArco/formeSvgdello Studio inC:/Users/Adriano/lavoro/TieVision-Assistant/vendor/TieVision-Studio/frontend/src/comune/overlay.js);labelssalta i valori null/non finiti.draw/anchorrestano.static/js/save_image.jscome da contratto (riusa l'idea distatic/js/image_to_png.js, senza toccarlo).tests/js/test_vision_overlay.js(fixturetests/js/fixtures/vision_check_task14.json:svgdà 25 forme, ognuna convector-effect="non-scaling-stroke"; roi constroke-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;labelsconvalue: null→ nessuna etichetta). Nuovotests/js/test_save_image.js(nome proposto, pulizia del baseName, ramoshowSaveFilePickere ramo<a download>con finti,AbortError→falsesenza eccezioni) + wrappertests/test_save_image_js.py.F2
operatore— pannello visione operatoretemplates/measure/task_execute.html~540-575): con camera solo «Acquisisci da camera» (grab(), testo{{ _('Acquisisci da camera') }}; in modoin_linearesta 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>). Includesave_image.js.static/js/vision_execute.jsrecord(): a salvataggio riuscitowindow.dispatchEvent(new CustomEvent('vision-recorded', { detail: { measurements: this.result.measurements } })); sutask-result-reset(evento già emesso daresetForNewCycle) tornasaved = false.task_execute.html: statovisionRecorded: false; ascoltavision-recorded(@vision-recorded.windowsulla radice oaddEventListenerin init) →measurements= le misure ricevute ({subtask_id, value, pass_fail, deviation: value == null ? null : value - nominal}) evisionRecorded = true; inresetForNewCyclevisionRecorded = false.canLeaveTask(:1376): perisVisionTaskservevisionRecorded(vale anche perdxf_compare, dove oggi «Completato» è attivo subito).missingQuotesLabel/blockedReason: perisVisionTasknon registrato →{{ _('Registra il pezzo')|tojson }}, non «Mancano N quote».:1454-1471):deviationnull sevaluenull; perisVisionTaskvisionRecorded = truese ha ripristinato misure.valuenull → cellaNaN(nonNumber(null).toFixed), esito fail; tolleranze come ora.<canvas x-ref="overlay">→<svg x-ref="overlay" ...>del contratto, nel wrapper dello zoom;drawOverlayimpostaviewBoxeinnerHTML = VisionOverlay.svg(overlay, { zoom: this.z.zoom })(vuoto seVisionOverlaymanca); ridisegna a ogni cambio diz.zoom($watchin init o chiamate inzoomBy/zFit). Le etichette HTML restano.tests/js/test_vision_rows.js,tests/js/test_vision_zoom.js,tests/test_vision_panel_fit.py; nuovi controlli (JS con finti diVisionOverlay/tmfSaveImage/window.dispatchEvent, oppure pytest sul testo del template): eventovision-recordedemesso dopo un record riuscito e non dopo uno fallito; barra con camera senza input file;canLeaveTaskdipende davisionRecorded; svg al posto del canvas; NaN nella cella.tests/test_template_js_syntax.pyverde.F3
wizard— wizard DXF, riepilogo, CSVtemplates/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). Includesave_image.jsevision_overlay.jsprima didxf_recipe_wizard.js(:1519).<canvas x-ref="overlay">(:833) →<svg x-ref="overlay" ...>del contratto;static/js/dxf_recipe_wizard.jsdrawOverlay(:585) usaVisionOverlay.svg(shapes, { zoom: this.zoom })(shapes =this.result.result.overlay),viewBoxdalle dimensioni naturali, ridisegno a ogni cambio dithis.zoom(zoomBy,fit, ruota); senzaVisionOverlayvuoto.templates/measure/task_complete.html:198:m.valueNone →NaN(oggi"%.3f"|formatva in errore);static/js/csv-export.js:91:formatNumber(null)→NaNper il valore misurato (le tolleranze nulle restano come ora).tests/js/test_csv_export.js(valore null →NaN); test pytest ditask_complete.htmlcon una misuravalue=None(cerca un test esistente che renda la pagina);tests/js/test_dxf_zoom.jse 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.pyverde.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.
🔵 daitarn — preso in carico 2026-10-28T20:13:52Z
ramo:
lavoro/daitarn· capacità: build-windows, gpuESITO: 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_authorisationcome 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.🤖 goldrake — liberata nessun segno di daitarn da 6.2 h — 2026-10-29T02:25:10Z
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
06f308bfd3384f17344aab01bba5c90480017ea010. 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.