# `AVModbus.PDU`
[🔗](https://github.com/piyopiyoex/avmodbus/blob/v0.1.0/lib/avmodbus/pdu.ex#L1)

transport-independent な Modbus request / response codec です。

request は tuple で表現し、同じ protocol core を client / server と RTU / ASCII で共有します。

# `address`

```elixir
@type address() :: 0..65535
```

# `exception`

```elixir
@type exception() ::
  :illegal_function
  | :illegal_data_address
  | :illegal_data_value
  | :server_device_failure
  | :acknowledge
  | :server_device_busy
  | :memory_parity_error
  | :gateway_path_unavailable
  | :gateway_target_device_failed_to_respond
  | byte()
```

# `request`

```elixir
@type request() ::
  {:read_coils, address(), pos_integer()}
  | {:read_discrete_inputs, address(), pos_integer()}
  | {:read_holding_registers, address(), pos_integer()}
  | {:read_input_registers, address(), pos_integer()}
  | {:write_single_coil, address(), boolean()}
  | {:write_single_register, address(), word()}
  | {:write_multiple_coils, address(), [boolean()]}
  | {:write_multiple_registers, address(), [word()]}
  | {:mask_write_register, address(), word(), word()}
  | {:read_write_multiple_registers, address(), pos_integer(), address(),
     [word()]}
  | :read_exception_status
  | {:diagnostics, word(), [word()]}
  | :get_comm_event_counter
  | :get_comm_event_log
  | :report_server_id
  | {:read_file_record, [{1..65535, 0..9999, pos_integer()}]}
  | {:write_file_record, [{1..65535, 0..9999, [word()]}]}
  | {:read_fifo_queue, address()}
  | {:read_device_identification, :basic | :regular | :extended | :individual,
     byte()}
  | {:encapsulated_interface_transport, byte(), binary()}
  | {:custom, 1..127, binary()}
```

# `result`

```elixir
@type result() :: :ok | {:ok, term()} | {:error, {:exception, exception()}}
```

# `word`

```elixir
@type word() :: 0..65535
```

# `broadcast_request?`

```elixir
@spec broadcast_request?(request()) :: boolean()
```

request を serial-line broadcast unit `0` に送信できるか返します。

server は unit `0` に response を返さないため、read component を含む request は broadcast できません。

# `decode_request`

```elixir
@spec decode_request(binary()) :: {:ok, request()} | {:error, exception()}
```

server 向け request PDU を decode / validate します。

defined function で shape が不正な request は wire に返すべき Modbus exception を返します。
undefined public function code は custom request として保持します。

# `decode_response`

```elixir
@spec decode_response(request(), binary()) :: {:ok, term()} | :ok | {:error, term()}
```

response PDU を request と照合して decode します。

# `encode_exception`

```elixir
@spec encode_exception(byte(), exception()) :: {:ok, binary()} | {:error, term()}
```

request function code に対する Modbus exception response を encode します。

# `encode_request`

```elixir
@spec encode_request(request()) :: {:ok, binary()} | {:error, term()}
```

# `encode_response`

```elixir
@spec encode_response(request(), result()) :: {:ok, binary()} | {:error, term()}
```

server handler result を response PDU に encode します。

returned value count や write echo を request と照合し、invalid handler result は wire に出しません。

# `function`

```elixir
@spec function(request()) :: {:ok, 1..127} | {:error, :unsupported_request}
```

# `response_length`

```elixir
@spec response_length(request(), binary()) ::
  {:ok, pos_integer()} | :more | :unknown | :invalid
```

partial response から expected PDU length を返します。

RTU stream parser は sub-millisecond の inter-frame silent interval scheduling に依存せず
frame boundary を判定できます。

---

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