UNPKG

node-red-contrib-iiot-opcua

Version:

An Industrial IoT OPC UA toolbox contribution package for Node-RED based on node-opcua.

326 lines (323 loc) 16.3 kB
/* The BSD 3-Clause License Copyright 2022 - DATATRONiQ GmbH (https://datatroniq.com) Copyright (c) 2018-2022 Klaus Landsdorf (http://node-red.plus/) Copyright 2015,2016 - Mika Karaila, Valmet Automation Inc. (node-red-contrib-opcua) All rights reserved. node-red-contrib-iiot-opcua */ 'use strict'; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const opcua_iiot_core_browser_1 = __importDefault(require("./core/opcua-iiot-core-browser")); const opcua_iiot_core_1 = require("./core/opcua-iiot-core"); /** * Browser Node-RED node. * * @param RED */ module.exports = function (RED) { // SOURCE-MAP-REQUIRED function OPCUAIIoTBrowser(config) { RED.nodes.createNode(this, config); this.nodeId = config.nodeId; this.name = config.name; this.justValue = config.justValue; this.sendNodesToRead = config.sendNodesToRead; this.sendNodesToBrowser = config.sendNodesToBrowser; this.sendNodesToListener = config.sendNodesToListener; this.multipleOutputs = config.multipleOutputs; this.showStatusActivities = config.showStatusActivities; this.showErrors = config.showErrors; this.recursiveBrowse = config.recursiveBrowse; this.recursiveDepth = config.recursiveDepth || 1; this.delayPerMessage = config.delayPerMessage || 0.2; this.connector = RED.nodes.getNode(config.connector); let self = this; const { iiot, browseTopic } = opcua_iiot_core_browser_1.default.initBrowserNode(); self.browseTopic = browseTopic; self.iiot = iiot; self.iiot.delayMessageTimer = []; const extractDataFromBrowserResults = (browserResultToFilter, lists) => { lists.addressItemList = []; browserResultToFilter.forEach((result) => { var _a; (_a = result.references) === null || _a === void 0 ? void 0 : _a.forEach((reference) => { opcua_iiot_core_browser_1.default.detailDebugLog('Add Reference To List :' + reference); lists.browserResults.push(opcua_iiot_core_browser_1.default.transformToEntry(reference)); if (reference.nodeId) { lists.nodesToRead.push(reference.nodeId.toString()); lists.addressItemList.push({ nodeId: reference.nodeId.toString(), browseName: reference.browseName.toString(), displayName: reference.displayName.toString(), nodeClass: reference.nodeClass.toString(), datatypeName: reference.typeDefinition.toString() }); } }); }); lists.addressSpaceItemList = lists.addressSpaceItemList.concat(lists.addressItemList); }; const browse = function (rootNodeId, msg, depth, lists, callback) { if ((0, opcua_iiot_core_1.checkSessionNotValid)(self.connector.iiot.opcuaSession, 'Browse')) { return; } opcua_iiot_core_browser_1.default.internalDebugLog('Browse Topic To Call Browse ' + rootNodeId); let rootNode = 'list'; opcua_iiot_core_browser_1.default.browse(self.connector.iiot.opcuaSession, rootNodeId) .then((browserResults) => { if (browserResults.length) { opcua_iiot_core_browser_1.default.detailDebugLog('Browser Result To String: ' + browserResults.toString()); extractDataFromBrowserResults(browserResults, lists); if (self.recursiveBrowse) { if (depth > 0) { let newDepth = depth - 1; let subLists = createListsObject(); if (self.multipleOutputs) { callback(rootNode, depth, msg, lists); } else { subLists = lists; browseNodeList(lists.addressItemList, msg, newDepth, subLists, callback); } } else { opcua_iiot_core_browser_1.default.internalDebugLog('Minimum Depth Reached On Browse At ' + rootNodeId); callback(rootNodeId, depth, msg, lists); } } else { callback(rootNodeId, depth, msg, lists); } } else { opcua_iiot_core_browser_1.default.internalDebugLog('No Browse Results On ' + rootNodeId); } }).catch(function (err) { opcua_iiot_core_browser_1.default.browseErrorHandling(self, err, msg, lists, callError, statusHandler); }); }; const callError = (err, msg) => { this.error(err, msg); }; const createListsObject = () => { return { nodesToBrowse: [], nodesToRead: [], addressItemList: [], addressSpaceItemList: [], browserResults: [] }; }; const browseNodeList = (addressSpaceItems, msg, depth, lists, callback) => { if ((0, opcua_iiot_core_1.checkSessionNotValid)(self.connector.iiot.opcuaSession, 'BrowseList')) { return; } opcua_iiot_core_browser_1.default.internalDebugLog('Browse For NodeId List'); let rootNode = 'list'; if (self.connector.iiot.opcuaSession) { opcua_iiot_core_browser_1.default.browseAddressSpaceItems(self.connector.iiot.opcuaSession, addressSpaceItems) .then((browserResults) => { opcua_iiot_core_browser_1.default.detailDebugLog('List Browser Result To String: ' + browserResults.toString()); extractDataFromBrowserResults(browserResults, lists); if (self.recursiveBrowse) { if (depth > 0) { let newDepth = depth - 1; let subLists = createListsObject(); if (self.multipleOutputs) { callback(rootNode, depth, msg, lists); } else { subLists = lists; } browseNodeList(lists.addressItemList, msg, newDepth, subLists, callback); } else { opcua_iiot_core_browser_1.default.internalDebugLog('Minimum Depth Reached On Browse List'); callback(rootNode, depth, msg, lists); } } else { callback(rootNode, depth, msg, lists); } }).catch(function (err) { opcua_iiot_core_browser_1.default.browseErrorHandling(self, err, msg, lists, callError, statusHandler); }); } }; const sendMessage = (rootNodeId, depth, originMessage, lists) => { if (!lists) { opcua_iiot_core_browser_1.default.internalDebugLog('Lists Not Valid!'); if (self.showErrors) { this.error(new Error('Lists Not Valid On Browse Send Message'), originMessage); } } const listenerParameters = getListenParameters(originMessage.payload); const payload = Object.assign(Object.assign(Object.assign(Object.assign({}, originMessage.payload), { nodetype: 'browse', justValue: self.justValue, rootNodeId, // @ts-ignore because TS is misunderstanding lists.brwoserResults type browserResults: lists.browserResults, recursiveBrowse: self.recursiveBrowse, recursiveDepth: depth, recursiveDepthMax: self.recursiveDepth, listenerParameters }), enhanceMessage(lists)), setMessageLists(lists)); const newMessaage = Object.assign(Object.assign({}, originMessage), { payload }); self.iiot.messageList.push(newMessaage); if (self.showStatusActivities && self.oldStatusParameter.text !== 'active') { self.oldStatusParameter = (0, opcua_iiot_core_1.setNodeStatusTo)(self, 'active', self.oldStatusParameter, self.showStatusActivities, statusHandler); } self.iiot.delayMessageTimer.push(setTimeout(() => { this.send(self.iiot.messageList.shift()); }, self.delayPerMessage * opcua_iiot_core_1.FAKTOR_SEC_TO_MSEC)); }; const resetAllTimer = function () { self.iiot.delayMessageTimer.forEach((timerId) => { if (timerId) clearTimeout(timerId); timerId = null; }); }; const getListenParameters = (payload) => { if ((payload === null || payload === void 0 ? void 0 : payload.injectType) === 'listen') { return payload; } else { return undefined; } }; const enhanceMessage = (lists) => { var _a; if (self.justValue) return { browserResults: lists.addressSpaceItemList }; return { browseTopic: self.browseTopic, browserResultsCount: lists.browserResults.length, endpoint: (_a = self.connector) === null || _a === void 0 ? void 0 : _a.endpoint, session: (self.connector.iiot.opcuaSession) ? self.connector.iiot.opcuaSession.name : 'none' }; }; const setMessageLists = (lists) => { return { nodesToRead: lists.nodesToRead.length ? lists.nodesToRead : lists.browserResults, nodesToReadCount: lists.nodesToRead.length || lists.browserResults.length, addressSpaceItemList: lists.addressSpaceItemList, addressSpaceItemListCount: lists.addressSpaceItemList.length, addressItemsToBrowse: lists.addressSpaceItemList, addressItemsToBrowseCount: lists.addressSpaceItemList.length, }; }; const browseSendResult = function (rootNodeId, depth, msg, lists) { opcua_iiot_core_browser_1.default.internalDebugLog(rootNodeId + ' called by depth ' + depth); if (self.multipleOutputs) { if (depth <= 0) { sendMessage(rootNodeId, depth, msg, lists); reset(lists); } } else { sendMessage(rootNodeId, depth, msg, lists); reset(lists); } }; const reset = (lists) => { lists = createListsObject(); }; const browseWithAddressSpaceItems = function (msg, depth, lists) { const payload = msg.payload; // Todo: a browse can overload addressSpaceItems, maybe this have to get more clear code and flow // Yes, it should get addressSpaceItems to browse from and should send addressSpaceItems as the result from the browse later // A good point could it be to use terms like addressSpaceItemsInput and addressSpaceItemsOutput if (payload.addressItemsToBrowse && payload.addressItemsToBrowse.length > 0) { payload.addressSpaceItems = payload.addressItemsToBrowse; } if (payload.addressSpaceItems && payload.addressSpaceItems.length > 0) { browseNodeList(payload.addressSpaceItems, msg, depth, lists, browseSendResult); } else { opcua_iiot_core_browser_1.default.detailDebugLog('Fallback NodeId On Browse Without AddressSpace Items'); self.browseTopic = self.nodeId || opcua_iiot_core_browser_1.default.browseToRoot(); browse(self.browseTopic, msg, depth, lists, browseSendResult); } }; const startBrowser = (msg) => { if ((0, opcua_iiot_core_1.checkSessionNotValid)(self.connector.iiot.opcuaSession, 'Browser')) { this.status({ fill: 'red', shape: 'ring', text: 'invalid connector session' }); return; } let lists = createListsObject(); let depth = (self.recursiveBrowse) ? self.recursiveDepth : 0; self.browseTopic = opcua_iiot_core_browser_1.default.extractNodeIdFromTopic(msg.payload, self); // set topic to the node object for HTTP requests at node if (self.browseTopic && self.browseTopic !== '') { browse(self.browseTopic, msg, depth, lists, browseSendResult); } else { browseWithAddressSpaceItems(msg, depth, lists); } }; this.on('input', (msg) => { if (!(0, opcua_iiot_core_1.checkConnectorState)(self, msg, 'Browser', errorHandler, emitHandler, statusHandler)) { return; } if (self.showStatusActivities) { self.oldStatusParameter = (0, opcua_iiot_core_1.setNodeStatusTo)(self, 'browsing', self.oldStatusParameter, self.showStatusActivities, statusHandler); } startBrowser(msg); }); const emitHandler = (msg) => { this.emit(msg); }; const errorHandler = (err, msg) => { this.error(err, msg); }; const statusHandler = (status) => { this.status(status); }; const onAlias = (event, callback) => { // @ts-ignore this.on(event, callback); }; (0, opcua_iiot_core_1.registerToConnector)(self, statusHandler, onAlias, errorHandler); this.on('close', (done) => { self.removeAllListeners(); (0, opcua_iiot_core_1.deregisterToConnector)(self, () => { (0, opcua_iiot_core_1.resetIiotNode)(self); done(); }); }); } RED.nodes.registerType('OPCUA-IIoT-Browser', OPCUAIIoTBrowser); RED.httpAdmin.get('/opcuaIIoT/browse/:id/:nodeId', RED.auth.needsPermission('opcuaIIoT.browse'), function (req, res) { var _a; let node = RED.nodes.getNode(req.params.id); let entries = []; let nodeRootId = decodeURIComponent(req.params.nodeId) || opcua_iiot_core_1.OBJECTS_ROOT; opcua_iiot_core_browser_1.default.detailDebugLog('request for ' + req.params.nodeId); if ((_a = node.iiot) === null || _a === void 0 ? void 0 : _a.opcuaSession) { opcua_iiot_core_browser_1.default.browse(node.iiot.opcuaSession, nodeRootId).then((browserResult) => { browserResult.forEach((result) => { if (result.references && result.references.length) { result.references.forEach((reference) => { entries.push(opcua_iiot_core_browser_1.default.transformToEntry(reference)); }); } }); res.json(entries); }).catch(function (err) { opcua_iiot_core_browser_1.default.internalDebugLog('Browser Error ' + err); if (node.showErrors) { node.error(err, { payload: 'Browse Internal Error' }); } entries.push({ displayName: { text: 'Objects' }.toString(), nodeId: opcua_iiot_core_1.OBJECTS_ROOT, browseName: 'Objects' }); res.json(entries); }); } else { res.json(entries); } }); }; //# sourceMappingURL=opcua-iiot-browser.js.map