Blackened files
This commit is contained in:
parent
be5ddb76ae
commit
4e7606aa0c
24 changed files with 200 additions and 120 deletions
|
|
@ -61,9 +61,7 @@ class MyPluginClass(MicroscopePlugin):
|
||||||
for _ in range(n_images):
|
for _ in range(n_images):
|
||||||
|
|
||||||
# Create a data stream to capture to
|
# Create a data stream to capture to
|
||||||
output = self.microscope.camera.new_image(
|
output = self.microscope.camera.new_image(temporary=False)
|
||||||
temporary=False
|
|
||||||
)
|
|
||||||
|
|
||||||
# Capture a still image from the Pi camera, into the data stream
|
# Capture a still image from the Pi camera, into the data stream
|
||||||
self.microscope.camera.capture(output.file, use_video_port=True)
|
self.microscope.camera.capture(output.file, use_video_port=True)
|
||||||
|
|
|
||||||
|
|
@ -39,10 +39,8 @@ class SnapshotAPI(MicroscopeView):
|
||||||
# Restart stream worker thread
|
# Restart stream worker thread
|
||||||
self.microscope.camera.start_worker()
|
self.microscope.camera.start_worker()
|
||||||
|
|
||||||
return Response(
|
return Response(self.microscope.camera.get_frame(), mimetype="image/jpeg")
|
||||||
self.microscope.camera.get_frame(),
|
|
||||||
mimetype="image/jpeg",
|
|
||||||
)
|
|
||||||
|
|
||||||
class StateAPI(MicroscopeView):
|
class StateAPI(MicroscopeView):
|
||||||
def get(self):
|
def get(self):
|
||||||
|
|
@ -240,7 +238,8 @@ def construct_blueprint(microscope_obj):
|
||||||
)
|
)
|
||||||
|
|
||||||
blueprint.add_url_rule(
|
blueprint.add_url_rule(
|
||||||
"/snapshot", view_func=SnapshotAPI.as_view("snapshot", microscope=microscope_obj)
|
"/snapshot",
|
||||||
|
view_func=SnapshotAPI.as_view("snapshot", microscope=microscope_obj),
|
||||||
)
|
)
|
||||||
|
|
||||||
blueprint.add_url_rule(
|
blueprint.add_url_rule(
|
||||||
|
|
|
||||||
|
|
@ -134,12 +134,8 @@ def construct_blueprint(microscope_obj):
|
||||||
|
|
||||||
blueprint = Blueprint("task_blueprint", __name__)
|
blueprint = Blueprint("task_blueprint", __name__)
|
||||||
|
|
||||||
blueprint.add_url_rule(
|
blueprint.add_url_rule("/", view_func=TaskListAPI.as_view("task_list"))
|
||||||
"/", view_func=TaskListAPI.as_view("task_list")
|
|
||||||
)
|
|
||||||
|
|
||||||
blueprint.add_url_rule(
|
blueprint.add_url_rule("/<task_id>/", view_func=TaskAPI.as_view("task"))
|
||||||
"/<task_id>/", view_func=TaskAPI.as_view("task")
|
|
||||||
)
|
|
||||||
|
|
||||||
return blueprint
|
return blueprint
|
||||||
|
|
|
||||||
|
|
@ -120,15 +120,10 @@ class BaseCamera(metaclass=ABCMeta):
|
||||||
self.stream_timeout = 20
|
self.stream_timeout = 20
|
||||||
self.stream_timeout_enabled = False
|
self.stream_timeout_enabled = False
|
||||||
|
|
||||||
self.state = {
|
self.state = {"board": None}
|
||||||
"board": None
|
|
||||||
}
|
|
||||||
|
|
||||||
# TODO: Load/save these to config
|
# TODO: Load/save these to config
|
||||||
self.paths = {
|
self.paths = {"default": BASE_CAPTURE_PATH, "temp": TEMP_CAPTURE_PATH}
|
||||||
"default": BASE_CAPTURE_PATH,
|
|
||||||
"temp": TEMP_CAPTURE_PATH
|
|
||||||
}
|
|
||||||
|
|
||||||
# Capture data
|
# Capture data
|
||||||
self.images = []
|
self.images = []
|
||||||
|
|
@ -296,7 +291,7 @@ class BaseCamera(metaclass=ABCMeta):
|
||||||
output = CaptureObject(filepath=filepath)
|
output = CaptureObject(filepath=filepath)
|
||||||
# Insert a temporary tag if temporary
|
# Insert a temporary tag if temporary
|
||||||
if temporary:
|
if temporary:
|
||||||
output.put_tags(['temporary'])
|
output.put_tags(["temporary"])
|
||||||
|
|
||||||
# Update capture list
|
# Update capture list
|
||||||
self.images.append(output)
|
self.images.append(output)
|
||||||
|
|
@ -339,7 +334,7 @@ class BaseCamera(metaclass=ABCMeta):
|
||||||
output = CaptureObject(filepath=filepath)
|
output = CaptureObject(filepath=filepath)
|
||||||
# Insert a temporary tag if temporary
|
# Insert a temporary tag if temporary
|
||||||
if temporary:
|
if temporary:
|
||||||
output.put_tags(['temporary'])
|
output.put_tags(["temporary"])
|
||||||
|
|
||||||
# Update capture list
|
# Update capture list
|
||||||
self.videos.append(output)
|
self.videos.append(output)
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ def pull_usercomment_dict(filepath):
|
||||||
try:
|
try:
|
||||||
exif_dict = piexif.load(filepath)
|
exif_dict = piexif.load(filepath)
|
||||||
except InvalidImageDataError:
|
except InvalidImageDataError:
|
||||||
logging.error("Invalid data at {}. Skipping.".format(filepath))
|
logging.warning("Invalid data at {}. Skipping.".format(filepath))
|
||||||
return None
|
return None
|
||||||
if "Exif" in exif_dict and 37510 in exif_dict["Exif"]:
|
if "Exif" in exif_dict and 37510 in exif_dict["Exif"]:
|
||||||
return yaml.load(exif_dict["Exif"][37510].decode())
|
return yaml.load(exif_dict["Exif"][37510].decode())
|
||||||
|
|
@ -316,4 +316,4 @@ class CaptureObject(object):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
pass
|
pass
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ import logging
|
||||||
# Type hinting
|
# Type hinting
|
||||||
from typing import Tuple
|
from typing import Tuple
|
||||||
|
|
||||||
from .base import BaseCamera, CaptureObject
|
from openflexure_microscope.camera.base import BaseCamera
|
||||||
|
|
||||||
|
|
||||||
# MAIN CLASS
|
# MAIN CLASS
|
||||||
|
|
@ -26,11 +26,9 @@ class MockStreamer(BaseCamera):
|
||||||
BaseCamera.__init__(self)
|
BaseCamera.__init__(self)
|
||||||
|
|
||||||
# Store state of PiCameraStreamer
|
# Store state of PiCameraStreamer
|
||||||
self.state.update({
|
self.state.update(
|
||||||
"stream_active": False,
|
{"stream_active": False, "record_active": False, "board": None}
|
||||||
"record_active": False,
|
)
|
||||||
"board": None
|
|
||||||
})
|
|
||||||
|
|
||||||
# Update config properties
|
# Update config properties
|
||||||
self.image_resolution = (1312, 976)
|
self.image_resolution = (1312, 976)
|
||||||
|
|
@ -57,7 +55,12 @@ class MockStreamer(BaseCamera):
|
||||||
)
|
)
|
||||||
|
|
||||||
draw = ImageDraw.Draw(image)
|
draw = ImageDraw.Draw(image)
|
||||||
draw.text((20, 70), "Camera disconnected: {}".format(datetime.now().strftime("%d/%m/%Y, %H:%M:%S")))
|
draw.text(
|
||||||
|
(20, 70),
|
||||||
|
"Camera disconnected: {}".format(
|
||||||
|
datetime.now().strftime("%d/%m/%Y, %H:%M:%S")
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
image.save(self.stream, format="JPEG")
|
image.save(self.stream, format="JPEG")
|
||||||
|
|
||||||
|
|
@ -80,12 +83,14 @@ class MockStreamer(BaseCamera):
|
||||||
conf_dict = BaseCamera.read_config(self)
|
conf_dict = BaseCamera.read_config(self)
|
||||||
|
|
||||||
# Include device-specific config items
|
# Include device-specific config items
|
||||||
conf_dict.update({
|
conf_dict.update(
|
||||||
"stream_resolution": self.stream_resolution,
|
{
|
||||||
"image_resolution": self.image_resolution,
|
"stream_resolution": self.stream_resolution,
|
||||||
"numpy_resolution": self.numpy_resolution,
|
"image_resolution": self.image_resolution,
|
||||||
"jpeg_quality": self.jpeg_quality,
|
"numpy_resolution": self.numpy_resolution,
|
||||||
})
|
"jpeg_quality": self.jpeg_quality,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
return conf_dict
|
return conf_dict
|
||||||
|
|
||||||
|
|
@ -179,7 +184,7 @@ class MockStreamer(BaseCamera):
|
||||||
|
|
||||||
with self.lock:
|
with self.lock:
|
||||||
if isinstance(output, str):
|
if isinstance(output, str):
|
||||||
output = open(output, 'wb')
|
output = open(output, "wb")
|
||||||
|
|
||||||
output.write(self.stream.getvalue())
|
output.write(self.stream.getvalue())
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -66,12 +66,14 @@ class PiCameraStreamer(BaseCamera):
|
||||||
) #: :py:class:`picamera.PiCamera`: Picamera object
|
) #: :py:class:`picamera.PiCamera`: Picamera object
|
||||||
|
|
||||||
# Store state of PiCameraStreamer
|
# Store state of PiCameraStreamer
|
||||||
self.state.update({
|
self.state.update(
|
||||||
"stream_active": False,
|
{
|
||||||
"record_active": False,
|
"stream_active": False,
|
||||||
"preview_active": False,
|
"record_active": False,
|
||||||
"board": f"picamera_{self.camera.revision}",
|
"preview_active": False,
|
||||||
})
|
"board": f"picamera_{self.camera.revision}",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
# Reset variable states
|
# Reset variable states
|
||||||
self.set_zoom(1.0)
|
self.set_zoom(1.0)
|
||||||
|
|
@ -113,13 +115,15 @@ class PiCameraStreamer(BaseCamera):
|
||||||
conf_dict = BaseCamera.read_config(self)
|
conf_dict = BaseCamera.read_config(self)
|
||||||
|
|
||||||
# Include device-specific config items
|
# Include device-specific config items
|
||||||
conf_dict.update({
|
conf_dict.update(
|
||||||
"stream_resolution": self.stream_resolution,
|
{
|
||||||
"image_resolution": self.image_resolution,
|
"stream_resolution": self.stream_resolution,
|
||||||
"numpy_resolution": self.numpy_resolution,
|
"image_resolution": self.image_resolution,
|
||||||
"jpeg_quality": self.jpeg_quality,
|
"numpy_resolution": self.numpy_resolution,
|
||||||
"picamera_settings": {},
|
"jpeg_quality": self.jpeg_quality,
|
||||||
})
|
"picamera_settings": {},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
# Include a subset of picamera properties
|
# Include a subset of picamera properties
|
||||||
# TODO: Expand this subset so we have more metadata?
|
# TODO: Expand this subset so we have more metadata?
|
||||||
|
|
@ -333,7 +337,7 @@ class PiCameraStreamer(BaseCamera):
|
||||||
return output
|
return output
|
||||||
|
|
||||||
else:
|
else:
|
||||||
logging.error(
|
logging.warning(
|
||||||
"Cannot start a new recording\
|
"Cannot start a new recording\
|
||||||
until the current recording has stopped."
|
until the current recording has stopped."
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -10,5 +10,14 @@ __all__ = [
|
||||||
"ThreadTerminationError",
|
"ThreadTerminationError",
|
||||||
]
|
]
|
||||||
|
|
||||||
from .pool import tasks, states, current_task, update_task_progress, cleanup_tasks, remove_task, update_task_data, taskify
|
from .pool import (
|
||||||
|
tasks,
|
||||||
|
states,
|
||||||
|
current_task,
|
||||||
|
update_task_progress,
|
||||||
|
cleanup_tasks,
|
||||||
|
remove_task,
|
||||||
|
update_task_data,
|
||||||
|
taskify,
|
||||||
|
)
|
||||||
from .thread import ThreadTerminationError
|
from .thread import ThreadTerminationError
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ class TaskMaster:
|
||||||
|
|
||||||
# Task management
|
# Task management
|
||||||
|
|
||||||
|
|
||||||
def tasks():
|
def tasks():
|
||||||
"""
|
"""
|
||||||
Dictionary of tasks in default taskmaster
|
Dictionary of tasks in default taskmaster
|
||||||
|
|
@ -62,6 +63,7 @@ def states():
|
||||||
global _default_task_master
|
global _default_task_master
|
||||||
return _default_task_master.states
|
return _default_task_master.states
|
||||||
|
|
||||||
|
|
||||||
def cleanup_tasks():
|
def cleanup_tasks():
|
||||||
global _default_task_master
|
global _default_task_master
|
||||||
return _default_task_master.cleanup()
|
return _default_task_master.cleanup()
|
||||||
|
|
@ -74,6 +76,7 @@ def remove_task(task_id: str):
|
||||||
|
|
||||||
# Operations on the current task
|
# Operations on the current task
|
||||||
|
|
||||||
|
|
||||||
def current_task():
|
def current_task():
|
||||||
current_task_thread = threading.current_thread()
|
current_task_thread = threading.current_thread()
|
||||||
if not isinstance(current_task_thread, TaskThread):
|
if not isinstance(current_task_thread, TaskThread):
|
||||||
|
|
@ -97,6 +100,7 @@ def update_task_data(data: dict):
|
||||||
|
|
||||||
# Main "taskify" functions
|
# Main "taskify" functions
|
||||||
|
|
||||||
|
|
||||||
def taskify(f):
|
def taskify(f):
|
||||||
"""
|
"""
|
||||||
A decorator that wraps the passed in function
|
A decorator that wraps the passed in function
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ class TaskThread(threading.Thread):
|
||||||
self._return_value = None # Return value
|
self._return_value = None # Return value
|
||||||
self._start_time = None # Task start time
|
self._start_time = None # Task start time
|
||||||
self._end_time = None # Task end time
|
self._end_time = None # Task end time
|
||||||
|
|
||||||
# Public state properties
|
# Public state properties
|
||||||
self.progress: int = None # Percent progress of the task
|
self.progress: int = None # Percent progress of the task
|
||||||
self.data = {} # Dictionary of custom data added during the task
|
self.data = {} # Dictionary of custom data added during the task
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,12 @@ from openflexure_microscope.api.v1.views import MicroscopeViewPlugin
|
||||||
from openflexure_microscope.api.utilities import JsonResponse
|
from openflexure_microscope.api.utilities import JsonResponse
|
||||||
|
|
||||||
# Task management
|
# Task management
|
||||||
from openflexure_microscope.common.tasks import current_task, update_task_progress, update_task_data, taskify
|
from openflexure_microscope.common.tasks import (
|
||||||
|
current_task,
|
||||||
|
update_task_progress,
|
||||||
|
update_task_data,
|
||||||
|
taskify,
|
||||||
|
)
|
||||||
|
|
||||||
# Exceptions
|
# Exceptions
|
||||||
from openflexure_microscope.exceptions import TaskDeniedException
|
from openflexure_microscope.exceptions import TaskDeniedException
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,9 @@ import pkg_resources
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from openflexure_microscope.stage.base import BaseStage
|
from openflexure_microscope.stage.base import BaseStage
|
||||||
|
from openflexure_microscope.stage.mock import MockStage
|
||||||
from openflexure_microscope.camera.base import BaseCamera
|
from openflexure_microscope.camera.base import BaseCamera
|
||||||
|
from openflexure_microscope.camera.mock import MockStreamer
|
||||||
|
|
||||||
from openflexure_microscope.plugins import PluginMount
|
from openflexure_microscope.plugins import PluginMount
|
||||||
from openflexure_microscope.task import TaskOrchestrator
|
from openflexure_microscope.task import TaskOrchestrator
|
||||||
|
|
@ -141,6 +142,22 @@ class Microscope:
|
||||||
logging.info("Repopulating PluginMount...")
|
logging.info("Repopulating PluginMount...")
|
||||||
self.attach_plugins(self.plugin_maps)
|
self.attach_plugins(self.plugin_maps)
|
||||||
|
|
||||||
|
def has_real_stage(self):
|
||||||
|
if hasattr(self, "stage") and not isinstance(
|
||||||
|
self.stage, MockStage
|
||||||
|
):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def has_real_camera(self):
|
||||||
|
if hasattr(self, "camera") and not isinstance(
|
||||||
|
self.camera, MockStreamer
|
||||||
|
):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
# Create unified state
|
# Create unified state
|
||||||
@property
|
@property
|
||||||
def state(self):
|
def state(self):
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ from openflexure_microscope.devel import (
|
||||||
JsonResponse,
|
JsonResponse,
|
||||||
request,
|
request,
|
||||||
jsonify,
|
jsonify,
|
||||||
taskify
|
taskify,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ from openflexure_microscope.devel import (
|
||||||
JsonResponse,
|
JsonResponse,
|
||||||
request,
|
request,
|
||||||
jsonify,
|
jsonify,
|
||||||
taskify
|
taskify,
|
||||||
)
|
)
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ from openflexure_microscope.devel import (
|
||||||
request,
|
request,
|
||||||
jsonify,
|
jsonify,
|
||||||
abort,
|
abort,
|
||||||
taskify
|
taskify,
|
||||||
)
|
)
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
import time
|
import time
|
||||||
import numpy as np
|
|
||||||
from typing import Tuple
|
from typing import Tuple
|
||||||
|
from functools import reduce
|
||||||
import uuid
|
import uuid
|
||||||
import itertools
|
import itertools
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from openflexure_microscope.camera.base import generate_basename
|
from openflexure_microscope.camera.base import generate_basename
|
||||||
|
|
||||||
from openflexure_microscope.devel import MicroscopePlugin
|
from openflexure_microscope.devel import MicroscopePlugin, update_task_progress, update_task_data
|
||||||
|
|
||||||
from .api import TileScanAPI
|
from .api import TileScanAPI
|
||||||
|
|
||||||
|
|
@ -53,16 +53,26 @@ class ScanPlugin(MicroscopePlugin):
|
||||||
|
|
||||||
api_views = {"/tile": TileScanAPI}
|
api_views = {"/tile": TileScanAPI}
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.images_to_be_captured: int = 1
|
||||||
|
update_task_data({"images_to_be_captured": self.images_to_be_captured})
|
||||||
|
|
||||||
|
@property
|
||||||
|
def progress(self):
|
||||||
|
progress = (self.images_captured_so_far / self.images_to_be_captured) * 100
|
||||||
|
logging.info(progress)
|
||||||
|
return progress
|
||||||
|
|
||||||
def capture(
|
def capture(
|
||||||
self,
|
self,
|
||||||
basename,
|
basename,
|
||||||
scan_id,
|
scan_id,
|
||||||
temporary: bool = False,
|
temporary: bool = False,
|
||||||
use_video_port: bool = False,
|
use_video_port: bool = False,
|
||||||
resize: Tuple[int, int] = None,
|
resize: Tuple[int, int] = None,
|
||||||
bayer: bool = False,
|
bayer: bool = False,
|
||||||
metadata: dict = {},
|
metadata: dict = {},
|
||||||
tags: list = [],
|
tags: list = [],
|
||||||
):
|
):
|
||||||
|
|
||||||
# Construct a tile filename
|
# Construct a tile filename
|
||||||
|
|
@ -97,21 +107,26 @@ class ScanPlugin(MicroscopePlugin):
|
||||||
output.put_tags(tags)
|
output.put_tags(tags)
|
||||||
|
|
||||||
def tile(
|
def tile(
|
||||||
self,
|
self,
|
||||||
basename: str = None,
|
basename: str = None,
|
||||||
temporary: bool = False,
|
temporary: bool = False,
|
||||||
step_size: int = [2000, 1500, 100],
|
step_size: int = [2000, 1500, 100],
|
||||||
grid: list = [3, 3, 5],
|
grid: list = [3, 3, 5],
|
||||||
style="raster",
|
style="raster",
|
||||||
autofocus_dz: int = 50,
|
autofocus_dz: int = 50,
|
||||||
use_video_port: bool = False,
|
use_video_port: bool = False,
|
||||||
resize: Tuple[int, int] = None,
|
resize: Tuple[int, int] = None,
|
||||||
bayer: bool = False,
|
bayer: bool = False,
|
||||||
fast_autofocus=False,
|
fast_autofocus=False,
|
||||||
metadata: dict = {},
|
metadata: dict = {},
|
||||||
tags: list = [],
|
tags: list = [],
|
||||||
):
|
):
|
||||||
|
|
||||||
|
# Keep task progress
|
||||||
|
# TODO: Make this line not nasty
|
||||||
|
self.images_to_be_captured = reduce((lambda x, y: x * y), grid)
|
||||||
|
self.images_captured_so_far = 0
|
||||||
|
|
||||||
# Generate a basename if none given
|
# Generate a basename if none given
|
||||||
if not basename:
|
if not basename:
|
||||||
basename = generate_basename()
|
basename = generate_basename()
|
||||||
|
|
@ -123,19 +138,24 @@ class ScanPlugin(MicroscopePlugin):
|
||||||
initial_position = self.microscope.stage.position
|
initial_position = self.microscope.stage.position
|
||||||
|
|
||||||
# Add scan metadata
|
# Add scan metadata
|
||||||
if not "time" in metadata:
|
if "time" not in metadata:
|
||||||
metadata["time"] = generate_basename()
|
metadata["time"] = generate_basename()
|
||||||
|
|
||||||
# Check if autofocus is enabled
|
# Check if autofocus is enabled
|
||||||
if autofocus_dz and hasattr(self.microscope.plugin, "default_autofocus"):
|
if (
|
||||||
|
autofocus_dz
|
||||||
|
and hasattr(self.microscope.plugin, "default_autofocus")
|
||||||
|
and self.microscope.has_real_stage()
|
||||||
|
and self.microscope.has_real_camera()
|
||||||
|
):
|
||||||
autofocus_enabled = True
|
autofocus_enabled = True
|
||||||
else:
|
else:
|
||||||
autofocus_enabled = False
|
autofocus_enabled = False
|
||||||
|
|
||||||
if fast_autofocus and not hasattr(
|
if fast_autofocus and not hasattr(
|
||||||
self.microscope.plugin.default_autofocus, "monitor_sharpness"
|
self.microscope.plugin.default_autofocus, "monitor_sharpness"
|
||||||
):
|
):
|
||||||
logging.error(
|
logging.warning(
|
||||||
"Can't use fast autofocus in the scan - the default plugin doesn't support monitor_sharpness; maybe it is too old?"
|
"Can't use fast autofocus in the scan - the default plugin doesn't support monitor_sharpness; maybe it is too old?"
|
||||||
)
|
)
|
||||||
fast_autofocus = False
|
fast_autofocus = False
|
||||||
|
|
@ -198,6 +218,9 @@ class ScanPlugin(MicroscopePlugin):
|
||||||
metadata=metadata,
|
metadata=metadata,
|
||||||
tags=tags,
|
tags=tags,
|
||||||
)
|
)
|
||||||
|
# Update task progress
|
||||||
|
self.images_captured_so_far += 1
|
||||||
|
update_task_progress(self.progress)
|
||||||
else:
|
else:
|
||||||
logging.debug("Entering z-stack")
|
logging.debug("Entering z-stack")
|
||||||
self.stack(
|
self.stack(
|
||||||
|
|
@ -218,7 +241,7 @@ class ScanPlugin(MicroscopePlugin):
|
||||||
next_z = self.microscope.stage.position[2]
|
next_z = self.microscope.stage.position[2]
|
||||||
if fast_autofocus:
|
if fast_autofocus:
|
||||||
next_z += (
|
next_z += (
|
||||||
autofocus_dz / 2
|
autofocus_dz / 2
|
||||||
) # Fast autofocus requires us to start at the top of the range
|
) # Fast autofocus requires us to start at the top of the range
|
||||||
if grid[2] > 1:
|
if grid[2] > 1:
|
||||||
next_z -= int(
|
next_z -= int(
|
||||||
|
|
@ -229,19 +252,19 @@ class ScanPlugin(MicroscopePlugin):
|
||||||
self.microscope.stage.move_abs(initial_position)
|
self.microscope.stage.move_abs(initial_position)
|
||||||
|
|
||||||
def stack(
|
def stack(
|
||||||
self,
|
self,
|
||||||
basename: str = None,
|
basename: str = None,
|
||||||
temporary: bool = False,
|
temporary: bool = False,
|
||||||
scan_id: str = None,
|
scan_id: str = None,
|
||||||
step_size: int = 100,
|
step_size: int = 100,
|
||||||
steps: int = 5,
|
steps: int = 5,
|
||||||
center: bool = True,
|
center: bool = True,
|
||||||
return_to_start: bool = True,
|
return_to_start: bool = True,
|
||||||
use_video_port: bool = False,
|
use_video_port: bool = False,
|
||||||
resize: Tuple[int, int] = None,
|
resize: Tuple[int, int] = None,
|
||||||
bayer: bool = False,
|
bayer: bool = False,
|
||||||
metadata: dict = {},
|
metadata: dict = {},
|
||||||
tags: list = [],
|
tags: list = [],
|
||||||
):
|
):
|
||||||
|
|
||||||
# Generate a basename if none given
|
# Generate a basename if none given
|
||||||
|
|
@ -278,6 +301,9 @@ class ScanPlugin(MicroscopePlugin):
|
||||||
metadata=metadata,
|
metadata=metadata,
|
||||||
tags=tags,
|
tags=tags,
|
||||||
)
|
)
|
||||||
|
# Update task progress
|
||||||
|
self.images_captured_so_far += 1
|
||||||
|
update_task_progress(self.progress)
|
||||||
|
|
||||||
if i != steps - 1:
|
if i != steps - 1:
|
||||||
logging.debug("Moving z by {}".format(step_size))
|
logging.debug("Moving z by {}".format(step_size))
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,7 @@ def load_plugin_module(plugin_path):
|
||||||
plugin_module = importlib.import_module(plugin_path)
|
plugin_module = importlib.import_module(plugin_path)
|
||||||
plugin_name = name_from_module(plugin_path)
|
plugin_name = name_from_module(plugin_path)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error("Error loading plugin.")
|
logging.warning("Error loading plugin.")
|
||||||
logging.error(e)
|
logging.error(e)
|
||||||
return None, None
|
return None, None
|
||||||
|
|
||||||
|
|
@ -216,7 +216,7 @@ class PluginMount(object):
|
||||||
)
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
logging.error(f"Error loading plugin {plugin_map}. Moving on.")
|
logging.warning(f"Error loading plugin {plugin_map}. Moving on.")
|
||||||
|
|
||||||
|
|
||||||
class MicroscopePlugin:
|
class MicroscopePlugin:
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ from openflexure_microscope.devel import (
|
||||||
JsonResponse,
|
JsonResponse,
|
||||||
request,
|
request,
|
||||||
jsonify,
|
jsonify,
|
||||||
taskify
|
taskify,
|
||||||
)
|
)
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ class ExamplePlugin(MicroscopePlugin):
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
# Update task progress (if running as a task)
|
# Update task progress (if running as a task)
|
||||||
percent_complete = int(((t + 1)/run_time)*100)
|
percent_complete = int(((t + 1) / run_time) * 100)
|
||||||
update_task_progress(percent_complete)
|
update_task_progress(percent_complete)
|
||||||
|
|
||||||
return vals
|
return vals
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import numpy as np
|
||||||
from abc import ABCMeta, abstractmethod
|
from abc import ABCMeta, abstractmethod
|
||||||
from openflexure_microscope.common.lock import StrictLock
|
from openflexure_microscope.common.lock import StrictLock
|
||||||
|
|
||||||
|
|
@ -110,4 +111,4 @@ class BaseStage(metaclass=ABCMeta):
|
||||||
array of 3D positions with x=y=0. This, along with all the
|
array of 3D positions with x=y=0. This, along with all the
|
||||||
keyword arguments, is then passed to ``scan_linear``.
|
keyword arguments, is then passed to ``scan_linear``.
|
||||||
"""
|
"""
|
||||||
return self.scan_linear([[0, 0, z] for z in dz], **kwargs)
|
return self.scan_linear([[0, 0, z] for z in dz], **kwargs)
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ from collections.abc import Iterable
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
|
||||||
class MockStage(BaseStage):
|
class MockStage(BaseStage):
|
||||||
def __init__(self, port=None, **kwargs):
|
def __init__(self, port=None, **kwargs):
|
||||||
BaseStage.__init__(self)
|
BaseStage.__init__(self)
|
||||||
|
|
@ -68,7 +69,9 @@ class MockStage(BaseStage):
|
||||||
else:
|
else:
|
||||||
self._backlash = np.array([int(blsh)] * self.n_axes, dtype=np.int)
|
self._backlash = np.array([int(blsh)] * self.n_axes, dtype=np.int)
|
||||||
|
|
||||||
def move_rel(self, displacement: list, axis=None, backlash=True, simulate_time: bool=True):
|
def move_rel(
|
||||||
|
self, displacement: list, axis=None, backlash=True, simulate_time: bool = True
|
||||||
|
):
|
||||||
if simulate_time:
|
if simulate_time:
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
if axis is not None:
|
if axis is not None:
|
||||||
|
|
@ -81,7 +84,7 @@ class MockStage(BaseStage):
|
||||||
|
|
||||||
self._position = list(np.array(self._position) + np.array(move))
|
self._position = list(np.array(self._position) + np.array(move))
|
||||||
|
|
||||||
def move_abs(self, final, simulate_time: bool=True, **kwargs):
|
def move_abs(self, final, simulate_time: bool = True, **kwargs):
|
||||||
if simulate_time:
|
if simulate_time:
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -136,7 +136,7 @@ class Sangaboard(ExtensibleSerialInstrument):
|
||||||
# make sure we close the serial port cleanly (otherwise it hangs open).
|
# make sure we close the serial port cleanly (otherwise it hangs open).
|
||||||
self.close()
|
self.close()
|
||||||
logging.error(e)
|
logging.error(e)
|
||||||
logging.error(
|
logging.warning(
|
||||||
"You may need to update the firmware running on the Sangaboard."
|
"You may need to update the firmware running on the Sangaboard."
|
||||||
)
|
)
|
||||||
raise e
|
raise e
|
||||||
|
|
@ -164,12 +164,12 @@ class Sangaboard(ExtensibleSerialInstrument):
|
||||||
r"OpenFlexure Motor Board v(([\d]+)(?:\.([\d]+))+)", self.firmware
|
r"OpenFlexure Motor Board v(([\d]+)(?:\.([\d]+))+)", self.firmware
|
||||||
)
|
)
|
||||||
if not match:
|
if not match:
|
||||||
logging.error(
|
logging.warning(
|
||||||
'Version string "{}" not recognised.'.format(self.firmware)
|
'Version string "{}" not recognised.'.format(self.firmware)
|
||||||
)
|
)
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
logging.error("No firmware version string was returned.")
|
logging.warning("No firmware version string was returned.")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Check for matching/valid version number
|
# Check for matching/valid version number
|
||||||
|
|
@ -179,7 +179,7 @@ class Sangaboard(ExtensibleSerialInstrument):
|
||||||
self.firmware_version = match.group(1)
|
self.firmware_version = match.group(1)
|
||||||
|
|
||||||
if version_tuple not in Sangaboard.valid_firmwares:
|
if version_tuple not in Sangaboard.valid_firmwares:
|
||||||
logging.error(
|
logging.warning(
|
||||||
"This version of the Python module requires firmware v0.5 (with legacy support for v0.4)"
|
"This version of the Python module requires firmware v0.5 (with legacy support for v0.4)"
|
||||||
)
|
)
|
||||||
return False
|
return False
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,11 @@
|
||||||
import logging
|
import logging
|
||||||
from openflexure_microscope.common.tasks import tasks, states, taskify, cleanup_tasks, remove_task
|
from openflexure_microscope.common.tasks import (
|
||||||
|
tasks,
|
||||||
|
states,
|
||||||
|
taskify,
|
||||||
|
cleanup_tasks,
|
||||||
|
remove_task,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class TaskOrchestrator:
|
class TaskOrchestrator:
|
||||||
|
|
@ -14,7 +20,9 @@ class TaskOrchestrator:
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def tasks(self):
|
def tasks(self):
|
||||||
logging.warning("TaskOrchestrator class is deprecated. Please use common.tasks.tasks().values() instead.")
|
logging.warning(
|
||||||
|
"TaskOrchestrator class is deprecated. Please use common.tasks.tasks().values() instead."
|
||||||
|
)
|
||||||
return tasks().values()
|
return tasks().values()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
@ -22,7 +30,9 @@ class TaskOrchestrator:
|
||||||
"""
|
"""
|
||||||
Returns a list of dictionary representations of all tasks in the session.
|
Returns a list of dictionary representations of all tasks in the session.
|
||||||
"""
|
"""
|
||||||
logging.warning("TaskOrchestrator class is deprecated. Please use common.tasks.tasks() instead.")
|
logging.warning(
|
||||||
|
"TaskOrchestrator class is deprecated. Please use common.tasks.tasks() instead."
|
||||||
|
)
|
||||||
return states()
|
return states()
|
||||||
|
|
||||||
def task_from_id(self, task_id: str):
|
def task_from_id(self, task_id: str):
|
||||||
|
|
@ -41,7 +51,9 @@ class TaskOrchestrator:
|
||||||
function (function): The target function to run in the background
|
function (function): The target function to run in the background
|
||||||
args, kwargs: Arguments that will be passed to `function` at runtime.
|
args, kwargs: Arguments that will be passed to `function` at runtime.
|
||||||
"""
|
"""
|
||||||
logging.warning("TaskOrchestrator class is deprecated. Please use common.tasks.taskify() instead.")
|
logging.warning(
|
||||||
|
"TaskOrchestrator class is deprecated. Please use common.tasks.taskify() instead."
|
||||||
|
)
|
||||||
return taskify(function)(*args, **kwargs)
|
return taskify(function)(*args, **kwargs)
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
|
|
@ -49,12 +61,16 @@ class TaskOrchestrator:
|
||||||
Remove all inactive tasks from the task array.
|
Remove all inactive tasks from the task array.
|
||||||
This will remove tasks that either finished or never started.
|
This will remove tasks that either finished or never started.
|
||||||
"""
|
"""
|
||||||
logging.warning("TaskOrchestrator class is deprecated. Please use common.tasks.cleanup_tasks() instead.")
|
logging.warning(
|
||||||
|
"TaskOrchestrator class is deprecated. Please use common.tasks.cleanup_tasks() instead."
|
||||||
|
)
|
||||||
cleanup_tasks()
|
cleanup_tasks()
|
||||||
|
|
||||||
def delete(self, task_id: str):
|
def delete(self, task_id: str):
|
||||||
"""
|
"""
|
||||||
Delete a given task by ID, only if task is not currently running.
|
Delete a given task by ID, only if task is not currently running.
|
||||||
"""
|
"""
|
||||||
logging.warning("TaskOrchestrator class is deprecated. Please use common.tasks.remove_task() instead.")
|
logging.warning(
|
||||||
|
"TaskOrchestrator class is deprecated. Please use common.tasks.remove_task() instead."
|
||||||
|
)
|
||||||
remove_task(task_id)
|
remove_task(task_id)
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,9 @@ class TestCaptureMethods(unittest.TestCase):
|
||||||
# Capture to a context (auto-deletes files when done)
|
# Capture to a context (auto-deletes files when done)
|
||||||
with camera.new_image() as output:
|
with camera.new_image() as output:
|
||||||
|
|
||||||
camera.capture(output.file, use_video_port=use_video_port, resize=resize)
|
camera.capture(
|
||||||
|
output.file, use_video_port=use_video_port, resize=resize
|
||||||
|
)
|
||||||
|
|
||||||
# Ensure file deletion fails and returns False
|
# Ensure file deletion fails and returns False
|
||||||
self.assertFalse(output.delete_file())
|
self.assertFalse(output.delete_file())
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue