From 868c8206ac18e07f4e9d2eae32ec804344e31f2b Mon Sep 17 00:00:00 2001 From: Merritt Boyd Date: Sun, 20 Feb 2011 18:27:52 -0500 Subject: Websocket renderer and basic page server. --- config/C5Sign-websocket.xml | 9 +++ renderers/Websocket.xml | 10 ++++ renderers/WebsocketRenderer.py | 128 +++++++++++++++++++++++++++++++++++++++++ web/smootlight.html | 107 ++++++++++++++++++++++++++++++++++ 4 files changed, 254 insertions(+) create mode 100644 config/C5Sign-websocket.xml create mode 100644 renderers/Websocket.xml create mode 100644 renderers/WebsocketRenderer.py create mode 100644 web/smootlight.html diff --git a/config/C5Sign-websocket.xml b/config/C5Sign-websocket.xml new file mode 100644 index 0000000..549da07 --- /dev/null +++ b/config/C5Sign-websocket.xml @@ -0,0 +1,9 @@ + + config/C5Sign.xml + + + renderers/Websocket.xml + + + + diff --git a/renderers/Websocket.xml b/renderers/Websocket.xml new file mode 100644 index 0000000..6cf2e1f --- /dev/null +++ b/renderers/Websocket.xml @@ -0,0 +1,10 @@ + + renderers.WebsocketRenderer + + websocketrenderer + localhost + web/smootlight.html + 8081 + 8000 + + diff --git a/renderers/WebsocketRenderer.py b/renderers/WebsocketRenderer.py new file mode 100644 index 0000000..1a431fe --- /dev/null +++ b/renderers/WebsocketRenderer.py @@ -0,0 +1,128 @@ +from operationscore.Renderer import * +import util.TimeOps as timeops +import util.ComponentRegistry as compReg +import threading, socket, re, struct, hashlib, json, webbrowser + +class WebsocketRenderer(Renderer): + """Renders frame data over a websocket.""" + + def initRenderer(self): + self.hostname = self.argDict['Hostname'] + self.orig_port = int(self.argDict['SourcePort']) + self.port = int(self.argDict['Port']) + + self.clients = [] + self.clients_lock = threading.Lock() + + self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + self.sock.bind(('', self.port)) + self.sock.listen(1) + + self.connection_thread = threading.Thread(target=self.handle_connections) + self.connection_thread.daemon = True + self.connection_thread.start() + + self.serve_thread = threading.Thread(target=self.serve_page) + self.serve_thread.daemon = True + self.serve_thread.start() + + webbrowser.open('http://'+self.hostname+':'+str(self.orig_port)) + + def serve_page(self): + page = open(self.argDict['Page']).read() + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + sock.bind(('', self.orig_port)) + sock.listen(1) + + while True: + client, addr = sock.accept() + req = client.recv(4096) + client.send('HTTP/1.0 200 Found\r\n') + client.send('Content-type: text/html\r\n\r\n') + client.send(page) + client.close() + + def handle_connections(self): + while True: + client, addr = self.sock.accept() + print 'Accepted websocket connection from %s' % str(addr) + header = '' + while not re.search("\r?\n\r?\n.{8}", header): # Receive headers + 8 bytes data + header += client.recv(1024) + + key1 = re.search("Sec-WebSocket-Key1: (.*)$", header, re.M).group(1) + key2 = re.search("Sec-WebSocket-Key2: (.*)$", header, re.M).group(1) + + data = header[-8:] + + key1n = int(re.sub("[^\d]", '', key1)) + key1ns = key1.count(' ') + n1 = key1n // key1ns + + key2n = int(re.sub("[^\d]", '', key2)) + key2ns = key2.count(' ') + n2 = key2n // key2ns + + s = struct.pack("!II", n1, n2) + data + respkey = hashlib.md5(s).digest() + + if self.orig_port == 80: + origin = 'http://'+self.hostname + else: + origin = 'http://'+self.hostname+':'+str(self.orig_port) + + resp = \ + "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" + \ + "Upgrade: WebSocket\r\n" + \ + "Connection: Upgrade\r\n" + \ + "Sec-WebSocket-Origin:"+ origin + "\r\n" + \ + "Sec-WebSocket-Location: ws://"+self.hostname+":"+ \ + str(self.port)+"/\r\n" + \ + "Sec-WebSocket-Protocol: ledweb\r\n\r\n" + \ + respkey + "\r\n" + + client.send(resp) + self.clients_lock.acquire() + self.clients.append(client) + self.clients_lock.release() + + def render(self, lightSystem, currentTime=timeops.time()): + json_frame = [] + + for light in lightSystem: + loc = light.location + c = light.state(currentTime) + cs = 'rgb('+str(c[0])+','+str(c[1])+','+str(c[2])+')' + + json_frame.append((loc, cs)) + + size = compReg.getComponent('Screen').getSize() + + json_data = json.dumps(dict(status='ok', size=size, frame=json_frame)) + self.client_push(json_data) + + def client_push(self, data): + self.clients_lock.acquire() + dead_clients = [] + for i in range(len(self.clients)): + try: + self.clients[i].send("\x00") + self.clients[i].send(data) + self.clients[i].send("\xff") + except socket.error: + dead_clients.append(i) + + for i in range(len(dead_clients)): + self.close_sock(self.clients[dead_clients[i]-i]) + del self.clients[dead_clients[i]-i] + self.clients_lock.release() + + def close_sock(self, s): + try: + c.shutdown(socket.SHUT_RDWR) + c.close() + except Exception: + pass + \ No newline at end of file diff --git a/web/smootlight.html b/web/smootlight.html new file mode 100644 index 0000000..5b64a28 --- /dev/null +++ b/web/smootlight.html @@ -0,0 +1,107 @@ + + +SmootLight + + + + + +
+
+ + \ No newline at end of file -- cgit v1.2.3