aboutsummaryrefslogtreecommitdiff
path: root/voctogui/lib/connection.py
blob: bc8b86750f989178bbb98a8cacdc1801ebf6d4f2 (plain)
  1. #!/usr/bin/python3
  2. import logging
  3. import socket
  4. import json
  5. import sys
  6. from queue import Queue
  7. from gi.repository import Gtk, GObject
  8. log = logging.getLogger('Connection')
  9. conn = None
  10. port = 9999
  11. command_queue = Queue()
  12. signal_handlers = {}
  13. def establish(host):
  14. global conn, port, log
  15. log.info('establishing Connection to %s', host)
  16. conn = socket.create_connection( (host, port) )
  17. log.debug('Connection successful \o/')
  18. def fetchServerConfig():
  19. global conn, log
  20. log.info('reading server-config')
  21. fd = conn.makefile('rw')
  22. fd.write("get_config\n")
  23. fd.flush()
  24. while True:
  25. line = fd.readline()
  26. words = line.split(' ')
  27. signal = words[0]
  28. args = words[1:]
  29. if signal != 'server_config':
  30. continue
  31. server_config_json = " ".join(args)
  32. server_config = json.loads(server_config_json)
  33. return server_config
  34. def enterNonblockingMode():
  35. global conn, log
  36. log.debug('entering nonblocking-mode')
  37. conn.setblocking(False)
  38. GObject.io_add_watch(conn, GObject.IO_IN, on_data, [''])
  39. def on_data(conn, _, leftovers, *args):
  40. global log
  41. '''Asynchronous connection handler. Pushes data from socket
  42. into command queue linewise'''
  43. try:
  44. while True:
  45. try:
  46. leftovers.append(conn.recv(4096).decode(errors='replace'))
  47. if len(leftovers[-1]) == 0:
  48. log.info("Socket was closed")
  49. # FIXME try to reconnect
  50. conn.close()
  51. Gtk.main_quit()
  52. return False
  53. except UnicodeDecodeError as e:
  54. continue
  55. except:
  56. pass
  57. data = "".join(leftovers)
  58. del leftovers[:]
  59. lines = data.split('\n')
  60. for line in lines[:-1]:
  61. log.debug("got line: %r", line)
  62. line = line.strip()
  63. if command_queue.empty():
  64. log.debug('command_queue was empty, re-starting on_loop scheduling')
  65. GObject.idle_add(on_loop)
  66. command_queue.put((line, conn))
  67. if lines[-1] != '':
  68. log.debug("remaining %r", lines[-1])
  69. leftovers.append(lines[-1])
  70. return True
  71. def on_loop():
  72. '''Command handler. Processes commands in the command queue whenever
  73. nothing else is happening (registered as GObject idle callback)'''
  74. global command_queue
  75. log.debug('on_loop called')
  76. if command_queue.empty():
  77. log.debug('command_queue is empty again, stopping on_loop scheduling')
  78. return False
  79. line, requestor = command_queue.get()
  80. words = line.split()
  81. if len(words) < 1:
  82. log.debug('command_queue is empty again, stopping on_loop scheduling')
  83. return False
  84. signal = words[0]
  85. args = words[1:]
  86. log.info('received signal %s, dispatching', signal)
  87. if signal not in signal_handlers:
  88. return True
  89. for handler in signal_handlers[signal]:
  90. cb = handler['cb']
  91. if 'one' in handler and handler['one']:
  92. log.debug('removing one-time handler')
  93. del signal_handlers[signal]
  94. cb(*args)
  95. return True
  96. def send(command, *args):
  97. global conn, log
  98. if len(args) > 0:
  99. command += ' '+(' '.join(args))
  100. command += '\n'
  101. conn.send(command.encode('ascii'))
  102. def on(signal, cb):
  103. if signal not in signal_handlers:
  104. signal_handlers[signal] = []
  105. signal_handlers[signal].append({'cb': cb})
  106. def one(signal, cb):
  107. if signal not in signal_handlers:
  108. signal_handlers[signal] = []
  109. signal_handlers[signal].append({'cb': cb, 'one': True})