# Join a far-room leaf and heal its path

Observe a leaf join, deliver an injected temperature reading, lose its preferred relay and recover through the other relay.

## Run on Linux x86-64 with Docker and X11

The image, Java, compiler and simulator ran on this host under Apptainer.
Docker is unavailable here: the laptop Docker invocation is syntax-reviewed,
not claimed as laptop-tested. Windows, macOS and ARM display setup is outside
this recipe. No paid service or account is required.

Download `lab-files.tar.gz` from this lab page. In a terminal:

```bash
mkdir cooja-lab
cd cooja-lab
tar -xzf ~/Downloads/lab-files.tar.gz
git clone --depth 1 --branch release/v4.9 https://github.com/contiki-ng/contiki-ng.git contiki-ng
test "$(git -C contiki-ng rev-parse HEAD)" = c20b12cd2db707d6c07918f8b26cfeb52cf298ca
git -C contiki-ng submodule update --init --depth 1 tools/cooja
test "$(git -C contiki-ng/tools/cooja rev-parse HEAD)" = 33d41ae9f8acd6075fb903b0bcd5b4122bad8d8f
mkdir -p jdk17 home/contiki-ng
curl -fL 'https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.20.1%2B1/OpenJDK17U-jdk_x64_linux_hotspot_17.0.20.1_1.tar.gz' -o jdk17.tar.gz
echo '3808d1d15e3ec6bd5b84057fb5d84c33d8a1536a258146bcea2e603fc726e08e  jdk17.tar.gz' | sha256sum -c -
tar -xzf jdk17.tar.gz --strip-components=1 -C jdk17
xhost +si:localuser:$(id -un)
bash cooja-mesh-join-and-heal/docker-launch.sh
# After closing Cooja:
xhost -si:localuser:$(id -un)
```

The launcher binds the packet at `/lab`. Cooja loads `/lab/cooja-mesh-join-and-heal.csc`
and compiles the included upstream example files (any small scenario edits are listed in UPSTREAM.md). The project header enables
logging and any explicitly documented parameter changes. The supplied Makefiles
expect the recipe's `/home/user/contiki-ng` mount. The first Gradle build requires
Internet. To use your own native Java 17 installation, set CONTIKI in the supplied
Makefiles to your checkout and open the CSC with that release's Cooja.

The script starts automatically and stops at `TEST OK` or `TEST FAILED`. The GUI
stays open. Use **Mote output → Filter** to show the rows named in the steps;
clear the filter to inspect other messages. **File → Open simulation → Browse...**
loads another CSC. Reopen the original CSC for a fresh seed-1 run.
Times are simulated times, not wall-clock times. `expected-output.txt` is the
unaltered headless serial/test log, including actual measurements and the test result.

For headless Docker execution, replace `--gui --autostart` in the launcher with
`--no-gui --logdir=/lab/run-output` and remove the display requirement/mount.
Cooja writes `COOJA.testlog` under that writable output directory.

## The four-panel story and simulation boundary

The host chapter embeds the original four story panels; they also remain in the
[Zigbee/Thread module introduction](/zigbee-thread/index.html). Here the far-room
battery-role leaf is mote 5, powered relays are 2/3, and the collection root is 1.
It starts outside radio range and moves to (0,55) at 30.549 s. RPL route joining
is real Contiki-NG code, but this is not a Zigbee network or OpenThread.
The leaf-only role does not itself model radio sleep or a battery. The separate
mesh-exercises packet measures a TSCH duty-cycle proxy.

Seed 1; 240 s; UDGM 50/100 m, TX/RX success 1. Root=(0,0),
relay 2=(-30,25), relay 3=(30,25), leaf initially=(300,300).
GUI pauses at 110.549 s; click Start/Pause once to continue. Headless execution
does not pause. At 120.549 s sim.removeMote removes the actual preferred relay.
Reopen the CSC to reset all motes and positions before a repeat.

The upstream rpl-udp client is minimally adapted only for the required useful
reading: a 64-byte buffer and the snprintf format `temperature_c=21.5 seq=N`.
The injected temperature is in sensor/project-conf.h. This is a deterministic
test value, not a physical measurement or calibration. The server is unchanged.
UPSTREAM.md records the source commitment and hashes. Do not imply every C file
is unchanged; the sensor adaptation is explicit.

| Event | Simulation time |
|---|---|
| Leaf enters range | 30.549000 s |
| First parent, relay 2 | 41.934296 s |
| First useful reading at sink | 52.212400 s |
| Selected relay 2 switched off | 120.549000 s |
| New parent, relay 3 | 134.876272 s |
| First recovered reading | 144.581400 s |
| First recovered reply | 144.645656 s |
| Root ping after repair | 114 ms RTT, TTL 63 |

Keep the exact fault time; do not substitute the intended 120-second scheduling
value for the actual event time. The script's whole-run total includes the two
router clients' stock hello traffic as well as the 17 useful temperature readings.
Fifty total sink deliveries from 52 attempts is not 50 temperature readings.

## Preserve the Zigbee lifecycle portfolio on paper

Write one bounded claim per row: setup, joining custody, path, application
result, bridge boundary, recovery, unsupported behavior, decision and retest.
The simulated rows are RPL route formation, temperature payload delivery and
relay-loss recovery. Fill the following Zigbee rows as **not simulated**:

* Commissioning: who opens permit-join, its duration, Trust Center policy,
  intended role, install-code/key custody, child/device-list evidence and reset.
  A routed RPL leaf does not prove any of these security steps.
* Binding: source endpoint and On/Off client cluster 0x0006, destination endpoint
  or group and server cluster, plus local control while the hub application is
  unavailable. A UDP reply does not prove a binding-table entry or light control.
* Bridge: raw device-side report, mapped controller field, units, timestamps,
  caching/filtering and behavior when the bridge fails. No bridge runs here.
* OTA: Upgrade cluster 0x0019, Image Notify, Query Next Image, block transfer,
  validation, activation and Upgrade End. Define interruption, resume/rollback,
  normal-traffic protection and post-update verification before approving it.

For the observed path, accept only the recorded positions, seed, traffic and
firmware. Retest after moving a relay, changing firmware or traffic, or adding
wall/interference conditions. A different alternate path may not recover.
For joining authorization, local control, bridge behavior or OTA readiness,
hold the claim until its own real implementation traces exist. Preserve failures
and distinguish stale cached state from a fresh report after reset.


## Tested version

Contiki-NG release/v4.9 c20b12cd, Cooja 4.8 (2022071901), Temurin 17.0.20.1, image sha256:b14d5afb0a2a852d9e72bbed607ead03f6ccc5f4d4dee44f0799110be2f4b609
