commit 113bca34ac40a6ce147a4462bc19516b50159366
parent bcd32ef15c1a9820ab3ca150843d2e8a19eef410
Author: Milan Pässler <me@pbb.lc>
Date: Sun, 14 Jul 2019 13:19:20 +0200
parent bcd32ef15c1a9820ab3ca150843d2e8a19eef410
Author: Milan Pässler <me@pbb.lc>
Date: Sun, 14 Jul 2019 13:19:20 +0200
compiling but crashing
11 files changed, 163 insertions(+), 91 deletions(-)
diff --git a/src/backend_powermeter.nim b/src/backend_powermeter.nim @@ -0,0 +1,49 @@ +import asyncdispatch +import modbus +import types +import tables +import util +import json + +for key, device in server.config.devices.pairs(): + if device.type != PowerMeter: + continue + + server.state[key] = DeviceState( + type: PowerMeter, + power: 0f, + cosphi: 0f, + voltage: 0f, + `import`: 0f, + frequency: 0f + ) + +proc updatePowermeter(key: string, device: DeviceConfig) {.async.} = + echo device.address + server.state[key].voltage = await mb.asyncReadFloat(device.address, 0) + server.state[key].frequency = await mb.asyncReadFloat(device.address, 70) + server.state[key].`import` = await mb.asyncReadFloat(device.address, 72) + server.state[key].cosphi = await mb.asyncReadFloat(device.address, 30) + server.state[key].power = await mb.asyncReadFloat(device.address, 12) + + broadcast($(%*server.state)) + +proc updatePowermeters() {.async.} = + echo "updating powermeters" + for key, device in server.config.devices.pairs(): + if device.type != PowerMeter: + continue + + try: + await updatePowermeter(key, device) + except: + echo("error while updating powermeter ", key) + +proc timerFunc(fd: AsyncFD): bool {.gcsafe.} = + let fut = updatePowermeters() + fut.addCallback(proc () {.gcsafe.} = + addTimer(int(server.config.powermeterUpdateIntervalSec * 1000), true, timerFunc) + ) + return false + +addTimer(1000, true, timerFunc)
diff --git a/src/backend_relayboard.nim b/src/backend_relayboard.nim @@ -0,0 +1,12 @@ +import types +import modbus +import tables + +for key, device in server.config.devices.pairs(): + if device.type != RelayBoard: + continue + + var data: array[255, bool] + mb.modbus_set_slave(cint(device.address)) + discard mb.modbus_read_bits(cint(device.firstRegister), cint(device.count), data.addr) + server.state[key] = DeviceState(type: RelayBoard, relays: @data[0..device.count-1])
diff --git a/src/frontend_http.nim b/src/frontend_http.nim @@ -0,0 +1,19 @@ +import asynchttpserver +import asyncdispatch +import util +import types +import sequtils +import json +import vars + +proc processHttpClient(req: Request) {.async.} = + if req.reqMethod == HttpGet: + await req.respond(Http200, $(%* server.state)) + elif req.reqMethod == HttpPost: + await req.respond(Http200, await tryHandleRequest(req.body)) + else: + await req.respond(Http405, "405 Method Not Allowed") + +proc serveHttp*() {.async.} = + var httpServer = newAsyncHttpServer() + await httpServer.serve(Port(server.config.httpPort), processHttpClient)
diff --git a/src/frontend_tcp.nim b/src/frontend_tcp.nim @@ -4,18 +4,15 @@ import util import types import sequtils import json - -var clients: seq[AsyncSocket] = @[] +import vars proc broadcast(msg: string) {.locks: true.}= - for client in clients: + for client in tcpClients: try: asyncCheck client.send(msg) except: client.close() - clients.keepIf(proc(x: AsyncSocket): bool = x != client) - -registerBroadcastHandler(broadcast) + tcpClients.keepIf(proc(x: AsyncSocket): bool = x != client) proc processTcpClient(client: AsyncSocket) {.async.} = try: @@ -30,17 +27,18 @@ proc processTcpClient(client: AsyncSocket) {.async.} = await client.send(resp & '\n') except: client.close() - clients.keepIf(proc(x: AsyncSocket): bool = x != client) + tcpClients.keepIf(proc(x: AsyncSocket): bool = x != client) proc serveTcp*() {.async.} = + registerBroadcastHandler(broadcast) + tcpClients = @[] + var socket = newAsyncSocket() socket.setSockOpt(OptReuseAddr, true) socket.bindAddr(Port(server.config.tcpPort)) socket.listen() - echo("listening on port ", server.config.tcpPort) - while true: let client = await socket.accept() - clients.add(client) - asyncCheck processTcpClient(client)- \ No newline at end of file + tcpClients.add(client) + asyncCheck processTcpClient(client)
diff --git a/src/frontend_ws.nim b/src/frontend_ws.nim @@ -39,4 +39,4 @@ proc processWsClient(req: Request) {.gcsafe, async.} = proc serveWs*() {.async.} = var httpServer = newAsyncHttpServer() - asyncCheck httpServer.serve(Port(server.config.wsPort), processWsClient) + await httpServer.serve(Port(server.config.wsPort), processWsClient)
diff --git a/src/modbus.nim b/src/modbus.nim @@ -1,6 +1,7 @@ import types import tables import sequtils +import asyncdispatch {.passL: "-lmodbus".} type modbus = ref object @@ -11,41 +12,60 @@ 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_registers*(mb: modbus, ad: cint, nb: cint, dest: pointer): cint {.importc.} -proc modbus_get_float_abcd*(mb: modbus, src: pointer): cfloat {.importc.} - -var mb*: modbus - -proc modbus_read_float*(mb: modbus, ad: cint): cfloat = - var first = 0u16 - var second = 0u16 - echo mb.modbus_read_registers(cint(ad), 1, first.addr) - echo mb.modbus_read_registers(cint(ad+1), 1, second.addr) - let res = float32(uint32(second) + (uint32(first) * 65536)) - echo(ad, ' ', first, ' ', second, ' ', res) - return res - -proc initModbus*() = - let port: cint = int32(server.config.modbusPort) - mb = modbus_new_tcp(server.config.modbusAddr, port) - discard mb.modbus_connect() - - for key, device in server.config.devices.pairs(): - if device.type == PowerMeter: - server.state[key] = DeviceState( - type: PowerMeter, - power: 0f, - cosphi: 0f, - voltage: 0f, - `import`: 0f, - frequency: 0f - ) - elif device.type == RelayBoard: - var data: array[255, bool] - mb.modbus_set_slave(cint(device.address)) - discard mb.modbus_read_bits(cint(device.firstRegister), cint(device.count), data.addr) - server.state[key] = DeviceState(type: RelayBoard, relays: @data[0..device.count-1]) +proc modbus_read_input_registers*(mb: modbus, ad: cint, nb: cint, dest: pointer): cint {.importc.} +proc modbus_get_float_dcba*(src: pointer): cfloat {.importc.} + +let port: cint = int32(server.config.modbusPort) +var mb* = modbus_new_tcp(server.config.modbusAddr, port) + +discard mb.modbus_connect() + +proc asyncReadFloat*(mb: modbus, ad: uint8, reg: uint8): Future[float32] = + var fut = newFuture[float32]() + var retries = 5 + var data = [0u32] + + proc timerFunc(fd: AsyncFD): bool {.gcsafe.} = + mb.modbus_set_slave(cint(ad)) + + if mb.modbus_read_input_registers(cint(reg), 2, data.addr) != -1: + fut.complete(modbus_get_float_dcba(data.addr)) + else: + echo("modbus request try ", 6 - retries, " failed") + retries = retries - 1 + if retries == 0: + echo "failing" + fut.fail(newException(OsError, "modbus request failed")) + else: + addTimer(500, true, timerFunc) + + return true + + addTimer(1, true, timerFunc) + return fut + +proc asyncWriteBit*(mb: modbus, ad: uint8, reg: uint8, val: bool): Future[void] = + var fut = newFuture[void]() + var retries = 5 + + proc timerFunc(fd: AsyncFD): bool {.gcsafe.} = + mb.modbus_set_slave(cint(ad)) + + if mb.modbus_write_bit(cint(reg), cint(val)) != -1: + fut.complete() + else: + echo("modbus request try ", 6 - retries, " failed") + retries = retries - 1 + if retries == 0: + fut.fail(newException(OsError, "modbus request failed")) + else: + addTimer(500, true, timerFunc) + + return true + + addTimer(1, true, timerFunc) + return fut proc deinitModbus*() = mb.modbus_close() - mb.modbus_free()- \ No newline at end of file + mb.modbus_free()
diff --git a/src/powermeter.nim b/src/powermeter.nim @@ -1,27 +0,0 @@ -import asyncdispatch -import modbus -import types -import tables -import util -import json - -proc updatePowermeters*(fd: AsyncFD): bool {.gcsafe.} = - echo "updating powermeters" - for key, device in server.config.devices.pairs(): - if device.type != PowerMeter: - continue - - echo device.address - mb.modbus_set_slave(cint(device.address)) - server.state[key].voltage = mb.modbus_read_float(0) - server.state[key].frequency = mb.modbus_read_float(70) - server.state[key].`import` = mb.modbus_read_float(72) - server.state[key].cosphi = mb.modbus_read_float(30) - server.state[key].power = mb.modbus_read_float(12) - - broadcast($(%*server.state)) - - addTimer(int(server.config.powermeterUpdateIntervalSec * 1000), true, updatePowermeters) - return false - -addTimer(1000, true, updatePowermeters)- \ No newline at end of file
diff --git a/src/smartied.nim b/src/smartied.nim @@ -1,15 +1,14 @@ import asyncdispatch import frontend_tcp import frontend_ws -import powermeter +import backend_powermeter +import backend_relayboard import types import modbus import json -initModbus() - asyncCheck serveTcp() asyncCheck serveWs() runForever() -deinitModbus()- \ No newline at end of file +deinitModbus()
diff --git a/src/types.nim b/src/types.nim @@ -67,6 +67,6 @@ type Response* = object status*: ResponseStatus data*: JsonNode -var server* = Server(config: parseJson(readFile("../config.json")).to(Config)) +var server* = Server(config: parseJson(readFile("./config.json")).to(Config)) -echo server.config- \ No newline at end of file +echo server.config
diff --git a/src/util.nim b/src/util.nim @@ -3,6 +3,7 @@ import asyncdispatch import modbus import types import tables +import backend_relayboard var broadcastHandlers: seq[proc (msg: string)] = @[] @@ -20,8 +21,7 @@ proc handleRequest*(req: string): Future[JsonNode] {.async.} = let config = server.config.devices[action.setRelayBoard] server.state[action.setRelayBoard].relays[action.setRelay] = action.setValue - mb.modbus_set_slave(cint(config.address)) - discard mb.modbus_write_bit(cint(config.firstRegister + action.setRelay), cint(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() @@ -29,8 +29,7 @@ proc handleRequest*(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] - mb.modbus_set_slave(cint(config.address)) - discard mb.modbus_write_bit(cint(config.firstRegister + action.toggleRelay), cint(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() @@ -44,4 +43,4 @@ proc tryHandleRequest*(req: string): Future[string] {.async.} = resp = Response(status: Ok, data: await handleRequest(req)) except: resp = Response(status: Err, data: JsonNode()) - return $(%*resp)- \ No newline at end of file + return $(%*resp)
diff --git a/src/vars.nim b/src/vars.nim @@ -0,0 +1,9 @@ +import types +import ws +import asyncnet + +var wsClients* {.threadvar.}: seq[WebSocket] +var tcpClients* {.threadvar.}: seq[AsyncSocket] +var server* {.threadvar.}: Server +var mb* {.threadvar.}: modbus +var broadcastHandlers* {.threadvar.}: seq[proc (msg: string) {.gcsafe.}]