# HG changeset patch # User gyorgyf # Date 1348181748 -3600 # Node ID c97feb7ef9e9834db1a55ac6ebc535c29ab24ad0 # Parent 2ec521bbd543be5d72b7a9eec3265f841e4bfbc6 Strasbourg diff -r 2ec521bbd543 -r c97feb7ef9e9 mcserver/mcdevel.cfg --- a/mcserver/mcdevel.cfg Fri Jun 22 18:07:53 2012 +0100 +++ b/mcserver/mcdevel.cfg Thu Sep 20 23:55:48 2012 +0100 @@ -2,54 +2,41 @@ [global] # kakapo<->golden -server.socket_host = "192.168.2.184" -# server.socket_host = "127.0.0.1" +# server.socket_host = "192.168.2.184" +server.socket_host = "127.0.0.1" server.socket_port = 8030 -server.thread_pool = 30 +server.thread_pool = 50 server.show_tracebacks = False engine.autoreload_on = True #should be off engine.autoreload_frequency = 180 #sec -tools.sessions.on = True +tools.sessions.on = False tools.sessions.timeout = 600 #min tools.sessions.clean_freq = 120 #min -# tools.sessions.storage_type = 'directory' -# tools.sessions.SESSIONS_PATH = 'sessions/' -# tools.sessions.SESSIONS_PATH = 'sessions' # expose everything in static, but note that sessions are enabled in this scope tools.staticdir.on = True tools.staticdir.dir = 'static' -# tools.staticdir.content_types = {'n3': 'text/rdf+n3', 'rdf': 'application/rdf+xml'} -# log.error_file = "mcserver.log" +log.screen = None +# log.access_file = 'mood-access.log' +log.error_file = "mcserver.log" #main app config [/] +tools.proxy.on = True response.timeout = 600 #sec request.show_tracebacks = False -log.access_file = 'mcserver-access.log' -log.error_file = 'mcserver-error.log' +# log.access_file = 'mcserver-access.log' +# log.error_file = 'mcserver-error.log' # tools.staticdir.root = 'mcserver' tools.staticdir.on = True tools.staticdir.dir = 'static' +# [/mood] +# log.access_file = 'mood-access.log' + # explicit configuration of static content with sessions disabled [/script] tools.sessions.on = False - -[/css] -tools.sessions.on = False - -[/img] -tools.sessions.on = False - -[/images] -tools.sessions.on = False - -[/data] -tools.sessions.on = False - -[/temp] -tools.sessions.on = False diff -r 2ec521bbd543 -r c97feb7ef9e9 mcserver/mcserver.cfg --- a/mcserver/mcserver.cfg Fri Jun 22 18:07:53 2012 +0100 +++ b/mcserver/mcserver.cfg Thu Sep 20 23:55:48 2012 +0100 @@ -4,31 +4,29 @@ # kakapo<->golden server.socket_host = "192.168.122.144" server.socket_port = 8030 -server.thread_pool = 30 +server.thread_pool = 50 server.show_tracebacks = False engine.autoreload_on = False #should be off engine.autoreload_frequency = 180 #sec -tools.sessions.on = True +tools.sessions.on = False tools.sessions.timeout = 600 #min tools.sessions.clean_freq = 120 #min -# tools.sessions.storage_type = 'directory' -# tools.sessions.SESSIONS_PATH = 'sessions/' -# tools.sessions.SESSIONS_PATH = 'sessions' # expose everything in static, but note that sessions are enabled in this scope tools.staticdir.on = True tools.staticdir.dir = 'static' -# tools.staticdir.content_types = {'n3': 'text/rdf+n3', 'rdf': 'application/rdf+xml'} +log.screen = None +# log.access_file = 'mood-access.log' +log.error_file = "mcserver.log" #main app config [/] response.timeout = 600 #sec request.show_tracebacks = False -# log.access_file = 'mcserver-access.log' -# log.error_file = 'mcserver-error.log' +tools.proxy.on = True # tools.staticdir.root = 'mcserver' tools.staticdir.on = True tools.staticdir.dir = 'static' @@ -36,3 +34,6 @@ # explicit configuration of static content with sessions disabled [/script] tools.sessions.on = False + +[/static] +tools.sessions.on = False diff -r 2ec521bbd543 -r c97feb7ef9e9 mcserver/mcserver.py --- a/mcserver/mcserver.py Fri Jun 22 18:07:53 2012 +0100 +++ b/mcserver/mcserver.py Thu Sep 20 23:55:48 2012 +0100 @@ -27,48 +27,55 @@ class Input(object): - def __init__(self,x,y): + def __init__(self,x,y,age,dist): self.time = time.time() self.x=float(x) self.y=float(y) self.count = 1 + self.age = age + self.dist = dist def __cmp__(self,other): d = math.sqrt( math.pow((self.x-other.x),2) + math.pow((self.y-other.y),2) ) - if d < 0.05 : + if d < self.dist : return 0 else : return -1 def dead(self): - return time.time()-self.time > 1.5 + return time.time()-self.time > self.age def __repr__(self): - return (self.x,self.y,self.count).__repr__() + # return (self.x,self.y,self.count).__repr__() + return "(%.4f,%.4f,%i)" %(self.x,self.y,self.count) def inc(self): self.time = time.time() self.count += 1 - print self.count + # print self.count class MoodConductor: def __init__(self): self.inputs = [] + self.age = 3 + self.dist = 0.1 + self.ninp = 12 - def index(self): - return "" + # def index(self): + # return str() @cp.expose def mood(self,x,y): - print "Received coordinates", x,y, "\n" - i = Input(x,y) + # print "Received coordinates", x,y, "\n" + i = Input(x,y,self.age,self.dist) if i in self.inputs : self.inputs[self.inputs.index(i)].inc() else : self.inputs.insert(0,i) - self.inputs = self.inputs[:10] + self.inputs = self.inputs[:self.ninp] + cp.log.error("%s - %.6s-%.6s" %(str(cp.request.remote.ip),x,y)) return str() @cp.expose @@ -76,6 +83,21 @@ for i in self.inputs : if i.dead() : self.inputs.remove(i) return self.inputs.__repr__() + + @cp.expose + def config(self,**kwargs): + if kwargs.has_key('age') : + self.age = float(kwargs['age']) + if kwargs.has_key('dist') : + self.dist = float(kwargs['dist']) + if kwargs.has_key('ninp') : + self.ninp = int(kwargs['ninp']) + return str() + + @cp.expose + def getconf(self): + self.config_list = ['age','dist','ninp'] + return str(map(lambda x: (x,"%.3f" % self.__dict__[x]),self.config_list)) #+ " Sessions: " + str(cp.tools.sessions) diff -r 2ec521bbd543 -r c97feb7ef9e9 visualclient/visclient.py --- a/visualclient/visclient.py Fri Jun 22 18:07:53 2012 +0100 +++ b/visualclient/visclient.py Thu Sep 20 23:55:48 2012 +0100 @@ -15,6 +15,9 @@ import gradients from gradients import genericFxyGradient +from threading import Thread +from random import random + # from pytagcloud import create_tag_image, make_tags # from pytagcloud.lang.counter import get_tag_counts @@ -29,11 +32,63 @@ ecol = (0,0,0,255) # X,Y=1140,900 -X,Y = 600,400 -NBLOBS = 15 +# X,Y = 600,400 +X,Y = 800,600 + +NBLOBS = 18 BLOBSIZE = 25 G=110 FADE = 15 +DIST = 0.1 # blob equivalence tolerance +FRAMERATE = 60 + +# Connection: +# IP = "192.168.2.184:8030" +IP = "138.37.95.215" +HTTP_TIMEOUT = 3 +SERVER_UPDATE_INTERVAL = 0.8 + +class Indicator(object): + + off_color = pg.Color(110,0,0) + on_color = pg.Color(0,120,0) + + def __init__(self,bg,pos): + self.visible = True + self.ison = True + self.x,self.y = pos + self.xs = int(self.x * X) + self.ys = int(Y - (self.y * Y)) + self.c = self.off_color + self.size = 6 + self.bg = bg + + def reinit(self,bg): + self.bg = bg + self.xs = int(self.x * X) + self.ys = int(Y - (self.y * Y)) + + def draw(self): + if self.visible : + pg.draw.circle(self.bg, self.c, (self.xs,self.ys),self.size,0) + + def toggle(self): + if self.ison == True : + self.off() + else : + self.on() + return self + + def on(self): + self.c = self.on_color + self.ison = True + return self + + def off(self): + self.c = self.off_color + self.ison = False + return self + class Blob(object): @@ -55,12 +110,13 @@ def __cmp__(self,other): d = math.sqrt( math.pow((self.x-other.x),2) + math.pow((self.y-other.y),2) ) - if d < 0.03 : + if d < DIST : return 0 else : return -1 def draw(self): + if not self.visible : return d=int(self.size) self.bg.blit(gradients.radial(self.size, (self.c[0],self.c[1],self.c[2],self.alpha), (0,0,0,self.alpha)), (self.xs-d,self.ys-d)) self.alpha = 255 - int(self.age()*self.FADE) @@ -102,10 +158,11 @@ class VisualClient(object): + '''Main visualisation client.''' def __init__(self): # self.conn = ht.HTTPConnection("192.168.2.184:8030") - self.conn = ht.HTTPConnection("138.37.95.215") + # self.conn = ht.HTTPConnection("138.37.95.215") pg.init() @@ -119,6 +176,7 @@ self.bg = pg.Surface(self.screen.get_size()) self.bg = self.bg.convert() self.bg.fill((0,0,0)) + pg.display.set_gamma(100.0) self.scol = (0,255,0,255) @@ -128,28 +186,30 @@ self.blobs = [] self.moods = [] self.read_mood_data() - # pg.quit() - # sys.exit(-1) self.FADE = FADE + self.indicators = { + "conn":Indicator(self.bg,(0.98,0.02)), + "update":Indicator(self.bg,(0.96,0.02)), + "data":Indicator(self.bg,(0.94,0.02)), + "receive":Indicator(self.bg,(0.92,0.02))} + + self.thread = None + self.running = False + self.fullscreen = False + self.init_reconnect = False pass + def read_mood_data(self): - + '''Read the mood position and color information form csv file.''' with open('moods.csv') as mf: data = mf.readlines()[1:] for line in data : l = line.split(',') mood = Mood(l[0],l[1],l[2]) self.moods.append(mood) - # with open('feelings.txt') as ff: - # data = ff.readlines() - # data = map(lambda x: x.split('\t'),data) - # for mood in self.moods : - # for colors in data : - # if mood.word == colors[0] : - # mood.color = colors[2] with open('colors.txt') as ff: data = ff.readlines()[1:] data = map(lambda x: x.split(','),data) @@ -160,15 +220,43 @@ if d < cd : cd = d mood.color = tuple(map(lambda x: int(pow(math.atan((float(x)/7.0)),12.5)),(colors[2],colors[3],colors[4]))) + # mood.color = tuple(map(lambda x: int(x),(colors[2],colors[3],colors[4]))) + return True + - # for mood in self.moods: - # if mood.color : - # # mood.color = map(lambda x: '0x'+str(x).strip(),[mood.color[0:2],mood.color[2:4],mood.color[4:]]) - # mood.color = tuple(map(lambda x: int(x),mood.color)) - # print mood.color + def start_update_thread(self): + '''Start the thread that reads data from the server.''' + self.running = True + self.thread = Thread(target = self.update_thread) + self.thread.daemon = True + self.thread.start() + def stop_update_thread(self): + '''Stop the thread and allow some time fot the connections to close.''' + self.running = False + try : + self.thread.join(2) + except : + print "No update thread to join." + + def update_thread(self): + '''The server update thread''' + while self.running : + try : + self.update() + # self.indicators["update"].visible = True + except Exception, e: + if str(e).strip() : print "Exception: ", str(e), type(e), len(str(e).strip()) + self.indicators["conn"].off() + # self.indicators["update"].visible = False + time.sleep(SERVER_UPDATE_INTERVAL) + def update(self): + '''Update the blob list from the server. This should be in a thread.''' + + # indicate connection health by toggling an indictor + self.indicators["update"].toggle() # delete invisibles for blob in self.blobs : @@ -181,35 +269,42 @@ self.conn.endheaders() res = self.conn.getresponse() data = res.read() - data = eval(data) + data = eval(data) if not data : self.conn.close() + self.indicators["data"].toggle() return False for d in data : # coordstxt = "x:%s y:%s c:%s" %d - x,y,c = d - - cmood = None - d = cd = sys.float_info.max - for mood in self.moods : - d = mood.get_distance(x,y) - if d < cd : - cd = d - cmood = mood - - new = Blob(self.bg,x,y,mood=cmood,fade=self.FADE) - if not new in self.blobs : - self.blobs.insert(0,new) - elif c > self.blobs[self.blobs.index(new)].count: - self.blobs[self.blobs.index(new)].increment(c) - + x,y,count = d + self.add_blob(x,y,count) + self.indicators["receive"].toggle() self.conn.close() self.blobs = self.blobs[:NBLOBS] return True + + + def add_blob(self,x,y,count=1): + '''Insert a blob to the list of blobs''' + # find mood correxponding to x,y + cmood = None + d = cd = sys.float_info.max + for mood in self.moods : + d = mood.get_distance(x,y) + if d < cd : + cd = d + cmood = mood + # create new blob or increase click count on existing one + new = Blob(self.bg,x,y,mood=cmood,fade=self.FADE) + if not new in self.blobs : + self.blobs.insert(0,new) + elif count > self.blobs[self.blobs.index(new)].count: + self.blobs[self.blobs.index(new)].increment(count) + pass def draw(self): - self.bg.fill((0,0,0)) + self.bg.fill((0,0,0)) # self.bg.blit(gradients.radial(19, self.scol, self.ecol), (rect_x,rect_y)) l = copy.copy(self.blobs) l.reverse() @@ -220,63 +315,186 @@ pg.draw.line(self.bg, (G,G,G), (int(X/2.0),0),(int(X/2.0),Y), 1) pg.draw.line(self.bg, (G,G,G), (0,int(Y/2.0)),(X,int(Y/2.0)), 1) + # indicators + for i in self.indicators.itervalues() : + i.draw() + + def read_keys(self): + '''Read keys''' + for event in pg.event.get() : + # Quit (event) + if event.type == QUIT: + self.quit() + elif event.type == KEYDOWN : + # Post Quit: Esc, q + if event.key == K_ESCAPE or event.key == K_q : + pg.event.post(pg.event.Event(QUIT)) + # Reset: Space + elif event.key == K_SPACE : + self.blobs = [] + # Random : r + elif event.key == K_r : + self.add_blob(random(),random(),count=5) + # Connection : c + elif event.key == K_c : + self.init_reconnect = True + self.indicators["conn"].off() + # Fullscreen: f + elif event.key == K_f : + # pg.display.toggle_fullscreen() + self.toggle_screen_mode() + # Toggle fade: s + elif event.key == K_s : + if self.FADE : + print "fade off" + self.indicators["conn"].off() + self.FADE = 0 + for blob in self.blobs : + blob.fade(0) + else: + print "fade on" + self.indicators["conn"].on() + self.FADE = 15 + for blob in self.blobs : + blob.fade(15) + blob.reset_time() + pass + + def toggle_screen_mode(self): + '''Go back and forth between full screen mode.''' + if self.fullscreen == False: + globals()['_X'] = globals()['X'] + globals()['_Y'] = globals()['Y'] + globals()['X'] = 1440 + globals()['Y'] = 900 + self.screen = pg.display.set_mode((X, Y)) + self.fullscreen = True + self.bg = pg.Surface(self.screen.get_size()) + self.bg = self.bg.convert() + self.bg.fill((0,0,0)) + for i in self.indicators.itervalues() : + i.reinit(self.bg) + i.draw() + else : + globals()['X'] = globals()['_X'] + globals()['Y'] = globals()['_Y'] + self.screen = pg.display.set_mode((X, Y)) + self.fullscreen = False + self.bg = pg.Surface(self.screen.get_size()) + self.bg = self.bg.convert() + self.bg.fill((0,0,0)) + for i in self.indicators.itervalues() : + i.reinit(self.bg) + i.draw() + pg.display.toggle_fullscreen() + def run(self): - # conn = ht.HTTPConnection("192.168.2.184:8030") - self.conn = ht.HTTPConnection("138.37.95.215") - rect_x,rect_y = 0,0 - counter = 0 - + # setup connection + self.connect() + # main loop while True : # pg.draw.circle(screen, pg.Color(255,0,0), (300,50),20,0) # screen.blit(gradients.radial(99, scol, ecol), (401, 1)) - for event in pg.event.get() : - if event.type == QUIT: - self.conn.close() - pg.quit() - sys.exit() - elif event.type == KEYDOWN : - if event.key == K_ESCAPE : - pg.event.post(pg.event.Event(QUIT)) - elif event.key == K_SPACE : - self.blobs = [] - elif event.key == K_s : - if self.FADE : - print "fade off" - self.FADE = 0 - for blob in self.blobs : - blob.fade(0) - else: - print "fade on" - self.FADE = 15 - for blob in self.blobs : - blob.fade(15) - blob.reset_time() - - - + self.read_keys() # Draw self.draw() - - # update from the server - counter += 1 - if counter % 12 : - counter = 0 - try : - self.update() - except: - pass # update display self.screen.blit(self.bg, (0, 0)) pg.display.flip() - self.fpsClock.tick(50) - pass + self.fpsClock.tick(FRAMERATE) + + if self.init_reconnect: + self.reconnect() + + return True + + + def connect(self,retry = 5): + '''Connect to the server and test connection.''' + if not retry : + print "Server unreachable" + pg.quit() + raise SystemExit + if retry < 5 : + time.sleep(3) + try : + self.conn = ht.HTTPConnection(IP,timeout=HTTP_TIMEOUT) + # self.start_update_thread() + self.indicators["conn"].on() + except : + self.indicators["conn"].off() + self.connect(retry = retry-1) + + try: + self.conn.putrequest("GET","/moodconductor/index.html", skip_host=True) + self.conn.putheader("Host", "www.isophonics.net") + self.conn.endheaders() + res = self.conn.getresponse() + if res.status == 200 : + self.indicators["conn"].on() + else : + print "Server response:", res.status, res.reason + self.indicators["conn"].off() + if not hasattr(self,"noretry") and raw_input("Go offline? ") in ['y',''] : + return False + else : + self.noretry = None + self.connect(retry = retry-1) + except : + print "Exception while testing connection." + self.indicators["conn"].off() + # comment out in offline mode + if not hasattr(self,"noretry") and raw_input("Go offline? ") in ['y',''] : + return False + else : + self.noretry = None + self.connect(retry = retry-1) + self.start_update_thread() + return True + + + + def reconnect(self): + '''Called when c is pressed.''' + self.init_reconnect = False + # self.indicators["conn"].off().draw() + # self.screen.blit(self.bg, (0, 0)) + # pg.display.flip() + + self.stop_update_thread() + time.sleep(1) + try : + self.conn.close() + except : + self.indicators["conn"].off() + try : + self.conn = ht.HTTPConnection(IP,timeout=HTTP_TIMEOUT) + self.conn.connect() + self.indicators["conn"].on() + print "Reconnected." + except : + self.indicators["conn"].off() + print "Error while reconnecting." + self.start_update_thread() + + + def quit(self): + print "Quitting.." + self.indicators["conn"].off() + self.stop_update_thread() + self.conn.close() + pg.quit() + sys.exit() + + + def main():