From 1392b46636e9df2a4e4ecc56f12df2cb0301f6e8 Mon Sep 17 00:00:00 2001 From: jaseg Date: Mon, 17 Feb 2014 20:34:36 +0100 Subject: Network handling works now --- host/server.py | 164 +++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 137 insertions(+), 27 deletions(-) diff --git a/host/server.py b/host/server.py index 1ab7959..ab3f7b5 100755 --- a/host/server.py +++ b/host/server.py @@ -1,16 +1,27 @@ #!/usr/bin/env python +from socketserver import * +from time import time, strftime, sleep +from collections import namedtuple +from itertools import product +import threading +import random + from ctypes import CDLL, POINTER, c_void_p, Structure, c_uint8, c_size_t, cast, addressof + import numpy as np -from itertools import product + from matelight import sendframe, DISPLAY_WIDTH, DISPLAY_HEIGHT -from terminal import printframe + + +UDP_TIMEOUT = 3.0 + class COLOR(Structure): - _fields_ = [('r', c_uint8), ('g', c_uint8), ('b', c_uint8), ('a', c_uint8)] + _fields_ = [('r', c_uint8), ('g', c_uint8), ('b', c_uint8), ('a', c_uint8)] class FRAMEBUFFER(Structure): - _fields_ = [('data', POINTER(COLOR)), ('w', c_size_t), ('h', c_size_t)] + _fields_ = [('data', POINTER(COLOR)), ('w', c_size_t), ('h', c_size_t)] bdf = CDLL('./libbdf.so') bdf.read_bdf_file.restype = c_void_p @@ -19,31 +30,130 @@ bdf.framebuffer_render_text.restype = POINTER(FRAMEBUFFER) unifont = bdf.read_bdf_file('unifont.bdf') def render_text(text): - assert unifont - fb = bdf.framebuffer_render_text(bytes(str(text), 'UTF-8'), unifont) - fbd = fb.contents - buf = np.ctypeslib.as_array(cast(fbd.data, POINTER(c_uint8)), shape=(fbd.h, fbd.w, 4)) - # Set data pointer to NULL before freeing framebuffer struct to prevent free_framebuffer from also freeing the data - # buffer that is now used by numpy - #bdf.console_render_buffer(fb) - fbd.data = cast(c_void_p(), POINTER(COLOR)) - bdf.free_framebuffer(fb) - return buf + assert unifont + fb = bdf.framebuffer_render_text(bytes(str(text), 'UTF-8'), unifont) + fbd = fb.contents + buf = np.ctypeslib.as_array(cast(fbd.data, POINTER(c_uint8)), shape=(fbd.h, fbd.w, 4)) + # Set data pointer to NULL before freeing framebuffer struct to prevent free_framebuffer from also freeing the data + # buffer that is now used by numpy + #bdf.console_render_buffer(fb) + fbd.data = cast(c_void_p(), POINTER(COLOR)) + bdf.free_framebuffer(fb) + return buf def printframe(fb): - h,w,_ = fb.shape - print('\033[H\033[2J') - bdf.console_render_buffer(fb.ctypes.data_as(POINTER(c_uint8)), w, h) + h,w,_ = fb.shape + print('\033[s\033[H', end='') + bdf.console_render_buffer(fb.ctypes.data_as(POINTER(c_uint8)), w, h) + print('\033[0m\033[u', end='') + +def scroll(text): + """ Returns whether it could scroll all the text uninterrupted """ + fb = render_text(text); + h,w,_ = fb.shape + for i in range(-DISPLAY_WIDTH,w+1): + if current_entry.entrytype == 'udp' or (current_entry in defaulttexts and textqueue): + return False + leftpad = np.zeros((DISPLAY_HEIGHT, max(-i, 0), 4), dtype=np.uint8) + framedata = fb[:, max(0, i):min(i+DISPLAY_WIDTH, w)] + rightpad = np.zeros((DISPLAY_HEIGHT, min(DISPLAY_WIDTH, max(0, i+DISPLAY_WIDTH-w)), 4), dtype=np.uint8) + dice = np.concatenate((leftpad, framedata, rightpad), 1) + sendframe(dice) + #printframe(dice) + fb = render_text(text); + return True + +QueueEntry = namedtuple('QueueEntry', ['entrytype', 'remote', 'timestamp', 'text']) +defaulttexts = [QueueEntry('text', '127.0.0.1', 0, t) for t in [ + '\x1B[92mMate Light\x1B[93m@\x1B[92mPlay store or \x1B[94;101mtcp://matelight.cbrp3.c-base.org:1337\x1B[0;91m ♥', + '\x1B[92mMate Light\x1B[0;91m ♥ \x1B[92mUnicode', + '\x1B[92mMate Light\x1B[0m powered by \x1B[95mMicrosoft™ \x1B[96mMarquee Manager® Professional OEM', + '\x1B[92mMate Light\x1B[0m powered by \x1B[95mData Becker™ \x1B[96mLaufschriftstudio 2000® Platinum Edition', + '\x1B[92mMate Light\x1B[0m powered by \x1B[95mApple™ \x1B[96miScroll®', + '\x1B[92mMate Light\x1B[0m powered by \x1B[95mSiemens™ \x1B[96mLaufschrift® v.0.1.2b fuer Intel™ Pentium®', + ]] +current_entry = random.choice(defaulttexts) +conns = {} +textqueue = [] + +def log(*args): + print(strftime('[%m-%d %H:%M:%S]'), *args) + +class MateLightUDPHandler(BaseRequestHandler): + def handle(self): + try: + global current_entry, conns + data = self.request[0].strip() + if len(data) != FRAME_SIZE+4: + #raise ValueError('Invalid frame size: Expected {}, got {}'.format(FRAME_SIZE+4, len(data))) + return + frame = data[:-4] + #crc1, = struct.unpack('!I', data[-4:]) + #crc2, = zlib.crc32(frame, 0), + #if crc1 != crc2: + # raise ValueError('Invalid frame CRC checksum: Expected {}, got {}'.format(crc2, crc1)) + #socket.sendto(b'ACK', self.client_address) + a = np.array(list(frame), dtype=np.uint8) + timestamp = time() + addr = self.client_address[0] + if addr not in conns: + current_entry = QueueEntry('udp', addr, timestamp, '') + conns[addr] = current_entry + log('New UDP connection from', addr) + else: + conns[addr].timestamp = timestamp + if current_entry.entrytype == 'udp' and current_entry.remote == addr: + frame = a.reshape((DISPLAY_HEIGHT, DISPLAY_WIDTH, 3)) + sendframe(frame) + #printframe(frame) + except Exception as e: + print('Error receiving UDP frame:', e) + ex_type, ex, tb = sys.exc_info() + traceback.print_tb(tb) + +class MateLightTCPTextHandler(BaseRequestHandler): + def handle(self): + global current_entry, conns + data = str(self.request.recv(1024).strip(), 'UTF-8') + addr = self.client_address[0] + timestamp = time() + if len(data) > 140: + self.request.sendall('TOO MUCH INFORMATION!\n') + return + log('Text from {}: {}\x1B[0m'.format(addr, data)) + textqueue.append(QueueEntry('text', addr, timestamp, data)) + self.request.sendall(b'KTHXBYE!\n') + +TCPServer.allow_reuse_address = True +server = TCPServer(('', 1337), MateLightTCPTextHandler) +t = threading.Thread(target=server.serve_forever) +t.daemon = True +t.start() + +UDPServer.allow_reuse_address = True +userver = UDPServer(('', 1337), MateLightUDPHandler) +t = threading.Thread(target=userver.serve_forever) +t.daemon = True +t.start() if __name__ == '__main__': - fb = render_text('\033[31;48;5;167;4;6mfoo\033[0;91mbar\033[93;106;5mbaz\033[0m\033[0;94;6m♥♥♥'); - h,w,_ = fb.shape - for i in range(-DISPLAY_WIDTH,w+1): - leftpad = np.zeros((DISPLAY_HEIGHT, max(-i, 0), 4), dtype=np.uint8) - framedata = fb[:, max(0, i):min(i+DISPLAY_WIDTH, w)] - rightpad = np.zeros((DISPLAY_HEIGHT, min(DISPLAY_WIDTH, max(0, i+DISPLAY_WIDTH-w)), 4), dtype=np.uint8) - dice = np.concatenate((leftpad, framedata, rightpad), 1) - sendframe(dice) - printframe(dice) - fb = render_text('\033[31;48;5;167;4;6mfoo\033[0;91mbar\033[93;106;5mbaz\033[0m\033[0;94;6m♥♥♥'); + print('\n'*7) + while True: + if current_entry.entrytype == 'text': + if scroll(current_entry.text): + textqueue.remove(current_entry) + if textqueue: + current_entry = textqueue[0] + else: + if conns: + current_entry = random.choice(conns.values()) + else: + current_entry = random.choice(defaulttexts) + if current_entry.entrytype != 'udp' and textqueue: + current_entry = textqueue[0] + if current_entry.entrytype == 'udp': + if time() - current_entry.timestamp > UDP_TIMEOUT: + current_entry = random.choice(defaulttexts) + else: + sleep(0.2) -- cgit