@vitormnm/node-red-simple-opcua 1.6.2 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +89 -136
  2. package/client/lib/opcua-client-browser.js +238 -10
  3. package/client/lib/opcua-client-method-service.js +1 -1
  4. package/client/lib/opcua-client-subscription-service.js +0 -2
  5. package/client/opcua-client-config.html +118 -1
  6. package/client/opcua-client-config.js +74 -8
  7. package/client/opcua-client-help.html +6 -0
  8. package/client/opcua-client-utils.js +34 -10
  9. package/client/opcua-client.html +7 -0
  10. package/client/opcua-client.js +97 -1
  11. package/examples/flows_simple_opc.json +1 -1
  12. package/package.json +1 -1
  13. package/server/lib/opcua-address-space-alarm.js +11 -5
  14. package/server/lib/opcua-address-space-builder.js +65 -15
  15. package/server/lib/opcua-config.js +81 -23
  16. package/server/lib/opcua-server-events-child.js +1 -1
  17. package/server/lib/opcua-server-runtime-child.js +429 -59
  18. package/server/lib/opcua-server-runtime.js +49 -5
  19. package/server/lib/opcua-server-status-child.js +14 -14
  20. package/server/nodered/simple_opcua/server/certificates/mutex +0 -0
  21. package/server/nodered/simple_opcua/server/certificates/own/certs/server_selfsigned_cert_2048.pem +25 -0
  22. package/server/nodered/simple_opcua/server/certificates/own/certs/server_selfsigned_cert_2048.pem.mutex +0 -0
  23. package/server/nodered/simple_opcua/server/certificates/own/openssl.cnf +72 -0
  24. package/server/nodered/simple_opcua/server/certificates/own/private/private_key.pem +28 -0
  25. package/server/nodered/simple_opcua/server/certificates/trusted/certs/NodeOPCUA-Client@tuf[c5a9e20a8b680cdff76aaf0165bb3c9318da37a5].pem +25 -0
  26. package/server/nodered/simple_opcua/server/myServer1/mutex +0 -0
  27. package/server/nodered/simple_opcua/server/myServer1/own/certs/server_selfsigned_cert_2048.pem +25 -0
  28. package/server/nodered/simple_opcua/server/myServer1/own/certs/server_selfsigned_cert_2048.pem.mutex +0 -0
  29. package/server/nodered/simple_opcua/server/myServer1/own/openssl.cnf +72 -0
  30. package/server/nodered/simple_opcua/server/myServer1/own/private/private_key.pem +28 -0
  31. package/server/nodered/simple_opcua/server/myServer1/trusted/certs/NodeOPCUA-Client@tuf[91e520c64ff891c67168f08a46dd194071e15dae].pem +25 -0
  32. package/server/nodered/simple_opcua/server/myServer1/trusted/certs/NodeOPCUA-Client@tuf[98ae95da627cea4c500753c319161a3554ee38d7].pem +25 -0
  33. package/server/nodered/simple_opcua/server/myServer1/trusted/certs/NodeOPCUA-Client@tuf[aef8d7a1cfba13d84189a0bcf1694208fc51a7f9].pem +25 -0
  34. package/server/nodered/simple_opcua/server/myServer1/trusted/certs/NodeOPCUA-Client@tuf[c5a9e20a8b680cdff76aaf0165bb3c9318da37a5].pem +25 -0
  35. package/server/nodered/simple_opcua/server/myServer1/trusted/certs/NodeOPCUA-Client@tuf[ebdf9acf1d02e347917a14108d3144799c638ea3].pem +25 -0
  36. package/server/opcua-server-io.html +76 -0
  37. package/server/opcua-server-io.js +135 -23
  38. package/server/opcua-server.css +52 -0
  39. package/server/opcua-server.html +166 -44
  40. package/server/opcua-server.js +142 -7
  41. package/server/view/opcua-server.css +89 -6
  42. package/server/view/opcua-server.js +523 -42
