First Version of Step Controller

This commit is contained in:
manuel83 2018-12-03 22:16:03 +01:00
parent e2d4485f1f
commit 2b22f9ee4a
20 changed files with 1343 additions and 584 deletions

File diff suppressed because it is too large Load diff

11
core/api/step.py Normal file
View file

@ -0,0 +1,11 @@
class Step(object):
def __init__(self, key=None, cbpi=None):
self.cbpi = cbpi
self.id = key
async def run(self):
pass
def stop(self):
pass

View file

@ -17,7 +17,7 @@ class ActorHttp(HttpAPI):
:return: :return:
""" """
id = int(request.match_info['id']) id = int(request.match_info['id'])
self.cbpi.bus.fire(topic="actor/%s/on" % id, id=id, power=99) self.cbpi.bus.fire(topic="actor/%s/switch/on" % id, id=id, power=99)
return web.Response(status=204) return web.Response(status=204)
@ -90,16 +90,10 @@ class ActorController(ActorHttp, CRUDController):
self.cache[id].instance = clazz(**cfg) self.cache[id].instance = clazz(**cfg)
print("gpIO", self.cache[id].instance, self.cache[id].instance.gpio) print("gpIO", self.cache[id].instance, self.cache[id].instance.gpio)
@on_event(topic="actor/1/on")
def on1(self, **kwargs) -> None:
print("WOOOOHOOO111111")
@on_event(topic="actor/1/on")
def on3(self, **kwargs) -> None:
print("WOOOOHOOO22222")
@on_event(topic="actor/+/on")
@on_event(topic="actor/+/switch/on")
def on(self, id , power=100, **kwargs) -> None: def on(self, id , power=100, **kwargs) -> None:
''' '''
Method to switch an actor on. Method to switch an actor on.
@ -115,7 +109,7 @@ class ActorController(ActorHttp, CRUDController):
if id in self.cache: if id in self.cache:
print("POWER ON") print("POWER ON")
actor = self.cache[id ].instance actor = self.cache[id ].instance
print("ONNNNN", actor) self.cbpi.bus.fire("actor/%s/on/ok" % id)
actor.on(power) actor.on(power)
@on_event(topic="actor/+/toggle") @on_event(topic="actor/+/toggle")

View file

@ -1,3 +1,5 @@
import re
from aiohttp import web from aiohttp import web
from core.api import request_mapping, on_event from core.api import request_mapping, on_event
@ -76,11 +78,13 @@ class KettleController(CRUDController):
@on_event(topic="job/done") @on_event(topic="job/done")
def job_stop(self, key, **kwargs) -> None: def job_stop(self, key, **kwargs) -> None:
name = key.split("_") match = re.match("kettle_logic_(\d+)", key)
kettle = self.cache[int(name[2])] if match is not None:
kettle.instance = None kid = match.group(1)
kettle = self.cache[int(kid)]
kettle.instance = None
print("STOP KETTLE LOGIC", int(name[2])) print("STOP KETTLE LOGIC", kid)
@on_event(topic="kettle/+/automatic") @on_event(topic="kettle/+/automatic")
async def handle_automtic_event(self, id, **kwargs): async def handle_automtic_event(self, id, **kwargs):

View file

@ -13,6 +13,7 @@ from core.api.extension import CBPiExtension
from core.api.kettle_logic import CBPiKettleLogic from core.api.kettle_logic import CBPiKettleLogic
from core.api.property import Property from core.api.property import Property
from core.api.sensor import CBPiSensor from core.api.sensor import CBPiSensor
from core.api.step import Step
from core.utils.utils import load_config, json_dumps from core.utils.utils import load_config, json_dumps
logger = logging.getLogger(__file__) logger = logging.getLogger(__file__)
@ -101,13 +102,16 @@ class PluginController():
if issubclass(clazz, CBPiKettleLogic): if issubclass(clazz, CBPiKettleLogic):
self.cbpi.kettle.types[name] = {"class": clazz, "config": self._parse_props(clazz)} self.cbpi.kettle.types[name] = {"class": clazz, "config": self._parse_props(clazz)}
if issubclass(clazz, Step):
print("NAME", name)
self.cbpi.step.types[name] = {"class": clazz, "config": self._parse_props(clazz)}
if issubclass(clazz, CBPiExtension): if issubclass(clazz, CBPiExtension):
self.c = clazz(self.cbpi) self.c = clazz(self.cbpi)
def _parse_props(self, cls): def _parse_props(self, cls):
print("PARSE", cls)
name = cls.__name__ name = cls.__name__
result = {"name": name, "class": cls, "properties": [], "actions": []} result = {"name": name, "class": cls, "properties": [], "actions": []}

