diff --git a/openflexure_microscope/api/static/main_v1.js b/openflexure_microscope/api/static/main_v1.js
index 919bf6a8..d555db01 100644
--- a/openflexure_microscope/api/static/main_v1.js
+++ b/openflexure_microscope/api/static/main_v1.js
@@ -30,6 +30,19 @@ var fovY = 3146;
window.onload = function() {
getStagePositions()
updateTextBoxes()
+ getCaptures()
+}
+
+function getCaptures() {
+ function updateCapturesCallback(response, status) {
+ console.log(status);
+ updateCaptures(response);
+ }
+ safeRequest("GET", baseURI+"/capture", null, updateCapturesCallback, false)
+}
+
+function updateCaptures(response) {
+ console.log(response)
}
function updateTextBoxes() {
@@ -50,20 +63,20 @@ function updateStagePositions(response) {
function getStagePositions() {
function updatePositionCallback(response, status) {
- console.log(status, response);
- updateStagePositions(response);
+ console.log(status, response);
+ updateStagePositions(response);
}
- safeRequest("GET", baseURI+"/position", null, updatePositionCallback)
+ safeRequest("GET", baseURI+"/position", null, updatePositionCallback, false)
}
function setStagePositions(x_abs, y_abs, z_abs) {
// Make a position request
function absMoveCallback(response, status) {
- if (status == 400) {
- alert("Stage cannot be moved further than the safe range.");
- }
- console.log(status, response);
- updateStagePositions(response);
+ if (status == 400) {
+ alert("Stage cannot be moved further than the safe range.");
+ }
+ console.log(status, response);
+ updateStagePositions(response);
}
safeRequest("POST", baseURI+"/position", { "absolute": true, "x": x_abs, "y": y_abs, "z": z_abs}, absMoveCallback)
}
@@ -71,11 +84,11 @@ function setStagePositions(x_abs, y_abs, z_abs) {
function moveStagePositions(x_rel, y_rel, z_rel) {
// Make a position request
function relMoveCallback(response, status) {
- if (status == 400) {
- alert("Stage cannot be moved further than the safe range.");
- }
- console.log(status, response);
- updateStagePositions(response);
+ if (status == 400) {
+ alert("Stage cannot be moved further than the safe range.");
+ }
+ console.log(status, response);
+ updateStagePositions(response);
}
safeRequest("POST", baseURI+"/position", { "absolute": false, "x": x_rel, "y": y_rel, "z": z_rel}, relMoveCallback)
}
@@ -169,20 +182,32 @@ addEventListener("keyup", function (e) {
}, false);
// Methods for making requests
-function safeRequest(method, url, payload, callback) {
- if (requestLock == false) {
+function safeRequest(method, url, payload, callback, lock=true, force=false) {
+ if (force == true) {
+ allowRequest = true;
+ }
+ else {
+ allowRequest = !(requestLock);
+ }
+ if (allowRequest == true) {
var xhr = new XMLHttpRequest(); // new HttpRequest instance
xhr.open(method, url);
- requestLock = true;
+
+ if (lock == true) {
+ requestLock = true;
+ }
xhr.onload = function (e) {
- if (xhr.readyState === 4) {
- if (xhr.status !== 200) {
- console.error(xhr.statusText);
- }
- callback(JSON.parse(xhr.responseText), xhr.status);
- }
- requestLock = false;
+ if (xhr.readyState === 4) {
+ if (xhr.status !== 200) {
+ console.error(xhr.statusText);
+ }
+ callback(JSON.parse(xhr.responseText), xhr.status);
+ }
+
+ if (lock == true) {
+ requestLock = false;
+ }
};
if (method == "POST" || method == "PUT") {
diff --git a/openflexure_microscope/api/static/style_v1.css b/openflexure_microscope/api/static/style_v1.css
index d486979b..bc085f34 100644
--- a/openflexure_microscope/api/static/style_v1.css
+++ b/openflexure_microscope/api/static/style_v1.css
@@ -64,3 +64,12 @@ img {
display: table;
}
+/* Data divs */
+
+#captures {
+ font-size:12px;
+}
+
+.capture {
+ background-color: #d3d3d3;
+}
\ No newline at end of file
diff --git a/openflexure_microscope/api/templates/index_v1.html b/openflexure_microscope/api/templates/index_v1.html
index 9f75dd62..46ff4972 100644
--- a/openflexure_microscope/api/templates/index_v1.html
+++ b/openflexure_microscope/api/templates/index_v1.html
@@ -73,7 +73,11 @@
Captures
-
Not yet implemented.
+
+
Not yet implemented.
+
Capture 1
+
+
diff --git a/openflexure_microscope/api/utilities.py b/openflexure_microscope/api/utilities.py
index 40dd6c69..c559aba3 100644
--- a/openflexure_microscope/api/utilities.py
+++ b/openflexure_microscope/api/utilities.py
@@ -13,3 +13,14 @@ def gen(camera):
yield (b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
+
+
+def get_bool(get_arg):
+ """Convert GET request argument string to a Python bool"""
+ if (
+ get_arg == 'true' or
+ get_arg == 'True' or
+ get_arg == '1'):
+ return True
+ else:
+ return False
\ No newline at end of file
diff --git a/openflexure_microscope/api/v1.py b/openflexure_microscope/api/v1.py
index 0f5a81e2..a4c07276 100644
--- a/openflexure_microscope/api/v1.py
+++ b/openflexure_microscope/api/v1.py
@@ -1,5 +1,6 @@
#!/usr/bin/env python
"""
+TODO: Add proper docstrings
TODO: Bind to port 80
TODO: Reimplement capture methods
TODO: Implement API route to cleanly shut down server
@@ -18,7 +19,7 @@ from flask import (
import numpy as np
-from openflexure_microscope.api.utilities import parse_payload, gen
+from openflexure_microscope.api.utilities import parse_payload, gen, get_bool
from openflexure_microscope import Microscope
from openflexure_microscope.camera.pi import StreamingCamera
@@ -59,6 +60,9 @@ def stream():
"""Video streaming route. Put this in the src attribute of an img tag."""
global microscope
+ # Restart stream worker thread
+ microscope.camera.start_worker()
+
return Response(
gen(microscope.camera),
mimetype='multipart/x-mixed-replace; boundary=frame')
@@ -115,5 +119,59 @@ def position():
return jsonify(microscope.state['position'])
+# Capture routes
+@app.route(uri('/capture'), methods=['GET', 'POST', 'PUT'])
+def capture():
+ """Return JSONified microscope state"""
+ global microscope
+
+ if request.method == 'POST' or request.method == 'PUT':
+ state = parse_payload(request)
+ print(state)
+
+ if 'filename' in state:
+ filename = state['filename']
+ else:
+ filename = None
+
+ if 'keep_on_disk' in state:
+ keep_on_disk = bool(state['keep_on_disk'])
+ else:
+ keep_on_disk = True
+
+ if 'use_video_port' in state:
+ use_video_port = bool(state['use_video_port'])
+ else:
+ use_video_port = False
+
+ if 'size' in state:
+ if 'width' in state['size'] and 'height' in state['size']:
+ resize = (state['size']['width'], state['size']['height'])
+ else:
+ raise Exception("Invalid resize parameters passed. Ensure both width and height are specified.")
+ else:
+ resize = None
+
+ capture_obj = microscope.camera.capture(
+ write_to_file=True, # Always write data to disk when using API
+ keep_on_disk=keep_on_disk,
+ use_video_port=use_video_port,
+ filename=filename,
+ resize=resize)
+
+ return jsonify(capture_obj.metadata)
+
+ else: # GET requests
+ include_unavailable = get_bool(request.args.get('include_unavailable'))
+
+ if include_unavailable:
+ captures = [image for image in microscope.camera.images if image.metadata['available']]
+ else:
+ captures = microscope.camera.images
+
+ metadata_array = [capture.metadata for capture in captures]
+
+ return jsonify(metadata_array)
+
if __name__ == '__main__':
app.run(host='0.0.0.0', threaded=True, debug=True, use_reloader=False)
diff --git a/openflexure_microscope/camera/capture.py b/openflexure_microscope/camera/capture.py
index d2d0d698..5770da31 100644
--- a/openflexure_microscope/camera/capture.py
+++ b/openflexure_microscope/camera/capture.py
@@ -10,6 +10,7 @@ class StreamObject(object):
def __init__(
self,
write_to_file: bool=None,
+ keep_on_disk: bool=True,
filename: str=None,
folder: str=None,
fmt: str='file') -> None:
@@ -20,17 +21,18 @@ class StreamObject(object):
# Create file name
iterator = 0
- f_name = self.build_file_path(filename, folder, fmt)
+ f_path, f_name = self.build_file_path(filename, folder, fmt)
while os.path.isfile(f_name): # While file already exists
iterator += 1 # Add a file name iterator
- f_name = self.build_file_path(
- filename,
- folder,
- fmt,
+ f_path, f_name = self.build_file_path(
+ filename,
+ folder,
+ fmt,
iterator=iterator) # Rebuild file name
- self.file = f_name
+ self.file = f_path
+ self.filename = f_name
# Byte stream properties
self.stream = io.BytesIO() # Byte stream that data will be written to
@@ -45,7 +47,7 @@ class StreamObject(object):
self.target = self.file
# Keep on disk after close by default
- self.keep_on_disk = True
+ self.keep_on_disk = keep_on_disk
# Log if created by context manager
self.context_manager = False
@@ -95,7 +97,7 @@ class StreamObject(object):
else:
file_path = file_name
- return file_path
+ return (file_path, file_name)
def lock(self):
"""Set locked flag to True."""
@@ -138,7 +140,7 @@ class StreamObject(object):
# Get file path
if self.file_exists:
- d['file'] = self.file
+ d['file'] = self.filename
else:
d['file'] = None
@@ -148,6 +150,12 @@ class StreamObject(object):
else:
d['stream'] = False
+ # Combined availability of data
+ if self.stream_exists or self.file_exists:
+ d['available'] = True
+ else:
+ d['available'] = False
+
# Check if file was manually deleted
if self.keep_on_disk and not self.file_exists:
d['path'] = "{} (Deleted)".format(d['path'])
diff --git a/openflexure_microscope/camera/pi.py b/openflexure_microscope/camera/pi.py
index b9b3279f..9e631251 100644
--- a/openflexure_microscope/camera/pi.py
+++ b/openflexure_microscope/camera/pi.py
@@ -318,6 +318,7 @@ class StreamingCamera(BaseCamera):
self,
target=None,
write_to_file: bool=False,
+ keep_on_disk: bool=True,
use_video_port: bool=False,
filename: str=None,
folder: str='capture',
@@ -346,6 +347,7 @@ class StreamingCamera(BaseCamera):
# Create a new image and add to the image list
target_obj = self.new_image(StreamObject(
write_to_file=write_to_file,
+ keep_on_disk=keep_on_disk,
filename=filename,
folder=folder,
fmt=fmt))