# `BB.Message`
[🔗](https://github.com/beam-bots/bb/blob/main/lib/bb/message.ex#L5)

Message envelope and behaviour for payload types.

Messages in BB are wrapped in a standard envelope containing timing,
coordinate frame, and payload data.

The `:robot` field identifies the publishing robot module and is filled
in automatically by `BB.PubSub.publish/3` just before dispatch. It is
`nil` on freshly-constructed messages that have not yet been published,
and lets subscribers that listen to more than one robot attribute each
delivered message to its source.

## Usage

Use the `use BB.Message` macro to define a payload type:

    defmodule MyPayload do
      defstruct [:value]

      use BB.Message,
        schema: [
          value: [type: :float, required: true]
        ]
    end

    {:ok, msg} = MyPayload.new(:base_link, value: 1.5)

The macro will:
- Validate struct fields match schema keys at compile time
- Implement the `schema/0` callback
- Generate a `new/2` helper function

Note: `defstruct` must be defined before `use BB.Message`.

# `t`

```elixir
@type t() :: %BB.Message{
  frame_id: atom(),
  monotonic_time: integer(),
  node: node(),
  payload: struct(),
  robot: module() | nil,
  wall_time: integer()
}
```

# `schema`

```elixir
@callback schema() :: Spark.Options.t()
```

Returns a compiled Spark.Options schema for this payload type

# `new`

```elixir
@spec new(module(), atom(), keyword()) :: {:ok, t()} | {:error, term()}
```

Create a new message with validated payload.

Validates the attributes against the payload module's schema, then wraps
the resulting struct in a message envelope. The envelope is stamped with
the current monotonic time, wall-clock time, and the local node name,
so messages remain interpretable across nodes and after recording.

## Examples

    alias BB.Message.Geometry.Pose
    alias BB.Math.Transform

    {:ok, msg} = BB.Message.new(Pose, :end_effector, [
      transform: Transform.identity()
    ])

# `new!`

```elixir
@spec new!(module(), atom(), keyword()) :: t()
```

Like `new/3` but raises on validation error.

## Examples

    msg = BB.Message.new!(Pose, :end_effector, [
      transform: Transform.identity()
    ])

# `schema`

```elixir
@spec schema(t()) :: Spark.Options.t()
```

Get the payload schema from a message.

## Examples

    BB.Message.schema(msg)  #=> %Spark.Options{...}

---

*Consult [api-reference.md](api-reference.md) for complete listing*
