defmodule Modbux.Tcp.Server.Handler do @moduledoc """ A worker for each Modbus Client, handles Client requests. """ alias Modbux.Tcp.Server.Handler alias Modbux.Model.Shared alias Modbux.Tcp use GenServer, restart: :temporary require Logger defstruct model_pid: nil, parent_pid: nil, socket: nil @spec start_link([...]) :: :ignore | {:error, any} | {:ok, pid} def start_link([socket, model_pid, parent_pid]) do GenServer.start_link(__MODULE__, [socket, model_pid, parent_pid]) end def init([socket, model_pid, parent_pid]) do {:ok, %Handler{model_pid: model_pid, socket: socket, parent_pid: parent_pid}} end def handle_info({:tcp, socket, data}, state) do Logger.debug("(#{__MODULE__}) Received: #{inspect(data, base: :hex)} ") {cmd, transid} = Tcp.parse_req(data) Logger.debug("(#{__MODULE__}) Received Modbux request: #{inspect({cmd, transid})}") case Shared.apply(state.model_pid, cmd) do {:ok, values} -> Logger.debug("(#{__MODULE__}) msg send") resp = Tcp.pack_res(cmd, values, transid) if !is_nil(state.parent_pid), do: notify(state.parent_pid, cmd) :ok = :gen_tcp.send(socket, resp) {:error, reason} -> Logger.debug("(#{__MODULE__}) An error has occur: #{reason}") nil -> Logger.debug("(#{__MODULE__}) Message for another slave") end {:noreply, state} end def handle_info({:tcp_closed, _socket}, state) do Logger.debug("(#{__MODULE__}) Socket closed") {:stop, :normal, state} end def handle_info({:tcp_error, socket, reason}, state) do Logger.error("(#{__MODULE__}) TCP error: #{reason}") :gen_tcp.close(socket) {:stop, :normal, state} end def handle_info(:timeout, state) do Logger.debug("(#{__MODULE__}) timeout") :gen_tcp.close(state.socket) {:stop, :normal, state} end def terminate(:normal, _state), do: nil def terminate(reason, state) do Logger.error("(#{__MODULE__}) Error: #{inspect(reason)}") :gen_tcp.close(state.socket) end defp notify(pid, cmd) do send(pid, {:modbus_tcp, {:server_request, cmd}}) end end