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

transport-independent な Modbus server request processor です。

handler は 2-argument function または `{module, argument}` tuple で指定します。
module handler は `handle_request/3` を実装し、unit id と decoded request を受け取ります。

`start_link/1` は統一 startup facade として transport-specific managed server に委譲します。
request processor 自身は UART / socket を所有せず、serial / TCP server loop が framing と
lifecycle を担当して complete request PDU を `respond/3` に渡します。

AtomVM の SSL API で mutual-authenticated server を構築できるようになるまでは、TLS option を
`{:error, :tls_not_supported}` で fail closed します。

# `authorize`

```elixir
@type authorize() :: (term(), unit_id(), AVModbus.PDU.request() -&gt; boolean())
```

# `handler`

```elixir
@type handler() ::
  (unit_id(), AVModbus.PDU.request() -&gt; AVModbus.PDU.result())
  | {module(), term()}
```

# `policy`

```elixir
@type policy() :: %{
  optional(:authorize) =&gt; authorize() | nil,
  optional(:identification) =&gt; AVModbus.Server.Identification.objects() | nil,
  optional(:role) =&gt; term()
}
```

# `unit_id`

```elixir
@type unit_id() :: 0..255
```

# `handle_request`

```elixir
@callback handle_request(unit_id(), AVModbus.PDU.request(), term()) ::
  AVModbus.PDU.result()
```

# `allowed?`

```elixir
@spec allowed?(
  authorize() | nil,
  term(),
  unit_id(),
  AVModbus.PDU.request(),
  pos_integer() | :infinity
) ::
  boolean()
```

transport 自身が処理する request に対して authorization を確認します。

callback がない場合は許可します。callback crash、falsey result、timeout は拒否として扱います。

# `handle_request`

```elixir
@spec handle_request(handler(), unit_id(), AVModbus.PDU.request()) ::
  AVModbus.PDU.result()
```

wire response を encode せず server handler を呼び出します。

# `handle_request`

```elixir
@spec handle_request(
  handler(),
  unit_id(),
  AVModbus.PDU.request(),
  pos_integer() | :infinity
) ::
  AVModbus.PDU.result()
```

handler を isolated process で timeout 付き実行します。

# `port`

```elixir
@spec port(pid() | atom()) :: 0..65535 | {:error, term()}
```

TCP server が listen している port を返します。

# `respond`

```elixir
@spec respond(handler(), unit_id(), binary()) :: {:ok, binary()} | :ignore
```

request PDU を decode し、handler を呼び出して response PDU を encode します。

malformed request は Modbus exception response に変換します。empty PDU や public request range 外の
function byte は valid response function を導けないため無視します。

# `respond`

```elixir
@spec respond(handler(), unit_id(), binary(), pos_integer() | :infinity) ::
  {:ok, binary()} | :ignore
```

`respond/3` に handler execution timeout を追加した variant です。

handler は monitored process で実行され、crash / timeout は transport process を落とさず
`server_device_failure` response に変換されます。

# `respond`

```elixir
@spec respond(handler(), unit_id(), binary(), pos_integer() | :infinity, policy()) ::
  {:ok, binary()} | :ignore
```

`respond/4` に authorization、peer role、device identification policy を追加した variant です。

policy key は `:authorize`、`:role`、`:identification` です。authorization と response generation は
同じ handler timeout の中で 1 つの isolated process として実行します。

# `start_link`

```elixir
@spec start_link(keyword()) :: {:ok, pid()} | {:error, term()}
```

managed Modbus server を起動し、raw PID を返します。

`:transport` は必須で、`:tcp`、`:rtu`、`:ascii` のいずれかを指定します。
`:handler` と選択した transport module の options を同じ list に指定します。
`transport: :tls`、`tls:`、`ssl:` は現在 `{:error, :tls_not_supported}` を返します。

# `status`

```elixir
@spec status(pid() | atom()) :: term()
```

managed server の現在の transport state を返します。

# `stop`

```elixir
@spec stop(pid() | atom()) :: :ok
```

managed server を停止し、所有する listener または UART を閉じます。

---

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