Use LabThings ClientEvent

This commit is contained in:
Joel Collins 2020-03-27 15:07:11 +00:00
parent 9e4934c054
commit 43c8fea37f

View file

@ -3,16 +3,18 @@ import time
import os import os
import shutil import shutil
import threading import threading
import gevent.event
import datetime import datetime
import logging import logging
import threading
from abc import ABCMeta, abstractmethod from abc import ABCMeta, abstractmethod
from collections import OrderedDict from collections import OrderedDict
from .capture import CaptureObject, build_captures_from_exif from .capture import CaptureObject, build_captures_from_exif
from openflexure_microscope.utilities import entry_by_uuid from openflexure_microscope.utilities import entry_by_uuid
from labthings.core.lock import StrictLock from labthings.core.lock import StrictLock
from labthings.core.event import ClientEvent
from openflexure_microscope.paths import data_file_path from openflexure_microscope.paths import data_file_path
@ -45,51 +47,6 @@ def generate_numbered_basename(obj_list: list) -> str:
return basename return basename
class CameraEvent(object):
"""
A frame-signaller object used by any instances or subclasses of BaseCamera.
An event-like class that signals all active clients when a new frame is available.
"""
def __init__(self):
self.events = {}
def wait(self, timeout: int = 5):
"""Wait for the next frame (invoked from each client's thread)."""
ident = threading.get_ident()
if ident not in self.events:
# this is a new client
# add an entry for it in the self.events dict
# each entry has two elements, a threading.Event() and a timestamp
self.events[ident] = [gevent.event.Event(), time.time()]
return self.events[ident][0].wait(timeout)
def set(self):
"""Signal that a new frame is available."""
now = time.time()
remove = None
for ident, event in self.events.items():
if not event[0].isSet():
# if this client's event is not set, then set it
# also update the last set timestamp to now
event[0].set()
event[1] = now
else:
# if the client's event is already set, it means the client
# did not process a previous frame
# if the event stays set for more than 5 seconds, then assume
# the client is gone and remove it
if now - event[1] > 5:
remove = ident
if remove:
del self.events[remove]
def clear(self):
"""Clear frame event, once processed."""
self.events[threading.get_ident()][0].clear()
class BaseCamera(metaclass=ABCMeta): class BaseCamera(metaclass=ABCMeta):
""" """
Base implementation of StreamingCamera. Base implementation of StreamingCamera.
@ -99,11 +56,11 @@ class BaseCamera(metaclass=ABCMeta):
self.thread = None self.thread = None
self.camera = None self.camera = None
self.lock = StrictLock(timeout=1) self.lock = StrictLock(timeout=1, name="Camera")
self.frame = None self.frame = None
self.last_access = 0 self.last_access = 0
self.event = CameraEvent() self.event = ClientEvent()
self.stop = False # Used to indicate that the stream loop should break self.stop = False # Used to indicate that the stream loop should break
self.stream_timeout = 20 self.stream_timeout = 20
@ -307,6 +264,7 @@ class BaseCamera(metaclass=ABCMeta):
self.stop = False self.stop = False
if not self.stream_active: if not self.stream_active:
# Spawn a greenlet to handle stream
# start background frame thread # start background frame thread
self.thread = threading.Thread(target=self._thread) self.thread = threading.Thread(target=self._thread)
self.thread.daemon = True self.thread.daemon = True
@ -368,7 +326,6 @@ class BaseCamera(metaclass=ABCMeta):
for frame in self.frames_iterator: for frame in self.frames_iterator:
self.frame = frame self.frame = frame
self.event.set() # send signal to clients self.event.set() # send signal to clients
time.sleep(0)
# Handle timeout # Handle timeout
if ( if (