Mercurial > code > home > repos > light9
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 |