node-opcua-address-space 2.86.1 → 2.88.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.
Files changed (48) hide show
  1. package/LICENSE +1 -1
  2. package/dist/source/index.d.ts +1 -0
  3. package/dist/source/index.js +1 -0
  4. package/dist/source/index.js.map +1 -1
  5. package/dist/src/base_node_private.js +7 -6
  6. package/dist/src/base_node_private.js.map +1 -1
  7. package/dist/src/extension_object_array_node.d.ts +2 -40
  8. package/dist/src/extension_object_array_node.js +9 -45
  9. package/dist/src/extension_object_array_node.js.map +1 -1
  10. package/dist/src/idx_iterator.d.ts +8 -0
  11. package/dist/src/idx_iterator.js +51 -0
  12. package/dist/src/idx_iterator.js.map +1 -0
  13. package/dist/src/index_current.d.ts +1 -0
  14. package/dist/src/index_current.js +1 -0
  15. package/dist/src/index_current.js.map +1 -1
  16. package/dist/src/namespace_impl.js +7 -4
  17. package/dist/src/namespace_impl.js.map +1 -1
  18. package/dist/src/nodeid_manager.d.ts +6 -6
  19. package/dist/src/nodeid_manager.js +71 -97
  20. package/dist/src/nodeid_manager.js.map +1 -1
  21. package/dist/src/nodeset_tools/construct_namespace_dependency.d.ts +14 -0
  22. package/dist/src/nodeset_tools/construct_namespace_dependency.js +21 -1
  23. package/dist/src/nodeset_tools/construct_namespace_dependency.js.map +1 -1
  24. package/dist/src/nodeset_tools/nodeset_to_xml.d.ts +1 -3
  25. package/dist/src/nodeset_tools/nodeset_to_xml.js +24 -10
  26. package/dist/src/nodeset_tools/nodeset_to_xml.js.map +1 -1
  27. package/dist/src/private_namespace.d.ts +5 -0
  28. package/dist/src/private_namespace.js +20 -0
  29. package/dist/src/private_namespace.js.map +1 -0
  30. package/dist/src/ua_variable_impl.d.ts +11 -9
  31. package/dist/src/ua_variable_impl.js +23 -3
  32. package/dist/src/ua_variable_impl.js.map +1 -1
  33. package/dist/src/ua_variable_impl_ext_obj.d.ts +3 -1
  34. package/dist/src/ua_variable_impl_ext_obj.js +152 -20
  35. package/dist/src/ua_variable_impl_ext_obj.js.map +1 -1
  36. package/package.json +40 -40
  37. package/source/index.ts +1 -0
  38. package/src/base_node_private.ts +52 -48
  39. package/src/extension_object_array_node.ts +15 -54
  40. package/src/idx_iterator.ts +52 -0
  41. package/src/index_current.ts +1 -0
  42. package/src/namespace_impl.ts +23 -20
  43. package/src/nodeid_manager.ts +84 -100
  44. package/src/nodeset_tools/construct_namespace_dependency.ts +30 -9
  45. package/src/nodeset_tools/nodeset_to_xml.ts +30 -14
  46. package/src/private_namespace.ts +18 -0
  47. package/src/ua_variable_impl.ts +34 -13
  48. package/src/ua_variable_impl_ext_obj.ts +186 -33
@@ -1,12 +1,12 @@
1
1
  import * as chalk from "chalk";
2
2
  import assert from "node-opcua-assert";
3
- import { BindExtensionObjectOptions, UADataType, UAVariable, UAVariableType } from "node-opcua-address-space-base";
4
- import { NodeClass } from "node-opcua-data-model";
5
- import { getCurrentClock, PreciseClock } from "node-opcua-date-time";
3
+ import { BindExtensionObjectOptions, UADataType, UADynamicVariableArray, UAVariable, UAVariableType } from "node-opcua-address-space-base";
4
+ import { coerceQualifiedName, NodeClass } from "node-opcua-data-model";
5
+ import { getCurrentClock, PreciseClock, DateWithPicoseconds, coerceClock } from "node-opcua-date-time";
6
6
  import { DataValue } from "node-opcua-data-value";
7
7
  import { make_debugLog, make_warningLog, checkDebugFlag, make_errorLog } from "node-opcua-debug";
8
8
  import { ExtensionObject } from "node-opcua-extension-object";
