Merge remote-tracking branch 'origin/master' into labthings-070
This commit is contained in:
commit
ab3c4b3745
7 changed files with 140 additions and 111 deletions
|
|
@ -1,5 +1,5 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
from gevent import monkey
|
from labthings.server import monkey
|
||||||
|
|
||||||
monkey.patch_all()
|
monkey.patch_all()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,7 @@ def capture(
|
||||||
annotations=annotations,
|
annotations=annotations,
|
||||||
tags=tags,
|
tags=tags,
|
||||||
metadata=metadata,
|
metadata=metadata,
|
||||||
|
cache_key=folder
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ import time
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from PIL import Image, ImageFont, ImageDraw
|
from PIL import Image, ImageFont, ImageDraw
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import gevent
|
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
|
@ -221,7 +220,7 @@ class MissingCamera(BaseCamera):
|
||||||
# While the iterator is not closed
|
# While the iterator is not closed
|
||||||
try:
|
try:
|
||||||
while True:
|
while True:
|
||||||
gevent.sleep(1) # Only serve frames at 1fps
|
time.sleep(1) # Only serve frames at 1fps
|
||||||
# Reset stream
|
# Reset stream
|
||||||
self.stream.seek(0)
|
self.stream.seek(0)
|
||||||
self.stream.truncate()
|
self.stream.truncate()
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,6 @@ from PIL import Image
|
||||||
import dateutil.parser
|
import dateutil.parser
|
||||||
import atexit
|
import atexit
|
||||||
|
|
||||||
from gevent.fileobject import FileObjectThread
|
|
||||||
import gevent
|
|
||||||
|
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
from openflexure_microscope.camera import piexif
|
from openflexure_microscope.camera import piexif
|
||||||
|
|
@ -129,10 +126,6 @@ class CaptureObject(object):
|
||||||
"""Create a new StreamObject, to manage capture data."""
|
"""Create a new StreamObject, to manage capture data."""
|
||||||
# Stream for buffering capture data
|
# Stream for buffering capture data
|
||||||
self.stream = io.BytesIO()
|
self.stream = io.BytesIO()
|
||||||
# Event to notify when the stream has finished writing to disk
|
|
||||||
self.file_ready = gevent.event.Event()
|
|
||||||
# Access lock for the disk file
|
|
||||||
self.lock = gevent.lock.BoundedSemaphore()
|
|
||||||
|
|
||||||
# Store a nice ID
|
# Store a nice ID
|
||||||
self.id = uuid.uuid4() #: str: Unique capture ID
|
self.id = uuid.uuid4() #: str: Unique capture ID
|
||||||
|
|
@ -160,19 +153,12 @@ class CaptureObject(object):
|
||||||
def write(self, s):
|
def write(self, s):
|
||||||
self.stream.write(s)
|
self.stream.write(s)
|
||||||
|
|
||||||
def _stream_to_file(self):
|
|
||||||
with self.lock:
|
|
||||||
logging.info(f"Writing to disk {self.file}")
|
|
||||||
with FileObjectThread(open(self.file, "wb"), 'wb') as outfile:
|
|
||||||
outfile.write(self.stream.getbuffer())
|
|
||||||
self.stream.close()
|
|
||||||
self.file_ready.set()
|
|
||||||
logging.info(f"Finished writing to disk {self.file}")
|
|
||||||
|
|
||||||
def flush(self):
|
def flush(self):
|
||||||
logging.debug(f"Flushing {self.file}")
|
logging.info(f"Writing to disk {self.file}")
|
||||||
gevent.spawn(self._stream_to_file)
|
with open(self.file, "wb") as outfile:
|
||||||
logging.debug(f"Returning flushing {self.file}")
|
outfile.write(self.stream.getbuffer())
|
||||||
|
self.stream.close()
|
||||||
|
logging.info(f"Finished writing to disk {self.file}")
|
||||||
|
|
||||||
def open(self, mode):
|
def open(self, mode):
|
||||||
return open(self.file, mode)
|
return open(self.file, mode)
|
||||||
|
|
@ -275,20 +261,18 @@ class CaptureObject(object):
|
||||||
global EXIF_FORMATS
|
global EXIF_FORMATS
|
||||||
|
|
||||||
if self.format.upper() in EXIF_FORMATS and self.exists:
|
if self.format.upper() in EXIF_FORMATS and self.exists:
|
||||||
self.file_ready.wait()
|
logging.debug("Writing exif data to capture file")
|
||||||
with self.lock:
|
# Extract current Exif data
|
||||||
logging.debug("Writing exif data to capture file")
|
exif_dict = piexif.load(self.file)
|
||||||
# Extract current Exif data
|
# Serialize metadata
|
||||||
exif_dict = piexif.load(self.file)
|
metadata_string = json.dumps(self.metadata, cls=JSONEncoder)
|
||||||
# Serialize metadata
|
logging.debug(f"Saving metadata string to file: {metadata_string}")
|
||||||
metadata_string = json.dumps(self.metadata, cls=JSONEncoder)
|
# Insert metadata into exif_dict
|
||||||
logging.debug(f"Saving metadata string to file: {metadata_string}")
|
exif_dict["Exif"][piexif.ExifIFD.UserComment] = metadata_string.encode()
|
||||||
# Insert metadata into exif_dict
|
# Convert new exif dict to exif bytes
|
||||||
exif_dict["Exif"][piexif.ExifIFD.UserComment] = metadata_string.encode()
|
exif_bytes = piexif.dump(exif_dict)
|
||||||
# Convert new exif dict to exif bytes
|
# Insert exif into file
|
||||||
exif_bytes = piexif.dump(exif_dict)
|
piexif.insert(exif_bytes, self.file)
|
||||||
# Insert exif into file
|
|
||||||
piexif.insert(exif_bytes, self.file)
|
|
||||||
logging.info(f"Finished saving metadata to {self.file}")
|
logging.info(f"Finished saving metadata to {self.file}")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,7 @@ import logging
|
||||||
import pkg_resources
|
import pkg_resources
|
||||||
import uuid
|
import uuid
|
||||||
from typing import Tuple
|
from typing import Tuple
|
||||||
|
from expiringdict import ExpiringDict
|
||||||
import gevent
|
|
||||||
|
|
||||||
from openflexure_microscope.captures import CaptureManager
|
from openflexure_microscope.captures import CaptureManager
|
||||||
|
|
||||||
|
|
@ -61,6 +60,10 @@ class Microscope:
|
||||||
# Apply settings loaded from file
|
# Apply settings loaded from file
|
||||||
self.update_settings(self.settings_file.load())
|
self.update_settings(self.settings_file.load())
|
||||||
|
|
||||||
|
# Data cache
|
||||||
|
self.configuration_cache = ExpiringDict(max_len=100, max_age_seconds=3600)
|
||||||
|
self.metadata_cache = ExpiringDict(max_len=100, max_age_seconds=3600)
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
"""Create microscope on context enter."""
|
"""Create microscope on context enter."""
|
||||||
return self
|
return self
|
||||||
|
|
@ -237,8 +240,7 @@ class Microscope:
|
||||||
# Save config to file
|
# Save config to file
|
||||||
self.settings_file.save(current_config, backup=True)
|
self.settings_file.save(current_config, backup=True)
|
||||||
|
|
||||||
@property
|
def force_get_configuration(self):
|
||||||
def configuration(self):
|
|
||||||
with self.lock:
|
with self.lock:
|
||||||
initial_configuration = self.configuration_file.load()
|
initial_configuration = self.configuration_file.load()
|
||||||
|
|
||||||
|
|
@ -262,37 +264,62 @@ class Microscope:
|
||||||
initial_configuration.update(current_configuration)
|
initial_configuration.update(current_configuration)
|
||||||
return initial_configuration
|
return initial_configuration
|
||||||
|
|
||||||
|
def get_configuration(self, cache_key=None):
|
||||||
|
if cache_key:
|
||||||
|
cached_config = self.configuration_cache.get(cache_key, None)
|
||||||
|
if cached_config:
|
||||||
|
return cached_config
|
||||||
|
else:
|
||||||
|
full_config = self.force_get_configuration()
|
||||||
|
self.configuration_cache[cache_key] = full_config
|
||||||
|
return full_config
|
||||||
|
return self.force_get_configuration()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def metadata(self):
|
def configuration(self):
|
||||||
|
return self.get_configuration()
|
||||||
|
|
||||||
|
def force_get_metadata(self):
|
||||||
"""
|
"""
|
||||||
Microscope system metadata, to be applied to basically all captures
|
Read cachable bits of microscope metadata.
|
||||||
|
Currently ID, settings, and configuration can be cached
|
||||||
"""
|
"""
|
||||||
with Timer("Reading settings:"):
|
|
||||||
settings = self.read_settings(full=False)
|
|
||||||
with Timer("Reading state:"):
|
|
||||||
state = self.state
|
|
||||||
with Timer("Reading configuration:"):
|
|
||||||
configuration = self.configuration
|
|
||||||
system_metadata = {
|
system_metadata = {
|
||||||
"id": self.id,
|
"id": self.id,
|
||||||
"settings": settings,
|
"settings": self.read_settings(full=False),
|
||||||
"state": state,
|
"configuration": self.get_configuration(),
|
||||||
"configuration": configuration,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return system_metadata
|
return system_metadata
|
||||||
|
|
||||||
def add_metadata_to_capture(self, output, metadata, annotations, tags):
|
def get_metadata(self, cache_key=None):
|
||||||
logging.debug(f"Waiting for {output.file}")
|
"""
|
||||||
# Wait for the file to be written to disk
|
Read microscope metadata, with partial caching
|
||||||
with Timer("Waiting for file:"):
|
"""
|
||||||
output.file_ready.wait()
|
metadata = {}
|
||||||
|
|
||||||
|
# Load cached bits of metadata
|
||||||
|
if cache_key:
|
||||||
|
logging.debug(f"Reading cached microscope metadata: {cache_key}")
|
||||||
|
metadata = self.metadata_cache.get(cache_key, None)
|
||||||
|
if not metadata:
|
||||||
|
logging.debug(f"Building and caching microscope metadata: {cache_key}")
|
||||||
|
metadata = self.force_get_metadata()
|
||||||
|
self.metadata_cache[cache_key] = metadata
|
||||||
|
else:
|
||||||
|
logging.debug(f"Building microscope metadata: {cache_key}")
|
||||||
|
metadata = self.force_get_metadata()
|
||||||
|
|
||||||
with Timer("Building metadata"):
|
# Keys that should never be cached
|
||||||
full_metadata = {"instrument": self.metadata, **metadata}
|
metadata.update({
|
||||||
with Timer("Writing metadata to file"):
|
"state": self.state,
|
||||||
output.put_and_save(tags, annotations, full_metadata)
|
})
|
||||||
logging.info(f"Finished injecting EXIF data into {output.file}")
|
|
||||||
|
return metadata
|
||||||
|
|
||||||
|
@property
|
||||||
|
def metadata(self):
|
||||||
|
return self.get_metadata()
|
||||||
|
|
||||||
def capture(
|
def capture(
|
||||||
self,
|
self,
|
||||||
|
|
@ -306,6 +333,7 @@ class Microscope:
|
||||||
annotations: dict = None,
|
annotations: dict = None,
|
||||||
tags: list = None,
|
tags: list = None,
|
||||||
metadata: dict = None,
|
metadata: dict = None,
|
||||||
|
cache_key: str = None
|
||||||
):
|
):
|
||||||
logging.debug(f"Microscope capturing to {filename}")
|
logging.debug(f"Microscope capturing to {filename}")
|
||||||
if not annotations:
|
if not annotations:
|
||||||
|
|
@ -315,6 +343,10 @@ class Microscope:
|
||||||
if not tags:
|
if not tags:
|
||||||
tags = []
|
tags = []
|
||||||
|
|
||||||
|
# Read metadata for capture
|
||||||
|
full_metadata = {"instrument": self.get_metadata(cache_key), **metadata}
|
||||||
|
|
||||||
|
# Do capture
|
||||||
with self.camera.lock:
|
with self.camera.lock:
|
||||||
# Create output object
|
# Create output object
|
||||||
output = self.captures.new_image(
|
output = self.captures.new_image(
|
||||||
|
|
@ -322,7 +354,7 @@ class Microscope:
|
||||||
)
|
)
|
||||||
|
|
||||||
# Capture to output object
|
# Capture to output object
|
||||||
logging.info("Starting microscope capture...")
|
logging.info(f"Starting microscope capture {filename}")
|
||||||
self.camera.capture(
|
self.camera.capture(
|
||||||
output,
|
output,
|
||||||
use_video_port=use_video_port,
|
use_video_port=use_video_port,
|
||||||
|
|
@ -331,11 +363,7 @@ class Microscope:
|
||||||
fmt=fmt,
|
fmt=fmt,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Gether metadata from hardware in a greenlet
|
output.put_and_save(tags, annotations, full_metadata)
|
||||||
#gevent.get_hub().threadpool.spawn(self.add_metadata_to_capture, output, metadata, annotations, tags)
|
logging.debug(f"Finished capture to {output.file}")
|
||||||
gevent.spawn(self.add_metadata_to_capture, output, metadata, annotations, tags)
|
|
||||||
#self.add_metadata_to_capture(output, metadata, annotations, tags)
|
|
||||||
|
|
||||||
logging.info(f"Finished capture to {output.file}")
|
|
||||||
|
|
||||||
return output
|
return output
|
||||||
|
|
|
||||||
97
poetry.lock
generated
97
poetry.lock
generated
|
|
@ -91,25 +91,19 @@ d = ["aiohttp (>=3.3.2)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
category = "main"
|
category = "main"
|
||||||
description = ""
|
description = "Calibration and mapping between stage and camera coordinates in a microscope"
|
||||||
name = "camera-stage-mapping"
|
name = "camera-stage-mapping"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "^3.6"
|
python-versions = ">=3.6,<4.0"
|
||||||
version = "0.1.1"
|
version = "0.1.2"
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
numpy = "^1.17"
|
numpy = ">=1.17,<2.0"
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
all = ["opencv-python-headless (^4.1)", "scipy (^1.4)"]
|
all = ["opencv-python-headless (>=4.1,<5.0)", "scipy (>=1.4,<2.0)"]
|
||||||
correlation = ["opencv-python-headless (^4.1)", "scipy (^1.4)"]
|
correlation = ["opencv-python-headless (>=4.1,<5.0)", "scipy (>=1.4,<2.0)"]
|
||||||
ipython = []
|
|
||||||
ofmclient = []
|
|
||||||
|
|
||||||
[package.source]
|
|
||||||
reference = "f5d0d8977a2511b20b47061ac203551f97b8b4ab"
|
|
||||||
type = "git"
|
|
||||||
url = "https://gitlab.com/openflexure/microscope-extensions/camera-stage-mapping.git"
|
|
||||||
[[package]]
|
[[package]]
|
||||||
category = "main"
|
category = "main"
|
||||||
description = "Pure Python CBOR (de)serializer with extensive tag support"
|
description = "Pure Python CBOR (de)serializer with extensive tag support"
|
||||||
|
|
@ -174,6 +168,17 @@ optional = false
|
||||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||||
version = "0.16"
|
version = "0.16"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
category = "main"
|
||||||
|
description = "Dictionary with auto-expiring values for caching purposes"
|
||||||
|
name = "expiringdict"
|
||||||
|
optional = false
|
||||||
|
python-versions = "*"
|
||||||
|
version = "1.2.1"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
tests = ["dill", "coverage", "coveralls", "mock", "nose"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
category = "main"
|
category = "main"
|
||||||
description = "A simple framework for building complex web applications."
|
description = "A simple framework for building complex web applications."
|
||||||
|
|
@ -219,7 +224,7 @@ description = "Coroutine-based network library"
|
||||||
name = "gevent"
|
name = "gevent"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*"
|
python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*"
|
||||||
version = "20.6.0"
|
version = "20.6.2"
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
cffi = ">=1.12.2"
|
cffi = ">=1.12.2"
|
||||||
|
|
@ -231,9 +236,9 @@ setuptools = "*"
|
||||||
[package.extras]
|
[package.extras]
|
||||||
dnspython = ["dnspython (>=1.16.0)", "idna"]
|
dnspython = ["dnspython (>=1.16.0)", "idna"]
|
||||||
docs = ["repoze.sphinx.autointerface", "sphinxcontrib-programoutput"]
|
docs = ["repoze.sphinx.autointerface", "sphinxcontrib-programoutput"]
|
||||||
monitor = ["psutil (>=5.6.1)", "psutil (5.6.3)"]
|
monitor = ["psutil (>=5.7.0)"]
|
||||||
recommended = ["dnspython (>=1.16.0)", "idna", "cffi (>=1.12.2)", "psutil (>=5.6.1)", "psutil (5.6.3)", "selectors2", "backports.socketpair"]
|
recommended = ["dnspython (>=1.16.0)", "idna", "cffi (>=1.12.2)", "selectors2", "backports.socketpair", "psutil (>=5.7.0)"]
|
||||||
test = ["dnspython (>=1.16.0)", "idna", "requests", "objgraph", "cffi (>=1.12.2)", "psutil (>=5.6.1)", "psutil (5.6.3)", "selectors2", "futures", "mock", "backports.socketpair", "contextvars (2.4)", "coverage (<5.0)", "coveralls (>=1.7.0)"]
|
test = ["dnspython (>=1.16.0)", "idna", "requests", "objgraph", "cffi (>=1.12.2)", "selectors2", "futures", "mock", "backports.socketpair", "contextvars (2.4)", "coverage (<5.0)", "coveralls (>=1.7.0)", "psutil (>=5.7.0)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
category = "main"
|
category = "main"
|
||||||
|
|
@ -841,7 +846,7 @@ testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"]
|
||||||
rpi = ["RPi.GPIO"]
|
rpi = ["RPi.GPIO"]
|
||||||
|
|
||||||
[metadata]
|
[metadata]
|
||||||
content-hash = "0607e2b39e77ac530c84a4c2e1dfc14b373ca96322b634e52001cb0591b4b3e7"
|
content-hash = "94e35da71817800c617a54e54dc937df62953fa95caa2e2e05f69b1163582c4c"
|
||||||
python-versions = "^3.6"
|
python-versions = "^3.6"
|
||||||
|
|
||||||
[metadata.files]
|
[metadata.files]
|
||||||
|
|
@ -873,7 +878,10 @@ black = [
|
||||||
{file = "black-18.9b0-py36-none-any.whl", hash = "sha256:817243426042db1d36617910df579a54f1afd659adb96fc5032fcf4b36209739"},
|
{file = "black-18.9b0-py36-none-any.whl", hash = "sha256:817243426042db1d36617910df579a54f1afd659adb96fc5032fcf4b36209739"},
|
||||||
{file = "black-18.9b0.tar.gz", hash = "sha256:e030a9a28f542debc08acceb273f228ac422798e5215ba2a791a6ddeaaca22a5"},
|
{file = "black-18.9b0.tar.gz", hash = "sha256:e030a9a28f542debc08acceb273f228ac422798e5215ba2a791a6ddeaaca22a5"},
|
||||||
]
|
]
|
||||||
camera-stage-mapping = []
|
camera-stage-mapping = [
|
||||||
|
{file = "camera-stage-mapping-0.1.2.tar.gz", hash = "sha256:0d1a6f93b3a60054bc8057359464adbb40488b4daeb93c672b90e4fe525e7b69"},
|
||||||
|
{file = "camera_stage_mapping-0.1.2-py3-none-any.whl", hash = "sha256:3f3da3e95792e811e6808173c368be67ebb00e6f7d7e37d68cb36fa765d13da9"},
|
||||||
|
]
|
||||||
cbor2 = [
|
cbor2 = [
|
||||||
{file = "cbor2-5.1.0.tar.gz", hash = "sha256:43ce11e8c2fe4971d386d1a60cf83bfa0a4a667b97668ba76acbf5e6398821aa"},
|
{file = "cbor2-5.1.0.tar.gz", hash = "sha256:43ce11e8c2fe4971d386d1a60cf83bfa0a4a667b97668ba76acbf5e6398821aa"},
|
||||||
]
|
]
|
||||||
|
|
@ -927,6 +935,9 @@ docutils = [
|
||||||
{file = "docutils-0.16-py2.py3-none-any.whl", hash = "sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af"},
|
{file = "docutils-0.16-py2.py3-none-any.whl", hash = "sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af"},
|
||||||
{file = "docutils-0.16.tar.gz", hash = "sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc"},
|
{file = "docutils-0.16.tar.gz", hash = "sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc"},
|
||||||
]
|
]
|
||||||
|
expiringdict = [
|
||||||
|
{file = "expiringdict-1.2.1.tar.gz", hash = "sha256:fe2ba427220425c3c8a3d29f6d2e2985bcee323f8bcd4021e68ebefbd90d8250"},
|
||||||
|
]
|
||||||
flask = [
|
flask = [
|
||||||
{file = "Flask-1.1.2-py2.py3-none-any.whl", hash = "sha256:8a4fdd8936eba2512e9c85df320a37e694c93945b33ef33c89946a340a238557"},
|
{file = "Flask-1.1.2-py2.py3-none-any.whl", hash = "sha256:8a4fdd8936eba2512e9c85df320a37e694c93945b33ef33c89946a340a238557"},
|
||||||
{file = "Flask-1.1.2.tar.gz", hash = "sha256:4efa1ae2d7c9865af48986de8aeb8504bf32c7f3d6fdc9353d34b21f4b127060"},
|
{file = "Flask-1.1.2.tar.gz", hash = "sha256:4efa1ae2d7c9865af48986de8aeb8504bf32c7f3d6fdc9353d34b21f4b127060"},
|
||||||
|
|
@ -939,28 +950,34 @@ future = [
|
||||||
{file = "future-0.18.2.tar.gz", hash = "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"},
|
{file = "future-0.18.2.tar.gz", hash = "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"},
|
||||||
]
|
]
|
||||||
gevent = [
|
gevent = [
|
||||||
{file = "gevent-20.6.0-cp27-cp27m-macosx_10_15_x86_64.whl", hash = "sha256:ccac571d5bb7a85be736c0c5e241a5263a36904b358edc2dcceaa3b54a18eb25"},
|
{file = "gevent-20.6.2-cp27-cp27m-macosx_10_15_x86_64.whl", hash = "sha256:b03890bbddbae5667f5baad517417056496ff5e92c3c7945b27cc08f55a9fcb2"},
|
||||||
{file = "gevent-20.6.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:eb74efa2486b8c2576eaf2ef6a3a782b1a38fe414390a4a3ab411efca91e54f5"},
|
{file = "gevent-20.6.2-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:1ea0d34cb78cdf37870be3bfb9330ebda89197bed9e048c14f4a90dec19a33e0"},
|
||||||
{file = "gevent-20.6.0-cp27-cp27m-win32.whl", hash = "sha256:2701ac60993fb74f1f548348641c78160215aa9707b08e37caf10fe63712f5d9"},
|
{file = "gevent-20.6.2-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:73eb4cf3114fbb5dd801bd0b93941adfa2fa6d99e91976c20a121ea14b8b39b9"},
|
||||||
{file = "gevent-20.6.0-cp27-cp27m-win_amd64.whl", hash = "sha256:1a449ab1390fd48cc509d846a62d867e7c548f8f9d5632ca012af2741c97e6d6"},
|
{file = "gevent-20.6.2-cp27-cp27m-win32.whl", hash = "sha256:f41cc8e853ac2252bc58f6feabd74b8aae613e2d19097c5373463122f4dc08f0"},
|
||||||
{file = "gevent-20.6.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:e963584dd93b8268764297b13d6d99e24d9f1d63d921d5260286b3cde92f2851"},
|
{file = "gevent-20.6.2-cp27-cp27m-win_amd64.whl", hash = "sha256:d3baff87d935a5eeffb0e4f7cd5ffe258d2430cd62aeee2e5396f85da07df435"},
|
||||||
{file = "gevent-20.6.0-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:56f2949b068e922bce3695e80bbf484115536901ad77e833b0e1ac7e4d996654"},
|
{file = "gevent-20.6.2-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:7d8408854ce892f987305a0e9bf5c051f4ea29453665454396d6afb620c719b6"},
|
||||||
{file = "gevent-20.6.0-cp35-cp35m-win32.whl", hash = "sha256:d4376e9d8f6ecf68de182fed99eb7d7f255b15170edb0bc7d06720552f541c6f"},
|
{file = "gevent-20.6.2-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:ea2e4584950186b71d648bde6af40dae4d4c6f43db25a732ec056b27a7a83afe"},
|
||||||
{file = "gevent-20.6.0-cp35-cp35m-win_amd64.whl", hash = "sha256:6d172744705aa401b0014142343e866f98668bd86fc5ba070ace22f4e98f76dd"},
|
{file = "gevent-20.6.2-cp35-cp35m-win32.whl", hash = "sha256:c0f4340e40e0f9dfe93a52a12ddf5b1eeda9bbc89b99bf3b9b23acab0dfae0a4"},
|
||||||
{file = "gevent-20.6.0-cp36-cp36m-macosx_10_15_x86_64.whl", hash = "sha256:664f061993e1104f901801dc7b305824389f5c05936e7daeabee56bf76837726"},
|
{file = "gevent-20.6.2-cp35-cp35m-win_amd64.whl", hash = "sha256:13c74d6784ef5ada2666abf2bb310d27a1d14291f7cac46148f336b19f714d40"},
|
||||||
{file = "gevent-20.6.0-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:f9521f4721f50f7e1ade81bf61c17387d2cd901de65701038e74c5e87e2785cc"},
|
{file = "gevent-20.6.2-cp36-cp36m-macosx_10_15_x86_64.whl", hash = "sha256:78bd94f6f2ac366155169df3507068f6381f2ad77625633189ce183f86a57597"},
|
||||||
{file = "gevent-20.6.0-cp36-cp36m-win32.whl", hash = "sha256:779ab592efc30550879546bfe83ee6cb42538080fcd495817868ed7b7b17c3ff"},
|
{file = "gevent-20.6.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:0b16dd85eddaf6acdad373ce90ed4da09ef466cbc5e0ee5932d13f099929e844"},
|
||||||
{file = "gevent-20.6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:94241c8e962b5eab49d5459b319454161bdb1a0c53ffe883a94a9952ab916345"},
|
{file = "gevent-20.6.2-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:a47556cac07e31b3cef8fd701599b3b1365961fe3736471f41807ffa27c5c848"},
|
||||||
{file = "gevent-20.6.0-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:77d9237d17a64054e1a03883d3265d27fe98e49f41cf1dd22b42ee3206f99b53"},
|
{file = "gevent-20.6.2-cp36-cp36m-win32.whl", hash = "sha256:bef18b8bd3b728240b9bbd699737216b793d6c97b482431f69dcbe328ad73692"},
|
||||||
{file = "gevent-20.6.0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:5520e72522906483559bd12eacabf72f130972dabe8e05d159e227eca97cbcf9"},
|
{file = "gevent-20.6.2-cp36-cp36m-win_amd64.whl", hash = "sha256:d0a67a20ce325f6a2068e0bd9fbf83db8a5f5ced972ed8ac5c20079a7d98c7d1"},
|
||||||
{file = "gevent-20.6.0-cp37-cp37m-win32.whl", hash = "sha256:8625256ec3acc9b095050d15c256ad9ce3f8bac833982bd712af14a83991b067"},
|
{file = "gevent-20.6.2-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:b17915b65b49a425115ddc3087484c81b1e47ce38c931d18bb14e453753e4d06"},
|
||||||
{file = "gevent-20.6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:e5e1f2024fbf753549703cde287f6cc940248d860f617d657c3b3ea0638035f0"},
|
{file = "gevent-20.6.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:ebb8a545112110e3a6edf905ae1556b0538fc148c743aa7d8cfaebbbc23de31d"},
|
||||||
{file = "gevent-20.6.0-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:3165510ed37f8ea38345a8a2464d5c700f7cc8828194612889f6c583e2889ff1"},
|
{file = "gevent-20.6.2-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6c864b5604166ac8351e3128a1135b883b9e978fd24afbd75a249dcb42bc8ab5"},
|
||||||
{file = "gevent-20.6.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:b5f73ef6442e72774359afcf892c47cbdcb0811a4577fc1b4b11191aa362e353"},
|
{file = "gevent-20.6.2-cp37-cp37m-win32.whl", hash = "sha256:e5ca5ee80a9d9e697c9fc22b4bbce9ad06870f83fc8e7774e5504892ef702476"},
|
||||||
{file = "gevent-20.6.0-cp38-cp38-win32.whl", hash = "sha256:6e232e1870dfd2e32aaa26083488c116492e9378d288dffca0e38c1521ba502d"},
|
{file = "gevent-20.6.2-cp37-cp37m-win_amd64.whl", hash = "sha256:f2a02d9004ccb18edd9eaf6f25da9a7763de41a69754d5e4d872a8cbf8bd0b72"},
|
||||||
{file = "gevent-20.6.0-cp38-cp38-win_amd64.whl", hash = "sha256:dffc2d2bb815a6ab0370e3e478094e0c43f610a3466e241bc150af21293763db"},
|
{file = "gevent-20.6.2-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:354f932c284fa45826b32f42927d892096cce05671b50b3ff59528230217ad47"},
|
||||||
{file = "gevent-20.6.0-pp27-pypy_73-win32.whl", hash = "sha256:ea79e58f7927939ac60d3c73aa9e5d6186c3fea77e4414d768d658e1f58ef7a3"},
|
{file = "gevent-20.6.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:67776cb33b638a3c61a0351d9d1e8f33a46b47de619e249de1159892f9ff035c"},
|
||||||
{file = "gevent-20.6.0.tar.gz", hash = "sha256:51cab8f792961a75da39452586bb3474eaa1607ab2c83398745ef1b9ba09cb92"},
|
{file = "gevent-20.6.2-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:68764aca061bbbbade43727e797f9c28042f6d90cca5fb6514ef726d43ab00ca"},
|
||||||
|
{file = "gevent-20.6.2-cp38-cp38-win32.whl", hash = "sha256:0f3fbb1703b10609856e5dffb0e358bf5edf57e52dc7cd7226e3f8674fdc0a0f"},
|
||||||
|
{file = "gevent-20.6.2-cp38-cp38-win_amd64.whl", hash = "sha256:a18d8dd9bfa994a22f30adfa0563d80f0809140045c34f85535f422813d25855"},
|
||||||
|
{file = "gevent-20.6.2-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:9527087984f1659be899b3300d5d61c7c5b01d8beae106aff5160316da8bc56f"},
|
||||||
|
{file = "gevent-20.6.2-pp27-pypy_73-macosx_10_7_x86_64.whl", hash = "sha256:76ef4c6e3332e6f7278142d791b28695adfce39735900fccef2a0f1d894f6b36"},
|
||||||
|
{file = "gevent-20.6.2-pp27-pypy_73-win32.whl", hash = "sha256:3cb2f6978615d52e4e4e667b035c11a7272bb68b14d119faf1b138164b2f354f"},
|
||||||
|
{file = "gevent-20.6.2.tar.gz", hash = "sha256:a23c2abf08e851c988723f6a2996d495f513a2c0dc70f9956af03af8debdb5d1"},
|
||||||
]
|
]
|
||||||
gevent-websocket = [
|
gevent-websocket = [
|
||||||
{file = "gevent-websocket-0.10.1.tar.gz", hash = "sha256:7eaef32968290c9121f7c35b973e2cc302ffb076d018c9068d2f5ca8b2d85fb0"},
|
{file = "gevent-websocket-0.10.1.tar.gz", hash = "sha256:7eaef32968290c9121f7c35b973e2cc302ffb076d018c9068d2f5ca8b2d85fb0"},
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ build-backend = "poetry.masonry.api"
|
||||||
|
|
||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "openflexure-microscope-server"
|
name = "openflexure-microscope-server"
|
||||||
version = "2.2.3"
|
version = "2.2.5"
|
||||||
description = "Python module, and Flask-based web API, to run the OpenFlexure Microscope."
|
description = "Python module, and Flask-based web API, to run the OpenFlexure Microscope."
|
||||||
|
|
||||||
authors = [
|
authors = [
|
||||||
|
|
@ -44,9 +44,9 @@ opencv-python-headless = [
|
||||||
]
|
]
|
||||||
labthings = {git = "https://github.com/labthings/python-labthings.git", rev = "simpler-spec"}
|
labthings = {git = "https://github.com/labthings/python-labthings.git", rev = "simpler-spec"}
|
||||||
pynpm = "^0.1.2"
|
pynpm = "^0.1.2"
|
||||||
camera-stage-mapping = {git = "https://gitlab.com/openflexure/microscope-extensions/camera-stage-mapping.git", rev = "labthings-0.7"}
|
|
||||||
sangaboard = "^0.2"
|
sangaboard = "^0.2"
|
||||||
gevent = "20.6.0" # Pinned version to guarantee piwheels wheel
|
expiringdict = "^1.2.1"
|
||||||
|
camera-stage-mapping = "^0.1.2"
|
||||||
|
|
||||||
[tool.poetry.extras]
|
[tool.poetry.extras]
|
||||||
rpi = ["RPi.GPIO"]
|
rpi = ["RPi.GPIO"]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue