Add bg park postitions. Closes #18
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"hasAlphabet": true,
|
||||
"parkPosition": { "x": 0, "y": 80 },
|
||||
"bounds": { "xMin": -150, "xMax": 150, "yMin": -30, "yMax": 145 },
|
||||
"spots": [
|
||||
{ "id": "YES", "label": "YES", "x": -70, "y": 90 },
|
||||
|
||||
@@ -46,6 +46,7 @@ This file tells the web UI where your letters/targets are located in **physical
|
||||
```json
|
||||
{
|
||||
"hasAlphabet": true,
|
||||
"parkPosition": { "x": 0, "y": 80 },
|
||||
"bounds": {
|
||||
"xMin": -150,
|
||||
"xMax": 150,
|
||||
@@ -60,9 +61,20 @@ This file tells the web UI where your letters/targets are located in **physical
|
||||
}
|
||||
```
|
||||
- **hasAlphabet**: (Optional, boolean) If `true`, this background is used for the "Input Text" feature. The letters in `spots` are used to spell out typed input.
|
||||
- **parkPosition**: (Optional, object) Defines a background-specific "Park Position" or resting spot where the arm can go when not actively in use. If omitted, defaults to `{ "x": 0, "y": 0 }`.
|
||||
- **bounds**: Defines the physical bounding box (in mm) that your background image represents. This automatically adjusts the UI's aspect ratio and coordinate mapping!
|
||||
- **spots**: Array of target objects.
|
||||
- **id**: Unique identifier for the dropdown list.
|
||||
- **label**: Human-readable text shown on the UI.
|
||||
- **x**: The X coordinate in physical mm (`-150` to `+150`).
|
||||
- **y**: The Y coordinate in physical mm (`-30` to `+145`).
|
||||
- **id**: Unique identifier for the dropdown list.
|
||||
- **label**: Human-readable text shown on the UI.
|
||||
- **x**: The X coordinate in physical mm (`-150` to `+150`).
|
||||
- **y**: The Y coordinate in physical mm (`-30` to `+145`).
|
||||
|
||||
## 5. Excluded Zones (No-Go Zones)
|
||||
|
||||
When defining spots or the `parkPosition`, you **must avoid** the central mechanism housing. The SCARA arm will reject commands that enter this region to prevent mechanical collisions.
|
||||
|
||||
The exact exclusion box (the "No-Go Zone") is:
|
||||
- **X Range:** `-30` mm to `+30` mm
|
||||
- **Y Range:** `-10` mm to `+40` mm
|
||||
|
||||
Make sure that your `parkPosition` and all clickable spots lie outside this rectangle.
|
||||
|
||||
@@ -96,9 +96,14 @@ function buildHTML() {
|
||||
<div id="homing-banner" class="homing-banner ${isHomed ? 'homed' : 'not-homed'}">
|
||||
${isHomed ? '✓ HOMED' : '⚠ NOT HOMED! Movement Locked.'}
|
||||
</div>
|
||||
<button id="btn-force-home" class="btn btn-danger btn-full" style="padding: 10px; font-weight:bold;">
|
||||
${isHomed ? 'FORCE HOME ALL' : 'HOME ALL MOTORS'}
|
||||
</button>
|
||||
<div style="display:flex; gap: 8px;">
|
||||
<button id="btn-force-home" class="btn btn-danger" style="flex:1; padding: 10px; font-weight:bold;">
|
||||
${isHomed ? 'FORCE HOME ALL' : 'HOME ALL MOTORS'}
|
||||
</button>
|
||||
<button id="btn-park" class="btn btn-secondary" style="flex:1; padding: 10px; font-weight:bold;">
|
||||
GO TO PARK
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Controls Card -->
|
||||
@@ -191,12 +196,21 @@ async function loadSpots() {
|
||||
try {
|
||||
const res = await fetch(`web/assets/backgrounds/${currentBg}/spots.json?v=${Date.now()}`);
|
||||
const data = await res.json();
|
||||
let parsedSpots = [];
|
||||
let parkPosition = { x: 0, y: 0 };
|
||||
|
||||
if (Array.isArray(data)) {
|
||||
spots = data;
|
||||
parsedSpots = data;
|
||||
} else {
|
||||
spots = data.spots || [];
|
||||
parsedSpots = data.spots || [];
|
||||
if (data.bounds) bounds = data.bounds;
|
||||
if (data.parkPosition) parkPosition = data.parkPosition;
|
||||
}
|
||||
|
||||
if (!parsedSpots.find(s => s.id === 'PARK')) {
|
||||
parsedSpots.push({ id: 'PARK', label: 'Park Position', x: parkPosition.x, y: parkPosition.y });
|
||||
}
|
||||
spots = parsedSpots;
|
||||
document.getElementById('bg-image').src = `web/assets/backgrounds/${currentBg}/bg.svg?v=${Date.now()}`;
|
||||
renderSpots();
|
||||
} catch(e) {
|
||||
@@ -335,6 +349,11 @@ export default {
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('btn-park').addEventListener('click', () => {
|
||||
const parkSpot = spots.find(s => s.id === 'PARK');
|
||||
if (parkSpot) executeMove(parkSpot);
|
||||
});
|
||||
|
||||
// ── List Selection ────────────────────────────────────────
|
||||
const listEl = document.getElementById('spot-list');
|
||||
const goBtn = document.getElementById('btn-go');
|
||||
|
||||
@@ -112,9 +112,14 @@ function buildHTML() {
|
||||
<div id="homing-banner" class="homing-banner ${isHomed ? 'homed' : 'not-homed'}">
|
||||
${isHomed ? '✓ HOMED' : '⚠ NOT HOMED! Movement Locked.'}
|
||||
</div>
|
||||
<button id="btn-force-home" class="btn btn-danger btn-full" style="padding: 10px; font-weight:bold;">
|
||||
${isHomed ? 'FORCE HOME ALL' : 'HOME ALL MOTORS'}
|
||||
</button>
|
||||
<div style="display:flex; gap: 8px;">
|
||||
<button id="btn-force-home" class="btn btn-danger" style="flex:1; padding: 10px; font-weight:bold;">
|
||||
${isHomed ? 'FORCE HOME ALL' : 'HOME ALL MOTORS'}
|
||||
</button>
|
||||
<button id="btn-park" class="btn btn-secondary" style="flex:1; padding: 10px; font-weight:bold;">
|
||||
GO TO PARK
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Controls Card -->
|
||||
@@ -245,12 +250,21 @@ async function loadSpots() {
|
||||
UI.log(`Background '${currentBg}' does not support an alphabet.`, 'warn');
|
||||
}
|
||||
|
||||
let parsedSpots = [];
|
||||
let parkPosition = { x: 0, y: 0 };
|
||||
|
||||
if (Array.isArray(data)) {
|
||||
spots = data;
|
||||
parsedSpots = data;
|
||||
} else {
|
||||
spots = data.spots || [];
|
||||
parsedSpots = data.spots || [];
|
||||
if (data.bounds) bounds = data.bounds;
|
||||
if (data.parkPosition) parkPosition = data.parkPosition;
|
||||
}
|
||||
|
||||
if (!parsedSpots.find(s => s.id === 'PARK')) {
|
||||
parsedSpots.push({ id: 'PARK', label: 'Park Position', x: parkPosition.x, y: parkPosition.y });
|
||||
}
|
||||
spots = parsedSpots;
|
||||
|
||||
document.getElementById('bg-image').src = `web/assets/backgrounds/${currentBg}/bg.svg?v=${Date.now()}`;
|
||||
|
||||
@@ -404,6 +418,11 @@ export default {
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('btn-park').addEventListener('click', () => {
|
||||
const parkSpot = spots.find(s => s.id === 'PARK');
|
||||
if (parkSpot) executeMove(parkSpot);
|
||||
});
|
||||
|
||||
// ── Spelling Logic ────────────────────────────────────────
|
||||
const btnSpell = document.getElementById('btn-spell');
|
||||
const btnStop = document.getElementById('btn-stop');
|
||||
|
||||
@@ -3,6 +3,7 @@ import UI from '../ui.js';
|
||||
import IK from '../kinematics.js';
|
||||
import Settings from '../settings.js';
|
||||
import SequenceRunner from '../sequence-runner.js';
|
||||
import Motion from '../motion.js';
|
||||
|
||||
let eventCleanup = [];
|
||||
let isHomed = false;
|
||||
@@ -244,9 +245,14 @@ function buildHTML() {
|
||||
<div id="homing-banner" class="homing-banner ${isHomed ? 'homed' : 'not-homed'}">
|
||||
${isHomed ? '✓ HOMED' : '⚠ NOT HOMED! Movement Locked.'}
|
||||
</div>
|
||||
<button id="btn-force-home" class="btn btn-danger btn-full" style="padding: 10px; font-weight:bold;">
|
||||
${isHomed ? 'FORCE HOME ALL' : 'HOME ALL MOTORS'}
|
||||
</button>
|
||||
<div style="display:flex; gap: 8px;">
|
||||
<button id="btn-force-home" class="btn btn-danger" style="flex:1; padding: 10px; font-weight:bold;">
|
||||
${isHomed ? 'FORCE HOME ALL' : 'HOME ALL MOTORS'}
|
||||
</button>
|
||||
<button id="btn-park" class="btn btn-secondary" style="flex:1; padding: 10px; font-weight:bold;">
|
||||
GO TO PARK
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Settings Card -->
|
||||
@@ -409,14 +415,23 @@ async function loadSpots() {
|
||||
try {
|
||||
const res = await fetch(`web/assets/backgrounds/${currentBg}/spots.json?v=${Date.now()}`);
|
||||
const data = await res.json();
|
||||
let parsedSpots = [];
|
||||
let parkPosition = { x: 0, y: 0 };
|
||||
|
||||
if (Array.isArray(data)) {
|
||||
spots = data;
|
||||
parsedSpots = data;
|
||||
hasAlphabet = false;
|
||||
} else {
|
||||
spots = data.spots || [];
|
||||
parsedSpots = data.spots || [];
|
||||
if (data.bounds) bounds = data.bounds;
|
||||
if (data.parkPosition) parkPosition = data.parkPosition;
|
||||
hasAlphabet = !!data.hasAlphabet;
|
||||
}
|
||||
|
||||
if (!parsedSpots.find(s => s.id === 'PARK')) {
|
||||
parsedSpots.push({ id: 'PARK', label: 'Park Position', x: parkPosition.x, y: parkPosition.y });
|
||||
}
|
||||
spots = parsedSpots;
|
||||
|
||||
document.getElementById('text-input-row').style.display = hasAlphabet ? 'grid' : 'none';
|
||||
document.getElementById('text-input-divider').style.display = hasAlphabet ? 'block' : 'none';
|
||||
@@ -723,6 +738,20 @@ export default {
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('btn-park').addEventListener('click', async () => {
|
||||
const parkSpot = spots.find(s => s.id === 'PARK');
|
||||
if (parkSpot) {
|
||||
if (!isHomed) {
|
||||
UI.log('Cannot move: System is not homed!', 'error');
|
||||
return;
|
||||
}
|
||||
const modeEl = document.getElementById('seq-mode-select');
|
||||
const mode = modeEl ? modeEl.value : 'direct';
|
||||
UI.log(`Moving to Park Position (${parkSpot.x}, ${parkSpot.y})`, 'info');
|
||||
await Motion.goto(parkSpot.x, parkSpot.y, mode);
|
||||
}
|
||||
});
|
||||
|
||||
// ── Sync Steps from BLE ───────────────────────────────────
|
||||
const onBLEStatus = (e) => {
|
||||
const msg = e.detail;
|
||||
|
||||
Reference in New Issue
Block a user