diff --git a/web/pose_3d.js b/web/pose_3d.js index a4ad9d1..f45dad3 100644 --- a/web/pose_3d.js +++ b/web/pose_3d.js @@ -496,6 +496,84 @@ function bindJogAutoApply() { }); } +// ----- named poses ----- +async function onSavePose() { + const name = ($('p3PoseName').value || '').trim(); + if (!name) { alert('Inserisci un nome'); return; } + if (!/^[A-Za-z0-9_-]{1,40}$/.test(name)) { + alert('Nome: solo [A-Za-z0-9_-], 1-40 caratteri'); + return; + } + const side = state.active_side; + const pose = state.target[side] || state.current[side]; + if (!pose) { alert('Nessuna posa da salvare'); return; } + if (state.saved_poses[name]) { + if (!confirm(`Posa "${name}" esiste già — sovrascrivere?`)) return; + } + try { + await jsonFetch(`/api/pose_3d/poses/${encodeURIComponent(name)}`, { + method: 'PUT', headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + side, position: pose.position, quaternion: pose.quaternion, + }), + }); + $('p3PoseName').value = ''; + await pollState(); // refresh dropdown + $('p3PoseSelect').value = name; + } catch (e) { + showError(`Salvataggio fallito: ${e.message}`); + } +} + +async function onGoPose() { + const name = $('p3PoseSelect').value; + if (!name) { alert('Seleziona una posa dal dropdown'); return; } + if (state.mode === 'jog') { + if (!confirm(`Eseguire posa "${name}" SUL ROBOT REALE? (Jog mode)`)) return; + } + const dry_run = (state.mode === 'editor'); + clearError(); + try { + const res = await jsonFetch( + `/api/pose_3d/poses/${encodeURIComponent(name)}/go`, + { + method: 'POST', headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ dry_run, precise: state.precise }), + }, + ); + const r = res.result || {}; + if (r.reached_target === false || r.stalled === true) { + showError(`Vai a "${name}": residual ${(r.position_error_mm||0).toFixed(1)} mm`); + } else { + $('p3Status').textContent = `✓ Posa "${name}" applicata` + + (dry_run ? ' (simulato)' : ''); + $('p3Status').className = 'p3-status ok'; + } + } catch (e) { + showError(e.message); + } +} + +async function onDeletePose() { + const name = $('p3PoseSelect').value; + if (!name) { alert('Seleziona una posa dal dropdown'); return; } + if (!confirm(`Cancellare definitivamente la posa "${name}"?`)) return; + try { + await jsonFetch(`/api/pose_3d/poses/${encodeURIComponent(name)}`, { + method: 'DELETE', + }); + await pollState(); + } catch (e) { + showError(e.message); + } +} + +function bindPoseButtons() { + $('p3PoseSaveBtn').addEventListener('click', onSavePose); + $('p3PoseGoBtn').addEventListener('click', onGoPose); + $('p3PoseDelBtn').addEventListener('click', onDeletePose); +} + // ----- init triggered on first activation ----- let inited = false; function ensureInit() { @@ -511,6 +589,7 @@ function ensureInit() { bindModeControls(); bindPreciseControl(); bindJogAutoApply(); + bindPoseButtons(); pollState(); pollTimer = setInterval(pollState, POLL_MS); }