Merge branch 'format-js' into 'v3'
Enable JS formatting checks in CI, reformat to match See merge request openflexure/openflexure-microscope-server!422
This commit is contained in:
commit
a89306b3b8
70 changed files with 727 additions and 1544 deletions
|
|
@ -2,17 +2,17 @@ module.exports = {
|
||||||
root: true,
|
root: true,
|
||||||
|
|
||||||
env: {
|
env: {
|
||||||
node: true
|
node: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
parser: "babel-eslint"
|
parser: "babel-eslint",
|
||||||
},
|
},
|
||||||
|
|
||||||
rules: {
|
rules: {
|
||||||
"no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
|
"no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
|
||||||
"no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off"
|
"no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off",
|
||||||
},
|
},
|
||||||
|
|
||||||
extends: ["plugin:vue/recommended", "eslint:recommended", "@vue/prettier"]
|
extends: ["plugin:vue/recommended", "eslint:recommended", "@vue/prettier"],
|
||||||
};
|
};
|
||||||
|
|
|
||||||
7
webapp/.prettierrc.json
Normal file
7
webapp/.prettierrc.json
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"trailingComma": "all",
|
||||||
|
"semi": true,
|
||||||
|
"singleQuote": false,
|
||||||
|
"printWidth": 100,
|
||||||
|
"tabWidth": 2
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
presets: ["@vue/app"]
|
presets: ["@vue/app"],
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
"gv": "genversion src/version.js",
|
"gv": "genversion src/version.js",
|
||||||
"serve": "npm run gv && vue-cli-service serve --mode development",
|
"serve": "npm run gv && vue-cli-service serve --mode development",
|
||||||
"build": "npm run gv && vue-cli-service build --openssl-legacy-provider --mode production",
|
"build": "npm run gv && vue-cli-service build --openssl-legacy-provider --mode production",
|
||||||
"lint": "vue-cli-service lint",
|
"lint": "vue-cli-service lint --no-fix --max-warnings 0",
|
||||||
"lint:fix": "vue-cli-service lint --fix"
|
"lint:fix": "vue-cli-service lint --fix"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div id="app" class="uk-height-1-1 uk-margin-remove uk-padding-remove" :class="handleTheme">
|
||||||
id="app"
|
|
||||||
class="uk-height-1-1 uk-margin-remove uk-padding-remove"
|
|
||||||
:class="handleTheme"
|
|
||||||
>
|
|
||||||
<loadingContent v-if="!$store.getters.ready" />
|
<loadingContent v-if="!$store.getters.ready" />
|
||||||
<appContent v-if="$store.getters.ready" />
|
<appContent v-if="$store.getters.ready" />
|
||||||
<!-- Runtime modals -->
|
<!-- Runtime modals -->
|
||||||
<div
|
<div id="modal-center" ref="keyboardManualModal" class="uk-flex-top" uk-modal>
|
||||||
id="modal-center"
|
|
||||||
ref="keyboardManualModal"
|
|
||||||
class="uk-flex-top"
|
|
||||||
uk-modal
|
|
||||||
>
|
|
||||||
<div class="uk-modal-dialog uk-modal-body uk-margin-auto-vertical">
|
<div class="uk-modal-dialog uk-modal-body uk-margin-auto-vertical">
|
||||||
<button class="uk-modal-close-default" type="button" uk-close></button>
|
<button class="uk-modal-close-default" type="button" uk-close></button>
|
||||||
<div
|
<div
|
||||||
|
|
@ -62,7 +53,7 @@ export default {
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
appContent,
|
appContent,
|
||||||
loadingContent
|
loadingContent,
|
||||||
},
|
},
|
||||||
|
|
||||||
data: function() {
|
data: function() {
|
||||||
|
|
@ -71,16 +62,13 @@ export default {
|
||||||
arrowKeysDown: {},
|
arrowKeysDown: {},
|
||||||
keyboardManual: [],
|
keyboardManual: [],
|
||||||
systemDark: undefined,
|
systemDark: undefined,
|
||||||
themeObserver: undefined
|
themeObserver: undefined,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
isSystemDark: function() {
|
isSystemDark: function() {
|
||||||
if (
|
if (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) {
|
||||||
window.matchMedia &&
|
|
||||||
window.matchMedia("(prefers-color-scheme: dark)").matches
|
|
||||||
) {
|
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -97,9 +85,9 @@ export default {
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
"uk-light": isDark,
|
"uk-light": isDark,
|
||||||
"uk-background-secondary": isDark
|
"uk-background-secondary": isDark,
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
|
|
@ -132,7 +120,7 @@ export default {
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
this.checkConnection();
|
this.checkConnection();
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// Keyboard shortcuts
|
// Keyboard shortcuts
|
||||||
|
|
@ -147,18 +135,18 @@ export default {
|
||||||
this.arrowKeysDown[event.keyCode] = true; //Add key to array
|
this.arrowKeysDown[event.keyCode] = true; //Add key to array
|
||||||
this.navigateKeyHandler();
|
this.navigateKeyHandler();
|
||||||
},
|
},
|
||||||
"keydown"
|
"keydown",
|
||||||
);
|
);
|
||||||
Mousetrap.bind(
|
Mousetrap.bind(
|
||||||
["up", "down", "left", "right"],
|
["up", "down", "left", "right"],
|
||||||
event => {
|
event => {
|
||||||
delete this.arrowKeysDown[event.keyCode]; //Remove key from array
|
delete this.arrowKeysDown[event.keyCode]; //Remove key from array
|
||||||
},
|
},
|
||||||
"keyup"
|
"keyup",
|
||||||
);
|
);
|
||||||
this.keyboardManual.push({
|
this.keyboardManual.push({
|
||||||
shortcut: "←↑→↓",
|
shortcut: "←↑→↓",
|
||||||
description: "Move the microscope stage"
|
description: "Move the microscope stage",
|
||||||
});
|
});
|
||||||
|
|
||||||
// Focus keys
|
// Focus keys
|
||||||
|
|
@ -170,7 +158,7 @@ export default {
|
||||||
});
|
});
|
||||||
this.keyboardManual.push({
|
this.keyboardManual.push({
|
||||||
shortcut: "pgup / pgdn",
|
shortcut: "pgup / pgdn",
|
||||||
description: "Move the microscope focus"
|
description: "Move the microscope focus",
|
||||||
});
|
});
|
||||||
|
|
||||||
// Capture
|
// Capture
|
||||||
|
|
@ -179,7 +167,7 @@ export default {
|
||||||
});
|
});
|
||||||
this.keyboardManual.push({
|
this.keyboardManual.push({
|
||||||
shortcut: "c",
|
shortcut: "c",
|
||||||
description: "Take a capture"
|
description: "Take a capture",
|
||||||
});
|
});
|
||||||
|
|
||||||
// Autofocus
|
// Autofocus
|
||||||
|
|
@ -188,7 +176,7 @@ export default {
|
||||||
});
|
});
|
||||||
this.keyboardManual.push({
|
this.keyboardManual.push({
|
||||||
shortcut: "a",
|
shortcut: "a",
|
||||||
description: "Fast autofocus"
|
description: "Fast autofocus",
|
||||||
});
|
});
|
||||||
|
|
||||||
// Increment/decrement tab
|
// Increment/decrement tab
|
||||||
|
|
@ -200,7 +188,7 @@ export default {
|
||||||
});
|
});
|
||||||
this.keyboardManual.push({
|
this.keyboardManual.push({
|
||||||
shortcut: "shift+↑ / shift+↓",
|
shortcut: "shift+↑ / shift+↓",
|
||||||
description: "Switch tab"
|
description: "Switch tab",
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -224,15 +212,10 @@ export default {
|
||||||
// TODO: more robust check - e.g. use a microscope Thing
|
// TODO: more robust check - e.g. use a microscope Thing
|
||||||
// TODO: should we purge existing consumedThings?
|
// TODO: should we purge existing consumedThings?
|
||||||
try {
|
try {
|
||||||
await this.$store.dispatch(
|
await this.$store.dispatch("wot/fetchThingDescriptions", `${baseUri}/thing_descriptions/`);
|
||||||
"wot/fetchThingDescriptions",
|
|
||||||
`${baseUri}/thing_descriptions/`
|
|
||||||
);
|
|
||||||
for (let requiredThing of ["camera", "stage"]) {
|
for (let requiredThing of ["camera", "stage"]) {
|
||||||
if (!this.$store.getters["wot/thingAvailable"](requiredThing)) {
|
if (!this.$store.getters["wot/thingAvailable"](requiredThing)) {
|
||||||
throw new Error(
|
throw new Error(`No ${requiredThing} found, the GUI won't work without one.`);
|
||||||
`No ${requiredThing} found, the GUI won't work without one.`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
|
@ -291,8 +274,8 @@ export default {
|
||||||
// Make a position request
|
// Make a position request
|
||||||
// Emit a signal to move, acted on by panelControl.vue
|
// Emit a signal to move, acted on by panelControl.vue
|
||||||
this.$root.$emit("globalMoveStepEvent", x_rel, y_rel, z_rel);
|
this.$root.$emit("globalMoveStepEvent", x_rel, y_rel, z_rel);
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -366,5 +349,4 @@ html {
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div id="app-content" class="uk-margin-remove uk-padding-remove uk-height-1-1" uk-grid>
|
||||||
id="app-content"
|
|
||||||
class="uk-margin-remove uk-padding-remove uk-height-1-1"
|
|
||||||
uk-grid
|
|
||||||
>
|
|
||||||
<!-- Initialisation modals -->
|
<!-- Initialisation modals -->
|
||||||
<calibrationWizard
|
<calibrationWizard ref="calibrationWizard" @onClose="enterApp()"></calibrationWizard>
|
||||||
ref="calibrationWizard"
|
|
||||||
@onClose="enterApp()"
|
|
||||||
></calibrationWizard>
|
|
||||||
<!-- Vertical tab bar -->
|
<!-- Vertical tab bar -->
|
||||||
<div id="switcher-left-container">
|
<div id="switcher-left-container">
|
||||||
<div
|
<div
|
||||||
|
|
@ -63,10 +56,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Corresponding vertical tab content -->
|
<!-- Corresponding vertical tab content -->
|
||||||
<div
|
<div id="container-left" class="uk-padding-remove uk-height-1-1 uk-width-expand">
|
||||||
id="container-left"
|
|
||||||
class="uk-padding-remove uk-height-1-1 uk-width-expand"
|
|
||||||
>
|
|
||||||
<!-- For each top tab -->
|
<!-- For each top tab -->
|
||||||
<tabContent
|
<tabContent
|
||||||
v-for="item in topTabs"
|
v-for="item in topTabs"
|
||||||
|
|
@ -79,7 +69,6 @@
|
||||||
<component :is="item.component"></component>
|
<component :is="item.component"></component>
|
||||||
</tabContent>
|
</tabContent>
|
||||||
|
|
||||||
|
|
||||||
<!-- For each bottom tab -->
|
<!-- For each bottom tab -->
|
||||||
<tabContent
|
<tabContent
|
||||||
v-for="item in bottomTabs"
|
v-for="item in bottomTabs"
|
||||||
|
|
@ -96,7 +85,6 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
// Import generic components
|
// Import generic components
|
||||||
import tabIcon from "./genericComponents/tabIcon";
|
import tabIcon from "./genericComponents/tabIcon";
|
||||||
import tabContent from "./genericComponents/tabContent";
|
import tabContent from "./genericComponents/tabContent";
|
||||||
|
|
@ -141,24 +129,24 @@ export default {
|
||||||
id: "settings",
|
id: "settings",
|
||||||
icon: "settings",
|
icon: "settings",
|
||||||
component: settingsContent,
|
component: settingsContent,
|
||||||
class: "uk-margin-auto-top"
|
class: "uk-margin-auto-top",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "logging",
|
id: "logging",
|
||||||
icon: "assignment_late",
|
icon: "assignment_late",
|
||||||
component: loggingContent
|
component: loggingContent,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "about",
|
id: "about",
|
||||||
icon: "info",
|
icon: "info",
|
||||||
component: aboutContent
|
component: aboutContent,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "power",
|
id: "power",
|
||||||
icon: "power_settings_new",
|
icon: "power_settings_new",
|
||||||
component: powerContent
|
component: powerContent,
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -179,28 +167,28 @@ export default {
|
||||||
{
|
{
|
||||||
id: "view",
|
id: "view",
|
||||||
icon: "visibility",
|
icon: "visibility",
|
||||||
component: viewContent
|
component: viewContent,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "control",
|
id: "control",
|
||||||
icon: "gamepad",
|
icon: "gamepad",
|
||||||
component: controlContent
|
component: controlContent,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "background detect",
|
id: "background detect",
|
||||||
icon: "background_replace",
|
icon: "background_replace",
|
||||||
component: backgroundDetectContent
|
component: backgroundDetectContent,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "slide scan",
|
id: "slide scan",
|
||||||
icon: "settings_overscan",
|
icon: "settings_overscan",
|
||||||
component: slideScanContent
|
component: slideScanContent,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "scan list",
|
id: "scan list",
|
||||||
icon: "photo_library",
|
icon: "photo_library",
|
||||||
component: ScanListContent
|
component: ScanListContent,
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
if (!this.$store.state.galleryEnabled) {
|
if (!this.$store.state.galleryEnabled) {
|
||||||
tabs = tabs.filter(tab => tab.id != "gallery");
|
tabs = tabs.filter(tab => tab.id != "gallery");
|
||||||
|
|
@ -210,7 +198,7 @@ export default {
|
||||||
|
|
||||||
currentTabIndex: function() {
|
currentTabIndex: function() {
|
||||||
return this.tabOrder.indexOf(this.currentTab);
|
return this.tabOrder.indexOf(this.currentTab);
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
|
|
@ -239,8 +227,7 @@ export default {
|
||||||
},
|
},
|
||||||
incrementTabBy: function(n) {
|
incrementTabBy: function(n) {
|
||||||
const newIndex =
|
const newIndex =
|
||||||
(((this.currentTabIndex + n) % this.tabOrder.length) +
|
(((this.currentTabIndex + n) % this.tabOrder.length) + this.tabOrder.length) %
|
||||||
this.tabOrder.length) %
|
|
||||||
this.tabOrder.length;
|
this.tabOrder.length;
|
||||||
const newId = this.tabOrder[newIndex];
|
const newId = this.tabOrder[newIndex];
|
||||||
this.currentTab = newId;
|
this.currentTab = newId;
|
||||||
|
|
@ -250,8 +237,8 @@ export default {
|
||||||
},
|
},
|
||||||
enterApp: function() {
|
enterApp: function() {
|
||||||
// Stuff to do once connected and all init modals are finished
|
// Stuff to do once connected and all init modals are finished
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,70 +0,0 @@
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<label>{{ label }}</label>
|
|
||||||
|
|
||||||
<div class="uk-form-controls">
|
|
||||||
<div v-for="option in options" :key="option">
|
|
||||||
<label>
|
|
||||||
<input
|
|
||||||
class="uk-checkbox"
|
|
||||||
type="checkbox"
|
|
||||||
:value="option"
|
|
||||||
:checked="value && value.includes(option)"
|
|
||||||
v-bind="$attrs"
|
|
||||||
@change="updateValue($event.target)"
|
|
||||||
/>
|
|
||||||
{{ option }}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: "CheckList",
|
|
||||||
|
|
||||||
props: {
|
|
||||||
value: {
|
|
||||||
type: Array,
|
|
||||||
required: false,
|
|
||||||
default: function() {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
type: Array,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
name: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
label: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
updateValue(target) {
|
|
||||||
var newSelected = this.value != null ? [...this.value] : []; // Clone value array
|
|
||||||
|
|
||||||
if (target.checked) {
|
|
||||||
if (!newSelected.includes(target.value)) {
|
|
||||||
newSelected.push(target.value);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (newSelected.includes(target.value)) {
|
|
||||||
newSelected = newSelected.filter(function(value) {
|
|
||||||
return value != target.value;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.$emit("input", newSelected);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped></style>
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<!-- eslint-disable-next-line vue/no-v-html -->
|
|
||||||
<p v-html="content"></p>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: "HtmlBlock",
|
|
||||||
|
|
||||||
props: {
|
|
||||||
label: {
|
|
||||||
type: String,
|
|
||||||
required: false,
|
|
||||||
default: ""
|
|
||||||
},
|
|
||||||
name: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
content: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped></style>
|
|
||||||
|
|
@ -1,121 +0,0 @@
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<form @submit.prevent="handleMetadataSubmit">
|
|
||||||
<div class="uk-margin-remove uk-flex uk-flex-middle">
|
|
||||||
<div
|
|
||||||
class="uk-margin-remove-top uk-padding-remove uk-grid-small uk-width-expand"
|
|
||||||
uk-grid
|
|
||||||
>
|
|
||||||
<div class="uk-margin-remove uk-width-1-2">
|
|
||||||
<input
|
|
||||||
ref="textboxKey"
|
|
||||||
v-model="newMetadata.key"
|
|
||||||
class="uk-input uk-form-width-small uk-form-small"
|
|
||||||
type="text"
|
|
||||||
name="flavor"
|
|
||||||
placeholder="Key"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="uk-margin-remove uk-width-1-2">
|
|
||||||
<input
|
|
||||||
v-model="newMetadata.value"
|
|
||||||
class="uk-input uk-form-width-small uk-form-small"
|
|
||||||
type="text"
|
|
||||||
name="flavor"
|
|
||||||
placeholder="Value"
|
|
||||||
@keyup.enter="handleMetadataSubmit()"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<a
|
|
||||||
href="#"
|
|
||||||
class="uk-icon uk-margin-left"
|
|
||||||
@click="handleMetadataSubmit()"
|
|
||||||
><span class="material-symbols-outlined">add_circle</span></a
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<div
|
|
||||||
v-for="(val, key) in value"
|
|
||||||
:key="key"
|
|
||||||
class="uk-width-1-1 uk-margin-small uk-margin-remove-left uk-margin-remove-right uk-flex uk-flex-middle"
|
|
||||||
>
|
|
||||||
<div class="uk-margin-remove-top uk-padding-remove uk-width-expand">
|
|
||||||
<labelInput
|
|
||||||
:name="key"
|
|
||||||
:label="key"
|
|
||||||
:value="value[key]"
|
|
||||||
@input="value[key] = $event"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<a href="#" class="uk-icon uk-width-auto" @click="delMetadataKey(key)"
|
|
||||||
><span class="material-symbols-outlined">delete</span></a
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import labelInput from "../fieldComponents/labelInput";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "KeyvalList",
|
|
||||||
|
|
||||||
components: {
|
|
||||||
labelInput
|
|
||||||
},
|
|
||||||
|
|
||||||
props: {
|
|
||||||
value: {
|
|
||||||
type: Object,
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
data: function() {
|
|
||||||
return {
|
|
||||||
newMetadata: {
|
|
||||||
key: "",
|
|
||||||
value: ""
|
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
handleMetadataSubmit: function() {
|
|
||||||
var newSelected = {};
|
|
||||||
|
|
||||||
if (this.value != null) {
|
|
||||||
Object.assign(newSelected, this.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
newSelected[this.newMetadata.key] = this.newMetadata.value;
|
|
||||||
this.newMetadata.key = "";
|
|
||||||
this.newMetadata.value = "";
|
|
||||||
|
|
||||||
this.$emit("input", newSelected);
|
|
||||||
|
|
||||||
// Move focus back to key textbox
|
|
||||||
if (this.$refs.textboxKey) {
|
|
||||||
this.$refs.textboxKey.focus();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
delMetadataKey: function(key) {
|
|
||||||
var newSelected = {};
|
|
||||||
|
|
||||||
if (this.value != null) {
|
|
||||||
Object.assign(newSelected, this.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$delete(newSelected, key);
|
|
||||||
|
|
||||||
this.$emit("input", newSelected);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped></style>
|
|
||||||
|
|
@ -1,65 +0,0 @@
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<label class="uk-form-label uk-text-bold">{{ label }}</label>
|
|
||||||
|
|
||||||
<div
|
|
||||||
uk-tooltip="title: Click to edit value; delay: 250"
|
|
||||||
@click="setEditing(true)"
|
|
||||||
>
|
|
||||||
<div v-show="editing == false">
|
|
||||||
<label> {{ value }} </label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<input
|
|
||||||
v-show="editing == true"
|
|
||||||
ref="textinput"
|
|
||||||
class="uk-input uk-form-small"
|
|
||||||
type="text"
|
|
||||||
:name="name"
|
|
||||||
:value="value"
|
|
||||||
autofocus
|
|
||||||
@blur="setEditing(false)"
|
|
||||||
@keyup.enter="setEditing(false)"
|
|
||||||
@input="$emit('input', $event.target.value)"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: "LabelInput",
|
|
||||||
|
|
||||||
props: {
|
|
||||||
label: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
name: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
value: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
data: function() {
|
|
||||||
return {
|
|
||||||
editing: false
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
setEditing(editing) {
|
|
||||||
this.editing = editing;
|
|
||||||
if (editing == true) {
|
|
||||||
this.$nextTick(() => this.$refs.textinput.focus());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped></style>
|
|
||||||
|
|
@ -1,44 +0,0 @@
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<label class="uk-form-label">{{ label }}</label>
|
|
||||||
|
|
||||||
<input
|
|
||||||
class="uk-input uk-form-small"
|
|
||||||
type="number"
|
|
||||||
:name="name"
|
|
||||||
:value="value"
|
|
||||||
:placeholder="placeholder"
|
|
||||||
v-bind="$attrs"
|
|
||||||
@input="$emit('input', Number($event.target.value))"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: "NumberInput",
|
|
||||||
|
|
||||||
props: {
|
|
||||||
value: {
|
|
||||||
type: Number,
|
|
||||||
required: false,
|
|
||||||
default: 0
|
|
||||||
},
|
|
||||||
placeholder: {
|
|
||||||
type: Number,
|
|
||||||
required: false,
|
|
||||||
default: 0
|
|
||||||
},
|
|
||||||
name: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
label: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped></style>
|
|
||||||
|
|
@ -1,49 +0,0 @@
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<label>{{ label }}</label>
|
|
||||||
|
|
||||||
<div class="uk-form-controls">
|
|
||||||
<div v-for="option in options" :key="option">
|
|
||||||
<label
|
|
||||||
><input
|
|
||||||
class="uk-radio"
|
|
||||||
type="radio"
|
|
||||||
:name="name"
|
|
||||||
:value="option"
|
|
||||||
:checked="value == option"
|
|
||||||
@input="$emit('input', $event.target.value)"
|
|
||||||
/>
|
|
||||||
{{ option }}</label
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: "RadioList",
|
|
||||||
|
|
||||||
props: {
|
|
||||||
value: {
|
|
||||||
type: String,
|
|
||||||
required: false,
|
|
||||||
default: ""
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
type: Array,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
name: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
label: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped></style>
|
|
||||||
|
|
@ -1,44 +0,0 @@
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<label class="uk-form-label">{{ label }}</label>
|
|
||||||
|
|
||||||
<select
|
|
||||||
class="uk-select uk-form-small"
|
|
||||||
:value="value"
|
|
||||||
v-bind="$attrs"
|
|
||||||
@input="$emit('input', $event.target.value)"
|
|
||||||
>
|
|
||||||
<option v-for="option in options" :key="option">
|
|
||||||
{{ option }}
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: "SelectList",
|
|
||||||
|
|
||||||
props: {
|
|
||||||
value: {
|
|
||||||
type: String,
|
|
||||||
required: false,
|
|
||||||
default: ""
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
type: Array,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
name: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
label: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped></style>
|
|
||||||
|
|
@ -1,79 +0,0 @@
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<form @submit.prevent="handleTagSubmit">
|
|
||||||
<div class="uk-margin-small uk-flex uk-flex-middle">
|
|
||||||
<div class="uk-margin-remove-top uk-width-expand">
|
|
||||||
<div class="uk-inline uk-width-1-1">
|
|
||||||
<span class="uk-form-icon"
|
|
||||||
><span class="material-symbols-outlined">label</span></span
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
v-model="newTag"
|
|
||||||
class="uk-input uk-form-small"
|
|
||||||
type="text"
|
|
||||||
name="flavor"
|
|
||||||
placeholder="Tag"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<a href="#" class="uk-icon uk-margin-left" @click="handleTagSubmit()"
|
|
||||||
><span class="material-symbols-outlined">add_circle</span></a
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<span
|
|
||||||
v-for="tag in value"
|
|
||||||
:key="tag"
|
|
||||||
class="uk-label uk-margin-small-right deletable-label"
|
|
||||||
@click="delTag(tag)"
|
|
||||||
>
|
|
||||||
{{ tag }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: "TagList",
|
|
||||||
|
|
||||||
props: {
|
|
||||||
value: {
|
|
||||||
type: Array,
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
data: function() {
|
|
||||||
return {
|
|
||||||
newTag: ""
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
handleTagSubmit: function() {
|
|
||||||
var newSelected = this.value != null ? [...this.value] : []; // Clone value array
|
|
||||||
|
|
||||||
newSelected.push(this.newTag);
|
|
||||||
this.newTag = "";
|
|
||||||
|
|
||||||
this.$emit("input", newSelected);
|
|
||||||
},
|
|
||||||
|
|
||||||
delTag: function(tag) {
|
|
||||||
var newSelected = this.value != null ? [...this.value] : []; // Clone value array
|
|
||||||
|
|
||||||
if (newSelected.includes(tag)) {
|
|
||||||
newSelected = newSelected.filter(function(value) {
|
|
||||||
return value != tag;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$emit("input", newSelected);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped></style>
|
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<label v-if="label" class="uk-form-label">{{ label }}</label>
|
|
||||||
|
|
||||||
<input
|
|
||||||
class="uk-input uk-form-small"
|
|
||||||
type="text"
|
|
||||||
:name="name"
|
|
||||||
:value="value"
|
|
||||||
:placeholder="placeholder"
|
|
||||||
v-bind="$attrs"
|
|
||||||
@input="$emit('input', $event.target.value)"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: "TextInput",
|
|
||||||
|
|
||||||
props: {
|
|
||||||
placeholder: {
|
|
||||||
type: String,
|
|
||||||
required: false,
|
|
||||||
default: null
|
|
||||||
},
|
|
||||||
label: {
|
|
||||||
type: String,
|
|
||||||
required: false,
|
|
||||||
default: null
|
|
||||||
},
|
|
||||||
name: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
value: {
|
|
||||||
type: String,
|
|
||||||
required: false,
|
|
||||||
default: ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped></style>
|
|
||||||
|
|
@ -22,20 +22,20 @@ export default {
|
||||||
|
|
||||||
data: function() {
|
data: function() {
|
||||||
return {
|
return {
|
||||||
isVisible: false
|
isVisible: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
streamImgUri: function() {
|
streamImgUri: function() {
|
||||||
return `${this.$store.getters.baseUri}/camera/mjpeg_stream`;
|
return `${this.$store.getters.baseUri}/camera/mjpeg_stream`;
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
visibilityChanged(isVisible) {
|
visibilityChanged(isVisible) {
|
||||||
this.isVisible = isVisible;
|
this.isVisible = isVisible;
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div class="progress uk-margin-top uk-margin-horizontal-remove uk-padding-remove">
|
||||||
class="progress uk-margin-top uk-margin-horizontal-remove uk-padding-remove"
|
|
||||||
>
|
|
||||||
<div class="indeterminate"></div>
|
<div class="indeterminate"></div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -21,16 +19,16 @@ export default {
|
||||||
classObject: function() {
|
classObject: function() {
|
||||||
return {
|
return {
|
||||||
"tabicon-active": this.currentTab == this.id,
|
"tabicon-active": this.currentTab == this.id,
|
||||||
"uk-disabled": this.requireConnection && !this.$store.getters.ready
|
"uk-disabled": this.requireConnection && !this.$store.getters.ready,
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
setThisTab(event) {
|
setThisTab(event) {
|
||||||
this.$emit("set-tab", event, this.id);
|
this.$emit("set-tab", event, this.id);
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -77,8 +75,7 @@ export default {
|
||||||
left: 0;
|
left: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
will-change: left, right;
|
will-change: left, right;
|
||||||
-webkit-animation: indeterminate 2.1s cubic-bezier(0.65, 0.815, 0.735, 0.395)
|
-webkit-animation: indeterminate 2.1s cubic-bezier(0.65, 0.815, 0.735, 0.395) infinite;
|
||||||
infinite;
|
|
||||||
animation: indeterminate 2.1s cubic-bezier(0.65, 0.815, 0.735, 0.395) infinite;
|
animation: indeterminate 2.1s cubic-bezier(0.65, 0.815, 0.735, 0.395) infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -90,10 +87,8 @@ export default {
|
||||||
left: 0;
|
left: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
will-change: left, right;
|
will-change: left, right;
|
||||||
-webkit-animation: indeterminate-short 2.1s cubic-bezier(0.165, 0.84, 0.44, 1)
|
-webkit-animation: indeterminate-short 2.1s cubic-bezier(0.165, 0.84, 0.44, 1) infinite;
|
||||||
infinite;
|
animation: indeterminate-short 2.1s cubic-bezier(0.165, 0.84, 0.44, 1) infinite;
|
||||||
animation: indeterminate-short 2.1s cubic-bezier(0.165, 0.84, 0.44, 1)
|
|
||||||
infinite;
|
|
||||||
-webkit-animation-delay: 1.15s;
|
-webkit-animation-delay: 1.15s;
|
||||||
animation-delay: 1.15s;
|
animation-delay: 1.15s;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,17 +15,17 @@ export default {
|
||||||
props: {
|
props: {
|
||||||
tabID: {
|
tabID: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true
|
required: true,
|
||||||
},
|
},
|
||||||
currentTab: {
|
currentTab: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true
|
required: true,
|
||||||
},
|
},
|
||||||
requireConnection: Boolean
|
requireConnection: Boolean,
|
||||||
},
|
},
|
||||||
computed: {},
|
computed: {},
|
||||||
|
|
||||||
methods: {}
|
methods: {},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<a
|
<a href="#" class="uk-link" :class="classObject" :uk-tooltip="tooltipOptions" @click="setThisTab">
|
||||||
href="#"
|
|
||||||
class="uk-link"
|
|
||||||
:class="classObject"
|
|
||||||
:uk-tooltip="tooltipOptions"
|
|
||||||
@click="setThisTab"
|
|
||||||
>
|
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
<div v-if="showTitle" class="tabtitle">
|
<div v-if="showTitle" class="tabtitle">
|
||||||
{{ computedTitle }}
|
{{ computedTitle }}
|
||||||
|
|
@ -20,33 +14,33 @@ export default {
|
||||||
props: {
|
props: {
|
||||||
tabID: {
|
tabID: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true
|
required: true,
|
||||||
},
|
},
|
||||||
title: {
|
title: {
|
||||||
type: String,
|
type: String,
|
||||||
required: false,
|
required: false,
|
||||||
default: undefined
|
default: undefined,
|
||||||
},
|
},
|
||||||
showTitle: {
|
showTitle: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: false,
|
required: false,
|
||||||
default: true
|
default: true,
|
||||||
},
|
},
|
||||||
showTooltip: {
|
showTooltip: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: false,
|
required: false,
|
||||||
default: true
|
default: true,
|
||||||
},
|
},
|
||||||
currentTab: {
|
currentTab: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true
|
required: true,
|
||||||
},
|
},
|
||||||
clickCallback: {
|
clickCallback: {
|
||||||
type: Function,
|
type: Function,
|
||||||
required: false,
|
required: false,
|
||||||
default: null
|
default: null,
|
||||||
},
|
},
|
||||||
requireConnection: Boolean
|
requireConnection: Boolean,
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
|
|
@ -72,9 +66,9 @@ export default {
|
||||||
classObject: function() {
|
classObject: function() {
|
||||||
return {
|
return {
|
||||||
"tabicon-active": this.currentTab == this.tabID,
|
"tabicon-active": this.currentTab == this.tabID,
|
||||||
"uk-disabled": this.requireConnection && !this.$store.getters.ready
|
"uk-disabled": this.requireConnection && !this.$store.getters.ready,
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
|
@ -83,8 +77,8 @@ export default {
|
||||||
if (this.clickCallback) {
|
if (this.clickCallback) {
|
||||||
this.clickCallback();
|
this.clickCallback();
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div v-observe-visibility="visibilityChanged" class="uk-margin-remove uk-padding-remove">
|
||||||
v-observe-visibility="visibilityChanged"
|
|
||||||
class="uk-margin-remove uk-padding-remove"
|
|
||||||
>
|
|
||||||
<div v-if="taskStarted" ref="isPollingElement">
|
<div v-if="taskStarted" ref="isPollingElement">
|
||||||
<action-progress-bar v-if="taskStarted && hideOnRun" :progress="progress" :task-status="taskStatus" />
|
<action-progress-bar
|
||||||
|
v-if="taskStarted && hideOnRun"
|
||||||
|
:progress="progress"
|
||||||
|
:task-status="taskStatus"
|
||||||
|
/>
|
||||||
<!-- hideOnRun selects if the button hides, don't show progress bar if button doesn't hide. -->
|
<!-- hideOnRun selects if the button hides, don't show progress bar if button doesn't hide. -->
|
||||||
<button
|
<button
|
||||||
v-if="canTerminate && taskRunning"
|
v-if="canTerminate && taskRunning"
|
||||||
|
|
@ -22,7 +23,10 @@
|
||||||
:disabled="isDisabled"
|
:disabled="isDisabled"
|
||||||
:hidden="taskStarted && hideOnRun"
|
:hidden="taskStarted && hideOnRun"
|
||||||
class="uk-button uk-width-1-1"
|
class="uk-button uk-width-1-1"
|
||||||
:class="[isDisabled ? 'uk-button-disabled' : '', buttonPrimary ? 'uk-button-primary' : 'uk-button-default']"
|
:class="[
|
||||||
|
isDisabled ? 'uk-button-disabled' : '',
|
||||||
|
buttonPrimary ? 'uk-button-primary' : 'uk-button-default',
|
||||||
|
]"
|
||||||
@click="bootstrapTask()"
|
@click="bootstrapTask()"
|
||||||
>
|
>
|
||||||
{{ submitLabel }}
|
{{ submitLabel }}
|
||||||
|
|
@ -35,18 +39,12 @@
|
||||||
class=""
|
class=""
|
||||||
uk-modal="bg-close: false; esc-close: false; stack: true;"
|
uk-modal="bg-close: false; esc-close: false; stack: true;"
|
||||||
>
|
>
|
||||||
<div
|
<div id="status-modal" class="uk-modal-dialog uk-modal-body uk-margin-auto-vertical">
|
||||||
id="status-modal"
|
|
||||||
class="uk-modal-dialog uk-modal-body uk-margin-auto-vertical"
|
|
||||||
>
|
|
||||||
<h2>{{ submitLabel }}</h2>
|
<h2>{{ submitLabel }}</h2>
|
||||||
<action-log-display :log="log" :task-status="taskStatus" />
|
<action-log-display :log="log" :task-status="taskStatus" />
|
||||||
<div id="progress-and-cancel-row">
|
<div id="progress-and-cancel-row">
|
||||||
<div class="stretchy">
|
<div class="stretchy">
|
||||||
<action-progress-bar
|
<action-progress-bar :progress="progress" :task-status="taskStatus" />
|
||||||
:progress="progress"
|
|
||||||
:task-status="taskStatus"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
|
@ -84,67 +82,67 @@ export default {
|
||||||
props: {
|
props: {
|
||||||
action: {
|
action: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true
|
required: true,
|
||||||
},
|
},
|
||||||
thing: {
|
thing: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true
|
required: true,
|
||||||
},
|
},
|
||||||
submitData: {
|
submitData: {
|
||||||
type: [Object, Array],
|
type: [Object, Array],
|
||||||
required: false,
|
required: false,
|
||||||
default: () => ({})
|
default: () => ({}),
|
||||||
},
|
},
|
||||||
pollInterval: {
|
pollInterval: {
|
||||||
type: Number,
|
type: Number,
|
||||||
required: false,
|
required: false,
|
||||||
default: 1
|
default: 1,
|
||||||
},
|
},
|
||||||
submitLabel: {
|
submitLabel: {
|
||||||
type: String,
|
type: String,
|
||||||
required: false,
|
required: false,
|
||||||
default: "Submit"
|
default: "Submit",
|
||||||
},
|
},
|
||||||
canTerminate: {
|
canTerminate: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: false,
|
required: false,
|
||||||
default: true
|
default: true,
|
||||||
},
|
},
|
||||||
requiresConfirmation: {
|
requiresConfirmation: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: false,
|
required: false,
|
||||||
default: false
|
default: false,
|
||||||
},
|
},
|
||||||
confirmationMessage: {
|
confirmationMessage: {
|
||||||
type: String,
|
type: String,
|
||||||
required: false,
|
required: false,
|
||||||
default: "Start task?"
|
default: "Start task?",
|
||||||
},
|
},
|
||||||
buttonPrimary: {
|
buttonPrimary: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: false,
|
required: false,
|
||||||
default: true
|
default: true,
|
||||||
},
|
},
|
||||||
submitOnEvent: {
|
submitOnEvent: {
|
||||||
type: String,
|
type: String,
|
||||||
required: false,
|
required: false,
|
||||||
default: null
|
default: null,
|
||||||
},
|
},
|
||||||
modalProgress: {
|
modalProgress: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: false,
|
required: false,
|
||||||
default: false
|
default: false,
|
||||||
},
|
},
|
||||||
isDisabled: {
|
isDisabled: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: false,
|
required: false,
|
||||||
default: false
|
default: false,
|
||||||
},
|
},
|
||||||
hideOnRun: {
|
hideOnRun: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: false,
|
required: false,
|
||||||
default: true
|
default: true,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
data: function() {
|
data: function() {
|
||||||
|
|
@ -154,14 +152,14 @@ export default {
|
||||||
taskStarted: false,
|
taskStarted: false,
|
||||||
taskRunning: false,
|
taskRunning: false,
|
||||||
log: [],
|
log: [],
|
||||||
taskStatus: ""
|
taskStatus: "",
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
submitUrl() {
|
submitUrl() {
|
||||||
return this.thingActionUrl(this.thing, this.action);
|
return this.thingActionUrl(this.thing, this.action);
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
|
|
@ -179,7 +177,7 @@ export default {
|
||||||
},
|
},
|
||||||
taskStatus(newval) {
|
taskStatus(newval) {
|
||||||
this.$emit("update:taskStatus", newval);
|
this.$emit("update:taskStatus", newval);
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
|
|
@ -195,13 +193,14 @@ export default {
|
||||||
// Otherwise, this is just normal property access
|
// Otherwise, this is just normal property access
|
||||||
return this[n];
|
return this[n];
|
||||||
}
|
}
|
||||||
const TypedArray = Reflect.getPrototypeOf(Int8Array);
|
const TypedArray = Reflect.getPrototypeOf(Int8Array);
|
||||||
for (const C of [Array, String, TypedArray]) {
|
for (const C of [Array, String, TypedArray]) {
|
||||||
Object.defineProperty(C.prototype, "from_index",
|
Object.defineProperty(C.prototype, "from_index", {
|
||||||
{ value: from_index,
|
value: from_index,
|
||||||
writable: true,
|
writable: true,
|
||||||
enumerable: false,
|
enumerable: false,
|
||||||
configurable: true });
|
configurable: true,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
// Check for already running tasks
|
// Check for already running tasks
|
||||||
if (this.taskStarted != true) {
|
if (this.taskStarted != true) {
|
||||||
|
|
@ -234,10 +233,7 @@ export default {
|
||||||
if (task.status == "pending" || task.status == "running") {
|
if (task.status == "pending" || task.status == "running") {
|
||||||
this.taskStarted = true;
|
this.taskStarted = true;
|
||||||
this.$emit("taskStarted");
|
this.$emit("taskStarted");
|
||||||
this.startPolling(
|
this.startPolling(task.id, task.links.find(t => t.rel == "self").href);
|
||||||
task.id,
|
|
||||||
task.links.find(t => t.rel == "self").href
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -250,7 +246,7 @@ export default {
|
||||||
() => {
|
() => {
|
||||||
this.startTask();
|
this.startTask();
|
||||||
},
|
},
|
||||||
() => {}
|
() => {},
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
this.startTask();
|
this.startTask();
|
||||||
|
|
@ -271,10 +267,7 @@ export default {
|
||||||
UIkit.modal(this.$refs.statusModal).show();
|
UIkit.modal(this.$refs.statusModal).show();
|
||||||
}
|
}
|
||||||
// Start the store polling TaskId for success
|
// Start the store polling TaskId for success
|
||||||
response = await this.startPolling(
|
response = await this.startPolling(response.data.id, response.data.href);
|
||||||
response.data.id,
|
|
||||||
response.data.href
|
|
||||||
);
|
|
||||||
if (response.status == "completed") {
|
if (response.status == "completed") {
|
||||||
this.$emit("response", response);
|
this.$emit("response", response);
|
||||||
this.$emit("completed", response.output);
|
this.$emit("completed", response.output);
|
||||||
|
|
@ -312,43 +305,46 @@ export default {
|
||||||
|
|
||||||
var checkCondition = (resolve, reject) => {
|
var checkCondition = (resolve, reject) => {
|
||||||
// If the condition is met, we're done!
|
// If the condition is met, we're done!
|
||||||
axios
|
axios.get(this.taskUrl, { baseURL: this.$store.getters.baseUri }).then(response => {
|
||||||
.get(this.taskUrl, { baseURL: this.$store.getters.baseUri })
|
var result = response.data.status;
|
||||||
.then(response => {
|
this.taskStatus = result;
|
||||||
var result = response.data.status;
|
if ((result == "running") | (result == "pending")) {
|
||||||
this.taskStatus = result;
|
// If the task is still running, we update progress/log,
|
||||||
if ((result == "running") | (result == "pending")) {
|
// and schedule another poll
|
||||||
// If the task is still running, we update progress/log,
|
this.progress = response.data.progress;
|
||||||
// and schedule another poll
|
this.log = response.data.log;
|
||||||
this.progress = response.data.progress;
|
// Check again after timeout
|
||||||
this.log = response.data.log;
|
setTimeout(checkCondition, interval, resolve, reject);
|
||||||
// Check again after timeout
|
}
|
||||||
setTimeout(checkCondition, interval, resolve, reject);
|
// If task ends with an error
|
||||||
|
else if (result == "error") {
|
||||||
|
// Pass the error string back with reject
|
||||||
|
if (!this.progress) this.progress = 1;
|
||||||
|
// Test whether the log is empty or the most recent message is not from an error
|
||||||
|
// If so, return a default message
|
||||||
|
if (
|
||||||
|
(response.data.log.length == 0) |
|
||||||
|
(response.data.log.from_index(-1).levelname != "ERROR")
|
||||||
|
) {
|
||||||
|
var message = "Unexpected error, please check the logs";
|
||||||
}
|
}
|
||||||
// If task ends with an error
|
// As LabThings Actions add the message from any raised exception to the log, the
|
||||||
else if (result == "error") {
|
// last message in the log is the message from the Exception.
|
||||||
// Pass the error string back with reject
|
//If the Exception was raised with no message, use a default.
|
||||||
if (!this.progress) this.progress = 1;
|
|
||||||
// Test whether the log is empty or the most recent message is not from an error
|
|
||||||
// If so, return a default message
|
|
||||||
if (response.data.log.length == 0 | response.data.log.from_index(-1).levelname != "ERROR") {
|
|
||||||
var message = "Unexpected error, please check the logs";
|
|
||||||
}
|
|
||||||
// As LabThings Actions add the message from any raised exception to the log, the
|
|
||||||
// last message in the log is the message from the Exception.
|
|
||||||
//If the Exception was raised with no message, use a default.
|
|
||||||
else {
|
|
||||||
message = response.data.log.from_index(-1).message||"Unexpected error, please check the logs";
|
|
||||||
}
|
|
||||||
// Raise an Error with the chosen message
|
|
||||||
reject(new Error(message));
|
|
||||||
}
|
|
||||||
// If task ends without reporting an error
|
|
||||||
// (NB this includes cancellation)
|
|
||||||
else {
|
else {
|
||||||
resolve(response.data);
|
message =
|
||||||
|
response.data.log.from_index(-1).message ||
|
||||||
|
"Unexpected error, please check the logs";
|
||||||
}
|
}
|
||||||
});
|
// Raise an Error with the chosen message
|
||||||
|
reject(new Error(message));
|
||||||
|
}
|
||||||
|
// If task ends without reporting an error
|
||||||
|
// (NB this includes cancellation)
|
||||||
|
else {
|
||||||
|
resolve(response.data);
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return new Promise(checkCondition);
|
return new Promise(checkCondition);
|
||||||
|
|
@ -362,8 +358,8 @@ export default {
|
||||||
terminateTask: function() {
|
terminateTask: function() {
|
||||||
axios.delete(this.taskUrl, { baseURL: this.$store.getters.baseUri });
|
axios.delete(this.taskUrl, { baseURL: this.$store.getters.baseUri });
|
||||||
this.$root.$emit("modalClosed");
|
this.$root.$emit("modalClosed");
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,24 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div class="log-wrapper" @mouseenter="onMouseEnter" @mouseleave="onMouseLeave">
|
||||||
class="log-wrapper"
|
<div ref="logContainer" class="log-container uk-margin-left uk-margin-right uk-margin">
|
||||||
@mouseenter="onMouseEnter"
|
<div v-if="log">
|
||||||
@mouseleave="onMouseLeave"
|
<div v-for="(item, index) in log" :key="`log_entry_${index}`">
|
||||||
>
|
{{ item.message }}
|
||||||
<div
|
</div>
|
||||||
ref="logContainer"
|
|
||||||
class="log-container uk-margin-left uk-margin-right uk-margin"
|
|
||||||
>
|
|
||||||
<div v-if="log">
|
|
||||||
<div v-for="(item, index) in log" :key="`log_entry_${index}`">
|
|
||||||
{{ item.message }}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div v-if="taskStatus == 'error'" class="uk-alert uk-alert-danger">
|
||||||
<div v-if="taskStatus == 'error'" class="uk-alert uk-alert-danger">
|
The task failed due to an error. There may be more information in the log.
|
||||||
The task failed due to an error. There may be more information in the log.
|
|
||||||
</div>
|
|
||||||
<div v-if="taskStatus == 'cancelled'" class="uk-alert uk-alert-warning">
|
|
||||||
The task was cancelled.
|
|
||||||
</div>
|
|
||||||
<div v-if="taskStatus == 'completed'" class="uk-alert uk-alert-success">
|
|
||||||
The task completed successfully.
|
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="taskStatus == 'cancelled'" class="uk-alert uk-alert-warning">
|
||||||
|
The task was cancelled.
|
||||||
|
</div>
|
||||||
|
<div v-if="taskStatus == 'completed'" class="uk-alert uk-alert-success">
|
||||||
|
The task completed successfully.
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Paused banner outside scroll container, positioned relative to wrapper -->
|
<!-- Paused banner outside scroll container, positioned relative to wrapper -->
|
||||||
<div v-if="userIsHovering" class="paused-banner">
|
<div v-if="userIsHovering" class="paused-banner">
|
||||||
Auto-scroll paused
|
Auto-scroll paused
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -38,12 +31,12 @@ export default {
|
||||||
props: {
|
props: {
|
||||||
taskStatus: {
|
taskStatus: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true
|
required: true,
|
||||||
},
|
},
|
||||||
log: {
|
log: {
|
||||||
type: Array,
|
type: Array,
|
||||||
required: true
|
required: true,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
|
|
@ -58,7 +51,7 @@ export default {
|
||||||
},
|
},
|
||||||
taskStatus: function() {
|
taskStatus: function() {
|
||||||
this.scrollToBottom();
|
this.scrollToBottom();
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
|
@ -70,15 +63,15 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
scrollToBottom() {
|
scrollToBottom() {
|
||||||
/*Scroll to bottom of log unless the user is hovering over the log.*/
|
/*Scroll to bottom of log unless the user is hovering over the log.*/
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
if (this.userIsHovering) return;
|
if (this.userIsHovering) return;
|
||||||
|
|
||||||
const viewer = this.$refs.logContainer;
|
const viewer = this.$refs.logContainer;
|
||||||
viewer.scrollTop = viewer.scrollHeight;
|
viewer.scrollTop = viewer.scrollHeight;
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,12 +13,12 @@ export default {
|
||||||
progress: {
|
progress: {
|
||||||
type: Number,
|
type: Number,
|
||||||
required: false,
|
required: false,
|
||||||
default: null
|
default: null,
|
||||||
},
|
},
|
||||||
taskStatus: {
|
taskStatus: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true
|
required: true,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
|
|
@ -33,8 +33,8 @@ export default {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -81,8 +81,7 @@ export default {
|
||||||
left: 0;
|
left: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
will-change: left, right;
|
will-change: left, right;
|
||||||
-webkit-animation: indeterminate 2.1s cubic-bezier(0.65, 0.815, 0.735, 0.395)
|
-webkit-animation: indeterminate 2.1s cubic-bezier(0.65, 0.815, 0.735, 0.395) infinite;
|
||||||
infinite;
|
|
||||||
animation: indeterminate 2.1s cubic-bezier(0.65, 0.815, 0.735, 0.395) infinite;
|
animation: indeterminate 2.1s cubic-bezier(0.65, 0.815, 0.735, 0.395) infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -94,10 +93,8 @@ export default {
|
||||||
left: 0;
|
left: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
will-change: left, right;
|
will-change: left, right;
|
||||||
-webkit-animation: indeterminate-short 2.1s cubic-bezier(0.165, 0.84, 0.44, 1)
|
-webkit-animation: indeterminate-short 2.1s cubic-bezier(0.165, 0.84, 0.44, 1) infinite;
|
||||||
infinite;
|
animation: indeterminate-short 2.1s cubic-bezier(0.165, 0.84, 0.44, 1) infinite;
|
||||||
animation: indeterminate-short 2.1s cubic-bezier(0.165, 0.84, 0.44, 1)
|
|
||||||
infinite;
|
|
||||||
-webkit-animation-delay: 1.15s;
|
-webkit-animation-delay: 1.15s;
|
||||||
animation-delay: 1.15s;
|
animation-delay: 1.15s;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,18 @@
|
||||||
<template>
|
<template>
|
||||||
<a
|
<a
|
||||||
class="uk-button"
|
class="uk-button"
|
||||||
:class="[isDisabled ? 'uk-button-disabled' : '', buttonPrimary ? 'uk-button-primary' : 'uk-button-default']"
|
:class="[
|
||||||
:href="URL"
|
isDisabled ? 'uk-button-disabled' : '',
|
||||||
download
|
buttonPrimary ? 'uk-button-primary' : 'uk-button-default',
|
||||||
> {{ buttonLabel }}</a
|
]"
|
||||||
>
|
:href="url"
|
||||||
|
download
|
||||||
|
>
|
||||||
|
{{ buttonLabel }}</a
|
||||||
|
>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "EndpointButton",
|
name: "EndpointButton",
|
||||||
|
|
||||||
|
|
@ -17,27 +20,27 @@ export default {
|
||||||
buttonPrimary: {
|
buttonPrimary: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: false,
|
required: false,
|
||||||
default: true
|
default: true,
|
||||||
},
|
},
|
||||||
URL: {
|
url: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
isDisabled: {
|
isDisabled: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: false,
|
required: false,
|
||||||
default: false
|
default: false,
|
||||||
},
|
},
|
||||||
destinationName: {
|
destinationName: {
|
||||||
type: String,
|
type: String,
|
||||||
required: false,
|
required: false,
|
||||||
default: null
|
default: null,
|
||||||
},
|
},
|
||||||
buttonLabel: {
|
buttonLabel: {
|
||||||
type: String,
|
type: String,
|
||||||
required: false,
|
required: false,
|
||||||
default: "Download File"
|
default: "Download File",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -51,8 +51,8 @@
|
||||||
<label v-if="dataType == 'number_object'" class="uk-form-label"
|
<label v-if="dataType == 'number_object'" class="uk-form-label"
|
||||||
>{{ label }}
|
>{{ label }}
|
||||||
<div v-for="(val, key) in value" :key="key">
|
<div v-for="(val, key) in value" :key="key">
|
||||||
<label>{{internalLabels[key]}}</label>
|
<label>{{ internalLabels[key] }}</label>
|
||||||
<div class="input-and-buttons-container" >
|
<div class="input-and-buttons-container">
|
||||||
<input
|
<input
|
||||||
v-model="internalValue[key]"
|
v-model="internalValue[key]"
|
||||||
class="uk-form-small numeric-setting-line-input"
|
class="uk-form-small numeric-setting-line-input"
|
||||||
|
|
@ -89,24 +89,26 @@ export default {
|
||||||
name: "InputFromSchema",
|
name: "InputFromSchema",
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
syncPropertyButton
|
syncPropertyButton,
|
||||||
},
|
},
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
dataSchema: {
|
dataSchema: {
|
||||||
type: Object
|
type: Object,
|
||||||
|
required: true,
|
||||||
},
|
},
|
||||||
value: {
|
value: {
|
||||||
type: null
|
type: null,
|
||||||
|
required: true,
|
||||||
},
|
},
|
||||||
label: {
|
label: {
|
||||||
type: String,
|
type: String,
|
||||||
default: ""
|
default: "",
|
||||||
},
|
},
|
||||||
animate: {
|
animate: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
|
|
@ -116,44 +118,21 @@ export default {
|
||||||
// (see resetInternalValue). If we do this here there is a chance we get errors
|
// (see resetInternalValue). If we do this here there is a chance we get errors
|
||||||
// as internalValue is still null when rendering starts.
|
// as internalValue is still null when rendering starts.
|
||||||
internalValue: Array.isArray(this.value)
|
internalValue: Array.isArray(this.value)
|
||||||
? [...this.value]
|
? [...this.value]
|
||||||
: typeof this.value === 'object'
|
: typeof this.value === "object"
|
||||||
? { ...this.value }
|
? { ...this.value }
|
||||||
: this.value,
|
: this.value,
|
||||||
// Is edited can't be computed as we mutate internalValue
|
// Is edited can't be computed as we mutate internalValue
|
||||||
isEdited: false,
|
isEdited: false,
|
||||||
animateUpdate: false,
|
animateUpdate: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
|
||||||
if (this.value !== undefined) {
|
|
||||||
this.resetInternalValue();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
|
||||||
value() {
|
|
||||||
// Fire updateIsEdited on both value and internal value change,
|
|
||||||
// as change in value may not causse internalValue to change.
|
|
||||||
this.updateIsEdited();
|
|
||||||
this.resetInternalValue();
|
|
||||||
},
|
|
||||||
internalValue() {
|
|
||||||
this.updateIsEdited();
|
|
||||||
},
|
|
||||||
animate(updated) {
|
|
||||||
if (updated) {
|
|
||||||
this.animateUpdate = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
computed: {
|
||||||
internalLabels: function() {
|
internalLabels: function() {
|
||||||
if (this.dataType == "number_object") {
|
if (this.dataType == "number_object") {
|
||||||
let labels = {};
|
let labels = {};
|
||||||
for (const key in this.internalValue){
|
for (const key in this.internalValue) {
|
||||||
labels[key] = this.dataSchema.properties[key].title
|
labels[key] = this.dataSchema.properties[key].title;
|
||||||
}
|
}
|
||||||
return labels;
|
return labels;
|
||||||
}
|
}
|
||||||
|
|
@ -204,6 +183,29 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "other";
|
return "other";
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
value() {
|
||||||
|
// Fire updateIsEdited on both value and internal value change,
|
||||||
|
// as change in value may not causse internalValue to change.
|
||||||
|
this.updateIsEdited();
|
||||||
|
this.resetInternalValue();
|
||||||
|
},
|
||||||
|
internalValue() {
|
||||||
|
this.updateIsEdited();
|
||||||
|
},
|
||||||
|
animate(updated) {
|
||||||
|
if (updated) {
|
||||||
|
this.animateUpdate = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
if (this.value !== undefined) {
|
||||||
|
this.resetInternalValue();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -215,10 +217,10 @@ export default {
|
||||||
this.internalValue = JSON.parse(JSON.stringify(this.value));
|
this.internalValue = JSON.parse(JSON.stringify(this.value));
|
||||||
},
|
},
|
||||||
requestUpdate: async function() {
|
requestUpdate: async function() {
|
||||||
this.$emit("requestUpdate")
|
this.$emit("requestUpdate");
|
||||||
},
|
},
|
||||||
sendValue: async function() {
|
sendValue: async function() {
|
||||||
this.$emit("sendValue", this.internalValue)
|
this.$emit("sendValue", this.internalValue);
|
||||||
},
|
},
|
||||||
checkboxUpdated: function() {
|
checkboxUpdated: function() {
|
||||||
if (this.internalValue != this.$refs.checkbox.checked) {
|
if (this.internalValue != this.$refs.checkbox.checked) {
|
||||||
|
|
@ -245,7 +247,7 @@ export default {
|
||||||
},
|
},
|
||||||
animationEnd: function() {
|
animationEnd: function() {
|
||||||
this.animateUpdate = false;
|
this.animateUpdate = false;
|
||||||
this.$emit('animationShown');
|
this.$emit("animationShown");
|
||||||
},
|
},
|
||||||
deepStringify: function(val) {
|
deepStringify: function(val) {
|
||||||
// Create a json string where all internal numbers are also JSON strings. This is
|
// Create a json string where all internal numbers are also JSON strings. This is
|
||||||
|
|
@ -256,16 +258,13 @@ export default {
|
||||||
if (Array.isArray(val)) {
|
if (Array.isArray(val)) {
|
||||||
return JSON.stringify(val.map(String));
|
return JSON.stringify(val.map(String));
|
||||||
}
|
}
|
||||||
if (val && typeof val === 'object') {
|
if (val && typeof val === "object") {
|
||||||
const normalized = Object.fromEntries(
|
const normalized = Object.fromEntries(Object.entries(val).map(([k, v]) => [k, String(v)]));
|
||||||
Object.entries(val).map(([k, v]) => [k, String(v)])
|
|
||||||
);
|
|
||||||
return JSON.stringify(normalized);
|
return JSON.stringify(normalized);
|
||||||
}
|
}
|
||||||
return JSON.stringify(String(val));
|
return JSON.stringify(String(val));
|
||||||
}
|
},
|
||||||
|
},
|
||||||
}
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -288,8 +287,12 @@ export default {
|
||||||
background-color: #fff3cd;
|
background-color: #fff3cd;
|
||||||
}
|
}
|
||||||
@keyframes green-flash {
|
@keyframes green-flash {
|
||||||
0% { background-color: #3fda63; }
|
0% {
|
||||||
100% { background-color: white; }
|
background-color: #3fda63;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.flash {
|
.flash {
|
||||||
animation: green-flash 0.7s ease;
|
animation: green-flash 0.7s ease;
|
||||||
|
|
|
||||||
|
|
@ -18,58 +18,56 @@ export default {
|
||||||
name: "PropertyControl",
|
name: "PropertyControl",
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
InputFromSchema
|
InputFromSchema,
|
||||||
},
|
},
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
label: {
|
label: {
|
||||||
type: String,
|
type: String,
|
||||||
default: ""
|
default: "",
|
||||||
},
|
},
|
||||||
propertyName: {
|
propertyName: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true
|
required: true,
|
||||||
},
|
},
|
||||||
thingName: {
|
thingName: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true
|
required: true,
|
||||||
},
|
},
|
||||||
readBack: {
|
readBack: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: false,
|
required: false,
|
||||||
default: false
|
default: false,
|
||||||
},
|
},
|
||||||
readBackDelay: {
|
readBackDelay: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 1000,
|
default: 1000,
|
||||||
required: false
|
required: false,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
value: undefined,
|
value: undefined,
|
||||||
animate: false
|
animate: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
propertyDescription: function() {
|
propertyDescription: function() {
|
||||||
try {
|
try {
|
||||||
return this.thingDescription(this.thingName).properties[
|
return this.thingDescription(this.thingName).properties[this.propertyName];
|
||||||
this.propertyName
|
|
||||||
];
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
propertyDescription: function() {
|
propertyDescription: function() {
|
||||||
// Ensure we read the property once the URL is known
|
// Ensure we read the property once the URL is known
|
||||||
this.readProperty();
|
this.readProperty();
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted: function() {
|
mounted: function() {
|
||||||
|
|
@ -83,21 +81,14 @@ export default {
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
readProperty: async function() {
|
readProperty: async function() {
|
||||||
let data = await this.readThingProperty(
|
let data = await this.readThingProperty(this.thingName, this.propertyName);
|
||||||
this.thingName,
|
|
||||||
this.propertyName
|
|
||||||
);
|
|
||||||
this.value = data;
|
this.value = data;
|
||||||
return data;
|
return data;
|
||||||
},
|
},
|
||||||
writeProperty: async function(requestedValue) {
|
writeProperty: async function(requestedValue) {
|
||||||
try {
|
try {
|
||||||
this.value=requestedValue;
|
this.value = requestedValue;
|
||||||
await this.writeThingProperty(
|
await this.writeThingProperty(this.thingName, this.propertyName, requestedValue);
|
||||||
this.thingName,
|
|
||||||
this.propertyName,
|
|
||||||
requestedValue
|
|
||||||
);
|
|
||||||
if (this.readBack) {
|
if (this.readBack) {
|
||||||
await new Promise(r => setTimeout(r, this.readBackDelay));
|
await new Promise(r => setTimeout(r, this.readBackDelay));
|
||||||
let newVal = await this.readProperty();
|
let newVal = await this.readProperty();
|
||||||
|
|
@ -106,7 +97,9 @@ export default {
|
||||||
} else {
|
} else {
|
||||||
this.animate = true;
|
this.animate = true;
|
||||||
await this.modalNotify(
|
await this.modalNotify(
|
||||||
`Set ${this.label} to ${formatValue(newVal)} (closest valid value to requested ${formatValue(requestedValue)}).`
|
`Set ${this.label} to ${formatValue(
|
||||||
|
newVal,
|
||||||
|
)} (closest valid value to requested ${formatValue(requestedValue)}).`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -120,9 +113,9 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
resetAnimate: function() {
|
resetAnimate: function() {
|
||||||
this.animate = false;
|
this.animate = false;
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,15 +22,14 @@ export default {
|
||||||
name: "ServerSpecifiedActionButton",
|
name: "ServerSpecifiedActionButton",
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
ActionButton
|
ActionButton,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
actionData: {
|
actionData: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true,
|
required: true,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
|
@ -38,8 +37,8 @@ export default {
|
||||||
if (this.actionData.notify_on_success) {
|
if (this.actionData.notify_on_success) {
|
||||||
this.modalNotify(this.actionData.success_message);
|
this.modalNotify(this.actionData.success_message);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,15 +17,15 @@ export default {
|
||||||
name: "ServerSpecifiedPropertyControl",
|
name: "ServerSpecifiedPropertyControl",
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
PropertyControl
|
PropertyControl,
|
||||||
},
|
},
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
propertyData: {
|
propertyData: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true,
|
required: true,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<a @click.prevent="$emit('click')" class="sync-button">
|
<a class="sync-button" @click.prevent="$emit('click')">
|
||||||
<span
|
<span
|
||||||
class="material-symbols-outlined sync-icon"
|
class="material-symbols-outlined sync-icon"
|
||||||
title="Reread value from microscope. Required if microscope is updated externally"
|
title="Reread value from microscope. Required if microscope is updated externally"
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: "syncPropertyButton"
|
name: "SyncPropertyButton",
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -24,14 +24,13 @@ export default {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.material-symbols-outlined.sync-icon{
|
.material-symbols-outlined.sync-icon {
|
||||||
color: #888;
|
color: #888;
|
||||||
transition: transform 0.3s ease, color 0.3s ease;
|
transition: transform 0.3s ease, color 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.material-symbols-outlined.sync-icon:hover{
|
.material-symbols-outlined.sync-icon:hover {
|
||||||
transform: rotate(-90deg);
|
transform: rotate(-90deg);
|
||||||
color: #c5247f;
|
color: #c5247f;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="uk-flex uk-flex-column uk-flex-center uk-height-1-1">
|
<div class="uk-flex uk-flex-column uk-flex-center uk-height-1-1">
|
||||||
<div
|
<div v-if="$store.state.waiting" class="uk-align-center" uk-spinner="ratio: 3"></div>
|
||||||
v-if="$store.state.waiting"
|
|
||||||
class="uk-align-center"
|
|
||||||
uk-spinner="ratio: 3"
|
|
||||||
></div>
|
|
||||||
<div v-if="$store.state.waiting" class="uk-align-center">Loading...</div>
|
<div v-if="$store.state.waiting" class="uk-align-center">Loading...</div>
|
||||||
<span class="material-symbols-outlined uk-align-center error-icon"
|
<span class="material-symbols-outlined uk-align-center error-icon">error_outline</span>
|
||||||
>error_outline</span
|
|
||||||
>
|
|
||||||
<div v-if="$store.state.error" class="uk-align-center">
|
<div v-if="$store.state.error" class="uk-align-center">
|
||||||
{{ $store.state.error }}
|
{{ $store.state.error }}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -29,7 +23,7 @@ export default {
|
||||||
|
|
||||||
data: function() {
|
data: function() {
|
||||||
return {};
|
return {};
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,13 @@
|
||||||
<h2 class="uk-modal-title">Microscope Calibration</h2>
|
<h2 class="uk-modal-title">Microscope Calibration</h2>
|
||||||
|
|
||||||
<component
|
<component
|
||||||
v-if="currentTask"
|
|
||||||
:is="currentTask.component"
|
:is="currentTask.component"
|
||||||
|
v-if="currentTask"
|
||||||
:key="taskIndex"
|
:key="taskIndex"
|
||||||
v-bind="currentTask.props"
|
v-bind="currentTask.props"
|
||||||
:first="isFirstTask"
|
:first="isFirstTask"
|
||||||
:final="isFinalTask"
|
:final="isFinalTask"
|
||||||
:startOnLast="movingBackward"
|
:start-on-last="movingBackward"
|
||||||
@next="nextTask"
|
@next="nextTask"
|
||||||
@back="previousTask"
|
@back="previousTask"
|
||||||
/>
|
/>
|
||||||
|
|
@ -26,7 +26,7 @@ import cameraStageMappingTask from "./calibrationWizardComponents/cameraStageMap
|
||||||
import finalStep from "./calibrationWizardComponents/finalStep.vue";
|
import finalStep from "./calibrationWizardComponents/finalStep.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "calibrationWizard",
|
name: "CalibrationWizard",
|
||||||
|
|
||||||
components: {},
|
components: {},
|
||||||
|
|
||||||
|
|
@ -36,7 +36,7 @@ export default {
|
||||||
availableCalibrationTasks: {},
|
availableCalibrationTasks: {},
|
||||||
tasks: [],
|
tasks: [],
|
||||||
taskIndex: 0,
|
taskIndex: 0,
|
||||||
movingBackward: false
|
movingBackward: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -49,7 +49,7 @@ export default {
|
||||||
},
|
},
|
||||||
isFinalTask() {
|
isFinalTask() {
|
||||||
return this.taskIndex === this.tasks.length - 1;
|
return this.taskIndex === this.tasks.length - 1;
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
|
|
@ -57,12 +57,10 @@ export default {
|
||||||
// Check which Things are available on mount.
|
// Check which Things are available on mount.
|
||||||
const allCalibrationTasks = {
|
const allCalibrationTasks = {
|
||||||
camera: cameraCalibrationTask,
|
camera: cameraCalibrationTask,
|
||||||
camera_stage_mapping: cameraStageMappingTask
|
camera_stage_mapping: cameraStageMappingTask,
|
||||||
};
|
};
|
||||||
this.availableCalibrationTasks = Object.fromEntries(
|
this.availableCalibrationTasks = Object.fromEntries(
|
||||||
Object.entries(allCalibrationTasks).filter(([thing]) =>
|
Object.entries(allCalibrationTasks).filter(([thing]) => this.thingAvailable(thing)),
|
||||||
this.thingAvailable(thing)
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -81,10 +79,7 @@ export default {
|
||||||
|
|
||||||
for (const name of calibrateableThings) {
|
for (const name of calibrateableThings) {
|
||||||
try {
|
try {
|
||||||
const thingNeedsCal = await this.readThingProperty(
|
const thingNeedsCal = await this.readThingProperty(name, "calibration_required");
|
||||||
name,
|
|
||||||
"calibration_required"
|
|
||||||
);
|
|
||||||
if (thingNeedsCal) {
|
if (thingNeedsCal) {
|
||||||
needsCalibration.push(name);
|
needsCalibration.push(name);
|
||||||
}
|
}
|
||||||
|
|
@ -111,7 +106,7 @@ export default {
|
||||||
if (includeWelcome) {
|
if (includeWelcome) {
|
||||||
tasks.push({
|
tasks.push({
|
||||||
component: singleStepTask,
|
component: singleStepTask,
|
||||||
props: { stepComponent: welcomeStep }
|
props: { stepComponent: welcomeStep },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -124,7 +119,7 @@ export default {
|
||||||
// Always include the final step
|
// Always include the final step
|
||||||
tasks.push({
|
tasks.push({
|
||||||
component: singleStepTask,
|
component: singleStepTask,
|
||||||
props: { stepComponent: finalStep }
|
props: { stepComponent: finalStep },
|
||||||
});
|
});
|
||||||
|
|
||||||
this.tasks = tasks;
|
this.tasks = tasks;
|
||||||
|
|
@ -192,7 +187,7 @@ export default {
|
||||||
} else {
|
} else {
|
||||||
this.hide();
|
this.hide();
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,8 @@ gets very confusing.
|
||||||
<div>
|
<div>
|
||||||
<h3 v-if="title">{{ title }}</h3>
|
<h3 v-if="title">{{ title }}</h3>
|
||||||
<component
|
<component
|
||||||
v-if="currentStep"
|
|
||||||
:is="currentStep.component"
|
:is="currentStep.component"
|
||||||
|
v-if="currentStep"
|
||||||
:key="stepIndex"
|
:key="stepIndex"
|
||||||
v-bind="currentStep.props"
|
v-bind="currentStep.props"
|
||||||
/>
|
/>
|
||||||
|
|
@ -37,11 +37,7 @@ gets very confusing.
|
||||||
>
|
>
|
||||||
Back
|
Back
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button class="uk-button uk-button-primary uk-margin-left" type="button" @click="nextStep">
|
||||||
class="uk-button uk-button-primary uk-margin-left"
|
|
||||||
type="button"
|
|
||||||
@click="nextStep"
|
|
||||||
>
|
|
||||||
{{ nextButtonText }}
|
{{ nextButtonText }}
|
||||||
</button>
|
</button>
|
||||||
</p>
|
</p>
|
||||||
|
|
@ -50,28 +46,28 @@ gets very confusing.
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: "calibrationWizardTask",
|
name: "CalibrationWizardTask",
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
title: {
|
title: {
|
||||||
type: String,
|
type: String,
|
||||||
default: null
|
default: null,
|
||||||
},
|
},
|
||||||
first: Boolean,
|
first: Boolean,
|
||||||
final: Boolean,
|
final: Boolean,
|
||||||
startOnLast: {
|
startOnLast: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false,
|
||||||
},
|
},
|
||||||
steps: {
|
steps: {
|
||||||
type: Array,
|
type: Array,
|
||||||
required: true
|
required: true,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
stepIndex: this.startOnLast ? this.steps.length - 1 : 0
|
stepIndex: this.startOnLast ? this.steps.length - 1 : 0,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -83,10 +79,8 @@ export default {
|
||||||
return !this.first || this.stepIndex > 0;
|
return !this.first || this.stepIndex > 0;
|
||||||
},
|
},
|
||||||
nextButtonText() {
|
nextButtonText() {
|
||||||
return this.final && this.stepIndex === this.steps.length - 1
|
return this.final && this.stepIndex === this.steps.length - 1 ? "Finish" : "Next";
|
||||||
? "Finish"
|
},
|
||||||
: "Next";
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
|
@ -111,7 +105,7 @@ export default {
|
||||||
} else {
|
} else {
|
||||||
this.$emit("next");
|
this.$emit("next");
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,6 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: "camCalibrationExplanation"
|
name: "CamCalibrationExplanation",
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,7 @@
|
||||||
|
|
||||||
<template #below-stream>
|
<template #below-stream>
|
||||||
<div class="action-button-container">
|
<div class="action-button-container">
|
||||||
<cameraCalibrationSettings
|
<cameraCalibrationSettings :show-extra-settings="false" :camera-uri="cameraUri" />
|
||||||
:show-extra-settings="false"
|
|
||||||
:camera-uri="cameraUri"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</stepTemplateWithStream>
|
</stepTemplateWithStream>
|
||||||
|
|
@ -20,18 +17,18 @@ import stepTemplateWithStream from "../stepTemplateWithStream.vue";
|
||||||
import cameraCalibrationSettings from "../../../tabContentComponents/settingsComponents/cameraSettingsComponents/cameraCalibrationSettings.vue";
|
import cameraCalibrationSettings from "../../../tabContentComponents/settingsComponents/cameraSettingsComponents/cameraCalibrationSettings.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "cameraMainCalibrationStep",
|
name: "CameraMainCalibrationStep",
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
stepTemplateWithStream,
|
stepTemplateWithStream,
|
||||||
cameraCalibrationSettings
|
cameraCalibrationSettings,
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
cameraUri: function() {
|
cameraUri: function() {
|
||||||
return `${this.$store.getters.baseUri}/camera/`;
|
return `${this.$store.getters.baseUri}/camera/`;
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
title="Camera Calibration"
|
title="Camera Calibration"
|
||||||
:first="first"
|
:first="first"
|
||||||
:final="final"
|
:final="final"
|
||||||
:startOnLast="startOnLast"
|
:start-on-last="startOnLast"
|
||||||
:steps="steps"
|
:steps="steps"
|
||||||
@next="$emit('next')"
|
@next="$emit('next')"
|
||||||
@back="$emit('back')"
|
@back="$emit('back')"
|
||||||
|
|
@ -16,7 +16,7 @@ import camCalibrationExplanation from "./cameraCalibrationSteps/camCalibrationEx
|
||||||
import cameraMainCalibrationStep from "./cameraCalibrationSteps/cameraMainCalibrationStep.vue";
|
import cameraMainCalibrationStep from "./cameraCalibrationSteps/cameraMainCalibrationStep.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "cameraCalibrationTask",
|
name: "CameraCalibrationTask",
|
||||||
components: { calibrationWizardTask },
|
components: { calibrationWizardTask },
|
||||||
props: {
|
props: {
|
||||||
// Standard calibrationWizardTask props below:
|
// Standard calibrationWizardTask props below:
|
||||||
|
|
@ -24,17 +24,14 @@ export default {
|
||||||
final: Boolean,
|
final: Boolean,
|
||||||
startOnLast: {
|
startOnLast: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
data: function() {
|
data: function() {
|
||||||
return {
|
return {
|
||||||
steps: [
|
steps: [{ component: camCalibrationExplanation }, { component: cameraMainCalibrationStep }],
|
||||||
{ component: camCalibrationExplanation },
|
|
||||||
{ component: cameraMainCalibrationStep }
|
|
||||||
]
|
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
title="Camera-Stage Mapping"
|
title="Camera-Stage Mapping"
|
||||||
:first="first"
|
:first="first"
|
||||||
:final="final"
|
:final="final"
|
||||||
:startOnLast="startOnLast"
|
:start-on-last="startOnLast"
|
||||||
:steps="steps"
|
:steps="steps"
|
||||||
@next="$emit('next')"
|
@next="$emit('next')"
|
||||||
@back="$emit('back')"
|
@back="$emit('back')"
|
||||||
|
|
@ -17,7 +17,7 @@ import focusStep from "./csmSteps/focusStep.vue";
|
||||||
import runCsmStep from "./csmSteps/runCsmStep.vue";
|
import runCsmStep from "./csmSteps/runCsmStep.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "cameraCalibrationTask",
|
name: "CameraCalibrationTask",
|
||||||
components: { calibrationWizardTask },
|
components: { calibrationWizardTask },
|
||||||
props: {
|
props: {
|
||||||
// Standard calibrationWizardTask props below:
|
// Standard calibrationWizardTask props below:
|
||||||
|
|
@ -25,18 +25,14 @@ export default {
|
||||||
final: Boolean,
|
final: Boolean,
|
||||||
startOnLast: {
|
startOnLast: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
data: function() {
|
data: function() {
|
||||||
return {
|
return {
|
||||||
steps: [
|
steps: [{ component: csmExplanation }, { component: focusStep }, { component: runCsmStep }],
|
||||||
{ component: csmExplanation },
|
|
||||||
{ component: focusStep },
|
|
||||||
{ component: runCsmStep }
|
|
||||||
]
|
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,6 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: "csmExplanation"
|
name: "CsmExplanation",
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,7 @@
|
||||||
<p>
|
<p>
|
||||||
Use the buttons below to bring the sample into focus.
|
Use the buttons below to bring the sample into focus.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>You may also adjust the z position with <b>page up</b> and <b>page down</b>.</p>
|
||||||
You may also adjust the z position with <b>page up</b> and
|
|
||||||
<b>page down</b>.
|
|
||||||
</p>
|
|
||||||
<template #below-stream>
|
<template #below-stream>
|
||||||
<div class="action-button-container">
|
<div class="action-button-container">
|
||||||
<action-button
|
<action-button
|
||||||
|
|
@ -16,7 +13,7 @@
|
||||||
:button-primary="false"
|
:button-primary="false"
|
||||||
:submit-data="{ x: 0, y: 0, z: -100 }"
|
:submit-data="{ x: 0, y: 0, z: -100 }"
|
||||||
:submit-label="' - '"
|
:submit-label="' - '"
|
||||||
:hideOnRun="false"
|
:hide-on-run="false"
|
||||||
:can-terminate="false"
|
:can-terminate="false"
|
||||||
/>
|
/>
|
||||||
<action-button
|
<action-button
|
||||||
|
|
@ -27,7 +24,7 @@
|
||||||
:submit-data="{ x: 0, y: 0, z: 100 }"
|
:submit-data="{ x: 0, y: 0, z: 100 }"
|
||||||
:submit-label="'+'"
|
:submit-label="'+'"
|
||||||
:can-terminate="false"
|
:can-terminate="false"
|
||||||
:hideOnRun="false"
|
:hide-on-run="false"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -38,12 +35,12 @@
|
||||||
import stepTemplateWithStream from "../stepTemplateWithStream.vue";
|
import stepTemplateWithStream from "../stepTemplateWithStream.vue";
|
||||||
import ActionButton from "../../../labThingsComponents/actionButton.vue";
|
import ActionButton from "../../../labThingsComponents/actionButton.vue";
|
||||||
export default {
|
export default {
|
||||||
name: "cameraMainCalibrationStep",
|
name: "CameraMainCalibrationStep",
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
stepTemplateWithStream,
|
stepTemplateWithStream,
|
||||||
ActionButton
|
ActionButton,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
|
||||||
|
|
@ -17,12 +17,12 @@ import stepTemplateWithStream from "../stepTemplateWithStream.vue";
|
||||||
import CSMCalibrationSettings from "../../../tabContentComponents/settingsComponents/CSMSettingsComponents/CSMCalibrationSettings.vue";
|
import CSMCalibrationSettings from "../../../tabContentComponents/settingsComponents/CSMSettingsComponents/CSMCalibrationSettings.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "cameraMainCalibrationStep",
|
name: "CameraMainCalibrationStep",
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
stepTemplateWithStream,
|
stepTemplateWithStream,
|
||||||
CSMCalibrationSettings
|
CSMCalibrationSettings,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,6 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: "finalStep"
|
name: "FinalStep",
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ supplied by the wizard.
|
||||||
:title="title"
|
:title="title"
|
||||||
:first="first"
|
:first="first"
|
||||||
:final="final"
|
:final="final"
|
||||||
:startOnLast="startOnLast"
|
:start-on-last="startOnLast"
|
||||||
:steps="steps"
|
:steps="steps"
|
||||||
@next="$emit('next')"
|
@next="$emit('next')"
|
||||||
@back="$emit('back')"
|
@back="$emit('back')"
|
||||||
|
|
@ -26,36 +26,36 @@ supplied by the wizard.
|
||||||
import calibrationWizardTask from "./calibrationWizardTask.vue";
|
import calibrationWizardTask from "./calibrationWizardTask.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "singleStepTask",
|
name: "SingleStepTask",
|
||||||
components: { calibrationWizardTask },
|
components: { calibrationWizardTask },
|
||||||
props: {
|
props: {
|
||||||
// This must be sent
|
// This must be sent
|
||||||
stepComponent: {
|
stepComponent: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true,
|
||||||
},
|
},
|
||||||
// This is optional.
|
// This is optional.
|
||||||
stepProps: {
|
stepProps: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => ({})
|
default: () => ({}),
|
||||||
},
|
},
|
||||||
// Standard calibrationWizardTask props below:
|
// Standard calibrationWizardTask props below:
|
||||||
title: {
|
title: {
|
||||||
type: String,
|
type: String,
|
||||||
default: null
|
default: null,
|
||||||
},
|
},
|
||||||
first: Boolean,
|
first: Boolean,
|
||||||
final: Boolean,
|
final: Boolean,
|
||||||
startOnLast: {
|
startOnLast: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
data: function() {
|
data: function() {
|
||||||
return {
|
return {
|
||||||
steps: [{ component: this.stepComponent, props: this.stepProps }]
|
steps: [{ component: this.stepComponent, props: this.stepProps }],
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,11 @@
|
||||||
import miniStreamDisplay from "../../genericComponents/miniStreamDisplay.vue";
|
import miniStreamDisplay from "../../genericComponents/miniStreamDisplay.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "stepTemplateWithStream",
|
name: "StepTemplateWithStream",
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
miniStreamDisplay
|
miniStreamDisplay,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@
|
||||||
</b>
|
</b>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Your microscope will still function, however some functionality will be
|
Your microscope will still function, however some functionality will be limited, and image
|
||||||
limited, and image quality will likely suffer.
|
quality will likely suffer.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<b>Click Next to begin microscope calibration.</b>
|
<b>Click Next to begin microscope calibration.</b>
|
||||||
|
|
@ -17,6 +17,6 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: "welcomeStep"
|
name: "WelcomeStep",
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<form
|
<form class="uk-form-stacked" action="" method="GET" @submit="overrideAPIHost">
|
||||||
class="uk-form-stacked"
|
|
||||||
action=""
|
|
||||||
method="GET"
|
|
||||||
@submit="overrideAPIHost"
|
|
||||||
>
|
|
||||||
<label class="uk-form-label">Override API origin</label>
|
<label class="uk-form-label">Override API origin</label>
|
||||||
<input
|
<input v-model="newOrigin" name="overrideOrigin" class="uk-input" type="text" />
|
||||||
v-model="newOrigin"
|
|
||||||
name="overrideOrigin"
|
|
||||||
class="uk-input"
|
|
||||||
type="text"
|
|
||||||
/>
|
|
||||||
<label class="uk-form-label">
|
<label class="uk-form-label">
|
||||||
<input
|
<input v-model="reloadWhenOverridingOrigin" class="uk-input uk-checkbox" type="checkbox" />
|
||||||
v-model="reloadWhenOverridingOrigin"
|
|
||||||
class="uk-input uk-checkbox"
|
|
||||||
type="checkbox"
|
|
||||||
/>
|
|
||||||
Reload web app with new origin
|
Reload web app with new origin
|
||||||
</label>
|
</label>
|
||||||
<button class="uk-button uk-button-default uk-margin-small">
|
<button class="uk-button uk-button-default uk-margin-small">
|
||||||
|
|
@ -38,7 +24,7 @@ export default {
|
||||||
data: function() {
|
data: function() {
|
||||||
return {
|
return {
|
||||||
newOrigin: this.$store.state.origin,
|
newOrigin: this.$store.state.origin,
|
||||||
reloadWhenOverridingOrigin: true
|
reloadWhenOverridingOrigin: true,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -62,8 +48,8 @@ export default {
|
||||||
this.$store.commit("changeOrigin", this.newOrigin);
|
this.$store.commit("changeOrigin", this.newOrigin);
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -53,9 +53,7 @@
|
||||||
<div v-else-if="$store.state.waiting">
|
<div v-else-if="$store.state.waiting">
|
||||||
Loading...
|
Loading...
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="$store.state.error">
|
<div v-else-if="$store.state.error"><b>Error:</b> {{ $store.state.error }}</div>
|
||||||
<b>Error:</b> {{ $store.state.error }}
|
|
||||||
</div>
|
|
||||||
<div v-else>No active connection</div>
|
<div v-else>No active connection</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -70,37 +68,33 @@ export default {
|
||||||
data: function() {
|
data: function() {
|
||||||
return {
|
return {
|
||||||
version: undefined,
|
version: undefined,
|
||||||
version_source: undefined
|
version_source: undefined,
|
||||||
}
|
};
|
||||||
},
|
|
||||||
|
|
||||||
async mounted() {
|
|
||||||
let version_data = await this.readThingProperty(
|
|
||||||
"system",
|
|
||||||
"version_data"
|
|
||||||
);
|
|
||||||
this.version = version_data.version;
|
|
||||||
this.version_source = this.truncate(version_data.version_source);
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
things: function() {
|
things: function() {
|
||||||
return this.$store.getters["wot/thingDescriptions"];
|
return this.$store.getters["wot/thingDescriptions"];
|
||||||
}
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
async mounted() {
|
||||||
|
let version_data = await this.readThingProperty("system", "version_data");
|
||||||
|
this.version = version_data.version;
|
||||||
|
this.version_source = this.truncate(version_data.version_source);
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
truncate(string, max_length=15) {
|
truncate(string, max_length = 15) {
|
||||||
if (!(typeof string === 'string' || string instanceof String)) {
|
if (!(typeof string === "string" || string instanceof String)) {
|
||||||
return string;
|
return string;
|
||||||
}
|
}
|
||||||
if (string.length <= max_length) {
|
if (string.length <= max_length) {
|
||||||
return string;
|
return string;
|
||||||
}
|
}
|
||||||
return string.slice(0, max_length - 3) + "...";
|
return string.slice(0, max_length - 3) + "...";
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ export default {
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
devTools,
|
devTools,
|
||||||
statusPane
|
statusPane,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="uk-padding-small" v-observe-visibility="visibilityChanged">
|
<div v-observe-visibility="visibilityChanged" class="uk-padding-small">
|
||||||
<div>
|
<div>
|
||||||
<ul uk-accordion="multiple: true">
|
<ul uk-accordion="multiple: true">
|
||||||
<li>
|
<li>
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
thing="camera"
|
thing="camera"
|
||||||
action="image_is_sample"
|
action="image_is_sample"
|
||||||
submit-label="Check Current Image"
|
submit-label="Check Current Image"
|
||||||
:isDisabled="!ready"
|
:is-disabled="!ready"
|
||||||
:can-terminate="false"
|
:can-terminate="false"
|
||||||
:poll-interval="0.1"
|
:poll-interval="0.1"
|
||||||
@response="alertImageLabel"
|
@response="alertImageLabel"
|
||||||
|
|
@ -53,13 +53,13 @@ import InputFromSchema from "../../labThingsComponents/inputFromSchema.vue";
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
ActionButton,
|
ActionButton,
|
||||||
InputFromSchema
|
InputFromSchema,
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
backgroundDetectorStatus: undefined,
|
backgroundDetectorStatus: undefined,
|
||||||
animate: false
|
animate: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -67,7 +67,13 @@ export default {
|
||||||
ready() {
|
ready() {
|
||||||
const status = this.backgroundDetectorStatus;
|
const status = this.backgroundDetectorStatus;
|
||||||
return status && status.ready === true;
|
return status && status.ready === true;
|
||||||
}
|
},
|
||||||
|
},
|
||||||
|
async created() {
|
||||||
|
this.backgroundDetectorStatus = await this.readThingProperty(
|
||||||
|
"camera",
|
||||||
|
"background_detector_status",
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
|
@ -86,27 +92,17 @@ export default {
|
||||||
readSettings: async function() {
|
readSettings: async function() {
|
||||||
this.backgroundDetectorStatus = await this.readThingProperty(
|
this.backgroundDetectorStatus = await this.readThingProperty(
|
||||||
"camera",
|
"camera",
|
||||||
"background_detector_status"
|
"background_detector_status",
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
writeSettings: async function(requestedValue) {
|
writeSettings: async function(requestedValue) {
|
||||||
await this.invokeAction(
|
await this.invokeAction("camera", "update_detector_settings", { data: requestedValue });
|
||||||
"camera",
|
|
||||||
"update_detector_settings",
|
|
||||||
{"data": requestedValue}
|
|
||||||
);
|
|
||||||
this.animate = true;
|
this.animate = true;
|
||||||
this.readSettings();
|
this.readSettings();
|
||||||
},
|
},
|
||||||
resetAnimate: function() {
|
resetAnimate: function() {
|
||||||
this.animate = false;
|
this.animate = false;
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
async created() {
|
|
||||||
this.backgroundDetectorStatus = await this.readThingProperty(
|
|
||||||
"camera",
|
|
||||||
"background_detector_status"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ export default {
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
paneBackgroundDetect,
|
paneBackgroundDetect,
|
||||||
streamDisplay
|
streamDisplay,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -18,12 +18,7 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<label class="uk-margin-small-right"
|
<label class="uk-margin-small-right"
|
||||||
><input
|
><input v-model="invert.x" class="uk-checkbox" type="checkbox" /> Invert x</label
|
||||||
v-model="invert.x"
|
|
||||||
class="uk-checkbox"
|
|
||||||
type="checkbox"
|
|
||||||
/>
|
|
||||||
Invert x</label
|
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -38,12 +33,7 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<label class="uk-margin-small-right"
|
<label class="uk-margin-small-right"
|
||||||
><input
|
><input v-model="invert.y" class="uk-checkbox" type="checkbox" /> Invert y</label
|
||||||
v-model="invert.y"
|
|
||||||
class="uk-checkbox"
|
|
||||||
type="checkbox"
|
|
||||||
/>
|
|
||||||
Invert y</label
|
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -59,7 +49,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<br />
|
||||||
<action-button
|
<action-button
|
||||||
thing="stage"
|
thing="stage"
|
||||||
action="set_zero_position"
|
action="set_zero_position"
|
||||||
|
|
@ -165,8 +155,7 @@
|
||||||
<div v-else class="uk-text-warning">
|
<div v-else class="uk-text-warning">
|
||||||
<p>Stage positioning is disabled since no stage is connected.</p>
|
<p>Stage positioning is disabled since no stage is connected.</p>
|
||||||
<p>
|
<p>
|
||||||
Please check all data and power connections to your motor controller
|
Please check all data and power connections to your motor controller board.
|
||||||
board.
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -183,7 +172,7 @@ export default {
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
ActionButton,
|
ActionButton,
|
||||||
syncPropertyButton
|
syncPropertyButton,
|
||||||
},
|
},
|
||||||
|
|
||||||
data: function() {
|
data: function() {
|
||||||
|
|
@ -193,16 +182,16 @@ export default {
|
||||||
stepSize: {
|
stepSize: {
|
||||||
x: 200,
|
x: 200,
|
||||||
y: 200,
|
y: 200,
|
||||||
z: 50
|
z: 50,
|
||||||
},
|
},
|
||||||
invert: {
|
invert: {
|
||||||
x: false,
|
x: false,
|
||||||
y: false,
|
y: false,
|
||||||
z: false
|
z: false,
|
||||||
},
|
},
|
||||||
setPosition: null,
|
setPosition: null,
|
||||||
isAutofocusing: 0,
|
isAutofocusing: 0,
|
||||||
moveLock: false
|
moveLock: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -214,11 +203,8 @@ export default {
|
||||||
return `${this.baseUri}/stage/position`;
|
return `${this.baseUri}/stage/position`;
|
||||||
},
|
},
|
||||||
moveInImageCoordinatesUri: function() {
|
moveInImageCoordinatesUri: function() {
|
||||||
return this.thingActionUrl(
|
return this.thingActionUrl("camera_stage_mapping", "move_in_image_coordinates");
|
||||||
"camera_stage_mapping",
|
},
|
||||||
"move_in_image_coordinates"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
|
|
@ -226,20 +212,19 @@ export default {
|
||||||
deep: true,
|
deep: true,
|
||||||
handler() {
|
handler() {
|
||||||
this.setLocalStorageObj("navigation_stepSize", this.stepSize);
|
this.setLocalStorageObj("navigation_stepSize", this.stepSize);
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
invert: {
|
invert: {
|
||||||
deep: true,
|
deep: true,
|
||||||
handler() {
|
handler() {
|
||||||
this.setLocalStorageObj("navigation_invert", this.invert);
|
this.setLocalStorageObj("navigation_invert", this.invert);
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
async mounted() {
|
async mounted() {
|
||||||
// Reload saved settings
|
// Reload saved settings
|
||||||
this.stepSize =
|
this.stepSize = this.getLocalStorageObj("navigation_stepSize") || this.stepSize;
|
||||||
this.getLocalStorageObj("navigation_stepSize") || this.stepSize;
|
|
||||||
this.invert = this.getLocalStorageObj("navigation_invert") || this.invert;
|
this.invert = this.getLocalStorageObj("navigation_invert") || this.invert;
|
||||||
let self = this;
|
let self = this;
|
||||||
// A global signal listener to perform a move action
|
// A global signal listener to perform a move action
|
||||||
|
|
@ -255,7 +240,7 @@ export default {
|
||||||
x_steps * this.stepSize.x * (this.invert.x ? -1 : 1),
|
x_steps * this.stepSize.x * (this.invert.x ? -1 : 1),
|
||||||
y_steps * this.stepSize.y * (this.invert.y ? -1 : 1),
|
y_steps * this.stepSize.y * (this.invert.y ? -1 : 1),
|
||||||
z_steps * this.stepSize.z * (this.invert.z ? -1 : 1),
|
z_steps * this.stepSize.z * (this.invert.z ? -1 : 1),
|
||||||
false
|
false,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
// Update the current position in text boxes
|
// Update the current position in text boxes
|
||||||
|
|
@ -287,7 +272,7 @@ export default {
|
||||||
this.setPosition = {
|
this.setPosition = {
|
||||||
x: this.setPosition.x + x,
|
x: this.setPosition.x + x,
|
||||||
y: this.setPosition.y + y,
|
y: this.setPosition.y + y,
|
||||||
z: this.setPosition.z + z
|
z: this.setPosition.z + z,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
await this.timeout(1); // Wait for Vue to update the position
|
await this.timeout(1); // Wait for Vue to update the position
|
||||||
|
|
@ -304,7 +289,7 @@ export default {
|
||||||
|
|
||||||
if (!this.moveInImageCoordinatesUri) {
|
if (!this.moveInImageCoordinatesUri) {
|
||||||
this.modalError(
|
this.modalError(
|
||||||
"Moving in image coordinates is not supported - you will need to upgrade your microscope's software."
|
"Moving in image coordinates is not supported - you will need to upgrade your microscope's software.",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -312,7 +297,7 @@ export default {
|
||||||
axios
|
axios
|
||||||
.post(this.moveInImageCoordinatesUri, {
|
.post(this.moveInImageCoordinatesUri, {
|
||||||
x: x, // NB the coordinates are numpy/PIL style, meaning X and Y are swapped.
|
x: x, // NB the coordinates are numpy/PIL style, meaning X and Y are swapped.
|
||||||
y: y
|
y: y,
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.updatePosition(); // Update the position in text boxes
|
this.updatePosition(); // Update the position in text boxes
|
||||||
|
|
@ -346,8 +331,8 @@ export default {
|
||||||
link.setAttribute("download", `OFM_${new Date().toISOString()}.jpeg`);
|
link.setAttribute("download", `OFM_${new Date().toISOString()}.jpeg`);
|
||||||
document.body.appendChild(link);
|
document.body.appendChild(link);
|
||||||
link.click();
|
link.click();
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ export default {
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
paneControl,
|
paneControl,
|
||||||
streamDisplay
|
streamDisplay,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div v-observe-visibility="visibilityChanged" class="uk-padding uk-padding-remove-top">
|
||||||
v-observe-visibility="visibilityChanged"
|
|
||||||
class="uk-padding uk-padding-remove-top"
|
|
||||||
>
|
|
||||||
<!-- Logging nav bar -->
|
<!-- Logging nav bar -->
|
||||||
<nav
|
<nav class="logging-navbar uk-navbar-container uk-navbar-transparent" uk-navbar="mode: click">
|
||||||
class="logging-navbar uk-navbar-container uk-navbar-transparent"
|
|
||||||
uk-navbar="mode: click"
|
|
||||||
>
|
|
||||||
<!-- Left side controls -->
|
<!-- Left side controls -->
|
||||||
<div
|
<div class="uk-navbar-left uk-padding-remove-top uk-padding-remove-bottom">
|
||||||
class="uk-navbar-left uk-padding-remove-top uk-padding-remove-bottom"
|
|
||||||
>
|
|
||||||
<select v-model="filterLevel" class="uk-select">
|
<select v-model="filterLevel" class="uk-select">
|
||||||
<option v-for="level in allLevels" :key="level">{{ level }}</option>
|
<option v-for="level in allLevels" :key="level">{{ level }}</option>
|
||||||
</select>
|
</select>
|
||||||
|
|
@ -30,11 +22,11 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<EndpointButton
|
<EndpointButton
|
||||||
class="uk-button uk-width-1-1"
|
class="uk-button uk-width-1-1"
|
||||||
:URL="logFileURI"
|
:url="logFileURI"
|
||||||
buttonLabel="Download Log File"
|
button-label="Download Log File"
|
||||||
:buttonPrimary="false"
|
:button-primary="false"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -50,7 +42,7 @@
|
||||||
class="logging-entry"
|
class="logging-entry"
|
||||||
:class="{
|
:class="{
|
||||||
'uk-alert-warning uk-alert': item.level == 'WARNING',
|
'uk-alert-warning uk-alert': item.level == 'WARNING',
|
||||||
'uk-alert-danger uk-alert': item.level == 'ERROR'
|
'uk-alert-danger uk-alert': item.level == 'ERROR',
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<b>{{ formatDateTime(item.timestamp) }}: {{ item.level }}</b>
|
<b>{{ formatDateTime(item.timestamp) }}: {{ item.level }}</b>
|
||||||
|
|
@ -64,11 +56,7 @@
|
||||||
More info...
|
More info...
|
||||||
</a>
|
</a>
|
||||||
<!-- eslint-disable vue/no-v-html -->
|
<!-- eslint-disable vue/no-v-html -->
|
||||||
<div
|
<div v-if="item.expanded" class="logging-message" v-html="item.message"></div>
|
||||||
v-if="item.expanded"
|
|
||||||
class="logging-message"
|
|
||||||
v-html="item.message"
|
|
||||||
></div>
|
|
||||||
<!-- eslint-enable -->
|
<!-- eslint-enable -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -101,7 +89,7 @@ export default {
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
Paginate,
|
Paginate,
|
||||||
EndpointButton
|
EndpointButton,
|
||||||
},
|
},
|
||||||
|
|
||||||
data: function() {
|
data: function() {
|
||||||
|
|
@ -110,7 +98,7 @@ export default {
|
||||||
page: 1,
|
page: 1,
|
||||||
logs: [],
|
logs: [],
|
||||||
allLevels: ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
|
allLevels: ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
|
||||||
filterLevel: "WARNING"
|
filterLevel: "WARNING",
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -142,7 +130,7 @@ export default {
|
||||||
},
|
},
|
||||||
numberOfPages: function() {
|
numberOfPages: function() {
|
||||||
return Math.floor(this.filteredItems.length / this.maxitems);
|
return Math.floor(this.filteredItems.length / this.maxitems);
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
|
@ -172,7 +160,7 @@ export default {
|
||||||
summary: m[3],
|
summary: m[3],
|
||||||
message: this.escapeText(m[3]),
|
message: this.escapeText(m[3]),
|
||||||
sequence: logs.length,
|
sequence: logs.length,
|
||||||
expanded: false
|
expanded: false,
|
||||||
});
|
});
|
||||||
} else if (logs) {
|
} else if (logs) {
|
||||||
// If a line does not look like a log entry, append it to the last
|
// If a line does not look like a log entry, append it to the last
|
||||||
|
|
@ -198,9 +186,7 @@ export default {
|
||||||
} else {
|
} else {
|
||||||
// if there's no existing log message to append to, discard lines
|
// if there's no existing log message to append to, discard lines
|
||||||
// until we find one.
|
// until we find one.
|
||||||
console.log(
|
console.log("Ignored non-matching lines at the start of the log file.");
|
||||||
"Ignored non-matching lines at the start of the log file."
|
|
||||||
);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -216,8 +202,8 @@ export default {
|
||||||
let div = document.createElement("div");
|
let div = document.createElement("div");
|
||||||
div.innerText = unsafeText;
|
div.innerText = unsafeText;
|
||||||
return div.innerHTML;
|
return div.innerHTML;
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
<template>
|
<template>
|
||||||
<!-- Grid managing tab content -->
|
<!-- Grid managing tab content -->
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h1 id="power-title"> Power</h1>
|
<h1 id="power-title">Power</h1>
|
||||||
<p id="power-msg"> It's essential to turn off your OpenFlexure Microscope here before unplugging it.
|
<p id="power-msg">
|
||||||
<br>
|
It's essential to turn off your OpenFlexure Microscope here before unplugging it.
|
||||||
<br>
|
<br />
|
||||||
Unplugging the microscope unexpectedly can damage the SD card or onboard computer.
|
<br />
|
||||||
|
Unplugging the microscope unexpectedly can damage the SD card or onboard computer.
|
||||||
</p>
|
</p>
|
||||||
<div class="buttons-container">
|
<div class="buttons-container">
|
||||||
<button
|
<button
|
||||||
|
|
@ -37,44 +38,38 @@ export default {
|
||||||
data: function() {
|
data: function() {
|
||||||
return {
|
return {
|
||||||
isRaspberrypi: undefined,
|
isRaspberrypi: undefined,
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
things: function() {
|
things: function() {
|
||||||
return this.$store.getters["wot/thingDescriptions"];
|
return this.$store.getters["wot/thingDescriptions"];
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
async mounted() {
|
async mounted() {
|
||||||
this.isRaspberrypi = await this.readThingProperty(
|
this.isRaspberrypi = await this.readThingProperty("system", "is_raspberrypi");
|
||||||
"system",
|
|
||||||
"is_raspberrypi"
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
systemRequest: function(action) {
|
systemRequest: function(action) {
|
||||||
let message = "";
|
let message = "";
|
||||||
if (action == "reboot") {
|
if (action == "reboot") {
|
||||||
message = "Restart microscope?"
|
message = "Restart microscope?";
|
||||||
}
|
} else {
|
||||||
else {
|
message = "Shutdown microscope?";
|
||||||
message = "Shutdown microscope?"
|
|
||||||
}
|
}
|
||||||
this.modalConfirm(message).then(
|
this.modalConfirm(message).then(
|
||||||
() => {
|
() => {
|
||||||
this.$store.commit("resetState");
|
this.$store.commit("resetState");
|
||||||
this.$store.commit("wot/deleteAllThingDescriptions");
|
this.$store.commit("wot/deleteAllThingDescriptions");
|
||||||
// Post and silence errors
|
// Post and silence errors
|
||||||
axios
|
axios.post(this.thingActionUrl("system", action)).catch(() => {});
|
||||||
.post(this.thingActionUrl("system", action))
|
|
||||||
.catch(() => {});
|
|
||||||
},
|
},
|
||||||
() => {}
|
() => {},
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -82,7 +77,6 @@ export default {
|
||||||
// Custom UIkit CSS modifications
|
// Custom UIkit CSS modifications
|
||||||
@import "../../assets/less/theme.less";
|
@import "../../assets/less/theme.less";
|
||||||
|
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
@ -109,7 +103,7 @@ export default {
|
||||||
|
|
||||||
.shutdown-button {
|
.shutdown-button {
|
||||||
display: inline;
|
display: inline;
|
||||||
text-align:center;
|
text-align: center;
|
||||||
margin: 30px 30px 30px 30px;
|
margin: 30px 30px 30px 30px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div v-observe-visibility="visibilityChanged">
|
<div v-observe-visibility="visibilityChanged">
|
||||||
<div id="openseadragon"></div>
|
<div id="openseadragon"></div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
@ -13,26 +13,25 @@ export default {
|
||||||
props: {
|
props: {
|
||||||
src: {
|
src: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true
|
required: true,
|
||||||
|
|
||||||
},
|
},
|
||||||
brightness: {
|
brightness: {
|
||||||
type: Number,
|
type: Number,
|
||||||
required: true
|
required: true,
|
||||||
},
|
},
|
||||||
contrast: {
|
contrast: {
|
||||||
type: Number,
|
type: Number,
|
||||||
required: true
|
required: true,
|
||||||
},
|
},
|
||||||
saturation: {
|
saturation: {
|
||||||
type: Number,
|
type: Number,
|
||||||
required: true
|
required: true,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
data: function() {
|
data: function() {
|
||||||
return {
|
return {
|
||||||
osdViewer: null
|
osdViewer: null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -41,7 +40,7 @@ export default {
|
||||||
immediate: true,
|
immediate: true,
|
||||||
handler(newVal) {
|
handler(newVal) {
|
||||||
this.loadOpenSeaDragon(newVal);
|
this.loadOpenSeaDragon(newVal);
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
brightness() {
|
brightness() {
|
||||||
this.updateFilter();
|
this.updateFilter();
|
||||||
|
|
@ -51,7 +50,7 @@ export default {
|
||||||
},
|
},
|
||||||
saturation() {
|
saturation() {
|
||||||
this.updateFilter();
|
this.updateFilter();
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
async mounted() {
|
async mounted() {
|
||||||
|
|
@ -69,7 +68,7 @@ export default {
|
||||||
visibilityChanged(isVisible) {
|
visibilityChanged(isVisible) {
|
||||||
if (isVisible) {
|
if (isVisible) {
|
||||||
this.loadOpenSeaDragon();
|
this.loadOpenSeaDragon();
|
||||||
}else{
|
} else {
|
||||||
this.osdViewer.destroy();
|
this.osdViewer.destroy();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -79,13 +78,13 @@ export default {
|
||||||
}
|
}
|
||||||
this.osdViewer = OpenSeaDragon({
|
this.osdViewer = OpenSeaDragon({
|
||||||
id: "openseadragon",
|
id: "openseadragon",
|
||||||
crossOriginPolicy: 'Anonymous',
|
crossOriginPolicy: "Anonymous",
|
||||||
tileSources: this.src,
|
tileSources: this.src,
|
||||||
showNavigationControl: false,
|
showNavigationControl: false,
|
||||||
maxZoomPixelRatio: 2,
|
maxZoomPixelRatio: 2,
|
||||||
gestureSettingsMouse: {
|
gestureSettingsMouse: {
|
||||||
clickToZoom: false
|
clickToZoom: false,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
this.updateFilter();
|
this.updateFilter();
|
||||||
|
|
@ -106,7 +105,7 @@ export default {
|
||||||
this.osdViewer.setFullScreen(true);
|
this.osdViewer.setFullScreen(true);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -117,4 +116,4 @@ export default {
|
||||||
background-color: black;
|
background-color: black;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,7 @@
|
||||||
<div class="uk-card">
|
<div class="uk-card">
|
||||||
<div class="uk-card-body">
|
<div class="uk-card-body">
|
||||||
<div class="uk-card-media-top">
|
<div class="uk-card-media-top">
|
||||||
<div
|
<div class="view-image uk-padding-remove uk-height-1-1">
|
||||||
class="view-image uk-padding-remove uk-height-1-1"
|
|
||||||
>
|
|
||||||
<img
|
<img
|
||||||
id="thumbnail-stitched-image"
|
id="thumbnail-stitched-image"
|
||||||
class="thumbnail-fit"
|
class="thumbnail-fit"
|
||||||
|
|
@ -15,39 +13,36 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<h3 class="uk-card-title scan-card-title">{{ scanData.name }}</h3>
|
<h3 class="uk-card-title scan-card-title">{{ scanData.name }}</h3>
|
||||||
<h4 class="ongoing-msg" v-if="ongoing">Scan in progress</h4>
|
<h4 v-if="ongoing" class="ongoing-msg">Scan in progress</h4>
|
||||||
<div class="button-container" v-if="!ongoing">
|
<div v-if="!ongoing" class="button-container">
|
||||||
<div class="uk-button-group scan-card-buttons">
|
<div class="uk-button-group scan-card-buttons">
|
||||||
<action-button
|
<action-button
|
||||||
class="uk-width-1-2"
|
class="uk-width-1-2"
|
||||||
thing="smart_scan"
|
thing="smart_scan"
|
||||||
action="download_zip"
|
action="download_zip"
|
||||||
submit-label="Download All"
|
submit-label="Download All"
|
||||||
:can-terminate="false"
|
:can-terminate="false"
|
||||||
:submit-data="{ scan_name: scanData.name }"
|
:submit-data="{ scan_name: scanData.name }"
|
||||||
:button-primary="true"
|
:button-primary="true"
|
||||||
@response="downloadZipFile"
|
@response="downloadZipFile"
|
||||||
@error="modalError"
|
@error="modalError"
|
||||||
/>
|
/>
|
||||||
<EndpointButton
|
<EndpointButton
|
||||||
class="uk-width-1-2"
|
class="uk-width-1-2"
|
||||||
:buttonPrimary=true
|
:button-primary="true"
|
||||||
:isDisabled=!scanData.stitch_available
|
:is-disabled="!scanData.stitch_available"
|
||||||
:URL="downloadStitchFile"
|
:url="downloadStitchFile"
|
||||||
buttonLabel="Download JPEG"
|
button-label="Download JPEG"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button class="uk-button uk-button-default uk-width-1-1" @click="deleteScan">
|
||||||
class="uk-button uk-button-default uk-width-1-1"
|
|
||||||
@click="deleteScan"
|
|
||||||
>
|
|
||||||
Delete
|
Delete
|
||||||
</button>
|
</button>
|
||||||
<action-button
|
<action-button
|
||||||
|
v-if="scanData.can_stitch | (scanData.stitch_available & !scanData.dzi)"
|
||||||
submit-label="Stitch Images"
|
submit-label="Stitch Images"
|
||||||
thing="smart_scan"
|
thing="smart_scan"
|
||||||
action="stitch_scan"
|
action="stitch_scan"
|
||||||
v-if="scanData.can_stitch | (scanData.stitch_available & !scanData.dzi)"
|
|
||||||
:can-terminate="true"
|
:can-terminate="true"
|
||||||
:submit-data="{ scan_name: scanData.name }"
|
:submit-data="{ scan_name: scanData.name }"
|
||||||
:button-primary="false"
|
:button-primary="false"
|
||||||
|
|
@ -55,10 +50,11 @@
|
||||||
@error="modalError"
|
@error="modalError"
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
v-if="scanData.dzi" class="uk-button uk-button-default uk-width-1-1"
|
v-if="scanData.dzi"
|
||||||
|
class="uk-button uk-button-default uk-width-1-1"
|
||||||
@click="requestViewer"
|
@click="requestViewer"
|
||||||
>
|
>
|
||||||
Show Stitched Scan
|
Show Stitched Scan
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="scan-info">
|
<div class="scan-info">
|
||||||
|
|
@ -68,9 +64,15 @@
|
||||||
<li>duration: {{ formatDuration(scanData.duration) }}</li>
|
<li>duration: {{ formatDuration(scanData.duration) }}</li>
|
||||||
</ul>
|
</ul>
|
||||||
<ul v-if="!ongoing">
|
<ul v-if="!ongoing">
|
||||||
<li v-if="scanData.number_of_images<3" class="warning-msg">Not enough images to stitch</li>
|
<li v-if="scanData.number_of_images < 3" class="warning-msg">
|
||||||
<li v-else-if="!scanData.dzi && scanData.stitch_available" class="alert-msg">Interactive preview not available</li>
|
Not enough images to stitch
|
||||||
<li v-else-if="!scanData.stitch_available" class="alert-msg">High quality stitch not available</li>
|
</li>
|
||||||
|
<li v-else-if="!scanData.dzi && scanData.stitch_available" class="alert-msg">
|
||||||
|
Interactive preview not available
|
||||||
|
</li>
|
||||||
|
<li v-else-if="!scanData.stitch_available" class="alert-msg">
|
||||||
|
High quality stitch not available
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -90,16 +92,16 @@ export default {
|
||||||
props: {
|
props: {
|
||||||
scanData: {
|
scanData: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true,
|
||||||
},
|
},
|
||||||
scansUri: {
|
scansUri: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true
|
required: true,
|
||||||
},
|
},
|
||||||
ongoing: {
|
ongoing: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: true
|
required: true,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
|
|
@ -114,7 +116,7 @@ export default {
|
||||||
methods: {
|
methods: {
|
||||||
formatDate(timestamp) {
|
formatDate(timestamp) {
|
||||||
// Multiply by 1000 as JS uses ms not s
|
// Multiply by 1000 as JS uses ms not s
|
||||||
let d = new Date(timestamp*1000);
|
let d = new Date(timestamp * 1000);
|
||||||
// Convert to a string in a very javascript way!
|
// Convert to a string in a very javascript way!
|
||||||
let yyyy = d.getFullYear();
|
let yyyy = d.getFullYear();
|
||||||
let mm = d.getMonth() + 1;
|
let mm = d.getMonth() + 1;
|
||||||
|
|
@ -146,14 +148,14 @@ export default {
|
||||||
requestViewer() {
|
requestViewer() {
|
||||||
// Notify parent that thumbnail was clicked
|
// Notify parent that thumbnail was clicked
|
||||||
if (!this.ongoing) {
|
if (!this.ongoing) {
|
||||||
this.$emit('viewer-requested', this.scanData);
|
this.$emit("viewer-requested", this.scanData);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async deleteScan() {
|
async deleteScan() {
|
||||||
try {
|
try {
|
||||||
await this.modalConfirm(`Are you sure you want to delete ${this.scanData.name}?`);
|
await this.modalConfirm(`Are you sure you want to delete ${this.scanData.name}?`);
|
||||||
await axios.delete(`${this.scansUri}/${this.scanData.name}`);
|
await axios.delete(`${this.scansUri}/${this.scanData.name}`);
|
||||||
this.$emit('update-requested');
|
this.$emit("update-requested");
|
||||||
this.modalNotify(`Deleted ${this.scanData.name}`);
|
this.modalNotify(`Deleted ${this.scanData.name}`);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// if the confirmation was cancelled, it's rejected with null error
|
// if the confirmation was cancelled, it's rejected with null error
|
||||||
|
|
@ -170,15 +172,15 @@ export default {
|
||||||
console.log(link);
|
console.log(link);
|
||||||
document.body.appendChild(link);
|
document.body.appendChild(link);
|
||||||
link.click();
|
link.click();
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
ul {
|
ul {
|
||||||
display: block;
|
display: block;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
list-style-type:none;
|
list-style-type: none;
|
||||||
margin: 5px 0px 10px 0px;
|
margin: 5px 0px 10px 0px;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
@ -207,5 +209,4 @@ ul {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 2rem 0;
|
padding: 2rem 0;
|
||||||
}
|
}
|
||||||
|
</style>
|
||||||
</style>
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div id="scan-modal" ref="scanModal" uk-modal>
|
<div id="scan-modal" ref="scanModal" uk-modal>
|
||||||
<div
|
<div v-if="selectedScan" id="scan-modal-body" class="uk-modal-dialog uk-modal-body">
|
||||||
id="scan-modal-body"
|
|
||||||
class="uk-modal-dialog uk-modal-body"
|
|
||||||
v-if="selectedScan"
|
|
||||||
>
|
|
||||||
<h2 id="scan-modal-title" class="uk-modal-title">
|
<h2 id="scan-modal-title" class="uk-modal-title">
|
||||||
{{ selectedScan.name }}
|
{{ selectedScan.name }}
|
||||||
<button class="uk-modal-close uk-float-right" type="button">
|
<button class="uk-modal-close uk-float-right" type="button">
|
||||||
|
|
@ -16,11 +12,7 @@
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<!-- Viewer -->
|
<!-- Viewer -->
|
||||||
<div
|
<div v-if="selectedScanDZI" id="viewer_container" class="viewer_container">
|
||||||
v-if="selectedScanDZI"
|
|
||||||
id="viewer_container"
|
|
||||||
class="viewer_container"
|
|
||||||
>
|
|
||||||
<OpenSeadragonViewer
|
<OpenSeadragonViewer
|
||||||
id="openseadragon"
|
id="openseadragon"
|
||||||
ref="openseadragon"
|
ref="openseadragon"
|
||||||
|
|
@ -32,40 +24,19 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Controls -->
|
<!-- Controls -->
|
||||||
<div
|
<div v-if="selectedScanDZI" class="viewer-controls">
|
||||||
v-if="selectedScanDZI"
|
|
||||||
class="viewer-controls"
|
|
||||||
>
|
|
||||||
<div class="controlsContainer">
|
<div class="controlsContainer">
|
||||||
<label>
|
<label>
|
||||||
Brightness
|
Brightness
|
||||||
<input
|
<input v-model.number="brightness" type="range" min="0.2" max="1.8" step="0.01" />
|
||||||
type="range"
|
|
||||||
min="0.2"
|
|
||||||
max="1.8"
|
|
||||||
step="0.01"
|
|
||||||
v-model.number="brightness"
|
|
||||||
/>
|
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
Contrast
|
Contrast
|
||||||
<input
|
<input v-model.number="contrast" type="range" min="0.2" max="1.8" step="0.01" />
|
||||||
type="range"
|
|
||||||
min="0.2"
|
|
||||||
max="1.8"
|
|
||||||
step="0.01"
|
|
||||||
v-model.number="contrast"
|
|
||||||
/>
|
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
Saturation
|
Saturation
|
||||||
<input
|
<input v-model.number="saturation" type="range" min="0" max="2" step="0.01" />
|
||||||
type="range"
|
|
||||||
min="0"
|
|
||||||
max="2"
|
|
||||||
step="0.01"
|
|
||||||
v-model.number="saturation"
|
|
||||||
/>
|
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -155,26 +126,26 @@ input[type="range"] {
|
||||||
}
|
}
|
||||||
|
|
||||||
.controlsContainer {
|
.controlsContainer {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap: 1.5rem;
|
gap: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.viewer-controls {
|
.viewer-controls {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
margin-top: 0.5rem;
|
margin-top: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.viewer_container {
|
.viewer_container {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.reset-button{
|
.reset-button {
|
||||||
margin-top: 0.5rem;
|
margin-top: 0.5rem;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
v-observe-visibility="visibilityChanged"
|
v-observe-visibility="visibilityChanged"
|
||||||
class="galleryDisplay uk-padding uk-padding-remove-top">
|
class="galleryDisplay uk-padding uk-padding-remove-top"
|
||||||
|
>
|
||||||
<!-- Gallery nav bar -->
|
<!-- Gallery nav bar -->
|
||||||
<nav
|
<nav class="gallery-navbar uk-navbar-container uk-navbar-transparent" uk-navbar="mode: click">
|
||||||
class="gallery-navbar uk-navbar-container uk-navbar-transparent"
|
|
||||||
uk-navbar="mode: click"
|
|
||||||
>
|
|
||||||
<!-- Right side buttons -->
|
<!-- Right side buttons -->
|
||||||
<div class="uk-navbar-right">
|
<div class="uk-navbar-right">
|
||||||
<div class="uk-grid">
|
<div class="uk-grid">
|
||||||
|
|
@ -50,18 +48,17 @@
|
||||||
|
|
||||||
<ScanViewerModal
|
<ScanViewerModal
|
||||||
ref="scanViewer"
|
ref="scanViewer"
|
||||||
:selectedScan="selectedScan"
|
:selected-scan="selectedScan"
|
||||||
:baseUri="$store.getters.baseUri"
|
:base-uri="$store.getters.baseUri"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
||||||
<!-- Gallery -->
|
<!-- Gallery -->
|
||||||
<div
|
<div
|
||||||
v-if="$store.getters.ready"
|
v-if="$store.getters.ready"
|
||||||
class="uk-padding-remove-top"
|
class="uk-padding-remove-top"
|
||||||
uk-lightbox="toggle: .lightbox-link"
|
uk-lightbox="toggle: .lightbox-link"
|
||||||
>
|
>
|
||||||
<!-- Gallery capture cards -->
|
<!-- Gallery capture cards -->
|
||||||
<div class="gallery-grid uk-grid-match" uk-grid>
|
<div class="gallery-grid uk-grid-match" uk-grid>
|
||||||
<div v-if="scansEmpty">
|
<div v-if="scansEmpty">
|
||||||
<h2>No scans available</h2>
|
<h2>No scans available</h2>
|
||||||
|
|
@ -99,7 +96,7 @@ export default {
|
||||||
scans: [],
|
scans: [],
|
||||||
ongoing: null,
|
ongoing: null,
|
||||||
selectedScan: null,
|
selectedScan: null,
|
||||||
osdViewer: null
|
osdViewer: null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -109,19 +106,19 @@ export default {
|
||||||
"smart_scan",
|
"smart_scan",
|
||||||
"scans",
|
"scans",
|
||||||
"readproperty",
|
"readproperty",
|
||||||
true
|
true,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
scansEmpty() {
|
scansEmpty() {
|
||||||
return this.scans.length == 0;
|
return this.scans.length == 0;
|
||||||
},
|
},
|
||||||
selectedScanDZI() {
|
selectedScanDZI() {
|
||||||
if (this.selectedScan && this.selectedScan.dzi!="") {
|
if (this.selectedScan && this.selectedScan.dzi != "") {
|
||||||
return `${this.$store.getters.baseUri}/scans/${this.selectedScan.name}/images/${this.selectedScan.dzi}`;
|
return `${this.$store.getters.baseUri}/scans/${this.selectedScan.name}/images/${this.selectedScan.dzi}`;
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
async mounted() {
|
async mounted() {
|
||||||
|
|
@ -132,7 +129,7 @@ export default {
|
||||||
this.updateScans();
|
this.updateScans();
|
||||||
});
|
});
|
||||||
this.$root.$on("modalClosed", () => {
|
this.$root.$on("modalClosed", () => {
|
||||||
// Handle the modal closed event here
|
// Handle the modal closed event here
|
||||||
this.updateScans();
|
this.updateScans();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
@ -151,7 +148,7 @@ export default {
|
||||||
// If the connection is now disconnected, empty capture list
|
// If the connection is now disconnected, empty capture list
|
||||||
this.captures = {};
|
this.captures = {};
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -175,8 +172,8 @@ export default {
|
||||||
async updateScans() {
|
async updateScans() {
|
||||||
try {
|
try {
|
||||||
let scans_information = await this.readThingProperty("smart_scan", "scans");
|
let scans_information = await this.readThingProperty("smart_scan", "scans");
|
||||||
let scans = scans_information.scans
|
let scans = scans_information.scans;
|
||||||
this.ongoing = scans_information.ongoing
|
this.ongoing = scans_information.ongoing;
|
||||||
if (!scans | (scans.length == 0)) {
|
if (!scans | (scans.length == 0)) {
|
||||||
this.scans = scans;
|
this.scans = scans;
|
||||||
}
|
}
|
||||||
|
|
@ -194,13 +191,13 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
isOngoing(name) {
|
isOngoing(name) {
|
||||||
return name === this.ongoing
|
return name === this.ongoing;
|
||||||
},
|
},
|
||||||
async deleteAllScans() {
|
async deleteAllScans() {
|
||||||
try {
|
try {
|
||||||
await this.modalConfirm(
|
await this.modalConfirm(
|
||||||
"Are you sure you want to delete all scans from the microscope? " +
|
"Are you sure you want to delete all scans from the microscope? " +
|
||||||
"This is <b>irreversible</b>!"
|
"This is <b>irreversible</b>!",
|
||||||
);
|
);
|
||||||
await axios.delete(`${this.scansUri}`);
|
await axios.delete(`${this.scansUri}`);
|
||||||
await this.updateScans();
|
await this.updateScans();
|
||||||
|
|
@ -217,9 +214,9 @@ export default {
|
||||||
} else {
|
} else {
|
||||||
this.modalError("Scan not stitched for viewing in webapp, please download or stitch");
|
this.modalError("Scan not stitched for viewing in webapp, please download or stitch");
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
|
@ -238,4 +235,4 @@ export default {
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
margin-bottom: 2px;
|
margin-bottom: 2px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div id="CSMSettings" class="uk-grid uk-grid-divider uk-child-width-expand" uk-grid>
|
||||||
id="CSMSettings"
|
|
||||||
class="uk-grid uk-grid-divider uk-child-width-expand"
|
|
||||||
uk-grid
|
|
||||||
>
|
|
||||||
<div class="uk-width-xlarge">
|
<div class="uk-width-xlarge">
|
||||||
<h3>Camera to Stage Mapping</h3>
|
<h3>Camera to Stage Mapping</h3>
|
||||||
<p>
|
<p>
|
||||||
Camera-stage mapping allows the stage to move relative to the camera
|
Camera-stage mapping allows the stage to move relative to the camera view. This enables
|
||||||
view. This enables functions like click-to-move, and more precise tile
|
functions like click-to-move, and more precise tile scans.
|
||||||
scans.
|
|
||||||
</p>
|
</p>
|
||||||
<CSMCalibrationSettings />
|
<CSMCalibrationSettings />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -29,8 +24,8 @@ export default {
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
CSMCalibrationSettings,
|
CSMCalibrationSettings,
|
||||||
miniStreamDisplay
|
miniStreamDisplay,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div v-observe-visibility="visibilityChanged" id="CSMCalibrationSettings">
|
<div id="CSMCalibrationSettings" v-observe-visibility="visibilityChanged">
|
||||||
<!--Show auto calibrate if default plugin is enabled-->
|
<!--Show auto calibrate if default plugin is enabled-->
|
||||||
<div v-if="'calibrate_xy' in actions" class="uk-margin-small">
|
<div v-if="'calibrate_xy' in actions" class="uk-margin-small">
|
||||||
<action-button
|
<action-button
|
||||||
|
|
@ -26,15 +26,18 @@
|
||||||
>
|
>
|
||||||
Download Calibration Data
|
Download Calibration Data
|
||||||
</button>
|
</button>
|
||||||
<div v-if="this.csmMatrix!='undefined'" style="margin:10px;">
|
<div v-if="csmMatrix != 'undefined'" style="margin:10px;">
|
||||||
<details><summary>Calibration Details</summary>
|
<details
|
||||||
<strong>CSM calculated for images with a resolution of {{ csmResolution }}</strong>
|
><summary>Calibration Details</summary>
|
||||||
<ul>
|
<strong>CSM calculated for images with a resolution of {{ csmResolution }}</strong>
|
||||||
<li>Current CSM Matrix: <tt>{{ csmMatrix }}</tt></li>
|
<ul>
|
||||||
<li>Pixels per motor step: {{ csmRatio }}</li>
|
<li>
|
||||||
<li>Full field of view: {{ csmFOV }} motor steps</li>
|
Current CSM Matrix: <tt>{{ csmMatrix }}</tt>
|
||||||
</ul>
|
</li>
|
||||||
</details>
|
<li>Pixels per motor step: {{ csmRatio }}</li>
|
||||||
|
<li>Full field of view: {{ csmFOV }} motor steps</li>
|
||||||
|
</ul>
|
||||||
|
</details>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -47,36 +50,32 @@ export default {
|
||||||
name: "CSMCalibrationSettings",
|
name: "CSMCalibrationSettings",
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
ActionButton
|
ActionButton,
|
||||||
},
|
},
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
showExtraSettings: {
|
showExtraSettings: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: false,
|
required: false,
|
||||||
default: true
|
default: true,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
csmMatrix: "undefined",
|
csmMatrix: "undefined",
|
||||||
csmResolution: "undefined",
|
csmResolution: "undefined",
|
||||||
csmRatio: "undefined"
|
csmRatio: "undefined",
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
actions() {
|
actions() {
|
||||||
return this.$store.getters["wot/thingDescription"]("camera_stage_mapping")
|
return this.$store.getters["wot/thingDescription"]("camera_stage_mapping").actions;
|
||||||
.actions;
|
|
||||||
},
|
},
|
||||||
properties() {
|
properties() {
|
||||||
return this.$store.getters["wot/thingDescription"]("camera_stage_mapping")
|
return this.$store.getters["wot/thingDescription"]("camera_stage_mapping").properties;
|
||||||
.properties;
|
},
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
|
@ -87,10 +86,7 @@ export default {
|
||||||
},
|
},
|
||||||
getCalibrationData: async function() {
|
getCalibrationData: async function() {
|
||||||
try {
|
try {
|
||||||
let data = await this.readThingProperty(
|
let data = await this.readThingProperty("camera_stage_mapping", "last_calibration");
|
||||||
"camera_stage_mapping",
|
|
||||||
"last_calibration"
|
|
||||||
);
|
|
||||||
if (data == {}) {
|
if (data == {}) {
|
||||||
throw "No calibration data available.";
|
throw "No calibration data available.";
|
||||||
}
|
}
|
||||||
|
|
@ -107,33 +103,29 @@ export default {
|
||||||
},
|
},
|
||||||
updateDisplayedCSM: async function() {
|
updateDisplayedCSM: async function() {
|
||||||
let csmMatrix = await this.readThingProperty(
|
let csmMatrix = await this.readThingProperty(
|
||||||
"camera_stage_mapping",
|
|
||||||
"image_to_stage_displacement_matrix"
|
|
||||||
);
|
|
||||||
this.csmResolution = await this.readThingProperty(
|
|
||||||
"camera_stage_mapping",
|
"camera_stage_mapping",
|
||||||
"image_resolution"
|
"image_to_stage_displacement_matrix",
|
||||||
);
|
|
||||||
let streamResolution = await this.readThingProperty(
|
|
||||||
"camera",
|
|
||||||
"stream_resolution"
|
|
||||||
);
|
);
|
||||||
|
this.csmResolution = await this.readThingProperty("camera_stage_mapping", "image_resolution");
|
||||||
|
let streamResolution = await this.readThingProperty("camera", "stream_resolution");
|
||||||
csmMatrix[0][0] = Number(csmMatrix[0][0].toFixed(3));
|
csmMatrix[0][0] = Number(csmMatrix[0][0].toFixed(3));
|
||||||
csmMatrix[0][1] = Number(csmMatrix[0][1].toFixed(3));
|
csmMatrix[0][1] = Number(csmMatrix[0][1].toFixed(3));
|
||||||
csmMatrix[1][0] = Number(csmMatrix[1][0].toFixed(3));
|
csmMatrix[1][0] = Number(csmMatrix[1][0].toFixed(3));
|
||||||
csmMatrix[1][1] = Number(csmMatrix[1][1].toFixed(3));
|
csmMatrix[1][1] = Number(csmMatrix[1][1].toFixed(3));
|
||||||
this.csmMatrix = csmMatrix;
|
this.csmMatrix = csmMatrix;
|
||||||
this.csmRatio = Number((Math.abs(csmMatrix[1][0]) + Math.abs(csmMatrix[0][1])) / 2).toFixed(3);
|
this.csmRatio = Number((Math.abs(csmMatrix[1][0]) + Math.abs(csmMatrix[0][1])) / 2).toFixed(
|
||||||
|
3,
|
||||||
|
);
|
||||||
this.csmFOV = [
|
this.csmFOV = [
|
||||||
Number((streamResolution[0]**2 * this.csmRatio / this.csmResolution[1]).toFixed(0)),
|
Number(((streamResolution[0] ** 2 * this.csmRatio) / this.csmResolution[1]).toFixed(0)),
|
||||||
Number((streamResolution[1]**2 * this.csmRatio / this.csmResolution[0]).toFixed(0)),
|
Number(((streamResolution[1] ** 2 * this.csmRatio) / this.csmResolution[0]).toFixed(0)),
|
||||||
]
|
];
|
||||||
},
|
},
|
||||||
onRecalibrateResponse: function() {
|
onRecalibrateResponse: function() {
|
||||||
this.modalNotify("Finished stage-to-camera calibration.");
|
this.modalNotify("Finished stage-to-camera calibration.");
|
||||||
this.updateDisplayedCSM();
|
this.updateDisplayedCSM();
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -34,8 +34,19 @@ export default {
|
||||||
},
|
},
|
||||||
set(value) {
|
set(value) {
|
||||||
this.$store.commit("changeAppTheme", value);
|
this.$store.commit("changeAppTheme", value);
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
appTheme: function() {
|
||||||
|
this.setLocalStorageObj("appTheme", this.appTheme);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
// Try loading settings from localStorage. If null, don't change.
|
||||||
|
this.appTheme = this.getLocalStorageObj("appTheme") || this.appTheme;
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
|
@ -45,19 +56,8 @@ export default {
|
||||||
} else {
|
} else {
|
||||||
await document.exitFullscreen();
|
await document.exitFullscreen();
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
|
||||||
appTheme: function() {
|
|
||||||
this.setLocalStorageObj("appTheme", this.appTheme);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
// Try loading settings from localStorage. If null, don't change.
|
|
||||||
this.appTheme = this.getLocalStorageObj("appTheme") || this.appTheme;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ export default {
|
||||||
components: {
|
components: {
|
||||||
cameraCalibrationSettings,
|
cameraCalibrationSettings,
|
||||||
miniStreamDisplay,
|
miniStreamDisplay,
|
||||||
ServerSpecifiedPropertyControl
|
ServerSpecifiedPropertyControl,
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
|
|
@ -45,15 +45,12 @@ export default {
|
||||||
computed: {
|
computed: {
|
||||||
cameraUri: function() {
|
cameraUri: function() {
|
||||||
return `${this.$store.getters.baseUri}/camera/`;
|
return `${this.$store.getters.baseUri}/camera/`;
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
async created() {
|
async created() {
|
||||||
this.manualCameraSettings = await this.readThingProperty(
|
this.manualCameraSettings = await this.readThingProperty("camera", "manual_camera_settings");
|
||||||
"camera",
|
},
|
||||||
"manual_camera_settings"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,44 +28,44 @@ export default {
|
||||||
name: "CameraCalibrationSettings",
|
name: "CameraCalibrationSettings",
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
ServerSpecifiedActionButton
|
ServerSpecifiedActionButton,
|
||||||
},
|
},
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
showExtraSettings: {
|
showExtraSettings: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: false,
|
required: false,
|
||||||
default: true
|
default: true,
|
||||||
},
|
},
|
||||||
cameraUri: {
|
cameraUri: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true
|
required: true,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
primaryCalibrationActions: [],
|
primaryCalibrationActions: [],
|
||||||
secondaryCalibrationActions: []
|
secondaryCalibrationActions: [],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
actions() {
|
actions() {
|
||||||
return this.$store.getters["wot/thingDescription"]("camera").actions;
|
return this.$store.getters["wot/thingDescription"]("camera").actions;
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
async created() {
|
async created() {
|
||||||
this.primaryCalibrationActions = await this.readThingProperty(
|
this.primaryCalibrationActions = await this.readThingProperty(
|
||||||
"camera",
|
"camera",
|
||||||
"primary_calibration_actions"
|
"primary_calibration_actions",
|
||||||
);
|
);
|
||||||
this.secondaryCalibrationActions = await this.readThingProperty(
|
this.secondaryCalibrationActions = await this.readThingProperty(
|
||||||
"camera",
|
"camera",
|
||||||
"secondary_calibration_actions"
|
"secondary_calibration_actions",
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,30 @@
|
||||||
<template>
|
<template>
|
||||||
<div id="stageSettings" class="uk-width-large" v-observe-visibility="visibilityChanged">
|
<div id="stageSettings" v-observe-visibility="visibilityChanged" class="uk-width-large">
|
||||||
The microscope stage is a <b>{{ stageType }}</b>
|
The microscope stage is a <b>{{ stageType }}</b>
|
||||||
<div>
|
<div>
|
||||||
<div class="uk-margin">
|
<div class="uk-margin">
|
||||||
|
<p>Your z motor is currently {{ z_inverted }} inverted.</p>
|
||||||
<p>
|
<p>
|
||||||
<br>
|
We expect that moving in +z:
|
||||||
Your z motor is currently {{ this.z_inverted }} inverted.
|
</p>
|
||||||
<br>
|
|
||||||
<br>
|
|
||||||
We expect that moving in +z:
|
|
||||||
<ul>
|
<ul>
|
||||||
<li> Moves your objective up, towards the sample and illumination</li>
|
<li>Moves your objective up, towards the sample and illumination</li>
|
||||||
<li> Turns the exposed z gear anti-clockwise (when viewed from above)</li>
|
<li>Turns the exposed z gear anti-clockwise (when viewed from above)</li>
|
||||||
</ul>
|
</ul>
|
||||||
If this is not the case, click the button below to switch.
|
<p>
|
||||||
|
If this is not the case, click the button below to switch.
|
||||||
</p>
|
</p>
|
||||||
<div class="uk-margin">
|
<div class="uk-margin">
|
||||||
<div class="uk-margin">
|
<div class="uk-margin">
|
||||||
<action-button
|
<action-button
|
||||||
class="uk-width-1-2"
|
class="uk-width-1-2"
|
||||||
thing="stage"
|
thing="stage"
|
||||||
action="invert_axis_direction"
|
action="invert_axis_direction"
|
||||||
:submit-data="{ axis: 'z' }"
|
:submit-data="{ axis: 'z' }"
|
||||||
submit-label="Invert z"
|
submit-label="Invert z"
|
||||||
@response=readAxis()
|
@response="readAxis()"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -48,26 +47,23 @@ export default {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
methods:{
|
computed: {
|
||||||
|
stageType: function() {
|
||||||
|
return this.thingDescription("stage").title;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
visibilityChanged(isVisible) {
|
visibilityChanged(isVisible) {
|
||||||
if (isVisible) {
|
if (isVisible) {
|
||||||
this.readAxis();
|
this.readAxis();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async readAxis() {
|
async readAxis() {
|
||||||
let axes_inverted = await this.readThingProperty(
|
let axes_inverted = await this.readThingProperty("stage", "axis_inverted");
|
||||||
"stage",
|
this.z_inverted = axes_inverted["z"] ? "" : "not ";
|
||||||
"axis_inverted"
|
},
|
||||||
);
|
|
||||||
this.z_inverted = axes_inverted['z'] ? "" : "not ";
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
|
||||||
stageType: function() {
|
|
||||||
return this.thingDescription("stage").title;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@
|
||||||
<div>
|
<div>
|
||||||
<h3>Stream Settings</h3>
|
<h3>Stream Settings</h3>
|
||||||
<label
|
<label
|
||||||
><input v-model="disableStream" class="uk-checkbox" type="checkbox" />
|
><input v-model="disableStream" class="uk-checkbox" type="checkbox" /> Disable Web
|
||||||
Disable Web Stream</label
|
Stream</label
|
||||||
>
|
>
|
||||||
<p class="uk-margin-small">
|
<p class="uk-margin-small">
|
||||||
This will disable the embedded web stream of the camera.
|
This will disable the embedded web stream of the camera.
|
||||||
|
|
@ -29,8 +29,8 @@ export default {
|
||||||
},
|
},
|
||||||
set(value) {
|
set(value) {
|
||||||
this.$store.commit("changeDisableStream", value);
|
this.$store.commit("changeDisableStream", value);
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
|
|
@ -38,8 +38,8 @@ export default {
|
||||||
// (the next 3 functions all relate to this)
|
// (the next 3 functions all relate to this)
|
||||||
disableStream: function(newValue) {
|
disableStream: function(newValue) {
|
||||||
this.setLocalStorageObj("disableStream", newValue);
|
this.setLocalStorageObj("disableStream", newValue);
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<!-- Grid managing tab content -->
|
<!-- Grid managing tab content -->
|
||||||
<div uk-grid class="uk-height-1-1 uk-margin-remove uk-padding-remove">
|
<div uk-grid class="uk-height-1-1 uk-margin-remove uk-padding-remove">
|
||||||
<calibrationWizard
|
<calibrationWizard ref="calibrationWizard"></calibrationWizard>
|
||||||
ref="calibrationWizard"
|
|
||||||
></calibrationWizard>
|
|
||||||
<div class="settings-nav">
|
<div class="settings-nav">
|
||||||
<ul class="uk-nav uk-nav-default">
|
<ul class="uk-nav uk-nav-default">
|
||||||
<li class="uk-nav-header">Application Settings</li>
|
<li class="uk-nav-header">Application Settings</li>
|
||||||
|
|
@ -22,11 +20,13 @@
|
||||||
</li>
|
</li>
|
||||||
<li class="uk-nav-header">Microscope Settings</li>
|
<li class="uk-nav-header">Microscope Settings</li>
|
||||||
<button
|
<button
|
||||||
submit-label="Launch Calibration Wizard"
|
submit-label="Launch Calibration Wizard"
|
||||||
type="button"
|
type="button"
|
||||||
class="uk-button uk-button-default uk-width-1-1"
|
class="uk-button uk-button-default uk-width-1-1"
|
||||||
@click="startModals"
|
@click="startModals"
|
||||||
>Launch Calibration Wizard</button>
|
>
|
||||||
|
Launch Calibration Wizard
|
||||||
|
</button>
|
||||||
<li>
|
<li>
|
||||||
<tabIcon
|
<tabIcon
|
||||||
id="settings-camera-icon"
|
id="settings-camera-icon"
|
||||||
|
|
@ -69,42 +69,26 @@
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="view-component uk-width-expand uk-padding-small">
|
<div class="view-component uk-width-expand uk-padding-small">
|
||||||
<tabContent
|
<tabContent tab-i-d="display" :require-connection="false" :current-tab="currentTab">
|
||||||
tab-i-d="display"
|
|
||||||
:require-connection="false"
|
|
||||||
:current-tab="currentTab"
|
|
||||||
>
|
|
||||||
<div class="settings-pane uk-padding-small">
|
<div class="settings-pane uk-padding-small">
|
||||||
<appSettings />
|
<appSettings />
|
||||||
<streamSettings />
|
<streamSettings />
|
||||||
</div>
|
</div>
|
||||||
</tabContent>
|
</tabContent>
|
||||||
|
|
||||||
<tabContent
|
<tabContent tab-i-d="camera" :require-connection="true" :current-tab="currentTab">
|
||||||
tab-i-d="camera"
|
|
||||||
:require-connection="true"
|
|
||||||
:current-tab="currentTab"
|
|
||||||
>
|
|
||||||
<div class="settings-pane uk-padding-small">
|
<div class="settings-pane uk-padding-small">
|
||||||
<cameraSettings />
|
<cameraSettings />
|
||||||
</div>
|
</div>
|
||||||
</tabContent>
|
</tabContent>
|
||||||
|
|
||||||
<tabContent
|
<tabContent tab-i-d="stage" :require-connection="true" :current-tab="currentTab">
|
||||||
tab-i-d="stage"
|
|
||||||
:require-connection="true"
|
|
||||||
:current-tab="currentTab"
|
|
||||||
>
|
|
||||||
<div class="settings-pane uk-padding-small">
|
<div class="settings-pane uk-padding-small">
|
||||||
<stageSettings />
|
<stageSettings />
|
||||||
</div>
|
</div>
|
||||||
</tabContent>
|
</tabContent>
|
||||||
|
|
||||||
<tabContent
|
<tabContent tab-i-d="mapping" :require-connection="true" :current-tab="currentTab">
|
||||||
tab-i-d="mapping"
|
|
||||||
:require-connection="true"
|
|
||||||
:current-tab="currentTab"
|
|
||||||
>
|
|
||||||
<div class="settings-pane uk-padding-small">
|
<div class="settings-pane uk-padding-small">
|
||||||
<CSMSettings />
|
<CSMSettings />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -136,13 +120,13 @@ export default {
|
||||||
appSettings,
|
appSettings,
|
||||||
tabIcon,
|
tabIcon,
|
||||||
tabContent,
|
tabContent,
|
||||||
calibrationWizard
|
calibrationWizard,
|
||||||
},
|
},
|
||||||
|
|
||||||
data: function() {
|
data: function() {
|
||||||
return {
|
return {
|
||||||
selected: "display",
|
selected: "display",
|
||||||
currentTab: "display"
|
currentTab: "display",
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -154,8 +138,8 @@ export default {
|
||||||
},
|
},
|
||||||
startModals: function() {
|
startModals: function() {
|
||||||
this.$refs.calibrationWizard.force_show();
|
this.$refs.calibrationWizard.force_show();
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@
|
||||||
thing-name="autofocus"
|
thing-name="autofocus"
|
||||||
property-name="stack_dz"
|
property-name="stack_dz"
|
||||||
label="Stack dz (steps)"
|
label="Stack dz (steps)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="uk-margin">
|
<div class="uk-margin">
|
||||||
<propertyControl
|
<propertyControl
|
||||||
|
|
@ -83,12 +83,7 @@
|
||||||
</ul>
|
</ul>
|
||||||
<label class="uk-form-label" for="form-stacked-text">Sample ID</label>
|
<label class="uk-form-label" for="form-stacked-text">Sample ID</label>
|
||||||
<div class="uk-form-controls">
|
<div class="uk-form-controls">
|
||||||
<input
|
<input v-model="scan_name" class="uk-input uk-form-small" type="text" name="Scan Name" />
|
||||||
v-model="scan_name"
|
|
||||||
class="uk-input uk-form-small"
|
|
||||||
type="text"
|
|
||||||
name="Scan Name"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="uk-margin">
|
<div class="uk-margin">
|
||||||
<action-button
|
<action-button
|
||||||
|
|
@ -110,11 +105,7 @@
|
||||||
Live stitching preview
|
Live stitching preview
|
||||||
</h2>
|
</h2>
|
||||||
<mini-stream-display v-if="displayImageOnRight" />
|
<mini-stream-display v-if="displayImageOnRight" />
|
||||||
<action-log-display
|
<action-log-display id="log-display" :log="log" :task-status="taskStatus" />
|
||||||
id="log-display"
|
|
||||||
:log="log"
|
|
||||||
:task-status="taskStatus"
|
|
||||||
/>
|
|
||||||
<action-progress-bar :progress="progress" :task-status="taskStatus" />
|
<action-progress-bar :progress="progress" :task-status="taskStatus" />
|
||||||
<button
|
<button
|
||||||
v-if="cancellable"
|
v-if="cancellable"
|
||||||
|
|
@ -124,11 +115,7 @@
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</button>
|
</button>
|
||||||
<div
|
<div v-if="!cancellable" class="uk-margin uk-grid-small uk-child-width-expand" uk-grid>
|
||||||
v-if="!cancellable"
|
|
||||||
class="uk-margin uk-grid-small uk-child-width-expand"
|
|
||||||
uk-grid
|
|
||||||
>
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="uk-button"
|
class="uk-button"
|
||||||
|
|
@ -182,7 +169,7 @@ export default {
|
||||||
actionLogDisplay,
|
actionLogDisplay,
|
||||||
actionProgressBar,
|
actionProgressBar,
|
||||||
MiniStreamDisplay,
|
MiniStreamDisplay,
|
||||||
ActionButton
|
ActionButton,
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
|
|
@ -195,7 +182,7 @@ export default {
|
||||||
progress: null,
|
progress: null,
|
||||||
log: [],
|
log: [],
|
||||||
lastStitchedImage: null,
|
lastStitchedImage: null,
|
||||||
scan_name: ""
|
scan_name: "",
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -208,7 +195,7 @@ export default {
|
||||||
},
|
},
|
||||||
displayImageOnRight() {
|
displayImageOnRight() {
|
||||||
return this.scanning & (this.lastStitchedImage !== null);
|
return this.scanning & (this.lastStitchedImage !== null);
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
|
@ -225,17 +212,11 @@ export default {
|
||||||
async pollScan() {
|
async pollScan() {
|
||||||
if (this.cancellable) {
|
if (this.cancellable) {
|
||||||
// while the scan is running
|
// while the scan is running
|
||||||
let mtime = await this.readThingProperty(
|
let mtime = await this.readThingProperty("smart_scan", "latest_preview_stitch_time");
|
||||||
"smart_scan",
|
|
||||||
"latest_preview_stitch_time"
|
|
||||||
);
|
|
||||||
if (mtime !== null) {
|
if (mtime !== null) {
|
||||||
this.lastStitchedImage = `${this.$store.getters.baseUri}/smart_scan/latest_preview_stitch.jpg?t=${mtime}`;
|
this.lastStitchedImage = `${this.$store.getters.baseUri}/smart_scan/latest_preview_stitch.jpg?t=${mtime}`;
|
||||||
}
|
}
|
||||||
this.lastScanName = await this.readThingProperty(
|
this.lastScanName = await this.readThingProperty("smart_scan", "latest_scan_name");
|
||||||
"smart_scan",
|
|
||||||
"latest_scan_name"
|
|
||||||
);
|
|
||||||
setTimeout(this.pollScan, 1000); // keep rescheduling until it's stopped
|
setTimeout(this.pollScan, 1000); // keep rescheduling until it's stopped
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -249,8 +230,8 @@ export default {
|
||||||
console.log(link);
|
console.log(link);
|
||||||
document.body.appendChild(link);
|
document.body.appendChild(link);
|
||||||
link.click();
|
link.click();
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ export default {
|
||||||
isVisible: false,
|
isVisible: false,
|
||||||
displaySize: [0, 0],
|
displaySize: [0, 0],
|
||||||
displayPosition: [0, 0],
|
displayPosition: [0, 0],
|
||||||
resizeTimeoutId: setTimeout(this.doneResizing, 500)
|
resizeTimeoutId: setTimeout(this.doneResizing, 500),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -58,7 +58,7 @@ export default {
|
||||||
},
|
},
|
||||||
streamImgUri: function() {
|
streamImgUri: function() {
|
||||||
return `${this.$store.getters.baseUri}/camera/mjpeg_stream`;
|
return `${this.$store.getters.baseUri}/camera/mjpeg_stream`;
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
|
|
@ -119,18 +119,14 @@ export default {
|
||||||
// factor.
|
// factor.
|
||||||
let scale = Math.max(
|
let scale = Math.max(
|
||||||
event.target.naturalWidth / event.target.offsetWidth,
|
event.target.naturalWidth / event.target.offsetWidth,
|
||||||
event.target.naturalHeight / event.target.offsetHeight
|
event.target.naturalHeight / event.target.offsetHeight,
|
||||||
);
|
);
|
||||||
|
|
||||||
let xRelative = (0.5 * event.target.offsetWidth - xCoordinate) * scale;
|
let xRelative = (0.5 * event.target.offsetWidth - xCoordinate) * scale;
|
||||||
let yRelative = (0.5 * event.target.offsetHeight - yCoordinate) * scale;
|
let yRelative = (0.5 * event.target.offsetHeight - yCoordinate) * scale;
|
||||||
|
|
||||||
// Emit a signal to move, acted on by paneControl.vue
|
// Emit a signal to move, acted on by paneControl.vue
|
||||||
this.$root.$emit(
|
this.$root.$emit("globalMoveInImageCoordinatesEvent", -xRelative, -yRelative);
|
||||||
"globalMoveInImageCoordinatesEvent",
|
|
||||||
-xRelative,
|
|
||||||
-yRelative
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
handleResize: function() {
|
handleResize: function() {
|
||||||
|
|
@ -167,18 +163,13 @@ export default {
|
||||||
let windowChromeHeight = window.outerHeight - window.innerHeight;
|
let windowChromeHeight = window.outerHeight - window.innerHeight;
|
||||||
let elementPositionOnDisplay = [
|
let elementPositionOnDisplay = [
|
||||||
Math.max(0, windowPositionOnDisplay[0] + elementPositionOnWindow[0]),
|
Math.max(0, windowPositionOnDisplay[0] + elementPositionOnWindow[0]),
|
||||||
Math.max(
|
Math.max(0, windowPositionOnDisplay[1] + elementPositionOnWindow[1] + windowChromeHeight),
|
||||||
0,
|
|
||||||
windowPositionOnDisplay[1] +
|
|
||||||
elementPositionOnWindow[1] +
|
|
||||||
windowChromeHeight
|
|
||||||
)
|
|
||||||
];
|
];
|
||||||
|
|
||||||
this.displaySize = elementSize;
|
this.displaySize = elementSize;
|
||||||
this.displayPosition = elementPositionOnDisplay;
|
this.displayPosition = elementPositionOnDisplay;
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ export default {
|
||||||
name: "ViewContent",
|
name: "ViewContent",
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
streamDisplay
|
streamDisplay,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -6,19 +6,18 @@ import UIkit from "uikit";
|
||||||
|
|
||||||
import VueObserveVisibility from "vue-observe-visibility";
|
import VueObserveVisibility from "vue-observe-visibility";
|
||||||
|
|
||||||
|
|
||||||
// Import MD icons
|
// Import MD icons
|
||||||
import "material-symbols/outlined.css";
|
import "material-symbols/outlined.css";
|
||||||
import version from './version.js'
|
import version from "./version.js";
|
||||||
|
|
||||||
// UIKit overrides
|
// UIKit overrides
|
||||||
UIkit.mixin(
|
UIkit.mixin(
|
||||||
{
|
{
|
||||||
data: {
|
data: {
|
||||||
animation: false
|
animation: false,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
"accordion"
|
"accordion",
|
||||||
);
|
);
|
||||||
|
|
||||||
// Use visibility observer
|
// Use visibility observer
|
||||||
|
|
@ -38,12 +37,7 @@ Vue.mixin({
|
||||||
return this.$store.getters["wot/thingAvailable"](thing);
|
return this.$store.getters["wot/thingAvailable"](thing);
|
||||||
},
|
},
|
||||||
async readThingProperty(thing, property, silence_errors = false) {
|
async readThingProperty(thing, property, silence_errors = false) {
|
||||||
let url = this.$store.getters["wot/thingPropertyUrl"](
|
let url = this.$store.getters["wot/thingPropertyUrl"](thing, property, "readproperty", false);
|
||||||
thing,
|
|
||||||
property,
|
|
||||||
"readproperty",
|
|
||||||
false
|
|
||||||
);
|
|
||||||
try {
|
try {
|
||||||
let response = await axios.get(url);
|
let response = await axios.get(url);
|
||||||
return response.data;
|
return response.data;
|
||||||
|
|
@ -57,7 +51,7 @@ Vue.mixin({
|
||||||
thing,
|
thing,
|
||||||
property,
|
property,
|
||||||
"writeproperty",
|
"writeproperty",
|
||||||
false
|
false,
|
||||||
);
|
);
|
||||||
// `false` fails because axios somehow eats it!
|
// `false` fails because axios somehow eats it!
|
||||||
// Other values should not be stringified or pydantic
|
// Other values should not be stringified or pydantic
|
||||||
|
|
@ -68,12 +62,7 @@ Vue.mixin({
|
||||||
await axios.put(url, value);
|
await axios.put(url, value);
|
||||||
},
|
},
|
||||||
async invokeAction(thing, action, data) {
|
async invokeAction(thing, action, data) {
|
||||||
let url = this.$store.getters["wot/thingActionUrl"](
|
let url = this.$store.getters["wot/thingActionUrl"](thing, action, "invokeaction", false);
|
||||||
thing,
|
|
||||||
action,
|
|
||||||
"invokeaction",
|
|
||||||
false
|
|
||||||
);
|
|
||||||
try {
|
try {
|
||||||
let response = await axios.post(url, data);
|
let response = await axios.post(url, data);
|
||||||
return response;
|
return response;
|
||||||
|
|
@ -87,7 +76,7 @@ Vue.mixin({
|
||||||
thing,
|
thing,
|
||||||
action,
|
action,
|
||||||
"invokeaction",
|
"invokeaction",
|
||||||
allow_missing
|
allow_missing,
|
||||||
);
|
);
|
||||||
return url;
|
return url;
|
||||||
},
|
},
|
||||||
|
|
@ -109,14 +98,14 @@ Vue.mixin({
|
||||||
},
|
},
|
||||||
function() {
|
function() {
|
||||||
reject();
|
reject();
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
.finally(function() {
|
.finally(function() {
|
||||||
// Re-enable the GPU preview, if it was active before the modal
|
// Re-enable the GPU preview, if it was active before the modal
|
||||||
if (context.$store.state.autoGpuPreview) {
|
if (context.$store.state.autoGpuPreview) {
|
||||||
context.$root.$emit("globalTogglePreview", true);
|
context.$root.$emit("globalTogglePreview", true);
|
||||||
}
|
}
|
||||||
context.$root.$emit("modalClosed");
|
context.$root.$emit("modalClosed");
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
return new Promise(showModal);
|
return new Promise(showModal);
|
||||||
|
|
@ -125,7 +114,7 @@ Vue.mixin({
|
||||||
modalNotify: function(message, status = "success") {
|
modalNotify: function(message, status = "success") {
|
||||||
UIkit.notification({
|
UIkit.notification({
|
||||||
message: message,
|
message: message,
|
||||||
status: status
|
status: status,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -140,7 +129,7 @@ Vue.mixin({
|
||||||
<p>${message}</p>
|
<p>${message}</p>
|
||||||
</div>
|
</div>
|
||||||
`,
|
`,
|
||||||
{ stack: true }
|
{ stack: true },
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -149,25 +138,25 @@ Vue.mixin({
|
||||||
this.$store.commit("setErrorMessage", errormsg);
|
this.$store.commit("setErrorMessage", errormsg);
|
||||||
UIkit.notification({
|
UIkit.notification({
|
||||||
message: `${errormsg}`,
|
message: `${errormsg}`,
|
||||||
status: "danger"
|
status: "danger",
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
getErrorMessage: function(error) {
|
getErrorMessage: function(error) {
|
||||||
// Format the error.
|
// Format the error.
|
||||||
let data = this.getErrorData(error)
|
let data = this.getErrorData(error);
|
||||||
// Get error data may get an object. Handle edge cases and if not try to use
|
// Get error data may get an object. Handle edge cases and if not try to use
|
||||||
// JSON to get the best string. This stops "objectObject" showing as an error.
|
// JSON to get the best string. This stops "objectObject" showing as an error.
|
||||||
if (data === null) return "null";
|
if (data === null) return "null";
|
||||||
if (data === undefined) return "undefined";
|
if (data === undefined) return "undefined";
|
||||||
if (typeof data === 'string') return data;
|
if (typeof data === "string") return data;
|
||||||
try {
|
try {
|
||||||
return JSON.stringify(data, null, 2);
|
return JSON.stringify(data, null, 2);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return String(data);
|
return String(data);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getErrorData: function(error){
|
getErrorData: function(error) {
|
||||||
// If a response was obtained, extract the most specific message
|
// If a response was obtained, extract the most specific message
|
||||||
if (error.response) {
|
if (error.response) {
|
||||||
// If the response is a nicely formatted JSON response from the server
|
// If the response is a nicely formatted JSON response from the server
|
||||||
|
|
@ -176,7 +165,7 @@ Vue.mixin({
|
||||||
}
|
}
|
||||||
if (error.response.data.detail) {
|
if (error.response.data.detail) {
|
||||||
try {
|
try {
|
||||||
return error.response.data.detail[0].msg
|
return error.response.data.detail[0].msg;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return error.response.data.detail;
|
return error.response.data.detail;
|
||||||
}
|
}
|
||||||
|
|
@ -219,11 +208,11 @@ Vue.mixin({
|
||||||
setLocalStorageObj: function(keyName, object) {
|
setLocalStorageObj: function(keyName, object) {
|
||||||
const parsed = JSON.stringify(object);
|
const parsed = JSON.stringify(object);
|
||||||
localStorage.setItem(keyName, parsed);
|
localStorage.setItem(keyName, parsed);
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
new Vue({
|
new Vue({
|
||||||
store,
|
store,
|
||||||
render: h => h(App)
|
render: h => h(App),
|
||||||
}).$mount("#app");
|
}).$mount("#app");
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ function getOriginFromLocation() {
|
||||||
|
|
||||||
export default new Vuex.Store({
|
export default new Vuex.Store({
|
||||||
modules: {
|
modules: {
|
||||||
wot: wotStoreModule
|
wot: wotStoreModule,
|
||||||
},
|
},
|
||||||
state: {
|
state: {
|
||||||
origin: getOriginFromLocation(),
|
origin: getOriginFromLocation(),
|
||||||
|
|
@ -32,7 +32,7 @@ export default new Vuex.Store({
|
||||||
galleryEnabled: true,
|
galleryEnabled: true,
|
||||||
appTheme: "system",
|
appTheme: "system",
|
||||||
activeStreams: {},
|
activeStreams: {},
|
||||||
microscopeHostname: ""
|
microscopeHostname: "",
|
||||||
},
|
},
|
||||||
|
|
||||||
mutations: {
|
mutations: {
|
||||||
|
|
@ -77,13 +77,13 @@ export default new Vuex.Store({
|
||||||
},
|
},
|
||||||
changeMicroscopeHostname(state, value) {
|
changeMicroscopeHostname(state, value) {
|
||||||
state.microscopeHostname = value;
|
state.microscopeHostname = value;
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
actions: {},
|
actions: {},
|
||||||
|
|
||||||
getters: {
|
getters: {
|
||||||
baseUri: state => state.origin,
|
baseUri: state => state.origin,
|
||||||
ready: state => state.available
|
ready: state => state.available,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ export const wotStoreModule = {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
thingDescriptions: {},
|
thingDescriptions: {},
|
||||||
servient: null,
|
servient: null,
|
||||||
helpers: null
|
helpers: null,
|
||||||
}),
|
}),
|
||||||
mutations: {
|
mutations: {
|
||||||
addThingDescription(state, { thingName, thingDescription }) {
|
addThingDescription(state, { thingName, thingDescription }) {
|
||||||
|
|
@ -16,7 +16,7 @@ export const wotStoreModule = {
|
||||||
},
|
},
|
||||||
removeAllThingDescriptions(state) {
|
removeAllThingDescriptions(state) {
|
||||||
state.thingDescriptions = {};
|
state.thingDescriptions = {};
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
async start() {
|
async start() {
|
||||||
|
|
@ -36,7 +36,7 @@ export const wotStoreModule = {
|
||||||
.pop();
|
.pop();
|
||||||
commit("addThingDescription", {
|
commit("addThingDescription", {
|
||||||
thingName: thing_name,
|
thingName: thing_name,
|
||||||
thingDescription: td
|
thingDescription: td,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
async fetchThingDescriptions({ commit }, uri) {
|
async fetchThingDescriptions({ commit }, uri) {
|
||||||
|
|
@ -47,10 +47,10 @@ export const wotStoreModule = {
|
||||||
let thing_name = k.replace(/\/$/, "").replace(/^\//, "");
|
let thing_name = k.replace(/\/$/, "").replace(/^\//, "");
|
||||||
commit("addThingDescription", {
|
commit("addThingDescription", {
|
||||||
thingName: thing_name,
|
thingName: thing_name,
|
||||||
thingDescription: response.data[k]
|
thingDescription: response.data[k],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
thingDescriptions: state => {
|
thingDescriptions: state => {
|
||||||
|
|
@ -62,13 +62,7 @@ export const wotStoreModule = {
|
||||||
thingAvailable: state => thingName => {
|
thingAvailable: state => thingName => {
|
||||||
return thingName in state.thingDescriptions;
|
return thingName in state.thingDescriptions;
|
||||||
},
|
},
|
||||||
thingFormUrl: state => (
|
thingFormUrl: state => (thing, affordanceType, affordance, op, allowUndefined = true) => {
|
||||||
thing,
|
|
||||||
affordanceType,
|
|
||||||
affordance,
|
|
||||||
op,
|
|
||||||
allowUndefined = true
|
|
||||||
) => {
|
|
||||||
// Find the URL for a particular operation
|
// Find the URL for a particular operation
|
||||||
let td = state.thingDescriptions[thing];
|
let td = state.thingDescriptions[thing];
|
||||||
if (!td) {
|
if (!td) {
|
||||||
|
|
@ -91,31 +85,15 @@ export const wotStoreModule = {
|
||||||
}
|
}
|
||||||
return href;
|
return href;
|
||||||
},
|
},
|
||||||
thingPropertyUrl: (_state, getters) => (
|
thingPropertyUrl: (_state, getters) => (thing, property, op, allowUndefined) => {
|
||||||
thing,
|
|
||||||
property,
|
|
||||||
op,
|
|
||||||
allowUndefined
|
|
||||||
) => {
|
|
||||||
// Find the URL for a particular property
|
// Find the URL for a particular property
|
||||||
return getters.thingFormUrl(
|
return getters.thingFormUrl(thing, "properties", property, op, allowUndefined);
|
||||||
thing,
|
|
||||||
"properties",
|
|
||||||
property,
|
|
||||||
op,
|
|
||||||
allowUndefined
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
thingActionUrl: (_state, getters) => (
|
thingActionUrl: (_state, getters) => (thing, action, op, allowUndefined) => {
|
||||||
thing,
|
|
||||||
action,
|
|
||||||
op,
|
|
||||||
allowUndefined
|
|
||||||
) => {
|
|
||||||
// Find the URL for a particular action
|
// Find the URL for a particular action
|
||||||
return getters.thingFormUrl(thing, "actions", action, op, allowUndefined);
|
return getters.thingFormUrl(thing, "actions", action, op, allowUndefined);
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export function findFormHref(affordance, op) {
|
export function findFormHref(affordance, op) {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
outputDir: "../src/openflexure_microscope_server/static"
|
outputDir: "../src/openflexure_microscope_server/static",
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue