Mercurial > code > home > repos > light9
annotate light8/dmxserver.py @ 134:f2f73a2171e6
many adjustments to the loops and timing
many adjustments to the loops and timing
now sends the hardware updates only when clients change, but at least 1Hz
new option to adjust the rate of the loop that considers sending changes (if
the lights have changed)
author | drewp |
---|---|
date | Sat, 14 Jun 2003 14:59:09 +0000 |
parents | 2c25a69c084d |
children | 8e6165bc1ca5 |
rev | line source |
---|---|
116
9ddea0c614ee
much prettier stdout, including a clock (so you can tell the server's running)
drewp
parents:
112
diff
changeset
|
1 #!/usr/bin/python |
0 | 2 """ |
3 | |
4 this is the only process to talk to the dmx hardware. other clients | |
5 can connect to this server and present dmx output, and this server | |
6 will max ('pile-on') all the client requests. | |
7 | |
8 this server has a level display which is the final set of values that | |
9 goes to the hardware. | |
10 | |
11 clients shall connect to the xmlrpc server and send: | |
12 | |
13 their PID (or some other cookie) | |
14 | |
15 a length-n list of 0..1 levels which will represent the channel | |
16 values for the n first dmx channels. | |
17 | |
18 server is port 8030; xmlrpc method is called outputlevels(pid,levellist). | |
19 | |
116
9ddea0c614ee
much prettier stdout, including a clock (so you can tell the server's running)
drewp
parents:
112
diff
changeset
|
20 todo: |
9ddea0c614ee
much prettier stdout, including a clock (so you can tell the server's running)
drewp
parents:
112
diff
changeset
|
21 save dmx on quit and restore on restart |
9ddea0c614ee
much prettier stdout, including a clock (so you can tell the server's running)
drewp
parents:
112
diff
changeset
|
22 if parport fails, run in dummy mode (and make an option for that too) |
0 | 23 """ |
24 | |
25 from __future__ import division | |
26 from twisted.internet import reactor | |
27 from twisted.web import xmlrpc, server | |
116
9ddea0c614ee
much prettier stdout, including a clock (so you can tell the server's running)
drewp
parents:
112
diff
changeset
|
28 import sys,time |
118
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
29 from optik import OptionParser |
0 | 30 from io import ParportDMX |
118
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
31 from updatefreq import Updatefreq |
0 | 32 |
33 class XMLRPCServe(xmlrpc.XMLRPC): | |
118
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
34 def __init__(self,options): |
116
9ddea0c614ee
much prettier stdout, including a clock (so you can tell the server's running)
drewp
parents:
112
diff
changeset
|
35 |
9ddea0c614ee
much prettier stdout, including a clock (so you can tell the server's running)
drewp
parents:
112
diff
changeset
|
36 xmlrpc.XMLRPC.__init__(self) |
9ddea0c614ee
much prettier stdout, including a clock (so you can tell the server's running)
drewp
parents:
112
diff
changeset
|
37 |
118
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
38 self.clientlevels={} # clientID : list of levels |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
39 self.lastseen={} # clientID : time last seen |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
40 self.clientfreq={} # clientID : updatefreq |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
41 |
0 | 42 self.combinedlevels=[] # list of levels, after max'ing the clients |
43 self.clientschanged=1 # have clients sent anything since the last send? | |
118
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
44 self.options=options |
134 | 45 self.lastupdate=0 # time of last dmx send |
46 self.laststatsprint=0 # time | |
47 | |
48 # desired seconds between sendlevels() calls | |
49 self.calldelay=1/options.updates_per_sec | |
0 | 50 |
51 print "starting parport connection" | |
52 self.parportdmx=ParportDMX() | |
112
afbdae5e1359
dmx light output is now via a separate process which light8 talks to.
drewp
parents:
0
diff
changeset
|
53 self.parportdmx.golive() |
0 | 54 |
134 | 55 self.updatefreq=Updatefreq() # freq of actual dmx sends |
116
9ddea0c614ee
much prettier stdout, including a clock (so you can tell the server's running)
drewp
parents:
112
diff
changeset
|
56 self.num_unshown_updates=None |
118
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
57 self.lastshownlevels=None |
134 | 58 # start the loop |
0 | 59 self.sendlevels() |
134 | 60 |
61 # the other loop | |
62 self.purgeclients() | |
118
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
63 |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
64 def purgeclients(self): |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
65 |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
66 """forget about any clients who haven't sent levels in a while |
134 | 67 (5 seconds). this runs in a loop""" |
68 | |
69 purge_age=10 # seconds | |
70 | |
71 reactor.callLater(1,self.purgeclients) | |
72 | |
118
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
73 now=time.time() |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
74 for cid,lastseen in self.lastseen.items(): |
134 | 75 if lastseen<now-purge_age: |
76 print ("forgetting client %s (no activity for %s sec)" % | |
77 (cid,purge_age)) | |
118
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
78 del self.clientlevels[cid] |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
79 del self.lastseen[cid] |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
80 del self.clientfreq[cid] |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
81 |
0 | 82 def sendlevels(self): |
134 | 83 |
84 """sends to dmx if levels have changed, or if we havent sent | |
85 in a while""" | |
86 | |
87 reactor.callLater(self.calldelay,self.sendlevels) | |
88 | |
0 | 89 if self.clientschanged: |
90 # recalc levels | |
91 | |
118
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
92 self.calclevels() |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
93 |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
94 if (self.num_unshown_updates is None or # first time |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
95 self.options.fast_updates or # show always |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
96 (self.combinedlevels!=self.lastshownlevels and # changed |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
97 self.num_unshown_updates>10)): # not too frequent |
116
9ddea0c614ee
much prettier stdout, including a clock (so you can tell the server's running)
drewp
parents:
112
diff
changeset
|
98 self.num_unshown_updates=0 |
118
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
99 self.printlevels() |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
100 self.lastshownlevels=self.combinedlevels[:] |
116
9ddea0c614ee
much prettier stdout, including a clock (so you can tell the server's running)
drewp
parents:
112
diff
changeset
|
101 else: |
9ddea0c614ee
much prettier stdout, including a clock (so you can tell the server's running)
drewp
parents:
112
diff
changeset
|
102 self.num_unshown_updates+=1 |
9ddea0c614ee
much prettier stdout, including a clock (so you can tell the server's running)
drewp
parents:
112
diff
changeset
|
103 |
134 | 104 if time.time()>self.laststatsprint+2: |
105 self.laststatsprint=time.time() | |
118
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
106 self.printstats() |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
107 |
134 | 108 if self.clientschanged or time.time()>self.lastupdate+1: |
109 self.lastupdate=time.time() | |
110 self.sendlevels_dmx() | |
111 | |
112 self.clientschanged=0 # clear the flag | |
113 | |
118
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
114 def calclevels(self): |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
115 """combine all the known client levels into self.combinedlevels""" |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
116 self.combinedlevels=[] |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
117 for chan in range(0,self.parportdmx.dimmers): |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
118 x=0 |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
119 for clientlist in self.clientlevels.values(): |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
120 if len(clientlist)>chan: |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
121 # clamp client levels to 0..1 |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
122 cl=max(0,min(1,clientlist[chan])) |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
123 x=max(x,cl) |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
124 self.combinedlevels.append(x) |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
125 |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
126 def printlevels(self): |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
127 """write all the levels to stdout""" |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
128 print "Levels:","".join(["% 2d "%(x*100) for |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
129 x in self.combinedlevels]) |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
130 |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
131 def printstats(self): |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
132 """print the clock, freq, etc, with a \r at the end""" |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
133 |
134 | 134 sys.stdout.write("dmxserver up at %s, [polls %s] "% |
118
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
135 (time.strftime("%H:%M:%S"), |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
136 str(self.updatefreq), |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
137 )) |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
138 for cid,freq in self.clientfreq.items(): |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
139 sys.stdout.write("[%s %s] " % (cid,str(freq))) |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
140 sys.stdout.write("\r") |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
141 sys.stdout.flush() |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
142 |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
143 def sendlevels_dmx(self): |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
144 """output self.combinedlevels to dmx, and keep the updates/sec stats""" |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
145 # they'll get divided by 100 |
116
9ddea0c614ee
much prettier stdout, including a clock (so you can tell the server's running)
drewp
parents:
112
diff
changeset
|
146 if self.parportdmx: |
118
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
147 self.parportdmx.sendlevels([l*100 for l in self.combinedlevels]) |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
148 self.updatefreq.update() |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
149 |
0 | 150 def xmlrpc_echo(self,x): |
151 return x | |
152 | |
118
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
153 def xmlrpc_outputlevels(self,cid,levellist): |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
154 """send a unique id for your client (name+pid maybe), then |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
155 the variable-length dmx levellist (scaled 0..1)""" |
134 | 156 if levellist!=self.clientlevels.get(cid,[]): |
157 self.clientlevels[cid]=levellist | |
158 self.clientschanged=1 | |
159 if cid not in self.lastseen: | |
160 print "hello new client %s" % cid | |
161 self.clientfreq[cid]=Updatefreq() | |
162 | |
118
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
163 self.lastseen[cid]=time.time() |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
164 self.clientfreq[cid].update() |
0 | 165 return "ok" |
166 | |
118
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
167 parser=OptionParser() |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
168 parser.add_option("-f","--fast-updates",action='store_true', |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
169 help=('display all dmx output to stdout instead ' |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
170 'of the usual reduced output')) |
134 | 171 parser.add_option("-r","--updates-per-sec",type='float',default=20, |
172 help=('dmx output frequency')) | |
118
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
173 (options,songfiles)=parser.parse_args() |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
174 |
134 | 175 print options |
176 | |
116
9ddea0c614ee
much prettier stdout, including a clock (so you can tell the server's running)
drewp
parents:
112
diff
changeset
|
177 print "starting xmlrpc server on port 8030" |
118
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
178 reactor.listenTCP(8030,server.Site(XMLRPCServe(options))) |
0 | 179 reactor.run() |
180 |