craftbeerpi4-pione/cbpi/controller/step_controller.py

264 lines
8.8 KiB
Python
Raw Normal View History

2021-01-22 23:25:20 +01:00
import asyncio
2021-02-16 20:37:51 +01:00
import copy
2021-01-22 23:25:20 +01:00
import json
import logging
import os.path
2018-12-03 22:16:03 +01:00
2021-02-16 20:37:51 +01:00
import shortuuid
from cbpi.api.dataclasses import Props, Step
from tabulate import tabulate
2018-12-08 14:21:00 +01:00
2021-02-16 20:37:51 +01:00
from ..api.step import StepMove, StepResult, StepState
2018-12-09 22:20:33 +01:00
2019-07-27 21:08:19 +02:00
2021-01-22 23:25:20 +01:00
class StepController:
2018-11-29 21:59:08 +01:00
2018-12-03 22:16:03 +01:00
def __init__(self, cbpi):
self.cbpi = cbpi
2019-01-04 09:29:09 +01:00
self.logger = logging.getLogger(__name__)
2021-01-22 23:25:20 +01:00
self.path = os.path.join(".", 'config', "step_data.json")
self._loop = asyncio.get_event_loop()
self.basic_data = {}
self.step = None
self.types = {}
self.cbpi.app.on_cleanup.append(self.shutdown)
2019-07-27 21:08:19 +02:00
async def init(self):
2021-01-22 23:25:20 +01:00
logging.info("INIT STEP Controller")
self.load(startActive=True)
2021-02-16 20:37:51 +01:00
def create(self, data):
id = data.get("id")
name = data.get("name")
type = data.get("type")
status = StepState(data.get("status", "I"))
props = data.get("props", {})
try:
type_cfg = self.types.get(type)
clazz = type_cfg.get("class")
instance = clazz(self.cbpi, id, name, Props(props), self.done)
except Exception as e:
logging.warning("Failed to create step instance %s - %s" % (id, e))
instance = None
return Step(id, name, type=type, status=status, instance=instance, props=Props(props) )
2021-01-22 23:25:20 +01:00
def load(self, startActive=False):
# create file if not exists
if os.path.exists(self.path) is False:
with open(self.path, "w") as file:
json.dump(dict(basic={}, profile=[]), file, indent=4, sort_keys=True)
#load from json file
with open(self.path) as json_file:
data = json.load(json_file)
self.basic_data = data["basic"]
self.profile = data["profile"]
# Start step after start up
2021-02-16 20:37:51 +01:00
self.profile = list(map(lambda item: self.create(item), self.profile))
2021-01-22 23:25:20 +01:00
if startActive is True:
active_step = self.find_by_status("A")
2021-02-16 20:37:51 +01:00
if active_step is not None:
2021-01-22 23:25:20 +01:00
self._loop.create_task(self.start_step(active_step))
2021-02-16 20:37:51 +01:00
async def add(self, item: Step):
logging.debug("Add step")
item.id = shortuuid.uuid()
item.status = StepState.INITIAL
print(item)
try:
type_cfg = self.types.get(item.type)
clazz = type_cfg.get("class")
print("CLASS", clazz)
item.instance = clazz(self.cbpi, item.id, item.name, item.props, self.done)
except Exception as e:
logging.warning("Failed to create step instance %s - %s " % (id, e))
item.instance = None
2021-01-22 23:25:20 +01:00
self.profile.append(item)
await self.save()
return item
2021-02-16 20:37:51 +01:00
async def update(self, item: Step):
2021-01-22 23:25:20 +01:00
logging.info("update step")
2021-02-16 20:37:51 +01:00
try:
type_cfg = self.types.get(item.type)
clazz = type_cfg.get("class")
2021-02-10 07:38:55 +01:00
2021-02-16 20:37:51 +01:00
item.instance = clazz(self.cbpi, item.id, item.name, item.props, self.done)
except Exception as e:
logging.warning("Failed to create step instance %s - %s " % (item.id, e))
item.instance = None
self.profile = list(map(lambda old: item if old.id == item.id else old, self.profile))
2021-01-22 23:25:20 +01:00
await self.save()
2021-02-16 20:37:51 +01:00
return item
2021-01-22 23:25:20 +01:00
async def save(self):
logging.debug("save profile")
2021-02-16 20:37:51 +01:00
data = dict(basic=self.basic_data, profile=list(map(lambda item: item.to_dict(), self.profile)))
2021-01-22 23:25:20 +01:00
with open(self.path, "w") as file:
json.dump(data, file, indent=4, sort_keys=True)
2021-02-10 07:38:55 +01:00
self.push_udpate()
2021-01-22 23:25:20 +01:00
async def start(self):
2021-02-16 20:37:51 +01:00
if self.find_by_status(StepState.ACTIVE) is not None:
2021-01-22 23:25:20 +01:00
logging.error("Steps already running")
return
2021-02-16 20:37:51 +01:00
step = self.find_by_status(StepState.STOP)
2021-01-22 23:25:20 +01:00
if step is not None:
logging.info("Resume step")
await self.start_step(step)
await self.save()
return
2021-02-16 20:37:51 +01:00
step = self.find_by_status(StepState.INITIAL)
2021-01-22 23:25:20 +01:00
if step is not None:
2021-02-16 20:37:51 +01:00
logging.info("Start Step")
2021-01-22 23:25:20 +01:00
await self.start_step(step)
await self.save()
return
2021-02-16 20:37:51 +01:00
self.cbpi.notify(message="BREWING COMPLETE")
2021-01-22 23:25:20 +01:00
logging.info("BREWING COMPLETE")
async def next(self):
logging.info("Trigger Next")
2021-02-16 20:37:51 +01:00
step = self.find_by_status(StepState.ACTIVE)
2021-01-22 23:25:20 +01:00
if step is not None:
2021-02-16 20:37:51 +01:00
if step.instance is not None:
await step.instance.next()
step = self.find_by_status(StepState.STOP)
2021-02-10 07:38:55 +01:00
if step is not None:
2021-02-16 20:37:51 +01:00
if step.instance is not None:
step.status = StepState.DONE
2021-02-10 07:38:55 +01:00
await self.save()
await self.start()
2019-01-04 09:29:09 +01:00
else:
2021-01-22 23:25:20 +01:00
logging.info("No Step is running")
async def resume(self):
step = self.find_by_status("P")
if step is not None:
instance = step.get("instance")
if instance is not None:
await self.start_step(step)
else:
logging.info("Nothing to resume")
async def stop(self):
2021-02-16 20:37:51 +01:00
step = self.find_by_status(StepState.ACTIVE)
if step != None:
2021-01-22 23:25:20 +01:00
logging.info("CALLING STOP STEP")
2021-02-16 20:37:51 +01:00
try:
await step.instance.stop()
step.status = StepState.STOP
await self.save()
except Exception as e:
logging.error("Failed to stop step - Id: %s" % step.id)
2021-01-22 23:25:20 +01:00
async def reset_all(self):
2021-02-16 20:37:51 +01:00
if self.find_by_status(StepState.ACTIVE) is not None:
2021-01-22 23:25:20 +01:00
logging.error("Please stop before reset")
return
2021-02-16 20:37:51 +01:00
2021-01-22 23:25:20 +01:00
for item in self.profile:
2021-02-16 20:37:51 +01:00
logging.info("Reset %s" % item)
item.status = StepState.INITIAL
try:
await item.instance.reset()
except:
logging.warning("No Step Instance - Id: %s", item.id)
await self.save()
2021-02-10 07:38:55 +01:00
self.push_udpate()
2021-01-22 23:25:20 +01:00
2021-02-16 20:37:51 +01:00
def get_types(self):
2021-01-22 23:25:20 +01:00
result = {}
for key, value in self.types.items():
result[key] = dict(name=value.get("name"), properties=value.get("properties"), actions=value.get("actions"))
return result
2019-01-04 09:29:09 +01:00
2021-01-22 23:25:20 +01:00
def get_state(self):
2021-02-16 20:37:51 +01:00
return {"basic": self.basic_data, "profile": list(map(lambda item: item.to_dict(), self.profile)), "types":self.get_types()}
2019-07-27 21:08:19 +02:00
2021-02-16 20:37:51 +01:00
async def move(self, id, direction: StepMove):
2021-01-22 23:25:20 +01:00
index = self.get_index_by_id(id)
if direction not in [-1, 1]:
self.logger.error("Cant move. Direction 1 and -1 allowed")
2019-07-27 21:08:19 +02:00
return
2021-01-22 23:25:20 +01:00
self.profile[index], self.profile[index+direction] = self.profile[index+direction], self.profile[index]
await self.save()
2021-02-10 07:38:55 +01:00
self.push_udpate()
2021-01-22 23:25:20 +01:00
async def delete(self, id):
step = self.find_by_id(id)
2021-02-16 20:37:51 +01:00
if step is None:
logging.error("Cant find step - Nothing deleted - Id: %s", id)
return
if step.status == StepState.ACTIVE:
2021-01-22 23:25:20 +01:00
logging.error("Cant delete active Step %s", id)
return
2021-02-16 20:37:51 +01:00
self.profile = list(filter(lambda item: item.id != id, self.profile))
2021-01-22 23:25:20 +01:00
await self.save()
2021-02-16 20:37:51 +01:00
2021-01-22 23:25:20 +01:00
async def shutdown(self, app):
logging.info("Mash Profile Shutdonw")
for p in self.profile:
2021-02-16 20:37:51 +01:00
instance = p.instance
2021-01-22 23:25:20 +01:00
# Stopping all running task
if instance.task != None and instance.task.done() is False:
logging.info("Stop Step")
2021-02-10 07:38:55 +01:00
await instance.stop()
2021-01-22 23:25:20 +01:00
await instance.task
await self.save()
2021-02-16 20:37:51 +01:00
def done(self, step, result):
if result == StepResult.NEXT:
step_current = self.find_by_id(step.id)
step_current.status = StepState.DONE
async def wrapper():
await self.save()
await self.start()
asyncio.create_task(wrapper())
2019-01-04 09:29:09 +01:00
2021-01-22 23:25:20 +01:00
def find_by_status(self, status):
2021-02-16 20:37:51 +01:00
return next((item for item in self.profile if item.status == status), None)
2021-01-22 23:25:20 +01:00
def find_by_id(self, id):
2021-02-16 20:37:51 +01:00
return next((item for item in self.profile if item.id == id), None)
2021-01-22 23:25:20 +01:00
def get_index_by_id(self, id):
2021-02-16 20:37:51 +01:00
return next((i for i, item in enumerate(self.profile) if item.id == id), None)
2021-01-22 23:25:20 +01:00
2021-02-10 07:38:55 +01:00
def push_udpate(self):
2021-02-16 20:37:51 +01:00
self.cbpi.ws.send(dict(topic="step_update", data=list(map(lambda item: item.to_dict(), self.profile))))
2021-01-30 22:29:33 +01:00
2021-01-22 23:25:20 +01:00
async def start_step(self,step):
2021-02-10 07:38:55 +01:00
try:
2021-02-16 20:37:51 +01:00
logging.info("Try to start step %s" % step)
await step.instance.start()
step.status = StepState.ACTIVE
2021-02-10 07:38:55 +01:00
except Exception as e:
2021-02-16 20:37:51 +01:00
logging.error("Faild to start step %s" % step)
2021-01-22 23:25:20 +01:00
async def save_basic(self, data):
logging.info("SAVE Basic Data")
self.basic_data = {**self.basic_data, **data,}
await self.save()
2021-02-10 07:38:55 +01:00
self.push_udpate()