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
JavaScript
/*
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
*/
;
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