@vitormnm/node-red-simple-opcua 1.4.0 → 1.4.1

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.
@@ -214,6 +214,8 @@ class OpcUaServerProcess {
214
214
  msg.payload = result.payload;
215
215
  this.assignReadMetadata(msg, identifierType, result.identifiers);
216
216
 
217
+
218
+
217
219
  if (result.identifiers.length === 1) {
218
220
  msg.topic = result.identifiers[0];
219
221
  }
@@ -306,49 +308,137 @@ class OpcUaServerProcess {
306
308
  }
307
309
 
308
310
 
309
-
310
311
  writeFromPayload(msg, nodeId) {
311
312
  try {
312
- var writtenPaths = null
313
- const payload = msg ? msg.payload : undefined;
313
+ let writtenPaths = null;
314
+ let payload = msg ? msg.payload : undefined;
315
+
314
316
  const target = msg && msg.opcuaServerIo ? msg.opcuaServerIo : {};
315
317
  const identifierType = this.resolveIdentifierType(target);
316
318
 
319
+ // Buffer serializado pelo IPC
320
+ if (
321
+ payload &&
322
+ typeof payload === "object" &&
323
+ payload.type === "Buffer" &&
324
+ Array.isArray(payload.data)
325
+ ) {
326
+ payload = Buffer.from(payload.data);
327
+ }
317
328
 
329
+ const dataType =
330
+ target.dataType ||
331
+ target.type ||
332
+ target.builtInType ||
333
+ "";
334
+
335
+ const isByteString =
336
+ typeof dataType === "string" &&
337
+ dataType.toLowerCase() === "bytestring";
338
+
339
+
340
+
341
+ // Buffer ou Uint8Array
342
+ if (Buffer.isBuffer(payload) || payload instanceof Uint8Array) {
343
+
344
+ const identifier = this.resolveIdentifier(target);
345
+
346
+ this.node.writeValue(
347
+ identifierType,
348
+ identifier,
349
+ Buffer.isBuffer(payload)
350
+ ? payload
351
+ : Buffer.from(payload)
352
+ );
353
+
354
+ writtenPaths = [identifier];
355
+ }
356
+
357
+ // Array de números
358
+ else if (
359
+ Array.isArray(payload) &&
360
+ payload.every(item => typeof item === "number")
361
+ ) {
362
+
363
+ const identifier = this.resolveIdentifier(target);
364
+
365
+ this.node.writeValue(
366
+ identifierType,
367
+ identifier,
368
+ isByteString
369
+ ? Buffer.from(payload)
370
+ : payload
371
+ );
372
+
373
+ writtenPaths = [identifier];
374
+ }
375
+
376
+ // Array de objetos
377
+ else if (Array.isArray(payload)) {
318
378
 
319
- if (Array.isArray(payload)) {
320
379
  if (!payload.length) {
321
380
  throw new Error("msg.payload array does not contain any items");
322
381
  }
323
382
 
324
- payload.forEach((item) => {
383
+ payload.forEach(item => {
325
384
  this.writePayloadItem(identifierType, item);
326
385
  });
327
386
 
328
- writtenPaths = payload.map((item) => this.resolvePayloadItemIdentifier(item));
329
- } else if (payload && typeof payload === "object" && !Array.isArray(payload)) {
387
+ writtenPaths = payload.map(item =>
388
+ this.resolvePayloadItemIdentifier(item)
389
+ );
390
+ }
391
+
392
+ // Objeto { path: value }
393
+ else if (
394
+ payload &&
395
+ typeof payload === "object" &&
396
+ !Array.isArray(payload)
397
+ ) {
330
398
 
331
399
  const identifiers = Object.keys(payload);
400
+
332
401
  if (!identifiers.length) {
333
- throw new Error("msg.payload object does not contain any " + this.getIdentifierLabel(identifierType));
402
+ throw new Error(
403
+ "msg.payload object does not contain any " +
404
+ this.getIdentifierLabel(identifierType)
405
+ );
334
406
  }
335
407
 
336
- identifiers.forEach((identifier) => {
337
- this.node.writeValue(identifierType, identifier, payload[identifier]);
408
+ identifiers.forEach(identifier => {
409
+ this.node.writeValue(
410
+ identifierType,
411
+ identifier,
412
+ payload[identifier]
413
+ );
338
414
  });
339
415
 
340
416
  writtenPaths = identifiers;
341
- } else {
417
+ }
418
+
419
+ // Valor simples
420
+ else {
342
421
 
343
422
  const identifier = this.resolveIdentifier(target);
344
- this.node.writeValue(identifierType, identifier, payload);
345
423
 
424
+ this.node.writeValue(
425
+ identifierType,
426
+ identifier,
427
+ payload
428
+ );
346
429
 
347
- writtenPaths = [identifier]
430
+ writtenPaths = [identifier];
348
431
  }
349
432
 
350
433
  msg.opcua = msg.opcua || {};
351
- this.assignWriteMetadata(msg, identifierType, writtenPaths);
434
+
435
+ this.assignWriteMetadata(
436
+ msg,
437
+ identifierType,
438
+ writtenPaths
439
+ );
440
+
441
+
352
442
  if (writtenPaths.length === 1) {
353
443
  msg.topic = writtenPaths[0];
354
444
  }
@@ -356,7 +446,7 @@ class OpcUaServerProcess {
356
446
  process.send({
357
447
  type: "send",
358
448
  data: msg,
359
- nodeId: nodeId
449
+ nodeId
360
450
  });
361
451
 
362
452
  process.send({
@@ -364,28 +454,29 @@ class OpcUaServerProcess {
364
454
  data: {
365
455
  fill: "green",
366
456
  shape: "dot",
367
- text: writtenPaths.length > 1 ? "write " + writtenPaths.length + " tags" : "write " + writtenPaths[0]
457
+ text:
458
+ writtenPaths.length > 1
459
+ ? `write ${writtenPaths.length} tags`
460
+ : `write ${writtenPaths[0]}`
368
461
  },
369
- nodeId: nodeId
462
+ nodeId
370
463
  });
371
464
 
372
-
373
465
  } catch (error) {
374
466
 
375
467
  process.send({
376
468
  type: "error",
377
- data: { fill: "red", shape: "ring", text: "failed write" },
469
+ data: {
470
+ fill: "red",
471
+ shape: "ring",
472
+ text: "failed write"
473
+ },
378
474
  error: error.message,
379
- nodeId: nodeId
475
+ nodeId
380
476
  });
381
477
  }
382
-
383
-
384
-
385
- //return [path];
386
478
  }
387
479
 
388
-
389
480
  resolveIdentifierType(target) {
390
481
  return target && target.identifierType === "nodeId" ? "nodeId" : "path";
391
482
  }
@@ -726,4 +817,4 @@ process.on("unhandledRejection", (reason) => {
726
817
  data: "Unhandled Rejection: " + (reason?.message || reason),
727
818
  nodeId: nodeId
728
819
  });
729
- });
820
+ });
@@ -98,7 +98,7 @@
98
98
  },
99
99
  inputs: 1,
100
100
  outputs: 1,
101
- icon: "bridge.svg",
101
+ icon: "opcua.svg",
102
102
  label: function () {
103
103
  return this.name || this.methodName || this.tagPath || this.tagNodeId || "opcua-server-io";
104
104
  },
@@ -12,10 +12,19 @@ module.exports = function (RED) {
12
12
  }
13
13
  });
14
14
 
15
+
16
+ const path = require("path");
17
+
18
+
19
+
20
+
21
+
22
+
15
23
  function OpcUaServerIoNode(config) {
16
24
  RED.nodes.createNode(this, config);
17
25
  const node = this;
18
26
 
27
+
19
28
  node.name = (config.name || "").trim();
20
29
  node.serverRef = (config.serverRef || "").trim();
21
30
  node.mode = config.mode || "read";
@@ -106,6 +115,20 @@ module.exports = function (RED) {
106
115
  });
107
116
  }
