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 <kaspar@monostable.co.uk>
This commit is contained in:
parent
757ee216cf
commit
297908fbc8
4 changed files with 298 additions and 39 deletions
|
|
@ -0,0 +1,131 @@
|
||||||
|
<config lang="json">
|
||||||
|
{
|
||||||
|
"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": []
|
||||||
|
}
|
||||||
|
</config>
|
||||||
|
|
||||||
|
<script lang="javascript">
|
||||||
|
class ImJoyPlugin {
|
||||||
|
async setup() {
|
||||||
|
api.log('initialized')
|
||||||
|
}
|
||||||
|
|
||||||
|
async run(ctx) {
|
||||||
|
let pluginInEditor, stopped, editorWindow;
|
||||||
|
const config = {lang: 'javascript'}
|
||||||
|
config.templates = [
|
||||||
|
{
|
||||||
|
name: "New",
|
||||||
|
url: null,
|
||||||
|
lang: 'javascript',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Snap Image Template",
|
||||||
|
url: "./OpenFlexureSnapImageTemplate.imjoy.html",
|
||||||
|
lang: "html",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
config.ui_elements = {
|
||||||
|
save: {
|
||||||
|
_rintf: true,
|
||||||
|
type: 'button',
|
||||||
|
label: "Save",
|
||||||
|
visible: false,
|
||||||
|
icon: "content-save",
|
||||||
|
callback(content) {
|
||||||
|
console.log(content)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
run: {
|
||||||
|
_rintf: true,
|
||||||
|
type: 'button',
|
||||||
|
label: "Run",
|
||||||
|
icon: "play",
|
||||||
|
visible: true,
|
||||||
|
shortcut: 'Shift-Enter',
|
||||||
|
async callback(content) {
|
||||||
|
try {
|
||||||
|
editorWindow.setLoader(true);
|
||||||
|
editorWindow.updateUIElement('stop', {
|
||||||
|
visible: true
|
||||||
|
})
|
||||||
|
api.showProgress(0);
|
||||||
|
pluginInEditor = await api.getPlugin({src: content, hot_reloading: true})
|
||||||
|
|
||||||
|
if (stopped) {
|
||||||
|
pluginInEditor = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (pluginInEditor && pluginInEditor.run) {
|
||||||
|
return await pluginInEditor.run({
|
||||||
|
config: {},
|
||||||
|
data: {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (stopped) {
|
||||||
|
pluginInEditor = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
api.showMessage("Failed to load plugin, error: " + e.toString());
|
||||||
|
} finally {
|
||||||
|
editorWindow.updateUIElement('stop', {
|
||||||
|
visible: false
|
||||||
|
})
|
||||||
|
editorWindow.setLoader(false);
|
||||||
|
api.showProgress(100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
stop: {
|
||||||
|
_rintf: true,
|
||||||
|
type: 'button',
|
||||||
|
label: "Stop",
|
||||||
|
style: "color: #ff0080cf;",
|
||||||
|
icon: "stop",
|
||||||
|
visible: false,
|
||||||
|
async callback() {
|
||||||
|
stopped = true;
|
||||||
|
await editorWindow.setLoader(false);
|
||||||
|
await editorWindow.updateUIElement('stop', {
|
||||||
|
visible: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
export: {
|
||||||
|
_rintf: true,
|
||||||
|
type: 'button',
|
||||||
|
label: "Export",
|
||||||
|
icon: "file-download-outline",
|
||||||
|
visible: true,
|
||||||
|
async callback(content) {
|
||||||
|
const fileName = (pluginInEditor && pluginInEditor.config.name && pluginInEditor.config.name + '.imjoy.html') || config.name + '.imjoy.html' || "myPlugin.imjoy.html";
|
||||||
|
await api.exportFile(content, fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
editorWindow = await api.createWindow({
|
||||||
|
src: 'https://if.imjoy.io',
|
||||||
|
name: (ctx && ctx.data && ctx.data.name) ||'OpenFlexure Script Editor',
|
||||||
|
config,
|
||||||
|
data: {code: ctx && ctx.data && ctx.data.code}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
api.export(new ImJoyPlugin())
|
||||||
|
</script>
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
<docs>
|
||||||
|
[TODO: write documentation for this plugin.]
|
||||||
|
</docs>
|
||||||
|
|
||||||
|
<config lang="json">
|
||||||
|
{
|
||||||
|
"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": []
|
||||||
|
}
|
||||||
|
</config>
|
||||||
|
|
||||||
|
<script lang="javascript">
|
||||||
|
class ImJoyPlugin {
|
||||||
|
async setup() {
|
||||||
|
api.log('initialized')
|
||||||
|
}
|
||||||
|
|
||||||
|
async run(ctx) {
|
||||||
|
// get a list of service
|
||||||
|
const services = await api.getServices({name: "OpenFlexure"});
|
||||||
|
// get the first service
|
||||||
|
const ofm = services[0];
|
||||||
|
// snap an image
|
||||||
|
await ofm.snapImage();
|
||||||
|
console.log("snapped an image");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
api.export(new ImJoyPlugin())
|
||||||
|
</script>
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
<docs>
|
||||||
|
[TODO: write documentation for this plugin.]
|
||||||
|
</docs>
|
||||||
|
|
||||||
|
<config lang="json">
|
||||||
|
{
|
||||||
|
"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": []
|
||||||
|
}
|
||||||
|
</config>
|
||||||
|
|
||||||
|
<script lang="javascript">
|
||||||
|
class ImJoyPlugin {
|
||||||
|
async setup() {
|
||||||
|
api.log('initialized')
|
||||||
|
}
|
||||||
|
|
||||||
|
async run(ctx) {
|
||||||
|
// get a list of service
|
||||||
|
const services = await api.getServices({name: "OpenFlexure"});
|
||||||
|
// get the first service
|
||||||
|
const ofm = services[0];
|
||||||
|
// snap an image
|
||||||
|
const start = await ofm.getXYZPosition();
|
||||||
|
console.log(`Starting position: ${start}`);
|
||||||
|
// move the Z axis up and down
|
||||||
|
await ofm.setPosition(start[2] + 100);
|
||||||
|
console.log(`Moved Z to ${await ofm.getPosition()}`);
|
||||||
|
await ofm.setPosition(start[2]);
|
||||||
|
console.log(`Moved Z to ${await ofm.getPosition()}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
api.export(new ImJoyPlugin())
|
||||||
|
</script>
|
||||||
|
|
@ -98,6 +98,21 @@ import * as imjoyCore from "imjoy-core";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import UIkit from "uikit";
|
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 {
|
export default {
|
||||||
name: "ImJoyContent",
|
name: "ImJoyContent",
|
||||||
|
|
||||||
|
|
@ -123,6 +138,9 @@ export default {
|
||||||
},
|
},
|
||||||
moveActionUri: function() {
|
moveActionUri: function() {
|
||||||
return `${this.$store.getters.baseUri}/api/v2/actions/stage/move`;
|
return `${this.$store.getters.baseUri}/api/v2/actions/stage/move`;
|
||||||
|
},
|
||||||
|
baseUri: function() {
|
||||||
|
return this.$store.getters.baseUri;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
|
@ -168,7 +186,7 @@ export default {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
imjoy.start({ workspace: "default" }).then(() => {
|
imjoy.start({ workspace: "default" }).then(async () => {
|
||||||
console.log("ImJoy started");
|
console.log("ImJoy started");
|
||||||
imjoy.event_bus.on("add_window", w => {
|
imjoy.event_bus.on("add_window", w => {
|
||||||
this.addWindow(w);
|
this.addWindow(w);
|
||||||
|
|
@ -176,8 +194,16 @@ export default {
|
||||||
this.setupMicroscopeAPI();
|
this.setupMicroscopeAPI();
|
||||||
this.setupViewers();
|
this.setupViewers();
|
||||||
// load the script editor for openflexure
|
// load the script editor for openflexure
|
||||||
this.loadPlugin(
|
await this.loadPlugin(
|
||||||
"https://gist.githubusercontent.com/oeway/7a9dcb49c51b1f99b2c3619f470fe35c/raw/OpenFlexureScriptEditor.imjoy.html"
|
`${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();
|
this.setupPluginEngine();
|
||||||
});
|
});
|
||||||
|
|
@ -232,22 +258,10 @@ export default {
|
||||||
// See here: https://valelab4.ucsf.edu/~MM/doc/MMCore/html/class_c_m_m_core.html
|
// See here: https://valelab4.ucsf.edu/~MM/doc/MMCore/html/class_c_m_m_core.html
|
||||||
const snapshotUri = this.snapshotStreamUri;
|
const snapshotUri = this.snapshotStreamUri;
|
||||||
const captureActionUri = this.captureActionUri;
|
const captureActionUri = this.captureActionUri;
|
||||||
const getPositionUri = this.getPositionUri;
|
|
||||||
const moveActionUri = this.moveActionUri;
|
|
||||||
const modalError = this.modalError;
|
const modalError = this.modalError;
|
||||||
async function pollUntilComplete(response) {
|
const getPositionAsObject = this.getStagePosition;
|
||||||
// Poll an action until its status is completed
|
const setPositionAsObject = this.setStagePosition;
|
||||||
// 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 service = {
|
const service = {
|
||||||
type: "_microscope-control",
|
type: "_microscope-control",
|
||||||
name: "OpenFlexure",
|
name: "OpenFlexure",
|
||||||
|
|
@ -276,9 +290,7 @@ export default {
|
||||||
async getImage() {
|
async getImage() {
|
||||||
// FIXME: handle nicely getImage() being called before snapImage()
|
// FIXME: handle nicely getImage() being called before snapImage()
|
||||||
// Wait until the capture has completed and we can retrieve it
|
// Wait until the capture has completed and we can retrieve it
|
||||||
service.lastSnapResponse = await pollUntilComplete(
|
service.lastSnapResponse = await pollAction(service.lastSnapResponse);
|
||||||
service.lastSnapResponse
|
|
||||||
);
|
|
||||||
let capture = service.lastSnapResponse.data.output;
|
let capture = service.lastSnapResponse.data.output;
|
||||||
// TODO: check links.download.href exists and is JPEG
|
// TODO: check links.download.href exists and is JPEG
|
||||||
let jpeg = await axios
|
let jpeg = await axios
|
||||||
|
|
@ -299,35 +311,37 @@ export default {
|
||||||
setPosition(z) {
|
setPosition(z) {
|
||||||
// The C api suggests this defaults to just Z, and can accept a "label" to
|
// The C api suggests this defaults to just Z, and can accept a "label" to
|
||||||
// select the stage.
|
// select the stage.
|
||||||
return service.setXYZPosition({
|
return setPositionAsObject({
|
||||||
"z": z,
|
z: z,
|
||||||
"absolute": true,
|
absolute: true
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
async getPosition() {
|
async getPosition() {
|
||||||
const pos = await service.getXYZPosition();
|
const {z} = await getPositionAsObject();
|
||||||
return pos.z;
|
return z;
|
||||||
},
|
},
|
||||||
async setXYPosition(x, y) {
|
async setXYPosition(x, y) {
|
||||||
return service.setXYZPosition({
|
return setPositionAsObject({
|
||||||
"x": x,
|
x: x,
|
||||||
"y": y,
|
y: y,
|
||||||
"absolute": true,
|
absolute: true
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
async getXYPosition() {
|
async getXYPosition() {
|
||||||
const pos = await service.getXYZPosition();
|
const {x, y} = await getPositionAsObject();
|
||||||
return [pos.x, pos.y];
|
return [x, y];
|
||||||
},
|
},
|
||||||
async getXYZPosition() {
|
async getXYZPosition() {
|
||||||
return axios
|
const {x, y, z} = await getPositionAsObject();
|
||||||
.get(getPositionUri)
|
return [x, y, z];
|
||||||
.then(r => r.data)
|
|
||||||
},
|
},
|
||||||
async setXYZPosition(payload) {
|
async setXYZPosition(x, y, z) {
|
||||||
return axios
|
return setPositionAsObject({
|
||||||
.post(moveActionUri, payload)
|
x: x,
|
||||||
.then(pollUntilComplete)
|
y: y,
|
||||||
|
z: z,
|
||||||
|
absolute: true
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
this.imjoy.pm.registerService({ name: "openflexure" }, service);
|
this.imjoy.pm.registerService({ name: "openflexure" }, service);
|
||||||
|
|
@ -463,6 +477,29 @@ export default {
|
||||||
if (uri) {
|
if (uri) {
|
||||||
this.loadPlugin(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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue