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()