# `ADS.Adapter`
[🔗](https://github.com/tallakt/yaads/blob/v0.1.0/lib/ads/adapter.ex#L1)

A network adapter of a TwinCAT system, as its system service lists them, for
`ADS.Client.adapters/2`: index group 701, `SYSTEMSERVICE_IPHELPERAPI` in Beckhoff's
Tc2_Utilities, offset 1, `IPHELPERAPI_ADAPTERSINFO`, on AMS port 10000. A read of 4 bytes
answers the size of the whole list; a read of less than that, `:device_invalid_size`.

Beckhoff doesn't document how the list comes; this is as TwinCAT 3.1 4026 answers. It is the
`IP_ADAPTER_INFO` of Windows' `GetAdaptersInfo`, as Microsoft documents it, laid out as a
32-bit program has it, with a 64-bit `time_t`: 648 bytes for each adapter, each pointer an offset
from the start of the list. All numbers are little-endian:

| Bytes | Field |
|---|---|
| 4 | the offset of the next adapter, 0 after the last |
| 4 | a combined index |
| 260 | its name, with a zero after |
| 132 | its description, with a zero after |
| 4, 8 | the length of its hardware address, and the address |
| 4 | its index |
| 4 | its type, as `MIB_IF_TYPE_*` in Windows (see `t:type/0`) |
| 4 | whether DHCP is enabled |
| 4 | a pointer, 0 |
| 40 | its first IP address, and the offset of its next |
| 40 | its first gateway, and the offset of its next |
| 40 | its DHCP server |
| 4, 40, 40 | whether it has WINS, and its two WINS servers |
| 8, 8 | when its DHCP lease was had and when it ends |

Each address of 40 bytes is the offset of the next, 0 after the last; the address and the mask
as 16 bytes of text each, with zeros after; and 4 bytes more. An adapter's further addresses come
after it, before the next adapter. `0.0.0.0` stands for none: an adapter without an address,
gateway or DHCP server lists that.

The lease times aren't read: a system has answered bytes there that aren't times, for an adapter
without DHCP; nor WINS, which TwinCAT systems don't use. The strings are in the system's code
page, as Windows' ANSI functions give them, Windows-1252 unless told otherwise (see
`ADS.Codepage`).

# `t`

```elixir
@type t() :: %ADS.Adapter{
  addresses: [{:inet.ip4_address(), :inet.ip4_address()}],
  description: String.t(),
  dhcp: boolean(),
  dhcp_server: :inet.ip4_address() | nil,
  gateways: [:inet.ip4_address()],
  index: non_neg_integer(),
  mac: String.t(),
  name: String.t(),
  type: type()
}
```

# `type`

```elixir
@type type() ::
  :other
  | :ethernet
  | :token_ring
  | :fddi
  | :ppp
  | :loopback
  | :slip
  | :wireless
  | non_neg_integer()
```

An adapter's type, as Windows' `MIB_IF_TYPE_*` and `IF_TYPE_IEEE80211` name them, or its number.

# `decode_all`

```elixir
@spec decode_all(binary(), ADS.Codepage.t()) :: {:ok, [t()]} | :error
```

The adapters from the bytes of the list, or `:error` if they don't make one. No bytes are no
adapters.

    iex> address = fn ip, mask -> <<0::32, ip::binary-16, mask::binary-16, 0::32>> end
    iex> text = fn string -> string <> :binary.copy(<<0>>, 16 - byte_size(string)) end
    iex> list =
    ...>   <<0::32, 9::32-little, "{5E97F165}", 0::size(250)-unit(8), "Intel Ethernet",
    ...>     0::size(118)-unit(8), 6::32-little, 0, 1, 5, 0x76, 0x34, 0xAE, 0, 0, 9::32-little,
    ...>     6::32-little, 0::32, 0::32>> <>
    ...>     address.(text.("10.0.0.5"), text.("255.255.255.0")) <>
    ...>     address.(text.("10.0.0.1"), text.("255.255.255.255")) <>
    ...>     address.(text.(""), text.("")) <> <<0::32>> <> address.(<<0::128>>, <<0::128>>) <>
    ...>     address.(<<0::128>>, <<0::128>>) <> <<0::128>>
    iex> ADS.Adapter.decode_all(list)
    {:ok, [%ADS.Adapter{name: "{5E97F165}", description: "Intel Ethernet",
                        mac: "00:01:05:76:34:AE", index: 9, type: :ethernet, dhcp: false,
                        addresses: [{{10, 0, 0, 5}, {255, 255, 255, 0}}],
                        gateways: [{10, 0, 0, 1}], dhcp_server: nil}]}

---

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