defmodule Modbux.Tcp do @moduledoc """ Tcp message helper, functions that handles TCP responses/requests messages. """ alias Modbux.Request alias Modbux.Response require Logger @spec pack_req( {:fc | :phr | :rc | :rhr | :ri | :rir, integer, integer, maybe_improper_list | integer}, integer ) :: <<_::48, _::_*8>> def pack_req(cmd, transid) do cmd |> Request.pack() |> wrap(transid) end @spec parse_req(<<_::48, _::_*8>>) :: {{:einval | :error | :fc | :phr | :rc | :rhr | :ri | :rir, byte, char, [any] | char}, char} def parse_req(wraped) do {pack, transid} = wraped |> unwrap {pack |> Request.parse(), transid} end @spec pack_res( {:fc | :phr | :rc | :rhr | :ri | :rir, integer, any, maybe_improper_list | integer}, any, integer ) :: <<_::48, _::_*8>> def pack_res(cmd, values, transid) do cmd |> Response.pack(values) |> wrap(transid) end @spec parse_res(any, <<_::48, _::_*8>>, char) :: nil | [any] | {:error, any} | {:error, byte, <<_::104>>} def parse_res(cmd, wraped, transid) do Response.parse(cmd, wraped |> unwrap(transid)) end @spec res_len({:fc | :phr | :rc | :rhr | :ri | :rir, any, any, any}) :: number def res_len(cmd) do Response.length(cmd) + 6 end @spec req_len({:fc | :phr | :rc | :rhr | :ri | :rir, any, any, any}) :: integer def req_len(cmd) do Request.length(cmd) + 6 end @spec wrap(binary, integer) :: <<_::48, _::_*8>> def wrap(payload, transid) do size = :erlang.byte_size(payload) <> end @spec unwrap(<<_::48, _::_*8>>, char) :: nil | binary def unwrap(<> = msg, transid) do r_size = :erlang.byte_size(payload) data = if size == r_size do payload else Logger.error( "#{__MODULE__} size = #{size}, payload_size = #{r_size}, msg = #{inspect(msg, base: :hex)}" ) nil end data end @spec unwrap(<<_::48, _::_*8>>) :: {binary(), char()} def unwrap(<>) do ^size = :erlang.byte_size(payload) {payload, transid} end def unwrap(inv_data) do Logger.error("#{__MODULE__} invalid data: #{inspect(inv_data, base: :hex)}") raise("#{__MODULE__} invalid data: #{inspect(inv_data)}") end end