forked from platypush/platypush
New API for firing events and registering/unregistering event handlers
both for plugins and backends
This commit is contained in:
parent
e445029007
commit
a9fb6a38dd
4 changed files with 113 additions and 13 deletions
|
@ -14,6 +14,7 @@ from platypush.bus import Bus
|
||||||
from platypush.config import Config
|
from platypush.config import Config
|
||||||
from platypush.context import get_backend, get_plugin
|
from platypush.context import get_backend, get_plugin
|
||||||
from platypush.utils import get_message_class_by_type, set_timeout, clear_timeout
|
from platypush.utils import get_message_class_by_type, set_timeout, clear_timeout
|
||||||
|
from platypush.event import EventGenerator
|
||||||
from platypush.message import Message
|
from platypush.message import Message
|
||||||
from platypush.message.event import Event, StopEvent
|
from platypush.message.event import Event, StopEvent
|
||||||
from platypush.message.request import Request
|
from platypush.message.request import Request
|
||||||
|
@ -21,7 +22,7 @@ from platypush.message.response import Response
|
||||||
from platypush.utils import get_redis_queue_name_by_message, set_thread_name
|
from platypush.utils import get_redis_queue_name_by_message, set_thread_name
|
||||||
|
|
||||||
|
|
||||||
class Backend(Thread):
|
class Backend(Thread, EventGenerator):
|
||||||
"""
|
"""
|
||||||
Parent class for backends.
|
Parent class for backends.
|
||||||
|
|
||||||
|
@ -45,7 +46,8 @@ class Backend(Thread):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self._thread_name = self.__class__.__name__
|
self._thread_name = self.__class__.__name__
|
||||||
super().__init__(name=self._thread_name)
|
EventGenerator.__init__(self)
|
||||||
|
Thread.__init__(self, name=self._thread_name)
|
||||||
|
|
||||||
# If no bus is specified, create an internal queue where
|
# If no bus is specified, create an internal queue where
|
||||||
# the received messages will be pushed
|
# the received messages will be pushed
|
||||||
|
@ -266,4 +268,3 @@ class Backend(Thread):
|
||||||
|
|
||||||
|
|
||||||
# vim:sw=4:ts=4:et:
|
# vim:sw=4:ts=4:et:
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
import inspect
|
||||||
|
import threading
|
||||||
|
|
||||||
|
|
||||||
|
class EventGenerator(object):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self._event_handlers = {} # Event type => callback map
|
||||||
|
|
||||||
|
def fire_event(self, event):
|
||||||
|
"""
|
||||||
|
Fires an event (instance of :class:`platypush.message.event.Event` or a
|
||||||
|
subclass) to the internal bus and triggers any handler callback
|
||||||
|
associated to the event type or any of its super-classes.
|
||||||
|
|
||||||
|
:param event: Event to fire
|
||||||
|
:type event: :class:`platypush.message.event.Event` or a subclass
|
||||||
|
"""
|
||||||
|
|
||||||
|
def hndl_thread():
|
||||||
|
hndl(event)
|
||||||
|
|
||||||
|
from platypush.backend import Backend
|
||||||
|
from platypush.context import get_bus
|
||||||
|
|
||||||
|
bus = self.bus if isinstance(self, Backend) else get_bus()
|
||||||
|
bus.post(event)
|
||||||
|
handlers = set()
|
||||||
|
|
||||||
|
for cls in inspect.getmro(event.__class__):
|
||||||
|
if cls in self._event_handlers:
|
||||||
|
handlers.update(self._event_handlers[cls])
|
||||||
|
|
||||||
|
for hndl in handlers:
|
||||||
|
threading.Thread(target=hndl_thread).start()
|
||||||
|
|
||||||
|
|
||||||
|
def register_handler(self, event_type, callback):
|
||||||
|
"""
|
||||||
|
Registers a callback handler for a camera event type.
|
||||||
|
|
||||||
|
:param event_type: Event type to listen to. Must be a subclass of
|
||||||
|
:class:`platypush.message.event.Event`
|
||||||
|
:type event_type: :class:`platypush.message.event.Event` or a subclass
|
||||||
|
|
||||||
|
:param callback: Callback function. It will take an event of type
|
||||||
|
:class:`platypush.message.event.Event` as a parameter
|
||||||
|
:type callback: function
|
||||||
|
"""
|
||||||
|
|
||||||
|
if event_type not in self._event_handlers:
|
||||||
|
self._event_handlers[event_type] = set()
|
||||||
|
self._event_handlers[event_type].add(callback)
|
||||||
|
|
||||||
|
|
||||||
|
def unregister_handler(self, event_type, callback):
|
||||||
|
"""
|
||||||
|
Unregisters a callback handler from a camera event type.
|
||||||
|
|
||||||
|
:param event_type: Event type the callback is registered to
|
||||||
|
:type event_type: :class:`platypush.message.event.Event` or a subclass
|
||||||
|
|
||||||
|
:param callback: Callback function to unregister
|
||||||
|
:type callback: function
|
||||||
|
"""
|
||||||
|
|
||||||
|
if event_type not in self._event_handlers:
|
||||||
|
return
|
||||||
|
if callback not in self._event_handlers[event_type]:
|
||||||
|
return
|
||||||
|
|
||||||
|
self._event_handlers[event_type].remove(callback)
|
||||||
|
|
||||||
|
if not self._event_handlers[event_type]:
|
||||||
|
del self._event_handlers[event_type]
|
||||||
|
|
||||||
|
|
||||||
|
# vim:sw=4:ts=4:et:
|
|
@ -1,10 +1,13 @@
|
||||||
|
import inspect
|
||||||
import sys
|
import sys
|
||||||
import logging
|
import logging
|
||||||
|
import threading
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
|
||||||
from platypush.config import Config
|
from platypush.config import Config
|
||||||
|
from platypush.event import EventGenerator
|
||||||
from platypush.message.response import Response
|
from platypush.message.response import Response
|
||||||
from platypush.utils import get_decorators
|
from platypush.utils import get_decorators
|
||||||
|
|
||||||
|
@ -33,10 +36,12 @@ def action(f):
|
||||||
return _execute_action
|
return _execute_action
|
||||||
|
|
||||||
|
|
||||||
class Plugin(object):
|
class Plugin(EventGenerator):
|
||||||
""" Base plugin class """
|
""" Base plugin class """
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
self.logger = logging.getLogger(self.__class__.__name__)
|
self.logger = logging.getLogger(self.__class__.__name__)
|
||||||
if 'logging' in kwargs:
|
if 'logging' in kwargs:
|
||||||
self.logger.setLevel(getattr(logging, kwargs['logging'].upper()))
|
self.logger.setLevel(getattr(logging, kwargs['logging'].upper()))
|
||||||
|
@ -46,6 +51,7 @@ class Plugin(object):
|
||||||
.get('action', [])
|
.get('action', [])
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def run(self, method, *args, **kwargs):
|
def run(self, method, *args, **kwargs):
|
||||||
if method not in self.registered_actions:
|
if method not in self.registered_actions:
|
||||||
raise RuntimeError('{} is not a registered action on {}'.format(
|
raise RuntimeError('{} is not a registered action on {}'.format(
|
||||||
|
@ -55,4 +61,3 @@ class Plugin(object):
|
||||||
|
|
||||||
|
|
||||||
# vim:sw=4:ts=4:et:
|
# vim:sw=4:ts=4:et:
|
||||||
|
|
||||||
|
|
|
@ -9,10 +9,9 @@ import cv2
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from platypush.config import Config
|
from platypush.config import Config
|
||||||
from platypush.context import get_bus
|
|
||||||
from platypush.message.event.camera import CameraRecordingStartedEvent, \
|
from platypush.message.event.camera import CameraRecordingStartedEvent, \
|
||||||
CameraRecordingStoppedEvent, CameraVideoRenderedEvent, \
|
CameraRecordingStoppedEvent, CameraVideoRenderedEvent, \
|
||||||
CameraPictureTakenEvent
|
CameraPictureTakenEvent, CameraEvent
|
||||||
|
|
||||||
from platypush.plugins import Plugin, action
|
from platypush.plugins import Plugin, action
|
||||||
|
|
||||||
|
@ -123,7 +122,7 @@ class CameraPlugin(Plugin):
|
||||||
if device_id in self._devices:
|
if device_id in self._devices:
|
||||||
self._devices[device_id].release()
|
self._devices[device_id].release()
|
||||||
del self._devices[device_id]
|
del self._devices[device_id]
|
||||||
get_bus().post(CameraRecordingStoppedEvent(device_id=device_id))
|
self.fire_event(CameraRecordingStoppedEvent(device_id=device_id))
|
||||||
|
|
||||||
|
|
||||||
def _store_frame_to_file(self, frame, frames_dir, image_file):
|
def _store_frame_to_file(self, frame, frames_dir, image_file):
|
||||||
|
@ -135,9 +134,6 @@ class CameraPlugin(Plugin):
|
||||||
frames_dir, datetime.now().strftime('%Y-%m-%d_%H-%M-%S-%f.jpg'))
|
frames_dir, datetime.now().strftime('%Y-%m-%d_%H-%M-%S-%f.jpg'))
|
||||||
|
|
||||||
cv2.imwrite(filepath, frame)
|
cv2.imwrite(filepath, frame)
|
||||||
|
|
||||||
if image_file:
|
|
||||||
get_bus().post(CameraPictureTakenEvent(filename=image_file))
|
|
||||||
return filepath
|
return filepath
|
||||||
|
|
||||||
|
|
||||||
|
@ -189,7 +185,8 @@ class CameraPlugin(Plugin):
|
||||||
for f in files:
|
for f in files:
|
||||||
video.write(cv2.imread(f))
|
video.write(cv2.imread(f))
|
||||||
video.release()
|
video.release()
|
||||||
get_bus().post(CameraVideoRenderedEvent(filename=video_file))
|
|
||||||
|
self.fire_event(CameraVideoRenderedEvent(filename=video_file))
|
||||||
shutil.rmtree(frames_dir, ignore_errors=True)
|
shutil.rmtree(frames_dir, ignore_errors=True)
|
||||||
|
|
||||||
|
|
||||||
|
@ -215,7 +212,7 @@ class CameraPlugin(Plugin):
|
||||||
if frames_dir:
|
if frames_dir:
|
||||||
evt_args['frames_dir'] = frames_dir
|
evt_args['frames_dir'] = frames_dir
|
||||||
|
|
||||||
get_bus().post(CameraRecordingStartedEvent(**evt_args))
|
self.fire_event(CameraRecordingStartedEvent(**evt_args))
|
||||||
|
|
||||||
while self._is_recording[device_id].is_set():
|
while self._is_recording[device_id].is_set():
|
||||||
if duration and time.time() - recording_started_time >= duration \
|
if duration and time.time() - recording_started_time >= duration \
|
||||||
|
@ -241,6 +238,10 @@ class CameraPlugin(Plugin):
|
||||||
time.sleep(sleep_between_frames)
|
time.sleep(sleep_between_frames)
|
||||||
|
|
||||||
self._release_device(device_id, wait_thread_termination=False)
|
self._release_device(device_id, wait_thread_termination=False)
|
||||||
|
|
||||||
|
if image_file:
|
||||||
|
self.fire_event(CameraPictureTakenEvent(filename=image_file))
|
||||||
|
|
||||||
self.logger.info('Recording terminated')
|
self.logger.info('Recording terminated')
|
||||||
|
|
||||||
if video_file:
|
if video_file:
|
||||||
|
@ -273,6 +274,10 @@ class CameraPlugin(Plugin):
|
||||||
these parameters if you want to override the default configured ones.
|
these parameters if you want to override the default configured ones.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
recording_started = threading.Event()
|
||||||
|
def on_recording_started(event):
|
||||||
|
recording_started.set()
|
||||||
|
|
||||||
device_id = device_id if device_id is not None else self.default_device_id
|
device_id = device_id if device_id is not None else self.default_device_id
|
||||||
frames_dir = os.path.abspath(os.path.expanduser(frames_dir)) \
|
frames_dir = os.path.abspath(os.path.expanduser(frames_dir)) \
|
||||||
if frames_dir is not None else self.frames_dir
|
if frames_dir is not None else self.frames_dir
|
||||||
|
@ -284,6 +289,7 @@ class CameraPlugin(Plugin):
|
||||||
is not None else self.color_transform
|
is not None else self.color_transform
|
||||||
|
|
||||||
self._init_device(device_id)
|
self._init_device(device_id)
|
||||||
|
self.register_handler(CameraRecordingStartedEvent, on_recording_started)
|
||||||
|
|
||||||
frames_dir = os.path.join(frames_dir, str(device_id))
|
frames_dir = os.path.join(frames_dir, str(device_id))
|
||||||
if video_file:
|
if video_file:
|
||||||
|
@ -302,6 +308,9 @@ class CameraPlugin(Plugin):
|
||||||
|
|
||||||
self._recording_threads[device_id].start()
|
self._recording_threads[device_id].start()
|
||||||
self._is_recording[device_id].set()
|
self._is_recording[device_id].set()
|
||||||
|
|
||||||
|
recording_started.wait()
|
||||||
|
self.unregister_handler(CameraRecordingStartedEvent, on_recording_started)
|
||||||
return { 'path': video_file if video_file else frames_dir }
|
return { 'path': video_file if video_file else frames_dir }
|
||||||
|
|
||||||
@action
|
@action
|
||||||
|
@ -325,6 +334,10 @@ class CameraPlugin(Plugin):
|
||||||
:param device_id, warmup_frames, color_transform: Overrides the configured default parameters
|
:param device_id, warmup_frames, color_transform: Overrides the configured default parameters
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
picture_taken = threading.Event()
|
||||||
|
def on_picture_taken(event):
|
||||||
|
picture_taken.set()
|
||||||
|
|
||||||
image_file = os.path.abspath(os.path.expanduser(image_file))
|
image_file = os.path.abspath(os.path.expanduser(image_file))
|
||||||
device_id = device_id if device_id is not None else self.default_device_id
|
device_id = device_id if device_id is not None else self.default_device_id
|
||||||
warmup_frames = warmup_frames if warmup_frames is not None else \
|
warmup_frames = warmup_frames if warmup_frames is not None else \
|
||||||
|
@ -333,6 +346,7 @@ class CameraPlugin(Plugin):
|
||||||
is not None else self.color_transform
|
is not None else self.color_transform
|
||||||
|
|
||||||
self._init_device(device_id)
|
self._init_device(device_id)
|
||||||
|
self.register_handler(CameraPictureTakenEvent, on_picture_taken)
|
||||||
self._recording_threads[device_id] = threading.Thread(
|
self._recording_threads[device_id] = threading.Thread(
|
||||||
target=self._recording_thread(duration=None, video_file=None,
|
target=self._recording_thread(duration=None, video_file=None,
|
||||||
image_file=image_file,
|
image_file=image_file,
|
||||||
|
@ -344,6 +358,9 @@ class CameraPlugin(Plugin):
|
||||||
|
|
||||||
self._recording_threads[device_id].start()
|
self._recording_threads[device_id].start()
|
||||||
self._is_recording[device_id].set()
|
self._is_recording[device_id].set()
|
||||||
|
|
||||||
|
picture_taken.wait()
|
||||||
|
self.unregister_handler(CameraPictureTakenEvent, on_picture_taken)
|
||||||
return { 'path': image_file }
|
return { 'path': image_file }
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue