Draft of new semantics

This commit is contained in:
Joel Collins 2020-01-29 11:55:39 +00:00
parent b39a0653f2
commit 19af98736d
13 changed files with 252 additions and 187 deletions

View file

@ -121,7 +121,8 @@ class BaseCamera(metaclass=ABCMeta):
self.stream_timeout = 20
self.stream_timeout_enabled = False
self.status = {"board": None}
self.stream_active = False
self.record_active = False
self.paths = {"default": BASE_CAPTURE_PATH, "temp": TEMP_CAPTURE_PATH}
@ -129,8 +130,24 @@ class BaseCamera(metaclass=ABCMeta):
self.images = []
self.videos = []
@property
@abstractmethod
def apply_settings(self, config: dict):
def configuration(self):
"""The current camera configuration."""
pass
@property
@abstractmethod
def state(self):
"""The current read-only camera state."""
pass
@property
def settings(self):
return self.read_settings()
@abstractmethod
def update_settings(self, config: dict):
"""Update settings from a config dictionary"""
with self.lock:
# Apply valid config params to camera object
@ -187,7 +204,7 @@ class BaseCamera(metaclass=ABCMeta):
self.last_access = time.time()
self.stop = False
if not self.status["stream_active"]:
if not self.stream_active:
# start background frame thread
self.thread = threading.Thread(target=self._thread)
self.thread.daemon = True
@ -207,12 +224,12 @@ class BaseCamera(metaclass=ABCMeta):
logging.debug("Stopping worker thread")
timeout_time = time.time() + timeout
if self.status["stream_active"]:
if self.stream_active:
self.stop = True
self.thread.join() # Wait for stream thread to exit
logging.debug("Waiting for stream thread to exit.")
while self.status["stream_active"]:
while self.stream_active:
if time.time() > timeout_time:
logging.debug("Timeout waiting for worker thread close.")
raise TimeoutError("Timeout waiting for worker thread close.")
@ -352,7 +369,7 @@ class BaseCamera(metaclass=ABCMeta):
self.frames_iterator = self.frames()
logging.debug("Entering worker thread.")
self.status["stream_active"] = True
self.stream_active = True
for frame in self.frames_iterator:
self.frame = frame
@ -365,9 +382,7 @@ class BaseCamera(metaclass=ABCMeta):
and ( # If using timeout
time.time() - self.last_access > self.stream_timeout
)
and not self.status[ # And timeout time
"preview_active"
] # And GPU preview is not active
and not self.preview_active # And GPU preview is not active
):
self.frames_iterator.close()
break
@ -383,4 +398,4 @@ class BaseCamera(metaclass=ABCMeta):
logging.debug("BaseCamera worker thread exiting...")
# Set stream_activate state
self.status["stream_active"] = False
self.stream_active = False

View file

