Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

249 рядки
7.3KB

  1. import time
  2. import datetime
  3. from flask import json, request
  4. from flask_classy import route
  5. from modules import DBModel, cbpi, get_db
  6. from modules.core.baseview import BaseView
  7. class Step(DBModel):
  8. __fields__ = ["name","type", "stepstate", "state", "start", "end", "order", "config"]
  9. __table_name__ = "step"
  10. __json_fields__ = ["config", "stepstate"]
  11. __order_by__ = "order"
  12. __as_array__ = True
  13. @classmethod
  14. def get_max_order(cls):
  15. cur = get_db().cursor()
  16. cur.execute("SELECT max(step.'order') as 'order' FROM %s" % cls.__table_name__)
  17. r = cur.fetchone()
  18. return r.get("order")
  19. @classmethod
  20. def get_by_state(cls, state, order=True):
  21. cur = get_db().cursor()
  22. cur.execute("SELECT * FROM %s WHERE state = ? ORDER BY %s.'order'" % (cls.__table_name__,cls.__table_name__,), state)
  23. r = cur.fetchone()
  24. if r is not None:
  25. return cls(r)
  26. else:
  27. return None
  28. @classmethod
  29. def delete_all(cls):
  30. cur = get_db().cursor()
  31. cur.execute("DELETE FROM %s" % cls.__table_name__)
  32. get_db().commit()
  33. @classmethod
  34. def reset_all_steps(cls):
  35. cur = get_db().cursor()
  36. cur.execute("UPDATE %s SET state = 'I', stepstate = NULL , start = NULL, end = NULL " % cls.__table_name__)
  37. get_db().commit()
  38. @classmethod
  39. def update_state(cls, id, state):
  40. cur = get_db().cursor()
  41. cur.execute("UPDATE %s SET state = ? WHERE id =?" % cls.__table_name__, (state, id))
  42. get_db().commit()
  43. @classmethod
  44. def update_step_state(cls, id, state):
  45. cur = get_db().cursor()
  46. cur.execute("UPDATE %s SET stepstate = ? WHERE id =?" % cls.__table_name__, (json.dumps(state),id))
  47. get_db().commit()
  48. @classmethod
  49. def sort(cls, new_order):
  50. cur = get_db().cursor()
  51. for e in new_order:
  52. cur.execute("UPDATE %s SET '%s' = ? WHERE id = ?" % (cls.__table_name__, "order"), (e[1], e[0]))
  53. get_db().commit()
  54. class StepView(BaseView):
  55. model = Step
  56. def _pre_post_callback(self, data):
  57. order = self.model.get_max_order()
  58. data["order"] = 1 if order is None else order + 1
  59. data["state"] = "I"
  60. @route('/sort', methods=["POST"])
  61. def sort_steps(self):
  62. Step.sort(request.json)
  63. cbpi.emit("UPDATE_ALL_STEPS", self.model.get_all())
  64. return ('', 204)
  65. @route('/', methods=["DELETE"])
  66. def deleteAll(self):
  67. self.model.delete_all()
  68. cbpi.emit("UPDATE_ALL_STEPS", self.model.get_all())
  69. return ('', 204)
  70. @route('/action/<method>', methods=["POST"])
  71. def action(self, method):
  72. cbpi.cache["active_step"].__getattribute__(method)()
  73. return ('', 204)
  74. @route('/reset', methods=["POST"])
  75. def reset(self):
  76. self.model.reset_all_steps()
  77. self.stop_step()
  78. cbpi.emit("UPDATE_ALL_STEPS", self.model.get_all())
  79. cbpi.cache["active_brew"] = "none"
  80. return ('', 204)
  81. def stop_step(self):
  82. '''
  83. stop active step
  84. :return:
  85. '''
  86. step = cbpi.cache.get("active_step")
  87. cbpi.cache["active_step"] = None
  88. if step is not None:
  89. step.finish()
  90. @route('/reset/current', methods=['POST'])
  91. def resetCurrentStep(self):
  92. '''
  93. Reset current step
  94. :return:
  95. '''
  96. step = cbpi.cache.get("active_step")
  97. if step is not None:
  98. step.reset()
  99. if step.is_dirty():
  100. state = {}
  101. for field in step.managed_fields:
  102. state[field] = step.__getattribute__(field)
  103. Step.update_step_state(step.id, state)
  104. step.reset_dirty()
  105. cbpi.emit("UPDATE_ALL_STEPS", self.model.get_all())
  106. return ('', 204)
  107. def init_step(self, step):
  108. cbpi.log_action("Start Step %s" % step.name)
  109. type_cfg = cbpi.cache.get("step_types").get(step.type)
  110. if type_cfg is None:
  111. # if type not found
  112. return
  113. # copy config to stepstate
  114. # init step
  115. cfg = step.config.copy()
  116. cfg.update(dict(name=step.name, api=cbpi, id=step.id, timer_end=None, managed_fields=get_manged_fields_as_array(type_cfg)))
  117. instance = type_cfg.get("class")(**cfg)
  118. instance.init()
  119. # set step instance to ache
  120. cbpi.cache["active_step"] = instance
  121. @route('/start', methods=['POST'])
  122. def start(self):
  123. if "none" == cbpi.cache["active_brew"]:
  124. cbpi.cache["active_brew"] = cbpi.cache["config"]["brew_name"].__dict__["value"] + \
  125. "_" + datetime.datetime.now().strftime('%y-%m-%dT%H:%M')
  126. return self.next()
  127. @route('/next', methods=['POST'])
  128. def next(self):
  129. active = Step.get_by_state("A")
  130. inactive = Step.get_by_state('I')
  131. if (active is not None):
  132. active.state = 'D'
  133. active.end = int(time.time())
  134. self.stop_step()
  135. Step.update(**active.__dict__)
  136. if (inactive is not None):
  137. self.init_step(inactive)
  138. inactive.state = 'A'
  139. inactive.stepstate = inactive.config
  140. inactive.start = int(time.time())
  141. Step.update(**inactive.__dict__)
  142. else:
  143. cbpi.log_action("Brewing Finished")
  144. cbpi.notify("Brewing Finished", "You are done!", timeout=None)
  145. cbpi.cache["active_brew"] = "none"
  146. cbpi.emit("UPDATE_ALL_STEPS", Step.get_all())
  147. return ('', 204)
  148. def get_manged_fields_as_array(type_cfg):
  149. result = []
  150. for f in type_cfg.get("properties"):
  151. result.append(f.get("name"))
  152. return result
  153. @cbpi.try_catch(None)
  154. def init_after_startup():
  155. '''
  156. Restart after startup. Check is a step is in state A and reinitialize
  157. :return: None
  158. '''
  159. step = Step.get_by_state('A')
  160. # We have an active step
  161. if step is not None:
  162. # get the type
  163. type_cfg = cbpi.cache.get("step_types").get(step.type)
  164. if type_cfg is None:
  165. # step type not found. cant restart step
  166. return
  167. cfg = step.stepstate.copy()
  168. cfg.update(dict(api=cbpi, id=step.id, managed_fields=get_manged_fields_as_array(type_cfg)))
  169. instance = type_cfg.get("class")(**cfg)
  170. instance.init()
  171. cbpi.cache["active_step"] = instance
  172. @cbpi.initalizer(order=2000)
  173. def init(cbpi):
  174. StepView.register(cbpi.app, route_base='/api/step')
  175. def get_all():
  176. with cbpi.app.app_context():
  177. return Step.get_all()
  178. with cbpi.app.app_context():
  179. init_after_startup()
  180. cbpi.add_cache_callback("steps", get_all)
  181. @cbpi.backgroundtask(key="step_task", interval=0.1)
  182. def execute_step(api):
  183. '''
  184. Background job which executes the step
  185. :return:
  186. '''
  187. with cbpi.app.app_context():
  188. step = cbpi.cache.get("active_step")
  189. if step is not None:
  190. step.execute()
  191. if step.is_dirty():
  192. state = {}
  193. for field in step.managed_fields:
  194. state[field] = step.__getattribute__(field)
  195. Step.update_step_state(step.id, state)
  196. step.reset_dirty()
  197. cbpi.emit("UPDATE_ALL_STEPS", Step.get_all())
  198. if step.n is True:
  199. StepView().start()
  200. cbpi.emit("UPDATE_ALL_STEPS", Step.get_all())