import asyncdispatch, options, posix, tables import types, vars {.passL: "-lmodbus".} proc modbus_new_tcp*(ad: cstring, port: cint): modbus {.importc, dynlib: "libmodbus.so.5"} proc modbus_connect*(mb: modbus): cint {.importc.} proc modbus_close*(mb: modbus): void {.importc.} proc modbus_free*(mb: modbus): void {.importc.} proc modbus_set_slave*(mb: modbus, ad: cint): void {.importc.} proc modbus_read_bits*(mb: modbus, ad: cint, nb: cint, dest: pointer): cint {.importc.} proc modbus_write_bit*(mb: modbus, ad: cint, status: cint): cint {.importc.} proc modbus_read_input_registers*(mb: modbus, ad: cint, nb: cint, dest: pointer): cint {.importc.} proc modbus_get_float_dcba*(src: pointer): cfloat {.importc.} proc modbus_strerror*(src: cint): cstring {.importc.} proc retry*[T](mb: modbus, fun: proc(): (cint, T) {.gcsafe.}): Future[T] = var fut = newFuture[T]() var retries = 5 proc timerFunc(fd: AsyncFD): bool {.gcsafe.} = let (status, res) = fun() if status != -1: fut.complete(res) else: if errno == EBADF or errno == ECONNRESET or errno == EPIPE or errno == 112345691: mb.modbus_close() discard mb.modbus_connect() echo("modbus request try ", 6 - retries, " failed: ", modbus_strerror(errno)) retries = retries - 1 if retries == 0: echo "failing" let errmsg = $(modbus_strerror(errno)) fut.fail(newException(OsError, "modbus request failed: " & errmsg)) else: addTimer(100, true, timerFunc) return true addTimer(1, true, timerFunc) return fut proc asyncReadFloat*(mb: modbus, ad: uint8, reg: uint8): Future[float32] {.async.} = return await mb.retry(proc (): (cint, float32) = var data = [0u32] mb.modbus_set_slave(cint(ad)) let status = mb.modbus_read_input_registers(cint(reg), 2, data.addr) let res = modbus_get_float_dcba(data.addr) return (status, float32(res)) ) proc asyncWriteBit*(mb: modbus, ad: uint8, reg: uint8, val: bool): Future[void] {.async.} = discard await mb.retry(proc (): (cint, bool) = mb.modbus_set_slave(cint(ad)) let status = mb.modbus_write_bit(cint(reg), cint(val)) return (status, false) ) proc asyncReadBits*(mb: modbus, ad: uint8, reg: uint8, nb: uint8): Future[seq[bool]] {.async.} = var data: array[256, bool] discard await mb.retry(proc (): (cint, bool) = mb.modbus_set_slave(cint(ad)) let status = mb.modbus_read_bits(cint(reg), cint(nb), data.addr) return (status, false) ) return @data[0..nb-1] proc initModbus*() = let config = server.config.serverConfig.modbus.get let port: cint = int32(config.port) mb = modbus_new_tcp(config.host, port) discard mb.modbus_connect() proc deinitModbus*() = mb.modbus_close() mb.modbus_free()