9
- import { NodeId } from "node-opcua-nodeid";
9
+ import { coerceNodeId, NodeId, NodeIdType } from "node-opcua-nodeid";
10
10
  import { StatusCodes, CallbackT, StatusCode } from "node-opcua-status-code";
11
11
  import { StructureField } from "node-opcua-types";
12
12
  import { lowerFirstLetter } from "node-opcua-utils";
@@ -15,6 +15,9 @@ import { DataType, Variant, VariantLike, VariantArrayType } from "node-opcua-var
15
15
  import { valueRankToString } from "./base_node_private";
16
16
  import { UAVariableImpl } from "./ua_variable_impl";
17
17
  import { UADataTypeImpl } from "./ua_data_type_impl";
18
+ import { bindExtObjArrayNode } from "./extension_object_array_node";
19
+ import { IndexIterator } from "./idx_iterator";
20
+ import { DateTime } from "node-opcua-basic-types";
18
21
 
19
22
  const doDebug = checkDebugFlag(__filename);
20
23
  const debugLog = make_debugLog(__filename);
@@ -114,7 +117,7 @@ function propagateTouchValueDownward(self: UAVariableImpl, now: PreciseClock): v
114
117
  }
115
118
  }
116
119
 
117
- export function _setExtensionObject(self: UAVariableImpl, ext: ExtensionObject | ExtensionObject[]): void {
120
+ export function _setExtensionObject(self: UAVariableImpl, ext: ExtensionObject | ExtensionObject[], sourceTimestamp?: PreciseClock): void {
118
121
  // assert(!(ext as any).$isProxy, "internal error ! ExtensionObject has already been proxied !");
119
122
  if (Array.isArray(ext)) {
120
123
  assert(self.valueRank === 1, "Only Array is supported for the time being");
@@ -133,7 +136,7 @@ export function _setExtensionObject(self: UAVariableImpl, ext: ExtensionObject |
133
136
  self.$dataValue.statusCode = StatusCodes.Good;
134
137
  }
135
138
 
136
- const now = getCurrentClock();
139
+ const now = sourceTimestamp || getCurrentClock();
137
140
  propagateTouchValueUpward(self, now);
138
141
  propagateTouchValueDownward(self, now);
139
142
  }
@@ -193,7 +196,10 @@ function getOrCreateProperty(
193
196
  browseName: { namespaceIndex: structureNamespace, name: field.name!.toString() },
194
197
  componentOf: variableNode,
195
198
  dataType: field.dataType,
196
- minimumSamplingInterval: variableNode.minimumSamplingInterval
199
+ minimumSamplingInterval: variableNode.minimumSamplingInterval,
200
+ accessLevel: variableNode.accessLevel,
201
+ accessRestrictions: variableNode.accessRestrictions,
202
+ rolePermissions: variableNode.rolePermissions
197
203
  }) as UAVariableImpl;
198
204
  assert(property.minimumSamplingInterval === variableNode.minimumSamplingInterval);
199
205
  }
@@ -226,17 +232,46 @@ function bindProperty(variableNode: UAVariableImpl, propertyNode: UAVariableImpl
226
232
  return new DataValue(propertyNode.$dataValue);
227
233
  },
228
234
  timestamped_set: (_dataValue: DataValue, callback: CallbackT<StatusCode>) => {
229
- callback(null, StatusCodes.BadNotWritable);
235
+
236
+ propertyNode.setValueFromSource(_dataValue.value, _dataValue.statusCode, _dataValue.sourceTimestamp || new Date());
237
+ // callback(null, StatusCodes.BadNotWritable);
238
+ callback(null, StatusCodes.Good);
230
239
  }
231
240
  },
232
241
  true
233
242
  );
234
243
  }
235
244
 
