Started updating documentation
This commit is contained in:
parent
be74b92bd7
commit
802f5ba0c2
17 changed files with 248 additions and 368 deletions
|
|
@ -1,25 +1,15 @@
|
|||
HTTP API
|
||||
========
|
||||
|
||||
Summary
|
||||
-------
|
||||
.. qrefflask:: openflexure_microscope.api.app:app
|
||||
:undoc-endpoints: index
|
||||
:undoc-static:
|
||||
:endpoints:
|
||||
Live documentation
|
||||
------------------
|
||||
|
||||
Details
|
||||
-------
|
||||
Full, interactive Swagger documentation for your microscopes web API is available from the microscope itself. From any browser, go to ``http://{your microscope IP address}/api/v2/swagger-ui``.
|
||||
|
||||
Default views
|
||||
-------------
|
||||
.. autoflask:: openflexure_microscope.api.app:app
|
||||
:undoc-endpoints: index
|
||||
:undoc-static:
|
||||
:endpoints:
|
||||
:order: path
|
||||
|
||||
Example Requests
|
||||
----------------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
./apirequests.rst
|
||||
|
|
@ -1,108 +0,0 @@
|
|||
Introduction
|
||||
------------
|
||||
|
||||
In these examples, our microscope is located at the IP ``192.168.1.126``, running on port 5000.
|
||||
|
||||
|
||||
New Image Capture
|
||||
-----------------
|
||||
|
||||
Temprary capture, using video port.
|
||||
|
||||
CURL
|
||||
++++
|
||||
.. code-block:: none
|
||||
|
||||
curl -X POST -H "Content-Type: application/json" -d \\
|
||||
'{"temporary": true, "use_video_port": true}' http://192.168.1.126:5000/api/v1/camera/capture
|
||||
|
||||
HTTPie
|
||||
++++++
|
||||
.. code-block:: none
|
||||
|
||||
http POST http://192.168.1.126:5000/api/v1/camera/capture temporary:=true use_video_port:=true
|
||||
|
||||
Python Requests
|
||||
+++++++++++++++
|
||||
.. code-block:: python
|
||||
|
||||
json_payload = {
|
||||
"keep_on_disk": keep_on_disk,
|
||||
"use_video_port": use_video_port
|
||||
}
|
||||
requests.post('http://192.168.1.126:5000/api/v1/camera/capture', json=json_payload)
|
||||
|
||||
Update Config
|
||||
-------------
|
||||
|
||||
Example, changing picamera shutter speed to 2000, and the JPEG quality to 90.
|
||||
|
||||
CURL
|
||||
++++
|
||||
.. code-block:: none
|
||||
|
||||
curl -X POST -H "Content-Type: application/json" -d \\
|
||||
'{"camera_settings": {"picamera_settings": {"shutter_speed": 2000}, "jpeg_quality": 90}}' http://192.168.1.126:5000/api/v1/config
|
||||
|
||||
HTTPie
|
||||
++++++
|
||||
.. code-block:: none
|
||||
|
||||
http POST http://192.168.1.126:5000/api/v1/config camera_settings:='{"jpeg_quality": 90, "picamera_settings": {"shutter_speed": 2000}}'
|
||||
|
||||
Python Requests
|
||||
+++++++++++++++
|
||||
.. code-block:: python
|
||||
|
||||
json_payload = {
|
||||
camera_settings: {
|
||||
"jpeg_quality": 90,
|
||||
"picamera_settings": {"shutter_speed": 2000}
|
||||
}
|
||||
}
|
||||
requests.post('http://192.168.1.126:5000/api/v1/config', json=json_payload)
|
||||
|
||||
Read Config
|
||||
-----------
|
||||
|
||||
CURL
|
||||
++++
|
||||
.. code-block:: none
|
||||
|
||||
curl http://192.168.1.126:5000/api/v1/config
|
||||
|
||||
HTTPie
|
||||
++++++
|
||||
.. code-block:: none
|
||||
|
||||
http GET http://192.168.1.126:5000/api/v1/config
|
||||
|
||||
Python Requests
|
||||
+++++++++++++++
|
||||
.. code-block:: python
|
||||
|
||||
requests.get('http://192.168.1.126:5000/api/v1/config')
|
||||
|
||||
Recalibrate Picamera
|
||||
--------------------
|
||||
|
||||
Starts the automatic recalibration plugin
|
||||
|
||||
CURL
|
||||
++++
|
||||
.. code-block:: none
|
||||
|
||||
curl -X POST -H "Content-Type: application/json" \\
|
||||
http://192.168.1.126:5000/api/v1/plugin/default/camera_calibration/recalibrate
|
||||
|
||||
HTTPie
|
||||
++++++
|
||||
.. code-block:: none
|
||||
|
||||
http POST http://192.168.1.126:5000/api/v1/plugin/default/camera_calibration/recalibrate
|
||||
|
||||
Python Requests
|
||||
+++++++++++++++
|
||||
.. code-block:: python
|
||||
|
||||
requests.post('http://192.168.1.126:5000/api/v1/plugin/default/camera_calibration/recalibrate')
|
||||
|
|
@ -1,9 +1,35 @@
|
|||
Camera Class
|
||||
=======================================================
|
||||
============
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
picamera.rst
|
||||
basecamera.rst
|
||||
capture.rst
|
||||
capture.rst
|
||||
|
||||
Capturing from a camera object
|
||||
------------------------------
|
||||
|
||||
In the cases of both a Raspberry Pi Streaming Camera, and a Mock Camera (attached if no real camera can be found), the camera's ``capture`` method takes as it's first positional argument either a string describing a file path to save to, or any Python file-like object.
|
||||
|
||||
The :class:`openflexure_microscope.camera.capture.CaptureObject` class works by providing a file path string, but adds additional functionality around storing and retreiving EXIF metadata in compatible files.
|
||||
|
||||
If, for your application, you do not require this functionality, you can pass a simple string or file-like object. For example, to take an image that will be stored in-memory, processed rapidly, and then discarded, you could use a BytesIO stream:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import io
|
||||
from PIL import Image
|
||||
...
|
||||
|
||||
with microscope.camera.lock, io.BytesIO() as stream:
|
||||
|
||||
microscope.camera.capture(
|
||||
stream,
|
||||
use_video_port=True,
|
||||
bayer=False,
|
||||
)
|
||||
|
||||
stream.seek(0)
|
||||
image = Image.open(stream)
|
||||
|
|
@ -3,7 +3,7 @@ Capture Object
|
|||
|
||||
By default, all image and video capture data are stored to instances of :py:class:`openflexure_microscope.camera.capture.CaptureObject`. This class mostly wraps up complexity associated with moving data between disk and memory.
|
||||
|
||||
The class also includes some convenience features such as automatically cleaning up captures marked as temporary, handling metadata tags and file names, and generating image thumbnails. Often, the most recent capture wants to be stored in memory, for applications that involve immediately processing the capture data in some way. Rather than manually keep track of the location of the capture data, you can use the CaptureObject ``data`` attribute, which automatically returns capture data from the fastest available location. Additionally, the class handles storing capture metadata to Exif tags in supported formats.
|
||||
The class also includes some convenience features such as handling metadata tags and file names, and generating image thumbnails. Additionally, the class handles storing capture metadata to Exif tags in supported formats.
|
||||
|
||||
Below are details of available methods and attributes.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +0,0 @@
|
|||
Common utilities
|
||||
=======================================================
|
||||
|
||||
Tasks module
|
||||
------------
|
||||
.. automodule:: openflexure_microscope.common.tasks
|
||||
:members:
|
||||
|
||||
Lock module
|
||||
-----------
|
||||
.. automodule:: openflexure_microscope.common.lock
|
||||
:members:
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
Microscope configuration
|
||||
========================
|
||||
Microscope settings
|
||||
===================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
|
@ -7,65 +7,15 @@ Microscope configuration
|
|||
|
||||
.. _MicroscopeRC:
|
||||
|
||||
Microscope RC file
|
||||
------------------
|
||||
Microscope settings file
|
||||
------------------------
|
||||
|
||||
Microscope configuration is made persistent via a microscope runtime-config (RC) file. By default, this
|
||||
file exists at ``~/.openflexure/microscope_settings.json``, and contains basic parameters to set up the microscope.
|
||||
Microscope settings are made persistent via a microscope settings file. By default, this
|
||||
file exists at ``~/.openflexure/microscope_settings.json``.
|
||||
|
||||
Additionally, by default, configurations for specific pieces of hardware (i.e. the stage and 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.
|
||||
The class :class:`openflexure_microscope.config.OpenflexureSettingsFile` provides functionality for loading a JSON-format settings file as a Python dictionary, and merging changed settings back into the file.
|
||||
|
||||
The default settings are loaded by the :attr:`openflexure_microscope.config.user_settings`, which can be imported anywhere in the microscope server application to allow reading and writing of persistent settings.
|
||||
|
||||
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:
|
||||
|
|
@ -9,7 +9,6 @@ Welcome to OpenFlexure Microscope Software's documentation!
|
|||
config.rst
|
||||
microscope.rst
|
||||
camera.rst
|
||||
common.rst
|
||||
plugins.rst
|
||||
api.rst
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
Microscope class
|
||||
=======================================================
|
||||
|
||||
The main microscope class handles microscope settings, passing these between the settings file and their appropriate components (camera, stage), basic metadata about the current device status, and interfacing with the separate camera and stage components.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Contents:
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
Developing Plugins
|
||||
=======================================================
|
||||
Developing API Extensions
|
||||
=========================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
|
|
|||
|
|
@ -1,42 +1,21 @@
|
|||
Introduction
|
||||
============
|
||||
|
||||
Plugins allow functionality to be added to the OpenFlexure Microscope without having to modify the base code.
|
||||
Extensions allow functionality to be added to the OpenFlexure Microscope web API without having to modify the base code.
|
||||
They have full access to the :py:class:`openflexure_microscope.Microscope` object,
|
||||
including direct access to the attached :py:class:`openflexure_microscope.camera.pi.StreamingCamera` and :py:class:`openflexure_stage.stage.OpenFlexureStage` objects.
|
||||
This also allows direct access to the :py:class:`picamera.PiCamera` object.
|
||||
including direct access to any attached :py:class:`openflexure_microscope.camera.base.BaseCamera` and :py:class:`openflexure_stage.stage.OpenFlexureStage` objects.
|
||||
This also allows access to the :py:class:`picamera.PiCamera` object.
|
||||
|
||||
All microscope plugins must subclass :py:class:`openflexure_microscope.plugins.MicroscopePlugin` in order to be loaded by the microscopes :py:class:`openflexure_microscope.plugins.PluginMount`.
|
||||
Extensions can either be loaded from a single Python file, or as a Python package installed to the environment being used.
|
||||
|
||||
Once attached, all methods defined within the plugin class will be accessible from ``<microscope_object>.plugin.<plugin_namespace>.<plugin_method(args)>``.
|
||||
Here, ``<microscope_object>`` is an instance of :py:class:`openflexure_microscope.Microscope`. The ``<plugin_method>`` is the method defined within your plugin class. Finally, ``<plugin_namespace>`` is determined differently depending on the type of plugin.
|
||||
|
||||
Plugins can either be loaded as a single Python file located anywhere on disk, or as a Python package installed to the environment being used. If loaded from a single file, the namespace is set to the file name (excluding .py extension) of the plugin file. If loaded from a package, the namespace is set to the name of the top-level module in the package. For example, if your plugin class definition resides within ``my_openflexure_plugins.microscope.mypluginpackage``, the namespace will be set to ``mypluginpackage``. Where possible, try to use descriptive, unique package names for this reason. For example, rather than name your plugin package ``autofocus``, which would like cause namespace clashes, instead name it ``yourname_autofocus``, or similar.
|
||||
|
||||
Module (single-file) plugins
|
||||
Single-file extensions
|
||||
----------------------------
|
||||
For adding simple functionality, such as a few basic functions and API routes, a single Python file can be loaded as a plugin.
|
||||
For adding simple functionality, such as a few basic functions and API routes, a single Python file can be loaded as a extension. This Python file must contain all of your extension objects, and be located in the applications extensions directory (by default ``~/.openflexure/microscope_extensions``).
|
||||
|
||||
This Python file must contain all of your plugin classes. Relative imports will not work. External modules and packages can be used with absolute imports, however for more complex plugins, it is often worth instead making use of an installable package plugin.
|
||||
|
||||
Package plugins
|
||||
Package extensions
|
||||
---------------
|
||||
Generally, for adding anything other than very simple functionality, plugins should be written as `package distributions <https://packaging.python.org/tutorials/packaging-projects/>`_. This has the advantage of allowing relative imports, so functionality can be easily split over several files. For example, class definitions associated with API routes can be separated from class definitions associated with the microscope plugin.
|
||||
Generally, for adding anything other than very simple functionality, extensions should be written as `package distributions <https://packaging.python.org/tutorials/packaging-projects/>`_. This has the advantage of allowing relative imports, so functionality can be easily split over several files. For example, class definitions associated with API routes can be separated from class definitions associated with the microscope extension.
|
||||
|
||||
The main restriction is that the plugin package must be importable using an absolute import from within the Python environment being used to load your microscope.
|
||||
The main restriction is that the extension package must be importable using an absolute import from within the Python environment being used to load your microscope.
|
||||
|
||||
Loading plugins with microscope_settings.json
|
||||
---------------------------------------------
|
||||
Both types of plugin are loaded by specifying the plugin class in your :ref:`MicroscopeRC`. In the case of a single-file plugin, specify the path to the plugin file, followed by the name of your :py:class:`openflexure_microscope.plugins.MicroscopePlugin` child class, separated by a colon. For packaged plugins, specify the absolute module name in place of the path.
|
||||
|
||||
For example, the plugins section of your microscope_settings.json file may look like:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
...
|
||||
"plugins": [
|
||||
"openflexure_microscope.plugins.default:Plugin",
|
||||
"~/my_plugins/my_plugin_file.py:MyPluginClass",
|
||||
"my_openflexure_plugins.microscope.mypluginpackage:MyPluginClass"
|
||||
]
|
||||
...
|
||||
In order to enable your packaged extension, create a file in the applications extensions directory (by default ``~/.openflexure/microscope_extensions``) which imports your extension object(s) from your module.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
Adding web API routes
|
||||
=====================
|
||||
Adding web API views
|
||||
====================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
|
@ -7,37 +7,38 @@ Adding web API routes
|
|||
|
||||
Introduction
|
||||
------------
|
||||
Plugins can automatically create routes to expose plugin functionality via the web API. Creating API routes for your plugin is strongly recommended, as this is the primary way we encourage interaction with the microscope device.
|
||||
Extensions can create views to expose extension functionality via the web API. Creating API views for your extension is strongly recommended, as this is the primary way we encourage interaction with the microscope device.
|
||||
|
||||
To create API routes, add a dictionary to your plugin class, named ``api_views``. Within this dictionary, each key should be a string defining the route URL, whose value is a class, subclassing :py:class:`openflexure_microscope.api.v1.views.MicroscopeViewPlugin`.
|
||||
To create API views, add a dictionary to your extension class, named ``api_views``. Within this dictionary, each key should be a string defining the route URL, whose value is a class, subclassing :py:class:`openflexure_microscope.api.v1.views.MicroscopeViewExtension`.
|
||||
|
||||
For example, your ``api_views`` dictionary may look like:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class MyPluginClass(MicroscopePlugin):
|
||||
class MyExtensionClass(MicroscopeExtension):
|
||||
|
||||
api_views = {
|
||||
'/myplugin': MyRouteAPI,
|
||||
'/myextension': MyRouteAPI,
|
||||
}
|
||||
|
||||
Here, ``MyRouteAPI`` is a web API plugin class, subclassing :py:class:`openflexure_microscope.api.v1.views.MicroscopeViewPlugin`. If your plugin package were named ``myplugins.package``, an API route would be automatically added at ``/api/v1/plugin/myplugins/package/myplugin``.
|
||||
Here, ``MyRouteAPI`` is a web API extension class, subclassing :py:class:`openflexure_microscope.api.v1.views.MicroscopeViewExtension`. If your extension package were named ``myextensions.package``, an API route would be automatically added at ``/api/v1/extension/myextensions/package/myextension``.
|
||||
|
||||
The full URL that your plugin will attach to is essentially identical to it's full module path. That is, if your plugin is loaded from ``my_microscope_plugins.mypluginpackage:MyPluginClass``, then your plugin routes will appear at ``<microscope_url>/plugin/my_microscope_plugins/mypluginpackage/<route>``. While this means that plugin routes can get long very quickly, they will generally only ever be accessed by client applications, and so this generally should not be a problem.
|
||||
The full URL that your extension will attach to is essentially identical to it's full module path. That is, if your extension is loaded from ``my_microscope_extensions.myextensionpackage:MyExtensionClass``, then your extension views will appear at ``<microscope_url>/extension/my_microscope_extensions/myextensionpackage/<route>``. While this means that extension views can get long very quickly, they will generally only ever be accessed by client applications, and so this generally should not be a problem.
|
||||
|
||||
The MicroscopeViewPlugin class
|
||||
|
||||
The MicroscopeViewExtension class
|
||||
------------------------------
|
||||
|
||||
All API plugin classes must subclass :py:class:`openflexure_microscope.api.v1.views.MicroscopeViewPlugin`, which is itself a subclass of `Flask's MethodView <http://flask.pocoo.org/docs/1.0/api/#flask.views.MethodView>`_. This greatly simplifies defining different functionality associated with different HTTP methods at a single URL route.
|
||||
It is best practice to clearly separate out types of functionality by HTTP method. For example, a GET request should never change the state of the microscope. For this, POST or PUT requests are acceptable. Parameters should be passed to POST and PUT requests as JSON payloads, and your methods should include fallback code for cases where parameters are not passed, or are passed in an invalid format. The DELETE method should only be used in situations where your plugin creates additional URL routes for newly created objects, and should serve only to delete these objects and routes.
|
||||
All API extension classes must subclass :py:class:`openflexure_microscope.api.v1.views.MicroscopeViewExtension`, which is itself a subclass of `Flask's MethodView <http://flask.pocoo.org/docs/1.0/api/#flask.views.MethodView>`_. This greatly simplifies defining different functionality associated with different HTTP methods at a single URL route.
|
||||
It is best practice to clearly separate out types of functionality by HTTP method. For example, a GET request should never change the state of the microscope. For this, POST or PUT requests are acceptable. Parameters should be passed to POST and PUT requests as JSON payloads, and your methods should include fallback code for cases where parameters are not passed, or are passed in an invalid format. The DELETE method should only be used in situations where your extension creates additional URL views for newly created objects, and should serve only to delete these objects and views.
|
||||
|
||||
Each HTTP method maps to a function with the same name, in lowercase. For example, your :py:class:`openflexure_microscope.api.v1.views.MicroscopeViewPlugin` may look like:
|
||||
Each HTTP method maps to a function with the same name, in lowercase. For example, your :py:class:`openflexure_microscope.api.v1.views.MicroscopeViewExtension` may look like:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from flask import jsonify
|
||||
...
|
||||
class MyRouteAPI(MicroscopeViewPlugin):
|
||||
class MyRouteAPI(MicroscopeViewExtension):
|
||||
|
||||
def get(self):
|
||||
# Retrieve some information, without changing the state of the microscope
|
||||
|
|
@ -47,39 +48,39 @@ Each HTTP method maps to a function with the same name, in lowercase. For exampl
|
|||
# Change the state of the microscope based on passed parameters
|
||||
...
|
||||
|
||||
Sometimes you will need to create variable API routes. For example, the built-in routes for managing capture data use capture IDs in the request URL to specify which capture data should be returned. Plugins can also access this functionality. This is done using `Flask variable rules <http://flask.pocoo.org/docs/1.0/quickstart/#variable-rules>`_. Here, variables are added to the URL route string by marking them with ``<variable_name>``, which then passes ``variable_name`` to your request function as a keyword argument.
|
||||
Sometimes you will need to create variable API views. For example, the built-in views for managing capture data use capture IDs in the request URL to specify which capture data should be returned. Extensions can also access this functionality. This is done using `Flask variable rules <http://flask.pocoo.org/docs/1.0/quickstart/#variable-rules>`_. Here, variables are added to the URL route string by marking them with ``<variable_name>``, which then passes ``variable_name`` to your request function as a keyword argument.
|
||||
|
||||
For example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class MyPluginClass(MicroscopePlugin):
|
||||
class MyExtensionClass(MicroscopeExtension):
|
||||
|
||||
api_views = {
|
||||
'/myplugin/<object_id>': MyRouteAPI,
|
||||
'/myextension/<object_id>': MyRouteAPI,
|
||||
}
|
||||
...
|
||||
|
||||
class MyRouteAPI(MicroscopeViewPlugin):
|
||||
class MyRouteAPI(MicroscopeViewExtension):
|
||||
|
||||
def get(self, object_id):
|
||||
# Retrieve some information about object_id
|
||||
this_object = object_dictionary[object_id]
|
||||
...
|
||||
|
||||
Calling plugin methods from routes
|
||||
Calling extension methods from views
|
||||
++++++++++++++++++++++++++++++++++
|
||||
|
||||
Instances of MicroscopeViewPlugin have direct access to their associated microscope plugin methods, without needing to know the plugin namespace in advance. As described earlier in this section, all plugins get attached to the microscope in their own namespace, based on the plugins name. This means there are two equivalent ways to access your plugin methods from a web API plugin:
|
||||
Instances of MicroscopeViewExtension have direct access to their associated microscope extension methods, without needing to know the extension namespace in advance. As described earlier in this section, all extensions get attached to the microscope in their own namespace, based on the extensions name. This means there are two equivalent ways to access your extension methods from a web API extension:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
...
|
||||
# Call a method from our plugin, using the MicroscopeViewPlugin.plugin shortcut
|
||||
self.plugin.my_plugin_method()
|
||||
# Call a method from our extension, using the MicroscopeViewExtension.extension shortcut
|
||||
self.extension.my_extension_method()
|
||||
|
||||
# Call a method from our plugin, using the full route
|
||||
self.microscope.my_plugin_name.my_plugin_method()
|
||||
# Call a method from our extension, using the full route
|
||||
self.microscope.my_extension_name.my_extension_method()
|
||||
...
|
||||
|
||||
Building responses
|
||||
|
|
@ -98,14 +99,14 @@ An example web route with simple responses may look like:
|
|||
from flask import jsonify
|
||||
...
|
||||
|
||||
class MyPluginClass(MicroscopePlugin):
|
||||
class MyExtensionClass(MicroscopeExtension):
|
||||
|
||||
api_views = {
|
||||
'/myplugin/<object_id>': MyRouteAPI,
|
||||
'/myextension/<object_id>': MyRouteAPI,
|
||||
}
|
||||
...
|
||||
|
||||
class MyRouteAPI(MicroscopeViewPlugin):
|
||||
class MyRouteAPI(MicroscopeViewExtension):
|
||||
|
||||
def get(self, object_id):
|
||||
|
||||
|
|
@ -135,7 +136,7 @@ To ease obtaining values from a JSON payload attached to an HTTP POST request, y
|
|||
.. code-block:: python
|
||||
|
||||
...
|
||||
class MyRouteAPI(MicroscopeViewPlugin):
|
||||
class MyRouteAPI(MicroscopeViewExtension):
|
||||
|
||||
def post(self):
|
||||
# Get payload JSON from request
|
||||
|
|
@ -144,7 +145,7 @@ To ease obtaining values from a JSON payload attached to an HTTP POST request, y
|
|||
# Try to find value associated with 'my_string' key.
|
||||
# If that key doesn't exist in the payload, return '' instead.
|
||||
# If a value does exist, convert it to a string, regardless of its original type.
|
||||
new_plugin_string = payload.param('my_string', default='', convert=str)
|
||||
new_extension_string = payload.param('my_string', default='', convert=str)
|
||||
...
|
||||
|
||||
.. autoclass:: openflexure_microscope.api.utilities.JsonResponse
|
||||
|
|
|
|||
|
|
@ -1,33 +1,58 @@
|
|||
Basic plugin structure
|
||||
===========================
|
||||
Basic extension structure
|
||||
=========================
|
||||
|
||||
As described earlier, a plugin must subclass :py:class:`openflexure_microscope.plugins.MicroscopePlugin`. Each plugin in described by a single class, containing any number of methods. The methods defined within your plugin class will be attached to the microscope. By subclassing :py:class:`openflexure_microscope.plugins.MicroscopePlugin`, your methods automatically get access to the microscope object your plugin is attached to, through ``self.microscope``. This is an instance of :py:class:`openflexure_microscope.Microscope`, and gives unrestricted access to the attached camera and stage hardware.
|
||||
An extension starts as a simple instance of :py:class:`openflexure_microscope.common.flask_labthings.extensions.BaseExtension`.
|
||||
Each extension is described by a single ``BaseExtension`` instance, containing any number of methods, API views, and additional hardware components.
|
||||
|
||||
For example, a simple plugin file named ``myplugin.py``, may look like:
|
||||
In order to access the currently running microscope object, use the :py:func:`openflexure_microscope.common.flask_labthings.find` function, with the argument ``"org.openflexure.microscope"``. Likewise, any new components attached by other extensions can be found using their full name, as above.
|
||||
|
||||
A simple extension file, with no API views but application-available methods may look like:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from openflexure_microscope.plugins import MicroscopePlugin
|
||||
from openflexure_microscope.common.flask_labthings.extensions import BaseExtension
|
||||
from openflexure_microscope.common.flask_labthings.find import find_component
|
||||
|
||||
class MyPlugin(MicroscopePlugin):
|
||||
|
||||
def identify(self):
|
||||
"""
|
||||
Demonstrate access to Microscope.camera, and Microscope.stage
|
||||
"""
|
||||
def identify():
|
||||
"""
|
||||
Demonstrate access to Microscope.camera, and Microscope.stage
|
||||
"""
|
||||
microscope = find_component("org.openflexure.microscope")
|
||||
|
||||
parent_camera = self.microscope.camera
|
||||
parent_stage = self.microscope.stage
|
||||
parent_camera = microscope.camera
|
||||
parent_stage = microscope.stage
|
||||
|
||||
response = "My parent camera is {}, and my parent stage is {}.".format(parent_camera,
|
||||
parent_stage)
|
||||
return response
|
||||
response = "My parent camera is {}, and my parent stage is {}.".format(parent_camera,
|
||||
parent_stage)
|
||||
return response
|
||||
|
||||
def hello_world(self):
|
||||
"""
|
||||
Demonstrate passive method
|
||||
"""
|
||||
def hello_world():
|
||||
"""
|
||||
Demonstrate passive method
|
||||
"""
|
||||
|
||||
return "Hello world!"
|
||||
return "Hello world!"
|
||||
|
||||
|
||||
When this plugin is loaded and attached to a microscope object named ``microscope``, the plugin methods will be available at ``microscope.plugin.myplugin.identify()`` and ``microscope.plugin.myplugin.hello_world()``.
|
||||
# Create your extension object
|
||||
my_extension = BaseExtension("com.myname.myextension", version="0.0.0")
|
||||
|
||||
# Add methods to your extension
|
||||
my_extension.add_method(identify, "identify")
|
||||
my_extension.add_method(hello_world, "hello_world")
|
||||
|
||||
|
||||
Once this extension is loaded, any other extensions will have access to your methods:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from openflexure_microscope.common.flask_labthings.find import find_extension
|
||||
|
||||
def test_extension_method():
|
||||
# Find your extension. Returns None if it hasn't been found.
|
||||
my_found_extension = find_extension("com.myname.myextension")
|
||||
|
||||
# Call a function from your extension
|
||||
if my_found_extension:
|
||||
my_found_extension.identify()
|
||||
|
|
@ -6,21 +6,14 @@ Install
|
|||
|
||||
Stable installation
|
||||
+++++++++++++++++++
|
||||
For most users, this is the reccommended installation method.
|
||||
For most users, the OpenFlexure Microscope software should be installed using our `pre-built Raspbian SD card image. <https://openflexure.org/projects/microscope/install>`_
|
||||
|
||||
- Run ``curl -LSs get.openflexure.org/microscope |sudo bash``
|
||||
- See the `GitLab repo <https://gitlab.com/openflexure/openflexure-microscope-cli>`_ for details.
|
||||
- Follow on-screen prompts
|
||||
|
||||
Developer and non-interactive installation
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
The installer script can pull the latest development package from our git repository, and use install into a developer environment using Poetry.
|
||||
Options also exist to run the installer without any user prompts.
|
||||
|
||||
See the `installer script GitLab repo <https://gitlab.com/openflexure/openflexure-microscope-cli>`_ for details.
|
||||
|
||||
Manual installation
|
||||
+++++++++++++++++++
|
||||
|
||||
For offline (i.e. no real microscope connected) development, a basic development server can run on any system.
|
||||
|
||||
- (Recommended) create and activate a virtual environment
|
||||
- Install non-python dependencies with ``sudo apt-get install libatlas-base-dev libjasper-dev libjpeg-dev``
|
||||
- Install `Poetry <https://github.com/sdispater/poetry>`_, clone this repo, and ``poetry install`` from inside the repo.
|
||||
|
|
@ -28,6 +21,6 @@ Manual installation
|
|||
Managing the server
|
||||
-------------------
|
||||
|
||||
Managing the server through the installer script's CLI is documented `on our website <https://openflexure.gitlab.io/projects/microscope/#managing-the-microscope-server>`_.
|
||||
Managing the server through the installer script's CLI is documented `on our website <https://openflexure.org/projects/microscope/install/#managing-the-microscope-server>`_.
|
||||
|
||||
This includes starting the server as a background service, as well as starting a development server with real-time debug logging.
|
||||
Loading…
Add table
Add a link
Reference in a new issue