514 lines
23 KiB
HTML
514 lines
23 KiB
HTML
<!doctype html>
|
||
<html lang="it">
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||
<title>presa-mano</title>
|
||
<link rel="stylesheet" href="/static/style.css" />
|
||
</head>
|
||
<body>
|
||
<header>
|
||
<h1>presa-mano</h1>
|
||
<nav class="tabs">
|
||
<button class="tab active" data-tab="home">Home</button>
|
||
<button class="tab" data-tab="agent">Assistente AI</button>
|
||
<button class="tab" data-tab="ai-settings">Impostazioni AI</button>
|
||
<button class="tab" data-tab="workplane">Calibrazione Piano</button>
|
||
<button class="tab" data-tab="grasp">Cerca modello</button>
|
||
<button class="tab" data-tab="view3d">Vista 3D</button>
|
||
<button class="tab" data-tab="right-arm">Braccio DX</button>
|
||
<button class="tab" data-tab="ee">Movimento EE</button>
|
||
</nav>
|
||
<div class="status-strip" id="statusStrip"></div>
|
||
<span class="active-robot-pill" id="activeRobotPill" title="Robot attualmente collegato">robot: —</span>
|
||
</header>
|
||
|
||
<main>
|
||
|
||
<!-- ============================== HOME ============================== -->
|
||
<section class="panel active" id="tab-home">
|
||
<div class="posture-toolbar">
|
||
<label class="inline">Robot attivo
|
||
<select id="homeRobotSelect">
|
||
<option value="g1">G1</option>
|
||
<option value="h1">H1</option>
|
||
</select>
|
||
</label>
|
||
<button id="homeApplyRobotBtn" class="primary">Applica</button>
|
||
<label class="inline"><input type="checkbox" id="homeAutoPoll" checked /> Auto-poll</label>
|
||
<label class="inline">ogni
|
||
<input type="number" id="homePollMs" value="2000" min="500" step="500" style="width:80px"> ms
|
||
</label>
|
||
<button id="homeRefreshBtn">Aggiorna ora</button>
|
||
</div>
|
||
|
||
<div class="info-box">
|
||
Stato di comunicazione con i microservizi upstream. Il selettore sopra cambia il robot attivo (G1 o H1) e aggiorna <code>config.yaml</code> a runtime; le chiamate <code>/api/wrist</code>, <code>/api/posture</code>, <code>/api/robot</code> useranno il robot selezionato.
|
||
</div>
|
||
|
||
<h2>Stato dispositivi</h2>
|
||
<table class="device-status-table" id="homeDeviceTable">
|
||
<thead>
|
||
<tr>
|
||
<th>Servizio</th>
|
||
<th>URL</th>
|
||
<th>Stato</th>
|
||
<th>Latenza</th>
|
||
<th>Dettagli</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr><td colspan="5" class="dim">caricamento…</td></tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<h3>Ultimo esito</h3>
|
||
<pre id="homeLog" class="log small">—</pre>
|
||
</section>
|
||
|
||
<!-- ============================== ASSISTENTE AI ============================== -->
|
||
<section class="panel" id="tab-agent">
|
||
<div class="posture-toolbar">
|
||
<span class="post-health" id="agentHealth">—</span>
|
||
<span class="active-robot-pill" id="agentActiveRobotPill" title="Robot attivo">robot: —</span>
|
||
<button id="agentClearBtn" title="Resetta la conversazione">Nuova conversazione</button>
|
||
</div>
|
||
|
||
<div class="grid-2 agent-layout">
|
||
<!-- LEFT: chat history + input -->
|
||
<div>
|
||
<h2>Chat</h2>
|
||
<div id="agentChat" class="agent-chat">
|
||
<div class="agent-msg system">Ciao. Dimmi cosa vuoi fare con il robot. Es: "muovi il braccio destro 200 mm in X+", "calibra il braccio destro", "mostra lo stato dei dispositivi".</div>
|
||
</div>
|
||
<div class="agent-input-row">
|
||
<textarea id="agentInput" rows="3" placeholder="Scrivi un comando o una richiesta in linguaggio naturale… (Ctrl+Invio per inviare)"></textarea>
|
||
<button id="agentSendBtn" class="primary">Invia ▸</button>
|
||
</div>
|
||
<div class="marker-info" id="agentStatus">—</div>
|
||
</div>
|
||
|
||
<!-- RIGHT: saved tasks + tool trace -->
|
||
<div>
|
||
<h2>Task salvati</h2>
|
||
<div class="info-box">
|
||
Salva un prompt usato spesso (es. "calibra braccio dx") per riutilizzarlo con un click. Il prompt corrente nella textarea viene salvato.
|
||
</div>
|
||
<div class="kv compact">
|
||
<label>Nome <input type="text" id="agentTaskName" placeholder="es. calibrazione_dx" maxlength="80"></label>
|
||
<label>Descrizione <input type="text" id="agentTaskDesc" placeholder="(opzionale)" maxlength="400"></label>
|
||
</div>
|
||
<div class="actions">
|
||
<button id="agentTaskSaveBtn" class="primary">💾 Salva prompt corrente</button>
|
||
<button id="agentTaskReloadBtn">⟳ Ricarica</button>
|
||
</div>
|
||
<ul id="agentTaskList" class="agent-task-list">
|
||
<li class="dim">caricamento…</li>
|
||
</ul>
|
||
|
||
<h2>Tool trace ultima risposta</h2>
|
||
<pre id="agentTrace" class="log small">(nessuna chiamata)</pre>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ============================== IMPOSTAZIONI AI ============================== -->
|
||
<section class="panel" id="tab-ai-settings">
|
||
<div class="info-box">
|
||
Parametri del client OpenRouter usato dall'Assistente AI. Le modifiche vengono persistite in <code>config.yaml</code> via <code>PUT /api/config</code> e applicate alla chat successiva (non c'è bisogno di restart). Il <strong>prompt aggiuntivo</strong> viene appeso al prompt di sistema dinamico — non sostituisce le regole base (frame, tool, sicurezza).
|
||
</div>
|
||
|
||
<div class="grid-2">
|
||
<div>
|
||
<h2>Modello e parametri</h2>
|
||
<div class="ctrl-block">
|
||
<div class="kv compact">
|
||
<label>Modello (combo)
|
||
<select id="aiModelSelect">
|
||
<option value="">caricamento…</option>
|
||
</select>
|
||
</label>
|
||
<label>Modello (custom — sovrascrive la combo)
|
||
<input type="text" id="aiModelCustom" placeholder="es. anthropic/claude-sonnet-4.5" />
|
||
</label>
|
||
<label title="0 = deterministico, 1 = creativo. Per orchestrazione robotica tieni basso (0.0–0.3).">
|
||
Temperature
|
||
<input type="number" id="aiTemperature" min="0" max="2" step="0.05" />
|
||
</label>
|
||
<label title="Massimo numero di iterazioni di tool calling per singolo messaggio">
|
||
Max tool iterations
|
||
<input type="number" id="aiMaxIterations" min="1" max="20" step="1" />
|
||
</label>
|
||
<label title="Timeout HTTP per la singola chiamata a OpenRouter (secondi)">
|
||
Timeout (s)
|
||
<input type="number" id="aiTimeout" min="5" max="300" step="5" />
|
||
</label>
|
||
</div>
|
||
<div class="actions">
|
||
<button id="aiSaveBtn" class="primary">💾 Salva impostazioni</button>
|
||
<button id="aiReloadBtn">⟳ Ricarica da config</button>
|
||
<button id="aiResetTempBtn" title="Riporta temperature a 0.1 (default safe)">Reset temp</button>
|
||
</div>
|
||
<div class="marker-info" id="aiSettingsStatus">—</div>
|
||
</div>
|
||
|
||
<h2>Prompt aggiuntivo</h2>
|
||
<div class="ctrl-block">
|
||
<div class="info-box">
|
||
Testo che viene appeso al system prompt dinamico (sezione "Istruzioni aggiuntive dell'operatore"). Usa per personalizzare lo stile, aggiungere contesto specifico al tuo setup, o regole custom. Lascia vuoto per non aggiungere nulla.
|
||
</div>
|
||
<textarea id="aiPromptAddendum" rows="10" style="width:100%;font-family:ui-monospace,monospace;font-size:12px;padding:8px;background:#0e1116;color:inherit;border:1px solid var(--border);border-radius:4px"></textarea>
|
||
</div>
|
||
</div>
|
||
|
||
<div>
|
||
<h2>Anteprima system prompt corrente</h2>
|
||
<div class="info-box">
|
||
Quello che il modello riceve davvero al prossimo messaggio (include lo stato corrente: robot attivo, marker, ecc., più l'eventuale addendum sopra dopo che lo salvi).
|
||
</div>
|
||
<div class="actions">
|
||
<button id="aiPromptPreviewBtn">⟳ Aggiorna anteprima</button>
|
||
<button id="aiPromptCopyBtn">📋 Copia</button>
|
||
</div>
|
||
<pre id="aiPromptPreview" class="log small" style="max-height:560px;overflow:auto">(premi 'Aggiorna anteprima')</pre>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ============================== CALIBRAZIONE PIANO ============================== -->
|
||
<section class="panel" id="tab-workplane">
|
||
<div class="diag3d-toolbar">
|
||
<span class="post-health" id="wpStatusChip">—</span>
|
||
</div>
|
||
<div class="grid-2 diag3d-layout">
|
||
<div>
|
||
<div id="wpViewport" style="position:relative;width:min(100%,720px);height:440px;overflow:hidden;background:#000;border:1px solid var(--border);border-radius:4px;cursor:grab;user-select:none">
|
||
<div id="wpPanZoom" style="position:absolute;left:0;top:0;transform-origin:0 0;will-change:transform">
|
||
<img id="wpImg" alt="Nuwa RGB" style="display:block;max-width:none;pointer-events:none" draggable="false" />
|
||
<canvas id="wpOverlay" style="position:absolute;left:0;top:0;pointer-events:none"></canvas>
|
||
</div>
|
||
</div>
|
||
<div class="marker-info">RGB live · 2 click = angoli del rettangolo ROI · 3° click ricomincia</div>
|
||
<h3 style="margin-top:14px">Vista 3D (frame camera)</h3>
|
||
<div class="canvas-wrap" style="width:min(100%,720px)">
|
||
<canvas id="wp3dCanvas" width="720" height="360" style="display:block;background:#0a0d11;border:1px solid var(--border);border-radius:4px"></canvas>
|
||
</div>
|
||
<div class="marker-info">vista camera · blu = punti usati per il fit · ciano = scena tenuta · rosso = scena esclusa (sotto piano−offset) · drag = ruota · wheel = zoom</div>
|
||
</div>
|
||
<div>
|
||
<h2>Calibrazione</h2>
|
||
<div class="ctrl-block">
|
||
<div class="info-box">
|
||
Clicca due angoli opposti sul tavolo (senza oggetti) per definire il
|
||
rettangolo ROI, poi clicca <strong>Calcola piano</strong>. Regola
|
||
l'offset finché i punti gialli rappresentano l'oggetto e i rossi
|
||
spariscono. Poi <strong>Salva piano</strong>.
|
||
</div>
|
||
<div class="kv compact">
|
||
<label>ROI <span id="wpRoiInfo" class="dim">— · clicca 2 angoli sull'RGB</span></label>
|
||
<label>offset (mm) <input type="number" id="wpOffset" value="5.0" step="0.5" min="-200" max="200" /></label>
|
||
</div>
|
||
<div class="actions">
|
||
<button id="wpFitBtn" class="primary" disabled>Calcola piano (preview)</button>
|
||
<button id="wpSaveBtn" disabled>Salva piano</button>
|
||
<button id="wpClearBtn" class="danger" disabled>Rimuovi piano salvato</button>
|
||
</div>
|
||
</div>
|
||
|
||
<h2>Stato</h2>
|
||
<div id="wpStateBlock" class="ctrl-block">
|
||
<div class="dim">nessun piano</div>
|
||
</div>
|
||
|
||
<h2>Dettagli piano</h2>
|
||
<pre id="wpDetails" class="log small">—</pre>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ============================== TROVA UTENSILE ============================== -->
|
||
<!-- ============================== CERCA MODELLO ============================== -->
|
||
<section class="panel" id="tab-grasp">
|
||
<div class="grid-2">
|
||
<div>
|
||
<h2>RGB + detection — click per misurare</h2>
|
||
<div class="canvas-wrap">
|
||
<canvas id="graspCanvas" width="640" height="480"></canvas>
|
||
</div>
|
||
<div class="measure-strip" id="measureInfo">click sull'immagine per la distanza</div>
|
||
<div class="kv">
|
||
<label>Nuvola di <select id="graspObjectPick"><option value="">(top overall)</option></select></label>
|
||
<label>Confidenza <input type="number" id="graspConf" value="0.5" step="0.05" min="0" max="1" /></label>
|
||
</div>
|
||
<div id="graspLegend" class="grasp-legend"></div>
|
||
<div class="actions">
|
||
<button id="detectBtn">Rileva oggetto</button>
|
||
<button id="loopBtn">▶ Loop riconoscimento</button>
|
||
<label class="inline" style="margin-left:8px"><input type="number" id="loopRateMs" value="500" min="100" step="50" style="width:80px"> ms</label>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<h2>Fit modello 3D</h2>
|
||
<div class="kv">
|
||
<label>Modello
|
||
<select id="findModelPick"><option value="">— carico ricette… —</option></select>
|
||
</label>
|
||
<button id="findRecipesRefreshBtn" class="ghost" title="Ricarica ricette">↻</button>
|
||
</div>
|
||
<div class="actions">
|
||
<button id="findBtn" class="primary" disabled>Trova</button>
|
||
</div>
|
||
|
||
<div id="findStatus" class="status-line">—</div>
|
||
|
||
<div class="canvas-wrap">
|
||
<canvas id="findResultCanvas" width="640" height="480"></canvas>
|
||
</div>
|
||
|
||
<div class="kv">
|
||
<label class="inline"><input type="checkbox" id="findShowModel" checked> Punti modello (blu)</label>
|
||
<label class="inline"><input type="checkbox" id="findShowAccepted"> Punti accettati (verdi)</label>
|
||
<label class="inline"><input type="checkbox" id="findShowExcluded"> Punti esclusi (rossi)</label>
|
||
<label class="inline"><input type="checkbox" id="findShowSam21"> Punti SAM21 (gialli)</label>
|
||
<label class="inline"><input type="checkbox" id="findShowPlane"> Piano (viola)</label>
|
||
</div>
|
||
|
||
<pre id="findDetails" class="log small">—</pre>
|
||
|
||
<!-- DOM hooks dormienti consumati da app.js residuo. Tenuti per evitare null deref. -->
|
||
<div style="display:none">
|
||
<span id="statsWindow">5</span>
|
||
<table id="statsTable"><tbody>
|
||
<tr><td>x</td><td>—</td><td>—</td><td>—</td><td>—</td><td>—</td></tr>
|
||
<tr><td>y</td><td>—</td><td>—</td><td>—</td><td>—</td><td>—</td></tr>
|
||
<tr><td>z</td><td>—</td><td>—</td><td>—</td><td>—</td><td>—</td></tr>
|
||
</tbody></table>
|
||
<div id="statsMeta"></div>
|
||
<pre id="graspOut"></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ============================== VISTA 3D ============================== -->
|
||
<section class="panel" id="tab-view3d">
|
||
<div class="grid-2">
|
||
<div>
|
||
<h2>Scena 3D — frame base robot</h2>
|
||
<div class="canvas-wrap">
|
||
<canvas id="view3dCanvas" width="720" height="540"></canvas>
|
||
</div>
|
||
<div class="marker-info" id="view3dHint">drag = ruota · wheel = zoom · doppio click = reset vista · shift+drag = pan</div>
|
||
<div class="marker-info" id="view3dStatusLine">—</div>
|
||
</div>
|
||
<div>
|
||
<h2>Dati</h2>
|
||
<div class="kv">
|
||
<label>Classe (filtro) <input type="text" id="view3dClass" placeholder="(tutte)" /></label>
|
||
<label>Confidenza <input type="number" id="view3dConf" value="0.5" step="0.05" min="0" max="1" /></label>
|
||
<label class="inline"><input type="checkbox" id="view3dShowCloud" checked /> Punti oggetto</label>
|
||
<label class="inline"><input type="checkbox" id="view3dShowGrid" checked /> Griglia tavolo</label>
|
||
<label class="inline"><input type="checkbox" id="view3dShowPlan" checked /> Pianifica grasp</label>
|
||
<label class="inline"><input type="checkbox" id="view3dAutoFit" checked /> Auto-fit vista</label>
|
||
</div>
|
||
<div class="actions">
|
||
<button id="view3dRefreshBtn" class="primary">Aggiorna scena</button>
|
||
<button id="view3dLoopBtn">▶ Loop</button>
|
||
<label class="inline" style="margin-left:8px"><input type="number" id="view3dRateMs" value="800" min="200" step="50" style="width:80px"> ms</label>
|
||
<button id="view3dResetBtn">Reset vista</button>
|
||
</div>
|
||
<div class="legend">
|
||
<div class="legend-row"><span class="dot" style="background:#e74c3c"></span> X (rosso) — base</div>
|
||
<div class="legend-row"><span class="dot" style="background:#3ecf8e"></span> Y (verde) — base</div>
|
||
<div class="legend-row"><span class="dot" style="background:#4da3ff"></span> Z (blu) — base</div>
|
||
<div class="legend-row"><span class="dot" style="background:#f0ad4e"></span> Polso destro</div>
|
||
<div class="legend-row"><span class="dot" style="background:#9b59b6"></span> Polso sinistro</div>
|
||
<div class="legend-row"><span class="dot" style="background:#aaaaaa"></span> Punti oggetto</div>
|
||
<div class="legend-row"><span class="dot" style="background:#ffd54f"></span> Centroide oggetto</div>
|
||
<div class="legend-row"><span class="dot" style="background:#ff5e7e"></span> Punto di presa</div>
|
||
<div class="legend-row"><span class="dot" style="background:#ffffff;border:1px solid #888"></span> Pre-grasp + traiettoria</div>
|
||
</div>
|
||
<h3>Stato</h3>
|
||
<pre id="view3dOut" class="log">(nessun dato — premi "Aggiorna scena")</pre>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ============================== BRACCIO DX ============================== -->
|
||
<section class="panel" id="tab-right-arm">
|
||
<div id="raStatusBanner" class="ra-banner ra-banner-warn" hidden>
|
||
<span id="raStatusLabel">Braccio DX non attivo</span>
|
||
<button id="raActivateBtn" type="button">Attiva G1 right</button>
|
||
</div>
|
||
|
||
<div class="ra-grid">
|
||
<!-- FRONTE -->
|
||
<div class="ra-card">
|
||
<h3>Fronte</h3>
|
||
<div class="ra-imgwrap">
|
||
<img id="raFrontImg" alt="front camera" />
|
||
<canvas id="raFrontCanvas"></canvas>
|
||
</div>
|
||
<div class="ra-row">
|
||
<label>Classe <select id="raFrontClass"></select></label>
|
||
<label>Conf <input id="raFrontConf" type="number" step="0.05" min="0" max="1" value="0.4" /></label>
|
||
</div>
|
||
<div class="ra-row">
|
||
<button id="raFrontDetectBtn" type="button">Cerca su fronte</button>
|
||
<label><input id="raFrontAutoRefresh" type="checkbox" checked /> auto-refresh 1s</label>
|
||
</div>
|
||
<div id="raFrontResult" class="ra-result dim">— nessuna detection</div>
|
||
</div>
|
||
|
||
<!-- POLSO -->
|
||
<div class="ra-card">
|
||
<h3>Polso (Nuwa)</h3>
|
||
<div class="ra-imgwrap">
|
||
<img id="raWristImg" alt="wrist camera" />
|
||
<canvas id="raWristCanvas"></canvas>
|
||
</div>
|
||
<div class="ra-row">
|
||
<label>Classe <select id="raWristClass"></select></label>
|
||
<label>Conf <input id="raWristConf" type="number" step="0.05" min="0" max="1" value="0.4" /></label>
|
||
</div>
|
||
<div class="ra-row">
|
||
<button id="raWristDetectBtn" type="button">Cerca su polso</button>
|
||
<label><input id="raWristAutoRefresh" type="checkbox" checked /> auto-refresh 1s</label>
|
||
</div>
|
||
<div id="raWristResult" class="ra-result dim">— nessuna detection</div>
|
||
</div>
|
||
|
||
<!-- POSA POLSO -->
|
||
<div class="ra-card">
|
||
<h3>Posa polso DX</h3>
|
||
<table class="ra-pose-table" id="raPoseTable">
|
||
<tr><th>X</th><td id="raPoseX">—</td><td>m</td></tr>
|
||
<tr><th>Y</th><td id="raPoseY">—</td><td>m</td></tr>
|
||
<tr><th>Z</th><td id="raPoseZ">—</td><td>m</td></tr>
|
||
<tr><th>Roll</th><td id="raPoseR">—</td><td>°</td></tr>
|
||
<tr><th>Pitch</th><td id="raPoseP">—</td><td>°</td></tr>
|
||
<tr><th>Yaw</th><td id="raPoseW">—</td><td>°</td></tr>
|
||
</table>
|
||
<button id="raPoseRefreshBtn" type="button">Refresh</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- OFFSET + MOVE -->
|
||
<div class="ra-card ra-wide">
|
||
<h3>Offset presa rispetto a camera (mm)</h3>
|
||
<div class="ra-row">
|
||
<label>X <input id="raOffX" type="number" step="1" value="0" /></label>
|
||
<label>Y <input id="raOffY" type="number" step="1" value="0" /></label>
|
||
<label>Z <input id="raOffZ" type="number" step="1" value="100" /></label>
|
||
<span class="ra-sep">|</span>
|
||
<label>Sign X <input id="raSgnX" type="number" step="2" min="-1" max="1" value="1" /></label>
|
||
<label>Sign Y <input id="raSgnY" type="number" step="2" min="-1" max="1" value="-1" /></label>
|
||
<label>Sign Z <input id="raSgnZ" type="number" step="2" min="-1" max="1" value="-1" /></label>
|
||
</div>
|
||
<div class="ra-row">
|
||
<button id="raSaveOffsetBtn" type="button">Salva offset</button>
|
||
<label><input id="raMoveDryRun" type="checkbox" checked /> dry-run</label>
|
||
<button id="raMoveBtn" type="button" class="ra-danger">Muovi verso pezzo</button>
|
||
</div>
|
||
<div id="raMoveResult" class="ra-result dim">— nessun move</div>
|
||
</div>
|
||
|
||
<!-- HOME + CUSTOM -->
|
||
<div class="ra-row ra-wide-grid">
|
||
<div class="ra-card">
|
||
<h3>Posa Home (preset)</h3>
|
||
<div class="ra-pose-form">
|
||
<label>X <input id="raHomeX" type="number" step="0.01" /></label>
|
||
<label>Y <input id="raHomeY" type="number" step="0.01" /></label>
|
||
<label>Z <input id="raHomeZ" type="number" step="0.01" /></label>
|
||
<label>Roll <input id="raHomeR" type="number" step="1" /></label>
|
||
<label>Pitch <input id="raHomeP" type="number" step="1" /></label>
|
||
<label>Yaw <input id="raHomeW" type="number" step="1" /></label>
|
||
</div>
|
||
<button id="raHomeBtn" type="button">Vai a Home</button>
|
||
</div>
|
||
|
||
<div class="ra-card">
|
||
<h3>Posa Custom (editabile)</h3>
|
||
<div class="ra-pose-form">
|
||
<label>X <input id="raCustX" type="number" step="0.01" /></label>
|
||
<label>Y <input id="raCustY" type="number" step="0.01" /></label>
|
||
<label>Z <input id="raCustZ" type="number" step="0.01" /></label>
|
||
<label>Roll <input id="raCustR" type="number" step="1" /></label>
|
||
<label>Pitch <input id="raCustP" type="number" step="1" /></label>
|
||
<label>Yaw <input id="raCustW" type="number" step="1" /></label>
|
||
</div>
|
||
<div class="ra-row">
|
||
<button id="raCustomGoBtn" type="button">Vai a Custom</button>
|
||
<button id="raCustomSaveBtn" type="button">Salva custom</button>
|
||
<button id="raCustomUseCurrentBtn" type="button">Usa posa corrente</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ============================== EE ============================== -->
|
||
<section class="panel" id="tab-ee">
|
||
<div class="grid-2">
|
||
<div>
|
||
<h2>Polso corrente</h2>
|
||
<pre id="wristCurrent" class="log">—</pre>
|
||
<button id="refreshWristBtn">Aggiorna pose</button>
|
||
|
||
<h3>Posa assoluta</h3>
|
||
<div class="kv">
|
||
<label>Side <select id="eeSide"><option value="right">right</option><option value="left">left</option></select></label>
|
||
<label>Px <input type="number" id="absPx" step="0.01" value="0.30"></label>
|
||
<label>Py <input type="number" id="absPy" step="0.01" value="0.10"></label>
|
||
<label>Pz <input type="number" id="absPz" step="0.01" value="0.20"></label>
|
||
<label>Qx <input type="number" id="absQx" step="0.01" value="0"></label>
|
||
<label>Qy <input type="number" id="absQy" step="0.01" value="0"></label>
|
||
<label>Qz <input type="number" id="absQz" step="0.01" value="0"></label>
|
||
<label>Qw <input type="number" id="absQw" step="0.01" value="1"></label>
|
||
<label class="inline"><input type="checkbox" id="eeDryRun" /> Dry-run</label>
|
||
</div>
|
||
<button id="absSendBtn" class="primary">Invia assoluta</button>
|
||
|
||
<h3>Delta relativo</h3>
|
||
<div class="kv">
|
||
<label>ΔX <input type="number" id="relDx" step="0.01" value="0"></label>
|
||
<label>ΔY <input type="number" id="relDy" step="0.01" value="0"></label>
|
||
<label>ΔZ <input type="number" id="relDz" step="0.01" value="0.05"></label>
|
||
</div>
|
||
<button id="relSendBtn" class="primary">Invia relativa</button>
|
||
</div>
|
||
|
||
<div>
|
||
<h2>Mano + sicurezza</h2>
|
||
<div class="actions">
|
||
<button id="handOpenBtn">RIGHT_OPEN</button>
|
||
<button id="handCloseBtn">RIGHT_CLOSE</button>
|
||
<button id="handOpenLBtn">LEFT_OPEN</button>
|
||
<button id="handCloseLBtn">LEFT_CLOSE</button>
|
||
</div>
|
||
<div class="actions">
|
||
<button id="estopBtn" class="danger">EMERGENCY STOP</button>
|
||
<button id="unlockBtn">Unlock</button>
|
||
</div>
|
||
<h3>Ultimo esito</h3>
|
||
<pre id="eeLog" class="log">—</pre>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
</main>
|
||
<div id="toastHost" class="toast-host" aria-live="polite"></div>
|
||
<script type="importmap">
|
||
{
|
||
"imports": {
|
||
"three": "/static/vendor/three/build/three.module.js",
|
||
"three/addons/": "/static/vendor/three/addons/"
|
||
}
|
||
}
|
||
</script>
|
||
<script src="/static/app.js"></script>
|
||
<script type="module" src="/static/work_plane.js"></script>
|
||
<script type="module" src="/static/right_arm.js"></script>
|
||
</body>
|
||
</html>
|