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}, <<slave, 1, bytes, data::binary>>) do
^bytes = Helper.byte_count(count)
Helper.bin_to_bitlist(count, data)
end
def parse({:ri, slave, _address, count}, <<slave, 2, bytes, data::binary>>) do
^bytes = Helper.byte_count(count)
Helper.bin_to_bitlist(count, data)
end
def parse({:rhr, slave, _address, count}, <<slave, 3, bytes, data::binary>>) do
^bytes = 2 * count
Helper.bin_to_reglist(count, data)
end
def parse({:rir, slave, _address, count}, <<slave, 4, bytes, data::binary>>) do
^bytes = 2 * count
Helper.bin_to_reglist(count, data)
end
def parse({:fc, slave, address, 0}, <<slave, 5, address::16, 0x00, 0x00>>) do
nil
end
def parse({:fc, slave, address, 1}, <<slave, 5, address::16, 0xFF, 0x00>>) do
nil
end
def parse({:phr, slave, address, value}, <<slave, 6, address::16, value::16>>) do
nil
end
def parse({:fc, slave, address, values}, <<slave, 15, address::16, count::16>>) do
^count = Enum.count(values)
nil
end
def parse({:phr, slave, address, values}, <<slave, 16, address::16, count::16>>) 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, <<slave, _fc, _error_code>>) 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)
<<slave, function, bytes, data::binary>>
end
defp write(:d, slave, function, address, value) do
<<slave, function, address::16, Helper.bool_to_byte(value), 0x00>>
end
defp write(:a, slave, function, address, value) do
<<slave, function, address::16, value::16>>
end
defp writes(_type, slave, function, address, values) do
count = Enum.count(values)
<<slave, function, address::16, count::16>>
end
end