Subscriptions
There are three ways to consume a topic. They are not interchangeable — each answers a different question.
| Style | Question it answers | Runs on | Return |
|---|---|---|---|
@SubscribedTo | do something every time a value arrives | callback pool (or hardware thread with @OnHardwareThread) | — |
subscribe(name, type, handler) | same, but registered at runtime | callback pool | Subscription (cancellable) |
getLatestValue(name, type) | what is the value right now? | your own thread | Optional<T> |
1. @SubscribedTo — annotation
@SubscribedTo(topic = "intake/set/power")
@OnHardwareThread
public void onTarget(double power) {
intake.run(m -> m.setPower(power));
}
Declared on a Node method and bound at registerNode. The method takes 0 or 1 parameters; with one, the type must match the topic’s (primitives and wrappers are normalized). A value that doesn’t match the parameter type is silently dropped, not an error.
See @SubscribedTo for the full rules.
2. subscribe(...) — programmatic
Subscription sub = orchestrator.subscribe("g1/x", Boolean.class,
v -> frontLeft.run(m -> m.setPower(v ? 1.0 : 0.0)));
// later:
sub.unsubscribe();
Same delivery semantics as the annotation, but usable outside nodes — in onSafeInit, tests, or glue code. The handler still runs on the callback pool; route hardware work through run(...)/call(...), exactly as the annotation form does. unsubscribe() is idempotent.
This is also the pattern for quick verification OpModes: subscribe buttons directly to device writes and see the robot react, no node needed.
3. getLatestValue(...) — poll
double y = -orchestrator.getLatestValue("g1/left_stick_y", Float.class)
.map(Float::doubleValue).orElse(0.0);
No subscription at all — read the current state whenever you want. This is what a @RunPeriodically loop uses for its inputs. Returns an empty Optional if nothing has been published yet or the type doesn’t match, so orElse(...) a sensible default.
Choosing between them
- Reactive behavior (“when the button is pressed…”) → subscribe. Edges (
/rising,/falling) are events; events need subscriptions. - Continuous control (“every tick, set power from the stick”) → poll with
getLatestValueinside a@RunPeriodically(hardware = true)loop. - One-shot wiring in an OpMode or test →
subscribe(...). - Telemetry → poll in
onSafeLoop().
Subscriptions are hot. A
@SubscribedToon a topic published at 60 Hz fires 60 times a second. Keep handlers cheap (set a field, submit arun(...)); anything heavier belongs in a periodic loop.
Next steps
@SubscribedTo— full annotation reference.@RunPeriodically— the polling side.