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 
76 
77 
78 
79 
80 
81 
82 
83 
84 
85 
86 
87 
88 
89 
90 
91 
92 
93 
94 
95 
96 
97 
98 
99 
100 
101 
102 
103 
104 
105 
106 
107 
108 
109 
110 
111 
112 
113 
114 
115 
116 
117 
118 
119 
120 
121 
122 
123 
124 
125 
126 
127 
128 
129 
130 
131 
132 
133 
134 
135 
import asyncnet
import asyncdispatch
import strutils
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] =
  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:
      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

proc readRegisters*(unit_id: uint8, address: uint16, count: uint16): Future[seq[uint16]] {.async.} =
  return await retry(mkPacket_readRegisters(unit_id, address, count), parsePacket_readRegisters)

### 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 ###

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