@tier0/opcua-client 0.6.3 → 0.6.4

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.
package/nodes/read.js CHANGED
@@ -1,125 +1,130 @@
1
- 'use strict';
2
-
3
- const { createDiagnostics } = require('../lib/diagnostics');
4
-
5
- const { looksLikePointList, messagePoints } = require('../lib/points');
6
-
7
- const COMPACT_RESULT_FIELDS = Object.freeze([
8
- 'nodeId',
9
- 'alias',
10
- 'value',
11
- 'statusCode',
12
- 'sourceTimestamp',
13
- ]);
14
-
15
- function compactResult(result) {
16
- const output = {};
17
- for (const field of COMPACT_RESULT_FIELDS) {
18
- if (Object.prototype.hasOwnProperty.call(result, field)) output[field] = result[field];
19
- }
20
- return output;
21
- }
22
-
23
- function valueOnly(result) {
24
- return Object.prototype.hasOwnProperty.call(result, 'value') ? result.value : null;
25
- }
26
-
27
- module.exports = function registerReadNode(RED) {
28
- function OpcUaReadNode(config) {
29
- RED.nodes.createNode(this, config);
30
- const node = this;
31
- node.connection = RED.nodes.getNode(config.connection);
32
- const diagnostics = createDiagnostics(node, config, 'read');
33
- const consumer = node.connection?.registerConsumer?.('read', {
34
- nodeId: config.id, name: config.name,
35
- });
36
-
37
- node.on('input', async (msg, send, done) => {
38
- if (!node.connection) {
39
- const error = new Error('OPCUA_CONNECTION_NOT_CONFIGURED');
40
- diagnostics.failure(error, send);
41
- return done(error);
42
- }
43
-
44
- try {
45
- consumer?.markUsed('read');
46
- const points = messagePoints(msg, config.nodeId, {
47
- indexRange: config.indexRange,
48
- }, {
49
- configuredPayload: config.pointList,
50
- format: 'auto',
51
- });
52
- node.status({ fill: 'blue', shape: 'dot', text: `reading ${points.length}` });
53
- const values = await node.connection.client.readMany(
54
- points,
55
- Number(config.batchSize) || 500
56
- );
57
- const descriptors = new Map(points.map((point) => [point.nodeId, point]));
58
- const results = values.map((value) => ({
59
- ...descriptors.get(value.nodeId),
60
- ...value,
61
- }));
62
-
63
- // Undefined preserves the original output for Flow JSON created before
64
- // resultDetail was introduced. New editor nodes explicitly save compact.
65
- const failed = diagnostics.results(results, send);
66
- const resultDetail = config.resultDetail || 'full';
67
- const outputResults = resultDetail === 'compact'
68
- ? results.map(compactResult)
69
- : resultDetail === 'value'
70
- ? results.map(valueOnly)
71
- : results;
72
-
73
- if ((config.outputMode || 'batch') === 'split') {
74
- results.forEach((result, index) => {
75
- const output = {
76
- ...msg,
77
- topic: result.nodeId,
78
- payload: valueOnly(result),
79
- };
80
- if (resultDetail === 'value') delete output.opcua;
81
- else output.opcua = outputResults[index];
82
- send(output);
83
- });
84
- } else if (points.length === 1 && !looksLikePointList(msg.payload) &&
85
- !Array.isArray(msg.items) && !config.pointList) {
86
- const result = results[0];
87
- msg.topic = result.nodeId;
88
- msg.payload = valueOnly(result);
89
- if (resultDetail === 'value') delete msg.opcua;
90
- else msg.opcua = outputResults[0];
91
- send(msg);
92
- } else {
93
- msg.payload = outputResults;
94
- msg.opcua = {
95
- operation: 'read',
96
- total: results.length,
97
- good: results.filter((item) => item.statusCode === 'Good').length,
98
- resultDetail,
99
- };
100
- if (resultDetail === 'full') {
101
- msg.opcua.clientDiagnostics = node.connection.getDiagnostics?.() ||
102
- node.connection.client.getDiagnostics();
103
- }
104
- send(msg);
105
- }
106
-
107
- node.status({ fill: failed ? 'yellow' : 'green', shape: failed ? 'ring' : 'dot',
108
- text: `${results.length - failed}/${results.length} read` + (failed ? ` / ${failed} failed` : '') });
109
- done();
110
- } catch (error) {
111
- node.status({ fill: 'red', shape: 'ring', text: error.message });
112
- diagnostics.failure(error, send);
113
- done(error);
114
- }
115
- });
116
-
117
- node.on('close', (removed, done) => {
118
- if (typeof removed === 'function') done = removed;
119
- consumer?.release();
120
- done?.();
121
- });
122
- }
123
-
124
- RED.nodes.registerType('tier0-opcua-read', OpcUaReadNode);
125
- };
1
+ 'use strict';
2
+
3
+ const { createDiagnostics } = require('../lib/diagnostics');
4
+
5
+ const { looksLikePointList, messagePoints } = require('../lib/points');
6
+
7
+ const COMPACT_RESULT_FIELDS = Object.freeze([
8
+ 'nodeId',
9
+ 'alias',
10
+ 'value',
11
+ 'statusCode',
12
+ 'sourceTimestamp',
13
+ ]);
14
+
15
+ function compactResult(result) {
16
+ const output = {};
17
+ for (const field of COMPACT_RESULT_FIELDS) {
18
+ if (Object.prototype.hasOwnProperty.call(result, field)) output[field] = result[field];
19
+ }
20
+ return output;
21
+ }
22
+
23
+ function valueOnly(result) {
24
+ return Object.prototype.hasOwnProperty.call(result, 'value') ? result.value : null;
25
+ }
26
+
27
+ module.exports = function registerReadNode(RED) {
28
+ function OpcUaReadNode(config) {
29
+ RED.nodes.createNode(this, config);
30
+ const node = this;
31
+ node.connection = RED.nodes.getNode(config.connection);
32
+ const diagnostics = createDiagnostics(node, config, 'read');
33
+ const consumer = node.connection?.registerConsumer?.('read', {
34
+ nodeId: config.id, name: config.name,
35
+ });
36
+
37
+ node.on('input', async (msg, send, done) => {
38
+ if (!node.connection) {
39
+ const error = new Error('OPCUA_CONNECTION_NOT_CONFIGURED');
40
+ diagnostics.failure(error, send);
41
+ return done(error);
42
+ }
43
+
44
+ diagnostics.beginInput();
45
+ let inputError;
46
+ try {
47
+ consumer?.markUsed('read');
48
+ const points = messagePoints(msg, config.nodeId, {
49
+ indexRange: config.indexRange,
50
+ }, {
51
+ configuredPayload: config.pointList,
52
+ format: 'auto',
53
+ });
54
+ node.status({ fill: 'blue', shape: 'dot', text: `reading ${points.length}` });
55
+ const values = await node.connection.client.readMany(
56
+ points,
57
+ Number(config.batchSize) || 500
58
+ );
59
+ const descriptors = new Map(points.map((point) => [point.nodeId, point]));
60
+ const results = values.map((value) => ({
61
+ ...descriptors.get(value.nodeId),
62
+ ...value,
63
+ }));
64
+
65
+ // Undefined preserves the original output for Flow JSON created before
66
+ // resultDetail was introduced. New editor nodes explicitly save compact.
67
+ const failed = diagnostics.results(results, send);
68
+ const resultDetail = config.resultDetail || 'full';
69
+ const outputResults = resultDetail === 'compact'
70
+ ? results.map(compactResult)
71
+ : resultDetail === 'value'
72
+ ? results.map(valueOnly)
73
+ : results;
74
+
75
+ if ((config.outputMode || 'batch') === 'split') {
76
+ results.forEach((result, index) => {
77
+ const output = {
78
+ ...msg,
79
+ topic: result.nodeId,
80
+ payload: valueOnly(result),
81
+ };
82
+ if (resultDetail === 'value') delete output.opcua;
83
+ else output.opcua = outputResults[index];
84
+ send(output);
85
+ });
86
+ } else if (points.length === 1 && !looksLikePointList(msg.payload) &&
87
+ !Array.isArray(msg.items) && !config.pointList) {
88
+ const result = results[0];
89
+ msg.topic = result.nodeId;
90
+ msg.payload = valueOnly(result);
91
+ if (resultDetail === 'value') delete msg.opcua;
92
+ else msg.opcua = outputResults[0];
93
+ send(msg);
94
+ } else {
95
+ msg.payload = outputResults;
96
+ msg.opcua = {
97
+ operation: 'read',
98
+ total: results.length,
99
+ good: results.filter((item) => item.statusCode === 'Good').length,
100
+ resultDetail,
101
+ };
102
+ if (resultDetail === 'full') {
103
+ msg.opcua.clientDiagnostics = node.connection.getDiagnostics?.() ||
104
+ node.connection.client.getDiagnostics();
105
+ }
106
+ send(msg);
107
+ }
108
+
109
+ node.status({ fill: failed ? 'yellow' : 'green', shape: failed ? 'ring' : 'dot',
110
+ text: `${results.length - failed}/${results.length} read` + (failed ? ` / ${failed} failed` : '') });
111
+ done();
112
+ } catch (error) {
113
+ inputError = error;
114
+ node.status({ fill: 'red', shape: 'ring', text: error.message });
115
+ diagnostics.failure(error, send);
116
+ done(error);
117
+ } finally {
118
+ diagnostics.endInput(inputError);
119
+ }
120
+ });
121
+
122
+ node.on('close', (removed, done) => {
123
+ if (typeof removed === 'function') done = removed;
124
+ consumer?.release();
125
+ done?.();
126
+ });
127
+ }
128
+
129
+ RED.nodes.registerType('tier0-opcua-read', OpcUaReadNode);
130
+ };