summaryrefslogtreecommitdiff
path: root/voctocore/lib/controlserver.py
diff options
context:
space:
mode:
Diffstat (limited to 'voctocore/lib/controlserver.py')
-rw-r--r--voctocore/lib/controlserver.py118
1 files changed, 81 insertions, 37 deletions
diff --git a/voctocore/lib/controlserver.py b/voctocore/lib/controlserver.py
index fffeda8..e267ca7 100644
--- a/voctocore/lib/controlserver.py
+++ b/voctocore/lib/controlserver.py
@@ -1,65 +1,109 @@
-import socket, threading, queue, logging
+#!/usr/bin/python3
+import socket, logging
from gi.repository import GObject
-def controlServerEntrypoint(f):
- # mark the method as something that requires view's class
- f.is_control_server_entrypoint = True
- return f
+from lib.commands import ControlServerCommands
class ControlServer():
log = logging.getLogger('ControlServer')
- def __init__(self, videomix):
+
+ boundSocket = None
+
+ def __init__(self, pipeline):
'''Initialize server and start listening.'''
- self.videomix = videomix
+ self.commands = ControlServerCommands(pipeline)
- sock = socket.socket()
- sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- sock.bind(('0.0.0.0', 23000))
- sock.listen(1)
+ port = 9999
+ self.log.debug('Binding to Command-Socket on [::]:%u', port)
+ self.boundSocket = socket.socket(socket.AF_INET6)
+ self.boundSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ self.boundSocket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False)
+ self.boundSocket.bind(('::', port))
+ self.boundSocket.listen(1)
- # register socket for callback inside the GTK-Mainloop
- GObject.io_add_watch(sock, GObject.IO_IN, self.listener)
+ self.log.debug('Setting GObject io-watch on Socket')
+ GObject.io_add_watch(self.boundSocket, GObject.IO_IN, self.on_connect)
- def listener(self, sock, *args):
+ def on_connect(self, sock, *args):
'''Asynchronous connection listener. Starts a handler for each connection.'''
conn, addr = sock.accept()
- self.log.info("Connection from %s", addr)
+ self.log.info("Incomming Connection from %s", addr)
- # register data-received handler inside the GTK-Mainloop
- GObject.io_add_watch(conn, GObject.IO_IN, self.handler)
+ self.log.debug('Setting GObject io-watch on Connection')
+ GObject.io_add_watch(conn, GObject.IO_IN, self.on_data)
return True
- def handler(self, conn, *args):
+ def on_data(self, conn, *args):
'''Asynchronous connection handler. Processes each line from the socket.'''
- line = conn.recv(4096)
- if not len(line):
- self.log.debug("Connection closed.")
+ # construct a file-like object fro mthe socket
+ # to be able to read linewise and in utf-8
+ filelike = conn.makefile('rw')
+
+ # read a line from the socket
+ line = filelike.readline().strip()
+
+ # no data = remote closed connection
+ if len(line) == 0:
+ self.log.info("Connection closed.")
return False
- r = self.processLine(line.decode('utf-8'))
- if isinstance(r, str):
- conn.send((r+'\n').encode('utf-8'))
+ # 'quit' = remote wants us to close the connection
+ if line == 'quit':
+ self.log.info("Client asked us to close the Connection")
return False
- conn.send('OK\n'.encode('utf-8'))
+ # process the received line
+ success, msg = self.processLine(line)
+
+ # success = False -> error
+ if success == False:
+ # on error-responses the message is mandatory
+ if msg is None:
+ msg = '<no message>'
+
+ # respond with 'error' and the message
+ filelike.write('error '+msg+'\n')
+ self.log.info("Function-Call returned an Error: %s", msg)
+
+ # keep on listening on that connection
+ return True
+
+ # success = True and not message
+ if msg is None:
+ # respond with a simple 'ok'
+ filelike.write('ok\n')
+ else:
+ # respond with the returned message
+ filelike.write('ok '+msg+'\n')
return True
-
-
-
def processLine(self, line):
- command, argstring = (line.strip()+' ').split(' ', 1)
+ # split line into command and optional args
+ command, argstring = (line+' ').split(' ', 1)
args = argstring.strip().split()
- self.log.info(command % args)
- if not hasattr(self.videomix, command):
- return 'unknown command {}'.format(command)
+ # log function-call as parsed
+ self.log.info("Read Function-Call from Socket: %s( %s )", command, args)
+
+ # check that the function-call is a known Command
+ if not hasattr(self.commands, command):
+ return False, 'unknown command %s' % command
- f = getattr(self.videomix, command)
- if not hasattr(f, 'is_control_server_entrypoint'):
- return 'method {} not callable from controlserver'.format(command)
try:
- return f(*args)
+ # fetch the function-pointer
+ f = getattr(self.commands, command)
+
+ # call the function
+ ret = f(*args)
+
+ # if it returned an iterable, probably (Success, Message), pass that on
+ if hasattr(ret, '__iter__'):
+ return ret
+ else:
+ # otherwise construct a tuple
+ return (ret, None)
+
except Exception as e:
- return str(e)
+ # In case of an Exception, return that
+ return False, str(e)