Camera di stazione: acquisizione dal frontend e campi admin (#72)

Route POST /measure/api/vision/grab (proxy verso il servizio camera),
get_station_by_code, pulsante «Acquisisci dalla camera», grab() in
vision_execute.js, campi camera_url e camera_source nell'admin stazioni,
traduzioni it/en, extra_hosts host.docker.internal nel compose di sviluppo.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-15 09:51:07 +02:00
parent 5bf374aadd
commit 192d4b590c
11 changed files with 417 additions and 1 deletions
@@ -215,6 +215,26 @@
<p class="mt-1 text-xs text-[var(--text-secondary)]">{{ _('Vuoto = worker centrale. Indirizzo completo, con http o https') }}</p>
</div>
{# Camera della stazione (#72): servizio camera (vs-gui) e sorgente da cui prendere il frame #}
<div>
<label class="block text-sm font-medium text-[var(--text-primary)] mb-1">{{ _('Camera (URL)') }}</label>
<input type="text" x-model="form.camera_url"
class="w-full px-3 py-2 rounded-lg border border-[var(--border-color)] bg-[var(--bg-card)]
text-sm font-mono text-[var(--text-primary)] focus:outline-none focus:ring-2 focus:ring-primary/30 focus:border-primary transition-colors"
placeholder="host:8765"
maxlength="500">
<p class="mt-1 text-xs text-[var(--text-secondary)]">{{ _('Vuoto = nessuna camera. Indirizzo del servizio camera, con http o https, senza / finale') }}</p>
</div>
<div>
<label class="block text-sm font-medium text-[var(--text-primary)] mb-1">{{ _('Sorgente camera') }}</label>
<input type="text" x-model="form.camera_source"
class="w-full px-3 py-2 rounded-lg border border-[var(--border-color)] bg-[var(--bg-card)]
text-sm font-mono text-[var(--text-primary)] focus:outline-none focus:ring-2 focus:ring-primary/30 focus:border-primary transition-colors"
placeholder="camera:folder"
maxlength="200">
<p class="mt-1 text-xs text-[var(--text-secondary)]">{{ _('Es. camera:<seriale> oppure camera:folder') }}</p>
</div>
<div>
<label class="block text-sm font-medium text-[var(--text-primary)] mb-1">{{ _('Note') }}</label>
<textarea x-model="form.notes" rows="3"
@@ -464,6 +484,8 @@ function stationManagement(initialStations, initialRecipes) {
location: '',
notes: '',
vision_worker_url: '', // #45
camera_url: '', // #72
camera_source: '', // #72
active: true,
},
@@ -517,7 +539,7 @@ function stationManagement(initialStations, initialRecipes) {
this.isEditing = false;
this.editingId = null;
this.errorMsg = '';
this.form = { code: '', name: '', location: '', notes: '', vision_worker_url: '', active: true };
this.form = { code: '', name: '', location: '', notes: '', vision_worker_url: '', camera_url: '', camera_source: '', active: true };
this.showModal = true;
},
@@ -531,6 +553,8 @@ function stationManagement(initialStations, initialRecipes) {
location: station.location || '',
notes: station.notes || '',
vision_worker_url: station.vision_worker_url || '', // #45
camera_url: station.camera_url || '', // #72
camera_source: station.camera_source || '', // #72
active: station.active,
};
this.showModal = true;
@@ -551,6 +575,8 @@ function stationManagement(initialStations, initialRecipes) {
location: this.form.location || null,
notes: this.form.notes || null,
vision_worker_url: (this.form.vision_worker_url || '').trim() || null, // #45
camera_url: (this.form.camera_url || '').trim() || null, // #72
camera_source: (this.form.camera_source || '').trim() || null, // #72
active: this.form.active,
};
const resp = await fetch(`/admin/api/stations/${this.editingId}`, {
@@ -578,6 +604,8 @@ function stationManagement(initialStations, initialRecipes) {
location: this.form.location || null,
notes: this.form.notes || null,
vision_worker_url: (this.form.vision_worker_url || '').trim() || null, // #45
camera_url: (this.form.camera_url || '').trim() || null, // #72
camera_source: (this.form.camera_source || '').trim() || null, // #72
active: this.form.active,
};
const resp = await fetch('/admin/api/stations', {