25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

240 lines
7.0KB

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