Removed node-wot but kept centralised TD store
I had intended to store a ConsumedThing for each Thing, in a module in the store. Until that is possible (will require some more attention to dependency management) I will just store the TDs instead. This commit includes a lot of the code I wrote for node-wot use, but without any dependence on node-wot.
This commit is contained in:
parent
45d2884159
commit
595101248e
6 changed files with 265 additions and 16 deletions
|
|
@ -305,22 +305,22 @@ export default {
|
|||
},
|
||||
|
||||
methods: {
|
||||
checkConnection: function() {
|
||||
async checkConnection() {
|
||||
var baseUri = this.$store.getters.baseUri;
|
||||
this.$store.commit("changeWaiting", true);
|
||||
axios
|
||||
// TODO: more robust check - e.g. use a microscope Thing
|
||||
.get(`${baseUri}/stage/`)
|
||||
.then(() => {
|
||||
this.$store.commit("setConnected");
|
||||
this.$store.commit("setErrorMessage", null);
|
||||
})
|
||||
.catch(error => {
|
||||
this.$store.commit("setErrorMessage", error);
|
||||
})
|
||||
.finally(() => {
|
||||
this.$store.commit("changeWaiting", false);
|
||||
});
|
||||
// TODO: more robust check - e.g. use a microscope Thing
|
||||
// TODO: should we purge existing consumedThings?
|
||||
try {
|
||||
await axios.get(`${baseUri}/things/`);
|
||||
await this.getThingDescription("stage");
|
||||
await this.getThingDescription("camera");
|
||||
this.$store.commit("setConnected");
|
||||
this.$store.commit("setErrorMessage", null);
|
||||
} catch (error) {
|
||||
this.$store.commit("setErrorMessage", error);
|
||||
} finally {
|
||||
this.$store.commit("changeWaiting", false);
|
||||
}
|
||||
},
|
||||
|
||||
handleExit: function() {
|
||||
|
|
|
|||
200
webapp/src/components/labThingsComponents/propertyControl.vue
Normal file
200
webapp/src/components/labThingsComponents/propertyControl.vue
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
<template>
|
||||
<div>
|
||||
<label class="uk-form-label">{{ label }}</label>
|
||||
<div v-if="dataType == 'number'" class="input-and-buttons-container">
|
||||
<input
|
||||
v-model="value"
|
||||
class="uk-form-small numeric-setting-line-input"
|
||||
type="number"
|
||||
@focusin="focusIn"
|
||||
@focusout="focusOut"
|
||||
@keydown="keyDown"
|
||||
/>
|
||||
<a class="button-next-to-input" @click="readProperty">
|
||||
<i class="material-icons">refresh</i>
|
||||
</a>
|
||||
</div>
|
||||
<div v-if="dataType == 'number_array'" class="input-and-buttons-container">
|
||||
<input
|
||||
v-for="i in value.length"
|
||||
:key="i"
|
||||
v-model="value[i - 1]"
|
||||
class="uk-form-small numeric-setting-line-input"
|
||||
type="number"
|
||||
@focusin="focusIn"
|
||||
@focusout="focusOut"
|
||||
@keydown="keyDown"
|
||||
/>
|
||||
<a class="button-next-to-input" @click="readProperty">
|
||||
<i class="material-icons">refresh</i>
|
||||
</a>
|
||||
</div>
|
||||
<div v-if="dataType == 'number_object'" class="input-and-buttons-container">
|
||||
<input
|
||||
v-for="(_v, key) in value"
|
||||
:key="key"
|
||||
v-model="value[key]"
|
||||
class="uk-form-small numeric-setting-line-input"
|
||||
type="number"
|
||||
@focusin="focusIn"
|
||||
@focusout="focusOut"
|
||||
@keydown="keyDown"
|
||||
/>
|
||||
<a class="button-next-to-input" @click="readProperty">
|
||||
<i class="material-icons">refresh</i>
|
||||
</a>
|
||||
</div>
|
||||
<div v-else class="input-and-buttons-container">
|
||||
<input
|
||||
:value="value"
|
||||
class="uk-form-small numeric-setting-line-input"
|
||||
type="text"
|
||||
disabled="true"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from "axios";
|
||||
|
||||
export default {
|
||||
name: "PropertyControl",
|
||||
|
||||
props: {
|
||||
propertyName: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
thingDescription: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
readBackDelay: {
|
||||
type: Number,
|
||||
default: undefined,
|
||||
required: false
|
||||
}
|
||||
},
|
||||
|
||||
data: () => {
|
||||
return {
|
||||
value: {},
|
||||
valueOnEnter: undefined,
|
||||
focused: false
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
readBack: function() {
|
||||
return this.readBackDelay !== undefined;
|
||||
},
|
||||
propertyDescription: function() {
|
||||
return this.thingDescription.properties[this.propertyName];
|
||||
},
|
||||
dataType: function() {
|
||||
let prop = this.propertyDescription;
|
||||
const num_types = ["integer", "float", "number"];
|
||||
if (num_types.includes(prop.type)) {
|
||||
return "number";
|
||||
}
|
||||
console.log("Prop type", prop.type, "is not in", num_types);
|
||||
if (prop.type == "array") {
|
||||
if (num_types.includes(prop.items)) {
|
||||
return "number_array";
|
||||
}
|
||||
if (Array.isArray(prop.items)) {
|
||||
if (prop.items.every(t => num_types.includes(t))) {
|
||||
return "number_array";
|
||||
}
|
||||
}
|
||||
}
|
||||
if (prop.type == "object") {
|
||||
let numeric = true;
|
||||
for (let key in prop.properties) {
|
||||
if (!num_types.includes(prop.properties[key].type)) {
|
||||
numeric = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (numeric) {
|
||||
return "number_object";
|
||||
}
|
||||
}
|
||||
return "other";
|
||||
}
|
||||
},
|
||||
|
||||
mounted: function() {
|
||||
this.readProperty();
|
||||
},
|
||||
|
||||
methods: {
|
||||
readProperty: async function() {
|
||||
let response = await axios.get(this.propertyUrl);
|
||||
this.value = response.data;
|
||||
console.log("Read property", this.propertyUrl, response.data);
|
||||
return response.data;
|
||||
},
|
||||
writeProperty: async function() {
|
||||
try {
|
||||
let requestedValue = this.value;
|
||||
await axios.post(this.propertyUrl, requestedValue);
|
||||
if (this.readBack) {
|
||||
await new Promise(r => setTimeout(r, this.readBackDelay));
|
||||
let newVal = await this.readProperty();
|
||||
if (newVal == requestedValue) {
|
||||
await this.modalNotify(`Set ${this.label} to ${newVal}.`);
|
||||
} else {
|
||||
await this.modalNotify(
|
||||
`Set ${this.label} to ${newVal} (requested ${requestedValue}).`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
await this.modalNotify(`Set ${this.label} to ${this.value}.`);
|
||||
}
|
||||
} catch (error) {
|
||||
this.modalError(error); // Let mixin handle error
|
||||
}
|
||||
},
|
||||
focusIn: function(event) {
|
||||
this.valueOnEnter = event.target.value;
|
||||
},
|
||||
focusOut: function(event) {
|
||||
if (this.valueOnEnter != event.target.value) {
|
||||
this.writeProperty(event.target.value);
|
||||
}
|
||||
},
|
||||
keyDown: function(event) {
|
||||
// Pressing enter should set the property, whether or not we think it's changed.
|
||||
if (event.keyCode == 13) {
|
||||
this.writeProperty();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.input-and-buttons-container {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
justify-content: flex-start;
|
||||
align-content: stretch;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
.numeric-setting-line-input {
|
||||
flex-grow: 1;
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
width: 6em;
|
||||
}
|
||||
.button-next-to-input {
|
||||
flex-grow: 0;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -51,7 +51,7 @@ export default {
|
|||
return {
|
||||
settings: null,
|
||||
actions: {},
|
||||
properties: {},
|
||||
properties: {}
|
||||
};
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -34,6 +34,16 @@ Vue.config.productionTip = false;
|
|||
|
||||
Vue.mixin({
|
||||
methods: {
|
||||
async getThingDescription(thing) {
|
||||
// Get the thing description for the given thing
|
||||
let origin = this.$store.state.origin;
|
||||
let uri = `${origin}/${thing}/`;
|
||||
// We cache TDs in the store, and fetch only if needed.
|
||||
if (!(uri in store.state.wot.thingDescriptions)) {
|
||||
await store.dispatch("wot/fetchThingDescription", uri);
|
||||
}
|
||||
return store.state.wot.thingDescriptions[uri];
|
||||
},
|
||||
modalConfirm: function(modalText) {
|
||||
var context = this;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import Vue from "vue";
|
||||
import Vuex from "vuex";
|
||||
import wotStoreModule from "./wot-client";
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
||||
|
|
@ -76,7 +77,8 @@ function getOriginFromLocation() {
|
|||
|
||||
export default new Vuex.Store({
|
||||
modules: {
|
||||
imjoy: moduleImjoy
|
||||
imjoy: moduleImjoy,
|
||||
wot: wotStoreModule
|
||||
},
|
||||
state: {
|
||||
origin: getOriginFromLocation(),
|
||||
|
|
|
|||
37
webapp/src/wot-client.js
Normal file
37
webapp/src/wot-client.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
import axios from "axios";
|
||||
|
||||
export const wotStoreModule = {
|
||||
namespaced: true,
|
||||
state: () => ({
|
||||
thingDescriptions: {},
|
||||
servient: null,
|
||||
helpers: null
|
||||
}),
|
||||
mutations: {
|
||||
addThingDescription(state, { thingUri, thingDescription }) {
|
||||
state.thingDescriptions[thingUri] = thingDescription;
|
||||
},
|
||||
removeThingDescription(state, thingUri) {
|
||||
delete state.thingDescriptions[thingUri];
|
||||
},
|
||||
removeAllThingDescriptions(state) {
|
||||
state.thingDescriptions = {};
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
async start() {
|
||||
// Set up thing client - not currently used.
|
||||
},
|
||||
async fetchThingDescription({ commit }, uri) {
|
||||
// Fetch the thing description from the given URI and consume it
|
||||
// NB this should only be called once, or we'll duplicate effort.
|
||||
// Deduplication should be done elsewhere.
|
||||
let response = await axios.get(uri);
|
||||
let td = response.data;
|
||||
commit("addThingDescription", { thingUri: uri, thingDescription: td });
|
||||
}
|
||||
},
|
||||
getters: {}
|
||||
};
|
||||
|
||||
export default wotStoreModule;
|
||||
Loading…
Add table
Add a link
Reference in a new issue