Routing layer for BB.* calls used by the TUI.
When the TUI is launched against a remote BEAM node (via
BB.TUI.run(robot, node: :"robot@host")) all robot data needs to come
from that node — but the rendering, keyboard input and process state
live locally on the developer's machine. This module is the boundary
that decides where each call goes:
node == nil— call the localBB.*module directly.nodeis a connected remote node atom — call viaBB.TUI.Rpc, a thin wrapper over:rpc.call/4that exists so the cross-node paths can be mocked in tests (:rpcitself is a sticky kernel module that cannot be replaced at runtime).
PubSub across nodes
BB.PubSub is built on Registry, which is node-local, so we cannot
simply call BB.subscribe/2 from the dev node and expect to receive
messages published on the robot node. Instead subscribe/3 spawns a
small relay process on the remote node via Node.spawn_link/2. The
relay subscribes locally there and forwards every {:bb, _, _}
message back to the TUI process on the dev node.
This is the only "process with a runtime reason" introduced by the remote path: it exists because we need (1) a place to receive PubSub messages on the remote node and (2) fault isolation if the remote node goes away (the link will tear it down on disconnect).
Summary
Types
Either nil for local execution or a connected remote node.
Functions
Arms the robot.
Disarms the robot.
Returns the list of declared commands for the robot, normalized for the
UI. Returns [] if the command DSL is not available or raises.
Executes a command on the runtime.
Force-disarms the robot from an error state.
Returns the runtime robot struct (joints, actuators, etc.).
Returns the list of declared parameter bridges for the robot.
Returns the parameter list (with metadata maps) for the robot.
Lists parameters exposed by a remote bridge.
Returns the latest joint positions known by the runtime.
Publishes a PubSub message under the robot's topic.
Returns the runtime state machine state.
Returns the safety state of the robot.
Commands an actuator to a position.
Sets a parameter value.
Sets a parameter value on a remote bridge.
Subscribes to one or more PubSub paths for the given robot.
Types
@type maybe_node() :: node() | nil
Either nil for local execution or a connected remote node.
Functions
@spec arm(module(), maybe_node()) :: term()
Arms the robot.
@spec disarm(module(), maybe_node()) :: term()
Disarms the robot.
@spec discover_commands(module(), maybe_node()) :: [map()]
Returns the list of declared commands for the robot, normalized for the
UI. Returns [] if the command DSL is not available or raises.
Each command map has the shape:
%{
name: atom(),
handler: term(),
timeout: integer() | :infinity,
allowed_states: [atom()],
arguments: [%{name: atom(), type: String.t(), required: boolean(),
default: term(), doc: String.t() | nil}]
}Argument types are normalized to strings: "boolean", "integer",
"float", "atom", "string", or "enum:[a, b, c]". Mirrors
BB.LiveView.Components.Command so both UIs see the same shape.
Executes a command on the runtime.
Returns whatever the runtime returns — typically {:ok, pid} for the
command process, or {:error, reason}. Cross-node pids are tracked
transparently by the Erlang distribution layer.
@spec force_disarm(module(), maybe_node()) :: term()
Force-disarms the robot from an error state.
@spec get_robot(module(), maybe_node()) :: term()
Returns the runtime robot struct (joints, actuators, etc.).
@spec list_bridges(module(), maybe_node()) :: [map()]
Returns the list of declared parameter bridges for the robot.
Each bridge is rendered down to %{name: atom(), simulation: atom()} for
the UI; the underlying BB.Dsl.Bridge struct is not exposed so callers
don't depend on Spark internals. Bridges where :simulation is :omit
while the robot is in simulation mode are filtered out (matching
bb_liveview's discovery rules).
Returns [] when the DSL is unavailable or raises.
@spec list_parameters(module(), keyword(), maybe_node()) :: [{list(), term()}]
Returns the parameter list (with metadata maps) for the robot.
@spec list_remote_parameters(module(), atom(), maybe_node()) :: {:ok, [map()]} | {:error, term()}
Lists parameters exposed by a remote bridge.
Returns the bridge's flat parameter list (each entry a map carrying
:id, :value, :type, optionally :min, :max, :doc). Returns
{:error, reason} when the bridge is unavailable or the call fails.
@spec positions(module(), maybe_node()) :: %{required(atom()) => float()}
Returns the latest joint positions known by the runtime.
@spec publish(module(), list(), term(), maybe_node()) :: term()
Publishes a PubSub message under the robot's topic.
@spec runtime_state(module(), maybe_node()) :: atom()
Returns the runtime state machine state.
@spec safety_state(module(), maybe_node()) :: atom()
Returns the safety state of the robot.
@spec set_actuator(module(), atom(), number(), maybe_node()) :: term()
Commands an actuator to a position.
@spec set_parameter(module(), list(), term(), maybe_node()) :: term()
Sets a parameter value.
@spec set_remote_parameter(module(), atom(), term(), term(), maybe_node()) :: :ok | {:error, term()}
Sets a parameter value on a remote bridge.
@spec subscribe(module(), [list()], maybe_node()) :: :ok
Subscribes to one or more PubSub paths for the given robot.
Local node — calls BB.subscribe/2 for each path directly so messages
arrive at self().
Remote node — spawns a relay process on the remote node that subscribes
there and forwards every {:bb, _, _} message back to self().