commit 583f9b189f449a53d74c586c0737eb96b8dbd7d5
parent 03c8bf4a6af1a1574dd4976bbdc6f44ddeb22c39
Author: Leah (ctucx) <leah@ctu.cx>
Date: Sun, 14 Feb 2021 19:10:12 +0100
parent 03c8bf4a6af1a1574dd4976bbdc6f44ddeb22c39
Author: Leah (ctucx) <leah@ctu.cx>
Date: Sun, 14 Feb 2021 19:10:12 +0100
replace native modbus implementation with libmodbus again
6 files changed, 77 insertions(+), 135 deletions(-)
diff --git a/src/backend_powermeter.nim b/src/backend_powermeter.nim @@ -8,11 +8,11 @@ import vars import times proc updatePowermeter(key: string, device: DeviceConfig) {.async.} = - let voltage = mbFloatDCBA(await readInputRegisters(device.address, 0, 2)) - let frequency = mbFloatDCBA(await readInputRegisters(device.address, 70, 2)) - let `import` = mbFloatDCBA(await readInputRegisters(device.address, 72, 2)) - let cosphi = mbFloatDCBA(await readInputRegisters(device.address, 30, 2)) - let power = mbFloatDCBA(await readInputRegisters(device.address, 12, 2)) + let voltage = await mb.asyncReadFloat(device.address, 0) + let frequency = await mb.asyncReadFloat(device.address, 70) + let `import` = await mb.asyncReadFloat(device.address, 72) + let cosphi = await mb.asyncReadFloat(device.address, 30) + let power = await mb.asyncReadFloat(device.address, 12) server.state[key].voltage = voltage server.state[key].frequency = frequency
diff --git a/src/backend_relayboard.nim b/src/backend_relayboard.nim @@ -3,7 +3,6 @@ import types import modbus import tables import vars -import sequtils proc updateRelayboards() {.async.} = echo "updating relayboards" @@ -12,7 +11,7 @@ proc updateRelayboards() {.async.} = continue try: - let data = map(await readRegisters(device.address, device.firstRegister, device.count), proc (i: uint16): bool = i != 0) + let data = await mb.asyncReadBits(device.address, device.firstRegister, device.count) server.state[key] = DeviceState(type: RelayBoard, relays: data) except: let e = getCurrentException()
diff --git a/src/modbus.nim b/src/modbus.nim @@ -1,135 +1,78 @@ -import asyncnet +import types +import tables +import sequtils import asyncdispatch -import strutils +import posix import vars -import tables - -### modbus general ### - -var sock {.threadvar.}: AsyncSocket -var transaction_id {.threadvar.}: uint16 -var transactions {.threadvar.}: Table[uint16, proc(msg: string)] - -proc mkPacket_mbTcp(mb_packet: string): string = - inc(transaction_id) - return parseHexStr(toHex(transaction_id) & toHex(0u16) & toHex(uint16(len(mb_packet)))) & mb_packet - -proc reconnect() {.async.} = - echo "verbindung putt, ich fix das mal" - await sleepAsync(5000) - echo "jetzt" - sock = await asyncnet.dial(server.config.modbusAddr, Port(server.config.modbusPort)) - -proc readPacket_mbTcp(): Future[(uint16, string)] {.async.} = - var res = "" - - res = await sock.recv(8) - while res == "": - await reconnect() - res = await sock.recv(8) - let transaction_id = fromHex[uint16](toHex(res[0..1])) - let length = fromHex[uint16](toHex(res[4..5])) - let function_code = cast[uint8](res[7]) - - res = await sock.recv(int(length) - 2) - while res == "": - await reconnect() - res = await sock.recv(8) - - if function_code >= 128u8: - raise newException(OsError, "mordbus error: " & toHex(res)) - - return (transaction_id, res) - -proc processAnswers() {.async.} = - while true: - try: - let (transaction_id, mb_packet) = await readPacket_mbTcp() - transactions[transaction_id](mb_packet) - except: - let e = getCurrentException() - echo("error while processing mordbus answer: ", e.msg) - -proc doRequest[T](req: string, parse_proc: proc(foo: string): T): Future[T] = +{.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]() - - let tcp_packet = mkPacket_mbTcp(req) - asyncCheck sock.send(tcp_packet) - transactions[transaction_id] = proc(answer: string) = - #transactions.del(transaction_id) - fut.complete(parse_proc(answer)) - - return fut - -proc retry[T](req: string, parse_proc: proc(foo: string): T): Future[T] {.async.} = var retries = 5 - var res: T - while retries > 0: - try: - res = await doRequest(req, parse_proc) - return res - except: + 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 - let e = getCurrentException() - echo("error while processing mordbus answer: ", e.msg) - -### readInputRegisters ### - -proc mkPacket_readInputRegisters(unit_id: uint8, address: uint16, count: uint16): string = - return parseHexStr(toHex(unit_id) & toHex(4u8) & toHex(address) & toHex(count)) - -proc parsePacket_readInputRegisters(packet: string): seq[uint16] = - var res: seq[uint16] = @[] - let bytes = cast[uint8](packet[0]) - var i = 1 - while i < int(bytes): - res.add(fromHex[uint16](toHex(packet[i..i+1]))) - i += 2 - return res - -proc readInputRegisters*(unit_id: uint8, address: uint16, count: uint16): Future[seq[uint16]] {.async.} = - return await retry(mkPacket_readInputRegisters(unit_id, address, count), parsePacket_readInputRegisters) - -### readRegisters ### - -proc mkPacket_readRegisters(unit_id: uint8, address: uint16, count: uint16): string = - return parseHexStr(toHex(unit_id) & toHex(3u8) & toHex(address) & toHex(count)) - -proc parsePacket_readRegisters(packet: string): seq[uint16] = - var res: seq[uint16] = @[] - let bytes = cast[uint8](packet[0]) - var i = 1 - while i < int(bytes): - res.add(fromHex[uint16](toHex(packet[i..i+1]))) - i += 2 - return res + if retries == 0: + echo "failing" + let errmsg = $(modbus_strerror(errno)) + fut.fail(newException(OsError, "modbus request failed: " & errmsg)) + else: + addTimer(100, true, timerFunc) -proc readRegisters*(unit_id: uint8, address: uint16, count: uint16): Future[seq[uint16]] {.async.} = - return await retry(mkPacket_readRegisters(unit_id, address, count), parsePacket_readRegisters) + return true -### writeRegister ### - -proc mkPacket_writeRegister(unit_id: uint8, address: uint16, value: uint16): string = - return parseHexStr(toHex(unit_id) & toHex(6u8) & toHex(address) & toHex(value)) - -proc parsePacket_writeRegister(packet: string): bool = - return true - -proc writeRegister*(unit_id: uint8, address: uint16, value: uint16): Future[bool] {.async.} = - return await retry(mkPacket_writeRegister(unit_id, address, value), parsePacket_writeRegister) - -### conversion ### - -proc mbFloatDCBA*(input: seq[uint16]): float32 = - let i: uint32 = uint32(input[0]) * 65536u32 + uint32(input[1]) - return cast[float32](i) - -### main ### + addTimer(1, true, timerFunc) + return fut -proc initModbus*() {.async.} = - sock = await asyncnet.dial(server.config.modbusAddr, Port(server.config.modbusPort)) - transaction_id = 0u16 - transactions = initTable[uint16, proc(msg: string)]() - asyncCheck processAnswers() +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 port: cint = int32(server.config.modbusPort) + mb = modbus_new_tcp(server.config.modbusAddr, port) + discard mb.modbus_connect() + +proc deinitModbus*() = + mb.modbus_close() + mb.modbus_free()
diff --git a/src/smartied.nim b/src/smartied.nim @@ -18,7 +18,7 @@ if getEnv("ACCESS_TOKEN") != "": proc main() {.async.} = initUtil() - await initModbus() + initModbus() initBackendPowermeter() initBackendRelayboard() initBackendLacrosse()
diff --git a/src/util.nim b/src/util.nim @@ -41,7 +41,7 @@ proc handleRequest*(client: Client, req: string): Future[JsonNode] {.async.} = let config = server.config.devices[action.setRelayBoard] server.state[action.setRelayBoard].relays[action.setRelay] = action.setValue - discard await writeRegister(config.address, config.firstRegister + action.setRelay, uint16(server.state[action.setRelayBoard].relays[action.setRelay])) + await mb.asyncWriteBit(config.address, config.firstRegister + action.setRelay, server.state[action.setRelayBoard].relays[action.setRelay]) broadcast($(%*server.state)) return JsonNode() @@ -49,7 +49,7 @@ proc handleRequest*(client: Client, req: string): Future[JsonNode] {.async.} = let config = server.config.devices[action.toggleRelayBoard] server.state[action.toggleRelayBoard].relays[action.toggleRelay] = not server.state[action.toggleRelayBoard].relays[action.toggleRelay] - discard await writeRegister(config.address, config.firstRegister + action.toggleRelay, uint16(server.state[action.toggleRelayBoard].relays[action.toggleRelay])) + await mb.asyncWriteBit(config.address, config.firstRegister + action.toggleRelay, server.state[action.toggleRelayBoard].relays[action.toggleRelay]) broadcast($(%*server.state)) return JsonNode()