# Dockerized Inspire hand service + dashboard

One self-contained image (aarch64) running `inspire_g1` + `hand_bridge` + the Flask
dashboard. Build ON the robot (it pulls `unitree_sdk2` + CycloneDDS from the host).

## Build (once, on the robot)
```bash
cd ~/DFX_inspire_service
./docker/build_image.sh          # assembles context + docker build -> inspire-hand:latest
```

## Run
```bash
./docker/run.sh                  # or: docker compose -f docker/docker-compose.yml up -d
# dashboard -> http://<robot-ip>:8088
./docker/stop.sh
```

`run.sh` first stops any host-native `inspire_g1`/`hand_bridge` — running both the
container and the host service would put **two writers on the RS-485 bus** and
corrupt the hands.

## Why these flags
- `--network host` — CycloneDDS (rt/inspire/cmd) and the arm actions (rt/arm_sdk,
  reaching the G1 motion controller on eth0) need the host network.
- `--privileged -v /dev:/dev` — the CH340 `ttyUSB*` adapters re-enumerate on the hub;
  mounting `/dev` + privileged lets the in-container dynamic detection find them.
- `IFACE=eth0` — interface for arm actions (must be eth0). Fingers work on either.

## Record / replay in the image
The dashboard's Record/Replay card runs `g1_record_replay.py` as a child process, so the
image needs the script **and** a python that can import `unitree_sdk2py`. `build_image.sh`
pulls both into the context:

```bash
RECORDER_SRC=~/Manual_Recorder \
UNITREE_SDK2PY_DIR=~/unitree_sdk2_python \
  ./docker/build_image.sh
```

Neither is required — leave them out and the image still builds, with the panel reporting
*"recorder not found"*. `start.sh` prints which case you got.

- **That layer pip-installs (`cyclonedds`, `unitree_sdk2py`), so the robot needs network at
  BUILD time.** Offline alternative: copy the robot's own python site-packages into the
  image the same way `build_image.sh` copies the C libs.
- **Takes are a host volume** — `run.sh` mounts `$HAND_DATA_DIR/DataG1` to
  `/opt/recorder/DataG1`, because `run.sh` starts with `docker rm -f` and would otherwise
  destroy every recording ever made.
- **`arm_home.jsonl` is seeded on first start**, not baked into `DataG1/` — the bind-mount
  would hide a baked copy, and without it every take skips its return-to-home. It is hidden
  from the takes list and cannot be deleted from the panel: it is a home pose, not a take.

## Notes
- Sudo/udev not needed inside the container (privileged).
- Saved poses/combos persist via `-v $HAND_DATA_DIR:/opt/hand/data` + `HAND_DATA` (set by
  `run.sh`). They used to live in the image and were wiped by every rebuild.
