Current section
Files
Jump to
Current section
Files
lib/bb/command.ex
# SPDX-FileCopyrightText: 2025 James Harton
#
# SPDX-License-Identifier: Apache-2.0
defmodule BB.Command do
@moduledoc """
Behaviour for implementing robot commands.
Commands execute in a supervised task and receive a goal (arguments) and
context (robot state). The handler runs to completion and returns a result.
## Example
defmodule NavigateToPose do
@behaviour BB.Command
@impl true
def handle_command(%{target_pose: pose, tolerance: tol}, context) do
# Access robot state
current_pose = get_current_pose(context.robot_state)
# Do the work (this runs in a task, so blocking is fine)
case navigate_to(pose, tolerance: tol) do
:ok -> {:ok, %{final_pose: pose}}
{:error, reason} -> {:error, reason}
end
end
end
## State Transitions
By default, when a command completes successfully, the robot transitions to
`:idle`. Commands can override this by returning a `next_state` option:
def handle_command(_goal, _context) do
{:ok, :disarmed, next_state: :disarmed}
end
This is useful for commands like `Arm` and `Disarm` that need to control
the robot's state machine.
## Execution Model
Commands run in supervised tasks spawned by the Runtime. The caller receives
a `Task.t()` and can use `Task.await/2` or `Task.yield/2` to get the result.
Cancellation is handled by killing the task - handlers don't need to implement
cancellation logic. If graceful shutdown is needed, handlers can trap exits.
"""
alias BB.Command.Context
@type goal :: map()
@type result :: term()
@type options :: [next_state: BB.Robot.Runtime.robot_state()]
@doc """
Execute the command with the given goal.
Called in a supervised task. The handler should perform the work and return
the result. Blocking operations are fine since this runs in a separate process.
The context provides access to:
- `robot_module` - The robot module
- `robot` - The static robot struct (topology)
- `robot_state` - The dynamic robot state (ETS-backed joint positions etc)
- `execution_id` - Unique identifier for this execution
## Return Values
- `{:ok, result}` - Command succeeded, robot transitions to `:idle`
- `{:ok, result, options}` - Command succeeded with options:
- `next_state: state` - Robot transitions to specified state instead of `:idle`
- `{:error, reason}` - Command failed, robot transitions to `:idle`
"""
@callback handle_command(goal(), Context.t()) ::
{:ok, result()}
| {:ok, result(), options()}
| {:error, term()}
end