node-red-contrib-iiot-opcua
Version:
An Industrial IoT OPC UA toolbox contribution package for Node-RED based on node-opcua.
1,141 lines (1,135 loc) • 47 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
*/
'use strict';
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.setNodeOPCUAConnected = exports.checkConnectorState = exports.isNodeId = exports.setNodeInitalState = exports.isSessionBad = exports.availableMemory = exports.buildNodeListFromClient = exports.buildNodesFromCrawler = exports.buildNodesFromBrowser = exports.buildNodesToListen = exports.buildNodesToRead = exports.buildNodesToWrite = exports.normalizeMessage = exports.createItemForWriteList = exports.newOPCUANodeIdFromMsgTopic = exports.newOPCUANodeIdFromItemNodeId = exports.parseIdentifierFromItemNodeId = exports.parseIdentifierFromMsgTopic = exports.parseForNodeIdentifier = exports.parseNamespaceFromItemNodeId = exports.parseNamespaceFromMsgTopic = exports.convertDataValueByDataType = exports.convertDataValue = exports.valueIsString = exports.getBasicDataTypes = exports.getVariantValue = exports.buildNewVariant = exports.getNodeStatus = exports.calcMillisecondsByTimeAndUnit = exports.getTimeUnitName = exports.getNodeOPCUAServerPath = exports.getNodeOPCUAClientPath = exports.getNodeOPCUAPath = exports.getPathFromRequireResolve = exports.regex_ns_g = exports.regex_ns_b = exports.regex_ns_s = exports.regex_ns_i = exports.DEFAULT_TIMEOUT = exports.FAKTOR_SEC_TO_MSEC = exports.isWindows = exports.RUNNING_STATE = exports.TEN_SECONDS_TIMEOUT = exports.OBJECTS_ROOT = exports.FsmListenerStates = exports.FsmConnectorStates = exports.nodeOPCUAId = exports.underscore = exports.os = exports.Debug = void 0;
exports.isInitializedIIoTNode = exports.isNodeTypeToFilterResponse = exports.filterListByNodeId = exports.filterListEntryByNodeId = exports.resetIiotNode = exports.checkItemForUnsetState = exports.checkResponseItemIsNotToFilter = exports.checkCrawlerItemIsNotToFilter = exports.handleErrorInsideNode = exports.getItemFilterValueWithElement = exports.initCoreServerNode = exports.initCoreNode = exports.setNodeStatusTo = exports.checkSessionNotValid = exports.deregisterToConnector = exports.registerToConnector = exports.setNodeOPCUARestart = exports.setNodeOPCUASessionError = exports.setNodeOPCUASessionRestart = exports.setNodeOPCUASessionClosed = exports.setNodeOPCUASessionStarted = exports.setNodeOPCUALost = exports.setNodeOPCUAClosed = void 0;
// SOURCE-MAP-REQUIRED
const debug_1 = require("debug");
Object.defineProperty(exports, "Debug", { enumerable: true, get: function () { return debug_1.debug; } });
const os = __importStar(require("os"));
exports.os = os;
const underscore = __importStar(require("underscore"));
exports.underscore = underscore;
const underscore_1 = __importStar(require("underscore"));
const nodeOPCUAId = __importStar(require("node-opcua-nodeid"));
exports.nodeOPCUAId = nodeOPCUAId;
const assertion_1 = require("../types/assertion");
const node_opcua_1 = require("node-opcua");
var FsmConnectorStates;
(function (FsmConnectorStates) {
FsmConnectorStates["StateIdle"] = "idle";
FsmConnectorStates["StateInit"] = "init";
FsmConnectorStates["StateOpened"] = "opened";
FsmConnectorStates["StateSessionRequested"] = "sessionRequested";
FsmConnectorStates["StateSessionActive"] = "sessionActive";
FsmConnectorStates["StateSessionClosed"] = "sessionClosed";
FsmConnectorStates["StateSessionRestart"] = "sessionRestart";
FsmConnectorStates["StateClosed"] = "closed";
FsmConnectorStates["StateLocked"] = "locked";
FsmConnectorStates["StateUnlocked"] = "unlocked";
FsmConnectorStates["StateStopped"] = "stopped";
FsmConnectorStates["StateEnd"] = "end";
FsmConnectorStates["StateReconfigured"] = "reconfigured";
FsmConnectorStates["StateRenewed"] = "renewed";
})(FsmConnectorStates = exports.FsmConnectorStates || (exports.FsmConnectorStates = {}));
var FsmListenerStates;
(function (FsmListenerStates) {
FsmListenerStates["StateIdle"] = "idle";
FsmListenerStates["StateInit"] = "init";
FsmListenerStates["StateEnd"] = "end";
FsmListenerStates["StateRequested"] = "requested";
FsmListenerStates["StateStarted"] = "started";
FsmListenerStates["StateError"] = "error";
FsmListenerStates["StateTerminated"] = "terminated";
})(FsmListenerStates = exports.FsmListenerStates || (exports.FsmListenerStates = {}));
exports.OBJECTS_ROOT = 'ns=0;i=84';
exports.TEN_SECONDS_TIMEOUT = 10;
exports.RUNNING_STATE = FsmConnectorStates.StateSessionActive;
exports.isWindows = /^win/.test(os.platform());
exports.FAKTOR_SEC_TO_MSEC = 1000;
exports.DEFAULT_TIMEOUT = 1000;
exports.regex_ns_i = /ns=([0-9]+);i=([0-9]+)/;
exports.regex_ns_s = /ns=([0-9]+);s=(.*)/;
exports.regex_ns_b = /ns=([0-9]+);b=(.*)/;
exports.regex_ns_g = /ns=([0-9]+);g=(.*)/;
var logger;
(function (logger) {
logger.internalDebugLog = (0, debug_1.debug)('opcuaIIoT:core');
logger.detailDebugLog = (0, debug_1.debug)('opcuaIIoT:core:details');
logger.specialDebugLog = (0, debug_1.debug)('opcuaIIoT:core:special');
})(logger || (logger = {}));
logger.internalDebugLog(os.endianness());
logger.internalDebugLog(os.hostname());
logger.internalDebugLog(os.platform());
logger.internalDebugLog(os.type());
logger.internalDebugLog(os.arch());
const getPathFromRequireResolve = (requireResolve) => {
let pathToNodeOPCUA = '';
if (exports.isWindows) {
pathToNodeOPCUA = requireResolve.replace('\\index.js', '');
}
else {
pathToNodeOPCUA = requireResolve.replace('/index.js', '');
}
logger.internalDebugLog('path to node-opcua: ' + pathToNodeOPCUA);
return pathToNodeOPCUA;
};
exports.getPathFromRequireResolve = getPathFromRequireResolve;
function getNodeOPCUAPath() {
return (0, exports.getPathFromRequireResolve)(require.resolve('node-opcua'));
}
exports.getNodeOPCUAPath = getNodeOPCUAPath;
function getNodeOPCUAClientPath() {
return (0, exports.getPathFromRequireResolve)(require.resolve('node-opcua-client'));
}
exports.getNodeOPCUAClientPath = getNodeOPCUAClientPath;
function getNodeOPCUAServerPath() {
return (0, exports.getPathFromRequireResolve)(require.resolve('node-opcua-server'));
}
exports.getNodeOPCUAServerPath = getNodeOPCUAServerPath;
function getTimeUnitName(unit) {
switch (unit) {
case 'ms':
return 'msec.';
case 's':
return 'sec.';
case 'm':
return 'min.';
case 'h':
return 'h.';
default:
return '';
}
}
exports.getTimeUnitName = getTimeUnitName;
function calcMillisecondsByTimeAndUnit(time, unit) {
switch (unit) {
case 'ms':
return time;
case 's':
return time * 1000; // seconds
case 'm':
return time * 60000; // minutes
case 'h':
return time * 3600000; // hours
default:
return 10000; // 10 sec.
}
}
exports.calcMillisecondsByTimeAndUnit = calcMillisecondsByTimeAndUnit;
function getNodeStatus(statusValue, statusLog) {
let fillValue = 'yellow';
let shapeValue = 'ring';
switch (statusValue) {
case 'initialize':
case 'connecting':
fillValue = 'yellow';
break;
case 'connected':
case 'keepalive':
case 'subscribe':
case 'started':
if (!statusLog) {
statusValue = 'active';
shapeValue = 'dot';
}
fillValue = 'green';
break;
case 'active':
case 'subscribed':
case 'listening':
fillValue = 'green';
shapeValue = 'dot';
break;
case 'disconnected':
case 'terminated':
fillValue = 'red';
break;
case 'waiting':
fillValue = 'blue';
shapeValue = 'dot';
statusValue = 'waiting ...';
break;
case 'error':
fillValue = 'red';
shapeValue = 'dot';
break;
default:
if (!statusValue) {
fillValue = 'blue';
statusValue = 'waiting ...';
}
}
return { fill: fillValue, shape: shapeValue, text: statusValue };
}
exports.getNodeStatus = getNodeStatus;
function extractValue(value) {
logger.detailDebugLog('extractValue value:' + value);
if (underscore_1.default.isUndefined(value.value) === false) {
return value.value; // value could be null, 0, and any other, but not undefined
}
return value;
}
function buildNewVariant(datatype, value) {
let variantValue = {
dataType: node_opcua_1.DataType.Null,
value: null
};
let originValue = Object.assign({}, value);
value = extractValue(value);
logger.detailDebugLog('buildNewVariant datatype: ' + datatype + ' originValue:' + originValue + ' value:' + value);
switch (datatype) {
case 'Float':
case node_opcua_1.DataType.Float:
variantValue = {
dataType: node_opcua_1.DataType.Float,
value: parseFloat(value)
};
break;
case 'Double':
case node_opcua_1.DataType.Double:
variantValue = {
dataType: node_opcua_1.DataType.Double,
value: parseFloat(value)
};
break;
case 'UInt16':
case node_opcua_1.DataType.UInt16:
let uint16 = new Uint16Array([value]);
variantValue = {
dataType: node_opcua_1.DataType.UInt16,
value: uint16[0]
};
break;
case 'UInt32':
case node_opcua_1.DataType.UInt32:
let uint32 = new Uint32Array([value]);
variantValue = {
dataType: node_opcua_1.DataType.UInt32,
value: uint32[0]
};
break;
case 'Int32':
case node_opcua_1.DataType.Int32:
variantValue = {
dataType: node_opcua_1.DataType.Int32,
value: parseInt(value)
};
break;
case 'Int16':
case node_opcua_1.DataType.Int16:
variantValue = {
dataType: node_opcua_1.DataType.Int16,
value: parseInt(value)
};
break;
case 'Int64':
case node_opcua_1.DataType.Int64:
variantValue = {
dataType: node_opcua_1.DataType.Int64,
value: parseInt(value)
};
break;
case 'Boolean':
case node_opcua_1.DataType.Boolean:
if (value && value !== 'false') {
variantValue = {
dataType: node_opcua_1.DataType.Boolean,
value: true
};
}
else {
variantValue = {
dataType: node_opcua_1.DataType.Boolean,
value: false
};
}
break;
case 'LocalizedText':
case node_opcua_1.DataType.LocalizedText:
variantValue = {
dataType: node_opcua_1.DataType.LocalizedText,
value: JSON.parse(value) /* [{text:'Hello', locale:'en'}, {text:'Hallo', locale:'de'} ... ] */
};
break;
case 'DateTime':
case node_opcua_1.DataType.DateTime:
variantValue = {
dataType: node_opcua_1.DataType.DateTime,
value: new Date(value)
};
break;
default:
if (datatype !== '') {
const datatypeList = getBasicDataTypes();
datatypeList.forEach((item) => {
if (item.name === datatype || item.dataType === datatype) {
variantValue = {
dataType: item.dataType,
value: value
};
}
});
}
else {
variantValue = {
dataType: node_opcua_1.DataType.String,
value: value
};
}
break;
}
logger.detailDebugLog('buildNewVariant variantValue: ' + JSON.stringify(variantValue));
return {
value: variantValue
};
}
exports.buildNewVariant = buildNewVariant;
function getVariantValue(datatype, value) {
switch (datatype) {
case 'Float':
case 'Double':
case node_opcua_1.DataType.Double:
return parseFloat(value);
case 'UInt16':
case node_opcua_1.DataType.UInt16:
let uint16 = new Uint16Array([value]);
return uint16[0];
case 'UInt32':
case node_opcua_1.DataType.UInt32:
let uint32 = new Uint32Array([value]);
return uint32[0];
case 'UInt64':
case node_opcua_1.DataType.UInt64:
return parseInt(value);
case 'Int16':
case node_opcua_1.DataType.Int16:
case 'Int32':
case 'Integer':
case node_opcua_1.DataType.Int32:
case 'Int64':
case node_opcua_1.DataType.Int64:
return parseInt(value);
case 'Boolean':
case node_opcua_1.DataType.Boolean:
return (value && value !== 'false');
case 'DateTime':
case node_opcua_1.DataType.DateTime:
return new Date(value);
case 'String':
case node_opcua_1.DataType.String:
return (typeof value !== 'string') ? value.toString() : value;
default:
return value;
}
}
exports.getVariantValue = getVariantValue;
function getBasicDataTypes() {
return [
{ name: 'Null', dataType: node_opcua_1.DataType.Null },
{ name: 'Boolean', dataType: node_opcua_1.DataType.Boolean },
{ name: 'SByte', dataType: node_opcua_1.DataType.SByte },
{ name: 'Byte', dataType: node_opcua_1.DataType.Byte },
{ name: 'Int16', dataType: node_opcua_1.DataType.Int16 },
{ name: 'UInt16', dataType: node_opcua_1.DataType.UInt16 },
{ name: 'Int32', dataType: node_opcua_1.DataType.Int32 },
{ name: 'UInt32', dataType: node_opcua_1.DataType.UInt32 },
{ name: 'Int64', dataType: node_opcua_1.DataType.Int64 },
{ name: 'UInt64', dataType: node_opcua_1.DataType.UInt64 },
{ name: 'Float', dataType: node_opcua_1.DataType.Float },
{ name: 'Double', dataType: node_opcua_1.DataType.Double },
{ name: 'DateTime', dataType: node_opcua_1.DataType.DateTime },
{ name: 'String', dataType: node_opcua_1.DataType.String },
{ name: 'Guid', dataType: node_opcua_1.DataType.Guid },
{ name: 'ByteString', dataType: node_opcua_1.DataType.ByteString },
{ name: 'XmlElement', dataType: node_opcua_1.DataType.XmlElement },
{ name: 'NodeId', dataType: node_opcua_1.DataType.NodeId },
{ name: 'ExpandedNodeId', dataType: node_opcua_1.DataType.ExpandedNodeId },
{ name: 'StatusCode', dataType: node_opcua_1.DataType.StatusCode },
{ name: 'LocalizedText', dataType: node_opcua_1.DataType.LocalizedText },
{ name: 'ExtensionObject', dataType: node_opcua_1.DataType.ExtensionObject },
{ name: 'DataValue', dataType: node_opcua_1.DataType.DataValue },
{ name: 'Variant', dataType: node_opcua_1.DataType.Variant },
{ name: 'DiagnosticInfo', dataType: node_opcua_1.DataType.DiagnosticInfo }
];
}
exports.getBasicDataTypes = getBasicDataTypes;
function valueIsString(value) {
return typeof value === 'string';
}
exports.valueIsString = valueIsString;
function convertDataValue(value) {
if (valueIsString(value)) {
return value;
}
else
return convertDataValueByDataType(value.value.value, value.value.dataType);
}
exports.convertDataValue = convertDataValue;
function convertDataValueByDataType(value, dataType) {
let convertedValue = null;
const valueType = typeof value;
logger.detailDebugLog('convertDataValue: ' + JSON.stringify(value) +
' value origin type ' + valueType + ' convert to' + ' ' + dataType);
// TODO: we have to check if that is needed or to improve since v2.x - here was a problem, that the node-opcua sent strings or types
try {
switch (dataType) {
case 'NodeId':
case node_opcua_1.DataType.NodeId:
convertedValue = value.toString();
break;
case 'ExpandedNodeId':
case node_opcua_1.DataType.ExpandedNodeId:
if (value.value instanceof Buffer) {
convertedValue = value.toString();
}
else {
convertedValue = value;
}
break;
case 'ByteString':
case node_opcua_1.DataType.ByteString:
convertedValue = value;
break;
case 'Byte':
case node_opcua_1.DataType.Byte:
if (valueType === 'boolean') {
convertedValue = value ? 1 : 0;
}
else {
convertedValue = parseInt(value);
}
break;
case 'QualifiedName':
case node_opcua_1.DataType.QualifiedName:
convertedValue = value.toString();
break;
case 'LocalizedText':
case node_opcua_1.DataType.LocalizedText:
convertedValue = value;
break;
case 'Float':
case node_opcua_1.DataType.Float:
if (isNaN(value)) {
convertedValue = value;
}
else {
convertedValue = parseFloat(value);
}
break;
case 'Double':
case node_opcua_1.DataType.Double:
if (isNaN(value)) {
convertedValue = value;
}
else {
convertedValue = parseFloat(value);
}
break;
case 'UInt16':
case node_opcua_1.DataType.UInt16:
let uint16 = new Uint16Array([value]);
convertedValue = uint16[0];
break;
case 'UInt32':
case node_opcua_1.DataType.UInt32:
let uint32 = new Uint32Array([value]);
convertedValue = uint32[0];
break;
case 'Int16':
case node_opcua_1.DataType.Int16:
case 'Int32':
case node_opcua_1.DataType.Int32:
case 'Int64':
case node_opcua_1.DataType.Int64:
if (valueType === 'boolean') {
convertedValue = value ? 1 : 0;
}
else {
if (isNaN(value)) {
convertedValue = value;
}
else {
convertedValue = parseInt(value);
}
}
break;
case 'Boolean':
case node_opcua_1.DataType.Boolean:
convertedValue = value;
break;
case 'String':
case node_opcua_1.DataType.String:
if (valueType !== 'string') {
convertedValue = value.toString();
}
else {
convertedValue = value;
}
break;
case 'Null':
case node_opcua_1.DataType.Null:
convertedValue = null;
break;
case "DateTime":
case node_opcua_1.DataType.DateTime:
if (valueType !== 'string') {
convertedValue = value.toString();
}
else {
convertedValue = value;
}
break;
default:
logger.internalDebugLog('convertDataValue unused DataType: ' + dataType);
if (underscore_1.default.isUndefined(value)) {
convertedValue = null;
}
else {
convertedValue = value;
}
break;
}
}
catch (err) {
logger.detailDebugLog('convertDataValue ' + err);
}
logger.detailDebugLog('convertDataValue is: ' + convertedValue);
return convertedValue;
}
exports.convertDataValueByDataType = convertDataValueByDataType;
function parseNamespaceFromMsgTopic(msg) {
let nodeNamespace = '';
if (msg === null || msg === void 0 ? void 0 : msg.topic) {
// TODO: real parsing instead of string operations
// TODO: which type are relevant here? (String, Integer ...)
nodeNamespace = msg.topic.substring(3, msg.topic.indexOf(';'));
}
return Number.parseInt(nodeNamespace);
}
exports.parseNamespaceFromMsgTopic = parseNamespaceFromMsgTopic;
function parseNamespaceFromItemNodeId(item) {
if (typeof item === 'number') {
return 0;
}
if ((0, underscore_1.isObject)(item) && item.namespace) {
return item.namespace;
}
// TODO: real parsing instead of string operations
const nodeObject = JSON.parse(JSON.stringify(item));
return Number.parseInt(nodeObject.nodeId.substring(3, nodeObject.nodeId.indexOf(';')));
}
exports.parseNamespaceFromItemNodeId = parseNamespaceFromItemNodeId;
function parseForNodeIdentifier(nodeItem) {
if (nodeItem) {
// TODO: real parsing instead of string operations
if (nodeItem.includes(';i=')) {
return {
identifier: parseInt(nodeItem.substring(nodeItem.indexOf(';i=') + 3)),
type: node_opcua_1.NodeIdType.NUMERIC
};
}
else if (nodeItem.includes(';g=')) {
return {
identifier: nodeItem.substring(nodeItem.indexOf(';g=') + 3),
type: node_opcua_1.NodeIdType.GUID
};
}
else {
if (nodeItem.includes(';b=')) {
return {
identifier: nodeItem.substring(nodeItem.indexOf(';b=') + 3),
type: node_opcua_1.NodeIdType.BYTESTRING
};
}
else {
return {
identifier: nodeItem.substring(nodeItem.indexOf(';s=') + 3),
type: node_opcua_1.NodeIdType.STRING
};
}
}
}
return {
identifier: 'null',
type: 0x00
};
}
exports.parseForNodeIdentifier = parseForNodeIdentifier;
function parseIdentifierFromMsgTopic(msg) {
return parseForNodeIdentifier(msg.topic);
}
exports.parseIdentifierFromMsgTopic = parseIdentifierFromMsgTopic;
function parseIdentifierFromItemNodeId(item) {
return parseForNodeIdentifier(item.nodeId || item);
}
exports.parseIdentifierFromItemNodeId = parseIdentifierFromItemNodeId;
function newOPCUANodeIdFromItemNodeId(item) {
let namespace = parseNamespaceFromItemNodeId(item);
let nodeIdentifier = parseIdentifierFromItemNodeId(item);
logger.internalDebugLog('newOPCUANodeIdFromItemNodeId: ' + JSON.stringify(item) + ' -> ' + JSON.stringify(nodeIdentifier) + ' namespace:' + namespace);
return new node_opcua_1.NodeId(nodeIdentifier.type, nodeIdentifier.identifier, namespace);
}
exports.newOPCUANodeIdFromItemNodeId = newOPCUANodeIdFromItemNodeId;
function newOPCUANodeIdFromMsgTopic(msg) {
let namespace = parseNamespaceFromMsgTopic(msg);
let nodeIdentifier = parseIdentifierFromMsgTopic(msg);
logger.internalDebugLog('newOPCUANodeIdFromMsgTopic: ' + JSON.stringify(nodeIdentifier));
return new node_opcua_1.NodeId(nodeIdentifier.type, nodeIdentifier.identifier, namespace);
}
exports.newOPCUANodeIdFromMsgTopic = newOPCUANodeIdFromMsgTopic;
function createItemForWriteList(item, value) {
return {
nodeId: newOPCUANodeIdFromItemNodeId(item),
attributeId: node_opcua_1.AttributeIds.Value,
indexRange: undefined,
value
};
}
exports.createItemForWriteList = createItemForWriteList;
function normalizeMessage(msg) {
const payload = msg.payload;
const addressSpaceValues = payload.nodesToWrite || payload.addressSpaceItems;
if (!addressSpaceValues)
return [];
const writeValues = payload.valuesToWrite;
if (!(0, assertion_1.isNotDefined)(writeValues)) {
return addressSpaceValues.map((item, index) => {
return Object.assign(Object.assign({}, item), { value: underscore_1.default.isUndefined(writeValues[index]) ? null : writeValues[index] });
});
}
else
return addressSpaceValues.map((item, index) => {
if (item.value)
return item;
else
return Object.assign(Object.assign({}, item), { value: (Array.isArray(msg.payload) && msg.payload.length && msg.payload.length === msg.addressSpaceItems.length) ? msg.payload[index] : msg.payload });
});
}
exports.normalizeMessage = normalizeMessage;
function buildNodesToWrite(msg) {
logger.detailDebugLog('buildNodesToWrite input: ' + JSON.stringify(msg));
const writeInputs = normalizeMessage(msg);
const nodesToWrite = writeInputs.map((item) => createItemForWriteList(item, buildNewVariant(item.datatypeName, item.value)));
logger.internalDebugLog('buildNodesToWrite output: ' + JSON.stringify(nodesToWrite));
return nodesToWrite;
}
exports.buildNodesToWrite = buildNodesToWrite;
function buildNodesToRead(payload) {
var _a;
logger.detailDebugLog('buildNodesToRead input: ' + JSON.stringify(payload));
let injectArrayOfNodeIds = (((_a = payload.value) === null || _a === void 0 ? void 0 : _a.length) && payload.value.type === Array) ? payload.value : payload.addressSpaceItems;
/*
there are some possible ways to receive the Array<any> from Browser or Inject
TODO: payload.injectType and/or payload.nodetype should be used to make a clear decision here
read : nodesToRead
write : nodesToWrite
browse : browserResults
crawl : crawlerResults
value : inject with Array<any>
addressSpaceItems : inject fall back for all types
*/
let nodePayloadList = payload.nodesToRead || payload.nodesToWrite || payload.crawlerResults || payload.browserResults || injectArrayOfNodeIds;
if (nodePayloadList && nodePayloadList.length) {
return nodePayloadList.map((item) => {
return item;
});
}
else {
let nodeList = payload.nodesToRead || payload.nodesToWrite;
if (nodeList && nodeList.length) {
// legacy
return nodeList.map((item) => {
return item;
});
}
else if (payload.addressSpaceItems && payload.addressSpaceItems.length) {
return payload.addressSpaceItems.map((item) => item);
}
}
return [];
}
exports.buildNodesToRead = buildNodesToRead;
function buildNodesToListen(payload) {
var _a, _b;
if ((_a = payload.addressItemsToRead) === null || _a === void 0 ? void 0 : _a.length)
return payload.addressItemsToRead;
else if ((_b = payload.addressSpaceItems) === null || _b === void 0 ? void 0 : _b.length)
return payload.addressSpaceItems;
else
return payload.addressSpaceItemList;
}
exports.buildNodesToListen = buildNodesToListen;
function buildNodesFromBrowser(payload) {
return payload.browserResults || payload.addressSpaceItems;
}
exports.buildNodesFromBrowser = buildNodesFromBrowser;
function buildNodesFromCrawler(payload) {
return payload.crawlerResults || payload.addressSpaceItems;
}
exports.buildNodesFromCrawler = buildNodesFromCrawler;
function buildNodeListFromClient(payload) {
/*
The node type should help users to be aware of wiring mistakes.
We want to support the user with messages if the wiring and inputs are wrong.
That brings the needs of a nodeType (where does it come from) and injectType (where should it be for).
This was the real last feature and needs some more refactoring and work to be done well.
*/
switch (payload.nodetype) {
case 'read':
case 'write':
return buildNodesToRead(payload);
case 'node':
case 'listen':
case 'inject':
case 'events':
return buildNodesToListen(payload);
case 'browse':
return buildNodesFromBrowser(payload);
case 'crawl':
return buildNodesFromCrawler(payload);
default:
logger.internalDebugLog('unknown node type injected to filter for ' + payload.nodetype);
return [];
}
}
exports.buildNodeListFromClient = buildNodeListFromClient;
function availableMemory() {
return os.freemem() / os.totalmem() * 100.0;
}
exports.availableMemory = availableMemory;
function isSessionBad(err) {
return (err.toString().includes('Session') ||
err.toString().includes('Channel') ||
err.toString().includes('Transaction') ||
err.toString().includes('timeout') ||
err.toString().includes('Connection'));
}
exports.isSessionBad = isSessionBad;
function setNodeInitalState(nodeState, node, statusCall) {
switch (nodeState) {
case 'INITOPCUA':
case 'SESSIONREQUESTED':
node.oldStatusParameter = setNodeStatusTo(node, 'connecting', node.oldStatusParameter, node.showStatusActivities, statusCall);
break;
case 'OPEN':
case 'SESSIONCLOSED':
node.iiot.opcuaClient = node.connector.iiot.opcuaClient;
node.oldStatusParameter = setNodeStatusTo(node, 'connected', node.oldStatusParameter, node.showStatusActivities, statusCall);
break;
case 'SESSIONACTIVE':
node.iiot.opcuaSession = node.connector.iiot.opcuaSession;
node.oldStatusParameter = setNodeStatusTo(node, 'active', node.oldStatusParameter, node.showStatusActivities, statusCall);
break;
case 'LOCKED':
node.oldStatusParameter = setNodeStatusTo(node, 'locked', node.oldStatusParameter, node.showStatusActivities, statusCall);
break;
case 'UNLOCKED':
node.oldStatusParameter = setNodeStatusTo(node, 'unlocked', node.oldStatusParameter, node.showStatusActivities, statusCall);
break;
case 'STOPPED':
node.oldStatusParameter = setNodeStatusTo(node, 'stopped', node.oldStatusParameter, node.showStatusActivities, statusCall);
break;
case 'END':
node.oldStatusParameter = setNodeStatusTo(node, 'end', node.oldStatusParameter, node.showStatusActivities, statusCall);
break;
default:
node.oldStatusParameter = setNodeStatusTo(node, 'waiting', node.oldStatusParameter, node.showStatusActivities, statusCall);
}
}
exports.setNodeInitalState = setNodeInitalState;
function isNodeId(nodeId) {
if (!nodeId || !nodeId.identifierType) {
return false;
}
switch (nodeId.identifierType) {
case node_opcua_1.NodeIdType.NUMERIC:
case node_opcua_1.NodeIdType.STRING:
case node_opcua_1.NodeIdType.GUID:
return true;
default:
return false;
}
}
exports.isNodeId = isNodeId;
function checkConnectorState(node, msg, callerType, errorHandler, emitHandler, statusHandler) {
var _a, _b;
const state = (_b = (_a = node.connector) === null || _a === void 0 ? void 0 : _a.iiot.stateService) === null || _b === void 0 ? void 0 : _b.state.value;
logger.internalDebugLog('Check Connector State ' + state + ' By ' + callerType);
if (state !== exports.RUNNING_STATE) {
logger.internalDebugLog('Wrong Client State ' + state + ' By ' + callerType);
if (node.showErrors) {
errorHandler(new Error('Client Not ' + exports.RUNNING_STATE + ' On ' + callerType), msg);
}
node.oldStatusParameter = setNodeStatusTo(node, 'not running', node.oldStatusParameter, node.showStatusActivities, statusHandler);
emitHandler('opcua_client_not_ready');
return false;
}
else {
return true;
}
}
exports.checkConnectorState = checkConnectorState;
function setNodeOPCUAConnected(node, opcuaClient, statusHandler) {
if (isInitializedIIoTNode(node)) {
node.iiot.opcuaClient = opcuaClient;
}
node.oldStatusParameter = setNodeStatusTo(node, 'connecting', node.oldStatusParameter, node.showStatusActivities, statusHandler);
}
exports.setNodeOPCUAConnected = setNodeOPCUAConnected;
function setNodeOPCUAClosed(node, statusHandler) {
if (isInitializedIIoTNode(node)) {
// @ts-ignore
node.iiot.opcuaClient = null;
}
node.oldStatusParameter = setNodeStatusTo(node, 'disconnected', node.oldStatusParameter, node.showStatusActivities, statusHandler);
}
exports.setNodeOPCUAClosed = setNodeOPCUAClosed;
function setNodeOPCUALost(node, statusHandler) {
node.oldStatusParameter = setNodeStatusTo(node, 'lost', node.oldStatusParameter, node.showStatusActivities, statusHandler);
}
exports.setNodeOPCUALost = setNodeOPCUALost;
function setNodeOPCUASessionStarted(node, opcuaSession, statusHandler) {
if (isInitializedIIoTNode(node)) {
node.iiot.opcuaSession = opcuaSession;
}
node.oldStatusParameter = setNodeStatusTo(node, 'active', node.oldStatusParameter, node.showStatusActivities, statusHandler);
}
exports.setNodeOPCUASessionStarted = setNodeOPCUASessionStarted;
function setNodeOPCUASessionClosed(node, statusHandler) {
if (isInitializedIIoTNode(node)) {
node.iiot.opcuaSession = null;
}
node.oldStatusParameter = setNodeStatusTo(node, 'connecting', node.oldStatusParameter, node.showStatusActivities, statusHandler);
}
exports.setNodeOPCUASessionClosed = setNodeOPCUASessionClosed;
function setNodeOPCUASessionRestart(node, statusHandler) {
node.oldStatusParameter = setNodeStatusTo(node, 'restart', node.oldStatusParameter, node.showStatusActivities, statusHandler);
}
exports.setNodeOPCUASessionRestart = setNodeOPCUASessionRestart;
function setNodeOPCUASessionError(node, statusHandler) {
if (isInitializedIIoTNode(node)) {
node.iiot.opcuaSession = null;
}
node.oldStatusParameter = setNodeStatusTo(node, 'connecting', node.oldStatusParameter, node.showStatusActivities, statusHandler);
}
exports.setNodeOPCUASessionError = setNodeOPCUASessionError;
function setNodeOPCUARestart(node, opcuaClient, statusHandler) {
logger.internalDebugLog('Connector Restart');
if (opcuaClient && isInitializedIIoTNode(node)) {
node.iiot.opcuaClient = opcuaClient;
}
node.oldStatusParameter = setNodeStatusTo(node, 'connecting', node.oldStatusParameter, node.showStatusActivities, statusHandler);
}
exports.setNodeOPCUARestart = setNodeOPCUARestart;
function registerToConnector(node, statusCallback, onAlias, errorHandler) {
var _a, _b, _c;
if (!node) {
logger.internalDebugLog('Node Not Valid On Register To Connector');
return;
}
if ((0, assertion_1.isNotDefined)(node.connector)) {
errorHandler(new Error('Connector Config Node Not Valid On Registering Client Node ' + node.id), { payload: 'No Connector Configured' });
return;
}
if (!node.connector.statusCallbacks) {
node.connector.statusCallbacks = [];
}
node.connector.statusCallbacks.push(statusCallback);
(_a = node.connector.functions) === null || _a === void 0 ? void 0 : _a.registerForOPCUA(node, onAlias);
const statusCall = (status) => {
node.connector.statusCallbacks.forEach((callback) => callback(status));
};
node.connector.on('connector_init', (node) => {
var _a, _b;
if ((_a = node.iiot) === null || _a === void 0 ? void 0 : _a.opcuaClient) {
// @ts-ignore
node.iiot.opcuaClient = null;
}
if ((_b = node.iiot) === null || _b === void 0 ? void 0 : _b.opcuaSession) {
node.iiot.opcuaSession = null;
}
});
node.connector.on('connection_started', (opcuaClient) => {
setNodeOPCUAConnected(node.connector, opcuaClient, statusCall);
});
node.connector.on('server_connection_close', () => {
setNodeOPCUAClosed(node.connector, statusCall);
});
node.connector.on('server_connection_abort', () => {
setNodeOPCUAClosed(node.connector, statusCall);
});
node.connector.on('connection_closed', () => {
setNodeOPCUAClosed(node.connector, statusCall);
});
node.connector.on('server_connection_lost', () => {
setNodeOPCUALost(node.connector, statusCall);
});
node.connector.on('reset_opcua_connection', () => {
setNodeOPCUASessionClosed(node.connector, statusCall);
});
node.connector.on('session_started', (opcuaSession) => {
setNodeOPCUASessionStarted(node.connector, opcuaSession, statusCall);
});
node.connector.on('session_closed', () => {
setNodeOPCUASessionClosed(node.connector, statusCall);
});
node.connector.on('session_restart', () => {
setNodeOPCUAClosed(node.connector, statusCall);
});
node.connector.on('session_error', () => {
setNodeOPCUASessionError(node.connector, statusCall);
});
node.connector.on('after_reconnection', () => {
setNodeOPCUARestart(node.connector, node_opcua_1.OPCUAClient.create(node.connector.iiot.opcuaClient), statusCall); // TODO: investigate one args v two
});
setNodeInitalState((_c = (_b = node.connector) === null || _b === void 0 ? void 0 : _b.iiot) === null || _c === void 0 ? void 0 : _c.stateService.state.value, node, statusCall);
}
exports.registerToConnector = registerToConnector;
function deregisterToConnector(node, done) {
var _a, _b;
if (!node) {
logger.internalDebugLog('Node Not Valid On Register To Connector');
done();
return;
}
if (!node.connector) {
node.error(new Error('Connector Not Valid On Register To Connector'), { payload: 'No Connector Configured' });
done();
return;
}
if (isInitializedIIoTNode(node.connector)) {
(_b = (_a = node.connector) === null || _a === void 0 ? void 0 : _a.functions) === null || _b === void 0 ? void 0 : _b.deregisterForOPCUA(node, done);
}
else {
done();
}
}
exports.deregisterToConnector = deregisterToConnector;
function checkSessionNotValid(session, callerType) {
if (!session) {
logger.internalDebugLog('Session Not Valid On Check For ' + callerType);
return true;
}
if (session.sessionId === 'terminated') {
logger.internalDebugLog('Session Is Valid But Terminated On Check For ' + callerType);
return true;
}
return false;
}
exports.checkSessionNotValid = checkSessionNotValid;
function setNodeStatusTo(node, statusValue, oldStatus, showStatusActivities, status) {
let statusParameter = getNodeStatus(statusValue, showStatusActivities);
if (!underscore.isEqual(oldStatus, statusParameter) && statusParameter) {
logger.detailDebugLog('Node ' + node.id + ' Status To ' + statusValue);
if (typeof status === "function")
status(statusParameter);
else
logger.internalDebugLog("Status is not a function " + typeof status);
}
return statusParameter;
}
exports.setNodeStatusTo = setNodeStatusTo;
// sets some values within node.iiot
function initCoreNode() {
return {
reconnectTimeout: exports.DEFAULT_TIMEOUT,
sessionTimeout: undefined,
opcuaSession: undefined,
opcuaClient: undefined
};
}
exports.initCoreNode = initCoreNode;
function initCoreServerNode() {
return {
initialized: false,
opcuaServer: null
};
}
exports.initCoreServerNode = initCoreServerNode;
function getItemFilterValueWithElement(item, element) {
let filterValue = '';
switch (element.name) {
case 'browseName':
case 'statusCode':
filterValue = item[element.name].name;
break;
case 'displayName':
filterValue = item[element.name].text;
break;
case 'value':
case 'dataType':
if (item.value && item.value.hasOwnProperty('value')) {
filterValue = item.value[element.name];
}
else {
filterValue = item[element.name];
}
break;
case 'nodeClass':
filterValue = node_opcua_1.NodeClass[item['nodeClass']];
break;
default:
filterValue = item[element.name];
}
return filterValue;
}
exports.getItemFilterValueWithElement = getItemFilterValueWithElement;
function handleErrorInsideNode(node, err) {
logger.internalDebugLog(typeof node + ' ' + err.message);
if (node.showErrors) {
node.error(err, { payload: err.message });
}
}
exports.handleErrorInsideNode = handleErrorInsideNode;
function checkCrawlerItemIsNotToFilter(node, item, element, result) {
try {
let filterValue = getItemFilterValueWithElement(item, element);
if (filterValue && typeof filterValue !== "string" && filterValue.key && filterValue.key.match) {
if (!filterValue.key.match(element.value)) {
result = 0;
}
}
else {
if (filterValue && filterValue.match) {
if (!filterValue.match(element.value)) {
result = 0;
}
}
else {
if (filterValue && filterValue.toString) {
filterValue = filterValue.toString();
if (filterValue && filterValue.match) {
if (!filterValue.match(element.value)) {
result = 0;
}
}
}
}
}
}
catch (err) {
handleErrorInsideNode(node, err);
}
return result;
}
exports.checkCrawlerItemIsNotToFilter = checkCrawlerItemIsNotToFilter;
function checkResponseItemIsNotToFilter(node, item, element, result) {
try {
let filterValue = getItemFilterValueWithElement(item, element);
if (filterValue) {
if (typeof filterValue !== 'string' && filterValue.key && filterValue.key.match) {
result &= filterValue.key.match(element.value) !== null ? 1 : 0;
}
else {
if (filterValue.match) {
result &= filterValue.match(element.value) !== null ? 1 : 0;
}
else {
if (filterValue.toString) {
filterValue = filterValue.toString();
if (filterValue.match) {
result &= filterValue.match(element.value) !== null ? 1 : 0;
}
}
}
}
}
else {
result &= 0; // undefined items
}
}
catch (err) {
handleErrorInsideNode(node, err);
}
return result;
}
exports.checkResponseItemIsNotToFilter = checkResponseItemIsNotToFilter;
function checkItemForUnsetState(node, item) {
let result = 1;
if (node.activateUnsetFilter) {
result &= item !== null ? 1 : 0;
if (item && item.hasOwnProperty('value')) {
if (item.value && item.value.hasOwnProperty('value')) {
result &= item.value.value !== null ? 1 : 0;
}
else {
result &= item.value !== null ? 1 : 0;
}
}
}
return result;
}
exports.checkItemForUnsetState = checkItemForUnsetState;
function resetIiotNode(node) {
// coreListener.internalDebugLog('reset IIoT of the Node with id:' + node.id) /// Invalid import of lower hirarchy module
// Valid hirarchy is supposed to be: Core -> CoreSpecific (CoreListener, CoreConnector, ...) -> Specific (Listener, Browser, ...)
// This is because looped imports and usages before declaration can happen if this is not kept in mind
if (underscore_1.default.isObject(node) == false || underscore_1.default.isEmpty(node.iiot)) {
return;
}
else {
if (underscore_1.default.isFunction(node.resetAllTimer)) {
node.resetAllTimer(); // call to close all timer otherwise it stops until timeout and node-red hangs on
}
}
if (isInitializedIIoTNode(node.iiot) && underscore_1.default.isFunction(node.iiot.resetAllTimer)) {
node.iiot.resetAllTimer();
}
}
exports.resetIiotNode = resetIiotNode;
function filterListEntryByNodeId(nodeId, list) {
return list.filter((item) => {
return item.toString().includes(nodeId);
});
}
exports.filterListEntryByNodeId = filterListEntryByNodeId;
function filterListByNodeId(nodeId, list) {
if (nodeId === '') {
return list;
}
return list.filter((item) => {
// if item.nodeId is null, item may itself be a nodeId
return (item.nodeId || item).toString().includes(nodeId);
});
}
exports.filterListByNodeId = filterListByNodeId;
function isNodeTypeToFilterResponse(payload) {
return payload.nodetype === 'read' || payload.nodetype === 'browse' || payload.nodetype === 'crawl' || payload.nodetype === 'method';
}
exports.isNodeTypeToFilterResponse = isNodeTypeToFilterResponse;
function isInitializedIIoTNode(node) {
return !!node;
}
exports.isInitializedIIoTNode = isInitializedIIoTNode;
//# sourceMappingURL=opcua-iiot-core.js.map