Topics
A topic is a string name paired with a Java type. Publishers push values onto it; subscribers receive every value; anything can read the latest value on demand. Topics are the only way modules communicate — no direct references, no shared mutable state.
Topic<Double> power = orchestrator.getOrCreateTopic("intake/set/power", Double.class);
orchestrator.publish("intake/set/power", 0.75);
Naming
Slash-separated paths, rooted by the producing subsystem:
g1/left_stick_y gamepad axis (published by GamepadAdaptor)
g1/a/rising button press edge
drive/power/fl per-wheel power (published by the drive node)
intake/power current intake power
The root of the path says who publishes it: g1/... is gamepad 1, drive/... is the drive subsystem. Consumers never publish to another subsystem’s topics — they publish to their own state topics and subscribe to what they need.
Typing
A topic’s type is fixed at creation and checked on every publish.
- Primitives and wrappers are interchangeable.
double.classandDouble.classname the same topic type, so an annotation method declareddouble powerbinds cleanly to aDouble-typed topic. - Compatibility uses assignability, so a
Numbertopic acceptsIntegerandDoublepublishes, and anObjecttopic accepts anything. - Publishing an incompatible value throws
IllegalArgumentExceptionwith both type names — e.g. publishing aDoubleto a topic created asInteger. - Reading with the wrong type is safe:
getLatestValuereturns an emptyOptionalrather than throwing.
Decide topic types up front. Because the first publish creates the topic from the value’s runtime type, an accidental
publish("battery/volts", 12)(anint) would lock the topic toIntegerand make every laterDoublepublish throw. Declare the type withgetOrCreateTopicat the top of the owning node, then publish untyped values freely.
Auto-creation
You never have to pre-create a topic — the first publish creates it from the value’s runtime type, and the first subscribe creates it from the declared handler type. Pre-creating with getOrCreateTopic is still recommended for anything shared between modules, because it pins the type at one obvious place.
GamepadAdaptor pre-creates all of its topics when attached, so you can subscribe to g1/a/rising before the first gamepad poll ever runs.
Reading the latest value
Every topic remembers its most recent value:
Optional<Double> latest = orchestrator.getLatestValue("drive/power/fl", Double.class);
double fl = latest.orElse(0.0);
Topic<Double> flTopic = orchestrator.findTopic("drive/power/fl", Double.class).orElseThrow();
double fl2 = flTopic.latestValueOr(0.0);
long nanosSinceUpdate = System.nanoTime() - flTopic.latestPublishNanos();
Fetching the latest value is the right tool inside periodic loops; subscribing is the right tool when the event matters, not the current state. See Subscriptions for the tradeoff.
Next steps
- Subscriptions — reacting to values instead of polling them.
- GamepadAdaptor — the biggest topic source on most robots.