From 297908fbc8da17f99d53f687d43bd4d16fd5598d Mon Sep 17 00:00:00 2001 From: Richard Date: Tue, 27 Apr 2021 12:23:37 +0100 Subject: [PATCH] Stage move API now works The test plugin seems to have problems calling setPosition more than once. We don't understand this... Co-authored-by: Kaspar Emanuel --- .../public/OpenFlexureScriptEditor.imjoy.html | 131 ++++++++++++++++++ .../OpenFlexureSnapImageTemplate.imjoy.html | 43 ++++++ .../OpenFlexureTestMoveStage.imjoy.html | 48 +++++++ .../tabContentComponents/imjoyContent.vue | 115 +++++++++------ 4 files changed, 298 insertions(+), 39 deletions(-) create mode 100644 openflexure_microscope/api/static/public/OpenFlexureScriptEditor.imjoy.html create mode 100644 openflexure_microscope/api/static/public/OpenFlexureSnapImageTemplate.imjoy.html create mode 100644 openflexure_microscope/api/static/public/OpenFlexureTestMoveStage.imjoy.html diff --git a/openflexure_microscope/api/static/public/OpenFlexureScriptEditor.imjoy.html b/openflexure_microscope/api/static/public/OpenFlexureScriptEditor.imjoy.html new file mode 100644 index 00000000..9e18873f --- /dev/null +++ b/openflexure_microscope/api/static/public/OpenFlexureScriptEditor.imjoy.html @@ -0,0 +1,131 @@ + +{ + "name": "Script Editor", + "type": "web-worker", + "tags": [], + "ui": "", + "version": "0.1.0", + "cover": "", + "description": "A Script Editor for Controlling OpenFlexure Microscope with ImJoy", + "icon": "extension", + "inputs": null, + "outputs": null, + "api_version": "0.1.8", + "env": "", + "permissions": [], + "requirements": [], + "dependencies": [] +} + + + \ No newline at end of file diff --git a/openflexure_microscope/api/static/public/OpenFlexureSnapImageTemplate.imjoy.html b/openflexure_microscope/api/static/public/OpenFlexureSnapImageTemplate.imjoy.html new file mode 100644 index 00000000..34eff749 --- /dev/null +++ b/openflexure_microscope/api/static/public/OpenFlexureSnapImageTemplate.imjoy.html @@ -0,0 +1,43 @@ + +[TODO: write documentation for this plugin.] + + + +{ + "name": "OpenFlexureSnapImageTemplate", + "type": "web-worker", + "tags": [], + "ui": "", + "version": "0.1.0", + "cover": "", + "description": "[TODO: describe this plugin with one sentence.]", + "icon": "extension", + "inputs": null, + "outputs": null, + "api_version": "0.1.8", + "env": "", + "permissions": [], + "requirements": [], + "dependencies": [] +} + + + diff --git a/openflexure_microscope/api/static/public/OpenFlexureTestMoveStage.imjoy.html b/openflexure_microscope/api/static/public/OpenFlexureTestMoveStage.imjoy.html new file mode 100644 index 00000000..12289a4c --- /dev/null +++ b/openflexure_microscope/api/static/public/OpenFlexureTestMoveStage.imjoy.html @@ -0,0 +1,48 @@ + +[TODO: write documentation for this plugin.] + + + +{ + "name": "Test OFM Stage", + "type": "web-worker", + "tags": [], + "ui": "", + "version": "0.1.0", + "cover": "", + "description": "Move the OFM stage to test the API works", + "icon": "extension", + "inputs": null, + "outputs": null, + "api_version": "0.1.8", + "env": "", + "permissions": [], + "requirements": [], + "dependencies": [] +} + + + diff --git a/openflexure_microscope/api/static/src/components/tabContentComponents/imjoyContent.vue b/openflexure_microscope/api/static/src/components/tabContentComponents/imjoyContent.vue index 3806a321..e8eacc8e 100644 --- a/openflexure_microscope/api/static/src/components/tabContentComponents/imjoyContent.vue +++ b/openflexure_microscope/api/static/src/components/tabContentComponents/imjoyContent.vue @@ -98,6 +98,21 @@ import * as imjoyCore from "imjoy-core"; import axios from "axios"; import UIkit from "uikit"; +// TODO: move this to a module or something... +async function pollAction(response) { + // Poll an action until its status is completed + // TODO: raise an error if it fails or is cancelled + // For ease, this accepts and returns a response object from + // axios.get + // FIXME: this could be an infinite loop if the task is cancelled/errored + let r = response; + while ((r.data.status == "running") | (r.data.status == "pending")) { + await new Promise(resolve => setTimeout(resolve, 100)); + r = await axios.get(r.data.href); + } + return r; +} + export default { name: "ImJoyContent", @@ -123,6 +138,9 @@ export default { }, moveActionUri: function() { return `${this.$store.getters.baseUri}/api/v2/actions/stage/move`; + }, + baseUri: function() { + return this.$store.getters.baseUri; } }, mounted() { @@ -168,7 +186,7 @@ export default { } }); - imjoy.start({ workspace: "default" }).then(() => { + imjoy.start({ workspace: "default" }).then(async () => { console.log("ImJoy started"); imjoy.event_bus.on("add_window", w => { this.addWindow(w); @@ -176,8 +194,16 @@ export default { this.setupMicroscopeAPI(); this.setupViewers(); // load the script editor for openflexure - this.loadPlugin( - "https://gist.githubusercontent.com/oeway/7a9dcb49c51b1f99b2c3619f470fe35c/raw/OpenFlexureScriptEditor.imjoy.html" + await this.loadPlugin( + `${window.location.origin}/OpenFlexureScriptEditor.imjoy.html` + ); + // Load the snap image template + await this.loadPlugin( + `${window.location.origin}/OpenFlexureSnapImageTemplate.imjoy.html` + ); + // Load the snap image template + await this.loadPlugin( + `${window.location.origin}/OpenFlexureTestMoveStage.imjoy.html` ); this.setupPluginEngine(); }); @@ -232,22 +258,10 @@ export default { // See here: https://valelab4.ucsf.edu/~MM/doc/MMCore/html/class_c_m_m_core.html const snapshotUri = this.snapshotStreamUri; const captureActionUri = this.captureActionUri; - const getPositionUri = this.getPositionUri; - const moveActionUri = this.moveActionUri; const modalError = this.modalError; - async function pollUntilComplete(response) { - // Poll an action until its status is completed - // TODO: raise an error if it fails or is cancelled - // For ease, this accepts and returns a response object from - // axios.get - // FIXME: this could be an infinite loop if the task is cancelled/errored - let r = response; - while (r.data.status == "running" | r.data.status == "pending") { - await new Promise(resolve => setTimeout(resolve, 100)); - r = await axios.get(r.data.href); - } - return r; - } + const getPositionAsObject = this.getStagePosition; + const setPositionAsObject = this.setStagePosition; + const service = { type: "_microscope-control", name: "OpenFlexure", @@ -276,9 +290,7 @@ export default { async getImage() { // FIXME: handle nicely getImage() being called before snapImage() // Wait until the capture has completed and we can retrieve it - service.lastSnapResponse = await pollUntilComplete( - service.lastSnapResponse - ); + service.lastSnapResponse = await pollAction(service.lastSnapResponse); let capture = service.lastSnapResponse.data.output; // TODO: check links.download.href exists and is JPEG let jpeg = await axios @@ -299,35 +311,37 @@ export default { setPosition(z) { // The C api suggests this defaults to just Z, and can accept a "label" to // select the stage. - return service.setXYZPosition({ - "z": z, - "absolute": true, + return setPositionAsObject({ + z: z, + absolute: true }); }, async getPosition() { - const pos = await service.getXYZPosition(); - return pos.z; + const {z} = await getPositionAsObject(); + return z; }, async setXYPosition(x, y) { - return service.setXYZPosition({ - "x": x, - "y": y, - "absolute": true, + return setPositionAsObject({ + x: x, + y: y, + absolute: true }); }, async getXYPosition() { - const pos = await service.getXYZPosition(); - return [pos.x, pos.y]; + const {x, y} = await getPositionAsObject(); + return [x, y]; }, async getXYZPosition() { - return axios - .get(getPositionUri) - .then(r => r.data) + const {x, y, z} = await getPositionAsObject(); + return [x, y, z]; }, - async setXYZPosition(payload) { - return axios - .post(moveActionUri, payload) - .then(pollUntilComplete) + async setXYZPosition(x, y, z) { + return setPositionAsObject({ + x: x, + y: y, + z: z, + absolute: true + }); } }; this.imjoy.pm.registerService({ name: "openflexure" }, service); @@ -463,6 +477,29 @@ export default { if (uri) { this.loadPlugin(uri); } + } /* + async getPosition() { + const pos = await service.getXYZPosition(); + return pos.z; + }, + async setXYPosition(x, y) { + return service.setXYZPosition({ + "x": x, + "y": y, + "absolute": true, + }); + }, + async getXYPosition() { + const pos = await service.getXYZPosition(); + return [pos.x, pos.y]; + },*/, + async getStagePosition() { + return axios.get(this.getPositionUri).then(r => r.data); + }, + async setStagePosition(payload) { + return axios + .post(this.moveActionUri, payload) + .then(pollAction) } } };