defmodule Modbux.Response do @moduledoc """ Response helper, functions that handles Server & Slave response messages. """ alias Modbux.Helper @exceptions %{ 1 => :efun, 2 => :eaddr, 3 => :einval, 4 => :edevice, 5 => :ack, 6 => :sbusy, 7 => :nack, 8 => :ememp, 9 => :error, 10 => :egpath, 11 => :egtarg } @spec pack({:fc | :phr | :rc | :rhr | :ri | :rir, integer, any, maybe_improper_list | integer}, any) :: <<_::24, _::_*8>> def pack({:rc, slave, _address, count}, values) do ^count = Enum.count(values) data = Helper.bitlist_to_bin(values) reads(slave, 1, data) end def pack({:ri, slave, _address, count}, values) do ^count = Enum.count(values) data = Helper.bitlist_to_bin(values) reads(slave, 2, data) end def pack({:rhr, slave, _address, count}, values) do ^count = Enum.count(values) data = Helper.reglist_to_bin(values) reads(slave, 3, data) end def pack({:rir, slave, _address, count}, values) do ^count = Enum.count(values) data = Helper.reglist_to_bin(values) reads(slave, 4, data) end def pack({:fc, slave, address, value}, nil) when is_integer(value) do write(:d, slave, 5, address, value) end def pack({:phr, slave, address, value}, nil) when is_integer(value) do write(:a, slave, 6, address, value) end def pack({:fc, slave, address, values}, nil) when is_list(values) do writes(:d, slave, 15, address, values) end def pack({:phr, slave, address, values}, nil) when is_list(values) do writes(:a, slave, 16, address, values) end @spec parse(any, <<_::24, _::_*8>>) :: nil | [any] | {:error, any} | {:error, byte, <<_::104>>} def parse({:rc, slave, _address, count}, <>) do ^bytes = Helper.byte_count(count) Helper.bin_to_bitlist(count, data) end def parse({:ri, slave, _address, count}, <>) do ^bytes = Helper.byte_count(count) Helper.bin_to_bitlist(count, data) end def parse({:rhr, slave, _address, count}, <>) do ^bytes = 2 * count Helper.bin_to_reglist(count, data) end def parse({:rir, slave, _address, count}, <>) do ^bytes = 2 * count Helper.bin_to_reglist(count, data) end def parse({:fc, slave, address, 0}, <>) do nil end def parse({:fc, slave, address, 1}, <>) do nil end def parse({:phr, slave, address, value}, <>) do nil end def parse({:fc, slave, address, values}, <>) do ^count = Enum.count(values) nil end def parse({:phr, slave, address, values}, <>) do ^count = Enum.count(values) nil end # Error messages. def parse(_cmd, <<_slave, _fc, error_code>>) when error_code in 1..11 do {:error, @exceptions[error_code]} end def parse(_cmd, <>) do {:error, slave, "Unknown error"} end @spec length({:fc | :phr | :rc | :rhr | :ri | :rir, any, any, any}) :: number def length({:rc, _slave, _address, count}) do 3 + Helper.byte_count(count) end def length({:ri, _slave, _address, count}) do 3 + Helper.byte_count(count) end def length({:rhr, _slave, _address, count}) do 3 + 2 * count end def length({:rir, _slave, _address, count}) do 3 + 2 * count end def length({:fc, _slave, _address, _}) do 6 end def length({:phr, _slave, _address, _}) do 6 end defp reads(slave, function, data) do bytes = :erlang.byte_size(data) <> end defp write(:d, slave, function, address, value) do <> end defp write(:a, slave, function, address, value) do <> end defp writes(_type, slave, function, address, values) do count = Enum.count(values) <> end end