BB.TUI.Robot (BB.TUI v0.1.0)

Copy Markdown View Source

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 local BB.* module directly.
  • node is a connected remote node atom — call via BB.TUI.Rpc, a thin wrapper over :rpc.call/4 that exists so the cross-node paths can be mocked in tests (:rpc itself 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

maybe_node()

@type maybe_node() :: node() | nil

Either nil for local execution or a connected remote node.

Functions

arm(robot, node)

@spec arm(module(), maybe_node()) :: term()

Arms the robot.

disarm(robot, node)

@spec disarm(module(), maybe_node()) :: term()

Disarms the robot.

discover_commands(robot, node)

@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.

execute_command(robot, name, args, node)

@spec execute_command(module(), atom(), map(), maybe_node()) ::
  {:ok, pid()} | {:error, term()}

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.

force_disarm(robot, node)

@spec force_disarm(module(), maybe_node()) :: term()

Force-disarms the robot from an error state.

get_robot(robot, node)

@spec get_robot(module(), maybe_node()) :: term()

Returns the runtime robot struct (joints, actuators, etc.).

list_bridges(robot, node)

@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.

list_parameters(robot, opts, node)

@spec list_parameters(module(), keyword(), maybe_node()) :: [{list(), term()}]

Returns the parameter list (with metadata maps) for the robot.

list_remote_parameters(robot, bridge_name, node)

@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.

positions(robot, node)

@spec positions(module(), maybe_node()) :: %{required(atom()) => float()}

Returns the latest joint positions known by the runtime.

publish(robot, path, msg, node)

@spec publish(module(), list(), term(), maybe_node()) :: term()

Publishes a PubSub message under the robot's topic.

runtime_state(robot, node)

@spec runtime_state(module(), maybe_node()) :: atom()

Returns the runtime state machine state.

safety_state(robot, node)

@spec safety_state(module(), maybe_node()) :: atom()

Returns the safety state of the robot.

set_actuator(robot, actuator, position, node)

@spec set_actuator(module(), atom(), number(), maybe_node()) :: term()

Commands an actuator to a position.

set_parameter(robot, path, value, node)

@spec set_parameter(module(), list(), term(), maybe_node()) :: term()

Sets a parameter value.

set_remote_parameter(robot, bridge_name, param_id, value, node)

@spec set_remote_parameter(module(), atom(), term(), term(), maybe_node()) ::
  :ok | {:error, term()}

Sets a parameter value on a remote bridge.

subscribe(robot, paths, node)

@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().