view mcserver/mcserver.py @ 54:c0b34039917a tip

Server: added an exposed function to log the start time of a performance (for log-to-audio sync)
author Mathieu Barthet <mathieu.barthet@eecs.qmul.ac.uk>
date Wed, 14 Oct 2015 19:20:08 +0100
parents fe04ff3547c7
children
line wrap: on
line source
#!/usr/bin/env python
# encoding: utf-8
"""
mcserver.py

Created by George Fazekas on 2012-06-16.
Modifications by Mathieu Barthet.
Copyright (c) 2012 . All rights reserved.
"""

import os,sys,optparse,signal,time,math
import cherrypy as cp

from cherrypy.lib import static
import subprocess as sp
from subprocess import Popen as spopen


op = optparse.OptionParser()
op.add_option('-u', '--user', action="store", dest="USER", default="server", type="str")
options, args = op.parse_args()
CONFIG_FILE = "mc%(USER)s.cfg" %options.__dict__

if not os.path.isfile(CONFIG_FILE) :
	print >> sys.stderr, "Config file not found: %s" %CONFIG_FILE
	sys.exit(-1)


class Input(object):
	
	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 < self.dist : 
			return 0
		else :
			return -1
	
	def dead(self):
		return time.time()-self.time > self.age
		
	def __repr__(self):
		# 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


class MoodConductor:
	
	def __init__(self):
		self.inputs = []
		self.age = 3
		self.dist = 0.1
		self.ninp = 18
	
	# def index(self):
	# 	return str()
	
	@cp.expose
	def test(self):
		print "Server test function accessed."
		cp.log.error("Server test function accessed.")
		return "Server: Test passed."
	
	@cp.expose
	def mood(self,x,y):
		print "Received coordinates", x,y
		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[:self.ninp]
		cp.log.error("%s,%.6s,%.6s,%s" %(str(cp.request.remote.ip),x,y,i.time))
		return str()
		
	@cp.expose
	def result(self):
		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'])
		# cp.log.error("Config changed...")
		print "New configuration received from visual client."
		return str()

	@cp.expose
	def getconf(self):
		self.config_list = ['age','dist','ninp']
		cp.log.error(str(map(lambda x: (x,"%.3f" % self.__dict__[x]),self.config_list)))
		return str(map(lambda x: (x,"%.3f" % self.__dict__[x]),self.config_list)) #+ " Sessions: " + str(cp.tools.sessions)
	
	@cp.expose
	def start(self):
		timelog = "Performance start time: %s"%time.time()
		print "Start function accessed."
		cp.log.error(timelog)
		return timelog


def getProcessPids(port,kill=False):
	'''Get the pid of the offending Python process given a port after an unsuccessful restart.'''
	print "Running lsof -i :"+str(port)," ...\n\n"
	command = "lsof -i :"+str(port)
	w = spopen(command,stdout=sp.PIPE,stderr=sp.PIPE,shell=True)
	se = w.stderr.readlines()
	result = w.stdout.readlines()
	exitcode = w.wait()
	if not result : 
		print "getProcessPid:: Unable to obtain process pid. (lsof returned nothing. exitcode: %s) This is fine in most cases..." %str(exitcode)
		return False
	import pprint
	pprint.pprint(result)
	
	# get heading:
	ix = None
	head = result[0].upper()
	if 'PID' in head:
		head = filter(lambda x: x != str(), head.split(' '))
		head = map(lambda x: x.strip().replace(' ',''), head)
		if 'PID' in head : ix = head.index('PID')
	# get process pid
	pids = []
	for line in result :
		if 'python' in line.lower() :
			line = filter(lambda x: x != str(), line.split(' '))
			line = map(lambda x: x.strip().replace(' ',''), line)
			try :
				if ix : 
					pids.append(int(line[ix]))
				else:
					numbers = filter(lambda x: x.isdigit(), line)
					pids.append(int(numbers[0]))
			except:
				print 'Error parsing lsof results.' 
				return False
	print 'Pids found: ',pids
	# kill if specified
	if kill :
		pids_killed = []
		import signal
		for pid in pids:
			print 'Killing process: ',pid
			try :
				os.kill(pid,signal.SIGKILL)
				pids_killed.append(pid)
			except :
				print 'Failed: ',pid
		if pids_killed :
			print 'Processes killed:',pids_killed,' Waiting 10 secods...'
			import time
			time.sleep(10)
			print "Starting..."
			return True
	return False


def main(argv=None):
	
	# Configure and start
	cp.config.update(CONFIG_FILE)
	cp.config.update({'tools.staticdir.root': os.getcwd()})
	cp.tree.mount(MoodConductor(),script_name="/moodconductor",config=CONFIG_FILE)	
	port = int(cp.server.socket_port)
	ip = cp.server.socket_host
	print "Trying to bind: %(ip)s:%(port)s" %locals()
	getProcessPids(port,kill=True)
	print "Starting..."
	cp.quickstart()

if __name__ == "__main__":
	main()