# `EtherCAT.AL`
[🔗](https://github.com/tallakt/yaethercat/blob/v0.1.0/lib/ethercat/al.ex#L1)

The AL state machine of a SubDevice (ETG.1000.6, 6.4.1), through its AL control and AL status
registers (5.3).

| State | Code | The SubDevice |
|---|---|---|
| `:init` | 1 | is set up by the MainDevice, its mailbox sync managers among the rest |
| `:pre_operational` | 2 | has its mailbox working, and no process data |
| `:bootstrap` | 3 | takes firmware over FoE, and nothing else |
| `:safe_operational` | 4 | gives its inputs, and holds its outputs in their safe state |
| `:operational` | 8 | gives its inputs and drives its outputs |

From Init there is a way to Pre-Operational and Bootstrap only, and from Pre-Operational none
straight to Operational; any state can go down to one below it. A change that fails leaves the
SubDevice with its error flag set and an AL status code that says why, until the MainDevice
acknowledges it.

# `state`

```elixir
@type state() ::
  :init | :pre_operational | :bootstrap | :safe_operational | :operational
```

A state of the AL state machine.

# `status`

```elixir
@type status() :: %{
  state: state() | byte(),
  error: boolean(),
  id_loaded: boolean(),
  code: 0..65535
}
```

What a SubDevice's AL status registers say.

# `code`

```elixir
@spec code(state()) :: byte()
```

The code of a state in AL control and AL status.

# `decode_status`

```elixir
@spec decode_status(binary()) :: status()
```

AL status from its six bytes, from 0x0130: the state, the error flag, whether R6 holds a device
identification, and the AL status code, which counts only with the error flag set.

    iex> EtherCAT.AL.decode_status(<<0x14, 0, 0, 0, 0x1E, 0>>)
    %{state: :safe_operational, error: true, id_loaded: false, code: 0x001E}

# `describe`

```elixir
@spec describe(0..65535) :: String.t()
```

What an AL status code means, in a few words.

    iex> EtherCAT.AL.describe(0x001E)
    "The inputs are set up wrong"
    iex> EtherCAT.AL.describe(0x8001)
    "Vendor specific (0x8001)"

# `read_status`

```elixir
@spec read_status(EtherCAT.MainDevice.t(), 0..65535) ::
  {:ok, status()} | {:error, term()}
```

Reads a SubDevice's AL status, by station address.

# `request`

```elixir
@spec request(EtherCAT.MainDevice.t(), 0..65535, state(), keyword()) ::
  :ok | {:error, {:al_status, state() | byte(), 0..65535} | :timeout | term()}
```

Asks a SubDevice for a state, by station address, and waits until it is there: `:ok`, or
`{:error, {:al_status, state, code}}` with the state it is in and the AL status code it gave for
refusing, or `{:error, :timeout}`. A refusal is acknowledged, as is an error from before, so that
the SubDevice can be asked again.

Options:

  * `timeout:` (5 seconds), how long it may take

# `state`

```elixir
@spec state(byte()) :: state() | byte()
```

The state of a code, or the code itself for one the spec doesn't define.

# `states`

```elixir
@spec states() :: [state()]
```

Every state, from Init up.

---

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