View file

@ -1,8 +1,69 @@
import asyncio
from aiohttp import web
from core.api import on_event, request_mapping
class StepController(): class StepController():
def __init__(self, cbpi):
self.cbpi = cbpi
self.types = {}
self.steps = {
1: dict(name="S1", config=dict(time=1), type="CustomStep", state=None),
2: dict(name="S2", config=dict(time=1), type="CustomStep", state=None),
3: dict(name="S3", config=dict(time=1), type="CustomStep", state=None)
}
self.current_step = None
self.cbpi.register(self, "/step")
async def start(self): async def init(self):
#self.start()
pass pass
@request_mapping(path="/start", auth_required=False)
async def http_start(self, request):
self.cbpi.bus.fire("step/start")
return web.Response(text="OK")
@request_mapping(path="/reset", auth_required=False)
async def http_reset(self, request):
self.cbpi.bus.fire("step/reset")
return web.Response(text="OK")
@on_event("step/start")
def handle_start(self, topic, **kwargs):
self.start()
@on_event("step/reset")
def handle_rest(self, topic, **kwargs):
for key, step in self.steps.items():
step["state"] = None
self.current_step = Nonecd
@on_event("step/+/done")
def handle(self, topic, **kwargs):
self.start()
def _step_done(self, task):
self.steps[self.current_step.id]["state"] = "D"
step_id = self.current_step.id
self.current_step = None
self.cbpi.bus.fire("step/%s/done" % step_id)
def start(self):
if self.current_step is None:
loop = asyncio.get_event_loop()
open_step = False
for key, step in self.steps.items():
if step["state"] is None:
type = self.types.get(step["type"])
self.current_step = type["class"](key, self.cbpi)
task = loop.create_task(self.current_step.run())
task.add_done_callback(self._step_done)
open_step = True
break
if open_step == False:
self.cbpi.bus.fire("step/berwing/finished")
async def stop(self): async def stop(self):
pass pass

View file

@ -12,6 +12,7 @@ from aiohttp_swagger import setup_swagger
from core.controller.config_controller import ConfigController from core.controller.config_controller import ConfigController
from core.controller.kettle_controller import KettleController from core.controller.kettle_controller import KettleController
from core.controller.step_controller import StepController
from core.job.aiohttp import setup, get_scheduler_from_app from core.job.aiohttp import setup, get_scheduler_from_app
from core.controller.actor_controller import ActorController from core.controller.actor_controller import ActorController
@ -58,10 +59,13 @@ class CraftBeerPi():
self.system = SystemController(self) self.system = SystemController(self)
self.config2 = ConfigController(self) self.config2 = ConfigController(self)
self.kettle = KettleController(self) self.kettle = KettleController(self)
self.step = StepController(self)
self.notification = NotificationController(self) self.notification = NotificationController(self)
self.login = Login(self) self.login = Login(self)
self.register_events(self.ws)
def register_events(self, obj): def register_events(self, obj):
@ -173,6 +177,7 @@ class CraftBeerPi():
switcher[http_method]() switcher[http_method]()
if url_prefix is not None: if url_prefix is not None:
print("Prefx", url_prefix)
sub = web.Application() sub = web.Application()
sub.add_routes(routes) sub.add_routes(routes)
self.app.add_subapp(url_prefix, sub) self.app.add_subapp(url_prefix, sub)
@ -230,11 +235,14 @@ class CraftBeerPi():
async def init_controller(app): async def init_controller(app):
await self.sensor.init() await self.sensor.init()
await self.step.init()
await self.actor.init() await self.actor.init()
await self.kettle.init() await self.kettle.init()
import pprint import pprint
pprint.pprint(self.bus.dump()) pprint.pprint(self.bus.dump())
async def load_plugins(app): async def load_plugins(app):
#await PluginController.load_plugin_list() #await PluginController.load_plugin_list()
await self.plugin.load_plugins() await self.plugin.load_plugins()

View file

