Mercurial > code > home > repos > light9
annotate bin/dmxserver @ 209:1a84c5e83d3e
dmxserver and subcomposer work in new layout
author | drewp@bigasterisk.com |
---|---|
date | Sun, 10 Apr 2005 19:12:57 +0000 |
parents | light8/dmxserver.py@cde2ae379be0 |
children | f41004d5a507 |
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 | |
199 | 28 import sys,time,os |
194 | 29 from optparse import OptionParser |
209
1a84c5e83d3e
dmxserver and subcomposer work in new layout
drewp@bigasterisk.com
parents:
199
diff
changeset
|
30 import run_local |
1a84c5e83d3e
dmxserver and subcomposer work in new layout
drewp@bigasterisk.com
parents:
199
diff
changeset
|
31 from light9.io import ParportDMX |
1a84c5e83d3e
dmxserver and subcomposer work in new layout
drewp@bigasterisk.com
parents:
199
diff
changeset
|
32 from light9.updatefreq import Updatefreq |
0 | 33 |
34 class XMLRPCServe(xmlrpc.XMLRPC): | |
118
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
35 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
|
36 |
9ddea0c614ee
much prettier stdout, including a clock (so you can tell the server's running)
drewp
parents:
112
diff
changeset
|
37 xmlrpc.XMLRPC.__init__(self) |
9ddea0c614ee
much prettier stdout, including a clock (so you can tell the server's running)
drewp
parents:
112
diff
changeset
|
38 |
118
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
39 self.clientlevels={} # clientID : list of levels |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
40 self.lastseen={} # clientID : time last seen |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
41 self.clientfreq={} # clientID : updatefreq |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
42 |
0 | 43 self.combinedlevels=[] # list of levels, after max'ing the clients |
44 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
|
45 self.options=options |
134 | 46 self.lastupdate=0 # time of last dmx send |
47 self.laststatsprint=0 # time | |
48 | |
49 # desired seconds between sendlevels() calls | |
50 self.calldelay=1/options.updates_per_sec | |
0 | 51 |
52 print "starting parport connection" | |
53 self.parportdmx=ParportDMX() | |
199 | 54 if os.environ.get('DMXDUMMY',0): |
55 self.parportdmx.godummy() | |
56 else: | |
57 self.parportdmx.golive() | |
58 | |
0 | 59 |
134 | 60 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
|
61 self.num_unshown_updates=None |
118
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
62 self.lastshownlevels=None |
134 | 63 # start the loop |
0 | 64 self.sendlevels() |
134 | 65 |
66 # the other loop | |
67 self.purgeclients() | |
118
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
68 |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
69 def purgeclients(self): |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
70 |
194 | 71 """forget about any clients who haven't sent levels in a while. |
72 this runs in a loop""" | |
134 | 73 |
74 purge_age=10 # seconds | |
75 | |
76 reactor.callLater(1,self.purgeclients) | |
77 | |
118
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
78 now=time.time() |
194 | 79 cids=self.clientlevels.keys() |
80 for cid in cids: | |
81 lastseen=self.lastseen[cid] | |
134 | 82 if lastseen<now-purge_age: |
83 print ("forgetting client %s (no activity for %s sec)" % | |
84 (cid,purge_age)) | |
118
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
85 del self.clientlevels[cid] |
194 | 86 del self.clientfreq[cid] |
118
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
87 del self.lastseen[cid] |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
88 |
0 | 89 def sendlevels(self): |
134 | 90 |
91 """sends to dmx if levels have changed, or if we havent sent | |
92 in a while""" | |
93 | |
94 reactor.callLater(self.calldelay,self.sendlevels) | |
95 | |
0 | 96 if self.clientschanged: |
97 # recalc levels | |
98 | |
118
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
99 self.calclevels() |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
100 |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
101 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
|
102 self.options.fast_updates or # show always |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
103 (self.combinedlevels!=self.lastshownlevels and # changed |
140
8e6165bc1ca5
try to show levels more often (it's still not often enough)
drewp
parents:
134
diff
changeset
|
104 self.num_unshown_updates>5)): # not too frequent |
116
9ddea0c614ee
much prettier stdout, including a clock (so you can tell the server's running)
drewp
parents:
112
diff
changeset
|
105 self.num_unshown_updates=0 |
118
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
106 self.printlevels() |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
107 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
|
108 else: |
9ddea0c614ee
much prettier stdout, including a clock (so you can tell the server's running)
drewp
parents:
112
diff
changeset
|
109 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
|
110 |
134 | 111 if time.time()>self.laststatsprint+2: |
112 self.laststatsprint=time.time() | |
118
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
113 self.printstats() |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
114 |
134 | 115 if self.clientschanged or time.time()>self.lastupdate+1: |
116 self.lastupdate=time.time() | |
117 self.sendlevels_dmx() | |
118 | |
119 self.clientschanged=0 # clear the flag | |
120 | |
118
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
121 def calclevels(self): |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
122 """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
|
123 self.combinedlevels=[] |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
124 for chan in range(0,self.parportdmx.dimmers): |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
125 x=0 |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
126 for clientlist in self.clientlevels.values(): |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
127 if len(clientlist)>chan: |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
128 # clamp client levels to 0..1 |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
129 cl=max(0,min(1,clientlist[chan])) |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
130 x=max(x,cl) |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
131 self.combinedlevels.append(x) |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
132 |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
133 def printlevels(self): |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
134 """write all the levels to stdout""" |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
135 print "Levels:","".join(["% 2d "%(x*100) for |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
136 x in self.combinedlevels]) |
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 def printstats(self): |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
139 """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
|
140 |
134 | 141 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
|
142 (time.strftime("%H:%M:%S"), |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
143 str(self.updatefreq), |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
144 )) |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
145 for cid,freq in self.clientfreq.items(): |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
146 sys.stdout.write("[%s %s] " % (cid,str(freq))) |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
147 sys.stdout.write("\r") |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
148 sys.stdout.flush() |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
149 |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
150 def sendlevels_dmx(self): |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
151 """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
|
152 # 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
|
153 if self.parportdmx: |
118
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
154 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
|
155 self.updatefreq.update() |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
156 |
0 | 157 def xmlrpc_echo(self,x): |
158 return x | |
159 | |
118
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
160 def xmlrpc_outputlevels(self,cid,levellist): |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
161 """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
|
162 the variable-length dmx levellist (scaled 0..1)""" |
194 | 163 if levellist!=self.clientlevels.get(cid,None): |
134 | 164 self.clientlevels[cid]=levellist |
165 self.clientschanged=1 | |
166 if cid not in self.lastseen: | |
167 print "hello new client %s" % cid | |
168 self.clientfreq[cid]=Updatefreq() | |
169 | |
118
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
170 self.lastseen[cid]=time.time() |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
171 self.clientfreq[cid].update() |
0 | 172 return "ok" |
173 | |
118
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
174 parser=OptionParser() |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
175 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
|
176 help=('display all dmx output to stdout instead ' |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
177 'of the usual reduced output')) |
134 | 178 parser.add_option("-r","--updates-per-sec",type='float',default=20, |
179 help=('dmx output frequency')) | |
118
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
180 (options,songfiles)=parser.parse_args() |
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
181 |
134 | 182 print options |
183 | |
116
9ddea0c614ee
much prettier stdout, including a clock (so you can tell the server's running)
drewp
parents:
112
diff
changeset
|
184 print "starting xmlrpc server on port 8030" |
118
2c25a69c084d
now tracks update frequencies of the server and each client
drewp
parents:
116
diff
changeset
|
185 reactor.listenTCP(8030,server.Site(XMLRPCServe(options))) |
0 | 186 reactor.run() |
187 |