From 846f0c651662f0b62b322bf8fc5feadc3473783b Mon Sep 17 00:00:00 2001 From: Luke Rider Date: Sun, 3 Jun 2018 13:12:35 -0700 Subject: [PATCH 1/8] Initial commit of keezer manager project outline. Nothing has been run yet. --- students/luke/project/README.md | 46 +++++++++++++++++++ students/luke/project/keezer_manager.py | 37 +++++++++++++++ students/luke/project/keezer_output.py | 25 ++++++++++ students/luke/project/keezer_output_stdout.py | 28 +++++++++++ students/luke/project/keezer_sensor.py | 20 ++++++++ .../luke/project/keezer_sensor_generator.py | 39 ++++++++++++++++ 6 files changed, 195 insertions(+) create mode 100644 students/luke/project/README.md create mode 100755 students/luke/project/keezer_manager.py create mode 100755 students/luke/project/keezer_output.py create mode 100755 students/luke/project/keezer_output_stdout.py create mode 100755 students/luke/project/keezer_sensor.py create mode 100755 students/luke/project/keezer_sensor_generator.py diff --git a/students/luke/project/README.md b/students/luke/project/README.md new file mode 100644 index 00000000..761018df --- /dev/null +++ b/students/luke/project/README.md @@ -0,0 +1,46 @@ +Python 220 Project: Keezer Management + +Purpose: + +Keep track of my beer fridge. + +Measurement + - temperature + - weight of components + - CO2/beer gas tanks + - kegs + +Display + - LCD screen + +Alert + - send email when thresholds are reached + - tanks or kegs low + - temperature too high or low + +Retention + - store values for analysis + - graphing + - consumption projection + + +Design: + +The application will run on a small device such as a raspberry pi. Sensors connected via USB and/or GPIO inside the fridge will feed data to the application. External displays connected via USB or HDMI will provide output. Data will be logged over the network to a data sink, likely SQL but possibly an snmp trap sink. + +Classes: + - Driver + - Main application logic + - Scheduling + + - Sensor + - Define interface for concrete sensor subclasses + + - Display + - Accept sensor data + - Concrete subclasses will display or export data + + +Deliverables: + +The initial implementation of the keezer project will use flat text files for input and output using simple concrete subclasses. Subsequent iterations will add additional subclasses for actual devices, protocols, etc. diff --git a/students/luke/project/keezer_manager.py b/students/luke/project/keezer_manager.py new file mode 100755 index 00000000..3192c80a --- /dev/null +++ b/students/luke/project/keezer_manager.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 + +""" +Keezer management project. See README.md +""" + +import keezer_sensor as sns +import keezer_output as outp + +class KeezerManager: + """ + Driver class for keezer manager. Sensors register with driver, which + polls them periodically. Output devices register with driver, which + passes sensor polling data to them. + """ + + def __init__(self): + self.sensors = [] + self.outputs = [] + + + def register_sensor(self, sns): + self.sensors.append(sns) + + + def register_output(self, outp): + self.outputs.append(outp) + + +if __name__ == "__main__": + kman = KeezerManager + sns = sns.KSensor + outp = outp.KOutput + + # Assert that sensor and output have registered with keezer manager + assert(len(kman.sensors) == 1) + assert(len(kman.outputs) == 1) diff --git a/students/luke/project/keezer_output.py b/students/luke/project/keezer_output.py new file mode 100755 index 00000000..d48fc776 --- /dev/null +++ b/students/luke/project/keezer_output.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 + + +class KOutput: + """ Output class for keezer management project """ + + + def __init__(self): + """ Register self with driver """ + pass + + + def ingest(self, datum): + """ Take data from driver """ + pass + + + def do_output(self): + """ Show configured output """ + return True + + +if __name__ == "__main__": + outp = KOutput() + assert(outp.do_output()) diff --git a/students/luke/project/keezer_output_stdout.py b/students/luke/project/keezer_output_stdout.py new file mode 100755 index 00000000..79dfbbc2 --- /dev/null +++ b/students/luke/project/keezer_output_stdout.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 + + +class KOStdout(KOutput): + """ Output class for keezer management project """ + + + def __init__(self): + """ Register self with driver """ + super.__init__(self) + self.data = [] + + + def ingest(self, datum): + self.data.append(datum) + + + def do_output(self): + print(__name__ + ":") + for datum in self.data: + print(datum) + + return True + + +if __name__ == "__main__": + outp = KOutput() + assert(outp.do_output()) diff --git a/students/luke/project/keezer_sensor.py b/students/luke/project/keezer_sensor.py new file mode 100755 index 00000000..64a9d47a --- /dev/null +++ b/students/luke/project/keezer_sensor.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 + + +class KSensor: + """ Sensor class for keezer management project """ + + + def __init__(self): + """ Register self with driver """ + pass + + + def poll_sensor(self): + """ Return sensor reading """ + return True + + +if __name__ == "__main__": + sns = KSensor() + assert(sns.poll_sensor()) diff --git a/students/luke/project/keezer_sensor_generator.py b/students/luke/project/keezer_sensor_generator.py new file mode 100755 index 00000000..a514434f --- /dev/null +++ b/students/luke/project/keezer_sensor_generator.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 + +import keezer_sensor +import random +import time + +class KSGen(KSensor): + """ Use a generator to produce datapoints """ + + + def __init__(self, bounds = (0, 100)): + assert(len(bounds) == 2) + """ Register self with driver """ + super.__init__(self) + + random.seed(time.time()) + self.data_gen = make_data_gen(bounds) + + + def poll_sensor(self): + return data_gen() + + + def make_data_gen(bounds): + def gen(bounds): + while True: + yield random.randint(*bounds) + return gen + + + +if __name__ == "__main__": + sns = KSGen() + datum = sns.poll_sensor() + assert(datum > 0 && datum < 100) + + sns = KSGen((-100, 0) + datum = sns.poll_sensor() + assert(datum > -100 && datum < 0) From efb49013ec9c19f1729e05c210fe3fb7506db339 Mon Sep 17 00:00:00 2001 From: Luke Rider Date: Wed, 6 Jun 2018 08:39:38 -0700 Subject: [PATCH 2/8] minor improvements, still not working. --- students/luke/project/keezer_manager.py | 10 ++++-- students/luke/project/keezer_output_stdout.py | 2 +- .../luke/project/keezer_sensor_generator.py | 34 ++++++++++--------- 3 files changed, 26 insertions(+), 20 deletions(-) diff --git a/students/luke/project/keezer_manager.py b/students/luke/project/keezer_manager.py index 3192c80a..04e6e9b5 100755 --- a/students/luke/project/keezer_manager.py +++ b/students/luke/project/keezer_manager.py @@ -7,6 +7,10 @@ import keezer_sensor as sns import keezer_output as outp +# Test classes +import keezer_sensor_generator as ksg +import keezer_output_stdout as kos + class KeezerManager: """ Driver class for keezer manager. Sensors register with driver, which @@ -28,9 +32,9 @@ def register_output(self, outp): if __name__ == "__main__": - kman = KeezerManager - sns = sns.KSensor - outp = outp.KOutput + kman = KeezerManager() + sns = ksg.KSGenerator + outp = kos.KOStdout # Assert that sensor and output have registered with keezer manager assert(len(kman.sensors) == 1) diff --git a/students/luke/project/keezer_output_stdout.py b/students/luke/project/keezer_output_stdout.py index 79dfbbc2..493397e2 100755 --- a/students/luke/project/keezer_output_stdout.py +++ b/students/luke/project/keezer_output_stdout.py @@ -7,7 +7,7 @@ class KOStdout(KOutput): def __init__(self): """ Register self with driver """ - super.__init__(self) + super().__init__(self) self.data = [] diff --git a/students/luke/project/keezer_sensor_generator.py b/students/luke/project/keezer_sensor_generator.py index a514434f..541d7ac5 100755 --- a/students/luke/project/keezer_sensor_generator.py +++ b/students/luke/project/keezer_sensor_generator.py @@ -1,39 +1,41 @@ #!/usr/bin/env python3 +""" Use a generator to produce datapoints """ -import keezer_sensor import random import time +from keezer_sensor import KSensor -class KSGen(KSensor): +class KSGenerator(KSensor): """ Use a generator to produce datapoints """ - def __init__(self, bounds = (0, 100)): - assert(len(bounds) == 2) + def __init__(self, bounds=(0, 100)): """ Register self with driver """ - super.__init__(self) + assert len(bounds) == 2 + super().__init__(self) random.seed(time.time()) self.data_gen = make_data_gen(bounds) def poll_sensor(self): - return data_gen() + # pylint: disable=missing-docstring + return self.data_gen() - def make_data_gen(bounds): - def gen(bounds): - while True: - yield random.randint(*bounds) - return gen - +def make_data_gen(bounds): + # pylint: disable=missing-docstring + def gen(): + while True: + yield random.randint(*bounds) + return gen if __name__ == "__main__": - sns = KSGen() + sns = KSGenerator() datum = sns.poll_sensor() - assert(datum > 0 && datum < 100) + assert datum > 0 and datum < 100 - sns = KSGen((-100, 0) + sns = KSGenerator((-100, 0)) datum = sns.poll_sensor() - assert(datum > -100 && datum < 0) + assert datum > -100 and datum < 0 From 36f85d6401e9beb6357a6de44b2d2602e8fa8d44 Mon Sep 17 00:00:00 2001 From: lukeatuwpce Date: Sun, 10 Jun 2018 15:23:52 -0700 Subject: [PATCH 3/8] Reorgnize files into src, src/sensors, src/displays Prep work for dynamically loading sensor and display classes Rename files and classes with camel case Add use cases to README --- students/luke/project/README.md | 20 +++++ students/luke/project/keezer_manager.py | 41 ---------- .../KeezerDisplay.py} | 4 +- students/luke/project/src/KeezerManager.py | 75 +++++++++++++++++++ .../{keezer_sensor.py => src/KeezerSensor.py} | 4 +- .../displays/keezer_display_stdout.py} | 4 +- .../sensors}/keezer_sensor_generator.py | 15 ++-- 7 files changed, 107 insertions(+), 56 deletions(-) delete mode 100755 students/luke/project/keezer_manager.py rename students/luke/project/{keezer_output.py => src/KeezerDisplay.py} (88%) create mode 100755 students/luke/project/src/KeezerManager.py rename students/luke/project/{keezer_sensor.py => src/KeezerSensor.py} (87%) rename students/luke/project/{keezer_output_stdout.py => src/displays/keezer_display_stdout.py} (86%) rename students/luke/project/{ => src/sensors}/keezer_sensor_generator.py (68%) diff --git a/students/luke/project/README.md b/students/luke/project/README.md index 761018df..6de347ea 100644 --- a/students/luke/project/README.md +++ b/students/luke/project/README.md @@ -12,6 +12,8 @@ Measurement Display - LCD screen + - Display pounds of CO2 remaining + - Display gallons of beer remaining per keg Alert - send email when thresholds are reached @@ -24,6 +26,24 @@ Retention - consumption projection + + +Use case 1: +Exhaust CO2 cannister, swap with a fresh one, verify refill on LCD. +1. LCD reads 0 pounds CO2 remaining. +2. Open keezer, disconnect CO2 lines, remove tank and regulator +3. LCD reads negative value (tare weight of tank and regulator) +4. Move regulator to new tank, reconnect lines, place in keezer +5. LCD reads approximately 5 pounds CO2 remaining + +Use case 2: +Serve a dozen beers. +1. LCD reads 2 gallons remaining +2. Dispense beers +3. LCD reads 0.5 gallons remaining +4. Email alert received warning of imminent keg pop + + Design: The application will run on a small device such as a raspberry pi. Sensors connected via USB and/or GPIO inside the fridge will feed data to the application. External displays connected via USB or HDMI will provide output. Data will be logged over the network to a data sink, likely SQL but possibly an snmp trap sink. diff --git a/students/luke/project/keezer_manager.py b/students/luke/project/keezer_manager.py deleted file mode 100755 index 04e6e9b5..00000000 --- a/students/luke/project/keezer_manager.py +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env python3 - -""" -Keezer management project. See README.md -""" - -import keezer_sensor as sns -import keezer_output as outp - -# Test classes -import keezer_sensor_generator as ksg -import keezer_output_stdout as kos - -class KeezerManager: - """ - Driver class for keezer manager. Sensors register with driver, which - polls them periodically. Output devices register with driver, which - passes sensor polling data to them. - """ - - def __init__(self): - self.sensors = [] - self.outputs = [] - - - def register_sensor(self, sns): - self.sensors.append(sns) - - - def register_output(self, outp): - self.outputs.append(outp) - - -if __name__ == "__main__": - kman = KeezerManager() - sns = ksg.KSGenerator - outp = kos.KOStdout - - # Assert that sensor and output have registered with keezer manager - assert(len(kman.sensors) == 1) - assert(len(kman.outputs) == 1) diff --git a/students/luke/project/keezer_output.py b/students/luke/project/src/KeezerDisplay.py similarity index 88% rename from students/luke/project/keezer_output.py rename to students/luke/project/src/KeezerDisplay.py index d48fc776..ba6a787a 100755 --- a/students/luke/project/keezer_output.py +++ b/students/luke/project/src/KeezerDisplay.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 -class KOutput: +class KeezerDisplay: """ Output class for keezer management project """ @@ -21,5 +21,5 @@ def do_output(self): if __name__ == "__main__": - outp = KOutput() + outp = KeezerDisplay() assert(outp.do_output()) diff --git a/students/luke/project/src/KeezerManager.py b/students/luke/project/src/KeezerManager.py new file mode 100755 index 00000000..103a1ff5 --- /dev/null +++ b/students/luke/project/src/KeezerManager.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python3 + +""" +Keezer management project. See README.md +""" + +import os +import imp +import glob + +import KeezerSensor +import KeezerDisplay + +# Concrete sensor and display classes are loaded dynamically. There are +# a few approaches to this. In addition to the goergevreilly one copied +# below, there's another example at: +# https://www.blog.pythonlibrary.org/2012/07/31/advanced-python-how-to-dynamically-load-modules-or-classes/ + +# https://www.georgevreilly.com/blog/2016/03/02/PythonImportSubclassFromDynamicPath.html +def import_class(implementation_filename, base_class): + + + impl_dir, impl_filename = os.path.split(implementation_filename) + module_name, _ = os.path.splitext(impl_filename) + + try: + sys.path.insert(0, impl_dir) + fp, filename, description = imp.find_module(module_name) + module = imp.load_module(module_name, fp, filename, description) + for name in dir(module): + obj = getattr(module, name) + if (type(obj) == type(base_class) + and issubclass(obj, base_class) + and obj != base_class): + return obj + raise ValueError("No subclass of {0} in {1}".format( + base_class.__name__, implementation_filename)) + finally: + sys.path.pop(0) + + +class KeezerManager: + """ + Driver class for keezer manager. + + Loads sensor and display modules dynamically. + Polls sensors. + Passes sensor data to displays. + """ + + def __init__(self): + self.sensors = [] + self.outputs = [] + + """ Populate sensors from sensors/ directory""" + for file in glob.glob("sensors/*.py"): + self.sensors.append(import_class(file, KeezerSensor)) + + """ Populate displays from displays/ directory""" + for file in glob.glob("displays/*.py"): + self.sensors.append(import_class(file, KeezerDisplay)) + + + def run(self): + while True: + # sample time + # poll sensors + # send sensor data to display + + # sleep for remainder of polling interval + + +if __name__ == "__main__": + # List registered sensors and displays + diff --git a/students/luke/project/keezer_sensor.py b/students/luke/project/src/KeezerSensor.py similarity index 87% rename from students/luke/project/keezer_sensor.py rename to students/luke/project/src/KeezerSensor.py index 64a9d47a..31d5e87e 100755 --- a/students/luke/project/keezer_sensor.py +++ b/students/luke/project/src/KeezerSensor.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 -class KSensor: +class KeezerSensor: """ Sensor class for keezer management project """ @@ -16,5 +16,5 @@ def poll_sensor(self): if __name__ == "__main__": - sns = KSensor() + sns = KeezerSensor() assert(sns.poll_sensor()) diff --git a/students/luke/project/keezer_output_stdout.py b/students/luke/project/src/displays/keezer_display_stdout.py similarity index 86% rename from students/luke/project/keezer_output_stdout.py rename to students/luke/project/src/displays/keezer_display_stdout.py index 493397e2..6f162c7c 100755 --- a/students/luke/project/keezer_output_stdout.py +++ b/students/luke/project/src/displays/keezer_display_stdout.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 -class KOStdout(KOutput): +class keezer_display_stdout(keezer_display): """ Output class for keezer management project """ @@ -24,5 +24,5 @@ def do_output(self): if __name__ == "__main__": - outp = KOutput() + outp = keezer_display() assert(outp.do_output()) diff --git a/students/luke/project/keezer_sensor_generator.py b/students/luke/project/src/sensors/keezer_sensor_generator.py similarity index 68% rename from students/luke/project/keezer_sensor_generator.py rename to students/luke/project/src/sensors/keezer_sensor_generator.py index 541d7ac5..e7ac2a11 100755 --- a/students/luke/project/keezer_sensor_generator.py +++ b/students/luke/project/src/sensors/keezer_sensor_generator.py @@ -1,12 +1,11 @@ #!/usr/bin/env python3 -""" Use a generator to produce datapoints """ import random import time -from keezer_sensor import KSensor +from keezer_sensor import keezer_sensor -class KSGenerator(KSensor): - """ Use a generator to produce datapoints """ +class keezer_sensor_random(keezer_sensor): + """ Generate random data """ def __init__(self, bounds=(0, 100)): @@ -26,16 +25,14 @@ def poll_sensor(self): def make_data_gen(bounds): # pylint: disable=missing-docstring def gen(): - while True: - yield random.randint(*bounds) - return gen + return random.randint(*bounds) if __name__ == "__main__": - sns = KSGenerator() + sns = keezer_sensor() datum = sns.poll_sensor() assert datum > 0 and datum < 100 - sns = KSGenerator((-100, 0)) + sns = keezer_sensor_random((-100, 0)) datum = sns.poll_sensor() assert datum > -100 and datum < 0 From eaba1fea56eb5546f332778b3e4d371859156b56 Mon Sep 17 00:00:00 2001 From: lukeatuwpce Date: Sun, 10 Jun 2018 15:25:58 -0700 Subject: [PATCH 4/8] Update README --- students/luke/project/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/students/luke/project/README.md b/students/luke/project/README.md index 6de347ea..5d51942c 100644 --- a/students/luke/project/README.md +++ b/students/luke/project/README.md @@ -63,4 +63,4 @@ Classes: Deliverables: -The initial implementation of the keezer project will use flat text files for input and output using simple concrete subclasses. Subsequent iterations will add additional subclasses for actual devices, protocols, etc. +The initial implementation of the keezer project will use random data andflat text files for input and output using simple concrete subclasses. Subsequent iterations will add additional subclasses for actual devices, protocols, etc. From e8dbb4f9d7cdc85f9ebb639091380643ee0c7915 Mon Sep 17 00:00:00 2001 From: lukeatuwpce Date: Sun, 10 Jun 2018 16:30:13 -0700 Subject: [PATCH 5/8] Finish rename, fix imports and superclass inheritance --- students/luke/project/src/KeezerDisplay.py | 2 +- students/luke/project/src/KeezerManager.py | 52 +++++++++++++++---- students/luke/project/src/KeezerSensor.py | 2 +- .../src/displays/keezer_display_stdout.py | 28 ---------- .../src/sensors/keezer_sensor_generator.py | 38 -------------- 5 files changed, 45 insertions(+), 77 deletions(-) delete mode 100755 students/luke/project/src/displays/keezer_display_stdout.py delete mode 100755 students/luke/project/src/sensors/keezer_sensor_generator.py diff --git a/students/luke/project/src/KeezerDisplay.py b/students/luke/project/src/KeezerDisplay.py index ba6a787a..571a7f5d 100755 --- a/students/luke/project/src/KeezerDisplay.py +++ b/students/luke/project/src/KeezerDisplay.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 -class KeezerDisplay: +class KeezerDisplay(object): """ Output class for keezer management project """ diff --git a/students/luke/project/src/KeezerManager.py b/students/luke/project/src/KeezerManager.py index 103a1ff5..53bd127b 100755 --- a/students/luke/project/src/KeezerManager.py +++ b/students/luke/project/src/KeezerManager.py @@ -4,12 +4,27 @@ Keezer management project. See README.md """ +import sys import os import imp import glob +import datetime +import logging +import logging.handlers -import KeezerSensor -import KeezerDisplay +from KeezerSensor import KeezerSensor +from KeezerDisplay import KeezerDisplay + +dflformat = "%(asctime)s %(filename)s:%(lineno)-3d %(levelname)s %(message)s" +formatter = logging.Formatter(dflformat) + +console_handler = logging.StreamHandler() +console_handler.setLevel(logging.DEBUG) +console_handler.setFormatter(formatter) + +logger = logging.getLogger() +logger.setLevel(logging.DEBUG) +logger.addHandler(console_handler) # Concrete sensor and display classes are loaded dynamically. There are # a few approaches to this. In addition to the goergevreilly one copied @@ -18,6 +33,7 @@ # https://www.georgevreilly.com/blog/2016/03/02/PythonImportSubclassFromDynamicPath.html def import_class(implementation_filename, base_class): + """ Dynamically import class from filesystem using imp. """ impl_dir, impl_filename = os.path.split(implementation_filename) @@ -27,14 +43,26 @@ def import_class(implementation_filename, base_class): sys.path.insert(0, impl_dir) fp, filename, description = imp.find_module(module_name) module = imp.load_module(module_name, fp, filename, description) + logging.debug(f"trying to import fp {fp} " + f" filename {filename} " + f" description {description} ") for name in dir(module): + logging.debug(f"name {name}") obj = getattr(module, name) - if (type(obj) == type(base_class) - and issubclass(obj, base_class) - and obj != base_class): - return obj + logging.debug(f"obj {obj}") + try: + if (type(obj) == type(base_class) + and issubclass(obj, base_class) + and obj != base_class): + return obj + + except TypeError as excpt: + """ issubclass will throw TypeError for some imports """ + logging.debug(f"caught {excpt}") + raise ValueError("No subclass of {0} in {1}".format( base_class.__name__, implementation_filename)) + finally: sys.path.pop(0) @@ -43,26 +71,31 @@ class KeezerManager: """ Driver class for keezer manager. - Loads sensor and display modules dynamically. - Polls sensors. - Passes sensor data to displays. + - Load sensor and display modules dynamically. + - Poll sensors. + - Pass sensor data to displays. """ def __init__(self): + logging.debug("called") self.sensors = [] self.outputs = [] """ Populate sensors from sensors/ directory""" for file in glob.glob("sensors/*.py"): + logging.debug("appending sensor: " + file) self.sensors.append(import_class(file, KeezerSensor)) """ Populate displays from displays/ directory""" for file in glob.glob("displays/*.py"): + logging.debug("appending display: " + file) self.sensors.append(import_class(file, KeezerDisplay)) def run(self): + logging.debug("called") while True: + pass # sample time # poll sensors # send sensor data to display @@ -72,4 +105,5 @@ def run(self): if __name__ == "__main__": # List registered sensors and displays + km = KeezerManager() diff --git a/students/luke/project/src/KeezerSensor.py b/students/luke/project/src/KeezerSensor.py index 31d5e87e..b64c3192 100755 --- a/students/luke/project/src/KeezerSensor.py +++ b/students/luke/project/src/KeezerSensor.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 -class KeezerSensor: +class KeezerSensor(object): """ Sensor class for keezer management project """ diff --git a/students/luke/project/src/displays/keezer_display_stdout.py b/students/luke/project/src/displays/keezer_display_stdout.py deleted file mode 100755 index 6f162c7c..00000000 --- a/students/luke/project/src/displays/keezer_display_stdout.py +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env python3 - - -class keezer_display_stdout(keezer_display): - """ Output class for keezer management project """ - - - def __init__(self): - """ Register self with driver """ - super().__init__(self) - self.data = [] - - - def ingest(self, datum): - self.data.append(datum) - - - def do_output(self): - print(__name__ + ":") - for datum in self.data: - print(datum) - - return True - - -if __name__ == "__main__": - outp = keezer_display() - assert(outp.do_output()) diff --git a/students/luke/project/src/sensors/keezer_sensor_generator.py b/students/luke/project/src/sensors/keezer_sensor_generator.py deleted file mode 100755 index e7ac2a11..00000000 --- a/students/luke/project/src/sensors/keezer_sensor_generator.py +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env python3 - -import random -import time -from keezer_sensor import keezer_sensor - -class keezer_sensor_random(keezer_sensor): - """ Generate random data """ - - - def __init__(self, bounds=(0, 100)): - """ Register self with driver """ - assert len(bounds) == 2 - super().__init__(self) - - random.seed(time.time()) - self.data_gen = make_data_gen(bounds) - - - def poll_sensor(self): - # pylint: disable=missing-docstring - return self.data_gen() - - -def make_data_gen(bounds): - # pylint: disable=missing-docstring - def gen(): - return random.randint(*bounds) - - -if __name__ == "__main__": - sns = keezer_sensor() - datum = sns.poll_sensor() - assert datum > 0 and datum < 100 - - sns = keezer_sensor_random((-100, 0)) - datum = sns.poll_sensor() - assert datum > -100 and datum < 0 From 5062364ec033d0e26d78a7e674bbbdbe47f57243 Mon Sep 17 00:00:00 2001 From: lukeatuwpce Date: Sun, 10 Jun 2018 16:35:08 -0700 Subject: [PATCH 6/8] Put classes in correct list. --- students/luke/project/src/KeezerManager.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/students/luke/project/src/KeezerManager.py b/students/luke/project/src/KeezerManager.py index 53bd127b..9eec7d5a 100755 --- a/students/luke/project/src/KeezerManager.py +++ b/students/luke/project/src/KeezerManager.py @@ -79,7 +79,7 @@ class KeezerManager: def __init__(self): logging.debug("called") self.sensors = [] - self.outputs = [] + self.displays = [] """ Populate sensors from sensors/ directory""" for file in glob.glob("sensors/*.py"): @@ -89,7 +89,7 @@ def __init__(self): """ Populate displays from displays/ directory""" for file in glob.glob("displays/*.py"): logging.debug("appending display: " + file) - self.sensors.append(import_class(file, KeezerDisplay)) + self.displays.append(import_class(file, KeezerDisplay)) def run(self): @@ -106,4 +106,8 @@ def run(self): if __name__ == "__main__": # List registered sensors and displays km = KeezerManager() + for sensor in km.sensors: + print(f"sensor: {sensor}") + for display in km.displays: + print(f"display: {display}") From 98974a1f9344a54ff23dc7e1f37164d12e241e49 Mon Sep 17 00:00:00 2001 From: lukeatuwpce Date: Sun, 10 Jun 2018 16:41:06 -0700 Subject: [PATCH 7/8] fixup concerete classes --- students/luke/project/src/KeezerDisplay.py | 4 ++-- students/luke/project/src/KeezerSensor.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/students/luke/project/src/KeezerDisplay.py b/students/luke/project/src/KeezerDisplay.py index 571a7f5d..3b0f8c3a 100755 --- a/students/luke/project/src/KeezerDisplay.py +++ b/students/luke/project/src/KeezerDisplay.py @@ -15,11 +15,11 @@ def ingest(self, datum): pass - def do_output(self): + def display(self): """ Show configured output """ return True if __name__ == "__main__": outp = KeezerDisplay() - assert(outp.do_output()) + assert(outp.display()) diff --git a/students/luke/project/src/KeezerSensor.py b/students/luke/project/src/KeezerSensor.py index b64c3192..4bdc62f8 100755 --- a/students/luke/project/src/KeezerSensor.py +++ b/students/luke/project/src/KeezerSensor.py @@ -10,7 +10,7 @@ def __init__(self): pass - def poll_sensor(self): + def poll(self): """ Return sensor reading """ return True From 7d9dfab5ceaa2cdc291667aafa0212a306d20a1e Mon Sep 17 00:00:00 2001 From: lukeatuwpce Date: Sun, 10 Jun 2018 17:12:35 -0700 Subject: [PATCH 8/8] Fix subclass functions. Fix some pylint errors. Add polling interval. Add logic to poll sensors and output data. --- students/luke/project/src/KeezerDisplay.py | 2 +- students/luke/project/src/KeezerManager.py | 59 ++++++++++++++-------- students/luke/project/src/KeezerSensor.py | 2 +- 3 files changed, 40 insertions(+), 23 deletions(-) diff --git a/students/luke/project/src/KeezerDisplay.py b/students/luke/project/src/KeezerDisplay.py index 3b0f8c3a..fabfe642 100755 --- a/students/luke/project/src/KeezerDisplay.py +++ b/students/luke/project/src/KeezerDisplay.py @@ -15,7 +15,7 @@ def ingest(self, datum): pass - def display(self): + def display(self, data): """ Show configured output """ return True diff --git a/students/luke/project/src/KeezerManager.py b/students/luke/project/src/KeezerManager.py index 9eec7d5a..ce57eceb 100755 --- a/students/luke/project/src/KeezerManager.py +++ b/students/luke/project/src/KeezerManager.py @@ -8,23 +8,25 @@ import os import imp import glob -import datetime +import time import logging import logging.handlers from KeezerSensor import KeezerSensor from KeezerDisplay import KeezerDisplay -dflformat = "%(asctime)s %(filename)s:%(lineno)-3d %(levelname)s %(message)s" -formatter = logging.Formatter(dflformat) +POLL_INTERVAL=10 -console_handler = logging.StreamHandler() -console_handler.setLevel(logging.DEBUG) -console_handler.setFormatter(formatter) +DFLFMT = "%(asctime)s %(filename)s:%(lineno)-3d %(levelname)s %(message)s" +FORMATTER = logging.Formatter(DFLFMT) -logger = logging.getLogger() -logger.setLevel(logging.DEBUG) -logger.addHandler(console_handler) +CONSOLE_HANDLER = logging.StreamHandler() +CONSOLE_HANDLER.setLevel(logging.DEBUG) +CONSOLE_HANDLER.setFormatter(FORMATTER) + +LOGGER = logging.getLogger() +LOGGER.setLevel(logging.DEBUG) +LOGGER.addHandler(CONSOLE_HANDLER) # Concrete sensor and display classes are loaded dynamically. There are # a few approaches to this. In addition to the goergevreilly one copied @@ -52,16 +54,16 @@ def import_class(implementation_filename, base_class): logging.debug(f"obj {obj}") try: if (type(obj) == type(base_class) - and issubclass(obj, base_class) - and obj != base_class): - return obj + and issubclass(obj, base_class) + and obj != base_class): + return obj except TypeError as excpt: """ issubclass will throw TypeError for some imports """ logging.debug(f"caught {excpt}") raise ValueError("No subclass of {0} in {1}".format( - base_class.__name__, implementation_filename)) + base_class.__name__, implementation_filename)) finally: sys.path.pop(0) @@ -77,30 +79,44 @@ class KeezerManager: """ def __init__(self): + """ Load subclasses """ logging.debug("called") self.sensors = [] self.displays = [] """ Populate sensors from sensors/ directory""" for file in glob.glob("sensors/*.py"): - logging.debug("appending sensor: " + file) - self.sensors.append(import_class(file, KeezerSensor)) + logging.debug(f"appending sensor: {file}") + self.sensors.append(import_class(file, KeezerSensor)()) """ Populate displays from displays/ directory""" for file in glob.glob("displays/*.py"): - logging.debug("appending display: " + file) - self.displays.append(import_class(file, KeezerDisplay)) + logging.debug(f"appending display: {file}") + self.displays.append(import_class(file, KeezerDisplay)()) def run(self): + """ Main loop: poll sensors and feed to displays """ logging.debug("called") while True: - pass - # sample time - # poll sensors - # send sensor data to display + loop_start = time.clock() + logging.debug(f"loop started at {loop_start}") + + for sensor in self.sensors: + data = sensor.poll() + + for display in self.displays: + display.display(data) # sleep for remainder of polling interval + loop_end = time.clock() + logging.debug(f"loop ended at {loop_end}") + time_delta = loop_end - loop_start + sleep_for = POLL_INTERVAL - time_delta + if sleep_for < 0: + sleep_for = 0 + logging.debug(f"sleeping for {sleep_for}") + time.sleep(sleep_for) if __name__ == "__main__": @@ -111,3 +127,4 @@ def run(self): for display in km.displays: print(f"display: {display}") + km.run() diff --git a/students/luke/project/src/KeezerSensor.py b/students/luke/project/src/KeezerSensor.py index 4bdc62f8..54a9a77a 100755 --- a/students/luke/project/src/KeezerSensor.py +++ b/students/luke/project/src/KeezerSensor.py @@ -17,4 +17,4 @@ def poll(self): if __name__ == "__main__": sns = KeezerSensor() - assert(sns.poll_sensor()) + assert(sns.poll())