package/README.md CHANGED
@@ -1,136 +1,89 @@
1
- OPC UA client and server with a simple graphical interface for Node-RED.
2
- Fully parameterized in JSON.
3
-
4
- It supports the following OPC UA items on only 3 nodes.
5
-
6
- - alarms
7
- - events
8
- - events read and write tags in server(See which tags are being written to or read from the client directly on the server in a simple workflow)
9
- - methods(write methods in node-red flow)
10
- - variables
11
- - variables arrays
12
- - description and displayname nodes
13
- - objects
14
- - simple objectsType
15
- - custom namespace
16
- - custom nodeID
17
- - Subscription Variables and events in client
18
-
19
- **Server editor**
20
- ![node-red-si](/resources/editorServer.PNG)
21
-
22
- **Client editor**
23
- ![node-red-si](/resources/editorClient.PNG)
24
-
25
- ### Support the development of this project and others if you found it useful.
26
- <a href="https://buymeacoffee.com/vitormnm">
27
- <img src="./resources/bmc-button.svg" alt="Logo" width="200">
28
- </a>
29
-
30
- ---
31
-
32
- example json server config
33
- ```
34
- {
35
- "objects": [],
36
- "folders": [
37
- {
38
- "name": "MyServer",
39
- "displayName": "",
40
- "description": "",
41
- "nodeId": "",
42
- "namespaceId": 2,
43
- "objectsType": "",
44
- "folders": [
45
- {
46
- "name": "newFolder",
47
- "displayName": "",
48
- "description": "",
49
- "nodeId": "",
50
- "namespaceId": 2,
51
- "objectsType": "",
52
- "folders": [],
53
- "objects": [],
54
- "variables": [
55
- {
56
- "name": "newVariable",
57
- "type": "Int32",
58
- "value": "",
59
- "access": "readwrite",
60
- "description": "",
61
- "displayName": "",
62
- "nodeId": "",
63
- "namespaceId": 2
64
- }
65
- ],
66
- "alarms": [],
67
- "methods": [],
68
- "objectsTypes": []
69
- }
70
- ],
71
- "objects": [
72
- {
73
- "name": "newObject",
74
- "displayName": "",
75
- "description": "",
76
- "nodeId": "",
77
- "namespaceId": 2,
78
- "objectsType": "",
79
- "folders": [],
80
- "objects": [],
81
- "variables": [
82
- {
83
- "name": "newVariable",
84
- "type": "Int32",
85
- "value": "",
86
- "access": "readwrite",
87
- "description": "",
88
- "displayName": "",
89
- "nodeId": "",
90
- "namespaceId": 2
91
- }
92
- ],
93
- "alarms": [],
94
- "methods": [],
95
- "objectsTypes": []
96
- }
97
- ],
98
- "variables": [
99
- {
100
- "name": "newVariable",
101
- "type": "Int32",
102
- "value": "",
103
- "access": "readwrite",
104
- "description": "",
105
- "displayName": "",
106
- "nodeId": "",
107
- "namespaceId": 2
108
- }
109
- ],
110
- "alarms": [],
111
- "methods": [
112
- {
113
- "name": "newMethod",
114
- "description": "",
115
- "displayName": "",
116
- "nodeId": "",
117
- "namespaceId": 2,
118
- "inputs": [],
119
- "outputs": []
120
- }
121
- ],
122
- "objectsTypes": []
123
- }
124
- ],
125
- "objectsTypes": [],
126
- "nameSpaces": [
127
- {
128
- "id": 2,
129
- "name": "urn:node-red:opc-ua-server"
130
- }
131
- ]
132
- }
133
- ```
134
- Disclaimer
135
- This node was only used in simulation and testing environments.
136
-
1
+ # @vitormnm/node-red-simple-opcua
2
+
3
+ OPC UA client and server with a simple graphical interface for Node-RED.
4
+
5
+ This package provides a simplified, highly parameterized set of nodes to establish OPC UA Servers and Clients in Node-RED without complex coding.
6
+
7
+ ---
8
+
9
+ ## Features Overview
10
+
11
+ - **Dynamic OPC UA Address Space**: Build hierarchical folders, objects, variables, and alarms dynamically from a visual tree editor or JSON.
12
+ - **IPC Process Separation**: Server runtime runs in a child process, preventing heavy OPC UA operations from blocking the main Node-RED event loop.
13
+ - **Rich Node-RED Integration**: Support for reading, writing, subscribing, calling methods, and managing sessions directly in Node-RED flows.
14
+
15
+ ---
16
+
17
+ ## Server Functionalities
18
+
19
+ The server implementation consists of two nodes: `opc-ua-server` (the server runtime) and `opcua-server-io` (the data input/output node).
20
+
21
+ ### 1. OPC UA Server (`opc-ua-server`)
22
+ This node instantiates the OPC UA Server process.
23
+ - **Visual Tree Editor**: Build folders, variables, custom objects, ObjectTypes (templates), alarms, methods, and enumerations visually.
24
+ - **Dynamic Address Space**: Rebuild the address space at runtime by passing a new JSON tree configuration in `msg.payload`.
25
+ - **Authentication & Authorization**: Configure users, groups, and comma-separated access permissions for folders, variables, and methods.
26
+ - **Security Policies**: Supports multiple security policies (`None`, `Basic256Sha256`, `Aes128_Sha256_RsaOaep`, etc.) and security modes (`None`, `Sign`, `SignAndEncrypt`).
27
+ - **Certificate Management**: Manage trusted/rejected client certificates.
28
+
29
+ ### 2. Server I/O Node (`opcua-server-io`)
30
+ Interacts with an active local server instance. It supports the following modes:
31
+ - **Read**: Read values from server variables using a tag path or NodeId.
32
+ - **Write**: Write values to server variables.
33
+ - **Event**: Fire custom OPC UA events on target nodes.
34
+ - **Events**: Stream variable read, write, and alarm events.
35
+ - **Status**: Monitor server variable status snapshots.
36
+ - **Active Alarms**: Query active alarms on the server.
37
+ - **Get Sessions**: Retrieve active OPC UA client sessions.
38
+ - **Delete Sessions**: Forcefully close specific active sessions by ID.
39
+ - **Method Input / Output**: Route OPC UA method calls from clients into Node-RED flows and return output results.
40
+
41
+ ---
42
+
43
+ ## Client Functionalities
44
+
45
+ The client implementation consists of `opcua-client-config` (shared connection configuration) and `opcua-client` (the action node).
46
+
47
+ ### 1. Client Connection Config (`opcua-client-config`)
48
+ Manages connection configuration to an external OPC UA server.
49
+ - **Connection Strategy**: Limit reconnect retries to fail fast when servers are offline.
50
+ - **Authentication**: Supports anonymous or username/password authentication.
51
+ - **Session Caching**: Caches and reuse client sessions and method definitions.
52
+ - **Address Space Browser**: Provides an endpoint for browsing the server address space directly in the Node-RED editor.
53
+
54
+ ### 2. OPC UA Client (`opcua-client`)
55
+ Performs actions on the configured OPC UA server. It operates in the following modes:
56
+ - **Read**: Batch read selected variables.
57
+ - **Write**: Batch write values with automatic type resolution.
58
+ - **Browse**: Browse child nodes of one or more node paths.
59
+ - **Browse Recursive**: Recursively search all nodes starting from specified NodeIds, returning resolved type definitions, values, descriptions, and method arguments in optimized batched reads.
60
+ - **Method**: Call OPC UA methods on the server.
61
+ - **Subscription**: Subscribe to variable value updates (minimum 50ms sampling/publishing).
62
+ - **Events**: Subscribe to OPC UA events.
63
+
64
+ ---
65
+
66
+ ## Editor Interfaces
67
+
68
+ ### Server Editor
69
+ ![Server Editor](./resources/editorServer.PNG)
70
+
71
+ ### Client Editor
72
+ ![Client Editor](./resources/editorClient.PNG)
73
+
74
+ ---
75
+
76
+
77
+
78
+
79
+ ### Support the Project
80
+ If you find this project useful, please support its development:
81
+
82
+ <a href="https://buymeacoffee.com/vitormnm">
83
+ <img src="./resources/bmc-button.svg" alt="Buy Me A Coffee" width="200">
84
+ </a>
85
+
86
+
87
+ [@vitormnm](https://vitormiao.com/)
88
+
89
+ [![MIT License](https://img.shields.io/badge/License-MIT-green.svg)](https://choosealicense.com/licenses/mit/)
@@ -9,12 +9,15 @@ const {
9
9
  makeNodeId
10
10
  } = require("node-opcua");
11
11
 
12
+ const { enrichItemResultWithEnumeration } = require("../opcua-client-utils");
13
+
12
14
  async function browseNode(session, root) {
13
15
  const nodeID = normalizeNodeId(root.nodeID || root.nodeId || ROOT_NODE_ID);
14
16
  const result = {
15
17
  name: root.name || await readBrowseName(session, nodeID, "RootFolder"),
16
18
  nodeID,
17
- browse: []
19
+ status: "Good",
20
+ children: []
18
21
  };
19
22
 
20
23
 
@@ -30,11 +33,16 @@ async function browseNode(session, root) {
30
33
 
31
34
  let browseResult = await session.browse({
32
35
  nodeId: nodeID,
36
+ referenceTypeId: makeNodeId(33, 0), // HierarchicalReferences
33
37
  browseDirection: BrowseDirection.Forward,
34
38
  includeSubtypes: true,
35
39
  resultMask: 63
36
40
  });
37
41
 
42
+ if (browseResult.statusCode && !browseResult.statusCode.isGood()) {
43
+ throw new Error("Browse failed: " + browseResult.statusCode.toString());
44
+ }
45
+
38
46
  let references = [
39
47
  ...(browseResult.references || [])
40
48
  ];
@@ -61,19 +69,34 @@ async function browseNode(session, root) {
61
69
 
62
70
  // Monta lista de todos os atributos de todos os nós de uma vez
63
71
  const nodeIds = references.map(ref => normalizeNodeId(ref.nodeId));
64
-
65
-
66
- const attributesToRead = nodeIds.flatMap(nodeId => [
67
- { nodeId, attributeId: AttributeIds.Description },
68
- { nodeId, attributeId: AttributeIds.DataType },
69
- { nodeId, attributeId: AttributeIds.Value },
70
- ]);
72
+ const typeIds = references
73
+ .map(ref => ref.typeDefinition ? normalizeNodeId(ref.typeDefinition) : null)
74
+ .filter(Boolean);
75
+ const uniqueTypeIds = [...new Set(typeIds)];
76
+
77
+ const attributesToRead = [
78
+ ...nodeIds.flatMap(nodeId => [
79
+ { nodeId, attributeId: AttributeIds.Description },
80
+ { nodeId, attributeId: AttributeIds.DataType },
81
+ { nodeId, attributeId: AttributeIds.Value },
82
+ ]),
83
+ ...uniqueTypeIds.map(nodeId => ({ nodeId, attributeId: AttributeIds.BrowseName }))
84
+ ];
71
85
 
72
86
  // UMA única chamada para todos os nós e atributos
73
87
  const dataValues = await session.read(attributesToRead);
74
88
 
89
+ const typeNamesMap = new Map();
90
+ const typeStartIdx = nodeIds.length * 3;
91
+ uniqueTypeIds.forEach((typeId, index) => {
92
+ const browseNameVal = dataValues[typeStartIdx + index]?.value?.value;
93
+ const name = browseNameVal?.name || typeId;
94
+ typeNamesMap.set(typeId, name);
95
+ });
96
+
97
+ const cache = new Map();
75
98
  // Distribui os resultados por nó (3 atributos por nó)
76
- result.browse = await Promise.all(references.map(async (reference, i) => {
99
+ result.children = await Promise.all(references.map(async (reference, i) => {
77
100
  const childNodeId = nodeIds[i];
78
101
  const nodeClass = resolveNodeClassName(reference.nodeClass);
79
102
  const browseName = extractBrowseName(reference.browseName, childNodeId);
@@ -86,15 +109,33 @@ async function browseNode(session, root) {
86
109
 
87
110
  const item = { nodeID: childNodeId, nodeClass, browseName, displayName, description };
88
111
 
112
+ const typeNodeId = reference.typeDefinition ? normalizeNodeId(reference.typeDefinition) : null;
113
+ if (typeNodeId) {
114
+ const typeName = typeNamesMap.get(typeNodeId) || typeNodeId;
115
+ item.typeDefinition = typeNodeId;
116
+ item.hasTypeDefinition = {
117
+ nodeID: typeNodeId,
118
+ browseName: typeName,
119
+ displayName: typeName
120
+ };
121
+ }
122
+
89
123
  if (nodeClass === "Variable") {
90
124
  const dataTypeValue = dataValues[i * 3 + 1]?.value?.value;
91
- const rawValue = dataValues[i * 3 + 2]?.value?.value;
125
+ const rawValueVariant = dataValues[i * 3 + 2]?.value;
126
+ const rawValue = rawValueVariant?.value;
92
127
 
93
128
  item.dataType = dataTypeValue?.namespace === 0 && typeof dataTypeValue?.value === "number"
94
129
  ? (DataType[dataTypeValue.value] || dataTypeValue.toString())
95
130
  : (dataTypeValue?.toString() ?? "");
96
131
 
132
+ if (rawValueVariant?.dataType === DataType.Enumeration) {
133
+ item.dataType = "Enumeration";
134
+ }
135
+
97
136
  item.value = rawValue ?? "";
137
+
138
+ await enrichItemResultWithEnumeration(item, session, cache, childNodeId);
98
139
  }
99
140
 
100
141
  if (nodeClass === "Method") {
@@ -358,11 +399,198 @@ function resolveArgumentDataType(dataType) {
358
399
  return dataType.toString();
359
400
  }
360
401
 
402
+ async function browseRecursiveNode(session, root) {
403
+ const startNodeId = normalizeNodeId(root.nodeID || root.nodeId || ROOT_NODE_ID);
404
+ const rootName = root.name || await readBrowseName(session, startNodeId, "RootFolder");
405
+
406
+ const visited = new Set();
407
+ visited.add(startNodeId);
408
+
409
+ const allItems = [];
410
+
411
+ async function traverse(nodeID) {
412
+ let browseResult;
413
+ try {
414
+ browseResult = await session.browse({
415
+ nodeId: nodeID,
416
+ referenceTypeId: makeNodeId(33, 0), // HierarchicalReferences
417
+ browseDirection: BrowseDirection.Forward,
418
+ includeSubtypes: true,
419
+ resultMask: 63
420
+ });
421
+ } catch (err) {
422
+ if (nodeID === startNodeId) {
423
+ throw err;
424
+ }
425
+ return [];
426
+ }
427
+
428
+ if (browseResult.statusCode && !browseResult.statusCode.isGood()) {
429
+ if (nodeID === startNodeId) {
430
+ throw new Error("Browse failed: " + browseResult.statusCode.toString());
431
+ }
432
+ return [];
433
+ }
434
+
435
+ let references = [...(browseResult.references || [])];
436
+
437
+ while (browseResult.continuationPoint) {
438
+ try {
439
+ browseResult = await session.browseNext(
440
+ browseResult.continuationPoint,
441
+ false
442
+ );
443
+ references.push(...(browseResult.references || []));
444
+ } catch (err) {
445
+ break;
446
+ }
447
+ }
448
+
449
+ if (!references.length) {
450
+ return [];
451
+ }
452
+
453
+ const items = [];
454
+ for (const ref of references) {
455
+ const childNodeId = normalizeNodeId(ref.nodeId);
456
+ const nodeClass = resolveNodeClassName(ref.nodeClass);
457
+ const browseName = extractBrowseName(ref.browseName, childNodeId);
458
+ const displayName = extractDisplayName(ref.displayName, browseName);
459
+ const typeNodeId = ref.typeDefinition ? normalizeNodeId(ref.typeDefinition) : null;
460
+
461
+ const item = {
462
+ nodeID: childNodeId,
463
+ nodeClass,
464
+ browseName,
465
+ displayName
466
+ };
467
+ if (typeNodeId) {
468
+ item.typeDefinition = typeNodeId;
469
+ }
470
+
471
+ items.push(item);
472
+ allItems.push(item);
473
+
474
+ const expandable = nodeClass === "Object" || nodeClass === "Folder" || nodeClass === "View" || nodeClass === "ObjectType";
475
+ if (expandable && !visited.has(childNodeId)) {
476
+ visited.add(childNodeId);
477
+ item.children = await traverse(childNodeId);
478
+ }
479
+ }
480
+
481
+ return items;
482
+ }
483
+
484
+ const browseResult = await traverse(startNodeId);
485
+
486
+ if (allItems.length > 0) {
487
+ const cache = new Map();
488
+ const typeIds = allItems
489
+ .map(item => item.typeDefinition)
490
+ .filter(Boolean);
491
+ const uniqueTypeIds = [...new Set(typeIds)];
492
+
493
+ const typeNamesMap = new Map();
494
+ if (uniqueTypeIds.length > 0) {
495
+ try {
496
+ const typeAttributes = uniqueTypeIds.map(nodeId => ({ nodeId, attributeId: AttributeIds.BrowseName }));
497
+ const typeDataValues = await session.read(typeAttributes);
498
+ uniqueTypeIds.forEach((typeId, index) => {
499
+ const browseNameVal = typeDataValues[index]?.value?.value;
500
+ const name = browseNameVal?.name || typeId;
501
+ typeNamesMap.set(typeId, name);
502
+ });
503
+ } catch (err) {
504
+ // Ignore type names read error, we'll fallback to NodeId below
505
+ }
506
+ }
507
+
508
+ const BATCH_SIZE = 100;
509
+ for (let i = 0; i < allItems.length; i += BATCH_SIZE) {
510
+ const chunk = allItems.slice(i, i + BATCH_SIZE);
511
+ const attributesToRead = chunk.flatMap(item => [
512
+ { nodeId: item.nodeID, attributeId: AttributeIds.Description },
513
+ { nodeId: item.nodeID, attributeId: AttributeIds.DataType },
514
+ { nodeId: item.nodeID, attributeId: AttributeIds.Value }
515
+ ]);
516
+
517
+ try {
518
+ const dataValues = await session.read(attributesToRead);
519
+ await Promise.all(chunk.map(async (item, index) => {
520
+ const descValue = dataValues[index * 3]?.value?.value;
521
+ item.description = typeof descValue === "string"
522
+ ? descValue
523
+ : (descValue?.text ?? "");
524
+
525
+ if (item.nodeClass === "Variable") {
526
+ const dataTypeValue = dataValues[index * 3 + 1]?.value?.value;
527
+ const rawValueVariant = dataValues[index * 3 + 2]?.value;
528
+ const rawValue = rawValueVariant?.value;
529
+
530
+ item.dataType = dataTypeValue?.namespace === 0 && typeof dataTypeValue?.value === "number"
531
+ ? (DataType[dataTypeValue.value] || dataTypeValue.toString())
532
+ : (dataTypeValue?.toString() ?? "");
533
+
534
+ if (rawValueVariant?.dataType === DataType.Enumeration) {
535
+ item.dataType = "Enumeration";
536
+ }
537
+
538
+ item.value = rawValue ?? "";
539
+
540
+ await enrichItemResultWithEnumeration(item, session, cache, item.nodeID);
541
+ }
542
+
543
+ if (item.typeDefinition) {
544
+ const typeName = typeNamesMap.get(item.typeDefinition) || item.typeDefinition;
545
+ item.hasTypeDefinition = {
546
+ nodeID: item.typeDefinition,
547
+ browseName: typeName,
548
+ displayName: typeName
549
+ };
550
+ }
551
+ }));
552
+ } catch (readError) {
553
+ chunk.forEach(item => {
554
+ item.description = "";
555
+ if (item.nodeClass === "Variable") {
556
+ item.dataType = "";
557
+ item.value = "";
558
+ }
559
+ if (item.typeDefinition) {
560
+ const typeName = typeNamesMap.get(item.typeDefinition) || item.typeDefinition;
561
+ item.hasTypeDefinition = {
562
+ nodeID: item.typeDefinition,
563
+ browseName: typeName,
564
+ displayName: typeName
565
+ };
566
+ }
567
+ });
568
+ }
569
+ }
570
+
571
+ const methods = allItems.filter(item => item.nodeClass === "Method");
572
+ for (const method of methods) {
573
+ const definition = await readMethodArguments(session, method.nodeID);
574
+ method.inputArguments = definition.inputArguments;
575
+ method.outputArguments = definition.outputArguments;
576
+ }
577
+ }
578
+
579
+ return {
580
+ name: rootName,
581
+ nodeID: startNodeId,
582
+ status: "Good",
583
+ children: browseResult
584
+ };
585
+ }
586
+
361
587
  const ROOT_NODE_ID = "i=84";
362
588
 
363
589
  module.exports = {
364
590
  browseNode,
591
+ browseRecursiveNode,
365
592
  normalizeBrowseRoots,
366
593
  normalizeNodeId,
367
594
  ROOT_NODE_ID
368
595
  };
596
+
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
 
3
- const { dataValueToItemResult, ensureArrayPayload, resolveNodeId, resolveMethodObjectId, buildVariantFromItem, callResultToItemResult } = require("../opcua-client-utils");
3
+ const { dataValueToItemResult, ensureArrayPayload, resolveNodeId, resolveMethodObjectId, buildVariantFromItem, callResultToItemResult, getMethodArgumentDefinition } = require("../opcua-client-utils");
4
4
 
5
5
  class OpcUaClientMethodService {
6
6
  async execute(node, msg, session, itemsResolver) {
@@ -34,7 +34,6 @@ class OpcUaClientSubscriptionService {
34
34
 
35
35
  subscription.on("error", (error) => {
36
36
  node.status({ fill: "red", shape: "ring", text: "subscription error" });
37
- node.error(error);
38
37
  });
39
38
 
40
39
  node.subscription = subscription;
@@ -97,7 +96,6 @@ class OpcUaClientSubscriptionService {
97
96
 
98
97
  subscription.on("error", (error) => {
99
98
  node.status({ fill: "red", shape: "ring", text: "subscription error" });
100
- node.error(error);
101
99
  });
102
100
 
103
101
  node.subscription = subscription;