# `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.

## 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(), payload: struct(), timestamp: 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 with a fresh timestamp.

## 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*