245
+
246
+ function _initial_setup(variableNode: UAVariableImpl) {
247
+ const dataValue = variableNode.readValue();
248
+ const extObj = dataValue.value.value;
249
+ if (extObj instanceof ExtensionObject) {
250
+ variableNode.bindExtensionObject(extObj, { createMissingProp: true, force: true });
251
+ } else if (extObj instanceof Array) {
252
+ if (dataValue.value.arrayType === VariantArrayType.Array) {
253
+ variableNode.bindExtensionObjectArray(extObj, { createMissingProp: true, force: true });
254
+ } else if (dataValue.value.arrayType === VariantArrayType.Matrix) {
255
+ _bindExtensionObjectMatrix(variableNode, extObj, { createMissingProp: true, force: true });
256
+ } else {
257
+ throw new Error("Internal Error, unexpected case");
258
+ }
259
+ } else {
260
+ const msg = `variableNode ${variableNode.browseName.toString()} doesn't have $extensionObject property`;
261
+ errorLog(msg);
262
+ errorLog(dataValue.toString());
263
+ throw new Error(msg);
264
+ }
265
+ }
236
266
  export function _installExtensionObjectBindingOnProperties(
237
267
  variableNode: UAVariableImpl,
238
268
  options: BindExtensionObjectOptions
239
269
  ): void {
270
+
271
+ if (!variableNode.$extensionObject) {
272
+ _initial_setup(variableNode);
273
+ return;
274
+ }
240
275
  const addressSpace = variableNode.addressSpace;
241
276
  const dt = variableNode.getDataTypeNode();
242
277
  const definition = dt.getStructureDefinition();
@@ -256,7 +291,12 @@ export function _installExtensionObjectBindingOnProperties(
256
291
  continue;
257
292
  }
258
293
  propertyNode.$dataValue.statusCode = StatusCodes.Good;
259
- propertyNode.touchValue();
294
+ propertyNode.$dataValue.sourceTimestamp = variableNode.$dataValue.sourceTimestamp;
295
+ propertyNode.$dataValue.sourcePicoseconds = variableNode.$dataValue.sourcePicoseconds;
296
+ propertyNode.$dataValue.serverTimestamp = variableNode.$dataValue.serverTimestamp;
297
+ propertyNode.$dataValue.serverPicoseconds = variableNode.$dataValue.serverPicoseconds;
298
+
299
+ //xx propertyNode.touchValue();
260
300
 
261
301
  const basicDataType = addressSpace.findCorrespondingBasicDataType(field.dataType);
262
302
 
@@ -322,36 +362,43 @@ export function _installExtensionObjectBindingOnProperties(
322
362
  }
323
363
  }
324
364
 
