Week 12 · Coding Period

Making it upstreamable

The dashboard worked, but it was still a laptop demo held together by a hand-written service file. This week was about turning it into something that packages the way AGL packages things, ships a demo anyone can run offline, and stands up to a real maintainer review. It got that review, and it passed.

TL;DR  I wrote a Jetson Orin workspace config so the app deploys with AGL's own scripts, rebuilt the recipe on inherit flutter-app agl-app instead of a hand-rolled systemd unit, and recorded a CARLA LiDAR rosbag so the whole demo runs on the board with no simulator and no network. I published the app under Apache-2.0, build-verified the recipe on the Jetson image, and put it in front of my mentors. Rob and Jan-Simon reviewed the recipe, it passed, and I applied their two notes: keep the SRCREV pinned for Gerrit with a localdev auto-latest override, and add a real README. Next week the two patches go up to Gerrit.

1. A workspace config for the Jetson

AGL already has a way to push a Flutter app onto a target: a per-target JSON config that the workspace-automation script reads. The tree ships configs for qemu and the Raspberry Pi. I wrote the equivalent for the Jetson Orin Nano, following the same schema rather than inventing anything. It is a remote / custom-device config: it names the board over mDNS, and carries the install, run, and port-forward hooks so flutter run drives the app onto the board using AGL's existing tooling.

The one deliberate choice: the hostname stays a generic .local mDNS name, not my board's IP, because this file is meant to be shared upstream and a personal address does not belong in it.

2. The recipe, done the AGL way

My earlier recipe hand-wrote a systemd unit to launch the app. My mentor pointed out that AGL has a template for exactly this, and he was right. I rebuilt the recipe on inherit flutter-app agl-app, which pulls in the agl-app-flutter service template so I do not maintain the launch plumbing at all.

The one rule that makes it work: the app id the template launches has to match the bundle name the Flutter build installs. So AGL_APP_ID and PUBSPEC_APPNAME are both agl_hmi_demo. I cross-checked the whole shape against the in-tree flutter-ros-demo recipe, which is the closest existing example of a Flutter plus ROS 2 demo, so I was following the house style instead of guessing.

# the two lines that replace a hand-written service file
inherit flutter-app agl-app

PUBSPEC_APPNAME = "agl_hmi_demo"
AGL_APP_TEMPLATE = "agl-app-flutter"
AGL_APP_ID = "agl_hmi_demo"   # must equal PUBSPEC_APPNAME

3. Shipping the demo so it runs offline

Up to now the dashboard needed a live CARLA at the other end. That is a lot to ask of anyone who just wants to see the app do something. So I recorded a short CARLA LiDAR run into a rosbag and shipped it inside the app, together with a small helper script. The recipe installs the bag under /usr/share and the helper as a command on the path.

The helper starts a local rosbridge server on port 9090 and loop-plays the bag onto /carla/lidar. The app connects to that local rosbridge exactly as it would to a remote one. No CARLA, no GPU, no network.

recorded CARLA LiDAR bag (ships inside the app) │ ▼ loop-played by the helper script local rosbridge on :9090 → /carla/lidar (sensor_msgs/PointCloud2) │ ▼ the same dashboard, now drawing points with nothing else running
# the recipe installs these two things onto the image
#   the bag:    /usr/share/agl-lidar-hmi/rosbag/carla_lidar/
#   the helper: /usr/bin/agl-lidar-hmi-demo.sh

# on the board, the whole demo is one command:
agl-lidar-hmi-demo.sh
#   1. launches rosbridge_server on :9090
#   2. loop-plays the shipped bag onto /carla/lidar
#   then start the app and it connects to ws://127.0.0.1:9090

4. Published, and build-verified

I put the app on its own public repository under the Apache-2.0 license, and cleaned the recipe down to upstream style, dropping the teaching comments and anything the build did not need. I built the recipe against the Jetson image several times over and confirmed the artifacts actually land where the recipe says: the bag and its metadata under /usr/share, and the demo helper under /usr/bin. One real bug surfaced this way, an install step that tried to copy a file into a directory it had not created yet, and it was caught by the build before it ever reached a reviewer. That is the point of building it first.

5. The first review round

With the recipe clean and building, I asked my mentors to look at it. Rob reviewed it, said it looked good and was ready to submit, and left two suggestions: use ${AUTOREV} in SRCREV so development tracks the latest commit automatically, and add a README so people can tell what the repository is. Jan-Simon added an important refinement on the first point: bare ${AUTOREV} is fine for local development, but a patch going into Gerrit should pin an exact SRCREV so the build is reproducible.

Both notes are now applied. The recipe pins a real commit, and adds a localdev override that only uses auto-latest during local development, so I keep Rob's convenience without giving up Jan-Simon's reproducibility.

# pinned for Gerrit, auto-latest only while developing locally
SRCREV = "caebfd5...b6faa"
SRCREV:localdev = "${AUTOREV}"

And the README now describes the app, the pipeline, and how to run the offline demo, replacing the empty template text that was there before.

It passed  Two maintainers reviewed the recipe and both said it was ready to submit. That is the first time a piece of this project has been signed off against the actual AGL contribution bar rather than my own judgement of it.

6. Where it stands, and what's next

Status: the dashboard is now packaged the AGL way. There is a Jetson workspace config, a recipe built on the standard app template, a demo that runs offline from a shipped rosbag, a published Apache-2.0 repository, and a recipe that two maintainers have reviewed and cleared.

Week 11 made the source end of the pipeline real. This week made the delivery end real: the app no longer lives in my build tree alone, it lives in a repository and a recipe that the project's own maintainers have looked at and approved.

Links

← Week 11
Home →