comparison 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
comparison
equal deleted inserted replaced
133:e7b531d10cf9 134:f2f73a2171e6
40 self.clientfreq={} # clientID : updatefreq 40 self.clientfreq={} # clientID : updatefreq
41 41
42 self.combinedlevels=[] # list of levels, after max'ing the clients 42 self.combinedlevels=[] # list of levels, after max'ing the clients
43 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 44 self.options=options
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
45 50
46 print "starting parport connection" 51 print "starting parport connection"
47 self.parportdmx=ParportDMX() 52 self.parportdmx=ParportDMX()
48 self.parportdmx.golive() 53 self.parportdmx.golive()
49 54
50 # start the loop 55 self.updatefreq=Updatefreq() # freq of actual dmx sends
51 self.updatefreq=Updatefreq()
52 self.num_unshown_updates=None 56 self.num_unshown_updates=None
53 self.lastshownlevels=None 57 self.lastshownlevels=None
58 # start the loop
54 self.sendlevels() 59 self.sendlevels()
60
61 # the other loop
62 self.purgeclients()
55 63
56
57 def purgeclients(self): 64 def purgeclients(self):
58 65
59 """forget about any clients who haven't sent levels in a while 66 """forget about any clients who haven't sent levels in a while
60 (5 seconds)""" 67 (5 seconds). this runs in a loop"""
68
69 purge_age=10 # seconds
70
71 reactor.callLater(1,self.purgeclients)
72
61 now=time.time() 73 now=time.time()
62 for cid,lastseen in self.lastseen.items(): 74 for cid,lastseen in self.lastseen.items():
63 if lastseen<now-5: 75 if lastseen<now-purge_age:
64 print "forgetting client %s (no activity for 5sec)" % cid 76 print ("forgetting client %s (no activity for %s sec)" %
77 (cid,purge_age))
65 del self.clientlevels[cid] 78 del self.clientlevels[cid]
66 del self.lastseen[cid] 79 del self.lastseen[cid]
67 del self.clientfreq[cid] 80 del self.clientfreq[cid]
68 81
69 def sendlevels(self): 82 def sendlevels(self):
70 reactor.callLater(1/20,self.sendlevels) 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
71 if self.clientschanged: 89 if self.clientschanged:
72 # recalc levels 90 # recalc levels
73 91
74 self.purgeclients()
75 self.calclevels() 92 self.calclevels()
76 93
77 if (self.num_unshown_updates is None or # first time 94 if (self.num_unshown_updates is None or # first time
78 self.options.fast_updates or # show always 95 self.options.fast_updates or # show always
79 (self.combinedlevels!=self.lastshownlevels and # changed 96 (self.combinedlevels!=self.lastshownlevels and # changed
82 self.printlevels() 99 self.printlevels()
83 self.lastshownlevels=self.combinedlevels[:] 100 self.lastshownlevels=self.combinedlevels[:]
84 else: 101 else:
85 self.num_unshown_updates+=1 102 self.num_unshown_updates+=1
86 103
87 if (self.num_unshown_updates-1)%50==0: 104 if time.time()>self.laststatsprint+2:
105 self.laststatsprint=time.time()
88 self.printstats() 106 self.printstats()
89
90 self.sendlevels_dmx()
91 107
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
92 def calclevels(self): 114 def calclevels(self):
93 """combine all the known client levels into self.combinedlevels""" 115 """combine all the known client levels into self.combinedlevels"""
94 self.combinedlevels=[] 116 self.combinedlevels=[]
95 for chan in range(0,self.parportdmx.dimmers): 117 for chan in range(0,self.parportdmx.dimmers):
96 x=0 118 x=0
99 # clamp client levels to 0..1 121 # clamp client levels to 0..1
100 cl=max(0,min(1,clientlist[chan])) 122 cl=max(0,min(1,clientlist[chan]))
101 x=max(x,cl) 123 x=max(x,cl)
102 self.combinedlevels.append(x) 124 self.combinedlevels.append(x)
103 125
104
105 def printlevels(self): 126 def printlevels(self):
106 """write all the levels to stdout""" 127 """write all the levels to stdout"""
107 print "Levels:","".join(["% 2d "%(x*100) for 128 print "Levels:","".join(["% 2d "%(x*100) for
108 x in self.combinedlevels]) 129 x in self.combinedlevels])
109 130
110 def printstats(self): 131 def printstats(self):
111 """print the clock, freq, etc, with a \r at the end""" 132 """print the clock, freq, etc, with a \r at the end"""
112 133
113 sys.stdout.write("dmxserver up at %s, [server %s] "% 134 sys.stdout.write("dmxserver up at %s, [polls %s] "%
114 (time.strftime("%H:%M:%S"), 135 (time.strftime("%H:%M:%S"),
115 str(self.updatefreq), 136 str(self.updatefreq),
116 )) 137 ))
117 for cid,freq in self.clientfreq.items(): 138 for cid,freq in self.clientfreq.items():
118 sys.stdout.write("[%s %s] " % (cid,str(freq))) 139 sys.stdout.write("[%s %s] " % (cid,str(freq)))
119 sys.stdout.write("\r") 140 sys.stdout.write("\r")
120 sys.stdout.flush() 141 sys.stdout.flush()
121 142
122
123 def sendlevels_dmx(self): 143 def sendlevels_dmx(self):
124 """output self.combinedlevels to dmx, and keep the updates/sec stats""" 144 """output self.combinedlevels to dmx, and keep the updates/sec stats"""
125 # they'll get divided by 100 145 # they'll get divided by 100
126 if self.parportdmx: 146 if self.parportdmx:
127 self.parportdmx.sendlevels([l*100 for l in self.combinedlevels]) 147 self.parportdmx.sendlevels([l*100 for l in self.combinedlevels])
131 return x 151 return x
132 152
133 def xmlrpc_outputlevels(self,cid,levellist): 153 def xmlrpc_outputlevels(self,cid,levellist):
134 """send a unique id for your client (name+pid maybe), then 154 """send a unique id for your client (name+pid maybe), then
135 the variable-length dmx levellist (scaled 0..1)""" 155 the variable-length dmx levellist (scaled 0..1)"""
136 self.clientlevels[cid]=levellist 156 if levellist!=self.clientlevels.get(cid,[]):
137 self.clientschanged=1 157 self.clientlevels[cid]=levellist
138 if cid not in self.lastseen: 158 self.clientschanged=1
139 print "hello new client %s" % cid 159 if cid not in self.lastseen:
140 self.clientfreq[cid]=Updatefreq() 160 print "hello new client %s" % cid
161 self.clientfreq[cid]=Updatefreq()
162
141 self.lastseen[cid]=time.time() 163 self.lastseen[cid]=time.time()
142 self.clientfreq[cid].update() 164 self.clientfreq[cid].update()
143 return "ok" 165 return "ok"
144 166
145 parser=OptionParser() 167 parser=OptionParser()
146 parser.add_option("-f","--fast-updates",action='store_true', 168 parser.add_option("-f","--fast-updates",action='store_true',
147 help=('display all dmx output to stdout instead ' 169 help=('display all dmx output to stdout instead '
148 'of the usual reduced output')) 170 'of the usual reduced output'))
171 parser.add_option("-r","--updates-per-sec",type='float',default=20,
172 help=('dmx output frequency'))
149 (options,songfiles)=parser.parse_args() 173 (options,songfiles)=parser.parse_args()
174
175 print options
150 176
151 print "starting xmlrpc server on port 8030" 177 print "starting xmlrpc server on port 8030"
152 reactor.listenTCP(8030,server.Site(XMLRPCServe(options))) 178 reactor.listenTCP(8030,server.Site(XMLRPCServe(options)))
153 reactor.run() 179 reactor.run()
154 180