# `EtherCAT.Bus.Transport.UdpSocket`
[🔗](https://github.com/sid2baker/ethercat/blob/main/lib/ethercat/bus/transport/udp_socket.ex#L1)

UDP/IP transport for EtherCAT (spec §2.6).

Encapsulates EtherCAT frames in UDP/IP packets per Table 8:
- UDP destination port: 0x88A4 (34980) — the only header field ESCs check
- UDP payload = EtherCAT payload (`Bus.Frame.encode/1` output)
- ESC accepts frames with any source/destination IP address
- ESC clears the UDP checksum on forwarded frames (cannot update on-the-fly)

Implements the `EtherCAT.Bus.Transport` behaviour.

## Options

  - `:host` — destination IP tuple (default: `{255, 255, 255, 255}` broadcast)
  - `:port` — destination UDP port (default: `34980` = `0x88A4`)

# `t`

```elixir
@type t() :: %EtherCAT.Bus.Transport.UdpSocket{
  host: :inet.ip_address(),
  port: :inet.port_number(),
  raw: :gen_udp.socket() | nil
}
```

# `close`

```elixir
@spec close(t()) :: t()
```

Close the UDP socket. Returns the struct with `raw` set to `nil`.

# `drain`

```elixir
@spec drain(t()) :: :ok
```

Drain buffered datagrams and disable active delivery.

# `interface`

```elixir
@spec interface(t()) :: nil
```

Returns `nil` — UDP sockets don't use OS interface link monitoring.

# `match`

```elixir
@spec match(t(), term()) :: {:ok, binary(), integer(), nil} | :ignore
```

Match a `{:udp, raw, _ip, _port, data}` message from this socket.

Returns `{:ok, ecat_payload, rx_at, nil}` when the message belongs to this
socket. `src_mac` is always `nil` for UDP (no Ethernet headers).
The UDP checksum is cleared by the ESC (spec §2.6) — no validation needed.

# `name`

```elixir
@spec name(t()) :: String.t()
```

Returns a string identifier for telemetry: `"host:port"`.

# `open`

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

Open a UDP socket bound to port 0x88A4.

# `open?`

```elixir
@spec open?(t()) :: boolean()
```

Returns `true` when the socket is open.

# `rearm`

```elixir
@spec rearm(t()) :: :ok
```

Re-arm — delegates to `set_active_once/1` (UDP has no drain issue).

# `send`

```elixir
@spec send(t(), binary()) :: {:ok, integer()} | {:error, term()}
```

Send an EtherCAT payload as a UDP datagram.

# `set_active_once`

```elixir
@spec set_active_once(t()) :: :ok
```

Arm for one async delivery via `{:active, :once}`.

# `src_mac`

```elixir
@spec src_mac(t()) :: nil
```

Returns `nil` — UDP has no Ethernet-level source MAC.

---

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