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
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
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
45b12307c695 Initial revision
drewp
parents:
diff changeset
2 """
45b12307c695 Initial revision
drewp
parents:
diff changeset
3
45b12307c695 Initial revision
drewp
parents:
diff changeset
4 this is the only process to talk to the dmx hardware. other clients
45b12307c695 Initial revision
drewp
parents:
diff changeset
5 can connect to this server and present dmx output, and this server
45b12307c695 Initial revision
drewp
parents:
diff changeset
6 will max ('pile-on') all the client requests.
45b12307c695 Initial revision
drewp
parents:
diff changeset
7
45b12307c695 Initial revision
drewp
parents:
diff changeset
8 this server has a level display which is the final set of values that
45b12307c695 Initial revision
drewp
parents:
diff changeset
9 goes to the hardware.
45b12307c695 Initial revision
drewp
parents:
diff changeset
10
45b12307c695 Initial revision
drewp
parents:
diff changeset
11 clients shall connect to the xmlrpc server and send:
45b12307c695 Initial revision
drewp
parents:
diff changeset
12
45b12307c695 Initial revision
drewp
parents:
diff changeset
13 their PID (or some other cookie)
45b12307c695 Initial revision
drewp
parents:
diff changeset
14
45b12307c695 Initial revision
drewp
parents:
diff changeset
15 a length-n list of 0..1 levels which will represent the channel
45b12307c695 Initial revision
drewp
parents:
diff changeset
16 values for the n first dmx channels.
45b12307c695 Initial revision
drewp
parents:
diff changeset
17
45b12307c695 Initial revision
drewp
parents:
diff changeset
18 server is port 8030; xmlrpc method is called outputlevels(pid,levellist).
45b12307c695 Initial revision
drewp
parents:
diff changeset
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
45b12307c695 Initial revision
drewp
parents:
diff changeset
23 """
45b12307c695 Initial revision
drewp
parents:
diff changeset
24
45b12307c695 Initial revision
drewp
parents:
diff changeset
25 from __future__ import division
45b12307c695 Initial revision
drewp
parents:
diff changeset
26 from twisted.internet import reactor
45b12307c695 Initial revision
drewp
parents:
diff changeset
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
45b12307c695 Initial revision
drewp
parents:
diff changeset
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
45b12307c695 Initial revision
drewp
parents:
diff changeset
32
45b12307c695 Initial revision
drewp
parents:
diff changeset
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
45b12307c695 Initial revision
drewp
parents:
diff changeset
42 self.combinedlevels=[] # list of levels, after max'ing the clients
45b12307c695 Initial revision
drewp
parents:
diff changeset
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
f2f73a2171e6 many adjustments to the loops and timing
drewp
parents: 118
diff changeset
45 self.lastupdate=0 # time of last dmx send
f2f73a2171e6 many adjustments to the loops and timing
drewp
parents: 118
diff changeset
46 self.laststatsprint=0 # time
f2f73a2171e6 many adjustments to the loops and timing
drewp
parents: 118
diff changeset
47
f2f73a2171e6 many adjustments to the loops and timing
drewp
parents: 118
diff changeset
48 # desired seconds between sendlevels() calls
f2f73a2171e6 many adjustments to the loops and timing
drewp
parents: 118
diff changeset
49 self.calldelay=1/options.updates_per_sec
0
45b12307c695 Initial revision
drewp
parents:
diff changeset
50
45b12307c695 Initial revision
drewp
parents:
diff changeset
51 print "starting parport connection"
45b12307c695 Initial revision
drewp
parents:
diff changeset
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
45b12307c695 Initial revision
drewp
parents:
diff changeset
54
134
f2f73a2171e6 many adjustments to the loops and timing
drewp
parents: 118
diff changeset
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
f2f73a2171e6 many adjustments to the loops and timing
drewp
parents: 118
diff changeset
58 # start the loop
0
45b12307c695 Initial revision
drewp
parents:
diff changeset
59 self.sendlevels()
134
f2f73a2171e6 many adjustments to the loops and timing
drewp
parents: 118
diff changeset
60
f2f73a2171e6 many adjustments to the loops and timing
drewp
parents: 118
diff changeset
61 # the other loop
f2f73a2171e6 many adjustments to the loops and timing
drewp
parents: 118
diff changeset
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
f2f73a2171e6 many adjustments to the loops and timing
drewp
parents: 118
diff changeset
67 (5 seconds). this runs in a loop"""
f2f73a2171e6 many adjustments to the loops and timing
drewp
parents: 118
diff changeset
68
f2f73a2171e6 many adjustments to the loops and timing
drewp
parents: 118
diff changeset
69 purge_age=10 # seconds
f2f73a2171e6 many adjustments to the loops and timing
drewp
parents: 118
diff changeset
70
f2f73a2171e6 many adjustments to the loops and timing
drewp
parents: 118
diff changeset
71 reactor.callLater(1,self.purgeclients)
f2f73a2171e6 many adjustments to the loops and timing
drewp
parents: 118
diff changeset
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
f2f73a2171e6 many adjustments to the loops and timing
drewp
parents: 118
diff changeset
75 if lastseen<now-purge_age:
f2f73a2171e6 many adjustments to the loops and timing
drewp
parents: 118
diff changeset
76 print ("forgetting client %s (no activity for %s sec)" %
f2f73a2171e6 many adjustments to the loops and timing
drewp
parents: 118
diff changeset
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
45b12307c695 Initial revision
drewp
parents:
diff changeset
82 def sendlevels(self):
134
f2f73a2171e6 many adjustments to the loops and timing
drewp
parents: 118
diff changeset
83
f2f73a2171e6 many adjustments to the loops and timing
drewp
parents: 118
diff changeset
84 """sends to dmx if levels have changed, or if we havent sent
f2f73a2171e6 many adjustments to the loops and timing
drewp
parents: 118
diff changeset
85 in a while"""
f2f73a2171e6 many adjustments to the loops and timing
drewp
parents: 118
diff changeset
86
f2f73a2171e6 many adjustments to the loops and timing
drewp
parents: 118
diff changeset
87 reactor.callLater(self.calldelay,self.sendlevels)
f2f73a2171e6 many adjustments to the loops and timing
drewp
parents: 118
diff changeset
88
0
45b12307c695 Initial revision
drewp
parents:
diff changeset
89 if self.clientschanged:
45b12307c695 Initial revision
drewp
parents:
diff changeset
90 # recalc levels
45b12307c695 Initial revision
drewp
parents:
diff changeset
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
f2f73a2171e6 many adjustments to the loops and timing
drewp
parents: 118
diff changeset
104 if time.time()>self.laststatsprint+2:
f2f73a2171e6 many adjustments to the loops and timing
drewp
parents: 118
diff changeset
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
f2f73a2171e6 many adjustments to the loops and timing
drewp
parents: 118
diff changeset
108 if self.clientschanged or time.time()>self.lastupdate+1:
f2f73a2171e6 many adjustments to the loops and timing
drewp
parents: 118
diff changeset
109 self.lastupdate=time.time()
f2f73a2171e6 many adjustments to the loops and timing
drewp
parents: 118
diff changeset
110 self.sendlevels_dmx()
f2f73a2171e6 many adjustments to the loops and timing
drewp
parents: 118
diff changeset
111
f2f73a2171e6 many adjustments to the loops and timing
drewp
parents: 118
diff changeset
112 self.clientschanged=0 # clear the flag
f2f73a2171e6 many adjustments to the loops and timing
drewp
parents: 118
diff changeset
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
f2f73a2171e6 many adjustments to the loops and timing
drewp
parents: 118
diff changeset
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
45b12307c695 Initial revision
drewp
parents:
diff changeset
150 def xmlrpc_echo(self,x):
45b12307c695 Initial revision
drewp
parents:
diff changeset
151 return x
45b12307c695 Initial revision
drewp
parents:
diff changeset
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
f2f73a2171e6 many adjustments to the loops and timing
drewp
parents: 118
diff changeset
156 if levellist!=self.clientlevels.get(cid,[]):
f2f73a2171e6 many adjustments to the loops and timing
drewp
parents: 118
diff changeset
157 self.clientlevels[cid]=levellist
f2f73a2171e6 many adjustments to the loops and timing
drewp
parents: 118
diff changeset
158 self.clientschanged=1
f2f73a2171e6 many adjustments to the loops and timing
drewp
parents: 118
diff changeset
159 if cid not in self.lastseen:
f2f73a2171e6 many adjustments to the loops and timing
drewp
parents: 118
diff changeset
160 print "hello new client %s" % cid
f2f73a2171e6 many adjustments to the loops and timing
drewp
parents: 118
diff changeset
161 self.clientfreq[cid]=Updatefreq()
f2f73a2171e6 many adjustments to the loops and timing
drewp
parents: 118
diff changeset
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
45b12307c695 Initial revision
drewp
parents:
diff changeset
165 return "ok"
45b12307c695 Initial revision
drewp
parents:
diff changeset
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
f2f73a2171e6 many adjustments to the loops and timing
drewp
parents: 118
diff changeset
171 parser.add_option("-r","--updates-per-sec",type='float',default=20,
f2f73a2171e6 many adjustments to the loops and timing
drewp
parents: 118
diff changeset
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
f2f73a2171e6 many adjustments to the loops and timing
drewp
parents: 118
diff changeset
175 print options
f2f73a2171e6 many adjustments to the loops and timing
drewp
parents: 118
diff changeset
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
45b12307c695 Initial revision
drewp
parents:
diff changeset
179 reactor.run()
45b12307c695 Initial revision
drewp
parents:
diff changeset
180