Docs Concepts

Orchestrator

The bus that owns topics, nodes, and the thread workers.

Orchestrator

The Orchestrator is the pub/sub bus. There is exactly one per robot — it owns the topic registry, the node registry, the action registry, and all four thread workers. You almost never construct one yourself: SafeOpMode.init() creates it as the orchestrator field before onSafeInit() runs.

@Override
protected void onSafeInit() {
    // orchestrator, hardware, and safeMap are ready here.
    orchestrator.publish("robot/init", true);
}

Outside an OpMode (unit tests, for example), create one directly:

Orchestrator orch = Orchestrator.create("test");
HardwareActions hw = orch.hardware();
// ... use it ...
orch.close();

FtcOrchestrator.create() is the FTC-side factory: same bus, but named "ftc" and logging to android.util.Log instead of stderr.

What it does

GroupMethodsNotes
TopicsgetOrCreateTopic(name, type), findTopic(name), findTopic(name, type)Topics are created lazily; see Topics.
Publishpublish(name, value)Non-blocking. Updates the latest-value cache, then dispatches to subscribers.
Subscribesubscribe(name, type, handler)Handler runs on the callback pool. Returns a Subscription handle.
ReadgetLatestValue(name, type)Optional of the most recent value; empty if none or type-incompatible.
NodesregisterNode(name, node), unregisterNode(name), findNode(name)Registration binds all annotations on the node.
ActionsrunAction(name), cancelAllActions()See @RunnableAction.
SchedulingrunPeriodically(task, hz)The programmatic form of @RunPeriodically (non-hardware).
HardwarerunOnHardwareThread(task), hardware()Direct submission, or the HardwareActions facade.
Logginglog, warn, errorRouted through the configured LogSink.

Naming things

Both topic names and node names are plain strings. Two conventions used throughout the recipes:

  • Topics are slash-separated paths rooted by their source: g1/left_stick_y, drive/power/fl, intake/power. Producers own the first segment; consumers subscribe to the full path.
  • Node names identify the subsystem: "drive", "intake", "shooter". Names are unique — registering a second node under an existing name is a no-op that logs a warning and returns the original node. If a subsystem silently does nothing, grep for a duplicate registerNode call.

Lifecycle

orchestrator.close();   // SafeOpMode.stop() does this for you

close() is idempotent. It unregisters every node (calling each node’s close() hook), cancels all scheduled loops and actions, and shuts down all four workers. After close, publishes are ignored (with a warning) and runAction returns a failed future.

Next steps

  • Topics — the channels flowing through this bus.
  • Nodes — what registerNode actually binds.