Mercurial > ~darius > hgwebdir.cgi > beermon
view Control.py @ 9:f5cd94b55d5a
Ignore file
author | darius |
---|---|
date | Sat, 29 Sep 2007 14:40:54 +0000 |
parents | 45d9895a5020 |
children | 24c7e85c3efd |
line wrap: on
line source
#!/usr/bin/env python ############################################################################ # Control class for beermon # # $Id: Control.py,v 1.1 2007/09/29 14:39:59 darius Exp $ # # Depends on: Python 2.3 (I think) # ############################################################################ # # Copyright (C) 2007 Daniel O'Connor. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # ############################################################################ import threading, ConfigParser, time class OWReadError(Exception): """Raised when we failed to read from a 1-wire device, could be a timeout or the device is non-existent, etc""" pass class ThreadDied(Exception): """Raised when the monitor thread has died for some reason""" pass class Control(): """This class is responsible for controlling the temperature of the fermenter by switching the heater & cooler.""" staleDataTime = 30 def __init__(self, _log, m, conf): """m is a MonitorDev object, conf is a ConfigParser object""" global log log = _log self.m = m self.targetTemp = conf.getfloat('control', 'targetTemp') self.hysteresis = conf.getfloat('control', 'hysteresis') self.pollInterval = conf.getfloat('control', 'pollInterval') log.debug("target temperature - %3.2f" % (self.targetTemp)) log.debug("hysteresis - %3.2f" % (self.hysteresis)) log.debug("pollInterval - %d" % (self.pollInterval)) self.cv = threading.Condition() self.cv.acquire() def doit(self): """Runs forever controlling the temperature until something breaks or interrupted""" log.debug("=== Starting ===") log.debug("Fermenter Fridge Ambient State New State") while True: # Check if our monitor thread has died if (not self.m.isAlive()): raise ThreadDied, "Monitor thread has died" # Check for stale data if (self.m.lastUpdate[self.m.fermenterId] + self.staleDataTime < time.time()): log.debug("Stale data") self.cv.wait(self.pollInterval) self.m.setState('idle') continue # Work out what state we should go into nextState = "-" # Temperature diff, -ve => too cold, +ve => too warm diff = self.m.temps[self.m.fermenterId] - self.targetTemp if (self.m.currState == 'idle'): # If we're idle then only heat or cool if the temperate difference is out of the # hysteresis band if (abs(diff) > self.hysteresis): if (diff < 0 and self.m.minHeatOffTime + self.m.lastHeatOff < time.time()): nextState = 'heat' elif (diff > 0 and self.m.minHeatOffTime + self.m.lastHeatOff < time.time()): nextState = 'cool' elif (self.m.currState == 'cool'): # Work out if we should go idle (based on min on time & overshoot) if (diff + self.m.minCoolOvershoot < 0 and self.m.minCoolOnTime + self.m.lastCoolOn < time.time()): nextState = 'idle' elif (self.m.currState == 'heat'): # Ditto if (diff - self.m.minHeatOvershoot > 0 and self.m.minHeatOnTime + self.m.lastHeatOn < time.time()): nextState = 'idle' else: # Not possible.. raise KeyError log.debug("%3.2f %3.2f %3.2f %s %s" % (self.m.temps[self.m.fermenterId], self.m.temps[self.m.fridgeId], self.m.temps[self.m.ambientId], self.m.currState, nextState)) if (nextState != "-"): self.m.setState(nextState) self.cv.wait(self.pollInterval)