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