# `IEC60870.CS104.Frame`
[🔗](https://github.com/Ultraluv/ex_iec60870/blob/main/lib/iec60870/cs104/frame.ex#L1)

IEC 60870-5-104 APCI frame codec and stream deframer (`cs104_frame.c`).

Supports:
- `:i_frame`: `{:i_frame, send_seq, recv_seq, asdu_binary}`
- `:s_frame`: `{:s_frame, recv_seq}`
- `:u_frame`: `{:u_frame, :startdt_act | :startdt_con | :stopdt_act | :stopdt_con | :testfr_act | :testfr_con}`

# `t`

```elixir
@type t() ::
  {:i_frame, non_neg_integer(), non_neg_integer(), binary()}
  | {:s_frame, non_neg_integer()}
  | {:u_frame, u_type()}
```

# `u_type`

```elixir
@type u_type() ::
  :startdt_act
  | :startdt_con
  | :stopdt_act
  | :stopdt_con
  | :testfr_act
  | :testfr_con
```

# `check_sequence_number`

```elixir
@spec check_sequence_number([map()], non_neg_integer(), non_neg_integer()) ::
  {:ok, [map()], [map()]} | {:error, :sequence_number_out_of_range}
```

Validates whether `ack_seq_no` is within the sliding window of unacknowledged sent ASDUs
(each item is `%{seq_no: integer(), sent_time: integer()}`).
Returns `{:ok, remaining_sent_asdus}` or `{:error, :sequence_number_out_of_range}`.
Matches `checkSequenceNumber` in `cs104_connection.c` / `cs104_slave.c`.

# `decode`

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

Decodes a single complete 104 APCI frame binary (starting with `0x68`).

# `encode`

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

Encodes a 104 APCI frame to wire binary.

# `extract_frames`

```elixir
@spec extract_frames(binary()) :: {:ok, [t()], binary()} | {:error, term()}
```

Extracts complete APCI frames from a TCP byte stream buffer.
Returns `{:ok, frames, remaining_buffer}` or `{:error, reason}`.

---

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