From 25f8b4d6092ab3e6aff1f68919fe857560c5126d Mon Sep 17 00:00:00 2001 From: "Adriano (daitarn)" Date: Sat, 24 Oct 2026 18:26:00 +0200 Subject: [PATCH] maker: camera e modo di analisi nel task visione (Refs #97) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rotte /maker/api/camera/servizi e /maker/api/camera/sorgenti (url ammesso solo se e' un servizio delle stazioni), sezione «Acquisizione» nell'editor task (servizio, sorgente, analisi impulsiva/in linea) salvata come acquisition_json. Co-Authored-By: Claude Sonnet 5.5 Claude-Session: https://claude.ai/code/session_01Jbf7qKuxsnAVjxthRy7dnB --- src/frontend/flask_app/blueprints/maker.py | 49 ++++++ .../flask_app/static/js/task_acquisition.js | 70 +++++++++ .../templates/maker/task_editor.html | 95 +++++++++++- .../flask_app/tests/test_maker_acquisition.py | 140 ++++++++++++++++++ 4 files changed, 350 insertions(+), 4 deletions(-) create mode 100644 src/frontend/flask_app/static/js/task_acquisition.js create mode 100644 src/frontend/flask_app/tests/test_maker_acquisition.py diff --git a/src/frontend/flask_app/blueprints/maker.py b/src/frontend/flask_app/blueprints/maker.py index 3765ae7..5955fe2 100644 --- a/src/frontend/flask_app/blueprints/maker.py +++ b/src/frontend/flask_app/blueprints/maker.py @@ -541,6 +541,55 @@ def api_list_stations_for_maker(): return jsonify(out), 200 +def _camera_services() -> list[dict]: + """#97: servizi di acquisizione distinti delle stazioni che hanno una camera_url.""" + services: dict[str, list[str]] = {} + for st in _stations_for_maker(): + url = (st.get("camera_url") or "").strip().rstrip("/") + if not url: + continue + label = f"{st.get('code') or ''} - {st.get('name') or ''}" + services.setdefault(url, []).append(label) + return [{"camera_url": u, "stazioni": s} for u, s in services.items()] + + +@maker_bp.route("/api/camera/servizi", methods=["GET"]) +@login_required +@maker_or_admin_required +def api_camera_services(): + """#97: servizi di acquisizione fra cui scegliere la camera di un task.""" + return jsonify({"servizi": _camera_services()}), 200 + + +@maker_bp.route("/api/camera/sorgenti", methods=["GET"]) +@login_required +@maker_or_admin_required +def api_camera_sources(): + """#97: proxy di GET {url}/camera/sources; solo per i servizi delle stazioni.""" + url = (request.args.get("url") or "").strip().rstrip("/") + # Niente richieste verso indirizzi arbitrari: url ammesso solo se e' un servizio noto + if not url or url not in {s["camera_url"] for s in _camera_services()}: + return jsonify({ + "error": True, + "detail": _("Servizio di acquisizione non ammesso"), + }), 400 + try: + resp = http_requests.get(f"{url}/camera/sources", timeout=15) + body = resp.json() if resp.status_code == 200 else None + except (http_requests.RequestException, ValueError) as exc: + return jsonify({ + "error": True, + "detail": _("Servizio camera non raggiungibile: %(detail)s", detail=str(exc)), + }), 502 + sources = body.get("sources") if isinstance(body, dict) else None + if not isinstance(sources, list): + return jsonify({ + "error": True, + "detail": _("Risposta del servizio camera non valida (%(status)s)", status=resp.status_code), + }), 502 + return jsonify({"sources": sources}), 200 + + # Copia minima della logica di measure.api_vision_grab, ma per una stazione scelta per id @maker_bp.route("/api/stations//camera/grab", methods=["GET", "POST"]) @login_required diff --git a/src/frontend/flask_app/static/js/task_acquisition.js b/src/frontend/flask_app/static/js/task_acquisition.js new file mode 100644 index 0000000..12cb134 --- /dev/null +++ b/src/frontend/flask_app/static/js/task_acquisition.js @@ -0,0 +1,70 @@ +// «Acquisizione» dell'editor task (#97): camera (servizio + sorgente) e modo di analisi +// («impulsiva» / «in linea») dei task camera_measure e dxf_compare. +// Legge /maker/api/camera/servizi e /maker/api/camera/sorgenti?url=. Lo stato scelto sta +// nel componente padre (editTaskData.acquisition): questo componente carica solo gli elenchi. +// Uso: x-data="taskAcquisition(() => editTaskData.acquisition, msgs)". Dipende da Alpine.js. +function taskAcquisition(getAcq, msgs) { + return { + msgs: msgs || {}, + servizi: [], + sources: [], + loading: false, + error: '', + + async init() { + await this.loadServizi(); + // Task con camera gia' scelta: elenco sorgenti del suo servizio + const acq = getAcq(); + if (acq && acq.camera_url) await this.loadSources(acq.camera_url); + }, + + async loadServizi() { + try { + const resp = await fetch('/maker/api/camera/servizi'); + if (resp.ok) this.servizi = (await resp.json()).servizi || []; + } catch (e) { console.error('taskAcquisition servizi', e); } + }, + + // Servizio salvato nel task ma non piu' fra quelli delle stazioni: resta selezionabile + serviziConCorrente() { + const acq = getAcq(); + const out = this.servizi.slice(); + if (acq && acq.camera_url && !out.some(s => s.camera_url === acq.camera_url)) { + out.push({ camera_url: acq.camera_url, stazioni: [] }); + } + return out; + }, + + // Cambio servizio: la sorgente scelta non vale piu' + async onServizio() { + const acq = getAcq(); + acq.camera_source = ''; + this.sources = []; + this.error = ''; + if (acq.camera_url) await this.loadSources(acq.camera_url); + }, + + async loadSources(url) { + this.loading = true; + this.error = ''; + try { + const resp = await fetch('/maker/api/camera/sorgenti?url=' + encodeURIComponent(url)); + const data = await resp.json().catch(() => ({})); + if (resp.ok) this.sources = data.sources || []; + else this.error = data.detail || this.msgs.genericError || ''; + } catch (e) { + this.error = this.msgs.genericError || String(e); + } finally { this.loading = false; } + }, + + // Sorgente salvata ma non piu' elencata dal servizio: resta visibile + sourcesConCorrente() { + const acq = getAcq(); + const out = this.sources.slice(); + if (acq && acq.camera_source && !out.some(s => s.name === acq.camera_source)) { + out.push({ name: acq.camera_source, label: acq.camera_source }); + } + return out; + } + }; +} diff --git a/src/frontend/flask_app/templates/maker/task_editor.html b/src/frontend/flask_app/templates/maker/task_editor.html index 7605199..a06fed4 100644 --- a/src/frontend/flask_app/templates/maker/task_editor.html +++ b/src/frontend/flask_app/templates/maker/task_editor.html @@ -547,6 +547,71 @@
+ + +