Implemented root API v2 routes
This commit is contained in:
parent
5aa783c269
commit
6581612312
27 changed files with 1205 additions and 148 deletions
|
|
@ -100,7 +100,7 @@ class BaseCamera(metaclass=ABCMeta):
|
|||
last_access (time): Time of last client access to the camera
|
||||
stream_timeout (int): Number of inactive seconds before timing out the stream
|
||||
stream_timeout_enabled (bool): Enable or disable timing out the stream
|
||||
state (dict): Dictionary for capture state
|
||||
status (dict): Dictionary for capture state
|
||||
paths (dict): Dictionary of capture paths
|
||||
images (list): List of image capture objects
|
||||
videos (list): List of video capture objects
|
||||
|
|
@ -120,7 +120,7 @@ class BaseCamera(metaclass=ABCMeta):
|
|||
self.stream_timeout = 20
|
||||
self.stream_timeout_enabled = False
|
||||
|
||||
self.state = {"board": None}
|
||||
self.status = {"board": None}
|
||||
|
||||
# TODO: Load/save these to config
|
||||
self.paths = {"default": BASE_CAPTURE_PATH, "temp": TEMP_CAPTURE_PATH}
|
||||
|
|
@ -130,7 +130,7 @@ class BaseCamera(metaclass=ABCMeta):
|
|||
self.videos = []
|
||||
|
||||
@abstractmethod
|
||||
def apply_config(self, config: dict):
|
||||
def apply_settings(self, config: dict):
|
||||
"""Update settings from a config dictionary"""
|
||||
with self.lock:
|
||||
# Apply valid config params to camera object
|
||||
|
|
@ -139,11 +139,11 @@ class BaseCamera(metaclass=ABCMeta):
|
|||
setattr(self, key, value) # Set to the target value
|
||||
|
||||
@abstractmethod
|
||||
def read_config(self) -> dict:
|
||||
def read_settings(self) -> dict:
|
||||
"""Return the current settings as a dictionary"""
|
||||
return {"paths": self.paths}
|
||||
|
||||
def save_config(self):
|
||||
def save_settings(self):
|
||||
"""(Optional) Save any settings to disk that need to be stored"""
|
||||
return
|
||||
|
||||
|
|
@ -187,7 +187,7 @@ class BaseCamera(metaclass=ABCMeta):
|
|||
self.last_access = time.time()
|
||||
self.stop = False
|
||||
|
||||
if not self.state["stream_active"]:
|
||||
if not self.status["stream_active"]:
|
||||
# start background frame thread
|
||||
self.thread = threading.Thread(target=self._thread)
|
||||
self.thread.daemon = True
|
||||
|
|
@ -207,12 +207,12 @@ class BaseCamera(metaclass=ABCMeta):
|
|||
logging.debug("Stopping worker thread")
|
||||
timeout_time = time.time() + timeout
|
||||
|
||||
if self.state["stream_active"]:
|
||||
if self.status["stream_active"]:
|
||||
self.stop = True
|
||||
self.thread.join() # Wait for stream thread to exit
|
||||
logging.debug("Waiting for stream thread to exit.")
|
||||
|
||||
while self.state["stream_active"]:
|
||||
while self.status["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 +352,7 @@ class BaseCamera(metaclass=ABCMeta):
|
|||
self.frames_iterator = self.frames()
|
||||
logging.debug("Entering worker thread.")
|
||||
|
||||
self.state["stream_active"] = True
|
||||
self.status["stream_active"] = True
|
||||
|
||||
for frame in self.frames_iterator:
|
||||
self.frame = frame
|
||||
|
|
@ -365,7 +365,7 @@ class BaseCamera(metaclass=ABCMeta):
|
|||
and ( # If using timeout
|
||||
time.time() - self.last_access > self.stream_timeout
|
||||
)
|
||||
and not self.state[ # And timeout time
|
||||
and not self.status[ # And timeout time
|
||||
"preview_active"
|
||||
] # And GPU preview is not active
|
||||
):
|
||||
|
|
@ -383,4 +383,4 @@ class BaseCamera(metaclass=ABCMeta):
|
|||
|
||||
logging.debug("BaseCamera worker thread exiting...")
|
||||
# Set stream_activate state
|
||||
self.state["stream_active"] = False
|
||||
self.status["stream_active"] = False
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ def capture_from_exif(path, exif_dict):
|
|||
capture.timestring = exif_dict["time"]
|
||||
capture.format = exif_dict["format"]
|
||||
|
||||
capture._metadata = exif_dict["custom"]
|
||||
capture.custom_metadata = exif_dict["custom"]
|
||||
capture.tags = exif_dict["tags"]
|
||||
|
||||
return capture
|
||||
|
|
@ -110,7 +110,7 @@ class CaptureObject(object):
|
|||
|
||||
Attributes:
|
||||
timestring (str): Timestring of capture creation time
|
||||
_metadata (dict): Dictionary of custom metadata to be included in metadata file
|
||||
custom_metadata (dict): Dictionary of custom metadata to be included in metadata file
|
||||
tags (list): List of tags. Essentially just as extra custom metadata field, but useful for quick organisation
|
||||
filefolder (str): Folder in which the capture file will be stored
|
||||
filename (str): Full name of the capture file
|
||||
|
|
@ -132,7 +132,9 @@ class CaptureObject(object):
|
|||
self.split_file_path(self.file)
|
||||
|
||||
# Dictionary for storing custom metadata
|
||||
self._metadata = {}
|
||||
self.custom_metadata = {}
|
||||
# Dictionary for adding top-level metadata (cannmot be accessed through web API)
|
||||
self.system_metadata = {}
|
||||
|
||||
# List for storing tags
|
||||
self.tags = []
|
||||
|
|
@ -203,7 +205,7 @@ class CaptureObject(object):
|
|||
Args:
|
||||
data (dict): Dictionary of metadata to be added
|
||||
"""
|
||||
self._metadata.update(data)
|
||||
self.custom_metadata.update(data)
|
||||
self.save_metadata()
|
||||
|
||||
def save_metadata(self) -> None:
|
||||
|
|
@ -225,6 +227,7 @@ class CaptureObject(object):
|
|||
# Insert exif into file
|
||||
piexif.insert(exif_bytes, self.file)
|
||||
|
||||
|
||||
@property
|
||||
def metadata(self) -> dict:
|
||||
"""
|
||||
|
|
@ -233,13 +236,14 @@ class CaptureObject(object):
|
|||
"""
|
||||
d = {
|
||||
"id": self.id,
|
||||
"filename": self.filename,
|
||||
"time": self.timestring,
|
||||
"format": self.format,
|
||||
"tags": self.tags,
|
||||
"custom": self._metadata,
|
||||
"custom": self.custom_metadata,
|
||||
}
|
||||
|
||||
d.update(self.system_metadata)
|
||||
|
||||
# Add custom metadata to dictionary
|
||||
return d
|
||||
|
||||
|
|
@ -250,7 +254,7 @@ class CaptureObject(object):
|
|||
"""
|
||||
|
||||
# Create basic state dictionary
|
||||
d = {"path": self.file, "metadata": self.metadata}
|
||||
d = {"path": self.file, "filename": self.filename, "metadata": self.metadata}
|
||||
|
||||
# Combined availability of data
|
||||
if self.exists:
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ class MockStreamer(BaseCamera):
|
|||
BaseCamera.__init__(self)
|
||||
|
||||
# Store state of PiCameraStreamer
|
||||
self.state.update(
|
||||
self.status.update(
|
||||
{"stream_active": False, "record_active": False, "board": None}
|
||||
)
|
||||
|
||||
|
|
@ -74,13 +74,13 @@ class MockStreamer(BaseCamera):
|
|||
BaseCamera.close(self)
|
||||
|
||||
# HANDLE SETTINGS
|
||||
def read_config(self) -> dict:
|
||||
def read_settings(self) -> dict:
|
||||
"""
|
||||
Return config dictionary of the PiCameraStreamer.
|
||||
"""
|
||||
|
||||
# Get config items from the base class
|
||||
conf_dict = BaseCamera.read_config(self)
|
||||
conf_dict = BaseCamera.read_settings(self)
|
||||
|
||||
# Include device-specific config items
|
||||
conf_dict.update(
|
||||
|
|
@ -94,7 +94,7 @@ class MockStreamer(BaseCamera):
|
|||
|
||||
return conf_dict
|
||||
|
||||
def apply_config(self, config: dict):
|
||||
def apply_settings(self, config: dict):
|
||||
"""
|
||||
Write a config dictionary to the PiCameraStreamer config.
|
||||
|
||||
|
|
@ -110,7 +110,7 @@ class MockStreamer(BaseCamera):
|
|||
with self.lock:
|
||||
|
||||
# Apply valid config params to camera object
|
||||
if not self.state["record_active"]: # If not recording a video
|
||||
if not self.status["record_active"]: # If not recording a video
|
||||
|
||||
for key, value in config.items(): # For each provided setting
|
||||
if hasattr(self, key):
|
||||
|
|
|
|||
|
|
@ -75,8 +75,8 @@ class PiCameraStreamer(BaseCamera):
|
|||
picamera.PiCamera()
|
||||
) #: :py:class:`picamera.PiCamera`: Picamera object
|
||||
|
||||
# Store state of PiCameraStreamer
|
||||
self.state.update(
|
||||
# Store status of PiCameraStreamer
|
||||
self.status.update(
|
||||
{
|
||||
"stream_active": False,
|
||||
"record_active": False,
|
||||
|
|
@ -97,7 +97,7 @@ class PiCameraStreamer(BaseCamera):
|
|||
self.picamera_lst_path = settings_file_path("picamera_lst.npy") #: str: Path of .npy lens shading table file
|
||||
|
||||
# Update board identifier
|
||||
self.state.update({})
|
||||
self.status.update({})
|
||||
|
||||
# Create an empty stream
|
||||
self.stream = io.BytesIO()
|
||||
|
|
@ -118,13 +118,13 @@ class PiCameraStreamer(BaseCamera):
|
|||
self.camera.close()
|
||||
|
||||
# HANDLE SETTINGS
|
||||
def read_config(self) -> dict:
|
||||
def read_settings(self) -> dict:
|
||||
"""
|
||||
Return config dictionary of the PiCameraStreamer.
|
||||
"""
|
||||
|
||||
# Get config items from the base class
|
||||
conf_dict = BaseCamera.read_config(self)
|
||||
conf_dict = BaseCamera.read_settings(self)
|
||||
|
||||
# Include device-specific config items
|
||||
conf_dict.update(
|
||||
|
|
@ -149,12 +149,12 @@ class PiCameraStreamer(BaseCamera):
|
|||
|
||||
return conf_dict
|
||||
|
||||
def save_config(self):
|
||||
def save_settings(self):
|
||||
"""Save lens-shading table to disk"""
|
||||
logging.info("Saving picamera_lst to {}".format(self.picamera_lst_path))
|
||||
self.save_lens_shading_table()
|
||||
|
||||
def apply_config(self, config: dict):
|
||||
def apply_settings(self, config: dict):
|
||||
"""
|
||||
Write a config dictionary to the PiCameraStreamer config.
|
||||
|
||||
|
|
@ -172,10 +172,10 @@ class PiCameraStreamer(BaseCamera):
|
|||
with self.lock:
|
||||
|
||||
# Apply valid config params to Picamera object
|
||||
if not self.state["record_active"]: # If not recording a video
|
||||
if not self.status["record_active"]: # If not recording a video
|
||||
|
||||
# Pause stream while changing settings
|
||||
if self.state["stream_active"]: # If stream is active
|
||||
if self.status["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
|
||||
|
|
@ -299,13 +299,13 @@ class PiCameraStreamer(BaseCamera):
|
|||
Change the camera zoom, handling re-centering and scaling.
|
||||
"""
|
||||
with self.lock:
|
||||
self.state["zoom_value"] = float(zoom_value)
|
||||
if self.state["zoom_value"] < 1:
|
||||
self.state["zoom_value"] = 1
|
||||
self.status["zoom_value"] = float(zoom_value)
|
||||
if self.status["zoom_value"] < 1:
|
||||
self.status["zoom_value"] = 1
|
||||
# Richard's code for zooming !
|
||||
fov = self.camera.zoom
|
||||
centre = np.array([fov[0] + fov[2] / 2.0, fov[1] + fov[3] / 2.0])
|
||||
size = 1.0 / self.state["zoom_value"]
|
||||
size = 1.0 / self.status["zoom_value"]
|
||||
# If the new zoom value would be invalid, move the centre to
|
||||
# keep it within the camera's sensor (this is only relevant
|
||||
# when zooming out, if the FoV is not centred on (0.5, 0.5)
|
||||
|
|
@ -332,7 +332,7 @@ class PiCameraStreamer(BaseCamera):
|
|||
self.camera.preview.window = window
|
||||
if fullscreen:
|
||||
self.camera.preview.fullscreen = fullscreen
|
||||
self.state["preview_active"] = True
|
||||
self.status["preview_active"] = True
|
||||
except picamera.exc.PiCameraMMALError as e:
|
||||
logging.error(
|
||||
"Suppressed a MMALError in start_preview. Exception: {}".format(e)
|
||||
|
|
@ -347,7 +347,7 @@ class PiCameraStreamer(BaseCamera):
|
|||
def stop_preview(self):
|
||||
"""Stop the on board GPU camera preview."""
|
||||
self.camera.stop_preview()
|
||||
self.state["preview_active"] = False
|
||||
self.status["preview_active"] = False
|
||||
|
||||
def start_recording(self, output, fmt: str = "h264", quality: int = 15):
|
||||
"""Start recording.
|
||||
|
|
@ -365,7 +365,7 @@ class PiCameraStreamer(BaseCamera):
|
|||
"""
|
||||
with self.lock:
|
||||
# Start recording method only if a current recording is not running
|
||||
if not self.state["record_active"]:
|
||||
if not self.status["record_active"]:
|
||||
|
||||
# Start the camera video recording on port 2
|
||||
logging.info("Recording to {}".format(output))
|
||||
|
|
@ -378,8 +378,8 @@ class PiCameraStreamer(BaseCamera):
|
|||
quality=quality,
|
||||
)
|
||||
|
||||
# Update state dictionary
|
||||
self.state["record_active"] = True
|
||||
# Update status dictionary
|
||||
self.status["record_active"] = True
|
||||
|
||||
return output
|
||||
|
||||
|
|
@ -398,8 +398,8 @@ class PiCameraStreamer(BaseCamera):
|
|||
self.camera.stop_recording(splitter_port=2)
|
||||
logging.info("Recording stopped")
|
||||
|
||||
# Update state dictionary
|
||||
self.state["record_active"] = False
|
||||
# Update status dictionary
|
||||
self.status["record_active"] = False
|
||||
|
||||
def stop_stream_recording(
|
||||
self, splitter_port: int = 1, resolution: Tuple[int, int] = None
|
||||
|
|
@ -467,7 +467,7 @@ class PiCameraStreamer(BaseCamera):
|
|||
self.camera.resolution = resolution
|
||||
|
||||
# If the stream should be active
|
||||
if self.state["stream_active"]:
|
||||
if self.status["stream_active"]:
|
||||
try:
|
||||
# Start recording on stream port
|
||||
self.camera.start_recording(
|
||||
|
|
@ -633,7 +633,7 @@ class PiCameraStreamer(BaseCamera):
|
|||
# Start stream recording (and set resolution)
|
||||
self.start_stream_recording()
|
||||
|
||||
# Update state
|
||||
# Update status
|
||||
logging.debug("STREAM ACTIVE")
|
||||
|
||||
# While the iterator is not closed
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue