Leyk lightener application for raspberry pi and piface module.
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

piface_function.py 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479
  1. #!/usr/bin/env python
  2. # -*- coding: UTF-8 -*-
  3. try:
  4. import pifacedigitalio
  5. except ImportError:
  6. pifacedigitalio = None
  7. import geo
  8. import logging
  9. import state_machine
  10. import task
  11. import random
  12. import time
  13. logger = logging.getLogger('APP')
  14. geo_position = geo.gps.coordinate(lat=49.976596, lon=9.1481443)
  15. class pi_face(object):
  16. LOG_PREFIX = 'PiFace:'
  17. PF_OUT_NAMES = ['Output 0', # 0
  18. 'Output 1', # 1
  19. 'Output 2', # 2
  20. 'Ploenlein', # 3
  21. 'Bakery', # 4
  22. 'Mill', # 5
  23. 'Reese House', # 6
  24. 'Bake House'] # 7
  25. def __init__(self):
  26. if pifacedigitalio is not None:
  27. pifacedigitalio.init()
  28. pi = pifacedigitalio.PiFaceDigital()
  29. self.__pf_outputs__ = pi.output_pins
  30. self.__pf_output_states__ = 8 * [False]
  31. #
  32. self._reload = 0
  33. self._task_10ms = task.periodic(0.01 - 0.007, self.task_10ms)
  34. #
  35. for i in range(0, 8):
  36. self.set_output(i, self.__pf_output_states__[i])
  37. def set_output(self, index, state, tries=5):
  38. tries = min(max(1, tries), 5)
  39. try_txt = ['1st', '2nd', '3rd', '4th', '5th']
  40. state = state is True
  41. if pifacedigitalio is not None:
  42. cnt = 0
  43. while (cnt < tries) and (self.__pf_outputs__[index].value != state):
  44. self.__pf_outputs__[index].value = 1 * state
  45. time.sleep(0.1)
  46. cnt += 1
  47. if self.__pf_outputs__[index].value != state:
  48. 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])
  49. else:
  50. logger.info('%s Set output "%s" to %s.', self.LOG_PREFIX, self.PF_OUT_NAMES[index], repr(state))
  51. self.__pf_output_states__[index] = self.__pf_outputs__[index].value == 1
  52. else:
  53. logger.info('%s Set virtual output "%s" to %s.', self.LOG_PREFIX, self.PF_OUT_NAMES[index], repr(state))
  54. self.__pf_output_states__[index] = state
  55. def get_output(self, index):
  56. return self.__pf_output_states__[index]
  57. def get_ploenlein(self):
  58. return self.get_output(3)
  59. def set_bakery(self, state):
  60. self.set_output(4, state)
  61. def get_bakery(self):
  62. return self.get_output(4)
  63. def set_mill(self, state):
  64. self.set_output(5, state)
  65. def get_mill(self):
  66. return self.get_output(5)
  67. def set_ploenlein(self, state):
  68. self.set_output(3, state)
  69. def set_reese_house(self, state):
  70. self.set_output(6, state)
  71. def get_reese_house(self):
  72. return self.get_output(6)
  73. def set_bake_house(self, state):
  74. if state is True:
  75. self.set_output(7, state)
  76. self._task_10ms.run()
  77. elif state is False:
  78. self._task_10ms.stop()
  79. self.join()
  80. time.sleep(0.1)
  81. self.set_output(7, state)
  82. def get_bake_house(self):
  83. return not self._task_10ms._stopped
  84. def task_10ms(self, task_inst):
  85. if self._reload <= 0:
  86. if pifacedigitalio is not None:
  87. self.__pf_outputs__[7].turn_on()
  88. self._reload = random.choice(2 * [0] + 20 * [1] + 1 * [2])
  89. else:
  90. if pifacedigitalio is not None:
  91. self.__pf_outputs__[7].turn_off()
  92. self._reload -= 1
  93. def join(self):
  94. self._task_10ms.join()
  95. def stop(self):
  96. self._task_10ms.stop()
  97. self._task_10ms.join()
  98. for i in range(0, 8):
  99. self.control_output(i, False)
  100. pifacedigitalio.deinit()
  101. def __del__(self):
  102. self.stop()
  103. class state_machine_mode(state_machine.state_machine):
  104. LOG_PREFIX = 'LeykMode:'
  105. STATE_AUTOMATIC = 'automatic'
  106. STATE_MANUAL = 'manual'
  107. CONDITION_EXTERNAL_TRIGGER = 'external_trigger'
  108. TRANSITIONS = {
  109. STATE_AUTOMATIC: (
  110. (CONDITION_EXTERNAL_TRIGGER, 1, STATE_MANUAL),
  111. ),
  112. STATE_MANUAL: (
  113. (CONDITION_EXTERNAL_TRIGGER, 1, STATE_AUTOMATIC),
  114. ),
  115. }
  116. def __init__(self, **kwargs):
  117. state_machine.state_machine.__init__(self, self.STATE_AUTOMATIC, logging.INFO)
  118. self.__reset_triggers__()
  119. def __reset_triggers__(self):
  120. self.__to_automatic__ = False
  121. self.__to_manual__ = False
  122. def external_trigger(self):
  123. rv = False
  124. if self.this_state() == self.STATE_AUTOMATIC:
  125. rv = self.__to_manual__
  126. elif self.this_state() == self.STATE_MANUAL:
  127. rv = self.__to_automatic__
  128. self.__reset_triggers__()
  129. return rv
  130. def trigger_to_maual(self):
  131. if self.this_state() == self.STATE_AUTOMATIC:
  132. self.__to_manual__ = True
  133. return True
  134. return False
  135. def trigger_to_automatic(self):
  136. if self.this_state() == self.STATE_MANUAL:
  137. self.__to_automatic__ = True
  138. return True
  139. return False
  140. class state_machine_day_state(state_machine.state_machine):
  141. LOG_PREFIX = 'LeykState:'
  142. STATE_IDLE = 'idle'
  143. STATE_WAKE = 'wake'
  144. STATE_SUNRISE = 'sunrise'
  145. STATE_SUNSET = 'sunset'
  146. STATE_SLEEP = 'sleep'
  147. CONDITION_WAKE = 'condition_wake'
  148. CONDITION_SUNRISE = 'condition_sunrise'
  149. CONDITION_SUNSET = 'condition_sunset'
  150. CONDITION_SLEEP = 'condition_sleep'
  151. CONDITION_IDLE = 'condition_idle'
  152. TRANSITIONS = {
  153. STATE_IDLE: (
  154. (CONDITION_WAKE, 1, STATE_WAKE),
  155. (CONDITION_SUNRISE, 1, STATE_SUNRISE),
  156. (CONDITION_SUNSET, 1, STATE_SUNSET),
  157. (CONDITION_SLEEP, 1, STATE_SLEEP),
  158. ),
  159. STATE_WAKE: (
  160. (CONDITION_SUNRISE, 1, STATE_SUNRISE),
  161. (CONDITION_IDLE, 1, STATE_IDLE),
  162. ),
  163. STATE_SUNRISE: (
  164. (CONDITION_SUNSET, 1, STATE_SUNSET),
  165. (CONDITION_IDLE, 1, STATE_IDLE),
  166. ),
  167. STATE_SUNSET: (
  168. (CONDITION_SLEEP, 1, STATE_SLEEP),
  169. (CONDITION_IDLE, 1, STATE_IDLE),
  170. ),
  171. STATE_SLEEP: (
  172. (CONDITION_WAKE, 1, STATE_WAKE),
  173. (CONDITION_IDLE, 1, STATE_IDLE),
  174. ),
  175. }
  176. def __init__(self, **kwargs):
  177. state_machine.state_machine.__init__(self, self.STATE_IDLE, logging.INFO, **kwargs)
  178. def __current_state_calc__(self):
  179. def wake_time():
  180. tm = time.localtime()
  181. tm = list(tm)
  182. tm[3] = 6 # tm_hour
  183. tm[4] = 0 # tm_min
  184. tm[5] = 0 # tm_sec=0
  185. return time.mktime(time.struct_time(tm))
  186. def sunrise_time():
  187. return time.mktime(geo.sun.sunrise(geo_position)) + 30 * 60
  188. def sunset_time():
  189. return time.mktime(geo.sun.sunset(geo_position)) - 30 * 60
  190. def sleep_time():
  191. tm = time.localtime()
  192. tm = list(tm)
  193. tm[3] = 21 # tm_hour
  194. tm[4] = 15 # tm_min
  195. tm[5] = 0 # tm_sec=0
  196. return time.mktime(time.struct_time(tm))
  197. now = time.mktime(time.localtime())
  198. if now > sleep_time():
  199. return self.STATE_SLEEP
  200. elif now > sunset_time():
  201. return self.STATE_SUNSET
  202. elif now > sunrise_time():
  203. return self.STATE_SUNRISE
  204. elif now > wake_time():
  205. return self.STATE_WAKE
  206. else:
  207. return self.STATE_IDLE
  208. def condition_wake(self):
  209. if self.condition_idle():
  210. return False
  211. return self.__current_state_calc__() == self.STATE_WAKE
  212. def condition_sunrise(self):
  213. if self.condition_idle():
  214. return False
  215. return self.__current_state_calc__() == self.STATE_SUNRISE
  216. def condition_sunset(self):
  217. if self.condition_idle():
  218. return False
  219. return self.__current_state_calc__() == self.STATE_SUNSET
  220. def condition_sleep(self):
  221. if self.condition_idle():
  222. return False
  223. return self.__current_state_calc__() == self.STATE_SLEEP
  224. def condition_idle(self):
  225. return not self.sm_mode.this_state_is(self.sm_mode.STATE_AUTOMATIC)
  226. def report_sunset_sunrise(self):
  227. 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))
  228. )
  229. class leyk(object):
  230. LOG_PREFIX = 'Leyk:'
  231. def __init__(self):
  232. self.sm_mode = state_machine_mode()
  233. self.sm_day_state = state_machine_day_state(sm_mode=self.sm_mode)
  234. self.__pf__ = pi_face()
  235. self._queue = task.threaded_queue()
  236. self._queue.run()
  237. self._task_1s = task.periodic(1, self.task_1s)
  238. self._task_1s.run()
  239. self.sm_mode.register_state_change_callback(self.sm_mode.STATE_MANUAL, None, self._queue.clean_queue)
  240. self.sm_day_state.register_state_change_callback(self.sm_day_state.STATE_SLEEP, None, self.sm_day_state.report_sunset_sunrise)
  241. self.sm_day_state.register_state_change_callback(self.sm_day_state.STATE_SLEEP, None, self.fill_sleep_queue)
  242. self.sm_day_state.register_state_change_callback(self.sm_day_state.STATE_SUNRISE, None, self.sm_day_state.report_sunset_sunrise)
  243. self.sm_day_state.register_state_change_callback(self.sm_day_state.STATE_SUNRISE, None, self.fill_sunrise_queue)
  244. self.sm_day_state.register_state_change_callback(self.sm_day_state.STATE_SUNSET, None, self.sm_day_state.report_sunset_sunrise)
  245. self.sm_day_state.register_state_change_callback(self.sm_day_state.STATE_SUNSET, None, self.fill_sunset_queue)
  246. self.sm_day_state.register_state_change_callback(self.sm_day_state.STATE_WAKE, None, self.sm_day_state.report_sunset_sunrise)
  247. self.sm_day_state.register_state_change_callback(self.sm_day_state.STATE_WAKE, None, self.fill_wake_queue)
  248. def set_mode(self, mode):
  249. if mode == self.sm_mode.STATE_AUTOMATIC:
  250. return self.sm_mode.trigger_to_automatic()
  251. elif mode == self.sm_mode.STATE_MANUAL:
  252. return self.sm_mode.trigger_to_maual()
  253. else:
  254. return False
  255. def get_mode(self):
  256. return self.sm_mode.this_state()
  257. def get_state(self):
  258. return self.sm_day_state.this_state()
  259. def __queue_wrapper__(self, queue_inst, function, *args, **kwargs):
  260. function(*args, **kwargs)
  261. def set_ploenlein(self, state):
  262. if self.sm_mode.this_state_is(self.sm_mode.STATE_MANUAL):
  263. self.__pf__.set_ploenlein(state)
  264. return True
  265. else:
  266. return False
  267. def get_ploenlein(self):
  268. return self.__pf__.get_ploenlein()
  269. def set_bakery(self, state):
  270. if self.sm_mode.this_state_is(self.sm_mode.STATE_MANUAL):
  271. self.__pf__.set_bakery(state)
  272. return True
  273. else:
  274. return False
  275. def get_bakery(self):
  276. return self.__pf__.get_bakery()
  277. def set_bake_house(self, state):
  278. if self.sm_mode.this_state_is(self.sm_mode.STATE_MANUAL):
  279. self.__pf__.set_bake_house(state)
  280. return True
  281. else:
  282. return False
  283. def get_bake_house(self):
  284. return self.__pf__.get_bake_house()
  285. def set_mill(self, state):
  286. if self.sm_mode.this_state_is(self.sm_mode.STATE_MANUAL):
  287. self.__pf__.set_mill(state)
  288. return True
  289. else:
  290. return False
  291. def get_mill(self):
  292. return self.__pf__.get_mill()
  293. def set_reese_house(self, state):
  294. if self.sm_mode.this_state_is(self.sm_mode.STATE_MANUAL):
  295. self.__pf__.set_reese_house(state)
  296. return True
  297. else:
  298. return False
  299. def get_reese_house(self):
  300. return self.__pf__.get_reese_house()
  301. def wake_time(self):
  302. tm = time.localtime()
  303. tm = list(tm)
  304. tm[3] = 6 # tm_hour
  305. tm[4] = 0 # tm_min
  306. tm[5] = 0 # tm_sec=0
  307. return time.mktime(time.struct_time(tm))
  308. def sunrise_time(self):
  309. return time.mktime(geo.sun.sunrise(geo_position)) + 30 * 60
  310. def sunset_time(self):
  311. return time.mktime(geo.sun.sunset(geo_position)) - 30 * 60
  312. def sleep_time(self):
  313. tm = time.localtime()
  314. tm = list(tm)
  315. tm[3] = 21 # tm_hour
  316. tm[4] = 15 # tm_min
  317. tm[5] = 0 # tm_sec=0
  318. return time.mktime(time.struct_time(tm))
  319. def identify_current_state(self):
  320. now = time.mktime(time.localtime())
  321. if now > self.sleep_time():
  322. return self.ST_SLEEP
  323. elif now > self.sunset_time():
  324. return self.ST_SUNSET
  325. elif now > self.sunrise_time():
  326. return self.ST_SUNRISE
  327. elif now > self.wake_time():
  328. return self.ST_WAKE
  329. else:
  330. return self.ST_SLEEP
  331. def wait(self, queue_inst, delay):
  332. if delay > 0:
  333. logger.debug('%s Wait for %d seconds initiated. %d elements left in queue.', self.LOG_PREFIX, delay, queue_inst.qsize())
  334. cnt = 0
  335. while cnt < delay * 5:
  336. if queue_inst.qsize() == 0:
  337. logger.debug('%s Quit wait for %d seconds. %d elements left in queue.', self.LOG_PREFIX, delay, queue_inst.qsize())
  338. break
  339. time.sleep(0.2)
  340. cnt += 1
  341. def fill_wake_queue(self):
  342. no_delay = self.sm_day_state.previous_state_was(self.sm_day_state.STATE_IDLE)
  343. # WAKE
  344. self._queue.enqueue(1, self.__queue_wrapper__, self.__pf__.set_bakery, True)
  345. self._queue.enqueue(2, self.wait, 0 if no_delay else 5 * 60)
  346. self._queue.enqueue(3, self.__queue_wrapper__, self.__pf__.set_bake_house, True)
  347. self._queue.enqueue(4, self.wait, 0 if no_delay else 10 * 60)
  348. self._queue.enqueue(5, self.__queue_wrapper__, self.__pf__.set_reese_house, True)
  349. self._queue.enqueue(6, self.wait, 0 if no_delay else 7 * 60)
  350. self._queue.enqueue(7, self.__queue_wrapper__, self.__pf__.set_ploenlein, True)
  351. self._queue.enqueue(8, self.wait, 0 if no_delay else 8 * 60)
  352. self._queue.enqueue(9, self.__queue_wrapper__, self.__pf__.set_mill, True)
  353. def fill_sunrise_queue(self):
  354. no_delay = self.sm_day_state.previous_state_was(self.sm_day_state.STATE_IDLE)
  355. # SUNRISE
  356. self._queue.enqueue(1, self.__queue_wrapper__, self.__pf__.set_bake_house, True)
  357. self._queue.enqueue(2, self.__queue_wrapper__, self.__pf__.set_ploenlein, False)
  358. self._queue.enqueue(3, self.wait, 0 if no_delay else 5 * 60)
  359. self._queue.enqueue(4, self.__queue_wrapper__, self.__pf__.set_mill, False)
  360. self._queue.enqueue(5, self.wait, 0 if no_delay else 8 * 60)
  361. self._queue.enqueue(6, self.__queue_wrapper__, self.__pf__.set_bakery, False)
  362. self._queue.enqueue(7, self.wait, 0 if no_delay else 12 * 60)
  363. self._queue.enqueue(8, self.__queue_wrapper__, self.__pf__.set_reese_house, False)
  364. def fill_sunset_queue(self):
  365. no_delay = self.sm_day_state.previous_state_was(self.sm_day_state.STATE_IDLE)
  366. # SUNSET
  367. self._queue.enqueue(1, self.__queue_wrapper__, self.__pf__.set_bake_house, True)
  368. self._queue.enqueue(2, self.__queue_wrapper__, self.__pf__.set_bakery, True)
  369. self._queue.enqueue(3, self.wait, 0 if no_delay else 10 * 60)
  370. self._queue.enqueue(4, self.__queue_wrapper__, self.__pf__.set_reese_house, True)
  371. self._queue.enqueue(5, self.wait, 0 if no_delay else 8 * 60)
  372. self._queue.enqueue(6, self.__queue_wrapper__, self.__pf__.set_mill, True)
  373. self._queue.enqueue(7, self.wait, 0 if no_delay else 7 * 60)
  374. self._queue.enqueue(8, self.__queue_wrapper__, self.__pf__.set_ploenlein, True)
  375. def fill_sleep_queue(self):
  376. no_delay = self.sm_day_state.previous_state_was(self.sm_day_state.STATE_IDLE)
  377. # SLEEP
  378. self._queue.enqueue(1, self.__queue_wrapper__, self.__pf__.set_bake_house, False)
  379. self._queue.enqueue(2, self.wait, 0 if no_delay else 5 * 60)
  380. self._queue.enqueue(3, self.__queue_wrapper__, self.__pf__.set_bakery, False)
  381. self._queue.enqueue(4, self.wait, 0 if no_delay else 9 * 60)
  382. self._queue.enqueue(5, self.__queue_wrapper__, self.__pf__.set_ploenlein, False)
  383. self._queue.enqueue(6, self.wait, 0 if no_delay else 9 * 60)
  384. self._queue.enqueue(7, self.__queue_wrapper__, self.__pf__.set_mill, False)
  385. self._queue.enqueue(8, self.wait, 0 if no_delay else 6 * 60)
  386. self._queue.enqueue(9, self.__queue_wrapper__, self.__pf__.set_reese_house, False)
  387. def task_1s(self, task_inst):
  388. self.sm_mode.work()
  389. self.sm_day_state.work()
  390. def join(self):
  391. self._task_1s.join()
  392. self._queue.join()
  393. def stop(self):
  394. self._task_1s.stop()
  395. self._queue.stop()
  396. def __del__(self):
  397. self.stop()