@vitormnm/node-red-simple-opcua 1.4.3 → 1.5.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.
- package/client/icons/opcua.svg +132 -132
- package/client/lib/opcua-client-browser.js +368 -330
- package/client/lib/opcua-client-method-service.js +88 -88
- package/client/lib/opcua-client-read-service.js +15 -15
- package/client/lib/opcua-client-subscription-id-service.js +24 -24
- package/client/lib/opcua-client-subscription-service.js +170 -170
- package/client/lib/opcua-client-write-service.js +146 -146
- package/client/opcua-client-config.html +80 -80
- package/client/opcua-client-utils.js +12 -11
- package/client/opcua-client.html +140 -140
- package/client/view/opcua-client.js +1144 -1140
- package/icons/opcua.svg +132 -132
- package/icons/opcua2.svg +132 -132
- package/package.json +3 -3
- package/server/icons/opcua.svg +132 -132
- package/server/lib/opcua-address-space-alarm.js +341 -341
- package/server/lib/opcua-address-space-builder.js +1603 -1485
- package/server/lib/opcua-config.js +677 -546
- package/server/lib/opcua-constants.js +119 -109
- package/server/lib/opcua-server-events-child.js +139 -139
- package/server/lib/opcua-server-runtime-child.js +873 -819
- package/server/lib/opcua-server-runtime.js +376 -311
- package/server/lib/opcua-server-status-child.js +187 -187
- package/server/lib/server-node-utils.js +16 -16
- package/server/opcua-server-io.html +346 -346
- package/server/opcua-server-io.js +497 -496
- package/server/opcua-server-registry.js +270 -270
- package/server/opcua-server.css +265 -265
- package/server/opcua-server.html +155 -1643
- package/server/opcua-server.js +24 -13
- package/server/view/opcua-server.css +492 -0
- package/server/view/opcua-server.js +1435 -0
|
@@ -1,109 +1,119 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
const os = require("os");
|
|
4
|
-
const opcua = require("node-opcua");
|
|
5
|
-
|
|
6
|
-
const {
|
|
7
|
-
OPCUAServer,
|
|
8
|
-
Variant,
|
|
9
|
-
DataType,
|
|
10
|
-
StatusCodes,
|
|
11
|
-
SecurityPolicy,
|
|
12
|
-
VariantArrayType,
|
|
13
|
-
MessageSecurityMode,
|
|
14
|
-
UserTokenType,
|
|
15
|
-
coerceNodeId ,
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
return
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
.
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
.
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const os = require("os");
|
|
4
|
+
const opcua = require("node-opcua");
|
|
5
|
+
|
|
6
|
+
const {
|
|
7
|
+
OPCUAServer,
|
|
8
|
+
Variant,
|
|
9
|
+
DataType,
|
|
10
|
+
StatusCodes,
|
|
11
|
+
SecurityPolicy,
|
|
12
|
+
VariantArrayType,
|
|
13
|
+
MessageSecurityMode,
|
|
14
|
+
UserTokenType,
|
|
15
|
+
coerceNodeId ,
|
|
16
|
+
resolveNodeId,
|
|
17
|
+
PermissionType,
|
|
18
|
+
makeRoles,
|
|
19
|
+
WellKnownRoles,
|
|
20
|
+
OPCUACertificateManager,
|
|
21
|
+
} = opcua;
|
|
22
|
+
|
|
23
|
+
const DEFAULT_PORT = 4840;
|
|
24
|
+
const DEFAULT_SERVER_NAME = "Node-RED OPC UA Server";
|
|
25
|
+
const DEFAULT_NAMESPACE_URI = "urn:node-red:opc-ua-server";
|
|
26
|
+
const DEFAULT_RESOURCE_PATH = "/";
|
|
27
|
+
|
|
28
|
+
const SECURITY_POLICY_MAP = {
|
|
29
|
+
None: SecurityPolicy.None,
|
|
30
|
+
Basic128Rsa15: SecurityPolicy.Basic128Rsa15,
|
|
31
|
+
Basic256: SecurityPolicy.Basic256,
|
|
32
|
+
Basic256Sha256: SecurityPolicy.Basic256Sha256,
|
|
33
|
+
Aes128_Sha256_RsaOaep: SecurityPolicy.Aes128_Sha256_RsaOaep,
|
|
34
|
+
Aes256_Sha256_RsaPss: SecurityPolicy.Aes256_Sha256_RsaPss
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const SECURITY_MODE_MAP = {
|
|
38
|
+
None: MessageSecurityMode.None,
|
|
39
|
+
Sign: MessageSecurityMode.Sign,
|
|
40
|
+
SignAndEncrypt: MessageSecurityMode.SignAndEncrypt
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const DATA_TYPE_MAP = {
|
|
44
|
+
Int16: DataType.Int16,
|
|
45
|
+
UInt16: DataType.UInt16,
|
|
46
|
+
Int32: DataType.Int32,
|
|
47
|
+
UInt32: DataType.UInt32,
|
|
48
|
+
Float: DataType.Float,
|
|
49
|
+
Boolean: DataType.Boolean,
|
|
50
|
+
String: DataType.String,
|
|
51
|
+
ByteString : DataType.ByteString,
|
|
52
|
+
LocalizedText : DataType.LocalizedText
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
function normalizePort(port) {
|
|
56
|
+
const parsed = Number(port);
|
|
57
|
+
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
58
|
+
return DEFAULT_PORT;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return parsed;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function buildApplicationUri(serverName) {
|
|
65
|
+
const host = os.hostname() || "localhost";
|
|
66
|
+
return "urn:" + host + ":node-red:" + sanitizeUriSegment(serverName);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function sanitizeUriSegment(value) {
|
|
70
|
+
return String(value || "opc-ua-server")
|
|
71
|
+
.trim()
|
|
72
|
+
.replace(/\s+/g, "-")
|
|
73
|
+
.replace(/[^a-zA-Z0-9:_-]/g, "")
|
|
74
|
+
.toLowerCase();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function sanitizeNodeIdPath(path) {
|
|
78
|
+
return String(path || "")
|
|
79
|
+
.split(".")
|
|
80
|
+
.map((segment) => sanitizeNodeIdSegment(segment))
|
|
81
|
+
.filter((segment) => segment !== "")
|
|
82
|
+
.join(".");
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function sanitizeNodeIdSegment(segment) {
|
|
86
|
+
const normalized = String(segment || "")
|
|
87
|
+
.trim()
|
|
88
|
+
.replace(/\s+/g, "_")
|
|
89
|
+
.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
90
|
+
|
|
91
|
+
return normalized || "item";
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
module.exports = {
|
|
95
|
+
OPCUAServer,
|
|
96
|
+
Variant,
|
|
97
|
+
DataType,
|
|
98
|
+
OPCUACertificateManager,
|
|
99
|
+
StatusCodes,
|
|
100
|
+
VariantArrayType,
|
|
101
|
+
SecurityPolicy,
|
|
102
|
+
MessageSecurityMode,
|
|
103
|
+
UserTokenType,
|
|
104
|
+
coerceNodeId,
|
|
105
|
+
resolveNodeId,
|
|
106
|
+
PermissionType,
|
|
107
|
+
makeRoles,
|
|
108
|
+
WellKnownRoles,
|
|
109
|
+
DEFAULT_PORT,
|
|
110
|
+
DEFAULT_SERVER_NAME,
|
|
111
|
+
DEFAULT_NAMESPACE_URI,
|
|
112
|
+
DEFAULT_RESOURCE_PATH,
|
|
113
|
+
SECURITY_POLICY_MAP,
|
|
114
|
+
SECURITY_MODE_MAP,
|
|
115
|
+
DATA_TYPE_MAP,
|
|
116
|
+
normalizePort,
|
|
117
|
+
buildApplicationUri,
|
|
118
|
+
sanitizeNodeIdPath
|
|
119
|
+
};
|
|
@@ -1,140 +1,140 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const registry = require("../opcua-server-registry");
|
|
5
|
-
const { resolveRegisteredServer } = require("./server-node-utils");
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
function eventsServer(node, rootNode, nodeId) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
// 🔥 ALTERADO: usar Map para garantir unicidade por nodeID
|
|
15
|
-
node.queue = {
|
|
16
|
-
read: new Map(),
|
|
17
|
-
write: new Map(),
|
|
18
|
-
alarm: new Map()
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
if (node.flushTimer) {
|
|
22
|
-
clearInterval(node.flushTimer);
|
|
23
|
-
node.flushTimer = null;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
node.flushTimer = setInterval(() => {
|
|
28
|
-
flushQueue(node, nodeId);
|
|
29
|
-
|
|
30
|
-
}, rootNode.intervalMs);
|
|
31
|
-
|
|
32
|
-
///rootNode.intervalMs
|
|
33
|
-
node.enqueueAccessEvent = function (event) {
|
|
34
|
-
if (!matchesServer(node.serverRef, event)) {
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (event.operation === "read") {
|
|
39
|
-
upsertEvent(node.queue.read, event);
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
if (event.operation === "write") {
|
|
44
|
-
upsertEvent(node.queue.write, event);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (event.operation === "alarm") {
|
|
48
|
-
upsertEvent(node.queue.alarm, event);
|
|
49
|
-
}
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
registry.registerAccessListener(node.id, node);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
function flushQueue(node, nodeId) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
// ALTERADO: usar size (Map)
|
|
64
|
-
if (!node.queue.read.size && !node.queue.write.size) {
|
|
65
|
-
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
const payload = {
|
|
70
|
-
serverRef: node.serverRef || "",
|
|
71
|
-
intervalMs: node.intervalMs,
|
|
72
|
-
timestamp: new Date().toISOString(),
|
|
73
|
-
|
|
74
|
-
// 🔥 ALTERADO: converter Map -> Array
|
|
75
|
-
read: Array.from(node.queue.read.values()),
|
|
76
|
-
write: Array.from(node.queue.write.values()),
|
|
77
|
-
alarm: Array.from(node.queue.alarm.values())
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
// ALTERADO: limpar Maps
|
|
81
|
-
node.queue.read.clear();
|
|
82
|
-
node.queue.write.clear();
|
|
83
|
-
node.queue.alarm.clear();
|
|
84
|
-
|
|
85
|
-
process.send({
|
|
86
|
-
type: "send",
|
|
87
|
-
data: {
|
|
88
|
-
payload,
|
|
89
|
-
opcua: {
|
|
90
|
-
server: payload.serverRef
|
|
91
|
-
}
|
|
92
|
-
},
|
|
93
|
-
nodeId: nodeId
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
process.send({
|
|
97
|
-
type: "status",
|
|
98
|
-
data: {
|
|
99
|
-
fill: "green",
|
|
100
|
-
shape: "dot",
|
|
101
|
-
text: "read " + payload.read.length + " write " + payload.write.length
|
|
102
|
-
},
|
|
103
|
-
nodeId: nodeId
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
function upsertEvent(map, event) {
|
|
113
|
-
const key = String(event.nodeID || "").trim();
|
|
114
|
-
if (!key) return;
|
|
115
|
-
|
|
116
|
-
map.set(key, event); // sobrescreve automaticamente
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
function matchesServer(serverRef, event) {
|
|
120
|
-
if (!serverRef) {
|
|
121
|
-
return true;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
const normalizedRef = String(serverRef).trim();
|
|
125
|
-
return normalizedRef === String(event.serverId || "").trim()
|
|
126
|
-
|| normalizedRef === String(event.serverNodeName || "").trim()
|
|
127
|
-
|| normalizedRef === String(event.serverName || "").trim();
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
function normalizeInterval(value) {
|
|
131
|
-
const interval = Number(value);
|
|
132
|
-
if (!Number.isFinite(interval) || interval <= 0) {
|
|
133
|
-
return 500;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
return Math.trunc(interval);
|
|
137
|
-
}
|
|
138
|
-
module.exports = {
|
|
139
|
-
"eventsServer": eventsServer
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
const registry = require("../opcua-server-registry");
|
|
5
|
+
const { resolveRegisteredServer } = require("./server-node-utils");
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
function eventsServer(node, rootNode, nodeId) {
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
// 🔥 ALTERADO: usar Map para garantir unicidade por nodeID
|
|
15
|
+
node.queue = {
|
|
16
|
+
read: new Map(),
|
|
17
|
+
write: new Map(),
|
|
18
|
+
alarm: new Map()
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
if (node.flushTimer) {
|
|
22
|
+
clearInterval(node.flushTimer);
|
|
23
|
+
node.flushTimer = null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
node.flushTimer = setInterval(() => {
|
|
28
|
+
flushQueue(node, nodeId);
|
|
29
|
+
|
|
30
|
+
}, rootNode.intervalMs);
|
|
31
|
+
|
|
32
|
+
///rootNode.intervalMs
|
|
33
|
+
node.enqueueAccessEvent = function (event) {
|
|
34
|
+
if (!matchesServer(node.serverRef, event)) {
|
|
35
|
+
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (event.operation === "read") {
|
|
39
|
+
upsertEvent(node.queue.read, event);
|
|
40
|
+
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (event.operation === "write") {
|
|
44
|
+
upsertEvent(node.queue.write, event);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (event.operation === "alarm") {
|
|
48
|
+
upsertEvent(node.queue.alarm, event);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
registry.registerAccessListener(node.id, node);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
function flushQueue(node, nodeId) {
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
// ALTERADO: usar size (Map)
|
|
64
|
+
if (!node.queue.read.size && !node.queue.write.size) {
|
|
65
|
+
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
const payload = {
|
|
70
|
+
serverRef: node.serverRef || "",
|
|
71
|
+
intervalMs: node.intervalMs,
|
|
72
|
+
timestamp: new Date().toISOString(),
|
|
73
|
+
|
|
74
|
+
// 🔥 ALTERADO: converter Map -> Array
|
|
75
|
+
read: Array.from(node.queue.read.values()),
|
|
76
|
+
write: Array.from(node.queue.write.values()),
|
|
77
|
+
alarm: Array.from(node.queue.alarm.values())
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// ALTERADO: limpar Maps
|
|
81
|
+
node.queue.read.clear();
|
|
82
|
+
node.queue.write.clear();
|
|
83
|
+
node.queue.alarm.clear();
|
|
84
|
+
|
|
85
|
+
process.send({
|
|
86
|
+
type: "send",
|
|
87
|
+
data: {
|
|
88
|
+
payload,
|
|
89
|
+
opcua: {
|
|
90
|
+
server: payload.serverRef
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
nodeId: nodeId
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
process.send({
|
|
97
|
+
type: "status",
|
|
98
|
+
data: {
|
|
99
|
+
fill: "green",
|
|
100
|
+
shape: "dot",
|
|
101
|
+
text: "read " + payload.read.length + " write " + payload.write.length
|
|
102
|
+
},
|
|
103
|
+
nodeId: nodeId
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
function upsertEvent(map, event) {
|
|
113
|
+
const key = String(event.nodeID || "").trim();
|
|
114
|
+
if (!key) return;
|
|
115
|
+
|
|
116
|
+
map.set(key, event); // sobrescreve automaticamente
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function matchesServer(serverRef, event) {
|
|
120
|
+
if (!serverRef) {
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const normalizedRef = String(serverRef).trim();
|
|
125
|
+
return normalizedRef === String(event.serverId || "").trim()
|
|
126
|
+
|| normalizedRef === String(event.serverNodeName || "").trim()
|
|
127
|
+
|| normalizedRef === String(event.serverName || "").trim();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function normalizeInterval(value) {
|
|
131
|
+
const interval = Number(value);
|
|
132
|
+
if (!Number.isFinite(interval) || interval <= 0) {
|
|
133
|
+
return 500;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return Math.trunc(interval);
|
|
137
|
+
}
|
|
138
|
+
module.exports = {
|
|
139
|
+
"eventsServer": eventsServer
|
|
140
140
|
}
|