UNPKG

@thingweb/node-red-node-wot

Version:
302 lines (301 loc) 15 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const servient_manager_1 = __importDefault(require("./servients/servient-manager")); module.exports = function (RED) { function WoTServerConfig(config) { RED.nodes.createNode(this, config); const node = this; const userNodes = []; // Delete thingDescriptions in globalContext (to avoid having them remain if the wot-server-config node is deleted) // Deleting them here may cause the deletion of thingsDescriptions added to globalContext that should not be deleted, due to timing. node.context().global.set("thingDescriptions", {}); const servientManager = servient_manager_1.default.getInstance(); node.running = false; node.addUserNode = (n) => { n.setServientStatus(node.running); const foundUserNodes = userNodes.filter((userNode) => userNode.id === n.id); if (foundUserNodes.length === 0) { userNodes.push(n); } }; function getSecurityDefinition(scheme) { let params; if (scheme === "basic") { params = { scheme, in: "header" }; } else { params = { scheme }; } return params; } function waitForFinishPrepareRelatedNodes(userNodes, userNodeIds) { return __awaiter(this, void 0, void 0, function* () { const MAX_CHECK_COUNT = 50; const WAIT_MILLI_SEC = 100; //ms for (let i = 0; i < MAX_CHECK_COUNT; i++) { // Confirm that all user nodes have been added let prepareAllNodesFlg = true; for (const id of userNodeIds) { let foundFlg = false; for (const node of userNodes) { if (node.id === id) { foundFlg = true; } } if (!foundFlg) { // Returns false if no matching user node exists prepareAllNodesFlg = false; break; } } if (prepareAllNodesFlg) { // End because all nodes have been added return; } // wait yield ((sec) => { return new Promise((resolve, reject) => { setTimeout(() => { resolve(); }, sec); }); })(WAIT_MILLI_SEC); } throw new Error("Not enough WoT Nodes settings."); }); } function registerPropertiesProcess(userNode, thing, props) { return __awaiter(this, void 0, void 0, function* () { thing.setPropertyReadHandler(props.name, () => { return new Promise((resolve, reject) => { const finish = (payload) => { resolve(payload); }; let msg = { _wot: { finish }, }; userNode.send([msg, null]); }); }); if (!props.content.readOnly) { thing.setPropertyWriteHandler(props.name, (value) => __awaiter(this, void 0, void 0, function* () { const v = yield value.value(); return new Promise((resolve, reject) => { const finish = (payload) => { if (props.content.observable) { thing .emitPropertyChange(props.name) .then(() => { resolve(); }) .catch((err) => { node.error(`[error] emit property change error. error: ${err.toString()}`); console.error(`[error] emit property change error. error: `, err); reject(err); }); } else { resolve(); } }; let msg = {}; setOutput("msg", props.outputAttr, msg, node.context(), v); msg["_wot"] = { finish }; userNode.send([null, msg]); }); })); } }); } function registerActionsProcess(userNode, thing, props) { return __awaiter(this, void 0, void 0, function* () { thing.setActionHandler(props.name, (params) => __awaiter(this, void 0, void 0, function* () { const args = yield params.value(); return new Promise((resolve, reject) => { const finish = (payload) => { resolve(payload); }; let msg = {}; setOutput("msg", props.outputArgs, msg, node.context(), args); msg["_wot"] = { finish }; userNode.send(msg); }); })); }); } function createWoTScriptAndExpose(thingProps, servientWrapper, userNodes) { return __awaiter(this, void 0, void 0, function* () { // create TD let td = Object.assign({}, thingProps); for (const userNode of userNodes) { const props = userNode.getProps(); td[props.attrType] = Object.assign(Object.assign({}, td[props.attrType]), { [props.name]: props.content }); } const thing = yield servientWrapper.createThing(td); // get elements of TD from each node for (const userNode of userNodes) { const props = userNode.getProps(); if (!props.name) { node.warn(`[warn] Not enough settings for td. props.name not specified.`); console.warn("[warn] Not enough settings for td. props: ", props); continue; } if (props.attrType === "properties") { registerPropertiesProcess(userNode, thing, props); } else if (props.attrType === "actions") { registerActionsProcess(userNode, thing, props); } else if (props.attrType === "events") { // Nothing to do } } const thingDescription = yield servientWrapper.exposeThing(thing); const thingDescriptions = node.context().global.get("thingDescriptions") || {}; thingDescriptions[`${config.name}::${thingProps.title}`] = thingDescription; node.context().global.set("thingDescriptions", thingDescriptions); console.debug(`[info] servient started. ${config.name}::${thingProps.title}`); }); } function launchServient() { var _a; return __awaiter(this, void 0, void 0, function* () { if (config.bindingConfigConstValue && config.bindingConfigType) { node.bindingConfig = RED.util.evaluateNodeProperty(config.bindingConfigConstValue, config.bindingConfigType, node); } // create thing const bindingType = config.bindingType; const bindingConfig = node.bindingConfig; try { yield waitForFinishPrepareRelatedNodes(userNodes, config._users); // make thing title list and security definitions const securityDefinitions = []; const thingTitles = []; for (const userNode of userNodes) { if (userNode.type === "wot-server-td") { continue; } let thingNode = userNode.getThingNode(); if (!thingNode) { continue; } let title = (_a = thingNode.getProps()) === null || _a === void 0 ? void 0 : _a.title; if (title && !thingTitles.includes(title)) { thingTitles.push(title); // make security definitions for server let secDef = getSecurityDefinition(thingNode.getSecurityScheme()); if (secDef.scheme !== "nosec") { securityDefinitions.push(secDef); } } } // merge security params to bindingConfig bindingConfig["security"] = securityDefinitions; console.debug("[debug] createServient ", node.id, bindingType, bindingConfig); const servientWrapper = servientManager.createServientWrapper(node.id, bindingType, bindingConfig); yield servientWrapper.startServient(); // Generate and Expose a Thing for each Thing title for (const thingTitle of thingTitles) { const targetNodes = userNodes.filter((n) => n.type !== "wot-server-td" && n.getThingNode().getProps().title === thingTitle); if (targetNodes.length > 0) { const thingNode = targetNodes[0].getThingNode(); const thingProps = thingNode.getProps() || {}; // add security definition to thingProps const secScheme = thingNode.getSecurityScheme(); if (secScheme !== "nosec") { thingProps["securityDefinitions"] = { sc: getSecurityDefinition(secScheme), }; thingProps["security"] = ["sc"]; } yield createWoTScriptAndExpose(thingProps, servientWrapper, targetNodes); servientWrapper.addCredentials(thingProps.title, thingNode.getCredentials()); } } node.running = true; userNodes.forEach((n) => { if (n.type === "wot-server-td" && n.getOutputTDAfterServerStartFlag() === true) { // send trigger to wot-server-td node for getting TD n.receive({}); } n.setServientStatus(node.running); }); } catch (err) { throw err; } }); } console.debug("[debug] launch servient. node.id: ", node.id); launchServient() .then(() => { node.debug("[info] success to launch thing. name: " + config.name + " id: " + config.id); }) .catch((err) => { node.error("[error] Failed to launch thing. name: " + config.name + " id: " + config.id + " err:" + err); console.error("[error] Failed to launch thing. name: " + config.name + " id: " + config.id + " err: ", err); }); node.on("close", function (removed, done) { if (servientManager.existServienetWrapper(node.id)) { // Exit if already servient. console.debug("[debug] endServient. node.id: " + node.id); servientManager .removeServientWrapper(node.id) .then(() => { // end servient console.debug("[debug] servient ended. config.id: " + config.id); done(); }) .catch((err) => { node.error("[error] failed to remove server. name: " + config.name + " id: " + config.id + " err: " + err); console.error("[error] failed to remove server. name: " + config.name + " id: " + config.id + " err: ", err); done(err); }); } }); } RED.nodes.registerType("wot-server-config", WoTServerConfig); const setOutput = (type, valueName, msg, context, value) => { if (type === "msg") { const names = valueName.split("."); let target = msg; for (let i = 0; i < names.length - 1; i++) { let n = names[i]; if (target[n] && target[n] instanceof Object) { target = target[n]; } else { target[n] = {}; target = target[n]; } } target[names[names.length - 1]] = value; } else if (type === "node") { context.set(valueName, value); } else if (type === "flow") { context.flow.set(valueName, value); } else if (type === "global") { context.global.set(valueName, value); } }; };