@ -71,7 +71,7 @@ class EventBus(object):
print(self.loop) print(self.loop)
def fire(self, topic: str, **kwargs) -> None: def fire(self, topic: str, **kwargs) -> None:
self.logger.info("EMIT EVENT %s", topic) self.logger.info("EMIT EVENT %s Data: %s", topic, kwargs)
trx = dict(i=0) trx = dict(i=0)
for e in self.iter_match(topic): for e in self.iter_match(topic):
@ -91,10 +91,7 @@ class EventBus(object):
else: else:
content_obj.method(**kwargs, topic = topic) content_obj.method(**kwargs, topic = topic)
#if inspect.iscoroutinefunction(content_obj.method):
# self.loop.create_task(content_obj.method(**kwargs, trx=trx, topic=topic))
#else:
# content_obj.method(**kwargs, trx=trx, topic=topic)
if content_obj.once is False: if content_obj.once is False:
keep_idx.append(idx) keep_idx.append(idx)
@ -102,7 +99,7 @@ class EventBus(object):
if len(keep_idx) < len(e): if len(keep_idx) < len(e):
e[0].parent._content = [e[0].parent._content[i] for i in keep_idx] e[0].parent._content = [e[0].parent._content[i] for i in keep_idx]
print("DONE", trx)
def dump(self): def dump(self):
def rec(node, i=0): def rec(node, i=0):

View file

@ -1,132 +0,0 @@
import inspect
import logging
class EventBus(object):
def __init__(self, cbpi):
self.logger = logging.getLogger(__name__)
self._root = self.Node()
self.registry = {}
self.docs = {}
self.cbpi = cbpi
class Node(object):
__slots__ = '_children', '_content'
def __init__(self):
self._children = {}
self._content = None
class Content(object):
def __init__(self, parent, topic, method, once):
self.parent = parent
self.method = method
self.name = method.__name__
self.once = once
self.topic = topic
def register(self, topic, method, once=False):
print("REGISTER", topic, method)
if method in self.registry:
raise RuntimeError("Method %s already registerd. Please unregister first!" % method.__name__)
self.logger.info("Topic %s", topic)
node = self._root
for sym in topic.split('/'):
node = node._children.setdefault(sym, self.Node())
if not isinstance(node._content, list):
node._content = []
c = self.Content(node, topic, method, once)
node._content.append(c)
print(c, node._content, topic)
self.registry[method] = c
def get_callbacks(self, key):
try:
node = self._root
for sym in key.split('/'):
node = node._children[sym]
if node._content is None:
raise KeyError(key)
return node._content
except KeyError:
raise KeyError(key)
def unregister(self, method):
self.logger.info("Unregister %s", method.__name__)
if method in self.registry:
content = self.registry[method]
clean_idx = None
for idx, content_obj in enumerate(content.parent._content):
if method == content_obj.method:
clean_idx = idx
break
if clean_idx is not None:
del content.parent._content[clean_idx]
def fire(self, topic: str, **kwargs) -> None:
self.logger.info("EMIT EVENT %s", topic)
cleanup_methods = []
for content_array in self.iter_match(topic):
print(content_array)
cleanup = []
for idx, content_obj in enumerate(content_array):
if inspect.iscoroutinefunction(content_obj.method):
if hasattr(content_obj.method, "future"):
self.cbpi.app.loop.create_task(content_obj.method(**kwargs, future=content_obj.method.future, topic=topic))
else:
self.cbpi.app.loop.create_task(content_obj.method(**kwargs, topic = topic))
else:
if hasattr(content_obj.method, "future"):
content_obj.method(**kwargs, future=content_obj.method.future, topic=topic)
else:
content_obj.method(**kwargs, topic = topic)
if content_obj.once is True:
cleanup.append(idx)
for idx in cleanup:
del content_array[idx]
def iter_match(self, topic):
lst = topic.split('/')
normal = not topic.startswith('$')
def rec(node, i=0):
if i == len(lst):
if node._content is not None:
yield node._content
else:
part = lst[i]
if part in node._children:
for content in rec(node._children[part], i + 1):
yield content
if '+' in node._children and (normal or i > 0):
for content in rec(node._children['+'], i + 1):
yield content
if '#' in node._children and (normal or i > 0):
content = node._children['#']._content
if content is not None:
yield content
return rec(self._root)

View file

@ -23,7 +23,7 @@ class CustomActor(CBPiActor):
def on(self, power=100): def on(self, power=100):
print("###### ON", self.gpio)
self.state = True self.state = True

View file

