openflexure-microscope-server/webapp/src/mixins/labThingsMixins.js

154 lines
4.8 KiB
JavaScript

/**
* @fileoverview
* Global mixin providing access to the LabThings (WoT) API.
*
* This mixin is registered globally in `main.js` using. Do not
* manually import it in components.
*/
import axios from "axios";
import { useWotStore } from "@/stores/wot.js";
import { useSettingsStore } from "@/stores/settings.js";
import { mapState, mapStores } from "pinia";
export default {
data() {
return {
pollTimers: {},
};
},
computed: {
...mapState(useSettingsStore, ["baseUri"]),
...mapStores(useWotStore),
},
methods: {
thingDescription(thing) {
return this.wotStore.thingDescriptions[thing];
},
thingList() {
return this.wotStore.thingList();
},
thingAvailable(thing) {
return this.wotStore.thingAvailable(thing);
},
thingPropertyUrl(thing, property, allowUndefined = false) {
return this.wotStore.thingPropertyUrl(thing, property, "readproperty", allowUndefined);
},
thingActionUrl(thing, action, allowUndefined = false) {
return this.wotStore.thingActionUrl(thing, action, "invokeaction", allowUndefined);
},
thingActionAvailable(thing, action) {
return this.wotStore.thingAffordanceAvailable(thing, "actions", action);
},
thingPropertyAvailable(thing, property) {
return this.wotStore.thingAffordanceAvailable(thing, "properties", property);
},
async readThingProperty(thing, property, silenceErrors = false) {
let url = this.wotStore.thingPropertyUrl(thing, property, "readproperty", false);
try {
let response = await axios.get(url);
return response.data;
} catch (error) {
if (!silenceErrors) this.modalError(error);
return undefined;
}
},
async writeThingProperty(thing, property, value) {
let url = this.wotStore.thingPropertyUrl(thing, property, "writeproperty", false);
// `false` and 0 fail because axios turns it to ""
// Other values should not be stringified or pydantic
// can't parse them.
if (value === false || value === true || value === 0) {
value = JSON.stringify(value);
}
await axios.put(url, value);
},
async invokeAction(thing, action, data, handleErrors = true) {
let url = this.thingActionUrl(thing, action);
try {
let response = await axios.post(url, data);
return response;
} catch (error) {
if (handleErrors) {
this.modalError(error);
return undefined;
} else {
throw error;
}
}
},
async pollUntilComplete(
taskUrl,
ongoingMethod,
finalMethod,
interval = 500,
modalErrors = true,
) {
let response;
let finalMethodCalled = false;
try {
response = await axios.get(taskUrl, { baseURL: this.baseUri });
const result = response.data.status;
if (result === "running" || result === "pending") {
ongoingMethod?.(response);
this.pollTimers[taskUrl] = setTimeout(() => {
this.pollUntilComplete(taskUrl, ongoingMethod, finalMethod, interval);
}, interval);
} else {
clearTimeout(this.pollTimers[taskUrl]);
delete this.pollTimers[taskUrl];
finalMethodCalled = true;
finalMethod?.(response);
}
} catch (error) {
this.$emit("error", error);
if (modalErrors) {
this.modalError(error);
}
clearTimeout(this.pollTimers[taskUrl]);
delete this.pollTimers[taskUrl];
if (!finalMethodCalled) {
finalMethod?.(response);
}
}
},
terminateAction(taskUrl) {
axios.delete(taskUrl, { baseURL: this.baseUri });
},
async findOngoingActions(thing, action) {
let url = this.thingActionUrl(thing, action);
try {
return await axios.get(url);
} catch (error) {
console.warn("checkExistingTasks: request failed", error);
return null;
}
},
/**
* Find and ongoing Action and return its task response.
*
* Returns null if there is no ongoing action
*/
async getOngoingAction(thing, action) {
let response = await this.findOngoingActions(thing, action);
// Exit if response is null, due to an error.
if (response == null) return null;
// Check for a task that is ongoing.
// We can't handle multiple tasks ongoing, so this picks the first.
// ?? Is the "Nullish Coalescing-Operator" to turn undefined into null.
return response.data.find((t) => ["pending", "running"].includes(t.status)) ?? null;
},
async getThingEndpoint(thing, endpoint) {
let url = `${this.baseUri}/${thing}/${endpoint}`;
try {
const response = await axios.get(url);
return response.data;
} catch {
return undefined;
}
},
},
};