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

Public API for the EtherCAT bus scheduler.

`EtherCAT.Bus` is the single serialization point for all EtherCAT frame I/O.
Callers build `EtherCAT.Bus.Transaction` values and submit them as either:

- `transaction/2` — reliable work, eligible for batching with other reliable transactions
- `transaction/3` — realtime work with a staleness budget; stale work is discarded

Realtime and reliable transactions are strictly separated:
realtime always has priority, and realtime transactions never share a frame with
reliable transactions.

In this API, `reliable` means non-expiring background work: the bus keeps it
queued until it can be sent or the link fails. It does not mean the transport
can never time out.

`realtime` means deadline-sensitive work: it gets priority and is dropped
once it has become too old to be useful.

The public API is singleton-first: `info/0`, `transaction/1`,
`set_frame_timeout/1`, `settle/0`, and `quiesce/1` target the registered
`EtherCAT.Bus` process by default. Explicit server-ref variants remain
available for internal callers and tests.

# `call_error`

```elixir
@type call_error() :: {:error, :not_started | :timeout | {:server_exit, term()}}
```

# `server`

```elixir
@type server() :: :gen_statem.server_ref()
```

# `info`

```elixir
@spec info() :: {:ok, map()} | call_error()
```

Return low-level bus runtime information, including queue and in-flight state.

# `info`

```elixir
@spec info(server()) :: {:ok, map()} | call_error()
```

# `quiesce`

```elixir
@spec quiesce(non_neg_integer()) :: :ok | call_error()
```

Drain the bus, wait through a short quiet window, then drain once more.

This is useful when late transport traffic can still land just after the bus
first reports idle, for example after startup/configuration exchanges.

# `quiesce`

```elixir
@spec quiesce(server(), non_neg_integer()) :: :ok | call_error()
```

# `set_frame_timeout`

```elixir
@spec set_frame_timeout(pos_integer()) :: :ok | call_error() | {:error, term()}
```

Update the frame response timeout in milliseconds.

# `set_frame_timeout`

```elixir
@spec set_frame_timeout(server(), pos_integer()) ::
  :ok | call_error() | {:error, term()}
```

# `settle`

```elixir
@spec settle() :: :ok | call_error()
```

Wait until the bus is idle, then drain any buffered receive traffic.

This is useful after startup/configuration phases where callers want the next
transaction to begin from a quiescent transport state.

# `settle`

```elixir
@spec settle(server()) :: :ok | call_error()
```

# `start_link`

```elixir
@spec start_link(keyword()) :: :gen_statem.start_ret()
```

# `transaction`

```elixir
@spec transaction(EtherCAT.Bus.Transaction.t()) ::
  {:ok, [EtherCAT.Bus.Result.t()]} | call_error() | {:error, term()}
```

Execute a reliable transaction.

Reliable work may be batched with other reliable transactions when the bus is
already busy, but an idle bus sends immediately.

`Reliable` here means non-expiring background work. The bus will not discard
it due to age while it is queued, but the transaction can still fail due to
timeouts or link loss.

# `transaction`

```elixir
@spec transaction(server(), EtherCAT.Bus.Transaction.t()) ::
  {:ok, [EtherCAT.Bus.Result.t()]} | call_error() | {:error, term()}
@spec transaction(EtherCAT.Bus.Transaction.t(), pos_integer()) ::
  {:ok, [EtherCAT.Bus.Result.t()]} | call_error() | {:error, term()}
```

Execute a realtime transaction with a staleness budget in microseconds.

Realtime work is discarded if it has become stale by the time the bus is ready
to dispatch it. Realtime transactions are never mixed with reliable traffic.

`stale_after_us` is a max queued age relative to submission time, not an
absolute wall-clock deadline.

# `transaction`

```elixir
@spec transaction(server(), EtherCAT.Bus.Transaction.t(), pos_integer()) ::
  {:ok, [EtherCAT.Bus.Result.t()]} | call_error() | {:error, term()}
```

---

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