@ -48,12 +48,15 @@ class CustomLogic(CBPiKettleLogic):
async def run(self): async def run(self):
async def my_callback(id, **kwargs): async def my_callback(value, **kwargs):
self.cbpi.bus.unregister(my_callback)
kwargs["future"].set_result("AMAZING")
return "OK"
result = await self.wait_for_event("actor/+/on", callback=my_callback) if value == 5:
self.cbpi.bus.unregister(my_callback)
kwargs["future"].set_result("AMAZING")
else:
print("OTHER VALUE", value)
result = await self.wait_for_event("sensor/1", callback=my_callback)
print("THE RESULT", result) print("THE RESULT", result)
@ -68,7 +71,7 @@ class CustomLogic(CBPiKettleLogic):
break break
await asyncio.sleep(1) await asyncio.sleep(1)
''' '''
print("STOP LOGIC") print("YES IM FINISHED STOP LOGIC")
def setup(cbpi): def setup(cbpi):

View file

@ -1,5 +1,6 @@
import asyncio import asyncio
import logging import logging
import random
from core.api import CBPiActor, Property, action, background_task from core.api import CBPiActor, Property, action, background_task
from core.api.sensor import CBPiSensor from core.api.sensor import CBPiSensor
@ -27,10 +28,12 @@ class CustomSensor(CBPiSensor):
async def run(self, cbpi): async def run(self, cbpi):
self.value = 0 self.value = 0
while True: while True:
await asyncio.sleep(self.interval) #await asyncio.sleep(self.interval)
await asyncio.sleep(random.uniform(0, 1))
self.value = self.value + 1 self.value = self.value + 1
print("SENSOR IS RUNNING") cbpi.bus.fire("sensor/%s" % self.id, value=self.value)
print("SENSOR IS RUNNING", self.value)

View file

@ -0,0 +1,25 @@
import asyncio
from core.api.step import Step
class CustomStep(Step):
async def run(self):
i = 0
while i < 3:
await asyncio.sleep(1)
print("RUN STEP")
i = i + 1
def setup(cbpi):
'''
This method is called by the server during startup
Here you need to register your plugins at the server
:param cbpi: the cbpi core
:return:
'''
cbpi.plugin.register("CustomStep", CustomStep)

View file

@ -0,0 +1,2 @@
name: Manuel
version: 4

View file

@ -1,11 +1,13 @@
import logging import logging
import weakref import weakref
from collections import defaultdict from collections import defaultdict
import json
import aiohttp import aiohttp
from aiohttp import web from aiohttp import web
from typing import Iterable, Callable from typing import Iterable, Callable
from core.api import on_event
class WebSocket: class WebSocket:
def __init__(self, cbpi) -> None: def __init__(self, cbpi) -> None:
@ -15,6 +17,13 @@ class WebSocket:
self.logger = logging.getLogger(__name__) self.logger = logging.getLogger(__name__)
self.cbpi.app.add_routes([web.get('/ws', self.websocket_handler)]) self.cbpi.app.add_routes([web.get('/ws', self.websocket_handler)])
@on_event(topic="#")
async def listen(self, topic, **kwargs):
print("WS", topic)
self.send(json.dumps(dict(topic=topic, data=dict(**kwargs))))
def send(self, data): def send(self, data):
for ws in self._clients: for ws in self._clients:

Binary file not shown.

BIN
hello.mp3 Normal file

Binary file not shown.

BIN
hello3.mp3 Normal file

Binary file not shown.

View file

@ -1,4 +1,5 @@
import asyncio import asyncio
import re
from core.eventbus3 import EventBus from core.eventbus3 import EventBus
@ -66,6 +67,9 @@ id2 = bus.register("test/name", test2)
print(id1, id2) print(id1, id2)
from gtts import gTTS
print(hex(id(test2))) print(hex(id(test2)))
@ -78,3 +82,9 @@ bus.fire("test/name")
bus.unregister(test2) bus.unregister(test2)
bus.fire("test/name") bus.fire("test/name")
m0 = re.match("kettle_logic_(\d+)", "kettle_1logic_22")
print(m0)
#print(m0.group(1))

View file

@ -14,6 +14,17 @@ class KettleTestCase(AioHTTPTestCase):
@unittest_run_loop @unittest_run_loop
async def test_example(self): async def test_example(self):
assert await self.cbpi.kettle.toggle_automtic(1) is True result = await self.cbpi.kettle.toggle_automtic(1)
assert await self.cbpi.kettle.toggle_automtic(1) is True print("#### RESULT", result)
assert await self.cbpi.kettle.toggle_automtic(99) is False assert result[0] is True
print("FIRE")
await asyncio.sleep(1)
self.cbpi.bus.fire("actor/1/on", id=1)
await asyncio.sleep(5)
#assert await self.cbpi.kettle.toggle_automtic(1) is True
#assert await self.cbpi.kettle.toggle_automtic(99) is False