# `EtherCAT.Simulator.Fault`
[🔗](https://github.com/sid2baker/ethercat/blob/main/lib/ethercat/simulator/fault.ex#L1)

Builder API for `EtherCAT.Simulator.inject_fault/1`.

This keeps the public fault surface readable without changing the simulator's
internal tuple representation.

Typical usage:

    alias EtherCAT.Simulator.Fault

    EtherCAT.Simulator.inject_fault(Fault.drop_responses())

    EtherCAT.Simulator.inject_fault(
      Fault.disconnect(:outputs)
      |> Fault.next(30)
    )

    EtherCAT.Simulator.inject_fault(
      Fault.script([
        Fault.drop_responses(),
        Fault.wait_for(Fault.healthy_polls(:outputs, 10)),
        Fault.retreat_to_safeop(:outputs)
      ])
    )

    Fault.describe(Fault.disconnect(:outputs) |> Fault.next(3))

# `effect`

```elixir
@type effect() ::
  :drop_responses
  | {:wkc_offset, integer()}
  | {:command_wkc_offset, atom(), integer()}
  | {:logical_wkc_offset, atom(), integer()}
  | {:disconnect, atom()}
  | {:retreat_to_safeop, atom()}
  | {:power_cycle, atom()}
  | {:latch_al_error, atom(), non_neg_integer()}
  | {:mailbox_abort, atom(), non_neg_integer(), non_neg_integer(),
     non_neg_integer(), mailbox_step() | nil}
  | {:mailbox_protocol_fault, atom(), non_neg_integer(), non_neg_integer(),
     mailbox_step(), term()}
  | {:nested, t() | raw_fault()}
  | {:script, [t()]}
  | {:wait_for_milestone, milestone()}
```

# `mailbox_step`

```elixir
@type mailbox_step() ::
  :request | :upload_init | :upload_segment | :download_init | :download_segment
```

# `milestone`

```elixir
@type milestone() ::
  {:healthy_exchanges, pos_integer()}
  | {:healthy_polls, atom(), pos_integer()}
  | {:mailbox_step, atom(), mailbox_step(), pos_integer()}
```

# `raw_fault`

```elixir
@type raw_fault() ::
  EtherCAT.Simulator.fault()
  | EtherCAT.Simulator.immediate_fault()
  | EtherCAT.Simulator.fault_script_step()
```

# `schedule`

```elixir
@type schedule() ::
  :immediate
  | {:next_exchange, pos_integer()}
  | {:after_ms, non_neg_integer()}
  | {:after_milestone, milestone()}
```

# `t`

```elixir
@type t() :: %EtherCAT.Simulator.Fault{effect: effect(), schedule: schedule()}
```

# `after_milestone`

```elixir
@spec after_milestone(t(), milestone()) :: t()
```

# `after_ms`

```elixir
@spec after_ms(t(), non_neg_integer()) :: t()
```

# `command_wkc_offset`

```elixir
@spec command_wkc_offset(atom(), integer()) :: t()
```

# `describe`

```elixir
@spec describe(t() | raw_fault()) :: String.t()
```

# `disconnect`

```elixir
@spec disconnect(atom()) :: t()
```

# `drop_responses`

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

# `healthy_exchanges`

```elixir
@spec healthy_exchanges(pos_integer()) :: milestone()
```

# `healthy_polls`

```elixir
@spec healthy_polls(atom(), pos_integer()) :: milestone()
```

# `latch_al_error`

```elixir
@spec latch_al_error(atom(), non_neg_integer()) :: t()
```

# `logical_wkc_offset`

```elixir
@spec logical_wkc_offset(atom(), integer()) :: t()
```

# `mailbox_abort`

```elixir
@spec mailbox_abort(
  atom(),
  non_neg_integer(),
  non_neg_integer(),
  non_neg_integer(),
  keyword()
) :: t()
```

# `mailbox_protocol_fault`

```elixir
@spec mailbox_protocol_fault(
  atom(),
  non_neg_integer(),
  non_neg_integer(),
  mailbox_step(),
  term()
) :: t()
```

# `mailbox_step`

```elixir
@spec mailbox_step(atom(), mailbox_step(), pos_integer()) :: milestone()
```

# `next`

```elixir
@spec next(t(), pos_integer()) :: t()
```

# `normalize`

```elixir
@spec normalize(t() | raw_fault()) :: {:ok, raw_fault()} | :error
```

# `power_cycle`

```elixir
@spec power_cycle(atom()) :: t()
```

# `retreat_to_safeop`

```elixir
@spec retreat_to_safeop(atom()) :: t()
```

# `script`

```elixir
@spec script([t(), ...]) :: t()
```

# `wait_for`

```elixir
@spec wait_for(milestone()) :: t()
```

# `wkc_offset`

```elixir
@spec wkc_offset(integer()) :: t()
```

---

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