
local modbus = require('libmodbus')
local class = require('pl.class')
local stringx = require('pl.stringx')

local Magic = require('luamods.modbus_scan.Magic')

local ModbusClient = class()

function ModbusClient:_init(endpoint)
    local endpoint_split = stringx.split(endpoint, ':')
    if #endpoint_split == 2 then
        self.host = endpoint_split[1]
        self.service = endpoint_split[2]
    else
        self.host = endpoint_split[1]
        self.service = '502' -- Normal Modbus port.
    end

    self.ctx = modbus.new_tcp_pi(self.host, self.service)
    self.ctx:set_response_timeout(5, 0)
    self.ctx:set_error_recovery(modbus.ERROR_RECOVERY_LINK, modbus.ERROR_RECOVERY_LINK)
    --self.ctx:set_debug(true)
end

function ModbusClient:try_connect()
    local connected = false
    local try_count = 0

    repeat
        local _, err = self.ctx:connect()
        if err then
            try_count = try_count + 1
            os.execute('sleep 2')
        else
            connected = true
        end
    until (connected or try_count > 2)

    return connected
end

function ModbusClient:with_slave(slave, action)
    local changed_slave = self.ctx:set_slave(slave)
    if not changed_slave then
        return 'could not change slave'
    end

    return action(self.ctx)
end

function ModbusClient:read_magic(magic)
    local arr, err = self.ctx:read_registers(magic.addr, magic.len)
    if err then
        return nil
    end

    if magic.elem_type == 'string' then
        local str = ''
        for _, c in ipairs(arr) do
            str = str .. string.char(c)
        end

        return Magic(magic.addr, magic.len, magic.elem_type, str)
    elseif magic.elem_type == 'uint' then
        local val = arr[#arr]
        for i = 1, #arr - 1 do
            val = arr[i] << 16 * (#arr - i)
        end

        return Magic(magic.addr, magic.len, magic.elem_type, val)
    end

    return nil
end

function ModbusClient:close()
    self.ctx:close()
end

return ModbusClient
