Search documentation of bb

# BB.Robot.Runtime (bb v0.17.0)

Copy Markdown 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

### Types

robot_state()
simulation_mode()
t()

### Functions

cancel(robot_module)

Cancel the currently executing command.

category_availability(robot_module)

Get the availability of each command category.

check_allowed(robot_module, allowed_states)

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

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

execute(robot_module, command_name, goal)

Execute a command with the given goal.

executing?(robot_module)

Check if any command is currently executing.

executing?(robot_module, category)

Check if a specific category has commands executing.

executing_commands(robot_module)

Get information about all currently executing commands.

get_robot(robot_module)

Get the static robot struct (topology).

get_robot_state(robot_module)

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

operational_state(robot_module)

Get the actual operational state, without backwards compatibility translation.

positions(robot_module)

Get all joint positions as a map.

simulation_mode(robot_module)

Get the simulation mode for a robot.

start_link(arg)

Starts the runtime for a robot module.

state(robot_module)

Get the current robot state machine state.

transition(robot_module, new_state)

Transition the robot to a new state.

transition_operational_state(robot_module, execution_id, target_state)

Transition the operational state during command execution.

velocities(robot_module)

Get all joint velocities as a map.

via(robot_module)

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.