Add bg park postitions. Closes #18
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"hasAlphabet": true,
|
"hasAlphabet": true,
|
||||||
|
"parkPosition": { "x": 0, "y": 80 },
|
||||||
"bounds": { "xMin": -150, "xMax": 150, "yMin": -30, "yMax": 145 },
|
"bounds": { "xMin": -150, "xMax": 150, "yMin": -30, "yMax": 145 },
|
||||||
"spots": [
|
"spots": [
|
||||||
{ "id": "YES", "label": "YES", "x": -70, "y": 90 },
|
{ "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
|
```json
|
||||||
{
|
{
|
||||||
"hasAlphabet": true,
|
"hasAlphabet": true,
|
||||||
|
"parkPosition": { "x": 0, "y": 80 },
|
||||||
"bounds": {
|
"bounds": {
|
||||||
"xMin": -150,
|
"xMin": -150,
|
||||||
"xMax": 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.
|
- **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!
|
- **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.
|
- **spots**: Array of target objects.
|
||||||
- **id**: Unique identifier for the dropdown list.
|
- **id**: Unique identifier for the dropdown list.
|
||||||
- **label**: Human-readable text shown on the UI.
|
- **label**: Human-readable text shown on the UI.
|
||||||
- **x**: The X coordinate in physical mm (`-150` to `+150`).
|
- **x**: The X coordinate in physical mm (`-150` to `+150`).
|
||||||
- **y**: The Y coordinate in physical mm (`-30` to `+145`).
|
- **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'}">
|
<div id="homing-banner" class="homing-banner ${isHomed ? 'homed' : 'not-homed'}">
|
||||||
${isHomed ? '✓ HOMED' : '⚠ NOT HOMED! Movement Locked.'}
|
${isHomed ? '✓ HOMED' : '⚠ NOT HOMED! Movement Locked.'}
|
||||||
</div>
|
</div>
|
||||||
<button id="btn-force-home" class="btn btn-danger btn-full" style="padding: 10px; font-weight:bold;">
|
<div style="display:flex; gap: 8px;">
|
||||||
${isHomed ? 'FORCE HOME ALL' : 'HOME ALL MOTORS'}
|
<button id="btn-force-home" class="btn btn-danger" style="flex:1; padding: 10px; font-weight:bold;">
|
||||||
</button>
|
${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>
|
</div>
|
||||||
|
|
||||||
<!-- Controls Card -->
|
<!-- Controls Card -->
|
||||||
@@ -191,12 +196,21 @@ async function loadSpots() {
|
|||||||
try {
|
try {
|
||||||
const res = await fetch(`web/assets/backgrounds/${currentBg}/spots.json?v=${Date.now()}`);
|
const res = await fetch(`web/assets/backgrounds/${currentBg}/spots.json?v=${Date.now()}`);
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
|
let parsedSpots = [];
|
||||||
|
let parkPosition = { x: 0, y: 0 };
|
||||||
|
|
||||||
if (Array.isArray(data)) {
|
if (Array.isArray(data)) {
|
||||||
spots = data;
|
parsedSpots = data;
|
||||||
} else {
|
} else {
|
||||||
spots = data.spots || [];
|
parsedSpots = data.spots || [];
|
||||||
if (data.bounds) bounds = data.bounds;
|
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()}`;
|
document.getElementById('bg-image').src = `web/assets/backgrounds/${currentBg}/bg.svg?v=${Date.now()}`;
|
||||||
renderSpots();
|
renderSpots();
|
||||||
} catch(e) {
|
} 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 ────────────────────────────────────────
|
// ── List Selection ────────────────────────────────────────
|
||||||
const listEl = document.getElementById('spot-list');
|
const listEl = document.getElementById('spot-list');
|
||||||
const goBtn = document.getElementById('btn-go');
|
const goBtn = document.getElementById('btn-go');
|
||||||
|
|||||||
@@ -112,9 +112,14 @@ function buildHTML() {
|
|||||||
<div id="homing-banner" class="homing-banner ${isHomed ? 'homed' : 'not-homed'}">
|
<div id="homing-banner" class="homing-banner ${isHomed ? 'homed' : 'not-homed'}">
|
||||||
${isHomed ? '✓ HOMED' : '⚠ NOT HOMED! Movement Locked.'}
|
${isHomed ? '✓ HOMED' : '⚠ NOT HOMED! Movement Locked.'}
|
||||||
</div>
|
</div>
|
||||||
<button id="btn-force-home" class="btn btn-danger btn-full" style="padding: 10px; font-weight:bold;">
|
<div style="display:flex; gap: 8px;">
|
||||||
${isHomed ? 'FORCE HOME ALL' : 'HOME ALL MOTORS'}
|
<button id="btn-force-home" class="btn btn-danger" style="flex:1; padding: 10px; font-weight:bold;">
|
||||||
</button>
|
${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>
|
</div>
|
||||||
|
|
||||||
<!-- Controls Card -->
|
<!-- Controls Card -->
|
||||||
@@ -245,12 +250,21 @@ async function loadSpots() {
|
|||||||
UI.log(`Background '${currentBg}' does not support an alphabet.`, 'warn');
|
UI.log(`Background '${currentBg}' does not support an alphabet.`, 'warn');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let parsedSpots = [];
|
||||||
|
let parkPosition = { x: 0, y: 0 };
|
||||||
|
|
||||||
if (Array.isArray(data)) {
|
if (Array.isArray(data)) {
|
||||||
spots = data;
|
parsedSpots = data;
|
||||||
} else {
|
} else {
|
||||||
spots = data.spots || [];
|
parsedSpots = data.spots || [];
|
||||||
if (data.bounds) bounds = data.bounds;
|
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()}`;
|
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 ────────────────────────────────────────
|
// ── Spelling Logic ────────────────────────────────────────
|
||||||
const btnSpell = document.getElementById('btn-spell');
|
const btnSpell = document.getElementById('btn-spell');
|
||||||
const btnStop = document.getElementById('btn-stop');
|
const btnStop = document.getElementById('btn-stop');
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import UI from '../ui.js';
|
|||||||
import IK from '../kinematics.js';
|
import IK from '../kinematics.js';
|
||||||
import Settings from '../settings.js';
|
import Settings from '../settings.js';
|
||||||
import SequenceRunner from '../sequence-runner.js';
|
import SequenceRunner from '../sequence-runner.js';
|
||||||
|
import Motion from '../motion.js';
|
||||||
|
|
||||||
let eventCleanup = [];
|
let eventCleanup = [];
|
||||||
let isHomed = false;
|
let isHomed = false;
|
||||||
@@ -244,9 +245,14 @@ function buildHTML() {
|
|||||||
<div id="homing-banner" class="homing-banner ${isHomed ? 'homed' : 'not-homed'}">
|
<div id="homing-banner" class="homing-banner ${isHomed ? 'homed' : 'not-homed'}">
|
||||||
${isHomed ? '✓ HOMED' : '⚠ NOT HOMED! Movement Locked.'}
|
${isHomed ? '✓ HOMED' : '⚠ NOT HOMED! Movement Locked.'}
|
||||||
</div>
|
</div>
|
||||||
<button id="btn-force-home" class="btn btn-danger btn-full" style="padding: 10px; font-weight:bold;">
|
<div style="display:flex; gap: 8px;">
|
||||||
${isHomed ? 'FORCE HOME ALL' : 'HOME ALL MOTORS'}
|
<button id="btn-force-home" class="btn btn-danger" style="flex:1; padding: 10px; font-weight:bold;">
|
||||||
</button>
|
${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>
|
</div>
|
||||||
|
|
||||||
<!-- Settings Card -->
|
<!-- Settings Card -->
|
||||||
@@ -409,14 +415,23 @@ async function loadSpots() {
|
|||||||
try {
|
try {
|
||||||
const res = await fetch(`web/assets/backgrounds/${currentBg}/spots.json?v=${Date.now()}`);
|
const res = await fetch(`web/assets/backgrounds/${currentBg}/spots.json?v=${Date.now()}`);
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
|
let parsedSpots = [];
|
||||||
|
let parkPosition = { x: 0, y: 0 };
|
||||||
|
|
||||||
if (Array.isArray(data)) {
|
if (Array.isArray(data)) {
|
||||||
spots = data;
|
parsedSpots = data;
|
||||||
hasAlphabet = false;
|
hasAlphabet = false;
|
||||||
} else {
|
} else {
|
||||||
spots = data.spots || [];
|
parsedSpots = data.spots || [];
|
||||||
if (data.bounds) bounds = data.bounds;
|
if (data.bounds) bounds = data.bounds;
|
||||||
|
if (data.parkPosition) parkPosition = data.parkPosition;
|
||||||
hasAlphabet = !!data.hasAlphabet;
|
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-row').style.display = hasAlphabet ? 'grid' : 'none';
|
||||||
document.getElementById('text-input-divider').style.display = hasAlphabet ? 'block' : '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 ───────────────────────────────────
|
// ── Sync Steps from BLE ───────────────────────────────────
|
||||||
const onBLEStatus = (e) => {
|
const onBLEStatus = (e) => {
|
||||||
const msg = e.detail;
|
const msg = e.detail;
|
||||||
|
|||||||
Reference in New Issue
Block a user