123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491 |
- #!/usr/bin/env python
- # -*- coding: UTF-8 -*-
-
- try:
- import pifacedigitalio
- except ImportError:
- pifacedigitalio = None
- import geo
- import logging
- import state_machine
- import task
- import random
- import time
-
- try:
- from config import APP_NAME as ROOT_LOGGER_NAME
- except ImportError:
- ROOT_LOGGER_NAME = 'root'
- logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__)
-
- geo_position = geo.gps.coordinate(lat=49.976596, lon=9.1481443)
-
-
- class pi_face(object):
- LOG_PREFIX = 'PiFace:'
-
- PF_OUT_NAMES = ['Output 0', # 0
- 'Output 1', # 1
- 'Output 2', # 2
- 'Ploenlein', # 3
- 'Bakery', # 4
- 'Mill', # 5
- 'Reese House', # 6
- 'Bake House'] # 7
-
- def __init__(self):
- if pifacedigitalio is not None:
- pifacedigitalio.init()
- pi = pifacedigitalio.PiFaceDigital()
- self.__pf_outputs__ = pi.output_pins
- self.__pf_output_states__ = 8 * [False]
- #
- self._reload = 0
- self._task_10ms = task.periodic(0.01 - 0.007, self.task_10ms)
- #
- for i in range(0, 8):
- self.set_output(i, self.__pf_output_states__[i])
-
- def set_output(self, index, state, tries=5):
- tries = min(max(1, tries), 5)
- try_txt = ['1st', '2nd', '3rd', '4th', '5th']
- state = state is True
- if pifacedigitalio is not None:
- cnt = 0
- while (cnt < tries) and (self.__pf_outputs__[index].value != state):
- self.__pf_outputs__[index].value = 1 * state
- time.sleep(0.1)
- cnt += 1
- if self.__pf_outputs__[index].value != state:
- logger.warning('%s Control of output[%d] (%s) after %s try not successfull!', self.LOG_PREFIX, index, self.PF_OUT_NAMES[index], try_txt[cnt - 1])
- else:
- logger.info('%s Set output "%s" to %s.', self.LOG_PREFIX, self.PF_OUT_NAMES[index], repr(state))
- self.__pf_output_states__[index] = self.__pf_outputs__[index].value == 1
- else:
- logger.info('%s Set virtual output "%s" to %s.', self.LOG_PREFIX, self.PF_OUT_NAMES[index], repr(state))
- self.__pf_output_states__[index] = state
-
- def get_output(self, index):
- return self.__pf_output_states__[index]
-
- def get_ploenlein(self):
- return self.get_output(3)
-
- def set_bakery(self, state):
- self.set_output(4, state)
-
- def get_bakery(self):
- return self.get_output(4)
-
- def set_mill(self, state):
- self.set_output(5, state)
-
- def get_mill(self):
- return self.get_output(5)
-
- def set_ploenlein(self, state):
- self.set_output(3, state)
-
- def set_reese_house(self, state):
- self.set_output(6, state)
-
- def get_reese_house(self):
- return self.get_output(6)
-
- def set_bake_house(self, state):
- if state is True:
- self.set_output(7, state)
- self._task_10ms.run()
- elif state is False:
- self._task_10ms.stop()
- self.join()
- time.sleep(0.1)
- self.set_output(7, state)
-
- def get_bake_house(self):
- return not self._task_10ms._stopped
-
- def task_10ms(self, task_inst):
- if self._reload <= 0:
- if pifacedigitalio is not None:
- self.__pf_outputs__[7].turn_on()
- self._reload = random.choice(2 * [0] + 20 * [1] + 1 * [2])
- else:
- if pifacedigitalio is not None:
- self.__pf_outputs__[7].turn_off()
- self._reload -= 1
-
- def join(self):
- self._task_10ms.join()
-
- def stop(self):
- self._task_10ms.stop()
- self._task_10ms.join()
- for i in range(0, 8):
- self.control_output(i, False)
- pifacedigitalio.deinit()
-
- def __del__(self):
- self.stop()
-
-
- class state_machine_mode(state_machine.state_machine):
- LOG_PREFIX = 'LeykMode:'
-
- STATE_AUTOMATIC = 'automatic'
- STATE_MANUAL = 'manual'
-
- CONDITION_EXTERNAL_TRIGGER = 'external_trigger'
-
- TRANSITIONS = {
- STATE_AUTOMATIC: (
- (CONDITION_EXTERNAL_TRIGGER, 1, STATE_MANUAL),
- ),
- STATE_MANUAL: (
- (CONDITION_EXTERNAL_TRIGGER, 1, STATE_AUTOMATIC),
- ),
- }
-
- def __init__(self, **kwargs):
- state_machine.state_machine.__init__(self, self.STATE_AUTOMATIC, logging.INFO)
- self.__reset_triggers__()
-
- def __reset_triggers__(self):
- self.__to_automatic__ = False
- self.__to_manual__ = False
-
- def external_trigger(self):
- rv = False
- if self.this_state() == self.STATE_AUTOMATIC:
- rv = self.__to_manual__
- elif self.this_state() == self.STATE_MANUAL:
- rv = self.__to_automatic__
- self.__reset_triggers__()
- return rv
-
- def trigger_to_maual(self):
- if self.this_state() == self.STATE_AUTOMATIC:
- self.__to_manual__ = True
- return True
- return False
-
- def trigger_to_automatic(self):
- if self.this_state() == self.STATE_MANUAL:
- self.__to_automatic__ = True
- return True
- return False
-
-
- class state_machine_day_state(state_machine.state_machine):
- LOG_PREFIX = 'LeykState:'
-
- STATE_IDLE = 'idle'
- STATE_WAKE = 'wake'
- STATE_SUNRISE = 'sunrise'
- STATE_SUNSET = 'sunset'
- STATE_SLEEP = 'sleep'
-
- CONDITION_WAKE = 'condition_wake'
- CONDITION_SUNRISE = 'condition_sunrise'
- CONDITION_SUNSET = 'condition_sunset'
- CONDITION_SLEEP = 'condition_sleep'
- CONDITION_IDLE = 'condition_idle'
-
- TRANSITIONS = {
- STATE_IDLE: (
- (CONDITION_WAKE, 1, STATE_WAKE),
- (CONDITION_SUNRISE, 1, STATE_SUNRISE),
- (CONDITION_SUNSET, 1, STATE_SUNSET),
- (CONDITION_SLEEP, 1, STATE_SLEEP),
- ),
- STATE_WAKE: (
- (CONDITION_SUNRISE, 1, STATE_SUNRISE),
- (CONDITION_IDLE, 1, STATE_IDLE),
- ),
- STATE_SUNRISE: (
- (CONDITION_SLEEP, 1, STATE_SLEEP),
- (CONDITION_SUNSET, 1, STATE_SUNSET),
- (CONDITION_IDLE, 1, STATE_IDLE),
- ),
- STATE_SUNSET: (
- (CONDITION_SLEEP, 1, STATE_SLEEP),
- (CONDITION_IDLE, 1, STATE_IDLE),
- ),
- STATE_SLEEP: (
- (CONDITION_WAKE, 1, STATE_WAKE),
- (CONDITION_SUNRISE, 1, STATE_SUNRISE),
- (CONDITION_IDLE, 1, STATE_IDLE),
- ),
- }
-
- def __init__(self, **kwargs):
- state_machine.state_machine.__init__(self, self.STATE_IDLE, logging.INFO, **kwargs)
-
- def __current_state_calc__(self):
- def wake_time():
- tm = time.localtime()
- tm = list(tm)
- tm[3] = 6 # tm_hour
- tm[4] = 0 # tm_min
- tm[5] = 0 # tm_sec=0
- return time.mktime(time.struct_time(tm))
-
- def sunrise_time():
- return time.mktime(geo.sun.sunrise(geo_position)) + 30 * 60
-
- def sunset_time():
- return time.mktime(geo.sun.sunset(geo_position)) - 30 * 60
-
- def sleep_time():
- tm = time.localtime()
- tm = list(tm)
- tm[3] = 21 # tm_hour
- tm[4] = 15 # tm_min
- tm[5] = 0 # tm_sec=0
- return time.mktime(time.struct_time(tm))
-
- now = time.mktime(time.localtime())
- if now > sleep_time():
- return self.STATE_SLEEP
- elif now > sunset_time():
- return self.STATE_SUNSET
- elif now > sunrise_time():
- return self.STATE_SUNRISE
- elif now > wake_time():
- return self.STATE_WAKE
- else:
- return self.STATE_IDLE
-
- def condition_wake(self):
- if self.condition_idle():
- return False
- return self.__current_state_calc__() == self.STATE_WAKE
-
- def condition_sunrise(self):
- if self.condition_idle():
- return False
- return self.__current_state_calc__() == self.STATE_SUNRISE
-
- def condition_sunset(self):
- if self.condition_idle():
- return False
- return self.__current_state_calc__() == self.STATE_SUNSET
-
- def condition_sleep(self):
- if self.condition_idle():
- return False
- return self.__current_state_calc__() == self.STATE_SLEEP
-
- def condition_idle(self):
- return not self.sm_mode.this_state_is(self.sm_mode.STATE_AUTOMATIC)
-
- def report_sunset_sunrise(self):
- state_machine.logger.debug('Sunrise: %s - Sunset: %s;', time.strftime("%H:%M", geo.sun.sunrise(geo_position)), time.strftime("%H:%M", geo.sun.sunset(geo_position))
- )
-
-
- class leyk(object):
- LOG_PREFIX = 'Leyk:'
-
- def __init__(self):
- self.sm_mode = state_machine_mode()
- self.sm_day_state = state_machine_day_state(sm_mode=self.sm_mode)
- self.__pf__ = pi_face()
- self._queue = task.threaded_queue()
- self._queue.run()
- self._task_1s = task.periodic(1, self.task_1s)
- self._task_1s.run()
- self.sm_mode.register_state_change_callback(self.sm_mode.STATE_MANUAL, None, self._queue.clean_queue)
- self.sm_day_state.register_state_change_callback(None, None, self.clean_queue)
- self.sm_day_state.register_state_change_callback(self.sm_day_state.STATE_SLEEP, None, self.sm_day_state.report_sunset_sunrise)
- self.sm_day_state.register_state_change_callback(self.sm_day_state.STATE_SLEEP, None, self.fill_sleep_queue)
- self.sm_day_state.register_state_change_callback(self.sm_day_state.STATE_SUNRISE, None, self.sm_day_state.report_sunset_sunrise)
- self.sm_day_state.register_state_change_callback(self.sm_day_state.STATE_SUNRISE, None, self.fill_sunrise_queue)
- self.sm_day_state.register_state_change_callback(self.sm_day_state.STATE_SUNSET, None, self.sm_day_state.report_sunset_sunrise)
- self.sm_day_state.register_state_change_callback(self.sm_day_state.STATE_SUNSET, None, self.fill_sunset_queue)
- self.sm_day_state.register_state_change_callback(self.sm_day_state.STATE_WAKE, None, self.sm_day_state.report_sunset_sunrise)
- self.sm_day_state.register_state_change_callback(self.sm_day_state.STATE_WAKE, None, self.fill_wake_queue)
-
- def set_mode(self, mode):
- if mode == self.sm_mode.STATE_AUTOMATIC:
- return self.sm_mode.trigger_to_automatic()
- elif mode == self.sm_mode.STATE_MANUAL:
- return self.sm_mode.trigger_to_maual()
- else:
- return False
-
-
- def get_mode(self):
- return self.sm_mode.this_state()
-
- def get_state(self):
- return self.sm_day_state.this_state()
-
- def __queue_wrapper__(self, queue_inst, function, *args, **kwargs):
- function(*args, **kwargs)
-
- def set_ploenlein(self, state):
- if self.sm_mode.this_state_is(self.sm_mode.STATE_MANUAL):
- self.__pf__.set_ploenlein(state)
- return True
- else:
- return False
-
- def get_ploenlein(self):
- return self.__pf__.get_ploenlein()
-
- def set_bakery(self, state):
- if self.sm_mode.this_state_is(self.sm_mode.STATE_MANUAL):
- self.__pf__.set_bakery(state)
- return True
- else:
- return False
-
- def get_bakery(self):
- return self.__pf__.get_bakery()
-
- def set_bake_house(self, state):
- if self.sm_mode.this_state_is(self.sm_mode.STATE_MANUAL):
- self.__pf__.set_bake_house(state)
- return True
- else:
- return False
-
- def get_bake_house(self):
- return self.__pf__.get_bake_house()
-
- def set_mill(self, state):
- if self.sm_mode.this_state_is(self.sm_mode.STATE_MANUAL):
- self.__pf__.set_mill(state)
- return True
- else:
- return False
-
- def get_mill(self):
- return self.__pf__.get_mill()
-
- def set_reese_house(self, state):
- if self.sm_mode.this_state_is(self.sm_mode.STATE_MANUAL):
- self.__pf__.set_reese_house(state)
- return True
- else:
- return False
-
- def get_reese_house(self):
- return self.__pf__.get_reese_house()
-
- def wake_time(self):
- tm = time.localtime()
- tm = list(tm)
- tm[3] = 6 # tm_hour
- tm[4] = 0 # tm_min
- tm[5] = 0 # tm_sec=0
- return time.mktime(time.struct_time(tm))
-
- def sunrise_time(self):
- return time.mktime(geo.sun.sunrise(geo_position)) + 30 * 60
-
- def sunset_time(self):
- return time.mktime(geo.sun.sunset(geo_position)) - 30 * 60
-
- def sleep_time(self):
- tm = time.localtime()
- tm = list(tm)
- tm[3] = 21 # tm_hour
- tm[4] = 15 # tm_min
- tm[5] = 0 # tm_sec=0
- return time.mktime(time.struct_time(tm))
-
- def identify_current_state(self):
- now = time.mktime(time.localtime())
- if now > self.sleep_time():
- return self.ST_SLEEP
- elif now > self.sunset_time():
- return self.ST_SUNSET
- elif now > self.sunrise_time():
- return self.ST_SUNRISE
- elif now > self.wake_time():
- return self.ST_WAKE
- else:
- return self.ST_SLEEP
-
- def wait(self, queue_inst, delay):
- if delay > 0:
- logger.debug('%s Wait for %d seconds initiated. %d elements left in queue.', self.LOG_PREFIX, delay, queue_inst.qsize())
- cnt = 0
- while cnt < delay * 5:
- if queue_inst.qsize() == 0:
- logger.debug('%s Quit wait for %d seconds. %d elements left in queue.', self.LOG_PREFIX, delay, queue_inst.qsize())
- break
- time.sleep(0.2)
- cnt += 1
-
- def clean_queue(self):
- if self._queue.qsize() > 0:
- logger.info('Cleaning up remaining %d elements from queue', self._queue.qsize())
- self._queue.clean_queue()
-
- def fill_wake_queue(self):
- no_delay = self.sm_day_state.previous_state_was(self.sm_day_state.STATE_IDLE)
- # WAKE
- self._queue.enqueue(1, self.__queue_wrapper__, self.__pf__.set_bakery, True)
- self._queue.enqueue(2, self.wait, 0 if no_delay else 5 * 60)
- self._queue.enqueue(3, self.__queue_wrapper__, self.__pf__.set_bake_house, True)
- self._queue.enqueue(4, self.wait, 0 if no_delay else 10 * 60)
- self._queue.enqueue(5, self.__queue_wrapper__, self.__pf__.set_reese_house, True)
- self._queue.enqueue(6, self.wait, 0 if no_delay else 7 * 60)
- self._queue.enqueue(7, self.__queue_wrapper__, self.__pf__.set_ploenlein, True)
- self._queue.enqueue(8, self.wait, 0 if no_delay else 8 * 60)
- self._queue.enqueue(9, self.__queue_wrapper__, self.__pf__.set_mill, True)
-
- def fill_sunrise_queue(self):
- no_delay = self.sm_day_state.previous_state_was(self.sm_day_state.STATE_IDLE)
- # SUNRISE
- self._queue.enqueue(1, self.__queue_wrapper__, self.__pf__.set_bake_house, True)
- self._queue.enqueue(2, self.__queue_wrapper__, self.__pf__.set_ploenlein, False)
- self._queue.enqueue(3, self.wait, 0 if no_delay else 5 * 60)
- self._queue.enqueue(4, self.__queue_wrapper__, self.__pf__.set_mill, False)
- self._queue.enqueue(5, self.wait, 0 if no_delay else 8 * 60)
- self._queue.enqueue(6, self.__queue_wrapper__, self.__pf__.set_bakery, False)
- self._queue.enqueue(7, self.wait, 0 if no_delay else 12 * 60)
- self._queue.enqueue(8, self.__queue_wrapper__, self.__pf__.set_reese_house, False)
-
- def fill_sunset_queue(self):
- no_delay = self.sm_day_state.previous_state_was(self.sm_day_state.STATE_IDLE)
- # SUNSET
- self._queue.enqueue(1, self.__queue_wrapper__, self.__pf__.set_bake_house, True)
- self._queue.enqueue(2, self.__queue_wrapper__, self.__pf__.set_bakery, True)
- self._queue.enqueue(3, self.wait, 0 if no_delay else 10 * 60)
- self._queue.enqueue(4, self.__queue_wrapper__, self.__pf__.set_reese_house, True)
- self._queue.enqueue(5, self.wait, 0 if no_delay else 8 * 60)
- self._queue.enqueue(6, self.__queue_wrapper__, self.__pf__.set_mill, True)
- self._queue.enqueue(7, self.wait, 0 if no_delay else 7 * 60)
- self._queue.enqueue(8, self.__queue_wrapper__, self.__pf__.set_ploenlein, True)
-
- def fill_sleep_queue(self):
- no_delay = self.sm_day_state.previous_state_was(self.sm_day_state.STATE_IDLE)
- # SLEEP
- self._queue.enqueue(1, self.__queue_wrapper__, self.__pf__.set_bake_house, False)
- self._queue.enqueue(2, self.wait, 0 if no_delay else 5 * 60)
- self._queue.enqueue(3, self.__queue_wrapper__, self.__pf__.set_bakery, False)
- self._queue.enqueue(4, self.wait, 0 if no_delay else 9 * 60)
- self._queue.enqueue(5, self.__queue_wrapper__, self.__pf__.set_ploenlein, False)
- self._queue.enqueue(6, self.wait, 0 if no_delay else 9 * 60)
- self._queue.enqueue(7, self.__queue_wrapper__, self.__pf__.set_mill, False)
- self._queue.enqueue(8, self.wait, 0 if no_delay else 6 * 60)
- self._queue.enqueue(9, self.__queue_wrapper__, self.__pf__.set_reese_house, False)
-
- def task_1s(self, task_inst):
- self.sm_mode.work()
- self.sm_day_state.work()
-
- def join(self):
- self._task_1s.join()
- self._queue.join()
-
- def stop(self):
- self._task_1s.stop()
- self._queue.stop()
-
- def __del__(self):
- self.stop()
|