fix(vision): il gate del fuori tolleranza vale per l'acquisizione, non per la quota

Un'acquisizione camera produce N quote nello stesso istante: non c'e "andare
avanti" fra l'una e l'altra, quindi il gate sequenziale di save_measurement
non deve scattare dentro il lotto. execute_task valuta pending_authorisation
una sola volta per l'intera acquisizione (bloccando solo se il fail pendente
non appartiene alle quote di questo task) e passa enforce_tolerance_gate=False
a ogni save_measurement del lotto. Query delle quote ordinata per
marker_number, cosi l'esito non dipende dall'ordine di ritorno di SQLite/MySQL.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014BBnuACZSCJqXrMYC3LUMU
This commit is contained in:
2026-08-16 18:58:46 +02:00
parent ddf7788d77
commit b86eeada86
3 changed files with 186 additions and 11 deletions
+21 -11
View File
@@ -225,8 +225,17 @@ async def save_measurement(
input_duration_ms: int | None = None,
production_run_id: int | None = None,
vision_result_id: int | None = None,
enforce_tolerance_gate: bool = True,
) -> Measurement:
"""Save a single measurement with auto-calculated pass/fail."""
"""Save a single measurement with auto-calculated pass/fail.
``enforce_tolerance_gate=False`` skips only the pending-authorisation check
below - everything else (verdict, recipe rules, deviation) still applies.
This is for a caller that has already evaluated the gate itself for a whole
batch of quotes taken at the same instant (see Ruling R10 / vision_service):
the gate models "moving on" between measurements taken one after another,
which does not exist inside a single camera acquisition.
"""
# Get subtask for tolerance values
result = await db.execute(
select(RecipeSubtask).where(RecipeSubtask.id == subtask_id)
@@ -241,17 +250,18 @@ async def save_measurement(
# No moving on while a quote is out of tolerance and unauthorised. Measuring
# that same quote again is the way out that does not need the capoturno; the
# next quote is not.
blocking = await pending_authorisation(
db, version_id, measured_by, production_run_id,
)
if blocking is not None and blocking.subtask_id != subtask_id:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT,
detail=(
"A measurement is out of tolerance and awaiting the supervisor: "
"authorise it or measure that quote again before going on"
),
if enforce_tolerance_gate:
blocking = await pending_authorisation(
db, version_id, measured_by, production_run_id,
)
if blocking is not None and blocking.subtask_id != subtask_id:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT,
detail=(
"A measurement is out of tolerance and awaiting the supervisor: "
"authorise it or measure that quote again before going on"
),
)
pass_fail, deviation = calculate_pass_fail(value, subtask)