108
117
 
118
+ function restoreBuffers(value) {
119
+ // Formato exato que o Node.js IPC gera ao serializar um Buffer
120
+ if (
121
+ value !== null &&
122
+ typeof value === "object" &&
123
+ value.type === "Buffer" &&
124
+ Array.isArray(value.data)
125
+ ) {
126
+ return Buffer.from(value.data);
127
+ }
128
+
129
+ return value;
130
+ }
131
+
109
132
  function onMessage(msg, node) {
110
133
  if (msg.nodeId == node.id) {
111
134
 
@@ -115,7 +138,13 @@ module.exports = function (RED) {
115
138
  }
116
139
 
117
140
  if (msg.type === "send") {
118
- node.send(msg.data);
141
+ const data = msg.data;
142
+
143
+ if (data && data.payload !== undefined) {
144
+ data.payload = restoreBuffers(data.payload);
145
+ }
146
+
147
+ node.send(data);
119
148
  }
120
149
 
121
150
  if (msg.type === "error") {
@@ -132,7 +161,7 @@ module.exports = function (RED) {
132
161
  });
133
162
 
134
163
  node.send({
135
- topic : msg.data.nodeId,
164
+ topic: msg.data.nodeId,
136
165
  payload: msg.data.inputArguments,
137
166
  opcua: {
138
167
  server: msg.data.serverName,
@@ -238,11 +238,37 @@
238
238
  color: var(--red-ui-list-item-color-selected, inherit);
239
239
  }
240
240
 
241
- .opcua-tree-indent { flex: 0 0 auto; width: 14px; }
242
- .opcua-tree-twisty { width: 16px; text-align: center; color: #777; flex: 0 0 16px; }
243
- .opcua-tree-icon { width: 14px; text-align: center; color: #777; flex: 0 0 14px; }
244
- .opcua-tree-label { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
245
- .opcua-tree-type { color: #777; font-size: 11px; flex: 0 0 auto; }
241
+ .opcua-tree-indent {
242
+ flex: 0 0 auto;
243
+ width: 14px;
244
+ }
245
+
246
+ .opcua-tree-twisty {
247
+ width: 16px;
248
+ text-align: center;
249
+ color: #777;
250
+ flex: 0 0 16px;
251
+ }
252
+
253
+ .opcua-tree-icon {
254
+ width: 14px;
255
+ text-align: center;
256
+ color: #777;
257
+ flex: 0 0 14px;
258
+ }
259
+
260
+ .opcua-tree-label {
261
+ flex: 1 1 auto;
262
+ overflow: hidden;
263
+ text-overflow: ellipsis;
264
+ white-space: nowrap;
265
+ }
266
+
267
+ .opcua-tree-type {
268
+ color: #777;
269
+ font-size: 11px;
270
+ flex: 0 0 auto;
271
+ }
246
272
 
247
273
  .opcua-tree-context-menu {
248
274
  position: fixed;
@@ -251,7 +277,7 @@
251
277
  border: 1px solid var(--red-ui-form-input-border-color, #d9d9d9);
252
278
  border-radius: 4px;
253
279
  background: var(--red-ui-form-input-background, #fff);
254
- box-shadow: 0 6px 18px rgba(0,0,0,0.18);
280
+ box-shadow: 0 6px 18px rgba(0, 0, 0, 0.18);
255
281
  overflow: hidden;
256
282
  }
257
283
 
@@ -503,7 +529,9 @@
503
529
  margin-left: 0;
504
530
  }
505
531
 
506
- .opcua-tree-layout { grid-template-columns: 1fr; }
532
+ .opcua-tree-layout {
533
+ grid-template-columns: 1fr;
534
+ }
507
535
 
508
536
  .opcua-tree-header {
509
537
  align-items: flex-start;
@@ -644,7 +672,7 @@
644
672
  };
645
673
  }
646
674
 
647
- function normalizeBranch(branch) {
675
+ function normalizeBranch(branch) {
648
676
  branch = branch || {};
649
677
  return {
650
678
  name: branch.name ? String(branch.name) : "",
@@ -745,7 +773,7 @@
745
773
  var cv = String(currentValue || "");
746
774
  var opts = "";
747
775
  var noneSelected = (cv === "") ? " selected" : "";
748
- opts += "<option value=\"\"" + noneSelected + ">\u2014 none \u2014</option>";
776
+ opts += "<option value=\"\"" + noneSelected + ">\u2014 none \u2014</option>";
749
777
  names.forEach(function (n) {
750
778
  var esc = n.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
751
779
  var sel = (n === cv) ? " selected" : "";
@@ -770,8 +798,8 @@
770
798
  var tokens = pathToTokens(path);
771
799
  var current = editorState;
772
800
  var parts = [];
773
- // var preservedCollections = { alarms: true, methods: true, objectsTypes: true };
774
- var preservedCollections = { alarms: true, methods: true};
801
+ // var preservedCollections = { alarms: true, methods: true, objectsTypes: true };
802
+ var preservedCollections = { alarms: true, methods: true };
775
803
 
776
804
  tokens.forEach(function (token) {
777
805
  if (/^\d+$/.test(token)) {
@@ -787,7 +815,7 @@
787
815
  return parts.join(".");
788
816
  }
789
817
 
790
-
818
+
791
819
  function nodeIdSuffixFromValue(nodeId, defaultSuffix) {
792
820
  var raw = String(nodeId || "").trim();
793
821
  if (!raw) return defaultSuffix;
@@ -1059,9 +1087,9 @@
1059
1087
  ? (parentPath + ".objectsTypes")
1060
1088
  : kind === "alarm"
1061
1089
  ? (parentPath + ".alarms")
1062
- : kind === "method"
1063
- ? (parentPath + ".methods")
1064
- : (parentPath + ".objects");
1090
+ : kind === "method"
1091
+ ? (parentPath + ".methods")
1092
+ : (parentPath + ".objects");
1065
1093
  var target = getAtPath(editorState, branchTargetPath);
1066
1094
  if (!Array.isArray(target)) return;
1067
1095
  if (kind === "variable") {
@@ -1197,7 +1225,7 @@
1197
1225
  panel.append('<div class="form-row"><label>objectsType</label>' + buildObjectTypeSelect("opcua-detail-objectstype", item.objectsType || "") + '</div>');
1198
1226
  }
1199
1227
  if (nodeClass === "Variable") {
1200
- panel.append('<div class="form-row"><label>dataType</label><select id="opcua-detail-type"><option value="Int16">Int16</option><option value="Int32">Int32</option><option value="Float">Float</option><option value="Boolean">Boolean</option><option value="String">String</option></select></div>');
1228
+ panel.append('<div class="form-row"><label>dataType</label><select id="opcua-detail-type"><option value="Int16">Int16</option><option value="Int32">Int32</option><option value="Float">Float</option><option value="Boolean">Boolean</option><option value="String">String</option><option value="ByteString">ByteString</option></select></div>');
1201
1229
  panel.append('<div class="form-row"><label>Value</label><input type="text" id="opcua-detail-value"></div>');
1202
1230
  panel.append('<div class="form-row"><label>Access</label><select id="opcua-detail-access"><option value="readwrite">readwrite</option><option value="readonly">readonly</option></select></div>');
1203
1231
  }
@@ -1239,7 +1267,7 @@
1239
1267
  });
1240
1268
  panel.append('<div class="form-row"><label></label><a href="#" class="editor-button editor-button-small" id="opcua-method-add-output"><i class="fa fa-plus"></i> Add output</a></div>');
1241
1269
  }
1242
- if (nodeClass === "Alarm") {
1270
+ if (nodeClass === "Alarm") {
1243
1271
  panel.append('<div class="form-row"><label>alarmType</label><select id="opcua-detail-alarm-type"><option value="levelAlarm">levelAlarm</option><option value="digitalAlarm">digitalAlarm</option></select></div>');
1244
1272
  panel.append('<div class="form-row"><label>variablePath</label><input type="text" id="opcua-detail-variable-nodeid"></div>');
1245
1273
  panel.append('<div class="form-row"><label>severity</label><input type="number" id="opcua-detail-severity"></div>');
@@ -1423,7 +1451,7 @@
1423
1451
 
1424
1452
  $(document).on("input", "#opcua-detail-displayname", function () { updateNode(selectedPath, { displayName: $(this).val() }); });
1425
1453
 
1426
- $(document).on("input", "#opcua-detail-name", function () { updateNode(selectedPath, { name: $(this).val() }); });
1454
+ $(document).on("input", "#opcua-detail-name", function () { updateNode(selectedPath, { name: $(this).val() }); });
1427
1455
  $(document).on("change", "#opcua-detail-namespace", function () {
1428
1456
  var nextNamespaceId = normalizeNamespaceId($(this).val());
1429
1457
  var item = getAtPath(editorState, selectedPath);
@@ -1527,7 +1555,9 @@
1527
1555
  }
1528
1556
  }
1529
1557
  },
1530
- inputs: 1, outputs: 0, icon: "bridge.svg",
1558
+ inputs: 1,
1559
+ outputs: 0,
1560
+ icon: "opcua.svg",
1531
1561
  label: function () { return this.name || this.serverName || "opc-ua-server"; },
1532
1562
  oneditprepare: function () {
1533
1563
  var node = this;
@@ -9,6 +9,8 @@ module.exports = function (RED) {
9
9
  const path = require("path");
10
10
 
11
11
 
12
+
13
+
12
14
 
13
15
 
14
16
 
@@ -16,9 +18,11 @@ module.exports = function (RED) {
16
18
  RED.nodes.createNode(this, config);
17
19
  const node = this;
18
20
  const parser = new OpcUaServerConfigParser(node);
19
-
21
+
20
22
  const settings = parser.parseNodeConfig(config, this.credentials || {});
21
23
 
24
+
25
+
22
26
  node.name = settings.name;
23
27
  node.serverName = settings.serverName;
24
28
  node.server = null;
@@ -58,7 +62,7 @@ module.exports = function (RED) {
58
62
  });
59
63
 
60
64
 
61
-
65
+
62
66
  child.send({
63
67
  type: "createServer",
64
68
  config: config,
@@ -70,7 +74,7 @@ module.exports = function (RED) {
70
74
  child.on("message", (msg) => {
71
75
  if (msg.nodeId == node.id) {
72
76
  if (msg.type === "status") {
73
-
77
+
74
78
  node.status(msg.data); // aqui sim usa o node
75
79
  }
76
80
 
package/object.json DELETED
@@ -1,65 +0,0 @@
1
- {
2
- "objects": [],
3
- "folders": [
4
- {
5
- "name": "site",
6
- "folders": [
7
- {
8
- "name": "line1",
9
- "folders": [],
10
- "objects": [
11
- {
12
- "name": "motor",
13
- "folders": [],
14
- "objects": [],
15
- "variables": [
16
- {
17
- "name": "speed",
18
- "type": "Float",
19
- "value": "",
20
- "access": "readonly",
21
- "description": "",
22
- "displayName": ""
23
- }
24
- ],
25
- "method": [
26
- {
27
- "name": "cmd_on",
28
- "description": "",
29
- "displayName": "",
30
- "inputs": [
31
- {
32
- "name": "sp_speed",
33
- "type": "Float",
34
- "displayName": ""
35
- },
36
- {
37
- "name": "cmd_on",
38
- "type": "Boolean",
39
- "displayName": ""
40
- }
41
- ],
42
- "outputs": [
43
- {
44
- "name": "sts_spped",
45
- "type": "Float",
46
- "displayName": ""
47
- },
48
- {
49
- "name": "sts_motor",
50
- "type": "Boolean",
51
- "displayName": ""
52
- }
53
- ]
54
- }
55
- ]
56
- }
57
- ],
58
- "variables": []
59
- }
60
- ],
61
- "objects": [],
62
- "variables": []
63
- }
64
- ]
65
- }