UNPKG

node-red-contrib-iiot-opcua

Version:

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

937 lines (934 loc) 41.8 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 __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 node_opcua_1 = require("node-opcua"); const opcua_iiot_core_1 = require("./opcua-iiot-core"); const debug_1 = __importDefault(require("debug")); const path_1 = __importDefault(require("path")); const alarms_and_conditions_demo_1 = require("../helpers/alarms-and-conditions-demo"); const internalDebugLog = (0, debug_1.default)('opcuaIIoT:server'); // eslint-disable-line no-use-before-define const detailDebugLog = (0, debug_1.default)('opcuaIIoT:server:details'); // eslint-disable-line no-use-before-define const isa95DebugLog = (0, debug_1.default)('opcuaIIoT:server:ISA95'); // eslint-disable-line no-use-before-define const isa95DetailDebugLog = (0, debug_1.default)('opcuaIIoT:server:ISA95:details'); // eslint-disable-line no-use-before-define const flex = {}; // eslint-disable-line no-use-before-define const flexInternalDebugLog = (0, debug_1.default)('opcuaIIoT:server:flex'); // eslint-disable-line no-use-before-define const flexDetailDebugLog = (0, debug_1.default)('opcuaIIoT:server:flex:details'); // eslint-disable-line no-use-before-define let simulatorInterval = null; // eslint-disable-line no-use-before-define const maxTimeInterval = 500000; // eslint-disable-line no-use-before-define let timeInterval = 1; // eslint-disable-line no-use-before-define const UNLIMITED_LISTENERS = 0; // eslint-disable-line no-use-before-define let intervalList = []; // eslint-disable-line no-use-before-define let conditions = []; const simulateVariation = function (data) { let value = (1.0 + Math.sin(timeInterval / 360 * 3)) / 2.0; timeInterval++; if (timeInterval > maxTimeInterval) { timeInterval = 1; } if (data.tankLevel) { data.tankLevel.setValueFromSource({ dataType: 'Double', value }); } if (data.tankLevel2) { data.tankLevel2.setValueFromSource({ dataType: 'Double', value }); } }; const constructAddressSpaceFromScript = function (server, constructAddressSpaceScript, eventObjects) { return new Promise((resolve, reject) => { if (server.engine && constructAddressSpaceScript && constructAddressSpaceScript !== '') { try { constructAddressSpaceScript(server, server.engine.addressSpace, eventObjects, resolve); } catch (err) { reject(err); } } else { reject(new Error('Wrong Parameters Construct AddressSpace From Script')); } }); }; const constructAddressSpace = function (server, asoDemo) { return new Promise((resolve, reject) => { var _a; if (!server) { reject(new Error('Server Not Valid To Construct Address Space')); return; } const addressSpace = (_a = server.engine) === null || _a === void 0 ? void 0 : _a.addressSpace; if (!addressSpace) { reject(new Error('No AddressSpace From OPC UA Server Engine')); return; } addressSpace.installAlarmsAndConditionsService(); const namespace = addressSpace.getOwnNamespace(); const exclusiveLimitAlarmType = addressSpace.findEventType('ExclusiveLimitAlarmType'); if (!exclusiveLimitAlarmType) { throw new Error("cannot find ExclusiveLimitAlarmType in namespace 0"); } let nonExclusiveLimitAlarmType = addressSpace.findEventType('NonExclusiveLimitAlarmType'); if (!nonExclusiveLimitAlarmType) { throw new Error("cannot find NonExclusiveLimitAlarmType in namespace 0"); } let view = namespace.addView({ organizedBy: addressSpace === null || addressSpace === void 0 ? void 0 : addressSpace.rootFolder.views, browseName: 'PLUS4NODERED_VIEW', displayName: [ new node_opcua_1.LocalizedText({ text: 'OPCUA-IIoT plus4nodered.com Server View', locale: 'en-US' }), new node_opcua_1.LocalizedText({ text: 'OPCUA-IIoT plus4nodered.com Server Sicht', locale: 'de-DE' }) ] }); if (!asoDemo) { resolve(null); } else { let data = {}; (0, alarms_and_conditions_demo_1.constructAlarmAddressSpaceDemo)(data, addressSpace); timeInterval = 1; simulatorInterval = setInterval(function () { simulateVariation(data); }, 500); intervalList.push(simulatorInterval); let vendorName = namespace.addObject({ organizedBy: addressSpace.rootFolder.objects, typeDefinition: 'FolderType', nodeId: 'i=1234', browseName: 'PLUS4NODERED', displayName: [ new node_opcua_1.LocalizedText({ text: 'OPCUA-IIoT plus4nodered.com Server', locale: 'en-US' }), new node_opcua_1.LocalizedText({ text: 'OPCUA-IIoT plus4nodered.com Server', locale: 'de-DE' }) ], eventNotifier: 1, notifierOf: addressSpace.rootFolder.objects.server }); let variable1 = 1; intervalList.push(setInterval(function () { if (variable1 < 1000000) { variable1 += 1; } else { variable1 = 0; } }, 100)); namespace.addVariable({ componentOf: vendorName, nodeId: 'i=16479', browseName: 'MyVariable1', displayName: 'PLUS for Node-RED Double Variable', dataType: 'Double', value: { get: function () { return new node_opcua_1.Variant({ dataType: 'Double', value: variable1 }); } } }); let variable2 = 10.0; namespace.addVariable({ componentOf: vendorName, nodeId: 'b=1020FFAA', browseName: 'MyVariable2', displayName: 'PLUS for Node-RED Double Binary Variable', dataType: 'Double', value: { get: function () { return new node_opcua_1.Variant({ dataType: 'Double', value: variable2 }); }, set: function (variant) { variable2 = parseFloat(variant.value); return node_opcua_1.StatusCodes.Good; } } }); let variable3 = 1000.0; namespace.addVariable({ componentOf: vendorName, nodeId: 's=TestReadWrite', browseName: 'TestReadWrite', displayName: 'PLUS for Node-RED Test Read and Write', dataType: 'Double', value: { get: function () { return new node_opcua_1.Variant({ dataType: 'Double', value: variable3 }); }, set: function (variant) { variable3 = parseFloat(variant.value); return node_opcua_1.StatusCodes.Good; } } }); let memoryVariable = namespace.addVariable({ componentOf: vendorName, nodeId: 's=free_memory', browseName: 'FreeMemory', displayName: [ new node_opcua_1.LocalizedText({ text: 'P4NR Free Memory', locale: 'en-US' }), new node_opcua_1.LocalizedText({ text: 'P4NR Freier Arbeitsspeicher', locale: 'de-DE' }) ], dataType: 'Double', value: { get: function () { return new node_opcua_1.Variant({ dataType: 'Double', value: (0, opcua_iiot_core_1.availableMemory)() }); } } }); addressSpace.installHistoricalDataNode(memoryVariable); let counterValue = 0; intervalList.push(setInterval(function () { if (counterValue < 65000) { counterValue += 1; } else { counterValue = 0; } }, 1000)); let counterVariable = namespace.addVariable({ componentOf: vendorName, nodeId: 's=Counter', browseName: 'Counter', displayName: [ new node_opcua_1.LocalizedText({ text: 'P4NR Counter', locale: 'en-US' }), new node_opcua_1.LocalizedText({ text: 'P4NR Zähler', locale: 'de-DE' }) ], dataType: 'UInt16', value: { get: function () { return new node_opcua_1.Variant({ dataType: 'UInt16', value: counterValue }); } } }); addressSpace.installHistoricalDataNode(counterVariable); let fullCounterValue = 8900; let fullCounterVariable = namespace.addVariable({ componentOf: vendorName, eventSourceOf: vendorName, nodeId: 's=FullCounter', browseName: 'FullCounter', displayName: [ new node_opcua_1.LocalizedText({ text: 'P4NR Full-Counter', locale: 'en-US' }), new node_opcua_1.LocalizedText({ text: 'P4NR Voll-Zähler', locale: 'de-DE' }) ], dataType: 'Int32', value: { get: function () { return new node_opcua_1.Variant({ dataType: 'Int32', value: fullCounterValue }); } } }); addressSpace.installHistoricalDataNode(fullCounterVariable); namespace.instantiateNonExclusiveLimitAlarm(nonExclusiveLimitAlarmType, { componentOf: vendorName, conditionSource: fullCounterVariable, browseName: 'FullCounterLimitCondition', displayName: new node_opcua_1.LocalizedText({ text: 'Full Counter Limit Condition', locale: 'en-US' }), description: 'ExclusiveLimitAlarmType Condition', conditionName: 'FullCounterLimitCondition', inputNode: fullCounterVariable, highHighLimit: 9990, highLimit: 9000, lowLimit: -9000, lowLowLimit: -9990, optionals: [ "ConfirmedState", "Confirm" // confirm state and confirm Method ] }); intervalList.push(setInterval(function () { if (fullCounterValue < 10000) { fullCounterValue += 1; } else { fullCounterValue = -10000; } fullCounterVariable.setValueFromSource({ dataType: node_opcua_1.DataType.Int32, value: fullCounterValue }); }, 100)); let externalValueWithSourceTimestamp = new node_opcua_1.DataValue({ value: new node_opcua_1.Variant({ dataType: 'Double', value: 10.0 }), sourceTimestamp: null, sourcePicoseconds: 0 }); intervalList.push(setInterval(function () { externalValueWithSourceTimestamp.value.value = Math.random(); externalValueWithSourceTimestamp.sourceTimestamp = new Date(); }, 1000)); namespace.addVariable({ organizedBy: vendorName, nodeId: 's=Pressure', browseName: 'Pressure', displayName: [ new node_opcua_1.LocalizedText({ text: 'P4NR Pressure', locale: 'en-US' }), new node_opcua_1.LocalizedText({ text: 'P4NR Druck', locale: 'de-DE' }) ], dataType: 'Double', value: { timestamped_get: function () { return externalValueWithSourceTimestamp; } } }); namespace.addVariable({ organizedBy: vendorName, nodeId: 's=Matrix', browseName: 'Matrix', displayName: [new node_opcua_1.LocalizedText({ text: 'P4NR Matrix', locale: 'en-US' })], dataType: 'Double', valueRank: 2, arrayDimensions: [3, 3], value: { get: function () { return new node_opcua_1.Variant({ dataType: 'Double', arrayType: node_opcua_1.VariantArrayType.Matrix, dimensions: [3, 3], value: [1, 2, 3, 4, 5, 6, 7, 8, 9] }); } } }); namespace.addVariable({ organizedBy: vendorName, nodeId: 's=Position', browseName: 'Position', displayName: [new node_opcua_1.LocalizedText({ text: 'P4NR Position', locale: 'en-US' })], dataType: 'Double', valueRank: 1, arrayDimensions: null, value: { get: function () { return new node_opcua_1.Variant({ dataType: 'Double', arrayType: node_opcua_1.VariantArrayType.Array, value: [1, 2, 3, 4] }); } } }); let pumpSpeed = namespace.addVariable({ organizedBy: vendorName, nodeId: 's=PumpSpeed', browseName: 'PumpSpeed', displayName: [ new node_opcua_1.LocalizedText({ text: 'P4NR Pump Speed', locale: 'en-US' }), new node_opcua_1.LocalizedText({ text: 'P4NR Geschwindigkeit Pumpe', locale: 'de-DE' }) ], dataType: 'Double', value: { get: function () { return new node_opcua_1.Variant({ dataType: 'Double', value: 200 + 100 * Math.sin(Date.now() / 10000) }); } } }); namespace.addVariable({ organizedBy: vendorName, nodeId: 's=SomeDate', browseName: 'SomeDate', displayName: [ new node_opcua_1.LocalizedText({ text: 'P4NR Some Date', locale: 'en-US' }), new node_opcua_1.LocalizedText({ text: 'P4NR Einfaches Datum', locale: 'de-DE' }) ], dataType: 'DateTime', value: { get: function () { return new node_opcua_1.Variant({ dataType: node_opcua_1.DataType.DateTime, value: new Date(Date.UTC(2016, 9, 13, 8, 40, 0)) }); } } }); namespace.addVariable({ organizedBy: vendorName, nodeId: 's=MultiLanguageText', browseName: 'MultiLanguageText', displayName: [ new node_opcua_1.LocalizedText({ text: 'P4NR Multi Language Text', locale: 'en-US' }), new node_opcua_1.LocalizedText({ text: 'P4NR Mehrsprachiger Text', locale: 'de-DE' }) ], dataType: 'LocalizedText', value: { get: function () { return new node_opcua_1.Variant({ dataType: node_opcua_1.DataType.LocalizedText, value: [{ text: 'multilingual text', locale: 'en' }, { text: 'plus4nodered.de mehrsprachiger Text', locale: 'de' }, { text: 'plus4nodered.com texte multilingue', locale: 'fr' }] }); } } }); let fanSpeed = namespace.addVariable({ organizedBy: vendorName, nodeId: 's=FanSpeed', browseName: 'FanSpeed', displayName: [ new node_opcua_1.LocalizedText({ text: 'P4NR Fan Speed', locale: 'en-US' }), new node_opcua_1.LocalizedText({ text: 'P4NR Geschwindigkeit Lüfter', locale: 'de-DE' }) ], dataType: 'Double', value: new node_opcua_1.Variant({ dataType: 'Double', value: 1000.0 }) }); intervalList.push(setInterval(function () { fanSpeed.setValueFromSource(new node_opcua_1.Variant({ dataType: 'Double', value: 1000.0 + (Math.random() * 100 - 50) })); }, 10)); let method = namespace.addMethod(vendorName, { nodeId: 'i=12345', browseName: 'Bark', inputArguments: [ { name: 'barks', dataType: 'UInt32', description: { text: 'specifies the number of time I should bark' } }, { name: 'volume', dataType: 'UInt32', description: { text: 'specifies the sound volume [0 = quiet ,100 = loud]' } } ], outputArguments: [{ name: 'Barks', dataType: 'String', description: { text: 'the generated barks' }, valueRank: 1 }] }); method.bindMethod(function (inputArguments, context, callback) { let nbBarks = inputArguments[0].value; let volume = inputArguments[1].value; let soundVolume = new Array(volume).join('!'); let barks = []; for (let i = 0; i < nbBarks; i++) { barks.push('Whaff' + soundVolume); } let callMethodResult = { statusCode: node_opcua_1.StatusCodes.Good, outputArguments: [{ dataType: node_opcua_1.DataType.String, arrayType: node_opcua_1.VariantArrayType.Array, value: barks }] }; callback(null, callMethodResult); }); let temperatureValue = 170; let analogItemNode = namespace.addAnalogDataItem({ componentOf: vendorName, eventSourceOf: vendorName, nodeId: 's=TemperatureAnalogItem', browseName: 'TemperatureAnalogItem', displayName: [ new node_opcua_1.LocalizedText({ text: 'P4NR Temperature', locale: 'en-US' }), new node_opcua_1.LocalizedText({ text: 'P4NR Temperatur', locale: 'de-DE' }) ], definition: '(tempA -25) + tempB', valuePrecision: 0.5, engineeringUnitsRange: { low: 100, high: 200 }, instrumentRange: { low: -100, high: +200 }, engineeringUnits: node_opcua_1.standardUnits.degree_celsius, dataType: 'Double', value: { get: function () { return new node_opcua_1.Variant({ dataType: 'Double', value: temperatureValue }); } } }); namespace.instantiateExclusiveLimitAlarm(exclusiveLimitAlarmType, { componentOf: vendorName, conditionSource: analogItemNode, browseName: 'TemperatureLimitCondition', displayName: new node_opcua_1.LocalizedText({ text: 'Temperature Limit Condition', locale: 'en-US' }), description: 'ExclusiveLimitAlarmType Condition', conditionName: 'TemperatureLimitCondition', inputNode: analogItemNode, highHighLimit: 240, highLimit: 200, lowLimit: 100, lowLowLimit: 10, optionals: [ "ConfirmedState", "Confirm" // confirm state and confirm Method ] }); intervalList.push(setInterval(function () { if (temperatureValue < 250) { temperatureValue += 0.5; } else { temperatureValue = -40; } analogItemNode.setValueFromSource({ dataType: node_opcua_1.DataType.Double, value: temperatureValue }); }, 500)); view.addReference({ referenceType: 'Organizes', nodeId: pumpSpeed.nodeId }); resolve(''); } }); }; const buildGeneralServerOptions = (node, prefix) => __awaiter(void 0, void 0, void 0, function* () { let serverOptions = yield buildServerOptions(node, prefix); serverOptions.userManager = { isValidUser: function (userName, password) { return checkUser(node, userName, password); } }; return setDiscoveryOptions(node, serverOptions); }); const destructAddressSpace = function (done) { intervalList.forEach(function (value, index, list) { clearInterval(value); list[index] = null; }); intervalList = []; done(); }; const start = function (server, node) { return new Promise(function (resolve, reject) { if (!server) { reject(new Error('Server Not Valid To Start')); return; } if (!node) { reject(new Error('Node Not Valid To Start')); return; } server.start(() => { var _a; node.iiot.initialized = true; if ((_a = server.endpoints) === null || _a === void 0 ? void 0 : _a.length) { server.endpoints.forEach((endpoint) => { endpoint.endpointDescriptions().forEach((endpointDescription) => { var _a; internalDebugLog('Server endpointUrl:', endpointDescription.endpointUrl, 'securityMode:', endpointDescription.securityMode.toString(), 'securityPolicyUri:', endpointDescription.securityPolicyUri ? (_a = endpointDescription.securityPolicyUri) === null || _a === void 0 ? void 0 : _a.toString() : 'None Security Policy Uri'); }); }); let endpointUrl = server.endpoints[0].endpointDescriptions()[0].endpointUrl; internalDebugLog('Primary Server Endpoint URL ' + endpointUrl); } server.on('newChannel', function (channel) { internalDebugLog('Client connected with address = ' + channel.remoteAddress + ' port = ' + channel.remotePort); }); server.on('closeChannel', function (channel) { internalDebugLog('Client disconnected with address = ' + channel.remoteAddress + ' port = ' + channel.remotePort); }); server.on('create_session', function (session) { internalDebugLog('############## SESSION CREATED ##############'); if (session.clientDescription) { detailDebugLog('Client application URI:' + session.clientDescription.applicationUri); detailDebugLog('Client product URI:' + session.clientDescription.productUri); detailDebugLog('Client application name:' + session.clientDescription.applicationName ? session.clientDescription.applicationName.toString() : 'none application name'); detailDebugLog('Client application type:' + session.clientDescription.applicationType ? session.clientDescription.applicationType.toString() : 'none application type'); } internalDebugLog('Session name:' + session.sessionName ? session.sessionName.toString() : 'none session name'); internalDebugLog('Session timeout:' + session.sessionTimeout); internalDebugLog('Session id:' + session.sessionId); }); server.on('session_closed', function (session, reason) { internalDebugLog('############## SESSION CLOSED ##############'); internalDebugLog('reason:' + reason); internalDebugLog('Session name:' + session.sessionName ? session.sessionName.toString() : 'none session name'); }); internalDebugLog('Server Initialized'); if (server.serverInfo) { detailDebugLog('Server Info:' + JSON.stringify(server.serverInfo)); } resolve(''); }); }); }; const readConfigOfServerNode = function (node, config) { node.name = config.name; // network node.port = config.port; node.endpoint = config.endpoint; node.alternateHostname = config.alternateHostname; // limits node.maxAllowedSessionNumber = parseInt(config.maxAllowedSessionNumber) || 10; node.maxConnectionsPerEndpoint = parseInt(config.maxConnectionsPerEndpoint) || 10; node.maxAllowedSubscriptionNumber = parseInt(config.maxAllowedSubscriptionNumber) || 50; node.maxNodesPerRead = config.maxNodesPerRead || 1000; node.maxNodesPerBrowse = config.maxNodesPerBrowse || 2000; node.delayToClose = config.delayToClose || 1000; node.showStatusActivities = config.showStatusActivities; node.showErrors = config.showErrors; // certificates node.individualCerts = config.individualCerts; node.publicCertificateFile = config.publicCertificateFile; node.privateCertificateFile = config.privateCertificateFile; // Security node.allowAnonymous = config.allowAnonymous; // User Management node.opcuaUsers = config.users; // XML-Set Management node.xmlsets = config.xmlsets; // Audit node.isAuditing = config.isAuditing; // discovery node.disableDiscovery = !config.serverDiscovery; node.registerServerMethod = config.registerServerMethod || 1; node.discoveryServerEndpointUrl = config.discoveryServerEndpointUrl; node.capabilitiesForMDNS = (config.capabilitiesForMDNS) ? config.capabilitiesForMDNS.split(',') : [config.capabilitiesForMDNS]; }; const initServerNode = function (node) { node.iiot = (0, opcua_iiot_core_1.initCoreServerNode)(); if (node.setMaxListeners) node.setMaxListeners(UNLIMITED_LISTENERS); }; const loadNodeSets = function (node, dirname) { let standardNodeSetFile = node_opcua_1.nodesets.standard; let xmlFiles = [standardNodeSetFile]; if (node.xmlsets) { node.xmlsets.forEach((xmlsetFileName) => { detailDebugLog('Load XML Set for ' + xmlsetFileName.name); if (xmlsetFileName.path) { if (xmlsetFileName.path.startsWith('public/vendor/')) { xmlFiles.push(path_1.default.join(dirname, xmlsetFileName.path)); } else { xmlFiles.push(xmlsetFileName.path); } } }); detailDebugLog('appending xmlFiles: ' + xmlFiles.toString()); } detailDebugLog('node set:' + xmlFiles.toString()); node.iiot.xmlFiles = xmlFiles; }; const loadCertificates = function (node) { const nodeOPCUAServerPath = (0, opcua_iiot_core_1.getNodeOPCUAServerPath)(); detailDebugLog('config: ' + node.publicCertificateFile); if (!node.individualCerts || node.publicCertificateFile === null || node.publicCertificateFile === '') { node.publicCertificateFile = path_1.default.join(nodeOPCUAServerPath, '/certificates/server_selfsigned_cert_2048.pem'); detailDebugLog('default key: ' + node.publicCertificateFile); } detailDebugLog('config: ' + node.privateCertificateFile); if (!node.individualCerts || node.privateCertificateFile === null || node.privateCertificateFile === '') { node.privateCertificateFile = path_1.default.join(nodeOPCUAServerPath, '/certificates/PKI/own/private/private_key.pem'); detailDebugLog('default key: ' + node.privateCertificateFile); } return node; }; const checkUser = function (node, userName, password) { // valid if there is no user in the server if (node.opcuaUsers && node.opcuaUsers.length < 1) return true; let isValidUser = false; detailDebugLog('Server User Request For ' + userName); node.opcuaUsers.forEach(function (user) { if (userName === user.name && password === user.password) { isValidUser = true; } }); if (isValidUser) { detailDebugLog('Valid Server User Found'); } else { detailDebugLog('Server User ' + userName + ' Not Found'); } return isValidUser; }; const initRegisterServerMethod = function (node) { node.iiot.initialized = false; node.iiot.opcuaServer = null; if (!node.registerServerMethod) { node.registerServerMethod = node_opcua_1.RegisterServerMethod.HIDDEN; } else { switch (parseInt(node.registerServerMethod)) { case 2: node.registerServerMethod = node_opcua_1.RegisterServerMethod.MDNS; break; case 3: node.registerServerMethod = node_opcua_1.RegisterServerMethod.LDS; break; default: node.registerServerMethod = node_opcua_1.RegisterServerMethod.HIDDEN; } } return node; }; const setDiscoveryOptions = function (node, serverOptions) { if (!node.disableDiscovery) { serverOptions.registerServerMethod = node.registerServerMethod; if (node.discoveryServerEndpointUrl && node.discoveryServerEndpointUrl !== '') { serverOptions.discoveryServerEndpointUrl = node.discoveryServerEndpointUrl; } if (node.capabilitiesForMDNS && node.capabilitiesForMDNS.length) { serverOptions.capabilitiesForMDNS = node.capabilitiesForMDNS; } } else { node.registerServerMethod = node_opcua_1.RegisterServerMethod.HIDDEN; } return serverOptions; }; const getAddressSpace = function (node, msg, errorHandler) { var _a, _b; if (!((_b = (_a = node.iiot.opcuaServer) === null || _a === void 0 ? void 0 : _a.engine) === null || _b === void 0 ? void 0 : _b.addressSpace)) { errorHandler(new Error('Server AddressSpace Not Valid'), msg); return null; } return node.iiot.opcuaServer.engine.addressSpace; }; const addVariableToAddressSpace = function (node, msg, humanReadableType, isProperty, errorHandler) { let addressSpace = getAddressSpace(node, msg, errorHandler); if (!addressSpace) { return; } let rootFolder = addressSpace.findNode(msg.payload.referenceNodeId); let variableData = (0, opcua_iiot_core_1.getVariantValue)(msg.payload.datatype, msg.payload.value); let newNodeOPCUAVariable = {}; if (isProperty) { newNodeOPCUAVariable = { propertyOf: rootFolder // modellingRule: 'Mandatory' }; } else { newNodeOPCUAVariable = { componentOf: rootFolder }; } newNodeOPCUAVariable.nodeId = msg.payload.nodeId; newNodeOPCUAVariable.browseName = msg.payload.browsename; newNodeOPCUAVariable.displayName = new node_opcua_1.LocalizedText({ locale: "en-US", text: msg.payload.displayname }); newNodeOPCUAVariable.dataType = msg.payload.datatype; newNodeOPCUAVariable.value = { get() { return new node_opcua_1.Variant({ dataType: node_opcua_1.DataType[msg.payload.datatype], value: variableData }); }, set(variant) { variableData = variant.value; return node_opcua_1.StatusCodes.Good; } }; addressSpace.getOwnNamespace().addVariable(newNodeOPCUAVariable); internalDebugLog(msg.payload.nodeId + ' ' + humanReadableType + ' Added To Address Space'); }; const addObjectToAddressSpace = function (node, msg, humanReadableType, errorHandler) { let addressSpace = getAddressSpace(node, msg, errorHandler); if (!addressSpace) { return; } let rootFolder = addressSpace.findNode(msg.payload.referenceNodeId); if (rootFolder) { let newNodeOPCUObject = { organizedBy: rootFolder, typeDefinition: msg.payload.objecttype, nodeId: msg.payload.nodeId, browseName: msg.payload.browsename, displayName: new node_opcua_1.LocalizedText({ locale: null, text: msg.payload.displayname }) }; addressSpace.getOwnNamespace().addObject(newNodeOPCUObject); internalDebugLog(msg.payload.nodeId + ' ' + humanReadableType + ' Added To Address Space'); } else { errorHandler(new Error('Root Reference Not Found'), msg); } }; const deleteNodeFromAddressSpace = function (node, msg, errorHandler) { let addressSpace = getAddressSpace(node, msg, errorHandler); if (!addressSpace) { return; } if (msg.payload.nodeId) { let searchedNode = addressSpace.findNode(msg.payload.nodeId); if (searchedNode) { internalDebugLog('Delete NodeId ' + msg.payload.nodeId); addressSpace.deleteNode(searchedNode); } else { internalDebugLog('Delete NodeId Not Found ' + msg.payload.nodeId); } } else { errorHandler(new Error('OPC UA Command NodeId Not Valid'), msg); } }; const restartServer = function (node, statusHandler, emitHandler, sendHandler) { if (node.iiot.opcuaServer) { node.iiot.opcuaServer.shutdown(function () { emitHandler('shutdown'); }); } else { node.iiot.opcuaServer = null; emitHandler('shutdown'); } sendHandler({ payload: 'server shutdown' }); node.oldStatusParameter = (0, opcua_iiot_core_1.setNodeStatusTo)(node, 'shutdown', node.oldStatusParameter, node.showStatusActivities, statusHandler); }; const createServerNameWithPrefix = function (serverPort, prefix) { let serverPrefix = (prefix !== '') ? prefix + '-' : prefix; return 'NodeRED-IIoT-' + serverPrefix + 'Server-' + serverPort; }; const buildServerOptions = (node, prefix) => __awaiter(void 0, void 0, void 0, function* () { let today = new Date(); // const SecurityPolicy = require("node-opcua").SecurityPolicy; return { defaultSecureTokenLifetime: 60000000, port: typeof node.port === 'string' ? parseInt(node.port) : node.port, nodeset_filename: node.iiot.xmlFiles, resourcePath: '/' + node.endpoint || 'UA/NodeRED' + prefix + 'IIoTServer', buildInfo: { productName: node.name || 'NodeOPCUA IIoT Server', buildNumber: today.getTime().toString(), buildDate: today }, serverCapabilities: { operationLimits: { maxNodesPerRead: node.maxNodesPerRead, maxNodesPerBrowse: node.maxNodesPerBrowse } }, serverInfo: { applicationType: node_opcua_1.ApplicationType.Server, applicationUri: (0, node_opcua_1.makeApplicationUrn)(yield (0, node_opcua_1.extractFullyQualifiedDomainName)(), createServerNameWithPrefix(node.port, prefix)), productUri: createServerNameWithPrefix(node.port, prefix), applicationName: { text: 'Node-RED', locale: 'en' }, gatewayServerUri: null, discoveryProfileUri: null, discoveryUrls: [] }, maxAllowedSessionNumber: node.maxAllowedSessionNumber, maxConnectionsPerEndpoint: node.maxConnectionsPerEndpoint, allowAnonymous: node.allowAnonymous, securityPolicies: [ node_opcua_1.SecurityPolicy.None, node_opcua_1.SecurityPolicy.Basic128, node_opcua_1.SecurityPolicy.Basic192, node_opcua_1.SecurityPolicy.Basic192Rsa15, node_opcua_1.SecurityPolicy.Basic256Rsa15, node_opcua_1.SecurityPolicy.Basic256Sha256, node_opcua_1.SecurityPolicy.Aes128_Sha256_RsaOaep, node_opcua_1.SecurityPolicy.Aes256_Sha256_RsaPss, node_opcua_1.SecurityPolicy.PubSub_Aes128_CTR, node_opcua_1.SecurityPolicy.PubSub_Aes256_CTR ], certificateFile: node.publicCertificateFile, privateKeyFile: node.privateCertificateFile, alternateHostname: node.alternateHostname || '', userManager: checkUser, isAuditing: node.isAuditing, registerServerMethod: node.registerServerMethod, disableDiscovery: node.disableDiscovery }; }); const createServer = (node, serverOptions, postInitialize, statusHandler, verbose = false) => __awaiter(void 0, void 0, void 0, function* () { /* istanbul ignore next */ if (verbose) { coreServer.flexDetailDebugLog('serverOptions:' + JSON.stringify(serverOptions)); } const options = yield serverOptions; node.iiot.opcuaServer = yield coreServer.createServerObject(node.maxAllowedSubscriptionNumber, options); node.oldStatusParameter = (0, opcua_iiot_core_1.setNodeStatusTo)(node, 'waiting', node.oldStatusParameter, node.showStatusActivities, statusHandler); yield node.iiot.opcuaServer.initialize(); postInitialize(); coreServer.setOPCUAServerListener(node); }); const createServerObject = (maxSubscriptions, serverOptions) => __awaiter(void 0, void 0, void 0, function* () { // TODO: now via serverOptions.serverCapabilities.maxSessions serverOptions.maxAllowedSessionNumber = maxSubscriptions || 10; let server = new node_opcua_1.OPCUAServer(serverOptions); // TODO: later at server.engine.serverCapabilities.maxSessions = maxSubscriptions || 10 return server; }); const setOPCUAServerListener = function (node) { node.iiot.opcuaServer.on('newChannel', function (channel) { internalDebugLog('Client connected new channel with address = ', channel.remoteAddress, ' port = ', channel.remotePort); }); node.iiot.opcuaServer.on('closeChannel', function (channel) { internalDebugLog('Client disconnected close channel with address = ', channel.remoteAddress, ' port = ', channel.remotePort); }); node.iiot.opcuaServer.on('post_initialize', function () { internalDebugLog('initialized'); }); }; const coreServer = { internalDebugLog, detailDebugLog, isa95DebugLog, isa95DetailDebugLog, flexInternalDebugLog, flexDetailDebugLog, maxTimeInterval, timeInterval, simulatorInterval, simulateVariation, constructAddressSpaceFromScript, constructAddressSpace, destructAddressSpace, start, readConfigOfServerNode, initServerNode, loadNodeSets, loadCertificates, checkUser, initRegisterServerMethod, setDiscoveryOptions, getAddressSpace, addVariableToAddressSpace, addObjectToAddressSpace, deleteNodeFromAddressSpace, restartServer, createServerNameWithPrefix, createServerObject, setOPCUAServerListener, buildGeneralServerOptions, createServer, }; exports.default = coreServer; //# sourceMappingURL=opcua-iiot-core-server.js.map