@vitormnm/node-red-simple-opcua 1.0.2 → 1.1.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.
@@ -0,0 +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
140
+ }
@@ -0,0 +1,198 @@
1
+ "use strict";
2
+
3
+ const {
4
+ DATA_TYPE_MAP,
5
+ StatusCodes,
6
+ Variant,
7
+ VariantArrayType,
8
+ sanitizeNodeIdPath,
9
+ DataType,
10
+ coerceNodeId
11
+ } = require("./opcua-constants");
12
+
13
+
14
+ const { OpcUaAddressSpaceAlarm } = require("./opcua-address-space-alarm")
15
+
16
+
17
+ class OpcUaServerMethods {
18
+
19
+ constructor(options) {
20
+ this.addressSpace = options.addressSpace
21
+ this.node = options.addressSpace
22
+ this.registry = options.registry
23
+
24
+
25
+ //Use OpcUaAddressSpaceAlarm to set an alarm message to be stored in the history.
26
+ this.addressSpaceAlarm = new OpcUaAddressSpaceAlarm({
27
+ registry: this.registry,
28
+ node: this.node
29
+ })
30
+
31
+
32
+ }
33
+
34
+
35
+ start() {
36
+ this.acknowledgeTypeMethod()
37
+ this.confirmTypeMethod()
38
+ this.conditionRefreshMethod()
39
+ this.addCommentMethod()
40
+ }
41
+
42
+ confirmTypeMethod() {
43
+ const confirmTypeMethod = this.addressSpace.findNode("ns=0;i=9113");
44
+ confirmTypeMethod.bindMethod(
45
+ (inputArguments, context, callback) => {
46
+ const eventId = inputArguments[0].value;
47
+ const comment = inputArguments[1].value;
48
+ const alarm = context.object;
49
+
50
+ // severity atual
51
+ const severity = alarm.severity.readValue().value.value;
52
+
53
+ // message atual
54
+ const message = alarm.message.readValue().value.value.text;
55
+
56
+
57
+ if (alarm.activeState.getValue()) {
58
+
59
+ alarm.confirmedState.setValue(true);
60
+
61
+
62
+ this.addressSpaceAlarm.raiseNewConditionAlarm(alarm, message, severity, true)
63
+ alarm.raiseNewCondition({
64
+ message: message,
65
+ severity: severity,
66
+ retain: true
67
+ });
68
+
69
+
70
+ } else {
71
+ alarm.confirmedState.setValue(true);
72
+ this.addressSpaceAlarm.raiseNewConditionAlarm(alarm, message, severity, false)
73
+ }
74
+
75
+
76
+
77
+
78
+ callback(null, {
79
+ statusCode: StatusCodes.Good,
80
+ outputArguments: []
81
+ });
82
+ }
83
+ );
84
+
85
+ }
86
+
87
+ addCommentMethod() {
88
+ const addCommentMethod = this.addressSpace.findNode("ns=0;i=9029");
89
+ addCommentMethod.bindMethod(
90
+ (inputArguments, context, callback) => {
91
+ const eventId = inputArguments[0].value;
92
+ const comment = inputArguments[1].value;
93
+ const alarm = context.object;
94
+
95
+ // alarm.addComment(eventId, comment, context.session);
96
+ alarm.comment.setValueFromSource({
97
+ value: comment,
98
+ dataType: DataType.LocalizedText
99
+ });
100
+ callback(null, {
101
+ statusCode: StatusCodes.Good,
102
+ outputArguments: []
103
+ });
104
+ }
105
+ );
106
+ }
107
+
108
+ acknowledgeTypeMethod() {
109
+ const acknowledgeTypeMethod = this.addressSpace.findNode("ns=0;i=9111");
110
+
111
+ acknowledgeTypeMethod.bindMethod(
112
+ (inputArguments, context, callback) => {
113
+ const eventId = inputArguments[0].value;
114
+ const comment = inputArguments[1].value;
115
+
116
+ const alarm = context.object;
117
+ // severity atual
118
+ const severity = alarm.severity.readValue().value.value;
119
+
120
+ // message atual
121
+ const message = alarm.message.readValue().value.value.text;
122
+
123
+ alarm.ackedState.setValue(true);
124
+
125
+
126
+ alarm.confirmedState.setValue(false);
127
+ // IMPORTANTE: Para o cliente "ver", o alarme precisa gerar um evento de mudança
128
+
129
+
130
+
131
+
132
+ this.addressSpaceAlarm.raiseNewConditionAlarm(alarm, message, severity, true)
133
+
134
+ callback(null, {
135
+ statusCode: StatusCodes.Good,
136
+ outputArguments: []
137
+ });
138
+ }
139
+ );
140
+
141
+ }
142
+
143
+
144
+ conditionRefreshMethod() {
145
+ const conditionType = this.addressSpace.findObjectType("ConditionType");
146
+
147
+ const conditionRefreshMethod = conditionType.getMethodByName("ConditionRefresh");
148
+
149
+ conditionRefreshMethod.bindMethod(
150
+ (inputArguments, context, callback) => {
151
+
152
+ const subscriptionId = inputArguments[0].value;
153
+
154
+ var ActiveAlarms = this.registry.getActiveAlarms(this.node)
155
+
156
+
157
+ ActiveAlarms.forEach(element => {
158
+ const alarmNode = element.alarmNode
159
+
160
+ alarmNode.raiseNewCondition({
161
+ message: element.message,
162
+ severity: element.severity,
163
+ retain: element.retain
164
+ });
165
+
166
+ });
167
+
168
+
169
+ // for (const [, state] of retainedAlarms) {
170
+ // const alarm = state.alarm;
171
+
172
+ // alarm.raiseNewCondition({
173
+ // message: state.message,
174
+ // severity: state.severity,
175
+ // retain: state.retain
176
+ // });
177
+ // }
178
+
179
+ callback(null, {
180
+ statusCode: StatusCodes.Good,
181
+ outputArguments: [{
182
+ dataType: DataType.String,
183
+ value: JSON.stringify(ActiveAlarms)
184
+ }]
185
+ });
186
+
187
+
188
+ // reenviar alarmes ativos
189
+ // aqui você percorre seus alarmes e dispara eventos novamente
190
+
191
+ }
192
+ );
193
+ }
194
+
195
+
196
+ }
197
+
198
+ module.exports = { OpcUaServerMethods }