Generated Tracker Code#
Trackers whose live impl only moves a FlatBuffer between a plugin and the host via
SchemaTracker or SchemaPusher — schema-based trackers — are not hand-written. They are
declared in a TOML manifest and their C++ and Python glue is generated when you configure the build.
Adding one is a .fbs schema plus roughly ten lines of TOML; see
Schema-based trackers (manifest) for the authoring workflow.
This page records what is generated today, what is deliberately still hand-written, and what would have to change to generate the rest.
How it works#
Three inputs drive the generator:
src/core/deviceio_trackers/trackers.toml — one
[[tracker]]entry per tracker, carrying only the values that differ from the defaults.src/core/deviceio_trackers/defaults.toml — the defaults, expressed with
%placeholder%substitutions (%name%,%name_CamelCase%, and so on).src/core/codegen —
manifest.pyresolves the placeholders,templates.pyderives type and file names,templates/holds the C++ bodies as*.hpp.template/*.cpp.templateunderpull/andpush/(@KEY@substitution viatemplate_renderer.py), andgenerate_trackers.pyrenders the output.
cmake/GenerateTrackers.cmake runs the generator through execute_process at
configure time, not build time, because CMake needs the resulting source list before it can
define targets. Output lands in ${CMAKE_BINARY_DIR}/generated/trackers/ and is not checked
into git, exactly like the flatc output. Sources are wired through an explicit
generated_sources.cmake list (not by globbing that directory). Two consequences worth knowing:
Grepping
src/for a generated class such asSe3Trackerfinds the manifest entry and the generator, not a.cpp. Searching, debugging, and clangd all need a configured build tree.The manifests, every
.pyundersrc/core/codegen, and every template undertemplates/are registered asCMAKE_CONFIGURE_DEPENDS, so editing any of them re-runs configure on the next build.
The generator rewrites a file only when its content changed. Configure also passes
--prune-stale so that after renaming or removing a tracker, orphan headers cannot remain on
the generated include path and keep satisfying a stale #include.
What each entry produces#
Per manifest entry, in ${CMAKE_BINARY_DIR}/generated/trackers/:
deviceio_base/<header>_base.hpp # I<Class>Impl interface
deviceio_trackers/inc/deviceio_trackers/<header>.hpp
deviceio_trackers/<header>.cpp # the ITracker facade
live_trackers/live_<header>_impl.{hpp,cpp} # wraps SchemaTracker / SchemaPusher
replay_trackers/replay_<header>_impl.{hpp,cpp}
header defaults from name (<name>_tracker, or name when it already ends in
_tracker). Override it when the public #include stem must keep a historical path.
The shared registration points stay hand-written and #include a generated .inc fragment,
so hand-written trackers keep their own rows. The fragments under generated/trackers/inc/ cover
the live and replay factories (includes, forward declarations, try-create thunks, dispatch rows,
factory declarations and definitions), the MCAP recording traits in
src/core/mcap/cpp/inc/mcap/recording_traits.hpp, the pybind blocks in
src/core/deviceio_trackers/python/tracker_bindings.cpp, and a
_generated_tracker_exports.py that src/python/isaacteleop/deviceio_trackers/__init__.py
star-imports. Because that last one is spliced into __all__, a new manifest entry needs no
Python edit at all.
.pyi stubs need no work either: src/core/python/generate_stubs.py derives them from
the built module.
Generated today#
Manifest entry |
Class |
Notes |
|---|---|---|
|
|
One generic joint-space device (leader arm, exoskeleton, …) |
|
|
Overrides |
|
|
MCAP channels are |
|
|
Schema lives in |
|
|
|
|
|
Overrides |
Still hand-written#
Tracker |
Why it is not generated |
|---|---|
|
Real |
|
Native |
|
Genuinely schema-based by mechanism, but a vendor of an existing facade — see below |
|
|
|
Cross-process consumer: |
|
Deliberately kept as the untyped |
A quick way to tell the two groups apart: only schema-based impls mention SchemaTracker or
SchemaPusher. Of the hand-written live impls that use those helpers, each is listed above.
Future work#
Multi-endpoint reader (rejected generated shape). We briefly generated
HapticCommandReaderTracker with a multi_endpoint template (bucket samples by
HapticCommand.endpoint on one push-tensor collection). That shape was removed: it made the
codegen tree branchy for a single device, and a generic multi-endpoint reader does not fit
pull/ or push. The hand-written reader remains the supported approach; a future redesign
(split tensors/collections per endpoint, or a dedicated generator) is optional follow-up.
Vendored shape (Noitom). LiveFullBodyTrackerNoitomImpl reads
SchemaTracker<FullBodyPoseRecord, FullBodyPose>, so it is schema-based by mechanism, but it is a
second vendor of the hand-written FullBodyTracker rather than its own tracker type. Generating
it would need a shape that emits only a live impl and a vendor-keyed dispatch row — no facade,
base interface, recording traits, replay half, or pybind block, since it shares all of those with the
PICO vendor. Its collection_id and max_flatbuffer_size also arrive through
TrackerVendor::params at runtime instead of being fixed at generation time.
Schema pybind bindings. The field-by-field src/core/schema/python/*_bindings.h files are
still hand-written. They are derivable from the .bfbs reflection data flatc already emits
(--bfbs-gen-embed --reflect-names --reflect-types), so this is a viable follow-up, but it is a
separate generator with its own risks.
Manifest ``python_accessor`` (pybind method name). Generated pull trackers use
fragments/pybind_pull.template, which binds whatever string python_accessor holds (default
get_data; oglo_tactile and generic_3axis_pedal override with device-specific names).
direction = "push" uses push via defaults. Future work: remove the manifest key and
hard-code get_data on readers (or maybe pull, paired with push on producers).
Maybe rename the C++ facade to match if we pick pull, so Python and C++ stay aligned without
per-tracker overrides.
If a prospective shape makes the templates hard to follow, leaving that tracker hand-written is the correct outcome — the generator exists to remove mechanical duplication, not to model every device.