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

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.
@@ -1,1456 +0,0 @@
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
- const { OpcUaAddressSpaceAlarm } = require("./opcua-address-space-alarm")
14
-
15
- class OpcUaAddressSpaceBuilder {
16
- constructor(options) {
17
- this.namespace = options.namespace;
18
- this.namespaces = options.namespaces || new Map([[2, this.namespace]]);
19
- this.server = options.server;
20
- this.registry = options.registry;
21
- this.node = options.node;
22
- this.serverName = options.serverName;
23
- this.nodeEntries = new Map();
24
- this.variableStore = new Map();
25
- this.variableNodeIdStore = new Map();
26
- this.objectTypeStore = new Map();
27
- this.alarmStore = new Map();
28
- this.pendingAlarms = [];
29
-
30
- this.addressSpace = options.addressSpace;
31
-
32
-
33
-
34
- this.addressSpaceAlarm = new OpcUaAddressSpaceAlarm({
35
- namespace: this.namespace,
36
- registry: this.registry,
37
- server: this.server,
38
- node: this.node
39
- });
40
- }
41
-
42
- rebuild(treeConfig) {
43
-
44
- this.sync(treeConfig, { fullReset: true });
45
- }
46
-
47
- sync(treeConfig, options) {
48
- if (!this.namespace || !this.server) {
49
- throw new Error("Address space is not initialized");
50
- }
51
-
52
- const settings = options || {};
53
- const desiredEntries = this.collectDesiredEntries(treeConfig);
54
-
55
-
56
-
57
- if (settings.fullReset) {
58
- this.clearDynamicNodes();
59
- }
60
-
61
- const removalRoots = this.collectRemovalRoots(desiredEntries);
62
- removalRoots.forEach((path) => this.removeSubtree(path));
63
-
64
-
65
-
66
- this.updateExistingNodes(desiredEntries);
67
- this.createMissingNodes(desiredEntries);
68
- this.flushPendingAlarms();
69
-
70
- }
71
-
72
-
73
-
74
- clearDynamicNodes() {
75
- const paths = Array.from(this.nodeEntries.keys()).sort(comparePathDepthDesc);
76
-
77
- paths.forEach((path) => this.removeSingleNode(path));
78
- this.nodeEntries.clear();
79
- this.variableStore.clear();
80
- this.variableNodeIdStore.clear();
81
- this.objectTypeStore.clear();
82
- this.alarmStore.clear(); // Adicione esta linha
83
- this.pendingAlarms = [];
84
- }
85
-
86
- readValueByPath(path) {
87
- return this.getVariableRecordByPath(path).getValue();
88
- }
89
-
90
- readValueByNodeId(nodeId) {
91
- return this.getVariableRecordByNodeId(nodeId).getValue();
92
- }
93
-
94
- eventValueByPath(valuePayload) {
95
-
96
- const path = valuePayload.nodePath
97
- const value = valuePayload.value
98
- const message = valuePayload.message
99
- const severity = valuePayload.severity
100
- if (!this.nodeEntries.has(path)) {
101
- throw new Error("Unknown path: " + path);
102
- }
103
-
104
- const entry = this.nodeEntries.get(path);
105
-
106
-
107
-
108
- entry.node.raiseEvent("BaseEventType", {
109
- sourceName: {
110
- dataType: DataType.String,
111
- value: path
112
- },
113
- message: {
114
- dataType: DataType.LocalizedText,
115
- value: { text: message }
116
- },
117
- severity: {
118
- dataType: DataType.UInt16,
119
- value: severity
120
- }
121
- });
122
-
123
- }
124
-
125
- writeValueByPath(path, value) {
126
- return this.getVariableRecordByPath(path).setValue(value);
127
- }
128
-
129
- writeValueByNodeId(nodeId, value) {
130
- return this.getVariableRecordByNodeId(nodeId).setValue(value);
131
- }
132
-
133
- readValue(identifierType, identifier) {
134
- return this.getVariableRecord(identifierType, identifier).getValue();
135
- }
136
-
137
- writeValue(identifierType, identifier, value) {
138
- return this.getVariableRecord(identifierType, identifier).setValue(value);
139
- }
140
-
141
- collectDesiredEntries(treeConfig) {
142
- const desiredEntries = new Map();
143
- const objectTypeConfigs = this.buildObjectTypeConfigMap(treeConfig);
144
-
145
- (Array.isArray(treeConfig.objectsTypes) ? treeConfig.objectsTypes : []).forEach((objectTypeConfig) => {
146
- this.collectObjectTypeDefinition(desiredEntries, objectTypeConfig, objectTypeConfigs);
147
- });
148
-
149
- (Array.isArray(treeConfig.folders) ? treeConfig.folders : []).forEach((folderConfig) => {
150
- this.collectBranch(desiredEntries, "folder", folderConfig, "", "organizedBy", objectTypeConfigs);
151
- });
152
-
153
- (Array.isArray(treeConfig.objects) ? treeConfig.objects : []).forEach((objectConfig) => {
154
- this.collectBranch(desiredEntries, "object", objectConfig, "", "organizedBy", objectTypeConfigs);
155
- });
156
-
157
- return desiredEntries;
158
- }
159
-
160
- buildObjectTypeConfigMap(treeConfig) {
161
- const objectTypeConfigs = new Map();
162
-
163
- (Array.isArray(treeConfig.objectsTypes) ? treeConfig.objectsTypes : []).forEach((objectTypeConfig) => {
164
- if (objectTypeConfigs.has(objectTypeConfig.name)) {
165
- throw new Error("Duplicate object type name: " + objectTypeConfig.name);
166
- }
167
- objectTypeConfigs.set(objectTypeConfig.name, objectTypeConfig);
168
- });
169
-
170
- return objectTypeConfigs;
171
- }
172
-
173
- collectObjectTypeDefinition(desiredEntries, config, objectTypeConfigs) {
174
- const path = this.buildObjectTypePath(config.name);
175
- desiredEntries.set(path, this.buildEntryDefinition("objectTypeDefinition", config, path, "", "typeDefinition"));
176
- this.collectBranchChildren(desiredEntries, config, path, "componentOf", objectTypeConfigs, {
177
- skipAlarms: false,
178
- preserveCollectionNames: true,
179
- typeRootPath: path
180
- });
181
- }
182
-
183
- collectBranch(desiredEntries, kind, config, parentPath, relationship, objectTypeConfigs, options) {
184
- const settings = options || {};
185
- const path = settings.preserveCollectionNames
186
- ? this.buildCollectionPath(parentPath, kind === "folder" ? "folders" : "objects", config.name)
187
- : this.buildPath(parentPath, config.name);
188
- desiredEntries.set(path, this.buildEntryDefinition(kind, config, path, parentPath, relationship));
189
- this.collectBranchChildren(
190
- desiredEntries,
191
- config,
192
- path,
193
- kind === "folder" ? "organizedBy" : "componentOf",
194
- objectTypeConfigs,
195
- settings
196
- );
197
- }
198
-
199
- collectBranchChildren(desiredEntries, config, path, childRelationship, objectTypeConfigs, options) {
200
- const settings = options || {};
201
- const preserveCollectionNames = !!settings.preserveCollectionNames;
202
- (Array.isArray(config.folders) ? config.folders : []).forEach((folderConfig) => {
203
- this.collectBranch(
204
- desiredEntries,
205
- "folder",
206
- folderConfig,
207
- path,
208
- childRelationship,
209
- objectTypeConfigs,
210
- settings
211
- );
212
- });
213
-
214
- (Array.isArray(config.objects) ? config.objects : []).forEach((objectConfig) => {
215
- this.collectBranch(
216
- desiredEntries,
217
- "object",
218
- objectConfig,
219
- path,
220
- childRelationship,
221
- objectTypeConfigs,
222
- settings
223
- );
224
- });
225
-
226
- (Array.isArray(config.variables) ? config.variables : []).forEach((variableConfig) => {
227
- const childPath = preserveCollectionNames
228
- ? this.buildCollectionPath(path, "variables", variableConfig.name)
229
- : this.buildPath(path, variableConfig.name);
230
- desiredEntries.set(
231
- childPath,
232
- this.buildEntryDefinition("variable", variableConfig, childPath, path, "componentOf")
233
- );
234
- });
235
-
236
- (Array.isArray(config.methods) ? config.methods : []).forEach((methodConfig) => {
237
- const childPath = preserveCollectionNames
238
- ? this.buildCollectionPath(path, "methods", methodConfig.name)
239
- : this.buildPath(path, methodConfig.name);
240
- desiredEntries.set(
241
- childPath,
242
- this.buildEntryDefinition("method", methodConfig, childPath, path, "componentOf")
243
- );
244
- });
245
-
246
- (Array.isArray(config.objectsTypes) ? config.objectsTypes : []).forEach((objectTypeInstanceConfig) => {
247
- this.collectObjectTypeInstance(
248
- desiredEntries,
249
- objectTypeInstanceConfig,
250
- path,
251
- childRelationship,
252
- objectTypeConfigs,
253
- settings
254
- );
255
- });
256
-
257
- if (!settings.skipAlarms) {
258
- //alarmes
259
- (Array.isArray(config.alarms) ? config.alarms : []).forEach((alarmConfig) => {
260
- const childPath = preserveCollectionNames
261
- ? this.buildCollectionPath(path, "alarms", alarmConfig.name)
262
- : this.buildPath(path, alarmConfig.name);
263
-
264
- desiredEntries.set(
265
- childPath,
266
- this.buildEntryDefinition("alarm", alarmConfig, childPath, path, "componentOf")
267
- );
268
- //this.addAlarmPlaceholder(path, alarmConfig);
269
- });
270
- }
271
- }
272
-
273
- collectObjectTypeInstance(desiredEntries, instanceConfig, parentPath, relationship, objectTypeConfigs, options) {
274
- const path = this.buildPath(parentPath, instanceConfig.name);
275
- desiredEntries.set(
276
- path,
277
- this.buildEntryDefinition("objectTypeInstance", instanceConfig, path, parentPath, relationship)
278
- );
279
- // Variables, methods and nested objectsTypes inherited from the type definition
280
- // are instantiated automatically by node-opcua when addObject({ typeDefinition })
281
- // is called. We must NOT add them as independent desiredEntries — that would
282
- // create duplicate nodes on top of the ones the lib already created.
283
- // Only extra children defined directly on the instance (not inherited) are added here.
284
- const instanceOnlyConfig = {
285
- folders: Array.isArray(instanceConfig.folders) ? instanceConfig.folders : [],
286
- objects: Array.isArray(instanceConfig.objects) ? instanceConfig.objects : [],
287
- variables: Array.isArray(instanceConfig.variables) ? instanceConfig.variables : [],
288
- methods: Array.isArray(instanceConfig.methods) ? instanceConfig.methods : [],
289
- alarms: Array.isArray(instanceConfig.alarms) ? instanceConfig.alarms : [],
290
- objectsTypes: Array.isArray(instanceConfig.objectsTypes) ? instanceConfig.objectsTypes : []
291
- };
292
- this.collectBranchChildren(desiredEntries, instanceOnlyConfig, path, "componentOf", objectTypeConfigs, Object.assign({}, options, {
293
- skipAlarms: false,
294
- preserveCollectionNames: false
295
- }));
296
- }
297
-
298
- expandObjectTypeInstanceConfig(instanceConfig, objectTypeConfigs, stack) {
299
- const typeName = instanceConfig.objectsType;
300
- if (stack.indexOf(typeName) !== -1) {
301
- throw new Error("Circular object type reference: " + stack.concat(typeName).join(" -> "));
302
- }
303
-
304
- const baseConfig = objectTypeConfigs.get(typeName);
305
- if (!baseConfig) {
306
- throw new Error("Unknown object type: " + typeName);
307
- }
308
-
309
- return this.mergeObjectTypeConfigs(baseConfig, instanceConfig, objectTypeConfigs, stack.concat(typeName));
310
- }
311
-
312
- mergeObjectTypeConfigs(baseConfig, instanceConfig, objectTypeConfigs, stack) {
313
- const merged = {
314
- name: instanceConfig.name,
315
- displayName: instanceConfig.displayName || instanceConfig.name,
316
- description: instanceConfig.description || "",
317
- folders: this.cloneConfigs(baseConfig.folders),
318
- objects: this.cloneConfigs(baseConfig.objects),
319
- variables: this.cloneConfigs(baseConfig.variables),
320
- methods: this.cloneConfigs(baseConfig.methods),
321
- alarms: this.cloneConfigs(baseConfig.alarms),
322
- objectsTypes: []
323
- };
324
-
325
- const baseInstances = Array.isArray(baseConfig.objectsTypes) ? baseConfig.objectsTypes : [];
326
- const instanceOverrides = Array.isArray(instanceConfig.objectsTypes) ? instanceConfig.objectsTypes : [];
327
-
328
- merged.objectsTypes = baseInstances
329
- .map((nestedInstanceConfig) => this.mergeNestedObjectTypeInstance(nestedInstanceConfig, objectTypeConfigs, stack))
330
- .concat(this.cloneConfigs(instanceOverrides));
331
-
332
- merged.folders = merged.folders.concat(this.cloneConfigs(instanceConfig.folders));
333
- merged.objects = merged.objects.concat(this.cloneConfigs(instanceConfig.objects));
334
- merged.variables = merged.variables.concat(this.cloneConfigs(instanceConfig.variables));
335
- merged.methods = merged.methods.concat(this.cloneConfigs(instanceConfig.methods));
336
- merged.alarms = merged.alarms.concat(this.cloneConfigs(instanceConfig.alarms));
337
-
338
- return merged;
339
- }
340
-
341
- mergeNestedObjectTypeInstance(instanceConfig, objectTypeConfigs, stack) {
342
- const cloned = this.cloneConfig(instanceConfig);
343
- if (cloned && cloned.objectsType) {
344
- const typeName = cloned.objectsType;
345
- if (stack.indexOf(typeName) !== -1) {
346
- throw new Error("Circular object type reference: " + stack.concat(typeName).join(" -> "));
347
- }
348
- if (!objectTypeConfigs.has(typeName)) {
349
- throw new Error("Unknown object type: " + typeName);
350
- }
351
- }
352
- return cloned;
353
- }
354
-
355
- cloneConfigs(items) {
356
- return Array.isArray(items) ? items.map((item) => this.cloneConfig(item)) : [];
357
- }
358
-
359
- cloneConfig(item) {
360
- return item ? JSON.parse(JSON.stringify(item)) : item;
361
- }
362
-
363
- rewriteAlarmVariablePaths(branchConfig, currentPath, rootInstancePath) {
364
- if (!branchConfig || typeof branchConfig !== "object") {
365
- return;
366
- }
367
-
368
- (Array.isArray(branchConfig.alarms) ? branchConfig.alarms : []).forEach((alarmConfig) => {
369
- const variableNodeId = String(alarmConfig.variableNodeId || "").trim();
370
- if (!variableNodeId) {
371
- return;
372
- }
373
-
374
- if (variableNodeId.indexOf(".") === 0) {
375
- alarmConfig.variableNodeId = this.resolveObjectTypeRelativeReference(rootInstancePath, variableNodeId);
376
- return;
377
- }
378
-
379
- if (variableNodeId.indexOf(currentPath + ".") === 0 || variableNodeId === currentPath) {
380
- return;
381
- }
382
-
383
- if (variableNodeId.indexOf(rootInstancePath + ".") === 0 || variableNodeId === rootInstancePath) {
384
- return;
385
- }
386
-
387
- if (variableNodeId.indexOf("__objectTypes.") === 0) {
388
- return;
389
- }
390
-
391
- alarmConfig.variableNodeId = currentPath + "." + variableNodeId;
392
- });
393
-
394
- (Array.isArray(branchConfig.folders) ? branchConfig.folders : []).forEach((folderConfig) => {
395
- this.rewriteAlarmVariablePaths(folderConfig, this.buildPath(currentPath, folderConfig.name), rootInstancePath);
396
- });
397
-
398
- (Array.isArray(branchConfig.objects) ? branchConfig.objects : []).forEach((objectConfig) => {
399
- this.rewriteAlarmVariablePaths(objectConfig, this.buildPath(currentPath, objectConfig.name), rootInstancePath);
400
- });
401
- }
402
-
403
- buildEntryDefinition(kind, config, path, parentPath, relationship) {
404
- return {
405
- kind,
406
- path,
407
- parentPath,
408
- relationship,
409
- config,
410
- signature: this.buildSignature(kind, config, parentPath, relationship)
411
- };
412
- }
413
-
414
- buildSignature(kind, config, parentPath, relationship) {
415
- if (kind === "variable") {
416
- return JSON.stringify({
417
- kind,
418
- parentPath,
419
- relationship,
420
- nodeId: config.nodeId || "",
421
- namespaceId: this.resolveNamespaceId(config),
422
- displayName: config.displayName || config.name,
423
- description: config.description || "",
424
- type: config.type,
425
- access: config.access,
426
- isArray: this.isArrayValue(config.value)
427
- });
428
- }
429
-
430
- if (kind === "method") {
431
- return JSON.stringify({
432
- kind,
433
- parentPath,
434
- relationship,
435
- nodeId: config.nodeId || "",
436
- namespaceId: this.resolveNamespaceId(config),
437
- displayName: config.displayName || config.name,
438
- description: config.description || "",
439
- inputs: Array.isArray(config.inputs) ? config.inputs : [],
440
- outputs: Array.isArray(config.outputs) ? config.outputs : []
441
- });
442
- }
443
-
444
- if (kind === "objectTypeInstance") {
445
- return JSON.stringify({
446
- kind,
447
- parentPath,
448
- relationship,
449
- nodeId: config.nodeId || "",
450
- namespaceId: this.resolveNamespaceId(config),
451
- displayName: config.displayName || config.name,
452
- description: config.description || "",
453
- objectsType: config.objectsType
454
- });
455
- }
456
-
457
- return JSON.stringify({
458
- kind,
459
- parentPath,
460
- relationship,
461
- nodeId: config.nodeId || "",
462
- namespaceId: this.resolveNamespaceId(config),
463
- displayName: config.displayName || config.name,
464
- description: config.description || ""
465
- });
466
- }
467
-
468
- collectRemovalRoots(desiredEntries) {
469
- const candidates = [];
470
-
471
- this.nodeEntries.forEach((entry, path) => {
472
- const desired = desiredEntries.get(path);
473
-
474
- if (!desired) {
475
- candidates.push(path);
476
- return;
477
- }
478
-
479
- if (entry.kind !== desired.kind || entry.signature !== desired.signature) {
480
- candidates.push(path);
481
- }
482
- });
483
-
484
- return collapsePaths(candidates);
485
- }
486
-
487
- updateExistingNodes(desiredEntries) {
488
- desiredEntries.forEach((desired, path) => {
489
- const existing = this.nodeEntries.get(path);
490
- if (!existing || existing.kind !== desired.kind) {
491
- return;
492
- }
493
-
494
- if (existing.kind === "variable") {
495
- const record = this.variableStore.get(path);
496
- if (record) {
497
- record.setRuntimeValue(desired.config.value);
498
- }
499
- }
500
- });
501
- }
502
-
503
- createMissingNodes(desiredEntries) {
504
- const ordered = Array.from(desiredEntries.values()).sort(compareEntryCreationOrder);
505
- ordered.forEach((definition) => {
506
- if (this.nodeEntries.has(definition.path)) {
507
- return;
508
- }
509
-
510
- this.createNode(definition);
511
- });
512
- }
513
-
514
- createNode(definition) {
515
- const parentNode = this.resolveParentNode(definition.parentPath);
516
-
517
-
518
-
519
- if (definition.kind === "folder") {
520
- this.addFolder(parentNode, definition.config, definition.parentPath, definition.relationship, definition.path);
521
- return;
522
- }
523
-
524
- if (definition.kind === "objectTypeDefinition") {
525
-
526
- this.addObjectTypeDefinition(definition.config);
527
- return;
528
- }
529
-
530
- if (definition.kind === "object") {
531
- this.addObject(parentNode, definition.config, definition.parentPath, definition.relationship, definition.path);
532
- return;
533
- }
534
-
535
- if (definition.kind === "objectTypeInstance") {
536
- this.addObjectTypeInstance(parentNode, definition.config, definition.parentPath, definition.relationship, definition.path);
537
- return;
538
- }
539
-
540
- if (definition.kind === "alarm") {
541
- this.addAlarm(parentNode, definition.config, definition.parentPath, definition.relationship, definition.path);
542
- return;
543
-
544
- }
545
-
546
- if (definition.kind === "variable") {
547
- this.addVariable(parentNode, definition.config, definition.parentPath, definition.path);
548
- return;
549
- }
550
-
551
- if (definition.kind === "method") {
552
- this.addMethod(parentNode, definition.config, definition.parentPath, definition.path);
553
- }
554
-
555
-
556
- }
557
-
558
- resolveParentNode(parentPath) {
559
- if (!parentPath) {
560
- return this.server.engine.addressSpace.rootFolder.objects;
561
- }
562
-
563
- const parentEntry = this.nodeEntries.get(parentPath);
564
- if (!parentEntry || !parentEntry.node) {
565
- throw new Error("Parent node is not available for path: " + parentPath);
566
- }
567
-
568
- return parentEntry.node;
569
- }
570
-
571
- removeSubtree(rootPath) {
572
- const affectedPaths = Array.from(this.nodeEntries.keys())
573
- .filter((path) => isSamePathOrDescendant(path, rootPath))
574
- .sort(comparePathDepthDesc);
575
-
576
- affectedPaths.forEach((path) => this.removeSingleNode(path));
577
- }
578
-
579
- removeSingleNode(path) {
580
- const entry = this.nodeEntries.get(path);
581
- if (!entry) {
582
- return;
583
- }
584
-
585
- if (entry.kind === "variable") {
586
- const record = this.variableStore.get(path);
587
- if (record && record.nodeIdKey) {
588
- this.variableNodeIdStore.delete(record.nodeIdKey);
589
- }
590
- this.variableStore.delete(path);
591
- }
592
-
593
- if (entry.kind === "objectTypeDefinition") {
594
- this.objectTypeStore.delete(entry.config.name);
595
- }
596
-
597
- try {
598
- entry.namespace.deleteNode(entry.node);
599
-
600
- } catch (error) {
601
- console.error("error removeSingleNode")
602
- console.error(error)
603
- this.node.debug("Ignoring deleteNode error for " + path + ": " + error.message);
604
- }
605
-
606
-
607
-
608
- this.nodeEntries.delete(path);
609
- }
610
-
611
- addObject(parentNode, objectConfig, parentPath, relationship, pathOverride) {
612
- const namespace = this.getNamespaceForConfig(objectConfig);
613
-
614
- const addressSpace = this.server.engine.addressSpace;
615
- const serverNode = addressSpace.rootFolder.objects.server;
616
-
617
-
618
- const objectName = objectConfig.name;
619
- const nextPath = pathOverride || this.buildPath(parentPath, objectName);
620
- const options = {
621
- browseName: objectConfig.displayName || objectName,
622
- displayName: objectConfig.displayName || objectName,
623
- description: objectConfig.description || "",
624
- nodeId: this.resolveNodeId(objectConfig, nextPath, namespace),
625
- eventNotifier: 1, //enabled_events,
626
- eventSourceOf: serverNode
627
- };
628
-
629
- if (relationship === "organizedBy") {
630
- options.organizedBy = parentNode;
631
- } else {
632
- options.componentOf = parentNode;
633
- }
634
-
635
- if (this.isObjectTypePath(parentPath)) {
636
- options.modellingRule = "Mandatory";
637
- }
638
-
639
- const objectNode = namespace.addObject(options);
640
- this.registerNodeEntry("object", nextPath, parentPath, relationship, objectConfig, objectNode, namespace);
641
- }
642
-
643
- addObjectTypeDefinition(objectTypeConfig) {
644
- const namespace = this.getNamespaceForConfig(objectTypeConfig);
645
- const objectTypeNode = namespace.addObjectType({
646
- browseName: objectTypeConfig.name,
647
- displayName: objectTypeConfig.displayName || objectTypeConfig.name,
648
- description: objectTypeConfig.description || "",
649
- nodeId: this.resolveNodeId(objectTypeConfig, this.buildObjectTypePath(objectTypeConfig.name), namespace),
650
- subtypeOf: "BaseObjectType"
651
- });
652
-
653
-
654
- const path = this.buildObjectTypePath(objectTypeConfig.name);
655
-
656
-
657
-
658
- this.registerNodeEntry("objectTypeDefinition", path, "", "typeDefinition", objectTypeConfig, objectTypeNode, namespace);
659
- this.objectTypeStore.set(objectTypeConfig.name, {
660
- node: objectTypeNode,
661
- config: objectTypeConfig,
662
- path: path,
663
- namespace: namespace
664
- });
665
- }
666
-
667
- addObjectTypeInstance(parentNode, instanceConfig, parentPath, relationship, pathOverride) {
668
- const objectTypeEntry = this.objectTypeStore.get(instanceConfig.objectsType);
669
- if (!objectTypeEntry || !objectTypeEntry.node) {
670
- throw new Error("Object type is not available for instance " + instanceConfig.name + ": " + instanceConfig.objectsType);
671
- }
672
-
673
- const objectName = instanceConfig.name;
674
- const nextPath = pathOverride || this.buildPath(parentPath, objectName);
675
- const namespace = this.getNamespaceForConfig(instanceConfig);
676
-
677
- const options = {
678
- browseName: instanceConfig.displayName || objectName,
679
- displayName: instanceConfig.displayName || objectName,
680
- description: instanceConfig.description || "",
681
- nodeId: this.resolveNodeId(instanceConfig, nextPath, namespace),
682
- typeDefinition: objectTypeEntry.node.nodeId,
683
- eventNotifier: 1
684
- };
685
-
686
- if (relationship === "organizedBy") {
687
- options.organizedBy = parentNode;
688
- } else {
689
- options.componentOf = parentNode;
690
- }
691
-
692
- const objectNode = namespace.addObject(options);
693
- this.registerNodeEntry("objectTypeInstance", nextPath, parentPath, relationship, instanceConfig, objectNode, namespace);
694
-
695
- // Create the inherited children explicitly using the configs that opcua-config.js
696
- // already rewrote with the correct instance nodeIds (e.g. server1.newObjectType.cmd_on).
697
- // We do NOT call instantiate() because node-opcua cannot auto-assign nodeIds for the
698
- // cloned children when the type children already have explicit string nodeIds registered.
699
- this.createInheritedChildren(objectNode, nextPath, instanceConfig, namespace);
700
- }
701
-
702
- createInheritedChildren(instanceNode, instancePath, instanceConfig, namespace) {
703
- const typeEntry = this.objectTypeStore.get(instanceConfig.objectsType);
704
- if (!typeEntry) return;
705
-
706
- // Extract the type's nodeId value prefix (e.g. "Motor_type2") and the
707
- // instance's nodeId value prefix (e.g. "server1.newObjectType") so we can
708
- // rewrite every child nodeId from the type to the instance on-the-fly.
709
- const typeNodeId = typeEntry.config.nodeId || "";
710
- const instanceNodeId = instanceConfig.nodeId || "";
711
- const typePrefix = this.extractNodeIdStringValue(typeNodeId);
712
- const instancePrefix = this.extractNodeIdStringValue(instanceNodeId);
713
-
714
- this.createInheritedBranchChildren(typeEntry.config, instanceNode, instancePath, typePrefix, instancePrefix);
715
- }
716
-
717
- extractNodeIdStringValue(nodeId) {
718
- const m = String(nodeId || "").match(/(?:^|;)s=(.+)$/);
719
- return m ? m[1] : "";
720
- }
721
-
722
- rewriteInheritedNodeId(nodeId, typePrefix, instancePrefix) {
723
- if (!nodeId || !typePrefix || !instancePrefix) return nodeId;
724
- const m = String(nodeId).match(/^(ns=\d+;s=)([\s\S]*)$/);
725
- if (m && m[2].startsWith(typePrefix)) {
726
- return m[1] + instancePrefix + m[2].slice(typePrefix.length);
727
- }
728
- return nodeId;
729
- }
730
-
731
- createInheritedBranchChildren(typeConfig, parentOpcNode, parentPath, typePrefix, instancePrefix) {
732
- const variables = Array.isArray(typeConfig.variables) ? typeConfig.variables : [];
733
- const methods = Array.isArray(typeConfig.methods) ? typeConfig.methods : [];
734
- const folders = Array.isArray(typeConfig.folders) ? typeConfig.folders : [];
735
- const objects = Array.isArray(typeConfig.objects) ? typeConfig.objects : [];
736
-
737
- variables.forEach((varConfig) => {
738
- const childPath = this.buildPath(parentPath, varConfig.name);
739
- const rewrittenConfig = Object.assign({}, varConfig, {
740
- nodeId: this.rewriteInheritedNodeId(varConfig.nodeId, typePrefix, instancePrefix)
741
- });
742
- this.addVariable(parentOpcNode, rewrittenConfig, parentPath, childPath);
743
- });
744
-
745
- methods.forEach((methodConfig) => {
746
- const childPath = this.buildPath(parentPath, methodConfig.name);
747
- const rewrittenConfig = Object.assign({}, methodConfig, {
748
- nodeId: this.rewriteInheritedNodeId(methodConfig.nodeId, typePrefix, instancePrefix)
749
- });
750
- this.addMethod(parentOpcNode, rewrittenConfig, parentPath, childPath);
751
- });
752
-
753
- folders.forEach((folderConfig) => {
754
- const childPath = this.buildPath(parentPath, folderConfig.name);
755
- const rewrittenConfig = Object.assign({}, folderConfig, {
756
- nodeId: this.rewriteInheritedNodeId(folderConfig.nodeId, typePrefix, instancePrefix)
757
- });
758
- this.addFolder(parentOpcNode, rewrittenConfig, parentPath, "componentOf", childPath);
759
- const childNode = this.nodeEntries.get(childPath) && this.nodeEntries.get(childPath).node;
760
- if (childNode) {
761
- this.createInheritedBranchChildren(folderConfig, childNode, childPath, typePrefix, instancePrefix);
762
- }
763
- });
764
-
765
- objects.forEach((objectConfig) => {
766
- const childPath = this.buildPath(parentPath, objectConfig.name);
767
- const rewrittenConfig = Object.assign({}, objectConfig, {
768
- nodeId: this.rewriteInheritedNodeId(objectConfig.nodeId, typePrefix, instancePrefix)
769
- });
770
- this.addObject(parentOpcNode, rewrittenConfig, parentPath, "componentOf", childPath);
771
- const childNode = this.nodeEntries.get(childPath) && this.nodeEntries.get(childPath).node;
772
- if (childNode) {
773
- this.createInheritedBranchChildren(objectConfig, childNode, childPath, typePrefix, instancePrefix);
774
- }
775
- });
776
- }
777
-
778
- addAlarm(parentNode, alarmConfig, parentPath, relationship, pathOverride) {
779
-
780
-
781
-
782
- try {
783
-
784
- const name = alarmConfig.name;
785
- const variableNodeId = alarmConfig.variableNodeId;
786
- const objectName = alarmConfig.name;
787
- const nextPath = pathOverride || this.buildPath(parentPath, objectName);
788
- const path = pathOverride || this.buildPath(parentPath, name);
789
- const namespace = this.getNamespaceForConfig(alarmConfig);
790
- const options = {
791
- browseName: alarmConfig.displayName || objectName,
792
- displayName: alarmConfig.displayName || objectName,
793
- description: alarmConfig.description || "",
794
- nodeId: this.resolveNodeId(alarmConfig, nextPath, namespace),
795
- eventNotifier: 1 //enabled_events
796
- };
797
-
798
- if (relationship === "organizedBy") {
799
- options.organizedBy = parentNode;
800
- } else {
801
- options.componentOf = parentNode;
802
- }
803
-
804
- const variableToMonitor = this.resolveAlarmVariableRecord(parentPath, parentNode, variableNodeId)
805
-
806
- const sourceName = variableToMonitor.node.displayName[0].text
807
-
808
- const browseName = alarmConfig.displayName || objectName
809
- const inputNode = variableToMonitor.node
810
-
811
-
812
-
813
- const conditionName = alarmConfig.displayName || objectName
814
- const nodeId = this.resolveNodeId(alarmConfig, nextPath, namespace)
815
-
816
- const alarmNode = this.addressSpaceAlarm.createAlarm(namespace, browseName, parentNode, inputNode, conditionName, nodeId, sourceName, alarmConfig)
817
-
818
-
819
-
820
-
821
- // register alarm
822
- variableToMonitor.alarm = {
823
- node: alarmNode,
824
- alarmConfig: alarmConfig,
825
- }
826
- // const alarmNode = this.namespace.instantiateOffNormalAlarm(options);
827
- this.registerNodeEntry("alarm", nextPath, parentPath, relationship, alarmConfig, alarmNode, namespace);
828
-
829
-
830
-
831
-
832
- } catch (error) {
833
-
834
- if (error && error.message && error.message.indexOf("Unknown alarm variable reference:") === 0) {
835
- this.queuePendingAlarm(parentNode, alarmConfig, parentPath, relationship);
836
- return;
837
- }
838
- console.error("erro addAlarm")
839
- console.error(error)
840
- }
841
-
842
-
843
- }
844
-
845
- queuePendingAlarm(parentNode, alarmConfig, parentPath, relationship) {
846
- const path = this.buildPath(parentPath, alarmConfig.name);
847
- const alreadyQueued = this.pendingAlarms.some((item) => item.path === path);
848
- if (alreadyQueued) {
849
- return;
850
- }
851
-
852
- this.pendingAlarms.push({
853
- path: path,
854
- parentNode: parentNode,
855
- alarmConfig: this.cloneConfig(alarmConfig),
856
- parentPath: parentPath,
857
- relationship: relationship
858
- });
859
- }
860
-
861
- flushPendingAlarms() {
862
- if (!this.pendingAlarms.length) {
863
- return;
864
- }
865
-
866
- const pending = this.pendingAlarms.slice();
867
- this.pendingAlarms = [];
868
-
869
- pending.forEach((item) => {
870
- if (this.nodeEntries.has(item.path)) {
871
- return;
872
- }
873
-
874
- this.addAlarm(item.parentNode, item.alarmConfig, item.parentPath, item.relationship);
875
- });
876
-
877
- if (this.pendingAlarms.length) {
878
- const unresolved = this.pendingAlarms.map((item) => item.path).join(", ");
879
- this.pendingAlarms = [];
880
- throw new Error("Unresolved alarms after build: " + unresolved);
881
- }
882
- }
883
-
884
-
885
-
886
-
887
- addFolder(parentNode, folderConfig, parentPath, relationship, pathOverride) {
888
- const namespace = this.getNamespaceForConfig(folderConfig);
889
-
890
- const addressSpace = this.server.engine.addressSpace;
891
- const serverNode = addressSpace.rootFolder.objects.server;
892
-
893
- const folderName = folderConfig.name;
894
- const nextPath = pathOverride || this.buildPath(parentPath, folderName);
895
- const options = {
896
- browseName: folderConfig.displayName || folderName,
897
- displayName: folderConfig.displayName || folderName,
898
- description: folderConfig.description || "",
899
- nodeId: this.resolveNodeId(folderConfig, nextPath, namespace),
900
- typeDefinition: "FolderType",
901
- eventSourceOf: serverNode,
902
- eventNotifier: 1 //enabled_events
903
- };
904
-
905
- if (relationship === "organizedBy") {
906
- options.organizedBy = parentNode;
907
- } else {
908
- options.componentOf = parentNode;
909
- }
910
-
911
- if (this.isObjectTypePath(parentPath)) {
912
- options.modellingRule = "Mandatory";
913
- }
914
-
915
- const folderNode = namespace.addObject(options);
916
- this.registerNodeEntry("folder", nextPath, parentPath, relationship, folderConfig, folderNode, namespace);
917
- }
918
-
919
- addVariable(parentNode, variableConfig, parentPath, pathOverride) {
920
- const namespace = this.getNamespaceForConfig(variableConfig);
921
- const name = variableConfig.name;
922
- const type = variableConfig.type;
923
- const access = variableConfig.access;
924
- const path = pathOverride || this.buildPath(parentPath, name);
925
- const nodeId = this.resolveNodeId(variableConfig, path, namespace);
926
- const browseName = variableConfig.displayName || name;
927
- const state = {
928
- type,
929
- access,
930
- isArray: this.isArrayValue(variableConfig.value),
931
- currentValue: variableConfig.value
932
- };
933
-
934
- const variableNode = namespace.addVariable({
935
- componentOf: parentNode,
936
- browseName,
937
- displayName: browseName,
938
- description: variableConfig.description || "",
939
- nodeId,
940
- dataType: type,
941
- modellingRule: this.isObjectTypePath(parentPath) ? "Mandatory" : undefined,
942
- valueRank: state.isArray ? 1 : -1,
943
- accessLevel: access === "readwrite" ? "CurrentRead | CurrentWrite" : "CurrentRead",
944
- userAccessLevel: access === "readwrite" ? "CurrentRead | CurrentWrite" : "CurrentRead",
945
- minimumSamplingInterval: 500,
946
- value: {
947
- get: () => {
948
- this.emitTagAccess("read", {
949
- path,
950
- nodeID: nodeId,
951
- browseName,
952
- dataType: state.type,
953
- value: state.currentValue
954
- });
955
-
956
- const variantOptions = {
957
- dataType: DATA_TYPE_MAP[state.type],
958
- value: state.currentValue
959
- };
960
-
961
- if (this.isArrayValue(state.currentValue)) {
962
- variantOptions.arrayType = VariantArrayType.Array;
963
- }
964
-
965
- return new Variant(variantOptions);
966
- },
967
- set: (variant) => {
968
- if (state.access !== "readwrite") {
969
- return StatusCodes.BadNotWritable;
970
- }
971
-
972
- try {
973
- state.currentValue = this.coerceValue(variant.value, state.type, state.isArray);
974
- this.emitTagAccess("write", {
975
- path,
976
- nodeID: nodeId,
977
- browseName,
978
- dataType: state.type,
979
- value: state.currentValue
980
- });
981
-
982
- const alarm = this.variableStore.get(path).alarm
983
- this.addressSpaceAlarm.checkAlarm(alarm, variant.value)
984
-
985
-
986
-
987
- return StatusCodes.Good;
988
- } catch (error) {
989
- console.error("addVariable")
990
- console.error(error)
991
- // this.node.warn("Rejected OPC UA write for " + path + ": " + error.message);
992
- return StatusCodes.BadTypeMismatch;
993
- }
994
- }
995
- }
996
- });
997
-
998
- this.registerNodeEntry("variable", path, parentPath, "componentOf", variableConfig, variableNode, namespace);
999
- const record = {
1000
- node: variableNode,
1001
- path: path,
1002
- nodeId: nodeId,
1003
- nodeIdKey: this.normalizeNodeIdKey(nodeId),
1004
- getValue: () => state.currentValue,
1005
- setRuntimeValue: (nextValue) => {
1006
- state.currentValue = this.coerceValue(nextValue, state.type, state.isArray);
1007
- return state.currentValue;
1008
- },
1009
- setValue: (nextValue) => {
1010
- if (state.access !== "readwrite") {
1011
- throw new Error("Tag is read-only: " + path);
1012
- }
1013
-
1014
- state.currentValue = this.coerceValue(nextValue, state.type, state.isArray);
1015
-
1016
- const alarm = this.variableStore.get(path).alarm
1017
- this.addressSpaceAlarm.checkAlarm(alarm, state.currentValue)
1018
-
1019
- return state.currentValue;
1020
- }
1021
- };
1022
- this.variableStore.set(path, record);
1023
- this.variableNodeIdStore.set(record.nodeIdKey, record);
1024
- }
1025
-
1026
-
1027
-
1028
-
1029
-
1030
- addMethod(parentNode, methodConfig, parentPath, pathOverride) {
1031
- const namespace = this.getNamespaceForConfig(methodConfig);
1032
- const methodName = methodConfig.name;
1033
- const path = pathOverride || this.buildPath(parentPath, methodName);
1034
- const methodNode = namespace.addMethod(parentNode, {
1035
- browseName: methodConfig.displayName || methodName,
1036
- displayName: methodConfig.displayName || methodName,
1037
- description: { text: methodConfig.description || "" },
1038
- nodeId: this.resolveNodeId(methodConfig, path, namespace),
1039
- modellingRule: this.isObjectTypePath(parentPath) ? "Mandatory" : undefined,
1040
- inputArguments: methodConfig.inputs.map((arg) => ({
1041
- name: arg.name,
1042
- description: { text: arg.description || "" },
1043
- dataType: DATA_TYPE_MAP[arg.type]
1044
- })),
1045
- outputArguments: methodConfig.outputs.map((arg) => ({
1046
- name: arg.name,
1047
- description: { text: arg.description || "" },
1048
- dataType: DATA_TYPE_MAP[arg.type]
1049
- }))
1050
- });
1051
-
1052
- methodNode.bindMethod((inputArguments, context, callback) => {
1053
- const callId = Date.now() + "_" + Math.random();
1054
-
1055
- this.registry.emitMethodCall({
1056
- methodName: methodConfig.name,
1057
- callId,
1058
- inputArguments,
1059
- serverName: this.serverName
1060
- });
1061
-
1062
- this.registry.waitForMethodResponse(callId)
1063
- .then((outputs) => {
1064
- callback(null, {
1065
- statusCode: StatusCodes.Good,
1066
- outputArguments: outputs.map((output) => new Variant(output))
1067
- });
1068
- })
1069
- .catch(() => {
1070
- callback(null, {
1071
- statusCode: StatusCodes.BadInternalError
1072
- });
1073
- });
1074
- });
1075
-
1076
- this.registerNodeEntry("method", path, parentPath, "componentOf", methodConfig, methodNode, namespace);
1077
- }
1078
-
1079
- addAlarmPlaceholder(parentPath, alarmConfig) {
1080
- const path = this.buildPath(parentPath, alarmConfig.name);
1081
- this.node.debug("Alarm definition received but not implemented yet: " + path + " (" + alarmConfig.type + ")");
1082
- }
1083
-
1084
- registerNodeEntry(kind, path, parentPath, relationship, config, node, namespace) {
1085
- this.nodeEntries.set(path, {
1086
- kind,
1087
- path,
1088
- parentPath,
1089
- relationship,
1090
- config,
1091
- signature: this.buildSignature(kind, config, parentPath, relationship),
1092
- node,
1093
- namespace
1094
- });
1095
- }
1096
-
1097
- buildPath(parentPath, name) {
1098
- return parentPath ? parentPath + "." + name : name;
1099
- }
1100
-
1101
- buildObjectTypePath(name) {
1102
- return "__objectTypes." + name;
1103
- }
1104
-
1105
- buildCollectionPath(parentPath, collectionName, name) {
1106
-
1107
- // return parentPath ? parentPath + "." + collectionName + "." + name : collectionName + "." + name;
1108
- //change nodeId path
1109
- return parentPath ? parentPath + "." + name : name + "." + name;
1110
-
1111
- }
1112
-
1113
- isObjectTypePath(path) {
1114
- return String(path || "").indexOf("__objectTypes.") === 0;
1115
- }
1116
-
1117
- buildStableNodeId(path, namespace) {
1118
- return "ns=" + namespace.index + ";s=" + sanitizeNodeIdPath(path);
1119
- }
1120
-
1121
- resolveNodeId(config, path, namespace) {
1122
- const customNodeId = config && typeof config.nodeId === "string" ? config.nodeId.trim() : "";
1123
- return customNodeId || this.buildStableNodeId(path, namespace);
1124
- }
1125
-
1126
- resolveNamespaceId(config) {
1127
- const namespaceId = config && config.namespaceId !== undefined ? Number(config.namespaceId) : 2;
1128
- return Number.isInteger(namespaceId) && namespaceId >= 2 ? namespaceId : 2;
1129
- }
1130
-
1131
- getNamespaceForConfig(config) {
1132
- const namespaceId = this.resolveNamespaceId(config);
1133
- if (!this.namespaces.has(namespaceId)) {
1134
- throw new Error("Namespace " + namespaceId + " is not available");
1135
- }
1136
-
1137
- return this.namespaces.get(namespaceId);
1138
- }
1139
-
1140
- emitTagAccess(operation, details) {
1141
- this.registry.emitTagAccess({
1142
- operation,
1143
- serverId: this.node.id,
1144
- serverNodeName: this.node.name || "",
1145
- serverName: this.serverName,
1146
- timestamp: new Date().toISOString(),
1147
- path: details.path,
1148
- nodeID: details.nodeID,
1149
- browseName: details.browseName,
1150
- dataType: details.dataType,
1151
- value: details.value
1152
- });
1153
- }
1154
-
1155
- getVariableRecord(identifierType, identifier) {
1156
- if (identifierType === "nodeId") {
1157
- return this.getVariableRecordByNodeId(identifier);
1158
- }
1159
-
1160
- return this.getVariableRecordByPath(identifier);
1161
- }
1162
-
1163
- getVariableRecordByPath(path) {
1164
- if (!this.variableStore.has(path)) {
1165
- throw new Error("Unknown path: " + path);
1166
- }
1167
-
1168
- return this.variableStore.get(path);
1169
- }
1170
-
1171
- getVariableRecordByNodeId(nodeId) {
1172
- const key = this.normalizeNodeIdKey(nodeId);
1173
- if (!this.variableNodeIdStore.has(key)) {
1174
- throw new Error("Unknown nodeId: " + nodeId);
1175
- }
1176
-
1177
- return this.variableNodeIdStore.get(key);
1178
- }
1179
-
1180
- normalizeNodeIdKey(nodeId) {
1181
- return coerceNodeId(nodeId).toString();
1182
- }
1183
-
1184
- resolveAlarmVariableRecord(parentPath, parentNode, variableNodeId) {
1185
- const reference = String(variableNodeId || "").trim();
1186
- if (!reference) {
1187
- throw new Error("Alarm variableNodeId is required");
1188
- }
1189
-
1190
- if (reference.indexOf(".") === 0) {
1191
- const relativeReference = this.resolveObjectTypeRelativeReference(parentPath, reference);
1192
-
1193
- //not work
1194
- var corrente = parentNode.getComponentByName("corrent")
1195
-
1196
- return {
1197
- node: corrente
1198
- }
1199
- if (this.variableStore.has(relativeReference)) {
1200
- return this.variableStore.get(relativeReference);
1201
- }
1202
- }
1203
-
1204
- if (this.variableStore.has(reference)) {
1205
- return this.variableStore.get(reference);
1206
- }
1207
-
1208
- try {
1209
- return this.getVariableRecordByNodeId(reference);
1210
- } catch (error) {
1211
- // ignore and continue with path heuristics
1212
- }
1213
-
1214
- const stringNodeIdPath = this.extractStringNodeIdPath(reference);
1215
- if (stringNodeIdPath) {
1216
- if (this.variableStore.has(stringNodeIdPath)) {
1217
- return this.variableStore.get(stringNodeIdPath);
1218
- }
1219
-
1220
- const relativeStringNodeIdPath = this.buildPath(parentPath, stringNodeIdPath);
1221
- if (this.variableStore.has(relativeStringNodeIdPath)) {
1222
- return this.variableStore.get(relativeStringNodeIdPath);
1223
- }
1224
- }
1225
-
1226
- const relativePath = this.buildPath(parentPath, reference);
1227
- if (this.variableStore.has(relativePath)) {
1228
- return this.variableStore.get(relativePath);
1229
- }
1230
-
1231
- const suffixMatches = Array.from(this.variableStore.entries())
1232
- .filter(([path]) => {
1233
- return path === reference ||
1234
- path.indexOf("." + reference) !== -1 ||
1235
- (stringNodeIdPath && (path === stringNodeIdPath || path.indexOf("." + stringNodeIdPath) !== -1));
1236
- });
1237
-
1238
- if (suffixMatches.length === 1) {
1239
- return suffixMatches[0][1];
1240
- }
1241
-
1242
- throw new Error("Unknown alarm variable reference: " + reference);
1243
- }
1244
-
1245
- extractStringNodeIdPath(reference) {
1246
- const match = /^ns=\d+;s=(.+)$/.exec(String(reference || "").trim());
1247
- return match ? match[1] : "";
1248
- }
1249
-
1250
- resolveObjectTypeRelativeReference(rootInstancePath, reference) {
1251
- const tokens = String(reference || "")
1252
- .trim()
1253
- .replace(/^\.+/, "")
1254
- .split(".")
1255
- .filter((token) => token !== "")
1256
- .map((token) => this.normalizeCollectionToken(token));
1257
-
1258
- if (!tokens.length) {
1259
- return rootInstancePath;
1260
- }
1261
-
1262
- return rootInstancePath + "." + tokens.join(".");
1263
- }
1264
-
1265
- normalizeCollectionToken(token) {
1266
- const normalized = String(token || "").trim().toLowerCase();
1267
- const aliases = {
1268
- variaveis: "variables",
1269
- variables: "variables",
1270
- objetos: "objects",
1271
- objects: "objects",
1272
- pastas: "folders",
1273
- folders: "folders",
1274
- metodos: "methods",
1275
- methods: "methods",
1276
- alarmes: "alarms",
1277
- alarms: "alarms",
1278
- objecttypes: "objectsTypes",
1279
- objectstypes: "objectsTypes",
1280
- tiposobjetos: "objectsTypes"
1281
- };
1282
-
1283
- return aliases[normalized] || token;
1284
- }
1285
-
1286
- coerceValue(value, type, expectArray) {
1287
- if (expectArray) {
1288
- const items = this.extractArrayItems(value);
1289
- if (!items) {
1290
- throw new Error("Expected array value for type " + type);
1291
- }
1292
-
1293
- return items.map((item) => this.coerceScalarValue(item, type));
1294
- }
1295
-
1296
- if (typeof value === "string") {
1297
- const trimmed = value.trim();
1298
- if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
1299
- try {
1300
- const parsed = JSON.parse(trimmed);
1301
- if (Array.isArray(parsed)) {
1302
- throw new Error("Expected scalar value for type " + type + " but received array");
1303
- }
1304
- } catch (error) {
1305
- if (error.message.indexOf("Expected scalar value") === 0) {
1306
- throw error;
1307
- }
1308
-
1309
- throw new Error("Invalid array value for type " + type + ": " + error.message);
1310
- }
1311
- }
1312
- }
1313
-
1314
- if (this.extractArrayItems(value)) {
1315
- throw new Error("Expected scalar value for type " + type + " but received array");
1316
- }
1317
-
1318
- return this.coerceScalarValue(value, type);
1319
- }
1320
-
1321
- coerceScalarValue(value, type) {
1322
-
1323
- if (type === "Int16") {
1324
- const parsed = Number(value);
1325
- if (!Number.isFinite(parsed)) {
1326
- return 0;
1327
- }
1328
- return Math.trunc(parsed);
1329
- }
1330
-
1331
-
1332
- if (type === "Int32") {
1333
- const parsed = Number(value);
1334
- if (!Number.isFinite(parsed)) {
1335
- return 0;
1336
- }
1337
- return Math.trunc(parsed);
1338
- }
1339
-
1340
- if (type === "Float") {
1341
- const parsed = Number(value);
1342
- if (!Number.isFinite(parsed)) {
1343
- return 0;
1344
- }
1345
- return parsed;
1346
- }
1347
-
1348
- if (type === "Boolean") {
1349
- if (typeof value === "string") {
1350
- const normalized = value.trim().toLowerCase();
1351
- if (normalized === "false" || normalized === "0" || normalized === "") {
1352
- return false;
1353
- }
1354
- if (normalized === "true" || normalized === "1") {
1355
- return true;
1356
- }
1357
- }
1358
-
1359
- return Boolean(value);
1360
- }
1361
-
1362
- return value === undefined || value === null ? "" : String(value);
1363
- }
1364
-
1365
- isArrayValue(value) {
1366
- return this.extractArrayItems(value) !== null;
1367
- }
1368
-
1369
- extractArrayItems(value) {
1370
- if (Array.isArray(value)) {
1371
- return value;
1372
- }
1373
-
1374
- if (typeof value === "string") {
1375
- const trimmed = value.trim();
1376
- if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
1377
- const parsed = JSON.parse(trimmed);
1378
- return Array.isArray(parsed) ? parsed : null;
1379
- }
1380
- return null;
1381
- }
1382
-
1383
- if (ArrayBuffer.isView(value)) {
1384
- return Array.from(value);
1385
- }
1386
-
1387
- return null;
1388
- }
1389
- }
1390
-
1391
- function collapsePaths(paths) {
1392
- return Array.from(new Set(paths))
1393
- .sort(comparePathDepthAsc)
1394
- .filter((path, index, ordered) => {
1395
- for (let current = 0; current < index; current += 1) {
1396
- if (isSamePathOrDescendant(path, ordered[current])) {
1397
- return false;
1398
- }
1399
- }
1400
- return true;
1401
- });
1402
- }
1403
-
1404
- function isSamePathOrDescendant(path, rootPath) {
1405
- return path === rootPath || path.indexOf(rootPath + ".") === 0;
1406
- }
1407
-
1408
- function comparePathDepthAsc(left, right) {
1409
- return pathDepth(left) - pathDepth(right);
1410
- }
1411
-
1412
- function comparePathDepthDesc(left, right) {
1413
- return pathDepth(right) - pathDepth(left);
1414
- }
1415
-
1416
- function pathDepth(path) {
1417
- return String(path || "").split(".").length;
1418
- }
1419
-
1420
- function compareEntryCreationOrder(left, right) {
1421
- const depthDelta = pathDepth(left.path) - pathDepth(right.path);
1422
- if (depthDelta !== 0) {
1423
- return depthDelta;
1424
- }
1425
-
1426
- return kindRank(left.kind) - kindRank(right.kind);
1427
- }
1428
-
1429
- function kindRank(kind) {
1430
- if (kind === "objectTypeDefinition") {
1431
- return -1;
1432
- }
1433
- if (kind === "folder") {
1434
- return 0;
1435
- }
1436
- if (kind === "object") {
1437
- return 1;
1438
- }
1439
- if (kind === "objectTypeInstance") {
1440
- return 1;
1441
- }
1442
- if (kind === "variable") {
1443
- return 2;
1444
- }
1445
- if (kind === "method") {
1446
- return 3;
1447
- }
1448
- if (kind === "alarm") {
1449
- return 4;
1450
- }
1451
- return 10;
1452
- }
1453
-
1454
- module.exports = {
1455
- OpcUaAddressSpaceBuilder
1456
- };