From 88b946b473aa43d170ecf2b6c96f4537fc9c7c77 Mon Sep 17 00:00:00 2001 From: Joel Collins Date: Thu, 7 Feb 2019 11:19:00 +0000 Subject: [PATCH] Improved runtime-config docs --- docs/source/config.rst | 95 ++++++++++++++++++---------- openflexure_microscope/config.py | 40 ++++++++++++ openflexure_microscope/microscope.py | 9 ++- 3 files changed, 108 insertions(+), 36 deletions(-) diff --git a/docs/source/config.rst b/docs/source/config.rst index a10cc2ad..e2ec6bc7 100644 --- a/docs/source/config.rst +++ b/docs/source/config.rst @@ -10,57 +10,86 @@ Microscope configuration Microscope RC file ------------------ -Example -+++++++ -.. code-block:: python +Microscope configuration is made persistent via a microscope runtime-config (RC) file. By default, this +file exists at ``~/.openflexure/microscoperc.yaml``, and contains basic parameters to set up the microscope. - from openflexure_microscope import Microscope, config - from openflexure_microscope.camera.pi import StreamingCamera - from openflexure_stage import OpenFlexureStage - - # Load default user config from ~/.openflexure/microscoperc.yaml - openflexurerc = config.load_config() - - # Create a picamera StreamingCamera, using our config - camera_obj = StreamingCamera(config=openflexurerc) - - # Create an OpenFlexure Stage attached to /dev/ttyUSB0 - stage_obj = OpenFlexureStage("/dev/ttyUSB0") - - # Attach devices and config to a Microscope object - api_microscope = Microscope(camera_obj, stage_obj, config=openflexurerc) +Additionally, by default, configurations for specific pieces of hardware (i.e. the Pi camera) are located +in separate "auxillary" config files, and are linked together by adding the config file's path to your main +microscope RC file. This is set up by default, as shown below: Default microscoperc.yaml +++++++++++++++++++++++++ .. code-block:: yaml - # The analog gain offers higher sensitivity and less noise than using digital gain only - analog_gain: 1. - digital_gain: 1. - # Resolutions for streaming and capture video_resolution: [832, 624] image_resolution: [2592, 1944] numpy_resolution: [1312, 976] + # Field of view, in stage steps + fov: [4100, 3146] + # Capture quality jpeg_quality: 75 # Parameters specific to PiCamera objects - picamera_params: - exposure_mode: 'off' - awb_mode: 'off' - awb_gains: [0.9, 2.8] - framerate: 24 - shutter_speed: 30000 - saturation: 0 - led: false + picamera_settings: ~/.openflexure/picamera_settings.yaml # Default plugins plugins: - - openflexure_microscope.plugins.default:Plugin + - openflexure_microscope.plugins.default.autofocus:AutofocusPlugin + - openflexure_microscope.plugins.default.camera_calibration:Plugin -Config module -------------- +Example picamera_settings.yaml +++++++++++++++++++++++++++++++ + +Loading the runtime-config ++++++++ + +By default, a microscope object will load the a runtime-config from the default location. +This RC can then be passed to any hardware attached to the microscope. This can be particularly +useful when creating a dummy microscope object before attaching hardware. For example: + +.. code-block:: python + + from openflexure_microscope import Microscope + from openflexure_microscope.camera.pi import StreamingCamera + from openflexure_stage import OpenFlexureStage + + # Create an empty microscope + api_microscope = Microscope(None, None) + + # Create a picamera StreamingCamera, using our config + camera_obj = StreamingCamera(config=api_microscope.rc.read()) + + # Create an OpenFlexure Stage attached to /dev/ttyUSB0 + stage_obj = OpenFlexureStage("/dev/ttyUSB0") + + # Attach devices to the Microscope object + api_microscope.attach(camera_obj, stage_obj) + +Alternatively, a config can be loaded prior to creating a microscope, allowing the microscope +hardware to be created and attached in a single line. For example: + +.. code-block:: python + from openflexure_microscope.config import OpenflexureConfig + + from openflexure_microscope import Microscope + from openflexure_microscope.camera.pi import StreamingCamera + from openflexure_stage import OpenFlexureStage + + # Create a runtime-config from the default location + rc = OpenflexureConfig(expand=True) + + # Create a populated microscope + api_microscope = Microscope( + StreamingCamera(config=rc), + OpenFlexureStage("/dev/ttyUSB0"), + config=rc + ) + + +Config module and OpenflexureConfig class +----------------------------------------- .. automodule:: openflexure_microscope.config :members: \ No newline at end of file diff --git a/openflexure_microscope/config.py b/openflexure_microscope/config.py index 7dc3ee19..062e66b0 100644 --- a/openflexure_microscope/config.py +++ b/openflexure_microscope/config.py @@ -144,7 +144,13 @@ def initialise_file(config_path, populate: str = ""): class OpenflexureConfig: + """ + An object to handle expansion, conversion, and saving of the microscope configuration. + Args: + config_path (str): Path to the config YAML file (None falls back to default location) + expand (bool): Expand paths to valid auxillary config files. + """ def __init__(self, config_path: str = None, expand: bool = True): global DEFAULT_CONFIG, USER_CONFIG_FILE @@ -168,9 +174,18 @@ class OpenflexureConfig: @property def config(self): + """ + Return a dictionary representation of the current config as a property. + """ return self.read() def read(self, json_safe=False): + """ + Read the current full config. + + Args: + json_safe (bool): Converts invalid data types to JSON types, and removes any excessively large values (e.g. lens-shading tables). + """ if json_safe: logging.info("Reading config as JSON-safe dictionary") return json_map(self._config) @@ -179,9 +194,18 @@ class OpenflexureConfig: return self._config def write(self, update_dict: dict): + """ + Write properties to the config. Merges dictionaries, rather than fully replacing. + + Args: + update_dict (dict): Dictionary of config data to merge. + """ self._config.update(update_dict) def load(self): + """ + Loads config from a file on-disk, and expands auxillary config files if available. + """ # Unexpanded config dictionary (used at load/save time) self._config = load_yaml_file(self.config_path) @@ -191,6 +215,9 @@ class OpenflexureConfig: self._config = self.expand_config(self._config) def save(self, backup: bool = True): + """ + Save config to a file on-disk, and splits into auxillary config files if available. + """ # If the loaded config was in contracted format if self.expand: # Contract self._config into self.raw_config @@ -205,6 +232,13 @@ class OpenflexureConfig: save_yaml_file(self.config_path, save_config) def expand_config(self, config_dict): + """ + Search a config dictionary for paths to valid auxillary config files, + and expand into the config dictionary if available. + + Args: + config_dict (dict): Dictionary of config data to expand + """ return_config = {} # For each value in the raw loaded config for key, value in config_dict.items(): @@ -226,6 +260,12 @@ class OpenflexureConfig: return return_config def contract_config(self, config_dict): + """ + Split a config dictionary into auxillary config files, if available. + + Args: + config_dict (dict): Dictionary of config data to contract/split + """ return_config = {} # For each value in the expanded config for key, value in config_dict.items(): diff --git a/openflexure_microscope/microscope.py b/openflexure_microscope/microscope.py index c4e2efc0..f18d68dd 100644 --- a/openflexure_microscope/microscope.py +++ b/openflexure_microscope/microscope.py @@ -25,18 +25,21 @@ class Microscope(object): Args: camera (:py:class:`openflexure_microscope.camera.pi.StreamingCamera`): camera object stage (:py:class:`openflexure_stage.stage.OpenFlexureStage`): stage object - config_path (str): Path to a microscoperc.yaml runtime-config file + config (:py:class:`openflexure_microscope.config.OpenflexureConfig`): Runtime-config object, automatically created if None. attach_plugins (bool): Should the microscope attach plugins listen in 'config' immediately. """ def __init__( self, camera: StreamingCamera, stage: OpenFlexureStage, - config_path: str = None, + config = None, attach_plugins: bool = True): # Create RC object - self.rc = OpenflexureConfig(config_path=config_path, expand=True) + if config: + self.rc = config + else: + self.rc = OpenflexureConfig(expand=True) # Load config from default path # Attach initial hardware (may be NoneTypes) self.camera = None