Mercurial > ~darius > hgwebdir.cgi > beermon
comparison beermon.py @ 4:32a56dd33e42
- Reduce hystersis down to 0.5C
- Properly track stale data (hopefully anyway :)
- Check if the monitor thread has died (ie unexpected exception)
- Re-work some variable names to be clearer (& add comments)
author | darius |
---|---|
date | Fri, 28 Sep 2007 13:05:11 +0000 |
parents | 1af7c85d5a0e |
children | 8d471840b153 |
comparison
equal
deleted
inserted
replaced
3:1af7c85d5a0e | 4:32a56dd33e42 |
---|---|
2 | 2 |
3 ############################################################################ | 3 ############################################################################ |
4 # Monitor & control fermenter temperature | 4 # Monitor & control fermenter temperature |
5 # v1.0 | 5 # v1.0 |
6 # | 6 # |
7 # $Id: beermon.py,v 1.4 2007/09/24 13:34:47 darius Exp $ | 7 # $Id: beermon.py,v 1.5 2007/09/28 13:05:11 darius Exp $ |
8 # | 8 # |
9 # Depends on: Python 2.3 (I think) | 9 # Depends on: Python 2.3 (I think) |
10 # | 10 # |
11 ############################################################################ | 11 ############################################################################ |
12 # | 12 # |
40 from logging.handlers import RotatingFileHandler | 40 from logging.handlers import RotatingFileHandler |
41 | 41 |
42 class ROMReadError(Exception): | 42 class ROMReadError(Exception): |
43 pass | 43 pass |
44 | 44 |
45 class ThreadDied(Exception): | |
46 pass | |
47 | |
45 class Control(): | 48 class Control(): |
46 targetTemp = 18 | 49 targetTemp = 18 |
47 hysteresis = 1 | 50 hysteresis = 0.5 |
48 pollInterval = 30 | 51 pollInterval = 30 |
52 staleDataTime = 30 | |
49 | 53 |
50 def __init__(self, m, _log): | 54 def __init__(self, m, _log): |
51 self.m = m | 55 self.m = m |
52 global log | 56 global log |
53 log = _log | 57 log = _log |
64 log.debug("pollInterval - %d" % (self.pollInterval)) | 68 log.debug("pollInterval - %d" % (self.pollInterval)) |
65 | 69 |
66 log.debug("=== Starting ===") | 70 log.debug("=== Starting ===") |
67 log.debug("Fermenter Fridge Ambient State New State") | 71 log.debug("Fermenter Fridge Ambient State New State") |
68 while True: | 72 while True: |
69 if (self.m.lastUpdate == 0): | 73 # Check if our monitor thread has died |
70 log.debug("Invalid data") | 74 if (not self.m.isAlive()): |
75 raise ThreadDied, "Monitor thread has died" | |
76 | |
77 # Check for stale data | |
78 if (self.m.lastUpdate[self.m.fermenterId] + self.staleDataTime < time.time()): | |
79 log.debug("Stale data") | |
71 self.cv.wait(self.pollInterval) | 80 self.cv.wait(self.pollInterval) |
72 self.m.setState('idle') | 81 self.m.setState('idle') |
73 continue | 82 continue |
74 | 83 |
84 # Work out what state we should go into | |
75 nextState = "-" | 85 nextState = "-" |
76 | |
77 diff = self.m.temps[self.m.fermenterId] - self.targetTemp | 86 diff = self.m.temps[self.m.fermenterId] - self.targetTemp |
78 if (self.m.currState == 'idle'): | 87 if (self.m.currState == 'idle'): |
79 # If we're idle then only heat or cool if the temperate difference is out of the | 88 # If we're idle then only heat or cool if the temperate difference is out of the |
80 # hysteresis range | 89 # hysteresis band |
81 if (abs(diff) > self.hysteresis): | 90 if (abs(diff) > self.hysteresis): |
82 if (diff < 0 and self.m.minHeatOffTime + self.m.lastHeatOff < time.time()): | 91 if (diff < 0 and self.m.minHeatOffTime + self.m.lastHeatOff < time.time()): |
83 nextState = 'heat' | 92 nextState = 'heat' |
84 elif (diff > 0 and self.m.minHeatOffTime + self.m.lastHeatOff < time.time()): | 93 elif (diff > 0 and self.m.minHeatOffTime + self.m.lastHeatOff < time.time()): |
85 nextState = 'cool' | 94 nextState = 'cool' |
86 elif (self.m.currState == 'cool'): | 95 elif (self.m.currState == 'cool'): |
87 # Go idle as soon as we can, there will be overshoot anyway | 96 # Go idle as soon as we can, there will be overshoot anyway |
88 if (diff < 0 and self.m.minCoolOnTime + self.m.lastCoolOn < time.time()): | 97 if (diff < 0 and self.m.minCoolOnTime + self.m.lastCoolOn < time.time()): |
89 nextState = 'idle' | 98 nextState = 'idle' |
90 elif (self.m.currState == 'heat'): | 99 elif (self.m.currState == 'heat'): |
100 # Ditto | |
91 if (diff > 0 and self.m.minHeatOnTime + self.m.lastHeatOn < time.time()): | 101 if (diff > 0 and self.m.minHeatOnTime + self.m.lastHeatOn < time.time()): |
92 nextState = 'idle' | 102 nextState = 'idle' |
93 else: | 103 else: |
104 # Not possible.. | |
94 raise KeyError | 105 raise KeyError |
95 | 106 |
96 log.debug("%3.2f %3.2f %3.2f %s %s" % | 107 log.debug("%3.2f %3.2f %3.2f %s %s" % |
97 (self.m.temps[self.m.fermenterId], | 108 (self.m.temps[self.m.fermenterId], |
98 self.m.temps[self.m.fridgeId], self.m.temps[self.m.ambientId], | 109 self.m.temps[self.m.fridgeId], self.m.temps[self.m.ambientId], |
99 self.m.currState, nextState)) | 110 self.m.currState, nextState)) |
111 | |
100 if (nextState != "-"): | 112 if (nextState != "-"): |
101 self.m.setState(nextState) | 113 self.m.setState(nextState) |
102 | 114 |
103 self.cv.wait(self.pollInterval) | 115 self.cv.wait(self.pollInterval) |
104 | 116 |
105 | 117 |
106 class MonitorDev(threading.Thread): | 118 class MonitorDev(threading.Thread): |
107 # Match a ROM ID (eg 00:11:22:33:44:55:66:77) | 119 # Match a ROM ID (eg 00:11:22:33:44:55:66:77) |
122 | 134 |
123 # minimum time the heater must spend on/off | 135 # minimum time the heater must spend on/off |
124 minHeatOnTime = 60 | 136 minHeatOnTime = 60 |
125 minHeatOffTime = 60 | 137 minHeatOffTime = 60 |
126 | 138 |
139 # Dictionary of sensor IDs & temperatures | |
127 temps = {} | 140 temps = {} |
128 lastUpdate = 0 | 141 # Dictionary of sensor IDs & epoch times |
129 | 142 lastUpdate = {} |
143 | |
144 # List of all device IDs | |
145 devs = [] | |
146 # List of temperature sensor IDs | |
147 tempdevs = [] | |
148 | |
130 # Lock to gate access to the comms | 149 # Lock to gate access to the comms |
131 commsLock = None | 150 commsLock = None |
132 | 151 |
133 currState = 'idle' | 152 currState = 'idle' |
134 | 153 |
143 self.p = pexpect.spawn('/usr/bin/ssh', ['-xt', '-enone', '-i', '/home/darius/.ssh/id_wrt', 'root@wrt', '(echo logged in; microcom -D/dev/cua/1)']) | 162 self.p = pexpect.spawn('/usr/bin/ssh', ['-xt', '-enone', '-i', '/home/darius/.ssh/id_wrt', 'root@wrt', '(echo logged in; microcom -D/dev/cua/1)']) |
144 assert(self.p.expect('logged in') == 0) | 163 assert(self.p.expect('logged in') == 0) |
145 self.p.timeout = 3 | 164 self.p.timeout = 3 |
146 self.setspeed() | 165 self.setspeed() |
147 self.devs = self.find1wire() | 166 self.devs = self.find1wire() |
148 self.temps = filter(self.istemp, self.devs) | 167 self.tempdevs = filter(self.istemp, self.devs) |
149 | 168 |
150 self.start() | 169 self.start() |
151 | 170 |
152 def setspeed(self): | 171 def setspeed(self): |
153 self.commsLock.acquire() | 172 self.commsLock.acquire() |
201 return True | 220 return True |
202 else: | 221 else: |
203 return False | 222 return False |
204 | 223 |
205 def updateTemps(self): | 224 def updateTemps(self): |
206 tmp = {} | 225 for i in self.tempdevs: |
207 for i in self.temps: | 226 try: |
208 tmp[i] = float(self.readTemp(i)) | 227 self.temps[i] = float(self.readTemp(i)) |
209 | 228 self.lastUpdate[i] = time.time() |
210 self.temps = tmp | 229 except ROMReadError: |
211 self.lastUpdate = time.time() | 230 # Ignore this - just results in no update reflected by lastUpdate |
231 pass | |
232 | |
212 return(self.temps) | 233 return(self.temps) |
213 | 234 |
214 def readTemp(self, id): | 235 def readTemp(self, id): |
215 self.commsLock.acquire() | 236 self.commsLock.acquire() |
216 cmd = 'te ' + id | 237 cmd = 'te ' + id |
307 | 328 |
308 global log | 329 global log |
309 log = initLog() | 330 log = initLog() |
310 | 331 |
311 log.debug("=== Initing ===") | 332 log.debug("=== Initing ===") |
312 log.debug("$Id: beermon.py,v 1.4 2007/09/24 13:34:47 darius Exp $") | 333 log.debug("$Id: beermon.py,v 1.5 2007/09/28 13:05:11 darius Exp $") |
313 | 334 |
314 m = None | 335 m = None |
315 exitCode = 0 | 336 exitCode = 0 |
316 try: | 337 try: |
317 m = beermon.MonitorDev() | 338 m = beermon.MonitorDev() |