@vitormnm/node-red-simple-opcua 1.0.2 → 1.0.3

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,270 @@
1
+ "use strict";
2
+
3
+ const servers = new Map();
4
+ const methodHandlers = new Map();
5
+ const pendingCalls = new Map();
6
+ const accessListeners = new Map();
7
+ const childs = new Map();
8
+ const activeAlarms = new Map();
9
+
10
+ const serversNames = new Map(); //get serve name for opcua-server-io
11
+
12
+ function registerServer(node) {
13
+ servers.set(node.id, node);
14
+ }
15
+
16
+ function registerServerNames(node) {
17
+ serversNames.set(node, node);
18
+ }
19
+
20
+
21
+ function registerChild(serverName, child) {
22
+ if (!serverName || !child) {
23
+ return;
24
+ }
25
+
26
+ childs.set(serverName, child);
27
+
28
+ const cleanup = () => {
29
+ if (childs.get(serverName) === child) {
30
+ childs.delete(serverName);
31
+ }
32
+ };
33
+
34
+ child.once("exit", cleanup);
35
+ child.once("close", cleanup);
36
+ child.once("disconnect", cleanup);
37
+ }
38
+
39
+ function unregisterServer(nodeId) {
40
+ servers.delete(nodeId);
41
+ }
42
+
43
+ function unregisterServerNames(name) {
44
+ serversNames.delete(name);
45
+ }
46
+
47
+
48
+ function resolveServer(reference) {
49
+ const activeServers = Array.from(servers.values()).filter((node) => node && node.server);
50
+
51
+ if (!reference) {
52
+ if (activeServers.length === 1) {
53
+ return activeServers[0];
54
+ }
55
+ return null;
56
+ }
57
+
58
+ const normalizedReference = String(reference).trim();
59
+ return activeServers.find((node) =>
60
+ node.id === normalizedReference ||
61
+ node.name === normalizedReference ||
62
+ node.serverName === normalizedReference
63
+ ) || null;
64
+ }
65
+
66
+ function listActiveServers() {
67
+ return Array.from(serversNames.values())
68
+
69
+ // return Array.from(servers.values())
70
+ // .filter((node) => node && node.server)
71
+ // .map((node) => ({
72
+ // id: node.id,
73
+ // name: node.name || "",
74
+ // serverName: node.serverName || "",
75
+ // label: node.name || node.serverName || node.id,
76
+ // value: node.serverName || node.name || node.id
77
+ // }));
78
+ }
79
+
80
+ function resolveChild(serverName) {
81
+ const child = childs.get(serverName);
82
+
83
+ if (!child) {
84
+ return null;
85
+ }
86
+
87
+ if (child.killed || child.exitCode !== null || child.channel === null || !child.connected) {
88
+ childs.delete(serverName);
89
+ return null;
90
+ }
91
+
92
+ return child;
93
+ }
94
+
95
+ function unregisterChild(serverName, child) {
96
+ if (!serverName) {
97
+ return;
98
+ }
99
+
100
+ if (child) {
101
+ if (childs.get(serverName) === child) {
102
+ childs.delete(serverName);
103
+ }
104
+ return;
105
+ }
106
+
107
+ childs.delete(serverName);
108
+ }
109
+
110
+ function registerMethodHandler(methodName, nodeId) {
111
+ methodHandlers.set(methodName, nodeId);
112
+ }
113
+
114
+ function unregisterMethodHandler(methodName) {
115
+ methodHandlers.delete(methodName);
116
+ }
117
+
118
+ function emitMethodCall(call) {
119
+ const nodeId = methodHandlers.get(call.methodName);
120
+
121
+ process.send({
122
+ type: "sendMethod",
123
+ data: call,
124
+ nodeId: nodeId
125
+ });
126
+ //handler.sendMethodCall(call);
127
+ }
128
+
129
+ function waitForMethodResponse(callId) {
130
+ return new Promise((resolve, reject) => {
131
+ pendingCalls.set(callId, { resolve, reject });
132
+
133
+ setTimeout(() => {
134
+ if (pendingCalls.has(callId)) {
135
+ pendingCalls.delete(callId);
136
+ reject(new Error("Timeout waiting method response"));
137
+ }
138
+ }, 10000);
139
+ });
140
+ }
141
+
142
+ function resolveMethodCall(callId, payload) {
143
+ const pending = pendingCalls.get(callId);
144
+
145
+ if (!pending) {
146
+ return;
147
+ }
148
+
149
+ pending.resolve(payload);
150
+ pendingCalls.delete(callId);
151
+ }
152
+
153
+ function registerAccessListener(listenerId, node) {
154
+ accessListeners.set(listenerId, node);
155
+ }
156
+
157
+ // function registerActiveAlarms(alarm, node) {
158
+
159
+ // const lista = activeAlarms.get(node.id) ?? [];
160
+ // lista.push(alarm);
161
+ // activeAlarms.set(node.id, lista);
162
+
163
+
164
+ // }
165
+
166
+ function registerActiveAlarms(alarm, message, severity, retain, node) {
167
+
168
+
169
+ const alarmNodeId = alarm.nodeId.toString();
170
+ const lista = activeAlarms.get(node.id) ?? [];
171
+
172
+ // remove antigo se já existir
173
+ const novaLista = lista.filter(
174
+ x => x.alarm.nodeId.toString() !== alarmNodeId
175
+ );
176
+
177
+ if (retain) {
178
+ // adiciona novo
179
+ novaLista.push({
180
+ alarm,
181
+ message,
182
+ severity,
183
+ retain
184
+ });
185
+ }
186
+
187
+ // atualiza o map (com ou sem o alarme)
188
+ activeAlarms.set(node.id, novaLista);
189
+ }
190
+
191
+ function getActiveAlarms(node) {
192
+ try {
193
+
194
+
195
+ var lista = activeAlarms.get(node.id) ?? [];
196
+ var saida = []
197
+
198
+
199
+ lista.forEach(element => {
200
+
201
+ const alarmNode = element.alarm
202
+ const ConditionName = alarmNode.getPropertyByName("ConditionName").readValue().value.value;
203
+ const SourceName = alarmNode.getPropertyByName("SourceName").readValue().value.value;
204
+ const isActive = alarmNode.activeState.id.readValue().value.value;
205
+ const isAcked = alarmNode.ackedState.id.readValue().value.value;
206
+ const ConfirmedState = alarmNode.confirmedState.id.readValue().value.value;
207
+
208
+
209
+
210
+ saida.push({
211
+ activeState: isActive,
212
+ message: element.message,
213
+ severity: element.severity,
214
+ retain: element.retain,
215
+ sourceName: SourceName,
216
+ conditionName: ConditionName,
217
+ ConfirmedState: ConfirmedState,
218
+ ackedState: isAcked,
219
+ alarmNode: alarmNode,
220
+
221
+ })
222
+ });
223
+
224
+
225
+ // return activeAlarms.get(node.id) ?? [];
226
+ return saida
227
+
228
+
229
+ } catch (error) {
230
+ console.error("getActiveAlarms")
231
+ console.error(error)
232
+ }
233
+
234
+ }
235
+
236
+ function unregisterAccessListener(listenerId) {
237
+ accessListeners.delete(listenerId);
238
+ }
239
+
240
+ function emitTagAccess(event) {
241
+ accessListeners.forEach((listener) => {
242
+ if (!listener || typeof listener.enqueueAccessEvent !== "function") {
243
+ return;
244
+ }
245
+
246
+ listener.enqueueAccessEvent(event);
247
+ });
248
+ }
249
+
250
+ module.exports = {
251
+ registerServer,
252
+ registerServerNames,
253
+ registerChild,
254
+ unregisterChild,
255
+ resolveChild,
256
+ unregisterServer,
257
+ unregisterServerNames,
258
+ resolveServer,
259
+ listActiveServers,
260
+ registerMethodHandler,
261
+ unregisterMethodHandler,
262
+ emitMethodCall,
263
+ waitForMethodResponse,
264
+ resolveMethodCall,
265
+ registerAccessListener,
266
+ unregisterAccessListener,
267
+ emitTagAccess,
268
+ registerActiveAlarms,
269
+ getActiveAlarms,
270
+ };
@@ -0,0 +1,265 @@
1
+
2
+ body.opcua-tree-modal-open {
3
+ overflow: hidden;
4
+ }
5
+
6
+ .opcua-tree-editor {
7
+ width: 100%;
8
+ min-height: 120px;
9
+ }
10
+
11
+ .opcua-tree-modal {
12
+ position: fixed;
13
+ inset: 0;
14
+ z-index: 2000;
15
+ background: rgba(0, 0, 0, 0.45);
16
+ padding: 24px;
17
+ box-sizing: border-box;
18
+ }
19
+
20
+ .opcua-tree-modal__dialog {
21
+ display: flex;
22
+ flex-direction: column;
23
+ width: 100%;
24
+ height: 100%;
25
+ background: #f3f3f3;
26
+ border-radius: 8px;
27
+ overflow: hidden;
28
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
29
+ }
30
+
31
+ .opcua-tree-modal__header,
32
+ .opcua-tree-modal__toolbar {
33
+ display: flex;
34
+ align-items: center;
35
+ flex-wrap: wrap;
36
+ gap: 10px;
37
+ padding: 14px 18px;
38
+ background: #fff;
39
+ border-bottom: 1px solid #d9d9d9;
40
+ }
41
+
42
+ .opcua-tree-modal__title {
43
+ font-size: 18px;
44
+ font-weight: 600;
45
+ color: #333;
46
+ }
47
+
48
+ .opcua-tree-modal__header .editor-button-small {
49
+ margin-left: auto;
50
+ }
51
+
52
+ .opcua-tree-modal__body {
53
+ flex: 1 1 auto;
54
+ overflow: auto;
55
+ padding: 18px;
56
+ }
57
+
58
+ .opcua-tree-search {
59
+ display: flex;
60
+ align-items: center;
61
+ gap: 8px;
62
+ min-width: 280px;
63
+ margin-left: auto;
64
+ padding: 6px 10px;
65
+ border: 1px solid #d9d9d9;
66
+ border-radius: 6px;
67
+ background: #fafafa;
68
+ }
69
+
70
+ .opcua-tree-search input {
71
+ flex: 1 1 auto;
72
+ min-width: 120px;
73
+ border: 0;
74
+ outline: 0;
75
+ background: transparent;
76
+ box-shadow: none;
77
+ margin: 0;
78
+ }
79
+
80
+ .opcua-tree-search-results {
81
+ margin-bottom: 12px;
82
+ }
83
+
84
+ .opcua-tree-search-meta {
85
+ margin-bottom: 10px;
86
+ color: #666;
87
+ font-size: 12px;
88
+ }
89
+
90
+ .opcua-tree-search-result {
91
+ margin-bottom: 8px;
92
+ }
93
+
94
+ .opcua-tree-search-path {
95
+ margin-bottom: 4px;
96
+ color: #666;
97
+ font-size: 12px;
98
+ font-family: Consolas, monospace;
99
+ }
100
+
101
+ .opcua-tree-group,
102
+ .opcua-tree-node {
103
+ border: 1px solid #d9d9d9;
104
+ border-radius: 4px;
105
+ background: #fff;
106
+ margin-bottom: 8px;
107
+ }
108
+
109
+ .opcua-tree-node[data-depth="1"],
110
+ .opcua-tree-group[data-depth="1"] {
111
+ margin-left: 2px;
112
+ }
113
+
114
+ .opcua-tree-node[data-depth="2"],
115
+ .opcua-tree-group[data-depth="2"] {
116
+ margin-left: 2px;
117
+ }
118
+
119
+ .opcua-tree-node[data-depth="3"],
120
+ .opcua-tree-group[data-depth="3"] {
121
+ margin-left: 2px;
122
+ }
123
+
124
+ .opcua-tree-node[data-depth="4"],
125
+ .opcua-tree-group[data-depth="4"] {
126
+ margin-left: 2px;
127
+ }
128
+
129
+ .opcua-tree-header {
130
+ display: flex;
131
+ align-items: center;
132
+ flex-wrap: wrap;
133
+ gap: 4px;
134
+ padding: 4px 5px;
135
+ background: #f8f8f8;
136
+ }
137
+
138
+ .opcua-tree-body {
139
+ padding: 5px;
140
+ border-top: 1px solid #ededed;
141
+ }
142
+
143
+ .opcua-tree-toggle {
144
+ width: 24px;
145
+ height: 24px;
146
+ border: 0;
147
+ padding: 0;
148
+ background: transparent;
149
+ color: #666;
150
+ cursor: pointer;
151
+ flex: 0 0 auto;
152
+ }
153
+
154
+ .opcua-tree-title {
155
+ min-width: 100px;
156
+ font-weight: 600;
157
+ color: #444;
158
+ }
159
+
160
+ .opcua-tree-summary {
161
+ color: #777;
162
+ font-size: 12px;
163
+ white-space: nowrap;
164
+ }
165
+
166
+ .opcua-tree-input {
167
+ flex: 1 1 auto;
168
+ min-width: 220px;
169
+ }
170
+
171
+ .opcua-tree-actions {
172
+ margin-left: auto;
173
+ display: flex;
174
+ gap: 6px;
175
+ align-items: center;
176
+ flex-wrap: wrap;
177
+ }
178
+
179
+ .opcua-tree-empty {
180
+ padding: 14px;
181
+ border: 1px dashed #d9d9d9;
182
+ border-radius: 4px;
183
+ background: #fafafa;
184
+ color: #777;
185
+ text-align: center;
186
+ }
187
+
188
+ .opcua-tree-grid {
189
+ display: grid;
190
+ grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
191
+ gap: 12px;
192
+ }
193
+
194
+ .opcua-tree-grid .form-row {
195
+ margin-bottom: 0;
196
+ }
197
+
198
+ .opcua-tree-grid .form-row label {
199
+ width: 100px !important;
200
+ flex: 0 0 100px;
201
+ }
202
+
203
+ .opcua-tree-grid .form-row input,
204
+ .opcua-tree-grid .form-row select,
205
+ .opcua-tree-grid .form-row textarea {
206
+ width: auto;
207
+ flex: 1 1 auto;
208
+ min-width: 0;
209
+ }
210
+
211
+ .opcua-tree-form-row {
212
+ display: flex;
213
+ align-items: center;
214
+ gap: 8px;
215
+ }
216
+
217
+ .opcua-tree-form-row--full {
218
+ grid-column: 1 / -1;
219
+ }
220
+
221
+ .opcua-tree-divider {
222
+ grid-column: 1 / -1;
223
+ border-top: 1px solid #e3e3e3;
224
+ margin: 2px 0;
225
+ }
226
+
227
+ .opcua-tree-section-title {
228
+ grid-column: 1 / -1;
229
+ font-size: 12px;
230
+ font-weight: 700;
231
+ text-transform: uppercase;
232
+ letter-spacing: 0.04em;
233
+ color: #666;
234
+ margin-top: 2px;
235
+ }
236
+
237
+ @media (max-width: 900px) {
238
+ .opcua-tree-modal {
239
+ padding: 10px;
240
+ }
241
+
242
+ .opcua-tree-search {
243
+ min-width: 100%;
244
+ margin-left: 0;
245
+ }
246
+
247
+ .opcua-tree-grid {
248
+ grid-template-columns: 1fr;
249
+ }
250
+
251
+ .opcua-tree-header {
252
+ align-items: flex-start;
253
+ }
254
+
255
+ .opcua-tree-title,
256
+ .opcua-tree-summary,
257
+ .opcua-tree-input,
258
+ .opcua-tree-actions {
259
+ width: 100%;
260
+ }
261
+
262
+ .opcua-tree-actions {
263
+ margin-left: 0;
264
+ }
265
+ }