# HG changeset patch # User drewp # Date 2003-06-11 09:36:35 # Node ID afbdae5e1359774bdf9549bbc56140a7a1b1f7ea # Parent 0c619695d6c6e081b740f63744d5875805391db7 dmx light output is now via a separate process which light8 talks to. dmx light output is now via a separate process which light8 talks to. other programs can also submit dmx to the server diff --git a/light8/Lightboard.py b/light8/Lightboard.py --- a/light8/Lightboard.py +++ b/light8/Lightboard.py @@ -1,8 +1,9 @@ -from __future__ import nested_scopes +from __future__ import nested_scopes,division from Tix import * from signal import signal, SIGINT from time import time +import xmlrpclib import sys, cPickle, random from uihelpers import * @@ -22,9 +23,9 @@ class Pickles: self.windowpos = windowpos class Lightboard: - def __init__(self, master, parportdmx, DUMMY): + def __init__(self, master, DUMMY): self.master = master - self.parportdmx = parportdmx + self.DUMMY = DUMMY self.jostle_mode = 0 self.lastline = None @@ -48,7 +49,11 @@ class Lightboard: self.get_data() self.buildinterface() self.load() - print "Light 8.8: Enterring backgroundloop" + + # get a connection to the dmx server + self.dmxserver=xmlrpclib.Server("http://localhost:8030") + + print "Light 8.8: Entering backgroundloop" self.backgroundloop() self.updatestagelevels() self.rec_file = open('light9.log', 'a') @@ -294,7 +299,8 @@ class Lightboard: levels = [min(100, max(x + delta, 0)) for x in levels] # print "jostled", levels - self.parportdmx.sendlevels(levels) + self.dmxserver.outputlevels("light8-%s" %os.getpid(),[l/100 for l in levels]) +# self.parportdmx.sendlevels(levels) def updatestagelevels(self): self.master.after(100, self.updatestagelevels) diff --git a/light8/dmxserver.py b/light8/dmxserver.py --- a/light8/dmxserver.py +++ b/light8/dmxserver.py @@ -1,4 +1,3 @@ -#!/usr/bin/python """ this is the only process to talk to the dmx hardware. other clients @@ -17,170 +16,58 @@ clients shall connect to the xmlrpc serv server is port 8030; xmlrpc method is called outputlevels(pid,levellist). -todo: - save dmx on quit and restore on restart - if parport fails, run in dummy mode (and make an option for that too) """ from __future__ import division from twisted.internet import reactor from twisted.web import xmlrpc, server -import sys,time,os -from optparse import OptionParser + +import sys +sys.path.append("../light8") from io import ParportDMX -from updatefreq import Updatefreq class XMLRPCServe(xmlrpc.XMLRPC): - def __init__(self,options): - - xmlrpc.XMLRPC.__init__(self) - - self.clientlevels={} # clientID : list of levels - self.lastseen={} # clientID : time last seen - self.clientfreq={} # clientID : updatefreq - + def __init__(self): + self.clientlevels={} # clientPID : list of levels self.combinedlevels=[] # list of levels, after max'ing the clients self.clientschanged=1 # have clients sent anything since the last send? - self.options=options - self.lastupdate=0 # time of last dmx send - self.laststatsprint=0 # time - - # desired seconds between sendlevels() calls - self.calldelay=1/options.updates_per_sec print "starting parport connection" self.parportdmx=ParportDMX() - if os.environ.get('DMXDUMMY',0): - self.parportdmx.godummy() - else: - self.parportdmx.golive() - + self.parportdmx.golive() - self.updatefreq=Updatefreq() # freq of actual dmx sends - self.num_unshown_updates=None - self.lastshownlevels=None # start the loop + self.numupdates=0 self.sendlevels() - # the other loop - self.purgeclients() - - def purgeclients(self): - - """forget about any clients who haven't sent levels in a while. - this runs in a loop""" - - purge_age=10 # seconds - - reactor.callLater(1,self.purgeclients) - - now=time.time() - cids=self.clientlevels.keys() - for cid in cids: - lastseen=self.lastseen[cid] - if lastseenchan: + x=max(x,clientlist[chan]) + self.combinedlevels.append(x) - self.calclevels() - - if (self.num_unshown_updates is None or # first time - self.options.fast_updates or # show always - (self.combinedlevels!=self.lastshownlevels and # changed - self.num_unshown_updates>5)): # not too frequent - self.num_unshown_updates=0 - self.printlevels() - self.lastshownlevels=self.combinedlevels[:] - else: - self.num_unshown_updates+=1 - - if time.time()>self.laststatsprint+2: - self.laststatsprint=time.time() - self.printstats() - - if self.clientschanged or time.time()>self.lastupdate+1: - self.lastupdate=time.time() - self.sendlevels_dmx() - - self.clientschanged=0 # clear the flag + self.numupdates=self.numupdates+1 + if (self.numupdates%200)==0: + print self.combinedlevels + + # now send combinedlevels (they'll get divided by 100) + self.parportdmx.sendlevels([l*100 for l in self.combinedlevels]) - def calclevels(self): - """combine all the known client levels into self.combinedlevels""" - self.combinedlevels=[] - for chan in range(0,self.parportdmx.dimmers): - x=0 - for clientlist in self.clientlevels.values(): - if len(clientlist)>chan: - # clamp client levels to 0..1 - cl=max(0,min(1,clientlist[chan])) - x=max(x,cl) - self.combinedlevels.append(x) - - def printlevels(self): - """write all the levels to stdout""" - print "Levels:","".join(["% 2d "%(x*100) for - x in self.combinedlevels]) - - def printstats(self): - """print the clock, freq, etc, with a \r at the end""" - - sys.stdout.write("dmxserver up at %s, [polls %s] "% - (time.strftime("%H:%M:%S"), - str(self.updatefreq), - )) - for cid,freq in self.clientfreq.items(): - sys.stdout.write("[%s %s] " % (cid,str(freq))) - sys.stdout.write("\r") - sys.stdout.flush() - - def sendlevels_dmx(self): - """output self.combinedlevels to dmx, and keep the updates/sec stats""" - # they'll get divided by 100 - if self.parportdmx: - self.parportdmx.sendlevels([l*100 for l in self.combinedlevels]) - self.updatefreq.update() - def xmlrpc_echo(self,x): return x - def xmlrpc_outputlevels(self,cid,levellist): - """send a unique id for your client (name+pid maybe), then - the variable-length dmx levellist (scaled 0..1)""" - if levellist!=self.clientlevels.get(cid,None): - self.clientlevels[cid]=levellist - self.clientschanged=1 - if cid not in self.lastseen: - print "hello new client %s" % cid - self.clientfreq[cid]=Updatefreq() - - self.lastseen[cid]=time.time() - self.clientfreq[cid].update() + def xmlrpc_outputlevels(self,pid,levellist): + self.clientlevels[pid]=levellist + self.clientschanged=1 return "ok" -parser=OptionParser() -parser.add_option("-f","--fast-updates",action='store_true', - help=('display all dmx output to stdout instead ' - 'of the usual reduced output')) -parser.add_option("-r","--updates-per-sec",type='float',default=20, - help=('dmx output frequency')) -(options,songfiles)=parser.parse_args() - -print options - -print "starting xmlrpc server on port 8030" -reactor.listenTCP(8030,server.Site(XMLRPCServe(options))) +print "starting server on 8030" +reactor.listenTCP(8030,server.Site(XMLRPCServe())) reactor.run() diff --git a/light8/rsn.py b/light8/rsn.py --- a/light8/rsn.py +++ b/light8/rsn.py @@ -3,7 +3,7 @@ from __future__ import nested_scopes from Tix import * from signal import signal, SIGINT -import io +import xmlrpclib from uihelpers import * from Fader import Fader from Lightboard import Lightboard @@ -23,15 +23,13 @@ root.wm_geometry('+462+470') root.tk_focusFollowsMouse() -parportdmx = io.ParportDMX() - if not DUMMY: # this turns the parportdmx from dummy to live print "Light 8.8: Preparing DMX interface..." parportdmx.golive() print "Light 8.8: And this...is Mr. Lightboard" -mr_lightboard = Lightboard(root,parportdmx,DUMMY) +mr_lightboard = Lightboard(root,DUMMY) # root.tk_setPalette('gray40') signal(SIGINT, mr_lightboard.quit)