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

IEC 60870-5-101 FT 1.2 frame codec and stream deframer (`serial_transceiver_ft_1_2.c` & `link_layer.c`).

Supports:
- Single-character ACK (`0xE5`): `:single_char_ack`
- Fixed-length frame (`0x10, C, Addr..., CS, 0x16`):
  `{:fixed_frame, control_map, address}`
- Variable-length frame (`0x68, L, L, 0x68, C, Addr..., UserData..., CS, 0x16`):
  `{:variable_frame, control_map, address, user_data}`

# `control`

```elixir
@type control() :: %{
  dir: boolean(),
  prm: boolean(),
  fcb_or_acd: boolean(),
  fcv_or_dfc: boolean(),
  fc: 0..15
}
```

# `t`

```elixir
@type t() ::
  :single_char_ack
  | {:fixed_frame, control(), non_neg_integer()}
  | {:variable_frame, control(), non_neg_integer(), binary()}
```

# `checksum`

```elixir
@spec checksum(binary()) :: non_neg_integer()
```

Computes 8-bit modulo-256 sum of bytes in `bin`.

# `decode`

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

Decodes a single complete FT 1.2 frame given `address_length` (`0`, `1`, or `2`).

# `decode_address`

```elixir
@spec decode_address(binary(), 0..2) ::
  {:ok, non_neg_integer(), binary()} | {:error, term()}
```

Decodes a link layer address according to `address_length` (`0`, `1`, or `2`).

# `decode_control`

```elixir
@spec decode_control(non_neg_integer()) :: control()
```

Decodes an 8-bit control field byte into a map.

# `encode`

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

Encodes an FT 1.2 frame into wire binary given `address_length` (`0`, `1`, or `2`).

# `encode_address`

```elixir
@spec encode_address(non_neg_integer(), 0..2) :: binary()
```

Encodes a link layer address according to `address_length` (`0`, `1`, or `2`).

# `encode_control`

```elixir
@spec encode_control(map()) :: non_neg_integer()
```

Encodes a control field map into an 8-bit integer.

# `extract_frames`

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

Extracts complete FT 1.2 frames from a stream buffer given `address_length`.
Returns `{:ok, frames, remaining_buffer}` or `{:error, reason, remaining_buffer}`.

# `fc_ack`

# `fc_nack`

# `fc_request_for_access_demand`

# `fc_request_link_status`

# `fc_request_user_data_class_1`

# `fc_request_user_data_class_2`

# `fc_reset_fcb`

# `fc_reset_remote_link`

# `fc_reset_user_process`

# `fc_resp_nack_no_data`

# `fc_resp_user_data`

# `fc_service_not_functioning`

# `fc_service_not_implemented`

# `fc_status_of_link_or_access_demand`

# `fc_test_function_for_link`

# `fc_user_data_confirmed`

# `fc_user_data_no_reply`

---

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