comparison light8/dmxserver.py @ 118:2c25a69c084d

now tracks update frequencies of the server and each client now tracks update frequencies of the server and each client now forgets clients that haven't submitted a value in 5sec refactored sendlevels into a few methods new cmdline option --fast-updates to dump every DMX transmission to the console
author drewp
date Fri, 13 Jun 2003 13:59:08 +0000
parents 9ddea0c614ee
children f2f73a2171e6
comparison
equal deleted inserted replaced
117:c071ada6ae2b 118:2c25a69c084d
15 a length-n list of 0..1 levels which will represent the channel 15 a length-n list of 0..1 levels which will represent the channel
16 values for the n first dmx channels. 16 values for the n first dmx channels.
17 17
18 server is port 8030; xmlrpc method is called outputlevels(pid,levellist). 18 server is port 8030; xmlrpc method is called outputlevels(pid,levellist).
19 19
20
21 todo: 20 todo:
22 save dmx on quit and restore on restart 21 save dmx on quit and restore on restart
23 if parport fails, run in dummy mode (and make an option for that too) 22 if parport fails, run in dummy mode (and make an option for that too)
24 """ 23 """
25 24
26 from __future__ import division 25 from __future__ import division
27 from twisted.internet import reactor 26 from twisted.internet import reactor
28 from twisted.web import xmlrpc, server 27 from twisted.web import xmlrpc, server
29 import sys,time 28 import sys,time
29 from optik import OptionParser
30 from io import ParportDMX 30 from io import ParportDMX
31 from updatefreq import Updatefreq
31 32
32 class XMLRPCServe(xmlrpc.XMLRPC): 33 class XMLRPCServe(xmlrpc.XMLRPC):
33 def __init__(self): 34 def __init__(self,options):
34 35
35 xmlrpc.XMLRPC.__init__(self) 36 xmlrpc.XMLRPC.__init__(self)
36 37
37 self.clientlevels={} # clientPID : list of levels 38 self.clientlevels={} # clientID : list of levels
39 self.lastseen={} # clientID : time last seen
40 self.clientfreq={} # clientID : updatefreq
41
38 self.combinedlevels=[] # list of levels, after max'ing the clients 42 self.combinedlevels=[] # list of levels, after max'ing the clients
39 self.clientschanged=1 # have clients sent anything since the last send? 43 self.clientschanged=1 # have clients sent anything since the last send?
44 self.options=options
40 45
41 print "starting parport connection" 46 print "starting parport connection"
42 self.parportdmx=ParportDMX() 47 self.parportdmx=ParportDMX()
43 self.parportdmx.golive() 48 self.parportdmx.golive()
44 49
45 # start the loop 50 # start the loop
51 self.updatefreq=Updatefreq()
46 self.num_unshown_updates=None 52 self.num_unshown_updates=None
53 self.lastshownlevels=None
47 self.sendlevels() 54 self.sendlevels()
55
48 56
49 57 def purgeclients(self):
58
59 """forget about any clients who haven't sent levels in a while
60 (5 seconds)"""
61 now=time.time()
62 for cid,lastseen in self.lastseen.items():
63 if lastseen<now-5:
64 print "forgetting client %s (no activity for 5sec)" % cid
65 del self.clientlevels[cid]
66 del self.lastseen[cid]
67 del self.clientfreq[cid]
68
50 def sendlevels(self): 69 def sendlevels(self):
51 reactor.callLater(.02,self.sendlevels) 70 reactor.callLater(1/20,self.sendlevels)
52 if self.clientschanged: 71 if self.clientschanged:
53 # recalc levels 72 # recalc levels
54 oldlevels=self.combinedlevels[:]
55 self.combinedlevels=[]
56 for chan in range(0,self.parportdmx.dimmers):
57 x=0
58 for clientlist in self.clientlevels.values():
59 if len(clientlist)>chan:
60 x=max(x,clientlist[chan])
61 self.combinedlevels.append(x)
62 73
63 if self.num_unshown_updates is None or (self.combinedlevels!=oldlevels and 74 self.purgeclients()
64 self.num_unshown_updates>10): 75 self.calclevels()
76
77 if (self.num_unshown_updates is None or # first time
78 self.options.fast_updates or # show always
79 (self.combinedlevels!=self.lastshownlevels and # changed
80 self.num_unshown_updates>10)): # not too frequent
65 self.num_unshown_updates=0 81 self.num_unshown_updates=0
66 print "Levels:","".join(["% 2d "%x for x in self.combinedlevels]) 82 self.printlevels()
83 self.lastshownlevels=self.combinedlevels[:]
67 else: 84 else:
68 self.num_unshown_updates+=1 85 self.num_unshown_updates+=1
69 86
70 if (self.num_unshown_updates-1)%100==0: 87 if (self.num_unshown_updates-1)%50==0:
71 sys.stdout.write("dmxserver up at %s \r"%time.strftime("%H:%M:%S")) 88 self.printstats()
72 sys.stdout.flush() 89
73 # now send combinedlevels (they'll get divided by 100) 90 self.sendlevels_dmx()
91
92 def calclevels(self):
93 """combine all the known client levels into self.combinedlevels"""
94 self.combinedlevels=[]
95 for chan in range(0,self.parportdmx.dimmers):
96 x=0
97 for clientlist in self.clientlevels.values():
98 if len(clientlist)>chan:
99 # clamp client levels to 0..1
100 cl=max(0,min(1,clientlist[chan]))
101 x=max(x,cl)
102 self.combinedlevels.append(x)
103
104
105 def printlevels(self):
106 """write all the levels to stdout"""
107 print "Levels:","".join(["% 2d "%(x*100) for
108 x in self.combinedlevels])
109
110 def printstats(self):
111 """print the clock, freq, etc, with a \r at the end"""
112
113 sys.stdout.write("dmxserver up at %s, [server %s] "%
114 (time.strftime("%H:%M:%S"),
115 str(self.updatefreq),
116 ))
117 for cid,freq in self.clientfreq.items():
118 sys.stdout.write("[%s %s] " % (cid,str(freq)))
119 sys.stdout.write("\r")
120 sys.stdout.flush()
121
122
123 def sendlevels_dmx(self):
124 """output self.combinedlevels to dmx, and keep the updates/sec stats"""
125 # they'll get divided by 100
74 if self.parportdmx: 126 if self.parportdmx:
75 self.parportdmx.sendlevels([l*100 for l in self.combinedlevels]) 127 self.parportdmx.sendlevels([l*100 for l in self.combinedlevels])
76 128 self.updatefreq.update()
129
77 def xmlrpc_echo(self,x): 130 def xmlrpc_echo(self,x):
78 return x 131 return x
79 132
80 def xmlrpc_outputlevels(self,pid,levellist): 133 def xmlrpc_outputlevels(self,cid,levellist):
81 self.clientlevels[pid]=levellist 134 """send a unique id for your client (name+pid maybe), then
135 the variable-length dmx levellist (scaled 0..1)"""
136 self.clientlevels[cid]=levellist
82 self.clientschanged=1 137 self.clientschanged=1
138 if cid not in self.lastseen:
139 print "hello new client %s" % cid
140 self.clientfreq[cid]=Updatefreq()
141 self.lastseen[cid]=time.time()
142 self.clientfreq[cid].update()
83 return "ok" 143 return "ok"
84 144
145 parser=OptionParser()
146 parser.add_option("-f","--fast-updates",action='store_true',
147 help=('display all dmx output to stdout instead '
148 'of the usual reduced output'))
149 (options,songfiles)=parser.parse_args()
150
85 print "starting xmlrpc server on port 8030" 151 print "starting xmlrpc server on port 8030"
86 reactor.listenTCP(8030,server.Site(XMLRPCServe())) 152 reactor.listenTCP(8030,server.Site(XMLRPCServe(options)))
87 reactor.run() 153 reactor.run()
88 154