ctucx.git: smartied

[nimlang] smarthome server

1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 
33 
34 
35 
36 
37 
38 
39 
40 
41 
42 
43 
44 
45 
46 
47 
48 
49 
50 
51 
52 
53 
54 
55 
56 
57 
58 
59 
60 
61 
62 
63 
64 
65 
66 
67 
68 
69 
70 
71 
72 
73 
74 
75 
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()