325
- // eslint-disable-next-line complexity
326
- export function _bindExtensionObject(
327
- self: UAVariableImpl,
328
- optionalExtensionObject?: ExtensionObject | ExtensionObject[],
329
- options?: BindExtensionObjectOptions
330
- ): ExtensionObject | ExtensionObject[] | null {
331
- options = options || { createMissingProp: false };
332
-
333
-
334
- const addressSpace = self.addressSpace;
365
+ function isVariableContainingExtensionObject(uaVariable: UAVariableImpl): boolean {
366
+ const addressSpace = uaVariable.addressSpace;
335
367
  const structure = addressSpace.findDataType("Structure");
336
- let extensionObject_;
337
368
 
338
369
  if (!structure) {
339
370
  // the addressSpace is limited and doesn't provide extension object
340
371
  // bindExtensionObject cannot be performed and shall finish here.
341
- return null;
372
+ return false;
342
373
  }
343
374
 
344
375
  assert(structure.browseName.toString() === "Structure", "expecting DataType Structure to be in IAddressSpace");
345
376
 
346
- const dt = self.getDataTypeNode() as UADataTypeImpl;
377
+ const dt = uaVariable.getDataTypeNode() as UADataTypeImpl;
347
378
  if (!dt.isSupertypeOf(structure)) {
379
+ return false;
380
+ }
381
+ return true;
382
+
383
+ }
384
+ // eslint-disable-next-line complexity
385
+ export function _bindExtensionObject(
386
+ self: UAVariableImpl,
387
+ optionalExtensionObject?: ExtensionObject | ExtensionObject[],
388
+ options?: BindExtensionObjectOptions
389
+ ): ExtensionObject | ExtensionObject[] | null {
390
+ options = options || { createMissingProp: false };
391
+
392
+ if (!isVariableContainingExtensionObject(self)) {
348
393
  return null;
349
394
  }
395
+ const addressSpace = self.addressSpace;
396
+ let extensionObject_;
350
397
 
351
- if (self.valueRank !== -1 && self.valueRank !==1) {
398
+ if (self.valueRank !== -1 && self.valueRank !== 1) {
352
399
  throw new Error("Cannot bind an extension object here, valueRank must be scalar (-1) or one-dimensional (1)");
353
400
  }
354
-
401
+
355
402
  // istanbul ignore next
356
403
  if (doDebug) {
357
404
  debugLog(" ------------------------------ binding ", self.browseName.toString(), self.nodeId.toString());
@@ -365,6 +412,7 @@ export function _bindExtensionObject(
365
412
  ) {
366
413
  const parentDataType = (self.parent as UAVariable | UAVariableType).dataType;
367
414
  const dataTypeNode = addressSpace.findNode(parentDataType) as UADataType;
415
+ const structure = addressSpace.findDataType("Structure")!;
368
416
  // istanbul ignore next
369
417
  if (dataTypeNode && dataTypeNode.isSupertypeOf(structure)) {
370
418
  // warningLog(
@@ -392,9 +440,9 @@ export function _bindExtensionObject(
392
440
  warningLog(self.$extensionObject?.toString());
393
441
  throw new Error(
394
442
  "bindExtensionObject: $extensionObject is incorrect: we are expecting a " +
395
- self.dataType.toString({ addressSpace: self.addressSpace }) +
396
- " but we got a " +
397
- self.$extensionObject?.constructor.name
443
+ self.dataType.toString({ addressSpace: self.addressSpace }) +
444
+ " but we got a " +
445
+ self.$extensionObject?.constructor.name
398
446
  );
399
447
  }
400
448
  return self.$extensionObject;
@@ -422,6 +470,10 @@ export function _bindExtensionObject(
422
470
  return self.$extensionObject;
423
471
 
424
472
  function innerBindExtensionObject() {
473
+ const makePreciseClock = (sourceTimestamp: DateTime, picoseconds?: number): PreciseClock =>
474
+ ({ timestamp: sourceTimestamp as DateWithPicoseconds, picoseconds: picoseconds || 0 });
475
+
476
+
425
477
  if (s.value && (s.value.dataType === DataType.Null || (s.value.dataType === DataType.ExtensionObject && !s.value.value))) {
426
478
  if (self.valueRank === -1 /** Scalar */) {
427
479
  // create a structure and bind it
@@ -439,13 +491,16 @@ export function _bindExtensionObject(
439
491
  if (!self.checkExtensionObjectIsCorrect(ext)) {
440
492
  return callback(null, StatusCodes.BadInvalidArgument);
441
493
  }
442
- _setExtensionObject(self, ext);
494
+ const sourceTime = coerceClock(dataValue.sourceTimestamp, dataValue.sourcePicoseconds);
495
+ _setExtensionObject(self, ext, sourceTime);
443
496
  callback(null, StatusCodes.Good);
444
497
  }
445
498
  },
446
499
  true
447
500
  );
448
- _setExtensionObject(self, extensionObject_);
501
+ const sourceTime = coerceClock(self.$dataValue.sourceTimestamp, self.$dataValue.sourcePicoseconds);
502
+ _setExtensionObject(self, extensionObject_, sourceTime);
503
+
449
504
  } else if (self.valueRank === 1 /** Array */) {
450
505
  // create a structure and bind it
451
506
 
@@ -462,17 +517,19 @@ export function _bindExtensionObject(
462
517
  return d;
463
518
  },
464
519
  timestamped_set(dataValue: DataValue, callback: CallbackT<StatusCode>) {
465
- const ext = dataValue.value.value;
466
- if (!self.checkExtensionObjectIsCorrect(ext)) {
520
+ const extensionObjectArray = dataValue.value.value;
521
+ if (!self.checkExtensionObjectIsCorrect(extensionObjectArray)) {
467
522
  return callback(null, StatusCodes.BadInvalidArgument);
468
523
  }
469
- _setExtensionObject(self, ext);
524
+ const sourceTime = coerceClock(dataValue.sourceTimestamp, dataValue.sourcePicoseconds);
525
+ _setExtensionObject(self, extensionObjectArray, sourceTime);
470
526
  callback(null, StatusCodes.Good);
471
527
  }
472
528
  },
473
529
  true
474
530
  );
475
- _setExtensionObject(self, extensionObject_);
531
+ const sourceTime = coerceClock(self.$dataValue.sourceTimestamp, self.$dataValue.sourcePicoseconds);
532
+ _setExtensionObject(self, extensionObject_, sourceTime);
476
533
  } else {
477
534
  errorLog(self.toString());
478
535
  errorLog("Unsupported case ! valueRank= ", self.valueRank);
@@ -488,6 +545,102 @@ export function _bindExtensionObject(
488
545
  }
489
546
  }
490
547
 
548
+ const getIndexAsText = (index: number | number[]): string => {
549
+ if (typeof index === "number")
550
+ return `${index}`;
551
+ return `${index.map((a) => a.toString()).join(",")}`;
552
+ }
553
+ const composeBrowseNameAndNodeId = (uaVariable: UAVariable, indexes: number[]) => {
554
+ const iAsText = getIndexAsText(indexes);
555
+ const browseName = coerceQualifiedName(iAsText);
556
+ let nodeId: NodeId | undefined;
557
+ if (uaVariable.nodeId.identifierType === NodeIdType.STRING) {
558
+ nodeId = new NodeId(NodeIdType.STRING, uaVariable.nodeId.value as string + `[${iAsText}]`, uaVariable.nodeId.namespace);
559
+ }
560
+ return { browseName, nodeId };
561
+ }
562
+
563
+ export function _bindExtensionObjectArray(
564
+ uaVariable: UAVariableImpl,
565
+ optionalExtensionObjectArray?: ExtensionObject[],
566
+ options?: BindExtensionObjectOptions
567
+ ): ExtensionObject[] {
568
+ return _bindExtensionObjectMatrix(uaVariable, optionalExtensionObjectArray, options);
569
+ }
570
+ export function _bindExtensionObjectMatrix(
571
+ uaVariable: UAVariableImpl,
572
+ optionalExtensionObjectArray?: ExtensionObject[],
573
+ options?: BindExtensionObjectOptions
574
+ ): ExtensionObject[] {
575
+
576
+ if (uaVariable.valueRank < 1) {
577
+ throw new Error("Variable must be a MultiDimensional array");
578
+ }
579
+ const arrayDimensions = uaVariable.arrayDimensions || [];
580
+
581
+ if (!isVariableContainingExtensionObject(uaVariable)) {
582
+ return [];
583
+ }
584
+ if ((arrayDimensions.length === 0 || arrayDimensions.length === 1 && arrayDimensions[0] === 0) && optionalExtensionObjectArray) {
585
+ arrayDimensions[0] = optionalExtensionObjectArray.length;
586
+ }
587
+
588
+ const totalLength = arrayDimensions.reduce((p, c) => p * c, 1);
589
+
590
+ /** */
591
+ const addressSpace = uaVariable.addressSpace;
592
+ if (optionalExtensionObjectArray) {
593
+ if (optionalExtensionObjectArray.length !== totalLength) {
594
+ throw new Error(`optionalExtensionObjectArray must have the expected number of element matching ${arrayDimensions}`)
595
+ }
596
+ }
597
+ if (!optionalExtensionObjectArray) {
598
+ optionalExtensionObjectArray = [];
599
+ for (let i = 0; i < totalLength; i++) {
600
+ optionalExtensionObjectArray[i] = addressSpace.constructExtensionObject(uaVariable.dataType, {});;
601
+ }
602
+ }
603
+ uaVariable.$$extensionObjectArray = optionalExtensionObjectArray;
604
+
605
+ // uaVariable.$$getElementBrowseName = getElementBrowseNameByIndex;
606
+
607
+ uaVariable.bindVariable({
608
+ get: () => new Variant({
609
+ arrayType: VariantArrayType.Array,
610
+ dataType: DataType.ExtensionObject,
611
+ value: uaVariable.$$extensionObjectArray
612
+ })
613
+ }, true);
614
+ const namespace = uaVariable.namespace;
615
+ const indexIterator = new IndexIterator(arrayDimensions);
616
+ for (let i = 0; i < totalLength; i++) {
617
+
618
+ const index = indexIterator.next();
619
+
620
+ const { browseName, nodeId } = composeBrowseNameAndNodeId(uaVariable, index);
621
+
622
+ const uaElement = namespace.addVariable({
623
+ browseName,
624
+ nodeId,
625
+ componentOf: uaVariable,
626
+ dataType: uaVariable.dataType,
627
+ valueRank: -1,
628
+ accessLevel: uaVariable.userAccessLevel
629
+ });
630
+ {
631
+ const capturedIndex = i;
632
+ uaElement.bindVariable({
633
+ get: () => new Variant({
634
+ dataType: DataType.ExtensionObject,
635
+ arrayType: VariantArrayType.Scalar,
636
+ value: uaVariable.$$extensionObjectArray[capturedIndex]
637
+ })
638
+ })
639
+ }
640
+ }
641
+ return uaVariable.$$extensionObjectArray;
642
+ }
643
+
491
644
  export function extractPartialData(path: string | string[], extensionObject: ExtensionObject) {
492
645
  let name;
493
646
  if (typeof path === "string") {