node-red-contrib-opcua-server-refresh
Version:
Fork of the original 'node-red-contrib-opcua-server' package that is no longer maintained. This has been refactored to support the latest version of node-opcua and fixes incomplete/non-working features.
42 lines • 8.18 kB
JSON
[
{
"id": "ae470607af71d684",
"type": "opcua-compact-server-refresh",
"z": "2e8c7f5c.ab73d",
"port": 54840,
"endpoint": "",
"productUri": "",
"acceptExternalCommands": true,
"maxAllowedSessionNumber": 10,
"maxConnectionsPerEndpoint": 10,
"maxAllowedSubscriptionNumber": 100,
"alternateHostname": "",
"name": "",
"showStatusActivities": false,
"showErrors": false,
"allowAnonymous": true,
"individualCerts": false,
"isAuditing": false,
"serverDiscovery": true,
"users": [],
"xmlsetsOPCUA": [],
"publicCertificateFile": "",
"privateCertificateFile": "",
"registerServerMethod": 1,
"discoveryServerEndpointUrl": "",
"capabilitiesForMDNS": "",
"maxNodesPerRead": 1000,
"maxNodesPerWrite": 1000,
"maxNodesPerHistoryReadData": 100,
"maxNodesPerBrowse": 3000,
"maxBrowseContinuationPoints": 10,
"maxHistoryContinuationPoints": 10,
"delayToInit": 1000,
"delayToClose": 200,
"serverShutdownTimeout": 100,
"addressSpaceScript": "function(server, addressSpace, opcua, eventObjects, done) {\n\n node.warn(\"Starting OPCUA Server\");\n\n // Debug Statements to verify eventObjects and sandboxFlowContext\n if (!opcua) {\n node.error(\"OPCUA module is not available.\");\n return done(new Error(\"OPCUA module is undefined.\"));\n }\n\n const namespace = addressSpace.getOwnNamespace();\n\n const Variant = opcua.Variant;\n const DataType = opcua.DataType;\n const VariantArrayType = opcua.VariantArrayType;\n const DataValue = opcua.DataValue;\n const standardUnits = opcua.standardUnits;\n\n //Define ISA-95 folder structure for UNS\n const rootFolder = addressSpace.findNode(\"RootFolder\");\n\n const enterprise = namespace.addFolder(rootFolder.objects, { browseName: \"sji\" });\n\n const site = namespace.addFolder(enterprise, { browseName: \"garfield\" });\n\n const area = namespace.addFolder(site, { browseName: \"bop\" });\n\n const subsystem = namespace.addFolder(area, { browseName: \"flare\" });\n /*\n * variation 0:\n * ------------\n *\n * Add a variable in folder using a raw Variant.\n * Use this variation when the variable has to be read or written by the OPCUA clients\n */\n const variable0 = namespace.addVariable({\n organizedBy: subsystem,\n browseName: \"FanSpeed\",\n nodeId: \"ns=1;s=FanSpeed\",\n dataType: \"Double\",\n value: new Variant({ dataType: DataType.Double, value: 1000.0 })\n });\n\n setInterval(function () {\n const fluctuation = Math.random() * 100 - 50;\n variable0.setValueFromSource(new Variant({ dataType: DataType.Double, value: 1000.0 + fluctuation }));\n }, 10);\n \n /*\n * variation 1:\n * ------------\n *\n * Add a variable in folder using a single get function which returns the up to date variable value in Variant.\n * The server will set the timestamps automatically for us.\n * Use this variation when the variable value is controlled by the getter function\n * Avoid using this variation if the variable has to be made writable, as the server will call the getter\n * function prior to returning its value upon client read requests.\n */\n namespace.addVariable({\n organizedBy: subsystem,\n browseName: \"PumpSpeed\",\n nodeId: \"ns=1;s=PumpSpeed\",\n dataType: \"Double\",\n value: {\n /**\n * returns the current value as a Variant\n * @method get\n * @return {Variant}\n */\n get: function () {\n const pump_speed = 200 + 100 * Math.sin(Date.now() / 10000);\n return new Variant({ dataType: DataType.Double, value: pump_speed });\n }\n }\n });\n\n namespace.addVariable({\n organizedBy: subsystem,\n browseName: \"SomeDate\",\n nodeId: \"ns=1;s=SomeDate\",\n dataType: \"DateTime\",\n value: {\n get: function () {\n return new Variant({ dataType: DataType.DateTime, value: new Date(Date.UTC(2016, 9, 13, 8, 40, 0)) });\n }\n }\n });\n\n /*\n * variation 2:\n * ------------\n *\n * Add a variable in folder. This variable gets its value and source timestamps from the provided function.\n * The value and source timestamps are held in a external object.\n * The value and source timestamps are updated on a regular basis using a timer function.\n */\n const external_value_with_sourceTimestamp = new DataValue({\n value: new Variant({ dataType: DataType.Double, value: 10.0 }),\n sourceTimestamp: null,\n sourcePicoseconds: 0\n });\n setInterval(function () {\n external_value_with_sourceTimestamp.value.value = Math.random();\n external_value_with_sourceTimestamp.sourceTimestamp = new Date();\n }, 1000);\n\n namespace.addVariable({\n organizedBy: subsystem,\n browseName: \"Pressure\",\n nodeId: \"ns=1;s=Pressure\",\n dataType: \"Double\",\n value: {\n timestamped_get: function () {\n return external_value_with_sourceTimestamp;\n }\n }\n });\n /*\n * variation 3:\n * ------------\n *\n * Add a variable in a folder. This variable gets its value and source timestamps from the provided\n * asynchronous function.\n * The asynchronous function is called only when needed by the opcua Server read services and monitored item services\n *\n */\n\n namespace.addVariable({\n organizedBy: subsystem,\n browseName: \"Temperature\",\n nodeId: \"s=Temperature\",\n dataType: \"Double\",\n\n value: {\n refreshFunc: function (callback) {\n const temperature = 20 + 10 * Math.sin(Date.now() / 10000);\n const value = new Variant({ dataType: DataType.Double, value: temperature });\n const sourceTimestamp = new Date();\n\n // simulate a asynchronous behaviour\n setTimeout(function () {\n callback(null, new DataValue({ value: value, sourceTimestamp: sourceTimestamp }));\n }, 100);\n }\n }\n });\n\n // UAAnalogItem\n // add a UAAnalogItem\n const analogNode = namespace.addAnalogDataItem({\n organizedBy: subsystem,\n\n nodeId: \"s=TemperatureAnalogItem\",\n browseName: \"TemperatureAnalogItem\",\n definition: \"(tempA -25) + tempB\",\n valuePrecision: 0.5,\n engineeringUnitsRange: { low: 100, high: 200 },\n instrumentRange: { low: -100, high: +200 },\n engineeringUnits: standardUnits.degree_celsius,\n dataType: \"Double\",\n value: {\n get: function () {\n return new Variant({ dataType: DataType.Double, value: Math.random() + 19.0 });\n }\n }\n });\n\n const m3x3 = namespace.addVariable({\n organizedBy: addressSpace.rootFolder.objects,\n nodeId: \"s=Matrix\",\n browseName: \"Matrix\",\n dataType: \"Double\",\n valueRank: 2,\n arrayDimensions: [3, 3],\n value: {\n get: function () {\n return new Variant({\n dataType: DataType.Double,\n arrayType: VariantArrayType.Matrix,\n dimensions: [3, 3],\n value: [1, 2, 3, 4, 5, 6, 7, 8, 9]\n });\n }\n }\n });\n\n const xyz = namespace.addVariable({\n organizedBy: addressSpace.rootFolder.objects,\n nodeId: \"s=Position\",\n browseName: \"Position\",\n dataType: \"Double\",\n valueRank: 1,\n arrayDimensions: null,\n value: {\n get: function () {\n return new Variant({\n dataType: DataType.Double,\n arrayType: VariantArrayType.Array,\n value: [1, 2, 3, 4]\n });\n }\n }\n });\n \n //------------------------------------------------------------------------------\n // Add a view\n //------------------------------------------------------------------------------\n const view = namespace.addView({\n organizedBy: rootFolder.views,\n browseName: \"MyView\"\n });\n\n view.addReference({\n referenceType: \"Organizes\",\n nodeId: analogNode.nodeId\n });\n node.warn(\"Construction of new address space for OPC UA done\");\n done();\n}",
"x": 530,
"y": 320,
"wires": []
}
]