controller.py 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. # Copyright 2017 Digital
  2. #
  3. # This file is part of DigiLib.
  4. #
  5. # DigiLib is free software: you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation, either version 3 of the License, or
  8. # (at your option) any later version.
  9. #
  10. # DigiLib is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with DigiLib. If not, see <http://www.gnu.org/licen
  17. import logging
  18. log = logging.getLogger(__name__+"")
  19. lctrl = logging.getLogger(__name__+".ctrl")
  20. import time
  21. import curio
  22. import digilib.network
  23. import digilib.misc
  24. import digilib.gpio.wrapper
  25. class ControllerBase(object):
  26. """
  27. ControllerBase is the baseclass for Controller. All collectors need to inherit from CollectorBase or provide the same methods.
  28. A collector collects information form sensors and puts them in a pipe, so the CtrlManager can access it. The minutely, hourly and daily methods are easy to use but there execution time depends on when the core was started. use curio's execute_at feature to execute a function at a specific time.
  29. Parameters
  30. ----------
  31. """
  32. def __init__(self):
  33. """
  34. during initilazion, a Controller should register its methods.
  35. """
  36. super().__init__()
  37. async def collect_loadcell_data(self):
  38. """
  39. This method collects data from a sensor.
  40. """
  41. async def daily(self):
  42. """
  43. This method is called once every day by the core.
  44. """
  45. async def hourly(self):
  46. """
  47. This method is called once every hour by the core.
  48. """
  49. async def main_loop(self):
  50. """
  51. This is the main loop of the Controller class.
  52. """
  53. async def minutely(self):
  54. """
  55. This method is called once every minute by the core.
  56. """
  57. async def on_startup(self):
  58. """
  59. This method is called by the core when it starts. This is a good entry point for a Controller class, however the main_loop should be in a different method wich can be called here.
  60. """
  61. # self.main_loop()
  62. async def on_shutdown(self):
  63. """
  64. This method is called by the core when it shuts down.
  65. """
  66. class ButtonController(object):
  67. """
  68. ButtonController can be used with a hardware push button. It provides events you can register a callback to, join it or test the buttons state.
  69. .. A collector collects information form sensors and puts them in a pipe, so the CtrlManager can access it. The minutely, hourly and daily methods are easy to use but there execution time depends on when the core was started. use curio's execute_at feature to execute a function at a specific time.
  70. Parameters
  71. ----------
  72. pin_num: int
  73. the number of the pin wich is connected to the button and to logical HIGH through an appropriate resistor.
  74. time_short_press: int
  75. the maximum time the button needs to be pressed to be registered as a short press.
  76. time_long_press: int
  77. the maximum time the button needs to be pressed to be registered as a long press. if ``time_short_press`` is greater the long press feature is disabled.
  78. Attributes
  79. ----------
  80. STATE_PRESSED: int
  81. the value returned from gpio.read if the button is pressed
  82. STATE_RELEASED: int
  83. the value returned from gpio.read if the button is released
  84. """
  85. def __init__(self,pin_num,time_short_press,time_long_press,):
  86. """
  87. """
  88. super().__init__()
  89. self.pin_num = pin_num
  90. self.time_short_press = time_short_press
  91. self.time_long_press = time_long_press
  92. digilib.gpio.wrapper.setup(self.pin_num,digilib.gpio.wrapper.OUT)
  93. async def read_button_state(self):
  94. """
  95. This method reads the current state from the button's gpio pin.
  96. """
  97. return digilib.gpio.wrapper.read(self.pin_num)
  98. async def daily(self):
  99. """
  100. This method is called once every day by the core.
  101. """
  102. async def hourly(self):
  103. """
  104. This method is called once every hour by the core.
  105. """
  106. async def main_loop(self):
  107. """
  108. The main loop executes registered callbacks if the button state was changed, was pressed for ``self.time_short_press`` or ``self.time_long_press``.
  109. Attributes
  110. ----------
  111. prev_state: int
  112. the state of the button during the last loop.
  113. time_pressed: int
  114. the time when the button was pressed, taken from time.time()
  115. time_released: int
  116. the time when the button was released, taken from time.time()
  117. """
  118. prev_state = None
  119. time_pressed = 0
  120. time_released = 0
  121. try:
  122. while True:
  123. state = self.read_button_state()
  124. if state != prev_state:
  125. if state = self.STATE_PRESSED:
  126. # the button was pressed just now
  127. time_pressed = time.time()
  128. # TODO execute registered methods
  129. else:
  130. # the button was released just now
  131. time_released = time.time()
  132. # TODO execute registered methods
  133. if ( time_released - time_pressed
  134. >= self.time_short_press ):
  135. # the button was pressed for a short time.
  136. # TODO execute registered methods
  137. pass
  138. elif ( time_released - time_pressed
  139. >= self.time_long_press ):
  140. # the button was pressed for a short time.
  141. # TODO execute registered methods
  142. pass
  143. prev_state = state
  144. await curio.sleep(0.1)
  145. async def minutely(self):
  146. """
  147. This method is called once every minute by the core.
  148. """
  149. async def on_startup(self):
  150. """
  151. This method is called by the core when it starts. This is a good entry point for a Controller class, however the main_loop should be in a different method wich can be called here.
  152. """
  153. # self.main_loop()
  154. async def on_shutdown(self):
  155. """
  156. This method is called by the core when it shuts down.
  157. """
  158. class LED(ControllerBase):
  159. """
  160. Controllerbase controlls a normal LED.
  161. Parameters
  162. ----------
  163. pin_num: int
  164. number of the led's pin
  165. Attributes
  166. ----------
  167. lock: threading.Lock
  168. The lock used to protect gpio operations.
  169. """
  170. lock = None
  171. def __init__(self,pin_num):
  172. super().__init__()
  173. self.pin = pin.DigitalPin(pin_num,_gpio.OUT)
  174. self.lock = threading.Lock()
  175. self.on()
  176. async def on(self,args=[],command=None,respond=None):
  177. if self.lock.locked():
  178. response("This LED is already in use")
  179. return
  180. with self.lock:
  181. self.write(True)
  182. async def off(self,args=[],command=None,respond=None):
  183. if self.lock.locked():
  184. respond("This LED is already in use")
  185. return
  186. with self.lock:
  187. self.write(False)
  188. async def set(self,args=[],command=None,respond=None):
  189. if len(args) != 1:
  190. respond("one missing argument: state")
  191. return
  192. [state] = args
  193. if self.lock.locked():
  194. response("This LED is already in use")
  195. return
  196. with self.lock:
  197. self.write(state)
  198. class StatusLED(PinControllerBase):
  199. def __init__(self,pin_red,pin_green):
  200. super(StatusLED,self).__init__([pin_red,pin_green])
  201. self.pin_red = DigitalPin(pin_red,_gpio.OUT)
  202. self.pin_green = DigitalPin(pin_green,_gpio.OUT)
  203. self.green()
  204. def red(self,args=[],command=None,respond=None):
  205. if len(args) > 1:
  206. respond(errmsg.args(command,"one","optional","<state>"))
  207. return
  208. elif len(args) == 1:
  209. state = digilib.misc.parse_to_int_list(*args)
  210. else:
  211. state = 1
  212. self.pin_red.write(state)
  213. self.pin_green.write(int(not state))
  214. def green(self,args=[],command=None,respond=None):
  215. if len(args) > 1:
  216. respond(ERROR_TAKES_ARGUMENTS.format(
  217. command,"one","optional","<state>"))
  218. return
  219. elif len(args) == 1:
  220. state = int(*args)
  221. else:
  222. state = 1
  223. self.pin_green.write(state)
  224. self.pin_red.write(int(not state))
  225. class DebugPinController(PinControllerBase):
  226. def write(self,args=[],command=None,respond=None):
  227. if len(args) != 2:
  228. respond(ERROR_TAKES_ARGUMENTS.format(
  229. command, "two", "positional", "<name>"))
  230. return False
  231. pins = digilib.misc.parse_to_int_list(args[0])
  232. [state] = digilib.misc.parse_to_int_list(args[1])
  233. _gpio.write(pins,state)
  234. def read(self,args=[],command=None,respond=None):
  235. if len(args) != 2:
  236. respond(ERROR_TAKES_ARGUMENTS.format(
  237. command, "two", "positional", "<name>"))
  238. return False
  239. pins = digilib.misc.parse_to_int_list(args[0])
  240. [state] = digilib.misc.parse_to_int_list(args[1])
  241. rv = _gpio.read(pins,state)
  242. lgpio.debug(rv)
  243. respond(str(rv))
  244. def raise_exc(self,args=[],command=None,respond=None):
  245. raise Exception("Test Exception")
  246. async def araise_exc(self,args=[],command=None,respond=None):
  247. state = digilib.misc.parse_to_int_list("1,2,3,4")
  248. a = 1+2
  249. raise Exception("Test Async Exception")
  250. #