Blackened
This commit is contained in:
parent
0907d013cd
commit
76af0891e9
14 changed files with 95 additions and 63 deletions
|
|
@ -132,11 +132,11 @@ class ListAPI(MicroscopeView):
|
||||||
|
|
||||||
# Inject system metadata
|
# Inject system metadata
|
||||||
system_metadata = {
|
system_metadata = {
|
||||||
"microscope_settings": self.microscope.read_settings(),
|
"microscope_settings": self.microscope.read_settings(),
|
||||||
"microscope_state": self.microscope.state,
|
"microscope_state": self.microscope.state,
|
||||||
"microscope_id": self.microscope.id,
|
"microscope_id": self.microscope.id,
|
||||||
"microscope_name": self.microscope.name,
|
"microscope_name": self.microscope.name,
|
||||||
}
|
}
|
||||||
output.system_metadata.update(system_metadata)
|
output.system_metadata.update(system_metadata)
|
||||||
|
|
||||||
# Insert custom metadata
|
# Insert custom metadata
|
||||||
|
|
|
||||||
|
|
@ -8,38 +8,38 @@ _actions = {
|
||||||
"rule": "/camera/capture/",
|
"rule": "/camera/capture/",
|
||||||
"view_class": camera.CaptureAPI,
|
"view_class": camera.CaptureAPI,
|
||||||
"description": "Take a single still capture",
|
"description": "Take a single still capture",
|
||||||
"conditions": True
|
"conditions": True,
|
||||||
},
|
},
|
||||||
"preview_start": {
|
"preview_start": {
|
||||||
"rule": "/camera/preview/start",
|
"rule": "/camera/preview/start",
|
||||||
"view_class": camera.GPUPreviewStartAPI,
|
"view_class": camera.GPUPreviewStartAPI,
|
||||||
"description": "Start the on-board GPU preview",
|
"description": "Start the on-board GPU preview",
|
||||||
"conditions": True
|
"conditions": True,
|
||||||
},
|
},
|
||||||
"preview_stop": {
|
"preview_stop": {
|
||||||
"rule": "/camera/preview/stop",
|
"rule": "/camera/preview/stop",
|
||||||
"view_class": camera.GPUPreviewStopAPI,
|
"view_class": camera.GPUPreviewStopAPI,
|
||||||
"description": "Stop the on-board GPU preview",
|
"description": "Stop the on-board GPU preview",
|
||||||
"conditions": True
|
"conditions": True,
|
||||||
},
|
},
|
||||||
"move": {
|
"move": {
|
||||||
"rule": "/stage/move/",
|
"rule": "/stage/move/",
|
||||||
"view_class": stage.MoveStageAPI,
|
"view_class": stage.MoveStageAPI,
|
||||||
"description": "Move the microscope stage",
|
"description": "Move the microscope stage",
|
||||||
"conditions": True
|
"conditions": True,
|
||||||
},
|
},
|
||||||
"shutdown": {
|
"shutdown": {
|
||||||
"rule": "/system/shutdown/",
|
"rule": "/system/shutdown/",
|
||||||
"view_class": system.ShutdownAPI,
|
"view_class": system.ShutdownAPI,
|
||||||
"description": "Shutdown the device",
|
"description": "Shutdown the device",
|
||||||
"conditions": (platform == "linux")
|
"conditions": (platform == "linux"),
|
||||||
},
|
},
|
||||||
"reboot": {
|
"reboot": {
|
||||||
"rule": "/system/reboot/",
|
"rule": "/system/reboot/",
|
||||||
"view_class": system.RebootAPI,
|
"view_class": system.RebootAPI,
|
||||||
"description": "Reboot the device",
|
"description": "Reboot the device",
|
||||||
"conditions": (platform == "linux")
|
"conditions": (platform == "linux"),
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -57,7 +57,7 @@ def actions_representation():
|
||||||
"links": {"self": url_for(f".{name}")},
|
"links": {"self": url_for(f".{name}")},
|
||||||
"description": action["description"],
|
"description": action["description"],
|
||||||
"rule": action["rule"],
|
"rule": action["rule"],
|
||||||
"view_class": str(action["view_class"])
|
"view_class": str(action["view_class"]),
|
||||||
}
|
}
|
||||||
|
|
||||||
actions[name] = d
|
actions[name] = d
|
||||||
|
|
|
||||||
|
|
@ -84,11 +84,11 @@ class CaptureAPI(MicroscopeView):
|
||||||
|
|
||||||
# Inject system metadata
|
# Inject system metadata
|
||||||
system_metadata = {
|
system_metadata = {
|
||||||
"microscope_settings": self.microscope.read_settings(),
|
"microscope_settings": self.microscope.read_settings(),
|
||||||
"microscope_state": self.microscope.state,
|
"microscope_state": self.microscope.state,
|
||||||
"microscope_id": self.microscope.id,
|
"microscope_id": self.microscope.id,
|
||||||
"microscope_name": self.microscope.name,
|
"microscope_name": self.microscope.name,
|
||||||
}
|
}
|
||||||
output.system_metadata.update(system_metadata)
|
output.system_metadata.update(system_metadata)
|
||||||
|
|
||||||
# Insert custom metadata
|
# Insert custom metadata
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ class ShutdownAPI(MicroscopeView):
|
||||||
.. :quickref: Actions; Shutdown
|
.. :quickref: Actions; Shutdown
|
||||||
|
|
||||||
"""
|
"""
|
||||||
subprocess.Popen(['shutdown', '-h', 'now'])
|
subprocess.Popen(["shutdown", "-h", "now"])
|
||||||
|
|
||||||
return "{}", 201
|
return "{}", 201
|
||||||
|
|
||||||
|
|
@ -24,6 +24,6 @@ class RebootAPI(MicroscopeView):
|
||||||
.. :quickref: Actions; Shutdown
|
.. :quickref: Actions; Shutdown
|
||||||
|
|
||||||
"""
|
"""
|
||||||
subprocess.Popen(['sudo', 'shutdown', '-r', 'now'])
|
subprocess.Popen(["sudo", "shutdown", "-r", "now"])
|
||||||
|
|
||||||
return "{}", 201
|
return "{}", 201
|
||||||
|
|
|
||||||
|
|
@ -20,14 +20,22 @@ def captures_representation(capture_list: list, include_unavailable: bool = Fals
|
||||||
if include_unavailable:
|
if include_unavailable:
|
||||||
captures = {image.id: image.state for image in capture_list}
|
captures = {image.id: image.state for image in capture_list}
|
||||||
else:
|
else:
|
||||||
captures = {image.id: image.state for image in capture_list if image.state["available"]}
|
captures = {
|
||||||
|
image.id: image.state for image in capture_list if image.state["available"]
|
||||||
|
}
|
||||||
|
|
||||||
for capture_key, capture_repr in captures.items():
|
for capture_key, capture_repr in captures.items():
|
||||||
# Add API routes to returned representations
|
# Add API routes to returned representations
|
||||||
extra_state = {
|
extra_state = {
|
||||||
"links": {
|
"links": {
|
||||||
"self": "{}".format(url_for(".capture", capture_id=capture_key)),
|
"self": "{}".format(url_for(".capture", capture_id=capture_key)),
|
||||||
"download": "{}".format(url_for(".capture_download", capture_id=capture_key, filename=capture_repr["filename"])),
|
"download": "{}".format(
|
||||||
|
url_for(
|
||||||
|
".capture_download",
|
||||||
|
capture_id=capture_key,
|
||||||
|
filename=capture_repr["filename"],
|
||||||
|
)
|
||||||
|
),
|
||||||
"tags": "{}".format(url_for(".capture_tags", capture_id=capture_key)),
|
"tags": "{}".format(url_for(".capture_tags", capture_id=capture_key)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -131,7 +139,9 @@ class CaptureAPI(MicroscopeView):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
representation = captures_representation(self.microscope.camera.images)[capture_id]
|
representation = captures_representation(self.microscope.camera.images)[
|
||||||
|
capture_id
|
||||||
|
]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return abort(404) # 404 Not Found
|
return abort(404) # 404 Not Found
|
||||||
|
|
||||||
|
|
@ -379,9 +389,7 @@ def construct_blueprint(microscope_obj):
|
||||||
# Capture routes
|
# Capture routes
|
||||||
blueprint.add_url_rule(
|
blueprint.add_url_rule(
|
||||||
"/<capture_id>/download/<filename>",
|
"/<capture_id>/download/<filename>",
|
||||||
view_func=DownloadAPI.as_view(
|
view_func=DownloadAPI.as_view("capture_download", microscope=microscope_obj),
|
||||||
"capture_download", microscope=microscope_obj
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
blueprint.add_url_rule(
|
blueprint.add_url_rule(
|
||||||
|
|
@ -397,7 +405,6 @@ def construct_blueprint(microscope_obj):
|
||||||
)
|
)
|
||||||
|
|
||||||
blueprint.add_url_rule(
|
blueprint.add_url_rule(
|
||||||
"/",
|
"/", view_func=ListAPI.as_view("capture_list", microscope=microscope_obj)
|
||||||
view_func=ListAPI.as_view("capture_list", microscope=microscope_obj),
|
|
||||||
)
|
)
|
||||||
return blueprint
|
return blueprint
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ def plugins_representation(plugin_loader_object: PluginLoader):
|
||||||
"name": plugin["name"],
|
"name": plugin["name"],
|
||||||
"plugin": str(plugin["plugin"]),
|
"plugin": str(plugin["plugin"]),
|
||||||
"routes": plugin["routes"],
|
"routes": plugin["routes"],
|
||||||
"form": plugin["form"]
|
"form": plugin["form"],
|
||||||
}
|
}
|
||||||
plugins.append(d)
|
plugins.append(d)
|
||||||
|
|
||||||
|
|
@ -55,8 +55,7 @@ def construct_blueprint(microscope_obj):
|
||||||
|
|
||||||
# Create a base route to return plugin API forms, if any exist
|
# Create a base route to return plugin API forms, if any exist
|
||||||
blueprint.add_url_rule(
|
blueprint.add_url_rule(
|
||||||
"/",
|
"/", view_func=PluginFormAPI.as_view("plugins", microscope=microscope_obj)
|
||||||
view_func=PluginFormAPI.as_view("plugins", microscope=microscope_obj),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
all_routes = []
|
all_routes = []
|
||||||
|
|
|
||||||
|
|
@ -16,9 +16,7 @@ def tasks_representation():
|
||||||
for task_key, task_repr in tasks_dict.items():
|
for task_key, task_repr in tasks_dict.items():
|
||||||
# Add API routes to returned representations
|
# Add API routes to returned representations
|
||||||
extra_state = {
|
extra_state = {
|
||||||
"links": {
|
"links": {"self": "{}".format(url_for(".task", task_id=task_key))}
|
||||||
"self": "{}".format(url_for(".task", task_id=task_key)),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks_dict[task_key].update(extra_state)
|
tasks_dict[task_key].update(extra_state)
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,9 @@ def pull_usercomment_dict(filepath):
|
||||||
return json.loads(exif_dict["Exif"][37510].decode())
|
return json.loads(exif_dict["Exif"][37510].decode())
|
||||||
except json.decoder.JSONDecodeError:
|
except json.decoder.JSONDecodeError:
|
||||||
# TODO: Remove YAML support in a later version
|
# TODO: Remove YAML support in a later version
|
||||||
logging.warning(f"Capture {filepath} has metadata stored in YAML format. This is now deprecated in favour of JSON.")
|
logging.warning(
|
||||||
|
f"Capture {filepath} has metadata stored in YAML format. This is now deprecated in favour of JSON."
|
||||||
|
)
|
||||||
return yaml.load(exif_dict["Exif"][37510].decode())
|
return yaml.load(exif_dict["Exif"][37510].decode())
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
@ -227,7 +229,6 @@ class CaptureObject(object):
|
||||||
# Insert exif into file
|
# Insert exif into file
|
||||||
piexif.insert(exif_bytes, self.file)
|
piexif.insert(exif_bytes, self.file)
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def metadata(self) -> dict:
|
def metadata(self) -> dict:
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ class PiCameraStreamer(BaseCamera):
|
||||||
"sharpness",
|
"sharpness",
|
||||||
"annotate_text",
|
"annotate_text",
|
||||||
"annotate_text_size",
|
"annotate_text_size",
|
||||||
"zoom"
|
"zoom",
|
||||||
]
|
]
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
@ -93,13 +93,23 @@ class PiCameraStreamer(BaseCamera):
|
||||||
self.set_zoom(1.0)
|
self.set_zoom(1.0)
|
||||||
|
|
||||||
# Set default settings
|
# Set default settings
|
||||||
self.image_resolution = tuple(self.camera.MAX_RESOLUTION) #: tuple: Resolution for image captures
|
self.image_resolution = tuple(
|
||||||
self.stream_resolution = (832, 624) #: tuple: Resolution for stream and video captures
|
self.camera.MAX_RESOLUTION
|
||||||
self.numpy_resolution = (1312, 976) #: tuple: Resolution for numpy array captures
|
) #: tuple: Resolution for image captures
|
||||||
|
self.stream_resolution = (
|
||||||
|
832,
|
||||||
|
624,
|
||||||
|
) #: tuple: Resolution for stream and video captures
|
||||||
|
self.numpy_resolution = (
|
||||||
|
1312,
|
||||||
|
976,
|
||||||
|
) #: tuple: Resolution for numpy array captures
|
||||||
self.jpeg_quality = 75 #: int: JPEG quality
|
self.jpeg_quality = 75 #: int: JPEG quality
|
||||||
|
|
||||||
# Set default lens shading table path
|
# Set default lens shading table path
|
||||||
self.picamera_lst_path = settings_file_path("picamera_lst.npy") #: str: Path of .npy lens shading table file
|
self.picamera_lst_path = settings_file_path(
|
||||||
|
"picamera_lst.npy"
|
||||||
|
) #: str: Path of .npy lens shading table file
|
||||||
|
|
||||||
# Update board identifier
|
# Update board identifier
|
||||||
self.status.update({})
|
self.status.update({})
|
||||||
|
|
@ -138,7 +148,9 @@ class PiCameraStreamer(BaseCamera):
|
||||||
"image_resolution": self.image_resolution,
|
"image_resolution": self.image_resolution,
|
||||||
"numpy_resolution": self.numpy_resolution,
|
"numpy_resolution": self.numpy_resolution,
|
||||||
"jpeg_quality": self.jpeg_quality,
|
"jpeg_quality": self.jpeg_quality,
|
||||||
"picamera_lst_path": self.picamera_lst_path if os.path.isfile(self.picamera_lst_path) else None,
|
"picamera_lst_path": self.picamera_lst_path
|
||||||
|
if os.path.isfile(self.picamera_lst_path)
|
||||||
|
else None,
|
||||||
"picamera_settings": {},
|
"picamera_settings": {},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -273,7 +285,7 @@ class PiCameraStreamer(BaseCamera):
|
||||||
"""
|
"""
|
||||||
Read the current lens shading table as a numpy array, if it exists. Return None otherwise.
|
Read the current lens shading table as a numpy array, if it exists. Return None otherwise.
|
||||||
"""
|
"""
|
||||||
if hasattr(self.camera, 'lens_shading_table'):
|
if hasattr(self.camera, "lens_shading_table"):
|
||||||
return self.camera.lens_shading_table
|
return self.camera.lens_shading_table
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
@ -297,13 +309,17 @@ class PiCameraStreamer(BaseCamera):
|
||||||
elif (type(lst_array_or_path) == str) and os.path.isfile(lst_array_or_path):
|
elif (type(lst_array_or_path) == str) and os.path.isfile(lst_array_or_path):
|
||||||
self.camera.lens_shading_table = np.load(lst_array_or_path)
|
self.camera.lens_shading_table = np.load(lst_array_or_path)
|
||||||
else:
|
else:
|
||||||
logging.error("Unsupported or missing data for camera lens_shading_table. Must be numpy ndarray, or .npy file path string. Skipping.")
|
logging.error(
|
||||||
|
"Unsupported or missing data for camera lens_shading_table. Must be numpy ndarray, or .npy file path string. Skipping."
|
||||||
|
)
|
||||||
|
|
||||||
def set_zoom(self, zoom_value: float = 1.0) -> None:
|
def set_zoom(self, zoom_value: float = 1.0) -> None:
|
||||||
"""
|
"""
|
||||||
Change the camera zoom, handling re-centering and scaling.
|
Change the camera zoom, handling re-centering and scaling.
|
||||||
"""
|
"""
|
||||||
logging.warning("set_zoom is deprecated. Please use the 'zoom' property in picamera_settings.")
|
logging.warning(
|
||||||
|
"set_zoom is deprecated. Please use the 'zoom' property in picamera_settings."
|
||||||
|
)
|
||||||
with self.lock:
|
with self.lock:
|
||||||
self.status["zoom_value"] = float(zoom_value)
|
self.status["zoom_value"] = float(zoom_value)
|
||||||
if self.status["zoom_value"] < 1:
|
if self.status["zoom_value"] < 1:
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ class JSONEncoder(json.JSONEncoder):
|
||||||
"""
|
"""
|
||||||
A custom JSON encoder, with type conversions for PiCamera fractions, Numpy integers, and Numpy arrays
|
A custom JSON encoder, with type conversions for PiCamera fractions, Numpy integers, and Numpy arrays
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def default(self, o, markers=None):
|
def default(self, o, markers=None):
|
||||||
# PiCamera fractions
|
# PiCamera fractions
|
||||||
if isinstance(o, Fraction):
|
if isinstance(o, Fraction):
|
||||||
|
|
@ -39,6 +40,7 @@ class JSONEncoder(json.JSONEncoder):
|
||||||
|
|
||||||
# MAIN CONFIG CLASS
|
# MAIN CONFIG CLASS
|
||||||
|
|
||||||
|
|
||||||
class OpenflexureSettingsFile:
|
class OpenflexureSettingsFile:
|
||||||
"""
|
"""
|
||||||
An object to handle expansion, conversion, and saving of the microscope configuration.
|
An object to handle expansion, conversion, and saving of the microscope configuration.
|
||||||
|
|
@ -164,6 +166,7 @@ class OpenflexureSettingsFile:
|
||||||
|
|
||||||
# HANDLE BASIC LOADING AND SAVING OF SETTINGS FILES
|
# HANDLE BASIC LOADING AND SAVING OF SETTINGS FILES
|
||||||
|
|
||||||
|
|
||||||
def load_json_file(config_path) -> dict:
|
def load_json_file(config_path) -> dict:
|
||||||
"""
|
"""
|
||||||
Open a .json config file
|
Open a .json config file
|
||||||
|
|
|
||||||
|
|
@ -160,7 +160,9 @@ class Microscope:
|
||||||
and :py:attr:`openflexure_microscope.camera.base.BaseCamera.status`
|
and :py:attr:`openflexure_microscope.camera.base.BaseCamera.status`
|
||||||
"""
|
"""
|
||||||
# DEPRECATED
|
# DEPRECATED
|
||||||
logging.warning("Microscope.state is deprecated. Use Microscope.status instead. State will be removed in a future version.")
|
logging.warning(
|
||||||
|
"Microscope.state is deprecated. Use Microscope.status instead. State will be removed in a future version."
|
||||||
|
)
|
||||||
state = {
|
state = {
|
||||||
"camera": self.camera.status,
|
"camera": self.camera.status,
|
||||||
"stage": self.stage.status,
|
"stage": self.stage.status,
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ class AutofocusAPI(MicroscopeViewPlugin):
|
||||||
return jsonify(task.state), 202
|
return jsonify(task.state), 202
|
||||||
|
|
||||||
else:
|
else:
|
||||||
abort(503, 'No stage connected. Unable to autofocus.')
|
abort(503, "No stage connected. Unable to autofocus.")
|
||||||
|
|
||||||
|
|
||||||
class FastAutofocusAPI(MicroscopeViewPlugin):
|
class FastAutofocusAPI(MicroscopeViewPlugin):
|
||||||
|
|
@ -53,4 +53,4 @@ class FastAutofocusAPI(MicroscopeViewPlugin):
|
||||||
return jsonify(task.state), 202
|
return jsonify(task.state), 202
|
||||||
|
|
||||||
else:
|
else:
|
||||||
abort(503, 'No stage connected. Unable to autofocus.')
|
abort(503, "No stage connected. Unable to autofocus.")
|
||||||
|
|
|
||||||
|
|
@ -147,14 +147,16 @@ class ScanPlugin(MicroscopePlugin):
|
||||||
if "time" not in metadata:
|
if "time" not in metadata:
|
||||||
metadata["time"] = generate_basename()
|
metadata["time"] = generate_basename()
|
||||||
|
|
||||||
metadata.update({
|
metadata.update(
|
||||||
"scan_parameters": {
|
{
|
||||||
"step_size": step_size,
|
"scan_parameters": {
|
||||||
"grid": grid,
|
"step_size": step_size,
|
||||||
"style": style,
|
"grid": grid,
|
||||||
"autofocus_dz": autofocus_dz
|
"style": style,
|
||||||
|
"autofocus_dz": autofocus_dz,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
)
|
||||||
|
|
||||||
# Check if autofocus is enabled
|
# Check if autofocus is enabled
|
||||||
if (
|
if (
|
||||||
|
|
|
||||||
|
|
@ -154,7 +154,9 @@ class PluginLoader(object):
|
||||||
@property
|
@property
|
||||||
def state(self):
|
def state(self):
|
||||||
# DEPRECATED
|
# DEPRECATED
|
||||||
logging.warning("PluginMount.state is deprecated. Use PluginMount.active instead. State will be removed in a future version.")
|
logging.warning(
|
||||||
|
"PluginMount.state is deprecated. Use PluginMount.active instead. State will be removed in a future version."
|
||||||
|
)
|
||||||
return [m["python_name"] for m in self._plugins]
|
return [m["python_name"] for m in self._plugins]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
@ -195,13 +197,15 @@ class PluginLoader(object):
|
||||||
setattr(self, pythonsafe_plugin_name, plugin_object)
|
setattr(self, pythonsafe_plugin_name, plugin_object)
|
||||||
|
|
||||||
# Store the plugin object, and it's properties
|
# Store the plugin object, and it's properties
|
||||||
self._plugins.append({
|
self._plugins.append(
|
||||||
"name": plugin_name,
|
{
|
||||||
"python_name": pythonsafe_plugin_name,
|
"name": plugin_name,
|
||||||
"plugin": plugin_object,
|
"python_name": pythonsafe_plugin_name,
|
||||||
"routes": [],
|
"plugin": plugin_object,
|
||||||
"form": None
|
"routes": [],
|
||||||
})
|
"form": None,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
# Grant plugin access to the hardware
|
# Grant plugin access to the hardware
|
||||||
plugin_object.microscope = self.parent
|
plugin_object.microscope = self.parent
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue