Merge branch 'better-settings' into 'master'

Better settings

Closes #185 and #156

See merge request openflexure/openflexure-microscope-server!95
This commit is contained in:
Joel Collins 2020-11-24 12:39:41 +00:00
commit f54684b460
6 changed files with 262 additions and 129 deletions

View file

@ -170,6 +170,12 @@
/* UIkit modifiers /* UIkit modifiers
========================================================================== */ ========================================================================== */
h4, .uk-h4 {
line-height: normal;
font-size: 20px;
margin: 0;
}
/* /*
* Navbar * Navbar
*/ */

View file

@ -17,7 +17,7 @@
class="uk-flex uk-flex-column uk-padding-remove uk-width-auto uk-height-1-1 uk-text-center" class="uk-flex uk-flex-column uk-padding-remove uk-width-auto uk-height-1-1 uk-text-center"
> >
<!-- For each top tab --> <!-- For each top tab -->
<template v-for="(item, index) in enabledTopTabs"> <template v-for="(item, index) in topTabs">
<!-- Render the tab icon --> <!-- Render the tab icon -->
<tabIcon <tabIcon
:id="item.id + '-tab-icon'" :id="item.id + '-tab-icon'"
@ -34,8 +34,6 @@
<hr v-if="item.divide" :key="'tab-divider-' + index" /> <hr v-if="item.divide" :key="'tab-divider-' + index" />
</template> </template>
<hr id="extension-tab-divider" />
<!-- For each plugin tab --> <!-- For each plugin tab -->
<tabIcon <tabIcon
v-for="plugin in pluginsGuiList" v-for="plugin in pluginsGuiList"
@ -79,7 +77,7 @@
> >
<!-- For each top tab --> <!-- For each top tab -->
<tabContent <tabContent
v-for="item in enabledTopTabs" v-for="item in topTabs"
:id="item.id + '-tab-content'" :id="item.id + '-tab-content'"
:key="item.id + '-tab-content'" :key="item.id + '-tab-content'"
:tab-i-d="item.id" :tab-i-d="item.id"
@ -212,18 +210,6 @@ export default {
}, },
computed: { computed: {
enabledTopTabs: function() {
var enabledTabs = this.topTabs;
if (this.$store.state.IHIEnabled) {
enabledTabs.push({
id: "slidescan",
icon: "settings_overscan",
component: slideScanContent
});
}
return enabledTabs;
},
pluginsUri: function() { pluginsUri: function() {
return `${this.$store.getters.baseUri}/api/v2/extensions`; return `${this.$store.getters.baseUri}/api/v2/extensions`;
}, },
@ -241,7 +227,7 @@ export default {
tabOrder: function() { tabOrder: function() {
var ind = []; var ind = [];
for (const tab of this.enabledTopTabs) { for (const tab of this.topTabs) {
ind.push(tab.id); ind.push(tab.id);
} }
for (const plugin of this.pluginsGuiList) { for (const plugin of this.pluginsGuiList) {
@ -270,12 +256,24 @@ export default {
this.$store.commit("changeAutoGpuPreview", true); this.$store.commit("changeAutoGpuPreview", true);
this.$store.commit("changeTrackWindow", true); this.$store.commit("changeTrackWindow", true);
} }
// Update top tabs
this.updateTopTabs(this.$store.state.IHIEnabled);
// Update plugins // Update plugins
this.updatePlugins().then(() => { this.updatePlugins().then(() => {
// Start initialisation modals // Start initialisation modals
this.startModals(); this.startModals();
}); });
} }
// Watch for host 'ready', then update status
this.unwatchStoreFunction = this.$store.watch(
state => {
return state.IHIEnabled;
},
IHIEnabled => {
this.updateTopTabs(IHIEnabled);
}
);
}, },
mounted() { mounted() {
@ -302,6 +300,19 @@ export default {
}, },
methods: { methods: {
updateTopTabs: function(IHIEnabled) {
if (IHIEnabled) {
this.topTabs.push({
id: "slidescan",
icon: "settings_overscan",
component: slideScanContent,
divide: true
});
} else {
// If the connection is now disconnected, empty capture list
this.topTabs = this.topTabs.filter(tab => tab.id != "slidescan");
}
},
updatePlugins: function() { updatePlugins: function() {
return axios return axios
.get(this.pluginsUri) .get(this.pluginsUri)

View file

@ -1,32 +1,30 @@
<template> <template>
<div id="CSMCalibrationSettings"> <div id="CSMCalibrationSettings">
<form @submit.prevent="applyConfigRequest"> <!--Show auto calibrate if default plugin is enabled-->
<!--Show auto calibrate if default plugin is enabled--> <div v-if="'calibrate_xy' in recalibrationLinks" class="uk-margin-small">
<div v-if="'calibrate_xy' in recalibrationLinks" class="uk-margin-small"> <taskSubmitter
<taskSubmitter :button-primary="true"
:button-primary="true" :can-terminate="false"
:can-terminate="false" :requires-confirmation="true"
:requires-confirmation="true" :confirmation-message="
:confirmation-message=" 'Start recalibration of the stage to the camera? This may take a while, and the microscope will be locked during this time.'
'Start recalibration of the stage to the camera? This may take a while, and the microscope will be locked during this time.' "
" :submit-url="recalibrationLinks.calibrate_xy.href"
:submit-url="recalibrationLinks.calibrate_xy.href" :submit-label="'Auto-Calibrate using camera'"
:submit-label="'Auto-Calibrate using camera'" @response="onRecalibrateResponse"
@response="onRecalibrateResponse" @error="onRecalibrateError"
@error="onRecalibrateError"
>
</taskSubmitter>
</div>
<button
v-if="'get_calibration' in recalibrationLinks"
v-show="dataAvailable && showExtraSettings"
type="button"
class="uk-button uk-button-default uk-width-1-1"
@click="getCalibrationData()"
> >
Download calibration data </taskSubmitter>
</button> </div>
</form> <button
v-if="'get_calibration' in recalibrationLinks"
v-show="dataAvailable && showExtraSettings"
type="button"
class="uk-button uk-button-default uk-width-1-1"
@click="getCalibrationData()"
>
Download calibration data
</button>
</div> </div>
</template> </template>

View file

@ -1,31 +1,32 @@
<template> <template>
<div v-if="settings" id="cameraSettings"> <div id="cameraSettings">
<div class="uk-grid uk-grid-divider uk-child-width-expand" uk-grid> <div class="uk-grid uk-grid-divider uk-child-width-expand" uk-grid>
<div class="uk-width-large"> <div class="uk-width-large">
<h3>Manual camera settings</h3> <h3>Manual camera settings</h3>
<form @submit.prevent="applyConfigRequest"> <form @submit.prevent="applySettingsRequest">
<div v-if="settings.picamera"> <div class="uk-margin-small-bottom">
<h4>Raspberry Pi Camera</h4>
<!--PiCamera settings block--> <!--PiCamera settings block-->
<div v-if="settings.picamera.shutter_speed !== undefined"> <div v-if="picamera.shutter_speed !== undefined">
<label class="uk-form-label" for="form-stacked-text" <label class="uk-form-label" for="form-stacked-text"
>Exposure time</label >Exposure time</label
> >
<div class="uk-form-controls"> <div class="uk-form-controls">
<input <input
v-model="settings.picamera.shutter_speed" v-model="picamera.shutter_speed"
class="uk-input uk-form-small" class="uk-input uk-form-small"
type="number" type="number"
/> />
</div> </div>
</div> </div>
<div v-if="settings.picamera.analog_gain !== undefined"> <div v-if="picamera.analog_gain !== undefined">
<label class="uk-form-label" for="form-stacked-text" <label class="uk-form-label" for="form-stacked-text"
>Analogue gain</label >Analogue gain</label
> >
<div class="uk-form-controls"> <div class="uk-form-controls">
<input <input
v-model="settings.picamera.analog_gain" v-model="picamera.analog_gain"
class="uk-input uk-form-small" class="uk-input uk-form-small"
type="number" type="number"
step="0.000001" step="0.000001"
@ -33,13 +34,13 @@
</div> </div>
</div> </div>
<div v-if="settings.picamera.digital_gain !== undefined"> <div v-if="picamera.digital_gain !== undefined">
<label class="uk-form-label" for="form-stacked-text" <label class="uk-form-label" for="form-stacked-text"
>Digital gain</label >Digital gain</label
> >
<div class="uk-form-controls"> <div class="uk-form-controls">
<input <input
v-model="settings.picamera.digital_gain" v-model="picamera.digital_gain"
class="uk-input uk-form-small" class="uk-input uk-form-small"
type="number" type="number"
step="0.000001" step="0.000001"
@ -48,6 +49,40 @@
</div> </div>
</div> </div>
<div class="uk-margin-small-bottom">
<h4>Image quality</h4>
<div class="uk-child-width-1-2" uk-grid>
<div v-if="mjpeg_quality !== undefined">
<label class="uk-form-label" for="form-stacked-text"
>Web stream quality (%)</label
>
<div class="uk-form-controls">
<input
v-model="mjpeg_quality"
class="uk-input uk-form-small"
type="number"
step="1"
/>
</div>
</div>
<div v-if="jpeg_quality !== undefined">
<label class="uk-form-label" for="form-stacked-text"
>JPEG capture quality (%)</label
>
<div class="uk-form-controls">
<input
v-model="jpeg_quality"
class="uk-input uk-form-small"
type="number"
step="1"
/>
</div>
</div>
</div>
</div>
<button <button
type="submit" type="submit"
class="uk-button uk-button-primary uk-margin-small uk-width-1-1" class="uk-button uk-button-primary uk-margin-small uk-width-1-1"
@ -83,7 +118,13 @@ export default {
data: function() { data: function() {
return { return {
settings: {} picamera: {
shutter_speed: undefined,
analog_gain: undefined,
digital_gain: undefined
},
mjpeg_quality: undefined,
jpeg_quality: undefined
}; };
}, },
@ -102,24 +143,37 @@ export default {
axios axios
.get(this.settingsUri) .get(this.settingsUri)
.then(response => { .then(response => {
this.settings = response.data.camera; const cameraSettings = response.data.camera;
// Get base camera settings
this.mjpeg_quality = cameraSettings.mjpeg_quality;
this.jpeg_quality = cameraSettings.jpeg_quality;
// Get Pi Camera settings if they exist
if (cameraSettings.picamera) {
this.picamera.analog_gain = cameraSettings.picamera.analog_gain;
this.picamera.digital_gain = cameraSettings.picamera.digital_gain;
this.picamera.shutter_speed = cameraSettings.picamera.shutter_speed;
}
}) })
.catch(error => { .catch(error => {
this.modalError(error); // Let mixin handle error this.modalError(error); // Let mixin handle error
}); });
}, },
applyConfigRequest: function() { applySettingsRequest: function() {
// We have to use parseInt/parseFloat because JS sometimes seems to
// make the numbers be strings... TypeScript would solve this...
var payload = { var payload = {
camera: { camera: {
mjpeg_quality: parseInt(this.mjpeg_quality),
jpeg_quality: parseInt(this.jpeg_quality),
picamera: { picamera: {
shutter_speed: this.settings.picamera.shutter_speed, shutter_speed: parseFloat(this.picamera.shutter_speed),
analog_gain: this.settings.picamera.analog_gain, analog_gain: parseFloat(this.picamera.analog_gain),
digital_gain: this.settings.picamera.digital_gain digital_gain: parseFloat(this.picamera.digital_gain)
} }
} }
}; };
console.log(payload);
// Send request // Send request
axios axios
.put(this.settingsUri, payload) .put(this.settingsUri, payload)

View file

@ -1,46 +1,6 @@
<template> <template>
<div v-if="settings" id="microscopeSettings" class="uk-width-large"> <div v-if="settings" id="microscopeSettings" class="uk-width-large">
<form @submit.prevent="applyConfigRequest"> <form @submit.prevent="applySettingsRequest">
<label class="uk-form-label" for="form-stacked-text"
>Backlash compensation</label
>
<div class="uk-grid-small uk-child-width-1-3" uk-grid>
<div>
<label class="uk-form-label" for="form-stacked-text">x</label>
<div class="uk-form-controls">
<input
v-model="settings.stage.backlash.x"
class="uk-input uk-form-small"
type="number"
/>
</div>
</div>
<div>
<label class="uk-form-label" for="form-stacked-text">y</label>
<div class="uk-form-controls">
<input
v-model="settings.stage.backlash.y"
class="uk-input uk-form-small"
type="number"
/>
</div>
</div>
<div>
<label class="uk-form-label" for="form-stacked-text">z</label>
<div class="uk-form-controls">
<input
v-model="settings.stage.backlash.z"
class="uk-input uk-form-small"
type="number"
/>
</div>
</div>
</div>
<br />
<div> <div>
<label class="uk-form-label" for="form-stacked-text" <label class="uk-form-label" for="form-stacked-text"
>Microscope name</label >Microscope name</label
@ -57,7 +17,7 @@
<button <button
type="submit" type="submit"
class="uk-button uk-button-primary uk-form-small uk-float-right uk-margin-small uk-width-1-1" class="uk-button uk-button-primary uk-margin-small uk-width-1-1"
> >
Apply Settings Apply Settings
</button> </button>
@ -100,12 +60,9 @@ export default {
}); });
}, },
applyConfigRequest: function() { applySettingsRequest: function() {
var payload = { var payload = {
name: this.settings.name, name: this.settings.name
stage: {
backlash: this.settings.stage.backlash
}
}; };
// Send request to update config // Send request to update config

View file

@ -1,31 +1,96 @@
<template> <template>
<div id="stageSettings" class="uk-width-large"> <div id="stageSettings" class="uk-width-large">
<div> <div v-if="stageType === 'MissingStage'" class="uk-text-danger">
<b>No stage connected</b>
</div>
<div v-else>
<h3>Stage settings</h3> <h3>Stage settings</h3>
<p> <form @submit.prevent="setStageType">
<label class="uk-form-label"> <div class="uk-margin-small-bottom">
Stage geometry <h4>Stage geometry</h4>
<div v-if="stageType != 'MissingStage'"> <select v-model="stageType" class="uk-select uk-margin-small-top">
<form @submit.prevent="setStageType"> <option value="SangaStage">SangaStage (Standard)</option>
<div class="uk-margin-small"> <option value="SangaDeltaStage"> SangaStage (Delta)</option>
<select v-model="stageType" class="uk-select"> </select>
<option value="SangaStage">SangaStage (Standard)</option>
<option value="SangaDeltaStage"> SangaStage (Delta)</option> <button
</select> type="submit"
class="uk-button uk-button-primary uk-margin-small uk-width-1-1"
>
Change stage geometry
</button>
</div>
</form>
<form @submit.prevent="applySettingsRequest">
<div class="uk-margin-small-bottom">
<h4>Backlash compensation</h4>
<p class="uk-margin-remove">
Backlash compentation causes movements to overshoot by that number
of motor steps, reducing the effect of mechanical backlash.
</p>
<div
class="uk-grid-small uk-child-width-1-3 uk-margin-small-bottom"
uk-grid
>
<div>
<label class="uk-form-label" for="form-stacked-text">x</label>
<div class="uk-form-controls">
<input
v-model="backlash.x"
class="uk-input uk-form-small"
type="number"
/>
</div> </div>
<div> </div>
<button
type="submit" <div>
class="uk-button uk-button-primary uk-width-1-1" <label class="uk-form-label" for="form-stacked-text">y</label>
> <div class="uk-form-controls">
Change stage geometry <input
</button> v-model="backlash.y"
class="uk-input uk-form-small"
type="number"
/>
</div> </div>
</form> </div>
<div>
<label class="uk-form-label" for="form-stacked-text">z</label>
<div class="uk-form-controls">
<input
v-model="backlash.z"
class="uk-input uk-form-small"
type="number"
/>
</div>
</div>
</div> </div>
<div v-else class="uk-text-danger"><b>No stage connected</b></div>
</label> <h4>Settle time</h4>
</p> <div v-if="settle_time !== undefined">
<p class="uk-margin-small-bottom">
When moving or scanning, captures will be delayed by the settle
time (in seconds) to reduce motion blur.
</p>
<div class="uk-form-controls">
<input
v-model="settle_time"
class="uk-input uk-form-small"
type="number"
step="0.1"
/>
</div>
</div>
<button
type="submit"
class="uk-button uk-button-primary uk-margin-small uk-width-1-1"
>
Apply Settings
</button>
</div>
</form>
</div> </div>
</div> </div>
</template> </template>
@ -40,11 +105,20 @@ export default {
data: function() { data: function() {
return { return {
stageType: "MissingStage" stageType: "MissingStage",
backlash: {
x: undefined,
y: undefined,
z: undefined
},
settle_time: undefined
}; };
}, },
computed: { computed: {
settingsUri: function() {
return `${this.$store.getters.baseUri}/api/v2/instrument/settings`;
},
stageTypeUri: function() { stageTypeUri: function() {
return `${this.$store.getters.baseUri}/api/v2/instrument/stage/type`; return `${this.$store.getters.baseUri}/api/v2/instrument/stage/type`;
} }
@ -52,9 +126,42 @@ export default {
mounted() { mounted() {
this.getStageType(); this.getStageType();
this.updateSettings();
}, },
methods: { methods: {
updateSettings: function() {
axios
.get(this.settingsUri)
.then(response => {
const stageSettings = response.data.stage;
this.backlash.x = stageSettings.backlash.x;
this.backlash.y = stageSettings.backlash.y;
this.backlash.z = stageSettings.backlash.z;
this.settle_time = stageSettings.settle_time;
})
.catch(error => {
this.modalError(error); // Let mixin handle error
});
},
applySettingsRequest: function() {
var payload = {
stage: {
backlash: this.backlash
}
};
// Send request to update settings
axios
.put(this.settingsUri, payload)
.then(() => {
// Update local settings
this.updateSettings();
this.modalNotify("Stage settings applied.");
})
.catch(error => {
this.modalError(error); // Let mixin handle error
});
},
getStageType: function() { getStageType: function() {
axios axios
.get(this.stageTypeUri) .get(this.stageTypeUri)