@ -26,17 +26,13 @@ We override the logging settings in api.app by setting a level for PIL here.
pil_logger = logging.getLogger("PIL")
pil_logger.setLevel(logging.INFO)
# MAIN CLASS
class MockStreamer(BaseCamera):
class MissingCamera(BaseCamera):
def __init__(self):
# Run BaseCamera init
BaseCamera.__init__(self)
# Store state of PiCameraStreamer
self.status.update(
{"stream_active": False, "record_active": False, "board": None}
)
# Update config properties
self.image_resolution = (1312, 976)
self.stream_resolution = (640, 480)
@ -71,6 +67,16 @@ class MockStreamer(BaseCamera):
image.save(self.stream, format="JPEG")
@property
def configuration(self):
"""The current camera configuration."""
return {}
@property
def state(self):
"""The current read-only camera state."""
return {}
def initialisation(self):
"""Run any initialisation code when the frame iterator starts."""
pass
@ -101,7 +107,7 @@ class MockStreamer(BaseCamera):
return conf_dict
def apply_settings(self, config: dict):
def update_settings(self, config: dict):
"""
Write a config dictionary to the PiCameraStreamer config.
@ -117,7 +123,7 @@ class MockStreamer(BaseCamera):
with self.lock:
# Apply valid config params to camera object
if not self.status["record_active"]: # If not recording a video
if not self.record_active: # If not recording a video
for key, value in config.items(): # For each provided setting
if hasattr(self, key):

View file

@ -47,6 +47,7 @@ from .base import BaseCamera, CaptureObject
from .set_picamera_gain import set_analog_gain, set_digital_gain
from openflexure_microscope.paths import settings_file_path
from openflexure_microscope.utilities import serialise_array_b64
# MAIN CLASS
@ -85,14 +86,7 @@ class PiCameraStreamer(BaseCamera):
) #: :py:class:`picamera.PiCamera`: Picamera object
# Store status of PiCameraStreamer
self.status.update(
{
"stream_active": False,
"record_active": False,
"preview_active": False,
"board": f"picamera_{self.camera.revision}",
}
)
self.preview_active = False
# Reset variable states
self.set_zoom(1.0)
@ -116,15 +110,37 @@ class PiCameraStreamer(BaseCamera):
"picamera_lst.npy"
) #: str: Path of .npy lens shading table file
# Update board identifier
self.status.update({})
# Create an empty stream
self.stream = io.BytesIO()
# Start streaming
self.start_worker()
@property
def configuration(self):
"""The current camera configuration."""
config = {
"board": self.camera.revision,
}
if self.read_lens_shading_table():
b64_string, dtype, shape = serialise_array_b64(self.read_lens_shading_table())
config.update({
"lens_shading_table": {
"b64_string": b64_string,
"dtype": dtype,
"shape": shape,
}
})
return config
@property
def state(self):
"""The current read-only camera state."""
return {}
def initialisation(self):
"""Run any initialisation code when the frame iterator starts."""
pass
@ -176,7 +192,7 @@ class PiCameraStreamer(BaseCamera):
logging.info("Saving picamera_lst to {}".format(self.picamera_lst_path))
self.save_lens_shading_table()
def apply_settings(self, config: dict):
def update_settings(self, config: dict):
"""
Write a config dictionary to the PiCameraStreamer config.
@ -194,10 +210,10 @@ class PiCameraStreamer(BaseCamera):
with self.lock:
# Apply valid config params to Picamera object
if not self.status["record_active"]: # If not recording a video
if not self.record_active: # If not recording a video
# Pause stream while changing settings
if self.status["stream_active"]: # If stream is active
if self.stream_active: # If stream is active
logging.info("Pausing stream to update config.")
self.stop_stream_recording() # Pause stream
paused_stream = True # Remember to unpause stream when done
@ -365,7 +381,7 @@ class PiCameraStreamer(BaseCamera):
self.camera.preview.window = window
if fullscreen:
self.camera.preview.fullscreen = fullscreen
self.status["preview_active"] = True
self.preview_active = True
except picamera.exc.PiCameraMMALError as e:
logging.error(
"Suppressed a MMALError in start_preview. Exception: {}".format(e)
@ -380,7 +396,7 @@ class PiCameraStreamer(BaseCamera):
def stop_preview(self):
"""Stop the on board GPU camera preview."""
self.camera.stop_preview()
self.status["preview_active"] = False
self.preview_active = False
def start_recording(self, output, fmt: str = "h264", quality: int = 15):
"""Start recording.
@ -398,7 +414,7 @@ class PiCameraStreamer(BaseCamera):
"""
with self.lock:
# Start recording method only if a current recording is not running
if not self.status["record_active"]:
if not self.record_active:
# Start the camera video recording on port 2
logging.info("Recording to {}".format(output))
@ -412,7 +428,7 @@ class PiCameraStreamer(BaseCamera):
)
# Update status dictionary
self.status["record_active"] = True
self.record_active = True
return output
@ -432,7 +448,7 @@ class PiCameraStreamer(BaseCamera):
logging.info("Recording stopped")
# Update status dictionary
self.status["record_active"] = False
self.record_active = False
def stop_stream_recording(
self, splitter_port: int = 1, resolution: Tuple[int, int] = None
@ -500,7 +516,7 @@ class PiCameraStreamer(BaseCamera):
self.camera.resolution = resolution
# If the stream should be active
if self.status["stream_active"]:
if self.stream_active:
try:
# Start recording on stream port
self.camera.start_recording(