Mercurial > ~darius > hgwebdir.cgi > beermon
diff Control.py @ 6:45d9895a5020
Split into seperate files.
author | darius |
---|---|
date | Sat, 29 Sep 2007 14:39:59 +0000 |
parents | |
children | 24c7e85c3efd |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Control.py Sat Sep 29 14:39:59 2007 +0000 @@ -0,0 +1,121 @@ +#!/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) + +