Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@ comfortable immersive 3D reconstruction of its surroundings.

Ito is being reset around the v1 design in `docs/v1.md`. Protocol seams are
documented in `docs/protocol.md`, and architectural decisions are recorded in
`docs/adr/`.
`docs/adr/`. Local Docker Compose operation is documented in
`docs/local-v1.md`, and the current v1 acceptance record is in
`docs/acceptance-v1.md`.

The main source directories are:

- `server/`: Ito Server code.
- `server/processors`: 3D reconstruction algorithms, applied.
- `client/`: WebXR Pilot Client code.
- `drivers/`: robot-side drivers and robot reference material.
- `docs/`: design notes, protocol notes, and architectural decisions.
- `docs/`: design notes, protocol notes, and architectural decisions.
39 changes: 37 additions & 2 deletions client/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,40 @@
The Pilot Client is the WebXR application used by the pilot to perceive through
and control a robot.

This directory is currently a placeholder while Ito is reset around the v1
design in `../docs/v1.md`.
The v1 client is a static, plain-JavaScript A-Frame/WebXR application. The
non-VR page only exposes the browser-required Enter VR launch action; catalog,
acquisition, settings, session state, and session end controls are rendered in
VR.

## Run locally

From this directory:

```sh
python -m http.server 8080
```

Then open `http://localhost:8080/`. The client defaults to the Ito Server
control WebSocket at `ws://<page-host>:8765` and stores runtime settings in
browser Local Storage under `ito.pilotClient.settings.v1`.

## Tests

The client uses Node's built-in test runner and has no npm dependencies.

```sh
npm test
```

## Implementation notes

- WebSocket control-plane messages are MessagePack-encoded Ito envelopes.
- Pilot-facing text is loaded from `resources/en/default.json` and resolved by
resource key before falling back to driver/server free text.
- The Splat Scene is client-owned. `src/splat-scene.js` includes the Spark.JS
adapter seam and v1 Splat Batch binary header parser. Exact Spark insertion
performance still needs Pico 4 validation.
- Pilot Input Snapshots are generated at the configured rate with headset yaw
relative to session start plus current controller state. `src/webrtc.js`
creates non-trickle WebRTC offers for pilot-input and Splat Batch data
channels over the WebSocket control plane.
43 changes: 43 additions & 0 deletions client/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Ito Pilot</title>
<link rel="stylesheet" href="./styles.css" />
<script src="https://aframe.io/releases/1.5.0/aframe.min.js"></script>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Serve A-Frame locally for offline robot networks

The Docker/nginx pilot client serves the repo's static files, but this page still loads A-Frame from an external CDN. In the trusted/private or offline robot-network deployments described for local and physical testing, that leaves AFRAME undefined before main.js runs, so the Enter VR launch surface and all in-VR UI never initialize.

Useful? React with 👍 / 👎.

<script type="module" src="./src/main.js"></script>
</head>
<body>
<main id="launch">
<h1>Ito Pilot</h1>
<button id="enter-vr" type="button">Enter VR</button>
<p id="launch-status" role="status"></p>
</main>

<a-scene
embedded
renderer="antialias: true; colorManagement: true"
vr-mode-ui="enabled: false"
ito-xr-frame-events>
<a-assets></a-assets>
<a-entity id="rig" position="0 0 0">
<a-camera id="camera" position="0 1.6 0" look-controls="enabled: true"></a-camera>
<a-entity
id="left-controller"
laser-controls="hand: left"
raycaster="objects: .ito-clickable; far: 6"
line="color: #7ee2d3; opacity: 0.78"></a-entity>
<a-entity
id="right-controller"
laser-controls="hand: right"
raycaster="objects: .ito-clickable; far: 6"
line="color: #7ee2d3; opacity: 0.78"></a-entity>
</a-entity>
<a-entity id="ito-splat-root" ito-spark-scene></a-entity>
<a-entity id="ito-ui-root"></a-entity>
<a-sky color="#071017"></a-sky>
<a-light type="ambient" color="#ffffff" intensity="0.65"></a-light>
</a-scene>
</body>
</html>
9 changes: 9 additions & 0 deletions client/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "ito-pilot-client",
"version": "0.1.0",
"private": true,
"type": "module",
"scripts": {
"test": "node --test tests/*.test.js"
}
}
88 changes: 88 additions & 0 deletions client/resources/en/default.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
{
"app": {
"title": "Ito Pilot",
"enterVr": "Enter VR",
"webxrUnavailable": "Immersive VR is not available in this browser.",
"loading": "Loading..."
},
"catalog": {
"title": "Robot Catalog",
"refresh": "Refresh",
"empty": "No robots are reporting yet.",
"acquire": "Pilot",
"unavailable": "Unavailable"
},
"connection": {
"connecting": "Connecting...",
"connected": "Connected",
"failed": "Connection failed"
},
"session": {
"connecting": "Connecting to {{name}}...",
"active": "Piloting {{name}}",
"ending": "Ending session...",
"ended": "Session ended",
"returnToCatalog": "Return to Catalog",
"visualPaused": "Visual feed lost. Robot controls paused.",
"menu": "Menu",
"resume": "Resume",
"end": "End Session",
"settings": "Settings"
},
"settings": {
"title": "Settings",
"serverUrl": "Server",
"visualFreshnessTimeoutMs": "Visual timeout",
"pilotInputRateHz": "Input rate",
"splatBudget": "Splat budget",
"splatLifetimeMs": "Splat lifetime",
"save": "Save",
"reset": "Reset"
},
"enum": {
"robotType": {
"Mecha": "Mecha",
"Android Robot": "Android Robot",
"Droid": "Droid",
"Drone": "Drone",
"Car": "Car",
"Plane": "Plane"
},
"robotStatus": {
"Available": "Available",
"Occupied": "Occupied",
"Unavailable": "Unavailable"
}
},
"reason": {
"unknown": "Unknown reason.",
"request": {
"timeout": "The request timed out."
},
"robot": {
"unavailable": "The robot is unavailable.",
"driver_disconnected": "The robot driver disconnected.",
"driver_status_timeout": "The robot stopped reporting status."
},
"session": {
"acquire": {
"robot_unavailable": "That robot is not available."
},
"ended": {
"requested": "The session was ended.",
"pilot_requested": "The pilot ended the session.",
"endpoint_disappeared": "A required endpoint disconnected.",
"reconstruction_failed": "Reconstruction failed."
},
"resume_unavailable": "The previous session can no longer be resumed."
},
"connection": {
"hello_required": "The server rejected the connection handshake.",
"invalid_role": "The server rejected the client role."
},
"protocol": {
"invalid_message": "The server rejected an invalid protocol message.",
"version_mismatch": "The Ito protocol versions do not match."
}
}
}
Loading
Loading