# `MAVLink2.Link`
[🔗](https://github.com/isaiahdw/mavlink2/blob/v0.1.0/lib/mavlink2/link.ex#L1)

A MAVLink link over one transport: it owns the transport, reassembles
frames from the byte stream, numbers what it sends and delivers each
decoded frame to its subscribers as `{MAVLink2.Link, link, frame}`, where
`link` is the link's name or pid.

It knows framing and sequence numbers, not what a message means.

- A partial frame stays buffered until the rest arrives.
- Decode errors are counted in `stats/1`, not raised: a noisy line
  degrades.
- Outgoing frames are numbered per link; a gap in a sender's numbers
  (per system and component id) is counted as `:dropped`.

Options:

| | |
| --- | --- |
| `:dialect` | a `MAVLink2.Dialect` (required) |
| `:transport` | a `MAVLink2.Transport` (default `MAVLink2.Transport.UART`); the rest of the options go to its `open/1` |
| `:sendable` | the message ids the peer accepts; others are refused with `{:error, {:not_sendable, id}}`. Default: any id the dialect has |
| `:sysid`, `:compid` | this end's ids (default 1, 1): `sysid` from 1 to 32 bits, `compid` 1 to 255. 0 is the broadcast id and never a sender's, so the link refuses it, here and in `send_message/4` |
| `:unknown` | `:skip` (default) or `:keep`: deliver frames whose id the dialect lacks, unchecked, with `known: false` (see `MAVLink2.Frame.decode/3`). Each is counted in `:unknown` as well as `:rx` |
| `:signing` | a `MAVLink2.Signing`: every frame sent is signed, and a frame received is delivered only if it verifies (counted as `:unsigned`, `:bad_signature` or `:replay` otherwise, and delivered tagged to subscribers that ask; see `subscribe/3`). Without it, frames are sent unsigned and every frame received is delivered, signed or not |
| `:clock` | Unix time in microseconds for signing timestamps (default `System.os_time(:microsecond)`) |
| `:name` | registered name |

A transport that does not open logs a warning and the link returns
`:ignore`, so a board without the peer still starts.

# `child_spec`

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `forward`

```elixir
@spec forward(GenServer.server(), MAVLink2.Frame.t()) :: :ok | {:error, term()}
```

Writes a decoded frame's `raw` bytes unchanged: its sequence number,
source and signature are the original sender's. For routing; `:sendable`
does not apply. A link with `:signing` does not sign what it forwards.

# `info`

```elixir
@spec info(GenServer.server()) :: %{
  sysid: non_neg_integer(),
  compid: 0..255,
  dialect: module()
}
```

This end's `:sysid`, `:compid` and `:dialect`.

# `send`

```elixir
@spec send(GenServer.server(), struct(), keyword()) :: :ok | {:error, term()}
```

Encodes `message`, a struct of the link's dialect, and sends it.
`target_system` over 255 goes in the header (`TARGET32`) with 255 in the
payload, as MAVLink requires. Options are `send_message/4`'s other than
`:target_sysid`. Raises ArgumentError for a struct that is not of the
link's dialect, or a field value that does not fit its type.

    MAVLink2.Link.send(link, %MyDialect.Heartbeat{type: 6, autopilot: 8})

# `send_message`

```elixir
@spec send_message(GenServer.server(), non_neg_integer(), binary(), keyword()) ::
  :ok | {:error, term()}
```

Sends message `msgid` with `payload`. `sysid:` and `compid:` override the
link's; `target_sysid:` puts a target system in the header, for a target
over 255 (with 255 in the payload's `target_system`); `force: true` sends
an id outside `:sendable`, for probing.

# `start_link`

```elixir
@spec start_link(keyword()) :: GenServer.on_start()
```

Starts a link. Raises ArgumentError for an unknown option or a bad value;
see the module doc for the options.

    MAVLink2.Link.start_link(dialect: MyDialect, device: "/dev/ttyACM0", speed: 115_200)

# `stats`

```elixir
@spec stats(GenServer.server()) :: %{required(atom()) =&gt; non_neg_integer()}
```

Counts: `:rx`, `:tx`, `:errors`, `:dropped`, `:unknown`, `:unsigned`, `:bad_signature`, `:replay`.

# `subscribe`

```elixir
@spec subscribe(GenServer.server(), pid(), keyword()) :: :ok
```

Delivers every frame the link receives to `pid` as
`{MAVLink2.Link, link, frame}`, `frame` a `MAVLink2.Frame`. Subscribing
again replaces the options. The subscription ends when `pid` exits.

Options:

| | |
| --- | --- |
| `decode: true` | deliver `{MAVLink2.Link, link, frame, message}` instead, `message` the decoded struct (a dialect from `MAVLink2.Dialect.XML`). Frames the dialect cannot decode are not delivered |
| `only: [...]` | deliver only these messages: message structs' modules (`MyDialect.Heartbeat`) or ids |
| `rejected: true` | also deliver frames the link's `:signing` refuses, as `{MAVLink2.Link, link, {:rejected, reason, frame}}`, `reason` one of `:unsigned`, `:bad_signature`, `:replay`, so that one cannot be mistaken for a verified frame. Its timestamp moves nothing. A router forwards these; an application can show them as untrusted |

    :ok = MAVLink2.Link.subscribe(link, self(), decode: true, only: [MyDialect.Heartbeat])

    receive do
      {MAVLink2.Link, ^link, frame, %MyDialect.Heartbeat{} = heartbeat} -> {frame.sysid, heartbeat}
    end

# `unsubscribe`

```elixir
@spec unsubscribe(GenServer.server(), pid()) :: :ok
```

---

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