BB.Robot.Runtime (bb v0.15.0)

View Source

Runtime process for a BB robot.

Manages the robot's runtime state including:

  • The BB.Robot struct (static topology)
  • The BB.Robot.State ETS table (dynamic joint state)
  • Robot state machine (disarmed/idle/executing)
  • Command execution lifecycle
  • Sensor telemetry collection (subscribes to JointState messages)

Robot States

The robot progresses through these states:

  • :disarmed - Robot is not armed, commands restricted
  • :idle - Robot is armed and ready for commands
  • :executing - A command is currently executing

State Transitions

:disarmed arm :idle
:idle execute :executing
:executing complete :idle
:executing disarm :disarmed
:idle disarm :disarmed

Command Execution

Commands execute as supervised GenServers. The caller receives the command pid and can use BB.Command.await/2 or BB.Command.yield/2 to get the result. The Runtime monitors the command server and transitions back to :idle when it completes.

Summary

Functions

Cancel the currently executing command.

Get the availability of each command category.

Check if the robot is in one of the allowed states.

Returns a specification to start this module under a supervisor.

Execute a command with the given goal.

Check if any command is currently executing.

Check if a specific category has commands executing.

Get information about all currently executing commands.

Get the static robot struct (topology).

Get the robot state (ETS-backed joint positions/velocities).

Get the actual operational state, without backwards compatibility translation.

Get all joint positions as a map.

Get the simulation mode for a robot.

Starts the runtime for a robot module.

Get the current robot state machine state.

Transition the robot to a new state.

Transition the operational state during command execution.

Get all joint velocities as a map.

Returns the via tuple for process registration.

Types

robot_state()

@type robot_state() :: :disarmed | :disarming | :idle | :executing | :error | atom()

simulation_mode()

@type simulation_mode() :: nil | :kinematic | :external

t()

@type t() :: %BB.Robot.Runtime{
  category_counts: %{required(atom()) => non_neg_integer()},
  category_limits: %{required(atom()) => pos_integer()},
  commands: %{required(atom()) => BB.Dsl.Command.t()},
  current_command_name: atom() | nil,
  current_command_pid: pid() | nil,
  current_command_ref: reference() | nil,
  current_execution_id: reference() | nil,
  executing_commands: %{required(reference()) => BB.Robot.CommandInfo.t()},
  operational_state: atom(),
  parameter_store: module() | nil,
  parameter_store_state: term() | nil,
  robot: BB.Robot.t(),
  robot_module: module(),
  robot_state: BB.Robot.State.t(),
  simulation_mode: simulation_mode(),
  valid_states: [atom()]
}

Functions

cancel(robot_module)

@spec cancel(module()) :: :ok | {:error, :no_execution}

Cancel the currently executing command.

Stops the command server with :cancelled reason. Awaiting callers will receive the result from the command's result/1 callback.

category_availability(robot_module)

@spec category_availability(module()) :: %{
  required(atom()) => {non_neg_integer(), pos_integer()}
}

Get the availability of each command category.

Returns a map of category names to {current_count, limit} tuples.

check_allowed(robot_module, allowed_states)

@spec check_allowed(module(), [robot_state()]) ::
  :ok | {:error, BB.Error.State.NotAllowed.t()}

Check if the robot is in one of the allowed states.

Reads directly from ETS for fast concurrent access.

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

execute(robot_module, command_name, goal)

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

Execute a command with the given goal.

Returns {:ok, pid} where pid is the command server process. Use BB.Command.await/2 or BB.Command.yield/2 to get the result.

Examples

{:ok, cmd} = Runtime.execute(MyRobot, :navigate, %{target: pose})
{:ok, result} = BB.Command.await(cmd)

# Or with timeout
case BB.Command.yield(cmd, 5000) do
  nil -> still_running()
  {:ok, result} -> handle_result(result)
  {:error, reason} -> handle_error(reason)
end

Errors

  • {:error, %StateError{}} - Robot not in allowed state
  • {:error, {:unknown_command, name}} - Command not found
  • Other errors are returned through BB.Command.await/2

executing?(robot_module)

@spec executing?(module()) :: boolean()

Check if any command is currently executing.

Reads directly from ETS for fast concurrent access.

executing?(robot_module, category)

@spec executing?(module(), atom()) :: boolean()

Check if a specific category has commands executing.

executing_commands(robot_module)

@spec executing_commands(module()) :: [map()]

Get information about all currently executing commands.

get_robot(robot_module)

@spec get_robot(module()) :: BB.Robot.t()

Get the static robot struct (topology).

get_robot_state(robot_module)

@spec get_robot_state(module()) :: BB.Robot.State.t()

Get the robot state (ETS-backed joint positions/velocities).

operational_state(robot_module)

@spec operational_state(module()) :: atom()

Get the actual operational state, without backwards compatibility translation.

Unlike state/1, this returns the actual operational state regardless of whether commands are executing. Use this when you need to know the true operational context (e.g., :idle, :recording, :reacting).

Reads directly from ETS for fast concurrent access.

positions(robot_module)

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

Get all joint positions as a map.

Reads directly from ETS for fast concurrent access. Returns a map of joint names to their current positions (in radians for revolute joints, metres for prismatic joints).

Positions are updated automatically by the Runtime when sensors publish JointState messages.

Examples

iex> BB.Robot.Runtime.positions(MyRobot)
%{pan_joint: 0.0, tilt_joint: 0.0}

simulation_mode(robot_module)

@spec simulation_mode(module()) :: simulation_mode()

Get the simulation mode for a robot.

Returns nil if running in hardware mode, or the simulation mode atom (e.g., :kinematic, :external) if running in simulation.

start_link(arg)

Starts the runtime for a robot module.

state(robot_module)

@spec state(module()) :: robot_state()

Get the current robot state machine state.

Returns :disarmed if the robot is not armed (via BB.Safety), otherwise returns the internal operational state.

For backwards compatibility:

  • When operational_state is :idle but commands are executing, returns :executing
  • Custom operational states (e.g., :recording) are returned directly

Reads directly from ETS for fast concurrent access.

transition(robot_module, new_state)

@spec transition(module(), robot_state()) :: {:ok, robot_state()} | {:error, term()}

Transition the robot to a new state.

transition_operational_state(robot_module, execution_id, target_state)

@spec transition_operational_state(module(), reference(), atom()) ::
  :ok | {:error, term()}

Transition the operational state during command execution.

This is called by BB.Command.transition_state/2 to change the robot's operational state mid-execution. Only the command with the matching execution_id can trigger a transition.

velocities(robot_module)

@spec velocities(module()) :: %{required(atom()) => float()}

Get all joint velocities as a map.

Reads directly from ETS for fast concurrent access. Returns a map of joint names to their current velocities (in rad/s for revolute joints, m/s for prismatic joints).

Velocities are updated automatically by the Runtime when sensors publish JointState messages.

Examples

iex> BB.Robot.Runtime.velocities(MyRobot)
%{pan_joint: 0.0, tilt_joint: 0.0}

via(robot_module)

Returns the via tuple for process registration.