openflexure-microscope-server/openflexure_microscope/microscope.py
2018-12-04 15:15:09 +00:00

119 lines
4.1 KiB
Python

# -*- coding: utf-8 -*-
"""
Defines a microscope object, binding a camera and stage with basic functionality.
"""
import logging
import os
import numpy as np
from openflexure_stage import OpenFlexureStage
from .camera.pi import StreamingCamera
from .plugins import PluginMount, load_plugin, search_plugin_dirs
from .config import load_config, save_config, convert_config
class Microscope(object):
"""
A basic microscope object.
The camera and stage should already be initialised, and passed as arguments.
Args:
camera (:py:class:`openflexure_microscope.camera.pi.StreamingCamera`): camera object
microscope (:py:class:`openflexure_stage.stage.OpenFlexureStage`): stage object
load_config (bool): Should a config file be automatically loaded on init?
config_path (str): Path to the config YAML file to be loaded. If None, defaults to USER_CONFIG_PATH.
"""
def __init__(self, camera: StreamingCamera, stage: OpenFlexureStage):
self.attach(camera, stage)
# Create plugin mountpoint
self.plugin = PluginMount(self) #: :py:class:`openflexure_microscope.plugins.PluginMount`: Mounting point for all microscope plugins
def __enter__(self):
"""Create microscope on context enter."""
return self
def __exit__(self, exc_type, exc_value, traceback):
"""Close microscope on context exit."""
self.close()
def close(self):
"""Shut down the microscope hardware."""
if self.camera:
self.camera.close()
if self.stage:
self.stage.close()
def attach(self, camera: StreamingCamera, stage: OpenFlexureStage, apply_config: bool=True):
"""
Retroactively attaches a camera and stage to the microscope object.
Allows the microscope to be created as a "dummy", with hardware communications
opened at a later time.
Args:
camera (:py:class:`openflexure_microscope.camera.pi.StreamingCamera`): camera object
microscope (:py:class:`openflexure_stage.stage.OpenFlexureStage`): stage object
apply_config (bool): Should the microscope config dictionary be applied to the attached hardware?
"""
self.camera = camera #: :py:class:`openflexure_microscope.camera.pi.StreamingCamera`: Picamera object
if isinstance(camera, StreamingCamera):
logging.info("Attached camera {}".format(camera))
self.stage = stage #: :py:class:`openflexure_stage.stage.OpenFlexureStage`: OpenFlexure stage object
if isinstance(self.stage, OpenFlexureStage): # If a stage object has been attached
logging.info("Attached stage {}".format(stage))
self.stage.backlash = np.zeros(3, dtype=np.int)
def find_plugins(self, plugin_paths=[], include_default=True):
"""
Automatically search for plugins, and attach to self.
Args:
plugin_paths (list): List of strings of plugin directories.
include_default (bool): Also load plugins from the module default directory (DEFAULT_PLUGIN_PATH)
"""
# TODO: Get paths from rc file
plugins_list = search_plugin_dirs(plugin_paths, include_default=include_default)
for i in plugins_list:
module = load_plugin(i)
self.plugin.attach(module)
# Create unified state
@property
def state(self):
"""Dictionary of the basic microscope state.
Return:
dict: Dictionary containing position data, and :py:attr:`openflexure_microscope.camera.base.BaseCamera.state`
"""
state = {
'camera': {},
'stage': {},
'plugin': {}
}
# Add stage position
position = self.stage.position
state['stage']['position'] = {
'x': position[0],
'y': position[1],
'z': position[2],
}
backlash = self.stage.backlash.tolist()
state['stage']['backlash'] = {
'x': backlash[0],
'y': backlash[1],
'z': backlash[2],
}
# Add camera state
state['camera'] = self.camera.state
return state